dzql 0.6.15 → 0.6.17
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/package.json +1 -1
- package/src/cli/codegen/client.ts +60 -8
- package/src/cli/codegen/manifest.ts +6 -2
- package/src/cli/codegen/sql.ts +28 -1
- package/src/cli/codegen/types.ts +97 -3
- package/src/cli/compiler/ir.ts +62 -3
- package/src/shared/ir.ts +31 -0
package/package.json
CHANGED
|
@@ -15,8 +15,27 @@ export function generateClientSDK(manifest: Manifest): string {
|
|
|
15
15
|
const subscriptionFunctions = Object.entries(manifest.functions)
|
|
16
16
|
.filter(([_, def]) => def.isSubscription);
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
const
|
|
18
|
+
// Build a map of custom functions with typed params/returns
|
|
19
|
+
const typedCustomFunctions = new Map<string, { hasParams: boolean; hasReturns: boolean; returnsScalar: boolean }>();
|
|
20
|
+
if (manifest.customFunctions) {
|
|
21
|
+
for (const fn of manifest.customFunctions) {
|
|
22
|
+
const hasParams = fn.params && Object.keys(fn.params).length > 0;
|
|
23
|
+
const hasReturns = fn.returns !== undefined;
|
|
24
|
+
const returnsScalar = typeof fn.returns === 'string';
|
|
25
|
+
typedCustomFunctions.set(fn.name, { hasParams: !!hasParams, hasReturns, returnsScalar });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Generate entity types (now with auth, subscribable results, and custom function types)
|
|
30
|
+
const typeDefs = generateTypeDefinitions(
|
|
31
|
+
manifest.entities,
|
|
32
|
+
manifest.subscribables,
|
|
33
|
+
manifest.auth,
|
|
34
|
+
manifest.customFunctions
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Check if we have auth types
|
|
38
|
+
const hasAuth = manifest.auth !== undefined;
|
|
20
39
|
|
|
21
40
|
// Generate API interface with proper types
|
|
22
41
|
const apiMethods = Object.entries(manifest.functions).map(([funcName, def]) => {
|
|
@@ -29,18 +48,34 @@ export function generateClientSDK(manifest: Manifest): string {
|
|
|
29
48
|
// Check if this entity exists in manifest
|
|
30
49
|
const entityExists = manifest.entities[entity];
|
|
31
50
|
|
|
51
|
+
// Check if this is a subscribable
|
|
52
|
+
const subscribableExists = manifest.subscribables?.[entity];
|
|
53
|
+
|
|
32
54
|
let paramType = 'Record<string, unknown>';
|
|
33
55
|
let returnType = 'unknown';
|
|
34
56
|
|
|
57
|
+
// Handle auth functions
|
|
58
|
+
if (funcName === 'login_user' && hasAuth) {
|
|
59
|
+
return ` ${funcName}: (params: LoginParams) => Promise<LoginResult>;`;
|
|
60
|
+
}
|
|
61
|
+
if (funcName === 'register_user' && hasAuth) {
|
|
62
|
+
return ` ${funcName}: (params: RegisterParams) => Promise<RegisterResult>;`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle subscriptions
|
|
35
66
|
if (isSubscription) {
|
|
36
|
-
// subscribe_venue_detail -> VenueDetailParams
|
|
67
|
+
// subscribe_venue_detail -> VenueDetailParams, VenueDetailResult
|
|
37
68
|
paramType = `${pascalEntity}Params`;
|
|
38
|
-
|
|
39
|
-
|
|
69
|
+
const resultType = subscribableExists ? `${pascalEntity}Result` : 'unknown';
|
|
70
|
+
return ` ${funcName}: (params: ${paramType}, callback: (data: ${resultType}) => void) => Promise<{ data: ${resultType}; subscription_id: string; schema: unknown; unsubscribe: () => Promise<void> }>;`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Handle entity CRUD operations
|
|
74
|
+
if (op === 'get' && entityExists) {
|
|
40
75
|
// get_venue_detail (subscribable getter) vs get_venues (entity getter)
|
|
41
|
-
if (
|
|
76
|
+
if (subscribableExists) {
|
|
42
77
|
paramType = `${pascalEntity}Params`;
|
|
43
|
-
returnType =
|
|
78
|
+
returnType = `${pascalEntity}Result`;
|
|
44
79
|
} else {
|
|
45
80
|
paramType = `${pascalEntity}PK`;
|
|
46
81
|
returnType = `${pascalEntity} | null`;
|
|
@@ -58,7 +93,24 @@ export function generateClientSDK(manifest: Manifest): string {
|
|
|
58
93
|
paramType = `Lookup${pascalEntity}Params`;
|
|
59
94
|
returnType = `${pascalEntity}[]`;
|
|
60
95
|
}
|
|
61
|
-
|
|
96
|
+
|
|
97
|
+
// Handle custom functions with typed params/returns
|
|
98
|
+
const customFnInfo = typedCustomFunctions.get(funcName);
|
|
99
|
+
if (customFnInfo) {
|
|
100
|
+
const pascalFuncName = toPascalCase(funcName);
|
|
101
|
+
if (customFnInfo.hasParams) {
|
|
102
|
+
paramType = `${pascalFuncName}Params`;
|
|
103
|
+
}
|
|
104
|
+
if (customFnInfo.hasReturns) {
|
|
105
|
+
if (customFnInfo.returnsScalar) {
|
|
106
|
+
// Find the scalar type from the manifest
|
|
107
|
+
const fn = manifest.customFunctions?.find(f => f.name === funcName);
|
|
108
|
+
returnType = fn?.returns as string || 'unknown';
|
|
109
|
+
} else {
|
|
110
|
+
returnType = `${pascalFuncName}Result`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
62
114
|
|
|
63
115
|
return ` ${funcName}: (params: ${paramType}) => Promise<${returnType}>;`;
|
|
64
116
|
}).join('\n');
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { DomainIR, EntityIR, SubscribableIR } from "../../shared/ir.js";
|
|
1
|
+
import { DomainIR, EntityIR, SubscribableIR, AuthIR, CustomFunctionIR } from "../../shared/ir.js";
|
|
2
2
|
|
|
3
3
|
export interface Manifest {
|
|
4
4
|
version: string;
|
|
5
5
|
functions: Record<string, FunctionDef>;
|
|
6
6
|
entities: Record<string, EntityIR>;
|
|
7
7
|
subscribables: Record<string, SubscribableIR>;
|
|
8
|
+
auth?: AuthIR;
|
|
9
|
+
customFunctions?: CustomFunctionIR[];
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export interface FunctionDef {
|
|
@@ -90,6 +92,8 @@ export function generateManifest(ir: DomainIR): Manifest {
|
|
|
90
92
|
version: "2.0.0",
|
|
91
93
|
functions,
|
|
92
94
|
entities: ir.entities, // Pass through for client generator
|
|
93
|
-
subscribables: ir.subscribables
|
|
95
|
+
subscribables: ir.subscribables,
|
|
96
|
+
auth: ir.auth,
|
|
97
|
+
customFunctions: ir.customFunctions
|
|
94
98
|
};
|
|
95
99
|
}
|
package/src/cli/codegen/sql.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compilePermission } from "../compiler/permissions.js";
|
|
2
2
|
import { compileGraphRules } from "../compiler/graph_rules.js";
|
|
3
|
-
import type { EntityIR, ManyToManyIR } from "../../shared/ir.js";
|
|
3
|
+
import type { EntityIR, ManyToManyIR, IncludeIR } from "../../shared/ir.js";
|
|
4
4
|
|
|
5
5
|
/** Column info from EntityIR */
|
|
6
6
|
interface ColumnInfo {
|
|
@@ -313,6 +313,32 @@ export function generateSaveFunction(name: string, entityIR: EntityIR): string {
|
|
|
313
313
|
return sql;
|
|
314
314
|
}).join('\n');
|
|
315
315
|
|
|
316
|
+
// FK expansion (add related objects to output for real-time events)
|
|
317
|
+
// Only expand direct FKs (where key_id column exists), not reverse FKs (child arrays)
|
|
318
|
+
const includes: Record<string, IncludeIR> = entityIR.includes || {};
|
|
319
|
+
const includeKeys = Object.keys(includes);
|
|
320
|
+
const fkExpansion = includeKeys.map(key => {
|
|
321
|
+
const config: IncludeIR = includes[key];
|
|
322
|
+
const targetEntity = config.entity;
|
|
323
|
+
const fkField = `${key}_id`; // Convention: author -> author_id
|
|
324
|
+
|
|
325
|
+
// Only expand if this is a direct FK (key_id column exists)
|
|
326
|
+
const hasFkColumn = entityIR.columns.some((c: ColumnInfo) => c.name === fkField);
|
|
327
|
+
|
|
328
|
+
if (hasFkColumn) {
|
|
329
|
+
// Direct FK: single object expansion (e.g., author_id -> author object)
|
|
330
|
+
return `
|
|
331
|
+
-- FK: Add ${key} to output (from ${fkField})
|
|
332
|
+
IF (v_result->>'${fkField}') IS NOT NULL THEN
|
|
333
|
+
v_result := v_result || jsonb_build_object('${key}',
|
|
334
|
+
(SELECT to_jsonb(t.*) FROM ${targetEntity} t WHERE t.id = (v_result->>'${fkField}')::int));
|
|
335
|
+
END IF;`;
|
|
336
|
+
}
|
|
337
|
+
// Skip reverse FKs - they would require querying child tables which may not exist
|
|
338
|
+
// and are not needed for the primary use case of expanding the saved record
|
|
339
|
+
return '';
|
|
340
|
+
}).filter(s => s).join('\n');
|
|
341
|
+
|
|
316
342
|
return `
|
|
317
343
|
CREATE OR REPLACE FUNCTION dzql_v2.save_${name}(p_user_id int, p_data jsonb)
|
|
318
344
|
RETURNS jsonb
|
|
@@ -365,6 +391,7 @@ ${m2mExtraction}
|
|
|
365
391
|
END IF;
|
|
366
392
|
${m2mSync}
|
|
367
393
|
${m2mExpansion}
|
|
394
|
+
${fkExpansion}
|
|
368
395
|
|
|
369
396
|
-- Resolve notification recipients
|
|
370
397
|
v_notify_users := dzql_v2.${name}_notify_users(p_user_id, v_result);
|
package/src/cli/codegen/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityIR, SubscribableIR } from "../../shared/ir.js";
|
|
1
|
+
import { EntityIR, SubscribableIR, AuthIR, CustomFunctionIR } from "../../shared/ir.js";
|
|
2
2
|
|
|
3
3
|
const TYPE_MAP: Record<string, string> = {
|
|
4
4
|
'text': 'string',
|
|
@@ -19,6 +19,7 @@ const PARAM_TYPE_MAP: Record<string, string> = {
|
|
|
19
19
|
'integer': 'number',
|
|
20
20
|
'text': 'string',
|
|
21
21
|
'string': 'string',
|
|
22
|
+
'number': 'number',
|
|
22
23
|
'boolean': 'boolean',
|
|
23
24
|
'date': 'string',
|
|
24
25
|
'timestamptz': 'string'
|
|
@@ -26,7 +27,9 @@ const PARAM_TYPE_MAP: Record<string, string> = {
|
|
|
26
27
|
|
|
27
28
|
export function generateTypeDefinitions(
|
|
28
29
|
entities: Record<string, EntityIR>,
|
|
29
|
-
subscribables?: Record<string, SubscribableIR
|
|
30
|
+
subscribables?: Record<string, SubscribableIR>,
|
|
31
|
+
auth?: AuthIR,
|
|
32
|
+
customFunctions?: CustomFunctionIR[]
|
|
30
33
|
): string {
|
|
31
34
|
let output = "";
|
|
32
35
|
|
|
@@ -49,6 +52,7 @@ export type FilterValue<T> = T | FilterOperators<T>;
|
|
|
49
52
|
|
|
50
53
|
`;
|
|
51
54
|
|
|
55
|
+
// --- Entity Types ---
|
|
52
56
|
for (const [name, entity] of Object.entries(entities)) {
|
|
53
57
|
const pascalName = toPascalCase(name);
|
|
54
58
|
|
|
@@ -106,17 +110,107 @@ export type FilterValue<T> = T | FilterOperators<T>;
|
|
|
106
110
|
}\n\n`;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
// --- Subscribable Params ---
|
|
113
|
+
// --- Subscribable Params and Result Types ---
|
|
110
114
|
if (subscribables) {
|
|
111
115
|
for (const [name, sub] of Object.entries(subscribables)) {
|
|
112
116
|
const pascalName = toPascalCase(name);
|
|
113
117
|
|
|
118
|
+
// Params interface
|
|
114
119
|
output += `export interface ${pascalName}Params {\n`;
|
|
115
120
|
for (const [paramName, paramType] of Object.entries(sub.params)) {
|
|
116
121
|
const tsType = PARAM_TYPE_MAP[paramType as string] || 'any';
|
|
117
122
|
output += ` ${paramName}: ${tsType};\n`;
|
|
118
123
|
}
|
|
119
124
|
output += `}\n\n`;
|
|
125
|
+
|
|
126
|
+
// Result interface (extends root entity with includes)
|
|
127
|
+
const rootEntity = sub.root?.entity;
|
|
128
|
+
const rootEntityPascal = rootEntity ? toPascalCase(rootEntity) : null;
|
|
129
|
+
|
|
130
|
+
if (rootEntityPascal && entities[rootEntity]) {
|
|
131
|
+
output += `export interface ${pascalName}Result extends ${rootEntityPascal} {\n`;
|
|
132
|
+
|
|
133
|
+
// Add includes as optional fields
|
|
134
|
+
if (sub.includes) {
|
|
135
|
+
for (const [includeKey, includeIR] of Object.entries(sub.includes)) {
|
|
136
|
+
const includeEntity = includeIR.entity;
|
|
137
|
+
const includeEntityPascal = toPascalCase(includeEntity);
|
|
138
|
+
|
|
139
|
+
// Determine if it's an array (one-to-many) or single (many-to-one)
|
|
140
|
+
// For now, assume arrays unless the include key matches a FK pattern
|
|
141
|
+
const isArray = !includeKey.endsWith('_id') && includeKey !== rootEntity;
|
|
142
|
+
const includeType = isArray ? `${includeEntityPascal}[]` : includeEntityPascal;
|
|
143
|
+
|
|
144
|
+
output += ` ${includeKey}?: ${includeType};\n`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
output += `}\n\n`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// --- Auth Types ---
|
|
154
|
+
if (auth) {
|
|
155
|
+
// AuthUser interface
|
|
156
|
+
output += `export interface AuthUser {\n`;
|
|
157
|
+
for (const [fieldName, fieldType] of Object.entries(auth.userFields)) {
|
|
158
|
+
const tsType = PARAM_TYPE_MAP[fieldType] || fieldType;
|
|
159
|
+
output += ` ${fieldName}: ${tsType};\n`;
|
|
160
|
+
}
|
|
161
|
+
output += `}\n\n`;
|
|
162
|
+
|
|
163
|
+
// LoginParams interface
|
|
164
|
+
output += `export interface LoginParams {\n`;
|
|
165
|
+
for (const [paramName, paramType] of Object.entries(auth.loginParams)) {
|
|
166
|
+
const tsType = PARAM_TYPE_MAP[paramType] || paramType;
|
|
167
|
+
output += ` ${paramName}: ${tsType};\n`;
|
|
168
|
+
}
|
|
169
|
+
output += `}\n\n`;
|
|
170
|
+
|
|
171
|
+
// LoginResult interface
|
|
172
|
+
output += `export interface LoginResult extends AuthUser {\n`;
|
|
173
|
+
output += ` token: string;\n`;
|
|
174
|
+
output += `}\n\n`;
|
|
175
|
+
|
|
176
|
+
// RegisterParams interface
|
|
177
|
+
output += `export interface RegisterParams {\n`;
|
|
178
|
+
for (const [paramName, paramType] of Object.entries(auth.registerParams)) {
|
|
179
|
+
const tsType = PARAM_TYPE_MAP[paramType] || paramType;
|
|
180
|
+
output += ` ${paramName}: ${tsType};\n`;
|
|
181
|
+
}
|
|
182
|
+
output += `}\n\n`;
|
|
183
|
+
|
|
184
|
+
// RegisterResult interface
|
|
185
|
+
output += `export interface RegisterResult extends AuthUser {\n`;
|
|
186
|
+
output += ` token: string;\n`;
|
|
187
|
+
output += `}\n\n`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// --- Custom Function Types ---
|
|
191
|
+
if (customFunctions) {
|
|
192
|
+
for (const fn of customFunctions) {
|
|
193
|
+
const pascalName = toPascalCase(fn.name);
|
|
194
|
+
|
|
195
|
+
// Generate params interface if params are defined
|
|
196
|
+
if (fn.params && Object.keys(fn.params).length > 0) {
|
|
197
|
+
output += `export interface ${pascalName}Params {\n`;
|
|
198
|
+
for (const [paramName, paramType] of Object.entries(fn.params)) {
|
|
199
|
+
const tsType = PARAM_TYPE_MAP[paramType] || paramType;
|
|
200
|
+
output += ` ${paramName}: ${tsType};\n`;
|
|
201
|
+
}
|
|
202
|
+
output += `}\n\n`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Generate result interface if returns is an object
|
|
206
|
+
if (fn.returns && typeof fn.returns === 'object') {
|
|
207
|
+
output += `export interface ${pascalName}Result {\n`;
|
|
208
|
+
for (const [fieldName, fieldType] of Object.entries(fn.returns)) {
|
|
209
|
+
const tsType = PARAM_TYPE_MAP[fieldType] || fieldType;
|
|
210
|
+
output += ` ${fieldName}: ${tsType};\n`;
|
|
211
|
+
}
|
|
212
|
+
output += `}\n\n`;
|
|
213
|
+
}
|
|
120
214
|
}
|
|
121
215
|
}
|
|
122
216
|
|
package/src/cli/compiler/ir.ts
CHANGED
|
@@ -12,7 +12,8 @@ import type {
|
|
|
12
12
|
IncludeIR,
|
|
13
13
|
CustomFunctionIR,
|
|
14
14
|
ManyToManyIR,
|
|
15
|
-
GraphRuleIR
|
|
15
|
+
GraphRuleIR,
|
|
16
|
+
AuthIR
|
|
16
17
|
} from "../../shared/ir.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -175,6 +176,9 @@ export function generateIR(domain: DomainConfig): DomainIR {
|
|
|
175
176
|
};
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
// Parse includes (FK expansions)
|
|
180
|
+
const includes = parseIncludes(config.includes as Record<string, string | IncludeConfig> | undefined);
|
|
181
|
+
|
|
178
182
|
entities[name] = {
|
|
179
183
|
name,
|
|
180
184
|
table: name,
|
|
@@ -187,6 +191,7 @@ export function generateIR(domain: DomainConfig): DomainIR {
|
|
|
187
191
|
fieldDefaults: config.fieldDefaults || {},
|
|
188
192
|
permissions,
|
|
189
193
|
relationships: {},
|
|
194
|
+
includes,
|
|
190
195
|
manyToMany,
|
|
191
196
|
graphRules: {
|
|
192
197
|
onCreate: onCreateRules,
|
|
@@ -220,14 +225,68 @@ export function generateIR(domain: DomainConfig): DomainIR {
|
|
|
220
225
|
customFunctions.push({
|
|
221
226
|
name: fn.name,
|
|
222
227
|
sql: fn.sql,
|
|
223
|
-
args: fn.args || ['p_user_id', 'p_params']
|
|
228
|
+
args: fn.args || ['p_user_id', 'p_params'],
|
|
229
|
+
params: fn.params,
|
|
230
|
+
returns: fn.returns
|
|
224
231
|
});
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
234
|
|
|
235
|
+
// --- AUTH CONFIG ---
|
|
236
|
+
// Generate default auth config based on users entity if not provided
|
|
237
|
+
let auth: AuthIR | undefined;
|
|
238
|
+
|
|
239
|
+
if (domain.auth) {
|
|
240
|
+
// Use explicit auth config
|
|
241
|
+
auth = {
|
|
242
|
+
userFields: domain.auth.userFields || {},
|
|
243
|
+
loginParams: domain.auth.loginParams || { email: 'string', password: 'string' },
|
|
244
|
+
registerParams: domain.auth.registerParams || { email: 'string', password: 'string' }
|
|
245
|
+
};
|
|
246
|
+
} else if (entities['users']) {
|
|
247
|
+
// Derive auth config from users entity
|
|
248
|
+
const usersEntity = entities['users'];
|
|
249
|
+
const hiddenFields = new Set(usersEntity.hidden || []);
|
|
250
|
+
hiddenFields.add('password_hash');
|
|
251
|
+
hiddenFields.add('password');
|
|
252
|
+
|
|
253
|
+
// Map user columns to TypeScript types (excluding hidden fields)
|
|
254
|
+
const userFields: Record<string, string> = {
|
|
255
|
+
user_id: 'number' // Always include user_id
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
for (const col of usersEntity.columns) {
|
|
259
|
+
if (!hiddenFields.has(col.name) && col.name !== 'id') {
|
|
260
|
+
userFields[col.name] = mapPgTypeToTsType(col.type);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
auth = {
|
|
265
|
+
userFields,
|
|
266
|
+
loginParams: { email: 'string', password: 'string' },
|
|
267
|
+
registerParams: { email: 'string', password: 'string' }
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
228
271
|
return {
|
|
229
272
|
entities,
|
|
230
273
|
subscribables,
|
|
231
|
-
customFunctions
|
|
274
|
+
customFunctions,
|
|
275
|
+
auth
|
|
232
276
|
};
|
|
233
277
|
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Maps PostgreSQL column type to TypeScript type string
|
|
281
|
+
*/
|
|
282
|
+
function mapPgTypeToTsType(pgType: string): string {
|
|
283
|
+
const lower = pgType.toLowerCase();
|
|
284
|
+
if (lower.includes('int') || lower.includes('serial') || lower.includes('decimal') || lower.includes('numeric')) {
|
|
285
|
+
return 'number';
|
|
286
|
+
}
|
|
287
|
+
if (lower.includes('bool')) {
|
|
288
|
+
return 'boolean';
|
|
289
|
+
}
|
|
290
|
+
// Default to string for text, timestamps, etc.
|
|
291
|
+
return 'string';
|
|
292
|
+
}
|
package/src/shared/ir.ts
CHANGED
|
@@ -100,6 +100,20 @@ export interface CustomFunctionConfig {
|
|
|
100
100
|
name: string;
|
|
101
101
|
sql: string;
|
|
102
102
|
args?: string[];
|
|
103
|
+
/** Parameter types for TypeScript generation: { paramName: 'number' | 'string' | ... } */
|
|
104
|
+
params?: Record<string, string>;
|
|
105
|
+
/** Return type: either an object shape or a scalar type name */
|
|
106
|
+
returns?: Record<string, string> | string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Auth configuration for TypeScript type generation */
|
|
110
|
+
export interface AuthConfig {
|
|
111
|
+
/** Fields returned by auth functions (user profile shape) */
|
|
112
|
+
userFields?: Record<string, string>;
|
|
113
|
+
/** Login function parameter types */
|
|
114
|
+
loginParams?: Record<string, string>;
|
|
115
|
+
/** Register function parameter types */
|
|
116
|
+
registerParams?: Record<string, string>;
|
|
103
117
|
}
|
|
104
118
|
|
|
105
119
|
/** Complete domain configuration as provided in domain file */
|
|
@@ -107,6 +121,7 @@ export interface DomainConfig {
|
|
|
107
121
|
entities: Record<string, EntityConfig>;
|
|
108
122
|
subscribables?: Record<string, SubscribableConfig>;
|
|
109
123
|
customFunctions?: CustomFunctionConfig[];
|
|
124
|
+
auth?: AuthConfig;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
// ============================================
|
|
@@ -130,6 +145,7 @@ export interface EntityIR {
|
|
|
130
145
|
delete: string[];
|
|
131
146
|
};
|
|
132
147
|
relationships: Record<string, RelationshipIR>;
|
|
148
|
+
includes: Record<string, IncludeIR>; // FK expansions (e.g., author: users)
|
|
133
149
|
manyToMany: Record<string, ManyToManyIR>;
|
|
134
150
|
graphRules: {
|
|
135
151
|
onCreate: GraphRuleIR[];
|
|
@@ -190,10 +206,25 @@ export interface CustomFunctionIR {
|
|
|
190
206
|
name: string;
|
|
191
207
|
sql: string;
|
|
192
208
|
args?: string[]; // For manifest allowlist
|
|
209
|
+
/** Parameter types for TypeScript generation */
|
|
210
|
+
params?: Record<string, string>;
|
|
211
|
+
/** Return type: either an object shape or a scalar type name */
|
|
212
|
+
returns?: Record<string, string> | string;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Auth IR for TypeScript type generation */
|
|
216
|
+
export interface AuthIR {
|
|
217
|
+
/** Fields returned by auth functions (user profile shape) */
|
|
218
|
+
userFields: Record<string, string>;
|
|
219
|
+
/** Login function parameter types */
|
|
220
|
+
loginParams: Record<string, string>;
|
|
221
|
+
/** Register function parameter types */
|
|
222
|
+
registerParams: Record<string, string>;
|
|
193
223
|
}
|
|
194
224
|
|
|
195
225
|
export interface DomainIR {
|
|
196
226
|
entities: Record<string, EntityIR>;
|
|
197
227
|
subscribables: Record<string, SubscribableIR>;
|
|
198
228
|
customFunctions: CustomFunctionIR[];
|
|
229
|
+
auth?: AuthIR;
|
|
199
230
|
}
|