node-type-registry 0.18.0 → 0.19.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.
@@ -574,19 +574,6 @@ export interface BlueprintTableUniqueConstraint {
574
574
  /** Optional schema name override. */
575
575
  schema_name?: string;
576
576
  }
577
- /** A storage-specific RLS policy object for apply_storage_security(). Each entry defines an Authz* policy with explicit privileges, scoped to specific storage tables. */
578
- export interface BlueprintStoragePolicy {
579
- /** Authz* policy generator type (e.g., "AuthzPublishable", "AuthzDirectOwner", "AuthzEntityMembership"). */
580
- $type: string;
581
- /** Privilege array (e.g., ["select", "insert", "update", "delete"]). Intersected with each storage table's supported operations. */
582
- privileges: string[];
583
- /** Policy data config. Auto-derived from $type when omitted (e.g., AuthzPublishable defaults to {"is_published_field": "is_public", "require_published_at": false}). */
584
- data?: Record<string, unknown>;
585
- /** Which storage tables to apply this policy to. Defaults to all three when omitted. Uses logical names (not prefixed). */
586
- tables?: ('buckets' | 'files' | 'upload_requests')[];
587
- /** Custom RLS policy name suffix. Auto-derived from $type when omitted (pub/own/mem). */
588
- policy_name?: string;
589
- }
590
577
  /** 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). */
