node-type-registry 0.37.0 → 0.39.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.
Files changed (113) hide show
  1. package/blueprint-types.generated.d.ts +13 -11
  2. package/codegen/generate-types.js +11 -7
  3. package/data/index.d.ts +0 -15
  4. package/data/index.js +1 -31
  5. package/esm/blueprint-types.generated.d.ts +13 -11
  6. package/esm/codegen/generate-types.js +11 -7
  7. package/esm/data/index.d.ts +0 -15
  8. package/esm/data/index.js +0 -15
  9. package/esm/event/index.d.ts +2 -0
  10. package/esm/event/index.js +2 -0
  11. package/esm/event/referral.d.ts +2 -0
  12. package/esm/event/referral.js +62 -0
  13. package/esm/index.d.ts +4 -0
  14. package/esm/index.js +12 -0
  15. package/esm/job/index.d.ts +1 -0
  16. package/esm/job/index.js +1 -0
  17. package/esm/limit/index.d.ts +8 -0
  18. package/esm/limit/index.js +8 -0
  19. package/esm/module-presets/auth-email-magic.js +2 -2
  20. package/esm/module-presets/auth-email.js +5 -5
  21. package/esm/module-presets/auth-hardened.js +2 -2
  22. package/esm/module-presets/auth-passkey.js +2 -2
  23. package/esm/module-presets/auth-sso.d.ts +1 -1
  24. package/esm/module-presets/auth-sso.js +5 -5
  25. package/esm/module-presets/b2b-storage.js +6 -6
  26. package/esm/module-presets/b2b.js +2 -2
  27. package/esm/module-presets/minimal.d.ts +1 -1
  28. package/esm/module-presets/minimal.js +3 -3
  29. package/esm/{data/data-chunks.js → process/chunks.js} +2 -3
  30. package/esm/{data/data-file-embedding.js → process/file-embedding.js} +2 -2
  31. package/esm/process/index.d.ts +5 -0
  32. package/esm/process/index.js +5 -0
  33. package/event/index.d.ts +2 -0
  34. package/event/index.js +7 -0
  35. package/event/referral.d.ts +2 -0
  36. package/event/referral.js +65 -0
  37. package/index.d.ts +4 -0
  38. package/index.js +12 -0
  39. package/job/index.d.ts +1 -0
  40. package/job/index.js +5 -0
  41. package/limit/index.d.ts +8 -0
  42. package/limit/index.js +19 -0
  43. package/module-presets/auth-email-magic.js +2 -2
  44. package/module-presets/auth-email.js +5 -5
  45. package/module-presets/auth-hardened.js +2 -2
  46. package/module-presets/auth-passkey.js +2 -2
  47. package/module-presets/auth-sso.d.ts +1 -1
  48. package/module-presets/auth-sso.js +5 -5
  49. package/module-presets/b2b-storage.js +6 -6
  50. package/module-presets/b2b.js +2 -2
  51. package/module-presets/minimal.d.ts +1 -1
  52. package/module-presets/minimal.js +3 -3
  53. package/package.json +2 -2
  54. package/{data/data-chunks.js → process/chunks.js} +2 -3
  55. package/{data/data-file-embedding.js → process/file-embedding.js} +2 -2
  56. package/process/index.d.ts +5 -0
  57. package/process/index.js +13 -0
  58. /package/{data/event-tracker.d.ts → esm/event/tracker.d.ts} +0 -0
  59. /package/esm/{data/event-tracker.js → event/tracker.js} +0 -0
  60. /package/{data/data-job-trigger.d.ts → esm/job/trigger.d.ts} +0 -0
  61. /package/esm/{data/data-job-trigger.js → job/trigger.js} +0 -0
  62. /package/{data/data-aggregate-limit-counter.d.ts → esm/limit/enforce-aggregate.d.ts} +0 -0
  63. /package/esm/{data/data-aggregate-limit-counter.js → limit/enforce-aggregate.js} +0 -0
  64. /package/{data/data-limit-counter.d.ts → esm/limit/enforce-counter.d.ts} +0 -0
  65. /package/esm/{data/data-limit-counter.js → limit/enforce-counter.js} +0 -0
  66. /package/{data/data-feature-flag.d.ts → esm/limit/enforce-feature.d.ts} +0 -0
  67. /package/esm/{data/data-feature-flag.js → limit/enforce-feature.js} +0 -0
  68. /package/{data/data-meter-rate-limit.d.ts → esm/limit/enforce-rate.d.ts} +0 -0
  69. /package/esm/{data/data-meter-rate-limit.js → limit/enforce-rate.js} +0 -0
  70. /package/{data/data-billing-meter.d.ts → esm/limit/track-usage.d.ts} +0 -0
  71. /package/esm/{data/data-billing-meter.js → limit/track-usage.js} +0 -0
  72. /package/{data/limit-warning-aggregate.d.ts → esm/limit/warning-aggregate.d.ts} +0 -0
  73. /package/esm/{data/limit-warning-aggregate.js → limit/warning-aggregate.js} +0 -0
  74. /package/{data/limit-warning-counter.d.ts → esm/limit/warning-counter.d.ts} +0 -0
  75. /package/esm/{data/limit-warning-counter.js → limit/warning-counter.js} +0 -0
  76. /package/{data/limit-warning-rate.d.ts → esm/limit/warning-rate.d.ts} +0 -0
  77. /package/esm/{data/limit-warning-rate.js → limit/warning-rate.js} +0 -0
  78. /package/{data/data-chunks.d.ts → esm/process/chunks.d.ts} +0 -0
  79. /package/{data/process-extraction.d.ts → esm/process/extraction.d.ts} +0 -0
  80. /package/esm/{data/process-extraction.js → process/extraction.js} +0 -0
  81. /package/{data/data-file-embedding.d.ts → esm/process/file-embedding.d.ts} +0 -0
  82. /package/{data/data-image-embedding.d.ts → esm/process/image-embedding.d.ts} +0 -0
  83. /package/esm/{data/data-image-embedding.js → process/image-embedding.js} +0 -0
  84. /package/{data/process-image-versions.d.ts → esm/process/image-versions.d.ts} +0 -0
  85. /package/esm/{data/process-image-versions.js → process/image-versions.js} +0 -0
  86. /package/{esm/data/event-tracker.d.ts → event/tracker.d.ts} +0 -0
  87. /package/{data/event-tracker.js → event/tracker.js} +0 -0
  88. /package/{esm/data/data-job-trigger.d.ts → job/trigger.d.ts} +0 -0
  89. /package/{data/data-job-trigger.js → job/trigger.js} +0 -0
  90. /package/{esm/data/data-aggregate-limit-counter.d.ts → limit/enforce-aggregate.d.ts} +0 -0
  91. /package/{data/data-aggregate-limit-counter.js → limit/enforce-aggregate.js} +0 -0
  92. /package/{esm/data/data-limit-counter.d.ts → limit/enforce-counter.d.ts} +0 -0
  93. /package/{data/data-limit-counter.js → limit/enforce-counter.js} +0 -0
  94. /package/{esm/data/data-feature-flag.d.ts → limit/enforce-feature.d.ts} +0 -0
  95. /package/{data/data-feature-flag.js → limit/enforce-feature.js} +0 -0
  96. /package/{esm/data/data-meter-rate-limit.d.ts → limit/enforce-rate.d.ts} +0 -0
  97. /package/{data/data-meter-rate-limit.js → limit/enforce-rate.js} +0 -0
  98. /package/{esm/data/data-billing-meter.d.ts → limit/track-usage.d.ts} +0 -0
  99. /package/{data/data-billing-meter.js → limit/track-usage.js} +0 -0
  100. /package/{esm/data/limit-warning-aggregate.d.ts → limit/warning-aggregate.d.ts} +0 -0
  101. /package/{data/limit-warning-aggregate.js → limit/warning-aggregate.js} +0 -0
  102. /package/{esm/data/limit-warning-counter.d.ts → limit/warning-counter.d.ts} +0 -0
  103. /package/{data/limit-warning-counter.js → limit/warning-counter.js} +0 -0
  104. /package/{esm/data/limit-warning-rate.d.ts → limit/warning-rate.d.ts} +0 -0
  105. /package/{data/limit-warning-rate.js → limit/warning-rate.js} +0 -0
  106. /package/{esm/data/data-chunks.d.ts → process/chunks.d.ts} +0 -0
  107. /package/{esm/data/process-extraction.d.ts → process/extraction.d.ts} +0 -0
  108. /package/{data/process-extraction.js → process/extraction.js} +0 -0
  109. /package/{esm/data/data-file-embedding.d.ts → process/file-embedding.d.ts} +0 -0
  110. /package/{esm/data/data-image-embedding.d.ts → process/image-embedding.d.ts} +0 -0
  111. /package/{data/data-image-embedding.js → process/image-embedding.js} +0 -0
  112. /package/{esm/data/process-image-versions.d.ts → process/image-versions.d.ts} +0 -0
  113. /package/{data/process-image-versions.js → process/image-versions.js} +0 -0
