@rebasepro/sdk-generator 0.0.1-canary.4d4fb3e

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.
Files changed (84) hide show
  1. package/LICENSE +6 -0
  2. package/dist/common/src/collections/CollectionRegistry.d.ts +48 -0
  3. package/dist/common/src/collections/index.d.ts +1 -0
  4. package/dist/common/src/data/buildRebaseData.d.ts +14 -0
  5. package/dist/common/src/index.d.ts +3 -0
  6. package/dist/common/src/util/builders.d.ts +57 -0
  7. package/dist/common/src/util/callbacks.d.ts +6 -0
  8. package/dist/common/src/util/collections.d.ts +11 -0
  9. package/dist/common/src/util/common.d.ts +2 -0
  10. package/dist/common/src/util/conditions.d.ts +26 -0
  11. package/dist/common/src/util/entities.d.ts +36 -0
  12. package/dist/common/src/util/enums.d.ts +3 -0
  13. package/dist/common/src/util/index.d.ts +16 -0
  14. package/dist/common/src/util/navigation_from_path.d.ts +34 -0
  15. package/dist/common/src/util/navigation_utils.d.ts +20 -0
  16. package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
  17. package/dist/common/src/util/paths.d.ts +14 -0
  18. package/dist/common/src/util/permissions.d.ts +5 -0
  19. package/dist/common/src/util/references.d.ts +2 -0
  20. package/dist/common/src/util/relations.d.ts +12 -0
  21. package/dist/common/src/util/resolutions.d.ts +72 -0
  22. package/dist/common/src/util/storage.d.ts +24 -0
  23. package/dist/index.cjs +211 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.es.js +208 -0
  26. package/dist/index.es.js.map +1 -0
  27. package/dist/sdk-generator/src/generate-types.d.ts +2 -0
  28. package/dist/sdk-generator/src/index.d.ts +19 -0
  29. package/dist/sdk-generator/src/utils.d.ts +22 -0
  30. package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
  31. package/dist/types/src/controllers/auth.d.ts +117 -0
  32. package/dist/types/src/controllers/client.d.ts +58 -0
  33. package/dist/types/src/controllers/collection_registry.d.ts +44 -0
  34. package/dist/types/src/controllers/customization_controller.d.ts +54 -0
  35. package/dist/types/src/controllers/data.d.ts +141 -0
  36. package/dist/types/src/controllers/data_driver.d.ts +168 -0
  37. package/dist/types/src/controllers/database_admin.d.ts +11 -0
  38. package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
  39. package/dist/types/src/controllers/effective_role.d.ts +4 -0
  40. package/dist/types/src/controllers/index.d.ts +17 -0
  41. package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
  42. package/dist/types/src/controllers/navigation.d.ts +213 -0
  43. package/dist/types/src/controllers/registry.d.ts +51 -0
  44. package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
  45. package/dist/types/src/controllers/side_entity_controller.d.ts +89 -0
  46. package/dist/types/src/controllers/snackbar.d.ts +24 -0
  47. package/dist/types/src/controllers/storage.d.ts +173 -0
  48. package/dist/types/src/index.d.ts +4 -0
  49. package/dist/types/src/rebase_context.d.ts +101 -0
  50. package/dist/types/src/types/backend.d.ts +533 -0
  51. package/dist/types/src/types/builders.d.ts +14 -0
  52. package/dist/types/src/types/chips.d.ts +5 -0
  53. package/dist/types/src/types/collections.d.ts +812 -0
  54. package/dist/types/src/types/data_source.d.ts +64 -0
  55. package/dist/types/src/types/entities.d.ts +145 -0
  56. package/dist/types/src/types/entity_actions.d.ts +98 -0
  57. package/dist/types/src/types/entity_callbacks.d.ts +173 -0
  58. package/dist/types/src/types/entity_link_builder.d.ts +7 -0
  59. package/dist/types/src/types/entity_overrides.d.ts +9 -0
  60. package/dist/types/src/types/entity_views.d.ts +61 -0
  61. package/dist/types/src/types/export_import.d.ts +21 -0
  62. package/dist/types/src/types/index.d.ts +22 -0
  63. package/dist/types/src/types/locales.d.ts +4 -0
  64. package/dist/types/src/types/modify_collections.d.ts +5 -0
  65. package/dist/types/src/types/plugins.d.ts +225 -0
  66. package/dist/types/src/types/properties.d.ts +1091 -0
  67. package/dist/types/src/types/property_config.d.ts +70 -0
  68. package/dist/types/src/types/relations.d.ts +336 -0
  69. package/dist/types/src/types/slots.d.ts +228 -0
  70. package/dist/types/src/types/translations.d.ts +826 -0
  71. package/dist/types/src/types/user_management_delegate.d.ts +120 -0
  72. package/dist/types/src/types/websockets.d.ts +78 -0
  73. package/dist/types/src/users/index.d.ts +2 -0
  74. package/dist/types/src/users/roles.d.ts +22 -0
  75. package/dist/types/src/users/user.d.ts +46 -0
  76. package/jest.config.cjs +13 -0
  77. package/package.json +51 -0
  78. package/src/generate-types.ts +176 -0
  79. package/src/index.ts +71 -0
  80. package/src/json-logic-js.d.ts +8 -0
  81. package/src/utils.ts +42 -0
  82. package/test/sdk-generator.test.ts +78 -0
  83. package/tsconfig.json +26 -0
  84. package/vite.config.ts +49 -0