591
578
  export interface BlueprintBucketSeed {
592
579
  /** Bucket key name (e.g., "avatars", "documents"). Becomes the key column value. */
@@ -602,10 +589,8 @@ export interface BlueprintBucketSeed {
602
589
  /** CORS allowed origins for this bucket. */
603
590
  allowed_origins?: string[];
604
591
  }
605
- /** 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). */
592
+ /** Storage configuration for an entity type. Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions. */
606
593
  export interface BlueprintStorageConfig {
607
- /** 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. */
608
- policies?: BlueprintStoragePolicy[];
609
594
  /** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. Only used for app-level storage (not entity-scoped). */
610
595
  buckets?: BlueprintBucketSeed[];
611
596
  /** Override for presigned upload URL expiry time in seconds. */
@@ -616,8 +601,14 @@ export interface BlueprintStorageConfig {
616
601
  default_max_file_size?: number;
617
602
  /** CORS allowed origins for the storage module. */
618
603
  allowed_origins?: string[];
604
+ /** Per-table overrides for storage tables. Each key targets a specific storage table (files, buckets, upload_requests) and uses the same shape as table_provision: { nodes, fields, grants, use_rls, policies }. Fanned out to secure_table_provision targeting the corresponding table. When a key includes policies[], those REPLACE the default storage policies for that table; tables without a key still get defaults. */
605
+ provisions?: {
606
+ files?: BlueprintEntityTableProvision;
607
+ buckets?: BlueprintEntityTableProvision;
608
+ upload_requests?: BlueprintEntityTableProvision;
609
+ };
619
610
  }
620
- /** 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. */
611
+ /** Override object for the entity table created by a BlueprintEntityType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely. */
621
612
  export interface BlueprintEntityTableProvision {
622
613
  /** Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true. */
623
614
  use_rls?: boolean;
@@ -633,8 +624,8 @@ export interface BlueprintEntityTableProvision {
633
624
  /** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
634
625
  policies?: BlueprintPolicy[];
635
626
  }
636
- /** 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. */
637
- export interface BlueprintMembershipType {
627
+ /** An entity 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. */
628
+ export interface BlueprintEntityType {
638
629
  /** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
639
630
  name: string;
640
631
  /** Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming. */
@@ -655,6 +646,8 @@ export interface BlueprintMembershipType {
655
646
  has_levels?: boolean;
656
647
  /** Whether to provision a storage module (buckets, files, upload_requests tables) for this entity type. Defaults to false. */
657
648
  has_storage?: boolean;
649
+ /** Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false. */
650
+ has_invites?: boolean;
658
651
  /** Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false. */
659
652
  skip_entity_policies?: boolean;
660
653
  /** 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). */
@@ -876,5 +869,7 @@ export interface BlueprintDefinition {
876
869
  /** Unique constraints on table columns. */
877
870
  unique_constraints?: BlueprintUniqueConstraint[];
878
871
  /** Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security. */
879
- membership_types?: BlueprintMembershipType[];
872
+ entity_types?: BlueprintEntityType[];
873
+ /** App-level storage configuration. Creates a storage_module (membership_type = NULL), seeds initial buckets, and overrides module-level settings (expiry times, file size limits, CORS). Use provisions for per-table policy overrides. For entity-scoped storage, use entity_types[].has_storage + entity_types[].storage instead. */
874
+ storage?: BlueprintStorageConfig;
880
875
  }
@@ -394,21 +394,6 @@ function buildBlueprintTableUniqueConstraint() {
394
394
  addJSDoc(optionalProp('schema_name', t.tsStringKeyword()), 'Optional schema name override.')
395
395
  ]), 'A unique constraint nested inside a table definition (table_name not required).');
396
396
  }
397
- /**
398
- * Build the BlueprintStoragePolicy interface.
399
- *
400
- * Matches the jsonb policy objects accepted by apply_storage_security():
401
- * { "$type": "AuthzPublishable", "privileges": ["select"], "data": {...}, "tables": [...], "policy_name": "pub" }
402
- */
403
- function buildBlueprintStoragePolicy() {
404
- return addJSDoc(exportInterface('BlueprintStoragePolicy', [
405
- addJSDoc(requiredProp('$type', t.tsStringKeyword()), 'Authz* policy generator type (e.g., "AuthzPublishable", "AuthzDirectOwner", "AuthzEntityMembership").'),
406
- addJSDoc(requiredProp('privileges', t.tsArrayType(t.tsStringKeyword())), 'Privilege array (e.g., ["select", "insert", "update", "delete"]). Intersected with each storage table\'s supported operations.'),
407
- 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}).'),
408
- 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).'),
409
- addJSDoc(optionalProp('policy_name', t.tsStringKeyword()), 'Custom RLS policy name suffix. Auto-derived from $type when omitted (pub/own/mem).')
410
- ]), 'A storage-specific RLS policy object for apply_storage_security(). Each entry defines an Authz* policy with explicit privileges, scoped to specific storage tables.');
411
- }
412
397
  /**
413
398
  * Build the BlueprintBucketSeed interface.
414
399
  *
@@ -431,13 +416,17 @@ function buildBlueprintBucketSeed() {
431
416
  */
432
417
  function buildBlueprintStorageConfig() {
433
418
  return addJSDoc(exportInterface('BlueprintStorageConfig', [
434
- 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.'),
435
419
  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).'),
436
420
  addJSDoc(optionalProp('upload_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned upload URL expiry time in seconds.'),
437
421
  addJSDoc(optionalProp('download_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned download URL expiry time in seconds.'),
438
422
  addJSDoc(optionalProp('default_max_file_size', t.tsNumberKeyword()), 'Default maximum file size in bytes for the storage module.'),
439
- addJSDoc(optionalProp('allowed_origins', t.tsArrayType(t.tsStringKeyword())), 'CORS allowed origins for the storage module.')
440
- ]), '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).');
423
+ addJSDoc(optionalProp('allowed_origins', t.tsArrayType(t.tsStringKeyword())), 'CORS allowed origins for the storage module.'),
424
+ addJSDoc(optionalProp('provisions', t.tsTypeLiteral([
425
+ optionalProp('files', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))),
426
+ optionalProp('buckets', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))),
427
+ optionalProp('upload_requests', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision')))
428
+ ])), 'Per-table overrides for storage tables. Each key targets a specific storage table (files, buckets, upload_requests) and uses the same shape as table_provision: { nodes, fields, grants, use_rls, policies }. Fanned out to secure_table_provision targeting the corresponding table. When a key includes policies[], those REPLACE the default storage policies for that table; tables without a key still get defaults.')
429
+ ]), 'Storage configuration for an entity type. Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions.');
441
430
  }
442
431
  function buildBlueprintEntityTableProvision() {
443
432
  return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
@@ -449,10 +438,10 @@ function buildBlueprintEntityTableProvision() {
449
438
  requiredProp('privileges', t.tsArrayType(t.tsUnknownKeyword()))
450
439
  ]))), '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 [].'),
451
440
  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).')
452
- ]), '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.');
441
+ ]), 'Override object for the entity table created by a BlueprintEntityType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely.');
453
442
  }
