node-type-registry 0.17.0 → 0.18.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.
- package/blueprint-types.generated.d.ts +150 -96
- package/blueprint-types.generated.js +1 -0
- package/codegen/generate-types.js +82 -27
- package/esm/blueprint-types.generated.d.ts +150 -96
- package/esm/blueprint-types.generated.js +1 -0
- package/esm/codegen/generate-types.js +83 -28
- package/esm/relation/relation-many-to-many.js +20 -33
- package/package.json +2 -2
- package/relation/relation-many-to-many.js +20 -33
|
@@ -19,7 +19,7 @@
|
|
|
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 {
|
|
22
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
23
23
|
import { join } from 'path';
|
|
24
24
|
import { generateTypeScriptTypes } from 'schema-typescript';
|
|
25
25
|
import { allNodeTypes } from '../index';
|
|
@@ -29,7 +29,7 @@ import { allNodeTypes } from '../index';
|
|
|
29
29
|
/** Attach a JSDoc-style leading comment to an AST node. */
|
|
30
30
|
function addJSDoc(node, description) {
|
|
31
31
|
node.leadingComments = [
|
|
32
|
-
{ type: 'CommentBlock', value: `* ${description} ` }
|
|
32
|
+
{ type: 'CommentBlock', value: `* ${description} ` }
|
|
33
33
|
];
|
|
34
34
|
return node;
|
|
35
35
|
}
|
|
@@ -112,7 +112,7 @@ function generateParamsInterfaces(nodeTypes) {
|
|
|
112
112
|
const astNodes = generateTypeScriptTypes(sanitized, {
|
|
113
113
|
includePropertyComments: true,
|
|
114
114
|
includeTypeComments: false,
|
|
115
|
-
strictTypeSafety: true
|
|
115
|
+
strictTypeSafety: true
|
|
116
116
|
});
|
|
117
117
|
if (astNodes.length > 0) {
|
|
118
118
|
// The last node is the main interface for the title
|
|
@@ -208,7 +208,7 @@ function buildBlueprintField(meta) {
|
|
|
208
208
|
addJSDoc(requiredProp('type', t.tsStringKeyword()), 'The PostgreSQL type (e.g., "text", "integer", "boolean", "uuid").'),
|
|
209
209
|
addJSDoc(optionalProp('is_required', t.tsBooleanKeyword()), 'Whether the column has a NOT NULL constraint.'),
|
|
210
210
|
addJSDoc(optionalProp('default_value', t.tsStringKeyword()), 'SQL default value expression (e.g., "true", "now()").'),
|
|
211
|
-
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Comment/description for this field.')
|
|
211
|
+
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Comment/description for this field.')
|
|
212
212
|
]), 'A custom field (column) to add to a blueprint table.');
|
|
213
213
|
}
|
|
214
214
|
function buildBlueprintPolicy(authzNodes, _meta) {
|
|
@@ -225,14 +225,14 @@ function buildBlueprintPolicy(authzNodes, _meta) {
|
|
|
225
225
|
addJSDoc(optionalProp('permissive', t.tsBooleanKeyword()), 'Whether this policy is permissive (true) or restrictive (false). Defaults to true.'),
|
|
226
226
|
addJSDoc(optionalProp('policy_role', t.tsStringKeyword()), 'Role for this policy. Defaults to "authenticated".'),
|
|
227
227
|
addJSDoc(optionalProp('policy_name', t.tsStringKeyword()), 'Optional custom name for this policy.'),
|
|
228
|
-
addJSDoc(optionalProp('data', recordType(t.tsStringKeyword(), t.tsUnknownKeyword())), 'Policy-specific data (structure varies by policy type).')
|
|
228
|
+
addJSDoc(optionalProp('data', recordType(t.tsStringKeyword(), t.tsUnknownKeyword())), 'Policy-specific data (structure varies by policy type).')
|
|
229
229
|
]), 'An RLS policy entry for a blueprint table. Uses $type to match the blueprint JSON convention.');
|
|
230
230
|
}
|
|
231
231
|
function buildBlueprintFtsSource() {
|
|
232
232
|
return addJSDoc(exportInterface('BlueprintFtsSource', [
|
|
233
233
|
addJSDoc(requiredProp('field', t.tsStringKeyword()), 'Column name of the source field.'),
|
|
234
234
|
addJSDoc(requiredProp('weight', t.tsStringKeyword()), 'TSVector weight: "A", "B", "C", or "D".'),
|
|
235
|
-
addJSDoc(optionalProp('lang', t.tsStringKeyword()), 'Language for text search. Defaults to "english".')
|
|
235
|
+
addJSDoc(optionalProp('lang', t.tsStringKeyword()), 'Language for text search. Defaults to "english".')
|
|
236
236
|
]), 'A source field contributing to a full-text search tsvector column.');
|
|
237
237
|
}
|
|
238
238
|
function buildBlueprintFullTextSearch() {
|
|
@@ -240,14 +240,14 @@ function buildBlueprintFullTextSearch() {
|
|
|
240
240
|
addJSDoc(requiredProp('table_name', t.tsStringKeyword()), 'Table name this full-text search belongs to.'),
|
|
241
241
|
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name for disambiguation (falls back to top-level default).'),
|
|
242
242
|
addJSDoc(requiredProp('field', t.tsStringKeyword()), 'Name of the tsvector field on the table.'),
|
|
243
|
-
addJSDoc(requiredProp('sources', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFtsSource')))), 'Source fields that feed into this tsvector.')
|
|
243
|
+
addJSDoc(requiredProp('sources', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFtsSource')))), 'Source fields that feed into this tsvector.')
|
|
244
244
|
]), 'A full-text search configuration for a blueprint table (top-level, requires table_name).');
|
|
245
245
|
}
|
|
246
246
|
function buildBlueprintTableFullTextSearch() {
|
|
247
247
|
return addJSDoc(exportInterface('BlueprintTableFullTextSearch', [
|
|
248
248
|
addJSDoc(requiredProp('field', t.tsStringKeyword()), 'Name of the tsvector field on the table.'),
|
|
249
249
|
addJSDoc(requiredProp('sources', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFtsSource')))), 'Source fields that feed into this tsvector.'),
|
|
250
|
-
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
|
|
250
|
+
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
|
|
251
251
|
]), 'A full-text search configuration nested inside a table definition (table_name not required).');
|
|
252
252
|
}
|
|
253
253
|
function buildBlueprintIndex(meta) {
|
|
@@ -257,7 +257,7 @@ function buildBlueprintIndex(meta) {
|
|
|
257
257
|
// JSONB columns get Record<string, unknown> instead of the default
|
|
258
258
|
index_params: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
259
259
|
where_clause: recordType(t.tsStringKeyword(), t.tsUnknownKeyword()),
|
|
260
|
-
options: recordType(t.tsStringKeyword(), t.tsUnknownKeyword())
|
|
260
|
+
options: recordType(t.tsStringKeyword(), t.tsUnknownKeyword())
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
263
|
// Static fallback
|
|
@@ -270,7 +270,7 @@ function buildBlueprintIndex(meta) {
|
|
|
270
270
|
addJSDoc(optionalProp('is_unique', t.tsBooleanKeyword()), 'Whether this is a unique index.'),
|
|
271
271
|
addJSDoc(optionalProp('name', t.tsStringKeyword()), 'Optional custom name for the index.'),
|
|
272
272
|
addJSDoc(optionalProp('op_classes', t.tsArrayType(t.tsStringKeyword())), 'Operator classes for the index columns.'),
|
|
273
|
-
addJSDoc(optionalProp('options', recordType(t.tsStringKeyword(), t.tsUnknownKeyword())), 'Additional index-specific options.')
|
|
273
|
+
addJSDoc(optionalProp('options', recordType(t.tsStringKeyword(), t.tsUnknownKeyword())), 'Additional index-specific options.')
|
|
274
274
|
]), 'An index definition within a blueprint (top-level, requires table_name).');
|
|
275
275
|
}
|
|
276
276
|
function buildBlueprintTableIndex() {
|
|
@@ -282,7 +282,7 @@ function buildBlueprintTableIndex() {
|
|
|
282
282
|
addJSDoc(optionalProp('name', t.tsStringKeyword()), 'Optional custom name for the index.'),
|
|
283
283
|
addJSDoc(optionalProp('op_classes', t.tsArrayType(t.tsStringKeyword())), 'Operator classes for the index columns.'),
|
|
284
284
|
addJSDoc(optionalProp('options', recordType(t.tsStringKeyword(), t.tsUnknownKeyword())), 'Additional index-specific options.'),
|
|
285
|
-
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
|
|
285
|
+
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
|
|
286
286
|
]), 'An index definition nested inside a table definition (table_name not required).');
|
|
287
287
|
}
|
|
288
288
|
// ---------------------------------------------------------------------------
|
|
@@ -308,7 +308,7 @@ function buildNodeTypes(dataNodes) {
|
|
|
308
308
|
// BlueprintNode -- shorthand | object
|
|
309
309
|
results.push(addJSDoc(exportTypeAlias('BlueprintNode', t.tsUnionType([
|
|
310
310
|
t.tsTypeReference(t.identifier('BlueprintNodeShorthand')),
|
|
311
|
-
t.tsTypeReference(t.identifier('BlueprintNodeObject'))
|
|
311
|
+
t.tsTypeReference(t.identifier('BlueprintNodeObject'))
|
|
312
312
|
])), 'A node entry in a blueprint table. Either a string shorthand or a typed object.'));
|
|
313
313
|
return results;
|
|
314
314
|
}
|
|
@@ -322,7 +322,7 @@ function buildRelationTypes(relationNodes) {
|
|
|
322
322
|
requiredProp('source_table', t.tsStringKeyword()),
|
|
323
323
|
requiredProp('target_table', t.tsStringKeyword()),
|
|
324
324
|
optionalProp('source_schema_name', t.tsStringKeyword()),
|
|
325
|
-
optionalProp('target_schema_name', t.tsStringKeyword())
|
|
325
|
+
optionalProp('target_schema_name', t.tsStringKeyword())
|
|
326
326
|
];
|
|
327
327
|
// RelationSpatial is the only relation type that references *existing*
|
|
328
328
|
// columns rather than creating FK/junction fields. Its blueprint JSON
|
|
@@ -336,11 +336,11 @@ function buildRelationTypes(relationNodes) {
|
|
|
336
336
|
const baseType = t.tsTypeLiteral(baseMembers);
|
|
337
337
|
return t.tsIntersectionType([
|
|
338
338
|
baseType,
|
|
339
|
-
partialOf(t.tsTypeReference(t.identifier(`${nt.name}Params`)))
|
|
339
|
+
partialOf(t.tsTypeReference(t.identifier(`${nt.name}Params`)))
|
|
340
340
|
]);
|
|
341
341
|
});
|
|
342
342
|
return [
|
|
343
|
-
addJSDoc(exportTypeAlias('BlueprintRelation', t.tsUnionType(relationMembers)), 'A relation entry in a blueprint definition.')
|
|
343
|
+
addJSDoc(exportTypeAlias('BlueprintRelation', t.tsUnionType(relationMembers)), 'A relation entry in a blueprint definition.')
|
|
344
344
|
];
|
|
345
345
|
}
|
|
346
346
|
// ---------------------------------------------------------------------------
|
|
@@ -350,23 +350,70 @@ function buildBlueprintUniqueConstraint() {
|
|
|
350
350
|
return addJSDoc(exportInterface('BlueprintUniqueConstraint', [
|
|
351
351
|
addJSDoc(requiredProp('table_name', t.tsStringKeyword()), 'Table name this unique constraint belongs to.'),
|
|
352
352
|
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name for disambiguation (falls back to top-level default).'),
|
|
353
|
-
addJSDoc(requiredProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Column names that form the unique constraint.')
|
|
353
|
+
addJSDoc(requiredProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Column names that form the unique constraint.')
|
|
354
354
|
]), 'A unique constraint definition within a blueprint (top-level, requires table_name).');
|
|
355
355
|
}
|
|
356
356
|
function buildBlueprintTableUniqueConstraint() {
|
|
357
357
|
return addJSDoc(exportInterface('BlueprintTableUniqueConstraint', [
|
|
358
358
|
addJSDoc(requiredProp('columns', t.tsArrayType(t.tsStringKeyword())), 'Column names that form the unique constraint.'),
|
|
359
|
-
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
|
|
359
|
+
addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
|
|
360
360
|
]), 'A unique constraint nested inside a table definition (table_name not required).');
|
|
361
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Build the BlueprintStoragePolicy interface.
|
|
364
|
+
*
|
|
365
|
+
* Matches the jsonb policy objects accepted by apply_storage_security():
|
|
366
|
+
* { "$type": "AuthzPublishable", "privileges": ["select"], "data": {...}, "tables": [...], "policy_name": "pub" }
|
|
367
|
+
*/
|
|
368
|
+
function buildBlueprintStoragePolicy() {
|
|
369
|
+
return addJSDoc(exportInterface('BlueprintStoragePolicy', [
|
|
370
|
+
addJSDoc(requiredProp('$type', t.tsStringKeyword()), 'Authz* policy generator type (e.g., "AuthzPublishable", "AuthzDirectOwner", "AuthzEntityMembership").'),
|
|
371
|
+
addJSDoc(requiredProp('privileges', t.tsArrayType(t.tsStringKeyword())), 'Privilege array (e.g., ["select", "insert", "update", "delete"]). Intersected with each storage table\'s supported operations.'),
|
|
372
|
+
addJSDoc(optionalProp('data', recordType(t.tsStringKeyword(), t.tsUnknownKeyword())), 'Policy data config. Auto-derived from $type when omitted (e.g., AuthzPublishable defaults to {"is_published_field": "is_public", "require_published_at": false}).'),
|
|
373
|
+
addJSDoc(optionalProp('tables', t.tsArrayType(strUnion(['buckets', 'files', 'upload_requests']))), 'Which storage tables to apply this policy to. Defaults to all three when omitted. Uses logical names (not prefixed).'),
|
|
374
|
+
addJSDoc(optionalProp('policy_name', t.tsStringKeyword()), 'Custom RLS policy name suffix. Auto-derived from $type when omitted (pub/own/mem).')
|
|
375
|
+
]), 'A storage-specific RLS policy object for apply_storage_security(). Each entry defines an Authz* policy with explicit privileges, scoped to specific storage tables.');
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Build the BlueprintBucketSeed interface.
|
|
379
|
+
*
|
|
380
|
+
* Matches the bucket entries in storage_config.buckets[].
|
|
381
|
+
*/
|
|
382
|
+
function buildBlueprintBucketSeed() {
|
|
383
|
+
return addJSDoc(exportInterface('BlueprintBucketSeed', [
|
|
384
|
+
addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Bucket key name (e.g., "avatars", "documents"). Becomes the key column value.'),
|
|
385
|
+
addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this bucket.'),
|
|
386
|
+
addJSDoc(optionalProp('is_public', t.tsBooleanKeyword()), 'Whether the bucket is publicly readable. Defaults to false.'),
|
|
387
|
+
addJSDoc(optionalProp('allowed_mime_types', t.tsArrayType(t.tsStringKeyword())), 'MIME type allowlist (e.g., ["image/png", "image/jpeg"]). NULL means all types allowed.'),
|
|
388
|
+
addJSDoc(optionalProp('max_file_size', t.tsNumberKeyword()), 'Maximum file size in bytes for this bucket. NULL means no limit.'),
|
|
389
|
+
addJSDoc(optionalProp('allowed_origins', t.tsArrayType(t.tsStringKeyword())), 'CORS allowed origins for this bucket.')
|
|
390
|
+
]), 'A bucket seed entry for storage_config.buckets[]. Creates an initial bucket row in the {prefix}_buckets table during entity type provisioning. Only used for app-level storage (not entity-scoped).');
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Build the BlueprintStorageConfig interface.
|
|
394
|
+
*
|
|
395
|
+
* Matches the jsonb shape accepted by storage_config on entity_type_provision.
|
|
396
|
+
*/
|
|
397
|
+
function buildBlueprintStorageConfig() {
|
|
398
|
+
return addJSDoc(exportInterface('BlueprintStorageConfig', [
|
|
399
|
+
addJSDoc(optionalProp('policies', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintStoragePolicy')))), 'Custom RLS policies for storage tables. When provided, replaces the default policy set (AuthzPublishable + membership + AuthzDirectOwner). Each entry is a policy object with $type, privileges, and optional data/tables/policy_name.'),
|
|
400
|
+
addJSDoc(optionalProp('buckets', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintBucketSeed')))), 'Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. Only used for app-level storage (not entity-scoped).'),
|
|
401
|
+
addJSDoc(optionalProp('upload_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned upload URL expiry time in seconds.'),
|
|
402
|
+
addJSDoc(optionalProp('download_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned download URL expiry time in seconds.'),
|
|
403
|
+
addJSDoc(optionalProp('default_max_file_size', t.tsNumberKeyword()), 'Default maximum file size in bytes for the storage module.'),
|
|
404
|
+
addJSDoc(optionalProp('allowed_origins', t.tsArrayType(t.tsStringKeyword())), 'CORS allowed origins for the storage module.')
|
|
405
|
+
]), 'Storage configuration for an entity type. Controls RLS policies on storage tables, seeds initial buckets, and overrides module-level settings (expiry times, file size limits, CORS).');
|
|
406
|
+
}
|
|
362
407
|
function buildBlueprintEntityTableProvision() {
|
|
363
408
|
return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
|
|
364
409
|
addJSDoc(optionalProp('use_rls', t.tsBooleanKeyword()), 'Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true.'),
|
|
365
410
|
addJSDoc(optionalProp('nodes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintNode')))), 'Node objects applied to the entity table for field creation (e.g., DataTimestamps, DataPeoplestamps). Forwarded to secure_table_provision as-is.'),
|
|
366
411
|
addJSDoc(optionalProp('fields', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintField')))), 'Custom fields (columns) to add to the entity table. Forwarded to secure_table_provision as-is.'),
|
|
367
|
-
addJSDoc(optionalProp('
|
|
368
|
-
|
|
369
|
-
|
|
412
|
+
addJSDoc(optionalProp('grants', t.tsArrayType(t.tsTypeLiteral([
|
|
413
|
+
requiredProp('roles', t.tsArrayType(t.tsStringKeyword())),
|
|
414
|
+
requiredProp('privileges', t.tsArrayType(t.tsUnknownKeyword()))
|
|
415
|
+
]))), 'Unified grant objects for the entity table. Each entry is { roles: string[], privileges: unknown[] } where privileges are [verb, columns] tuples. Forwarded to secure_table_provision as-is. Defaults to [].'),
|
|
416
|
+
addJSDoc(optionalProp('policies', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintPolicy')))), 'RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op).')
|
|
370
417
|
]), 'Override object for the entity table created by a BlueprintMembershipType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely.');
|
|
371
418
|
}
|
|
372
419
|
function buildBlueprintMembershipType() {
|
|
@@ -380,8 +427,10 @@ function buildBlueprintMembershipType() {
|
|
|
380
427
|
addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
|
|
381
428
|
addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
|
|
382
429
|
addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
|
|
430
|
+
addJSDoc(optionalProp('has_storage', t.tsBooleanKeyword()), 'Whether to provision a storage module (buckets, files, upload_requests tables) for this entity type. Defaults to false.'),
|
|
383
431
|
addJSDoc(optionalProp('skip_entity_policies', t.tsBooleanKeyword()), 'Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false.'),
|
|
384
432
|
addJSDoc(optionalProp('table_provision', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))), 'Override for the entity table. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, its policies[] replaces the five default entity-table policies; is_visible becomes a no-op. When NULL (default), the five default policies are applied (gated by is_visible).'),
|
|
433
|
+
addJSDoc(optionalProp('storage', t.tsTypeReference(t.identifier('BlueprintStorageConfig'))), 'Storage configuration. Only used when has_storage is true. Controls RLS policies on storage tables, seeds initial buckets, and overrides module-level settings (expiry times, file size limits, CORS).')
|
|
385
434
|
]), 'A membership type entry for Phase 0 of construct_blueprint(). Provisions a full entity type with its own entity table, membership modules, and security policies via entity_type_provision.');
|
|
386
435
|
}
|
|
387
436
|
function buildBlueprintTable() {
|
|
@@ -391,12 +440,14 @@ function buildBlueprintTable() {
|
|
|
391
440
|
addJSDoc(requiredProp('nodes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintNode')))), "Array of node type entries that define the table's behavior."),
|
|
392
441
|
addJSDoc(optionalProp('fields', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintField')))), 'Custom fields (columns) to add to the table.'),
|
|
393
442
|
addJSDoc(optionalProp('policies', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintPolicy')))), 'RLS policies for this table.'),
|
|
394
|
-
addJSDoc(optionalProp('
|
|
395
|
-
|
|
443
|
+
addJSDoc(optionalProp('grants', t.tsArrayType(t.tsTypeLiteral([
|
|
444
|
+
requiredProp('roles', t.tsArrayType(t.tsStringKeyword())),
|
|
445
|
+
requiredProp('privileges', t.tsArrayType(t.tsUnknownKeyword()))
|
|
446
|
+
]))), 'Unified grant objects. Each entry is { roles: string[], privileges: unknown[] } where privileges are [verb, columns] tuples (e.g. [["select","*"]]). Enables per-role targeting. Defaults to [].'),
|
|
396
447
|
addJSDoc(optionalProp('use_rls', t.tsBooleanKeyword()), 'Whether to enable RLS on this table. Defaults to true.'),
|
|
397
448
|
addJSDoc(optionalProp('indexes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintTableIndex')))), 'Table-level indexes (table_name inherited from parent).'),
|
|
398
449
|
addJSDoc(optionalProp('full_text_searches', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintTableFullTextSearch')))), 'Table-level full-text search configurations (table_name inherited from parent).'),
|
|
399
|
-
addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintTableUniqueConstraint')))), 'Table-level unique constraints (table_name inherited from parent).')
|
|
450
|
+
addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintTableUniqueConstraint')))), 'Table-level unique constraints (table_name inherited from parent).')
|
|
400
451
|
]), 'A table definition within a blueprint.');
|
|
401
452
|
}
|
|
402
453
|
function buildBlueprintDefinition() {
|
|
@@ -406,7 +457,7 @@ function buildBlueprintDefinition() {
|
|
|
406
457
|
addJSDoc(optionalProp('indexes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintIndex')))), 'Indexes on table columns.'),
|
|
407
458
|
addJSDoc(optionalProp('full_text_searches', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFullTextSearch')))), 'Full-text search configurations.'),
|
|
408
459
|
addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintUniqueConstraint')))), 'Unique constraints on table columns.'),
|
|
409
|
-
addJSDoc(optionalProp('membership_types', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintMembershipType')))), 'Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security.')
|
|
460
|
+
addJSDoc(optionalProp('membership_types', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintMembershipType')))), 'Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security.')
|
|
410
461
|
]), 'The complete blueprint definition -- the JSONB shape accepted by construct_blueprint().');
|
|
411
462
|
}
|
|
412
463
|
// ---------------------------------------------------------------------------
|
|
@@ -417,8 +468,8 @@ function sectionComment(title) {
|
|
|
417
468
|
empty.leadingComments = [
|
|
418
469
|
{
|
|
419
470
|
type: 'CommentBlock',
|
|
420
|
-
value: `*\n * ===========================================================================\n * ${title}\n * ===========================================================================\n
|
|
421
|
-
}
|
|
471
|
+
value: `*\n * ===========================================================================\n * ${title}\n * ===========================================================================\n `
|
|
472
|
+
}
|
|
422
473
|
];
|
|
423
474
|
return empty;
|
|
424
475
|
}
|
|
@@ -460,6 +511,9 @@ function buildProgram(meta) {
|
|
|
460
511
|
statements.push(buildBlueprintTableIndex());
|
|
461
512
|
statements.push(buildBlueprintUniqueConstraint());
|
|
462
513
|
statements.push(buildBlueprintTableUniqueConstraint());
|
|
514
|
+
statements.push(buildBlueprintStoragePolicy());
|
|
515
|
+
statements.push(buildBlueprintBucketSeed());
|
|
516
|
+
statements.push(buildBlueprintStorageConfig());
|
|
463
517
|
statements.push(buildBlueprintEntityTableProvision());
|
|
464
518
|
statements.push(buildBlueprintMembershipType());
|
|
465
519
|
// -- Node types discriminated union --
|
|
@@ -477,6 +531,7 @@ function buildProgram(meta) {
|
|
|
477
531
|
const file = t.file(program);
|
|
478
532
|
const header = [
|
|
479
533
|
'// GENERATED FILE \u2014 DO NOT EDIT',
|
|
534
|
+
'/* eslint-disable @typescript-eslint/no-empty-object-type */',
|
|
480
535
|
'//',
|
|
481
536
|
'// Regenerate with:',
|
|
482
537
|
'// cd graphql/node-type-registry && pnpm generate:types',
|
|
@@ -484,9 +539,9 @@ function buildProgram(meta) {
|
|
|
484
539
|
'// These types match the JSONB shape expected by construct_blueprint().',
|
|
485
540
|
'// All field names are snake_case to match the SQL convention.',
|
|
486
541
|
'',
|
|
487
|
-
''
|
|
542
|
+
''
|
|
488
543
|
].join('\n');
|
|
489
|
-
const output = generate(file, { comments: true });
|
|
544
|
+
const output = generate(file, { comments: true, jsescOption: { quotes: 'single' } });
|
|
490
545
|
return header + output.code + '\n';
|
|
491
546
|
}
|
|
492
547
|
// ---------------------------------------------------------------------------
|
|
@@ -46,46 +46,33 @@ export const RelationManyToMany = {
|
|
|
46
46
|
},
|
|
47
47
|
"description": "Array of node objects for field creation on junction table. Each object has a $type key (e.g. DataId, DataEntityMembership) and optional data keys. Forwarded to secure_table_provision as-is. Empty array means no additional fields."
|
|
48
48
|
},
|
|
49
|
-
"
|
|
49
|
+
"grants": {
|
|
50
50
|
"type": "array",
|
|
51
51
|
"items": {
|
|
52
|
-
"type": "
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"roles": { "type": "array", "items": { "type": "string" } },
|
|
55
|
+
"privileges": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } }
|
|
56
|
+
},
|
|
57
|
+
"required": ["roles", "privileges"]
|
|
53
58
|
},
|
|
54
|
-
"description": "
|
|
59
|
+
"description": "Unified grant objects for the junction table. Each entry is { roles: string[], privileges: string[][] }. Forwarded to secure_table_provision as-is. Default: []"
|
|
55
60
|
},
|
|
56
|
-
"
|
|
61
|
+
"policies": {
|
|
57
62
|
"type": "array",
|
|
58
63
|
"items": {
|
|
59
|
-
"type": "
|
|
60
|
-
"
|
|
61
|
-
"type": "string"
|
|
62
|
-
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"$type": { "type": "string" },
|
|
67
|
+
"data": { "type": "object" },
|
|
68
|
+
"privileges": { "type": "array", "items": { "type": "string" } },
|
|
69
|
+
"policy_role": { "type": "string" },
|
|
70
|
+
"permissive": { "type": "boolean" },
|
|
71
|
+
"policy_name": { "type": "string" }
|
|
72
|
+
},
|
|
73
|
+
"required": ["$type"]
|
|
63
74
|
},
|
|
64
|
-
"description": "
|
|
65
|
-
},
|
|
66
|
-
"policy_type": {
|
|
67
|
-
"type": "string",
|
|
68
|
-
"description": "RLS policy type for the junction table. Forwarded to secure_table_provision as-is. NULL means no policy."
|
|
69
|
-
},
|
|
70
|
-
"policy_privileges": {
|
|
71
|
-
"type": "array",
|
|
72
|
-
"items": {
|
|
73
|
-
"type": "string"
|
|
74
|
-
},
|
|
75
|
-
"description": "Privileges the policy applies to. Forwarded to secure_table_provision as-is. NULL means derived from grant_privileges verbs."
|
|
76
|
-
},
|
|
77
|
-
"policy_role": {
|
|
78
|
-
"type": "string",
|
|
79
|
-
"description": "Database role the policy targets. Forwarded to secure_table_provision as-is. NULL means falls back to first grant_role."
|
|
80
|
-
},
|
|
81
|
-
"policy_permissive": {
|
|
82
|
-
"type": "boolean",
|
|
83
|
-
"description": "Whether the policy is PERMISSIVE (true) or RESTRICTIVE (false). Forwarded to secure_table_provision as-is.",
|
|
84
|
-
"default": true
|
|
85
|
-
},
|
|
86
|
-
"policy_data": {
|
|
87
|
-
"type": "object",
|
|
88
|
-
"description": "Policy configuration forwarded to secure_table_provision as-is. Structure varies by policy_type."
|
|
75
|
+
"description": "RLS policy objects for the junction table. Each entry has $type (Authz* generator), optional data, privileges, policy_role, permissive, policy_name. Forwarded to secure_table_provision as-is. Default: []"
|
|
89
76
|
}
|
|
90
77
|
},
|
|
91
78
|
"required": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-type-registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"registry",
|
|
48
48
|
"graphile"
|
|
49
49
|
],
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "058b8200e99eb505477d1599ee0d5ab795aa0123"
|
|
51
51
|
}
|
|
@@ -49,46 +49,33 @@ exports.RelationManyToMany = {
|
|
|
49
49
|
},
|
|
50
50
|
"description": "Array of node objects for field creation on junction table. Each object has a $type key (e.g. DataId, DataEntityMembership) and optional data keys. Forwarded to secure_table_provision as-is. Empty array means no additional fields."
|
|
51
51
|
},
|
|
52
|
-
"
|
|
52
|
+
"grants": {
|
|
53
53
|
"type": "array",
|
|
54
54
|
"items": {
|
|
55
|
-
"type": "
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"roles": { "type": "array", "items": { "type": "string" } },
|
|
58
|
+
"privileges": { "type": "array", "items": { "type": "array", "items": { "type": "string" } } }
|
|
59
|
+
},
|
|
60
|
+
"required": ["roles", "privileges"]
|
|
56
61
|
},
|
|
57
|
-
"description": "
|
|
62
|
+
"description": "Unified grant objects for the junction table. Each entry is { roles: string[], privileges: string[][] }. Forwarded to secure_table_provision as-is. Default: []"
|
|
58
63
|
},
|
|
59
|
-
"
|
|
64
|
+
"policies": {
|
|
60
65
|
"type": "array",
|
|
61
66
|
"items": {
|
|
62
|
-
"type": "
|
|
63
|
-
"
|
|
64
|
-
"type": "string"
|
|
65
|
-
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"$type": { "type": "string" },
|
|
70
|
+
"data": { "type": "object" },
|
|
71
|
+
"privileges": { "type": "array", "items": { "type": "string" } },
|
|
72
|
+
"policy_role": { "type": "string" },
|
|
73
|
+
"permissive": { "type": "boolean" },
|
|
74
|
+
"policy_name": { "type": "string" }
|
|
75
|
+
},
|
|
76
|
+
"required": ["$type"]
|
|
66
77
|
},
|
|
67
|
-
"description": "
|
|
68
|
-
},
|
|
69
|
-
"policy_type": {
|
|
70
|
-
"type": "string",
|
|
71
|
-
"description": "RLS policy type for the junction table. Forwarded to secure_table_provision as-is. NULL means no policy."
|
|
72
|
-
},
|
|
73
|
-
"policy_privileges": {
|
|
74
|
-
"type": "array",
|
|
75
|
-
"items": {
|
|
76
|
-
"type": "string"
|
|
77
|
-
},
|
|
78
|
-
"description": "Privileges the policy applies to. Forwarded to secure_table_provision as-is. NULL means derived from grant_privileges verbs."
|
|
79
|
-
},
|
|
80
|
-
"policy_role": {
|
|
81
|
-
"type": "string",
|
|
82
|
-
"description": "Database role the policy targets. Forwarded to secure_table_provision as-is. NULL means falls back to first grant_role."
|
|
83
|
-
},
|
|
84
|
-
"policy_permissive": {
|
|
85
|
-
"type": "boolean",
|
|
86
|
-
"description": "Whether the policy is PERMISSIVE (true) or RESTRICTIVE (false). Forwarded to secure_table_provision as-is.",
|
|
87
|
-
"default": true
|
|
88
|
-
},
|
|
89
|
-
"policy_data": {
|
|
90
|
-
"type": "object",
|
|
91
|
-
"description": "Policy configuration forwarded to secure_table_provision as-is. Structure varies by policy_type."
|
|
78
|
+
"description": "RLS policy objects for the junction table. Each entry has $type (Authz* generator), optional data, privileges, policy_role, permissive, policy_name. Forwarded to secure_table_provision as-is. Default: []"
|
|
92
79
|
}
|
|
93
80
|
},
|
|
94
81
|
"required": [
|