Conversation
Replace generic marketing language with specific descriptions of what the tool actually does. Key changes: - Hero: plain language headline, npx install command front and center, all 4 supported databases visible (PostgreSQL, MySQL, SQLite, MSSQL) - Features: concrete descriptions with real code examples instead of vague "enterprise-grade" phrasing - Output: new side-by-side section showing actual generated schema.ts and users.ts code, plus file type tags - Demo: realistic terminal walkthrough showing constraint, enum, and foreign key translation steps, plus full command reference - MCP: new section for Claude integration (was missing entirely) - CTA: reframed from hard consulting sell to open source + optional help - Footer: updated copyright to 2025-2026 https://claude.ai/code/session_01VZg64AntZqYqog4viJVu2d
Generators:
- cron-generator: detect expiry/soft-delete/status columns, emit cronJobs() + internalMutation cleanup jobs
- component-config-generator: emit convex.config.ts with migrations, rate-limiter, aggregate components
- scheduled-function-generator: per-table ctx.scheduler helpers for expiry self-deletion + stuck-status retry
- auth-generator: detect users+email+password tables, emit auth.ts (convexAuth + Password provider) + auth.config.ts
- convex-type-mapper: map bytea/blob/binary → v.id("_storage") for Convex file storage
- types + orchestrator (index.ts): wire all new generators into the generation pipeline
Casing:
- Rename all Heyoub → heyoub across package.json, docs, CLI, MCP configs, and landing page
Landing page:
- Full rewrite with Three.js (IcosahedronGeometry node graph, QuadraticBezierCurve3 edges,
4000-particle drift system, UnrealBloomPass post-processing)
- GSAP + ScrollTrigger scroll-reveal for feature cards and sections
- Custom cursor with trail
- 4 output code panels (schema, crons, convex.config, mutations)
- Updated terminal demo showing all new generator outputs
https://claude.ai/code/session_01VZg64AntZqYqog4viJVu2d
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a 3D, interactive landing page; introduces four Convex code generators (auth, crons, component config, scheduled helpers) with generator integration and new output fields; updates type mappings for binary storage and normalizes package/repo identifiers and documentation links to lowercase. Changes
Sequence Diagram(s)sequenceDiagram
participant Schema as Database Schema
participant Auth as Auth Generator
participant Cron as Cron Generator
participant Config as Component Config Generator
participant Helpers as Scheduled Helpers Generator
participant Index as Generator Index
participant FS as File System / Output
Schema->>Index: provide table definitions
Index->>Auth: call generateAuth(tables)
Auth->>Index: return authFile, authConfig (if detected)
Index->>Cron: call generateCrons(tables)
Cron->>Index: return cronsFile, mutationCount
Index->>Config: call generateComponentConfig(tables)
Config->>Index: return componentConfigFile, components list
loop per table
Index->>Helpers: call generateScheduledHelpers(table)
Helpers->>Index: return scheduledHelpers (optional)
end
Index->>FS: write generated files (crons, convex.config, auth files, per-table scheduled files)
FS->>Index: confirm writes / update ConvexGeneratedOutput
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/convex/convex-type-mapper.ts (1)
700-702:⚠️ Potential issue | 🟠 Major
pgToTsTypeforbyteais inconsistent with the new validator mapping.The validator now maps
bytea→v.id("_storage"), butpgToTsTypestill returnsArrayBuffer. If the document field holds a storage ID, the TypeScript type should beId<"_storage">, notArrayBuffer. This will produce incorrect generated type definitions.Proposed fix
// Binary - bytea: 'ArrayBuffer', + bytea: 'Id<"_storage">',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/convex/convex-type-mapper.ts` around lines 700 - 702, The pgToTsType mapping is inconsistent: update the entry that currently maps bytea to 'ArrayBuffer' so it returns the storage ID TS type matching the validator (i.e. Id<"_storage">). Locate the pgToTsType mapping object (the entry "bytea: 'ArrayBuffer'") and replace its return/type string with "Id<\"_storage\">" so generated TypeScript types align with the validator mapping v.id("_storage").
🧹 Nitpick comments (14)
src/generator/convex/index.ts (1)
423-429: Scheduled helpers could be written in the existing per-table loop to avoid a second iteration.Lines 337–387 already iterate
output.tablesto write per-table files. The scheduled helpers write at Lines 424–429 could be folded into that same loop to reduce duplication.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/index.ts` around lines 423 - 429, Fold the separate scheduled-helpers pass into the existing per-table write loop by checking files.scheduledHelpers inside the same iteration that currently writes per-table files (the loop iterating output.tables where other files are written), and if present create tableDir using path.join(baseDir, tableName) and call fs.writeFile(path.join(tableDir, 'scheduled.ts'), files.scheduledHelpers, 'utf-8'); remove the standalone loop that iterates output.tables solely for scheduledHelpers to avoid the duplicate iteration.src/generator/convex/cron-generator.ts (2)
194-232:mutationsparameter is unused inbuildCronSchedule.The
mutationsparameter is passed in but never referenced inside the function body.Proposed fix
-function buildCronSchedule(analysis: TableCronAnalysis, mutations: string[]): CronSchedule[] { +function buildCronSchedule(analysis: TableCronAnalysis): CronSchedule[] {And update the call site on Line 266:
- const schedules = buildCronSchedule(analysis, allMutations); + const schedules = buildCronSchedule(analysis);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/cron-generator.ts` around lines 194 - 232, buildCronSchedule currently declares an unused parameter mutations; remove the unused parameter from the function signature (change function buildCronSchedule(analysis: TableCronAnalysis)...) and update all call sites that pass a mutations array to stop passing that argument (search for calls to buildCronSchedule and remove the second argument), ensuring the function and its usages compile without the unused parameter.
73-80: DuplicatetoCamelCase/toPascalCase— same issue asscheduled-function-generator.ts.Import from
../../shared/types.jsinstead of redefining these utilities.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/cron-generator.ts` around lines 73 - 80, Remove the duplicate implementations of toCamelCase and toPascalCase in cron-generator.ts and import the shared utilities from ../../shared/types.js instead; specifically delete the local functions toCamelCase and toPascalCase and add an import that brings in the same-named helpers (or their exported names) so references inside cron-generator.ts use the shared implementations.src/generator/convex/scheduled-function-generator.ts (2)
16-18:PROCESSING_STATUS_VALUESis declared but never used.Proposed fix
const EXPIRY_COLUMN_PATTERNS = ['expires_at', 'expiry_at', 'expiration_at', 'valid_until']; const STATUS_COLUMN_PATTERNS = ['status', 'state']; -const PROCESSING_STATUS_VALUES = ['pending', 'queued'];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/scheduled-function-generator.ts` around lines 16 - 18, The constant PROCESSING_STATUS_VALUES is declared but never used; remove the unused declaration to clean up the codebase (delete the line defining PROCESSING_STATUS_VALUES) or, if it was intended to be used, replace the unused constant by referencing it where processing-state checks occur (e.g., use PROCESSING_STATUS_VALUES in the logic that handles status/state comparisons alongside STATUS_COLUMN_PATTERNS or in any scheduled-function code that filters pending/queued items). Ensure references use the exact symbol PROCESSING_STATUS_VALUES so imports/exports remain correct.
20-27:toCamelCase/toPascalCaseare duplicated across generators.These same utilities exist in
src/shared/types.ts(re-exported fromsrc/convex/types.ts) and are duplicated again incron-generator.ts. Import from the shared module instead.Proposed fix
import type { TableInfo } from '../../introspector/schema-introspector.js'; +import { toCamelCase, toPascalCase } from '../../shared/types.js'; -const EXPIRY_COLUMN_PATTERNS = ['expires_at', 'expiry_at', 'expiration_at', 'valid_until']; -const STATUS_COLUMN_PATTERNS = ['status', 'state']; -const PROCESSING_STATUS_VALUES = ['pending', 'queued']; - -function toCamelCase(str: string): string { - return str.replace(/_([a-z])/g, (_, l) => l.toUpperCase()); -} - -function toPascalCase(str: string): string { - const c = toCamelCase(str); - return c.charAt(0).toUpperCase() + c.slice(1); -} +const EXPIRY_COLUMN_PATTERNS = ['expires_at', 'expiry_at', 'expiration_at', 'valid_until']; +const STATUS_COLUMN_PATTERNS = ['status', 'state']; +const PROCESSING_STATUS_VALUES = ['pending', 'queued'];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/scheduled-function-generator.ts` around lines 20 - 27, Replace the duplicated toCamelCase and toPascalCase implementations in scheduled-function-generator.ts by importing the existing utilities instead: remove the local function definitions for toCamelCase and toPascalCase and add an import of those functions from the shared types module that re-exports them (the module that exposes the same utilities from convex/types). Ensure all usages in scheduled-function-generator.ts call the imported toCamelCase and toPascalCase so there is no duplicated implementation.src/generator/convex/component-config-generator.ts (1)
74-83:buildInstallCommentis dead code.This function is defined but never called. The install command is computed inline on Line 126. Remove it to avoid confusion.
Proposed fix
-function buildInstallComment(analysis: ComponentAnalysis): string { - const pkgs = [ - '@convex-dev/migrations', - analysis.needsRateLimiter ? '@convex-dev/rate-limiter' : null, - analysis.needsAggregate ? '@convex-dev/aggregate' : null, - ].filter(Boolean); - - return `// Install components: -// npm i ${pkgs.join(' ')}`; -}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/component-config-generator.ts` around lines 74 - 83, Remove the unused helper buildInstallComment (function buildInstallComment and its ComponentAnalysis parameter) since the install command is already computed inline elsewhere; delete the function declaration and any references/imports solely for it to avoid dead code and ensure no unused symbols remain (search for buildInstallComment and ComponentAnalysis in this module to confirm removal).src/convex/convex-type-mapper.ts (1)
95-111: Blanketv.id("_storage")mapping is too aggressive for small binary columns.Not all binary columns warrant file storage. Columns like
password_hash bytea,checksum bytea, oruuid_bytes binary(16)are small inline values that are better served byv.bytes(). Consider making this configurable (e.g., abinaryHandling: 'storage' | 'bytes'option) or only applying it toblob/longblobtypes where large payloads are expected.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/convex/convex-type-mapper.ts` around lines 95 - 111, The current blanket mapping of binary SQL types to v.id("_storage") in convex-type-mapper.ts (entries for keys like bytea, blob, tinyblob, mediumblob, longblob, binary, varbinary, image) is too aggressive; change the mapper to let small fixed-size binaries use v.bytes() and reserve v.id("_storage") for large/blob types or when configured. Add a configurable option (e.g., binaryHandling: 'storage' | 'bytes' or a size threshold) and update the type-to-convex logic to: 1) map types like blob/longblob to v.id("_storage") by default, 2) map fixed-width/bounded types such as bytea, binary(n), varbinary(n), checksum/password_hash examples to v.bytes(), and 3) allow callers to override via the new binaryHandling option so users can force storage for all binaries if desired; update the mapping lookup that currently returns 'v.id("_storage")' for those keys to consult this option and/or inferred size before choosing v.bytes() vs v.id("_storage").landing/index.html (2)
8-10: Google Fonts loaded withoutcrossoriginon the stylesheet link.The
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>is correct, but the actual stylesheet<link>on line 10 could benefit fromcrossorigin="anonymous"as well to ensure the preconnect is fully utilized for font files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/index.html` around lines 8 - 10, Add crossorigin="anonymous" to the Google Fonts stylesheet link so the preconnect to https://fonts.gstatic.com is actually leveraged; update the <link> element with href="https://fonts.googleapis.com/css2?family=Inter:...&display=swap" (the stylesheet <link> tag) to include crossorigin="anonymous".
376-390: CDN scripts loaded without Subresource Integrity (SRI) hashes.GSAP (v3.12.5) and Three.js (v0.169.0) are loaded from
cdn.jsdelivr.netwithoutintegrityattributes. A CDN compromise could inject malicious code. Consider adding SRI hashes, especially if this page is served in production.🔒 Example with SRI (hashes need to be generated for the exact versions)
- <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script> - <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script> + <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js" integrity="sha384-..." crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js" integrity="sha384-..." crossorigin="anonymous"></script>Generate hashes via
openssl dgst -sha384 -binary <file> | openssl base64 -Aor srihash.org.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/index.html` around lines 376 - 390, Add Subresource Integrity (SRI) to the external GSAP script tags and ensure crossorigin is set: compute the appropriate sha384 SRI hashes for "https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js" and "https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js" and add integrity="sha384-..." and crossorigin="anonymous" to those <script> tags; for the Three.js importmap entry you cannot add integrity inside the importmap, so either replace the importmap entry with an explicit module <script type="module" src="https://cdn.jsdelivr.net/npm/three@0.169.0/build/three.module.js" integrity="sha384-..." crossorigin="anonymous"> (and similarly for any addons used) or self-host the three.module.js file and update the importmap to a local path, then ensure script.js (module) continues to import "three" from that secured location.landing/style.css (3)
96-96: Stylelint: Keyframe namescrollFadeshould use kebab-case.The
keyframes-name-patternrule expects kebab-case.♻️ Proposed fix
-@keyframes scrollFade { from { opacity: 0.2; } to { opacity: 0.5; } } +@keyframes scroll-fade { from { opacity: 0.2; } to { opacity: 0.5; } }Also update the reference on line 93:
-.scroll-hint { ... animation: scrollFade 2s ease-in-out infinite alternate; } +.scroll-hint { ... animation: scroll-fade 2s ease-in-out infinite alternate; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/style.css` at line 96, Rename the keyframe identifier scrollFade to kebab-case (e.g., scroll-fade) in the `@keyframes` declaration and update every usage that references it (for example any animation or animation-name entries that currently use "scrollFade") so the keyframe name and all references match the new kebab-case identifier; ensure no leftover camelCase references remain.
63-64: Multiple!importantdeclarations on.btn-ghost— fragile override chain.Four
!importantflags in two lines suggest a specificity battle with.nav-links a. Instead of forcing overrides, increase specificity naturally (e.g.,.nav-links .btn-ghost) or restructure the selectors so the ghost button styles don't conflict.♻️ Proposed fix — raise specificity instead of !important
-.btn-ghost { border: 1px solid var(--border) !important; color: var(--fg) !important; padding: 0.45rem 1.1rem; border-radius: 6px; transition: background 0.2s !important; } -.btn-ghost:hover { background: var(--glass) !important; } +.nav-links .btn-ghost { border: 1px solid var(--border); color: var(--fg); padding: 0.45rem 1.1rem; border-radius: 6px; transition: background 0.2s; } +.nav-links .btn-ghost:hover { background: var(--glass); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/style.css` around lines 63 - 64, The CSS uses multiple "!important" flags on .btn-ghost which creates brittle overrides against .nav-links a; remove the unnecessary "!important" declarations and raise selector specificity instead (for example replace or add rules like .nav-links .btn-ghost and .nav-links .btn-ghost:hover) so the ghost button styles apply without !important, and ensure the new selectors appear after the generic .btn-ghost rules in the stylesheet to preserve cascade order.
24-24: Stylelint: Remove unnecessary quotes around font family names.Stylelint flags
'Inter','Outfit', and'Fira Code'across multiple lines. Per thefont-family-name-quotesrule, only font names that could be confused with CSS keywords or contain special characters need quoting.InterandOutfitare single words and need no quotes.Fira Codecontains a space but Stylelint's rule still flags it — you can use the unquoted two-word name or configure the rule. Consistently fixing these will clear the lint errors.♻️ Example fix (apply pattern to all occurrences)
- font-family: 'Inter', sans-serif; + font-family: Inter, sans-serif; - font-family: 'Outfit', sans-serif; + font-family: Outfit, sans-serif;Note:
'Fira Code'with a space is a borderline case — Stylelint'sfont-family-name-quotesrule in "always-unless-keyword" mode may want quotes, while in default mode it rejects them. Check your Stylelint config to decide.Also applies to: 58-58, 71-71, 101-101, 112-112, 167-167, 180-180
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/style.css` at line 24, Remove unnecessary quotes from font-family declarations flagged by Stylelint: replace occurrences like font-family: 'Inter', sans-serif; font-family: 'Outfit', sans-serif; and font-family: 'Fira Code', monospace; with unquoted names (e.g., font-family: Inter, sans-serif; font-family: Outfit, sans-serif; font-family: Fira Code, monospace;) to satisfy the font-family-name-quotes rule; update every matching declaration (the ones currently using 'Inter', 'Outfit', and 'Fira Code') for consistency, or if your Stylelint config requires it, adjust the rule instead of changing the declarations.landing/script.js (2)
345-362: Two separatemousemovelisteners for cursor and trail — consider consolidating.Lines 350-354 and 356-361 each add a
mousemovehandler. The trail handler also spawns asetTimeoutevery mouse move (potentially hundreds per second), creating timer churn. Merging both into one handler and usingrequestAnimationFrameor CSS transitions alone for the lag would be more efficient.♻️ Proposed consolidation
if (cursor && trail) { - let cx = 0, cy = 0; - document.addEventListener('mousemove', (e) => { - cx = e.clientX; cy = e.clientY; - cursor.style.left = cx + 'px'; - cursor.style.top = cy + 'px'; - }); - // Trail follows with CSS transition — set via style for smooth lag effect - document.addEventListener('mousemove', (e) => { - setTimeout(() => { - trail.style.left = e.clientX + 'px'; - trail.style.top = e.clientY + 'px'; - }, 40); - }); + document.addEventListener('mousemove', (e) => { + cursor.style.left = e.clientX + 'px'; + cursor.style.top = e.clientY + 'px'; + // Trail follows via its CSS transition — just update position directly + trail.style.left = e.clientX + 'px'; + trail.style.top = e.clientY + 'px'; + }); }The trail's CSS
transition: left 0.12s ease-out, top 0.12s ease-out(in style.css line 46) already provides the lag effect; thesetTimeoutis redundant.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/script.js` around lines 345 - 362, Consolidate the two mousemove handlers into a single document.addEventListener('mousemove') callback that updates both DOM nodes (cursor and cursor-trail) to avoid creating a setTimeout per move; remove the setTimeout call used for the trail and either update trail positions inside a requestAnimationFrame loop or rely on the existing CSS transition (on `#cursor-trail`) for the lag effect, updating both cursor.style.left/top and trail.style.left/top inside the single handler so you only reference the same cursor and trail elements once.
274-291: Global indexias delay multiplier causes unbounded delays for later.reveal-upelements.
iis the position in the fullquerySelectorAllresult set. The 15th.reveal-upelement on the page would wait 0.75 s after entering the viewport — even if it's the first element the user scrolls to. Consider using a section-relative index or a small fixed stagger.♻️ Suggested approach: cap or remove the global delay
document.querySelectorAll('.reveal-up').forEach((el, i) => { ScrollTrigger.create({ trigger: el, start: 'top 88%', onEnter: () => { gsap.to(el, { opacity: 1, y: 0, duration: 0.8, - delay: i * 0.05, + delay: 0, ease: 'power3.out', }); el.classList.add('in-view'); }, once: true, }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/script.js` around lines 274 - 291, The delay uses the global index i from document.querySelectorAll('.reveal-up'), causing large delays for later elements; change the delay strategy used in the ScrollTrigger/gsap block (ScrollTrigger.create and gsap.to) to a bounded or local index — for example compute a section-relative index (reset a counter per parent section) or cap the multiplier with Math.min(i, N) or replace per-element delay with a small fixed stagger (e.g., use gsap's stagger or a constant delay) so the delay never grows unbounded for elements far down the NodeList.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@landing/index.html`:
- Around line 13-20: Wrap the page's primary content sections in a semantic
<main> landmark: move the hero, features, output/demo/mcp, and CTA blocks (the
sections currently living directly in <body> — look for elements following the
Three.js canvas `#bg-canvas` and the cursor elements `#cursor` and `#cursor-trail`)
inside a newly added <main> element so assistive tech can identify the primary
content region; keep any <nav> outside <main> if present and ensure the <canvas
id="bg-canvas"> and custom cursor elements remain positioned as needed while
being visually outside or before the <main> content.
In `@landing/script.js`:
- Around line 199-203: The scrollProgress calculation can divide by zero when
document.body.scrollHeight equals window.innerHeight, causing NaN/Infinity to
propagate into camera.position.z and bloomPass.strength; update the scroll
handler (and initial/resize calculation if present) to compute a safe
denominator like let denom = document.body.scrollHeight - window.innerHeight; if
(denom <= 0) set scrollProgress = 0 else compute scrollProgress =
clamp(window.scrollY / denom, 0, 1), ensuring scrollProgress is always a finite
value between 0 and 1 before it is used by camera.position.z and
bloomPass.strength.
- Around line 371-381: The empty catch in the click handler for copyBtn (which
calls navigator.clipboard.writeText with installCmd.textContent) silently
swallows failures; update the catch to handle clipboard errors by logging the
error (console.error or a UI-visible logger) and providing a fallback copy
mechanism: attempt to create a temporary textarea, set its value to
installCmd.textContent.trim(), select it and run document.execCommand('copy'),
then remove it; if that also fails, update copyBtn or show a visible
tooltip/alert indicating the copy failed so the user gets feedback. Ensure the
code references the existing copyBtn, installCmd, and
navigator.clipboard.writeText flow and restores the button icon/state in all
failure paths.
- Around line 237-254: When teleporting a particle back to the edge inside the
update loop, also recompute its velocity so it points toward the center: after
assigning pos[i*3], pos[i*3+1], pos[i*3+2] (the new position), compute the
direction vector from that new position to the origin, normalize it and set
particleVelocities[i].dx, .dy, .dz to that normalized direction multiplied by
the particle speed (use the existing speed magnitude from particleVelocities[i]
if you want to preserve speed, or a chosen constant). This ensures the velocity
vector for particleVelocities[i] aligns with the new pos and prevents
stale-direction drift.
- Line 15: document.getElementById('bg-canvas') can return null causing
THREE.WebGLRenderer to create a detached canvas; add a null-check after const
canvas = document.getElementById('bg-canvas') (or guard before instantiating new
THREE.WebGLRenderer) and either log an error and return early or throw so the
rest of the init (renderer, scene, animate) does not run; reference the canvas
variable and the renderer construction site (where new THREE.WebGLRenderer is
called) to ensure you never pass a null canvas into the renderer.
In `@landing/style.css`:
- Around line 23-30: The global rule "body { cursor: none }" hides the system
pointer for keyboard and assistive-technology users; change the CSS so the
custom cursor hiding is scoped (e.g., apply cursor: none only to the custom
cursor overlay element/class used by your JS cursor) and restore the native
cursor for interactive and keyboard-focused elements by adding rules for
:focus-visible, a, button, input, textarea, [role="button"], etc., so those
selectors explicitly set cursor: auto (or revert) to ensure keyboard-only and AT
users see the system pointer and focus outlines.
In `@src/cli/errors/index.ts`:
- Line 123: The docsUrl value currently points to the wrong repository path;
update the docsUrl constant (docsUrl:
'https://github.com/heyoub/sunsetter-aqm/wiki/errors#ERR-001') to use the
correct repository name and casing:
'https://github.com/heyoub/SunSetter_AQM/wiki/errors#ERR-001', and then search
for and replace any other documentation URLs that use incorrect repo names
(e.g., the 'db.aqm' references) so all docs/help URLs in the codebase
consistently use 'https://github.com/heyoub/SunSetter_AQM'.
In `@src/generator/convex/auth-generator.ts`:
- Around line 55-56: The password column detection currently uses c.includes(p)
in the computation of hasPassword which produces false positives (e.g.,
reset_password_token). Update the check in the hasPassword logic to use exact
match against PASSWORD_COLUMN_PATTERNS (c === p) or implement anchored checks
such as c === p || c.startsWith(p + '_') || c.endsWith('_' + p) when iterating
over cols and PASSWORD_COLUMN_PATTERNS; keep the hasEmail behavior (exact match)
as the model and adjust the expression referencing hasPassword and
PASSWORD_COLUMN_PATTERNS accordingly.
In `@src/generator/convex/component-config-generator.ts`:
- Around line 44-47: The loop over table.foreignKeys accesses a non-existent
property fk.referencedTableName causing a TypeScript error; update the code in
component-config-generator.ts to use fk.referencedTable instead (i.e., replace
referencedTableName with referencedTable where you compute ref and update
inboundFKCount for table.foreignKeys), preserving the existing null-coalescing
logic for table.foreignKeys and inboundFKCount.
In `@src/generator/convex/cron-generator.ts`:
- Around line 82-149: generateCleanupMutation currently uses mutually exclusive
if/else-if returns so only one mutation (e.g., deleteExpired${pascal}) is
emitted per table; change it to allow multiple mutations by accumulating
snippets instead of returning on first match: initialize a snippets array at the
top of generateCleanupMutation, for each condition (analysis.hasExpiry &&
analysis.expiryField, analysis.hasSoftDelete && analysis.softDeleteField,
analysis.hasCreatedAt && analysis.createdAtField) push the corresponding
mutation string (the deleteExpired${pascal}, purgeDeleted${pascal},
cleanupOld${pascal} templates) into the array (use the same
toPascalCase/toCamelCase field logic), and at the end return
snippets.join("\n\n") so all applicable mutations are emitted. Ensure you remove
the early returns inside each branch.
- Around line 86-102: The generated cron mutation deleteExpired${pascal}
currently uses an unbounded .collect() (via
ctx.db.query("${analysis.tableName}")...collect()) which can OOM; change the
generated handler to process records in bounded batches (e.g., use
.take(batchSize) with a loop/while that re-queries until no more expired rows)
and delete each batch before fetching the next, ensuring you do not call
.collect() on the full result set; apply the same batching pattern to the
analogous soft-delete and created_at cleanup mutations produced by this
generator (references: internalMutation, deleteExpired${pascal}, any
soft-delete/created_at generator functions and the field variable produced by
toCamelCase(analysis.expiryField)).
In `@src/generator/convex/index.ts`:
- Around line 173-203: Remove the unnecessary casts to any on this.options and
use the strongly-typed flags from ConvexFunctionGeneratorOptions directly;
replace checks like (this.options as any).generateCrons with
this.options.generateCrons (and similarly for generateComponentConfig,
generateAuth, generateScheduledHelpers) within the code block that produces
output.cronsFile, output.componentConfigFile, authResult handling
(generateAuth), and the scheduled helpers loop so TypeScript enforces the
declared flags and no any casts remain.
In `@src/generator/convex/scheduled-function-generator.ts`:
- Around line 84-86: The Convex internal API path is missing the scheduled
segment in the scheduler invocations; update the three calls that reference
internal.${tableName}.deleteExpired${pascal},
internal.${tableName}.process${pascal}, and
internal.${tableName}.scheduleRetry${pascal} to include the scheduled namespace
so they read internal.${tableName}.scheduled.deleteExpired${pascal},
internal.${tableName}.scheduled.process${pascal}, and
internal.${tableName}.scheduled.scheduleRetry${pascal} respectively; locate the
calls made via ctx.scheduler.runAfter / runAt (the existing invocations that
pass id or args) and adjust the function path strings to insert ".scheduled"
between the tableName and the function name.
---
Outside diff comments:
In `@src/convex/convex-type-mapper.ts`:
- Around line 700-702: The pgToTsType mapping is inconsistent: update the entry
that currently maps bytea to 'ArrayBuffer' so it returns the storage ID TS type
matching the validator (i.e. Id<"_storage">). Locate the pgToTsType mapping
object (the entry "bytea: 'ArrayBuffer'") and replace its return/type string
with "Id<\"_storage\">" so generated TypeScript types align with the validator
mapping v.id("_storage").
---
Duplicate comments:
In `@src/cli/help.ts`:
- Around line 206-207: Update the hardcoded repo URLs in the help text so the
repository name is consistent with package.json and error docs (use
"sunsetter-aqm" rather than "db.aqm"); locate the string literals in
src/cli/help.ts that render the help lines with "GitHub Issues" and
"Documentation" and replace the host paths to use "sunsetter-aqm", and also
update the corresponding references in src/cli/errors/index.ts so both files
point to the same canonical repo name ("sunsetter-aqm").
In `@src/index.ts`:
- Line 811: The printed documentation URL in src/index.ts currently uses the
wrong repo name ('Documentation: https://github.com/heyoub/db.aqm' in the
console.log(chalk.gray(...)) call); update that string to match the repository
name used in package.json (SunSetter_AQM) so the message points to the correct
GitHub repo (adjust the URL in the console.log/chalk.gray call accordingly).
---
Nitpick comments:
In `@landing/index.html`:
- Around line 8-10: Add crossorigin="anonymous" to the Google Fonts stylesheet
link so the preconnect to https://fonts.gstatic.com is actually leveraged;
update the <link> element with
href="https://fonts.googleapis.com/css2?family=Inter:...&display=swap" (the
stylesheet <link> tag) to include crossorigin="anonymous".
- Around line 376-390: Add Subresource Integrity (SRI) to the external GSAP
script tags and ensure crossorigin is set: compute the appropriate sha384 SRI
hashes for "https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js" and
"https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js" and add
integrity="sha384-..." and crossorigin="anonymous" to those <script> tags; for
the Three.js importmap entry you cannot add integrity inside the importmap, so
either replace the importmap entry with an explicit module <script type="module"
src="https://cdn.jsdelivr.net/npm/three@0.169.0/build/three.module.js"
integrity="sha384-..." crossorigin="anonymous"> (and similarly for any addons
used) or self-host the three.module.js file and update the importmap to a local
path, then ensure script.js (module) continues to import "three" from that
secured location.
In `@landing/script.js`:
- Around line 345-362: Consolidate the two mousemove handlers into a single
document.addEventListener('mousemove') callback that updates both DOM nodes
(cursor and cursor-trail) to avoid creating a setTimeout per move; remove the
setTimeout call used for the trail and either update trail positions inside a
requestAnimationFrame loop or rely on the existing CSS transition (on
`#cursor-trail`) for the lag effect, updating both cursor.style.left/top and
trail.style.left/top inside the single handler so you only reference the same
cursor and trail elements once.
- Around line 274-291: The delay uses the global index i from
document.querySelectorAll('.reveal-up'), causing large delays for later
elements; change the delay strategy used in the ScrollTrigger/gsap block
(ScrollTrigger.create and gsap.to) to a bounded or local index — for example
compute a section-relative index (reset a counter per parent section) or cap the
multiplier with Math.min(i, N) or replace per-element delay with a small fixed
stagger (e.g., use gsap's stagger or a constant delay) so the delay never grows
unbounded for elements far down the NodeList.
In `@landing/style.css`:
- Line 96: Rename the keyframe identifier scrollFade to kebab-case (e.g.,
scroll-fade) in the `@keyframes` declaration and update every usage that
references it (for example any animation or animation-name entries that
currently use "scrollFade") so the keyframe name and all references match the
new kebab-case identifier; ensure no leftover camelCase references remain.
- Around line 63-64: The CSS uses multiple "!important" flags on .btn-ghost
which creates brittle overrides against .nav-links a; remove the unnecessary
"!important" declarations and raise selector specificity instead (for example
replace or add rules like .nav-links .btn-ghost and .nav-links .btn-ghost:hover)
so the ghost button styles apply without !important, and ensure the new
selectors appear after the generic .btn-ghost rules in the stylesheet to
preserve cascade order.
- Line 24: Remove unnecessary quotes from font-family declarations flagged by
Stylelint: replace occurrences like font-family: 'Inter', sans-serif;
font-family: 'Outfit', sans-serif; and font-family: 'Fira Code', monospace; with
unquoted names (e.g., font-family: Inter, sans-serif; font-family: Outfit,
sans-serif; font-family: Fira Code, monospace;) to satisfy the
font-family-name-quotes rule; update every matching declaration (the ones
currently using 'Inter', 'Outfit', and 'Fira Code') for consistency, or if your
Stylelint config requires it, adjust the rule instead of changing the
declarations.
In `@src/convex/convex-type-mapper.ts`:
- Around line 95-111: The current blanket mapping of binary SQL types to
v.id("_storage") in convex-type-mapper.ts (entries for keys like bytea, blob,
tinyblob, mediumblob, longblob, binary, varbinary, image) is too aggressive;
change the mapper to let small fixed-size binaries use v.bytes() and reserve
v.id("_storage") for large/blob types or when configured. Add a configurable
option (e.g., binaryHandling: 'storage' | 'bytes' or a size threshold) and
update the type-to-convex logic to: 1) map types like blob/longblob to
v.id("_storage") by default, 2) map fixed-width/bounded types such as bytea,
binary(n), varbinary(n), checksum/password_hash examples to v.bytes(), and 3)
allow callers to override via the new binaryHandling option so users can force
storage for all binaries if desired; update the mapping lookup that currently
returns 'v.id("_storage")' for those keys to consult this option and/or inferred
size before choosing v.bytes() vs v.id("_storage").
In `@src/generator/convex/component-config-generator.ts`:
- Around line 74-83: Remove the unused helper buildInstallComment (function
buildInstallComment and its ComponentAnalysis parameter) since the install
command is already computed inline elsewhere; delete the function declaration
and any references/imports solely for it to avoid dead code and ensure no unused
symbols remain (search for buildInstallComment and ComponentAnalysis in this
module to confirm removal).
In `@src/generator/convex/cron-generator.ts`:
- Around line 194-232: buildCronSchedule currently declares an unused parameter
mutations; remove the unused parameter from the function signature (change
function buildCronSchedule(analysis: TableCronAnalysis)...) and update all call
sites that pass a mutations array to stop passing that argument (search for
calls to buildCronSchedule and remove the second argument), ensuring the
function and its usages compile without the unused parameter.
- Around line 73-80: Remove the duplicate implementations of toCamelCase and
toPascalCase in cron-generator.ts and import the shared utilities from
../../shared/types.js instead; specifically delete the local functions
toCamelCase and toPascalCase and add an import that brings in the same-named
helpers (or their exported names) so references inside cron-generator.ts use the
shared implementations.
In `@src/generator/convex/index.ts`:
- Around line 423-429: Fold the separate scheduled-helpers pass into the
existing per-table write loop by checking files.scheduledHelpers inside the same
iteration that currently writes per-table files (the loop iterating
output.tables where other files are written), and if present create tableDir
using path.join(baseDir, tableName) and call fs.writeFile(path.join(tableDir,
'scheduled.ts'), files.scheduledHelpers, 'utf-8'); remove the standalone loop
that iterates output.tables solely for scheduledHelpers to avoid the duplicate
iteration.
In `@src/generator/convex/scheduled-function-generator.ts`:
- Around line 16-18: The constant PROCESSING_STATUS_VALUES is declared but never
used; remove the unused declaration to clean up the codebase (delete the line
defining PROCESSING_STATUS_VALUES) or, if it was intended to be used, replace
the unused constant by referencing it where processing-state checks occur (e.g.,
use PROCESSING_STATUS_VALUES in the logic that handles status/state comparisons
alongside STATUS_COLUMN_PATTERNS or in any scheduled-function code that filters
pending/queued items). Ensure references use the exact symbol
PROCESSING_STATUS_VALUES so imports/exports remain correct.
- Around line 20-27: Replace the duplicated toCamelCase and toPascalCase
implementations in scheduled-function-generator.ts by importing the existing
utilities instead: remove the local function definitions for toCamelCase and
toPascalCase and add an import of those functions from the shared types module
that re-exports them (the module that exposes the same utilities from
convex/types). Ensure all usages in scheduled-function-generator.ts call the
imported toCamelCase and toPascalCase so there is no duplicated implementation.
| <body> | ||
| <div class="sunset-bg"></div> | ||
|
|
||
| <nav> | ||
| <div class="container"> | ||
| <div class="logo"> | ||
| <span class="sun">☀️</span> SunSetter <span>AQM+</span> | ||
| </div> | ||
| <div class="nav-links"> | ||
| <a href="#features">Features</a> | ||
| <a href="#demo">Demo</a> | ||
| <a href="https://heyoub.dev" class="btn-secondary">Consulting</a> | ||
| <a href="https://github.com/Heyoub/SunSetter_AQM" class="btn-secondary">GitHub</a> | ||
| </div> | ||
| </div> | ||
| </nav> | ||
|
|
||
| <header class="hero"> | ||
| <div class="container"> | ||
| <div class="hero-content"> | ||
| <div class="badge">AQM = Actions · Queries · Mutations</div> | ||
| <h1>The "110%" Database Migration Suite for <span>Convex</span></h1> | ||
| <p class="subtitle">Stop losing your schema logic in migration. SunSetter parses SQL constraints into | ||
| Convex Validators and auto-suggests optimized indexes.</p> | ||
|
|
||
| <div class="hero-actions"> | ||
| <a href="#demo" class="btn-primary">Watch the Wizard</a> | ||
| <a href="#features" class="btn-text">Explore Features →</a> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="terminal-container"> | ||
| <div class="terminal-header"> | ||
| <div class="dots"> | ||
| <span></span><span></span><span></span> | ||
| </div> | ||
| <div class="terminal-title">sunsetter --wizard</div> | ||
| </div> | ||
| <div class="terminal-body" id="terminal-output"> | ||
| <div class="line"><span class="prompt">$</span> sunsetter wizard</div> | ||
| <div class="line typing" data-delay="1000">⏳ Introspecting PostgreSQL schema...</div> | ||
| <div class="line typing" data-delay="3000"><span class="success">✓</span> Found 14 tables in schema | ||
| "public"</div> | ||
| <div class="line typing" data-delay="4000">🧠 Analyzing SQL Constraints...</div> | ||
| <div class="line typing" data-delay="5000"><span class="highlight">Check found:</span> (age >= 18) → | ||
| v.number().gte(18)</div> | ||
| <div class="line typing" data-delay="6000">⚡ Generating Production Code...</div> | ||
| <div class="line typing" data-delay="8000"><span class="success">✓</span> Migration Complete! | ||
| Output: ./convex</div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </header> | ||
|
|
||
| <section id="features" class="features"> | ||
| <div class="container"> | ||
| <div class="section-title"> | ||
| <h2>Why Developers Choose <span>SunSetter</span></h2> | ||
| <p>Enterprise-grade logic preservation that goes beyond simple data moving.</p> | ||
| </div> | ||
|
|
||
| <div class="features-grid"> | ||
| <div class="feature-card"> | ||
| <div class="icon">🧠</div> | ||
| <h3>Intelligent Constraints</h3> | ||
| <p>Don't drop your <code>CHECK</code> constraints. We parse SQL ASTs into Convex <code>v</code> | ||
| validators automatically.</p> | ||
| </div> | ||
| <div class="feature-card"> | ||
| <div class="icon">⚡</div> | ||
| <h3>Auto-Index Optimization</h3> | ||
| <p>SunSetter analyzes foreign keys and query patterns to suggest high-impact Convex indexes for you. | ||
| </p> | ||
| </div> | ||
| <div class="feature-card"> | ||
| <div class="icon">🛡️</div> | ||
| <h3>Resilient Streaming</h3> | ||
| <p>O(1) memory usage via cursor-based pagination. Handles GBs of data without breaking a sweat.</p> | ||
| </div> | ||
| <div class="feature-card"> | ||
| <div class="icon">✨</div> | ||
| <h3>Full API Generation</h3> | ||
| <p>Gets you from zero to backend in seconds with generated Mutations, Queries, and Actions.</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </section> | ||
|
|
||
| <section class="code-preview"> | ||
| <div class="container"> | ||
| <div class="preview-split"> | ||
| <div class="preview-text"> | ||
| <h2>From Legacy SQL to <span>Modern Type-Safety</span></h2> | ||
| <p>SunSetter converts complex SQL relationships and constraints into beautiful, readable Convex | ||
| code.</p> | ||
| <ul class="benefit-list"> | ||
| <li>Enums auto-detected & mapped to <code>v.union()</code></li> | ||
| <li>Primary/Foreign keys mapped to <code>v.id()</code></li> | ||
| <li>JSONB fields mapped to <code>v.any()</code> or <code>v.array()</code></li> | ||
| </ul> | ||
| </div> | ||
| <div class="preview-code-box"> | ||
| <div class="code-header">schema.ts</div> | ||
| <pre><code><span class="keyword">export default</span> <span class="function">defineSchema</span>({ | ||
| <span class="variable">users</span>: <span class="function">defineTable</span>({ | ||
| <span class="variable">name</span>: <span class="variable">v</span>.<span class="keyword">string</span>(), | ||
| <span class="variable">age</span>: <span class="variable">v</span>.<span class="keyword">number</span>().<span class="function">gte</span>(<span class="number">18</span>), | ||
| <span class="variable">role</span>: <span class="variable">v</span>.<span class="function">union</span>( | ||
| <span class="variable">v</span>.<span class="function">literal</span>(<span class="string">"admin"</span>), | ||
| <span class="variable">v</span>.<span class="function">literal</span>(<span class="string">"user"</span>) | ||
| ) | ||
| }).<span class="function">index</span>(<span class="string">"by_role"</span>, [<span class="string">"role"</span>]) | ||
|
|
||
| <!-- Three.js canvas lives here, fixed behind everything --> | ||
| <canvas id="bg-canvas"></canvas> | ||
|
|
||
| <!-- Custom cursor --> | ||
| <div class="cursor" id="cursor"></div> | ||
| <div class="cursor-trail" id="cursor-trail"></div> |
There was a problem hiding this comment.
Missing <main> landmark — screen readers cannot identify the primary content region.
All page content sits directly inside <body> with no <main> element. This is an accessibility gap: assistive technologies rely on landmarks (<main>, <nav>, <footer>) to let users jump between regions. Wrap the sections (hero through CTA) in <main>.
♿ Proposed fix (wrap content sections)
<div class="cursor-trail" id="cursor-trail"></div>
+ <main>
<!-- NAV -->
<nav id="nav">
...
<!-- CTA -->
...
</section>
+ </main>
<!-- FOOTER -->
<footer>Note: <nav> can remain outside <main> if preferred, but the hero/features/output/demo/mcp/cta sections should be inside <main>.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@landing/index.html` around lines 13 - 20, Wrap the page's primary content
sections in a semantic <main> landmark: move the hero, features,
output/demo/mcp, and CTA blocks (the sections currently living directly in
<body> — look for elements following the Three.js canvas `#bg-canvas` and the
cursor elements `#cursor` and `#cursor-trail`) inside a newly added <main> element
so assistive tech can identify the primary content region; keep any <nav>
outside <main> if present and ensure the <canvas id="bg-canvas"> and custom
cursor elements remain positioned as needed while being visually outside or
before the <main> content.
| // THREE.JS SCENE | ||
| // ───────────────────────────────────────────────────────────────────────────── | ||
|
|
||
| const canvas = document.getElementById('bg-canvas'); |
There was a problem hiding this comment.
Guard against a missing canvas element.
document.getElementById('bg-canvas') can return null. If it does, THREE.WebGLRenderer will silently create its own detached canvas instead of rendering to the page's #bg-canvas. Add a null check or an early return to prevent a silently broken scene.
🛡️ Proposed fix
const canvas = document.getElementById('bg-canvas');
+if (!canvas) {
+ console.warn('bg-canvas element not found — 3D scene disabled');
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const canvas = document.getElementById('bg-canvas'); | |
| const canvas = document.getElementById('bg-canvas'); | |
| if (!canvas) { | |
| console.warn('bg-canvas element not found — 3D scene disabled'); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@landing/script.js` at line 15, document.getElementById('bg-canvas') can
return null causing THREE.WebGLRenderer to create a detached canvas; add a
null-check after const canvas = document.getElementById('bg-canvas') (or guard
before instantiating new THREE.WebGLRenderer) and either log an error and return
early or throw so the rest of the init (renderer, scene, animate) does not run;
reference the canvas variable and the renderer construction site (where new
THREE.WebGLRenderer is called) to ensure you never pass a null canvas into the
renderer.
| if (copyBtn && installCmd) { | ||
| copyBtn.addEventListener('click', async () => { | ||
| try { | ||
| await navigator.clipboard.writeText(installCmd.textContent.trim()); | ||
| copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>`; | ||
| setTimeout(() => { | ||
| copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>`; | ||
| }, 1500); | ||
| } catch {} | ||
| }); | ||
| } |
There was a problem hiding this comment.
Empty catch block silently swallows clipboard errors.
If the Clipboard API is unavailable (e.g., non-HTTPS, older browsers), the user gets no feedback at all. At minimum, fall back or log.
🛡️ Proposed fix
- } catch {}
+ } catch {
+ // Fallback: select the text so the user can copy manually
+ const range = document.createRange();
+ range.selectNodeContents(installCmd);
+ const sel = window.getSelection();
+ sel?.removeAllRanges();
+ sel?.addRange(range);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (copyBtn && installCmd) { | |
| copyBtn.addEventListener('click', async () => { | |
| try { | |
| await navigator.clipboard.writeText(installCmd.textContent.trim()); | |
| copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>`; | |
| setTimeout(() => { | |
| copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>`; | |
| }, 1500); | |
| } catch {} | |
| }); | |
| } | |
| if (copyBtn && installCmd) { | |
| copyBtn.addEventListener('click', async () => { | |
| try { | |
| await navigator.clipboard.writeText(installCmd.textContent.trim()); | |
| copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>`; | |
| setTimeout(() => { | |
| copyBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>`; | |
| }, 1500); | |
| } catch { | |
| // Fallback: select the text so the user can copy manually | |
| const range = document.createRange(); | |
| range.selectNodeContents(installCmd); | |
| const sel = window.getSelection(); | |
| sel?.removeAllRanges(); | |
| sel?.addRange(range); | |
| } | |
| }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@landing/script.js` around lines 371 - 381, The empty catch in the click
handler for copyBtn (which calls navigator.clipboard.writeText with
installCmd.textContent) silently swallows failures; update the catch to handle
clipboard errors by logging the error (console.error or a UI-visible logger) and
providing a fallback copy mechanism: attempt to create a temporary textarea, set
its value to installCmd.textContent.trim(), select it and run
document.execCommand('copy'), then remove it; if that also fails, update copyBtn
or show a visible tooltip/alert indicating the copy failed so the user gets
feedback. Ensure the code references the existing copyBtn, installCmd, and
navigator.clipboard.writeText flow and restores the button icon/state in all
failure paths.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 05a432601b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await ctx.db.delete(args.id); | ||
| return; | ||
| } | ||
| await ctx.scheduler.runAfter(delay, internal.${tableName}.deleteExpired${pascal}, { |
There was a problem hiding this comment.
Reference scheduled helpers via the scheduled module path
generateScheduledHelpers() writes helpers to convex/<table>/scheduled.ts, but the emitted scheduler references omit that module segment (for example internal.<table>.deleteExpired... in generateExpiryHelper and similarly in generateStatusHelper). In Convex, those functions are exposed under internal.<table>.scheduled.<fn>, so the current references resolve incorrectly and ctx.scheduler.runAfter(...) will fail at runtime when these helpers execute.
Useful? React with 👍 / 👎.
| const inboundFKCount: Record<string, number> = {}; | ||
| for (const table of tables) { | ||
| for (const fk of table.foreignKeys ?? []) { | ||
| const ref = fk.referencedTableName; |
There was a problem hiding this comment.
Use the correct foreign-key property when counting inbound refs
In analyzeForComponents(), this uses fk.referencedTableName, but ForeignKeyInfo exposes referencedTable. As written, ref becomes undefined, so inbound FK counts are accumulated under an undefined key and every real table appears to have zero inbound references; this prevents write-heavy tables from being detected and suppresses expected rate-limiter recommendations.
Useful? React with 👍 / 👎.
| const delay = args.expiresAt - Date.now(); | ||
| if (delay <= 0) { | ||
| // Already expired — delete immediately | ||
| await ctx.db.delete(args.id); |
There was a problem hiding this comment.
Respect Convex API version in scheduled helper DB calls
The scheduled-helper generator hardcodes old-style DB calls (e.g. ctx.db.delete(args.id) / ctx.db.patch(args.id, ...)) and does not accept convexApiVersion, unlike the existing query/mutation generators. For users targeting API 1.31, generated scheduled helpers will use the wrong method signatures and break during compile/runtime in Convex projects configured for that version.
Useful? React with 👍 / 👎.
- scheduled-function-generator: fix internal.* refs to include `.scheduled.`
path segment — generated code referenced internal.tableName.fn but file
lives at convex/tableName/scheduled.ts, so Convex resolves it as
internal.tableName.scheduled.fn
- component-config-generator: fix fk.referencedTableName → fk.referencedTable
(wrong property name caused undefined FK counts, breaking rate-limiter detection)
- convex-type-mapper pgToTsType(): bytea/blob/binary now map to Id<"_storage">
instead of ArrayBuffer (validator already mapped to v.id("_storage"))
- orchestrator index.ts:
- remove unnecessary (this.options as any) casts
- generateReport() now shows crons.ts, convex.config.ts, auth.ts,
auth.config.ts, and per-table scheduled.ts in output tree
- validate() syntax checks now include all new output files
- cron-generator: remove unused mutations param from buildCronSchedule()
https://claude.ai/code/session_01VZg64AntZqYqog4viJVu2d
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f00a323e4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| await Promise.all( | ||
| stuck.map((doc) => | ||
| ctx.db.patch(doc._id, { ${statusField}: "pending" }) |
There was a problem hiding this comment.
Honor Convex API version in cron-generated DB calls
The cron generator emits old-style database calls like ctx.db.patch(doc._id, ...) (and similar delete/get forms) without any convexApiVersion handling, while the main generators already switch signatures for 1.31. In projects configured for Convex 1.31, these generated cron mutations will be incompatible even though the rest of the generated code targets the new API, causing type/runtime failures when cron code is compiled or executed.
Useful? React with 👍 / 👎.
| for (const table of candidateTables) { | ||
| const cols = table.columns.map(c => c.columnName.toLowerCase()); | ||
| const hasEmail = cols.some(c => EMAIL_COLUMN_PATTERNS.includes(c)); | ||
| const hasPassword = cols.some(c => PASSWORD_COLUMN_PATTERNS.some(p => c.includes(p))); |
There was a problem hiding this comment.
Match password columns by name instead of substring
Auth detection currently treats any column containing a password-like substring as a credential field. For OAuth-only user tables that have fields such as password_reset_token but no actual password hash, this still marks hasPassword as true and generates Password-provider scaffolding that doesn't match the schema. That produces misleading auth files and can send users down a broken setup path.
Useful? React with 👍 / 👎.
Generators (cross-referenced against Convex docs): - cron-generator: .collect() → .take(500) to respect Convex mutation limits, accumulate all cleanup mutations instead of if/else, import shared utils - scheduled-function-generator: import shared toCamelCase/toPascalCase - auth-generator: exact-match password column detection (c === p not includes) - component-config-generator: add .js extension to component imports, remove dead buildInstallComment function Landing page: - Clipboard copy: add fallback selection when Clipboard API unavailable - Consolidate two mousemove listeners into one for custom cursor - Add <main> landmark wrapping all content sections - Add :focus-visible outline styles for keyboard navigation - Canvas null guard, scrollProgress div-by-zero fix, particle velocity reset URLs: - Fix GitHub URLs from db.aqm → SunSetter_AQM in help.ts and index.ts - Fix all 50 error wiki URLs from sunsetter-aqm → SunSetter_AQM https://claude.ai/code/session_01VZg64AntZqYqog4viJVu2d
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 850496558e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .query("${analysis.tableName}") | ||
| .filter((q) => q.lt(q.field("${field}"), now)) | ||
| .take(${CRON_BATCH_SIZE}); |
There was a problem hiding this comment.
Use indexed queries for cron cleanup scans
This cron mutation is generated with ctx.db.query(...).filter(...).take(...) and no withIndex, so on large tables with sparse matches Convex must scan many non-matching documents before finding a batch; that can exceed mutation read limits and cause the scheduled cleanup to fail repeatedly in production. Since schema generation only auto-adds FK/unique indexes, these timestamp/status fields are often unindexed by default.
Useful? React with 👍 / 👎.
| const config = { | ||
| providers: [ | ||
| { | ||
| domain: process.env.CONVEX_SITE_URL, |
There was a problem hiding this comment.
Read SITE_URL in generated auth config
The generated comment instructs users to set SITE_URL, but the actual config reads process.env.CONVEX_SITE_URL; if users follow the generated instructions, domain will be undefined and auth provider callbacks/redirects will be misconfigured at runtime.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (9)
landing/script.js (2)
270-271: Misleading comment: bloom strength does not react to scroll.
scrollProgressisn't used here — strength only oscillates with timet. The comment should match the actual behavior.- // Bloom strength reacts to scroll section + // Subtle bloom strength oscillation over time bloomPass.strength = 0.85 + Math.sin(t * 0.5) * 0.1;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/script.js` around lines 270 - 271, The comment claiming "Bloom strength reacts to scroll section" is incorrect because bloomPass.strength is driven by time variable t, not scrollProgress; update the comment to accurately describe the behavior (e.g., "Bloom strength oscillates over time" or note the time-based sinusoidal modulation) or, if you intended scroll-based control, modify the expression to incorporate scrollProgress into bloomPass.strength; key symbols: bloomPass.strength, t, and scrollProgress—either change the comment to reference t/time or change the assignment to use scrollProgress.
404-412: Smooth scroll: bare#href would throw.
document.querySelector('#')throws aSyntaxError. If any<a href="#">is added to the page, this handler will break. A quick guard avoids future fragility.🛡️ Suggested guard
document.querySelectorAll('a[href^="#"]').forEach((anchor) => { anchor.addEventListener('click', (e) => { - const target = document.querySelector(anchor.getAttribute('href')); + const href = anchor.getAttribute('href'); + if (!href || href === '#') return; + const target = document.querySelector(href); if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth' }); } }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/script.js` around lines 404 - 412, The click handler can call document.querySelector with a bare "#" which throws; update the listener on document.querySelectorAll('a[href^="#"]') so you first read const href = anchor.getAttribute('href') and guard against invalid values (e.g. if (!href || href === '#' || href.length === 1) return) before calling document.querySelector(href), then proceed with e.preventDefault() and target.scrollIntoView({ behavior: 'smooth' }) when a valid target is found; update references in this block (anchor.addEventListener, anchor.getAttribute, document.querySelector, target.scrollIntoView) accordingly.landing/style.css (3)
63-64: Consider a higher-specificity selector instead of!important.
.btn-ghostuses multiple!importantdeclarations to override.nav-links a. Using.nav-links .btn-ghostwould achieve the same result without the cascade override.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/style.css` around lines 63 - 64, The CSS uses multiple !important flags on .btn-ghost to override .nav-links a; replace these with a higher-specificity selector (e.g., .nav-links .btn-ghost) for both the base and hover rules, remove the !important tokens, and keep the same property values (border, color, padding, border-radius, transition, and hover background) so the styles win without forcing cascade overrides; update any references to .btn-ghost:hover to the matching .nav-links .btn-ghost:hover selector.
96-96: Stylelint: keyframe name should use kebab-case.
scrollFadeviolates thekeyframes-name-patternrule. Rename toscroll-fadefor consistency.-@keyframes scrollFade { from { opacity: 0.2; } to { opacity: 0.5; } } +@keyframes scroll-fade { from { opacity: 0.2; } to { opacity: 0.5; } }Also update the reference on line 93:
-.scroll-hint { ... animation: scrollFade 2s ease-in-out infinite alternate; } +.scroll-hint { ... animation: scroll-fade 2s ease-in-out infinite alternate; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/style.css` at line 96, Rename the `@keyframes` identifier "scrollFade" to kebab-case "scroll-fade" and update any references to it (e.g., the animation or animation-name usage that currently points to "scrollFade" on the nearby rule around line 93) so they reference "scroll-fade" instead; ensure the keyframe declaration name and all usages match exactly.
23-30: Stylelint: remove unnecessary quotes around single-word font family names.
'Inter'and'Outfit'are single-word identifiers that don't require quotes per CSS spec.'Fira Code'correctly uses quotes (contains a space). This applies to lines 24, 58, 71, 77, 101, 112, 167, and 180.♻️ Example fix for body
body { - font-family: 'Inter', sans-serif; + font-family: Inter, sans-serif;Apply the same pattern:
'Outfit'→Outfitwherever it appears. Keep'Fira Code'quoted.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/style.css` around lines 23 - 30, Remove unnecessary quotes around single-word font family names in the CSS: replace quoted 'Inter' and 'Outfit' occurrences with unquoted Inter and Outfit in the font-family declarations (e.g., the body rule and other rules referencing 'Inter'/'Outfit'), but keep quotes for multi-word families like 'Fira Code'; update each font-family declaration where 'Inter' or 'Outfit' appears to the unquoted form so the declarations (e.g., body { font-family: Inter, sans-serif } and other selectors using 'Outfit') follow the CSS spec.landing/index.html (2)
16-20: Decorative elements should be hidden from assistive technology.The canvas and custom cursor divs are purely visual. Without
aria-hidden="true", screen readers may announce or try to interact with them unnecessarily.♿ Proposed fix
- <canvas id="bg-canvas"></canvas> + <canvas id="bg-canvas" aria-hidden="true"></canvas> <!-- Custom cursor --> - <div class="cursor" id="cursor"></div> - <div class="cursor-trail" id="cursor-trail"></div> + <div class="cursor" id="cursor" aria-hidden="true"></div> + <div class="cursor-trail" id="cursor-trail" aria-hidden="true"></div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/index.html` around lines 16 - 20, Add accessibility attributes to the purely decorative elements so screen readers ignore them: set aria-hidden="true" on the canvas with id "bg-canvas" and on the two cursor divs with id "cursor" and id "cursor-trail" (you can also add role="none" and ensure they are not focusable if present, e.g., no tabindex or tabindex="-1"). This will prevent assistive tech from announcing or interacting with these decorative elements while keeping their visual behavior unchanged.
380-394: Consider adding Subresource Integrity (SRI) hashes to CDN scripts.The Three.js
0.169.0and GSAP3.12.5versions are valid releases (Three.js published September 26, 2024). While not required for a landing page, adding SRI hashes to the CDN-loaded scripts protects against CDN compromise. For example:<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js" integrity="sha384-..." crossorigin="anonymous"></script>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@landing/index.html` around lines 380 - 394, Add Subresource Integrity (SRI) attributes to the external CDN script tags and the module importmap entries: update the GSAP script tags (gsap@3.12.5 and ScrollTrigger) and the Three.js import URLs (three@0.169.0 and three/addons/) to include the appropriate integrity="sha..." and crossorigin="anonymous" attributes so the browser can verify the files before executing them; ensure the integrity hashes match the exact CDN files you reference and keep the local module script reference to script.js unchanged.src/generator/convex/auth-generator.ts (1)
165-175:schemaNotehardcodes"users"regardless of which table was actually detected.If the detected table is
accounts,members, orprofiles, the generated comment will incorrectly say "a users table with email + password columns was detected" and "Your existing 'users' table data should be migrated", misleading the developer.
analysis.usersTableis already in scope here and should be interpolated.♻️ Proposed fix
-// SunSetter detected a users table with email + password columns. +// SunSetter detected a ${analysis.usersTable} table with email + password columns. // To use Convex Auth, spread authTables into your defineSchema: // // import { authTables } from "@convex-dev/auth/server"; // export default defineSchema({ ...authTables, /* your tables */ }); // // The authTables spread adds users, authSessions, and supporting tables. -// Your existing "users" table data should be migrated or reconciled. +// Your existing "${analysis.usersTable}" table data should be migrated or reconciled.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/auth-generator.ts` around lines 165 - 175, The schemaNote string currently hardcodes "users" which misreports the detected table; update the template in schemaNote to interpolate analysis.usersTable (use analysis.usersTable wherever "users" appears in the message and in the quoted table name) so the generated comment correctly names the detected table and instructs migration for the actual table (refer to symbol schemaNote and the existing analysis.usersTable variable).src/generator/convex/scheduled-function-generator.ts (1)
185-185: Unusedpascalvariable — dead code.
pascalis computed at line 185 but never referenced in the body ofgenerateScheduledHelpers. Each helper generator (generateExpiryHelper,generateStatusHelper) recomputes its ownpascalfromtableNameinternally.♻️ Proposed fix
- const pascal = toPascalCase(table.tableName); - const helpers: string[] = []; + const helpers: string[] = [];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/generator/convex/scheduled-function-generator.ts` at line 185, The variable pascal computed in generateScheduledHelpers is unused and should be removed to avoid dead code; locate the computation "const pascal = toPascalCase(table.tableName)" inside generateScheduledHelpers and delete it, relying on generateExpiryHelper and generateStatusHelper which already compute their own pascal internally; ensure there are no other references to that removed variable within generateScheduledHelpers and run tests/TS compile to confirm no remaining usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/convex/convex-type-mapper.ts`:
- Around line 95-101: Update the comment block that begins "BINARY DATA → Convex
File Storage" (the paragraph that currently states "The tool generates
upload/download action stubs for each detected binary column") to clearly
indicate this feature is planned/aspirational and not implemented; replace that
sentence with wording such as "Planned: generator will eventually emit
upload/download action stubs for detected binary columns; currently no generator
(e.g., ActionGenerator, MutationGenerator) emits these stubs." This change will
avoid promising generated stubs where none exist and should be made in the
convex-type-mapper comment block that documents binary column handling.
- Around line 700-705: pgToTsType is missing mappings for MySQL blob variants,
causing types to default to any; update the pgToTsType map in
convex-type-mapper.ts to include "tinyblob", "mediumblob", and "longblob" and
map each to 'Id<"_storage">' (matching POSTGRES_TO_CONVEX_MAP's runtime
validator). Locate the pgToTsType constant near the existing binary mappings
(entries like bytea/blob/binary/varbinary/image) and add the three keys with the
same 'Id<"_storage">' value so TypeScript types and validators stay consistent.
In `@src/generator/convex/index.ts`:
- Around line 77-80: The four new generator flags (generateCrons,
generateComponentConfig, generateScheduledHelpers, generateAuth) default to
true, causing unexpected files to be emitted; change their defaults to false in
the ConvexFunctionGenerator constructor and any generateConvexCode call-sites so
consumers must opt-in, update parameter types/defaults where these flags are
declared, and add a short changelog/release note documenting the behavioral
change so consumers know to enable the flags explicitly.
In `@src/generator/convex/scheduled-function-generator.ts`:
- Around line 194-197: The helperCount is overcounting exported helpers — when
analysis.hasStatus is true we call generateStatusHelper (which emits
scheduleRetry{Pascal} and process{Pascal} only), so change helperCount += 3 to
helperCount += 2 and update the inline comment to reflect only "scheduleRetry +
process" (remove the "(implicit fail handler)" part); locate this in the block
that checks analysis.hasStatus and calls generateStatusHelper.
---
Duplicate comments:
In `@landing/index.html`:
- Around line 35-371: The <main> landmark has been added and the Hero through
CTA sections are correctly wrapped (ensure the <main> tag contains the hero,
features, output, demo, mcp-section, and cta-section and that <nav> and <footer>
remain outside); approve/merge the PR and remove the duplicate review comment to
avoid noise, referencing the <main> element and section IDs hero, features,
output, demo, mcp-section, and cta-section when confirming the change.
In `@landing/script.js`:
- Around line 15-27: The init currently warns when
document.getElementById('bg-canvas') returns null but still creates a
THREE.WebGLRenderer with canvas=null (symbols: canvas, renderer, W, H), so move
the Three.js setup and animation loop behind an early-exit guard: if canvas is
falsy, keep the console.warn and return/skip the rest; otherwise perform
renderer creation, setPixelRatio/setSize/setClearColor/toneMapping and any
animation logic—this prevents creating a detached canvas and stops the 3D
initialization when the element is missing.
- Around line 385-392: The catch block assumes window.getSelection() returns a
Selection; guard against null by checking the result (e.g., const sel =
window.getSelection();) and use optional chaining or an if-check before calling
methods on it so you don't call removeAllRanges() or addRange() on null; update
the code around range.selectNodeContents(installCmd) / const sel =
window.getSelection() to use sel?.removeAllRanges() and sel?.addRange(range) or
an early return when sel is null.
In `@landing/style.css`:
- Around line 196-204: There’s a duplicate ruleset for :focus-visible and for
a:focus-visible, button:focus-visible; consolidate them by keeping a single
combined selector (e.g., :focus-visible, a:focus-visible, button:focus-visible)
that applies outline and outline-offset to avoid redundancy and ensure
consistent keyboard focus styling across interactive elements.
In `@src/generator/convex/auth-generator.ts`:
- Around line 55-56: The duplicate review comment marker should be removed and
the code left as-is: keep the EMAIL_COLUMN_PATTERNS check used by hasEmail and
the exact-match logic for hasPassword (which uses PASSWORD_COLUMN_PATTERNS and
cols), so remove the "[duplicate_comment]" text from the PR/comment and ensure
hasEmail and hasPassword remain implemented as shown.
In `@src/generator/convex/component-config-generator.ts`:
- Around line 44-47: The foreign key access has already been corrected to use
fk.referencedTable in the loop that updates inboundFKCount (the for loop
iterating table.foreignKeys and the inboundFKCount map), so no code change is
needed; remove the duplicate review comment/placeholder and mark the issue
resolved to avoid redundant feedback.
In `@src/generator/convex/index.ts`:
- Around line 173-203: Remove the leftover duplicate comment marker and any
remaining ad-hoc casts of this.options; ensure the code uses the properly typed
this.options property instead of (this.options as any) and delete the redundant
"[duplicate_comment]" note. Specifically, update the block that checks
this.options.generateCrons/generateComponentConfig/generateAuth/generateScheduledHelpers
and any usages around generateCrons, generateComponentConfig, generateAuth, and
generateScheduledHelpers so they rely on the correct typed this.options and
remove the duplicate comment.
In `@src/generator/convex/scheduled-function-generator.ts`:
- Around line 75-76: The `.scheduled.` path segment is correct in the scheduler
call, so no code change is required to `ctx.scheduler.runAfter` referencing
`internal.${tableName}.scheduled.deleteExpired${pascal}`; instead remove the
duplicated review marker/comment that flagged this line (the
`[duplicate_comment]` entry) and keep the call as-is, ensuring the payload still
passes `id: args.id` to the scheduled function.
---
Nitpick comments:
In `@landing/index.html`:
- Around line 16-20: Add accessibility attributes to the purely decorative
elements so screen readers ignore them: set aria-hidden="true" on the canvas
with id "bg-canvas" and on the two cursor divs with id "cursor" and id
"cursor-trail" (you can also add role="none" and ensure they are not focusable
if present, e.g., no tabindex or tabindex="-1"). This will prevent assistive
tech from announcing or interacting with these decorative elements while keeping
their visual behavior unchanged.
- Around line 380-394: Add Subresource Integrity (SRI) attributes to the
external CDN script tags and the module importmap entries: update the GSAP
script tags (gsap@3.12.5 and ScrollTrigger) and the Three.js import URLs
(three@0.169.0 and three/addons/) to include the appropriate integrity="sha..."
and crossorigin="anonymous" attributes so the browser can verify the files
before executing them; ensure the integrity hashes match the exact CDN files you
reference and keep the local module script reference to script.js unchanged.
In `@landing/script.js`:
- Around line 270-271: The comment claiming "Bloom strength reacts to scroll
section" is incorrect because bloomPass.strength is driven by time variable t,
not scrollProgress; update the comment to accurately describe the behavior
(e.g., "Bloom strength oscillates over time" or note the time-based sinusoidal
modulation) or, if you intended scroll-based control, modify the expression to
incorporate scrollProgress into bloomPass.strength; key symbols:
bloomPass.strength, t, and scrollProgress—either change the comment to reference
t/time or change the assignment to use scrollProgress.
- Around line 404-412: The click handler can call document.querySelector with a
bare "#" which throws; update the listener on
document.querySelectorAll('a[href^="#"]') so you first read const href =
anchor.getAttribute('href') and guard against invalid values (e.g. if (!href ||
href === '#' || href.length === 1) return) before calling
document.querySelector(href), then proceed with e.preventDefault() and
target.scrollIntoView({ behavior: 'smooth' }) when a valid target is found;
update references in this block (anchor.addEventListener, anchor.getAttribute,
document.querySelector, target.scrollIntoView) accordingly.
In `@landing/style.css`:
- Around line 63-64: The CSS uses multiple !important flags on .btn-ghost to
override .nav-links a; replace these with a higher-specificity selector (e.g.,
.nav-links .btn-ghost) for both the base and hover rules, remove the !important
tokens, and keep the same property values (border, color, padding,
border-radius, transition, and hover background) so the styles win without
forcing cascade overrides; update any references to .btn-ghost:hover to the
matching .nav-links .btn-ghost:hover selector.
- Line 96: Rename the `@keyframes` identifier "scrollFade" to kebab-case
"scroll-fade" and update any references to it (e.g., the animation or
animation-name usage that currently points to "scrollFade" on the nearby rule
around line 93) so they reference "scroll-fade" instead; ensure the keyframe
declaration name and all usages match exactly.
- Around line 23-30: Remove unnecessary quotes around single-word font family
names in the CSS: replace quoted 'Inter' and 'Outfit' occurrences with unquoted
Inter and Outfit in the font-family declarations (e.g., the body rule and other
rules referencing 'Inter'/'Outfit'), but keep quotes for multi-word families
like 'Fira Code'; update each font-family declaration where 'Inter' or 'Outfit'
appears to the unquoted form so the declarations (e.g., body { font-family:
Inter, sans-serif } and other selectors using 'Outfit') follow the CSS spec.
In `@src/generator/convex/auth-generator.ts`:
- Around line 165-175: The schemaNote string currently hardcodes "users" which
misreports the detected table; update the template in schemaNote to interpolate
analysis.usersTable (use analysis.usersTable wherever "users" appears in the
message and in the quoted table name) so the generated comment correctly names
the detected table and instructs migration for the actual table (refer to symbol
schemaNote and the existing analysis.usersTable variable).
In `@src/generator/convex/scheduled-function-generator.ts`:
- Line 185: The variable pascal computed in generateScheduledHelpers is unused
and should be removed to avoid dead code; locate the computation "const pascal =
toPascalCase(table.tableName)" inside generateScheduledHelpers and delete it,
relying on generateExpiryHelper and generateStatusHelper which already compute
their own pascal internally; ensure there are no other references to that
removed variable within generateScheduledHelpers and run tests/TS compile to
confirm no remaining usages.
| // BINARY DATA → Convex File Storage | ||
| // Binary columns are mapped to v.id("_storage") because Convex stores | ||
| // files in its built-in file storage system rather than as raw bytes | ||
| // in documents. The tool generates upload/download action stubs for | ||
| // each detected binary column. | ||
| // Docs: https://docs.convex.dev/file-storage | ||
| // ═══════════════════════════════════════════════════════════════ |
There was a problem hiding this comment.
Comment about generated upload/download stubs may be misleading.
The comment claims "The tool generates upload/download action stubs for each detected binary column", but none of the provided generators (ActionGenerator, MutationGenerator, etc.) emit such stubs. If this is aspirational/planned-only, the comment should be rephrased accordingly to avoid misleading users into expecting generated stubs that don't yet exist.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/convex/convex-type-mapper.ts` around lines 95 - 101, Update the comment
block that begins "BINARY DATA → Convex File Storage" (the paragraph that
currently states "The tool generates upload/download action stubs for each
detected binary column") to clearly indicate this feature is
planned/aspirational and not implemented; replace that sentence with wording
such as "Planned: generator will eventually emit upload/download action stubs
for detected binary columns; currently no generator (e.g., ActionGenerator,
MutationGenerator) emits these stubs." This change will avoid promising
generated stubs where none exist and should be made in the convex-type-mapper
comment block that documents binary column handling.
| // Binary → Convex file storage reference | ||
| bytea: 'Id<"_storage">', | ||
| blob: 'Id<"_storage">', | ||
| binary: 'Id<"_storage">', | ||
| varbinary: 'Id<"_storage">', | ||
| image: 'Id<"_storage">', |
There was a problem hiding this comment.
pgToTsType is missing tinyblob, mediumblob, and longblob entries.
POSTGRES_TO_CONVEX_MAP maps all three to v.id("_storage"), so those columns get the correct runtime validator. However, pgToTsType has no matching entries, causing generated TypeScript types to fall back to any instead of Id<"_storage">. The validator and the TypeScript type are now inconsistent for those MySQL blob variants.
🛠️ Proposed fix
// Binary → Convex file storage reference
bytea: 'Id<"_storage">',
blob: 'Id<"_storage">',
+ tinyblob: 'Id<"_storage">',
+ mediumblob: 'Id<"_storage">',
+ longblob: 'Id<"_storage">',
binary: 'Id<"_storage">',
varbinary: 'Id<"_storage">',
image: 'Id<"_storage">',📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Binary → Convex file storage reference | |
| bytea: 'Id<"_storage">', | |
| blob: 'Id<"_storage">', | |
| binary: 'Id<"_storage">', | |
| varbinary: 'Id<"_storage">', | |
| image: 'Id<"_storage">', | |
| // Binary → Convex file storage reference | |
| bytea: 'Id<"_storage">', | |
| blob: 'Id<"_storage">', | |
| tinyblob: 'Id<"_storage">', | |
| mediumblob: 'Id<"_storage">', | |
| longblob: 'Id<"_storage">', | |
| binary: 'Id<"_storage">', | |
| varbinary: 'Id<"_storage">', | |
| image: 'Id<"_storage">', |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/convex/convex-type-mapper.ts` around lines 700 - 705, pgToTsType is
missing mappings for MySQL blob variants, causing types to default to any;
update the pgToTsType map in convex-type-mapper.ts to include "tinyblob",
"mediumblob", and "longblob" and map each to 'Id<"_storage">' (matching
POSTGRES_TO_CONVEX_MAP's runtime validator). Locate the pgToTsType constant near
the existing binary mappings (entries like bytea/blob/binary/varbinary/image)
and add the three keys with the same 'Id<"_storage">' value so TypeScript types
and validators stay consistent.
| generateCrons: true, | ||
| generateComponentConfig: true, | ||
| generateScheduledHelpers: true, | ||
| generateAuth: true, |
There was a problem hiding this comment.
All four new generator flags default to true — generates additional files for all existing consumers without opt-in.
Any caller relying on ConvexFunctionGenerator (or generateConvexCode) without explicitly setting these flags will now find crons.ts and convex.config.ts written to their output directory on every run, even when no cron patterns or component needs were detected (the empty-crons and migrations-only files are still written). This is a silent opt-in for new output files. Consider documenting this change in a changelog or release note, or defaulting the flags to false and requiring explicit enablement for the first release.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/generator/convex/index.ts` around lines 77 - 80, The four new generator
flags (generateCrons, generateComponentConfig, generateScheduledHelpers,
generateAuth) default to true, causing unexpected files to be emitted; change
their defaults to false in the ConvexFunctionGenerator constructor and any
generateConvexCode call-sites so consumers must opt-in, update parameter
types/defaults where these flags are declared, and add a short changelog/release
note documenting the behavioral change so consumers know to enable the flags
explicitly.
| if (analysis.hasStatus && analysis.statusField) { | ||
| helpers.push(generateStatusHelper(table.tableName, analysis.statusField)); | ||
| helperCount += 3; // scheduleRetry + process + (implicit fail handler) | ||
| } |
There was a problem hiding this comment.
helperCount += 3 overcounts — only 2 functions are emitted by generateStatusHelper.
The comment reads // scheduleRetry + process + (implicit fail handler), but the "implicit fail handler" is not a separate exported function — it is the catch block inside process${pascal}. Only scheduleRetry${pascal} and process${pascal} are exported, so helperCount should be incremented by 2 here, not 3.
🛠️ Proposed fix
- helperCount += 3; // scheduleRetry + process + (implicit fail handler)
+ helperCount += 2; // scheduleRetry + process🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/generator/convex/scheduled-function-generator.ts` around lines 194 - 197,
The helperCount is overcounting exported helpers — when analysis.hasStatus is
true we call generateStatusHelper (which emits scheduleRetry{Pascal} and
process{Pascal} only), so change helperCount += 3 to helperCount += 2 and update
the inline comment to reflect only "scheduleRetry + process" (remove the
"(implicit fail handler)" part); locate this in the block that checks
analysis.hasStatus and calls generateStatusHelper.
Summary
This PR significantly enhances the SunSetter AQM+ landing page with a modern video-game aesthetic featuring Three.js 3D visualization, while adding four new code generators for advanced Convex features (crons, scheduled functions, auth, and component config).
Key Changes
Landing Page Redesign
New Convex Code Generators
Cron Job Generator (
cron-generator.ts): Analyzes table schemas to auto-generate cleanup cron jobs for:Scheduled Function Generator (
scheduled-function-generator.ts): Creates per-table scheduled helpers usingctx.schedulerfor:Auth Generator (
auth-generator.ts): Detects user tables with email/password columns and scaffolds:convex/auth.tswith Convex Auth Password provider setupconvex/auth.config.tswith site configurationComponent Config Generator (
component-config-generator.ts): Analyzes schema patterns to recommend and generate:Infrastructure Updates
@Heyoub/sunsetter-aqm→@heyoub/sunsetter-aqm(npm convention)ConvexFunctionGeneratorOptionstype with new generation flagsNotable Implementation Details
EffectComposerwithUnrealBloomPassfor cinematic neon glowhttps://claude.ai/code/session_01VZg64AntZqYqog4viJVu2d
Summary by CodeRabbit
New Features
Bug Fixes