@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
package/dist/index.cjs ADDED
@@ -0,0 +1,211 @@
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.common));
3
+ })(this, (function(exports2, common) {
4
+ "use strict";
5
+ function toPascalCase(str) {
6
+ return str.split(/[_\-\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
7
+ }
8
+ function toCamelCase(str) {
9
+ const pascal = toPascalCase(str);
10
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
11
+ }
12
+ function toSafeIdentifier(str) {
13
+ return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, "_"));
14
+ }
15
+ function indent(text, spaces) {
16
+ const pad = " ".repeat(spaces);
17
+ return text.split("\n").map((line) => line.trim() ? pad + line : line).join("\n");
18
+ }
19
+ function propertyToTypeScriptType(prop) {
20
+ switch (prop.type) {
21
+ case "string": {
22
+ const sp = prop;
23
+ if (sp.enum) {
24
+ const ids = Array.isArray(sp.enum) ? sp.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(sp.enum);
25
+ return ids.map((v) => `"${v}"`).join(" | ");
26
+ }
27
+ return "string";
28
+ }
29
+ case "number": {
30
+ const np = prop;
31
+ if (np.enum) {
32
+ const ids = Array.isArray(np.enum) ? np.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(np.enum);
33
+ return ids.join(" | ");
34
+ }
35
+ return "number";
36
+ }
37
+ case "boolean":
38
+ return "boolean";
39
+ case "date":
40
+ return "string";
41
+ // ISO 8601 string over the wire
42
+ case "geopoint":
43
+ return "{ latitude: number; longitude: number; }";
44
+ case "reference":
45
+ return "string | number";
46
+ case "relation":
47
+ return "string | number";
48
+ case "map": {
49
+ const mapProp = prop;
50
+ if (mapProp.properties) {
51
+ const inner = Object.entries(mapProp.properties).map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v)};`).join(" ");
52
+ return `{ ${inner} }`;
53
+ }
54
+ return "Record<string, any>";
55
+ }
56
+ case "array": {
57
+ const arrProp = prop;
58
+ if (arrProp.of) {
59
+ return `Array<${propertyToTypeScriptType(arrProp.of)}>`;
60
+ }
61
+ return "Array<any>";
62
+ }
63
+ default:
64
+ return "any";
65
+ }
66
+ }
67
+ function generateTypedefs(collections) {
68
+ const lines = [
69
+ `/**`,
70
+ ` * This file was auto-generated by Rebase.`,
71
+ ` * Do not make direct changes to the file.`,
72
+ ` */`,
73
+ ``,
74
+ `export interface Database {`
75
+ ];
76
+ for (const collection of collections) {
77
+ toPascalCase(collection.slug);
78
+ const properties = collection.properties ?? {};
79
+ let resolvedRelations = {};
80
+ try {
81
+ resolvedRelations = common.resolveCollectionRelations(collection);
82
+ } catch {
83
+ }
84
+ lines.push(` ${toSafeIdentifier(collection.slug)}: {`);
85
+ lines.push(` Row: {`);
86
+ const emittedKeys = /* @__PURE__ */ new Set();
87
+ for (const [key, rawProp] of Object.entries(properties)) {
88
+ const prop = rawProp;
89
+ if (prop.type === "relation") continue;
90
+ const tsType = propertyToTypeScriptType(prop);
91
+ const isRequired = prop.validation?.required;
92
+ lines.push(` ${toSafeIdentifier(key)}${isRequired ? "" : "?"}: ${tsType};`);
93
+ emittedKeys.add(key);
94
+ }
95
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
96
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
97
+ const fkKey = relation.localKey;
98
+ if (emittedKeys.has(fkKey)) continue;
99
+ let fkType = "string | number";
100
+ try {
101
+ const target = relation.target();
102
+ if (target.properties) {
103
+ const idProp = Object.entries(target.properties).find(([_, p]) => p.isId);
104
+ if (idProp) {
105
+ fkType = idProp[1].type === "number" ? "number" : "string";
106
+ }
107
+ }
108
+ } catch {
109
+ }
110
+ const isRequired = relation.validation?.required;
111
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
112
+ emittedKeys.add(fkKey);
113
+ }
114
+ }
115
+ lines.push(` };`);
116
+ lines.push(` Insert: {`);
117
+ emittedKeys.clear();
118
+ for (const [key, rawProp] of Object.entries(properties)) {
119
+ const prop = rawProp;
120
+ if (prop.type === "relation") continue;
121
+ const tsType = propertyToTypeScriptType(prop);
122
+ const isRequired = prop.validation?.required;
123
+ const typedProp = prop;
124
+ const isAutoId = "isId" in prop && typedProp.isId && typedProp.isId !== "manual" && typedProp.isId !== true;
125
+ const isOptional = !isRequired || isAutoId;
126
+ lines.push(` ${toSafeIdentifier(key)}${isOptional ? "?" : ""}: ${tsType};`);
127
+ emittedKeys.add(key);
128
+ }
129
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
130
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
131
+ const fkKey = relation.localKey;
132
+ if (emittedKeys.has(fkKey)) continue;
133
+ let fkType = "string | number";
134
+ const isRequired = relation.validation?.required;
135
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
136
+ emittedKeys.add(fkKey);
137
+ }
138
+ }
139
+ lines.push(` };`);
140
+ lines.push(` Update: {`);
141
+ emittedKeys.clear();
142
+ for (const [key, rawProp] of Object.entries(properties)) {
143
+ const prop = rawProp;
144
+ if (prop.type === "relation") continue;
145
+ const tsType = propertyToTypeScriptType(prop);
146
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
147
+ emittedKeys.add(key);
148
+ }
149
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
150
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
151
+ const fkKey = relation.localKey;
152
+ if (emittedKeys.has(fkKey)) continue;
153
+ lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);
154
+ emittedKeys.add(fkKey);
155
+ }
156
+ }
157
+ lines.push(` };`);
158
+ lines.push(` };`);
159
+ }
160
+ lines.push(`}`);
161
+ lines.push(``);
162
+ return lines.join("\n");
163
+ }
164
+ function generateSDK(collections, options = {}) {
165
+ const files = [];
166
+ files.push({
167
+ path: "database.types.ts",
168
+ content: generateTypedefs(collections)
169
+ });
170
+ if (options.includeReadme !== false) {
171
+ files.push({
172
+ path: "README.md",
173
+ content: `# Rebase SDK
174
+
175
+ > Auto-generated by \`rebase generate-sdk\`. Do not edit manually.
176
+
177
+ ## Usage
178
+
179
+ 1. Install the client package:
180
+ \`\`\`bash
181
+ npm install @rebasepro/client
182
+ \`\`\`
183
+
184
+ 2. Initialize with your generated types:
185
+ \`\`\`typescript
186
+ import { createRebaseClient } from '@rebasepro/client';
187
+ import type { Database } from './database.types';
188
+
189
+ const rebase = createRebaseClient<Database>({
190
+ baseUrl: 'http://localhost:3001',
191
+ // token: '...', // User token if not using auth module
192
+ });
193
+
194
+ // Both syntax styles are fully typed!
195
+ const { data: users } = await rebase.data.users.find();
196
+ const { data: posts } = await rebase.data.collection('posts').find();
197
+ \`\`\`
198
+ `
199
+ });
200
+ }
201
+ return files;
202
+ }
203
+ exports2.generateSDK = generateSDK;
204
+ exports2.generateTypedefs = generateTypedefs;
205
+ exports2.indent = indent;
206
+ exports2.toCamelCase = toCamelCase;
207
+ exports2.toPascalCase = toPascalCase;
208
+ exports2.toSafeIdentifier = toSafeIdentifier;
209
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
210
+ }));
211
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/utils.ts","../src/generate-types.ts","../src/index.ts"],"sourcesContent":["/**\n * Utility functions for the SDK generator\n */\n\n/**\n * Convert a slug/snake_case string to PascalCase\n * e.g. \"private_notes\" → \"PrivateNotes\"\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/**\n * Convert a slug/snake_case string to camelCase\n * e.g. \"private_notes\" → \"privateNotes\"\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert a slug to a safe JS identifier\n * e.g. \"private-notes\" → \"privateNotes\"\n */\nexport function toSafeIdentifier(str: string): string {\n return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n\n/**\n * Indent a block of text by a given number of spaces\n */\nexport function indent(text: string, spaces: number): string {\n const pad = \" \".repeat(spaces);\n return text\n .split(\"\\n\")\n .map(line => (line.trim() ? pad + line : line))\n .join(\"\\n\");\n}\n","import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, RelationProperty, StringProperty, NumberProperty } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { toPascalCase, toSafeIdentifier } from \"./utils\";\n\nfunction propertyToTypeScriptType(prop: Property): string {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n const ids = Array.isArray(sp.enum)\n ? sp.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(sp.enum);\n return ids.map(v => `\"${v}\"`).join(\" | \");\n }\n return \"string\";\n }\n case \"number\": {\n const np = prop as NumberProperty;\n if (np.enum) {\n const ids = Array.isArray(np.enum)\n ? np.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(np.enum);\n return ids.join(\" | \");\n }\n return \"number\";\n }\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"string\"; // ISO 8601 string over the wire\n case \"geopoint\":\n return \"{ latitude: number; longitude: number; }\";\n case \"reference\":\n return \"string | number\";\n case \"relation\":\n return \"string | number\";\n case \"map\": {\n const mapProp = prop as MapProperty;\n if (mapProp.properties) {\n const inner = Object.entries(mapProp.properties)\n .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)\n .join(\" \");\n return `{ ${inner} }`;\n }\n return \"Record<string, any>\";\n }\n case \"array\": {\n const arrProp = prop as ArrayProperty;\n if (arrProp.of) {\n return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;\n }\n return \"Array<any>\";\n }\n default:\n return \"any\";\n }\n}\n\nexport function generateTypedefs(collections: EntityCollection[]): string {\n const lines: string[] = [\n `/**`,\n ` * This file was auto-generated by Rebase.`,\n ` * Do not make direct changes to the file.`,\n ` */`,\n ``,\n `export interface Database {`\n ];\n\n for (const collection of collections) {\n const typeName = toPascalCase(collection.slug);\n const properties = (collection.properties ?? {}) as Properties;\n\n // Resolve relations\n let resolvedRelations: Record<string, any> = {};\n try {\n resolvedRelations = resolveCollectionRelations(collection as PostgresCollection);\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 const target = relation.target();\n if (target.properties) {\n const idProp = Object.entries(target.properties).find(([_, p]: [string, any]) => p.isId);\n if (idProp) {\n fkType = (idProp[1] as Property).type === \"number\" ? \"number\" : \"string\";\n }\n }\n } catch { /* ignore */ }\n\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n 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 let 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\n return lines.join(\"\\n\");\n}\n","/**\n * @rebasepro/sdk-generator\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { EntityCollection } from \"@rebasepro/types\";\nimport { generateTypedefs } from \"./generate-types\";\n\nexport { generateTypedefs } from \"./generate-types\";\nexport { toPascalCase, toCamelCase, toSafeIdentifier, indent } from \"./utils\";\n\n// ─── Public API ────────────────────────────────────────────────────\n\nexport interface GeneratedFile {\n /** Relative file path within the output directory */\n path: string;\n /** File content */\n content: string;\n}\n\nexport interface GenerateSDKOptions {\n /** Whether to include a README file (default: true) */\n includeReadme?: boolean;\n}\n\nexport function generateSDK(\n collections: EntityCollection[],\n options: GenerateSDKOptions = {},\n): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n files.push({\n path: \"database.types.ts\",\n content: generateTypedefs(collections)\n });\n\n if (options.includeReadme !== false) {\n files.push({\n path: \"README.md\",\n content: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import type { Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // token: '...', // User token if not using auth module\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n const { data: posts } = await rebase.data.collection('posts').find();\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"names":["resolveCollectionRelations"],"mappings":"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"}
@@ -0,0 +1,208 @@
1
+ import { resolveCollectionRelations } from "@rebasepro/common";
2
+ function toPascalCase(str) {
3
+ return str.split(/[_\-\s]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
4
+ }
5
+ function toCamelCase(str) {
6
+ const pascal = toPascalCase(str);
7
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
8
+ }
9
+ function toSafeIdentifier(str) {
10
+ return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, "_"));
11
+ }
12
+ function indent(text, spaces) {
13
+ const pad = " ".repeat(spaces);
14
+ return text.split("\n").map((line) => line.trim() ? pad + line : line).join("\n");
15
+ }
16
+ function propertyToTypeScriptType(prop) {
17
+ switch (prop.type) {
18
+ case "string": {
19
+ const sp = prop;
20
+ if (sp.enum) {
21
+ const ids = Array.isArray(sp.enum) ? sp.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(sp.enum);
22
+ return ids.map((v) => `"${v}"`).join(" | ");
23
+ }
24
+ return "string";
25
+ }
26
+ case "number": {
27
+ const np = prop;
28
+ if (np.enum) {
29
+ const ids = Array.isArray(np.enum) ? np.enum.map((e) => typeof e === "object" ? String(e.id) : String(e)) : Object.keys(np.enum);
30
+ return ids.join(" | ");
31
+ }
32
+ return "number";
33
+ }
34
+ case "boolean":
35
+ return "boolean";
36
+ case "date":
37
+ return "string";
38
+ // ISO 8601 string over the wire
39
+ case "geopoint":
40
+ return "{ latitude: number; longitude: number; }";
41
+ case "reference":
42
+ return "string | number";
43
+ case "relation":
44
+ return "string | number";
45
+ case "map": {
46
+ const mapProp = prop;
47
+ if (mapProp.properties) {
48
+ const inner = Object.entries(mapProp.properties).map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v)};`).join(" ");
49
+ return `{ ${inner} }`;
50
+ }
51
+ return "Record<string, any>";
52
+ }
53
+ case "array": {
54
+ const arrProp = prop;
55
+ if (arrProp.of) {
56
+ return `Array<${propertyToTypeScriptType(arrProp.of)}>`;
57
+ }
58
+ return "Array<any>";
59
+ }
60
+ default:
61
+ return "any";
62
+ }
63
+ }
64
+ function generateTypedefs(collections) {
65
+ const lines = [
66
+ `/**`,
67
+ ` * This file was auto-generated by Rebase.`,
68
+ ` * Do not make direct changes to the file.`,
69
+ ` */`,
70
+ ``,
71
+ `export interface Database {`
72
+ ];
73
+ for (const collection of collections) {
74
+ toPascalCase(collection.slug);
75
+ const properties = collection.properties ?? {};
76
+ let resolvedRelations = {};
77
+ try {
78
+ resolvedRelations = resolveCollectionRelations(collection);
79
+ } catch {
80
+ }
81
+ lines.push(` ${toSafeIdentifier(collection.slug)}: {`);
82
+ lines.push(` Row: {`);
83
+ const emittedKeys = /* @__PURE__ */ new Set();
84
+ for (const [key, rawProp] of Object.entries(properties)) {
85
+ const prop = rawProp;
86
+ if (prop.type === "relation") continue;
87
+ const tsType = propertyToTypeScriptType(prop);
88
+ const isRequired = prop.validation?.required;
89
+ lines.push(` ${toSafeIdentifier(key)}${isRequired ? "" : "?"}: ${tsType};`);
90
+ emittedKeys.add(key);
91
+ }
92
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
93
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
94
+ const fkKey = relation.localKey;
95
+ if (emittedKeys.has(fkKey)) continue;
96
+ let fkType = "string | number";
97
+ try {
98
+ const target = relation.target();
99
+ if (target.properties) {
100
+ const idProp = Object.entries(target.properties).find(([_, p]) => p.isId);
101
+ if (idProp) {
102
+ fkType = idProp[1].type === "number" ? "number" : "string";
103
+ }
104
+ }
105
+ } catch {
106
+ }
107
+ const isRequired = relation.validation?.required;
108
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
109
+ emittedKeys.add(fkKey);
110
+ }
111
+ }
112
+ lines.push(` };`);
113
+ lines.push(` Insert: {`);
114
+ emittedKeys.clear();
115
+ for (const [key, rawProp] of Object.entries(properties)) {
116
+ const prop = rawProp;
117
+ if (prop.type === "relation") continue;
118
+ const tsType = propertyToTypeScriptType(prop);
119
+ const isRequired = prop.validation?.required;
120
+ const typedProp = prop;
121
+ const isAutoId = "isId" in prop && typedProp.isId && typedProp.isId !== "manual" && typedProp.isId !== true;
122
+ const isOptional = !isRequired || isAutoId;
123
+ lines.push(` ${toSafeIdentifier(key)}${isOptional ? "?" : ""}: ${tsType};`);
124
+ emittedKeys.add(key);
125
+ }
126
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
127
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
128
+ const fkKey = relation.localKey;
129
+ if (emittedKeys.has(fkKey)) continue;
130
+ let fkType = "string | number";
131
+ const isRequired = relation.validation?.required;
132
+ lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? "" : "?"}: ${fkType};`);
133
+ emittedKeys.add(fkKey);
134
+ }
135
+ }
136
+ lines.push(` };`);
137
+ lines.push(` Update: {`);
138
+ emittedKeys.clear();
139
+ for (const [key, rawProp] of Object.entries(properties)) {
140
+ const prop = rawProp;
141
+ if (prop.type === "relation") continue;
142
+ const tsType = propertyToTypeScriptType(prop);
143
+ lines.push(` ${toSafeIdentifier(key)}?: ${tsType};`);
144
+ emittedKeys.add(key);
145
+ }
146
+ for (const [relKey, relation] of Object.entries(resolvedRelations)) {
147
+ if (relation.direction === "owning" && relation.cardinality === "one" && relation.localKey) {
148
+ const fkKey = relation.localKey;
149
+ if (emittedKeys.has(fkKey)) continue;
150
+ lines.push(` ${toSafeIdentifier(fkKey)}?: string | number;`);
151
+ emittedKeys.add(fkKey);
152
+ }
153
+ }
154
+ lines.push(` };`);
155
+ lines.push(` };`);
156
+ }
157
+ lines.push(`}`);
158
+ lines.push(``);
159
+ return lines.join("\n");
160
+ }
161
+ function generateSDK(collections, options = {}) {
162
+ const files = [];
163
+ files.push({
164
+ path: "database.types.ts",
165
+ content: generateTypedefs(collections)
166
+ });
167
+ if (options.includeReadme !== false) {
168
+ files.push({
169
+ path: "README.md",
170
+ content: `# Rebase SDK
171
+
172
+ > Auto-generated by \`rebase generate-sdk\`. Do not edit manually.
173
+
174
+ ## Usage
175
+
176
+ 1. Install the client package:
177
+ \`\`\`bash
178
+ npm install @rebasepro/client
179
+ \`\`\`
180
+
181
+ 2. Initialize with your generated types:
182
+ \`\`\`typescript
183
+ import { createRebaseClient } from '@rebasepro/client';
184
+ import type { Database } from './database.types';
185
+
186
+ const rebase = createRebaseClient<Database>({
187
+ baseUrl: 'http://localhost:3001',
188
+ // token: '...', // User token if not using auth module
189
+ });
190
+
191
+ // Both syntax styles are fully typed!
192
+ const { data: users } = await rebase.data.users.find();
193
+ const { data: posts } = await rebase.data.collection('posts').find();
194
+ \`\`\`
195
+ `
196
+ });
197
+ }
198
+ return files;
199
+ }
200
+ export {
201
+ generateSDK,
202
+ generateTypedefs,
203
+ indent,
204
+ toCamelCase,
205
+ toPascalCase,
206
+ toSafeIdentifier
207
+ };
208
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","sources":["../src/utils.ts","../src/generate-types.ts","../src/index.ts"],"sourcesContent":["/**\n * Utility functions for the SDK generator\n */\n\n/**\n * Convert a slug/snake_case string to PascalCase\n * e.g. \"private_notes\" → \"PrivateNotes\"\n */\nexport function toPascalCase(str: string): string {\n return str\n .split(/[_\\-\\s]+/)\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/**\n * Convert a slug/snake_case string to camelCase\n * e.g. \"private_notes\" → \"privateNotes\"\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Convert a slug to a safe JS identifier\n * e.g. \"private-notes\" → \"privateNotes\"\n */\nexport function toSafeIdentifier(str: string): string {\n return toCamelCase(str.replace(/[^a-zA-Z0-9_]/g, \"_\"));\n}\n\n/**\n * Indent a block of text by a given number of spaces\n */\nexport function indent(text: string, spaces: number): string {\n const pad = \" \".repeat(spaces);\n return text\n .split(\"\\n\")\n .map(line => (line.trim() ? pad + line : line))\n .join(\"\\n\");\n}\n","import { EntityCollection, PostgresCollection, Property, Properties, MapProperty, ArrayProperty, RelationProperty, StringProperty, NumberProperty } from \"@rebasepro/types\";\nimport { resolveCollectionRelations } from \"@rebasepro/common\";\nimport { toPascalCase, toSafeIdentifier } from \"./utils\";\n\nfunction propertyToTypeScriptType(prop: Property): string {\n switch (prop.type) {\n case \"string\": {\n const sp = prop as StringProperty;\n if (sp.enum) {\n const ids = Array.isArray(sp.enum)\n ? sp.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(sp.enum);\n return ids.map(v => `\"${v}\"`).join(\" | \");\n }\n return \"string\";\n }\n case \"number\": {\n const np = prop as NumberProperty;\n if (np.enum) {\n const ids = Array.isArray(np.enum)\n ? np.enum.map((e: any) => typeof e === \"object\" ? String(e.id) : String(e))\n : Object.keys(np.enum);\n return ids.join(\" | \");\n }\n return \"number\";\n }\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"string\"; // ISO 8601 string over the wire\n case \"geopoint\":\n return \"{ latitude: number; longitude: number; }\";\n case \"reference\":\n return \"string | number\";\n case \"relation\":\n return \"string | number\";\n case \"map\": {\n const mapProp = prop as MapProperty;\n if (mapProp.properties) {\n const inner = Object.entries(mapProp.properties)\n .map(([k, v]) => `${toSafeIdentifier(k)}: ${propertyToTypeScriptType(v as Property)};`)\n .join(\" \");\n return `{ ${inner} }`;\n }\n return \"Record<string, any>\";\n }\n case \"array\": {\n const arrProp = prop as ArrayProperty;\n if (arrProp.of) {\n return `Array<${propertyToTypeScriptType(arrProp.of as Property)}>`;\n }\n return \"Array<any>\";\n }\n default:\n return \"any\";\n }\n}\n\nexport function generateTypedefs(collections: EntityCollection[]): string {\n const lines: string[] = [\n `/**`,\n ` * This file was auto-generated by Rebase.`,\n ` * Do not make direct changes to the file.`,\n ` */`,\n ``,\n `export interface Database {`\n ];\n\n for (const collection of collections) {\n const typeName = toPascalCase(collection.slug);\n const properties = (collection.properties ?? {}) as Properties;\n\n // Resolve relations\n let resolvedRelations: Record<string, any> = {};\n try {\n resolvedRelations = resolveCollectionRelations(collection as PostgresCollection);\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 const target = relation.target();\n if (target.properties) {\n const idProp = Object.entries(target.properties).find(([_, p]: [string, any]) => p.isId);\n if (idProp) {\n fkType = (idProp[1] as Property).type === \"number\" ? \"number\" : \"string\";\n }\n }\n } catch { /* ignore */ }\n\n const isRequired = relation.validation?.required;\n lines.push(` ${toSafeIdentifier(fkKey)}${isRequired ? \"\" : \"?\"}: ${fkType};`);\n emittedKeys.add(fkKey);\n }\n }\n 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 let 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\n return lines.join(\"\\n\");\n}\n","/**\n * @rebasepro/sdk-generator\n *\n * Generates a purely typed Typescript database definition.\n */\n\nimport { EntityCollection } from \"@rebasepro/types\";\nimport { generateTypedefs } from \"./generate-types\";\n\nexport { generateTypedefs } from \"./generate-types\";\nexport { toPascalCase, toCamelCase, toSafeIdentifier, indent } from \"./utils\";\n\n// ─── Public API ────────────────────────────────────────────────────\n\nexport interface GeneratedFile {\n /** Relative file path within the output directory */\n path: string;\n /** File content */\n content: string;\n}\n\nexport interface GenerateSDKOptions {\n /** Whether to include a README file (default: true) */\n includeReadme?: boolean;\n}\n\nexport function generateSDK(\n collections: EntityCollection[],\n options: GenerateSDKOptions = {},\n): GeneratedFile[] {\n const files: GeneratedFile[] = [];\n\n files.push({\n path: \"database.types.ts\",\n content: generateTypedefs(collections)\n });\n\n if (options.includeReadme !== false) {\n files.push({\n path: \"README.md\",\n content: `# Rebase SDK\n\n> Auto-generated by \\`rebase generate-sdk\\`. Do not edit manually.\n\n## Usage\n\n1. Install the client package:\n \\`\\`\\`bash\n npm install @rebasepro/client\n \\`\\`\\`\n\n2. Initialize with your generated types:\n \\`\\`\\`typescript\n import { createRebaseClient } from '@rebasepro/client';\n import type { Database } from './database.types';\n\n const rebase = createRebaseClient<Database>({\n baseUrl: 'http://localhost:3001',\n // token: '...', // User token if not using auth module\n });\n\n // Both syntax styles are fully typed!\n const { data: users } = await rebase.data.users.find();\n const { data: posts } = await rebase.data.collection('posts').find();\n \\`\\`\\`\n`\n });\n }\n\n return files;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { EntityCollection } from "@rebasepro/types";
2
+ export declare function generateTypedefs(collections: EntityCollection[]): string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @rebasepro/sdk-generator
3
+ *
4
+ * Generates a purely typed Typescript database definition.
5
+ */
6
+ import { EntityCollection } 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: EntityCollection[], options?: GenerateSDKOptions): GeneratedFile[];
@@ -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;
@@ -0,0 +1,7 @@
1
+ export type AnalyticsController = {
2
+ /**
3
+ * Callback used to get analytics events from the CMS
4
+ */
5
+ onAnalyticsEvent?: (event: AnalyticsEvent, data?: object) => void;
6
+ };
7
+ export type AnalyticsEvent = "entity_click" | "entity_click_from_reference" | "reference_selection_clear" | "reference_selection_toggle" | "reference_selected_single" | "reference_selection_new_entity" | "edit_entity_clicked" | "entity_edited" | "new_entity_click" | "new_entity_saved" | "copy_entity_click" | "entity_copied" | "single_delete_dialog_open" | "multiple_delete_dialog_open" | "single_entity_deleted" | "multiple_entities_deleted" | "drawer_navigate_to_home" | "drawer_navigate_to_collection" | "drawer_navigate_to_view" | "home_navigate_to_collection" | "home_favorite_navigate_to_collection" | "home_navigate_to_view" | "home_navigate_to_admin_view" | "home_favorite_navigate_to_view" | "home_move_card" | "home_move_group" | "home_drop_new_group" | "collection_inline_editing" | "view_mode_changed" | "kanban_card_moved" | "kanban_column_reorder" | "kanban_property_changed" | "kanban_new_entity_in_column" | "kanban_backfill_order" | "card_view_entity_click" | "unmapped_event";
@@ -0,0 +1,117 @@
1
+ import { StorageSource } from "./storage";
2
+ import { Role, User } from "../users";
3
+ import { RebaseData } from "./data";
4
+ /**
5
+ * Capabilities advertised by an auth provider.
6
+ * UI components use this to show/hide features dynamically
7
+ * (e.g. password reset, registration, session management).
8
+ * @group Hooks and utilities
9
+ */
10
+ export interface AuthCapabilities {
11
+ emailPasswordLogin?: boolean;
12
+ googleLogin?: boolean;
13
+ registration?: boolean;
14
+ passwordReset?: boolean;
15
+ sessionManagement?: boolean;
16
+ profileUpdate?: boolean;
17
+ emailVerification?: boolean;
18
+ }
19
+ /**
20
+ * Controller for retrieving the logged user or performing auth related operations.
21
+ * Note that if you are implementing your AuthController, you probably will want
22
+ * to do it as the result of a hook.
23
+ * @group Hooks and utilities
24
+ */
25
+ export type AuthController<USER extends User = User, ExtraData = unknown> = {
26
+ /**
27
+ * The user currently logged in
28
+ * The values can be: the user object, null if they skipped login
29
+ */
30
+ user: USER | null;
31
+ /**
32
+ * Initial loading flag. It is used not to display the login screen
33
+ * when the app first loads, and it has not been checked whether the user
34
+ * is logged in or not.
35
+ */
36
+ initialLoading?: boolean;
37
+ /**
38
+ * Loading flag. It is used to display a loading screen when the user is
39
+ * logging in or out.
40
+ */
41
+ authLoading: boolean;
42
+ /**
43
+ * Sign out
44
+ */
45
+ signOut: () => Promise<void>;
46
+ /**
47
+ * Error initializing the authentication
48
+ */
49
+ authError?: unknown;
50
+ /**
51
+ * Error dispatched by the auth provider
52
+ */
53
+ authProviderError?: unknown;
54
+ /**
55
+ * You can use this method to retrieve the auth token for the current user.
56
+ */
57
+ getAuthToken: () => Promise<string>;
58
+ /**
59
+ * Has the user skipped the login process
60
+ */
61
+ loginSkipped: boolean;
62
+ extra: ExtraData;
63
+ setExtra: (extra: ExtraData) => void;
64
+ setUser?(user: USER | null): void;
65
+ setUserRoles?(roles: Role[]): void;
66
+ /**
67
+ * Capabilities advertised by the auth provider.
68
+ * UI components use this to feature-detect what the backend supports.
69
+ */
70
+ capabilities?: AuthCapabilities;
71
+ };
72
+ /**
73
+ * Extended auth controller with common optional auth methods.
74
+ * Backend implementations (Rebase backend, Firebase, Supabase, etc.)
75
+ * extend this with their own backend-specific extras.
76
+ * @group Hooks and utilities
77
+ */
78
+ export interface AuthControllerExtended<USER extends User = User, ExtraData = unknown> extends AuthController<USER, ExtraData> {
79
+ /** Login with email and password */
80
+ emailPasswordLogin?(email: string, password: string): Promise<void>;
81
+ /** Login with a Google ID token or trigger Google popup */
82
+ googleLogin?(idToken: string): Promise<void>;
83
+ /** Register a new user */
84
+ register?(email: string, password: string, displayName?: string): Promise<void>;
85
+ /** Skip login (for anonymous access if enabled) */
86
+ skipLogin?(): void;
87
+ /** Request password reset email */
88
+ forgotPassword?(email: string): Promise<void>;
89
+ /** Reset password using a token */
90
+ resetPassword?(token: string, password: string): Promise<void>;
91
+ /** Change password for the authenticated user */
92
+ changePassword?(oldPassword: string, newPassword: string): Promise<void>;
93
+ /** Update user profile */
94
+ updateProfile?(displayName?: string, photoURL?: string): Promise<USER>;
95
+ }
96
+ /**
97
+ * Implement this function to allow access to specific users.
98
+ * @group Hooks and utilities
99
+ */
100
+ export type Authenticator<USER extends User = User> = (props: {
101
+ /**
102
+ * Logged-in user or null
103
+ */
104
+ user: USER | null;
105
+ /**
106
+ * AuthController
107
+ */
108
+ authController: AuthController<USER>;
109
+ /**
110
+ * Unified data access API
111
+ */
112
+ data: RebaseData;
113
+ /**
114
+ * Used storage implementation
115
+ */
116
+ storageSource: StorageSource;
117
+ }) => boolean | Promise<boolean>;