Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions packages/node-type-registry/src/blueprint-types.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1209,7 +1209,9 @@ export interface BlueprintBucketSeed {
}
/** 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. */
export interface BlueprintStorageConfig {
/** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. Only used for app-level storage (not entity-scoped). */
/** 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. */
storage_key?: string;
/** Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning. */
buckets?: BlueprintBucketSeed[];
/** Override for presigned upload URL expiry time in seconds. */
upload_url_expiry_seconds?: number;
Expand Down Expand Up @@ -1300,8 +1302,7 @@ export interface BlueprintEntityType {
has_profiles?: boolean;
/** Whether to provision a levels module for this entity type. Defaults to false. */
has_levels?: boolean;
/** Whether to provision a storage module (buckets, files tables) for this entity type. Defaults to false. */
has_storage?: boolean;

/** Whether to provision entity-scoped invite tables ({prefix}_invites, {prefix}_claimed_invites) and a submit_{prefix}_invite_code() function. Defaults to false. */
has_invites?: boolean;
/** 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. */
Expand All @@ -1310,8 +1311,8 @@ export interface BlueprintEntityType {
skip_entity_policies?: boolean;
/** 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). */
table_provision?: BlueprintEntityTableProvision;
/** 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). */
storage?: BlueprintStorageConfig;
/** 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. */
storage?: BlueprintStorageConfig[];
}
/**
* ===========================================================================
Expand Down Expand Up @@ -1594,8 +1595,8 @@ export interface BlueprintDefinition {
unique_constraints?: BlueprintUniqueConstraint[];
/** Entity types to provision in Phase 0 (before tables). Each entry creates an entity table with membership modules and security. */
entity_types?: BlueprintEntityType[];
/** 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. */
storage?: BlueprintStorageConfig;
/** App-level storage configuration (array-only). Creates storage_module(s) (membership_type = NULL), seeds initial buckets, and overrides module-level settings. Each entry creates a separate storage module. For entity-scoped storage, use entity_types[].storage instead. */
storage?: BlueprintStorageConfig[];
/** 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). */
achievements?: BlueprintAchievement[];
}
23 changes: 14 additions & 9 deletions packages/node-type-registry/src/codegen/generate-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,14 +787,18 @@ function buildBlueprintBucketSeed(): t.ExportNamedDeclaration {
function buildBlueprintStorageConfig(): t.ExportNamedDeclaration {
return addJSDoc(
exportInterface('BlueprintStorageConfig', [
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.'
),
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).'
'Initial bucket seed entries. Each creates a row in {prefix}_buckets during provisioning.'
),
addJSDoc(
optionalProp('upload_url_expiry_seconds', t.tsNumberKeyword()),
Expand Down Expand Up @@ -1024,10 +1028,7 @@ function buildBlueprintEntityType(): t.ExportNamedDeclaration {
optionalProp('has_levels', t.tsBooleanKeyword()),
'Whether to provision a levels module for this entity type. Defaults to false.'
),
addJSDoc(
optionalProp('has_storage', t.tsBooleanKeyword()),
'Whether to provision a storage module (buckets, files tables) for this entity type. Defaults to false.'
),

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.'
Expand All @@ -1050,9 +1051,11 @@ function buildBlueprintEntityType(): t.ExportNamedDeclaration {
addJSDoc(
optionalProp(
'storage',
t.tsTypeReference(t.identifier('BlueprintStorageConfig'))
t.tsArrayType(
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).'
'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").'
)
]),
'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.'
Expand Down Expand Up @@ -1187,9 +1190,11 @@ function buildBlueprintDefinition(): t.ExportNamedDeclaration {
addJSDoc(
optionalProp(
'storage',
t.tsTypeReference(t.identifier('BlueprintStorageConfig'))
t.tsArrayType(
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.'
'App-level storage configuration array. Each entry creates a storage_module (membership_type = NULL) with its own tables and settings. For entity-scoped storage, use entity_types[].storage instead.'
),
addJSDoc(
optionalProp(
Expand Down
8 changes: 4 additions & 4 deletions packages/node-type-registry/src/module-presets/b2b-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export const PresetB2bStorage: ModulePreset = {
'hierarchy), plus `storage_module` for file uploads. The storage module creates ' +
'`app_buckets` and `app_files` tables with full RLS: AuthzPublishable for public reads, ' +
'AuthzAppMembership for member access, AuthzDirectOwner for uploader-only modify/delete. ' +
'Entity-type provisioning with `has_storage=true` adds per-scope storage tables ' +
'automatically. Choose this when your B2B app needs file uploads, avatars, attachments, ' +
'or any object storage tied to workspaces.',
'Entity-type provisioning with a non-empty `storage` array adds per-scope storage tables ' +
'automatically (multiple modules per entity via storage_key). Choose this when your B2B ' +
'app needs file uploads, avatars, attachments, or any object storage tied to workspaces.',
good_for: [
'B2B SaaS with file uploads (documents, avatars, attachments)',
'Apps where storage is scoped to orgs/workspaces',
Expand Down Expand Up @@ -65,7 +65,7 @@ export const PresetB2bStorage: ModulePreset = {
'devices_module'
],
includes_notes: {
storage_module: 'File upload infrastructure: app_buckets + app_files tables with RLS. Entity-type storage scopes layered on top via `has_storage=true`.',
storage_module: 'File upload infrastructure: app_buckets + app_files tables with RLS. Entity-type storage scopes layered on top via the `storage` array (array-only format, supports multiple modules per entity via storage_key).',
devices_module: 'Device tracking and trusted-device MFA bypass.'
},
omits_notes: {
Expand Down
Loading