@@ -808,9 +808,13 @@ export interface BlueprintBucketSeed {
808
808
  /** CORS allowed origins for this bucket. */
809
809
  allowed_origins?: string[];
810
810
  }
811
- /** 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. */
811
+ /** Storage configuration with optional scope. When used at the top level of a blueprint, the scope field controls whether storage is app-level ("app", default) or org-level ("org"). Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions. */
812
812
  export interface BlueprintStorageConfig {
813
- /** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. Only used for app-level storage (not entity-scoped). */
813
+ /** Storage scope. "app" (default) creates app-level storage (no owner_id). "org" creates per-org/user storage (owner_id = org entity id, buckets seeded per-entity via AFTER INSERT trigger). Only "app" and "org" are allowed — child entity types get storage via entity_types[].storage. */
814
+ scope?: 'app' | 'org';
815
+ /** Discriminator for multi-module storage. Defaults to "default" (omitted from table names). Non-default keys appear as an infix: {prefix}_{storage_key}_buckets. Max 16 chars, lowercase snake_case. */
816
+ storage_key?: string;
817
+ /** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. */
814
818
  buckets?: BlueprintBucketSeed[];
815
819
  /** Override for presigned upload URL expiry time in seconds. */
816
820
  upload_url_expiry_seconds?: number;
@@ -881,10 +885,10 @@ export interface BlueprintEntityTableProvision {
881
885
  /** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
882
886
  policies?: BlueprintPolicy[];
883
887
  }
884
- /** 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. */
888
+ /** An entity type entry for Phase 0 of construct_blueprint(). When name is provided, provisions a new entity type with its own entity table, membership modules, and security policies via entity_type_provision. When name is omitted and only prefix is given, extends an existing entity type (e.g., the built-in "org") with additional capabilities like storage — without creating a new entity type. */
885
889
  export interface BlueprintEntityType {
886
- /** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
887
- name: string;
890
+ /** Entity type name (e.g., "data_room", "channel", "department"). Required when creating a new entity type. Omit when extending an existing entity type (e.g., prefix: "org") — the entry will add storage/config to the existing type without creating a new one. */
891
+ name?: string;
888
892
  /** Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming. */
889
893
  prefix: string;
890
894
  /** Human-readable description of this entity type. */
@@ -901,8 +905,6 @@ export interface BlueprintEntityType {
901
905
  has_profiles?: boolean;
902
906
  /** Whether to provision a levels module for this entity type. Defaults to false. */
903
907
  has_levels?: boolean;
904
- /** Whether to provision a storage module (buckets, files tables) for this entity type. Defaults to false. */
905
- has_storage?: boolean;
906
908
  /** Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false. */
907
909
  has_invites?: boolean;
908
910
  /** Whether to auto-attach an EventTracker to the claimed_invites table for invite-based achievements. Requires has_invites=true AND has_levels=true. When true, records 'invite_claimed' events credited to the sender (inviter) on each claimed invite. Defaults to false. */
@@ -911,8 +913,8 @@ export interface BlueprintEntityType {
911
913
  skip_entity_policies?: boolean;
912
914
  /** 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). */
913
915
  table_provision?: BlueprintEntityTableProvision;
914
- /** 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). */
915
- storage?: BlueprintStorageConfig;
916
+ /** Storage configuration (array-only). A non-empty array enables storage provisioning. Each entry creates a separate storage module with its own tables ({prefix}_{storage_key}_buckets/files). Controls RLS policies, bucket seeding, and module-level settings. */
917
+ storage?: BlueprintStorageConfig[];
916
918
  }
917
919
  /** String shorthand -- just the node type name. */
918
920
  export type BlueprintNodeShorthand = 'AuthzAllowAll' | 'AuthzAppMembership' | 'AuthzComposite' | 'AuthzDenyAll' | 'AuthzFilePath' | 'AuthzDirectOwner' | 'AuthzDirectOwnerAny' | 'AuthzEntityMembership' | 'AuthzMemberList' | 'AuthzNotReadOnly' | 'AuthzOrgHierarchy' | 'AuthzPeerOwnership' | 'AuthzPublishable' | 'AuthzRelatedEntityMembership' | 'AuthzRelatedMemberList' | 'AuthzRelatedPeerOwnership' | 'AuthzTemporal' | 'CheckGreaterThan' | 'CheckLessThan' | 'CheckNotEqual' | 'CheckOneOf' | 'LimitAggregate' | 'BillingMeter' | 'DataBulk' | 'ProcessChunks' | 'DataCompositeField' | 'DataDirectOwner' | 'DataEntityMembership' | 'ProcessFileEmbedding' | 'LimitFeatureFlag' | 'DataForceCurrentUser' | 'DataId' | 'ProcessImageEmbedding' | 'DataImmutableFields' | 'DataInflection' | 'DataInheritFromParent' | 'JobTrigger' | 'LimitCounter' | 'DataJsonb' | 'DataOwnedFields' | 'ProcessExtraction' | 'ProcessImageVersions' | 'DataOwnershipInEntity' | 'DataPeoplestamps' | 'DataPublishable' | 'DataRealtime' | 'DataSlug' | 'DataSoftDelete' | 'DataStatusField' | 'DataTags' | 'DataTimestamps' | 'SearchBm25' | 'SearchFullText' | 'SearchSpatial' | 'SearchSpatialAggregate' | 'SearchTrgm' | 'SearchUnified' | 'SearchVector' | 'TableOrganizationSettings' | 'TableUserProfiles' | 'TableUserSettings';
@@ -1177,8 +1179,8 @@ export interface BlueprintDefinition {
1177
1179
  unique_constraints?: BlueprintUniqueConstraint[];
1178
1180
  /** Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security. */
1179
1181
  entity_types?: BlueprintEntityType[];
1180
- /** 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. */
1181
- storage?: BlueprintStorageConfig;
1182
+ /** Top-level storage configuration array. Each entry has an optional scope ("app" or "org"). App-scoped (default) creates storage_module with membership_type = NULL. Org-scoped creates per-org/user storage with owner_id and AFTER INSERT bucket seeding. When infra is installed, a private "functions" bucket is auto-injected into org-scoped entries. For child entity type storage, use entity_types[].storage instead. */
1183
+ storage?: BlueprintStorageConfig[];
1182
1184
  /** Achievement definitions. Each entry creates a level with requirements and optional rewards in the events_module. Requires events_module to be provisioned (e.g., via entity_types[].has_levels = true or modules includes events_module). */
1183
1185
  achievements?: BlueprintAchievement[];
1184
1186
  }
@@ -515,7 +515,12 @@ function buildBlueprintBucketSeed() {
515
515
  */
516
516
  function buildBlueprintStorageConfig() {
517
517
  return addJSDoc(exportInterface('BlueprintStorageConfig', [
518
- 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).'),
518
+ addJSDoc(optionalProp('scope', t.tsUnionType([
519
+ t.tsLiteralType(t.stringLiteral('app')),
520
+ t.tsLiteralType(t.stringLiteral('org'))
521
+ ])), 'Storage scope. "app" (default) creates app-level storage (no owner_id). "org" creates per-org/user storage (owner_id = org entity id, buckets seeded per-entity via AFTER INSERT trigger). Only "app" and "org" are allowed — child entity types get storage via entity_types[].storage.'),
522
+ addJSDoc(optionalProp('storage_key', t.tsStringKeyword()), 'Discriminator for multi-module storage. Defaults to "default" (omitted from table names). Non-default keys appear as an infix: {prefix}_{storage_key}_buckets. Max 16 chars, lowercase snake_case.'),
523
+ addJSDoc(optionalProp('buckets', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintBucketSeed')))), 'Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning.'),
519
524
  addJSDoc(optionalProp('upload_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned upload URL expiry time in seconds.'),
520
525
  addJSDoc(optionalProp('download_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned download URL expiry time in seconds.'),
521
526
  addJSDoc(optionalProp('default_max_file_size', t.tsNumberKeyword()), 'Default maximum file size in bytes for the storage module.'),
@@ -526,7 +531,7 @@ function buildBlueprintStorageConfig() {
526
531
  optionalProp('files', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))),
527
532
  optionalProp('buckets', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision')))
528
533
  ])), 'Per-table overrides for storage tables. Each key targets a specific storage table (files, buckets) 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.')
529
- ]), '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.');
534
+ ]), 'Storage configuration with optional scope. When used at the top level of a blueprint, the scope field controls whether storage is app-level (\"app\", default) or org-level (\"org\"). Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions.');
530
535
  }
531
536
  // ---------------------------------------------------------------------------
532
537
  // Achievement types
@@ -573,7 +578,7 @@ function buildBlueprintEntityTableProvision() {
573
578
  }
574
579
  function buildBlueprintEntityType() {
575
580
  return addJSDoc(exportInterface('BlueprintEntityType', [
576
- addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
581
+ addJSDoc(optionalProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Required when creating a new entity type. Omit when extending an existing entity type (e.g., prefix: "org") — the entry will add storage/config to the existing type without creating a new one.'),
577
582
  addJSDoc(requiredProp('prefix', t.tsStringKeyword()), 'Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming.'),
578
583
  addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
579
584
  addJSDoc(optionalProp('parent_entity', t.tsStringKeyword()), 'Parent entity type name. Defaults to "org".'),
@@ -582,13 +587,12 @@ function buildBlueprintEntityType() {
582
587
  addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
583
588
  addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
584
589
  addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
585
- addJSDoc(optionalProp('has_storage', t.tsBooleanKeyword()), 'Whether to provision a storage module (buckets, files tables) for this entity type. Defaults to false.'),
586
590
  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.'),
587
591
  addJSDoc(optionalProp('has_invite_achievements', t.tsBooleanKeyword()), "Whether to auto-attach an EventTracker to the claimed_invites table for invite-based achievements. Requires has_invites=true AND has_levels=true. When true, records 'invite_claimed' events credited to the sender (inviter) on each claimed invite. Defaults to false."),
588
592
  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.'),
589
593
  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).'),
590
- 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).')
591
- ]), '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.');
594
+ addJSDoc(optionalProp('storage', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintStorageConfig')))), 'Storage module configuration array. Each entry provisions a separate storage module with its own tables, RLS, and settings. When non-empty, has_storage is derived as true. Each entry may specify a storage_key for multi-module support (defaults to "default").')
595
+ ]), 'An entity type entry for Phase 0 of construct_blueprint(). When name is provided, provisions a new entity type with its own entity table, membership modules, and security policies via entity_type_provision. When name is omitted and only prefix is given, extends an existing entity type (e.g., the built-in "org") with additional capabilities like storage — without creating a new entity type.');
592
596
  }
