node-type-registry 0.5.1 → 0.6.0
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.
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* when building blueprint JSON. The API itself accepts plain JSONB.
|
|
16
16
|
*
|
|
17
17
|
* Usage:
|
|
18
|
-
* npx ts-node src/codegen/generate-types.ts [--outdir <dir>]
|
|
18
|
+
* npx ts-node src/codegen/generate-types.ts [--outdir <dir>] [--meta <path>]
|
|
19
19
|
*/
|
|
20
20
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
21
|
if (k2 === undefined) k2 = k;
|
|
@@ -165,9 +165,79 @@ function generateParamsInterfaces(nodeTypes) {
|
|
|
165
165
|
return results;
|
|
166
166
|
}
|
|
167
167
|
// ---------------------------------------------------------------------------
|
|
168
|
-
//
|
|
168
|
+
// Introspection-driven structural type derivation
|
|
169
169
|
// ---------------------------------------------------------------------------
|
|
170
|
-
|
|
170
|
+
/** Map PostgreSQL type names to TypeScript type annotations. */
|
|
171
|
+
function pgTypeToTSType(pgType, isArray) {
|
|
172
|
+
let base;
|
|
173
|
+
switch (pgType) {
|
|
174
|
+
case 'bool':
|
|
175
|
+
case 'boolean':
|
|
176
|
+
base = t.tsBooleanKeyword();
|
|
177
|
+
break;
|
|
178
|
+
case 'int2':
|
|
179
|
+
case 'int4':
|
|
180
|
+
case 'int8':
|
|
181
|
+
case 'integer':
|
|
182
|
+
case 'smallint':
|
|
183
|
+
case 'bigint':
|
|
184
|
+
case 'float4':
|
|
185
|
+
case 'float8':
|
|
186
|
+
case 'float':
|
|
187
|
+
case 'double precision':
|
|
188
|
+
case 'numeric':
|
|
189
|
+
case 'real':
|
|
190
|
+
base = t.tsNumberKeyword();
|
|
191
|
+
break;
|
|
192
|
+
case 'jsonb':
|
|
193
|
+
case 'json':
|
|
194
|
+
base = recordType(t.tsStringKeyword(), t.tsUnknownKeyword());
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
base = t.tsStringKeyword();
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
return isArray ? t.tsArrayType(base) : base;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Derive a TS interface from an introspection table's columns.
|
|
204
|
+
*
|
|
205
|
+
* Fully procedural — uses field metadata to determine:
|
|
206
|
+
* - Which columns to skip (isPrimaryKey || isForeignKey)
|
|
207
|
+
* - Required vs optional (isNotNull && !hasDefault)
|
|
208
|
+
* - JSDoc from PG column comments (description)
|
|
209
|
+
*
|
|
210
|
+
* Special-case overrides (e.g. typed discriminants) can be passed via
|
|
211
|
+
* `typeOverrides` for columns that need a non-default TS type.
|
|
212
|
+
*/
|
|
213
|
+
function deriveInterfaceFromTable(table, interfaceName, description, typeOverrides) {
|
|
214
|
+
const members = [];
|
|
215
|
+
for (const field of table.fields) {
|
|
216
|
+
// Skip PK and FK columns — these are internal to the DB schema
|
|
217
|
+
if (field.isPrimaryKey || field.isForeignKey)
|
|
218
|
+
continue;
|
|
219
|
+
const tsType = typeOverrides?.[field.name] ?? pgTypeToTSType(field.type.pgType, field.type.isArray);
|
|
220
|
+
const doc = field.description ?? `${table.schemaName}.${table.name}.${field.name} (${field.type.pgType})`;
|
|
221
|
+
const isRequired = field.isNotNull && !field.hasDefault;
|
|
222
|
+
const prop = isRequired
|
|
223
|
+
? requiredProp(field.name, tsType)
|
|
224
|
+
: optionalProp(field.name, tsType);
|
|
225
|
+
members.push(addJSDoc(prop, doc));
|
|
226
|
+
}
|
|
227
|
+
return addJSDoc(exportInterface(interfaceName, members), description);
|
|
228
|
+
}
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
// Structural types — introspection-driven or static fallback
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
function findTable(tables, schemaName, tableName) {
|
|
233
|
+
return tables.find((tbl) => tbl.schemaName === schemaName && tbl.name === tableName);
|
|
234
|
+
}
|
|
235
|
+
function buildBlueprintField(meta) {
|
|
236
|
+
const table = meta && findTable(meta, 'metaschema_public', 'field');
|
|
237
|
+
if (table) {
|
|
238
|
+
return deriveInterfaceFromTable(table, 'BlueprintField', 'A custom field (column) to add to a blueprint table. Derived from _meta.');
|
|
239
|
+
}
|
|
240
|
+
// Static fallback
|
|
171
241
|
return addJSDoc(exportInterface('BlueprintField', [
|
|
172
242
|
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'The column name.'),
|
|
173
243
|
addJSDoc(requiredProp('type', t.tsStringKeyword()), 'The PostgreSQL type (e.g., "text", "integer", "boolean", "uuid").'),
|
|
@@ -176,12 +246,22 @@ function buildBlueprintField() {
|
|
|
176
246
|
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Comment/description for this field.'),
|
|
177
247
|
]), 'A custom field (column) to add to a blueprint table.');
|
|
178
248
|
}
|
|
179
|
-
function buildBlueprintPolicy(authzNodes) {
|
|
249
|
+
function buildBlueprintPolicy(authzNodes, meta) {
|
|
180
250
|
const policyTypeAnnotation = authzNodes.length > 0
|
|
181
251
|
? strUnion(authzNodes.map((nt) => nt.name))
|
|
182
252
|
: t.tsStringKeyword();
|
|
253
|
+
const table = meta && findTable(meta, 'metaschema_public', 'policy');
|
|
254
|
+
if (table) {
|
|
255
|
+
return deriveInterfaceFromTable(table, 'BlueprintPolicy', 'An RLS policy entry for a blueprint table. Derived from _meta.', {
|
|
256
|
+
// policy_type gets a typed union of known Authz* node names
|
|
257
|
+
policy_type: policyTypeAnnotation,
|
|
258
|
+
// data is untyped JSONB — use Record<string, unknown>
|
|
259
|
+
data: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// Static fallback
|
|
183
263
|
return addJSDoc(exportInterface('BlueprintPolicy', [
|
|
184
|
-
addJSDoc(requiredProp('
|
|
264
|
+
addJSDoc(requiredProp('policy_type', policyTypeAnnotation), 'Authz* policy type name (e.g., "AuthzDirectOwner", "AuthzAllowAll").'),
|
|
185
265
|
addJSDoc(optionalProp('policy_role', t.tsStringKeyword()), 'Role for this policy. Defaults to "authenticated".'),
|
|
186
266
|
addJSDoc(optionalProp('permissive', t.tsBooleanKeyword()), 'Whether this policy is permissive (true) or restrictive (false).'),
|
|
187
267
|
addJSDoc(optionalProp('policy_name', t.tsStringKeyword()), 'Optional custom name for this policy.'),
|
|
@@ -203,11 +283,21 @@ function buildBlueprintFullTextSearch() {
|
|
|
203
283
|
addJSDoc(requiredProp('sources', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFtsSource')))), 'Source fields that feed into this tsvector.'),
|
|
204
284
|
]), 'A full-text search configuration for a blueprint table.');
|
|
205
285
|
}
|
|
206
|
-
function buildBlueprintIndex() {
|
|
286
|
+
function buildBlueprintIndex(meta) {
|
|
287
|
+
const table = meta && findTable(meta, 'metaschema_public', 'index');
|
|
288
|
+
if (table) {
|
|
289
|
+
return deriveInterfaceFromTable(table, 'BlueprintIndex', 'An index definition within a blueprint. Derived from _meta.', {
|
|
290
|
+
// JSONB columns get Record<string, unknown> instead of the default
|
|
291
|
+
index_params: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
292
|
+
where_clause: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
293
|
+
options: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
// Static fallback
|
|
207
297
|
return addJSDoc(exportInterface('BlueprintIndex', [
|
|
208
298
|
addJSDoc(requiredProp('table_ref', t.tsStringKeyword()), 'Reference key of the table this index belongs to.'),
|
|
209
|
-
addJSDoc(optionalProp('column', t.tsStringKeyword()), 'Single column name for the index.
|
|
210
|
-
addJSDoc(optionalProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Array of column names for a multi-column index.
|
|
299
|
+
addJSDoc(optionalProp('column', t.tsStringKeyword()), 'Single column name for the index.'),
|
|
300
|
+
addJSDoc(optionalProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Array of column names for a multi-column index.'),
|
|
211
301
|
addJSDoc(requiredProp('access_method', t.tsStringKeyword()), 'Index access method (e.g., "BTREE", "GIN", "GIST", "HNSW", "BM25").'),
|
|
212
302
|
addJSDoc(optionalProp('is_unique', t.tsBooleanKeyword()), 'Whether this is a unique index.'),
|
|
213
303
|
addJSDoc(optionalProp('name', t.tsStringKeyword()), 'Optional custom name for the index.'),
|
|
@@ -300,7 +390,7 @@ function sectionComment(title) {
|
|
|
300
390
|
// ---------------------------------------------------------------------------
|
|
301
391
|
// Main generator
|
|
302
392
|
// ---------------------------------------------------------------------------
|
|
303
|
-
function buildProgram() {
|
|
393
|
+
function buildProgram(meta) {
|
|
304
394
|
const statements = [];
|
|
305
395
|
// Group node types by category
|
|
306
396
|
const categories = new Map();
|
|
@@ -321,13 +411,16 @@ function buildProgram() {
|
|
|
321
411
|
statements.push(sectionComment(`${cat.charAt(0).toUpperCase() + cat.slice(1)} node type parameters`));
|
|
322
412
|
statements.push(...generateParamsInterfaces(nts));
|
|
323
413
|
}
|
|
324
|
-
// --
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
414
|
+
// -- Structural types (_meta-driven when available, static fallback otherwise) --
|
|
415
|
+
const metaSource = meta
|
|
416
|
+
? 'Derived from _meta'
|
|
417
|
+
: 'Static fallback (no _meta provided)';
|
|
418
|
+
statements.push(sectionComment(`Structural types — ${metaSource}`));
|
|
419
|
+
statements.push(buildBlueprintField(meta));
|
|
420
|
+
statements.push(buildBlueprintPolicy(authzNodes, meta));
|
|
328
421
|
statements.push(buildBlueprintFtsSource());
|
|
329
422
|
statements.push(buildBlueprintFullTextSearch());
|
|
330
|
-
statements.push(buildBlueprintIndex());
|
|
423
|
+
statements.push(buildBlueprintIndex(meta));
|
|
331
424
|
// -- Node types discriminated union --
|
|
332
425
|
statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
|
|
333
426
|
statements.push(...buildNodeTypes(dataNodes));
|
|
@@ -362,7 +455,26 @@ function main() {
|
|
|
362
455
|
const args = process.argv.slice(2);
|
|
363
456
|
const outdirIdx = args.indexOf('--outdir');
|
|
364
457
|
const outdir = outdirIdx !== -1 ? args[outdirIdx + 1] : (0, path_1.join)(__dirname, '..');
|
|
365
|
-
const
|
|
458
|
+
const metaIdx = args.indexOf('--meta');
|
|
459
|
+
let meta;
|
|
460
|
+
if (metaIdx !== -1 && args[metaIdx + 1]) {
|
|
461
|
+
const metaPath = args[metaIdx + 1];
|
|
462
|
+
console.log(`Reading _meta from ${metaPath}`);
|
|
463
|
+
const raw = (0, fs_1.readFileSync)(metaPath, 'utf-8');
|
|
464
|
+
const parsed = JSON.parse(raw);
|
|
465
|
+
// Accept both { tables: [...] } (GQL query result) and raw [...] (array)
|
|
466
|
+
meta = (Array.isArray(parsed) ? parsed : parsed?.tables ?? parsed?.data?._meta?.tables);
|
|
467
|
+
if (meta) {
|
|
468
|
+
console.log(`Loaded ${meta.length} tables from _meta`);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
console.log('Could not find tables in _meta JSON; using static fallback types');
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
console.log('No --meta flag; using static fallback types');
|
|
476
|
+
}
|
|
477
|
+
const content = buildProgram(meta);
|
|
366
478
|
const filename = 'blueprint-types.generated.ts';
|
|
367
479
|
const filepath = (0, path_1.join)(outdir, filename);
|
|
368
480
|
if (!(0, fs_1.existsSync)(outdir))
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
* when building blueprint JSON. The API itself accepts plain JSONB.
|
|
15
15
|
*
|
|
16
16
|
* Usage:
|
|
17
|
-
* npx ts-node src/codegen/generate-types.ts [--outdir <dir>]
|
|
17
|
+
* npx ts-node src/codegen/generate-types.ts [--outdir <dir>] [--meta <path>]
|
|
18
18
|
*/
|
|
19
19
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
20
20
|
const generate = require('@babel/generator').default ?? require('@babel/generator');
|
|
21
21
|
import * as t from '@babel/types';
|
|
22
|
-
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
22
|
+
import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'fs';
|
|
23
23
|
import { join } from 'path';
|
|
24
24
|
import { generateTypeScriptTypes } from 'schema-typescript';
|
|
25
25
|
import { allNodeTypes } from '../index';
|
|
@@ -130,9 +130,79 @@ function generateParamsInterfaces(nodeTypes) {
|
|
|
130
130
|
return results;
|
|
131
131
|
}
|
|
132
132
|
// ---------------------------------------------------------------------------
|
|
133
|
-
//
|
|
133
|
+
// Introspection-driven structural type derivation
|
|
134
134
|
// ---------------------------------------------------------------------------
|
|
135
|
-
|
|
135
|
+
/** Map PostgreSQL type names to TypeScript type annotations. */
|
|
136
|
+
function pgTypeToTSType(pgType, isArray) {
|
|
137
|
+
let base;
|
|
138
|
+
switch (pgType) {
|
|
139
|
+
case 'bool':
|
|
140
|
+
case 'boolean':
|
|
141
|
+
base = t.tsBooleanKeyword();
|
|
142
|
+
break;
|
|
143
|
+
case 'int2':
|
|
144
|
+
case 'int4':
|
|
145
|
+
case 'int8':
|
|
146
|
+
case 'integer':
|
|
147
|
+
case 'smallint':
|
|
148
|
+
case 'bigint':
|
|
149
|
+
case 'float4':
|
|
150
|
+
case 'float8':
|
|
151
|
+
case 'float':
|
|
152
|
+
case 'double precision':
|
|
153
|
+
case 'numeric':
|
|
154
|
+
case 'real':
|
|
155
|
+
base = t.tsNumberKeyword();
|
|
156
|
+
break;
|
|
157
|
+
case 'jsonb':
|
|
158
|
+
case 'json':
|
|
159
|
+
base = recordType(t.tsStringKeyword(), t.tsUnknownKeyword());
|
|
160
|
+
break;
|
|
161
|
+
default:
|
|
162
|
+
base = t.tsStringKeyword();
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
return isArray ? t.tsArrayType(base) : base;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Derive a TS interface from an introspection table's columns.
|
|
169
|
+
*
|
|
170
|
+
* Fully procedural — uses field metadata to determine:
|
|
171
|
+
* - Which columns to skip (isPrimaryKey || isForeignKey)
|
|
172
|
+
* - Required vs optional (isNotNull && !hasDefault)
|
|
173
|
+
* - JSDoc from PG column comments (description)
|
|
174
|
+
*
|
|
175
|
+
* Special-case overrides (e.g. typed discriminants) can be passed via
|
|
176
|
+
* `typeOverrides` for columns that need a non-default TS type.
|
|
177
|
+
*/
|
|
178
|
+
function deriveInterfaceFromTable(table, interfaceName, description, typeOverrides) {
|
|
179
|
+
const members = [];
|
|
180
|
+
for (const field of table.fields) {
|
|
181
|
+
// Skip PK and FK columns — these are internal to the DB schema
|
|
182
|
+
if (field.isPrimaryKey || field.isForeignKey)
|
|
183
|
+
continue;
|
|
184
|
+
const tsType = typeOverrides?.[field.name] ?? pgTypeToTSType(field.type.pgType, field.type.isArray);
|
|
185
|
+
const doc = field.description ?? `${table.schemaName}.${table.name}.${field.name} (${field.type.pgType})`;
|
|
186
|
+
const isRequired = field.isNotNull && !field.hasDefault;
|
|
187
|
+
const prop = isRequired
|
|
188
|
+
? requiredProp(field.name, tsType)
|
|
189
|
+
: optionalProp(field.name, tsType);
|
|
190
|
+
members.push(addJSDoc(prop, doc));
|
|
191
|
+
}
|
|
192
|
+
return addJSDoc(exportInterface(interfaceName, members), description);
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Structural types — introspection-driven or static fallback
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
function findTable(tables, schemaName, tableName) {
|
|
198
|
+
return tables.find((tbl) => tbl.schemaName === schemaName && tbl.name === tableName);
|
|
199
|
+
}
|
|
200
|
+
function buildBlueprintField(meta) {
|
|
201
|
+
const table = meta && findTable(meta, 'metaschema_public', 'field');
|
|
202
|
+
if (table) {
|
|
203
|
+
return deriveInterfaceFromTable(table, 'BlueprintField', 'A custom field (column) to add to a blueprint table. Derived from _meta.');
|
|
204
|
+
}
|
|
205
|
+
// Static fallback
|
|
136
206
|
return addJSDoc(exportInterface('BlueprintField', [
|
|
137
207
|
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'The column name.'),
|
|
138
208
|
addJSDoc(requiredProp('type', t.tsStringKeyword()), 'The PostgreSQL type (e.g., "text", "integer", "boolean", "uuid").'),
|
|
@@ -141,12 +211,22 @@ function buildBlueprintField() {
|
|
|
141
211
|
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Comment/description for this field.'),
|
|
142
212
|
]), 'A custom field (column) to add to a blueprint table.');
|
|
143
213
|
}
|
|
144
|
-
function buildBlueprintPolicy(authzNodes) {
|
|
214
|
+
function buildBlueprintPolicy(authzNodes, meta) {
|
|
145
215
|
const policyTypeAnnotation = authzNodes.length > 0
|
|
146
216
|
? strUnion(authzNodes.map((nt) => nt.name))
|
|
147
217
|
: t.tsStringKeyword();
|
|
218
|
+
const table = meta && findTable(meta, 'metaschema_public', 'policy');
|
|
219
|
+
if (table) {
|
|
220
|
+
return deriveInterfaceFromTable(table, 'BlueprintPolicy', 'An RLS policy entry for a blueprint table. Derived from _meta.', {
|
|
221
|
+
// policy_type gets a typed union of known Authz* node names
|
|
222
|
+
policy_type: policyTypeAnnotation,
|
|
223
|
+
// data is untyped JSONB — use Record<string, unknown>
|
|
224
|
+
data: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
// Static fallback
|
|
148
228
|
return addJSDoc(exportInterface('BlueprintPolicy', [
|
|
149
|
-
addJSDoc(requiredProp('
|
|
229
|
+
addJSDoc(requiredProp('policy_type', policyTypeAnnotation), 'Authz* policy type name (e.g., "AuthzDirectOwner", "AuthzAllowAll").'),
|
|
150
230
|
addJSDoc(optionalProp('policy_role', t.tsStringKeyword()), 'Role for this policy. Defaults to "authenticated".'),
|
|
151
231
|
addJSDoc(optionalProp('permissive', t.tsBooleanKeyword()), 'Whether this policy is permissive (true) or restrictive (false).'),
|
|
152
232
|
addJSDoc(optionalProp('policy_name', t.tsStringKeyword()), 'Optional custom name for this policy.'),
|
|
@@ -168,11 +248,21 @@ function buildBlueprintFullTextSearch() {
|
|
|
168
248
|
addJSDoc(requiredProp('sources', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFtsSource')))), 'Source fields that feed into this tsvector.'),
|
|
169
249
|
]), 'A full-text search configuration for a blueprint table.');
|
|
170
250
|
}
|
|
171
|
-
function buildBlueprintIndex() {
|
|
251
|
+
function buildBlueprintIndex(meta) {
|
|
252
|
+
const table = meta && findTable(meta, 'metaschema_public', 'index');
|
|
253
|
+
if (table) {
|
|
254
|
+
return deriveInterfaceFromTable(table, 'BlueprintIndex', 'An index definition within a blueprint. Derived from _meta.', {
|
|
255
|
+
// JSONB columns get Record<string, unknown> instead of the default
|
|
256
|
+
index_params: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
257
|
+
where_clause: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
258
|
+
options: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// Static fallback
|
|
172
262
|
return addJSDoc(exportInterface('BlueprintIndex', [
|
|
173
263
|
addJSDoc(requiredProp('table_ref', t.tsStringKeyword()), 'Reference key of the table this index belongs to.'),
|
|
174
|
-
addJSDoc(optionalProp('column', t.tsStringKeyword()), 'Single column name for the index.
|
|
175
|
-
addJSDoc(optionalProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Array of column names for a multi-column index.
|
|
264
|
+
addJSDoc(optionalProp('column', t.tsStringKeyword()), 'Single column name for the index.'),
|
|
265
|
+
addJSDoc(optionalProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Array of column names for a multi-column index.'),
|
|
176
266
|
addJSDoc(requiredProp('access_method', t.tsStringKeyword()), 'Index access method (e.g., "BTREE", "GIN", "GIST", "HNSW", "BM25").'),
|
|
177
267
|
addJSDoc(optionalProp('is_unique', t.tsBooleanKeyword()), 'Whether this is a unique index.'),
|
|
178
268
|
addJSDoc(optionalProp('name', t.tsStringKeyword()), 'Optional custom name for the index.'),
|
|
@@ -265,7 +355,7 @@ function sectionComment(title) {
|
|
|
265
355
|
// ---------------------------------------------------------------------------
|
|
266
356
|
// Main generator
|
|
267
357
|
// ---------------------------------------------------------------------------
|
|
268
|
-
function buildProgram() {
|
|
358
|
+
function buildProgram(meta) {
|
|
269
359
|
const statements = [];
|
|
270
360
|
// Group node types by category
|
|
271
361
|
const categories = new Map();
|
|
@@ -286,13 +376,16 @@ function buildProgram() {
|
|
|
286
376
|
statements.push(sectionComment(`${cat.charAt(0).toUpperCase() + cat.slice(1)} node type parameters`));
|
|
287
377
|
statements.push(...generateParamsInterfaces(nts));
|
|
288
378
|
}
|
|
289
|
-
// --
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
379
|
+
// -- Structural types (_meta-driven when available, static fallback otherwise) --
|
|
380
|
+
const metaSource = meta
|
|
381
|
+
? 'Derived from _meta'
|
|
382
|
+
: 'Static fallback (no _meta provided)';
|
|
383
|
+
statements.push(sectionComment(`Structural types — ${metaSource}`));
|
|
384
|
+
statements.push(buildBlueprintField(meta));
|
|
385
|
+
statements.push(buildBlueprintPolicy(authzNodes, meta));
|
|
293
386
|
statements.push(buildBlueprintFtsSource());
|
|
294
387
|
statements.push(buildBlueprintFullTextSearch());
|
|
295
|
-
statements.push(buildBlueprintIndex());
|
|
388
|
+
statements.push(buildBlueprintIndex(meta));
|
|
296
389
|
// -- Node types discriminated union --
|
|
297
390
|
statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
|
|
298
391
|
statements.push(...buildNodeTypes(dataNodes));
|
|
@@ -327,7 +420,26 @@ function main() {
|
|
|
327
420
|
const args = process.argv.slice(2);
|
|
328
421
|
const outdirIdx = args.indexOf('--outdir');
|
|
329
422
|
const outdir = outdirIdx !== -1 ? args[outdirIdx + 1] : join(__dirname, '..');
|
|
330
|
-
const
|
|
423
|
+
const metaIdx = args.indexOf('--meta');
|
|
424
|
+
let meta;
|
|
425
|
+
if (metaIdx !== -1 && args[metaIdx + 1]) {
|
|
426
|
+
const metaPath = args[metaIdx + 1];
|
|
427
|
+
console.log(`Reading _meta from ${metaPath}`);
|
|
428
|
+
const raw = readFileSync(metaPath, 'utf-8');
|
|
429
|
+
const parsed = JSON.parse(raw);
|
|
430
|
+
// Accept both { tables: [...] } (GQL query result) and raw [...] (array)
|
|
431
|
+
meta = (Array.isArray(parsed) ? parsed : parsed?.tables ?? parsed?.data?._meta?.tables);
|
|
432
|
+
if (meta) {
|
|
433
|
+
console.log(`Loaded ${meta.length} tables from _meta`);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
console.log('Could not find tables in _meta JSON; using static fallback types');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
console.log('No --meta flag; using static fallback types');
|
|
441
|
+
}
|
|
442
|
+
const content = buildProgram(meta);
|
|
331
443
|
const filename = 'blueprint-types.generated.ts';
|
|
332
444
|
const filepath = join(outdir, filename);
|
|
333
445
|
if (!existsSync(outdir))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-type-registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Node type definitions for the Constructive blueprint system. Single source of truth for all Authz*, Data*, Relation*, and View* node types.",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"main": "index.js",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"@babel/generator": "^7.29.0",
|
|
35
35
|
"@babel/types": "^7.29.0",
|
|
36
36
|
"@pgsql/types": "^17.6.2",
|
|
37
|
-
"@pgsql/utils": "^17.8.
|
|
38
|
-
"pgsql-deparser": "^17.18.
|
|
39
|
-
"schema-typescript": "^0.14.
|
|
37
|
+
"@pgsql/utils": "^17.8.15",
|
|
38
|
+
"pgsql-deparser": "^17.18.2",
|
|
39
|
+
"schema-typescript": "^0.14.3"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"makage": "^0.
|
|
42
|
+
"makage": "^0.3.0"
|
|
43
43
|
},
|
|
44
44
|
"keywords": [
|
|
45
45
|
"constructive",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"registry",
|
|
49
49
|
"graphile"
|
|
50
50
|
],
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "baae875effd00af36577612c861d0f6f9a1a792a"
|
|
52
52
|
}
|