454
- function buildBlueprintMembershipType() {
455
- return addJSDoc(exportInterface('BlueprintMembershipType', [
443
+ function buildBlueprintEntityType() {
444
+ return addJSDoc(exportInterface('BlueprintEntityType', [
456
445
  addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
457
446
  addJSDoc(requiredProp('prefix', t.tsStringKeyword()), 'Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming.'),
458
447
  addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
@@ -463,10 +452,11 @@ function buildBlueprintMembershipType() {
463
452
  addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
464
453
  addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
465
454
  addJSDoc(optionalProp('has_storage', t.tsBooleanKeyword()), 'Whether to provision a storage module (buckets, files, upload_requests tables) for this entity type. Defaults to false.'),
455
+ addJSDoc(optionalProp('has_invites', t.tsBooleanKeyword()), 'Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false.'),
466
456
  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.'),
467
457
  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).'),
468
458
  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).')
469
- ]), '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.');
459
+ ]), 'An entity 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.');
470
460
  }
471
461
  function buildBlueprintTable() {
472
462
  return addJSDoc(exportInterface('BlueprintTable', [
@@ -492,7 +482,8 @@ function buildBlueprintDefinition() {
492
482
  addJSDoc(optionalProp('indexes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintIndex')))), 'Indexes on table columns.'),
493
483
  addJSDoc(optionalProp('full_text_searches', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFullTextSearch')))), 'Full-text search configurations.'),
494
484
  addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintUniqueConstraint')))), 'Unique constraints on table columns.'),
495
- 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.')
485
+ addJSDoc(optionalProp('entity_types', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintEntityType')))), 'Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security.'),
486
+ addJSDoc(optionalProp('storage', t.tsTypeReference(t.identifier('BlueprintStorageConfig'))), 'App-level storage configuration. Creates a storage_module (membership_type = NULL), seeds initial buckets, and overrides module-level settings (expiry times, file size limits, CORS). Use provisions for per-table policy overrides. For entity-scoped storage, use entity_types[].has_storage + entity_types[].storage instead.')
496
487
  ]), 'The complete blueprint definition -- the JSONB shape accepted by construct_blueprint().');
497
488
  }
498
489
  // ---------------------------------------------------------------------------
@@ -546,11 +537,10 @@ function buildProgram(meta) {
546
537
  statements.push(buildBlueprintTableIndex());
547
538
  statements.push(buildBlueprintUniqueConstraint());
548
539
  statements.push(buildBlueprintTableUniqueConstraint());
549
- statements.push(buildBlueprintStoragePolicy());
550
540
  statements.push(buildBlueprintBucketSeed());
551
541
  statements.push(buildBlueprintStorageConfig());
552
542
  statements.push(buildBlueprintEntityTableProvision());
553
- statements.push(buildBlueprintMembershipType());
543
+ statements.push(buildBlueprintEntityType());
554
544
  // -- Node types discriminated union --
555
545
  statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
556
546
  statements.push(...buildNodeTypes(dataNodes));
@@ -574,19 +574,6 @@ export interface BlueprintTableUniqueConstraint {
574
574
  /** Optional schema name override. */
575
575
  schema_name?: string;
576
576
  }
577
- /** A storage-specific RLS policy object for apply_storage_security(). Each entry defines an Authz* policy with explicit privileges, scoped to specific storage tables. */
578
- export interface BlueprintStoragePolicy {
579
- /** Authz* policy generator type (e.g., "AuthzPublishable", "AuthzDirectOwner", "AuthzEntityMembership"). */
580
- $type: string;
581
- /** Privilege array (e.g., ["select", "insert", "update", "delete"]). Intersected with each storage table's supported operations. */
582
- privileges: string[];
583
- /** Policy data config. Auto-derived from $type when omitted (e.g., AuthzPublishable defaults to {"is_published_field": "is_public", "require_published_at": false}). */
584
- data?: Record<string, unknown>;
585
- /** Which storage tables to apply this policy to. Defaults to all three when omitted. Uses logical names (not prefixed). */
586
- tables?: ('buckets' | 'files' | 'upload_requests')[];
587
- /** Custom RLS policy name suffix. Auto-derived from $type when omitted (pub/own/mem). */
588
- policy_name?: string;
589
- }
590
577
  /** 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). */
591
578
  export interface BlueprintBucketSeed {
592
579
  /** Bucket key name (e.g., "avatars", "documents"). Becomes the key column value. */
@@ -602,10 +589,8 @@ export interface BlueprintBucketSeed {
602
589
  /** CORS allowed origins for this bucket. */
603
590
  allowed_origins?: string[];
604
591
  }
605
- /** 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). */
592
+ /** Storage configuration for an entity type. Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions. */
606
593
  export interface BlueprintStorageConfig {
607
- /** 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. */
608
- policies?: BlueprintStoragePolicy[];
609
594
  /** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. Only used for app-level storage (not entity-scoped). */
610
595
  buckets?: BlueprintBucketSeed[];
611
596
  /** Override for presigned upload URL expiry time in seconds. */
@@ -616,8 +601,14 @@ export interface BlueprintStorageConfig {
616
601
  default_max_file_size?: number;
617
602
  /** CORS allowed origins for the storage module. */
618
603
  allowed_origins?: string[];
604
+ /** Per-table overrides for storage tables. Each key targets a specific storage table (files, buckets, upload_requests) and uses the same shape as table_provision: { nodes, fields, grants, use_rls, policies }. Fanned out to secure_table_provision targeting the corresponding table. When a key includes policies[], those REPLACE the default storage policies for that table; tables without a key still get defaults. */
605
+ provisions?: {
606
+ files?: BlueprintEntityTableProvision;
607
+ buckets?: BlueprintEntityTableProvision;
608
+ upload_requests?: BlueprintEntityTableProvision;
609
+ };
619
610
  }
620
- /** 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. */
611
+ /** Override object for the entity table created by a BlueprintEntityType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely. */
621
612
  export interface BlueprintEntityTableProvision {
622
613
  /** Whether to enable RLS on the entity table. Forwarded to secure_table_provision. Defaults to true. */
623
614
  use_rls?: boolean;
@@ -633,8 +624,8 @@ export interface BlueprintEntityTableProvision {
633
624
  /** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
634
625
  policies?: BlueprintPolicy[];
635
626
  }
636
- /** 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. */
637
- export interface BlueprintMembershipType {
627
+ /** An entity 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. */
628
+ export interface BlueprintEntityType {
638
629
  /** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
639
630
  name: string;
640
631
  /** Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming. */
@@ -655,6 +646,8 @@ export interface BlueprintMembershipType {
655
646
  has_levels?: boolean;
656
647
  /** Whether to provision a storage module (buckets, files, upload_requests tables) for this entity type. Defaults to false. */
657
648
  has_storage?: boolean;
649
+ /** Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false. */
650
+ has_invites?: boolean;
658
651
  /** Escape hatch: when true AND table_provision is NULL, zero policies are provisioned on the entity table. Defaults to false. */
659
652
  skip_entity_policies?: boolean;
660
653
  /** 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). */
@@ -876,5 +869,7 @@ export interface BlueprintDefinition {
876
869
  /** Unique constraints on table columns. */
877
870
  unique_constraints?: BlueprintUniqueConstraint[];
878
871
  /** Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security. */
879
- membership_types?: BlueprintMembershipType[];
872
+ entity_types?: BlueprintEntityType[];
873
+ /** App-level storage configuration. Creates a storage_module (membership_type = NULL), seeds initial buckets, and overrides module-level settings (expiry times, file size limits, CORS). Use provisions for per-table policy overrides. For entity-scoped storage, use entity_types[].has_storage + entity_types[].storage instead. */
874
+ storage?: BlueprintStorageConfig;
880
875
  }
@@ -359,21 +359,6 @@ function buildBlueprintTableUniqueConstraint() {
359
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
362
  /**
378
363
  * Build the BlueprintBucketSeed interface.
379
364
  *
@@ -396,13 +381,17 @@ function buildBlueprintBucketSeed() {
396
381
  */
397
382
  function buildBlueprintStorageConfig() {
398
383
  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
384
  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
385
  addJSDoc(optionalProp('upload_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned upload URL expiry time in seconds.'),
402
386
  addJSDoc(optionalProp('download_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned download URL expiry time in seconds.'),
403
387
  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).');
388
+ addJSDoc(optionalProp('allowed_origins', t.tsArrayType(t.tsStringKeyword())), 'CORS allowed origins for the storage module.'),
389
+ addJSDoc(optionalProp('provisions', t.tsTypeLiteral([
390
+ optionalProp('files', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))),
391
+ optionalProp('buckets', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))),
392
+ optionalProp('upload_requests', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision')))
393
+ ])), 'Per-table overrides for storage tables. Each key targets a specific storage table (files, buckets, upload_requests) and uses the same shape as table_provision: { nodes, fields, grants, use_rls, policies }. Fanned out to secure_table_provision targeting the corresponding table. When a key includes policies[], those REPLACE the default storage policies for that table; tables without a key still get defaults.')
394
+ ]), 'Storage configuration for an entity type. Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions.');
406
395
  }
407
396
  function buildBlueprintEntityTableProvision() {
408
397
  return addJSDoc(exportInterface('BlueprintEntityTableProvision', [
@@ -414,10 +403,10 @@ function buildBlueprintEntityTableProvision() {
414
403
  requiredProp('privileges', t.tsArrayType(t.tsUnknownKeyword()))
415
404
  ]))), '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
405
  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).')
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.');
406
+ ]), 'Override object for the entity table created by a BlueprintEntityType. Shape mirrors BlueprintTable / secure_table_provision vocabulary. When supplied, policies[] replaces the default entity-table policies entirely.');
418
407
  }
419
- function buildBlueprintMembershipType() {
420
- return addJSDoc(exportInterface('BlueprintMembershipType', [
408
+ function buildBlueprintEntityType() {
409
+ return addJSDoc(exportInterface('BlueprintEntityType', [
421
410
  addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
422
411
  addJSDoc(requiredProp('prefix', t.tsStringKeyword()), 'Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming.'),
423
412
  addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
@@ -428,10 +417,11 @@ function buildBlueprintMembershipType() {
428
417
  addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
429
418
  addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
430
419
  addJSDoc(optionalProp('has_storage', t.tsBooleanKeyword()), 'Whether to provision a storage module (buckets, files, upload_requests tables) for this entity type. Defaults to false.'),
420
+ addJSDoc(optionalProp('has_invites', t.tsBooleanKeyword()), 'Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false.'),
431
421
  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.'),
432
422
  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
423
  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).')
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.');
424
+ ]), 'An entity 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.');
435
425
  }
436
426
  function buildBlueprintTable() {
437
427
  return addJSDoc(exportInterface('BlueprintTable', [
@@ -457,7 +447,8 @@ function buildBlueprintDefinition() {
457
447
  addJSDoc(optionalProp('indexes', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintIndex')))), 'Indexes on table columns.'),
458
448
  addJSDoc(optionalProp('full_text_searches', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFullTextSearch')))), 'Full-text search configurations.'),
459
449
  addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintUniqueConstraint')))), 'Unique constraints on table columns.'),
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.')
450
+ addJSDoc(optionalProp('entity_types', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintEntityType')))), 'Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security.'),
451
+ addJSDoc(optionalProp('storage', t.tsTypeReference(t.identifier('BlueprintStorageConfig'))), 'App-level storage configuration. Creates a storage_module (membership_type = NULL), seeds initial buckets, and overrides module-level settings (expiry times, file size limits, CORS). Use provisions for per-table policy overrides. For entity-scoped storage, use entity_types[].has_storage + entity_types[].storage instead.')
461
452
  ]), 'The complete blueprint definition -- the JSONB shape accepted by construct_blueprint().');
462
453
  }
463
454
  // ---------------------------------------------------------------------------
@@ -511,11 +502,10 @@ function buildProgram(meta) {
511
502
  statements.push(buildBlueprintTableIndex());
512
503
  statements.push(buildBlueprintUniqueConstraint());
513
504
  statements.push(buildBlueprintTableUniqueConstraint());
514
- statements.push(buildBlueprintStoragePolicy());
515
505
  statements.push(buildBlueprintBucketSeed());
516
506
  statements.push(buildBlueprintStorageConfig());
517
507
  statements.push(buildBlueprintEntityTableProvision());
518
- statements.push(buildBlueprintMembershipType());
508
+ statements.push(buildBlueprintEntityType());
519
509
  // -- Node types discriminated union --
520
510
  statements.push(sectionComment('Node types -- discriminated union for nodes[] entries'));
521
511
  statements.push(...buildNodeTypes(dataNodes));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-type-registry",
3
- "version": "0.18.0",
3
+ "version": "0.19.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": "058b8200e99eb505477d1599ee0d5ab795aa0123"
50
+ "gitHead": "4ec54068fedd13e6145808b8059121f4a1b3891d"
51
51
  }