593
597
  function buildBlueprintTable() {
594
598
  return addJSDoc(exportInterface('BlueprintTable', [
@@ -615,7 +619,7 @@ function buildBlueprintDefinition() {
615
619
  addJSDoc(optionalProp('full_text_searches', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFullTextSearch')))), 'Full-text search configurations.'),
616
620
  addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintUniqueConstraint')))), 'Unique constraints on table columns.'),
617
621
  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.'),
618
- 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.'),
622
+ addJSDoc(optionalProp('storage', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintStorageConfig')))), 'Top-level storage configuration array. Each entry has an optional scope ("app" or "org"). App-scoped (default) creates storage_module with membership_type = NULL. Org-scoped creates per-org/user storage with owner_id and AFTER INSERT bucket seeding. When infra is installed, a private "functions" bucket is auto-injected into org-scoped entries. For child entity type storage, use entity_types[].storage instead.'),
619
623
  addJSDoc(optionalProp('achievements', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintAchievement')))), 'Achievement definitions. Each entry creates a level with requirements and optional rewards in the events_module. Requires events_module to be provisioned (e.g., via entity_types[].has_levels = true or modules includes events_module).')
620
624
  ]), 'The complete blueprint definition -- the JSONB shape accepted by construct_blueprint().');
621
625
  }
package/data/index.d.ts CHANGED
@@ -2,32 +2,17 @@ export { CheckGreaterThan } from './check-greater-than';
2
2
  export { CheckLessThan } from './check-less-than';
3
3
  export { CheckNotEqual } from './check-not-equal';
4
4
  export { CheckOneOf } from './check-one-of';
5
- export { LimitEnforceAggregate } from './data-aggregate-limit-counter';
6
- export { LimitTrackUsage } from './data-billing-meter';
7
5
  export { DataBulk } from './data-bulk';
8
- export { ProcessChunks } from './data-chunks';
9
6
  export { DataCompositeField } from './data-composite-field';
10
7
  export { DataDirectOwner } from './data-direct-owner';
11
8
  export { DataEntityMembership } from './data-entity-membership';
12
- export { EventTracker } from './event-tracker';
13
- export { ProcessFileEmbedding } from './data-file-embedding';
14
- export { LimitEnforceFeature } from './data-feature-flag';
15
9
  export { DataForceCurrentUser } from './data-force-current-user';
16
10
  export { DataId } from './data-id';
17
- export { ProcessImageEmbedding } from './data-image-embedding';
18
11
  export { DataImmutableFields } from './data-immutable-fields';
19
12
  export { DataInflection } from './data-inflection';
20
13
  export { DataInheritFromParent } from './data-inherit-from-parent';
21
- export { JobTrigger } from './data-job-trigger';
22
- export { LimitEnforceCounter } from './data-limit-counter';
23
- export { LimitEnforceRate } from './data-meter-rate-limit';
24
- export { LimitWarningCounter } from './limit-warning-counter';
25
- export { LimitWarningAggregate } from './limit-warning-aggregate';
26
- export { LimitWarningRate } from './limit-warning-rate';
27
14
  export { DataJsonb } from './data-jsonb';
28
15
  export { DataOwnedFields } from './data-owned-fields';
29
- export { ProcessExtraction } from './process-extraction';
30
- export { ProcessImageVersions } from './process-image-versions';
31
16
  export { DataOwnershipInEntity } from './data-ownership-in-entity';
32
17
  export { DataPeoplestamps } from './data-peoplestamps';
33
18
  export { DataPublishable } from './data-publishable';
package/data/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TableUserSettings = exports.TableUserProfiles = exports.TableOrganizationSettings = exports.SearchVector = exports.SearchUnified = exports.SearchTrgm = exports.SearchSpatialAggregate = exports.SearchSpatial = exports.SearchFullText = exports.SearchBm25 = exports.DataTimestamps = exports.DataTags = exports.DataStatusField = exports.DataSoftDelete = exports.DataSlug = exports.DataRealtime = exports.DataPublishable = exports.DataPeoplestamps = exports.DataOwnershipInEntity = exports.ProcessImageVersions = exports.ProcessExtraction = exports.DataOwnedFields = exports.DataJsonb = exports.LimitWarningRate = exports.LimitWarningAggregate = exports.LimitWarningCounter = exports.LimitEnforceRate = exports.LimitEnforceCounter = exports.JobTrigger = exports.DataInheritFromParent = exports.DataInflection = exports.DataImmutableFields = exports.ProcessImageEmbedding = exports.DataId = exports.DataForceCurrentUser = exports.LimitEnforceFeature = exports.ProcessFileEmbedding = exports.EventTracker = exports.DataEntityMembership = exports.DataDirectOwner = exports.DataCompositeField = exports.ProcessChunks = exports.DataBulk = exports.LimitTrackUsage = exports.LimitEnforceAggregate = exports.CheckOneOf = exports.CheckNotEqual = exports.CheckLessThan = exports.CheckGreaterThan = void 0;
3
+ exports.TableUserSettings = exports.TableUserProfiles = exports.TableOrganizationSettings = exports.SearchVector = exports.SearchUnified = exports.SearchTrgm = exports.SearchSpatialAggregate = exports.SearchSpatial = exports.SearchFullText = exports.SearchBm25 = exports.DataTimestamps = exports.DataTags = exports.DataStatusField = exports.DataSoftDelete = exports.DataSlug = exports.DataRealtime = exports.DataPublishable = exports.DataPeoplestamps = exports.DataOwnershipInEntity = exports.DataOwnedFields = exports.DataJsonb = exports.DataInheritFromParent = exports.DataInflection = exports.DataImmutableFields = exports.DataId = exports.DataForceCurrentUser = exports.DataEntityMembership = exports.DataDirectOwner = exports.DataCompositeField = exports.DataBulk = exports.CheckOneOf = exports.CheckNotEqual = exports.CheckLessThan = exports.CheckGreaterThan = void 0;
4
4
  var check_greater_than_1 = require("./check-greater-than");
5
5
  Object.defineProperty(exports, "CheckGreaterThan", { enumerable: true, get: function () { return check_greater_than_1.CheckGreaterThan; } });
6
6
  var check_less_than_1 = require("./check-less-than");
@@ -9,58 +9,28 @@ var check_not_equal_1 = require("./check-not-equal");
9
9
  Object.defineProperty(exports, "CheckNotEqual", { enumerable: true, get: function () { return check_not_equal_1.CheckNotEqual; } });
10
10
  var check_one_of_1 = require("./check-one-of");
11
11
  Object.defineProperty(exports, "CheckOneOf", { enumerable: true, get: function () { return check_one_of_1.CheckOneOf; } });
12
- var data_aggregate_limit_counter_1 = require("./data-aggregate-limit-counter");
13
- Object.defineProperty(exports, "LimitEnforceAggregate", { enumerable: true, get: function () { return data_aggregate_limit_counter_1.LimitEnforceAggregate; } });
14
- var data_billing_meter_1 = require("./data-billing-meter");
15
- Object.defineProperty(exports, "LimitTrackUsage", { enumerable: true, get: function () { return data_billing_meter_1.LimitTrackUsage; } });
16
12
  var data_bulk_1 = require("./data-bulk");
17
13
  Object.defineProperty(exports, "DataBulk", { enumerable: true, get: function () { return data_bulk_1.DataBulk; } });
18
- var data_chunks_1 = require("./data-chunks");
19
- Object.defineProperty(exports, "ProcessChunks", { enumerable: true, get: function () { return data_chunks_1.ProcessChunks; } });
20
14
  var data_composite_field_1 = require("./data-composite-field");
21
15
  Object.defineProperty(exports, "DataCompositeField", { enumerable: true, get: function () { return data_composite_field_1.DataCompositeField; } });
22
16
  var data_direct_owner_1 = require("./data-direct-owner");
23
17
  Object.defineProperty(exports, "DataDirectOwner", { enumerable: true, get: function () { return data_direct_owner_1.DataDirectOwner; } });
24
18
  var data_entity_membership_1 = require("./data-entity-membership");
25
19
  Object.defineProperty(exports, "DataEntityMembership", { enumerable: true, get: function () { return data_entity_membership_1.DataEntityMembership; } });
26
- var event_tracker_1 = require("./event-tracker");
27
- Object.defineProperty(exports, "EventTracker", { enumerable: true, get: function () { return event_tracker_1.EventTracker; } });
28
- var data_file_embedding_1 = require("./data-file-embedding");
29
- Object.defineProperty(exports, "ProcessFileEmbedding", { enumerable: true, get: function () { return data_file_embedding_1.ProcessFileEmbedding; } });
30
- var data_feature_flag_1 = require("./data-feature-flag");
31
- Object.defineProperty(exports, "LimitEnforceFeature", { enumerable: true, get: function () { return data_feature_flag_1.LimitEnforceFeature; } });
32
20
  var data_force_current_user_1 = require("./data-force-current-user");
33
21
  Object.defineProperty(exports, "DataForceCurrentUser", { enumerable: true, get: function () { return data_force_current_user_1.DataForceCurrentUser; } });
34
22
  var data_id_1 = require("./data-id");
35
23
  Object.defineProperty(exports, "DataId", { enumerable: true, get: function () { return data_id_1.DataId; } });
36
- var data_image_embedding_1 = require("./data-image-embedding");
37
- Object.defineProperty(exports, "ProcessImageEmbedding", { enumerable: true, get: function () { return data_image_embedding_1.ProcessImageEmbedding; } });
38
24
  var data_immutable_fields_1 = require("./data-immutable-fields");
39
25
  Object.defineProperty(exports, "DataImmutableFields", { enumerable: true, get: function () { return data_immutable_fields_1.DataImmutableFields; } });
40
26
  var data_inflection_1 = require("./data-inflection");
41
27
  Object.defineProperty(exports, "DataInflection", { enumerable: true, get: function () { return data_inflection_1.DataInflection; } });
42
28
  var data_inherit_from_parent_1 = require("./data-inherit-from-parent");
43
29
  Object.defineProperty(exports, "DataInheritFromParent", { enumerable: true, get: function () { return data_inherit_from_parent_1.DataInheritFromParent; } });
44
- var data_job_trigger_1 = require("./data-job-trigger");
45
- Object.defineProperty(exports, "JobTrigger", { enumerable: true, get: function () { return data_job_trigger_1.JobTrigger; } });
46
- var data_limit_counter_1 = require("./data-limit-counter");
47
- Object.defineProperty(exports, "LimitEnforceCounter", { enumerable: true, get: function () { return data_limit_counter_1.LimitEnforceCounter; } });
48
- var data_meter_rate_limit_1 = require("./data-meter-rate-limit");
49
- Object.defineProperty(exports, "LimitEnforceRate", { enumerable: true, get: function () { return data_meter_rate_limit_1.LimitEnforceRate; } });
50
- var limit_warning_counter_1 = require("./limit-warning-counter");
51
- Object.defineProperty(exports, "LimitWarningCounter", { enumerable: true, get: function () { return limit_warning_counter_1.LimitWarningCounter; } });
52
- var limit_warning_aggregate_1 = require("./limit-warning-aggregate");
53
- Object.defineProperty(exports, "LimitWarningAggregate", { enumerable: true, get: function () { return limit_warning_aggregate_1.LimitWarningAggregate; } });
54
- var limit_warning_rate_1 = require("./limit-warning-rate");
55
- Object.defineProperty(exports, "LimitWarningRate", { enumerable: true, get: function () { return limit_warning_rate_1.LimitWarningRate; } });
56
30
  var data_jsonb_1 = require("./data-jsonb");
57
31
  Object.defineProperty(exports, "DataJsonb", { enumerable: true, get: function () { return data_jsonb_1.DataJsonb; } });
58
32
  var data_owned_fields_1 = require("./data-owned-fields");
59
33
  Object.defineProperty(exports, "DataOwnedFields", { enumerable: true, get: function () { return data_owned_fields_1.DataOwnedFields; } });
60
- var process_extraction_1 = require("./process-extraction");
61
- Object.defineProperty(exports, "ProcessExtraction", { enumerable: true, get: function () { return process_extraction_1.ProcessExtraction; } });
62
- var process_image_versions_1 = require("./process-image-versions");
63
- Object.defineProperty(exports, "ProcessImageVersions", { enumerable: true, get: function () { return process_image_versions_1.ProcessImageVersions; } });
64
34
  var data_ownership_in_entity_1 = require("./data-ownership-in-entity");
65
35
  Object.defineProperty(exports, "DataOwnershipInEntity", { enumerable: true, get: function () { return data_ownership_in_entity_1.DataOwnershipInEntity; } });
66
36
  var data_peoplestamps_1 = require("./data-peoplestamps");
@@ -808,9 +808,13 @@ export interface BlueprintBucketSeed {
808
808
  /** CORS allowed origins for this bucket. */
809
809
  allowed_origins?: string[];
810
810
  }
811
- /** 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. */
811
+ /** Storage configuration with optional scope. When used at the top level of a blueprint, the scope field controls whether storage is app-level ("app", default) or org-level ("org"). Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions. */
812
812
  export interface BlueprintStorageConfig {
813
- /** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. Only used for app-level storage (not entity-scoped). */
813
+ /** Storage scope. "app" (default) creates app-level storage (no owner_id). "org" creates per-org/user storage (owner_id = org entity id, buckets seeded per-entity via AFTER INSERT trigger). Only "app" and "org" are allowed — child entity types get storage via entity_types[].storage. */
814
+ scope?: 'app' | 'org';
815
+ /** Discriminator for multi-module storage. Defaults to "default" (omitted from table names). Non-default keys appear as an infix: {prefix}_{storage_key}_buckets. Max 16 chars, lowercase snake_case. */
816
+ storage_key?: string;
817
+ /** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. */
814
818
  buckets?: BlueprintBucketSeed[];
815
819
  /** Override for presigned upload URL expiry time in seconds. */
816
820
  upload_url_expiry_seconds?: number;
@@ -881,10 +885,10 @@ export interface BlueprintEntityTableProvision {
881
885
  /** RLS policies for the entity table. When present, these policies fully replace the five default entity-table policies (is_visible becomes a no-op). */
882
886
  policies?: BlueprintPolicy[];
883
887
  }
884
- /** 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. */
888
+ /** An entity type entry for Phase 0 of construct_blueprint(). When name is provided, provisions a new entity type with its own entity table, membership modules, and security policies via entity_type_provision. When name is omitted and only prefix is given, extends an existing entity type (e.g., the built-in "org") with additional capabilities like storage — without creating a new entity type. */
885
889
  export interface BlueprintEntityType {
886
- /** Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database. */
887
- name: string;
890
+ /** Entity type name (e.g., "data_room", "channel", "department"). Required when creating a new entity type. Omit when extending an existing entity type (e.g., prefix: "org") — the entry will add storage/config to the existing type without creating a new one. */
891
+ name?: string;
888
892
  /** Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming. */
889
893
  prefix: string;
890
894
  /** Human-readable description of this entity type. */
@@ -901,8 +905,6 @@ export interface BlueprintEntityType {
901
905
  has_profiles?: boolean;
902
906
  /** Whether to provision a levels module for this entity type. Defaults to false. */
903
907
  has_levels?: boolean;
904
- /** Whether to provision a storage module (buckets, files tables) for this entity type. Defaults to false. */
905
- has_storage?: boolean;
906
908
  /** Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false. */
907
909
  has_invites?: boolean;
908
910
  /** Whether to auto-attach an EventTracker to the claimed_invites table for invite-based achievements. Requires has_invites=true AND has_levels=true. When true, records 'invite_claimed' events credited to the sender (inviter) on each claimed invite. Defaults to false. */
@@ -911,8 +913,8 @@ export interface BlueprintEntityType {
911
913
  skip_entity_policies?: boolean;
912
914
  /** 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). */
913
915
  table_provision?: BlueprintEntityTableProvision;
914
- /** 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). */
915
- storage?: BlueprintStorageConfig;
916
+ /** Storage configuration (array-only). A non-empty array enables storage provisioning. Each entry creates a separate storage module with its own tables ({prefix}_{storage_key}_buckets/files). Controls RLS policies, bucket seeding, and module-level settings. */
917
+ storage?: BlueprintStorageConfig[];
916
918
  }
917
919
  /** String shorthand -- just the node type name. */
918
920
  export type BlueprintNodeShorthand = 'AuthzAllowAll' | 'AuthzAppMembership' | 'AuthzComposite' | 'AuthzDenyAll' | 'AuthzFilePath' | 'AuthzDirectOwner' | 'AuthzDirectOwnerAny' | 'AuthzEntityMembership' | 'AuthzMemberList' | 'AuthzNotReadOnly' | 'AuthzOrgHierarchy' | 'AuthzPeerOwnership' | 'AuthzPublishable' | 'AuthzRelatedEntityMembership' | 'AuthzRelatedMemberList' | 'AuthzRelatedPeerOwnership' | 'AuthzTemporal' | 'CheckGreaterThan' | 'CheckLessThan' | 'CheckNotEqual' | 'CheckOneOf' | 'LimitAggregate' | 'BillingMeter' | 'DataBulk' | 'ProcessChunks' | 'DataCompositeField' | 'DataDirectOwner' | 'DataEntityMembership' | 'ProcessFileEmbedding' | 'LimitFeatureFlag' | 'DataForceCurrentUser' | 'DataId' | 'ProcessImageEmbedding' | 'DataImmutableFields' | 'DataInflection' | 'DataInheritFromParent' | 'JobTrigger' | 'LimitCounter' | 'DataJsonb' | 'DataOwnedFields' | 'ProcessExtraction' | 'ProcessImageVersions' | 'DataOwnershipInEntity' | 'DataPeoplestamps' | 'DataPublishable' | 'DataRealtime' | 'DataSlug' | 'DataSoftDelete' | 'DataStatusField' | 'DataTags' | 'DataTimestamps' | 'SearchBm25' | 'SearchFullText' | 'SearchSpatial' | 'SearchSpatialAggregate' | 'SearchTrgm' | 'SearchUnified' | 'SearchVector' | 'TableOrganizationSettings' | 'TableUserProfiles' | 'TableUserSettings';
@@ -1177,8 +1179,8 @@ export interface BlueprintDefinition {
1177
1179
  unique_constraints?: BlueprintUniqueConstraint[];
1178
1180
  /** Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security. */
1179
1181
  entity_types?: BlueprintEntityType[];
1180
- /** 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. */
1181
- storage?: BlueprintStorageConfig;
1182
+ /** Top-level storage configuration array. Each entry has an optional scope ("app" or "org"). App-scoped (default) creates storage_module with membership_type = NULL. Org-scoped creates per-org/user storage with owner_id and AFTER INSERT bucket seeding. When infra is installed, a private "functions" bucket is auto-injected into org-scoped entries. For child entity type storage, use entity_types[].storage instead. */
1183
+ storage?: BlueprintStorageConfig[];
1182
1184
  /** Achievement definitions. Each entry creates a level with requirements and optional rewards in the events_module. Requires events_module to be provisioned (e.g., via entity_types[].has_levels = true or modules includes events_module). */
1183
1185
  achievements?: BlueprintAchievement[];
1184
1186
  }
@@ -480,7 +480,12 @@ function buildBlueprintBucketSeed() {
480
480
  */
481
481
  function buildBlueprintStorageConfig() {
482
482
  return addJSDoc(exportInterface('BlueprintStorageConfig', [
483
- 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).'),
483
+ addJSDoc(optionalProp('scope', t.tsUnionType([
484
+ t.tsLiteralType(t.stringLiteral('app')),
485
+ t.tsLiteralType(t.stringLiteral('org'))
486
+ ])), 'Storage scope. "app" (default) creates app-level storage (no owner_id). "org" creates per-org/user storage (owner_id = org entity id, buckets seeded per-entity via AFTER INSERT trigger). Only "app" and "org" are allowed — child entity types get storage via entity_types[].storage.'),
487
+ addJSDoc(optionalProp('storage_key', t.tsStringKeyword()), 'Discriminator for multi-module storage. Defaults to "default" (omitted from table names). Non-default keys appear as an infix: {prefix}_{storage_key}_buckets. Max 16 chars, lowercase snake_case.'),
488
+ addJSDoc(optionalProp('buckets', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintBucketSeed')))), 'Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning.'),
484
489
  addJSDoc(optionalProp('upload_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned upload URL expiry time in seconds.'),
485
490
  addJSDoc(optionalProp('download_url_expiry_seconds', t.tsNumberKeyword()), 'Override for presigned download URL expiry time in seconds.'),
486
491
  addJSDoc(optionalProp('default_max_file_size', t.tsNumberKeyword()), 'Default maximum file size in bytes for the storage module.'),
@@ -491,7 +496,7 @@ function buildBlueprintStorageConfig() {
491
496
  optionalProp('files', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision'))),
492
497
  optionalProp('buckets', t.tsTypeReference(t.identifier('BlueprintEntityTableProvision')))
493
498
  ])), 'Per-table overrides for storage tables. Each key targets a specific storage table (files, buckets) 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.')
494
- ]), '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.');
499
+ ]), 'Storage configuration with optional scope. When used at the top level of a blueprint, the scope field controls whether storage is app-level (\"app\", default) or org-level (\"org\"). Seeds initial buckets, overrides module-level settings (expiry times, file size limits, CORS), and provides per-table provisioning overrides via provisions.');
495
500
  }
496
501
  // ---------------------------------------------------------------------------
497
502
  // Achievement types
@@ -538,7 +543,7 @@ function buildBlueprintEntityTableProvision() {
538
543
  }
539
544
  function buildBlueprintEntityType() {
540
545
  return addJSDoc(exportInterface('BlueprintEntityType', [
541
- addJSDoc(requiredProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Must be unique per database.'),
546
+ addJSDoc(optionalProp('name', t.tsStringKeyword()), 'Entity type name (e.g., "data_room", "channel", "department"). Required when creating a new entity type. Omit when extending an existing entity type (e.g., prefix: "org") — the entry will add storage/config to the existing type without creating a new one.'),
542
547
  addJSDoc(requiredProp('prefix', t.tsStringKeyword()), 'Short prefix for generated objects (e.g., "dr", "ch", "dept"). Used in table/trigger naming.'),
543
548
  addJSDoc(optionalProp('description', t.tsStringKeyword()), 'Human-readable description of this entity type.'),
544
549
  addJSDoc(optionalProp('parent_entity', t.tsStringKeyword()), 'Parent entity type name. Defaults to "org".'),
@@ -547,13 +552,12 @@ function buildBlueprintEntityType() {
547
552
  addJSDoc(optionalProp('has_limits', t.tsBooleanKeyword()), 'Whether to provision a limits module for this entity type. Defaults to false.'),
548
553
  addJSDoc(optionalProp('has_profiles', t.tsBooleanKeyword()), 'Whether to provision a profiles module for this entity type. Defaults to false.'),
549
554
  addJSDoc(optionalProp('has_levels', t.tsBooleanKeyword()), 'Whether to provision a levels module for this entity type. Defaults to false.'),
550
- addJSDoc(optionalProp('has_storage', t.tsBooleanKeyword()), 'Whether to provision a storage module (buckets, files tables) for this entity type. Defaults to false.'),
551
555
  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.'),
552
556
  addJSDoc(optionalProp('has_invite_achievements', t.tsBooleanKeyword()), "Whether to auto-attach an EventTracker to the claimed_invites table for invite-based achievements. Requires has_invites=true AND has_levels=true. When true, records 'invite_claimed' events credited to the sender (inviter) on each claimed invite. Defaults to false."),
553
557
  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.'),
554
558
  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).'),
555
- 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).')
556
- ]), '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.');
559
+ addJSDoc(optionalProp('storage', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintStorageConfig')))), 'Storage module configuration array. Each entry provisions a separate storage module with its own tables, RLS, and settings. When non-empty, has_storage is derived as true. Each entry may specify a storage_key for multi-module support (defaults to "default").')
560
+ ]), 'An entity type entry for Phase 0 of construct_blueprint(). When name is provided, provisions a new entity type with its own entity table, membership modules, and security policies via entity_type_provision. When name is omitted and only prefix is given, extends an existing entity type (e.g., the built-in "org") with additional capabilities like storage — without creating a new entity type.');
557
561
  }
558
562
  function buildBlueprintTable() {
559
563
  return addJSDoc(exportInterface('BlueprintTable', [
@@ -580,7 +584,7 @@ function buildBlueprintDefinition() {
580
584
  addJSDoc(optionalProp('full_text_searches', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintFullTextSearch')))), 'Full-text search configurations.'),
581
585
  addJSDoc(optionalProp('unique_constraints', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintUniqueConstraint')))), 'Unique constraints on table columns.'),
582
586
  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.'),
583
- 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.'),
587
+ addJSDoc(optionalProp('storage', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintStorageConfig')))), 'Top-level storage configuration array. Each entry has an optional scope ("app" or "org"). App-scoped (default) creates storage_module with membership_type = NULL. Org-scoped creates per-org/user storage with owner_id and AFTER INSERT bucket seeding. When infra is installed, a private "functions" bucket is auto-injected into org-scoped entries. For child entity type storage, use entity_types[].storage instead.'),
584
588
  addJSDoc(optionalProp('achievements', t.tsArrayType(t.tsTypeReference(t.identifier('BlueprintAchievement')))), 'Achievement definitions. Each entry creates a level with requirements and optional rewards in the events_module. Requires events_module to be provisioned (e.g., via entity_types[].has_levels = true or modules includes events_module).')
585
589
  ]), 'The complete blueprint definition -- the JSONB shape accepted by construct_blueprint().');
586
590
  }
@@ -2,32 +2,17 @@ export { CheckGreaterThan } from './check-greater-than';
2
2
  export { CheckLessThan } from './check-less-than';
3
3
  export { CheckNotEqual } from './check-not-equal';
4
4
  export { CheckOneOf } from './check-one-of';
5
- export { LimitEnforceAggregate } from './data-aggregate-limit-counter';
6
- export { LimitTrackUsage } from './data-billing-meter';
7
5
  export { DataBulk } from './data-bulk';
8
- export { ProcessChunks } from './data-chunks';
9
6
  export { DataCompositeField } from './data-composite-field';
10
7
  export { DataDirectOwner } from './data-direct-owner';
11
8
  export { DataEntityMembership } from './data-entity-membership';
12
- export { EventTracker } from './event-tracker';
13
- export { ProcessFileEmbedding } from './data-file-embedding';
14
- export { LimitEnforceFeature } from './data-feature-flag';
15
9
  export { DataForceCurrentUser } from './data-force-current-user';
16
10
  export { DataId } from './data-id';
17
- export { ProcessImageEmbedding } from './data-image-embedding';
18
11
  export { DataImmutableFields } from './data-immutable-fields';
19
12
  export { DataInflection } from './data-inflection';
20
13
  export { DataInheritFromParent } from './data-inherit-from-parent';
21
- export { JobTrigger } from './data-job-trigger';
22
- export { LimitEnforceCounter } from './data-limit-counter';
23
- export { LimitEnforceRate } from './data-meter-rate-limit';
24
- export { LimitWarningCounter } from './limit-warning-counter';
25
- export { LimitWarningAggregate } from './limit-warning-aggregate';
26
- export { LimitWarningRate } from './limit-warning-rate';
27
14
  export { DataJsonb } from './data-jsonb';
28
15
  export { DataOwnedFields } from './data-owned-fields';
29
- export { ProcessExtraction } from './process-extraction';
30
- export { ProcessImageVersions } from './process-image-versions';
31
16
  export { DataOwnershipInEntity } from './data-ownership-in-entity';
32
17
  export { DataPeoplestamps } from './data-peoplestamps';
33
18
  export { DataPublishable } from './data-publishable';
package/esm/data/index.js CHANGED
@@ -2,32 +2,17 @@ export { CheckGreaterThan } from './check-greater-than';
2
2
  export { CheckLessThan } from './check-less-than';
3
3
  export { CheckNotEqual } from './check-not-equal';
4
4
  export { CheckOneOf } from './check-one-of';
5
- export { LimitEnforceAggregate } from './data-aggregate-limit-counter';
6
- export { LimitTrackUsage } from './data-billing-meter';
7
5
  export { DataBulk } from './data-bulk';
8
- export { ProcessChunks } from './data-chunks';
9
6
  export { DataCompositeField } from './data-composite-field';
10
7
  export { DataDirectOwner } from './data-direct-owner';
11
8
  export { DataEntityMembership } from './data-entity-membership';
12
- export { EventTracker } from './event-tracker';
13
- export { ProcessFileEmbedding } from './data-file-embedding';
14
- export { LimitEnforceFeature } from './data-feature-flag';
15
9
  export { DataForceCurrentUser } from './data-force-current-user';
16
10
  export { DataId } from './data-id';
17
- export { ProcessImageEmbedding } from './data-image-embedding';
18
11
  export { DataImmutableFields } from './data-immutable-fields';
19
12
  export { DataInflection } from './data-inflection';
20
13
  export { DataInheritFromParent } from './data-inherit-from-parent';
21
- export { JobTrigger } from './data-job-trigger';
22
- export { LimitEnforceCounter } from './data-limit-counter';
23
- export { LimitEnforceRate } from './data-meter-rate-limit';
24
- export { LimitWarningCounter } from './limit-warning-counter';
25
- export { LimitWarningAggregate } from './limit-warning-aggregate';
26
- export { LimitWarningRate } from './limit-warning-rate';
27
14
  export { DataJsonb } from './data-jsonb';
28
15
  export { DataOwnedFields } from './data-owned-fields';
29
- export { ProcessExtraction } from './process-extraction';
30
- export { ProcessImageVersions } from './process-image-versions';
31
16
  export { DataOwnershipInEntity } from './data-ownership-in-entity';
32
17
  export { DataPeoplestamps } from './data-peoplestamps';
33
18
  export { DataPublishable } from './data-publishable';
@@ -0,0 +1,2 @@
1
+ export { EventReferral } from './referral';
2
+ export { EventTracker } from './tracker';
@@ -0,0 +1,2 @@
1
+ export { EventReferral } from './referral';
2
+ export { EventTracker } from './tracker';
@@ -0,0 +1,2 @@
1
+ import type { NodeTypeDefinition } from '../types';
2
+ export declare const EventReferral: NodeTypeDefinition;
@@ -0,0 +1,62 @@
1
+ import { conditionDefs, conditionProperties } from '../conditions';
2
+ export const EventReferral = {
3
+ name: 'EventReferral',
4
+ slug: 'event_referral',
5
+ category: 'event',
6
+ display_name: 'Event Referral',
7
+ description: 'Creates triggers that record events for the referrer (inviter) when their ' +
8
+ 'invitees perform actions on a watched table. Resolves the referrer automatically ' +
9
+ 'via the invites module\'s claimed_invites table using the membership_type context. ' +
10
+ 'Supports the same compound condition system as EventTracker. Use with achievements ' +
11
+ 'to unlock levels and grant credits based on invitee activity.',
12
+ parameter_schema: {
13
+ type: 'object',
14
+ $defs: conditionDefs,
15
+ properties: {
16
+ event_name: {
17
+ type: 'string',
18
+ description: 'Event type name to record for the referrer (e.g., "invitee_uploaded_avatar", "invitee_completed_onboarding")'
19
+ },
20
+ events: {
21
+ type: 'array',
22
+ items: {
23
+ type: 'string',
24
+ enum: [
25
+ 'INSERT',
26
+ 'UPDATE',
27
+ 'DELETE'
28
+ ]
29
+ },
30
+ description: 'DML events that trigger recording',
31
+ default: ['INSERT']
32
+ },
33
+ actor_field: {
34
+ type: 'string',
35
+ format: 'column-ref',
36
+ description: 'Column containing the invitee (actor) ID on the source table — used to look up the referrer via claimed_invites.receiver_id',
37
+ default: 'owner_id'
38
+ },
39
+ entity_field: {
40
+ type: 'string',
41
+ format: 'column-ref',
42
+ description: 'Column containing the entity ID (org/group) for entity-scoped referral events. Omit for user-only events.'
43
+ },
44
+ auto_register_type: {
45
+ type: 'boolean',
46
+ description: 'Automatically register the event_name in event_types during provisioning',
47
+ default: true
48
+ },
49
+ ...conditionProperties
50
+ },
51
+ required: [
52
+ 'event_name'
53
+ ]
54
+ },
55
+ tags: [
56
+ 'events',
57
+ 'referral',
58
+ 'invites',
59
+ 'analytics',
60
+ 'tracking'
61
+ ]
62
+ };
package/esm/index.d.ts CHANGED
@@ -2,7 +2,11 @@ export * from './authz';
2
2
  export * from './blueprint-types.generated';
3
3
  export * from './conditions';
4
4
  export * from './data';
5
+ export * from './event';
6
+ export * from './job';
7
+ export * from './limit';
5
8
  export * from './module-presets';
9
+ export * from './process';
6
10
  export * from './relation';
7
11
  export type { JSONSchema, NodeTypeDefinition } from './types';
8
12
  export * from './view';
package/esm/index.js CHANGED
@@ -2,16 +2,28 @@ export * from './authz';
2
2
  export * from './blueprint-types.generated';
3
3
  export * from './conditions';
4
4
  export * from './data';
5
+ export * from './event';
6
+ export * from './job';
7
+ export * from './limit';
5
8
  export * from './module-presets';
9
+ export * from './process';
6
10
  export * from './relation';
7
11
  export * from './view';
8
12
  import * as authz from './authz';
9
13
  import * as data from './data';
14
+ import * as event from './event';
15
+ import * as job from './job';
16
+ import * as limit from './limit';
17
+ import * as process from './process';
10
18
  import * as relation from './relation';
11
19
  import * as view from './view';
12
20
  export const allNodeTypes = [
13
21
  ...Object.values(authz),
14
22
  ...Object.values(data),
23
+ ...Object.values(event),
24
+ ...Object.values(job),
25
+ ...Object.values(limit),
26
+ ...Object.values(process),
15
27
  ...Object.values(relation),
16
28
  ...Object.values(view)
17
29
  ];
@@ -0,0 +1 @@
1
+ export { JobTrigger } from './trigger';
@@ -0,0 +1 @@
1
+ export { JobTrigger } from './trigger';
@@ -0,0 +1,8 @@
1
+ export { LimitEnforceAggregate } from './enforce-aggregate';
2
+ export { LimitEnforceCounter } from './enforce-counter';
3
+ export { LimitEnforceFeature } from './enforce-feature';
4
+ export { LimitEnforceRate } from './enforce-rate';
5
+ export { LimitTrackUsage } from './track-usage';
6
+ export { LimitWarningAggregate } from './warning-aggregate';
7
+ export { LimitWarningCounter } from './warning-counter';
8
+ export { LimitWarningRate } from './warning-rate';
@@ -0,0 +1,8 @@
1
+ export { LimitEnforceAggregate } from './enforce-aggregate';
2
+ export { LimitEnforceCounter } from './enforce-counter';
3
+ export { LimitEnforceFeature } from './enforce-feature';
4
+ export { LimitEnforceRate } from './enforce-rate';
5
+ export { LimitTrackUsage } from './track-usage';
6
+ export { LimitWarningAggregate } from './warning-aggregate';
7
+ export { LimitWarningCounter } from './warning-counter';
8
+ export { LimitWarningRate } from './warning-rate';