@rebasepro/codegen 0.0.1-canary.4829d6e

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rebase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # @rebasepro/codegen
2
+
3
+ Generates typed TypeScript definitions from Rebase collection definitions — produces `Database` interface with `Row`, `Insert`, and `Update` types for each collection.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rebasepro/codegen
9
+ ```
10
+
11
+ ### Peer Dependencies
12
+
13
+ - `@rebasepro/common`
14
+ - `@rebasepro/types`
15
+
16
+ ## What This Package Does
17
+
18
+ `@rebasepro/codegen` takes an array of `CollectionConfig` definitions and produces TypeScript type files that provide full autocompletion when used with `@rebasepro/client`. It handles property types, enums, relations, maps, arrays, geopoints, vectors, and validation-based optionality.
19
+
20
+ This is typically invoked via the CLI (`npx rebase generate-sdk`) rather than called directly.
21
+
22
+ ## Key Exports
23
+
24
+ | Export | Type | Description |
25
+ |---|---|---|
26
+ | `generateSDK` | Function | Main entry — returns array of `GeneratedFile` objects |
27
+ | `generateTypedefs` | Function | Generates the `database.types.ts` content string |
28
+ | `GeneratedFile` | Interface | `{ path: string; content: string }` |
29
+ | `GenerateSDKOptions` | Interface | `{ includeReadme?: boolean }` (default: `true`) |
30
+ | `toPascalCase` | Function | `"my_collection"` → `"MyCollection"` |
31
+ | `toCamelCase` | Function | `"my_collection"` → `"myCollection"` |
32
+ | `toSafeIdentifier` | Function | Converts slugs to valid JS identifiers |
33
+ | `indent` | Function | Indent text by N spaces |
34
+
35
+ ## Generated Output
36
+
37
+ `generateSDK()` produces:
38
+
39
+ 1. **`database.types.ts`** — A `Database` interface where each collection slug is a key containing:
40
+ - `Row` — Full snapshot type (read operations)
41
+ - `Insert` — Type for creating snapshots (auto-ID fields are optional)
42
+ - `Update` — All-optional partial type for updates
43
+
44
+ 2. **`README.md`** — Usage instructions (opt out with `includeReadme: false`)
45
+
46
+ ### Property Type Mapping
47
+
48
+ | Rebase Type | TypeScript Type |
49
+ |---|---|
50
+ | `string` | `string` (or union of enum values) |
51
+ | `number` | `number` (or union of enum values) |
52
+ | `boolean` | `boolean` |
53
+ | `date` | `string` (ISO 8601) |
54
+ | `geopoint` | `{ latitude: number; longitude: number }` |
55
+ | `reference` | `string \| number` |
56
+ | `relation` | Relation object type |
57
+ | `map` | Inline object type or `Record<string, any>` |
58
+ | `array` | `Array<T>` with inferred inner type |
59
+ | `vector` | `number[]` |
60
+ | `binary` | `string` |
61
+
62
+ ## Quick Start
63
+
64
+ ```typescript
65
+ import { generateSDK } from "@rebasepro/codegen";
66
+ import type { CollectionConfig } from "@rebasepro/types";
67
+
68
+ const collections: CollectionConfig[] = [/* your collections */];
69
+
70
+ const files = generateSDK(collections);
71
+ // files = [
72
+ // { path: "database.types.ts", content: "..." },
73
+ // { path: "README.md", content: "..." }
74
+ // ]
75
+
76
+ for (const file of files) {
77
+ fs.writeFileSync(path.join(outputDir, file.path), file.content);
78
+ }
79
+ ```
80
+
81
+ ## Related Packages
82
+
83
+ - `@rebasepro/client` — Consumes the generated types for typed API calls
84
+ - `@rebasepro/types` — Provides `CollectionConfig`, `Property`, and related type definitions
85
+ - `@rebasepro/common` — Provides `resolveCollectionRelations` used during generation
@@ -0,0 +1,2 @@
1
+ import { CollectionConfig } from "@rebasepro/types";
2
+ export declare function generateTypedefs(collections: CollectionConfig[]): string;
package/dist/index.cjs ADDED
@@ -0,0 +1,227 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/common")) : typeof define === "function" && define.amd ? define(["exports", "@rebasepro/common"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.RebaseSDKGenerator = {}, global._rebasepro_common));
3
+ })(this, function(exports, _rebasepro_common) {
4
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
5
+ //#region src/utils.ts
6
+ /**
7
+ * Utility functions for the SDK generator
8
+ */
9
+ /**
10
+ * Convert a slug/snake_case string to PascalCase
11
+ * e.g. "private_notes" → "PrivateNotes"
12
+ */
13
+ function toPascalCase(str) {
14
+ return str.split(/[_\-\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
15
+ }
16
+ /**
17
+ * Convert a slug/snake_case string to camelCase
18
+ * e.g. "private_notes" → "privateNotes"
19
+ */
20
+ function toCamelCase(str) {
21
+ if (!/[_\-\s]/.test(str)) return str.charAt(0).toLowerCase() + str.slice(1);
22
+ const pascal = toPascalCase(str);
23
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
24
+ }
25
+ /**
26
+ * Convert a slug to a safe JS identifier
27
+ * e.g. "private-notes" → "privateNotes"
28
+ */
29
+ function toSafeIdentifier(str) {
30
+ return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, "_"));
31
+ }
32
+ /**
33
+ * Indent a block of text by a given number of spaces
34
+ */
35
+ function indent(text, spaces) {
36
+ const pad = " ".repeat(spaces);
37
+ return text.split("\n").map((line) => line.trim() ? pad + line : line).join("\n");
38
+ }
39
+ //#endregion
40
+ //#region src/generate-types.ts
41
+ function propertyToTypeScriptType(prop) {
42
+ switch (prop.type) {
43
+ case "string": {
44
+ const sp = prop;
45
+ if (sp.enum) return (Array.isArray(sp.enum) ? sp.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(sp.enum)).map((v) => `"${v}"`).join(" | ");
46
+ return "string";
47
+ }
48
+ case "number": {
49
+ const np = prop;
50
+ if (np.enum) return (Array.isArray(np.enum) ? np.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(np.enum)).join(" | ");
51
+ return "number";
52
+ }
53
+ case "boolean": return "boolean";
54
+ case "date": return "string";
55
+ case "geopoint": return "{ latitude: number; longitude: number; }";
56
+ case "reference": return "string | number";
57
+ case "relation": return "string | number";
58
+ case "map": {
59
+ const mapProp = prop;
60
+ if (mapProp.properties) return `{ ${Object.entries(mapProp.properties).map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v)};`).join(" ")} }`;
61
+ return "Record<string, unknown>";
62
+ }
63
+ case "array": {
64
+ const arrProp = prop;
65
+ if (arrProp.of) return `Array<${propertyToTypeScriptType(arrProp.of)}>`;
66
+ return "Array<unknown>";
67
+ }
68
+ case "vector": return "number[]";
69
+ case "binary": return "string";
70
+ default: return "unknown";
71
+ }
72
+ }
73
+ function generateTypedefs(collections) {
74
+ const lines = [
75
+ "/**",
76
+ " * This file was auto-generated by Rebase.",
77
+ " * Do not make direct changes to the file.",
78
+ " */",
79
+ "",
80
+ "export interface Database {"
81
+ ];
82
+ for (const collection of collections) {
83
+ toPascalCase(collection.slug);
84
+ const properties = collection.properties ?? {};
85
+ let resolvedRelations = {};
86
+ try {
87
+ resolvedRelations = (0, _rebasepro_common.resolveCollectionRelations)(collection);
88
+ } catch {}
89
+ lines.push(` ${toSafeIdentifier(collection.slug)}: {`);
90
+ lines.push(" Row: {");
91
+ const emittedKeys = /* @__PURE__ */ new Set();
92
+ for (const [key, rawProp] of Object.entries(properties)) {
93
+ const prop = rawProp;
94
+ if (prop.type === "relation") continue;
95
+ const tsType = propertyToTypeScriptType(prop);
96
+ const isRequired = prop.validation?.required;
97
+ lines.push(` ${toSafeIdentifier(key)}${isRequired ? "" : "?"}: ${tsType};`);
98
+ emittedKeys.add(key);
99
+ }
100
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
101
+ const fkKey = relation.localKey;
102
+ if (emittedKeys.has(fkKey)) continue;
103
+ let fkType = "string | number";
104
+ try {
105
+ let target = relation.target();
106
+ if (target && (target.default || target.__esModule)) target = target.default || target;
107
+ if (target && target.properties) {
108
+ const idProp = Object.entries(target.properties).find(([_, p]) => p.isId);
109
+ if (idProp) fkType = idProp[1].type === "number" ? "number" : "string";
110
+ }
111
+ } catch {}
112
+ const isRequired = relation.validation?.required;
113
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
114
+ emittedKeys.add(fkKey);
115
+ }
116
+ for (const [key, rawProp] of Object.entries(properties)) if (rawProp.type === "relation") {
117
+ if (emittedKeys.has(key)) continue;
118
+ const isArray = resolvedRelations[key]?.cardinality === "many";
119
+ const relType = "{ id: string | number; path: string; __type: \"relation\"; data?: unknown }";
120
+ const tsType = isArray ? `Array<${relType}>` : relType;
121
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
122
+ emittedKeys.add(key);
123
+ }
124
+ lines.push(" };");
125
+ lines.push(" Insert: {");
126
+ emittedKeys.clear();
127
+ for (const [key, rawProp] of Object.entries(properties)) {
128
+ const prop = rawProp;
129
+ if (prop.type === "relation") continue;
130
+ const tsType = propertyToTypeScriptType(prop);
131
+ const isRequired = prop.validation?.required;
132
+ const typedProp = prop;
133
+ const isAutoId = "isId" in prop && typedProp.isId && typedProp.isId !== "manual" && typedProp.isId !== true;
134
+ const isOptional = !isRequired || isAutoId;
135
+ lines.push(` ${toSafeIdentifier(key)}${isOptional ? "?" : ""}: ${tsType};`);
136
+ emittedKeys.add(key);
137
+ }
138
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
139
+ const fkKey = relation.localKey;
140
+ if (emittedKeys.has(fkKey)) continue;
141
+ const fkType = "string | number";
142
+ const isRequired = relation.validation?.required;
143
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
144
+ emittedKeys.add(fkKey);
145
+ }
146
+ lines.push(" };");
147
+ lines.push(" Update: {");
148
+ emittedKeys.clear();
149
+ for (const [key, rawProp] of Object.entries(properties)) {
150
+ const prop = rawProp;
151
+ if (prop.type === "relation") continue;
152
+ const tsType = propertyToTypeScriptType(prop);
153
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
154
+ emittedKeys.add(key);
155
+ }
156
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
157
+ const fkKey = relation.localKey;
158
+ if (emittedKeys.has(fkKey)) continue;
159
+ lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);
160
+ emittedKeys.add(fkKey);
161
+ }
162
+ lines.push(" };");
163
+ lines.push(" };");
164
+ }
165
+ lines.push("}");
166
+ lines.push("");
167
+ lines.push("export type CollectionName = keyof Database;");
168
+ lines.push("export type CollectionsDictionary = { [K in CollectionName]: K };");
169
+ lines.push("");
170
+ lines.push("export const collectionsDictionary = {");
171
+ for (const collection of collections) lines.push(` ${toSafeIdentifier(collection.slug)}: "${collection.slug}",`);
172
+ lines.push("} as const;");
173
+ lines.push("");
174
+ return lines.join("\n");
175
+ }
176
+ //#endregion
177
+ //#region src/index.ts
178
+ function generateSDK(collections, options = {}) {
179
+ const files = [];
180
+ files.push({
181
+ path: "database.types.ts",
182
+ content: generateTypedefs(collections)
183
+ });
184
+ if (options.includeReadme !== false) files.push({
185
+ path: "README.md",
186
+ content: `# Rebase SDK
187
+
188
+ > Auto-generated by \`rebase generate-sdk\`. Do not edit manually.
189
+
190
+ ## Usage
191
+
192
+ 1. Install the client package:
193
+ \`\`\`bash
194
+ npm install @rebasepro/client
195
+ \`\`\`
196
+
197
+ 2. Initialize with your generated types:
198
+ \`\`\`typescript
199
+ import { createRebaseClient } from '@rebasepro/client';
200
+ import { Database, collectionsDictionary } from './database.types';
201
+
202
+ const rebase = createRebaseClient<Database>({
203
+ baseUrl: 'http://localhost:3001',
204
+ collections: collectionsDictionary,
205
+ });
206
+
207
+ // Both syntax styles are fully typed!
208
+ const { data: users } = await rebase.data.users.find();
209
+ console.log(users[0].email); // flat access — no .values wrapper
210
+
211
+ const { data: posts } = await rebase.data.collection('posts').find();
212
+ console.log(posts[0].title); // just post.title, not post.values.title
213
+ \`\`\`
214
+ `
215
+ });
216
+ return files;
217
+ }
218
+ //#endregion
219
+ exports.generateSDK = generateSDK;
220
+ exports.generateTypedefs = generateTypedefs;
221
+ exports.indent = indent;
222
+ exports.toCamelCase = toCamelCase;
223
+ exports.toPascalCase = toPascalCase;
224
+ exports.toSafeIdentifier = toSafeIdentifier;
225
+ });
226
+
227
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"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 { CollectionConfig, PostgresCollectionConfig, 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, unknown>\";\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<unknown>\";\n }\n case \"vector\":\n return \"number[]\";\n case \"binary\":\n return \"string\";\n default:\n return \"unknown\";\n }\n}\n\nexport function generateTypedefs(collections: CollectionConfig[]): 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?: unknown }\";\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/codegen\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { CollectionConfig } 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: CollectionConfig[],\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 { Database, collectionsDictionary } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n collections: collectionsDictionary,\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n console.log(users[0].email); // flat access — no .values wrapper\n\n const { data: posts } = await rebase.data.collection('posts').find();\n console.log(posts[0].title); // just post.title, not post.values.title\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @rebasepro/codegen
3
+ *
4
+ * Generates a purely typed Typescript database definition.
5
+ */
6
+ import { CollectionConfig } from "@rebasepro/types";
7
+ export { generateTypedefs } from "./generate-types";
8
+ export { toPascalCase, toCamelCase, toSafeIdentifier, indent } from "./utils";
9
+ export interface GeneratedFile {
10
+ /** Relative file path within the output directory */
11
+ path: string;
12
+ /** File content */
13
+ content: string;
14
+ }
15
+ export interface GenerateSDKOptions {
16
+ /** Whether to include a README file (default: true) */
17
+ includeReadme?: boolean;
18
+ }
19
+ export declare function generateSDK(collections: CollectionConfig[], options?: GenerateSDKOptions): GeneratedFile[];
@@ -0,0 +1,218 @@
1
+ import { resolveCollectionRelations } from "@rebasepro/common";
2
+ //#region src/utils.ts
3
+ /**
4
+ * Utility functions for the SDK generator
5
+ */
6
+ /**
7
+ * Convert a slug/snake_case string to PascalCase
8
+ * e.g. "private_notes" → "PrivateNotes"
9
+ */
10
+ function toPascalCase(str) {
11
+ return str.split(/[_\-\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
12
+ }
13
+ /**
14
+ * Convert a slug/snake_case string to camelCase
15
+ * e.g. "private_notes" → "privateNotes"
16
+ */
17
+ function toCamelCase(str) {
18
+ if (!/[_\-\s]/.test(str)) return str.charAt(0).toLowerCase() + str.slice(1);
19
+ const pascal = toPascalCase(str);
20
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
21
+ }
22
+ /**
23
+ * Convert a slug to a safe JS identifier
24
+ * e.g. "private-notes" → "privateNotes"
25
+ */
26
+ function toSafeIdentifier(str) {
27
+ return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, "_"));
28
+ }
29
+ /**
30
+ * Indent a block of text by a given number of spaces
31
+ */
32
+ function indent(text, spaces) {
33
+ const pad = " ".repeat(spaces);
34
+ return text.split("\n").map((line) => line.trim() ? pad + line : line).join("\n");
35
+ }
36
+ //#endregion
37
+ //#region src/generate-types.ts
38
+ function propertyToTypeScriptType(prop) {
39
+ switch (prop.type) {
40
+ case "string": {
41
+ const sp = prop;
42
+ if (sp.enum) return (Array.isArray(sp.enum) ? sp.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(sp.enum)).map((v) => `"${v}"`).join(" | ");
43
+ return "string";
44
+ }
45
+ case "number": {
46
+ const np = prop;
47
+ if (np.enum) return (Array.isArray(np.enum) ? np.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(np.enum)).join(" | ");
48
+ return "number";
49
+ }
50
+ case "boolean": return "boolean";
51
+ case "date": return "string";
52
+ case "geopoint": return "{ latitude: number; longitude: number; }";
53
+ case "reference": return "string | number";
54
+ case "relation": return "string | number";
55
+ case "map": {
56
+ const mapProp = prop;
57
+ if (mapProp.properties) return `{ ${Object.entries(mapProp.properties).map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v)};`).join(" ")} }`;
58
+ return "Record<string, unknown>";
59
+ }
60
+ case "array": {
61
+ const arrProp = prop;
62
+ if (arrProp.of) return `Array<${propertyToTypeScriptType(arrProp.of)}>`;
63
+ return "Array<unknown>";
64
+ }
65
+ case "vector": return "number[]";
66
+ case "binary": return "string";
67
+ default: return "unknown";
68
+ }
69
+ }
70
+ function generateTypedefs(collections) {
71
+ const lines = [
72
+ "/**",
73
+ " * This file was auto-generated by Rebase.",
74
+ " * Do not make direct changes to the file.",
75
+ " */",
76
+ "",
77
+ "export interface Database {"
78
+ ];
79
+ for (const collection of collections) {
80
+ toPascalCase(collection.slug);
81
+ const properties = collection.properties ?? {};
82
+ let resolvedRelations = {};
83
+ try {
84
+ resolvedRelations = resolveCollectionRelations(collection);
85
+ } catch {}
86
+ lines.push(` ${toSafeIdentifier(collection.slug)}: {`);
87
+ lines.push(" Row: {");
88
+ const emittedKeys = /* @__PURE__ */ new Set();
89
+ for (const [key, rawProp] of Object.entries(properties)) {
90
+ const prop = rawProp;
91
+ if (prop.type === "relation") continue;
92
+ const tsType = propertyToTypeScriptType(prop);
93
+ const isRequired = prop.validation?.required;
94
+ lines.push(` ${toSafeIdentifier(key)}${isRequired ? "" : "?"}: ${tsType};`);
95
+ emittedKeys.add(key);
96
+ }
97
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
98
+ const fkKey = relation.localKey;
99
+ if (emittedKeys.has(fkKey)) continue;
100
+ let fkType = "string | number";
101
+ try {
102
+ let target = relation.target();
103
+ if (target && (target.default || target.__esModule)) target = target.default || target;
104
+ if (target && target.properties) {
105
+ const idProp = Object.entries(target.properties).find(([_, p]) => p.isId);
106
+ if (idProp) fkType = idProp[1].type === "number" ? "number" : "string";
107
+ }
108
+ } catch {}
109
+ const isRequired = relation.validation?.required;
110
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
111
+ emittedKeys.add(fkKey);
112
+ }
113
+ for (const [key, rawProp] of Object.entries(properties)) if (rawProp.type === "relation") {
114
+ if (emittedKeys.has(key)) continue;
115
+ const isArray = resolvedRelations[key]?.cardinality === "many";
116
+ const relType = "{ id: string | number; path: string; __type: \"relation\"; data?: unknown }";
117
+ const tsType = isArray ? `Array<${relType}>` : relType;
118
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
119
+ emittedKeys.add(key);
120
+ }
121
+ lines.push(" };");
122
+ lines.push(" Insert: {");
123
+ emittedKeys.clear();
124
+ for (const [key, rawProp] of Object.entries(properties)) {
125
+ const prop = rawProp;
126
+ if (prop.type === "relation") continue;
127
+ const tsType = propertyToTypeScriptType(prop);
128
+ const isRequired = prop.validation?.required;
129
+ const typedProp = prop;
130
+ const isAutoId = "isId" in prop && typedProp.isId && typedProp.isId !== "manual" && typedProp.isId !== true;
131
+ const isOptional = !isRequired || isAutoId;
132
+ lines.push(` ${toSafeIdentifier(key)}${isOptional ? "?" : ""}: ${tsType};`);
133
+ emittedKeys.add(key);
134
+ }
135
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
136
+ const fkKey = relation.localKey;
137
+ if (emittedKeys.has(fkKey)) continue;
138
+ const fkType = "string | number";
139
+ const isRequired = relation.validation?.required;
140
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
141
+ emittedKeys.add(fkKey);
142
+ }
143
+ lines.push(" };");
144
+ lines.push(" Update: {");
145
+ emittedKeys.clear();
146
+ for (const [key, rawProp] of Object.entries(properties)) {
147
+ const prop = rawProp;
148
+ if (prop.type === "relation") continue;
149
+ const tsType = propertyToTypeScriptType(prop);
150
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
151
+ emittedKeys.add(key);
152
+ }
153
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
154
+ const fkKey = relation.localKey;
155
+ if (emittedKeys.has(fkKey)) continue;
156
+ lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);
157
+ emittedKeys.add(fkKey);
158
+ }
159
+ lines.push(" };");
160
+ lines.push(" };");
161
+ }
162
+ lines.push("}");
163
+ lines.push("");
164
+ lines.push("export type CollectionName = keyof Database;");
165
+ lines.push("export type CollectionsDictionary = { [K in CollectionName]: K };");
166
+ lines.push("");
167
+ lines.push("export const collectionsDictionary = {");
168
+ for (const collection of collections) lines.push(` ${toSafeIdentifier(collection.slug)}: "${collection.slug}",`);
169
+ lines.push("} as const;");
170
+ lines.push("");
171
+ return lines.join("\n");
172
+ }
173
+ //#endregion
174
+ //#region src/index.ts
175
+ function generateSDK(collections, options = {}) {
176
+ const files = [];
177
+ files.push({
178
+ path: "database.types.ts",
179
+ content: generateTypedefs(collections)
180
+ });
181
+ if (options.includeReadme !== false) files.push({
182
+ path: "README.md",
183
+ content: `# Rebase SDK
184
+
185
+ > Auto-generated by \`rebase generate-sdk\`. Do not edit manually.
186
+
187
+ ## Usage
188
+
189
+ 1. Install the client package:
190
+ \`\`\`bash
191
+ npm install @rebasepro/client
192
+ \`\`\`
193
+
194
+ 2. Initialize with your generated types:
195
+ \`\`\`typescript
196
+ import { createRebaseClient } from '@rebasepro/client';
197
+ import { Database, collectionsDictionary } from './database.types';
198
+
199
+ const rebase = createRebaseClient<Database>({
200
+ baseUrl: 'http://localhost:3001',
201
+ collections: collectionsDictionary,
202
+ });
203
+
204
+ // Both syntax styles are fully typed!
205
+ const { data: users } = await rebase.data.users.find();
206
+ console.log(users[0].email); // flat access — no .values wrapper
207
+
208
+ const { data: posts } = await rebase.data.collection('posts').find();
209
+ console.log(posts[0].title); // just post.title, not post.values.title
210
+ \`\`\`
211
+ `
212
+ });
213
+ return files;
214
+ }
215
+ //#endregion
216
+ export { generateSDK, generateTypedefs, indent, toCamelCase, toPascalCase, toSafeIdentifier };
217
+
218
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","names":[],"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 { CollectionConfig, PostgresCollectionConfig, 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, unknown>\";\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<unknown>\";\n }\n case \"vector\":\n return \"number[]\";\n case \"binary\":\n return \"string\";\n default:\n return \"unknown\";\n }\n}\n\nexport function generateTypedefs(collections: CollectionConfig[]): 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?: unknown }\";\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/codegen\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { CollectionConfig } 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: CollectionConfig[],\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 { Database, collectionsDictionary } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n collections: collectionsDictionary,\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n console.log(users[0].email); // flat access — no .values wrapper\n\n const { data: posts } = await rebase.data.collection('posts').find();\n console.log(posts[0].title); // just post.title, not post.values.title\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Utility functions for the SDK generator
3
+ */
4
+ /**
5
+ * Convert a slug/snake_case string to PascalCase
6
+ * e.g. "private_notes" → "PrivateNotes"
7
+ */
8
+ export declare function toPascalCase(str: string): string;
9
+ /**
10
+ * Convert a slug/snake_case string to camelCase
11
+ * e.g. "private_notes" → "privateNotes"
12
+ */
13
+ export declare function toCamelCase(str: string): string;
14
+ /**
15
+ * Convert a slug to a safe JS identifier
16
+ * e.g. "private-notes" → "privateNotes"
17
+ */
18
+ export declare function toSafeIdentifier(str: string): string;
19
+ /**
20
+ * Indent a block of text by a given number of spaces
21
+ */
22
+ export declare function indent(text: string, spaces: number): string;
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@rebasepro/codegen",
3
+ "version": "0.0.1-canary.4829d6e",
4
+ "description": "Generate a typed JS SDK from Rebase collection definitions",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.es.js",
7
+ "types": "./dist/index.d.ts",
8
+ "type": "module",
9
+ "source": "src/index.ts",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "keywords": [
17
+ "sdk",
18
+ "codegen",
19
+ "rebase",
20
+ "rest-api"
21
+ ],
22
+ "author": "rebase.pro",
23
+ "license": "MIT",
24
+ "peerDependencies": {
25
+ "@rebasepro/common": "0.0.1-canary.4829d6e",
26
+ "@rebasepro/types": "0.0.1-canary.4829d6e"
27
+ },
28
+ "devDependencies": {
29
+ "@jest/globals": "^30.4.1",
30
+ "@types/jest": "^30.0.0",
31
+ "@types/node": "^25.9.3",
32
+ "jest": "^30.4.2",
33
+ "ts-jest": "^29.4.11",
34
+ "typescript": "^6.0.3",
35
+ "vite": "^8.0.16",
36
+ "@rebasepro/common": "0.0.1-canary.4829d6e",
37
+ "@rebasepro/types": "0.0.1-canary.4829d6e"
38
+ },
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "import": "./dist/index.es.js",
43
+ "require": "./dist/index.cjs"
44
+ }
45
+ },
46
+ "gitHead": "d935eefa5aa8d1009a2398cfac2c1e4ee9aeb6b6",
47
+ "dependencies": {
48
+ "@rebasepro/client": "0.0.1-canary.4829d6e"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/rebasepro/rebase.git",
53
+ "directory": "packages/codegen"
54
+ },
55
+ "scripts": {
56
+ "test": "jest --config jest.config.cjs",
57
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
58
+ "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
59
+ }
60
+ }