@rebasepro/sdk-generator 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/src/collections/default-collections.d.ts +12 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/util/permissions.d.ts +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +3 -0
- package/dist/index.es.js.map +1 -1
- package/dist/types/src/controllers/auth.d.ts +2 -24
- package/dist/types/src/controllers/client.d.ts +0 -3
- package/dist/types/src/controllers/collection_registry.d.ts +1 -1
- package/dist/types/src/controllers/data_driver.d.ts +18 -0
- package/dist/types/src/controllers/registry.d.ts +5 -4
- package/dist/types/src/rebase_context.d.ts +1 -1
- package/dist/types/src/types/auth_adapter.d.ts +2 -4
- package/dist/types/src/types/collections.d.ts +0 -4
- package/dist/types/src/types/component_ref.d.ts +1 -1
- package/dist/types/src/types/cron.d.ts +1 -1
- package/dist/types/src/types/entity_views.d.ts +1 -0
- package/dist/types/src/types/export_import.d.ts +1 -1
- package/dist/types/src/types/formex.d.ts +2 -2
- package/dist/types/src/types/properties.d.ts +2 -2
- package/dist/types/src/types/translations.d.ts +28 -12
- package/dist/types/src/types/user_management_delegate.d.ts +6 -4
- package/dist/types/src/users/roles.d.ts +0 -8
- package/package.json +6 -6
- package/src/generate-types.ts +5 -5
- package/src/utils.ts +3 -0
- package/test/sdk-generator.test.ts +4 -0
- package/src/json-logic-js.d.ts +0 -8
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PostgresCollection } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Default users collection definition.
|
|
4
|
+
*
|
|
5
|
+
* Shared between the admin UI (for navigation/display) and the backend
|
|
6
|
+
* (for schema generation). Both consumers prepend this to the developer's
|
|
7
|
+
* collections array and rely on generic slug-based deduplication
|
|
8
|
+
* (Map keyed by slug, last-write-wins) so that developer-defined
|
|
9
|
+
* collections with the same slug override this default — no hardcoded
|
|
10
|
+
* string checks required.
|
|
11
|
+
*/
|
|
12
|
+
export declare const defaultUsersCollection: PostgresCollection;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthController, Entity, EntityCollection, User } from "@rebasepro/types";
|
|
2
|
+
export declare function checkOperation<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, entity: Entity<M> | null, targetOperation: "select" | "insert" | "update" | "delete"): boolean;
|
|
2
3
|
export declare function canReadCollection<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>): boolean;
|
|
3
4
|
export declare function canEditEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, path: string, entity: Entity<M> | null): boolean;
|
|
4
5
|
export declare function canCreateEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authController: AuthController<USER>, path: string, entity: Entity<M> | null): boolean;
|
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
return str.split(/[_\-\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
7
7
|
}
|
|
8
8
|
function toCamelCase(str) {
|
|
9
|
+
if (!/[_\-\s]/.test(str)) {
|
|
10
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
11
|
+
}
|
|
9
12
|
const pascal = toPascalCase(str);
|
|
10
13
|
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
11
14
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/utils.ts","../src/generate-types.ts","../src/index.ts"],"sourcesContent":["/**\n * Utility functions for the SDK generator\n */\n\n/**\n * Convert a slug/snake_case string to PascalCase\n * e.g. \"private_notes\" → \"PrivateNotes\"\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/**\n * Convert a slug/snake_case string to camelCase\n * e.g. \"private_notes\" → \"privateNotes\"\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert a slug to a safe JS identifier\n * e.g. \"private-notes\" → \"privateNotes\"\n */\nexport function toSafeIdentifier(str: string): string {\n return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n\n/**\n * Indent a block of text by a given number of spaces\n */\nexport function indent(text: string, spaces: number): string {\n const pad = \" \".repeat(spaces);\n return text\n .split(\"\\n\")\n .map(line => (line.trim() ? pad + line : line))\n .join(\"\\n\");\n}\n","import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, RelationProperty, StringProperty, NumberProperty } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { toPascalCase, toSafeIdentifier } from \"./utils\";\n\nfunction propertyToTypeScriptType(prop: Property): string {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n const ids = Array.isArray(sp.enum)\n ? sp.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(sp.enum);\n return ids.map(v => `\"${v}\"`).join(\" | \");\n }\n return \"string\";\n }\n case \"number\": {\n const np = prop as NumberProperty;\n if (np.enum) {\n const ids = Array.isArray(np.enum)\n ? np.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(np.enum);\n return ids.join(\" | \");\n }\n return \"number\";\n }\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"string\"; // ISO 8601 string over the wire\n case \"geopoint\":\n return \"{ latitude: number; longitude: number; }\";\n case \"reference\":\n return \"string | number\";\n case \"relation\":\n return \"string | number\";\n case \"map\": {\n const mapProp = prop as MapProperty;\n if (mapProp.properties) {\n const inner = Object.entries(mapProp.properties)\n .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)\n .join(\" \");\n return `{ ${inner} }`;\n }\n return \"Record<string, any>\";\n }\n case \"array\": {\n const arrProp = prop as ArrayProperty;\n if (arrProp.of) {\n return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;\n }\n return \"Array<any>\";\n }\n case \"vector\":\n return \"number[]\";\n case \"binary\":\n return \"string\";\n default:\n return \"any\";\n }\n}\n\nexport function generateTypedefs(collections: EntityCollection[]): string {\n const lines: string[] = [\n \"/**\",\n \" * This file was auto-generated by Rebase.\",\n \" * Do not make direct changes to the file.\",\n \" */\",\n \"\",\n \"export interface Database {\"\n ];\n\n for (const collection of collections) {\n const typeName = toPascalCase(collection.slug);\n const properties = (collection.properties ?? {}) as Properties;\n\n // Resolve relations\n let resolvedRelations: Record<string, any> = {};\n try {\n resolvedRelations = resolveCollectionRelations(collection);\n } catch { /* ignore */ }\n\n lines.push(` ${toSafeIdentifier(collection.slug)}: {`);\n\n // ── Row Type ──\n lines.push(\" Row: {\");\n const emittedKeys = new Set<string>();\n\n // 1. Direct properties\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n lines.push(` ${toSafeIdentifier(key)}${isRequired ? \"\" : \"?\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n // 2. FK columns from relations\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n\n let fkType = \"string | number\";\n try {\n let target = relation.target();\n if (target && (target.default || target.__esModule)) {\n target = target.default || target;\n }\n if (target && target.properties) {\n const idProp = Object.entries(target.properties).find(([_, p]: [string, any]) => p.isId);\n if (idProp) {\n fkType = (idProp[1] as Property).type === \"number\" ? \"number\" : \"string\";\n }\n }\n } catch { /* ignore */ }\n\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n\n // 3. Relation fields\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") {\n if (emittedKeys.has(key)) continue;\n const relation = resolvedRelations[key];\n const isArray = relation?.cardinality === \"many\";\n const relType = \"{ id: string | number; path: string; __type: \\\"relation\\\"; data?: any }\";\n const tsType = isArray ? `Array<${relType}>` : relType;\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n }\n lines.push(\" };\");\n\n // ── Insert Type ──\n lines.push(\" Insert: {\");\n emittedKeys.clear();\n\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n const typedProp = prop as StringProperty | NumberProperty;\n const isAutoId = \"isId\" in prop && typedProp.isId && typedProp.isId !== \"manual\" && typedProp.isId !== true;\n const isOptional = !isRequired || isAutoId;\n lines.push(` ${toSafeIdentifier(key)}${isOptional ? \"?\" : \"\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n const fkType = \"string | number\";\n // simple fallback\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n // ── Update Type ──\n lines.push(\" Update: {\");\n emittedKeys.clear();\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n lines.push(\" };\");\n }\n\n lines.push(\"}\");\n lines.push(\"\");\n lines.push(\"export type CollectionName = keyof Database;\");\n lines.push(\"export type CollectionsDictionary = { [K in CollectionName]: K };\");\n lines.push(\"\");\n lines.push(\"export const collectionsDictionary = {\");\n for (const collection of collections) {\n lines.push(` ${toSafeIdentifier(collection.slug)}: \"${collection.slug}\",`);\n }\n lines.push(\"} as const;\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * @rebasepro/sdk-generator\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { EntityCollection } from \"@rebasepro/types\";\nimport { generateTypedefs } from \"./generate-types\";\n\nexport { generateTypedefs } from \"./generate-types\";\nexport { toPascalCase, toCamelCase, toSafeIdentifier, indent } from \"./utils\";\n\n// ─── Public API ────────────────────────────────────────────────────\n\nexport interface GeneratedFile {\n /** Relative file path within the output directory */\n path: string;\n /** File content */\n content: string;\n}\n\nexport interface GenerateSDKOptions {\n /** Whether to include a README file (default: true) */\n includeReadme?: boolean;\n}\n\nexport function generateSDK(\n collections: EntityCollection[],\n options: GenerateSDKOptions = {}\n): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n files.push({\n path: \"database.types.ts\",\n content: generateTypedefs(collections)\n });\n\n if (options.includeReadme !== false) {\n files.push({\n path: \"README.md\",\n content: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import type { Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // token: '...', // User token if not using auth module\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n const { data: posts } = await rebase.data.collection('posts').find();\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"names":["resolveCollectionRelations"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/utils.ts","../src/generate-types.ts","../src/index.ts"],"sourcesContent":["/**\n * Utility functions for the SDK generator\n */\n\n/**\n * Convert a slug/snake_case string to PascalCase\n * e.g. \"private_notes\" → \"PrivateNotes\"\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/**\n * Convert a slug/snake_case string to camelCase\n * e.g. \"private_notes\" → \"privateNotes\"\n */\nexport function toCamelCase(str: string): string {\n if (!/[_\\-\\s]/.test(str)) {\n return str.charAt(0).toLowerCase() + str.slice(1);\n }\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert a slug to a safe JS identifier\n * e.g. \"private-notes\" → \"privateNotes\"\n */\nexport function toSafeIdentifier(str: string): string {\n return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n\n/**\n * Indent a block of text by a given number of spaces\n */\nexport function indent(text: string, spaces: number): string {\n const pad = \" \".repeat(spaces);\n return text\n .split(\"\\n\")\n .map(line => (line.trim() ? pad + line : line))\n .join(\"\\n\");\n}\n","import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, Relation, RelationProperty, StringProperty, NumberProperty } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { toPascalCase, toSafeIdentifier } from \"./utils\";\n\nfunction propertyToTypeScriptType(prop: Property): string {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n const ids = Array.isArray(sp.enum)\n ? sp.enum.map((e: string | number | { id: string | number }) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(sp.enum);\n return ids.map(v => `\"${v}\"`).join(\" | \");\n }\n return \"string\";\n }\n case \"number\": {\n const np = prop as NumberProperty;\n if (np.enum) {\n const ids = Array.isArray(np.enum)\n ? np.enum.map((e: string | number | { id: string | number }) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(np.enum);\n return ids.join(\" | \");\n }\n return \"number\";\n }\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"string\"; // ISO 8601 string over the wire\n case \"geopoint\":\n return \"{ latitude: number; longitude: number; }\";\n case \"reference\":\n return \"string | number\";\n case \"relation\":\n return \"string | number\";\n case \"map\": {\n const mapProp = prop as MapProperty;\n if (mapProp.properties) {\n const inner = Object.entries(mapProp.properties)\n .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)\n .join(\" \");\n return `{ ${inner} }`;\n }\n return \"Record<string, any>\";\n }\n case \"array\": {\n const arrProp = prop as ArrayProperty;\n if (arrProp.of) {\n return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;\n }\n return \"Array<any>\";\n }\n case \"vector\":\n return \"number[]\";\n case \"binary\":\n return \"string\";\n default:\n return \"any\";\n }\n}\n\nexport function generateTypedefs(collections: EntityCollection[]): string {\n const lines: string[] = [\n \"/**\",\n \" * This file was auto-generated by Rebase.\",\n \" * Do not make direct changes to the file.\",\n \" */\",\n \"\",\n \"export interface Database {\"\n ];\n\n for (const collection of collections) {\n const typeName = toPascalCase(collection.slug);\n const properties = (collection.properties ?? {}) as Properties;\n\n // Resolve relations\n let resolvedRelations: Record<string, Relation> = {};\n try {\n resolvedRelations = resolveCollectionRelations(collection);\n } catch { /* ignore */ }\n\n lines.push(` ${toSafeIdentifier(collection.slug)}: {`);\n\n // ── Row Type ──\n lines.push(\" Row: {\");\n const emittedKeys = new Set<string>();\n\n // 1. Direct properties\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n lines.push(` ${toSafeIdentifier(key)}${isRequired ? \"\" : \"?\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n // 2. FK columns from relations\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n\n let fkType = \"string | number\";\n try {\n let target = relation.target();\n if (target && (target.default || target.__esModule)) {\n target = target.default || target;\n }\n if (target && target.properties) {\n const idProp = Object.entries(target.properties).find(([_, p]) => (p as Record<string, unknown>).isId);\n if (idProp) {\n fkType = (idProp[1] as Property).type === \"number\" ? \"number\" : \"string\";\n }\n }\n } catch { /* ignore */ }\n\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n\n // 3. Relation fields\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") {\n if (emittedKeys.has(key)) continue;\n const relation = resolvedRelations[key];\n const isArray = relation?.cardinality === \"many\";\n const relType = \"{ id: string | number; path: string; __type: \\\"relation\\\"; data?: any }\";\n const tsType = isArray ? `Array<${relType}>` : relType;\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n }\n lines.push(\" };\");\n\n // ── Insert Type ──\n lines.push(\" Insert: {\");\n emittedKeys.clear();\n\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n const typedProp = prop as StringProperty | NumberProperty;\n const isAutoId = \"isId\" in prop && typedProp.isId && typedProp.isId !== \"manual\" && typedProp.isId !== true;\n const isOptional = !isRequired || isAutoId;\n lines.push(` ${toSafeIdentifier(key)}${isOptional ? \"?\" : \"\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n const fkType = \"string | number\";\n // simple fallback\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n // ── Update Type ──\n lines.push(\" Update: {\");\n emittedKeys.clear();\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n lines.push(\" };\");\n }\n\n lines.push(\"}\");\n lines.push(\"\");\n lines.push(\"export type CollectionName = keyof Database;\");\n lines.push(\"export type CollectionsDictionary = { [K in CollectionName]: K };\");\n lines.push(\"\");\n lines.push(\"export const collectionsDictionary = {\");\n for (const collection of collections) {\n lines.push(` ${toSafeIdentifier(collection.slug)}: \"${collection.slug}\",`);\n }\n lines.push(\"} as const;\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * @rebasepro/sdk-generator\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { EntityCollection } from \"@rebasepro/types\";\nimport { generateTypedefs } from \"./generate-types\";\n\nexport { generateTypedefs } from \"./generate-types\";\nexport { toPascalCase, toCamelCase, toSafeIdentifier, indent } from \"./utils\";\n\n// ─── Public API ────────────────────────────────────────────────────\n\nexport interface GeneratedFile {\n /** Relative file path within the output directory */\n path: string;\n /** File content */\n content: string;\n}\n\nexport interface GenerateSDKOptions {\n /** Whether to include a README file (default: true) */\n includeReadme?: boolean;\n}\n\nexport function generateSDK(\n collections: EntityCollection[],\n options: GenerateSDKOptions = {}\n): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n files.push({\n path: \"database.types.ts\",\n content: generateTypedefs(collections)\n });\n\n if (options.includeReadme !== false) {\n files.push({\n path: \"README.md\",\n content: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import type { Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // token: '...', // User token if not using auth module\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n const { data: posts } = await rebase.data.collection('posts').find();\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"names":["resolveCollectionRelations"],"mappings":"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"}
|
package/dist/index.es.js
CHANGED
|
@@ -3,6 +3,9 @@ function toPascalCase(str) {
|
|
|
3
3
|
return str.split(/[_\-\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
4
4
|
}
|
|
5
5
|
function toCamelCase(str) {
|
|
6
|
+
if (!/[_\-\s]/.test(str)) {
|
|
7
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
8
|
+
}
|
|
6
9
|
const pascal = toPascalCase(str);
|
|
7
10
|
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
8
11
|
}
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","sources":["../src/utils.ts","../src/generate-types.ts","../src/index.ts"],"sourcesContent":["/**\n * Utility functions for the SDK generator\n */\n\n/**\n * Convert a slug/snake_case string to PascalCase\n * e.g. \"private_notes\" → \"PrivateNotes\"\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/**\n * Convert a slug/snake_case string to camelCase\n * e.g. \"private_notes\" → \"privateNotes\"\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert a slug to a safe JS identifier\n * e.g. \"private-notes\" → \"privateNotes\"\n */\nexport function toSafeIdentifier(str: string): string {\n return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n\n/**\n * Indent a block of text by a given number of spaces\n */\nexport function indent(text: string, spaces: number): string {\n const pad = \" \".repeat(spaces);\n return text\n .split(\"\\n\")\n .map(line => (line.trim() ? pad + line : line))\n .join(\"\\n\");\n}\n","import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, RelationProperty, StringProperty, NumberProperty } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { toPascalCase, toSafeIdentifier } from \"./utils\";\n\nfunction propertyToTypeScriptType(prop: Property): string {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n const ids = Array.isArray(sp.enum)\n ? sp.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(sp.enum);\n return ids.map(v => `\"${v}\"`).join(\" | \");\n }\n return \"string\";\n }\n case \"number\": {\n const np = prop as NumberProperty;\n if (np.enum) {\n const ids = Array.isArray(np.enum)\n ? np.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(np.enum);\n return ids.join(\" | \");\n }\n return \"number\";\n }\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"string\"; // ISO 8601 string over the wire\n case \"geopoint\":\n return \"{ latitude: number; longitude: number; }\";\n case \"reference\":\n return \"string | number\";\n case \"relation\":\n return \"string | number\";\n case \"map\": {\n const mapProp = prop as MapProperty;\n if (mapProp.properties) {\n const inner = Object.entries(mapProp.properties)\n .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)\n .join(\" \");\n return `{ ${inner} }`;\n }\n return \"Record<string, any>\";\n }\n case \"array\": {\n const arrProp = prop as ArrayProperty;\n if (arrProp.of) {\n return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;\n }\n return \"Array<any>\";\n }\n case \"vector\":\n return \"number[]\";\n case \"binary\":\n return \"string\";\n default:\n return \"any\";\n }\n}\n\nexport function generateTypedefs(collections: EntityCollection[]): string {\n const lines: string[] = [\n \"/**\",\n \" * This file was auto-generated by Rebase.\",\n \" * Do not make direct changes to the file.\",\n \" */\",\n \"\",\n \"export interface Database {\"\n ];\n\n for (const collection of collections) {\n const typeName = toPascalCase(collection.slug);\n const properties = (collection.properties ?? {}) as Properties;\n\n // Resolve relations\n let resolvedRelations: Record<string, any> = {};\n try {\n resolvedRelations = resolveCollectionRelations(collection);\n } catch { /* ignore */ }\n\n lines.push(` ${toSafeIdentifier(collection.slug)}: {`);\n\n // ── Row Type ──\n lines.push(\" Row: {\");\n const emittedKeys = new Set<string>();\n\n // 1. Direct properties\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n lines.push(` ${toSafeIdentifier(key)}${isRequired ? \"\" : \"?\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n // 2. FK columns from relations\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n\n let fkType = \"string | number\";\n try {\n let target = relation.target();\n if (target && (target.default || target.__esModule)) {\n target = target.default || target;\n }\n if (target && target.properties) {\n const idProp = Object.entries(target.properties).find(([_, p]: [string, any]) => p.isId);\n if (idProp) {\n fkType = (idProp[1] as Property).type === \"number\" ? \"number\" : \"string\";\n }\n }\n } catch { /* ignore */ }\n\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n\n // 3. Relation fields\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") {\n if (emittedKeys.has(key)) continue;\n const relation = resolvedRelations[key];\n const isArray = relation?.cardinality === \"many\";\n const relType = \"{ id: string | number; path: string; __type: \\\"relation\\\"; data?: any }\";\n const tsType = isArray ? `Array<${relType}>` : relType;\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n }\n lines.push(\" };\");\n\n // ── Insert Type ──\n lines.push(\" Insert: {\");\n emittedKeys.clear();\n\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n const typedProp = prop as StringProperty | NumberProperty;\n const isAutoId = \"isId\" in prop && typedProp.isId && typedProp.isId !== \"manual\" && typedProp.isId !== true;\n const isOptional = !isRequired || isAutoId;\n lines.push(` ${toSafeIdentifier(key)}${isOptional ? \"?\" : \"\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n const fkType = \"string | number\";\n // simple fallback\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n // ── Update Type ──\n lines.push(\" Update: {\");\n emittedKeys.clear();\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n lines.push(\" };\");\n }\n\n lines.push(\"}\");\n lines.push(\"\");\n lines.push(\"export type CollectionName = keyof Database;\");\n lines.push(\"export type CollectionsDictionary = { [K in CollectionName]: K };\");\n lines.push(\"\");\n lines.push(\"export const collectionsDictionary = {\");\n for (const collection of collections) {\n lines.push(` ${toSafeIdentifier(collection.slug)}: \"${collection.slug}\",`);\n }\n lines.push(\"} as const;\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * @rebasepro/sdk-generator\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { EntityCollection } from \"@rebasepro/types\";\nimport { generateTypedefs } from \"./generate-types\";\n\nexport { generateTypedefs } from \"./generate-types\";\nexport { toPascalCase, toCamelCase, toSafeIdentifier, indent } from \"./utils\";\n\n// ─── Public API ────────────────────────────────────────────────────\n\nexport interface GeneratedFile {\n /** Relative file path within the output directory */\n path: string;\n /** File content */\n content: string;\n}\n\nexport interface GenerateSDKOptions {\n /** Whether to include a README file (default: true) */\n includeReadme?: boolean;\n}\n\nexport function generateSDK(\n collections: EntityCollection[],\n options: GenerateSDKOptions = {}\n): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n files.push({\n path: \"database.types.ts\",\n content: generateTypedefs(collections)\n });\n\n if (options.includeReadme !== false) {\n files.push({\n path: \"README.md\",\n content: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import type { Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // token: '...', // User token if not using auth module\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n const { data: posts } = await rebase.data.collection('posts').find();\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.es.js","sources":["../src/utils.ts","../src/generate-types.ts","../src/index.ts"],"sourcesContent":["/**\n * Utility functions for the SDK generator\n */\n\n/**\n * Convert a slug/snake_case string to PascalCase\n * e.g. \"private_notes\" → \"PrivateNotes\"\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/**\n * Convert a slug/snake_case string to camelCase\n * e.g. \"private_notes\" → \"privateNotes\"\n */\nexport function toCamelCase(str: string): string {\n if (!/[_\\-\\s]/.test(str)) {\n return str.charAt(0).toLowerCase() + str.slice(1);\n }\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert a slug to a safe JS identifier\n * e.g. \"private-notes\" → \"privateNotes\"\n */\nexport function toSafeIdentifier(str: string): string {\n return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n\n/**\n * Indent a block of text by a given number of spaces\n */\nexport function indent(text: string, spaces: number): string {\n const pad = \" \".repeat(spaces);\n return text\n .split(\"\\n\")\n .map(line => (line.trim() ? pad + line : line))\n .join(\"\\n\");\n}\n","import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, Relation, RelationProperty, StringProperty, NumberProperty } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { toPascalCase, toSafeIdentifier } from \"./utils\";\n\nfunction propertyToTypeScriptType(prop: Property): string {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n const ids = Array.isArray(sp.enum)\n ? sp.enum.map((e: string | number | { id: string | number }) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(sp.enum);\n return ids.map(v => `\"${v}\"`).join(\" | \");\n }\n return \"string\";\n }\n case \"number\": {\n const np = prop as NumberProperty;\n if (np.enum) {\n const ids = Array.isArray(np.enum)\n ? np.enum.map((e: string | number | { id: string | number }) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(np.enum);\n return ids.join(\" | \");\n }\n return \"number\";\n }\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"string\"; // ISO 8601 string over the wire\n case \"geopoint\":\n return \"{ latitude: number; longitude: number; }\";\n case \"reference\":\n return \"string | number\";\n case \"relation\":\n return \"string | number\";\n case \"map\": {\n const mapProp = prop as MapProperty;\n if (mapProp.properties) {\n const inner = Object.entries(mapProp.properties)\n .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)\n .join(\" \");\n return `{ ${inner} }`;\n }\n return \"Record<string, any>\";\n }\n case \"array\": {\n const arrProp = prop as ArrayProperty;\n if (arrProp.of) {\n return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;\n }\n return \"Array<any>\";\n }\n case \"vector\":\n return \"number[]\";\n case \"binary\":\n return \"string\";\n default:\n return \"any\";\n }\n}\n\nexport function generateTypedefs(collections: EntityCollection[]): string {\n const lines: string[] = [\n \"/**\",\n \" * This file was auto-generated by Rebase.\",\n \" * Do not make direct changes to the file.\",\n \" */\",\n \"\",\n \"export interface Database {\"\n ];\n\n for (const collection of collections) {\n const typeName = toPascalCase(collection.slug);\n const properties = (collection.properties ?? {}) as Properties;\n\n // Resolve relations\n let resolvedRelations: Record<string, Relation> = {};\n try {\n resolvedRelations = resolveCollectionRelations(collection);\n } catch { /* ignore */ }\n\n lines.push(` ${toSafeIdentifier(collection.slug)}: {`);\n\n // ── Row Type ──\n lines.push(\" Row: {\");\n const emittedKeys = new Set<string>();\n\n // 1. Direct properties\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n lines.push(` ${toSafeIdentifier(key)}${isRequired ? \"\" : \"?\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n // 2. FK columns from relations\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n\n let fkType = \"string | number\";\n try {\n let target = relation.target();\n if (target && (target.default || target.__esModule)) {\n target = target.default || target;\n }\n if (target && target.properties) {\n const idProp = Object.entries(target.properties).find(([_, p]) => (p as Record<string, unknown>).isId);\n if (idProp) {\n fkType = (idProp[1] as Property).type === \"number\" ? \"number\" : \"string\";\n }\n }\n } catch { /* ignore */ }\n\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n\n // 3. Relation fields\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") {\n if (emittedKeys.has(key)) continue;\n const relation = resolvedRelations[key];\n const isArray = relation?.cardinality === \"many\";\n const relType = \"{ id: string | number; path: string; __type: \\\"relation\\\"; data?: any }\";\n const tsType = isArray ? `Array<${relType}>` : relType;\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n }\n lines.push(\" };\");\n\n // ── Insert Type ──\n lines.push(\" Insert: {\");\n emittedKeys.clear();\n\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n const isRequired = prop.validation?.required;\n const typedProp = prop as StringProperty | NumberProperty;\n const isAutoId = \"isId\" in prop && typedProp.isId && typedProp.isId !== \"manual\" && typedProp.isId !== true;\n const isOptional = !isRequired || isAutoId;\n lines.push(` ${toSafeIdentifier(key)}${isOptional ? \"?\" : \"\"}: ${tsType};`);\n emittedKeys.add(key);\n }\n\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n const fkType = \"string | number\";\n // simple fallback\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n // ── Update Type ──\n lines.push(\" Update: {\");\n emittedKeys.clear();\n for (const [key, rawProp] of Object.entries(properties)) {\n const prop = rawProp as Property;\n if (prop.type === \"relation\") continue;\n const tsType = propertyToTypeScriptType(prop);\n lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);\n emittedKeys.add(key);\n }\n for (const [relKey, relation] of Object.entries(resolvedRelations)) {\n if (relation.direction === \"owning\" && relation.cardinality === \"one\" && relation.localKey) {\n const fkKey = relation.localKey;\n if (emittedKeys.has(fkKey)) continue;\n lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);\n emittedKeys.add(fkKey);\n }\n }\n lines.push(\" };\");\n\n lines.push(\" };\");\n }\n\n lines.push(\"}\");\n lines.push(\"\");\n lines.push(\"export type CollectionName = keyof Database;\");\n lines.push(\"export type CollectionsDictionary = { [K in CollectionName]: K };\");\n lines.push(\"\");\n lines.push(\"export const collectionsDictionary = {\");\n for (const collection of collections) {\n lines.push(` ${toSafeIdentifier(collection.slug)}: \"${collection.slug}\",`);\n }\n lines.push(\"} as const;\");\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * @rebasepro/sdk-generator\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { EntityCollection } from \"@rebasepro/types\";\nimport { generateTypedefs } from \"./generate-types\";\n\nexport { generateTypedefs } from \"./generate-types\";\nexport { toPascalCase, toCamelCase, toSafeIdentifier, indent } from \"./utils\";\n\n// ─── Public API ────────────────────────────────────────────────────\n\nexport interface GeneratedFile {\n /** Relative file path within the output directory */\n path: string;\n /** File content */\n content: string;\n}\n\nexport interface GenerateSDKOptions {\n /** Whether to include a README file (default: true) */\n includeReadme?: boolean;\n}\n\nexport function generateSDK(\n collections: EntityCollection[],\n options: GenerateSDKOptions = {}\n): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n files.push({\n path: \"database.types.ts\",\n content: generateTypedefs(collections)\n });\n\n if (options.includeReadme !== false) {\n files.push({\n path: \"README.md\",\n content: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import type { Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // token: '...', // User token if not using auth module\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n const { data: posts } = await rebase.data.collection('posts').find();\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { StorageSource } from "./storage";
|
|
2
1
|
import { Role, User } from "../users";
|
|
3
|
-
import { RebaseData } from "./data";
|
|
4
2
|
/**
|
|
5
3
|
* Capabilities advertised by an auth provider.
|
|
6
4
|
* UI components use this to show/hide features dynamically
|
|
@@ -89,6 +87,8 @@ export interface AuthControllerExtended<USER extends User = User, ExtraData = un
|
|
|
89
87
|
code: string;
|
|
90
88
|
redirectUri: string;
|
|
91
89
|
}) => Promise<void>;
|
|
90
|
+
/** Generic OAuth login — works with any provider. Posts payload to /auth/{providerId}. */
|
|
91
|
+
oauthLogin?: (providerId: string, payload: Record<string, unknown>) => Promise<void>;
|
|
92
92
|
/** Register a new user */
|
|
93
93
|
register?(email: string, password: string, displayName?: string): Promise<void>;
|
|
94
94
|
/** Skip login (for anonymous access if enabled) */
|
|
@@ -102,25 +102,3 @@ export interface AuthControllerExtended<USER extends User = User, ExtraData = un
|
|
|
102
102
|
/** Update user profile */
|
|
103
103
|
updateProfile?(displayName?: string, photoURL?: string): Promise<USER>;
|
|
104
104
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Implement this function to allow access to specific users.
|
|
107
|
-
* @group Hooks and utilities
|
|
108
|
-
*/
|
|
109
|
-
export type Authenticator<USER extends User = User> = (props: {
|
|
110
|
-
/**
|
|
111
|
-
* Logged-in user or null
|
|
112
|
-
*/
|
|
113
|
-
user: USER | null;
|
|
114
|
-
/**
|
|
115
|
-
* AuthController
|
|
116
|
-
*/
|
|
117
|
-
authController: AuthController<USER>;
|
|
118
|
-
/**
|
|
119
|
-
* Unified data access API
|
|
120
|
-
*/
|
|
121
|
-
data: RebaseData;
|
|
122
|
-
/**
|
|
123
|
-
* Used storage implementation
|
|
124
|
-
*/
|
|
125
|
-
storageSource: StorageSource;
|
|
126
|
-
}) => boolean | Promise<boolean>;
|
|
@@ -65,7 +65,6 @@ export interface AdminRole {
|
|
|
65
65
|
name: string;
|
|
66
66
|
isAdmin: boolean;
|
|
67
67
|
defaultPermissions: Record<string, unknown> | null;
|
|
68
|
-
config: Record<string, unknown> | null;
|
|
69
68
|
}
|
|
70
69
|
/**
|
|
71
70
|
* Client-side Admin API interface.
|
|
@@ -123,7 +122,6 @@ export interface AdminAPI {
|
|
|
123
122
|
name: string;
|
|
124
123
|
isAdmin?: boolean;
|
|
125
124
|
defaultPermissions?: Record<string, unknown>;
|
|
126
|
-
config?: Record<string, unknown>;
|
|
127
125
|
}): Promise<{
|
|
128
126
|
role: AdminRole;
|
|
129
127
|
}>;
|
|
@@ -131,7 +129,6 @@ export interface AdminAPI {
|
|
|
131
129
|
name?: string;
|
|
132
130
|
isAdmin?: boolean;
|
|
133
131
|
defaultPermissions?: Record<string, unknown>;
|
|
134
|
-
config?: Record<string, unknown>;
|
|
135
132
|
}): Promise<{
|
|
136
133
|
role: AdminRole;
|
|
137
134
|
}>;
|
|
@@ -4,7 +4,7 @@ import type { EntityReference } from "../types/entities";
|
|
|
4
4
|
* Controller that provides access to the registered entity collections.
|
|
5
5
|
* @group Models
|
|
6
6
|
*/
|
|
7
|
-
export type CollectionRegistryController<DB = Record<string, unknown>, EC extends EntityCollection = EntityCollection
|
|
7
|
+
export type CollectionRegistryController<DB = Record<string, unknown>, EC extends EntityCollection = EntityCollection> = {
|
|
8
8
|
/**
|
|
9
9
|
* List of the mapped collections in the CMS.
|
|
10
10
|
* Each entry relates to a collection in the root database.
|
|
@@ -17,6 +17,21 @@ export type ListenEntityProps<M extends Record<string, unknown> = Record<string,
|
|
|
17
17
|
onUpdate: (entity: Entity<M> | null) => void;
|
|
18
18
|
onError?: (error: Error) => void;
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for vector similarity search queries.
|
|
22
|
+
* Vector search applies an ORDER BY distance expression and optionally
|
|
23
|
+
* filters results by a distance threshold.
|
|
24
|
+
*/
|
|
25
|
+
export interface VectorSearchParams {
|
|
26
|
+
/** Property name containing the vector column */
|
|
27
|
+
property: string;
|
|
28
|
+
/** Query vector to compare against */
|
|
29
|
+
vector: number[];
|
|
30
|
+
/** Distance function (default: "cosine") */
|
|
31
|
+
distance?: "cosine" | "l2" | "inner_product";
|
|
32
|
+
/** Only return results within this distance threshold */
|
|
33
|
+
threshold?: number;
|
|
34
|
+
}
|
|
20
35
|
/**
|
|
21
36
|
* @internal
|
|
22
37
|
*/
|
|
@@ -30,6 +45,8 @@ export interface FetchCollectionProps<M extends Record<string, unknown> = Record
|
|
|
30
45
|
orderBy?: string;
|
|
31
46
|
searchString?: string;
|
|
32
47
|
order?: "desc" | "asc";
|
|
48
|
+
/** Vector similarity search configuration */
|
|
49
|
+
vectorSearch?: VectorSearchParams;
|
|
33
50
|
}
|
|
34
51
|
/**
|
|
35
52
|
* @internal
|
|
@@ -187,6 +204,7 @@ export interface RestFetchService {
|
|
|
187
204
|
startAfter?: Record<string, unknown>;
|
|
188
205
|
searchString?: string;
|
|
189
206
|
databaseId?: string;
|
|
207
|
+
vectorSearch?: VectorSearchParams;
|
|
190
208
|
}, include?: string[]): Promise<Record<string, unknown>[]>;
|
|
191
209
|
/**
|
|
192
210
|
* Fetch a single flattened entity with optional relation includes.
|
|
@@ -4,6 +4,7 @@ import type { EntityCollectionsBuilder } from "../types/builders";
|
|
|
4
4
|
import type { EntityCustomView } from "../types/entity_views";
|
|
5
5
|
import type { EntityAction } from "../types/entity_actions";
|
|
6
6
|
import type { AppView, NavigationGroupMapping } from "./navigation";
|
|
7
|
+
import type { RebasePlugin } from "../types/plugins";
|
|
7
8
|
/**
|
|
8
9
|
* Options to enable the built-in collection editor.
|
|
9
10
|
* When provided to `<RebaseCMS>`, the editor is auto-wired as a native feature.
|
|
@@ -19,12 +20,12 @@ export interface CollectionEditorOptions {
|
|
|
19
20
|
/** Suggested base paths shown when creating new collections. */
|
|
20
21
|
pathSuggestions?: string[];
|
|
21
22
|
}
|
|
22
|
-
export interface RebaseCMSConfig<EC extends EntityCollection =
|
|
23
|
+
export interface RebaseCMSConfig<EC extends EntityCollection = EntityCollection> {
|
|
23
24
|
collections?: EC[] | EntityCollectionsBuilder<EC>;
|
|
24
25
|
homePage?: ReactNode;
|
|
25
|
-
entityViews?: EntityCustomView
|
|
26
|
+
entityViews?: EntityCustomView[];
|
|
26
27
|
entityActions?: EntityAction[];
|
|
27
|
-
plugins?:
|
|
28
|
+
plugins?: RebasePlugin[];
|
|
28
29
|
/**
|
|
29
30
|
* Centralized configuration for how collections and views are grouped
|
|
30
31
|
* in the navigation sidebar and home page.
|
|
@@ -42,7 +43,7 @@ export interface RebaseCMSConfig<EC extends EntityCollection = any> {
|
|
|
42
43
|
collectionEditor?: boolean | CollectionEditorOptions;
|
|
43
44
|
}
|
|
44
45
|
export interface RebaseStudioConfig {
|
|
45
|
-
tools?: ("sql" | "js" | "rls" | "schema" | "storage" | "cron" | "schema-visualizer" | "branches" | "api")[];
|
|
46
|
+
tools?: ("sql" | "js" | "rls" | "schema" | "storage" | "cron" | "schema-visualizer" | "branches" | "api" | "logs")[];
|
|
46
47
|
homePage?: ReactNode;
|
|
47
48
|
devViews?: AppView[];
|
|
48
49
|
}
|
|
@@ -28,7 +28,7 @@ export type RebaseCallContext<USER extends User = User> = {
|
|
|
28
28
|
* const { client } = props.context;
|
|
29
29
|
* const result = await client.functions.invoke('extract-job', { url });
|
|
30
30
|
*/
|
|
31
|
-
client: RebaseClient
|
|
31
|
+
client: RebaseClient;
|
|
32
32
|
/**
|
|
33
33
|
* Unified data access — `context.data.products.create(...)`.
|
|
34
34
|
* Access any collection as a dynamic property.
|
|
@@ -133,7 +133,7 @@ export interface AuthUserData {
|
|
|
133
133
|
displayName?: string | null;
|
|
134
134
|
photoUrl?: string | null;
|
|
135
135
|
emailVerified?: boolean;
|
|
136
|
-
metadata?: Record<string,
|
|
136
|
+
metadata?: Record<string, unknown>;
|
|
137
137
|
createdAt?: Date;
|
|
138
138
|
updatedAt?: Date;
|
|
139
139
|
}
|
|
@@ -146,7 +146,7 @@ export interface AuthCreateUserData {
|
|
|
146
146
|
password?: string;
|
|
147
147
|
displayName?: string;
|
|
148
148
|
photoUrl?: string;
|
|
149
|
-
metadata?: Record<string,
|
|
149
|
+
metadata?: Record<string, unknown>;
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
152
|
* Role data exposed by the auth adapter.
|
|
@@ -168,7 +168,6 @@ export interface AuthRoleData {
|
|
|
168
168
|
edit?: boolean;
|
|
169
169
|
delete?: boolean;
|
|
170
170
|
}> | null;
|
|
171
|
-
config?: Record<string, unknown> | null;
|
|
172
171
|
}
|
|
173
172
|
/**
|
|
174
173
|
* Data for creating a role.
|
|
@@ -180,7 +179,6 @@ export interface AuthCreateRoleData {
|
|
|
180
179
|
isAdmin?: boolean;
|
|
181
180
|
defaultPermissions?: AuthRoleData["defaultPermissions"];
|
|
182
181
|
collectionPermissions?: AuthRoleData["collectionPermissions"];
|
|
183
|
-
config?: AuthRoleData["config"];
|
|
184
182
|
}
|
|
185
183
|
/**
|
|
186
184
|
* User management operations for the admin panel.
|
|
@@ -589,10 +589,6 @@ export interface FilterPreset<Key extends string = string> {
|
|
|
589
589
|
*/
|
|
590
590
|
sort?: [Key, "asc" | "desc"];
|
|
591
591
|
}
|
|
592
|
-
/**
|
|
593
|
-
* @deprecated Use {@link FilterPreset} instead.
|
|
594
|
-
*/
|
|
595
|
-
export type QuickFilter<Key extends string = string> = FilterPreset<Key>;
|
|
596
592
|
/**
|
|
597
593
|
* Used to indicate valid filter combinations (e.g. created in Firestore)
|
|
598
594
|
* If the user selects a specific filter/sort combination, the CMS checks if it's
|
|
@@ -37,7 +37,7 @@ export interface LazyComponentRef<P = unknown> {
|
|
|
37
37
|
*
|
|
38
38
|
* @group Types
|
|
39
39
|
*/
|
|
40
|
-
export type ComponentRef<P =
|
|
40
|
+
export type ComponentRef<P = any> = React.ComponentType<P> | LazyComponentRef<P> | (() => Promise<{
|
|
41
41
|
default: React.ComponentType<P>;
|
|
42
42
|
}>) | string;
|
|
43
43
|
/**
|
|
@@ -44,7 +44,7 @@ export interface CronJobContext {
|
|
|
44
44
|
/** A simple logger scoped to this job run. */
|
|
45
45
|
log: (...args: unknown[]) => void;
|
|
46
46
|
/** The RebaseClient instance to interact with the database. */
|
|
47
|
-
client: RebaseClient
|
|
47
|
+
client: RebaseClient;
|
|
48
48
|
}
|
|
49
49
|
export type CronJobRunState = "idle" | "running" | "success" | "error" | "disabled";
|
|
50
50
|
/**
|
|
@@ -50,6 +50,7 @@ export interface FormContext<M extends Record<string, unknown> = Record<string,
|
|
|
50
50
|
export type EntityCustomView<M extends Record<string, unknown> = Record<string, unknown>> = {
|
|
51
51
|
key: string;
|
|
52
52
|
name: string;
|
|
53
|
+
icon?: string | React.ReactNode;
|
|
53
54
|
tabComponent?: React.ReactNode;
|
|
54
55
|
includeActions?: boolean | "bottom";
|
|
55
56
|
Builder?: ComponentRef<EntityCustomViewParams<M>>;
|
|
@@ -15,7 +15,7 @@ export interface ExportConfig<USER extends User = User> {
|
|
|
15
15
|
export interface ExportMappingFunction<USER extends User = User> {
|
|
16
16
|
key: string;
|
|
17
17
|
builder: ({ entity, context }: {
|
|
18
|
-
entity: Entity
|
|
18
|
+
entity: Entity;
|
|
19
19
|
context: RebaseContext<USER>;
|
|
20
20
|
}) => Promise<string> | string;
|
|
21
21
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FormEvent } from "react";
|
|
2
|
-
export type FormexController<T =
|
|
2
|
+
export type FormexController<T = unknown> = {
|
|
3
3
|
values: T;
|
|
4
4
|
initialValues: T;
|
|
5
5
|
setValues: (values: T) => void;
|
|
@@ -32,7 +32,7 @@ export type FormexController<T = any> = {
|
|
|
32
32
|
canUndo: boolean;
|
|
33
33
|
canRedo: boolean;
|
|
34
34
|
};
|
|
35
|
-
export type FormexResetProps<T =
|
|
35
|
+
export type FormexResetProps<T = unknown> = {
|
|
36
36
|
values?: T;
|
|
37
37
|
submitCount?: number;
|
|
38
38
|
errors?: Record<string, string>;
|
|
@@ -104,8 +104,8 @@ export interface BaseUIConfig<CustomProps = unknown> {
|
|
|
104
104
|
disabled?: boolean | PropertyDisabledConfig;
|
|
105
105
|
widthPercentage?: number;
|
|
106
106
|
customProps?: CustomProps;
|
|
107
|
-
Field?: ComponentRef
|
|
108
|
-
Preview?: ComponentRef
|
|
107
|
+
Field?: ComponentRef;
|
|
108
|
+
Preview?: ComponentRef;
|
|
109
109
|
}
|
|
110
110
|
export interface BaseProperty<CustomProps = unknown> {
|
|
111
111
|
ui?: BaseUIConfig<CustomProps>;
|
|
@@ -105,6 +105,12 @@ export interface RebaseTranslations {
|
|
|
105
105
|
navigation_drawer: string;
|
|
106
106
|
collapse: string;
|
|
107
107
|
expand: string;
|
|
108
|
+
/** Tooltip for the language switcher in the drawer footer */
|
|
109
|
+
change_language?: string;
|
|
110
|
+
/** Tooltip for the theme toggle in the drawer footer */
|
|
111
|
+
toggle_theme?: string;
|
|
112
|
+
/** Aria label for the user menu trigger in the drawer footer */
|
|
113
|
+
user_menu?: string;
|
|
108
114
|
error: string;
|
|
109
115
|
error_uploading_file: string;
|
|
110
116
|
error_deleting: string;
|
|
@@ -426,18 +432,28 @@ export interface RebaseTranslations {
|
|
|
426
432
|
deleted: string;
|
|
427
433
|
select_reference: string;
|
|
428
434
|
select_references: string;
|
|
429
|
-
account_settings
|
|
430
|
-
profile
|
|
431
|
-
sessions
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
435
|
+
account_settings?: string;
|
|
436
|
+
profile?: string;
|
|
437
|
+
sessions?: string;
|
|
438
|
+
security?: string;
|
|
439
|
+
change_password?: string;
|
|
440
|
+
current_password?: string;
|
|
441
|
+
new_password?: string;
|
|
442
|
+
confirm_password?: string;
|
|
443
|
+
password_changed?: string;
|
|
444
|
+
passwords_dont_match?: string;
|
|
445
|
+
password_too_short?: string;
|
|
446
|
+
password_change_not_available?: string;
|
|
447
|
+
changing_password?: string;
|
|
448
|
+
display_name?: string;
|
|
449
|
+
photo_url?: string;
|
|
450
|
+
save_profile?: string;
|
|
451
|
+
saving?: string;
|
|
452
|
+
no_active_sessions?: string;
|
|
453
|
+
revoking?: string;
|
|
454
|
+
revoke_all_sessions?: string;
|
|
455
|
+
unknown_device?: string;
|
|
456
|
+
current?: string;
|
|
441
457
|
role_id: string;
|
|
442
458
|
role_name: string;
|
|
443
459
|
add_reference: string;
|
|
@@ -104,15 +104,17 @@ export interface UserManagementDelegate<USER extends User = User> {
|
|
|
104
104
|
* If true, the UI will allow the user to create the default roles (admin, editor, viewer).
|
|
105
105
|
*/
|
|
106
106
|
allowDefaultRolesCreation?: boolean;
|
|
107
|
-
/**
|
|
108
|
-
* Should collection config permissions be included?
|
|
109
|
-
*/
|
|
110
|
-
includeCollectionConfigPermissions?: boolean;
|
|
111
107
|
/**
|
|
112
108
|
* Optionally define roles for a given user. This is useful when the roles
|
|
113
109
|
* are coming from a separate provider than the one issuing the tokens.
|
|
114
110
|
*/
|
|
115
111
|
defineRolesFor?: (user: USER) => Promise<Role[] | undefined> | Role[] | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Whether any admin users exist. Used by the bootstrap banner to decide
|
|
114
|
+
* whether to prompt. Populated via a lightweight check (e.g. `limit=1`
|
|
115
|
+
* query) instead of loading all users.
|
|
116
|
+
*/
|
|
117
|
+
hasAdminUsers?: boolean;
|
|
116
118
|
/**
|
|
117
119
|
* Optional function to bootstrap an admin user.
|
|
118
120
|
* Often used when the database is empty.
|
|
@@ -11,12 +11,4 @@ export type Role = {
|
|
|
11
11
|
* If this flag is true, the user can perform any action
|
|
12
12
|
*/
|
|
13
13
|
isAdmin?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Permissions related to editing the collections
|
|
16
|
-
*/
|
|
17
|
-
config?: {
|
|
18
|
-
createCollections?: boolean;
|
|
19
|
-
editCollections?: boolean | "own";
|
|
20
|
-
deleteCollections?: boolean | "own";
|
|
21
|
-
};
|
|
22
14
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/sdk-generator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Generate a typed JS SDK from Rebase collection definitions",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"author": "rebase.pro",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@rebasepro/common": "0.2.
|
|
23
|
-
"@rebasepro/types": "0.2.
|
|
22
|
+
"@rebasepro/common": "0.2.4",
|
|
23
|
+
"@rebasepro/types": "0.2.4"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@jest/globals": "^29.7.0",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"ts-jest": "^29.4.10",
|
|
31
31
|
"typescript": "^5.9.3",
|
|
32
32
|
"vite": "^7.3.3",
|
|
33
|
-
"@rebasepro/common": "0.2.
|
|
34
|
-
"@rebasepro/types": "0.2.
|
|
33
|
+
"@rebasepro/common": "0.2.4",
|
|
34
|
+
"@rebasepro/types": "0.2.4"
|
|
35
35
|
},
|
|
36
36
|
"exports": {
|
|
37
37
|
".": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"gitHead": "d935eefa5aa8d1009a2398cfac2c1e4ee9aeb6b6",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@rebasepro/client": "0.2.
|
|
45
|
+
"@rebasepro/client": "0.2.4"
|
|
46
46
|
},
|
|
47
47
|
"repository": {
|
|
48
48
|
"type": "git",
|
package/src/generate-types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, RelationProperty, StringProperty, NumberProperty } from "@rebasepro/types";
|
|
1
|
+
import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, Relation, RelationProperty, StringProperty, NumberProperty } from "@rebasepro/types";
|
|
2
2
|
import { resolveCollectionRelations } from "@rebasepro/common";
|
|
3
3
|
import { toPascalCase, toSafeIdentifier } from "./utils";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ function propertyToTypeScriptType(prop: Property): string {
|
|
|
8
8
|
const sp = prop as StringProperty;
|
|
9
9
|
if (sp.enum) {
|
|
10
10
|
const ids = Array.isArray(sp.enum)
|
|
11
|
-
? sp.enum.map((e:
|
|
11
|
+
? sp.enum.map((e: string | number | { id: string | number }) => typeof e === "object" ? String(e.id) : String(e))
|
|
12
12
|
: Object.keys(sp.enum);
|
|
13
13
|
return ids.map(v => `"${v}"`).join(" | ");
|
|
14
14
|
}
|
|
@@ -18,7 +18,7 @@ function propertyToTypeScriptType(prop: Property): string {
|
|
|
18
18
|
const np = prop as NumberProperty;
|
|
19
19
|
if (np.enum) {
|
|
20
20
|
const ids = Array.isArray(np.enum)
|
|
21
|
-
? np.enum.map((e:
|
|
21
|
+
? np.enum.map((e: string | number | { id: string | number }) => typeof e === "object" ? String(e.id) : String(e))
|
|
22
22
|
: Object.keys(np.enum);
|
|
23
23
|
return ids.join(" | ");
|
|
24
24
|
}
|
|
@@ -75,7 +75,7 @@ export function generateTypedefs(collections: EntityCollection[]): string {
|
|
|
75
75
|
const properties = (collection.properties ?? {}) as Properties;
|
|
76
76
|
|
|
77
77
|
// Resolve relations
|
|
78
|
-
let resolvedRelations: Record<string,
|
|
78
|
+
let resolvedRelations: Record<string, Relation> = {};
|
|
79
79
|
try {
|
|
80
80
|
resolvedRelations = resolveCollectionRelations(collection);
|
|
81
81
|
} catch { /* ignore */ }
|
|
@@ -110,7 +110,7 @@ export function generateTypedefs(collections: EntityCollection[]): string {
|
|
|
110
110
|
target = target.default || target;
|
|
111
111
|
}
|
|
112
112
|
if (target && target.properties) {
|
|
113
|
-
const idProp = Object.entries(target.properties).find(([_, p]
|
|
113
|
+
const idProp = Object.entries(target.properties).find(([_, p]) => (p as Record<string, unknown>).isId);
|
|
114
114
|
if (idProp) {
|
|
115
115
|
fkType = (idProp[1] as Property).type === "number" ? "number" : "string";
|
|
116
116
|
}
|
package/src/utils.ts
CHANGED
|
@@ -18,6 +18,9 @@ export function toPascalCase(str: string): string {
|
|
|
18
18
|
* e.g. "private_notes" → "privateNotes"
|
|
19
19
|
*/
|
|
20
20
|
export function toCamelCase(str: string): string {
|
|
21
|
+
if (!/[_\-\s]/.test(str)) {
|
|
22
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
23
|
+
}
|
|
21
24
|
const pascal = toPascalCase(str);
|
|
22
25
|
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
23
26
|
}
|
|
@@ -44,6 +44,10 @@ describe("Utils", () => {
|
|
|
44
44
|
it("converts kebab-case to camelCase", () => {
|
|
45
45
|
expect(toCamelCase("private-notes")).toBe("privateNotes");
|
|
46
46
|
});
|
|
47
|
+
it("preserves already camelCase and PascalCase when no separators are present", () => {
|
|
48
|
+
expect(toCamelCase("customDomain")).toBe("customDomain");
|
|
49
|
+
expect(toCamelCase("GitRepoUrl")).toBe("gitRepoUrl");
|
|
50
|
+
});
|
|
47
51
|
});
|
|
48
52
|
|
|
49
53
|
describe("toSafeIdentifier", () => {
|