@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 +21 -0
- package/README.md +85 -0
- package/dist/generate-types.d.ts +2 -0
- package/dist/index.cjs +227 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.es.js +218 -0
- package/dist/index.es.js.map +1 -0
- package/dist/utils.d.ts +22 -0
- package/package.json +60 -0
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
|
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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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[];
|
package/dist/index.es.js
ADDED
|
@@ -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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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -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
|
+
}
|