-
Notifications
You must be signed in to change notification settings - Fork 192
feat: appwrite generate #1303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: appwrite generate #1303
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughThe PR removes the previous monolithic CLI Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes 🚥 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. 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/db.ts`:
- Around line 452-459: The printed success/log lines (success, console.log, log)
currently list appwrite.ts, appwrite.db.ts, appwrite.types.ts and an import of
"./${outputDir}/appwrite.js" which doesn't match what writeFiles actually
creates (appwrite/index.ts, appwrite/db.ts, appwrite/types.ts); update the
console output to show the real generated paths (e.g., path.join(outputDir,
"index.ts"), path.join(outputDir, "db.ts"), path.join(outputDir, "types.ts"))
and change the import example to reference the directory (e.g., import {
databases } from "./${outputDir}"; or "./${outputDir}/index" without a .js
extension) so displayed file names and import sample reflect the actual output.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
templates/cli/cli.ts.twigtemplates/cli/lib/commands/db.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
- GitHub Check: build (8.3, WebNode)
- GitHub Check: build (8.3, Python311)
- GitHub Check: build (8.3, Python39)
- GitHub Check: build (8.3, Python313)
- GitHub Check: build (8.3, Ruby27)
- GitHub Check: build (8.3, Python312)
- GitHub Check: build (8.3, Node18)
- GitHub Check: build (8.3, DotNet60)
- GitHub Check: build (8.3, KotlinJava8)
- GitHub Check: build (8.3, AppleSwift56)
- GitHub Check: build (8.3, CLIBun13)
- GitHub Check: build (8.3, Go118)
- GitHub Check: build (8.3, CLIBun11)
- GitHub Check: kotlin (server)
- GitHub Check: build (8.3, Android5Java17)
- GitHub Check: swift (server)
- GitHub Check: apple (client)
- GitHub Check: android (client)
🔇 Additional comments (2)
templates/cli/cli.ts.twig (1)
26-26: No issues found.Also applies to: 126-126
templates/cli/lib/commands/db.ts (1)
171-180: Quote or sanitize entity names used as property keys.Line 174 and Line 259 use
entity.nameas an unquoted key in generated types and runtime objects. If a table/collection name contains spaces, hyphens, or reserved words (which Appwrite names commonly allow), the generated TS/JS will be invalid and fail to compile. Prefer quoting string keys (and update usage docs to bracket access) or sanitize to a safe identifier consistently.🔧 Suggested fix (quote keys to guarantee valid output)
- const tableTypes = dbEntities - .map((entity) => { - const typeName = this.toPascalCase(entity.name); - return ` ${entity.name}: { + const tableTypes = dbEntities + .map((entity) => { + const typeName = this.toPascalCase(entity.name); + const entityKey = JSON.stringify(entity.name); + return ` ${entityKey}: { create: (data: Omit<${typeName}, keyof Models.Row>, options?: { rowId?: string; permissions?: string[] }) => Promise<${typeName}>; get: (id: string) => Promise<${typeName}>; update: (id: string, data: Partial<Omit<${typeName}, keyof Models.Row>>, options?: { permissions?: string[] }) => Promise<${typeName}>; delete: (id: string) => Promise<void>; list: (queries?: string[]) => Promise<{ total: number; rows: ${typeName}[] }>; }`; })- .map((entity) => { - const entityName = entity.name; - const typeName = this.toPascalCase(entity.name); + .map((entity) => { + const entityName = entity.name; + const typeName = this.toPascalCase(entity.name); + const entityKey = JSON.stringify(entityName); - return ` ${entityName}: { + return ` ${entityKey}: { create: (data: Omit<${typeName}, keyof Models.Row>, options?: { rowId?: string; permissions?: string[] }) => tablesDB.createRow<${typeName}>({ databaseId: '${dbId}', tableId: '${entity.$id}', rowId: options?.rowId ?? ID.unique(), data, permissions: options?.permissions, }),What characters are permitted in Appwrite collection/table names and IDs?Also applies to: 259-295, 320-322
⛔ Skipped due to learnings
Learnt from: ArnabChatterjee20k Repo: appwrite/sdk-generator PR: 1266 File: templates/apple/Sources/Channel.swift.twig:13-19 Timestamp: 2025-12-18T18:43:35.419Z Learning: In the appwrite/sdk-generator repository, helper classes like Channel and Query accept IDs without validation or escaping. These are lightweight helper utilities that construct strings directly from provided parameters without sanitizing or encoding them. This is an intentional design pattern across the SDK.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
127d23b to
4f578c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
templates/cli/lib/commands/db.ts (1)
306-314: Generated client lacks runtime validation for required environment variables.The generated code uses non-null assertions (
!) on environment variables (lines 310-312). If these aren't set, the SDK will silently passundefinedvalues, potentially causing cryptic errors downstream rather than clear configuration messages.🔧 Suggested improvement
Consider generating code that validates env vars at initialization:
const client = new Client() - .setEndpoint(process.env.APPWRITE_ENDPOINT!) - .setProject(process.env.APPWRITE_PROJECT_ID!) - .setKey(process.env.APPWRITE_API_KEY!); + .setEndpoint(process.env.APPWRITE_ENDPOINT ?? throwEnvError('APPWRITE_ENDPOINT')) + .setProject(process.env.APPWRITE_PROJECT_ID ?? throwEnvError('APPWRITE_PROJECT_ID')) + .setKey(process.env.APPWRITE_API_KEY ?? throwEnvError('APPWRITE_API_KEY')); + +function throwEnvError(name: string): never { + throw new Error(\`Missing required environment variable: \${name}\`); +}Alternatively, add a comment in the generated code instructing users to set these variables.
♻️ Duplicate comments (1)
templates/cli/lib/commands/db.ts (1)
449-456: File paths in success output don't match actual generated files.This issue was previously identified. The console output shows
appwrite.ts,appwrite.db.ts,appwrite.types.tsbutwriteFilescreatesappwrite/index.ts,appwrite/db.ts,appwrite/types.ts. The import example on line 455 also needs correction.🔧 Suggested fix
success(`Generated files:`); - console.log(` - ${path.join(outputDir, "appwrite.ts")}`); - console.log(` - ${path.join(outputDir, "appwrite.db.ts")}`); - console.log(` - ${path.join(outputDir, "appwrite.types.ts")}`); + console.log(` - ${path.join(outputDir, "appwrite", "index.ts")}`); + console.log(` - ${path.join(outputDir, "appwrite", "db.ts")}`); + console.log(` - ${path.join(outputDir, "appwrite", "types.ts")}`); console.log(""); log(`Import the generated SDK in your project:`); - console.log(` import { databases } from "./${outputDir}/appwrite.js";`); + console.log(` import { databases } from "./${path.join(outputDir, "appwrite")}";`);
🧹 Nitpick comments (2)
templates/cli/lib/commands/db.ts (2)
99-107: Consider improving type safety instead of usingas anycast.The
as anycast on line 102 bypasses TypeScript's type checking. Since both tables and collections share the same attribute structure for type resolution, consider defining a common interface or union type to maintain type safety.♻️ Suggested approach
Define a shared entity type that both tables and collections conform to:
// At the top of the file or in config.ts type Entity = NonNullable<ConfigType["tables"]>[number] | NonNullable<ConfigType["collections"]>[number]; type EntityList = NonNullable<ConfigType["tables"]> | NonNullable<ConfigType["collections"]>;Then update the method signature and remove the cast:
private generateTableType( - entity: NonNullable<ConfigType["tables"]>[number] | NonNullable<ConfigType["collections"]>[number], - entities: NonNullable<ConfigType["tables"]> | NonNullable<ConfigType["collections"]>, + entity: Entity, + entities: EntityList, ): string {This would require updating
getTypeto accept the unified type as well.
154-162: Consider extracting entity grouping logic to reduce duplication.The logic for grouping entities by
databaseId(lines 154-162) is duplicated ingenerateDbFile(lines 240-247). Extracting this into a reusable helper would improve maintainability.♻️ Suggested helper
private groupEntitiesByDatabase<T extends { databaseId: string }>( entities: T[] ): Map<string, T[]> { const entitiesByDb = new Map<string, T[]>(); for (const entity of entities) { const dbId = entity.databaseId; if (!entitiesByDb.has(dbId)) { entitiesByDb.set(dbId, []); } entitiesByDb.get(dbId)!.push(entity); } return entitiesByDb; }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
templates/cli/cli.ts.twigtemplates/cli/lib/commands/db.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- templates/cli/cli.ts.twig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
- GitHub Check: build (8.3, WebChromium)
- GitHub Check: build (8.3, Python311)
- GitHub Check: build (8.3, Python313)
- GitHub Check: build (8.3, AppleSwift56)
- GitHub Check: build (8.3, Python312)
- GitHub Check: build (8.3, Ruby30)
- GitHub Check: build (8.3, Go118)
- GitHub Check: build (8.3, KotlinJava11)
- GitHub Check: build (8.3, Swift56)
- GitHub Check: build (8.3, KotlinJava8)
- GitHub Check: build (8.3, FlutterStable)
- GitHub Check: build (8.3, Node16)
- GitHub Check: build (8.3, Android14Java17)
- GitHub Check: build (8.3, Android5Java17)
- GitHub Check: build (8.3, DartBeta)
- GitHub Check: android (client)
- GitHub Check: apple (client)
- GitHub Check: swift (server)
🔇 Additional comments (7)
templates/cli/lib/commands/db.ts (7)
1-12: LGTM!Imports and interface definition are appropriate for the code generation functionality.
15-71: LGTM!Type mapping logic correctly handles all attribute types including relationships, arrays, and nullable modifiers.
73-84: LGTM!String transformation helpers are correctly implemented.
109-138: LGTM!Enum generation logic is correct. The non-null assertion on line 127 is safe given the truthiness check on line 122.
337-367: LGTM!The public
generatemethod has proper validation for required configuration and clean separation between generation and file writing.
375-413: LGTM!File writing logic correctly creates the output structure. The synchronous fs methods are acceptable for a CLI tool where blocking is not a concern.
466-469: LGTM!Command definition is clean with appropriate description and default value for the output option.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
4f578c4 to
adab5ed
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
templates/cli/lib/commands/db.ts (1)
38-57: Type annotation and relationship lookup mismatch when processing tables.The
getTypemethod parameter is typed ascollections: NonNullable<ConfigType["collections"]>, but it's called withentities as anywhich can be either tables or collections. SinceColumnSchemaequalsAttributeSchema, tables support relationship attributes. When relationships exist across tables and collections (e.g., a table referencing a collection), the lookup at line 39 will fail to find the related entity and throw an error.Fix by:
- Updating the
getTypemethod signature to acceptentities: NonNullable<ConfigType["tables"]> | NonNullable<ConfigType["collections"]>instead ofcollections- Removing the
as anycast and letting TypeScript enforce type safety- Ensuring the relationship lookup searches the appropriate entity type
♻️ Duplicate comments (1)
templates/cli/lib/commands/db.ts (1)
450-456: Generated file paths in success output don't match actual output.The console output lists
appwrite.ts,appwrite.db.ts, andappwrite.types.ts, butwriteFilesactually createsappwrite/index.ts,appwrite/db.ts, andappwrite/types.ts. The import example also points to the wrong path.✅ Suggested fix
success(`Generated files:`); - console.log(` - ${path.join(outputDir, "appwrite.ts")}`); - console.log(` - ${path.join(outputDir, "appwrite.db.ts")}`); - console.log(` - ${path.join(outputDir, "appwrite.types.ts")}`); + console.log(` - ${path.join(outputDir, "appwrite", "index.ts")}`); + console.log(` - ${path.join(outputDir, "appwrite", "db.ts")}`); + console.log(` - ${path.join(outputDir, "appwrite", "types.ts")}`); console.log(""); log(`Import the generated SDK in your project:`); - console.log(` import { databases } from "./${outputDir}/appwrite.js";`); + console.log(` import { databases } from "./${path.join(outputDir, "appwrite")}";`);
🧹 Nitpick comments (1)
templates/cli/lib/commands/db.ts (1)
100-103: Type safety bypass withas anycast.Casting
entities as anydefeats TypeScript's type checking and could mask bugs if tables and collections have incompatible structures for thegetTypemethod.Consider refactoring
getTypeto accept the union type directly, or use a type predicate/guard to narrow properly:♻️ Suggested approach
- return ` ${attr.key}: ${this.getType(attr, entities as any)};`; + // Type assertion is safer when getType is updated to handle both entity types + return ` ${attr.key}: ${this.getType(attr, entities)};`;This requires updating
getType's signature to:private getType( attribute: z.infer<typeof AttributeSchema>, entities: NonNullable<ConfigType["tables"]> | NonNullable<ConfigType["collections"]>, ): string
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
templates/cli/cli.ts.twigtemplates/cli/lib/commands/db.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- templates/cli/cli.ts.twig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: build (8.3, Node16)
- GitHub Check: build (8.3, DartBeta)
- GitHub Check: build (8.3, Android14Java17)
- GitHub Check: build (8.3, CLIBun11)
- GitHub Check: build (8.3, DartStable)
- GitHub Check: apple (client)
- GitHub Check: android (client)
- GitHub Check: swift (server)
🔇 Additional comments (10)
templates/cli/lib/commands/db.ts (10)
1-12: LGTM!Imports and the
GenerateResultinterface are well-structured. The interface provides a clear contract for the generation output.
73-84: LGTM!String transformation helpers are correctly implemented for PascalCase and UPPER_SNAKE_CASE conversions.
109-138: LGTM!The enum generation logic correctly handles both tables and collections, with proper field iteration and enum value formatting.
140-204: LGTM!The types file generation is well-structured:
- Properly prioritizes tables over collections
- Groups entities by database ID for type organization
- Generates strongly-typed
DatabaseIdandDatabaseTablestypes- Method signatures in the generated types correctly reference entity types
206-229: LGTM!Dependency detection logic is reasonable with appropriate fallbacks for different package managers (npm/deno).
338-368: LGTM!The
generatemethod properly validates configuration, handles both tables and collections, and provides clear feedback when no entities are found.
376-398: LGTM!File writing logic is straightforward and correct. Uses
recursive: truefor safe directory creation and proper path joining.
403-414: LGTM!The main entry file generation is clean with appropriate documentation and ES module-compatible exports.
467-470: LGTM!The CLI command is properly configured with a clear description and sensible default output directory.
307-323: Verify that TablesDB supports row-level CRUD methods in the generated code.The generated code calls
tablesDB.createRow(),tablesDB.getRow(),tablesDB.updateRow(),tablesDB.deleteRow(), andtablesDB.listRows(). While TablesDB exists in the Appwrite SDK, these specific row-level methods should be confirmed against the target SDK version's API documentation to ensure the generated code will work as intended.Additionally, the non-null assertions on environment variables (lines 112-114) will throw a runtime error if
APPWRITE_ENDPOINT,APPWRITE_PROJECT_ID, orAPPWRITE_API_KEYare not set. Consider documenting this requirement or adding validation with more descriptive error messages.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 38-56: The "relationship" switch case contains lexical
declarations (relatedCollection, type) causing a noSwitchDeclarations lint
error; wrap the entire case body in a block scope to fix it. In the switch
handling the relationship attribute (where relatedCollection is computed from
collections and type is set via toPascalCase(relatedCollection.name) and
potentially suffixed with "[]"), enclose those statements in braces { ... } and
keep the throw, type assignments, and any break/return inside that block so
variables are block-scoped and the lint error is resolved.
- Around line 185-193: The generated object keys use entity.name unquoted which
can produce invalid TypeScript for names with spaces or special characters;
update the template where entity.name is inserted (the map that builds the API
shape using toPascalCase(entity.name), Models.Row, QueryBuilder) to emit the
entity key as a quoted string (e.g., wrap entity.name in single quotes) wherever
it appears (the create/get/update/delete/list block and any other occurrences
such as the later occurrence noted around the same template), matching how dbId
is already quoted so all generated keys are valid TS identifiers.
🧹 Nitpick comments (1)
templates/cli/lib/utils.ts (1)
395-405: Consider consolidating case helpers to avoid divergence.There are already
toPascalCaseandtoUpperSnakeCasehelpers intemplates/cli/lib/type-generation/languages/language.ts; a shared utility or re-export would prevent subtle casing mismatches across generators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
templates/cli/lib/commands/config.ts (1)
209-274: Add validation to enforce exactly one relationship target fieldThe schema allows both
relatedCollectionandrelatedTableto be optional without validation, creating ambiguous configurations. While some code paths use the??operator to handle either field, the type generation code (e.g.,typescript.tsline 36-41) only checksrelatedCollectionand throws if it's missing, making the schema inconsistent. Enforce exactly one of these fields whentype === "relationship".🔧 Suggested refinement
const AttributeSchema = AttributeSchemaBase.refine( (data) => { if (data.required === true && data.default !== null) { return false; } return true; }, { message: "When 'required' is true, 'default' must be null", path: ["default"], }, ).refine( (data) => { if ( data.type === "string" && (data.size === undefined || data.size === null) ) { return false; } return true; }, { message: "When 'type' is 'string', 'size' must be defined", path: ["size"], }, +).refine( + (data) => { + if (data.type !== "relationship") return true; + const hasCollection = !!data.relatedCollection; + const hasTable = !!data.relatedTable; + return (hasCollection ? 1 : 0) + (hasTable ? 1 : 0) === 1; + }, + { + message: + "For relationship attributes, set exactly one of 'relatedCollection' or 'relatedTable'", + path: ["relatedCollection"], + }, );
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 17-29: The getType method currently builds enum type names using
only attribute.key which causes global name collisions; update
getType(attribute, collections, entityName) to namespace enum names by
prefixing/suffixing with entityName (e.g.,
`${toPascalCase(entityName)}${toPascalCase(attribute.key)}`) wherever
attribute.format === "enum" and in returned string references; then update all
call sites (notably generateTableType) to pass entity.name into getType; finally
update generateEnums to declare enums using the same namespaced pattern so enum
declarations and usages match (use function/class names getType,
generateTableType, generateEnums and symbols attribute.key and entity.name to
locate places to change).
♻️ Duplicate comments (2)
templates/cli/lib/commands/generators/databases.ts (2)
38-59: Block-scope therelationshipcase to satisfynoSwitchDeclarationsBiome flags lexical declarations in switch cases; wrap this case in a block so variables are scoped correctly.
🧩 Proposed fix
- case "relationship": - // Handle both collections (relatedCollection) and tables (relatedTable) - const relatedId = attribute.relatedCollection ?? attribute.relatedTable; - const relatedEntity = collections.find( - (c) => c.$id === relatedId || c.name === relatedId, - ); - if (!relatedEntity) { - throw new Error( - `Related entity with ID '${relatedId}' not found.`, - ); - } - type = toPascalCase(relatedEntity.name); - if ( - (attribute.relationType === "oneToMany" && - attribute.side === "parent") || - (attribute.relationType === "manyToOne" && - attribute.side === "child") || - attribute.relationType === "manyToMany" - ) { - type = `${type}[]`; - } - break; + case "relationship": { + // Handle both collections (relatedCollection) and tables (relatedTable) + const relatedId = attribute.relatedCollection ?? attribute.relatedTable; + const relatedEntity = collections.find( + (c) => c.$id === relatedId || c.name === relatedId, + ); + if (!relatedEntity) { + throw new Error( + `Related entity with ID '${relatedId}' not found.`, + ); + } + type = toPascalCase(relatedEntity.name); + if ( + (attribute.relationType === "oneToMany" && + attribute.side === "parent") || + (attribute.relationType === "manyToOne" && + attribute.side === "child") || + attribute.relationType === "manyToMany" + ) { + type = `${type}[]`; + } + break; + }
183-195: Quote entity name keys in generated types/helpersUsing
entity.nameas a bare object key can emit invalid TypeScript when names contain spaces or hyphens. Quote the keys in both the type map and helpers.🧷 Proposed fix
- return ` ${entity.name}: { + return ` '${entity.name}': { create: (data: Omit<${typeName}, keyof Models.Row>, options?: { rowId?: string; permissions?: Permission[]; transactionId?: string }) => Promise<${typeName}>; get: (id: string) => Promise<${typeName}>; update: (id: string, data: Partial<Omit<${typeName}, keyof Models.Row>>, options?: { permissions?: Permission[]; transactionId?: string }) => Promise<${typeName}>; delete: (id: string, options?: { transactionId?: string }) => Promise<void>; list: (options?: { queries?: (q: QueryBuilder<${typeName}>) => string[] }) => Promise<{ total: number; rows: ${typeName}[] }>; }`;- return ` ${entityName}: { + return ` '${entityName}': { create: (data: Omit<${typeName}, keyof Models.Row>, options?: { rowId?: string; permissions?: Permission[]; transactionId?: string }) => tablesDB.createRow<${typeName}>({Also applies to: 267-313
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 85-88: The generated type properties are always required; update
the mapping over fields (where typeName and attributes are built, using
toPascalCase and this.getType) to append an optional operator when attr.required
is false or undefined—i.e., change the property key generation from
`${attr.key}: ...` to include `${attr.key}${attr.required ? '' : '?'}: ...` so
non-required attributes are marked optional in the generated types.
♻️ Duplicate comments (3)
templates/cli/lib/commands/generators/databases.ts (3)
38-59: Block‑scope therelationshipcase to satisfynoSwitchDeclarations.The
constdeclarations inside this switch case can leak across cases and currently trip Biome’s lint error. Wrap the case in braces.🧩 Proposed fix
- case "relationship": - // Handle both collections (relatedCollection) and tables (relatedTable) - const relatedId = attribute.relatedCollection ?? attribute.relatedTable; - const relatedEntity = collections.find( - (c) => c.$id === relatedId || c.name === relatedId, - ); - if (!relatedEntity) { - throw new Error( - `Related entity with ID '${relatedId}' not found.`, - ); - } - type = toPascalCase(relatedEntity.name); - if ( - (attribute.relationType === "oneToMany" && - attribute.side === "parent") || - (attribute.relationType === "manyToOne" && - attribute.side === "child") || - attribute.relationType === "manyToMany" - ) { - type = `${type}[]`; - } - break; + case "relationship": { + // Handle both collections (relatedCollection) and tables (relatedTable) + const relatedId = attribute.relatedCollection ?? attribute.relatedTable; + const relatedEntity = collections.find( + (c) => c.$id === relatedId || c.name === relatedId, + ); + if (!relatedEntity) { + throw new Error( + `Related entity with ID '${relatedId}' not found.`, + ); + } + type = toPascalCase(relatedEntity.name); + if ( + (attribute.relationType === "oneToMany" && + attribute.side === "parent") || + (attribute.relationType === "manyToOne" && + attribute.side === "child") || + attribute.relationType === "manyToMany" + ) { + type = `${type}[]`; + } + break; + }
17-29: Namespace enum type names to avoid cross‑entity collisions.Enums currently use only
field.key, so identical field names across entities collide and break TS compilation. Prefix with the entity name and updategetType/generateEnums(and call sites) consistently.🧩 Proposed fix
- private getType( - attribute: z.infer<typeof AttributeSchema>, - collections: NonNullable<ConfigType["collections"]>, - ): string { + private getType( + attribute: z.infer<typeof AttributeSchema>, + collections: NonNullable<ConfigType["collections"]>, + entityName: string, + ): string { @@ - if (attribute.format === "enum") { - type = toPascalCase(attribute.key); + if (attribute.format === "enum") { + type = `${toPascalCase(entityName)}${toPascalCase(attribute.key)}`; } break; @@ - const enumName = toPascalCase(field.key); + const enumName = `${toPascalCase(entity.name)}${toPascalCase(field.key)}`;- const attributes = fields - .map((attr) => ` ${attr.key}: ${this.getType(attr, entities as any)};`) + const attributes = fields + .map((attr) => ` ${attr.key}: ${this.getType(attr, entities as any, entity.name)};`) .join("\n");Also applies to: 93-112
193-214: Quote entity names when emitting object keys.
entity.namecan contain characters invalid for unquoted identifiers, yielding invalid TS in both the type map and runtime map. Quote these keys.🧩 Proposed fix
- return ` ${entity.name}: {\n${baseMethods}${bulkMethods}\n }`; + return ` '${entity.name}': {\n${baseMethods}${bulkMethods}\n }`; @@ - return ` ${entityName}: {\n${baseMethods}${bulkMethods}\n }`; + return ` '${entityName}': {\n${baseMethods}${bulkMethods}\n }`;Also applies to: 283-363
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 160-162: The code currently detects Deno via
fs.existsSync(path.resolve(cwd, "deno.json")) and returns the old import string
"https://deno.land/x/appwrite/mod.ts"; update that return to use the recommended
Deno npm specifier (for example "npm:node-appwrite") or an alternative JSR
specifier ("jsr:`@appwrite/node`") so generated templates use the supported import
style; locate the check using fs.existsSync(...) and replace the returned URL
string accordingly, ensuring the new specifier is the single exported import
string used by the generator.
♻️ Duplicate comments (5)
templates/cli/lib/commands/generators/databases.ts (5)
38-59: Wrap the relationship case in a block to satisfynoSwitchDeclarations.The
constdeclarations on lines 40-43 are flagged by Biome because lexical declarations in switch cases can be erroneously accessed by other clauses. Wrap the case body in braces.🧩 Proposed fix
- case "relationship": - // Handle both collections (relatedCollection) and tables (relatedTable) - const relatedId = attribute.relatedCollection ?? attribute.relatedTable; - const relatedEntity = collections.find( - (c) => c.$id === relatedId || c.name === relatedId, - ); + case "relationship": { + // Handle both collections (relatedCollection) and tables (relatedTable) + const relatedId = attribute.relatedCollection ?? attribute.relatedTable; + const relatedEntity = collections.find( + (c) => c.$id === relatedId || c.name === relatedId, + ); if (!relatedEntity) { throw new Error( `Related entity with ID '${relatedId}' not found.`, ); } type = toPascalCase(relatedEntity.name); if ( (attribute.relationType === "oneToMany" && attribute.side === "parent") || (attribute.relationType === "manyToOne" && attribute.side === "child") || attribute.relationType === "manyToMany" ) { type = `${type}[]`; } break; + }
27-29: Namespace enum names to prevent collisions across entities.When multiple entities have enum fields with the same key (e.g.,
status), the generated enum types will collide. ThegetTypemethod should accept the entity name and prefix the enum type accordingly.🧩 Proposed fix
Update method signature and enum reference:
private getType( attribute: z.infer<typeof AttributeSchema>, collections: NonNullable<ConfigType["collections"]>, + entityName: string, ): string { ... if (attribute.format === "enum") { - type = toPascalCase(attribute.key); + type = `${toPascalCase(entityName)}${toPascalCase(attribute.key)}`; }Then update all call sites to pass the entity name, and update
generateEnums(line 102) to use the same naming pattern.
85-88: Add optional operator (?) for non-required fields.All attributes are currently generated as required properties. When
attr.requiredisfalseorundefined, the field should be marked optional to allow proper type-checking on create operations.🧩 Proposed fix
const attributes = fields - .map((attr) => ` ${attr.key}: ${this.getType(attr, entities as any)};`) + .map((attr) => { + const optional = attr.required ? "" : "?"; + return ` ${attr.key}${optional}: ${this.getType(attr, entities as any)};`; + }) .join("\n");
226-226: Quote entity names when used as object keys.
entity.nameis used directly as an object key without quoting. Appwrite collection names can contain hyphens and other characters that would produce invalid TypeScript (e.g.,my-table: {...}is invalid).🧩 Proposed fix
- return ` ${entity.name}: {\n${baseMethods}${bulkMethods}\n }`; + return ` '${entity.name}': {\n${baseMethods}${bulkMethods}\n }`;
377-377: Quote entity names when used as object keys (same issue as line 226).
entityNameis used directly without quotes, which can produce invalid TypeScript for names with special characters.🧩 Proposed fix
- return ` ${entityName}: {\n${baseMethods}${bulkMethods}\n }`; + return ` '${entityName}': {\n${baseMethods}${bulkMethods}\n }`;
🧹 Nitpick comments (1)
templates/cli/lib/commands/generators/databases.ts (1)
155-157: Consider logging a warning when package.json parsing fails.The empty catch block silently swallows errors. While the fallback behavior is fine, a parse failure in
package.jsonmight indicate a real problem the user should know about.♻️ Suggested improvement
} catch { - // Fallback if package.json is invalid + // Fallback if package.json is invalid - log for debugging + console.warn("Warning: Could not parse package.json, using default Appwrite dependency"); }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 103-110: The enum member identifiers generated in the enum
creation block (see enumName, enumValues, and the toUpperSnakeCase usage) are
not sanitized for special characters or leading digits, which produces invalid
TypeScript identifiers for values like "test@2024" or "123test"; update the
generation to post-process the result of toUpperSnakeCase(element) by
removing/replace any non-alphanumeric characters with underscores, collapsing
consecutive underscores, trimming trailing/leading underscores, and if the first
character is a digit prefix the identifier with an underscore (or otherwise
ensure it begins with a letter/underscore) before emitting it; apply this
sanitized identifier when building enumValues so generated enums are valid
TypeScript.
♻️ Duplicate comments (1)
templates/cli/lib/commands/generators/databases.ts (1)
162-164: Update the Deno import specifier to the current recommended form.The generator still emits the legacy
deno.land/ximport. Appwrite’s docs now recommend the npm or JSR specifier for Deno; consider updating to avoid stale/unsupported imports.🔧 Proposed fix
- if (fs.existsSync(path.resolve(cwd, "deno.json"))) { - return "https://deno.land/x/appwrite/mod.ts"; - } + if (fs.existsSync(path.resolve(cwd, "deno.json"))) { + return "npm:node-appwrite"; + // or: "jsr:`@appwrite/node`" + }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 123-137: The enum generation can produce duplicate keys after
sanitizeEnumKey (e.g., "test@2024" and "test#2024"), so update the block that
builds enumValues for an enum (where enumName, field.elements and
sanitizeEnumKey are used) to detect collisions: maintain a Set (e.g., usedKeys)
while mapping field.elements, and when sanitizeEnumKey(element) yields an
already-used key, append a deterministic disambiguator (like _1, _2 or _{index})
to the key before adding it and the final `key = "original"` line; ensure you
add the final key to usedKeys and keep comma placement logic unchanged so the
generated enumTypes string compiles.
🧹 Nitpick comments (1)
templates/cli/lib/commands/generators/databases.ts (1)
504-525: Consider using async fs operations for consistency.The method is declared
asyncbut uses synchronous fs operations (existsSync,mkdirSync,writeFileSync). While this works fine for CLI tools, usingfs.promiseswould be more consistent with the async signature.♻️ Optional refactor
- async writeFiles(outputDir: string, result: GenerateResult): Promise<void> { - const appwriteDir = path.join(outputDir, "appwrite"); - if (!fs.existsSync(appwriteDir)) { - fs.mkdirSync(appwriteDir, { recursive: true }); - } - - fs.writeFileSync( - path.join(appwriteDir, "databases.ts"), - result.databasesContent, - "utf-8", - ); - fs.writeFileSync( - path.join(appwriteDir, "types.ts"), - result.typesContent, - "utf-8", - ); - fs.writeFileSync( - path.join(appwriteDir, "index.ts"), - result.indexContent, - "utf-8", - ); - } + async writeFiles(outputDir: string, result: GenerateResult): Promise<void> { + const appwriteDir = path.join(outputDir, "appwrite"); + await fs.promises.mkdir(appwriteDir, { recursive: true }); + + await Promise.all([ + fs.promises.writeFile(path.join(appwriteDir, "databases.ts"), result.databasesContent, "utf-8"), + fs.promises.writeFile(path.join(appwriteDir, "types.ts"), result.typesContent, "utf-8"), + fs.promises.writeFile(path.join(appwriteDir, "index.ts"), result.indexContent, "utf-8"), + ]); + }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 419-420: The generated import unconditionally includes TablesDB
which only exists in server SDKs; use the existing supportsBulkMethods() check
to conditionally emit imports and code: when supportsBulkMethods() is true
import TablesDB (and keep existing TablesDB-typed usage such as TablesDB in the
generated code), otherwise import Databases (or omit TablesDB references) and
adjust the generated code to use Databases-compatible APIs and types (e.g.,
replace TablesDB-specific calls with Databases equivalents or alternative
client-friendly implementations), or explicitly abort generation with a clear
message if you intend to support server SDKs only. Ensure the decision point
lives where the return string is produced so the import line and any
TablesDB-typed symbols are only emitted when supportsBulkMethods() is true.
🧹 Nitpick comments (4)
templates/cli/lib/commands/generators/databases.ts (4)
21-25: Consider renaming parameter for clarity.The parameter
collectionsis used to search for related entities, but it actually receives eithertablesorcollectionsbased on the configuration. Consider renaming toentitiesto match its actual usage.♻️ Suggested refactor
private getType( attribute: z.infer<typeof AttributeSchema>, - collections: NonNullable<ConfigType["collections"]>, + entities: Entities, entityName: string, ): string {And update references within the method accordingly.
428-473: Consider reducinganyusage in generated runtime helpers.The
createTableApifunction usesanyfor data parameters. While the public API throughDatabaseTablesis properly typed, theseanycasts in the implementation could mask runtime type mismatches during development.
542-568: Async method performs only synchronous work.The
generatemethod is markedasyncbut contains noawaitexpressions and performs no asynchronous operations. Consider making it synchronous for clarity.♻️ Suggested refactor
- async generate(config: ConfigType): Promise<GenerateResult> { + generate(config: ConfigType): GenerateResult {
570-591: Use async file system methods or make method synchronous.The
writeFilesmethod is markedasyncbut uses synchronousfsmethods (existsSync,mkdirSync,writeFileSync). This inconsistency could be confusing. Either usefs.promisesmethods withawait, or make the method synchronous.♻️ Option 1: Make synchronous
- async writeFiles(outputDir: string, result: GenerateResult): Promise<void> { + writeFiles(outputDir: string, result: GenerateResult): void {♻️ Option 2: Use async fs methods
+ import { promises as fsPromises } from "fs"; ... async writeFiles(outputDir: string, result: GenerateResult): Promise<void> { const appwriteDir = path.join(outputDir, "appwrite"); - if (!fs.existsSync(appwriteDir)) { - fs.mkdirSync(appwriteDir, { recursive: true }); - } + await fsPromises.mkdir(appwriteDir, { recursive: true }); - fs.writeFileSync( + await fsPromises.writeFile( path.join(appwriteDir, "databases.ts"), result.databasesContent, "utf-8", ); // ... similar for other files }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 345-356: The generated tableIdMap currently builds plain object
literals which are vulnerable to prototype key collisions; update
generateTableIdMap to emit null-prototype objects and safe property checks by
creating the outer map and each DB map with Object.create(null) (e.g. emit
"const tableIdMap: Record<string, Record<string, string>> =
Object.create(null);" and for each DB emit "tableIdMap['dbId'] =
Object.create(null);" and assign properties into those objects), and ensure any
runtime lookup code uses Object.prototype.hasOwnProperty.call(tableIdMap[dbId],
key) or equivalent to check keys safely; make the same change for the other
similar generator block referenced (the block around the generate function at
the later lines).
- Around line 123-140: The enum emission currently injects raw field elements
into string literals (in the block that builds enumValues inside the loop over
fields, using sanitizeEnumKey, usedKeys and enumName), which breaks when values
contain quotes or backslashes; change the emitted RHS to use a properly escaped
string by replacing the inline "${element}" with JSON.stringify(element) (and
remove the manual surrounding quotes), so each enum member line becomes e.g. `
${key} = ${JSON.stringify(element)}${isLast ? "" : ","}` ensuring values are
safely escaped.
- Around line 101-110: The generated TypeScript type in generateTableType uses
raw attribute keys which fails for keys with hyphens/special chars; update the
mapping that builds attributes (in generateTableType) to quote the attribute
name (e.g., use JSON.stringify(attr.key) or `["..."]`-style string literal) when
emitting `${attr.key}${attr.required ? "" : "?"}: ...` so the key becomes a
valid quoted identifier while preserving the optional marker and using
getType(entity) for the value; check getFields/getType usage remains unchanged
and ensure the output still produces valid TS like "my-key"?: string or
["my-key"]?: string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 167-187: The SDK detection only inspects packageJson.dependencies
(variable packageJson and deps) so
devDependencies/peerDependencies/optionalDependencies are ignored; update the
logic in the function in templates/cli/lib/commands/generators/databases.ts to
aggregate all dependency sections (dependencies, devDependencies,
peerDependencies, optionalDependencies) into deps (or check each section when
looking for "@appwrite.io/console", "react-native-appwrite", "appwrite",
"node-appwrite") before returning the detected SDK so packages installed as
dev/peer/optional are correctly detected.
- Around line 271-275: In generateDatabaseTablesType and
generateTablesWithRelationships, escape any embedded entity/db names by emitting
proper JS/TS string literals using JSON.stringify instead of raw
`'${entity.name}'` or `'${dbId}'`; locate the string templates that produce
lines like ` '${entity.name}': {...}` and ` '${dbId}': {...}` and replace
the interpolation with JSON.stringify(entity.name) and JSON.stringify(dbId) (or
equivalent variable) so quotes and backslashes are escaped when generating the
TypeScript output.
- Around line 106-107: Generated type/enum names currently use only entity.name
(via typeName = toPascalCase(entity.name)), causing collisions across databases;
update generation to ensure global uniqueness by either (A) prefixing/suffixing
the derived name with a database identifier (e.g., use toPascalCase(database.id)
+ toPascalCase(entity.name) when computing typeName and any enum names) or (B)
add a validation step before codegen that scans all entities across databases
for duplicate entity.name and throws a clear configuration error; implement one
of these fixes and update all places that reference typeName/enum generation to
use the new unique naming scheme or to short-circuit on validation failure.
♻️ Duplicate comments (3)
templates/cli/lib/commands/generators/databases.ts (3)
106-111: Quote attribute keys in generated types to keep TS valid.Unquoted keys (e.g.,
my-key) are invalid in TypeScript type literals. Please emit quoted keys.🔧 Suggested fix
- const attributes = fields - .map( - (attr) => - ` ${attr.key}${attr.required ? "" : "?"}: ${this.getType(attr, entities as any, entity.name)};`, - ) - .join("\n"); + const attributes = fields + .map((attr) => { + const optional = attr.required ? "" : "?"; + const key = JSON.stringify(attr.key); + return ` ${key}${optional}: ${this.getType(attr, entities as any, entity.name)};`; + }) + .join("\n");
124-141: Escape enum values before emitting string literals.Raw enum values containing quotes/backslashes will generate invalid TypeScript.
🔧 Suggested fix
- return ` ${key} = "${element}"${isLast ? "" : ","}`; + return ` ${key} = ${JSON.stringify(element)}${isLast ? "" : ","}`;
346-357: Harden tableIdMap/proxy against prototype keys.Plain object literals plus
inchecks can be poisoned by keys like__proto__. Use null‑prototype maps andhasOwnPropertychecks.Also applies to: 488-510
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generators/databases.ts`:
- Around line 298-299: The dbIdType union is built from raw db IDs and can
produce invalid TypeScript when IDs contain quotes or escape sequences; update
the construction used with entitiesByDb so that dbIds.map(...) uses a proper JS
string literal encoder (e.g., JSON.stringify) instead of `'${id}'` so each id
becomes a safely escaped string literal for the TypeScript union (locate dbIds,
dbIdType and entitiesByDb to apply the change).
♻️ Duplicate comments (4)
templates/cli/lib/commands/generators/databases.ts (4)
106-119: Verify global uniqueness of entity names across databases.Type names are derived from
entity.namealone (line 110). If two databases contain entities with the same name (e.g., both have a "users" table), the generated types will collide, causing TypeScript compilation errors. Consider either:
- Prefixing type names with database ID:
toPascalCase(entity.databaseId) + toPascalCase(entity.name)- Adding configuration validation to enforce entity name uniqueness across all databases
Run the following script to check if configuration validation exists:
#!/bin/bash # Search for entity name uniqueness validation in config schema rg -n "superRefine|refine|unique.*name|name.*unique" templates/cli/lib/commands/config.ts -A 5
168-202: Include dev/peer/optional dependencies in SDK detection.The function only inspects
packageJson.dependencies(line 178), so SDKs installed asdevDependencies,peerDependencies, oroptionalDependencieswon't be detected, causing incorrect fallback to"appwrite".🔧 Proposed fix
const packageJson = JSON.parse(packageJsonRaw); - const deps = packageJson.dependencies ?? {}; + const deps = { + ...(packageJson.dependencies ?? {}), + ...(packageJson.devDependencies ?? {}), + ...(packageJson.peerDependencies ?? {}), + ...(packageJson.optionalDependencies ?? {}), + };
275-279: Escape entity names and database IDs in generated type literals.Entity names and database IDs are interpolated directly into string literals without escaping. If these contain quotes or backslashes, the generated TypeScript will be invalid.
🔧 Proposed fix
- return ` '${entity.name}': {\n${baseMethods}${bulkMethods}\n }`; + return ` ${JSON.stringify(entity.name)}: {\n${baseMethods}${bulkMethods}\n }`; }) .join(";\n"); - return ` '${dbId}': {\n${tableTypes}\n }`; + return ` ${JSON.stringify(dbId)}: {\n${tableTypes}\n }`;
367-385: Escape values in tablesWithRelationships Set initialization.The composite key
'${dbId}:${entity.name}'is not escaped, which could produce invalid JavaScript if either value contains quotes.🔧 Proposed fix
if (this.hasRelationshipColumns(entity)) { - tablesWithRelationships.push(`'${dbId}:${entity.name}'`); + tablesWithRelationships.push(JSON.stringify(`${dbId}:${entity.name}`)); }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@templates/cli/lib/commands/generate.ts`:
- Around line 41-53: Normalize the incoming options.language value (trim and
toLowerCase) before validating and using it: create a normalized variable from
options.language, call LanguageDetector.isSupported(normalized), use
getSupportedLanguages() for the error message, pass the normalized value to
createGenerator(normalized as SupportedLanguage), and assign detectedLanguage =
normalized; ensure original options.language is not used directly for validation
or generator creation and update log to show the normalized language.
In `@templates/cli/lib/commands/generators/base.ts`:
- Around line 68-83: In writeFiles, guard against path traversal by resolving
and validating each target path before creating directories or writing files:
for each relativePath from result.files, compute the resolvedFilePath (using
path.resolve) against the sdkDir (not outputDir directly), ensure the resolved
path is inside sdkDir (e.g., path.relative(sdkDir, resolvedFilePath) does not
start with '..' and resolvedFilePath is not equal to sdkDir's parent), and throw
or skip with an error if the check fails; only then create fileDir and call
fs.writeFileSync. Reference: function writeFiles, variables relativePath,
sdkDir, filePath (resolvedFilePath) and result.files.
♻️ Duplicate comments (5)
templates/cli/lib/commands/generators/typescript/databases.ts (5)
108-121: Type names can collide across databases.
typeNameis derived only fromentity.name, so identically named entities in different databases will produce duplicate type/enum names and break compilation. Consider prefixing withdatabaseIdor validating uniqueness upfront.
170-193: Include dev/peer/optional dependencies in SDK detection.SDKs installed as dev/peer/optional dependencies won’t be detected, leading to incorrect imports.
🛠️ Proposed fix
- const deps = packageJson.dependencies ?? {}; + const deps = { + ...(packageJson.dependencies ?? {}), + ...(packageJson.devDependencies ?? {}), + ...(packageJson.peerDependencies ?? {}), + ...(packageJson.optionalDependencies ?? {}), + };
257-281: Escape database and entity names in generated type keys.Raw
'${entity.name}'and'${dbId}'will break when names contain quotes or backslashes.🛠️ Proposed fix
- return ` '${entity.name}': {\n${baseMethods}${bulkMethods}\n }`; + return ` ${JSON.stringify(entity.name)}: {\n${baseMethods}${bulkMethods}\n }`; }) .join(";\n"); - return ` '${dbId}': {\n${tableTypes}\n }`; + return ` ${JSON.stringify(dbId)}: {\n${tableTypes}\n }`;
299-302: Escape database IDs in the literal union.Unescaped IDs can produce invalid TypeScript if they contain quotes or backslashes.
🛠️ Proposed fix
- const dbIdType = dbIds.map((id) => `'${id}'`).join(" | "); + const dbIdType = dbIds.map((id) => JSON.stringify(id)).join(" | ");
369-386: Escape relationship table identifiers.Raw
'${dbId}:${entity.name}'can emit invalid string literals.🛠️ Proposed fix
- tablesWithRelationships.push(`'${dbId}:${entity.name}'`); + tablesWithRelationships.push(JSON.stringify(`${dbId}:${entity.name}`));
🧹 Nitpick comments (1)
templates/cli/lib/commands/generators/language-detector.ts (1)
31-43: Consider lowering confidence when onlypackage.jsonis present.Using
package.jsonas a primary indicator will mark many JS-only projects as TypeScript with high confidence. Consider moving it to secondary indicators or requiringtsconfig.jsonfor high confidence.🛠️ Proposed tweak
- primaryIndicators: ["tsconfig.json", "package.json", "deno.json"], + primaryIndicators: ["tsconfig.json", "deno.json"], secondaryIndicators: [ + "package.json", ".nvmrc", "package-lock.json", "yarn.lock",
43a4f36 to
5a9613a
Compare
5a9613a to
2cf10eb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
templates/cli/lib/commands/config.ts (1)
268-307: ColumnSchema is missing the same required/default & string size validations.Tables now skip the validations enforced for collection attributes, which can allow invalid configs to pass (e.g., required + non-null default, or string without size). Suggest mirroring the AttributeSchema refinements.
✅ Suggested fix
const ColumnSchema = z .object({ key: z.string(), type: z.enum([ "string", @@ encrypt: z.boolean().optional(), }) - .strict(); + .strict() + .refine(validateRequiredDefault, { + message: "When 'required' is true, 'default' must be null", + path: ["default"], + }) + .refine(validateStringSize, { + message: "When 'type' is 'string', 'size' must be defined", + path: ["size"], + });templates/cli/lib/config.ts (1)
46-69: Fix property name for Zod v4.3.5 compatibility ingetSchemaKeys.The project uses Zod v4.3.5, which stores internal schema properties on
_def(notdef). The current check on line 48 fails to unwrap ZodEffects, causinggetSchemaKeys()to return empty sets forTableSchema,CollectionSchema, andTopicSchema—all of which use.superRefine(). This results in emptyKeysTable,KeysCollection, andKeysTopicssets, stripping all fields from these resources during validation.Fix
function getSchemaKeys(schema: z.ZodTypeAny): Set<string> { // For ZodEffects (created by .refine(), .superRefine(), .transform()) - if ("def" in schema && "schema" in (schema.def as any)) { - return getSchemaKeys((schema.def as any).schema); - } + const def: any = (schema as any)._def; + if (def && "schema" in def) { + return getSchemaKeys(def.schema); + } // For ZodObject if ("shape" in schema) { return new Set(Object.keys((schema as any).shape)); } return new Set(); }
🧹 Nitpick comments (1)
templates/cli/lib/commands/config-validations.ts (1)
54-98: Consider validating both attributes and columns when both are present.Right now the function selects either
attributesorcolumnsand ignores the other. If mixed data ever appears (e.g., merged configs), duplicates in the second array won’t be caught. Optional, but safer.♻️ Suggested adjustment
export const validateContainerDuplicates = ( data: CollectionOrTableData, ctx: z.RefinementCtx, ) => { - const items = data.attributes || data.columns || []; - const itemType = data.attributes ? "Attribute" : "Column"; - const itemPath = data.attributes ? "attributes" : "columns"; - - // Validate duplicate item keys - if (items.length > 0) { - const seenKeys = new Set<string>(); - - items.forEach((item, index) => { - if (seenKeys.has(item.key)) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: `${itemType} with the key '${item.key}' already exists. ${itemType} keys must be unique, try again with a different key.`, - path: [itemPath, index, "key"], - }); - } else { - seenKeys.add(item.key); - } - }); - } + const validateItems = ( + items: AttributeOrColumn[] | undefined, + itemType: "Attribute" | "Column", + itemPath: "attributes" | "columns", + ) => { + if (!items || items.length === 0) return; + const seenKeys = new Set<string>(); + items.forEach((item, index) => { + if (seenKeys.has(item.key)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `${itemType} with the key '${item.key}' already exists. ${itemType} keys must be unique, try again with a different key.`, + path: [itemPath, index, "key"], + }); + } else { + seenKeys.add(item.key); + } + }); + }; + + validateItems(data.attributes, "Attribute", "attributes"); + validateItems(data.columns, "Column", "columns");
Summary by CodeRabbit
New Features
generateCLI command to produce type-safe SDKs from project schemas-l/--language)-o/--output)Refactor
Bug Fixes / Validation
✏️ Tip: You can customize this high-level summary in your review settings.