@@ -0,0 +1,120 @@
1
+ import { Role, User } from "../users";
2
+ /**
3
+ * Result of creating a new user via admin flow.
4
+ * Contains the created user plus information about how credentials were delivered.
5
+ */
6
+ export interface UserCreationResult<USER extends User = User> {
7
+ /** The created user */
8
+ user: USER;
9
+ /** Whether an invitation email was sent to the user */
10
+ invitationSent: boolean;
11
+ /**
12
+ * Temporary password (only present when email service is not configured).
13
+ * This is returned one-time and should be shown to the admin to share manually.
14
+ */
15
+ temporaryPassword?: string;
16
+ }
17
+ /**
18
+ * Delegate to manage users, roles, and their permissions.
19
+ * This interface allows the CMS to be completely agnostic of the underlying
20
+ * authentication provider or backend.
21
+ *
22
+ * @group Models
23
+ */
24
+ export interface UserManagementDelegate<USER extends User = User> {
25
+ /**
26
+ * Are the users and roles currently being fetched?
27
+ */
28
+ loading: boolean;
29
+ /**
30
+ * List of users managed by the CMS.
31
+ */
32
+ users: USER[];
33
+ /**
34
+ * Optional error if users failed to load.
35
+ */
36
+ usersError?: Error;
37
+ /**
38
+ * Function to get a user by its uid. This is used to show
39
+ * user information when assigning ownership of an entity.
40
+ * @param uid
41
+ */
42
+ getUser: (uid: string) => USER | null;
43
+ /**
44
+ * Search users with server-side pagination.
45
+ * When provided, the CMS will use this for the users table
46
+ * instead of loading all users into memory.
47
+ */
48
+ searchUsers?: (options: {
49
+ search?: string;
50
+ limit?: number;
51
+ offset?: number;
52
+ orderBy?: string;
53
+ orderDir?: "asc" | "desc";
54
+ }) => Promise<{
55
+ users: USER[];
56
+ total: number;
57
+ }>;
58
+ /**
59
+ * Save a user (create or update)
60
+ * @param user
61
+ */
62
+ saveUser?: (user: USER) => Promise<USER>;
63
+ /**
64
+ * Create a new user with invitation/password generation support.
65
+ * Returns additional info about how the credentials were delivered.
66
+ * Falls back to saveUser if not provided.
67
+ */
68
+ createUser?: (user: USER) => Promise<UserCreationResult<USER>>;
69
+ /**
70
+ * Reset the password for an existing user.
71
+ * Returns a temporary password if no email service is configured,
72
+ * or a flag indicating an email invitation was sent.
73
+ */
74
+ resetPassword?: (user: USER) => Promise<UserCreationResult<USER>>;
75
+ /**
76
+ * Delete a user
77
+ * @param user
78
+ */
79
+ deleteUser?: (user: USER) => Promise<void>;
80
+ /**
81
+ * List of roles defined in the CMS.
82
+ */
83
+ roles?: Role[];
84
+ /**
85
+ * Optional error if roles failed to load.
86
+ */
87
+ rolesError?: Error;
88
+ /**
89
+ * Save a role (create or update)
90
+ * @param role
91
+ */
92
+ saveRole?: (role: Role) => Promise<void>;
93
+ /**
94
+ * Delete a role
95
+ * @param role
96
+ */
97
+ deleteRole?: (role: Role) => Promise<void>;
98
+ /**
99
+ * Is the currently logged in user an admin?
100
+ */
101
+ isAdmin?: boolean;
102
+ /**
103
+ * If true, the UI will allow the user to create the default roles (admin, editor, viewer).
104
+ */
105
+ allowDefaultRolesCreation?: boolean;
106
+ /**
107
+ * Should collection config permissions be included?
108
+ */
109
+ includeCollectionConfigPermissions?: boolean;
110
+ /**
111
+ * Optionally define roles for a given user. This is useful when the roles
112
+ * are coming from a separate provider than the one issuing the tokens.
113
+ */
114
+ defineRolesFor?: (user: USER) => Promise<Role[] | undefined> | Role[] | undefined;
115
+ /**
116
+ * Optional function to bootstrap an admin user.
117
+ * Often used when the database is empty.
118
+ */
119
+ bootstrapAdmin?: () => Promise<void>;
120
+ }
@@ -0,0 +1,78 @@
1
+ import { Entity } from "./entities";
2
+ export interface WebSocketErrorPayload {
3
+ error?: string | {
4
+ message: string;
5
+ code?: string;
6
+ };
7
+ message?: string;
8
+ code?: string;
9
+ }
10
+ export interface WebSocketMessage {
11
+ type: string;
12
+ payload?: unknown;
13
+ subscriptionId?: string;
14
+ requestId?: string;
15
+ entities?: Entity<Record<string, unknown>>[];
16
+ entity?: Entity<Record<string, unknown>> | null;
17
+ error?: string;
18
+ }
19
+ export interface CollectionUpdateMessage extends WebSocketMessage {
20
+ type: "collection_update";
21
+ subscriptionId: string;
22
+ entities: Entity<Record<string, unknown>>[];
23
+ }
24
+ export interface EntityUpdateMessage extends WebSocketMessage {
25
+ type: "entity_update";
26
+ subscriptionId: string;
27
+ entity: Entity<Record<string, unknown>> | null;
28
+ }
29
+ /**
30
+ * Lightweight patch message sent to collection subscribers when a single
31
+ * entity is created, updated, or deleted. The client can merge this into
32
+ * its cached collection data for near-instant cross-tab updates without
33
+ * waiting for a full collection refetch.
34
+ */
35
+ export interface CollectionEntityPatchMessage extends WebSocketMessage {
36
+ type: "collection_entity_patch";
37
+ subscriptionId: string;
38
+ entityId: string;
39
+ /** The updated entity, or null if deleted */
40
+ entity: Entity<Record<string, unknown>> | null;
41
+ }
42
+ /**
43
+ * Column metadata returned by table introspection.
44
+ */
45
+ export interface TableColumnInfo {
46
+ column_name: string;
47
+ data_type: string;
48
+ udt_name: string;
49
+ is_nullable: string;
50
+ column_default: string | null;
51
+ character_maximum_length: number | null;
52
+ /** Enum values, populated for USER-DEFINED (enum) columns */
53
+ enum_values?: string[];
54
+ }
55
+ export interface TableForeignKeyInfo {
56
+ column_name: string;
57
+ foreign_table_name: string;
58
+ foreign_column_name: string;
59
+ }
60
+ export interface TableJunctionInfo {
61
+ junction_table_name: string;
62
+ source_column_name: string;
63
+ target_table_name: string;
64
+ target_column_name: string;
65
+ }
66
+ export interface TablePolicyInfo {
67
+ policy_name: string;
68
+ roles: string[];
69
+ cmd: string;
70
+ qual?: string;
71
+ with_check?: string;
72
+ }
73
+ export interface TableMetadata {
74
+ columns: TableColumnInfo[];
75
+ foreignKeys: TableForeignKeyInfo[];
76
+ junctions: TableJunctionInfo[];
77
+ policies: TablePolicyInfo[];
78
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./user";
2
+ export * from "./roles";
@@ -0,0 +1,22 @@
1
+ export type Role = {
2
+ /**
3
+ * ID of the role
4
+ */
5
+ id: string;
6
+ /**
7
+ * Name of the role
8
+ */
9
+ name: string;
10
+ /**
11
+ * If this flag is true, the user can perform any action
12
+ */
13
+ isAdmin?: boolean;
14
+ /**
15
+ * Permissions related to editing the collections
16
+ */
17
+ config?: {
18
+ createCollections?: boolean;
19
+ editCollections?: boolean | "own";
20
+ deleteCollections?: boolean | "own";
21
+ };
22
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * This interface represents a user.
3
+ * It has some of the same fields as a Firebase User.
4
+ * Note that in the default implementation, we simply take the Firebase user
5
+ * and use it as a Rebase user, so that means that even if they are not mapped
6
+ * in this interface, it contains all the methods of the former, such as `delete`,
7
+ * `getIdToken`, etc.
8
+ *
9
+ * @group Models
10
+ */
11
+ export type User = {
12
+ /**
13
+ * The user's unique ID, scoped to the project.
14
+ */
15
+ readonly uid: string;
16
+ /**
17
+ * The display name of the user.
18
+ */
19
+ readonly displayName: string | null;
20
+ /**
21
+ * The email of the user.
22
+ */
23
+ readonly email: string | null;
24
+ /**
25
+ * The profile photo URL of the user.
26
+ */
27
+ readonly photoURL: string | null;
28
+ /**
29
+ * The provider used to authenticate the user.
30
+ */
31
+ readonly providerId: string;
32
+ /**
33
+ *
34
+ */
35
+ readonly isAnonymous: boolean;
36
+ /**
37
+ * Role IDs assigned to this user (e.g. ["admin", "editor"]).
38
+ * These are plain string IDs — use the UserManagementDelegate to look up full Role objects.
39
+ */
40
+ roles?: string[];
41
+ /**
42
+ * The date and time when the user was created.
43
+ */
44
+ createdAt?: Date | string | null;
45
+ getIdToken?: (forceRefresh?: boolean) => Promise<string>;
46
+ };
@@ -0,0 +1,13 @@
1
+ /** @type {import("ts-jest").JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: "ts-jest",
4
+ testEnvironment: "node",
5
+ testMatch: [
6
+ "**/test/**/*.test.ts",
7
+ "**/src/**/*.test.ts"
8
+ ],
9
+ moduleNameMapper: {
10
+ "^@rebasepro/types$": "<rootDir>/../types/src",
11
+ "^@rebasepro/common$": "<rootDir>/../common/src",
12
+ }
13
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@rebasepro/sdk-generator",
3
+ "version": "0.0.1-canary.4d4fb3e",
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
+ "scripts": {
14
+ "test": "jest --config jest.config.cjs",
15
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.json",
16
+ "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
17
+ },
18
+ "keywords": [
19
+ "sdk",
20
+ "codegen",
21
+ "rebase",
22
+ "rest-api"
23
+ ],
24
+ "author": "rebase.pro",
25
+ "license": "MIT",
26
+ "peerDependencies": {
27
+ "@rebasepro/common": "workspace:*",
28
+ "@rebasepro/types": "workspace:*"
29
+ },
30
+ "devDependencies": {
31
+ "@rebasepro/common": "0.0.1-canary.4d4fb3e",
32
+ "@rebasepro/types": "0.0.1-canary.4d4fb3e",
33
+ "@types/jest": "^29.5.14",
34
+ "@types/node": "^20.19.17",
35
+ "jest": "^29.7.0",
36
+ "ts-jest": "^29.3.4",
37
+ "typescript": "^5.9.3",
38
+ "vite": "^7.2.4"
39
+ },
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "import": "./dist/index.es.js",
44
+ "require": "./dist/index.cjs"
45
+ }
46
+ },
47
+ "gitHead": "71bcef3c51a458cd054f7924cc18efbbe515dcc8",
48
+ "dependencies": {
49
+ "@rebasepro/client": "0.0.1-canary.4d4fb3e"
50
+ }
51
+ }
@@ -0,0 +1,176 @@
1
+ import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, RelationProperty, StringProperty, NumberProperty } from "@rebasepro/types";
2
+ import { resolveCollectionRelations } from "@rebasepro/common";
3
+ import { toPascalCase, toSafeIdentifier } from "./utils";
4
+
5
+ function propertyToTypeScriptType(prop: Property): string {
6
+ switch (prop.type) {
7
+ case "string": {
8
+ const sp = prop as StringProperty;
9
+ if (sp.enum) {
10
+ const ids = Array.isArray(sp.enum)
11
+ ? sp.enum.map((e: any) => typeof e === "object" ? String(e.id) : String(e))
12
+ : Object.keys(sp.enum);
13
+ return ids.map(v => `"${v}"`).join(" | ");
14
+ }
15
+ return "string";
16
+ }
17
+ case "number": {
18
+ const np = prop as NumberProperty;
19
+ if (np.enum) {
20
+ const ids = Array.isArray(np.enum)
21
+ ? np.enum.map((e: any) => typeof e === "object" ? String(e.id) : String(e))
22
+ : Object.keys(np.enum);
23
+ return ids.join(" | ");
24
+ }
25
+ return "number";
26
+ }
27
+ case "boolean":
28
+ return "boolean";
29
+ case "date":
30
+ return "string"; // ISO 8601 string over the wire
31
+ case "geopoint":
32
+ return "{ latitude: number; longitude: number; }";
33
+ case "reference":
34
+ return "string | number";
35
+ case "relation":
36
+ return "string | number";
37
+ case "map": {
38
+ const mapProp = prop as MapProperty;
39
+ if (mapProp.properties) {
40
+ const inner = Object.entries(mapProp.properties)
41
+ .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)
42
+ .join(" ");
43
+ return `{ ${inner} }`;
44
+ }
45
+ return "Record<string, any>";
46
+ }
47
+ case "array": {
48
+ const arrProp = prop as ArrayProperty;
49
+ if (arrProp.of) {
50
+ return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;
51
+ }
52
+ return "Array<any>";
53
+ }
54
+ default:
55
+ return "any";
56
+ }
57
+ }
58
+
59
+ export function generateTypedefs(collections: EntityCollection[]): string {
60
+ const lines: string[] = [
61
+ `/**`,
62
+ ` * This file was auto-generated by Rebase.`,
63
+ ` * Do not make direct changes to the file.`,
64
+ ` */`,
65
+ ``,
66
+ `export interface Database {`
67
+ ];
68
+
69
+ for (const collection of collections) {
70
+ const typeName = toPascalCase(collection.slug);
71
+ const properties = (collection.properties ?? {}) as Properties;
72
+
73
+ // Resolve relations
74
+ let resolvedRelations: Record<string, any> = {};
75
+ try {
76
+ resolvedRelations = resolveCollectionRelations(collection as PostgresCollection);
77
+ } catch { /* ignore */ }
78
+
79
+ lines.push(` ${toSafeIdentifier(collection.slug)}: {`);
80
+
81
+ // ── Row Type ──
82
+ lines.push(` Row: {`);
83
+ const emittedKeys = new Set<string>();
84
+
85
+ // 1. Direct properties
86
+ for (const [key, rawProp] of Object.entries(properties)) {
87
+ const prop = rawProp as Property;
88
+ if (prop.type === "relation") continue;
89
+
90
+ const tsType = propertyToTypeScriptType(prop);
91
+ const isRequired = prop.validation?.required;
92
+ lines.push(` ${toSafeIdentifier(key)}${isRequired ? "" : "?"}: ${tsType};`);
93
+ emittedKeys.add(key);
94
+ }
95
+
96
+ // 2. FK columns from relations
97
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
98
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
99
+ const fkKey = relation.localKey;
100
+ if (emittedKeys.has(fkKey)) continue;
101
+
102
+ let fkType = "string | number";
103
+ try {
104
+ const target = relation.target();
105
+ if (target.properties) {
106
+ const idProp = Object.entries(target.properties).find(([_, p]: [string, any]) => p.isId);
107
+ if (idProp) {
108
+ fkType = (idProp[1] as Property).type === "number" ? "number" : "string";
109
+ }
110
+ }
111
+ } catch { /* ignore */ }
112
+
113
+ const isRequired = relation.validation?.required;
114
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
115
+ emittedKeys.add(fkKey);
116
+ }
117
+ }
118
+ lines.push(` };`);
119
+
120
+ // ── Insert Type ──
121
+ lines.push(` Insert: {`);
122
+ emittedKeys.clear();
123
+
124
+ for (const [key, rawProp] of Object.entries(properties)) {
125
+ const prop = rawProp as Property;
126
+ if (prop.type === "relation") continue;
127
+ const tsType = propertyToTypeScriptType(prop);
128
+ const isRequired = prop.validation?.required;
129
+ const typedProp = prop as StringProperty | NumberProperty;
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
+
136
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
137
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
138
+ const fkKey = relation.localKey;
139
+ if (emittedKeys.has(fkKey)) continue;
140
+ let fkType = "string | number";
141
+ // simple fallback
142
+ const isRequired = relation.validation?.required;
143
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
144
+ emittedKeys.add(fkKey);
145
+ }
146
+ }
147
+ lines.push(` };`);
148
+
149
+ // ── Update Type ──
150
+ lines.push(` Update: {`);
151
+ emittedKeys.clear();
152
+ for (const [key, rawProp] of Object.entries(properties)) {
153
+ const prop = rawProp as Property;
154
+ if (prop.type === "relation") continue;
155
+ const tsType = propertyToTypeScriptType(prop);
156
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
157
+ emittedKeys.add(key);
158
+ }
159
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
160
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
161
+ const fkKey = relation.localKey;
162
+ if (emittedKeys.has(fkKey)) continue;
163
+ lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);
164
+ emittedKeys.add(fkKey);
165
+ }
166
+ }
167
+ lines.push(` };`);
168
+
169
+ lines.push(` };`);
170
+ }
171
+
172
+ lines.push(`}`);
173
+ lines.push(``);
174
+
175
+ return lines.join("\n");
176
+ }
package/src/index.ts ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @rebasepro/sdk-generator
3
+ *
4
+ * Generates a purely typed Typescript database definition.
5
+ */
6
+
7
+ import { EntityCollection } from "@rebasepro/types";
8
+ import { generateTypedefs } from "./generate-types";
9
+
10
+ export { generateTypedefs } from "./generate-types";
11
+ export { toPascalCase, toCamelCase, toSafeIdentifier, indent } from "./utils";
12
+
13
+ // ─── Public API ────────────────────────────────────────────────────
14
+
15
+ export interface GeneratedFile {
16
+ /** Relative file path within the output directory */
17
+ path: string;
18
+ /** File content */
19
+ content: string;
20
+ }
21
+
22
+ export interface GenerateSDKOptions {
23
+ /** Whether to include a README file (default: true) */
24
+ includeReadme?: boolean;
25
+ }
26
+
27
+ export function generateSDK(
28
+ collections: EntityCollection[],
29
+ options: GenerateSDKOptions = {},
30
+ ): GeneratedFile[] {
31
+ const files: GeneratedFile[] = [];
32
+
33
+ files.push({
34
+ path: "database.types.ts",
35
+ content: generateTypedefs(collections)
36
+ });
37
+
38
+ if (options.includeReadme !== false) {
39
+ files.push({
40
+ path: "README.md",
41
+ content: `# Rebase SDK
42
+
43
+ > Auto-generated by \`rebase generate-sdk\`. Do not edit manually.
44
+
45
+ ## Usage
46
+
47
+ 1. Install the client package:
48
+ \`\`\`bash
49
+ npm install @rebasepro/client
50
+ \`\`\`
51
+
52
+ 2. Initialize with your generated types:
53
+ \`\`\`typescript
54
+ import { createRebaseClient } from '@rebasepro/client';
55
+ import type { Database } from './database.types';
56
+
57
+ const rebase = createRebaseClient<Database>({
58
+ baseUrl: 'http://localhost:3001',
59
+ // token: '...', // User token if not using auth module
60
+ });
61
+
62
+ // Both syntax styles are fully typed!
63
+ const { data: users } = await rebase.data.users.find();
64
+ const { data: posts } = await rebase.data.collection('posts').find();
65
+ \`\`\`
66
+ `
67
+ });
68
+ }
69
+
70
+ return files;
71
+ }
@@ -0,0 +1,8 @@
1
+ declare module "json-logic-js" {
2
+ interface JsonLogic {
3
+ apply(logic: any, data?: any): any;
4
+ add_operation(name: string, fn: (...args: any[]) => any): void;
5
+ }
6
+ const jsonLogic: JsonLogic;
7
+ export default jsonLogic;
8
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Utility functions for the SDK generator
3
+ */
4
+
5
+ /**
6
+ * Convert a slug/snake_case string to PascalCase
7
+ * e.g. "private_notes" → "PrivateNotes"
8
+ */
9
+ export function toPascalCase(str: string): string {
10
+ return str
11
+ .split(/[_\-\s]+/)
12
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
13
+ .join("");
14
+ }
15
+
16
+ /**
17
+ * Convert a slug/snake_case string to camelCase
18
+ * e.g. "private_notes" → "privateNotes"
19
+ */
20
+ export function toCamelCase(str: string): string {
21
+ const pascal = toPascalCase(str);
22
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
23
+ }
24
+
25
+ /**
26
+ * Convert a slug to a safe JS identifier
27
+ * e.g. "private-notes" → "privateNotes"
28
+ */
29
+ export function toSafeIdentifier(str: string): string {
30
+ return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, "_"));
31
+ }
32
+
33
+ /**
34
+ * Indent a block of text by a given number of spaces
35
+ */
36
+ export function indent(text: string, spaces: number): string {
37
+ const pad = " ".repeat(spaces);
38
+ return text
39
+ .split("\n")
40
+ .map(line => (line.trim() ? pad + line : line))
41
+ .join("\n");
42
+ }