@happyvertical/smrt-assets 0.57.20 → 0.57.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/package.json +8 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/__smrt-register__.ts","../src/asset-association.ts","../src/asset-associations.ts","../src/asset-status.ts","../src/asset-type.ts","../src/private-asset-access.ts","../src/asset.ts","../src/asset-capabilities.ts","../src/asset-conventions.ts","../src/asset-metafield.ts","../src/asset-metafields.ts","../src/asset-store.ts","../src/assets.ts","../src/asset-runtime.ts","../src/asset-serving.ts","../src/asset-statuses.ts","../src/asset-tag.ts","../src/asset-tags.ts","../src/asset-types.ts","../src/folder.ts","../src/folders.ts","../src/media-bundle-persistence.ts","../src/owned-asset-helpers.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"packageVersion\\\":\\\"0.57.20\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-assets:AssetAssociation\\\":{\\\"name\\\":\\\"assetassociation\\\",\\\"className\\\":\\\"AssetAssociation\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetAssociation\\\",\\\"collection\\\":\\\"assetassociations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-association.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"metaType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"_meta\\\":{\\\"required\\\":true}},\\\"metaId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"_meta\\\":{\\\"required\\\":true}},\\\"role\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"_meta\\\":{\\\"required\\\":true}},\\\"sortOrder\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"assetId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"Asset\\\",\\\"description\\\":\\\"The file attached to the related record.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The file attached to the related record.\\\",\\\"required\\\":true}}},\\\"methods\\\":{\\\"hydrate\\\":{\\\"name\\\":\\\"hydrate\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<T | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"conflictColumns\\\":[\\\"asset_id\\\",\\\"meta_type\\\",\\\"meta_id\\\",\\\"role\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtPolymorphicAssociation\\\",\\\"exportName\\\":\\\"AssetAssociation\\\",\\\"collectionExportName\\\":\\\"AssetAssociationCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"metaType\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"metaId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"role\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"assetId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"asset_associations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_associations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"meta_id\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"role\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"sort_order\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"meta_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"role\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"sort_order\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":0},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_associations_asset_id_meta_type_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"meta_type\\\",\\\"meta_id\\\",\\\"role\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_associations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_associations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_associations_meta_type_meta_id_idx\\\",\\\"columns\\\":[\\\"meta_type\\\",\\\"meta_id\\\"]}],\\\"version\\\":\\\"faa13cea\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetAssociationCollection\\\":{\\\"name\\\":\\\"assetassociationcollection\\\",\\\"className\\\":\\\"AssetAssociationCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetAssociationCollection\\\",\\\"collection\\\":\\\"assetassociations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-associations.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"byRight\\\":{\\\"name\\\":\\\"byRight\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"byLeft\\\":{\\\"name\\\":\\\"byLeft\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetAssociation[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"attach\\\":{\\\"name\\\":\\\"attach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetAssociation>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"detach\\\":{\\\"name\\\":\\\"detach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setLinks\\\":{\\\"name\\\":\\\"setLinks\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtJunctionBase\\\",\\\"extendsTypeArg\\\":\\\"AssetAssociation\\\",\\\"exportName\\\":\\\"AssetAssociationCollection\\\",\\\"collectionExportName\\\":\\\"AssetAssociationCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_associations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_associations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"meta_id\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"role\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"sort_order\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"meta_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"role\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"sort_order\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":0},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_associations_asset_id_meta_type_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"meta_type\\\",\\\"meta_id\\\",\\\"role\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_associations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_associations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_associations_meta_type_meta_id_idx\\\",\\\"columns\\\":[\\\"meta_type\\\",\\\"meta_id\\\"]}],\\\"version\\\":\\\"faa13cea\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetMetafield\\\":{\\\"name\\\":\\\"assetmetafield\\\",\\\"className\\\":\\\"AssetMetafield\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetMetafield\\\",\\\"collection\\\":\\\"assetmetafields\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-metafield.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The name of an additional asset detail, such as Width or Height.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name of an additional asset detail, such as Width or Height.\\\"}},\\\"validation\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Rules that determine which values this asset detail accepts.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Rules that determine which values this asset detail accepts.\\\"}}},\\\"methods\\\":{\\\"getValidation\\\":{\\\"name\\\":\\\"getValidation\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setValidation\\\":{\\\"name\\\":\\\"setValidation\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"rules\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AssetMetafield | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetMetafield\\\",\\\"collectionExportName\\\":\\\"AssetMetafieldCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_metafields\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_metafields\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"validation\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"validation\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_metafields_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_metafields_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_metafields_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"7393718c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetMetafieldCollection\\\":{\\\"name\\\":\\\"assetmetafieldcollection\\\",\\\"className\\\":\\\"AssetMetafieldCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetMetafieldCollection\\\",\\\"collection\\\":\\\"assetmetafields\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-metafields.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getOrCreate\\\":{\\\"name\\\":\\\"getOrCreate\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"validation\\\",\\\"type\\\":\\\"string | Record<string, unknown>\\\",\\\"optional\\\":true,\\\"unionBranches\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"Record<string, unknown>\\\"}]}],\\\"returnType\\\":\\\"Promise<AssetMetafield>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"initializeCommonMetafields\\\":{\\\"name\\\":\\\"initializeCommonMetafields\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AssetMetafield\\\",\\\"exportName\\\":\\\"AssetMetafieldCollection\\\",\\\"collectionExportName\\\":\\\"AssetMetafieldCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_metafields\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_metafields\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"validation\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"validation\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_metafields_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_metafields_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_metafields_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"7393718c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetStatus\\\":{\\\"name\\\":\\\"assetstatus\\\",\\\"className\\\":\\\"AssetStatus\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetStatus\\\",\\\"collection\\\":\\\"assetstatuses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-status.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The name of this lifecycle stage, such as Draft or Published.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name of this lifecycle stage, such as Draft or Published.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain when an asset should use this lifecycle stage.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain when an asset should use this lifecycle stage.\\\"}}},\\\"methods\\\":{\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AssetStatus | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetStatus\\\",\\\"collectionExportName\\\":\\\"AssetStatusCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_statuses\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_statuses\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_statuses_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_statuses_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_statuses_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"8251a52f\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetStatusCollection\\\":{\\\"name\\\":\\\"assetstatuscollection\\\",\\\"className\\\":\\\"AssetStatusCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetStatusCollection\\\",\\\"collection\\\":\\\"assetstatuses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-statuses.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getOrCreate\\\":{\\\"name\\\":\\\"getOrCreate\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"description\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetStatus>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"initializeCommonStatuses\\\":{\\\"name\\\":\\\"initializeCommonStatuses\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AssetStatus\\\",\\\"exportName\\\":\\\"AssetStatusCollection\\\",\\\"collectionExportName\\\":\\\"AssetStatusCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_statuses\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_statuses\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_statuses_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_statuses_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_statuses_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"8251a52f\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetTag\\\":{\\\"name\\\":\\\"assettag\\\",\\\"className\\\":\\\"AssetTag\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetTag\\\",\\\"collection\\\":\\\"assettags\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-tag.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"assetId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"Asset\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"CASCADE\\\"}},\\\"tagId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":true,\\\"related\\\":\\\"@happyvertical/smrt-tags:Tag\\\",\\\"_meta\\\":{\\\"onDelete\\\":\\\"CASCADE\\\"}}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"conflictColumns\\\":[\\\"asset_id\\\",\\\"tag_id\\\"],\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false,\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetTag\\\",\\\"collectionExportName\\\":\\\"AssetTagCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"assetId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"tagId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"crossPackageRef\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_tags\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"tag_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tag_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_tags_asset_id_tag_id_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"tag_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_tags_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_tags_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_tags_tag_id_idx\\\",\\\"columns\\\":[\\\"tag_id\\\"]}],\\\"version\\\":\\\"e60d24f5\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetTagCollection\\\":{\\\"name\\\":\\\"assettagcollection\\\",\\\"className\\\":\\\"AssetTagCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetTagCollection\\\",\\\"collection\\\":\\\"assettags\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-tags.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"byRight\\\":{\\\"name\\\":\\\"byRight\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"byLeft\\\":{\\\"name\\\":\\\"byLeft\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"attach\\\":{\\\"name\\\":\\\"attach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"detach\\\":{\\\"name\\\":\\\"detach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setLinks\\\":{\\\"name\\\":\\\"setLinks\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"rightIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false},\\\"extends\\\":\\\"SmrtJunction\\\",\\\"extendsTypeArg\\\":\\\"AssetTag\\\",\\\"exportName\\\":\\\"AssetTagCollection\\\",\\\"collectionExportName\\\":\\\"AssetTagCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_tags\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"tag_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tag_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_tags_asset_id_tag_id_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"tag_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_tags_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_tags_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_tags_tag_id_idx\\\",\\\"columns\\\":[\\\"tag_id\\\"]}],\\\"version\\\":\\\"e60d24f5\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetType\\\":{\\\"name\\\":\\\"assettype\\\",\\\"className\\\":\\\"AssetType\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetType\\\",\\\"collection\\\":\\\"assettypes\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-type.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The name of this file category, such as Image or Document.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name of this file category, such as Image or Document.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain which kinds of files belong in this category.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain which kinds of files belong in this category.\\\"}}},\\\"methods\\\":{\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AssetType | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetType\\\",\\\"collectionExportName\\\":\\\"AssetTypeCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_types\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_types\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_types_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_types_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_types_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"a9c93be2\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetTypeCollection\\\":{\\\"name\\\":\\\"assettypecollection\\\",\\\"className\\\":\\\"AssetTypeCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetTypeCollection\\\",\\\"collection\\\":\\\"assettypes\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-types.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getOrCreate\\\":{\\\"name\\\":\\\"getOrCreate\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"description\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetType>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"initializeCommonTypes\\\":{\\\"name\\\":\\\"initializeCommonTypes\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AssetType\\\",\\\"exportName\\\":\\\"AssetTypeCollection\\\",\\\"collectionExportName\\\":\\\"AssetTypeCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_types\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_types\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_types_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_types_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_types_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"a9c93be2\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:Asset\\\":{\\\"name\\\":\\\"asset\\\",\\\"className\\\":\\\"Asset\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:Asset\\\",\\\"collection\\\":\\\"assets\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"accessMode\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"public\\\",\\\"description\\\":\\\"Whether this record is public to ordinary asset APIs or private.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Whether this record is public to ordinary asset APIs or private.\\\"},\\\"enum\\\":[\\\"public\\\",\\\"private\\\"]},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"A recognizable name for this file or media item.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A recognizable name for this file or media item.\\\"}},\\\"sourceUri\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Where the original file is stored or can be retrieved.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the original file is stored or can be retrieved.\\\"}},\\\"mimeType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The file format, such as image/jpeg or application/pdf.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The file format, such as image/jpeg or application/pdf.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain what the file contains and when it should be used.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain what the file contains and when it should be used.\\\"}},\\\"metadata\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Additional details recorded while processing this file.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Additional details recorded while processing this file.\\\"}},\\\"version\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":1,\\\"description\\\":\\\"The revision number of this file in its version history.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The revision number of this file in its version history.\\\"}},\\\"primaryVersionId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Asset\\\",\\\"description\\\":\\\"The first version of this file, linking its revision history.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The first version of this file, linking its revision history.\\\"}},\\\"typeSlug\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The category of asset, such as an image, video, or document.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The category of asset, such as an image, video, or document.\\\"}},\\\"statusSlug\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The stage of this asset, such as draft, published, or archived.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The stage of this asset, such as draft, published, or archived.\\\"}},\\\"ownerProfileId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-profiles:Profile\\\",\\\"description\\\":\\\"The person or organization responsible for this asset.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The person or organization responsible for this asset.\\\"}},\\\"sourceAssetId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Asset\\\",\\\"description\\\":\\\"The original asset used to produce this file or variation.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The original asset used to produce this file or variation.\\\"}},\\\"folderId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Folder\\\",\\\"description\\\":\\\"The folder that organizes this file in the asset library.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The folder that organizes this file in the asset library.\\\"}},\\\"sourceType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The service or source from which this file was obtained.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The service or source from which this file was obtained.\\\"}},\\\"externalId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The reference used to identify this file at its original source.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The reference used to identify this file at its original source.\\\"}},\\\"externalRefs\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"References that connect this file to copies held by other services.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"References that connect this file to copies held by other services.\\\"}},\\\"createdAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"updatedAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false}},\\\"methods\\\":{\\\"getSlug\\\":{\\\"name\\\":\\\"getSlug\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<string | null | undefined>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getMetadata\\\":{\\\"name\\\":\\\"getMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setMetadata\\\":{\\\"name\\\":\\\"setMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metadata\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"mergeMetadata\\\":{\\\"name\\\":\\\"mergeMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metadata\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getExternalRefs\\\":{\\\"name\\\":\\\"getExternalRefs\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, AssetExternalReference>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getExternalRef\\\":{\\\"name\\\":\\\"getExternalRef\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"AssetExternalReference | null\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setExternalRef\\\":{\\\"name\\\":\\\"setExternalRef\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"reference\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":false,\\\"typeUnresolved\\\":true}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getTags\\\":{\\\"name\\\":\\\"getTags\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Tag[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"hasTag\\\":{\\\"name\\\":\\\"hasTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tagSlug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"context\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getSource\\\":{\\\"name\\\":\\\"getSource\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Asset | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getDerivatives\\\":{\\\"name\\\":\\\"getDerivatives\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getType\\\":{\\\"name\\\":\\\"getType\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AssetType | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getStatus\\\":{\\\"name\\\":\\\"getStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AssetStatus | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getAssociations\\\":{\\\"name\\\":\\\"getAssociations\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AssetAssociation[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"associateWith\\\":{\\\"name\\\":\\\"associateWith\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"role\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":true,\\\"default\\\":\\\"default\\\"}],\\\"returnType\\\":\\\"Promise<AssetAssociation>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"Asset\\\",\\\"collectionExportName\\\":\\\"AssetCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"version\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"assets\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"assets\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"access_mode\\\\\\\" TEXT DEFAULT 'public',\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"source_uri\\\\\\\" TEXT,\\\\n \\\\\\\"mime_type\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"metadata\\\\\\\" TEXT,\\\\n \\\\\\\"version\\\\\\\" INTEGER DEFAULT 1,\\\\n \\\\\\\"primary_version_id\\\\\\\" UUID,\\\\n \\\\\\\"type_slug\\\\\\\" TEXT,\\\\n \\\\\\\"status_slug\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID,\\\\n \\\\\\\"source_asset_id\\\\\\\" UUID,\\\\n \\\\\\\"folder_id\\\\\\\" UUID,\\\\n \\\\\\\"source_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT,\\\\n \\\\\\\"external_refs\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"access_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"public\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"source_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"mime_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":1},\\\"primary_version_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"type_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"status_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"source_asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"folder_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"source_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_refs\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"assets_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"assets_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"assets_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"assets_primary_version_id_idx\\\",\\\"columns\\\":[\\\"primary_version_id\\\"]},{\\\"name\\\":\\\"assets_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]},{\\\"name\\\":\\\"assets_source_asset_id_idx\\\",\\\"columns\\\":[\\\"source_asset_id\\\"]},{\\\"name\\\":\\\"assets_folder_id_idx\\\",\\\"columns\\\":[\\\"folder_id\\\"]}],\\\"version\\\":\\\"0e75029e\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetCollection\\\":{\\\"name\\\":\\\"assetcollection\\\",\\\"className\\\":\\\"AssetCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetCollection\\\",\\\"collection\\\":\\\"assets\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/assets.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByTenant\\\":{\\\"name\\\":\\\"findByTenant\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tenantId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findGlobal\\\":{\\\"name\\\":\\\"findGlobal\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findWithGlobals\\\":{\\\"name\\\":\\\"findWithGlobals\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tenantId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"addTag\\\":{\\\"name\\\":\\\"addTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"tag\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"AssetTagInput\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Tag>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"removeTag\\\":{\\\"name\\\":\\\"removeTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"tag\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"Pick<AssetTagInput, 'context'>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setTags\\\":{\\\"name\\\":\\\"setTags\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"tags\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"Pick<AssetTagInput, 'context'>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Tag[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getTagsForAssets\\\":{\\\"name\\\":\\\"getTagsForAssets\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Map<string, Tag[]>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByTag\\\":{\\\"name\\\":\\\"getByTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tag\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"Pick<AssetTagInput, 'context'>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByType\\\":{\\\"name\\\":\\\"getByType\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"typeSlug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByStatus\\\":{\\\"name\\\":\\\"getByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"statusSlug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByOwner\\\":{\\\"name\\\":\\\"getByOwner\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"ownerProfileId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"createNewVersion\\\":{\\\"name\\\":\\\"createNewVersion\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"newSourceUri\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"updates\\\",\\\"type\\\":\\\"Partial<Asset>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Asset>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getLatestVersion\\\":{\\\"name\\\":\\\"getLatestVersion\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"listVersions\\\":{\\\"name\\\":\\\"listVersions\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getDerivatives\\\":{\\\"name\\\":\\\"getDerivatives\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"sourceAssetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByMimeType\\\":{\\\"name\\\":\\\"getByMimeType\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"mimePattern\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"rollbackToVersion\\\":{\\\"name\\\":\\\"rollbackToVersion\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"targetVersion\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByFolder\\\":{\\\"name\\\":\\\"getByFolder\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"Asset\\\",\\\"exportName\\\":\\\"AssetCollection\\\",\\\"collectionExportName\\\":\\\"AssetCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"assets\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"assets\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"access_mode\\\\\\\" TEXT DEFAULT 'public',\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"source_uri\\\\\\\" TEXT,\\\\n \\\\\\\"mime_type\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"metadata\\\\\\\" TEXT,\\\\n \\\\\\\"version\\\\\\\" INTEGER DEFAULT 1,\\\\n \\\\\\\"primary_version_id\\\\\\\" UUID,\\\\n \\\\\\\"type_slug\\\\\\\" TEXT,\\\\n \\\\\\\"status_slug\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID,\\\\n \\\\\\\"source_asset_id\\\\\\\" UUID,\\\\n \\\\\\\"folder_id\\\\\\\" UUID,\\\\n \\\\\\\"source_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT,\\\\n \\\\\\\"external_refs\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"access_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"public\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"source_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"mime_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":1},\\\"primary_version_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"type_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"status_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"source_asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"folder_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"source_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_refs\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"assets_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"assets_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"assets_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"assets_primary_version_id_idx\\\",\\\"columns\\\":[\\\"primary_version_id\\\"]},{\\\"name\\\":\\\"assets_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]},{\\\"name\\\":\\\"assets_source_asset_id_idx\\\",\\\"columns\\\":[\\\"source_asset_id\\\"]},{\\\"name\\\":\\\"assets_folder_id_idx\\\",\\\"columns\\\":[\\\"folder_id\\\"]}],\\\"version\\\":\\\"0e75029e\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:Folder\\\":{\\\"name\\\":\\\"folder\\\",\\\"className\\\":\\\"Folder\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:Folder\\\",\\\"collection\\\":\\\"folders\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/folder.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"parentId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Folder\\\",\\\"_meta\\\":{\\\"nullable\\\":true}},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"A recognizable name for this group of files.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A recognizable name for this group of files.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain the purpose of this folder and which files belong here.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain the purpose of this folder and which files belong here.\\\"}},\\\"ownerProfileId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-profiles:Profile\\\",\\\"description\\\":\\\"The person or organization responsible for this folder.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The person or organization responsible for this folder.\\\"}},\\\"createdAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"updatedAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false}},\\\"methods\\\":{\\\"getParent\\\":{\\\"name\\\":\\\"getParent\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getChildren\\\":{\\\"name\\\":\\\"getChildren\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getAncestors\\\":{\\\"name\\\":\\\"getAncestors\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getDescendants\\\":{\\\"name\\\":\\\"getDescendants\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getHierarchy\\\":{\\\"name\\\":\\\"getHierarchy\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<HierarchyView<this>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"moveTo\\\":{\\\"name\\\":\\\"moveTo\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"newParent\\\",\\\"type\\\":\\\"this | string | null\\\",\\\"optional\\\":false,\\\"unionBranches\\\":[{\\\"type\\\":\\\"this\\\"},{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"null\\\"}]}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Folder | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"]},\\\"extends\\\":\\\"SmrtHierarchical\\\",\\\"exportName\\\":\\\"Folder\\\",\\\"collectionExportName\\\":\\\"FolderCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"folders\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"folders\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"parent_id\\\\\\\" UUID,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"parent_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"folders_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"folders_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"folders_parent_id_idx\\\",\\\"columns\\\":[\\\"parent_id\\\"]},{\\\"name\\\":\\\"folders_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]}],\\\"version\\\":\\\"1d0b1fcb\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:FolderCollection\\\":{\\\"name\\\":\\\"foldercollection\\\",\\\"className\\\":\\\"FolderCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:FolderCollection\\\",\\\"collection\\\":\\\"folders\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/folders.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getTree\\\":{\\\"name\\\":\\\"getTree\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"rootId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Folder[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPath\\\":{\\\"name\\\":\\\"getPath\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Folder[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getContents\\\":{\\\"name\\\":\\\"getContents\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetCollection\\\",\\\"type\\\":\\\"AssetCollection\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"moveAsset\\\":{\\\"name\\\":\\\"moveAsset\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"asset\\\",\\\"type\\\":\\\"Asset\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string | null\\\",\\\"optional\\\":false,\\\"unionBranches\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"null\\\"}]}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"Folder\\\",\\\"exportName\\\":\\\"FolderCollection\\\",\\\"collectionExportName\\\":\\\"FolderCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"folders\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"folders\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"parent_id\\\\\\\" UUID,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"parent_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"folders_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"folders_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"folders_parent_id_idx\\\",\\\"columns\\\":[\\\"parent_id\\\"]},{\\\"name\\\":\\\"folders_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]}],\\\"version\\\":\\\"1d0b1fcb\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"recipes\\\":[{\\\"id\\\":\\\"assets.attachments\\\",\\\"className\\\":\\\"AssetAttachmentsRecipe\\\",\\\"label\\\":\\\"Attachments\\\",\\\"summary\\\":\\\"Attach authorized files to records and retain their history.\\\",\\\"synonyms\\\":[\\\"file attachments\\\",\\\"record files\\\",\\\"supporting documents\\\"],\\\"models\\\":[\\\"@happyvertical/smrt-assets:Asset\\\",\\\"@happyvertical/smrt-assets:AssetAssociation\\\"],\\\"nav\\\":[{\\\"label\\\":\\\"Attachments\\\",\\\"model\\\":\\\"@happyvertical/smrt-assets:Asset\\\",\\\"icon\\\":\\\"fileText\\\",\\\"description\\\":\\\"Supporting files connected to the record you are viewing.\\\",\\\"key\\\":\\\"attachments\\\",\\\"noun\\\":\\\"attachment\\\"}],\\\"requires\\\":[\\\"assets.library\\\"],\\\"group\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Files and media\\\",\\\"summary\\\":\\\"Organize files and media, then attach them to the records that need them.\\\"},\\\"section\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Assets\\\",\\\"icon\\\":\\\"folder\\\",\\\"description\\\":\\\"Files, media and attachments for your work.\\\"},\\\"options\\\":{\\\"@happyvertical/smrt-assets:Asset\\\":{\\\"fields\\\":{\\\"name\\\":{\\\"label\\\":\\\"File name\\\",\\\"order\\\":1},\\\"description\\\":{\\\"label\\\":\\\"Description\\\",\\\"order\\\":2},\\\"mimeType\\\":{\\\"label\\\":\\\"File type\\\",\\\"order\\\":3}}},\\\"@happyvertical/smrt-assets:AssetAssociation\\\":{\\\"fields\\\":{\\\"assetId\\\":{\\\"label\\\":\\\"Attachment\\\",\\\"order\\\":1},\\\"metaType\\\":{\\\"help\\\":\\\"The kind of record this file is connected to, such as an article or event.\\\"},\\\"metaId\\\":{\\\"help\\\":\\\"The specific record that this file is connected to.\\\"},\\\"role\\\":{\\\"label\\\":\\\"Role\\\",\\\"order\\\":2,\\\"help\\\":\\\"How this file is used by the related record, such as a cover image or attachment.\\\"},\\\"sortOrder\\\":{\\\"label\\\":\\\"Display order\\\",\\\"order\\\":3,\\\"help\\\":\\\"The position of this attachment among files with the same role; lower numbers appear first.\\\"}}}},\\\"help\\\":{\\\"markdown\\\":\\\"## Overview\\\\n\\\\nAttachments keep authorized supporting files connected to a record without making a public storage link. The application supplies the authorization and upload endpoint; this recipe records the asset and its relationship.\\\\n\\\\n## Tasks\\\\n\\\\n### Describe an attachment\\\\n\\\\n1. Use a clear {field:Asset.name} so people can identify the file.\\\\n2. Add a {field:Asset.description} when the attachment needs context.\\\\n3. Check the {field:Asset.mimeType} before offering the file to a viewer.\\\\n\\\\n### Link an attachment to a record\\\\n\\\\n1. Select the {field:AssetAssociation.assetId} to link.\\\\n2. Set a {field:AssetAssociation.role} when the record uses more than one file.\\\\n3. Adjust {field:AssetAssociation.sortOrder} to control display order.\\\\n\\\",\\\"fieldRefs\\\":[\\\"Asset.description\\\",\\\"Asset.mimeType\\\",\\\"Asset.name\\\",\\\"AssetAssociation.assetId\\\",\\\"AssetAssociation.role\\\",\\\"AssetAssociation.sortOrder\\\"]},\\\"runtime\\\":\\\"both\\\",\\\"demoSeed\\\":{\\\"export\\\":\\\"@happyvertical/smrt-assets/playground#assetAttachmentMockAttachments\\\"},\\\"demo\\\":{\\\"mode\\\":\\\"server\\\",\\\"reasons\\\":[\\\"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624).\\\"]}},{\\\"id\\\":\\\"assets.library\\\",\\\"className\\\":\\\"AssetLibraryRecipe\\\",\\\"label\\\":\\\"Asset Library\\\",\\\"summary\\\":\\\"Organize files and media with folders, types, and statuses.\\\",\\\"synonyms\\\":[\\\"media library\\\",\\\"file library\\\",\\\"digital assets\\\"],\\\"models\\\":[\\\"@happyvertical/smrt-assets:Asset\\\",\\\"@happyvertical/smrt-assets:Folder\\\",\\\"@happyvertical/smrt-assets:AssetType\\\",\\\"@happyvertical/smrt-assets:AssetStatus\\\",\\\"@happyvertical/smrt-assets:AssetMetafield\\\"],\\\"nav\\\":[{\\\"label\\\":\\\"Files & media\\\",\\\"model\\\":\\\"@happyvertical/smrt-assets:Asset\\\",\\\"icon\\\":\\\"archive\\\",\\\"description\\\":\\\"Files and media you can organize, reuse and share.\\\",\\\"noun\\\":\\\"asset\\\"},{\\\"label\\\":\\\"Folders\\\",\\\"model\\\":\\\"@happyvertical/smrt-assets:Folder\\\",\\\"icon\\\":\\\"folder\\\",\\\"description\\\":\\\"Organize assets into clear, reusable groups.\\\",\\\"noun\\\":\\\"folder\\\"}],\\\"requires\\\":[],\\\"group\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Files and media\\\",\\\"summary\\\":\\\"Organize files and media, then attach them to the records that need them.\\\"},\\\"section\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Assets\\\",\\\"icon\\\":\\\"folder\\\",\\\"description\\\":\\\"Files, media and attachments for your work.\\\"},\\\"options\\\":{\\\"@happyvertical/smrt-assets:Asset\\\":{\\\"fields\\\":{\\\"name\\\":{\\\"label\\\":\\\"Name\\\",\\\"order\\\":1},\\\"description\\\":{\\\"order\\\":2},\\\"folderId\\\":{\\\"label\\\":\\\"Folder\\\",\\\"order\\\":3},\\\"typeSlug\\\":{\\\"label\\\":\\\"Type\\\",\\\"order\\\":4},\\\"statusSlug\\\":{\\\"label\\\":\\\"Status\\\",\\\"order\\\":5},\\\"mimeType\\\":{\\\"label\\\":\\\"File type\\\",\\\"order\\\":6}}},\\\"@happyvertical/smrt-assets:Folder\\\":{\\\"fields\\\":{\\\"parentId\\\":{\\\"help\\\":\\\"The containing folder; leave empty to place this folder at the top level.\\\"},\\\"name\\\":{\\\"label\\\":\\\"Name\\\",\\\"order\\\":1},\\\"description\\\":{\\\"order\\\":2}}}},\\\"help\\\":{\\\"markdown\\\":\\\"## Overview\\\\n\\\\nThe Asset Library keeps files and media in one place. Organize assets into folders, classify them by type and status, and keep a clear description for people who need to find them later.\\\\n\\\\n## Tasks\\\\n\\\\n### Add an asset\\\\n\\\\n1. Give the asset a recognizable {field:Asset.name}.\\\\n2. Add a {field:Asset.description} that explains how the file can be used.\\\\n3. Select its {field:Asset.folderId}, {field:Asset.typeSlug}, and {field:Asset.statusSlug}.\\\\n4. Confirm the {field:Asset.mimeType} matches the file you intend to manage.\\\\n\\\\n### Organize folders\\\\n\\\\n1. Create a folder with a descriptive {field:Folder.name}.\\\\n2. Use the {field:Folder.description} to explain the folder's purpose.\\\\n\\\",\\\"fieldRefs\\\":[\\\"Asset.description\\\",\\\"Asset.folderId\\\",\\\"Asset.mimeType\\\",\\\"Asset.name\\\",\\\"Asset.statusSlug\\\",\\\"Asset.typeSlug\\\",\\\"Folder.description\\\",\\\"Folder.name\\\"]},\\\"runtime\\\":\\\"both\\\",\\\"demoSeed\\\":{\\\"export\\\":\\\"@happyvertical/smrt-assets/playground#assetGridMockAssets\\\"},\\\"demo\\\":{\\\"mode\\\":\\\"server\\\",\\\"reasons\\\":[\\\"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624).\\\"]}}],\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-tags\\\",\\\"@happyvertical/smrt-tenancy\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3624\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-tenancy\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * AssetAssociation model - Links assets to any SmrtObject via polymorphic join\n *\n * Enables many-to-many relationships between assets and arbitrary objects\n * (e.g., Article, Profile, Event) with role-based categorization.\n *\n * The polymorphic right side (`metaType` + `metaId` + `role` + `sortOrder`)\n * and the registry-aware `hydrate()` helper come from\n * `SmrtPolymorphicAssociation` in core; this class only adds the `assetId`\n * left/owner FK.\n */\n\nimport {\n field,\n foreignKey,\n SmrtPolymorphicAssociation,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { AssetAssociationOptions } from './types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n conflictColumns: ['asset_id', 'meta_type', 'meta_id', 'role'],\n api: { include: ['list', 'get', 'create', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetAssociation extends SmrtPolymorphicAssociation { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n /**\n * Optional tenant scope. Without this, the generated `api`/`mcp` `create`\n * routes let any caller forge a polymorphic link between an asset and an\n * arbitrary object across tenant boundaries — the #1540 generated-route\n * tenant-context fix only filters models that ARE `@TenantScoped`, so this\n * model must opt in to be covered (S5 #1396).\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** FK to Asset.id */\n @foreignKey('Asset', { required: true })\n @field({ description: 'The file attached to the related record.' })\n assetId = '';\n\n constructor(options: AssetAssociationOptions = {}) {\n super(options);\n if (options.assetId) this.assetId = options.assetId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","/**\n * AssetAssociationCollection - Polymorphic junction collection.\n *\n * Extends `SmrtJunctionBase`: the left side is a mandatory composite key\n * (`metaType` + `metaId`). Owner methods retain both required arguments without\n * pretending to implement the single-owner `SmrtJunction` contract.\n * `byRight(assetId)` and junction registration/positioning are inherited.\n *\n * For non-polymorphic links between two domain models, prefer a dedicated\n * noun-table junction (e.g. `content_assets`, `place_assets`) extending\n * `SmrtJunction` directly. Use `AssetAssociation` only for generic/provenance\n * relationships that aren't worth a dedicated table.\n */\n\nimport {\n type JunctionAttachOptions,\n type JunctionFilterOptions,\n ObjectRegistry,\n type SmrtCreateInput,\n SmrtJunctionBase,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { AssetAssociation } from './asset-association';\n\n/**\n * `metaType` filter matching every name stored rows may use for the owner's\n * class: its current qualified name and any deprecated\n * `previousQualifiedNames` (#3338). Rows written before and after a model\n * moved package both match; new rows always store the current name\n * (`SmrtPolymorphicAssociation.save()`).\n */\nasync function metaTypeFilter(metaType: string): Promise<string | string[]> {\n // Async so an owner class known only from its installed manifest is\n // lazily loaded first; otherwise its old names are invisible here.\n const names = await ObjectRegistry.getEquivalentQualifiedNamesAsync(\n metaType,\n { source: 'AssetAssociationCollection' },\n );\n return names.length === 1 ? names[0] : names;\n}\n\n// Decorator with empty config — only needed so the scanner detects the\n// class. See FactContentCollection for the full rationale; the short\n// version is: api/mcp/cli on a collection decorator clobber the item\n// class's own config, so leave them off here.\n@smrt()\nexport class AssetAssociationCollection extends SmrtJunctionBase<AssetAssociation> { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n static readonly _itemClass = AssetAssociation;\n\n protected rightField = 'assetId';\n\n /**\n * List associations for a polymorphic owner.\n *\n * Composite left key — pass both halves.\n */\n async byLeft(\n metaType: string,\n metaId: string,\n opts: JunctionFilterOptions = {},\n ): Promise<AssetAssociation[]> {\n return (await this.list({\n // Spread opts first so the fixed polymorphic owner keys always win.\n where: { ...opts, metaType: await metaTypeFilter(metaType), metaId },\n orderBy: 'sort_order ASC',\n })) as AssetAssociation[];\n }\n\n /**\n * Create an association. Composite left (metaType, metaId) precedes right (assetId).\n */\n async attach(\n metaType: string,\n metaId: string,\n assetId: string,\n opts: JunctionAttachOptions = {},\n ): Promise<AssetAssociation> {\n return (await this.create({\n // Spread opts first so the fixed key fields always win.\n ...opts,\n assetId,\n metaType,\n metaId,\n } as SmrtCreateInput<AssetAssociation>)) as AssetAssociation;\n }\n\n /**\n * Delete matching associations. Composite left (metaType, metaId) precedes right (assetId).\n */\n async detach(\n metaType: string,\n metaId: string,\n assetId: string,\n opts: JunctionFilterOptions = {},\n ): Promise<void> {\n const links = (await this.list({\n where: {\n ...opts,\n metaType: await metaTypeFilter(metaType),\n metaId,\n assetId,\n },\n })) as AssetAssociation[];\n for (const link of links) {\n await link.delete();\n }\n }\n\n /**\n * Replace all associations for a polymorphic owner with the given asset IDs.\n * Not transactional — see `SmrtJunction.setLinks` for caveats.\n */\n async setLinks(\n metaType: string,\n metaId: string,\n assetIds: string[],\n opts: JunctionAttachOptions = {},\n ): Promise<void> {\n // Strip the right-side key (assetId) from snapshot opts — see the\n // base class docstring for the rationale; the same contract applies\n // here.\n const snapshotOpts: JunctionAttachOptions = { ...opts };\n delete snapshotOpts.assetId;\n\n const existing = (await this.list({\n where: {\n ...snapshotOpts,\n metaType: await metaTypeFilter(metaType),\n metaId,\n },\n })) as AssetAssociation[];\n for (const link of existing) {\n await link.delete();\n }\n\n const positionKey = this.positionField;\n for (let i = 0; i < assetIds.length; i++) {\n const rowOpts: JunctionAttachOptions = { ...opts };\n if (positionKey && rowOpts[positionKey] === undefined) {\n rowOpts[positionKey] = i;\n }\n await this.attach(metaType, metaId, assetIds[i], rowOpts);\n }\n }\n}\n","/**\n * AssetStatus model - Defines the lifecycle status of an asset\n *\n * Lookup table for asset status classification (e.g., 'draft', 'published', 'archived')\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { AssetStatusOptions } from './types';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetStatus extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // slug is inherited as an accessor from SmrtObject\n @field({\n description:\n 'The name of this lifecycle stage, such as Draft or Published.',\n })\n name = ''; // Display name (e.g., 'Draft', 'Published')\n @field({\n description: 'Explain when an asset should use this lifecycle stage.',\n })\n description = ''; // Optional description\n\n constructor(options: AssetStatusOptions = {}) {\n super(options);\n if (options.slug) this.slug = options.slug;\n if (options.name) this.name = options.name;\n if (options.description) this.description = options.description;\n }\n\n /**\n * Get asset status by slug\n *\n * @param slug - The slug to search for\n * @returns AssetStatus instance or null\n */\n static async getBySlug(_slug: string): Promise<AssetStatus | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * AssetType model - Defines the high-level type of an asset\n *\n * Lookup table for asset type classification (e.g., 'image', 'video', 'document')\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { AssetTypeOptions } from './types';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetType extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // slug is inherited as an accessor from SmrtObject\n @field({\n description: 'The name of this file category, such as Image or Document.',\n })\n name = ''; // Display name (e.g., 'Image', 'Video')\n @field({\n description: 'Explain which kinds of files belong in this category.',\n })\n description = ''; // Optional description\n\n constructor(options: AssetTypeOptions = {}) {\n super(options);\n if (options.slug) this.slug = options.slug;\n if (options.name) this.name = options.name;\n if (options.description) this.description = options.description;\n }\n\n /**\n * Get asset type by slug\n *\n * @param slug - The slug to search for\n * @returns AssetType instance or null\n */\n static async getBySlug(_slug: string): Promise<AssetType | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","import type { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n GlobalInterceptors,\n type InterceptorContext,\n ObjectRegistry,\n resolveGetStringFilter,\n type SmrtObject,\n} from '@happyvertical/smrt-core';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\n\n/** Trusted server capability. Never construct this from an untrusted API payload. */\nexport interface PrivateAssetAccess {\n tenantId: string;\n actorUserId: string;\n ownerProfileId: string;\n assetIds: readonly string[];\n}\ninterface Scope extends PrivateAssetAccess {\n active: boolean;\n}\nconst KEY = Symbol.for('smrt:private-asset-access');\nconst root = globalThis as typeof globalThis & {\n [KEY]?: AsyncLocalStorage<Scope>;\n};\nfunction scopeStorage(): AsyncLocalStorage<Scope> | undefined {\n if (root[KEY]) return root[KEY];\n if (typeof process === 'undefined' || !process.getBuiltinModule)\n return undefined;\n const { AsyncLocalStorage } = process.getBuiltinModule('node:async_hooks');\n root[KEY] = new AsyncLocalStorage<Scope>();\n return root[KEY];\n}\n\nfunction currentScope(): Scope | undefined {\n const scope = scopeStorage()?.getStore();\n if (!scope) return undefined;\n const actor = getCurrentTenant();\n if (\n !scope.active ||\n actor?.tenantId !== scope.tenantId ||\n actor.userId !== scope.actorUserId\n )\n throw new Error(\n 'Private asset capability is closed or actor identity changed',\n );\n return scope;\n}\n\n/**\n * Run an already-authorized operation on exact private asset IDs. The host must\n * authorize the relationship first; this capability does not grant permissions.\n * Nested capabilities may only narrow their parent's IDs and retain its owner.\n */\nexport async function withPrivateAssetAccess<T>(\n access: PrivateAssetAccess,\n callback: () => Promise<T>,\n): Promise<T> {\n const storage = scopeStorage();\n if (!storage)\n throw new Error('Private asset capabilities require a Node server');\n const actor = getCurrentTenant();\n if (\n !access.tenantId ||\n !access.actorUserId ||\n !access.ownerProfileId ||\n !access.assetIds.length ||\n access.assetIds.some((id) => !id) ||\n actor?.tenantId !== access.tenantId ||\n actor.userId !== access.actorUserId\n )\n throw new Error(\n 'Private asset capability requires an exact authenticated actor and asset IDs',\n );\n const parent = currentScope();\n if (\n parent &&\n (parent.ownerProfileId !== access.ownerProfileId ||\n access.assetIds.some((id) => !parent.assetIds.includes(id)))\n )\n throw new Error('Nested private asset capability cannot widen access');\n const scope: Scope = {\n ...access,\n assetIds: [...access.assetIds],\n active: true,\n };\n return storage.run(scope, async () => {\n try {\n return await callback();\n } finally {\n scope.active = false;\n }\n });\n}\n\nfunction applies(context: InterceptorContext): boolean {\n const identity = context.qualifiedClassName ?? context.className;\n return (\n ObjectRegistry.getSTIBase(identity) === '@happyvertical/smrt-assets:Asset'\n );\n}\nfunction predicate(): Record<string, unknown> {\n const scope = currentScope();\n return scope\n ? {\n accessMode: 'private',\n tenantId: scope.tenantId,\n ownerProfileId: scope.ownerProfileId,\n 'id in': [...scope.assetIds],\n }\n : { accessMode: 'public' };\n}\nfunction assertOwner(\n record: Record<string, unknown>,\n scope: Scope | undefined,\n): void {\n if (\n !scope ||\n record.tenantId !== scope.tenantId ||\n record.ownerProfileId !== scope.ownerProfileId ||\n !scope.assetIds.includes(String(record.id))\n )\n throw new Error('Private asset access denied');\n}\nasync function guardMutation(\n instance: SmrtObject,\n context: InterceptorContext,\n): Promise<void> {\n if (!applies(context)) return;\n const record = instance as unknown as Record<string, unknown>;\n // The instance executor is the transaction executor, including uncommitted\n // writes. Never consult a separate root connection here.\n const prior = instance.id\n ? await instance.db.get(instance.tableName, { id: instance.id })\n : null;\n const scope = currentScope();\n if (prior?.access_mode === 'private') {\n assertOwner(\n {\n id: prior.id,\n tenantId: prior.tenant_id,\n ownerProfileId: prior.owner_profile_id,\n },\n scope,\n );\n if (\n record.accessMode !== 'private' ||\n record.tenantId !== prior.tenant_id ||\n record.ownerProfileId !== prior.owner_profile_id\n )\n throw new Error(\n 'Private asset classification and ownership are immutable',\n );\n }\n if (record.accessMode !== 'public' && record.accessMode !== 'private')\n throw new Error('Invalid asset access mode');\n if (record.accessMode === 'private') {\n assertOwner(record, scope);\n if (\n record.sourceUri &&\n /^https?:$/.test(new URL(String(record.sourceUri)).protocol)\n )\n throw new Error(\n 'Private assets require non-public storage, not HTTP(S) source locators',\n );\n }\n}\n\n// Mandatory owning-package policy: importing Asset installs it, including when\n// consumers load Asset through the generated parent model rather than education.\nGlobalInterceptors.register({\n name: 'asset-private-records',\n priority: 90,\n bulkMutation: {\n compatible(className) {\n return (\n ObjectRegistry.getSTIBase(className) !==\n '@happyvertical/smrt-assets:Asset'\n );\n },\n },\n beforeList(_name, options, context) {\n if (!applies(context)) return;\n const condition = predicate();\n return {\n ...options,\n where: Array.isArray(options.where)\n ? options.where.map((group) => [condition, ...group])\n : { ...options.where, ...condition },\n };\n },\n beforeGet(_name, filter, context) {\n if (!applies(context)) return;\n return {\n ...(typeof filter === 'string' ? resolveGetStringFilter(filter) : filter),\n ...predicate(),\n };\n },\n beforeQuery(_name, _options, context) {\n if (applies(context))\n throw new Error(\n 'Raw Asset collection queries cannot enforce private record access; use guarded list/get APIs',\n );\n },\n writeGuard(instance, context) {\n if (!applies(context)) return;\n const record = instance as unknown as Record<string, unknown>;\n const scope = currentScope();\n if (record.accessMode === 'private') {\n assertOwner(record, scope);\n return {\n access_mode: 'private',\n tenant_id: scope!.tenantId,\n owner_profile_id: scope!.ownerProfileId,\n 'id in': [...scope!.assetIds],\n };\n }\n return { access_mode: 'public' };\n },\n beforeSave: guardMutation,\n beforeDelete: guardMutation,\n});\n","/**\n * Asset model - Core entity for asset management\n *\n * Represents a digital asset with versioning, metadata, and tag support.\n * Supports multi-tenancy with optional tenant scoping.\n *\n * Post R3-D: `parentId` was renamed to `sourceAssetId` to reflect what\n * the column actually means — a derivation pointer (\"this asset was\n * produced from that source asset\"), not a structural-hierarchy edge.\n * Folder, which used `parent_id` for actual hierarchy via STI on this\n * table, has moved to its own `folders` table. See `folder.ts` and the\n * `R3-D` changeset for the migration.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n ObjectRegistry,\n type SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { type Tag, TagCollection } from '@happyvertical/smrt-tags';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { AssetAssociation } from './asset-association';\nimport { AssetAssociationCollection } from './asset-associations';\nimport { AssetStatus } from './asset-status';\nimport { AssetType } from './asset-type';\nimport type { AssetOptions } from './types';\nimport './private-asset-access';\n\nexport interface AssetExternalReference {\n provider: string;\n assetId?: string | null;\n externalId?: string | null;\n sourceRef?: Record<string, unknown> | null;\n status?: string | null;\n metadata?: Record<string, unknown> | null;\n syncedAt?: string | null;\n [key: string]: unknown;\n}\n\nfunction parseAssetRecord(\n value: string | Record<string, unknown> | null | undefined,\n): Record<string, unknown> {\n if (!value) return {};\n if (typeof value !== 'string') return value;\n if (!value.trim()) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction stringifyAssetRecord(\n value: Record<string, unknown> | null | undefined,\n): string {\n if (!value || Object.keys(value).length === 0) return '';\n return JSON.stringify(value);\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: { skipApiCheck: true },\n})\nexport class Asset extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // Tenant isolation (optional - assets can be global or tenant-specific)\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Private records require an exact trusted capability for every operation. */\n @field({\n description:\n 'Whether this record is public to ordinary asset APIs or private.',\n })\n accessMode: 'public' | 'private' = 'public';\n\n // Core fields\n @field({ description: 'A recognizable name for this file or media item.' })\n name = ''; // User-friendly name\n // slug is inherited as an accessor from SmrtObject\n @field({\n description: 'Where the original file is stored or can be retrieved.',\n })\n sourceUri = ''; // URI to the actual file (e.g., 's3://bucket/key', 'file:///path')\n @field({\n description: 'The file format, such as image/jpeg or application/pdf.',\n })\n mimeType = ''; // MIME type (e.g., 'image/jpeg', 'video/mp4')\n @field({\n description: 'Explain what the file contains and when it should be used.',\n })\n description = ''; // Optional description\n @field({\n description: 'Additional details recorded while processing this file.',\n })\n metadata = ''; // JSON metadata owned by SMRT asset processors\n @field({\n description: 'The revision number of this file in its version history.',\n })\n version = 1; // Version number\n\n // Foreign key references (stored as IDs/slugs)\n // Self-referential: the version chain points at the first version's\n // Asset record (createNewVersion() chains via primaryVersionId).\n @foreignKey('Asset')\n @field({\n description:\n 'The first version of this file, linking its revision history.',\n })\n primaryVersionId: string | null = null; // Points to first version's ID\n @field({\n description: 'The category of asset, such as an image, video, or document.',\n })\n typeSlug = ''; // FK to AssetType.slug\n @field({\n description:\n 'The stage of this asset, such as draft, published, or archived.',\n })\n statusSlug = ''; // FK to AssetStatus.slug\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n @field({\n description: 'The person or organization responsible for this asset.',\n })\n ownerProfileId: string | null = null; // FK to Profile.id (nullable)\n /**\n * FK to the source Asset this one was derived from (e.g. thumbnail\n * derived from an original image, transcoded video, AI variation).\n *\n * Renamed from `parentId` in R3-D to make the derivation semantics\n * explicit and free `parentId` to mean exactly structural hierarchy\n * (SmrtHierarchical) across the framework. The column on the assets\n * table is `source_asset_id`.\n */\n @foreignKey('Asset')\n @field({\n description: 'The original asset used to produce this file or variation.',\n })\n sourceAssetId: string | null = null;\n @foreignKey('Folder')\n @field({\n description: 'The folder that organizes this file in the asset library.',\n })\n folderId: string | null = null; // FK to Folder.id\n\n // Provenance fields\n @field({\n description: 'The service or source from which this file was obtained.',\n })\n sourceType = ''; // 'local', 'shutterstock', 'google-photos', 'upstream-smrt'\n @field({\n description:\n 'The reference used to identify this file at its original source.',\n })\n externalId = ''; // Original ID in upstream source\n @field({\n description:\n 'References that connect this file to copies held by other services.',\n })\n externalRefs = ''; // JSON map of provider references, keyed by provider slug\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n /**\n * A file's name is not its identity: two pictures named \"photo.jpg\" are two\n * assets. Without an explicit slug the default (name-derived) slug would\n * make the second save adopt the first row through the (tenant, slug,\n * context) natural key and overwrite it, so an asset's default slug is its\n * id. An explicit slug is kept.\n */\n override async getSlug(): Promise<string | null | undefined> {\n if (!this.slug && this.id) this.slug = String(this.id);\n return super.getSlug();\n }\n\n constructor(options: AssetOptions = {}) {\n super(options);\n if (options.accessMode !== undefined) this.accessMode = options.accessMode;\n if (options.name) this.name = options.name;\n if (options.slug) this.slug = options.slug;\n if (options.sourceUri) this.sourceUri = options.sourceUri;\n if (options.mimeType) this.mimeType = options.mimeType;\n if (options.description) this.description = options.description;\n if (options.metadata !== undefined)\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : stringifyAssetRecord(options.metadata);\n if (options.version !== undefined) this.version = options.version;\n if (options.primaryVersionId !== undefined)\n this.primaryVersionId = options.primaryVersionId;\n if (options.typeSlug) this.typeSlug = options.typeSlug;\n if (options.statusSlug) this.statusSlug = options.statusSlug;\n if (options.ownerProfileId !== undefined)\n this.ownerProfileId = options.ownerProfileId;\n if (options.sourceAssetId !== undefined)\n this.sourceAssetId = options.sourceAssetId;\n if (options.folderId !== undefined) this.folderId = options.folderId;\n if (options.sourceType) this.sourceType = options.sourceType;\n if (options.externalId) this.externalId = options.externalId;\n if (options.externalRefs !== undefined)\n this.externalRefs =\n typeof options.externalRefs === 'string'\n ? options.externalRefs\n : stringifyAssetRecord(options.externalRefs);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n getMetadata(): Record<string, unknown> {\n return parseAssetRecord(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = stringifyAssetRecord(metadata);\n }\n\n mergeMetadata(metadata: Record<string, unknown>): void {\n this.setMetadata({\n ...this.getMetadata(),\n ...metadata,\n });\n }\n\n getExternalRefs(): Record<string, AssetExternalReference> {\n const refs = parseAssetRecord(this.externalRefs);\n const normalized: Record<string, AssetExternalReference> = {};\n for (const [provider, value] of Object.entries(refs)) {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n normalized[provider] = {\n ...(value as Record<string, unknown>),\n provider,\n } as AssetExternalReference;\n }\n }\n return normalized;\n }\n\n getExternalRef(provider: string): AssetExternalReference | null {\n return this.getExternalRefs()[provider] ?? null;\n }\n\n setExternalRef(\n provider: string,\n reference: Omit<AssetExternalReference, 'provider'> & { provider?: string },\n ): void {\n this.externalRefs = stringifyAssetRecord({\n ...this.getExternalRefs(),\n [provider]: {\n ...(this.getExternalRef(provider) ?? {}),\n ...reference,\n provider: reference.provider ?? provider,\n },\n });\n }\n\n /**\n * Get all tags for this asset from @happyvertical/smrt-tags, through the\n * `asset_tags` join (`AssetTag`), sorted by name.\n *\n * @returns Array of Tag instances from @happyvertical/smrt-tags package\n */\n async getTags(): Promise<Tag[]> {\n if (!this.id) return [];\n const { AssetTagCollection } = await import('./asset-tags');\n const links = await AssetTagCollection.create({ db: this.db });\n const rows = await links.byLeft(this.id);\n if (rows.length === 0) return [];\n const tags = await TagCollection.create({ db: this.db });\n const found = (await tags.listByIds([\n ...new Set(rows.map((row) => row.tagId)),\n ])) as Tag[];\n return found.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Check if this asset has a specific tag\n *\n * @param tagSlug - The slug of the tag to check\n * @param context - Tag context; any context when omitted\n * @returns True if the asset has this tag\n */\n async hasTag(tagSlug: string, context?: string): Promise<boolean> {\n const tags = await this.getTags();\n return tags.some(\n (tag) =>\n tag.slug === tagSlug &&\n (context === undefined || tag.context === context),\n );\n }\n\n /**\n * Resolve the AssetCollection lazily. Going through `ObjectRegistry`\n * mirrors the pattern used by `SmrtHierarchical._hierarchyCollection`\n * so source/derivative lookups inherit tenant scoping and ORM\n * hydration without hard-coding an import of `./assets` (which would\n * create a module-import cycle).\n *\n * The return type is `SmrtCollection<Asset>` (the framework base, type-\n * only import from core) rather than the concrete `AssetCollection` —\n * importing the concrete class is what would create the cycle, but the\n * base-class shape gives callers full `.get()` / `.list()` type\n * safety here.\n *\n * R5-canon: hardcode the base Asset's qualified key so a different\n * package also registering a class called `Asset` can't be picked\n * by `findClass`'s multi-strategy fallback. Crucially we DON'T\n * resolve via `this.constructor._smrtQualifiedName` — for an STI\n * subclass like `Image`, that would yield the Image collection\n * (which auto-filters `_meta_type = '...:Image'` on `get`/`list`),\n * and `getSource()` / `getDerivatives()` would miss cross-type\n * derivation links (Image derived from a plain Asset, etc.).\n * `sourceAssetId` is a base-table derivation link, so it always\n * resolves through the base Asset collection.\n */\n private async _assetCollection(): Promise<SmrtCollection<Asset>> {\n return await ObjectRegistry.getCollection<Asset>(\n '@happyvertical/smrt-assets:Asset',\n this.options,\n );\n }\n\n /**\n * Get the source asset this one was derived from, if any.\n *\n * Renamed from `getParent` in R3-D. The relationship is \"I was produced\n * from that asset\" (e.g. a thumbnail's source is its original image),\n * not a structural-hierarchy parent.\n *\n * Goes through the AssetCollection so tenant interceptors and ORM\n * hydration apply — important because a tenant-scoped consumer with\n * cross-tenant derivative chains would otherwise return assets from\n * tenants the caller cannot see, and a raw `db.get` returns\n * snake_case rows that leave camelCase props (e.g. `sourceUri`) at\n * their constructor defaults.\n *\n * @returns Source Asset instance, or null if this asset has no source\n */\n async getSource(): Promise<Asset | null> {\n if (!this.sourceAssetId) return null;\n const collection = await this._assetCollection();\n return (await collection.get({ id: this.sourceAssetId })) as Asset | null;\n }\n\n /**\n * Get all assets derived from this one (e.g. thumbnails, variants,\n * transcodes, AI edits).\n *\n * Renamed from `getChildren` in R3-D to match the derivation\n * semantics. Goes through the AssetCollection so tenant interceptors\n * and ORM hydration apply (see `getSource` for why this matters —\n * the pre-R3-D `getChildren` used raw `db.list`, which both bypassed\n * tenant scoping and dropped camelCase property hydration; that\n * latent breakage is fixed here).\n *\n * @returns Array of derivative Asset instances\n */\n async getDerivatives(): Promise<Asset[]> {\n if (!this.id) return [];\n const collection = await this._assetCollection();\n return (await collection.list({\n where: { sourceAssetId: this.id },\n })) as Asset[];\n }\n\n /**\n * Get the type of this asset\n *\n * @returns AssetType instance or null\n */\n async getType(): Promise<AssetType | null> {\n if (!this.typeSlug) return null;\n\n return await AssetType.getBySlug(this.typeSlug);\n }\n\n /**\n * Get the status of this asset\n *\n * @returns AssetStatus instance or null\n */\n async getStatus(): Promise<AssetStatus | null> {\n if (!this.statusSlug) return null;\n\n return await AssetStatus.getBySlug(this.statusSlug);\n }\n\n /**\n * Get all associations for this asset\n *\n * @returns Array of AssetAssociation instances\n */\n async getAssociations(): Promise<AssetAssociation[]> {\n const associations = await AssetAssociationCollection.create({\n db: this.db,\n });\n return await associations.byRight(this.id!);\n }\n\n /**\n * Associate this asset with a target object\n *\n * @param metaType - Target class name or qualified name (e.g., 'Article' or '@pkg:Article')\n * @param metaId - Target object ID\n * @param role - Association role (default: 'default')\n * @returns The created AssetAssociation\n */\n async associateWith(\n metaType: string,\n metaId: string,\n role = 'default',\n ): Promise<AssetAssociation> {\n const associations = await AssetAssociationCollection.create({\n db: this.db,\n });\n return await associations.attach(metaType, metaId, this.id!, { role });\n }\n\n /**\n * Get asset by slug\n *\n * @param slug - The slug to search for\n * @returns Asset instance or null\n */\n static async getBySlug(_slug: string): Promise<Asset | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","import type { Asset } from './asset';\nimport type { AssetAssociationCollection } from './asset-associations';\nimport type { AssetStore, StoreOptions } from './asset-store';\nimport type { AssetCollection } from './assets';\n\nexport type AssetVariantFit = 'cover' | 'contain' | 'inside';\nexport type AssetCapabilitySource = 'cached' | 'generated' | 'external';\n\nexport interface AssetCapabilityRuntime {\n readonly collection: AssetCollection;\n readonly associations: AssetAssociationCollection;\n readonly store: AssetStore;\n storeDerivedAsset(\n source: Asset,\n name: string,\n data: Buffer,\n opts: Omit<StoreOptions, 'sourceAssetId'> & {\n role?: string;\n derivativeMetaType?: string;\n linkAssociation?: boolean;\n },\n ): Promise<Asset>;\n}\n\nexport interface AssetVariantRequest {\n variant: string;\n width?: number;\n height?: number;\n fit?: AssetVariantFit;\n mimeType?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetVariantResult {\n asset: Asset;\n variant: string;\n source: AssetCapabilitySource;\n url?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetProcessInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n variants?: AssetVariantRequest[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetProcessResult {\n asset: Asset;\n metadata?: Record<string, unknown> | null;\n variants?: AssetVariantResult[];\n warnings?: string[];\n}\n\nexport interface AssetEnsureVariantInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n request: AssetVariantRequest;\n}\n\nexport interface AssetNearbySearchInput {\n runtime: AssetCapabilityRuntime;\n tenantId?: string | null;\n latitude: number;\n longitude: number;\n radiusMeters?: number;\n limit?: number;\n source?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetSearchItem {\n asset?: Asset | null;\n assetId: string;\n origin: string;\n title?: string | null;\n previewUrl?: string | null;\n deliveryUrl?: string | null;\n mimeType?: string | null;\n width?: number | null;\n height?: number | null;\n distanceMeters?: number | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetSearchResult {\n items: AssetSearchItem[];\n nextCursor?: string | null;\n}\n\nexport interface AssetExternalSyncInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n externalId?: string | null;\n sourceRef?: AssetExternalSourceRef | null;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetExternalSourceRef {\n system: string;\n id: string;\n kind?: string | null;\n}\n\nexport interface AssetExternalSyncResult {\n asset: Asset;\n provider: string;\n status: 'ready' | 'pending' | 'skipped';\n externalAssetId?: string | null;\n externalId?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetWorkflowInputSelection {\n slotKey: string;\n assetIds: string[];\n}\n\nexport interface AssetWorkflowInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n workflowSlug: string;\n clientRequestId?: string | null;\n name?: string;\n inputSelections?: AssetWorkflowInputSelection[];\n sourceAssetIds?: string[];\n parameters?: Record<string, unknown>;\n requestContext?: Record<string, unknown>;\n requestedPlacement?: string | null;\n priority?: number | null;\n}\n\nexport interface AssetWorkflowResult {\n provider: string;\n jobId: string;\n status: string;\n idempotent?: boolean;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetCapabilityProvider {\n readonly name: string;\n processAsset?(input: AssetProcessInput): Promise<AssetProcessResult>;\n ensureVariant?(input: AssetEnsureVariantInput): Promise<AssetVariantResult>;\n searchNearbyAssets?(\n input: AssetNearbySearchInput,\n ): Promise<AssetSearchResult>;\n syncExternalAsset?(\n input: AssetExternalSyncInput,\n ): Promise<AssetExternalSyncResult>;\n submitAssetWorkflow?(input: AssetWorkflowInput): Promise<AssetWorkflowResult>;\n}\n\nexport type AssetCapabilityName =\n | 'processAsset'\n | 'ensureVariant'\n | 'searchNearbyAssets'\n | 'syncExternalAsset'\n | 'submitAssetWorkflow';\n\nexport class AssetCapabilityUnavailableError extends Error {\n constructor(\n public readonly capability: AssetCapabilityName,\n message = `No asset capability provider is registered for ${capability}.`,\n ) {\n super(message);\n this.name = 'AssetCapabilityUnavailableError';\n }\n}\n\nexport class AssetCapabilitySkippedError extends Error {\n constructor(\n public readonly capability: AssetCapabilityName,\n message: string,\n ) {\n super(message);\n this.name = 'AssetCapabilitySkippedError';\n }\n}\n","/**\n * Asset conventions\n *\n * Shared constants and metadata-key conventions for how apps and agents\n * describe asset relationships and derivation state on top of `smrt-assets`.\n *\n * These are advisory: `AssetAssociation.role` and `Asset` metadata fields\n * accept any string, but standardising on these values makes cross-agent\n * serving, filtering, and UI work possible without everybody inventing\n * their own vocabulary.\n */\n\n/**\n * Canonical association roles used with `AssetAssociation` and noun join\n * tables like `content_assets`.\n *\n * - `source_document`: original upstream document (e.g. agenda PDF) that\n * later derivatives are extracted from.\n * - `document_image`: page image or other visual derived from a source\n * document.\n * - `thumbnail`: small preview rendition of another asset.\n * - `asset_variant`: deterministic sized rendition of another asset.\n * - `proof`: non-canonical evidence asset (e.g. a screenshot used to\n * back a fact).\n * - `derivation_source`: source side of a derivation link (used when a\n * derivative points back to the asset it was produced from).\n * - `attachment`: generic \"this asset belongs to that object\" link —\n * the default for noun join tables.\n * - `hero`: primary/featured asset for an owner (e.g. a place's main picture).\n * - `depicts`: the asset shows the owner — a photo of a place or a person\n * (`place_assets`, `profile_assets`).\n */\nexport const ASSET_ROLES = {\n SOURCE_DOCUMENT: 'source_document',\n DOCUMENT_IMAGE: 'document_image',\n THUMBNAIL: 'thumbnail',\n ASSET_VARIANT: 'asset_variant',\n PROOF: 'proof',\n DERIVATION_SOURCE: 'derivation_source',\n ATTACHMENT: 'attachment',\n HERO: 'hero',\n DEPICTS: 'depicts',\n} as const;\n\nexport type AssetRole = (typeof ASSET_ROLES)[keyof typeof ASSET_ROLES];\n\n/**\n * Canonical metadata keys used on `Asset.description`-adjacent metadata\n * surfaces or stored via `AssetAssociation` sidecars. These keys are the\n * recommended names for cross-package extraction, provenance, and\n * content-addressing state.\n *\n * Apps are free to add their own keys; using these for the common cases\n * keeps derived-asset tooling portable.\n *\n * - `extractionStatus`: `'pending' | 'running' | 'succeeded' | 'failed'`\n * - `extractionError`: error string from the last extraction attempt\n * - `extractedAt`: ISO timestamp of the last successful extraction\n * - `sourceUrl`: upstream URL the asset was fetched from\n * - `sourceHash`: content-address hash of the source bytes (e.g. sha256)\n * - `pageNumber`: 1-based page number within a multi-page source document\n */\nexport const ASSET_METADATA_KEYS = {\n EXTRACTION_STATUS: 'extractionStatus',\n EXTRACTION_ERROR: 'extractionError',\n EXTRACTED_AT: 'extractedAt',\n SOURCE_URL: 'sourceUrl',\n SOURCE_HASH: 'sourceHash',\n PAGE_NUMBER: 'pageNumber',\n} as const;\n\nexport type AssetMetadataKey =\n (typeof ASSET_METADATA_KEYS)[keyof typeof ASSET_METADATA_KEYS];\n\n/**\n * Known extraction status values for `ASSET_METADATA_KEYS.EXTRACTION_STATUS`.\n */\nexport const ASSET_EXTRACTION_STATUS = {\n PENDING: 'pending',\n RUNNING: 'running',\n SUCCEEDED: 'succeeded',\n FAILED: 'failed',\n} as const;\n\nexport type AssetExtractionStatus =\n (typeof ASSET_EXTRACTION_STATUS)[keyof typeof ASSET_EXTRACTION_STATUS];\n","/**\n * AssetMetafield model - Defines the controlled vocabulary for metadata keys\n *\n * Lookup table for metadata field definitions with validation rules\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { AssetMetafieldOptions } from './types';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetMetafield extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // slug is inherited as an accessor from SmrtObject\n @field({\n description:\n 'The name of an additional asset detail, such as Width or Height.',\n })\n name = ''; // Display name (e.g., 'Width', 'Height')\n @field({\n description: 'Rules that determine which values this asset detail accepts.',\n })\n validation = ''; // JSON validation rules stored as text\n\n constructor(options: AssetMetafieldOptions = {}) {\n super(options);\n if (options.slug) this.slug = options.slug;\n if (options.name) this.name = options.name;\n if (options.validation) this.validation = options.validation;\n }\n\n /**\n * Get validation rules as parsed object\n *\n * @returns Parsed validation object or empty object if no validation\n */\n getValidation(): Record<string, unknown> {\n if (!this.validation) return {};\n try {\n return JSON.parse(this.validation);\n } catch {\n return {};\n }\n }\n\n /**\n * Set validation rules from object\n *\n * @param rules - Validation rules object\n */\n setValidation(rules: Record<string, unknown>): void {\n this.validation = JSON.stringify(rules);\n }\n\n /**\n * Get asset metafield by slug\n *\n * @param slug - The slug to search for\n * @returns AssetMetafield instance or null\n */\n static async getBySlug(_slug: string): Promise<AssetMetafield | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * AssetMetafieldCollection - Collection manager for AssetMetafield instances\n *\n * Manages asset metafield definitions with validation rules\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AssetMetafield } from './asset-metafield';\n\nexport class AssetMetafieldCollection extends SmrtCollection<AssetMetafield> {\n static readonly _itemClass = AssetMetafield;\n\n /**\n * Get or create an asset metafield by slug\n *\n * @param slug - The metafield slug\n * @param name - The display name (defaults to slug)\n * @param validation - Optional validation rules (JSON string or object)\n * @returns The existing or newly created AssetMetafield\n */\n async getOrCreate(\n slug: string,\n name?: string,\n validation?: string | Record<string, unknown>,\n ): Promise<AssetMetafield> {\n const existing = await this.list({ where: { slug }, limit: 1 });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const validationString =\n typeof validation === 'string'\n ? validation\n : validation\n ? JSON.stringify(validation)\n : '';\n\n return await this.create({\n slug,\n name: name || slug,\n validation: validationString,\n });\n }\n\n /**\n * Initialize common asset metafields\n *\n * Creates standard metafields with validation rules:\n * - width (integer, min: 0)\n * - height (integer, min: 0)\n * - duration (number, min: 0)\n * - size (integer, min: 0)\n * - author (string)\n * - copyright (string)\n */\n async initializeCommonMetafields(): Promise<void> {\n await this.getOrCreate('width', 'Width', {\n type: 'integer',\n min: 0,\n description: 'Width in pixels',\n });\n await this.getOrCreate('height', 'Height', {\n type: 'integer',\n min: 0,\n description: 'Height in pixels',\n });\n await this.getOrCreate('duration', 'Duration', {\n type: 'number',\n min: 0,\n description: 'Duration in seconds',\n });\n await this.getOrCreate('size', 'File Size', {\n type: 'integer',\n min: 0,\n description: 'File size in bytes',\n });\n await this.getOrCreate('author', 'Author', {\n type: 'string',\n description: 'Content creator',\n });\n await this.getOrCreate('copyright', 'Copyright', {\n type: 'string',\n description: 'Copyright notice',\n });\n }\n}\n","/**\n * AssetStore - File I/O + Asset record management\n *\n * Writes buffers to disk via @happyvertical/files and creates\n * corresponding Asset records in the database via AssetCollection.\n *\n * Supports provider-agnostic storage (local, S3, etc.) via @happyvertical/files.\n */\n\nimport type {\n FilesystemInterface,\n GetFilesystemOptions,\n} from '@happyvertical/files';\nimport type { Asset } from './asset';\nimport type { AssetCollection } from './assets';\n// The files SDK is loaded through the lazy boundary in files-runtime.ts so it\n// never enters provider-neutral consumer bundles (#1977/#1979).\nimport { getFilesystemLazy, isFileNotFoundError } from './files-runtime.js';\n\n/**\n * MIME type to file extension mapping\n */\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/svg+xml': 'svg',\n 'video/mp4': 'mp4',\n 'video/webm': 'webm',\n 'audio/wav': 'wav',\n 'audio/mpeg': 'mp3',\n 'audio/ogg': 'ogg',\n 'audio/flac': 'flac',\n 'application/pdf': 'pdf',\n 'application/json': 'json',\n 'text/plain': 'txt',\n};\n\n/**\n * Options for storing an asset\n */\nexport interface StoreOptions {\n /** MIME type of the data */\n mimeType: string;\n\n /** Asset type slug (e.g., 'video', 'audio', 'image', 'reference-image') */\n typeSlug?: string;\n\n /**\n * Source asset ID (for derivatives like thumbnails, variants, AI edits).\n *\n * Renamed from `parentId` in R3-D — see Asset.sourceAssetId.\n */\n sourceAssetId?: string;\n\n /** Asset status slug */\n statusSlug?: string;\n\n /** Tenant scope applied before the Asset record is persisted. */\n tenantId?: string | null;\n\n /** Profile that owns the Asset from its initial persisted record. */\n ownerProfileId?: string | null;\n\n /** Description */\n description?: string;\n\n /** Original source system for imported/derived assets */\n sourceType?: string;\n\n /** External id in the source system */\n externalId?: string;\n\n /** JSON metadata stored on the asset record */\n metadata?: string | Record<string, unknown> | null;\n}\n\nfunction serializeStoreMetadata(\n metadata: StoreOptions['metadata'],\n): string | undefined {\n if (metadata === undefined) return undefined;\n if (metadata === null) return '';\n if (typeof metadata === 'string') return metadata;\n return JSON.stringify(metadata);\n}\n\n/**\n * Provider options for configuring the filesystem backend.\n * Accepts any @happyvertical/files options or a simple basePath string for local storage.\n */\nexport type ProviderOptions = GetFilesystemOptions | string;\n\n/**\n * Asset storage operation currently being resolved.\n */\nexport type AssetStorageOperation = 'read' | 'write' | 'delete';\n\n/**\n * Context passed to an AssetStore resolver.\n *\n * Downstream apps can use this hook to route a logical Asset to whichever\n * storage backend is appropriate for the current operation.\n */\nexport interface AssetStorageResolveRequest {\n operation: AssetStorageOperation;\n asset: Asset;\n path: string;\n sourceUri: string;\n mimeType?: string;\n typeSlug?: string;\n defaultProviderOptions: GetFilesystemOptions;\n defaultFilesystem: FilesystemInterface;\n}\n\n/**\n * Resolved storage target for an AssetStore operation.\n */\nexport interface AssetStorageResolution {\n filesystem?: FilesystemInterface;\n providerOptions?: ProviderOptions;\n path?: string;\n sourceUri?: string;\n}\n\nexport type AssetStorageResolver = (\n request: AssetStorageResolveRequest,\n) =>\n | AssetStorageResolution\n | null\n | undefined\n | Promise<AssetStorageResolution | null | undefined>;\n\nexport interface AssetStoreOptions {\n resolver?: AssetStorageResolver;\n}\n\ninterface ResolvedAssetStorageTarget {\n filesystem: FilesystemInterface;\n providerOptions: GetFilesystemOptions;\n path: string;\n sourceUri: string;\n}\n\nfunction normalizeProviderOptions(\n providerOrBasePath: ProviderOptions,\n): GetFilesystemOptions {\n if (typeof providerOrBasePath === 'string') {\n return { type: 'local', basePath: providerOrBasePath };\n }\n return providerOrBasePath;\n}\n\n/**\n * AssetStore manages file storage and Asset record creation.\n *\n * Files are stored using @happyvertical/files with the convention:\n * `{typeSlug}/{assetId}.{ext}`\n *\n * @example\n * ```typescript\n * import { AssetStore } from '@happyvertical/smrt-assets';\n *\n * // Local storage (backward-compatible)\n * const store = new AssetStore('dev/data/assets', assetCollection);\n * await store.initialize();\n *\n * // Provider-agnostic storage\n * const s3Store = new AssetStore({ type: 's3', bucket: 'my-bucket' }, assetCollection);\n * await s3Store.initialize();\n *\n * // Store a video buffer\n * const asset = await store.store('my-video', videoBuffer, {\n * mimeType: 'video/mp4',\n * typeSlug: 'video',\n * });\n *\n * // Read it back\n * const data = await store.read(asset);\n *\n * // Delete it\n * await store.remove(asset);\n * ```\n */\nexport class AssetStore {\n private fs: FilesystemInterface | null = null;\n private readonly fsOptions: GetFilesystemOptions;\n private readonly fsCache = new Map<string, FilesystemInterface>();\n private readonly resolver?: AssetStorageResolver;\n\n constructor(\n providerOrBasePath: ProviderOptions,\n private readonly collection: AssetCollection,\n options: AssetStoreOptions = {},\n ) {\n this.fsOptions = normalizeProviderOptions(providerOrBasePath);\n this.resolver = options.resolver;\n }\n\n /** The base path for local storage, or empty string for non-local providers */\n get basePath(): string {\n return this.fsOptions.basePath ?? '';\n }\n\n /**\n * Initialize the filesystem adapter.\n * Must be called before any file operations.\n */\n async initialize(): Promise<this> {\n this.fs = await getFilesystemLazy(this.fsOptions);\n this.fsCache.set(this.providerCacheKey(this.fsOptions), this.fs);\n return this;\n }\n\n /**\n * Get the initialized filesystem (throws if not initialized)\n */\n private getFs(): FilesystemInterface {\n if (!this.fs) {\n throw new Error('AssetStore not initialized. Call initialize() first.');\n }\n return this.fs;\n }\n\n private providerCacheKey(providerOptions: GetFilesystemOptions): string {\n return JSON.stringify(providerOptions);\n }\n\n private async getFilesystemForOptions(\n providerOptions: GetFilesystemOptions,\n ): Promise<FilesystemInterface> {\n const key = this.providerCacheKey(providerOptions);\n const cached = this.fsCache.get(key);\n if (cached) return cached;\n\n const filesystem = await getFilesystemLazy(providerOptions);\n this.fsCache.set(key, filesystem);\n return filesystem;\n }\n\n /**\n * Build a sourceUri for the given file path based on the provider type.\n */\n private buildSourceUri(filePath: string): string {\n return AssetStore.buildSourceUriForProvider(filePath, this.fsOptions);\n }\n\n private static buildSourceUriForProvider(\n filePath: string,\n providerOptions: GetFilesystemOptions,\n ): string {\n const type = providerOptions.type;\n if (type === 's3') {\n const bucket = providerOptions.bucket ?? '';\n return `s3://${bucket}/${filePath}`;\n }\n // Default to file:// for local and other types\n const base = providerOptions.basePath ?? '';\n return base ? `file://${base}/${filePath}` : `file://${filePath}`;\n }\n\n private static providerRelativePath(\n filePath: string,\n providerOptions: GetFilesystemOptions,\n ): string {\n const base = providerOptions.basePath ?? '';\n return base && filePath.startsWith(base)\n ? filePath.slice(base.length + 1)\n : filePath;\n }\n\n private static requireAssetId(asset: Asset): string {\n if (!asset.id) {\n throw new Error('Asset must be saved before storing file data.');\n }\n return asset.id;\n }\n\n private async resolveStorage(\n request: Omit<\n AssetStorageResolveRequest,\n 'defaultProviderOptions' | 'defaultFilesystem'\n >,\n ): Promise<ResolvedAssetStorageTarget> {\n const defaultFilesystem = this.getFs();\n const resolution = this.resolver\n ? await this.resolver({\n ...request,\n defaultProviderOptions: this.fsOptions,\n defaultFilesystem,\n })\n : undefined;\n if (\n request.operation === 'write' &&\n resolution?.filesystem &&\n !resolution.providerOptions &&\n !resolution.sourceUri\n ) {\n throw new Error(\n 'Asset storage resolver must return providerOptions or sourceUri when overriding filesystem for write operations.',\n );\n }\n const providerOptions = resolution?.providerOptions\n ? normalizeProviderOptions(resolution.providerOptions)\n : this.fsOptions;\n const path = AssetStore.providerRelativePath(\n resolution?.path ?? request.path,\n providerOptions,\n );\n const sourceUri =\n resolution?.sourceUri ??\n (resolution?.path || resolution?.providerOptions\n ? AssetStore.buildSourceUriForProvider(path, providerOptions)\n : request.sourceUri);\n const filesystem =\n resolution?.filesystem ??\n (providerOptions === this.fsOptions\n ? defaultFilesystem\n : await this.getFilesystemForOptions(providerOptions));\n\n return {\n filesystem,\n providerOptions,\n path,\n sourceUri,\n };\n }\n\n /**\n * Resolve an existing asset's future storage URI before writing bytes.\n * Persist this URI with the caller's storage intent. Resolvers used for\n * recoverable writes must return the same target for the same asset.\n */\n async planFile(\n asset: Asset,\n opts: { mimeType: string; typeSlug?: string },\n ): Promise<string> {\n const ext = MIME_TO_EXT[opts.mimeType] ?? 'bin';\n const typeSlug = opts.typeSlug ?? 'file';\n const path = `${typeSlug}/${AssetStore.requireAssetId(asset)}.${ext}`;\n const target = await this.resolveStorage({\n operation: 'write',\n asset,\n path,\n sourceUri: this.buildSourceUri(path),\n mimeType: opts.mimeType,\n typeSlug,\n });\n return target.sourceUri;\n }\n\n /**\n * Preserve bytes at a previously committed plan. Existing bytes must match;\n * missing files are written, while all other read failures fail closed.\n * The caller owns serialization/fencing and must persist sourceUri first.\n */\n async preserveFile(\n asset: Asset,\n data: Buffer,\n opts: { mimeType: string; typeSlug?: string },\n ): Promise<string> {\n const planned = await this.planFile(asset, opts);\n if (!asset.sourceUri || asset.sourceUri !== planned) {\n throw new Error('Asset storage plan changed');\n }\n try {\n const existing = await this.read(asset);\n if (!existing.equals(data))\n throw new Error('Asset storage integrity conflict');\n return planned;\n } catch (error) {\n if (!isFileNotFoundError(error)) throw error;\n }\n const written = await this.writeAssetData(asset, data, opts, planned);\n if (written !== planned) throw new Error('Asset storage plan changed');\n return written;\n }\n\n private async writeAssetData(\n asset: Asset,\n data: Buffer,\n opts: { mimeType: string; typeSlug?: string },\n expectedSourceUri?: string,\n ): Promise<string> {\n const ext = MIME_TO_EXT[opts.mimeType] ?? 'bin';\n const typeSlug = opts.typeSlug ?? 'file';\n const assetId = AssetStore.requireAssetId(asset);\n const filePath = `${typeSlug}/${assetId}.${ext}`;\n const sourceUri = this.buildSourceUri(filePath);\n const target = await this.resolveStorage({\n operation: 'write',\n asset,\n path: filePath,\n sourceUri,\n mimeType: opts.mimeType,\n typeSlug,\n });\n\n if (expectedSourceUri && target.sourceUri !== expectedSourceUri) {\n throw new Error('Asset storage plan changed');\n }\n await target.filesystem.write(target.path, data, { createParents: true });\n return target.sourceUri;\n }\n\n /**\n * Write file data for an existing Asset record (no DB record created).\n *\n * Use this when you've already created the record (e.g., via a\n * collection.create()) and only need to persist the file data.\n *\n * @param asset - The existing asset to write data for\n * @param data - File data as a Buffer\n * @param opts - Storage options (mimeType required)\n * @returns The sourceUri for the written file\n */\n async storeFile(\n asset: Asset,\n data: Buffer,\n opts: { mimeType: string; typeSlug?: string },\n ): Promise<string> {\n return this.writeAssetData(asset, data, opts);\n }\n\n /**\n * Write buffer to disk and create an Asset record.\n *\n * @param name - Human-readable name for the asset\n * @param data - File data as a Buffer\n * @param opts - Storage options (mimeType required)\n * @returns Created Asset instance\n */\n async store(name: string, data: Buffer, opts: StoreOptions): Promise<Asset> {\n const typeSlug = opts.typeSlug ?? 'file';\n\n // Create the Asset record first to get an ID\n const asset = (await this.collection.create({\n name,\n mimeType: opts.mimeType,\n typeSlug,\n statusSlug: opts.statusSlug ?? 'active',\n tenantId: opts.tenantId ?? null,\n ownerProfileId: opts.ownerProfileId ?? null,\n sourceAssetId: opts.sourceAssetId ?? null,\n description: opts.description ?? '',\n sourceType: opts.sourceType ?? '',\n externalId: opts.externalId ?? '',\n metadata: serializeStoreMetadata(opts.metadata) ?? '',\n sourceUri: '', // Will be updated after file write\n })) as Asset;\n\n try {\n // Write file to storage\n asset.sourceUri = await this.writeAssetData(asset, data, {\n mimeType: opts.mimeType,\n typeSlug,\n });\n } catch (err) {\n // Clean up orphaned Asset record on file write failure\n await asset.delete();\n throw err;\n }\n\n // Update the Asset with the file URI\n await asset.save();\n\n return asset;\n }\n\n /**\n * Store a new version of an existing asset.\n *\n * @param asset - The existing asset to version\n * @param data - File data for the new version\n * @param opts - Optional overrides for store options\n * @returns The newly created version Asset\n */\n async storeVersion(\n asset: Asset,\n data: Buffer,\n opts: Partial<StoreOptions> = {},\n ): Promise<Asset> {\n const primaryVersionId =\n asset.primaryVersionId ?? AssetStore.requireAssetId(asset);\n const { metadata, ...versionOptions } = opts;\n const versionUpdates: Partial<Asset> = { ...versionOptions };\n const serializedMetadata = serializeStoreMetadata(metadata);\n if (serializedMetadata !== undefined) {\n versionUpdates.metadata = serializedMetadata;\n } else {\n delete versionUpdates.metadata;\n }\n const newVersion = await this.collection.createNewVersion(\n primaryVersionId,\n '', // sourceUri will be set by store\n versionUpdates,\n );\n\n const mimeType = opts.mimeType ?? asset.mimeType;\n const typeSlug = opts.typeSlug ?? asset.typeSlug ?? 'file';\n\n try {\n newVersion.sourceUri = await this.writeAssetData(newVersion, data, {\n mimeType,\n typeSlug,\n });\n } catch (err) {\n await newVersion.delete();\n throw err;\n }\n\n if (opts.mimeType) newVersion.mimeType = opts.mimeType;\n await newVersion.save();\n\n return newVersion;\n }\n\n /**\n * Read file data for a specific version of an asset.\n *\n * @param asset - The asset (any version in the chain)\n * @param version - The version number to read\n * @returns File data as a Buffer\n */\n async readVersion(asset: Asset, version: number): Promise<Buffer> {\n const primaryVersionId =\n asset.primaryVersionId ?? AssetStore.requireAssetId(asset);\n const versions = await this.collection.listVersions(primaryVersionId);\n const targetVersion = versions.find((v) => v.version === version);\n\n if (!targetVersion) {\n throw new Error(\n `Version ${version} not found for asset ${primaryVersionId}`,\n );\n }\n\n return this.read(targetVersion);\n }\n\n /**\n * Read file data from an Asset's sourceUri.\n *\n * @param asset - Asset to read data for\n * @returns File data as a Buffer\n */\n async read(asset: Asset): Promise<Buffer> {\n const filePath = AssetStore.pathFromUri(asset.sourceUri);\n const target = await this.resolveStorage({\n operation: 'read',\n asset,\n path: AssetStore.providerRelativePath(filePath, this.fsOptions),\n sourceUri: asset.sourceUri,\n });\n return (await target.filesystem.read(target.path, { raw: true })) as Buffer;\n }\n\n /**\n * Read file by asset ID.\n *\n * @param id - Asset ID to look up\n * @returns Object with data Buffer and Asset, or null if not found\n */\n async readById(id: string): Promise<{ data: Buffer; asset: Asset } | null> {\n const asset = (await this.collection.get({ id })) as Asset | null;\n if (!asset) return null;\n\n try {\n const data = await this.read(asset);\n return { data, asset };\n } catch {\n return null;\n }\n }\n\n /**\n * Delete retained file bytes without requiring an existing Asset record.\n *\n * @param asset - Asset to remove\n */\n async removeFile(asset: Asset): Promise<void> {\n // Delete the file if it exists\n if (asset.sourceUri) {\n const filePath = AssetStore.pathFromUri(asset.sourceUri);\n const target = await this.resolveStorage({\n operation: 'delete',\n asset,\n path: AssetStore.providerRelativePath(filePath, this.fsOptions),\n sourceUri: asset.sourceUri,\n });\n try {\n await target.filesystem.delete(target.path);\n } catch (err) {\n if (!isFileNotFoundError(err)) {\n throw err;\n }\n // An already-missing file is successful idempotent cleanup.\n }\n }\n }\n\n /** Delete bytes and the owner record. Storage-only recovery uses removeFile. */\n async remove(asset: Asset): Promise<void> {\n await this.removeFile(asset);\n await asset.delete();\n }\n\n /**\n * Extract filesystem path from a sourceUri.\n * Handles file://, s3://, and plain paths.\n *\n * @param sourceUri - Asset sourceUri (e.g., 'file:///path/to/file.mp4', 's3://bucket/key')\n * @returns Filesystem path\n */\n static pathFromUri(sourceUri: string): string {\n if (sourceUri.startsWith('file://')) {\n return sourceUri.slice(7);\n }\n if (sourceUri.startsWith('s3://')) {\n // Return everything after s3://bucket/\n const withoutScheme = sourceUri.slice(5);\n const slashIdx = withoutScheme.indexOf('/');\n return slashIdx >= 0 ? withoutScheme.slice(slashIdx + 1) : withoutScheme;\n }\n return sourceUri;\n }\n}\n","/**\n * AssetCollection - Collection manager for Asset instances\n *\n * Provides tag management, versioning, tenant-aware queries, and operations for assets\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { type Tag, TagCollection } from '@happyvertical/smrt-tags';\nimport {\n getCurrentTenant,\n withTenantGlobalRead,\n} from '@happyvertical/smrt-tenancy';\nimport { Asset } from './asset';\nimport type { AssetTag } from './asset-tag';\nimport type { AssetTagCollection } from './asset-tags';\n\n/** Tag context used for asset tags unless a caller names another. */\nexport const ASSET_TAG_CONTEXT = 'asset';\n\nexport interface AssetTagInput {\n /** Display name for a newly created tag (defaults to the label given). */\n name?: string;\n /** Tag context (namespace); defaults to {@link ASSET_TAG_CONTEXT}. */\n context?: string;\n}\n\n/** The slug a tag label is stored under: `Town hall` → `town-hall`. */\nexport function assetTagSlug(label: string): string {\n return String(label ?? '')\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '');\n}\n\nfunction titleFromSlug(slug: string): string {\n const words = slug.replace(/-/g, ' ');\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\nexport class AssetCollection extends SmrtCollection<Asset> {\n static readonly _itemClass = Asset;\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenant-Aware Query Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find all assets belonging to a specific tenant\n *\n * @param tenantId - The tenant ID to filter by\n * @returns Array of assets belonging to this tenant\n */\n async findByTenant(tenantId: string): Promise<Asset[]> {\n return (await this.list({ where: { tenantId } })) as Asset[];\n }\n\n /**\n * Find all global assets (assets without a tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global assets\n */\n async findGlobal(): Promise<Asset[]> {\n const tenantId = getCurrentTenant()?.tenantId;\n const read = async () => this.list({ where: { tenantId: null } });\n return tenantId ? withTenantGlobalRead(tenantId, read) : read();\n }\n\n /**\n * Find assets belonging to a tenant plus all global assets.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - The tenant ID to include\n * @returns Array of tenant-specific and global assets\n */\n async findWithGlobals(tenantId: string): Promise<Asset[]> {\n return withTenantGlobalRead(tenantId, async () =>\n this.list({ where: [[{ tenantId }], [{ tenantId: null }]] }),\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tags (asset_tags → @happyvertical/smrt-tags Tag)\n // ─────────────────────────────────────────────────────────────────────────────\n\n private async assetTagCollection(): Promise<AssetTagCollection> {\n const { AssetTagCollection } = await import('./asset-tags');\n return AssetTagCollection.create({ db: this.db });\n }\n\n private async tagCollection(): Promise<TagCollection> {\n return TagCollection.create({ db: this.db });\n }\n\n private async requireAsset(assetId: string): Promise<Asset> {\n const asset = (await this.get({ id: assetId })) as Asset | null;\n if (!asset?.id) throw new Error(`Asset '${assetId}' not found`);\n return asset;\n }\n\n /**\n * Find the asset's tag for `tag` (a slug or a label) in the asset's own\n * tenant, optionally creating it. A tag of another tenant is never used.\n */\n private async resolveAssetTag(\n asset: Asset,\n tag: string,\n options: AssetTagInput & { create: boolean },\n ): Promise<Tag | null> {\n const slug = assetTagSlug(tag);\n if (!slug) throw new Error('A tag needs at least one letter or digit');\n const context = options.context ?? ASSET_TAG_CONTEXT;\n const tags = await this.tagCollection();\n const matches = (await tags.list({\n where: asset.tenantId\n ? { slug, context, tenantId: asset.tenantId }\n : { slug, context },\n limit: 10,\n })) as Tag[];\n const existing = matches.find(\n (candidate) => (candidate.tenantId ?? null) === (asset.tenantId ?? null),\n );\n if (existing || !options.create) return existing ?? null;\n\n const trimmed = tag.trim();\n return (await tags.create({\n slug,\n name:\n options.name?.trim() ||\n (trimmed !== slug ? trimmed : titleFromSlug(slug)),\n context,\n level: 0,\n tenantId: asset.tenantId ?? null,\n })) as Tag;\n }\n\n /**\n * Add a tag to an asset. `tag` is a slug (`town-hall`) or a label\n * (`Town hall`); the tag is found or created in the asset's tenant under\n * `context` (default {@link ASSET_TAG_CONTEXT}). Adding a tag twice is a\n * no-op. Returns the Tag.\n *\n * @throws when the asset does not exist (or is not visible in the active\n * tenant context)\n */\n async addTag(\n assetId: string,\n tag: string,\n options: AssetTagInput = {},\n ): Promise<Tag> {\n const asset = await this.requireAsset(assetId);\n const resolved = (await this.resolveAssetTag(asset, tag, {\n ...options,\n create: true,\n })) as Tag;\n const links = await this.assetTagCollection();\n const existing = await links.byLeft(asset.id as string, {\n tagId: resolved.id,\n });\n if (existing.length === 0) {\n await links.attach(asset.id as string, resolved.id as string, {\n tenantId: asset.tenantId ?? null,\n });\n }\n return resolved;\n }\n\n /**\n * Remove a tag (slug or label) from an asset. Unknown tags are ignored.\n * The Tag itself is kept; only the link goes.\n */\n async removeTag(\n assetId: string,\n tag: string,\n options: Pick<AssetTagInput, 'context'> = {},\n ): Promise<void> {\n const asset = await this.requireAsset(assetId);\n const resolved = await this.resolveAssetTag(asset, tag, {\n ...options,\n create: false,\n });\n if (!resolved?.id) return;\n const links = await this.assetTagCollection();\n await links.detach(asset.id as string, resolved.id);\n }\n\n /**\n * Make the asset's tags (in `context`) exactly `tags`: missing ones are\n * added, others in the same context removed. Returns the resulting Tags.\n */\n async setTags(\n assetId: string,\n tags: string[],\n options: Pick<AssetTagInput, 'context'> = {},\n ): Promise<Tag[]> {\n const asset = await this.requireAsset(assetId);\n const context = options.context ?? ASSET_TAG_CONTEXT;\n const wanted: Tag[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const slug = assetTagSlug(tag);\n if (!slug || seen.has(slug)) continue;\n seen.add(slug);\n wanted.push(await this.addTag(asset.id as string, tag, { context }));\n }\n const wantedIds = new Set(wanted.map((tag) => tag.id));\n const current = await asset.getTags();\n for (const tag of current) {\n if (tag.context === context && !wantedIds.has(tag.id)) {\n await this.removeTag(asset.id as string, tag.slug, { context });\n }\n }\n return wanted;\n }\n\n /**\n * Tags for many assets in two queries, keyed by asset id (assets without\n * tags map to an empty list). Useful for list views.\n */\n async getTagsForAssets(assetIds: string[]): Promise<Map<string, Tag[]>> {\n const result = new Map<string, Tag[]>();\n const ids = [...new Set(assetIds.filter(Boolean))];\n for (const id of ids) result.set(id, []);\n if (ids.length === 0) return result;\n\n const links = await this.assetTagCollection();\n const rows = (await links.list({\n where: { 'assetId in': ids },\n })) as AssetTag[];\n if (rows.length === 0) return result;\n\n const tags = await this.tagCollection();\n const tagById = new Map(\n (\n (await tags.listByIds([\n ...new Set(rows.map((row) => row.tagId)),\n ])) as Tag[]\n )\n .filter((tag) => tag.id)\n .map((tag) => [tag.id as string, tag]),\n );\n for (const row of rows) {\n const tag = tagById.get(row.tagId);\n if (tag) result.get(row.assetId)?.push(tag);\n }\n for (const list of result.values()) {\n list.sort((a, b) => a.name.localeCompare(b.name));\n }\n return result;\n }\n\n /**\n * Get all assets with a specific tag (slug or label), within the active\n * tenant context.\n */\n async getByTag(\n tag: string,\n options: Pick<AssetTagInput, 'context'> = {},\n ): Promise<Asset[]> {\n const slug = assetTagSlug(tag);\n if (!slug) return [];\n const tags = await this.tagCollection();\n const matches = (await tags.list({\n where: { slug, context: options.context ?? ASSET_TAG_CONTEXT },\n })) as Tag[];\n const tagIds = matches.map((match) => match.id).filter(Boolean) as string[];\n if (tagIds.length === 0) return [];\n\n const links = await this.assetTagCollection();\n const rows = (await links.list({\n where: { 'tagId in': tagIds },\n })) as AssetTag[];\n const assetIds = [...new Set(rows.map((row) => row.assetId))];\n if (assetIds.length === 0) return [];\n return (await this.listByIds(assetIds)) as Asset[];\n }\n\n /**\n * Get assets by type\n *\n * @param typeSlug - The asset type slug (e.g., 'image', 'video')\n * @returns Array of assets matching the type\n */\n async getByType(typeSlug: string): Promise<Asset[]> {\n return (await this.list({ where: { typeSlug } })) as Asset[];\n }\n\n /**\n * Get assets by status\n *\n * @param statusSlug - The asset status slug (e.g., 'published', 'draft')\n * @returns Array of assets matching the status\n */\n async getByStatus(statusSlug: string): Promise<Asset[]> {\n return (await this.list({ where: { statusSlug } })) as Asset[];\n }\n\n /**\n * Get assets by owner\n *\n * @param ownerProfileId - The profile ID of the owner\n * @returns Array of assets owned by this profile\n */\n async getByOwner(ownerProfileId: string): Promise<Asset[]> {\n return (await this.list({ where: { ownerProfileId } })) as Asset[];\n }\n\n /**\n * Create a new version of an existing asset\n *\n * @param primaryVersionId - The primary version ID (first version's ID)\n * @param newSourceUri - The new source URI for this version\n * @param updates - Optional additional updates\n * @returns The newly created asset version\n */\n async createNewVersion(\n primaryVersionId: string,\n newSourceUri: string,\n updates: Partial<Asset> = {},\n ): Promise<Asset> {\n // Get the current latest version\n const versions = await this.listVersions(primaryVersionId);\n if (versions.length === 0) {\n throw new Error(\n `No asset found with primary version ID: ${primaryVersionId}`,\n );\n }\n\n // Sort by version number to find the latest\n versions.sort((a, b) => b.version - a.version);\n const latestVersion = versions[0];\n const newVersionNumber = latestVersion.version + 1;\n\n // Give the new version a distinct, collision-free slug. The assets table is\n // unique on (slug, context, meta_type), so a reused slug would\n // upsert-overwrite an existing row instead of appending a version. Build\n // `<primary-slug>-v<n>` and verify it's not already taken by another version\n // in the chain OR by an unrelated asset — appending a disambiguator until\n // free. (The chain itself is tracked by `primaryVersionId`, not the slug.)\n const primary =\n versions.find((v) => v.id === primaryVersionId) ??\n versions[versions.length - 1];\n const baseSlug = String(primary?.slug ?? '') || 'asset';\n const takenInChain = new Set(\n versions.map((v) => v.slug).filter(Boolean) as string[],\n );\n let candidate = `${baseSlug}-v${newVersionNumber}`;\n for (let attempt = 1; ; attempt += 1) {\n const collides =\n takenInChain.has(candidate) ||\n (await this.get({ slug: candidate })) !== null;\n if (!collides) break;\n candidate = `${baseSlug}-v${newVersionNumber}-${attempt}`;\n }\n\n // Create new version\n return (await this.create({\n ...latestVersion,\n id: undefined, // Generate new ID\n slug: candidate,\n sourceUri: newSourceUri,\n version: newVersionNumber,\n primaryVersionId,\n createdAt: new Date(),\n updatedAt: new Date(),\n ...updates,\n })) as Asset;\n }\n\n /**\n * Get the latest version of an asset\n *\n * @param primaryVersionId - The primary version ID\n * @returns The latest version or null\n */\n async getLatestVersion(primaryVersionId: string): Promise<Asset | null> {\n const versions = await this.listVersions(primaryVersionId);\n if (versions.length === 0) return null;\n\n // Sort by version number descending\n versions.sort((a, b) => b.version - a.version);\n return versions[0];\n }\n\n /**\n * List all versions of an asset\n *\n * @param primaryVersionId - The primary version ID\n * @returns Array of all asset versions, ordered by version number\n */\n async listVersions(primaryVersionId: string): Promise<Asset[]> {\n // Use the collection's `list`/`get` (not raw `db.list`) so rows are properly\n // hydrated into `Asset` instances — `Object.assign(new Asset(), rawRow)`\n // would leave snake_case columns (`source_uri`, `primary_version_id`) on the\n // instance and never populate the camelCase fields. The chain is the rows\n // pointing at this primary plus the primary itself; the adapter has no OR, so\n // fetch both and dedupe by id.\n const [chained, primary] = await Promise.all([\n this.list({ where: { primaryVersionId } }) as Promise<Asset[]>,\n this.get({ id: primaryVersionId }) as Promise<Asset | null>,\n ]);\n\n const byId = new Map<string, Asset>();\n for (const asset of [...(primary ? [primary] : []), ...chained]) {\n if (asset?.id) byId.set(asset.id, asset);\n }\n\n const assets = Array.from(byId.values());\n assets.sort((a, b) => a.version - b.version);\n return assets;\n }\n\n /**\n * Get derivative assets of a source asset.\n *\n * Renamed from `getChildren(parentId)` in R3-D to match the rename of\n * the underlying column (`parent_id` → `source_asset_id`) and method\n * (`Asset.getChildren` → `Asset.getDerivatives`).\n *\n * @param sourceAssetId - The source asset ID\n * @returns Array of derivative assets\n */\n async getDerivatives(sourceAssetId: string): Promise<Asset[]> {\n return (await this.list({ where: { sourceAssetId } })) as Asset[];\n }\n\n /**\n * Get assets by MIME type pattern\n *\n * @param mimePattern - MIME type pattern (e.g., 'image/*', 'video/mp4')\n * @returns Array of matching assets\n */\n async getByMimeType(mimePattern: string): Promise<Asset[]> {\n const pattern = mimePattern.replace('*', '%');\n // Collection `list` (not raw `db.list`) so the rows hydrate into proper\n // `Asset` instances; the `<field> like` operator key is mapped to the column.\n return (await this.list({\n where: { 'mimeType like': pattern },\n })) as Asset[];\n }\n\n /**\n * Rollback to a previous version by creating a new version with the target's content.\n * Does NOT delete intermediate versions (safe rollback).\n *\n * @param primaryVersionId - The primary version ID of the version chain\n * @param targetVersion - The version number to rollback to\n * @returns The newly created asset version with content copied from target\n */\n async rollbackToVersion(\n primaryVersionId: string,\n targetVersion: number,\n ): Promise<Asset> {\n const versions = await this.listVersions(primaryVersionId);\n const target = versions.find((v) => v.version === targetVersion);\n\n if (!target) {\n throw new Error(\n `Version ${targetVersion} not found for asset ${primaryVersionId}`,\n );\n }\n\n // Create a new version that copies the target's sourceUri\n return await this.createNewVersion(primaryVersionId, target.sourceUri, {\n description: `Rollback to version ${targetVersion}`,\n } as Partial<Asset>);\n }\n\n /**\n * Get assets in a specific folder\n *\n * @param folderId - The folder ID to list contents for\n * @returns Array of assets in this folder\n */\n async getByFolder(folderId: string): Promise<Asset[]> {\n return (await this.list({ where: { folderId } })) as Asset[];\n }\n}\n","/**\n * AssetRuntime - shared asset runtime surface\n *\n * Bundles the pieces an app or agent needs to work with `smrt-assets`\n * consistently:\n *\n * - `AssetCollection` for records\n * - `AssetAssociationCollection` for generic/provenance links\n * - `AssetStore` for provider-agnostic byte storage\n * - a single `ProviderOptions` that wires storage in one place\n *\n * Agents that want to accept a shared asset runtime should take an\n * `AssetRuntime` (or `AssetRuntimeLike`) in their options, rather than\n * asking callers to hand-wire a collection + store.\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport type { Asset } from './asset';\nimport type { AssetAssociation } from './asset-association';\nimport { AssetAssociationCollection } from './asset-associations';\nimport {\n type AssetCapabilityName,\n type AssetCapabilityProvider,\n AssetCapabilitySkippedError,\n AssetCapabilityUnavailableError,\n type AssetExternalSourceRef,\n type AssetExternalSyncResult,\n type AssetNearbySearchInput,\n type AssetProcessResult,\n type AssetSearchResult,\n type AssetVariantRequest,\n type AssetVariantResult,\n type AssetWorkflowInput,\n type AssetWorkflowResult,\n} from './asset-capabilities';\nimport {\n ASSET_ROLES,\n type AssetExtractionStatus,\n type AssetRole,\n} from './asset-conventions';\nimport {\n AssetStore,\n type AssetStoreOptions,\n type ProviderOptions,\n type StoreOptions,\n} from './asset-store';\nimport { AssetCollection } from './assets';\n\n/**\n * DB configuration accepted by the runtime — mirrors the shape\n * `SmrtCollection.create({ db })` already accepts.\n */\nexport type AssetRuntimeDb = NonNullable<SmrtClassOptions['db']>;\n\n/**\n * Options for constructing an `AssetRuntime` via `createAssetRuntime()`.\n */\nexport interface AssetRuntimeOptions {\n /**\n * Database used for `AssetCollection` and `AssetAssociationCollection`.\n *\n * Accepts anything `SmrtCollection.create({ db })` accepts — a string\n * URL, a config object, or a live `DatabaseInterface`.\n */\n db: AssetRuntimeDb;\n\n /**\n * Storage provider for `AssetStore`.\n *\n * A string is treated as a local filesystem `basePath`; otherwise\n * forwarded to `@happyvertical/files`.\n */\n storage: ProviderOptions;\n\n /**\n * Optional behavior for the underlying `AssetStore`.\n *\n * Use this to provide a storage resolver while keeping the convenience\n * runtime factory.\n */\n storeOptions?: AssetStoreOptions;\n\n /**\n * Optional collection instance. If omitted, one is created from `db`.\n * Useful in tests or when the caller already has a configured\n * collection (e.g. from `ObjectRegistry`).\n */\n collection?: AssetCollection;\n\n /**\n * Optional associations collection. If omitted, one is created from `db`.\n */\n associations?: AssetAssociationCollection;\n\n /**\n * Optional asset capability providers.\n *\n * Providers let callers keep one app-facing asset runtime while\n * delegating processing, variant generation, search, external sync, or\n * workflow submission to local processors or external systems.\n */\n capabilityProviders?: AssetCapabilityProvider[];\n}\n\n/**\n * Structural shape of the runtime. Agents and serving helpers should\n * depend on `AssetRuntimeLike` rather than the concrete class so tests\n * can pass in a minimal object.\n */\nexport interface AssetRuntimeLike {\n readonly collection: AssetCollection;\n readonly associations: AssetAssociationCollection;\n readonly store: AssetStore;\n readonly capabilityProviders?: readonly AssetCapabilityProvider[];\n\n /** Persist original bytes and their Asset record through the shared store. */\n storeSourceAsset(\n name: string,\n data: Buffer,\n opts: StoreOptions,\n ): Promise<Asset>;\n}\n\n/**\n * Options for `AssetRuntime.storeDerivedAsset()`.\n */\nexport interface StoreDerivedAssetOptions\n extends Omit<StoreOptions, 'sourceAssetId'> {\n /**\n * Relationship role for the derivation link.\n *\n * Defaults to `ASSET_ROLES.DERIVATION_SOURCE` when `linkAssociation`\n * is true (the default). Set explicitly for roles like\n * `document_image` or `thumbnail` when a derivative has a more\n * specific semantic than \"came from\".\n */\n role?: AssetRole | string;\n\n /**\n * If true (default), also create an `AssetAssociation` record with\n * `assetId=source.id`, `metaType=<derivativeMetaType>`,\n * `metaId=<newAssetId>`, `role=<role>` so the provenance link is\n * queryable independent of `sourceAssetId`.\n *\n * Set to `false` if you only want the column-level `sourceAssetId`\n * derivation link.\n */\n linkAssociation?: boolean;\n\n /**\n * `metaType` string stored on the `AssetAssociation`. This describes\n * the *derivative* object (the target of `metaId`), not the source.\n *\n * Defaults to `'Asset'`. Callers whose derivatives are STI subclasses\n * — e.g. an `Image` produced from a PDF — should pass the subclass\n * name here so consumers can distinguish subtype derivatives from\n * plain assets. This matches the convention used by\n * `smrt-images`' `ImageDeriver.deriveWithAssociations()`.\n */\n derivativeMetaType?: string;\n}\n\n/**\n * Options for `AssetRuntime.linkDerivation()`.\n */\nexport interface LinkDerivationOptions {\n role?: AssetRole | string;\n /**\n * `metaType` describing the derivative object (target of `metaId`).\n * See `StoreDerivedAssetOptions.derivativeMetaType`.\n */\n derivativeMetaType?: string;\n}\n\n/**\n * Convenience runtime for `smrt-assets` callers. See the package\n * `CLAUDE.md` for the full \"source vs derived\" vocabulary.\n */\nexport class AssetRuntime implements AssetRuntimeLike {\n readonly capabilityProviders: AssetCapabilityProvider[];\n\n constructor(\n public readonly collection: AssetCollection,\n public readonly associations: AssetAssociationCollection,\n public readonly store: AssetStore,\n capabilityProviders: AssetCapabilityProvider[] = [],\n ) {\n this.capabilityProviders = [...capabilityProviders];\n }\n\n registerCapabilityProvider(provider: AssetCapabilityProvider): this {\n this.capabilityProviders.push(provider);\n return this;\n }\n\n private providersFor(\n capability: AssetCapabilityName,\n ): AssetCapabilityProvider[] {\n const providers = this.capabilityProviders.filter(\n (candidate) => typeof candidate[capability] === 'function',\n );\n if (providers.length === 0) {\n throw new AssetCapabilityUnavailableError(capability);\n }\n return providers;\n }\n\n async processAsset(\n asset: Asset,\n input: {\n variants?: AssetVariantRequest[];\n metadata?: Record<string, unknown>;\n } = {},\n ): Promise<AssetProcessResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('processAsset')) {\n const processAsset = provider.processAsset;\n if (!processAsset) continue;\n try {\n return await processAsset({\n runtime: this,\n asset,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError('processAsset', skipped?.message);\n }\n\n async ensureVariant(\n asset: Asset,\n request: AssetVariantRequest,\n ): Promise<AssetVariantResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('ensureVariant')) {\n const ensureVariant = provider.ensureVariant;\n if (!ensureVariant) continue;\n try {\n return await ensureVariant({\n runtime: this,\n asset,\n request,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'ensureVariant',\n skipped?.message,\n );\n }\n\n async searchNearbyAssets(\n input: Omit<AssetNearbySearchInput, 'runtime'>,\n ): Promise<AssetSearchResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('searchNearbyAssets')) {\n const searchNearbyAssets = provider.searchNearbyAssets;\n if (!searchNearbyAssets) continue;\n try {\n return await searchNearbyAssets({\n runtime: this,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'searchNearbyAssets',\n skipped?.message,\n );\n }\n\n async syncExternalAsset(\n asset: Asset,\n input: {\n externalId?: string | null;\n sourceRef?: AssetExternalSourceRef | null;\n metadata?: Record<string, unknown>;\n } = {},\n ): Promise<AssetExternalSyncResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('syncExternalAsset')) {\n const syncExternalAsset = provider.syncExternalAsset;\n if (!syncExternalAsset) continue;\n try {\n return await syncExternalAsset({\n runtime: this,\n asset,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'syncExternalAsset',\n skipped?.message,\n );\n }\n\n async submitAssetWorkflow(\n asset: Asset,\n input: Omit<AssetWorkflowInput, 'runtime' | 'asset'>,\n ): Promise<AssetWorkflowResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('submitAssetWorkflow')) {\n const submitAssetWorkflow = provider.submitAssetWorkflow;\n if (!submitAssetWorkflow) continue;\n try {\n return await submitAssetWorkflow({\n runtime: this,\n asset,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'submitAssetWorkflow',\n skipped?.message,\n );\n }\n\n /**\n * Create a new source asset with both a record and bytes on disk.\n *\n * This is the same as `AssetStore.store()`, but exposed on the runtime\n * so callers only need one handle.\n */\n storeSourceAsset(\n name: string,\n data: Buffer,\n opts: StoreOptions,\n ): Promise<Asset> {\n return this.store.store(name, data, opts);\n }\n\n /**\n * Create a derivative of `source`, persist its bytes, and optionally\n * record a provenance association.\n *\n * The new asset's `sourceAssetId` always points at `source.id`. When\n * `linkAssociation` is true (the default), the runtime also writes\n * an `AssetAssociation` so queries by role (e.g. \"all `document_image`\n * derivatives for this `source_document`\") work without scanning\n * `source_asset_id` chains.\n */\n async storeDerivedAsset(\n source: Asset,\n name: string,\n data: Buffer,\n opts: StoreDerivedAssetOptions,\n ): Promise<Asset> {\n if (!source.id) {\n throw new Error(\n 'storeDerivedAsset: source asset must be persisted (missing id)',\n );\n }\n\n const {\n role: rawRole,\n linkAssociation = true,\n derivativeMetaType = 'Asset',\n ...storeOpts\n } = opts;\n const role = rawRole ?? ASSET_ROLES.DERIVATION_SOURCE;\n\n const derived = await this.store.store(name, data, {\n ...storeOpts,\n sourceAssetId: source.id,\n });\n\n if (linkAssociation && derived.id) {\n await this.associations.attach(\n derivativeMetaType,\n derived.id,\n source.id,\n { role },\n );\n }\n\n return derived;\n }\n\n /**\n * Record a provenance association between an existing source asset\n * and an existing derivative asset without touching bytes.\n */\n async linkDerivation(\n source: Asset,\n derivative: Asset,\n opts: LinkDerivationOptions = {},\n ): Promise<AssetAssociation> {\n if (!source.id || !derivative.id) {\n throw new Error(\n 'linkDerivation: both source and derivative must be persisted (missing id)',\n );\n }\n const role = opts.role ?? ASSET_ROLES.DERIVATION_SOURCE;\n const derivativeMetaType = opts.derivativeMetaType ?? 'Asset';\n return this.associations.attach(\n derivativeMetaType,\n derivative.id,\n source.id,\n { role },\n );\n }\n\n /**\n * Update the standard extraction-status metadata on an asset's\n * `description` JSON sidecar. This is a thin convenience over the\n * convention in `asset-conventions.ts` — callers that store\n * metadata elsewhere can ignore it.\n *\n * **How existing descriptions are handled**:\n * - Empty / unset → fresh JSON object.\n * - Valid JSON object → merged into; existing keys preserved.\n * - Free-form prose or non-object JSON → preserved under the\n * reserved `text` key of the resulting object (e.g.\n * `{ text: \"original prose\", extractionStatus: \"...\" }`). No prose\n * is discarded.\n *\n * Callers that already use `text` for something else, or that need\n * an entirely separate metadata surface, should either round-trip\n * the JSON themselves or skip this helper — its only job is the\n * `extractionStatus` / `extractionError` / `extractedAt` triple.\n *\n * Error handling: when `status` transitions away from `failed`\n * without a new `extra.error`, the stale `extractionError` is\n * cleared so downstream consumers don't misread the current state.\n * When `status === 'succeeded'`, `extractedAt` is stamped to now\n * unless the caller provides one.\n */\n async setExtractionStatus(\n asset: Asset,\n status: AssetExtractionStatus,\n extra: { error?: string; extractedAt?: Date } = {},\n ): Promise<void> {\n const existing = parseDescriptionSidecar(asset.description);\n const next: Record<string, unknown> = {\n ...existing,\n extractionStatus: status,\n };\n if (extra.error !== undefined) {\n next.extractionError = extra.error;\n } else if (status !== 'failed') {\n // Transitioning out of a prior failure — drop the stale error\n // so consumers don't misread it as still-failing.\n delete next.extractionError;\n }\n if (status === 'succeeded') {\n next.extractedAt = (extra.extractedAt ?? new Date()).toISOString();\n }\n asset.description = JSON.stringify(next);\n await asset.save();\n }\n}\n\n/**\n * Factory — lazily creates a shared asset runtime from DB + storage\n * config. Initializes the store's filesystem adapter before returning.\n *\n * @example\n * ```ts\n * import { createAssetRuntime, ASSET_ROLES } from '@happyvertical/smrt-assets';\n *\n * const runtime = await createAssetRuntime({\n * db: { type: 'sqlite', url: 'app.db' },\n * storage: { type: 's3', bucket: 'my-app' },\n * });\n *\n * const pdf = await runtime.storeSourceAsset('agenda.pdf', bytes, {\n * mimeType: 'application/pdf',\n * typeSlug: 'document',\n * });\n *\n * await runtime.storeDerivedAsset(pdf, 'agenda-p1.png', pageBytes, {\n * mimeType: 'image/png',\n * typeSlug: 'image',\n * role: ASSET_ROLES.DOCUMENT_IMAGE,\n * });\n * ```\n */\nexport async function createAssetRuntime(\n options: AssetRuntimeOptions,\n): Promise<AssetRuntime> {\n const collection =\n options.collection ?? (await AssetCollection.create({ db: options.db }));\n const associations =\n options.associations ??\n (await AssetAssociationCollection.create({ db: options.db }));\n const store = await new AssetStore(\n options.storage,\n collection,\n options.storeOptions,\n ).initialize();\n return new AssetRuntime(\n collection,\n associations,\n store,\n options.capabilityProviders,\n );\n}\n\n/**\n * Parse the `description` field as a JSON sidecar object used by\n * `setExtractionStatus()`. Free-form prose and non-object JSON values\n * are preserved under the reserved `text` key so the helper is\n * non-destructive on assets whose descriptions were authored as\n * human-readable text.\n */\nfunction parseDescriptionSidecar(description: string): Record<string, unknown> {\n if (!description) return {};\n try {\n const parsed = JSON.parse(description);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n // Non-object JSON (number, string, array, null) — treat the whole\n // value as prose-equivalent and stash it under `text`.\n return { text: description };\n } catch {\n // Not JSON — preserve the original prose under `text`.\n return { text: description };\n }\n}\n","/**\n * Generic asset-serving contract\n *\n * Framework-agnostic helpers that turn a `smrt-assets` runtime plus\n * an asset id (or asset instance) into a standard `Response`. Apps\n * (SvelteKit, Hono, plain Node) can wire this once and stop inventing\n * agent-specific routes.\n *\n * `serveAsset()` returns a Web `Response` built against the global\n * `Response` available in Node 18+. If a runtime needs an older Node,\n * pass its own Response constructor via `responseCtor`.\n */\n\nimport type { Asset } from './asset';\nimport type { AssetRuntimeLike } from './asset-runtime';\n\n/**\n * Result of `resolveAssetForServing()` — lets advanced callers\n * stream bytes themselves while still reusing the access check.\n */\nexport interface ResolvedAssetBytes {\n asset: Asset;\n data: Buffer;\n contentType: string;\n filename: string;\n size: number;\n}\n\n/**\n * Options for `serveAsset()` and `resolveAssetForServing()`.\n */\nexport interface ServeAssetOptions {\n /** Shared asset runtime (collection + store). */\n runtime: AssetRuntimeLike;\n\n /**\n * Either an asset id to look up or a fully-loaded `Asset`\n * instance. Passing the instance skips the `collection.get()` call.\n */\n asset: string | Asset;\n\n /**\n * Tenant of the caller. When provided, the asset must either\n * belong to this tenant or be a global asset (`tenantId = null`)\n * or a 403 is returned.\n */\n tenantId?: string | null;\n\n /**\n * Optional `Content-Disposition` — `'inline'` (default) or\n * `'attachment'`. Use `'attachment'` for download links.\n */\n disposition?: 'inline' | 'attachment';\n\n /**\n * Extra headers merged into the 200 response. Does not affect\n * 403/404/500 responses.\n */\n headers?: Record<string, string>;\n\n /**\n * Optional override for the filename used in `Content-Disposition`.\n * Defaults to the basename of the asset's `sourceUri` or `name`.\n */\n filename?: string;\n\n /**\n * Optional access-check hook. Return `false` to short-circuit with a\n * 403 before bytes are loaded. Runs after the built-in tenant check.\n */\n canAccess?: (asset: Asset) => boolean | Promise<boolean>;\n\n /**\n * How to handle assets whose `sourceUri` is an `http(s)` URL rather\n * than a store-backed path.\n *\n * - `'error'` (default): treat a remote URI as an error (`500`). This\n * is the safe default — `sourceUri` is attacker-settable on many\n * asset-creation paths, so proxying it blindly is an SSRF vector\n * (fetching `169.254.169.254` cloud metadata, internal services,\n * etc.). Opt into `'proxy'` only for trusted sources.\n * - `'proxy'`: fetch the bytes via `globalThis.fetch` and return them\n * inline. Keeps the origin URL hidden from the client and preserves\n * the same tenant/access checks the local path gets. Guarded against\n * SSRF: the host must resolve to a public IP (private, loopback,\n * link-local, and cloud-metadata ranges are rejected), only\n * `http(s)` is allowed, and redirects are re-validated.\n * - `'redirect'`: return a `302` to `sourceUri` instead of fetching.\n * Skips the byte round-trip but exposes the origin URL.\n *\n * Anything that does not look like `http://` or `https://` is read\n * through the store as usual.\n */\n remoteMode?: 'proxy' | 'redirect' | 'error';\n\n /**\n * Custom `fetch` implementation for the `remoteMode: 'proxy'` path.\n * Defaults to `globalThis.fetch` (Node 18+).\n */\n fetchImpl?: typeof fetch;\n\n /**\n * Maximum number of bytes to buffer when `remoteMode: 'proxy'` fetches\n * a remote asset. Protects against memory-exhaustion DoS from a hostile\n * (or compromised) origin streaming an unbounded body. Defaults to\n * {@link DEFAULT_REMOTE_MAX_BYTES} (50 MiB).\n */\n remoteMaxBytes?: number;\n\n /**\n * Timeout in milliseconds for the `remoteMode: 'proxy'` fetch. Defaults\n * to {@link DEFAULT_REMOTE_TIMEOUT_MS} (10s). Prevents a slow/hung\n * origin from pinning the request indefinitely.\n */\n remoteTimeoutMs?: number;\n\n /**\n * Custom `Response`-like constructor, for runtimes that don't have\n * a global `Response` (pre-Node-18, workers with a custom shim).\n * Defaults to `globalThis.Response`.\n */\n responseCtor?: ResponseConstructor;\n}\n\ntype ResponseConstructor = typeof Response;\n\n/** Default cap (50 MiB) on bytes buffered from a proxied remote asset. */\nexport const DEFAULT_REMOTE_MAX_BYTES = 50 * 1024 * 1024;\n\n/** Default timeout (10s) for a proxied remote-asset fetch. */\nexport const DEFAULT_REMOTE_TIMEOUT_MS = 10_000;\n\n/** Maximum number of redirects to follow during a proxied fetch. */\nconst MAX_REMOTE_REDIRECTS = 5;\n\n/**\n * MIME types safe to serve `inline`. Anything else is forced to\n * `attachment` so a browser won't render attacker-controlled content\n * (HTML/SVG/XML) in the serving origin's context (stored XSS).\n */\nconst INLINE_SAFE_MIME_TYPES = new Set<string>([\n 'application/json',\n 'application/pdf',\n 'audio/aac',\n 'audio/mpeg',\n 'audio/ogg',\n 'audio/wav',\n 'audio/webm',\n 'image/avif',\n 'image/gif',\n 'image/jpeg',\n 'image/png',\n 'image/webp',\n 'text/csv',\n 'text/plain',\n 'video/mp4',\n 'video/ogg',\n 'video/webm',\n]);\n\n/** Parse the bare type (drop parameters like `; charset=...`) and lowercase. */\nfunction normalizeMimeType(raw: string): string {\n return (raw.split(';')[0] ?? '').trim().toLowerCase();\n}\n\n/** Whether a MIME type is on the inline-safe safelist. */\nfunction isInlineSafeMimeType(contentType: string): boolean {\n return INLINE_SAFE_MIME_TYPES.has(normalizeMimeType(contentType));\n}\n\nfunction isRemoteUri(uri: string): boolean {\n return /^https?:\\/\\//i.test(uri);\n}\n\n/**\n * Reject hostnames/IPs that point at private, loopback, link-local, or\n * cloud-metadata ranges — the core SSRF guard for proxied fetches.\n *\n * This is a literal-IP check; it does NOT perform DNS resolution, so a\n * hostname that resolves to a private IP is not caught here. Treat\n * `remoteMode: 'proxy'` as trusted-source-only and keep the default at\n * `'error'`; this guard is defense-in-depth for the literal-IP and obvious\n * cloud-metadata cases that make up the bulk of real SSRF payloads.\n */\nfunction isBlockedHost(hostname: string): boolean {\n let host = hostname.trim().toLowerCase();\n // Strip IPv6 brackets if present (URL.hostname keeps them off, but be safe).\n if (host.startsWith('[') && host.endsWith(']')) {\n host = host.slice(1, -1);\n }\n // Strip terminal dot(s): a FQDN like `localhost.` or\n // `metadata.google.internal.` is equivalent to the dotless form and would\n // otherwise sail past the exact-match checks below while still resolving to\n // loopback/metadata.\n host = host.replace(/\\.+$/, '');\n\n if (host === '' || host === 'localhost' || host.endsWith('.localhost')) {\n return true;\n }\n\n // Cloud metadata endpoints (AWS/GCP/Azure/OpenStack link-local + alibaba).\n if (\n host === '169.254.169.254' ||\n host === 'metadata.google.internal' ||\n host === 'metadata' ||\n host === '100.100.100.200'\n ) {\n return true;\n }\n\n // IPv4 literal?\n const ipv4 = host.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n if (ipv4) {\n const octets = ipv4.slice(1).map((o) => Number(o));\n if (octets.some((o) => o > 255)) return true; // malformed → block\n return isBlockedIpv4(octets as [number, number, number, number]);\n }\n\n // IPv6 literal?\n if (host.includes(':')) {\n return isBlockedIpv6(host);\n }\n\n return false;\n}\n\nfunction isBlockedIpv4(octets: [number, number, number, number]): boolean {\n const [a, b] = octets;\n if (a === 0) return true; // 0.0.0.0/8\n if (a === 10) return true; // 10.0.0.0/8 private\n if (a === 127) return true; // loopback\n if (a === 169 && b === 254) return true; // link-local + metadata\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12 private\n if (a === 192 && b === 168) return true; // 192.168.0.0/16 private\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT\n if (a >= 224) return true; // multicast + reserved\n return false;\n}\n\n/**\n * Expand an IPv6 literal (possibly with a `::` run and/or a trailing dotted\n * IPv4 tail) into its eight 16-bit hextets. Returns `null` when the input\n * isn't a parseable IPv6 address — callers treat unparseable as \"not a\n * recognised v6 literal\" and fall back to blocking via the malformed paths.\n */\nfunction expandIpv6(host: string): number[] | null {\n let h = host.toLowerCase();\n // Drop a zone id (e.g. `fe80::1%eth0`) — irrelevant for range checks.\n const pct = h.indexOf('%');\n if (pct !== -1) h = h.slice(0, pct);\n\n // A trailing dotted-quad IPv4 tail (`::ffff:127.0.0.1`) becomes two hextets.\n // Strip it off entirely (colon included) and remember the two hextets it\n // contributes to the explicit-tail side.\n let tailHextets: number[] = [];\n const lastColon = h.lastIndexOf(':');\n const tail = lastColon === -1 ? '' : h.slice(lastColon + 1);\n if (tail.includes('.')) {\n const m = tail.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n if (!m) return null;\n const octets = m.slice(1).map((o) => Number(o));\n if (octets.some((o) => o > 255)) return null;\n tailHextets = [(octets[0] << 8) | octets[1], (octets[2] << 8) | octets[3]];\n h = h.slice(0, lastColon); // drop ':a.b.c.d' so no dangling empty segment\n // `h` now ends just before the colon that preceded the dotted quad. If the\n // address was exactly `::a.b.c.d`, `h` is now `:` (the leading half of the\n // `::`); normalise that so the `::` detection below still fires.\n if (h === ':') h = '::';\n }\n\n const parseHextets = (parts: string[]): number[] | null => {\n const out: number[] = [];\n for (const p of parts) {\n if (p === '' || !/^[0-9a-f]{1,4}$/.test(p)) return null;\n out.push(Number.parseInt(p, 16));\n }\n return out;\n };\n\n const doubleColon = h.indexOf('::');\n let head: number[] | null;\n let midTail: number[] | null;\n if (doubleColon !== -1) {\n if (h.indexOf('::', doubleColon + 1) !== -1) return null; // only one `::`\n const before = h.slice(0, doubleColon);\n const after = h.slice(doubleColon + 2);\n head = parseHextets(before === '' ? [] : before.split(':'));\n midTail = parseHextets(after === '' ? [] : after.split(':'));\n } else {\n head = parseHextets(h === '' ? [] : h.split(':'));\n midTail = [];\n }\n if (head === null || midTail === null) return null;\n\n const explicit = [...head, ...midTail, ...tailHextets];\n if (doubleColon !== -1) {\n if (explicit.length > 7) return null; // `::` must stand for ≥1 hextet\n const fill = 8 - explicit.length;\n return [...head, ...new Array(fill).fill(0), ...midTail, ...tailHextets];\n }\n if (explicit.length !== 8) return null;\n return explicit;\n}\n\nfunction isBlockedIpv6(host: string): boolean {\n const hextets = expandIpv6(host);\n if (hextets === null) return true; // unparseable v6 literal → block\n\n const [h0, h1, h2, h3, h4, h5, h6, h7] = hextets;\n\n const embeddedV4 = (): [number, number, number, number] => [\n (h6 >> 8) & 0xff,\n h6 & 0xff,\n (h7 >> 8) & 0xff,\n h7 & 0xff,\n ];\n\n // ::/128 unspecified and ::1/128 loopback.\n if (h0 === 0 && h1 === 0 && h2 === 0 && h3 === 0 && h4 === 0 && h5 === 0) {\n if (h6 === 0 && h7 === 0) return true; // ::\n if (h6 === 0 && h7 === 1) return true; // ::1\n // ::/96 IPv4-compatible (deprecated). Node normalises `::127.0.0.1` to\n // `::7f00:1`, which lands here — decode and re-check the embedded v4.\n return isBlockedIpv4(embeddedV4());\n }\n\n // ::ffff:0:0/96 — IPv4-mapped. Decode the embedded v4 (covers both the\n // dotted `::ffff:127.0.0.1` and hex `::ffff:7f00:1` forms, since both expand\n // to the same hextets) and run it through the v4 blocklist.\n if (\n h0 === 0 &&\n h1 === 0 &&\n h2 === 0 &&\n h3 === 0 &&\n h4 === 0 &&\n h5 === 0xffff\n ) {\n return isBlockedIpv4(embeddedV4());\n }\n\n // fe80::/10 link-local (fe80–febf): top 10 bits == 1111111010.\n if ((h0 & 0xffc0) === 0xfe80) return true;\n // fec0::/10 site-local (deprecated, but block defensively).\n if ((h0 & 0xffc0) === 0xfec0) return true;\n // fc00::/7 unique-local (fc00–fdff): top 7 bits == 1111110.\n if ((h0 & 0xfe00) === 0xfc00) return true;\n // ff00::/8 multicast.\n if ((h0 & 0xff00) === 0xff00) return true;\n\n return false;\n}\n\n/**\n * Validate a remote URL for proxying: only `http(s)` and only public hosts.\n * Throws `AssetServeError(502)` on a rejected target. The distinct `message`s\n * (invalid URL vs unsupported scheme vs blocked host) are server-side only —\n * `serveAsset` returns a single generic 502 body for all of them so the client\n * can't probe internal reachability via status OR body differences (S5 #1396).\n */\nfunction assertSafeRemoteUrl(rawUrl: string): URL {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new AssetServeError('Invalid remote asset URL', 502);\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new AssetServeError('Unsupported remote asset scheme', 502);\n }\n if (isBlockedHost(url.hostname)) {\n throw new AssetServeError('Remote asset host not allowed', 502);\n }\n return url;\n}\n\n/**\n * Fetch a remote asset with SSRF protection, a byte cap, and a timeout.\n *\n * Redirects are followed manually (`redirect: 'manual'`) so each hop's\n * `Location` is re-validated through {@link assertSafeRemoteUrl} — otherwise\n * a public host could 302 to `169.254.169.254`.\n */\nasync function fetchRemoteAsset(\n startUrl: string,\n fetchImpl: typeof fetch,\n maxBytes: number,\n timeoutMs: number,\n): Promise<{ data: Buffer; contentType: string | undefined }> {\n let current = assertSafeRemoteUrl(startUrl);\n\n for (let hop = 0; hop <= MAX_REMOTE_REDIRECTS; hop++) {\n // Use AbortController + setTimeout rather than AbortSignal.timeout():\n // this helper deliberately supports pre-Node-18 runtimes via\n // `responseCtor`/`fetchImpl`, where AbortSignal.timeout() may be missing\n // (S5 #1396, Copilot review). Clear the timer in `finally` so it never\n // leaks past the fetch.\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let response: Response;\n try {\n response = await fetchImpl(current.toString(), {\n redirect: 'manual',\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n // Manual redirect handling — re-validate each Location hop.\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (!location) {\n throw new AssetServeError(\n 'Remote asset redirect missing Location',\n 502,\n );\n }\n current = assertSafeRemoteUrl(new URL(location, current).toString());\n continue;\n }\n\n if (!response.ok) {\n // Keep the upstream status in `message` for server-side logs only; the\n // client gets the generic opaque 502 body so the origin's status can't\n // leak through the response (S5 #1396, Copilot review).\n throw new AssetServeError(\n `Remote asset upstream returned ${response.status}`,\n 502,\n );\n }\n\n const data = await readBodyWithCap(response, maxBytes);\n return {\n data,\n contentType: response.headers.get('content-type') ?? undefined,\n };\n }\n\n throw new AssetServeError('Too many remote asset redirects', 502);\n}\n\n/**\n * Read a response body into a Buffer, aborting if it exceeds `maxBytes`.\n * Streams when possible so an oversized body is rejected without first\n * buffering the whole thing; falls back to a post-hoc length check when the\n * response exposes no readable stream (e.g. test doubles).\n */\nasync function readBodyWithCap(\n response: Response,\n maxBytes: number,\n): Promise<Buffer> {\n const declared = response.headers.get('content-length');\n if (declared && Number(declared) > maxBytes) {\n throw new AssetServeError('Remote asset exceeds size limit', 502);\n }\n\n const body = response.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n throw new AssetServeError('Remote asset exceeds size limit', 502);\n }\n chunks.push(value);\n }\n }\n return Buffer.concat(chunks.map((c) => Buffer.from(c)));\n }\n\n // No stream available — buffer then enforce the cap.\n const buf = Buffer.from(await response.arrayBuffer());\n if (buf.byteLength > maxBytes) {\n throw new AssetServeError('Remote asset exceeds size limit', 502);\n }\n return buf;\n}\n\n/**\n * Resolve an asset + bytes for serving, enforcing the same tenant\n * and access checks as `serveAsset()` but returning the parts so\n * callers can render their own framework response.\n *\n * Throws `AssetServeError` with a status on any error so callers can\n * map to their HTTP layer.\n */\nexport async function resolveAssetForServing(\n options: ServeAssetOptions,\n): Promise<ResolvedAssetBytes> {\n const {\n runtime,\n asset: assetOrId,\n tenantId,\n canAccess,\n disposition: _disposition,\n } = options;\n\n const asset =\n typeof assetOrId === 'string'\n ? ((await runtime.collection.get({ id: assetOrId })) as Asset | null)\n : assetOrId;\n\n if (!asset) {\n throw new AssetServeError('Asset not found', 404);\n }\n\n if (\n tenantId !== undefined &&\n asset.tenantId !== null &&\n asset.tenantId !== tenantId\n ) {\n throw new AssetServeError('Asset not visible to this tenant', 403);\n }\n\n if (canAccess) {\n const allowed = await canAccess(asset);\n if (!allowed) {\n throw new AssetServeError('Asset access denied', 403);\n }\n }\n\n // Default to 'error': `sourceUri` is attacker-settable on many\n // asset-creation paths, so proxying it blindly is an SSRF vector. Callers\n // serving trusted sources opt into 'proxy' explicitly (S5 #1396).\n const remoteMode = options.remoteMode ?? 'error';\n let data: Buffer;\n let remoteContentType: string | undefined;\n\n if (isRemoteUri(asset.sourceUri)) {\n if (remoteMode === 'error') {\n throw new AssetServeError('Remote asset not supported', 500);\n }\n if (remoteMode === 'redirect') {\n throw new RedirectToSourceUri(asset.sourceUri);\n }\n // Proxy — fetch the bytes from the origin URL with SSRF guards,\n // a byte cap, and a timeout.\n const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new AssetServeError(\n 'No fetch implementation available for remote asset',\n 500,\n );\n }\n const maxBytes = options.remoteMaxBytes ?? DEFAULT_REMOTE_MAX_BYTES;\n const timeoutMs = options.remoteTimeoutMs ?? DEFAULT_REMOTE_TIMEOUT_MS;\n try {\n const result = await fetchRemoteAsset(\n asset.sourceUri,\n fetchImpl,\n maxBytes,\n timeoutMs,\n );\n data = result.data;\n remoteContentType = result.contentType;\n } catch (err) {\n if (err instanceof AssetServeError) throw err;\n // Log the underlying error so operators can debug without\n // leaking paths/bucket names to the HTTP client.\n console.error('serveAsset: remote fetch failed', err);\n throw new AssetServeError('Failed to fetch remote asset', 502);\n }\n } else {\n try {\n data = await runtime.store.read(asset);\n } catch (err) {\n console.error('serveAsset: store read failed', err);\n // Generic, non-leaking client body (no paths/bucket names).\n throw new AssetServeError(\n 'Failed to read asset bytes',\n 500,\n 'Failed to read asset bytes',\n );\n }\n }\n\n const contentType =\n asset.mimeType || remoteContentType || 'application/octet-stream';\n const filename = options.filename ?? deriveFilename(asset);\n\n return {\n asset,\n data,\n contentType,\n filename,\n size: data.byteLength,\n };\n}\n\n/**\n * Internal signal for `remoteMode: 'redirect'`. Caught by `serveAsset`\n * and turned into a 302; `resolveAssetForServing` re-throws so direct\n * callers can inspect it via `err.location`.\n */\nclass RedirectToSourceUri extends Error {\n constructor(public readonly location: string) {\n super('Asset stored at remote URL');\n this.name = 'RedirectToSourceUri';\n }\n}\n\n/**\n * Serve an asset as a standard Web `Response`.\n *\n * Returns:\n * - `404` when the asset id doesn't resolve\n * - `403` when the tenant mismatches or `canAccess` denies\n * - `302` when `remoteMode: 'redirect'` and the asset's `sourceUri`\n * is an `http(s)` URL\n * - `502` when `remoteMode: 'proxy'` and the origin fetch fails\n * - `500` when bytes fail to read (file missing, provider error)\n * - `200` with the raw bytes, `Content-Type`, `Content-Length`, and\n * `Content-Disposition` set otherwise.\n *\n * The resulting `Response` is framework-agnostic: SvelteKit `+server.ts`\n * endpoints, Hono, and plain Node HTTP all accept it via their Web\n * interop layer.\n */\nexport async function serveAsset(\n options: ServeAssetOptions,\n): Promise<Response> {\n const Ctor =\n options.responseCtor ?? (globalThis.Response as ResponseConstructor);\n if (!Ctor) {\n throw new Error(\n 'serveAsset: no Response constructor available. Pass options.responseCtor or run on Node 18+.',\n );\n }\n\n try {\n const { data, contentType, filename, size } =\n await resolveAssetForServing(options);\n\n // Force `attachment` for any type not on the inline-safe safelist so a\n // browser won't render attacker-controlled HTML/SVG/XML in the serving\n // origin's context (stored XSS). An explicit `disposition: 'attachment'`\n // is always honoured; a requested `'inline'` is downgraded for unsafe\n // types. Pair with `X-Content-Type-Options: nosniff` so the browser\n // can't MIME-sniff a safelisted type into something executable (S5 #1396).\n const requestedDisposition = options.disposition ?? 'inline';\n const disposition =\n requestedDisposition === 'attachment' || isInlineSafeMimeType(contentType)\n ? requestedDisposition\n : 'attachment';\n\n // Spread caller-supplied headers FIRST so the safety headers below always\n // win. Otherwise a caller could pass `Content-Disposition: inline` or\n // `X-Content-Type-Options: ` and defeat the forced-attachment + nosniff\n // protections for an XSS-prone type (S5 #1396 follow-up). HTTP header names\n // are case-insensitive, so drop any case-variant of a protected name from\n // the caller's set before applying the canonical ones.\n const PROTECTED_HEADERS = new Set([\n 'content-type',\n 'content-length',\n 'content-disposition',\n 'x-content-type-options',\n ]);\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(options.headers ?? {})) {\n if (!PROTECTED_HEADERS.has(name.toLowerCase())) {\n headers[name] = value;\n }\n }\n headers['Content-Type'] = contentType;\n headers['Content-Length'] = String(size);\n headers['Content-Disposition'] = buildContentDisposition(\n disposition,\n filename,\n );\n headers['X-Content-Type-Options'] = 'nosniff';\n\n // `new Response(body)` accepts Buffer at runtime (it's a Uint8Array\n // subclass), but the strongly-typed `BodyInit` name lives in\n // `lib.dom.d.ts`, which the package-build tsconfig doesn't pull in.\n // Cast through `unknown` instead of referencing a DOM type so `.d.ts`\n // generation works without needing DOM lib.\n return new Ctor(\n data as unknown as ConstructorParameters<ResponseConstructor>[0],\n {\n status: 200,\n headers,\n },\n );\n } catch (err) {\n if (err instanceof RedirectToSourceUri) {\n return new Ctor(null, {\n status: 302,\n headers: { Location: err.location },\n });\n }\n if (err instanceof AssetServeError) {\n // Return the opaque, status-keyed body — never `err.message`, which can\n // carry the specific rejection reason (SSRF cause, upstream status) and\n // would give clients an oracle about WHY a target was rejected\n // (S5 #1396, Copilot review). The specific reason is logged below.\n if (err.status >= 500) {\n console.error('serveAsset: request failed', err.status, err.message);\n }\n return new Ctor(err.clientMessage, {\n status: err.status,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n // Don't surface the underlying error to clients — it can leak\n // paths/bucket names/provider details. Log for operators.\n console.error('serveAsset: unexpected error', err);\n return new Ctor('Internal error serving asset', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n}\n\n/**\n * Generic, status-keyed client-facing bodies. The `message` of an\n * {@link AssetServeError} can carry a specific reason for server-side logs,\n * but the body returned to the HTTP client is one of these opaque strings so\n * a caller can't probe internal reachability or upstream state via the\n * response text — e.g. distinguishing \"blocked host\" from \"bad scheme\", or\n * reading the upstream origin's status (S5 #1396, Copilot review).\n */\nfunction defaultClientMessage(status: number): string {\n switch (status) {\n case 403:\n return 'Forbidden';\n case 404:\n return 'Not found';\n case 502:\n return 'Bad gateway';\n default:\n return 'Internal error serving asset';\n }\n}\n\n/**\n * Error raised by `resolveAssetForServing()` with the HTTP status\n * callers should use when rendering their own response.\n *\n * `message` is the specific, server-side reason — safe to log, never sent to\n * the client. `clientMessage` is the opaque body {@link serveAsset} returns to\n * the HTTP client; it defaults to a generic, status-keyed string so the\n * specific reason can't leak (SSRF rejection cause, upstream status, etc.).\n */\nexport class AssetServeError extends Error {\n /** Opaque body safe to return to the HTTP client. */\n public readonly clientMessage: string;\n\n constructor(\n message: string,\n public readonly status: number,\n clientMessage?: string,\n ) {\n super(message);\n this.name = 'AssetServeError';\n this.clientMessage = clientMessage ?? defaultClientMessage(status);\n }\n}\n\nfunction deriveFilename(asset: Asset): string {\n if (asset.name) return asset.name;\n const fromUri = extractBasename(asset.sourceUri);\n if (fromUri) return fromUri;\n return asset.id ?? 'asset';\n}\n\nfunction extractBasename(uri: string): string {\n if (!uri) return '';\n const noScheme = uri.replace(/^[a-z][a-z0-9+\\-.]*:\\/\\//i, '');\n const last = noScheme.split('/').pop() ?? '';\n return last;\n}\n\nfunction sanitizeDispositionFilename(filename: string): string {\n // Strip C0/DEL/C1 controls and replace isolated UTF-16 surrogates so\n // UTF-8 encoding cannot throw. Iteration preserves valid surrogate pairs.\n // Replace quotes, backslashes, and path separators in both name forms.\n let stripped = '';\n for (const ch of filename) {\n const code = ch.charCodeAt(0);\n if (code > 0x1f && (code < 0x7f || code > 0x9f)) {\n stripped +=\n ch.length === 1 && code >= 0xd800 && code <= 0xdfff ? '\\uFFFD' : ch;\n }\n }\n const safe = stripped.replace(/[\"\\\\/]/g, '_').trim();\n return safe || 'asset';\n}\n\nfunction buildContentDisposition(\n disposition: 'inline' | 'attachment',\n filename: string,\n): string {\n const sanitized = sanitizeDispositionFilename(filename);\n // Keep the legacy quoted parameter ASCII-safe for native Web Headers.\n // The extended parameter retains the sanitized Unicode download name.\n const fallback = Array.from(sanitized, (ch) =>\n ch.charCodeAt(0) <= 0x7e ? ch : '_',\n ).join('');\n // RFC 8187 attr-char excludes these characters left by encodeURIComponent.\n const encoded = encodeURIComponent(sanitized).replace(\n /['()*]/g,\n (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n return `${disposition}; filename=\"${fallback}\"; filename*=UTF-8''${encoded}`;\n}\n","/**\n * AssetStatusCollection - Collection manager for AssetStatus instances\n *\n * Manages asset status lookup table with common status initialization\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AssetStatus } from './asset-status';\n\nexport class AssetStatusCollection extends SmrtCollection<AssetStatus> {\n static readonly _itemClass = AssetStatus;\n\n /**\n * Get or create an asset status by slug\n *\n * @param slug - The asset status slug\n * @param name - The display name (defaults to slug)\n * @param description - Optional description\n * @returns The existing or newly created AssetStatus\n */\n async getOrCreate(\n slug: string,\n name?: string,\n description?: string,\n ): Promise<AssetStatus> {\n const existing = await this.list({ where: { slug }, limit: 1 });\n if (existing.length > 0) {\n return existing[0];\n }\n\n return await this.create({\n slug,\n name: name || slug,\n description,\n });\n }\n\n /**\n * Initialize common asset statuses\n *\n * Creates standard asset statuses if they don't exist:\n * - draft\n * - published\n * - archived\n * - deleted\n */\n async initializeCommonStatuses(): Promise<void> {\n await this.getOrCreate('draft', 'Draft', 'Work in progress, not yet ready');\n await this.getOrCreate('published', 'Published', 'Live and available');\n await this.getOrCreate(\n 'archived',\n 'Archived',\n 'No longer active but preserved',\n );\n await this.getOrCreate('deleted', 'Deleted', 'Marked for deletion');\n }\n}\n","/**\n * AssetTag model - Links an asset to a `@happyvertical/smrt-tags` Tag.\n *\n * Backs the `asset_tags` join table that `AssetCollection.addTag()` /\n * `removeTag()` / `getByTag()` and `Asset.getTags()` / `hasTag()` use. Before\n * this model existed those methods wrote raw rows to an `asset_tags` table\n * that no manifest declared, so no migration ever created it.\n *\n * The link is keyed by the Tag's id (not its slug): a tag's natural key is\n * `(tenant, slug, context)`, so a slug alone is ambiguous across tenants and\n * contexts. Both sides cascade: deleting the asset or the tag removes the\n * link. Rows carry the asset's tenant.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface AssetTagOptions extends SmrtObjectOptions {\n assetId?: string;\n tagId?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'asset_tags',\n conflictColumns: ['asset_id', 'tag_id'],\n api: false,\n mcp: false,\n cli: false,\n})\nexport class AssetTag extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** FK to Asset.id — the link goes with the asset. */\n @foreignKey('Asset', { required: true, onDelete: 'CASCADE' })\n assetId = '';\n\n /** FK to smrt-tags Tag.id — the link goes with the tag. */\n @crossPackageRef('@happyvertical/smrt-tags:Tag', {\n required: true,\n onDelete: 'CASCADE',\n })\n tagId = '';\n\n constructor(options: AssetTagOptions = {}) {\n super(options);\n if (options.assetId) this.assetId = options.assetId;\n if (options.tagId) this.tagId = options.tagId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","/**\n * AssetTagCollection - junction collection for `asset_tags` (Asset ↔ Tag).\n *\n * Left side is the asset, right side the Tag id. The table has no position\n * column, so neither ordering nor `setLinks` index assignment applies.\n */\n\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { AssetTag } from './asset-tag';\n\n// Empty decorator so the scanner detects the collection without clobbering\n// the item class's api/mcp/cli config (see AssetAssociationCollection).\n@smrt()\nexport class AssetTagCollection extends SmrtJunction<AssetTag> { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n static readonly _itemClass = AssetTag;\n protected leftField = 'assetId';\n protected rightField = 'tagId';\n protected sortField: string | null = null;\n protected positionField: string | null = null;\n}\n","/**\n * AssetTypeCollection - Collection manager for AssetType instances\n *\n * Manages asset type lookup table with common type initialization\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AssetType } from './asset-type';\n\nexport class AssetTypeCollection extends SmrtCollection<AssetType> {\n static readonly _itemClass = AssetType;\n\n /**\n * Get or create an asset type by slug\n *\n * @param slug - The asset type slug\n * @param name - The display name (defaults to slug)\n * @param description - Optional description\n * @returns The existing or newly created AssetType\n */\n async getOrCreate(\n slug: string,\n name?: string,\n description?: string,\n ): Promise<AssetType> {\n const existing = await this.list({ where: { slug }, limit: 1 });\n if (existing.length > 0) {\n return existing[0];\n }\n\n return await this.create({\n slug,\n name: name || slug,\n description,\n });\n }\n\n /**\n * Initialize common asset types\n *\n * Creates standard asset types if they don't exist:\n * - image\n * - video\n * - document\n * - audio\n *\n * Note: `folder` was removed in R3-D. Folders are no longer an asset\n * subtype — they live on their own `folders` table. See `folder.ts`.\n */\n async initializeCommonTypes(): Promise<void> {\n await this.getOrCreate(\n 'image',\n 'Image',\n 'Image files (JPEG, PNG, GIF, etc.)',\n );\n await this.getOrCreate(\n 'video',\n 'Video',\n 'Video files (MP4, AVI, MOV, etc.)',\n );\n await this.getOrCreate(\n 'document',\n 'Document',\n 'Document files (PDF, DOCX, TXT, etc.)',\n );\n await this.getOrCreate(\n 'audio',\n 'Audio',\n 'Audio files (MP3, WAV, AAC, etc.)',\n );\n }\n}\n","/**\n * Folder model — hierarchical container for organizing assets.\n *\n * Folders are now their own SmrtHierarchical model with their own `folders`\n * table (R3-D). Prior to R3-D, Folder was an STI subclass of Asset and\n * folder rows lived in the `assets` table with `type_slug='folder'`. The\n * column `assets.parent_id` did double duty: structural hierarchy for\n * folder rows, derivation source for asset rows.\n *\n * Splitting Folder onto its own table eliminates the overload and lets\n * Folder participate in the framework-wide `parentId` convention as a\n * SmrtHierarchical subclass (Place, Event, Tag, Account, Zone).\n *\n * Migration path (4 steps; see changeset):\n *\n * 1. Create `folders` table.\n * 2. Copy folder rows from `assets` (`type_slug='folder'`) into `folders`.\n * 3. On `assets`, rename `parent_id` → `source_asset_id` (handled in\n * asset.ts schema generator output) and copy values for non-folder\n * rows.\n * 4. Delete folder rows from `assets`.\n *\n * @see Asset.sourceAssetId for the renamed derivation pointer.\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtHierarchical,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { FolderOptions } from './types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: { skipApiCheck: true },\n})\nexport class Folder extends SmrtHierarchical { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // Tenant isolation (optional — folders can be global or tenant-specific,\n // mirroring Asset's tenancy mode).\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // Core fields. `parentId` is inherited from SmrtHierarchical.\n @field({ description: 'A recognizable name for this group of files.' })\n name = '';\n // `slug` is inherited as an accessor from SmrtObject.\n @field({\n description:\n 'Explain the purpose of this folder and which files belong here.',\n })\n description = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n @field({\n description: 'The person or organization responsible for this folder.',\n })\n ownerProfileId: string | null = null;\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: FolderOptions = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.slug !== undefined) this.slug = options.slug;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.parentId !== undefined)\n this.parentId = options.parentId ?? null;\n if (options.ownerProfileId !== undefined)\n this.ownerProfileId = options.ownerProfileId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n // Hierarchy traversal (getParent / getChildren / getAncestors /\n // getDescendants / getHierarchy / moveTo) provided by SmrtHierarchical.\n\n /**\n * Look up a folder by slug.\n */\n static async getBySlug(_slug: string): Promise<Folder | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * FolderCollection — collection manager for Folder instances.\n *\n * Provides tree-oriented helpers that wrap the inherited SmrtHierarchical\n * traversal methods, plus folder-content helpers (`getContents`,\n * `moveAsset`) for working with the assets in a folder.\n *\n * Post R3-D, Folder lives on its own `folders` table; tree queries no\n * longer have to filter by `type_slug='folder'`.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport type { Asset } from './asset';\nimport type { AssetCollection } from './assets';\nimport { Folder } from './folder';\n\nexport class FolderCollection extends SmrtCollection<Folder> {\n static readonly _itemClass = Folder;\n\n /**\n * Get the folder tree starting from an optional root.\n *\n * When `rootId` is omitted, returns top-level folders (those with no\n * parent). When `rootId` is provided, returns all descendants of that\n * folder via the inherited SmrtHierarchical BFS traversal — same\n * cycle-safe, one-query-per-depth behaviour as Place/Event.\n *\n * @param rootId - Optional root folder ID; if omitted, returns top-level folders\n * @returns Array of folders (flat list; use parentId to reconstruct tree)\n */\n async getTree(rootId?: string): Promise<Folder[]> {\n if (!rootId) {\n return (await this.list({\n where: { parentId: null },\n orderBy: 'name ASC',\n })) as Folder[];\n }\n\n // Walk the tree BFS-by-level, sorting siblings by name within each\n // batch. This preserves two contracts from the pre-R3-D\n // implementation that `SmrtHierarchical.getDescendants` alone does\n // not — global name-sort would interleave parents and grandchildren\n // (root → Zulu → Alpha would surface as `[Alpha, Zulu]`, putting\n // grandchild before parent), and storage-order BFS would render\n // unsorted siblings.\n const result: Folder[] = [];\n const queue: string[] = [rootId];\n const visited = new Set<string>([rootId]);\n\n while (queue.length > 0) {\n const currentId = queue.shift()!;\n const children = (await this.list({\n where: { parentId: currentId },\n orderBy: 'name ASC',\n })) as Folder[];\n\n for (const child of children) {\n if (child.id && !visited.has(child.id)) {\n visited.add(child.id);\n result.push(child);\n queue.push(child.id);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get the path from root to a given folder (ancestors + self).\n *\n * @param folderId - The folder ID to get the path for\n * @returns Array of folders from root to the given folder (inclusive)\n */\n async getPath(folderId: string): Promise<Folder[]> {\n const folder = (await this.get({ id: folderId })) as Folder | null;\n if (!folder) return [];\n const ancestors = await folder.getAncestors();\n return [...ancestors, folder];\n }\n\n /**\n * Get all assets that are direct children of a folder.\n *\n * Folder membership is recorded on `Asset.folderId`, not on the folder\n * itself, so this is delegated to the asset collection.\n *\n * @param folderId - The folder ID\n * @param assetCollection - An AssetCollection instance for querying\n * @returns Array of assets in this folder\n */\n async getContents(\n folderId: string,\n assetCollection: AssetCollection,\n ): Promise<Asset[]> {\n return (await assetCollection.list({\n where: { folderId },\n })) as Asset[];\n }\n\n /**\n * Move an asset into a folder (or out, by passing `null`).\n *\n * @param asset - The asset to move\n * @param folderId - The target folder ID (or null to move to root)\n */\n async moveAsset(asset: Asset, folderId: string | null): Promise<void> {\n asset.folderId = folderId;\n await asset.save();\n }\n}\n","export type MediaSupportFileVisibility =\n | 'visible'\n | 'hidden-retained'\n | 'drop-after-extract';\n\nexport interface MediaBundleFileDescriptor {\n path: string;\n relativePath?: string;\n name?: string;\n mimeType?: string;\n sizeBytes?: number;\n sha256?: string;\n modifiedAt?: Date | string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MediaBundleGpsTrackPoint {\n tSeconds: number;\n recordedAt?: Date | string | null;\n latitude: number;\n longitude: number;\n altitude?: number | null;\n heading?: number | null;\n speedMps?: number | null;\n sourceFilePath?: string;\n}\n\nexport interface MediaBundleNormalizedMetadata {\n captureTime?: Date | string | null;\n width?: number;\n height?: number;\n durationMs?: number;\n mimeType?: string;\n gpsTrack?: MediaBundleGpsTrackPoint[];\n raw?: Record<string, unknown>;\n private?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface MediaBundleSupportFileInspection {\n file: MediaBundleFileDescriptor;\n role: string;\n relationship: string;\n visibility?: MediaSupportFileVisibility;\n metadata?: MediaBundleNormalizedMetadata;\n}\n\nexport interface MediaBundleInspection {\n handlerId: string;\n handlerVersion: string;\n formatFamily: string;\n primary: MediaBundleFileDescriptor;\n supportFiles: MediaBundleSupportFileInspection[];\n metadata: MediaBundleNormalizedMetadata;\n capabilities: string[];\n warnings: string[];\n errors: string[];\n raw?: Record<string, unknown>;\n}\n\nexport type MediaBundleInspectionLike = MediaBundleInspection;\n\nexport interface PersistMediaBundleAssetInput {\n file: MediaBundleFileDescriptor;\n role: 'primary' | 'support';\n typeSlug?: string;\n parentAssetId?: string | null;\n relationship?: string;\n visibility?: MediaSupportFileVisibility;\n metadata: MediaBundleNormalizedMetadata;\n inspection: MediaBundleInspection;\n}\n\nexport interface PersistMediaBundleAssociationInput {\n primaryAssetId: string;\n supportAssetId: string;\n relationship: string;\n visibility: MediaSupportFileVisibility;\n metadata?: MediaBundleNormalizedMetadata;\n}\n\nexport interface PersistMediaBundleMetadataArtifactInput {\n primaryAssetId: string;\n inspection: MediaBundleInspection;\n}\n\nexport interface SmrtMediaBundlePersistenceAdapter {\n upsertAsset(input: PersistMediaBundleAssetInput): Promise<{ id: string }>;\n associateSupportFile?(\n input: PersistMediaBundleAssociationInput,\n ): Promise<void>;\n writeMetadataArtifact?(\n input: PersistMediaBundleMetadataArtifactInput,\n ): Promise<{ id: string } | null | undefined>;\n replaceGpsTrack?(\n primaryAssetId: string,\n points: MediaBundleGpsTrackPoint[],\n ): Promise<number>;\n}\n\nexport interface PersistMediaBundleInspectionOptions {\n primaryTypeSlug?: string;\n supportTypeSlug?: string;\n supportVisibility?: MediaSupportFileVisibility;\n writeGpsTrack?: boolean;\n writeMetadataArtifact?: boolean;\n}\n\nexport interface PersistMediaBundleInspectionResult {\n primaryAssetId: string;\n supportAssetIds: string[];\n metadataArtifactId?: string;\n gpsTrackPointCount: number;\n warnings: string[];\n}\n\nexport async function persistMediaBundleInspection(\n adapter: SmrtMediaBundlePersistenceAdapter,\n inspection: MediaBundleInspection,\n options: PersistMediaBundleInspectionOptions = {},\n): Promise<PersistMediaBundleInspectionResult> {\n const warnings = [...inspection.warnings];\n const primary = await adapter.upsertAsset({\n file: inspection.primary,\n role: 'primary',\n typeSlug: options.primaryTypeSlug,\n metadata: inspection.metadata,\n inspection,\n });\n\n const supportAssetIds: string[] = [];\n for (const support of inspection.supportFiles) {\n // Support metadata is retained on support assets; canonical GPS persistence\n // is attached to the primary media through inspection.metadata.gpsTrack.\n const visibility =\n support.visibility ?? options.supportVisibility ?? 'hidden-retained';\n if (visibility === 'drop-after-extract') continue;\n\n const supportAsset = await adapter.upsertAsset({\n file: support.file,\n role: 'support',\n typeSlug: options.supportTypeSlug,\n parentAssetId: primary.id,\n relationship: support.relationship,\n visibility,\n metadata: support.metadata ?? {},\n inspection,\n });\n supportAssetIds.push(supportAsset.id);\n\n await adapter.associateSupportFile?.({\n primaryAssetId: primary.id,\n supportAssetId: supportAsset.id,\n relationship: support.relationship,\n visibility,\n metadata: support.metadata,\n });\n }\n\n let metadataArtifactId: string | undefined;\n if (\n options.writeMetadataArtifact !== false &&\n adapter.writeMetadataArtifact\n ) {\n const artifact = await adapter.writeMetadataArtifact({\n primaryAssetId: primary.id,\n inspection,\n });\n metadataArtifactId = artifact?.id;\n }\n\n let gpsTrackPointCount = 0;\n const gpsTrack = inspection.metadata.gpsTrack ?? [];\n if (options.writeGpsTrack !== false && gpsTrack.length > 0) {\n if (adapter.replaceGpsTrack) {\n gpsTrackPointCount = await adapter.replaceGpsTrack(primary.id, gpsTrack);\n } else {\n warnings.push('adapter cannot persist gps track');\n }\n }\n\n return {\n primaryAssetId: primary.id,\n supportAssetIds,\n metadataArtifactId,\n gpsTrackPointCount,\n warnings,\n };\n}\n","import type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { withSystemContext } from '@happyvertical/smrt-tenancy';\nimport type { Asset } from './asset';\nimport { AssetCollection } from './assets';\n\nexport const OWNED_ASSET_RELATIONSHIP_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nexport interface AssetOwnerRecord {\n getAssets(relationship?: string): Promise<Asset[]>;\n addAsset(\n asset: Asset,\n relationship?: string,\n sortOrder?: number,\n ): Promise<void>;\n removeAsset(assetId: string, relationship?: string): Promise<void>;\n}\n\nexport interface AssetOwnerCollection<OwnerType extends AssetOwnerRecord> {\n get(where: { id: string }): Promise<OwnerType | null>;\n}\n\nexport function assertValidOwnedAssetRelationship(relationship: string): void {\n if (!OWNED_ASSET_RELATIONSHIP_PATTERN.test(relationship)) {\n throw new Error(\n `Invalid relationship type \"${relationship}\"; must start with a letter or underscore and contain only letters, digits, and underscores`,\n );\n }\n}\n\nexport function assertValidOwnedAssetSortOrder(sortOrder: number): void {\n if (!Number.isInteger(sortOrder) || sortOrder < 0 || sortOrder > 2147483647) {\n throw new Error(\n `Invalid sortOrder \"${sortOrder}\"; must be a non-negative integer`,\n );\n }\n}\n\n/**\n * Refuse to link an asset to an owner (place, profile, …) of another tenant.\n *\n * The asset's tenant is read from STORAGE by id under system context, never\n * taken from the caller's object: `new Asset({ id: foreignId })` or a stale\n * copy carries no (or the wrong) tenant and would otherwise pass. Rules:\n *\n * - a tenant owner may link its own assets and global (tenantless) ones;\n * - a global owner may link only global assets — a tenant's asset linked to\n * a global owner would be listed to every tenant through the global link;\n * - an asset id with no stored row is refused.\n *\n * @param ownerLabel - plain noun for the error (\"place\", \"profile\")\n */\nexport async function assertAssetLinkable(\n db: SmrtCollectionOptions['db'],\n assetId: string,\n ownerTenantId: string | null | undefined,\n ownerLabel: string,\n): Promise<void> {\n const assets = await AssetCollection.create({ db });\n const [stored] = await withSystemContext(async () =>\n assets.listByIds([assetId]),\n );\n if (!stored) {\n throw new Error(`Cannot associate asset ${assetId}: it does not exist`);\n }\n const assetTenant = stored.tenantId ? String(stored.tenantId) : null;\n const ownerTenant = ownerTenantId ? String(ownerTenantId) : null;\n if (assetTenant === null || assetTenant === ownerTenant) return;\n throw new Error(\n ownerTenant === null\n ? `Cannot associate a tenant's asset with a shared ${ownerLabel}`\n : `Cannot associate an asset from another tenant with this ${ownerLabel}`,\n );\n}\n\nexport async function resolveOwnedAssetsById(\n db: SmrtCollectionOptions['db'],\n assetIds: string[],\n tenantId?: string | null,\n): Promise<Asset[]> {\n if (assetIds.length === 0) {\n return [];\n }\n\n const assets = await AssetCollection.create({ db });\n const resolved = tenantId\n ? await withSystemContext(async () => assets.listByIds(assetIds))\n : await assets.listByIds(assetIds);\n const visibleAssets = tenantId\n ? resolved.filter(\n (asset) => asset.tenantId === tenantId || asset.tenantId === null,\n )\n : resolved;\n const assetsById = new Map(\n visibleAssets\n .filter((asset) => asset.id)\n .map((asset) => [asset.id as string, asset]),\n );\n\n return assetIds\n .map((assetId) => assetsById.get(assetId))\n .filter(Boolean) as Asset[];\n}\n\nasync function getOwnerRecord<OwnerType extends AssetOwnerRecord>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerId: string,\n): Promise<OwnerType | null> {\n return collection.get({ id: ownerId });\n}\n\nexport async function getOwnedAssetsFromCollection<\n OwnerType extends AssetOwnerRecord,\n>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerId: string,\n relationship?: string,\n): Promise<Asset[]> {\n const owner = await getOwnerRecord(collection, ownerId);\n if (!owner) {\n return [];\n }\n\n return owner.getAssets(relationship);\n}\n\nexport async function addOwnedAssetFromCollection<\n OwnerType extends AssetOwnerRecord,\n>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerType: string,\n ownerId: string,\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n): Promise<void> {\n const owner = await getOwnerRecord(collection, ownerId);\n if (!owner) {\n throw new Error(`${ownerType} '${ownerId}' not found`);\n }\n\n await owner.addAsset(asset, relationship, sortOrder);\n}\n\nexport async function removeOwnedAssetFromCollection<\n OwnerType extends AssetOwnerRecord,\n>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerType: string,\n ownerId: string,\n assetId: string,\n relationship?: string,\n): Promise<void> {\n const owner = await getOwnerRecord(collection, ownerId);\n if (!owner) {\n throw new Error(`${ownerType} '${ownerId}' not found`);\n }\n\n await owner.removeAsset(assetId, relationship);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/__smrt-register__.ts","../src/asset-association.ts","../src/asset-associations.ts","../src/asset-status.ts","../src/asset-type.ts","../src/private-asset-access.ts","../src/asset.ts","../src/asset-capabilities.ts","../src/asset-conventions.ts","../src/asset-metafield.ts","../src/asset-metafields.ts","../src/asset-store.ts","../src/assets.ts","../src/asset-runtime.ts","../src/asset-serving.ts","../src/asset-statuses.ts","../src/asset-tag.ts","../src/asset-tags.ts","../src/asset-types.ts","../src/folder.ts","../src/folders.ts","../src/media-bundle-persistence.ts","../src/owned-asset-helpers.ts"],"sourcesContent":["// Generated by @happyvertical/smrt-core smrtPlugin at library build time.\n// The package manifest is inlined so self-registration cannot miss when a\n// downstream bundler relocates this module away from dist/manifest.json\n// (https://github.com/happyvertical/smrt/issues/1506,\n// https://github.com/happyvertical/smrt/issues/1507).\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\nObjectRegistry.registerPackageManifest(JSON.parse(\"{\\\"version\\\":\\\"1.0.0\\\",\\\"timestamp\\\":0,\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"packageVersion\\\":\\\"0.57.21\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-assets:AssetAssociation\\\":{\\\"name\\\":\\\"assetassociation\\\",\\\"className\\\":\\\"AssetAssociation\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetAssociation\\\",\\\"collection\\\":\\\"assetassociations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-association.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"metaType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"_meta\\\":{\\\"required\\\":true}},\\\"metaId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"_meta\\\":{\\\"required\\\":true}},\\\"role\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":true,\\\"_meta\\\":{\\\"required\\\":true}},\\\"sortOrder\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":false,\\\"default\\\":0},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"assetId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"Asset\\\",\\\"description\\\":\\\"The file attached to the related record.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The file attached to the related record.\\\",\\\"required\\\":true}}},\\\"methods\\\":{\\\"hydrate\\\":{\\\"name\\\":\\\"hydrate\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<T | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"conflictColumns\\\":[\\\"asset_id\\\",\\\"meta_type\\\",\\\"meta_id\\\",\\\"role\\\"],\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtPolymorphicAssociation\\\",\\\"exportName\\\":\\\"AssetAssociation\\\",\\\"collectionExportName\\\":\\\"AssetAssociationCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"metaType\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"metaId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"role\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"text\\\"},{\\\"field\\\":\\\"assetId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"asset_associations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_associations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"meta_id\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"role\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"sort_order\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"meta_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"role\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"sort_order\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":0},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_associations_asset_id_meta_type_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"meta_type\\\",\\\"meta_id\\\",\\\"role\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_associations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_associations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_associations_meta_type_meta_id_idx\\\",\\\"columns\\\":[\\\"meta_type\\\",\\\"meta_id\\\"]}],\\\"version\\\":\\\"faa13cea\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetAssociationCollection\\\":{\\\"name\\\":\\\"assetassociationcollection\\\",\\\"className\\\":\\\"AssetAssociationCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetAssociationCollection\\\",\\\"collection\\\":\\\"assetassociations\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-associations.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"byRight\\\":{\\\"name\\\":\\\"byRight\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"byLeft\\\":{\\\"name\\\":\\\"byLeft\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetAssociation[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"attach\\\":{\\\"name\\\":\\\"attach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetAssociation>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"detach\\\":{\\\"name\\\":\\\"detach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setLinks\\\":{\\\"name\\\":\\\"setLinks\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtJunctionBase\\\",\\\"extendsTypeArg\\\":\\\"AssetAssociation\\\",\\\"exportName\\\":\\\"AssetAssociationCollection\\\",\\\"collectionExportName\\\":\\\"AssetAssociationCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_associations\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_associations\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"meta_id\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"role\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"sort_order\\\\\\\" INTEGER DEFAULT 0,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"meta_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"role\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"sort_order\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":0},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_associations_asset_id_meta_type_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"meta_type\\\",\\\"meta_id\\\",\\\"role\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_associations_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_associations_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_associations_meta_type_meta_id_idx\\\",\\\"columns\\\":[\\\"meta_type\\\",\\\"meta_id\\\"]}],\\\"version\\\":\\\"faa13cea\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetMetafield\\\":{\\\"name\\\":\\\"assetmetafield\\\",\\\"className\\\":\\\"AssetMetafield\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetMetafield\\\",\\\"collection\\\":\\\"assetmetafields\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-metafield.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The name of an additional asset detail, such as Width or Height.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name of an additional asset detail, such as Width or Height.\\\"}},\\\"validation\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Rules that determine which values this asset detail accepts.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Rules that determine which values this asset detail accepts.\\\"}}},\\\"methods\\\":{\\\"getValidation\\\":{\\\"name\\\":\\\"getValidation\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setValidation\\\":{\\\"name\\\":\\\"setValidation\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"rules\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AssetMetafield | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetMetafield\\\",\\\"collectionExportName\\\":\\\"AssetMetafieldCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_metafields\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_metafields\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"validation\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"validation\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_metafields_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_metafields_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_metafields_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"7393718c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetMetafieldCollection\\\":{\\\"name\\\":\\\"assetmetafieldcollection\\\",\\\"className\\\":\\\"AssetMetafieldCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetMetafieldCollection\\\",\\\"collection\\\":\\\"assetmetafields\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-metafields.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getOrCreate\\\":{\\\"name\\\":\\\"getOrCreate\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"validation\\\",\\\"type\\\":\\\"string | Record<string, unknown>\\\",\\\"optional\\\":true,\\\"unionBranches\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"Record<string, unknown>\\\"}]}],\\\"returnType\\\":\\\"Promise<AssetMetafield>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"initializeCommonMetafields\\\":{\\\"name\\\":\\\"initializeCommonMetafields\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AssetMetafield\\\",\\\"exportName\\\":\\\"AssetMetafieldCollection\\\",\\\"collectionExportName\\\":\\\"AssetMetafieldCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_metafields\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_metafields\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"validation\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"validation\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_metafields_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_metafields_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_metafields_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"7393718c\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetStatus\\\":{\\\"name\\\":\\\"assetstatus\\\",\\\"className\\\":\\\"AssetStatus\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetStatus\\\",\\\"collection\\\":\\\"assetstatuses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-status.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The name of this lifecycle stage, such as Draft or Published.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name of this lifecycle stage, such as Draft or Published.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain when an asset should use this lifecycle stage.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain when an asset should use this lifecycle stage.\\\"}}},\\\"methods\\\":{\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AssetStatus | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetStatus\\\",\\\"collectionExportName\\\":\\\"AssetStatusCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_statuses\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_statuses\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_statuses_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_statuses_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_statuses_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"8251a52f\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetStatusCollection\\\":{\\\"name\\\":\\\"assetstatuscollection\\\",\\\"className\\\":\\\"AssetStatusCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetStatusCollection\\\",\\\"collection\\\":\\\"assetstatuses\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-statuses.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getOrCreate\\\":{\\\"name\\\":\\\"getOrCreate\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"description\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetStatus>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"initializeCommonStatuses\\\":{\\\"name\\\":\\\"initializeCommonStatuses\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AssetStatus\\\",\\\"exportName\\\":\\\"AssetStatusCollection\\\",\\\"collectionExportName\\\":\\\"AssetStatusCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_statuses\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_statuses\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_statuses_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_statuses_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_statuses_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"8251a52f\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetTag\\\":{\\\"name\\\":\\\"assettag\\\",\\\"className\\\":\\\"AssetTag\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetTag\\\",\\\"collection\\\":\\\"assettags\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-tag.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"assetId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":true,\\\"related\\\":\\\"Asset\\\",\\\"_meta\\\":{\\\"required\\\":true,\\\"onDelete\\\":\\\"CASCADE\\\"}},\\\"tagId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":true,\\\"related\\\":\\\"@happyvertical/smrt-tags:Tag\\\",\\\"_meta\\\":{\\\"onDelete\\\":\\\"CASCADE\\\"}}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"conflictColumns\\\":[\\\"asset_id\\\",\\\"tag_id\\\"],\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false,\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetTag\\\",\\\"collectionExportName\\\":\\\"AssetTagCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"assetId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"foreignKey\\\"},{\\\"field\\\":\\\"tagId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"crossPackageRef\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_tags\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"tag_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tag_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_tags_asset_id_tag_id_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"tag_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_tags_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_tags_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_tags_tag_id_idx\\\",\\\"columns\\\":[\\\"tag_id\\\"]}],\\\"version\\\":\\\"e60d24f5\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetTagCollection\\\":{\\\"name\\\":\\\"assettagcollection\\\",\\\"className\\\":\\\"AssetTagCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetTagCollection\\\",\\\"collection\\\":\\\"assettags\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-tags.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"byRight\\\":{\\\"name\\\":\\\"byRight\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"byLeft\\\":{\\\"name\\\":\\\"byLeft\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"attach\\\":{\\\"name\\\":\\\"attach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<TItem>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"detach\\\":{\\\"name\\\":\\\"detach\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"rightId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionFilterOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setLinks\\\":{\\\"name\\\":\\\"setLinks\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"leftId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"rightIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"opts\\\",\\\"type\\\":\\\"JunctionAttachOptions\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false},\\\"extends\\\":\\\"SmrtJunction\\\",\\\"extendsTypeArg\\\":\\\"AssetTag\\\",\\\"exportName\\\":\\\"AssetTagCollection\\\",\\\"collectionExportName\\\":\\\"AssetTagCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_tags\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_tags\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"asset_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"tag_id\\\\\\\" UUID NOT NULL\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":true,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"CASCADE\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tag_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_tags_asset_id_tag_id_idx\\\",\\\"columns\\\":[\\\"asset_id\\\",\\\"tag_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_tags_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"asset_tags_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"asset_tags_tag_id_idx\\\",\\\"columns\\\":[\\\"tag_id\\\"]}],\\\"version\\\":\\\"e60d24f5\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetType\\\":{\\\"name\\\":\\\"assettype\\\",\\\"className\\\":\\\"AssetType\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetType\\\",\\\"collection\\\":\\\"assettypes\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-type.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The name of this file category, such as Image or Document.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The name of this file category, such as Image or Document.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain which kinds of files belong in this category.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain which kinds of files belong in this category.\\\"}}},\\\"methods\\\":{\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<AssetType | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true}},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"AssetType\\\",\\\"collectionExportName\\\":\\\"AssetTypeCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_types\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_types\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_types_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_types_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_types_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"a9c93be2\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetTypeCollection\\\":{\\\"name\\\":\\\"assettypecollection\\\",\\\"className\\\":\\\"AssetTypeCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetTypeCollection\\\",\\\"collection\\\":\\\"assettypes\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset-types.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getOrCreate\\\":{\\\"name\\\":\\\"getOrCreate\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true},{\\\"name\\\":\\\"description\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<AssetType>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"initializeCommonTypes\\\":{\\\"name\\\":\\\"initializeCommonTypes\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"AssetType\\\",\\\"exportName\\\":\\\"AssetTypeCollection\\\",\\\"collectionExportName\\\":\\\"AssetTypeCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"asset_types\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"asset_types\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"asset_types_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"asset_types_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"asset_types_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]}],\\\"version\\\":\\\"a9c93be2\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:Asset\\\":{\\\"name\\\":\\\"asset\\\",\\\"className\\\":\\\"Asset\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:Asset\\\",\\\"collection\\\":\\\"assets\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/asset.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"created_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"updated_at\\\":{\\\"type\\\":\\\"datetime\\\",\\\"required\\\":false},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"accessMode\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"public\\\",\\\"description\\\":\\\"Whether this record is public to ordinary asset APIs or private.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Whether this record is public to ordinary asset APIs or private.\\\"},\\\"enum\\\":[\\\"public\\\",\\\"private\\\"]},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"A recognizable name for this file or media item.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A recognizable name for this file or media item.\\\"}},\\\"sourceUri\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Where the original file is stored or can be retrieved.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Where the original file is stored or can be retrieved.\\\"}},\\\"mimeType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The file format, such as image/jpeg or application/pdf.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The file format, such as image/jpeg or application/pdf.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain what the file contains and when it should be used.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain what the file contains and when it should be used.\\\"}},\\\"metadata\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Additional details recorded while processing this file.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Additional details recorded while processing this file.\\\"}},\\\"version\\\":{\\\"type\\\":\\\"integer\\\",\\\"required\\\":true,\\\"default\\\":1,\\\"description\\\":\\\"The revision number of this file in its version history.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The revision number of this file in its version history.\\\"}},\\\"primaryVersionId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Asset\\\",\\\"description\\\":\\\"The first version of this file, linking its revision history.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The first version of this file, linking its revision history.\\\"}},\\\"typeSlug\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The category of asset, such as an image, video, or document.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The category of asset, such as an image, video, or document.\\\"}},\\\"statusSlug\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The stage of this asset, such as draft, published, or archived.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The stage of this asset, such as draft, published, or archived.\\\"}},\\\"ownerProfileId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-profiles:Profile\\\",\\\"description\\\":\\\"The person or organization responsible for this asset.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The person or organization responsible for this asset.\\\"}},\\\"sourceAssetId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Asset\\\",\\\"description\\\":\\\"The original asset used to produce this file or variation.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The original asset used to produce this file or variation.\\\"}},\\\"folderId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Folder\\\",\\\"description\\\":\\\"The folder that organizes this file in the asset library.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The folder that organizes this file in the asset library.\\\"}},\\\"sourceType\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The service or source from which this file was obtained.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The service or source from which this file was obtained.\\\"}},\\\"externalId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"The reference used to identify this file at its original source.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The reference used to identify this file at its original source.\\\"}},\\\"externalRefs\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"References that connect this file to copies held by other services.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"References that connect this file to copies held by other services.\\\"}},\\\"createdAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"updatedAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false}},\\\"methods\\\":{\\\"getSlug\\\":{\\\"name\\\":\\\"getSlug\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<string | null | undefined>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getMetadata\\\":{\\\"name\\\":\\\"getMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, unknown>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setMetadata\\\":{\\\"name\\\":\\\"setMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metadata\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"mergeMetadata\\\":{\\\"name\\\":\\\"mergeMetadata\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"metadata\\\",\\\"type\\\":\\\"Record<string, unknown>\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getExternalRefs\\\":{\\\"name\\\":\\\"getExternalRefs\\\",\\\"async\\\":false,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Record<string, AssetExternalReference>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getExternalRef\\\":{\\\"name\\\":\\\"getExternalRef\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"AssetExternalReference | null\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setExternalRef\\\":{\\\"name\\\":\\\"setExternalRef\\\",\\\"async\\\":false,\\\"parameters\\\":[{\\\"name\\\":\\\"provider\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"reference\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":false,\\\"typeUnresolved\\\":true}],\\\"returnType\\\":\\\"void\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getTags\\\":{\\\"name\\\":\\\"getTags\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Tag[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"hasTag\\\":{\\\"name\\\":\\\"hasTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tagSlug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"context\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<boolean>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getSource\\\":{\\\"name\\\":\\\"getSource\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Asset | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getDerivatives\\\":{\\\"name\\\":\\\"getDerivatives\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getType\\\":{\\\"name\\\":\\\"getType\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AssetType | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getStatus\\\":{\\\"name\\\":\\\"getStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AssetStatus | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getAssociations\\\":{\\\"name\\\":\\\"getAssociations\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<AssetAssociation[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"associateWith\\\":{\\\"name\\\":\\\"associateWith\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"metaType\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"metaId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"role\\\",\\\"type\\\":\\\"any\\\",\\\"optional\\\":true,\\\"default\\\":\\\"default\\\"}],\\\"returnType\\\":\\\"Promise<AssetAssociation>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"tableStrategy\\\":\\\"sti\\\",\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"]},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"Asset\\\",\\\"collectionExportName\\\":\\\"AssetCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"version\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"integer\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"assets\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"assets\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"access_mode\\\\\\\" TEXT DEFAULT 'public',\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"source_uri\\\\\\\" TEXT,\\\\n \\\\\\\"mime_type\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"metadata\\\\\\\" TEXT,\\\\n \\\\\\\"version\\\\\\\" INTEGER DEFAULT 1,\\\\n \\\\\\\"primary_version_id\\\\\\\" UUID,\\\\n \\\\\\\"type_slug\\\\\\\" TEXT,\\\\n \\\\\\\"status_slug\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID,\\\\n \\\\\\\"source_asset_id\\\\\\\" UUID,\\\\n \\\\\\\"folder_id\\\\\\\" UUID,\\\\n \\\\\\\"source_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT,\\\\n \\\\\\\"external_refs\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"access_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"public\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"source_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"mime_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":1},\\\"primary_version_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"type_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"status_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"source_asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"folder_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"source_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_refs\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"assets_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"assets_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"assets_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"assets_primary_version_id_idx\\\",\\\"columns\\\":[\\\"primary_version_id\\\"]},{\\\"name\\\":\\\"assets_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]},{\\\"name\\\":\\\"assets_source_asset_id_idx\\\",\\\"columns\\\":[\\\"source_asset_id\\\"]},{\\\"name\\\":\\\"assets_folder_id_idx\\\",\\\"columns\\\":[\\\"folder_id\\\"]}],\\\"version\\\":\\\"0e75029e\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:AssetCollection\\\":{\\\"name\\\":\\\"assetcollection\\\",\\\"className\\\":\\\"AssetCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:AssetCollection\\\",\\\"collection\\\":\\\"assets\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/assets.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"findByTenant\\\":{\\\"name\\\":\\\"findByTenant\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tenantId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findGlobal\\\":{\\\"name\\\":\\\"findGlobal\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"findWithGlobals\\\":{\\\"name\\\":\\\"findWithGlobals\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tenantId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"addTag\\\":{\\\"name\\\":\\\"addTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"tag\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"AssetTagInput\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Tag>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"removeTag\\\":{\\\"name\\\":\\\"removeTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"tag\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"Pick<AssetTagInput, 'context'>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"setTags\\\":{\\\"name\\\":\\\"setTags\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"tags\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"Pick<AssetTagInput, 'context'>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Tag[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getTagsForAssets\\\":{\\\"name\\\":\\\"getTagsForAssets\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"assetIds\\\",\\\"type\\\":\\\"string[]\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Map<string, Tag[]>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByTag\\\":{\\\"name\\\":\\\"getByTag\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"tag\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"options\\\",\\\"type\\\":\\\"Pick<AssetTagInput, 'context'>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByType\\\":{\\\"name\\\":\\\"getByType\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"typeSlug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByStatus\\\":{\\\"name\\\":\\\"getByStatus\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"statusSlug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByOwner\\\":{\\\"name\\\":\\\"getByOwner\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"ownerProfileId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"createNewVersion\\\":{\\\"name\\\":\\\"createNewVersion\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"newSourceUri\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"updates\\\",\\\"type\\\":\\\"Partial<Asset>\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Asset>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getLatestVersion\\\":{\\\"name\\\":\\\"getLatestVersion\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"listVersions\\\":{\\\"name\\\":\\\"listVersions\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getDerivatives\\\":{\\\"name\\\":\\\"getDerivatives\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"sourceAssetId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByMimeType\\\":{\\\"name\\\":\\\"getByMimeType\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"mimePattern\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"rollbackToVersion\\\":{\\\"name\\\":\\\"rollbackToVersion\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"primaryVersionId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"targetVersion\\\",\\\"type\\\":\\\"number\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getByFolder\\\":{\\\"name\\\":\\\"getByFolder\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"Asset\\\",\\\"exportName\\\":\\\"AssetCollection\\\",\\\"collectionExportName\\\":\\\"AssetCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"assets\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"assets\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"_meta_type\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"_meta_data\\\\\\\" JSON,\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"access_mode\\\\\\\" TEXT DEFAULT 'public',\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"source_uri\\\\\\\" TEXT,\\\\n \\\\\\\"mime_type\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"metadata\\\\\\\" TEXT,\\\\n \\\\\\\"version\\\\\\\" INTEGER DEFAULT 1,\\\\n \\\\\\\"primary_version_id\\\\\\\" UUID,\\\\n \\\\\\\"type_slug\\\\\\\" TEXT,\\\\n \\\\\\\"status_slug\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID,\\\\n \\\\\\\"source_asset_id\\\\\\\" UUID,\\\\n \\\\\\\"folder_id\\\\\\\" UUID,\\\\n \\\\\\\"source_type\\\\\\\" TEXT,\\\\n \\\\\\\"external_id\\\\\\\" TEXT,\\\\n \\\\\\\"external_refs\\\\\\\" TEXT\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"_meta_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"_meta_data\\\":{\\\"type\\\":\\\"JSON\\\",\\\"notNull\\\":false},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false},\\\"access_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"default\\\":\\\"public\\\"},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"source_uri\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"mime_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"metadata\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"version\\\":{\\\"type\\\":\\\"INTEGER\\\",\\\"notNull\\\":false,\\\"default\\\":1},\\\"primary_version_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"type_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"status_slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false},\\\"source_asset_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"assets\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"folder_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"source_type\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_id\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false},\\\"external_refs\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"assets_slug_context_meta_type_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\",\\\"_meta_type\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"assets_meta_type_idx\\\",\\\"columns\\\":[\\\"_meta_type\\\"]},{\\\"name\\\":\\\"assets_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"assets_primary_version_id_idx\\\",\\\"columns\\\":[\\\"primary_version_id\\\"]},{\\\"name\\\":\\\"assets_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]},{\\\"name\\\":\\\"assets_source_asset_id_idx\\\",\\\"columns\\\":[\\\"source_asset_id\\\"]},{\\\"name\\\":\\\"assets_folder_id_idx\\\",\\\"columns\\\":[\\\"folder_id\\\"]}],\\\"version\\\":\\\"0e75029e\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:Folder\\\":{\\\"name\\\":\\\"folder\\\",\\\"className\\\":\\\"Folder\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:Folder\\\",\\\"collection\\\":\\\"folders\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/folder.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{\\\"parentId\\\":{\\\"type\\\":\\\"foreignKey\\\",\\\"required\\\":false,\\\"related\\\":\\\"Folder\\\",\\\"_meta\\\":{\\\"nullable\\\":true}},\\\"tenantId\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"_meta\\\":{\\\"sqlType\\\":\\\"UUID\\\",\\\"nullable\\\":true,\\\"__tenancy\\\":{\\\"isTenantIdField\\\":true,\\\"autoFilter\\\":true,\\\"required\\\":false,\\\"autoPopulate\\\":true,\\\"nullable\\\":true,\\\"mode\\\":\\\"optional\\\",\\\"field\\\":\\\"tenantId\\\",\\\"allowSuperAdminBypass\\\":false}}},\\\"name\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"A recognizable name for this group of files.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"A recognizable name for this group of files.\\\"}},\\\"description\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"description\\\":\\\"Explain the purpose of this folder and which files belong here.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"Explain the purpose of this folder and which files belong here.\\\"}},\\\"ownerProfileId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":false,\\\"related\\\":\\\"@happyvertical/smrt-profiles:Profile\\\",\\\"description\\\":\\\"The person or organization responsible for this folder.\\\",\\\"_meta\\\":{\\\"description\\\":\\\"The person or organization responsible for this folder.\\\"}},\\\"createdAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false},\\\"updatedAt\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false}},\\\"methods\\\":{\\\"getParent\\\":{\\\"name\\\":\\\"getParent\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this | null>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getChildren\\\":{\\\"name\\\":\\\"getChildren\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getAncestors\\\":{\\\"name\\\":\\\"getAncestors\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getDescendants\\\":{\\\"name\\\":\\\"getDescendants\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<this[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getHierarchy\\\":{\\\"name\\\":\\\"getHierarchy\\\",\\\"async\\\":true,\\\"parameters\\\":[],\\\"returnType\\\":\\\"Promise<HierarchyView<this>>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"moveTo\\\":{\\\"name\\\":\\\"moveTo\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"newParent\\\",\\\"type\\\":\\\"this | string | null\\\",\\\"optional\\\":false,\\\"unionBranches\\\":[{\\\"type\\\":\\\"this\\\"},{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"null\\\"}]}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getBySlug\\\":{\\\"name\\\":\\\"getBySlug\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"_slug\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Folder | null>\\\",\\\"isStatic\\\":true,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]},\\\"cli\\\":{\\\"skipApiCheck\\\":true},\\\"tenantScoped\\\":{\\\"mode\\\":\\\"optional\\\"},\\\"conflictColumns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"]},\\\"extends\\\":\\\"SmrtHierarchical\\\",\\\"exportName\\\":\\\"Folder\\\",\\\"collectionExportName\\\":\\\"FolderCollection\\\",\\\"displayLabelField\\\":\\\"name\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"folders\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"folders\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"parent_id\\\\\\\" UUID,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"parent_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"folders_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"folders_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"folders_parent_id_idx\\\",\\\"columns\\\":[\\\"parent_id\\\"]},{\\\"name\\\":\\\"folders_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]}],\\\"version\\\":\\\"1d0b1fcb\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"},\\\"@happyvertical/smrt-assets:FolderCollection\\\":{\\\"name\\\":\\\"foldercollection\\\",\\\"className\\\":\\\"FolderCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-assets:FolderCollection\\\",\\\"collection\\\":\\\"folders\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/assets/src/folders.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-assets\\\",\\\"fields\\\":{},\\\"methods\\\":{\\\"getTree\\\":{\\\"name\\\":\\\"getTree\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"rootId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":true}],\\\"returnType\\\":\\\"Promise<Folder[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getPath\\\":{\\\"name\\\":\\\"getPath\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Folder[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"getContents\\\":{\\\"name\\\":\\\"getContents\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"assetCollection\\\",\\\"type\\\":\\\"AssetCollection\\\",\\\"optional\\\":false}],\\\"returnType\\\":\\\"Promise<Asset[]>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true},\\\"moveAsset\\\":{\\\"name\\\":\\\"moveAsset\\\",\\\"async\\\":true,\\\"parameters\\\":[{\\\"name\\\":\\\"asset\\\",\\\"type\\\":\\\"Asset\\\",\\\"optional\\\":false},{\\\"name\\\":\\\"folderId\\\",\\\"type\\\":\\\"string | null\\\",\\\"optional\\\":false,\\\"unionBranches\\\":[{\\\"type\\\":\\\"string\\\"},{\\\"type\\\":\\\"null\\\"}]}],\\\"returnType\\\":\\\"Promise<void>\\\",\\\"isStatic\\\":false,\\\"isPublic\\\":true}},\\\"decoratorConfig\\\":{\\\"api\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\",\\\"delete\\\"]},\\\"mcp\\\":{\\\"include\\\":[\\\"list\\\",\\\"get\\\",\\\"create\\\",\\\"update\\\"]}},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"Folder\\\",\\\"exportName\\\":\\\"FolderCollection\\\",\\\"collectionExportName\\\":\\\"FolderCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"folders\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"folders\\\\\\\" (\\\\n \\\\\\\"id\\\\\\\" UUID PRIMARY KEY NOT NULL,\\\\n \\\\\\\"slug\\\\\\\" TEXT NOT NULL,\\\\n \\\\\\\"context\\\\\\\" TEXT NOT NULL DEFAULT '',\\\\n \\\\\\\"created_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"updated_at\\\\\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\\\n \\\\\\\"parent_id\\\\\\\" UUID,\\\\n \\\\\\\"tenant_id\\\\\\\" UUID,\\\\n \\\\\\\"name\\\\\\\" TEXT,\\\\n \\\\\\\"description\\\\\\\" TEXT,\\\\n \\\\\\\"owner_profile_id\\\\\\\" UUID\\\\n);\\\",\\\"columns\\\":{\\\"id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"primaryKey\\\":true,\\\"referenceKind\\\":\\\"id\\\",\\\"notNull\\\":true},\\\"slug\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true},\\\"context\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"\\\"},\\\"created_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"updated_at\\\":{\\\"type\\\":\\\"TIMESTAMP\\\",\\\"notNull\\\":true,\\\"default\\\":\\\"current_timestamp\\\"},\\\"parent_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"foreignKey\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"foreignKey\\\":{\\\"table\\\":\\\"folders\\\",\\\"column\\\":\\\"id\\\",\\\"onDelete\\\":\\\"NO ACTION\\\",\\\"onUpdate\\\":\\\"CASCADE\\\"}},\\\"tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"tenantId\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"name\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"description\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false},\\\"owner_profile_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":false,\\\"unique\\\":false}},\\\"indexes\\\":[{\\\"name\\\":\\\"folders_slug_context_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"slug\\\",\\\"context\\\"],\\\"unique\\\":true,\\\"nullsNotDistinct\\\":true},{\\\"name\\\":\\\"folders_tenant_id_created_at_idx\\\",\\\"columns\\\":[\\\"tenant_id\\\",\\\"created_at\\\"]},{\\\"name\\\":\\\"folders_parent_id_idx\\\",\\\"columns\\\":[\\\"parent_id\\\"]},{\\\"name\\\":\\\"folders_owner_profile_id_idx\\\",\\\"columns\\\":[\\\"owner_profile_id\\\"]}],\\\"version\\\":\\\"1d0b1fcb\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-assets\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"recipes\\\":[{\\\"id\\\":\\\"assets.attachments\\\",\\\"className\\\":\\\"AssetAttachmentsRecipe\\\",\\\"label\\\":\\\"Attachments\\\",\\\"summary\\\":\\\"Attach authorized files to records and retain their history.\\\",\\\"synonyms\\\":[\\\"file attachments\\\",\\\"record files\\\",\\\"supporting documents\\\"],\\\"models\\\":[\\\"@happyvertical/smrt-assets:Asset\\\",\\\"@happyvertical/smrt-assets:AssetAssociation\\\"],\\\"nav\\\":[{\\\"label\\\":\\\"Attachments\\\",\\\"model\\\":\\\"@happyvertical/smrt-assets:Asset\\\",\\\"icon\\\":\\\"fileText\\\",\\\"description\\\":\\\"Supporting files connected to the record you are viewing.\\\",\\\"key\\\":\\\"attachments\\\",\\\"noun\\\":\\\"attachment\\\"}],\\\"requires\\\":[\\\"assets.library\\\"],\\\"group\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Files and media\\\",\\\"summary\\\":\\\"Organize files and media, then attach them to the records that need them.\\\"},\\\"section\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Assets\\\",\\\"icon\\\":\\\"folder\\\",\\\"description\\\":\\\"Files, media and attachments for your work.\\\"},\\\"options\\\":{\\\"@happyvertical/smrt-assets:Asset\\\":{\\\"fields\\\":{\\\"name\\\":{\\\"label\\\":\\\"File name\\\",\\\"order\\\":1},\\\"description\\\":{\\\"label\\\":\\\"Description\\\",\\\"order\\\":2},\\\"mimeType\\\":{\\\"label\\\":\\\"File type\\\",\\\"order\\\":3}}},\\\"@happyvertical/smrt-assets:AssetAssociation\\\":{\\\"fields\\\":{\\\"assetId\\\":{\\\"label\\\":\\\"Attachment\\\",\\\"order\\\":1},\\\"metaType\\\":{\\\"help\\\":\\\"The kind of record this file is connected to, such as an article or event.\\\"},\\\"metaId\\\":{\\\"help\\\":\\\"The specific record that this file is connected to.\\\"},\\\"role\\\":{\\\"label\\\":\\\"Role\\\",\\\"order\\\":2,\\\"help\\\":\\\"How this file is used by the related record, such as a cover image or attachment.\\\"},\\\"sortOrder\\\":{\\\"label\\\":\\\"Display order\\\",\\\"order\\\":3,\\\"help\\\":\\\"The position of this attachment among files with the same role; lower numbers appear first.\\\"}}}},\\\"help\\\":{\\\"markdown\\\":\\\"## Overview\\\\n\\\\nAttachments keep authorized supporting files connected to a record without making a public storage link. The application supplies the authorization and upload endpoint; this recipe records the asset and its relationship.\\\\n\\\\n## Tasks\\\\n\\\\n### Describe an attachment\\\\n\\\\n1. Use a clear {field:Asset.name} so people can identify the file.\\\\n2. Add a {field:Asset.description} when the attachment needs context.\\\\n3. Check the {field:Asset.mimeType} before offering the file to a viewer.\\\\n\\\\n### Link an attachment to a record\\\\n\\\\n1. Select the {field:AssetAssociation.assetId} to link.\\\\n2. Set a {field:AssetAssociation.role} when the record uses more than one file.\\\\n3. Adjust {field:AssetAssociation.sortOrder} to control display order.\\\\n\\\",\\\"fieldRefs\\\":[\\\"Asset.description\\\",\\\"Asset.mimeType\\\",\\\"Asset.name\\\",\\\"AssetAssociation.assetId\\\",\\\"AssetAssociation.role\\\",\\\"AssetAssociation.sortOrder\\\"]},\\\"runtime\\\":\\\"both\\\",\\\"demoSeed\\\":{\\\"export\\\":\\\"@happyvertical/smrt-assets/playground#assetAttachmentMockAttachments\\\"},\\\"demo\\\":{\\\"mode\\\":\\\"server\\\",\\\"reasons\\\":[\\\"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624).\\\"]}},{\\\"id\\\":\\\"assets.library\\\",\\\"className\\\":\\\"AssetLibraryRecipe\\\",\\\"label\\\":\\\"Asset Library\\\",\\\"summary\\\":\\\"Organize files and media with folders, types, and statuses.\\\",\\\"synonyms\\\":[\\\"media library\\\",\\\"file library\\\",\\\"digital assets\\\"],\\\"models\\\":[\\\"@happyvertical/smrt-assets:Asset\\\",\\\"@happyvertical/smrt-assets:Folder\\\",\\\"@happyvertical/smrt-assets:AssetType\\\",\\\"@happyvertical/smrt-assets:AssetStatus\\\",\\\"@happyvertical/smrt-assets:AssetMetafield\\\"],\\\"nav\\\":[{\\\"label\\\":\\\"Files & media\\\",\\\"model\\\":\\\"@happyvertical/smrt-assets:Asset\\\",\\\"icon\\\":\\\"archive\\\",\\\"description\\\":\\\"Files and media you can organize, reuse and share.\\\",\\\"noun\\\":\\\"asset\\\"},{\\\"label\\\":\\\"Folders\\\",\\\"model\\\":\\\"@happyvertical/smrt-assets:Folder\\\",\\\"icon\\\":\\\"folder\\\",\\\"description\\\":\\\"Organize assets into clear, reusable groups.\\\",\\\"noun\\\":\\\"folder\\\"}],\\\"requires\\\":[],\\\"group\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Files and media\\\",\\\"summary\\\":\\\"Organize files and media, then attach them to the records that need them.\\\"},\\\"section\\\":{\\\"id\\\":\\\"assets\\\",\\\"label\\\":\\\"Assets\\\",\\\"icon\\\":\\\"folder\\\",\\\"description\\\":\\\"Files, media and attachments for your work.\\\"},\\\"options\\\":{\\\"@happyvertical/smrt-assets:Asset\\\":{\\\"fields\\\":{\\\"name\\\":{\\\"label\\\":\\\"Name\\\",\\\"order\\\":1},\\\"description\\\":{\\\"order\\\":2},\\\"folderId\\\":{\\\"label\\\":\\\"Folder\\\",\\\"order\\\":3},\\\"typeSlug\\\":{\\\"label\\\":\\\"Type\\\",\\\"order\\\":4},\\\"statusSlug\\\":{\\\"label\\\":\\\"Status\\\",\\\"order\\\":5},\\\"mimeType\\\":{\\\"label\\\":\\\"File type\\\",\\\"order\\\":6}}},\\\"@happyvertical/smrt-assets:Folder\\\":{\\\"fields\\\":{\\\"parentId\\\":{\\\"help\\\":\\\"The containing folder; leave empty to place this folder at the top level.\\\"},\\\"name\\\":{\\\"label\\\":\\\"Name\\\",\\\"order\\\":1},\\\"description\\\":{\\\"order\\\":2}}}},\\\"help\\\":{\\\"markdown\\\":\\\"## Overview\\\\n\\\\nThe Asset Library keeps files and media in one place. Organize assets into folders, classify them by type and status, and keep a clear description for people who need to find them later.\\\\n\\\\n## Tasks\\\\n\\\\n### Add an asset\\\\n\\\\n1. Give the asset a recognizable {field:Asset.name}.\\\\n2. Add a {field:Asset.description} that explains how the file can be used.\\\\n3. Select its {field:Asset.folderId}, {field:Asset.typeSlug}, and {field:Asset.statusSlug}.\\\\n4. Confirm the {field:Asset.mimeType} matches the file you intend to manage.\\\\n\\\\n### Organize folders\\\\n\\\\n1. Create a folder with a descriptive {field:Folder.name}.\\\\n2. Use the {field:Folder.description} to explain the folder's purpose.\\\\n\\\",\\\"fieldRefs\\\":[\\\"Asset.description\\\",\\\"Asset.folderId\\\",\\\"Asset.mimeType\\\",\\\"Asset.name\\\",\\\"Asset.statusSlug\\\",\\\"Asset.typeSlug\\\",\\\"Folder.description\\\",\\\"Folder.name\\\"]},\\\"runtime\\\":\\\"both\\\",\\\"demoSeed\\\":{\\\"export\\\":\\\"@happyvertical/smrt-assets/playground#assetGridMockAssets\\\"},\\\"demo\\\":{\\\"mode\\\":\\\"server\\\",\\\"reasons\\\":[\\\"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624).\\\"]}}],\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\",\\\"@happyvertical/smrt-tags\\\",\\\"@happyvertical/smrt-tenancy\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3624\\\"],\\\"via\\\":[\\\"@happyvertical/smrt-tenancy\\\"]},\\\"stampsConstructors\\\":true}\"));\n","/**\n * AssetAssociation model - Links assets to any SmrtObject via polymorphic join\n *\n * Enables many-to-many relationships between assets and arbitrary objects\n * (e.g., Article, Profile, Event) with role-based categorization.\n *\n * The polymorphic right side (`metaType` + `metaId` + `role` + `sortOrder`)\n * and the registry-aware `hydrate()` helper come from\n * `SmrtPolymorphicAssociation` in core; this class only adds the `assetId`\n * left/owner FK.\n */\n\nimport {\n field,\n foreignKey,\n SmrtPolymorphicAssociation,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { AssetAssociationOptions } from './types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n conflictColumns: ['asset_id', 'meta_type', 'meta_id', 'role'],\n api: { include: ['list', 'get', 'create', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetAssociation extends SmrtPolymorphicAssociation { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n /**\n * Optional tenant scope. Without this, the generated `api`/`mcp` `create`\n * routes let any caller forge a polymorphic link between an asset and an\n * arbitrary object across tenant boundaries — the #1540 generated-route\n * tenant-context fix only filters models that ARE `@TenantScoped`, so this\n * model must opt in to be covered (S5 #1396).\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** FK to Asset.id */\n @foreignKey('Asset', { required: true })\n @field({ description: 'The file attached to the related record.' })\n assetId = '';\n\n constructor(options: AssetAssociationOptions = {}) {\n super(options);\n if (options.assetId) this.assetId = options.assetId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","/**\n * AssetAssociationCollection - Polymorphic junction collection.\n *\n * Extends `SmrtJunctionBase`: the left side is a mandatory composite key\n * (`metaType` + `metaId`). Owner methods retain both required arguments without\n * pretending to implement the single-owner `SmrtJunction` contract.\n * `byRight(assetId)` and junction registration/positioning are inherited.\n *\n * For non-polymorphic links between two domain models, prefer a dedicated\n * noun-table junction (e.g. `content_assets`, `place_assets`) extending\n * `SmrtJunction` directly. Use `AssetAssociation` only for generic/provenance\n * relationships that aren't worth a dedicated table.\n */\n\nimport {\n type JunctionAttachOptions,\n type JunctionFilterOptions,\n ObjectRegistry,\n type SmrtCreateInput,\n SmrtJunctionBase,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { AssetAssociation } from './asset-association';\n\n/**\n * `metaType` filter matching every name stored rows may use for the owner's\n * class: its current qualified name and any deprecated\n * `previousQualifiedNames` (#3338). Rows written before and after a model\n * moved package both match; new rows always store the current name\n * (`SmrtPolymorphicAssociation.save()`).\n */\nasync function metaTypeFilter(metaType: string): Promise<string | string[]> {\n // Async so an owner class known only from its installed manifest is\n // lazily loaded first; otherwise its old names are invisible here.\n const names = await ObjectRegistry.getEquivalentQualifiedNamesAsync(\n metaType,\n { source: 'AssetAssociationCollection' },\n );\n return names.length === 1 ? names[0] : names;\n}\n\n// Decorator with empty config — only needed so the scanner detects the\n// class. See FactContentCollection for the full rationale; the short\n// version is: api/mcp/cli on a collection decorator clobber the item\n// class's own config, so leave them off here.\n@smrt()\nexport class AssetAssociationCollection extends SmrtJunctionBase<AssetAssociation> { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n static readonly _itemClass = AssetAssociation;\n\n protected rightField = 'assetId';\n\n /**\n * List associations for a polymorphic owner.\n *\n * Composite left key — pass both halves.\n */\n async byLeft(\n metaType: string,\n metaId: string,\n opts: JunctionFilterOptions = {},\n ): Promise<AssetAssociation[]> {\n return (await this.list({\n // Spread opts first so the fixed polymorphic owner keys always win.\n where: { ...opts, metaType: await metaTypeFilter(metaType), metaId },\n orderBy: 'sort_order ASC',\n })) as AssetAssociation[];\n }\n\n /**\n * Create an association. Composite left (metaType, metaId) precedes right (assetId).\n */\n async attach(\n metaType: string,\n metaId: string,\n assetId: string,\n opts: JunctionAttachOptions = {},\n ): Promise<AssetAssociation> {\n return (await this.create({\n // Spread opts first so the fixed key fields always win.\n ...opts,\n assetId,\n metaType,\n metaId,\n } as SmrtCreateInput<AssetAssociation>)) as AssetAssociation;\n }\n\n /**\n * Delete matching associations. Composite left (metaType, metaId) precedes right (assetId).\n */\n async detach(\n metaType: string,\n metaId: string,\n assetId: string,\n opts: JunctionFilterOptions = {},\n ): Promise<void> {\n const links = (await this.list({\n where: {\n ...opts,\n metaType: await metaTypeFilter(metaType),\n metaId,\n assetId,\n },\n })) as AssetAssociation[];\n for (const link of links) {\n await link.delete();\n }\n }\n\n /**\n * Replace all associations for a polymorphic owner with the given asset IDs.\n * Not transactional — see `SmrtJunction.setLinks` for caveats.\n */\n async setLinks(\n metaType: string,\n metaId: string,\n assetIds: string[],\n opts: JunctionAttachOptions = {},\n ): Promise<void> {\n // Strip the right-side key (assetId) from snapshot opts — see the\n // base class docstring for the rationale; the same contract applies\n // here.\n const snapshotOpts: JunctionAttachOptions = { ...opts };\n delete snapshotOpts.assetId;\n\n const existing = (await this.list({\n where: {\n ...snapshotOpts,\n metaType: await metaTypeFilter(metaType),\n metaId,\n },\n })) as AssetAssociation[];\n for (const link of existing) {\n await link.delete();\n }\n\n const positionKey = this.positionField;\n for (let i = 0; i < assetIds.length; i++) {\n const rowOpts: JunctionAttachOptions = { ...opts };\n if (positionKey && rowOpts[positionKey] === undefined) {\n rowOpts[positionKey] = i;\n }\n await this.attach(metaType, metaId, assetIds[i], rowOpts);\n }\n }\n}\n","/**\n * AssetStatus model - Defines the lifecycle status of an asset\n *\n * Lookup table for asset status classification (e.g., 'draft', 'published', 'archived')\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { AssetStatusOptions } from './types';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetStatus extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // slug is inherited as an accessor from SmrtObject\n @field({\n description:\n 'The name of this lifecycle stage, such as Draft or Published.',\n })\n name = ''; // Display name (e.g., 'Draft', 'Published')\n @field({\n description: 'Explain when an asset should use this lifecycle stage.',\n })\n description = ''; // Optional description\n\n constructor(options: AssetStatusOptions = {}) {\n super(options);\n if (options.slug) this.slug = options.slug;\n if (options.name) this.name = options.name;\n if (options.description) this.description = options.description;\n }\n\n /**\n * Get asset status by slug\n *\n * @param slug - The slug to search for\n * @returns AssetStatus instance or null\n */\n static async getBySlug(_slug: string): Promise<AssetStatus | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * AssetType model - Defines the high-level type of an asset\n *\n * Lookup table for asset type classification (e.g., 'image', 'video', 'document')\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { AssetTypeOptions } from './types';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetType extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // slug is inherited as an accessor from SmrtObject\n @field({\n description: 'The name of this file category, such as Image or Document.',\n })\n name = ''; // Display name (e.g., 'Image', 'Video')\n @field({\n description: 'Explain which kinds of files belong in this category.',\n })\n description = ''; // Optional description\n\n constructor(options: AssetTypeOptions = {}) {\n super(options);\n if (options.slug) this.slug = options.slug;\n if (options.name) this.name = options.name;\n if (options.description) this.description = options.description;\n }\n\n /**\n * Get asset type by slug\n *\n * @param slug - The slug to search for\n * @returns AssetType instance or null\n */\n static async getBySlug(_slug: string): Promise<AssetType | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","import type { AsyncLocalStorage } from 'node:async_hooks';\nimport {\n GlobalInterceptors,\n type InterceptorContext,\n ObjectRegistry,\n resolveGetStringFilter,\n type SmrtObject,\n} from '@happyvertical/smrt-core';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\n\n/** Trusted server capability. Never construct this from an untrusted API payload. */\nexport interface PrivateAssetAccess {\n tenantId: string;\n actorUserId: string;\n ownerProfileId: string;\n assetIds: readonly string[];\n}\ninterface Scope extends PrivateAssetAccess {\n active: boolean;\n}\nconst KEY = Symbol.for('smrt:private-asset-access');\nconst root = globalThis as typeof globalThis & {\n [KEY]?: AsyncLocalStorage<Scope>;\n};\nfunction scopeStorage(): AsyncLocalStorage<Scope> | undefined {\n if (root[KEY]) return root[KEY];\n if (typeof process === 'undefined' || !process.getBuiltinModule)\n return undefined;\n const { AsyncLocalStorage } = process.getBuiltinModule('node:async_hooks');\n root[KEY] = new AsyncLocalStorage<Scope>();\n return root[KEY];\n}\n\nfunction currentScope(): Scope | undefined {\n const scope = scopeStorage()?.getStore();\n if (!scope) return undefined;\n const actor = getCurrentTenant();\n if (\n !scope.active ||\n actor?.tenantId !== scope.tenantId ||\n actor.userId !== scope.actorUserId\n )\n throw new Error(\n 'Private asset capability is closed or actor identity changed',\n );\n return scope;\n}\n\n/**\n * Run an already-authorized operation on exact private asset IDs. The host must\n * authorize the relationship first; this capability does not grant permissions.\n * Nested capabilities may only narrow their parent's IDs and retain its owner.\n */\nexport async function withPrivateAssetAccess<T>(\n access: PrivateAssetAccess,\n callback: () => Promise<T>,\n): Promise<T> {\n const storage = scopeStorage();\n if (!storage)\n throw new Error('Private asset capabilities require a Node server');\n const actor = getCurrentTenant();\n if (\n !access.tenantId ||\n !access.actorUserId ||\n !access.ownerProfileId ||\n !access.assetIds.length ||\n access.assetIds.some((id) => !id) ||\n actor?.tenantId !== access.tenantId ||\n actor.userId !== access.actorUserId\n )\n throw new Error(\n 'Private asset capability requires an exact authenticated actor and asset IDs',\n );\n const parent = currentScope();\n if (\n parent &&\n (parent.ownerProfileId !== access.ownerProfileId ||\n access.assetIds.some((id) => !parent.assetIds.includes(id)))\n )\n throw new Error('Nested private asset capability cannot widen access');\n const scope: Scope = {\n ...access,\n assetIds: [...access.assetIds],\n active: true,\n };\n return storage.run(scope, async () => {\n try {\n return await callback();\n } finally {\n scope.active = false;\n }\n });\n}\n\nfunction applies(context: InterceptorContext): boolean {\n const identity = context.qualifiedClassName ?? context.className;\n return (\n ObjectRegistry.getSTIBase(identity) === '@happyvertical/smrt-assets:Asset'\n );\n}\nfunction predicate(): Record<string, unknown> {\n const scope = currentScope();\n return scope\n ? {\n accessMode: 'private',\n tenantId: scope.tenantId,\n ownerProfileId: scope.ownerProfileId,\n 'id in': [...scope.assetIds],\n }\n : { accessMode: 'public' };\n}\nfunction assertOwner(\n record: Record<string, unknown>,\n scope: Scope | undefined,\n): void {\n if (\n !scope ||\n record.tenantId !== scope.tenantId ||\n record.ownerProfileId !== scope.ownerProfileId ||\n !scope.assetIds.includes(String(record.id))\n )\n throw new Error('Private asset access denied');\n}\nasync function guardMutation(\n instance: SmrtObject,\n context: InterceptorContext,\n): Promise<void> {\n if (!applies(context)) return;\n const record = instance as unknown as Record<string, unknown>;\n // The instance executor is the transaction executor, including uncommitted\n // writes. Never consult a separate root connection here.\n const prior = instance.id\n ? await instance.db.get(instance.tableName, { id: instance.id })\n : null;\n const scope = currentScope();\n if (prior?.access_mode === 'private') {\n assertOwner(\n {\n id: prior.id,\n tenantId: prior.tenant_id,\n ownerProfileId: prior.owner_profile_id,\n },\n scope,\n );\n if (\n record.accessMode !== 'private' ||\n record.tenantId !== prior.tenant_id ||\n record.ownerProfileId !== prior.owner_profile_id\n )\n throw new Error(\n 'Private asset classification and ownership are immutable',\n );\n }\n if (record.accessMode !== 'public' && record.accessMode !== 'private')\n throw new Error('Invalid asset access mode');\n if (record.accessMode === 'private') {\n assertOwner(record, scope);\n if (\n record.sourceUri &&\n /^https?:$/.test(new URL(String(record.sourceUri)).protocol)\n )\n throw new Error(\n 'Private assets require non-public storage, not HTTP(S) source locators',\n );\n }\n}\n\n// Mandatory owning-package policy: importing Asset installs it, including when\n// consumers load Asset through the generated parent model rather than education.\nGlobalInterceptors.register({\n name: 'asset-private-records',\n priority: 90,\n bulkMutation: {\n compatible(className) {\n return (\n ObjectRegistry.getSTIBase(className) !==\n '@happyvertical/smrt-assets:Asset'\n );\n },\n },\n beforeList(_name, options, context) {\n if (!applies(context)) return;\n const condition = predicate();\n return {\n ...options,\n where: Array.isArray(options.where)\n ? options.where.map((group) => [condition, ...group])\n : { ...options.where, ...condition },\n };\n },\n beforeGet(_name, filter, context) {\n if (!applies(context)) return;\n return {\n ...(typeof filter === 'string' ? resolveGetStringFilter(filter) : filter),\n ...predicate(),\n };\n },\n beforeQuery(_name, _options, context) {\n if (applies(context))\n throw new Error(\n 'Raw Asset collection queries cannot enforce private record access; use guarded list/get APIs',\n );\n },\n writeGuard(instance, context) {\n if (!applies(context)) return;\n const record = instance as unknown as Record<string, unknown>;\n const scope = currentScope();\n if (record.accessMode === 'private') {\n assertOwner(record, scope);\n return {\n access_mode: 'private',\n tenant_id: scope!.tenantId,\n owner_profile_id: scope!.ownerProfileId,\n 'id in': [...scope!.assetIds],\n };\n }\n return { access_mode: 'public' };\n },\n beforeSave: guardMutation,\n beforeDelete: guardMutation,\n});\n","/**\n * Asset model - Core entity for asset management\n *\n * Represents a digital asset with versioning, metadata, and tag support.\n * Supports multi-tenancy with optional tenant scoping.\n *\n * Post R3-D: `parentId` was renamed to `sourceAssetId` to reflect what\n * the column actually means — a derivation pointer (\"this asset was\n * produced from that source asset\"), not a structural-hierarchy edge.\n * Folder, which used `parent_id` for actual hierarchy via STI on this\n * table, has moved to its own `folders` table. See `folder.ts` and the\n * `R3-D` changeset for the migration.\n */\n\nimport {\n crossPackageRef,\n field,\n foreignKey,\n ObjectRegistry,\n type SmrtCollection,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { type Tag, TagCollection } from '@happyvertical/smrt-tags';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { AssetAssociation } from './asset-association';\nimport { AssetAssociationCollection } from './asset-associations';\nimport { AssetStatus } from './asset-status';\nimport { AssetType } from './asset-type';\nimport type { AssetOptions } from './types';\nimport './private-asset-access';\n\nexport interface AssetExternalReference {\n provider: string;\n assetId?: string | null;\n externalId?: string | null;\n sourceRef?: Record<string, unknown> | null;\n status?: string | null;\n metadata?: Record<string, unknown> | null;\n syncedAt?: string | null;\n [key: string]: unknown;\n}\n\nfunction parseAssetRecord(\n value: string | Record<string, unknown> | null | undefined,\n): Record<string, unknown> {\n if (!value) return {};\n if (typeof value !== 'string') return value;\n if (!value.trim()) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : {};\n } catch {\n return {};\n }\n}\n\nfunction stringifyAssetRecord(\n value: Record<string, unknown> | null | undefined,\n): string {\n if (!value || Object.keys(value).length === 0) return '';\n return JSON.stringify(value);\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: { skipApiCheck: true },\n})\nexport class Asset extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // Tenant isolation (optional - assets can be global or tenant-specific)\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Private records require an exact trusted capability for every operation. */\n @field({\n description:\n 'Whether this record is public to ordinary asset APIs or private.',\n })\n accessMode: 'public' | 'private' = 'public';\n\n // Core fields\n @field({ description: 'A recognizable name for this file or media item.' })\n name = ''; // User-friendly name\n // slug is inherited as an accessor from SmrtObject\n @field({\n description: 'Where the original file is stored or can be retrieved.',\n })\n sourceUri = ''; // URI to the actual file (e.g., 's3://bucket/key', 'file:///path')\n @field({\n description: 'The file format, such as image/jpeg or application/pdf.',\n })\n mimeType = ''; // MIME type (e.g., 'image/jpeg', 'video/mp4')\n @field({\n description: 'Explain what the file contains and when it should be used.',\n })\n description = ''; // Optional description\n @field({\n description: 'Additional details recorded while processing this file.',\n })\n metadata = ''; // JSON metadata owned by SMRT asset processors\n @field({\n description: 'The revision number of this file in its version history.',\n })\n version = 1; // Version number\n\n // Foreign key references (stored as IDs/slugs)\n // Self-referential: the version chain points at the first version's\n // Asset record (createNewVersion() chains via primaryVersionId).\n @foreignKey('Asset')\n @field({\n description:\n 'The first version of this file, linking its revision history.',\n })\n primaryVersionId: string | null = null; // Points to first version's ID\n @field({\n description: 'The category of asset, such as an image, video, or document.',\n })\n typeSlug = ''; // FK to AssetType.slug\n @field({\n description:\n 'The stage of this asset, such as draft, published, or archived.',\n })\n statusSlug = ''; // FK to AssetStatus.slug\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n @field({\n description: 'The person or organization responsible for this asset.',\n })\n ownerProfileId: string | null = null; // FK to Profile.id (nullable)\n /**\n * FK to the source Asset this one was derived from (e.g. thumbnail\n * derived from an original image, transcoded video, AI variation).\n *\n * Renamed from `parentId` in R3-D to make the derivation semantics\n * explicit and free `parentId` to mean exactly structural hierarchy\n * (SmrtHierarchical) across the framework. The column on the assets\n * table is `source_asset_id`.\n */\n @foreignKey('Asset')\n @field({\n description: 'The original asset used to produce this file or variation.',\n })\n sourceAssetId: string | null = null;\n @foreignKey('Folder')\n @field({\n description: 'The folder that organizes this file in the asset library.',\n })\n folderId: string | null = null; // FK to Folder.id\n\n // Provenance fields\n @field({\n description: 'The service or source from which this file was obtained.',\n })\n sourceType = ''; // 'local', 'shutterstock', 'google-photos', 'upstream-smrt'\n @field({\n description:\n 'The reference used to identify this file at its original source.',\n })\n externalId = ''; // Original ID in upstream source\n @field({\n description:\n 'References that connect this file to copies held by other services.',\n })\n externalRefs = ''; // JSON map of provider references, keyed by provider slug\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n /**\n * A file's name is not its identity: two pictures named \"photo.jpg\" are two\n * assets. Without an explicit slug the default (name-derived) slug would\n * make the second save adopt the first row through the (tenant, slug,\n * context) natural key and overwrite it, so an asset's default slug is its\n * id. An explicit slug is kept.\n */\n override async getSlug(): Promise<string | null | undefined> {\n if (!this.slug && this.id) this.slug = String(this.id);\n return super.getSlug();\n }\n\n constructor(options: AssetOptions = {}) {\n super(options);\n if (options.accessMode !== undefined) this.accessMode = options.accessMode;\n if (options.name) this.name = options.name;\n if (options.slug) this.slug = options.slug;\n if (options.sourceUri) this.sourceUri = options.sourceUri;\n if (options.mimeType) this.mimeType = options.mimeType;\n if (options.description) this.description = options.description;\n if (options.metadata !== undefined)\n this.metadata =\n typeof options.metadata === 'string'\n ? options.metadata\n : stringifyAssetRecord(options.metadata);\n if (options.version !== undefined) this.version = options.version;\n if (options.primaryVersionId !== undefined)\n this.primaryVersionId = options.primaryVersionId;\n if (options.typeSlug) this.typeSlug = options.typeSlug;\n if (options.statusSlug) this.statusSlug = options.statusSlug;\n if (options.ownerProfileId !== undefined)\n this.ownerProfileId = options.ownerProfileId;\n if (options.sourceAssetId !== undefined)\n this.sourceAssetId = options.sourceAssetId;\n if (options.folderId !== undefined) this.folderId = options.folderId;\n if (options.sourceType) this.sourceType = options.sourceType;\n if (options.externalId) this.externalId = options.externalId;\n if (options.externalRefs !== undefined)\n this.externalRefs =\n typeof options.externalRefs === 'string'\n ? options.externalRefs\n : stringifyAssetRecord(options.externalRefs);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n getMetadata(): Record<string, unknown> {\n return parseAssetRecord(this.metadata);\n }\n\n setMetadata(metadata: Record<string, unknown>): void {\n this.metadata = stringifyAssetRecord(metadata);\n }\n\n mergeMetadata(metadata: Record<string, unknown>): void {\n this.setMetadata({\n ...this.getMetadata(),\n ...metadata,\n });\n }\n\n getExternalRefs(): Record<string, AssetExternalReference> {\n const refs = parseAssetRecord(this.externalRefs);\n const normalized: Record<string, AssetExternalReference> = {};\n for (const [provider, value] of Object.entries(refs)) {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n normalized[provider] = {\n ...(value as Record<string, unknown>),\n provider,\n } as AssetExternalReference;\n }\n }\n return normalized;\n }\n\n getExternalRef(provider: string): AssetExternalReference | null {\n return this.getExternalRefs()[provider] ?? null;\n }\n\n setExternalRef(\n provider: string,\n reference: Omit<AssetExternalReference, 'provider'> & { provider?: string },\n ): void {\n this.externalRefs = stringifyAssetRecord({\n ...this.getExternalRefs(),\n [provider]: {\n ...(this.getExternalRef(provider) ?? {}),\n ...reference,\n provider: reference.provider ?? provider,\n },\n });\n }\n\n /**\n * Get all tags for this asset from @happyvertical/smrt-tags, through the\n * `asset_tags` join (`AssetTag`), sorted by name.\n *\n * @returns Array of Tag instances from @happyvertical/smrt-tags package\n */\n async getTags(): Promise<Tag[]> {\n if (!this.id) return [];\n const { AssetTagCollection } = await import('./asset-tags');\n const links = await AssetTagCollection.create({ db: this.db });\n const rows = await links.byLeft(this.id);\n if (rows.length === 0) return [];\n const tags = await TagCollection.create({ db: this.db });\n const found = (await tags.listByIds([\n ...new Set(rows.map((row) => row.tagId)),\n ])) as Tag[];\n return found.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Check if this asset has a specific tag\n *\n * @param tagSlug - The slug of the tag to check\n * @param context - Tag context; any context when omitted\n * @returns True if the asset has this tag\n */\n async hasTag(tagSlug: string, context?: string): Promise<boolean> {\n const tags = await this.getTags();\n return tags.some(\n (tag) =>\n tag.slug === tagSlug &&\n (context === undefined || tag.context === context),\n );\n }\n\n /**\n * Resolve the AssetCollection lazily. Going through `ObjectRegistry`\n * mirrors the pattern used by `SmrtHierarchical._hierarchyCollection`\n * so source/derivative lookups inherit tenant scoping and ORM\n * hydration without hard-coding an import of `./assets` (which would\n * create a module-import cycle).\n *\n * The return type is `SmrtCollection<Asset>` (the framework base, type-\n * only import from core) rather than the concrete `AssetCollection` —\n * importing the concrete class is what would create the cycle, but the\n * base-class shape gives callers full `.get()` / `.list()` type\n * safety here.\n *\n * R5-canon: hardcode the base Asset's qualified key so a different\n * package also registering a class called `Asset` can't be picked\n * by `findClass`'s multi-strategy fallback. Crucially we DON'T\n * resolve via `this.constructor._smrtQualifiedName` — for an STI\n * subclass like `Image`, that would yield the Image collection\n * (which auto-filters `_meta_type = '...:Image'` on `get`/`list`),\n * and `getSource()` / `getDerivatives()` would miss cross-type\n * derivation links (Image derived from a plain Asset, etc.).\n * `sourceAssetId` is a base-table derivation link, so it always\n * resolves through the base Asset collection.\n */\n private async _assetCollection(): Promise<SmrtCollection<Asset>> {\n return await ObjectRegistry.getCollection<Asset>(\n '@happyvertical/smrt-assets:Asset',\n this.options,\n );\n }\n\n /**\n * Get the source asset this one was derived from, if any.\n *\n * Renamed from `getParent` in R3-D. The relationship is \"I was produced\n * from that asset\" (e.g. a thumbnail's source is its original image),\n * not a structural-hierarchy parent.\n *\n * Goes through the AssetCollection so tenant interceptors and ORM\n * hydration apply — important because a tenant-scoped consumer with\n * cross-tenant derivative chains would otherwise return assets from\n * tenants the caller cannot see, and a raw `db.get` returns\n * snake_case rows that leave camelCase props (e.g. `sourceUri`) at\n * their constructor defaults.\n *\n * @returns Source Asset instance, or null if this asset has no source\n */\n async getSource(): Promise<Asset | null> {\n if (!this.sourceAssetId) return null;\n const collection = await this._assetCollection();\n return (await collection.get({ id: this.sourceAssetId })) as Asset | null;\n }\n\n /**\n * Get all assets derived from this one (e.g. thumbnails, variants,\n * transcodes, AI edits).\n *\n * Renamed from `getChildren` in R3-D to match the derivation\n * semantics. Goes through the AssetCollection so tenant interceptors\n * and ORM hydration apply (see `getSource` for why this matters —\n * the pre-R3-D `getChildren` used raw `db.list`, which both bypassed\n * tenant scoping and dropped camelCase property hydration; that\n * latent breakage is fixed here).\n *\n * @returns Array of derivative Asset instances\n */\n async getDerivatives(): Promise<Asset[]> {\n if (!this.id) return [];\n const collection = await this._assetCollection();\n return (await collection.list({\n where: { sourceAssetId: this.id },\n })) as Asset[];\n }\n\n /**\n * Get the type of this asset\n *\n * @returns AssetType instance or null\n */\n async getType(): Promise<AssetType | null> {\n if (!this.typeSlug) return null;\n\n return await AssetType.getBySlug(this.typeSlug);\n }\n\n /**\n * Get the status of this asset\n *\n * @returns AssetStatus instance or null\n */\n async getStatus(): Promise<AssetStatus | null> {\n if (!this.statusSlug) return null;\n\n return await AssetStatus.getBySlug(this.statusSlug);\n }\n\n /**\n * Get all associations for this asset\n *\n * @returns Array of AssetAssociation instances\n */\n async getAssociations(): Promise<AssetAssociation[]> {\n const associations = await AssetAssociationCollection.create({\n db: this.db,\n });\n return await associations.byRight(this.id!);\n }\n\n /**\n * Associate this asset with a target object\n *\n * @param metaType - Target class name or qualified name (e.g., 'Article' or '@pkg:Article')\n * @param metaId - Target object ID\n * @param role - Association role (default: 'default')\n * @returns The created AssetAssociation\n */\n async associateWith(\n metaType: string,\n metaId: string,\n role = 'default',\n ): Promise<AssetAssociation> {\n const associations = await AssetAssociationCollection.create({\n db: this.db,\n });\n return await associations.attach(metaType, metaId, this.id!, { role });\n }\n\n /**\n * Get asset by slug\n *\n * @param slug - The slug to search for\n * @returns Asset instance or null\n */\n static async getBySlug(_slug: string): Promise<Asset | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","import type { Asset } from './asset';\nimport type { AssetAssociationCollection } from './asset-associations';\nimport type { AssetStore, StoreOptions } from './asset-store';\nimport type { AssetCollection } from './assets';\n\nexport type AssetVariantFit = 'cover' | 'contain' | 'inside';\nexport type AssetCapabilitySource = 'cached' | 'generated' | 'external';\n\nexport interface AssetCapabilityRuntime {\n readonly collection: AssetCollection;\n readonly associations: AssetAssociationCollection;\n readonly store: AssetStore;\n storeDerivedAsset(\n source: Asset,\n name: string,\n data: Buffer,\n opts: Omit<StoreOptions, 'sourceAssetId'> & {\n role?: string;\n derivativeMetaType?: string;\n linkAssociation?: boolean;\n },\n ): Promise<Asset>;\n}\n\nexport interface AssetVariantRequest {\n variant: string;\n width?: number;\n height?: number;\n fit?: AssetVariantFit;\n mimeType?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetVariantResult {\n asset: Asset;\n variant: string;\n source: AssetCapabilitySource;\n url?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetProcessInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n variants?: AssetVariantRequest[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetProcessResult {\n asset: Asset;\n metadata?: Record<string, unknown> | null;\n variants?: AssetVariantResult[];\n warnings?: string[];\n}\n\nexport interface AssetEnsureVariantInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n request: AssetVariantRequest;\n}\n\nexport interface AssetNearbySearchInput {\n runtime: AssetCapabilityRuntime;\n tenantId?: string | null;\n latitude: number;\n longitude: number;\n radiusMeters?: number;\n limit?: number;\n source?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetSearchItem {\n asset?: Asset | null;\n assetId: string;\n origin: string;\n title?: string | null;\n previewUrl?: string | null;\n deliveryUrl?: string | null;\n mimeType?: string | null;\n width?: number | null;\n height?: number | null;\n distanceMeters?: number | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetSearchResult {\n items: AssetSearchItem[];\n nextCursor?: string | null;\n}\n\nexport interface AssetExternalSyncInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n externalId?: string | null;\n sourceRef?: AssetExternalSourceRef | null;\n metadata?: Record<string, unknown>;\n}\n\nexport interface AssetExternalSourceRef {\n system: string;\n id: string;\n kind?: string | null;\n}\n\nexport interface AssetExternalSyncResult {\n asset: Asset;\n provider: string;\n status: 'ready' | 'pending' | 'skipped';\n externalAssetId?: string | null;\n externalId?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetWorkflowInputSelection {\n slotKey: string;\n assetIds: string[];\n}\n\nexport interface AssetWorkflowInput {\n runtime: AssetCapabilityRuntime;\n asset: Asset;\n workflowSlug: string;\n clientRequestId?: string | null;\n name?: string;\n inputSelections?: AssetWorkflowInputSelection[];\n sourceAssetIds?: string[];\n parameters?: Record<string, unknown>;\n requestContext?: Record<string, unknown>;\n requestedPlacement?: string | null;\n priority?: number | null;\n}\n\nexport interface AssetWorkflowResult {\n provider: string;\n jobId: string;\n status: string;\n idempotent?: boolean;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface AssetCapabilityProvider {\n readonly name: string;\n processAsset?(input: AssetProcessInput): Promise<AssetProcessResult>;\n ensureVariant?(input: AssetEnsureVariantInput): Promise<AssetVariantResult>;\n searchNearbyAssets?(\n input: AssetNearbySearchInput,\n ): Promise<AssetSearchResult>;\n syncExternalAsset?(\n input: AssetExternalSyncInput,\n ): Promise<AssetExternalSyncResult>;\n submitAssetWorkflow?(input: AssetWorkflowInput): Promise<AssetWorkflowResult>;\n}\n\nexport type AssetCapabilityName =\n | 'processAsset'\n | 'ensureVariant'\n | 'searchNearbyAssets'\n | 'syncExternalAsset'\n | 'submitAssetWorkflow';\n\nexport class AssetCapabilityUnavailableError extends Error {\n constructor(\n public readonly capability: AssetCapabilityName,\n message = `No asset capability provider is registered for ${capability}.`,\n ) {\n super(message);\n this.name = 'AssetCapabilityUnavailableError';\n }\n}\n\nexport class AssetCapabilitySkippedError extends Error {\n constructor(\n public readonly capability: AssetCapabilityName,\n message: string,\n ) {\n super(message);\n this.name = 'AssetCapabilitySkippedError';\n }\n}\n","/**\n * Asset conventions\n *\n * Shared constants and metadata-key conventions for how apps and agents\n * describe asset relationships and derivation state on top of `smrt-assets`.\n *\n * These are advisory: `AssetAssociation.role` and `Asset` metadata fields\n * accept any string, but standardising on these values makes cross-agent\n * serving, filtering, and UI work possible without everybody inventing\n * their own vocabulary.\n */\n\n/**\n * Canonical association roles used with `AssetAssociation` and noun join\n * tables like `content_assets`.\n *\n * - `source_document`: original upstream document (e.g. agenda PDF) that\n * later derivatives are extracted from.\n * - `document_image`: page image or other visual derived from a source\n * document.\n * - `thumbnail`: small preview rendition of another asset.\n * - `asset_variant`: deterministic sized rendition of another asset.\n * - `proof`: non-canonical evidence asset (e.g. a screenshot used to\n * back a fact).\n * - `derivation_source`: source side of a derivation link (used when a\n * derivative points back to the asset it was produced from).\n * - `attachment`: generic \"this asset belongs to that object\" link —\n * the default for noun join tables.\n * - `hero`: primary/featured asset for an owner (e.g. a place's main picture).\n * - `depicts`: the asset shows the owner — a photo of a place or a person\n * (`place_assets`, `profile_assets`).\n */\nexport const ASSET_ROLES = {\n SOURCE_DOCUMENT: 'source_document',\n DOCUMENT_IMAGE: 'document_image',\n THUMBNAIL: 'thumbnail',\n ASSET_VARIANT: 'asset_variant',\n PROOF: 'proof',\n DERIVATION_SOURCE: 'derivation_source',\n ATTACHMENT: 'attachment',\n HERO: 'hero',\n DEPICTS: 'depicts',\n} as const;\n\nexport type AssetRole = (typeof ASSET_ROLES)[keyof typeof ASSET_ROLES];\n\n/**\n * Canonical metadata keys used on `Asset.description`-adjacent metadata\n * surfaces or stored via `AssetAssociation` sidecars. These keys are the\n * recommended names for cross-package extraction, provenance, and\n * content-addressing state.\n *\n * Apps are free to add their own keys; using these for the common cases\n * keeps derived-asset tooling portable.\n *\n * - `extractionStatus`: `'pending' | 'running' | 'succeeded' | 'failed'`\n * - `extractionError`: error string from the last extraction attempt\n * - `extractedAt`: ISO timestamp of the last successful extraction\n * - `sourceUrl`: upstream URL the asset was fetched from\n * - `sourceHash`: content-address hash of the source bytes (e.g. sha256)\n * - `pageNumber`: 1-based page number within a multi-page source document\n */\nexport const ASSET_METADATA_KEYS = {\n EXTRACTION_STATUS: 'extractionStatus',\n EXTRACTION_ERROR: 'extractionError',\n EXTRACTED_AT: 'extractedAt',\n SOURCE_URL: 'sourceUrl',\n SOURCE_HASH: 'sourceHash',\n PAGE_NUMBER: 'pageNumber',\n} as const;\n\nexport type AssetMetadataKey =\n (typeof ASSET_METADATA_KEYS)[keyof typeof ASSET_METADATA_KEYS];\n\n/**\n * Known extraction status values for `ASSET_METADATA_KEYS.EXTRACTION_STATUS`.\n */\nexport const ASSET_EXTRACTION_STATUS = {\n PENDING: 'pending',\n RUNNING: 'running',\n SUCCEEDED: 'succeeded',\n FAILED: 'failed',\n} as const;\n\nexport type AssetExtractionStatus =\n (typeof ASSET_EXTRACTION_STATUS)[keyof typeof ASSET_EXTRACTION_STATUS];\n","/**\n * AssetMetafield model - Defines the controlled vocabulary for metadata keys\n *\n * Lookup table for metadata field definitions with validation rules\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport type { AssetMetafieldOptions } from './types';\n\n@smrt({\n tableStrategy: 'sti',\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create'] },\n cli: { skipApiCheck: true },\n})\nexport class AssetMetafield extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // slug is inherited as an accessor from SmrtObject\n @field({\n description:\n 'The name of an additional asset detail, such as Width or Height.',\n })\n name = ''; // Display name (e.g., 'Width', 'Height')\n @field({\n description: 'Rules that determine which values this asset detail accepts.',\n })\n validation = ''; // JSON validation rules stored as text\n\n constructor(options: AssetMetafieldOptions = {}) {\n super(options);\n if (options.slug) this.slug = options.slug;\n if (options.name) this.name = options.name;\n if (options.validation) this.validation = options.validation;\n }\n\n /**\n * Get validation rules as parsed object\n *\n * @returns Parsed validation object or empty object if no validation\n */\n getValidation(): Record<string, unknown> {\n if (!this.validation) return {};\n try {\n return JSON.parse(this.validation);\n } catch {\n return {};\n }\n }\n\n /**\n * Set validation rules from object\n *\n * @param rules - Validation rules object\n */\n setValidation(rules: Record<string, unknown>): void {\n this.validation = JSON.stringify(rules);\n }\n\n /**\n * Get asset metafield by slug\n *\n * @param slug - The slug to search for\n * @returns AssetMetafield instance or null\n */\n static async getBySlug(_slug: string): Promise<AssetMetafield | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * AssetMetafieldCollection - Collection manager for AssetMetafield instances\n *\n * Manages asset metafield definitions with validation rules\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AssetMetafield } from './asset-metafield';\n\nexport class AssetMetafieldCollection extends SmrtCollection<AssetMetafield> {\n static readonly _itemClass = AssetMetafield;\n\n /**\n * Get or create an asset metafield by slug\n *\n * @param slug - The metafield slug\n * @param name - The display name (defaults to slug)\n * @param validation - Optional validation rules (JSON string or object)\n * @returns The existing or newly created AssetMetafield\n */\n async getOrCreate(\n slug: string,\n name?: string,\n validation?: string | Record<string, unknown>,\n ): Promise<AssetMetafield> {\n const existing = await this.list({ where: { slug }, limit: 1 });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const validationString =\n typeof validation === 'string'\n ? validation\n : validation\n ? JSON.stringify(validation)\n : '';\n\n return await this.create({\n slug,\n name: name || slug,\n validation: validationString,\n });\n }\n\n /**\n * Initialize common asset metafields\n *\n * Creates standard metafields with validation rules:\n * - width (integer, min: 0)\n * - height (integer, min: 0)\n * - duration (number, min: 0)\n * - size (integer, min: 0)\n * - author (string)\n * - copyright (string)\n */\n async initializeCommonMetafields(): Promise<void> {\n await this.getOrCreate('width', 'Width', {\n type: 'integer',\n min: 0,\n description: 'Width in pixels',\n });\n await this.getOrCreate('height', 'Height', {\n type: 'integer',\n min: 0,\n description: 'Height in pixels',\n });\n await this.getOrCreate('duration', 'Duration', {\n type: 'number',\n min: 0,\n description: 'Duration in seconds',\n });\n await this.getOrCreate('size', 'File Size', {\n type: 'integer',\n min: 0,\n description: 'File size in bytes',\n });\n await this.getOrCreate('author', 'Author', {\n type: 'string',\n description: 'Content creator',\n });\n await this.getOrCreate('copyright', 'Copyright', {\n type: 'string',\n description: 'Copyright notice',\n });\n }\n}\n","/**\n * AssetStore - File I/O + Asset record management\n *\n * Writes buffers to disk via @happyvertical/files and creates\n * corresponding Asset records in the database via AssetCollection.\n *\n * Supports provider-agnostic storage (local, S3, etc.) via @happyvertical/files.\n */\n\nimport type {\n FilesystemInterface,\n GetFilesystemOptions,\n} from '@happyvertical/files';\nimport type { Asset } from './asset';\nimport type { AssetCollection } from './assets';\n// The files SDK is loaded through the lazy boundary in files-runtime.ts so it\n// never enters provider-neutral consumer bundles (#1977/#1979).\nimport { getFilesystemLazy, isFileNotFoundError } from './files-runtime.js';\n\n/**\n * MIME type to file extension mapping\n */\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/svg+xml': 'svg',\n 'video/mp4': 'mp4',\n 'video/webm': 'webm',\n 'audio/wav': 'wav',\n 'audio/mpeg': 'mp3',\n 'audio/ogg': 'ogg',\n 'audio/flac': 'flac',\n 'application/pdf': 'pdf',\n 'application/json': 'json',\n 'text/plain': 'txt',\n};\n\n/**\n * Options for storing an asset\n */\nexport interface StoreOptions {\n /** MIME type of the data */\n mimeType: string;\n\n /** Asset type slug (e.g., 'video', 'audio', 'image', 'reference-image') */\n typeSlug?: string;\n\n /**\n * Source asset ID (for derivatives like thumbnails, variants, AI edits).\n *\n * Renamed from `parentId` in R3-D — see Asset.sourceAssetId.\n */\n sourceAssetId?: string;\n\n /** Asset status slug */\n statusSlug?: string;\n\n /** Tenant scope applied before the Asset record is persisted. */\n tenantId?: string | null;\n\n /** Profile that owns the Asset from its initial persisted record. */\n ownerProfileId?: string | null;\n\n /** Description */\n description?: string;\n\n /** Original source system for imported/derived assets */\n sourceType?: string;\n\n /** External id in the source system */\n externalId?: string;\n\n /** JSON metadata stored on the asset record */\n metadata?: string | Record<string, unknown> | null;\n}\n\nfunction serializeStoreMetadata(\n metadata: StoreOptions['metadata'],\n): string | undefined {\n if (metadata === undefined) return undefined;\n if (metadata === null) return '';\n if (typeof metadata === 'string') return metadata;\n return JSON.stringify(metadata);\n}\n\n/**\n * Provider options for configuring the filesystem backend.\n * Accepts any @happyvertical/files options or a simple basePath string for local storage.\n */\nexport type ProviderOptions = GetFilesystemOptions | string;\n\n/**\n * Asset storage operation currently being resolved.\n */\nexport type AssetStorageOperation = 'read' | 'write' | 'delete';\n\n/**\n * Context passed to an AssetStore resolver.\n *\n * Downstream apps can use this hook to route a logical Asset to whichever\n * storage backend is appropriate for the current operation.\n */\nexport interface AssetStorageResolveRequest {\n operation: AssetStorageOperation;\n asset: Asset;\n path: string;\n sourceUri: string;\n mimeType?: string;\n typeSlug?: string;\n defaultProviderOptions: GetFilesystemOptions;\n defaultFilesystem: FilesystemInterface;\n}\n\n/**\n * Resolved storage target for an AssetStore operation.\n */\nexport interface AssetStorageResolution {\n filesystem?: FilesystemInterface;\n providerOptions?: ProviderOptions;\n path?: string;\n sourceUri?: string;\n}\n\nexport type AssetStorageResolver = (\n request: AssetStorageResolveRequest,\n) =>\n | AssetStorageResolution\n | null\n | undefined\n | Promise<AssetStorageResolution | null | undefined>;\n\nexport interface AssetStoreOptions {\n resolver?: AssetStorageResolver;\n}\n\ninterface ResolvedAssetStorageTarget {\n filesystem: FilesystemInterface;\n providerOptions: GetFilesystemOptions;\n path: string;\n sourceUri: string;\n}\n\nfunction normalizeProviderOptions(\n providerOrBasePath: ProviderOptions,\n): GetFilesystemOptions {\n if (typeof providerOrBasePath === 'string') {\n return { type: 'local', basePath: providerOrBasePath };\n }\n return providerOrBasePath;\n}\n\n/**\n * AssetStore manages file storage and Asset record creation.\n *\n * Files are stored using @happyvertical/files with the convention:\n * `{typeSlug}/{assetId}.{ext}`\n *\n * @example\n * ```typescript\n * import { AssetStore } from '@happyvertical/smrt-assets';\n *\n * // Local storage (backward-compatible)\n * const store = new AssetStore('dev/data/assets', assetCollection);\n * await store.initialize();\n *\n * // Provider-agnostic storage\n * const s3Store = new AssetStore({ type: 's3', bucket: 'my-bucket' }, assetCollection);\n * await s3Store.initialize();\n *\n * // Store a video buffer\n * const asset = await store.store('my-video', videoBuffer, {\n * mimeType: 'video/mp4',\n * typeSlug: 'video',\n * });\n *\n * // Read it back\n * const data = await store.read(asset);\n *\n * // Delete it\n * await store.remove(asset);\n * ```\n */\nexport class AssetStore {\n private fs: FilesystemInterface | null = null;\n private readonly fsOptions: GetFilesystemOptions;\n private readonly fsCache = new Map<string, FilesystemInterface>();\n private readonly resolver?: AssetStorageResolver;\n\n constructor(\n providerOrBasePath: ProviderOptions,\n private readonly collection: AssetCollection,\n options: AssetStoreOptions = {},\n ) {\n this.fsOptions = normalizeProviderOptions(providerOrBasePath);\n this.resolver = options.resolver;\n }\n\n /** The base path for local storage, or empty string for non-local providers */\n get basePath(): string {\n return this.fsOptions.basePath ?? '';\n }\n\n /**\n * Initialize the filesystem adapter.\n * Must be called before any file operations.\n */\n async initialize(): Promise<this> {\n this.fs = await getFilesystemLazy(this.fsOptions);\n this.fsCache.set(this.providerCacheKey(this.fsOptions), this.fs);\n return this;\n }\n\n /**\n * Get the initialized filesystem (throws if not initialized)\n */\n private getFs(): FilesystemInterface {\n if (!this.fs) {\n throw new Error('AssetStore not initialized. Call initialize() first.');\n }\n return this.fs;\n }\n\n private providerCacheKey(providerOptions: GetFilesystemOptions): string {\n return JSON.stringify(providerOptions);\n }\n\n private async getFilesystemForOptions(\n providerOptions: GetFilesystemOptions,\n ): Promise<FilesystemInterface> {\n const key = this.providerCacheKey(providerOptions);\n const cached = this.fsCache.get(key);\n if (cached) return cached;\n\n const filesystem = await getFilesystemLazy(providerOptions);\n this.fsCache.set(key, filesystem);\n return filesystem;\n }\n\n /**\n * Build a sourceUri for the given file path based on the provider type.\n */\n private buildSourceUri(filePath: string): string {\n return AssetStore.buildSourceUriForProvider(filePath, this.fsOptions);\n }\n\n private static buildSourceUriForProvider(\n filePath: string,\n providerOptions: GetFilesystemOptions,\n ): string {\n const type = providerOptions.type;\n if (type === 's3') {\n const bucket = providerOptions.bucket ?? '';\n return `s3://${bucket}/${filePath}`;\n }\n // Default to file:// for local and other types\n const base = providerOptions.basePath ?? '';\n return base ? `file://${base}/${filePath}` : `file://${filePath}`;\n }\n\n private static providerRelativePath(\n filePath: string,\n providerOptions: GetFilesystemOptions,\n ): string {\n const base = providerOptions.basePath ?? '';\n return base && filePath.startsWith(base)\n ? filePath.slice(base.length + 1)\n : filePath;\n }\n\n private static requireAssetId(asset: Asset): string {\n if (!asset.id) {\n throw new Error('Asset must be saved before storing file data.');\n }\n return asset.id;\n }\n\n private async resolveStorage(\n request: Omit<\n AssetStorageResolveRequest,\n 'defaultProviderOptions' | 'defaultFilesystem'\n >,\n ): Promise<ResolvedAssetStorageTarget> {\n const defaultFilesystem = this.getFs();\n const resolution = this.resolver\n ? await this.resolver({\n ...request,\n defaultProviderOptions: this.fsOptions,\n defaultFilesystem,\n })\n : undefined;\n if (\n request.operation === 'write' &&\n resolution?.filesystem &&\n !resolution.providerOptions &&\n !resolution.sourceUri\n ) {\n throw new Error(\n 'Asset storage resolver must return providerOptions or sourceUri when overriding filesystem for write operations.',\n );\n }\n const providerOptions = resolution?.providerOptions\n ? normalizeProviderOptions(resolution.providerOptions)\n : this.fsOptions;\n const path = AssetStore.providerRelativePath(\n resolution?.path ?? request.path,\n providerOptions,\n );\n const sourceUri =\n resolution?.sourceUri ??\n (resolution?.path || resolution?.providerOptions\n ? AssetStore.buildSourceUriForProvider(path, providerOptions)\n : request.sourceUri);\n const filesystem =\n resolution?.filesystem ??\n (providerOptions === this.fsOptions\n ? defaultFilesystem\n : await this.getFilesystemForOptions(providerOptions));\n\n return {\n filesystem,\n providerOptions,\n path,\n sourceUri,\n };\n }\n\n /**\n * Resolve an existing asset's future storage URI before writing bytes.\n * Persist this URI with the caller's storage intent. Resolvers used for\n * recoverable writes must return the same target for the same asset.\n */\n async planFile(\n asset: Asset,\n opts: { mimeType: string; typeSlug?: string },\n ): Promise<string> {\n const ext = MIME_TO_EXT[opts.mimeType] ?? 'bin';\n const typeSlug = opts.typeSlug ?? 'file';\n const path = `${typeSlug}/${AssetStore.requireAssetId(asset)}.${ext}`;\n const target = await this.resolveStorage({\n operation: 'write',\n asset,\n path,\n sourceUri: this.buildSourceUri(path),\n mimeType: opts.mimeType,\n typeSlug,\n });\n return target.sourceUri;\n }\n\n /**\n * Preserve bytes at a previously committed plan. Existing bytes must match;\n * missing files are written, while all other read failures fail closed.\n * The caller owns serialization/fencing and must persist sourceUri first.\n */\n async preserveFile(\n asset: Asset,\n data: Buffer,\n opts: { mimeType: string; typeSlug?: string },\n ): Promise<string> {\n const planned = await this.planFile(asset, opts);\n if (!asset.sourceUri || asset.sourceUri !== planned) {\n throw new Error('Asset storage plan changed');\n }\n try {\n const existing = await this.read(asset);\n if (!existing.equals(data))\n throw new Error('Asset storage integrity conflict');\n return planned;\n } catch (error) {\n if (!isFileNotFoundError(error)) throw error;\n }\n const written = await this.writeAssetData(asset, data, opts, planned);\n if (written !== planned) throw new Error('Asset storage plan changed');\n return written;\n }\n\n private async writeAssetData(\n asset: Asset,\n data: Buffer,\n opts: { mimeType: string; typeSlug?: string },\n expectedSourceUri?: string,\n ): Promise<string> {\n const ext = MIME_TO_EXT[opts.mimeType] ?? 'bin';\n const typeSlug = opts.typeSlug ?? 'file';\n const assetId = AssetStore.requireAssetId(asset);\n const filePath = `${typeSlug}/${assetId}.${ext}`;\n const sourceUri = this.buildSourceUri(filePath);\n const target = await this.resolveStorage({\n operation: 'write',\n asset,\n path: filePath,\n sourceUri,\n mimeType: opts.mimeType,\n typeSlug,\n });\n\n if (expectedSourceUri && target.sourceUri !== expectedSourceUri) {\n throw new Error('Asset storage plan changed');\n }\n await target.filesystem.write(target.path, data, { createParents: true });\n return target.sourceUri;\n }\n\n /**\n * Write file data for an existing Asset record (no DB record created).\n *\n * Use this when you've already created the record (e.g., via a\n * collection.create()) and only need to persist the file data.\n *\n * @param asset - The existing asset to write data for\n * @param data - File data as a Buffer\n * @param opts - Storage options (mimeType required)\n * @returns The sourceUri for the written file\n */\n async storeFile(\n asset: Asset,\n data: Buffer,\n opts: { mimeType: string; typeSlug?: string },\n ): Promise<string> {\n return this.writeAssetData(asset, data, opts);\n }\n\n /**\n * Write buffer to disk and create an Asset record.\n *\n * @param name - Human-readable name for the asset\n * @param data - File data as a Buffer\n * @param opts - Storage options (mimeType required)\n * @returns Created Asset instance\n */\n async store(name: string, data: Buffer, opts: StoreOptions): Promise<Asset> {\n const typeSlug = opts.typeSlug ?? 'file';\n\n // Create the Asset record first to get an ID\n const asset = (await this.collection.create({\n name,\n mimeType: opts.mimeType,\n typeSlug,\n statusSlug: opts.statusSlug ?? 'active',\n tenantId: opts.tenantId ?? null,\n ownerProfileId: opts.ownerProfileId ?? null,\n sourceAssetId: opts.sourceAssetId ?? null,\n description: opts.description ?? '',\n sourceType: opts.sourceType ?? '',\n externalId: opts.externalId ?? '',\n metadata: serializeStoreMetadata(opts.metadata) ?? '',\n sourceUri: '', // Will be updated after file write\n })) as Asset;\n\n try {\n // Write file to storage\n asset.sourceUri = await this.writeAssetData(asset, data, {\n mimeType: opts.mimeType,\n typeSlug,\n });\n } catch (err) {\n // Clean up orphaned Asset record on file write failure\n await asset.delete();\n throw err;\n }\n\n // Update the Asset with the file URI\n await asset.save();\n\n return asset;\n }\n\n /**\n * Store a new version of an existing asset.\n *\n * @param asset - The existing asset to version\n * @param data - File data for the new version\n * @param opts - Optional overrides for store options\n * @returns The newly created version Asset\n */\n async storeVersion(\n asset: Asset,\n data: Buffer,\n opts: Partial<StoreOptions> = {},\n ): Promise<Asset> {\n const primaryVersionId =\n asset.primaryVersionId ?? AssetStore.requireAssetId(asset);\n const { metadata, ...versionOptions } = opts;\n const versionUpdates: Partial<Asset> = { ...versionOptions };\n const serializedMetadata = serializeStoreMetadata(metadata);\n if (serializedMetadata !== undefined) {\n versionUpdates.metadata = serializedMetadata;\n } else {\n delete versionUpdates.metadata;\n }\n const newVersion = await this.collection.createNewVersion(\n primaryVersionId,\n '', // sourceUri will be set by store\n versionUpdates,\n );\n\n const mimeType = opts.mimeType ?? asset.mimeType;\n const typeSlug = opts.typeSlug ?? asset.typeSlug ?? 'file';\n\n try {\n newVersion.sourceUri = await this.writeAssetData(newVersion, data, {\n mimeType,\n typeSlug,\n });\n } catch (err) {\n await newVersion.delete();\n throw err;\n }\n\n if (opts.mimeType) newVersion.mimeType = opts.mimeType;\n await newVersion.save();\n\n return newVersion;\n }\n\n /**\n * Read file data for a specific version of an asset.\n *\n * @param asset - The asset (any version in the chain)\n * @param version - The version number to read\n * @returns File data as a Buffer\n */\n async readVersion(asset: Asset, version: number): Promise<Buffer> {\n const primaryVersionId =\n asset.primaryVersionId ?? AssetStore.requireAssetId(asset);\n const versions = await this.collection.listVersions(primaryVersionId);\n const targetVersion = versions.find((v) => v.version === version);\n\n if (!targetVersion) {\n throw new Error(\n `Version ${version} not found for asset ${primaryVersionId}`,\n );\n }\n\n return this.read(targetVersion);\n }\n\n /**\n * Read file data from an Asset's sourceUri.\n *\n * @param asset - Asset to read data for\n * @returns File data as a Buffer\n */\n async read(asset: Asset): Promise<Buffer> {\n const filePath = AssetStore.pathFromUri(asset.sourceUri);\n const target = await this.resolveStorage({\n operation: 'read',\n asset,\n path: AssetStore.providerRelativePath(filePath, this.fsOptions),\n sourceUri: asset.sourceUri,\n });\n return (await target.filesystem.read(target.path, { raw: true })) as Buffer;\n }\n\n /**\n * Read file by asset ID.\n *\n * @param id - Asset ID to look up\n * @returns Object with data Buffer and Asset, or null if not found\n */\n async readById(id: string): Promise<{ data: Buffer; asset: Asset } | null> {\n const asset = (await this.collection.get({ id })) as Asset | null;\n if (!asset) return null;\n\n try {\n const data = await this.read(asset);\n return { data, asset };\n } catch {\n return null;\n }\n }\n\n /**\n * Delete retained file bytes without requiring an existing Asset record.\n *\n * @param asset - Asset to remove\n */\n async removeFile(asset: Asset): Promise<void> {\n // Delete the file if it exists\n if (asset.sourceUri) {\n const filePath = AssetStore.pathFromUri(asset.sourceUri);\n const target = await this.resolveStorage({\n operation: 'delete',\n asset,\n path: AssetStore.providerRelativePath(filePath, this.fsOptions),\n sourceUri: asset.sourceUri,\n });\n try {\n await target.filesystem.delete(target.path);\n } catch (err) {\n if (!isFileNotFoundError(err)) {\n throw err;\n }\n // An already-missing file is successful idempotent cleanup.\n }\n }\n }\n\n /** Delete bytes and the owner record. Storage-only recovery uses removeFile. */\n async remove(asset: Asset): Promise<void> {\n await this.removeFile(asset);\n await asset.delete();\n }\n\n /**\n * Extract filesystem path from a sourceUri.\n * Handles file://, s3://, and plain paths.\n *\n * @param sourceUri - Asset sourceUri (e.g., 'file:///path/to/file.mp4', 's3://bucket/key')\n * @returns Filesystem path\n */\n static pathFromUri(sourceUri: string): string {\n if (sourceUri.startsWith('file://')) {\n return sourceUri.slice(7);\n }\n if (sourceUri.startsWith('s3://')) {\n // Return everything after s3://bucket/\n const withoutScheme = sourceUri.slice(5);\n const slashIdx = withoutScheme.indexOf('/');\n return slashIdx >= 0 ? withoutScheme.slice(slashIdx + 1) : withoutScheme;\n }\n return sourceUri;\n }\n}\n","/**\n * AssetCollection - Collection manager for Asset instances\n *\n * Provides tag management, versioning, tenant-aware queries, and operations for assets\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { type Tag, TagCollection } from '@happyvertical/smrt-tags';\nimport {\n getCurrentTenant,\n withTenantGlobalRead,\n} from '@happyvertical/smrt-tenancy';\nimport { Asset } from './asset';\nimport type { AssetTag } from './asset-tag';\nimport type { AssetTagCollection } from './asset-tags';\n\n/** Tag context used for asset tags unless a caller names another. */\nexport const ASSET_TAG_CONTEXT = 'asset';\n\nexport interface AssetTagInput {\n /** Display name for a newly created tag (defaults to the label given). */\n name?: string;\n /** Tag context (namespace); defaults to {@link ASSET_TAG_CONTEXT}. */\n context?: string;\n}\n\n/** The slug a tag label is stored under: `Town hall` → `town-hall`. */\nexport function assetTagSlug(label: string): string {\n return String(label ?? '')\n .normalize('NFKD')\n .replace(/[\\u0300-\\u036f]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '');\n}\n\nfunction titleFromSlug(slug: string): string {\n const words = slug.replace(/-/g, ' ');\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\nexport class AssetCollection extends SmrtCollection<Asset> {\n static readonly _itemClass = Asset;\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tenant-Aware Query Methods\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Find all assets belonging to a specific tenant\n *\n * @param tenantId - The tenant ID to filter by\n * @returns Array of assets belonging to this tenant\n */\n async findByTenant(tenantId: string): Promise<Asset[]> {\n return (await this.list({ where: { tenantId } })) as Asset[];\n }\n\n /**\n * Find all global assets (assets without a tenant).\n *\n * Routes through the shared tenant-global helper so it does not throw under\n * an active tenant context (an explicit `tenant_id IS NULL` filter would be\n * flagged as an isolation violation). (#1600)\n *\n * @returns Array of global assets\n */\n async findGlobal(): Promise<Asset[]> {\n const tenantId = getCurrentTenant()?.tenantId;\n const read = async () => this.list({ where: { tenantId: null } });\n return tenantId ? withTenantGlobalRead(tenantId, read) : read();\n }\n\n /**\n * Find assets belonging to a tenant plus all global assets.\n *\n * Fails closed if an active tenant context requests a different tenant's\n * rows; the admin/system path keeps the cross-tenant capability. (#1600)\n *\n * @param tenantId - The tenant ID to include\n * @returns Array of tenant-specific and global assets\n */\n async findWithGlobals(tenantId: string): Promise<Asset[]> {\n return withTenantGlobalRead(tenantId, async () =>\n this.list({ where: [[{ tenantId }], [{ tenantId: null }]] }),\n );\n }\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Tags (asset_tags → @happyvertical/smrt-tags Tag)\n // ─────────────────────────────────────────────────────────────────────────────\n\n private async assetTagCollection(): Promise<AssetTagCollection> {\n const { AssetTagCollection } = await import('./asset-tags');\n return AssetTagCollection.create({ db: this.db });\n }\n\n private async tagCollection(): Promise<TagCollection> {\n return TagCollection.create({ db: this.db });\n }\n\n private async requireAsset(assetId: string): Promise<Asset> {\n const asset = (await this.get({ id: assetId })) as Asset | null;\n if (!asset?.id) throw new Error(`Asset '${assetId}' not found`);\n return asset;\n }\n\n /**\n * Find the asset's tag for `tag` (a slug or a label) in the asset's own\n * tenant, optionally creating it. A tag of another tenant is never used.\n */\n private async resolveAssetTag(\n asset: Asset,\n tag: string,\n options: AssetTagInput & { create: boolean },\n ): Promise<Tag | null> {\n const slug = assetTagSlug(tag);\n if (!slug) throw new Error('A tag needs at least one letter or digit');\n const context = options.context ?? ASSET_TAG_CONTEXT;\n const tags = await this.tagCollection();\n const matches = (await tags.list({\n where: asset.tenantId\n ? { slug, context, tenantId: asset.tenantId }\n : { slug, context },\n limit: 10,\n })) as Tag[];\n const existing = matches.find(\n (candidate) => (candidate.tenantId ?? null) === (asset.tenantId ?? null),\n );\n if (existing || !options.create) return existing ?? null;\n\n const trimmed = tag.trim();\n return (await tags.create({\n slug,\n name:\n options.name?.trim() ||\n (trimmed !== slug ? trimmed : titleFromSlug(slug)),\n context,\n level: 0,\n tenantId: asset.tenantId ?? null,\n })) as Tag;\n }\n\n /**\n * Add a tag to an asset. `tag` is a slug (`town-hall`) or a label\n * (`Town hall`); the tag is found or created in the asset's tenant under\n * `context` (default {@link ASSET_TAG_CONTEXT}). Adding a tag twice is a\n * no-op. Returns the Tag.\n *\n * @throws when the asset does not exist (or is not visible in the active\n * tenant context)\n */\n async addTag(\n assetId: string,\n tag: string,\n options: AssetTagInput = {},\n ): Promise<Tag> {\n const asset = await this.requireAsset(assetId);\n const resolved = (await this.resolveAssetTag(asset, tag, {\n ...options,\n create: true,\n })) as Tag;\n const links = await this.assetTagCollection();\n const existing = await links.byLeft(asset.id as string, {\n tagId: resolved.id,\n });\n if (existing.length === 0) {\n await links.attach(asset.id as string, resolved.id as string, {\n tenantId: asset.tenantId ?? null,\n });\n }\n return resolved;\n }\n\n /**\n * Remove a tag (slug or label) from an asset. Unknown tags are ignored.\n * The Tag itself is kept; only the link goes.\n */\n async removeTag(\n assetId: string,\n tag: string,\n options: Pick<AssetTagInput, 'context'> = {},\n ): Promise<void> {\n const asset = await this.requireAsset(assetId);\n const resolved = await this.resolveAssetTag(asset, tag, {\n ...options,\n create: false,\n });\n if (!resolved?.id) return;\n const links = await this.assetTagCollection();\n await links.detach(asset.id as string, resolved.id);\n }\n\n /**\n * Make the asset's tags (in `context`) exactly `tags`: missing ones are\n * added, others in the same context removed. Returns the resulting Tags.\n */\n async setTags(\n assetId: string,\n tags: string[],\n options: Pick<AssetTagInput, 'context'> = {},\n ): Promise<Tag[]> {\n const asset = await this.requireAsset(assetId);\n const context = options.context ?? ASSET_TAG_CONTEXT;\n const wanted: Tag[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const slug = assetTagSlug(tag);\n if (!slug || seen.has(slug)) continue;\n seen.add(slug);\n wanted.push(await this.addTag(asset.id as string, tag, { context }));\n }\n const wantedIds = new Set(wanted.map((tag) => tag.id));\n const current = await asset.getTags();\n for (const tag of current) {\n if (tag.context === context && !wantedIds.has(tag.id)) {\n await this.removeTag(asset.id as string, tag.slug, { context });\n }\n }\n return wanted;\n }\n\n /**\n * Tags for many assets in two queries, keyed by asset id (assets without\n * tags map to an empty list). Useful for list views.\n */\n async getTagsForAssets(assetIds: string[]): Promise<Map<string, Tag[]>> {\n const result = new Map<string, Tag[]>();\n const ids = [...new Set(assetIds.filter(Boolean))];\n for (const id of ids) result.set(id, []);\n if (ids.length === 0) return result;\n\n const links = await this.assetTagCollection();\n const rows = (await links.list({\n where: { 'assetId in': ids },\n })) as AssetTag[];\n if (rows.length === 0) return result;\n\n const tags = await this.tagCollection();\n const tagById = new Map(\n (\n (await tags.listByIds([\n ...new Set(rows.map((row) => row.tagId)),\n ])) as Tag[]\n )\n .filter((tag) => tag.id)\n .map((tag) => [tag.id as string, tag]),\n );\n for (const row of rows) {\n const tag = tagById.get(row.tagId);\n if (tag) result.get(row.assetId)?.push(tag);\n }\n for (const list of result.values()) {\n list.sort((a, b) => a.name.localeCompare(b.name));\n }\n return result;\n }\n\n /**\n * Get all assets with a specific tag (slug or label), within the active\n * tenant context.\n */\n async getByTag(\n tag: string,\n options: Pick<AssetTagInput, 'context'> = {},\n ): Promise<Asset[]> {\n const slug = assetTagSlug(tag);\n if (!slug) return [];\n const tags = await this.tagCollection();\n const matches = (await tags.list({\n where: { slug, context: options.context ?? ASSET_TAG_CONTEXT },\n })) as Tag[];\n const tagIds = matches.map((match) => match.id).filter(Boolean) as string[];\n if (tagIds.length === 0) return [];\n\n const links = await this.assetTagCollection();\n const rows = (await links.list({\n where: { 'tagId in': tagIds },\n })) as AssetTag[];\n const assetIds = [...new Set(rows.map((row) => row.assetId))];\n if (assetIds.length === 0) return [];\n return (await this.listByIds(assetIds)) as Asset[];\n }\n\n /**\n * Get assets by type\n *\n * @param typeSlug - The asset type slug (e.g., 'image', 'video')\n * @returns Array of assets matching the type\n */\n async getByType(typeSlug: string): Promise<Asset[]> {\n return (await this.list({ where: { typeSlug } })) as Asset[];\n }\n\n /**\n * Get assets by status\n *\n * @param statusSlug - The asset status slug (e.g., 'published', 'draft')\n * @returns Array of assets matching the status\n */\n async getByStatus(statusSlug: string): Promise<Asset[]> {\n return (await this.list({ where: { statusSlug } })) as Asset[];\n }\n\n /**\n * Get assets by owner\n *\n * @param ownerProfileId - The profile ID of the owner\n * @returns Array of assets owned by this profile\n */\n async getByOwner(ownerProfileId: string): Promise<Asset[]> {\n return (await this.list({ where: { ownerProfileId } })) as Asset[];\n }\n\n /**\n * Create a new version of an existing asset\n *\n * @param primaryVersionId - The primary version ID (first version's ID)\n * @param newSourceUri - The new source URI for this version\n * @param updates - Optional additional updates\n * @returns The newly created asset version\n */\n async createNewVersion(\n primaryVersionId: string,\n newSourceUri: string,\n updates: Partial<Asset> = {},\n ): Promise<Asset> {\n // Get the current latest version\n const versions = await this.listVersions(primaryVersionId);\n if (versions.length === 0) {\n throw new Error(\n `No asset found with primary version ID: ${primaryVersionId}`,\n );\n }\n\n // Sort by version number to find the latest\n versions.sort((a, b) => b.version - a.version);\n const latestVersion = versions[0];\n const newVersionNumber = latestVersion.version + 1;\n\n // Give the new version a distinct, collision-free slug. The assets table is\n // unique on (slug, context, meta_type), so a reused slug would\n // upsert-overwrite an existing row instead of appending a version. Build\n // `<primary-slug>-v<n>` and verify it's not already taken by another version\n // in the chain OR by an unrelated asset — appending a disambiguator until\n // free. (The chain itself is tracked by `primaryVersionId`, not the slug.)\n const primary =\n versions.find((v) => v.id === primaryVersionId) ??\n versions[versions.length - 1];\n const baseSlug = String(primary?.slug ?? '') || 'asset';\n const takenInChain = new Set(\n versions.map((v) => v.slug).filter(Boolean) as string[],\n );\n let candidate = `${baseSlug}-v${newVersionNumber}`;\n for (let attempt = 1; ; attempt += 1) {\n const collides =\n takenInChain.has(candidate) ||\n (await this.get({ slug: candidate })) !== null;\n if (!collides) break;\n candidate = `${baseSlug}-v${newVersionNumber}-${attempt}`;\n }\n\n // Create new version\n return (await this.create({\n ...latestVersion,\n id: undefined, // Generate new ID\n slug: candidate,\n sourceUri: newSourceUri,\n version: newVersionNumber,\n primaryVersionId,\n createdAt: new Date(),\n updatedAt: new Date(),\n ...updates,\n })) as Asset;\n }\n\n /**\n * Get the latest version of an asset\n *\n * @param primaryVersionId - The primary version ID\n * @returns The latest version or null\n */\n async getLatestVersion(primaryVersionId: string): Promise<Asset | null> {\n const versions = await this.listVersions(primaryVersionId);\n if (versions.length === 0) return null;\n\n // Sort by version number descending\n versions.sort((a, b) => b.version - a.version);\n return versions[0];\n }\n\n /**\n * List all versions of an asset\n *\n * @param primaryVersionId - The primary version ID\n * @returns Array of all asset versions, ordered by version number\n */\n async listVersions(primaryVersionId: string): Promise<Asset[]> {\n // Use the collection's `list`/`get` (not raw `db.list`) so rows are properly\n // hydrated into `Asset` instances — `Object.assign(new Asset(), rawRow)`\n // would leave snake_case columns (`source_uri`, `primary_version_id`) on the\n // instance and never populate the camelCase fields. The chain is the rows\n // pointing at this primary plus the primary itself; the adapter has no OR, so\n // fetch both and dedupe by id.\n const [chained, primary] = await Promise.all([\n this.list({ where: { primaryVersionId } }) as Promise<Asset[]>,\n this.get({ id: primaryVersionId }) as Promise<Asset | null>,\n ]);\n\n const byId = new Map<string, Asset>();\n for (const asset of [...(primary ? [primary] : []), ...chained]) {\n if (asset?.id) byId.set(asset.id, asset);\n }\n\n const assets = Array.from(byId.values());\n assets.sort((a, b) => a.version - b.version);\n return assets;\n }\n\n /**\n * Get derivative assets of a source asset.\n *\n * Renamed from `getChildren(parentId)` in R3-D to match the rename of\n * the underlying column (`parent_id` → `source_asset_id`) and method\n * (`Asset.getChildren` → `Asset.getDerivatives`).\n *\n * @param sourceAssetId - The source asset ID\n * @returns Array of derivative assets\n */\n async getDerivatives(sourceAssetId: string): Promise<Asset[]> {\n return (await this.list({ where: { sourceAssetId } })) as Asset[];\n }\n\n /**\n * Get assets by MIME type pattern\n *\n * @param mimePattern - MIME type pattern (e.g., 'image/*', 'video/mp4')\n * @returns Array of matching assets\n */\n async getByMimeType(mimePattern: string): Promise<Asset[]> {\n const pattern = mimePattern.replace('*', '%');\n // Collection `list` (not raw `db.list`) so the rows hydrate into proper\n // `Asset` instances; the `<field> like` operator key is mapped to the column.\n return (await this.list({\n where: { 'mimeType like': pattern },\n })) as Asset[];\n }\n\n /**\n * Rollback to a previous version by creating a new version with the target's content.\n * Does NOT delete intermediate versions (safe rollback).\n *\n * @param primaryVersionId - The primary version ID of the version chain\n * @param targetVersion - The version number to rollback to\n * @returns The newly created asset version with content copied from target\n */\n async rollbackToVersion(\n primaryVersionId: string,\n targetVersion: number,\n ): Promise<Asset> {\n const versions = await this.listVersions(primaryVersionId);\n const target = versions.find((v) => v.version === targetVersion);\n\n if (!target) {\n throw new Error(\n `Version ${targetVersion} not found for asset ${primaryVersionId}`,\n );\n }\n\n // Create a new version that copies the target's sourceUri\n return await this.createNewVersion(primaryVersionId, target.sourceUri, {\n description: `Rollback to version ${targetVersion}`,\n } as Partial<Asset>);\n }\n\n /**\n * Get assets in a specific folder\n *\n * @param folderId - The folder ID to list contents for\n * @returns Array of assets in this folder\n */\n async getByFolder(folderId: string): Promise<Asset[]> {\n return (await this.list({ where: { folderId } })) as Asset[];\n }\n}\n","/**\n * AssetRuntime - shared asset runtime surface\n *\n * Bundles the pieces an app or agent needs to work with `smrt-assets`\n * consistently:\n *\n * - `AssetCollection` for records\n * - `AssetAssociationCollection` for generic/provenance links\n * - `AssetStore` for provider-agnostic byte storage\n * - a single `ProviderOptions` that wires storage in one place\n *\n * Agents that want to accept a shared asset runtime should take an\n * `AssetRuntime` (or `AssetRuntimeLike`) in their options, rather than\n * asking callers to hand-wire a collection + store.\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport type { Asset } from './asset';\nimport type { AssetAssociation } from './asset-association';\nimport { AssetAssociationCollection } from './asset-associations';\nimport {\n type AssetCapabilityName,\n type AssetCapabilityProvider,\n AssetCapabilitySkippedError,\n AssetCapabilityUnavailableError,\n type AssetExternalSourceRef,\n type AssetExternalSyncResult,\n type AssetNearbySearchInput,\n type AssetProcessResult,\n type AssetSearchResult,\n type AssetVariantRequest,\n type AssetVariantResult,\n type AssetWorkflowInput,\n type AssetWorkflowResult,\n} from './asset-capabilities';\nimport {\n ASSET_ROLES,\n type AssetExtractionStatus,\n type AssetRole,\n} from './asset-conventions';\nimport {\n AssetStore,\n type AssetStoreOptions,\n type ProviderOptions,\n type StoreOptions,\n} from './asset-store';\nimport { AssetCollection } from './assets';\n\n/**\n * DB configuration accepted by the runtime — mirrors the shape\n * `SmrtCollection.create({ db })` already accepts.\n */\nexport type AssetRuntimeDb = NonNullable<SmrtClassOptions['db']>;\n\n/**\n * Options for constructing an `AssetRuntime` via `createAssetRuntime()`.\n */\nexport interface AssetRuntimeOptions {\n /**\n * Database used for `AssetCollection` and `AssetAssociationCollection`.\n *\n * Accepts anything `SmrtCollection.create({ db })` accepts — a string\n * URL, a config object, or a live `DatabaseInterface`.\n */\n db: AssetRuntimeDb;\n\n /**\n * Storage provider for `AssetStore`.\n *\n * A string is treated as a local filesystem `basePath`; otherwise\n * forwarded to `@happyvertical/files`.\n */\n storage: ProviderOptions;\n\n /**\n * Optional behavior for the underlying `AssetStore`.\n *\n * Use this to provide a storage resolver while keeping the convenience\n * runtime factory.\n */\n storeOptions?: AssetStoreOptions;\n\n /**\n * Optional collection instance. If omitted, one is created from `db`.\n * Useful in tests or when the caller already has a configured\n * collection (e.g. from `ObjectRegistry`).\n */\n collection?: AssetCollection;\n\n /**\n * Optional associations collection. If omitted, one is created from `db`.\n */\n associations?: AssetAssociationCollection;\n\n /**\n * Optional asset capability providers.\n *\n * Providers let callers keep one app-facing asset runtime while\n * delegating processing, variant generation, search, external sync, or\n * workflow submission to local processors or external systems.\n */\n capabilityProviders?: AssetCapabilityProvider[];\n}\n\n/**\n * Structural shape of the runtime. Agents and serving helpers should\n * depend on `AssetRuntimeLike` rather than the concrete class so tests\n * can pass in a minimal object.\n */\nexport interface AssetRuntimeLike {\n readonly collection: AssetCollection;\n readonly associations: AssetAssociationCollection;\n readonly store: AssetStore;\n readonly capabilityProviders?: readonly AssetCapabilityProvider[];\n\n /** Persist original bytes and their Asset record through the shared store. */\n storeSourceAsset(\n name: string,\n data: Buffer,\n opts: StoreOptions,\n ): Promise<Asset>;\n}\n\n/**\n * Options for `AssetRuntime.storeDerivedAsset()`.\n */\nexport interface StoreDerivedAssetOptions\n extends Omit<StoreOptions, 'sourceAssetId'> {\n /**\n * Relationship role for the derivation link.\n *\n * Defaults to `ASSET_ROLES.DERIVATION_SOURCE` when `linkAssociation`\n * is true (the default). Set explicitly for roles like\n * `document_image` or `thumbnail` when a derivative has a more\n * specific semantic than \"came from\".\n */\n role?: AssetRole | string;\n\n /**\n * If true (default), also create an `AssetAssociation` record with\n * `assetId=source.id`, `metaType=<derivativeMetaType>`,\n * `metaId=<newAssetId>`, `role=<role>` so the provenance link is\n * queryable independent of `sourceAssetId`.\n *\n * Set to `false` if you only want the column-level `sourceAssetId`\n * derivation link.\n */\n linkAssociation?: boolean;\n\n /**\n * `metaType` string stored on the `AssetAssociation`. This describes\n * the *derivative* object (the target of `metaId`), not the source.\n *\n * Defaults to `'Asset'`. Callers whose derivatives are STI subclasses\n * — e.g. an `Image` produced from a PDF — should pass the subclass\n * name here so consumers can distinguish subtype derivatives from\n * plain assets. This matches the convention used by\n * `smrt-images`' `ImageDeriver.deriveWithAssociations()`.\n */\n derivativeMetaType?: string;\n}\n\n/**\n * Options for `AssetRuntime.linkDerivation()`.\n */\nexport interface LinkDerivationOptions {\n role?: AssetRole | string;\n /**\n * `metaType` describing the derivative object (target of `metaId`).\n * See `StoreDerivedAssetOptions.derivativeMetaType`.\n */\n derivativeMetaType?: string;\n}\n\n/**\n * Convenience runtime for `smrt-assets` callers. See the package\n * `CLAUDE.md` for the full \"source vs derived\" vocabulary.\n */\nexport class AssetRuntime implements AssetRuntimeLike {\n readonly capabilityProviders: AssetCapabilityProvider[];\n\n constructor(\n public readonly collection: AssetCollection,\n public readonly associations: AssetAssociationCollection,\n public readonly store: AssetStore,\n capabilityProviders: AssetCapabilityProvider[] = [],\n ) {\n this.capabilityProviders = [...capabilityProviders];\n }\n\n registerCapabilityProvider(provider: AssetCapabilityProvider): this {\n this.capabilityProviders.push(provider);\n return this;\n }\n\n private providersFor(\n capability: AssetCapabilityName,\n ): AssetCapabilityProvider[] {\n const providers = this.capabilityProviders.filter(\n (candidate) => typeof candidate[capability] === 'function',\n );\n if (providers.length === 0) {\n throw new AssetCapabilityUnavailableError(capability);\n }\n return providers;\n }\n\n async processAsset(\n asset: Asset,\n input: {\n variants?: AssetVariantRequest[];\n metadata?: Record<string, unknown>;\n } = {},\n ): Promise<AssetProcessResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('processAsset')) {\n const processAsset = provider.processAsset;\n if (!processAsset) continue;\n try {\n return await processAsset({\n runtime: this,\n asset,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError('processAsset', skipped?.message);\n }\n\n async ensureVariant(\n asset: Asset,\n request: AssetVariantRequest,\n ): Promise<AssetVariantResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('ensureVariant')) {\n const ensureVariant = provider.ensureVariant;\n if (!ensureVariant) continue;\n try {\n return await ensureVariant({\n runtime: this,\n asset,\n request,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'ensureVariant',\n skipped?.message,\n );\n }\n\n async searchNearbyAssets(\n input: Omit<AssetNearbySearchInput, 'runtime'>,\n ): Promise<AssetSearchResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('searchNearbyAssets')) {\n const searchNearbyAssets = provider.searchNearbyAssets;\n if (!searchNearbyAssets) continue;\n try {\n return await searchNearbyAssets({\n runtime: this,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'searchNearbyAssets',\n skipped?.message,\n );\n }\n\n async syncExternalAsset(\n asset: Asset,\n input: {\n externalId?: string | null;\n sourceRef?: AssetExternalSourceRef | null;\n metadata?: Record<string, unknown>;\n } = {},\n ): Promise<AssetExternalSyncResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('syncExternalAsset')) {\n const syncExternalAsset = provider.syncExternalAsset;\n if (!syncExternalAsset) continue;\n try {\n return await syncExternalAsset({\n runtime: this,\n asset,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'syncExternalAsset',\n skipped?.message,\n );\n }\n\n async submitAssetWorkflow(\n asset: Asset,\n input: Omit<AssetWorkflowInput, 'runtime' | 'asset'>,\n ): Promise<AssetWorkflowResult> {\n let skipped: Error | null = null;\n for (const provider of this.providersFor('submitAssetWorkflow')) {\n const submitAssetWorkflow = provider.submitAssetWorkflow;\n if (!submitAssetWorkflow) continue;\n try {\n return await submitAssetWorkflow({\n runtime: this,\n asset,\n ...input,\n });\n } catch (cause) {\n if (cause instanceof AssetCapabilitySkippedError) {\n skipped = cause;\n continue;\n }\n throw cause;\n }\n }\n throw new AssetCapabilityUnavailableError(\n 'submitAssetWorkflow',\n skipped?.message,\n );\n }\n\n /**\n * Create a new source asset with both a record and bytes on disk.\n *\n * This is the same as `AssetStore.store()`, but exposed on the runtime\n * so callers only need one handle.\n */\n storeSourceAsset(\n name: string,\n data: Buffer,\n opts: StoreOptions,\n ): Promise<Asset> {\n return this.store.store(name, data, opts);\n }\n\n /**\n * Create a derivative of `source`, persist its bytes, and optionally\n * record a provenance association.\n *\n * The new asset's `sourceAssetId` always points at `source.id`. When\n * `linkAssociation` is true (the default), the runtime also writes\n * an `AssetAssociation` so queries by role (e.g. \"all `document_image`\n * derivatives for this `source_document`\") work without scanning\n * `source_asset_id` chains.\n */\n async storeDerivedAsset(\n source: Asset,\n name: string,\n data: Buffer,\n opts: StoreDerivedAssetOptions,\n ): Promise<Asset> {\n if (!source.id) {\n throw new Error(\n 'storeDerivedAsset: source asset must be persisted (missing id)',\n );\n }\n\n const {\n role: rawRole,\n linkAssociation = true,\n derivativeMetaType = 'Asset',\n ...storeOpts\n } = opts;\n const role = rawRole ?? ASSET_ROLES.DERIVATION_SOURCE;\n\n const derived = await this.store.store(name, data, {\n ...storeOpts,\n sourceAssetId: source.id,\n });\n\n if (linkAssociation && derived.id) {\n await this.associations.attach(\n derivativeMetaType,\n derived.id,\n source.id,\n { role },\n );\n }\n\n return derived;\n }\n\n /**\n * Record a provenance association between an existing source asset\n * and an existing derivative asset without touching bytes.\n */\n async linkDerivation(\n source: Asset,\n derivative: Asset,\n opts: LinkDerivationOptions = {},\n ): Promise<AssetAssociation> {\n if (!source.id || !derivative.id) {\n throw new Error(\n 'linkDerivation: both source and derivative must be persisted (missing id)',\n );\n }\n const role = opts.role ?? ASSET_ROLES.DERIVATION_SOURCE;\n const derivativeMetaType = opts.derivativeMetaType ?? 'Asset';\n return this.associations.attach(\n derivativeMetaType,\n derivative.id,\n source.id,\n { role },\n );\n }\n\n /**\n * Update the standard extraction-status metadata on an asset's\n * `description` JSON sidecar. This is a thin convenience over the\n * convention in `asset-conventions.ts` — callers that store\n * metadata elsewhere can ignore it.\n *\n * **How existing descriptions are handled**:\n * - Empty / unset → fresh JSON object.\n * - Valid JSON object → merged into; existing keys preserved.\n * - Free-form prose or non-object JSON → preserved under the\n * reserved `text` key of the resulting object (e.g.\n * `{ text: \"original prose\", extractionStatus: \"...\" }`). No prose\n * is discarded.\n *\n * Callers that already use `text` for something else, or that need\n * an entirely separate metadata surface, should either round-trip\n * the JSON themselves or skip this helper — its only job is the\n * `extractionStatus` / `extractionError` / `extractedAt` triple.\n *\n * Error handling: when `status` transitions away from `failed`\n * without a new `extra.error`, the stale `extractionError` is\n * cleared so downstream consumers don't misread the current state.\n * When `status === 'succeeded'`, `extractedAt` is stamped to now\n * unless the caller provides one.\n */\n async setExtractionStatus(\n asset: Asset,\n status: AssetExtractionStatus,\n extra: { error?: string; extractedAt?: Date } = {},\n ): Promise<void> {\n const existing = parseDescriptionSidecar(asset.description);\n const next: Record<string, unknown> = {\n ...existing,\n extractionStatus: status,\n };\n if (extra.error !== undefined) {\n next.extractionError = extra.error;\n } else if (status !== 'failed') {\n // Transitioning out of a prior failure — drop the stale error\n // so consumers don't misread it as still-failing.\n delete next.extractionError;\n }\n if (status === 'succeeded') {\n next.extractedAt = (extra.extractedAt ?? new Date()).toISOString();\n }\n asset.description = JSON.stringify(next);\n await asset.save();\n }\n}\n\n/**\n * Factory — lazily creates a shared asset runtime from DB + storage\n * config. Initializes the store's filesystem adapter before returning.\n *\n * @example\n * ```ts\n * import { createAssetRuntime, ASSET_ROLES } from '@happyvertical/smrt-assets';\n *\n * const runtime = await createAssetRuntime({\n * db: { type: 'sqlite', url: 'app.db' },\n * storage: { type: 's3', bucket: 'my-app' },\n * });\n *\n * const pdf = await runtime.storeSourceAsset('agenda.pdf', bytes, {\n * mimeType: 'application/pdf',\n * typeSlug: 'document',\n * });\n *\n * await runtime.storeDerivedAsset(pdf, 'agenda-p1.png', pageBytes, {\n * mimeType: 'image/png',\n * typeSlug: 'image',\n * role: ASSET_ROLES.DOCUMENT_IMAGE,\n * });\n * ```\n */\nexport async function createAssetRuntime(\n options: AssetRuntimeOptions,\n): Promise<AssetRuntime> {\n const collection =\n options.collection ?? (await AssetCollection.create({ db: options.db }));\n const associations =\n options.associations ??\n (await AssetAssociationCollection.create({ db: options.db }));\n const store = await new AssetStore(\n options.storage,\n collection,\n options.storeOptions,\n ).initialize();\n return new AssetRuntime(\n collection,\n associations,\n store,\n options.capabilityProviders,\n );\n}\n\n/**\n * Parse the `description` field as a JSON sidecar object used by\n * `setExtractionStatus()`. Free-form prose and non-object JSON values\n * are preserved under the reserved `text` key so the helper is\n * non-destructive on assets whose descriptions were authored as\n * human-readable text.\n */\nfunction parseDescriptionSidecar(description: string): Record<string, unknown> {\n if (!description) return {};\n try {\n const parsed = JSON.parse(description);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n // Non-object JSON (number, string, array, null) — treat the whole\n // value as prose-equivalent and stash it under `text`.\n return { text: description };\n } catch {\n // Not JSON — preserve the original prose under `text`.\n return { text: description };\n }\n}\n","/**\n * Generic asset-serving contract\n *\n * Framework-agnostic helpers that turn a `smrt-assets` runtime plus\n * an asset id (or asset instance) into a standard `Response`. Apps\n * (SvelteKit, Hono, plain Node) can wire this once and stop inventing\n * agent-specific routes.\n *\n * `serveAsset()` returns a Web `Response` built against the global\n * `Response` available in Node 18+. If a runtime needs an older Node,\n * pass its own Response constructor via `responseCtor`.\n */\n\nimport type { Asset } from './asset';\nimport type { AssetRuntimeLike } from './asset-runtime';\n\n/**\n * Result of `resolveAssetForServing()` — lets advanced callers\n * stream bytes themselves while still reusing the access check.\n */\nexport interface ResolvedAssetBytes {\n asset: Asset;\n data: Buffer;\n contentType: string;\n filename: string;\n size: number;\n}\n\n/**\n * Options for `serveAsset()` and `resolveAssetForServing()`.\n */\nexport interface ServeAssetOptions {\n /** Shared asset runtime (collection + store). */\n runtime: AssetRuntimeLike;\n\n /**\n * Either an asset id to look up or a fully-loaded `Asset`\n * instance. Passing the instance skips the `collection.get()` call.\n */\n asset: string | Asset;\n\n /**\n * Tenant of the caller. When provided, the asset must either\n * belong to this tenant or be a global asset (`tenantId = null`)\n * or a 403 is returned.\n */\n tenantId?: string | null;\n\n /**\n * Optional `Content-Disposition` — `'inline'` (default) or\n * `'attachment'`. Use `'attachment'` for download links.\n */\n disposition?: 'inline' | 'attachment';\n\n /**\n * Extra headers merged into the 200 response. Does not affect\n * 403/404/500 responses.\n */\n headers?: Record<string, string>;\n\n /**\n * Optional override for the filename used in `Content-Disposition`.\n * Defaults to the basename of the asset's `sourceUri` or `name`.\n */\n filename?: string;\n\n /**\n * Optional access-check hook. Return `false` to short-circuit with a\n * 403 before bytes are loaded. Runs after the built-in tenant check.\n */\n canAccess?: (asset: Asset) => boolean | Promise<boolean>;\n\n /**\n * How to handle assets whose `sourceUri` is an `http(s)` URL rather\n * than a store-backed path.\n *\n * - `'error'` (default): treat a remote URI as an error (`500`). This\n * is the safe default — `sourceUri` is attacker-settable on many\n * asset-creation paths, so proxying it blindly is an SSRF vector\n * (fetching `169.254.169.254` cloud metadata, internal services,\n * etc.). Opt into `'proxy'` only for trusted sources.\n * - `'proxy'`: fetch the bytes via `globalThis.fetch` and return them\n * inline. Keeps the origin URL hidden from the client and preserves\n * the same tenant/access checks the local path gets. Guarded against\n * SSRF: the host must resolve to a public IP (private, loopback,\n * link-local, and cloud-metadata ranges are rejected), only\n * `http(s)` is allowed, and redirects are re-validated.\n * - `'redirect'`: return a `302` to `sourceUri` instead of fetching.\n * Skips the byte round-trip but exposes the origin URL.\n *\n * Anything that does not look like `http://` or `https://` is read\n * through the store as usual.\n */\n remoteMode?: 'proxy' | 'redirect' | 'error';\n\n /**\n * Custom `fetch` implementation for the `remoteMode: 'proxy'` path.\n * Defaults to `globalThis.fetch` (Node 18+).\n */\n fetchImpl?: typeof fetch;\n\n /**\n * Maximum number of bytes to buffer when `remoteMode: 'proxy'` fetches\n * a remote asset. Protects against memory-exhaustion DoS from a hostile\n * (or compromised) origin streaming an unbounded body. Defaults to\n * {@link DEFAULT_REMOTE_MAX_BYTES} (50 MiB).\n */\n remoteMaxBytes?: number;\n\n /**\n * Timeout in milliseconds for the `remoteMode: 'proxy'` fetch. Defaults\n * to {@link DEFAULT_REMOTE_TIMEOUT_MS} (10s). Prevents a slow/hung\n * origin from pinning the request indefinitely.\n */\n remoteTimeoutMs?: number;\n\n /**\n * Custom `Response`-like constructor, for runtimes that don't have\n * a global `Response` (pre-Node-18, workers with a custom shim).\n * Defaults to `globalThis.Response`.\n */\n responseCtor?: ResponseConstructor;\n}\n\ntype ResponseConstructor = typeof Response;\n\n/** Default cap (50 MiB) on bytes buffered from a proxied remote asset. */\nexport const DEFAULT_REMOTE_MAX_BYTES = 50 * 1024 * 1024;\n\n/** Default timeout (10s) for a proxied remote-asset fetch. */\nexport const DEFAULT_REMOTE_TIMEOUT_MS = 10_000;\n\n/** Maximum number of redirects to follow during a proxied fetch. */\nconst MAX_REMOTE_REDIRECTS = 5;\n\n/**\n * MIME types safe to serve `inline`. Anything else is forced to\n * `attachment` so a browser won't render attacker-controlled content\n * (HTML/SVG/XML) in the serving origin's context (stored XSS).\n */\nconst INLINE_SAFE_MIME_TYPES = new Set<string>([\n 'application/json',\n 'application/pdf',\n 'audio/aac',\n 'audio/mpeg',\n 'audio/ogg',\n 'audio/wav',\n 'audio/webm',\n 'image/avif',\n 'image/gif',\n 'image/jpeg',\n 'image/png',\n 'image/webp',\n 'text/csv',\n 'text/plain',\n 'video/mp4',\n 'video/ogg',\n 'video/webm',\n]);\n\n/** Parse the bare type (drop parameters like `; charset=...`) and lowercase. */\nfunction normalizeMimeType(raw: string): string {\n return (raw.split(';')[0] ?? '').trim().toLowerCase();\n}\n\n/** Whether a MIME type is on the inline-safe safelist. */\nfunction isInlineSafeMimeType(contentType: string): boolean {\n return INLINE_SAFE_MIME_TYPES.has(normalizeMimeType(contentType));\n}\n\nfunction isRemoteUri(uri: string): boolean {\n return /^https?:\\/\\//i.test(uri);\n}\n\n/**\n * Reject hostnames/IPs that point at private, loopback, link-local, or\n * cloud-metadata ranges — the core SSRF guard for proxied fetches.\n *\n * This is a literal-IP check; it does NOT perform DNS resolution, so a\n * hostname that resolves to a private IP is not caught here. Treat\n * `remoteMode: 'proxy'` as trusted-source-only and keep the default at\n * `'error'`; this guard is defense-in-depth for the literal-IP and obvious\n * cloud-metadata cases that make up the bulk of real SSRF payloads.\n */\nfunction isBlockedHost(hostname: string): boolean {\n let host = hostname.trim().toLowerCase();\n // Strip IPv6 brackets if present (URL.hostname keeps them off, but be safe).\n if (host.startsWith('[') && host.endsWith(']')) {\n host = host.slice(1, -1);\n }\n // Strip terminal dot(s): a FQDN like `localhost.` or\n // `metadata.google.internal.` is equivalent to the dotless form and would\n // otherwise sail past the exact-match checks below while still resolving to\n // loopback/metadata.\n host = host.replace(/\\.+$/, '');\n\n if (host === '' || host === 'localhost' || host.endsWith('.localhost')) {\n return true;\n }\n\n // Cloud metadata endpoints (AWS/GCP/Azure/OpenStack link-local + alibaba).\n if (\n host === '169.254.169.254' ||\n host === 'metadata.google.internal' ||\n host === 'metadata' ||\n host === '100.100.100.200'\n ) {\n return true;\n }\n\n // IPv4 literal?\n const ipv4 = host.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n if (ipv4) {\n const octets = ipv4.slice(1).map((o) => Number(o));\n if (octets.some((o) => o > 255)) return true; // malformed → block\n return isBlockedIpv4(octets as [number, number, number, number]);\n }\n\n // IPv6 literal?\n if (host.includes(':')) {\n return isBlockedIpv6(host);\n }\n\n return false;\n}\n\nfunction isBlockedIpv4(octets: [number, number, number, number]): boolean {\n const [a, b] = octets;\n if (a === 0) return true; // 0.0.0.0/8\n if (a === 10) return true; // 10.0.0.0/8 private\n if (a === 127) return true; // loopback\n if (a === 169 && b === 254) return true; // link-local + metadata\n if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12 private\n if (a === 192 && b === 168) return true; // 192.168.0.0/16 private\n if (a === 100 && b >= 64 && b <= 127) return true; // 100.64.0.0/10 CGNAT\n if (a >= 224) return true; // multicast + reserved\n return false;\n}\n\n/**\n * Expand an IPv6 literal (possibly with a `::` run and/or a trailing dotted\n * IPv4 tail) into its eight 16-bit hextets. Returns `null` when the input\n * isn't a parseable IPv6 address — callers treat unparseable as \"not a\n * recognised v6 literal\" and fall back to blocking via the malformed paths.\n */\nfunction expandIpv6(host: string): number[] | null {\n let h = host.toLowerCase();\n // Drop a zone id (e.g. `fe80::1%eth0`) — irrelevant for range checks.\n const pct = h.indexOf('%');\n if (pct !== -1) h = h.slice(0, pct);\n\n // A trailing dotted-quad IPv4 tail (`::ffff:127.0.0.1`) becomes two hextets.\n // Strip it off entirely (colon included) and remember the two hextets it\n // contributes to the explicit-tail side.\n let tailHextets: number[] = [];\n const lastColon = h.lastIndexOf(':');\n const tail = lastColon === -1 ? '' : h.slice(lastColon + 1);\n if (tail.includes('.')) {\n const m = tail.match(/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/);\n if (!m) return null;\n const octets = m.slice(1).map((o) => Number(o));\n if (octets.some((o) => o > 255)) return null;\n tailHextets = [(octets[0] << 8) | octets[1], (octets[2] << 8) | octets[3]];\n h = h.slice(0, lastColon); // drop ':a.b.c.d' so no dangling empty segment\n // `h` now ends just before the colon that preceded the dotted quad. If the\n // address was exactly `::a.b.c.d`, `h` is now `:` (the leading half of the\n // `::`); normalise that so the `::` detection below still fires.\n if (h === ':') h = '::';\n }\n\n const parseHextets = (parts: string[]): number[] | null => {\n const out: number[] = [];\n for (const p of parts) {\n if (p === '' || !/^[0-9a-f]{1,4}$/.test(p)) return null;\n out.push(Number.parseInt(p, 16));\n }\n return out;\n };\n\n const doubleColon = h.indexOf('::');\n let head: number[] | null;\n let midTail: number[] | null;\n if (doubleColon !== -1) {\n if (h.indexOf('::', doubleColon + 1) !== -1) return null; // only one `::`\n const before = h.slice(0, doubleColon);\n const after = h.slice(doubleColon + 2);\n head = parseHextets(before === '' ? [] : before.split(':'));\n midTail = parseHextets(after === '' ? [] : after.split(':'));\n } else {\n head = parseHextets(h === '' ? [] : h.split(':'));\n midTail = [];\n }\n if (head === null || midTail === null) return null;\n\n const explicit = [...head, ...midTail, ...tailHextets];\n if (doubleColon !== -1) {\n if (explicit.length > 7) return null; // `::` must stand for ≥1 hextet\n const fill = 8 - explicit.length;\n return [...head, ...new Array(fill).fill(0), ...midTail, ...tailHextets];\n }\n if (explicit.length !== 8) return null;\n return explicit;\n}\n\nfunction isBlockedIpv6(host: string): boolean {\n const hextets = expandIpv6(host);\n if (hextets === null) return true; // unparseable v6 literal → block\n\n const [h0, h1, h2, h3, h4, h5, h6, h7] = hextets;\n\n const embeddedV4 = (): [number, number, number, number] => [\n (h6 >> 8) & 0xff,\n h6 & 0xff,\n (h7 >> 8) & 0xff,\n h7 & 0xff,\n ];\n\n // ::/128 unspecified and ::1/128 loopback.\n if (h0 === 0 && h1 === 0 && h2 === 0 && h3 === 0 && h4 === 0 && h5 === 0) {\n if (h6 === 0 && h7 === 0) return true; // ::\n if (h6 === 0 && h7 === 1) return true; // ::1\n // ::/96 IPv4-compatible (deprecated). Node normalises `::127.0.0.1` to\n // `::7f00:1`, which lands here — decode and re-check the embedded v4.\n return isBlockedIpv4(embeddedV4());\n }\n\n // ::ffff:0:0/96 — IPv4-mapped. Decode the embedded v4 (covers both the\n // dotted `::ffff:127.0.0.1` and hex `::ffff:7f00:1` forms, since both expand\n // to the same hextets) and run it through the v4 blocklist.\n if (\n h0 === 0 &&\n h1 === 0 &&\n h2 === 0 &&\n h3 === 0 &&\n h4 === 0 &&\n h5 === 0xffff\n ) {\n return isBlockedIpv4(embeddedV4());\n }\n\n // fe80::/10 link-local (fe80–febf): top 10 bits == 1111111010.\n if ((h0 & 0xffc0) === 0xfe80) return true;\n // fec0::/10 site-local (deprecated, but block defensively).\n if ((h0 & 0xffc0) === 0xfec0) return true;\n // fc00::/7 unique-local (fc00–fdff): top 7 bits == 1111110.\n if ((h0 & 0xfe00) === 0xfc00) return true;\n // ff00::/8 multicast.\n if ((h0 & 0xff00) === 0xff00) return true;\n\n return false;\n}\n\n/**\n * Validate a remote URL for proxying: only `http(s)` and only public hosts.\n * Throws `AssetServeError(502)` on a rejected target. The distinct `message`s\n * (invalid URL vs unsupported scheme vs blocked host) are server-side only —\n * `serveAsset` returns a single generic 502 body for all of them so the client\n * can't probe internal reachability via status OR body differences (S5 #1396).\n */\nfunction assertSafeRemoteUrl(rawUrl: string): URL {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new AssetServeError('Invalid remote asset URL', 502);\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new AssetServeError('Unsupported remote asset scheme', 502);\n }\n if (isBlockedHost(url.hostname)) {\n throw new AssetServeError('Remote asset host not allowed', 502);\n }\n return url;\n}\n\n/**\n * Fetch a remote asset with SSRF protection, a byte cap, and a timeout.\n *\n * Redirects are followed manually (`redirect: 'manual'`) so each hop's\n * `Location` is re-validated through {@link assertSafeRemoteUrl} — otherwise\n * a public host could 302 to `169.254.169.254`.\n */\nasync function fetchRemoteAsset(\n startUrl: string,\n fetchImpl: typeof fetch,\n maxBytes: number,\n timeoutMs: number,\n): Promise<{ data: Buffer; contentType: string | undefined }> {\n let current = assertSafeRemoteUrl(startUrl);\n\n for (let hop = 0; hop <= MAX_REMOTE_REDIRECTS; hop++) {\n // Use AbortController + setTimeout rather than AbortSignal.timeout():\n // this helper deliberately supports pre-Node-18 runtimes via\n // `responseCtor`/`fetchImpl`, where AbortSignal.timeout() may be missing\n // (S5 #1396, Copilot review). Clear the timer in `finally` so it never\n // leaks past the fetch.\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let response: Response;\n try {\n response = await fetchImpl(current.toString(), {\n redirect: 'manual',\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n // Manual redirect handling — re-validate each Location hop.\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (!location) {\n throw new AssetServeError(\n 'Remote asset redirect missing Location',\n 502,\n );\n }\n current = assertSafeRemoteUrl(new URL(location, current).toString());\n continue;\n }\n\n if (!response.ok) {\n // Keep the upstream status in `message` for server-side logs only; the\n // client gets the generic opaque 502 body so the origin's status can't\n // leak through the response (S5 #1396, Copilot review).\n throw new AssetServeError(\n `Remote asset upstream returned ${response.status}`,\n 502,\n );\n }\n\n const data = await readBodyWithCap(response, maxBytes);\n return {\n data,\n contentType: response.headers.get('content-type') ?? undefined,\n };\n }\n\n throw new AssetServeError('Too many remote asset redirects', 502);\n}\n\n/**\n * Read a response body into a Buffer, aborting if it exceeds `maxBytes`.\n * Streams when possible so an oversized body is rejected without first\n * buffering the whole thing; falls back to a post-hoc length check when the\n * response exposes no readable stream (e.g. test doubles).\n */\nasync function readBodyWithCap(\n response: Response,\n maxBytes: number,\n): Promise<Buffer> {\n const declared = response.headers.get('content-length');\n if (declared && Number(declared) > maxBytes) {\n throw new AssetServeError('Remote asset exceeds size limit', 502);\n }\n\n const body = response.body;\n if (body && typeof body.getReader === 'function') {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) {\n total += value.byteLength;\n if (total > maxBytes) {\n await reader.cancel().catch(() => {});\n throw new AssetServeError('Remote asset exceeds size limit', 502);\n }\n chunks.push(value);\n }\n }\n return Buffer.concat(chunks.map((c) => Buffer.from(c)));\n }\n\n // No stream available — buffer then enforce the cap.\n const buf = Buffer.from(await response.arrayBuffer());\n if (buf.byteLength > maxBytes) {\n throw new AssetServeError('Remote asset exceeds size limit', 502);\n }\n return buf;\n}\n\n/**\n * Resolve an asset + bytes for serving, enforcing the same tenant\n * and access checks as `serveAsset()` but returning the parts so\n * callers can render their own framework response.\n *\n * Throws `AssetServeError` with a status on any error so callers can\n * map to their HTTP layer.\n */\nexport async function resolveAssetForServing(\n options: ServeAssetOptions,\n): Promise<ResolvedAssetBytes> {\n const {\n runtime,\n asset: assetOrId,\n tenantId,\n canAccess,\n disposition: _disposition,\n } = options;\n\n const asset =\n typeof assetOrId === 'string'\n ? ((await runtime.collection.get({ id: assetOrId })) as Asset | null)\n : assetOrId;\n\n if (!asset) {\n throw new AssetServeError('Asset not found', 404);\n }\n\n if (\n tenantId !== undefined &&\n asset.tenantId !== null &&\n asset.tenantId !== tenantId\n ) {\n throw new AssetServeError('Asset not visible to this tenant', 403);\n }\n\n if (canAccess) {\n const allowed = await canAccess(asset);\n if (!allowed) {\n throw new AssetServeError('Asset access denied', 403);\n }\n }\n\n // Default to 'error': `sourceUri` is attacker-settable on many\n // asset-creation paths, so proxying it blindly is an SSRF vector. Callers\n // serving trusted sources opt into 'proxy' explicitly (S5 #1396).\n const remoteMode = options.remoteMode ?? 'error';\n let data: Buffer;\n let remoteContentType: string | undefined;\n\n if (isRemoteUri(asset.sourceUri)) {\n if (remoteMode === 'error') {\n throw new AssetServeError('Remote asset not supported', 500);\n }\n if (remoteMode === 'redirect') {\n throw new RedirectToSourceUri(asset.sourceUri);\n }\n // Proxy — fetch the bytes from the origin URL with SSRF guards,\n // a byte cap, and a timeout.\n const fetchImpl = options.fetchImpl ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new AssetServeError(\n 'No fetch implementation available for remote asset',\n 500,\n );\n }\n const maxBytes = options.remoteMaxBytes ?? DEFAULT_REMOTE_MAX_BYTES;\n const timeoutMs = options.remoteTimeoutMs ?? DEFAULT_REMOTE_TIMEOUT_MS;\n try {\n const result = await fetchRemoteAsset(\n asset.sourceUri,\n fetchImpl,\n maxBytes,\n timeoutMs,\n );\n data = result.data;\n remoteContentType = result.contentType;\n } catch (err) {\n if (err instanceof AssetServeError) throw err;\n // Log the underlying error so operators can debug without\n // leaking paths/bucket names to the HTTP client.\n console.error('serveAsset: remote fetch failed', err);\n throw new AssetServeError('Failed to fetch remote asset', 502);\n }\n } else {\n try {\n data = await runtime.store.read(asset);\n } catch (err) {\n console.error('serveAsset: store read failed', err);\n // Generic, non-leaking client body (no paths/bucket names).\n throw new AssetServeError(\n 'Failed to read asset bytes',\n 500,\n 'Failed to read asset bytes',\n );\n }\n }\n\n const contentType =\n asset.mimeType || remoteContentType || 'application/octet-stream';\n const filename = options.filename ?? deriveFilename(asset);\n\n return {\n asset,\n data,\n contentType,\n filename,\n size: data.byteLength,\n };\n}\n\n/**\n * Internal signal for `remoteMode: 'redirect'`. Caught by `serveAsset`\n * and turned into a 302; `resolveAssetForServing` re-throws so direct\n * callers can inspect it via `err.location`.\n */\nclass RedirectToSourceUri extends Error {\n constructor(public readonly location: string) {\n super('Asset stored at remote URL');\n this.name = 'RedirectToSourceUri';\n }\n}\n\n/**\n * Serve an asset as a standard Web `Response`.\n *\n * Returns:\n * - `404` when the asset id doesn't resolve\n * - `403` when the tenant mismatches or `canAccess` denies\n * - `302` when `remoteMode: 'redirect'` and the asset's `sourceUri`\n * is an `http(s)` URL\n * - `502` when `remoteMode: 'proxy'` and the origin fetch fails\n * - `500` when bytes fail to read (file missing, provider error)\n * - `200` with the raw bytes, `Content-Type`, `Content-Length`, and\n * `Content-Disposition` set otherwise.\n *\n * The resulting `Response` is framework-agnostic: SvelteKit `+server.ts`\n * endpoints, Hono, and plain Node HTTP all accept it via their Web\n * interop layer.\n */\nexport async function serveAsset(\n options: ServeAssetOptions,\n): Promise<Response> {\n const Ctor =\n options.responseCtor ?? (globalThis.Response as ResponseConstructor);\n if (!Ctor) {\n throw new Error(\n 'serveAsset: no Response constructor available. Pass options.responseCtor or run on Node 18+.',\n );\n }\n\n try {\n const { data, contentType, filename, size } =\n await resolveAssetForServing(options);\n\n // Force `attachment` for any type not on the inline-safe safelist so a\n // browser won't render attacker-controlled HTML/SVG/XML in the serving\n // origin's context (stored XSS). An explicit `disposition: 'attachment'`\n // is always honoured; a requested `'inline'` is downgraded for unsafe\n // types. Pair with `X-Content-Type-Options: nosniff` so the browser\n // can't MIME-sniff a safelisted type into something executable (S5 #1396).\n const requestedDisposition = options.disposition ?? 'inline';\n const disposition =\n requestedDisposition === 'attachment' || isInlineSafeMimeType(contentType)\n ? requestedDisposition\n : 'attachment';\n\n // Spread caller-supplied headers FIRST so the safety headers below always\n // win. Otherwise a caller could pass `Content-Disposition: inline` or\n // `X-Content-Type-Options: ` and defeat the forced-attachment + nosniff\n // protections for an XSS-prone type (S5 #1396 follow-up). HTTP header names\n // are case-insensitive, so drop any case-variant of a protected name from\n // the caller's set before applying the canonical ones.\n const PROTECTED_HEADERS = new Set([\n 'content-type',\n 'content-length',\n 'content-disposition',\n 'x-content-type-options',\n ]);\n const headers: Record<string, string> = {};\n for (const [name, value] of Object.entries(options.headers ?? {})) {\n if (!PROTECTED_HEADERS.has(name.toLowerCase())) {\n headers[name] = value;\n }\n }\n headers['Content-Type'] = contentType;\n headers['Content-Length'] = String(size);\n headers['Content-Disposition'] = buildContentDisposition(\n disposition,\n filename,\n );\n headers['X-Content-Type-Options'] = 'nosniff';\n\n // `new Response(body)` accepts Buffer at runtime (it's a Uint8Array\n // subclass), but the strongly-typed `BodyInit` name lives in\n // `lib.dom.d.ts`, which the package-build tsconfig doesn't pull in.\n // Cast through `unknown` instead of referencing a DOM type so `.d.ts`\n // generation works without needing DOM lib.\n return new Ctor(\n data as unknown as ConstructorParameters<ResponseConstructor>[0],\n {\n status: 200,\n headers,\n },\n );\n } catch (err) {\n if (err instanceof RedirectToSourceUri) {\n return new Ctor(null, {\n status: 302,\n headers: { Location: err.location },\n });\n }\n if (err instanceof AssetServeError) {\n // Return the opaque, status-keyed body — never `err.message`, which can\n // carry the specific rejection reason (SSRF cause, upstream status) and\n // would give clients an oracle about WHY a target was rejected\n // (S5 #1396, Copilot review). The specific reason is logged below.\n if (err.status >= 500) {\n console.error('serveAsset: request failed', err.status, err.message);\n }\n return new Ctor(err.clientMessage, {\n status: err.status,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n // Don't surface the underlying error to clients — it can leak\n // paths/bucket names/provider details. Log for operators.\n console.error('serveAsset: unexpected error', err);\n return new Ctor('Internal error serving asset', {\n status: 500,\n headers: { 'Content-Type': 'text/plain; charset=utf-8' },\n });\n }\n}\n\n/**\n * Generic, status-keyed client-facing bodies. The `message` of an\n * {@link AssetServeError} can carry a specific reason for server-side logs,\n * but the body returned to the HTTP client is one of these opaque strings so\n * a caller can't probe internal reachability or upstream state via the\n * response text — e.g. distinguishing \"blocked host\" from \"bad scheme\", or\n * reading the upstream origin's status (S5 #1396, Copilot review).\n */\nfunction defaultClientMessage(status: number): string {\n switch (status) {\n case 403:\n return 'Forbidden';\n case 404:\n return 'Not found';\n case 502:\n return 'Bad gateway';\n default:\n return 'Internal error serving asset';\n }\n}\n\n/**\n * Error raised by `resolveAssetForServing()` with the HTTP status\n * callers should use when rendering their own response.\n *\n * `message` is the specific, server-side reason — safe to log, never sent to\n * the client. `clientMessage` is the opaque body {@link serveAsset} returns to\n * the HTTP client; it defaults to a generic, status-keyed string so the\n * specific reason can't leak (SSRF rejection cause, upstream status, etc.).\n */\nexport class AssetServeError extends Error {\n /** Opaque body safe to return to the HTTP client. */\n public readonly clientMessage: string;\n\n constructor(\n message: string,\n public readonly status: number,\n clientMessage?: string,\n ) {\n super(message);\n this.name = 'AssetServeError';\n this.clientMessage = clientMessage ?? defaultClientMessage(status);\n }\n}\n\nfunction deriveFilename(asset: Asset): string {\n if (asset.name) return asset.name;\n const fromUri = extractBasename(asset.sourceUri);\n if (fromUri) return fromUri;\n return asset.id ?? 'asset';\n}\n\nfunction extractBasename(uri: string): string {\n if (!uri) return '';\n const noScheme = uri.replace(/^[a-z][a-z0-9+\\-.]*:\\/\\//i, '');\n const last = noScheme.split('/').pop() ?? '';\n return last;\n}\n\nfunction sanitizeDispositionFilename(filename: string): string {\n // Strip C0/DEL/C1 controls and replace isolated UTF-16 surrogates so\n // UTF-8 encoding cannot throw. Iteration preserves valid surrogate pairs.\n // Replace quotes, backslashes, and path separators in both name forms.\n let stripped = '';\n for (const ch of filename) {\n const code = ch.charCodeAt(0);\n if (code > 0x1f && (code < 0x7f || code > 0x9f)) {\n stripped +=\n ch.length === 1 && code >= 0xd800 && code <= 0xdfff ? '\\uFFFD' : ch;\n }\n }\n const safe = stripped.replace(/[\"\\\\/]/g, '_').trim();\n return safe || 'asset';\n}\n\nfunction buildContentDisposition(\n disposition: 'inline' | 'attachment',\n filename: string,\n): string {\n const sanitized = sanitizeDispositionFilename(filename);\n // Keep the legacy quoted parameter ASCII-safe for native Web Headers.\n // The extended parameter retains the sanitized Unicode download name.\n const fallback = Array.from(sanitized, (ch) =>\n ch.charCodeAt(0) <= 0x7e ? ch : '_',\n ).join('');\n // RFC 8187 attr-char excludes these characters left by encodeURIComponent.\n const encoded = encodeURIComponent(sanitized).replace(\n /['()*]/g,\n (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n return `${disposition}; filename=\"${fallback}\"; filename*=UTF-8''${encoded}`;\n}\n","/**\n * AssetStatusCollection - Collection manager for AssetStatus instances\n *\n * Manages asset status lookup table with common status initialization\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AssetStatus } from './asset-status';\n\nexport class AssetStatusCollection extends SmrtCollection<AssetStatus> {\n static readonly _itemClass = AssetStatus;\n\n /**\n * Get or create an asset status by slug\n *\n * @param slug - The asset status slug\n * @param name - The display name (defaults to slug)\n * @param description - Optional description\n * @returns The existing or newly created AssetStatus\n */\n async getOrCreate(\n slug: string,\n name?: string,\n description?: string,\n ): Promise<AssetStatus> {\n const existing = await this.list({ where: { slug }, limit: 1 });\n if (existing.length > 0) {\n return existing[0];\n }\n\n return await this.create({\n slug,\n name: name || slug,\n description,\n });\n }\n\n /**\n * Initialize common asset statuses\n *\n * Creates standard asset statuses if they don't exist:\n * - draft\n * - published\n * - archived\n * - deleted\n */\n async initializeCommonStatuses(): Promise<void> {\n await this.getOrCreate('draft', 'Draft', 'Work in progress, not yet ready');\n await this.getOrCreate('published', 'Published', 'Live and available');\n await this.getOrCreate(\n 'archived',\n 'Archived',\n 'No longer active but preserved',\n );\n await this.getOrCreate('deleted', 'Deleted', 'Marked for deletion');\n }\n}\n","/**\n * AssetTag model - Links an asset to a `@happyvertical/smrt-tags` Tag.\n *\n * Backs the `asset_tags` join table that `AssetCollection.addTag()` /\n * `removeTag()` / `getByTag()` and `Asset.getTags()` / `hasTag()` use. Before\n * this model existed those methods wrote raw rows to an `asset_tags` table\n * that no manifest declared, so no migration ever created it.\n *\n * The link is keyed by the Tag's id (not its slug): a tag's natural key is\n * `(tenant, slug, context)`, so a slug alone is ambiguous across tenants and\n * contexts. Both sides cascade: deleting the asset or the tag removes the\n * link. Rows carry the asset's tenant.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n\nexport interface AssetTagOptions extends SmrtObjectOptions {\n assetId?: string;\n tagId?: string;\n tenantId?: string | null;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableName: 'asset_tags',\n conflictColumns: ['asset_id', 'tag_id'],\n api: false,\n mcp: false,\n cli: false,\n})\nexport class AssetTag extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** FK to Asset.id — the link goes with the asset. */\n @foreignKey('Asset', { required: true, onDelete: 'CASCADE' })\n assetId = '';\n\n /** FK to smrt-tags Tag.id — the link goes with the tag. */\n @crossPackageRef('@happyvertical/smrt-tags:Tag', {\n required: true,\n onDelete: 'CASCADE',\n })\n tagId = '';\n\n constructor(options: AssetTagOptions = {}) {\n super(options);\n if (options.assetId) this.assetId = options.assetId;\n if (options.tagId) this.tagId = options.tagId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n}\n","/**\n * AssetTagCollection - junction collection for `asset_tags` (Asset ↔ Tag).\n *\n * Left side is the asset, right side the Tag id. The table has no position\n * column, so neither ordering nor `setLinks` index assignment applies.\n */\n\nimport { SmrtJunction, smrt } from '@happyvertical/smrt-core';\nimport { AssetTag } from './asset-tag';\n\n// Empty decorator so the scanner detects the collection without clobbering\n// the item class's api/mcp/cli config (see AssetAssociationCollection).\n@smrt()\nexport class AssetTagCollection extends SmrtJunction<AssetTag> { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n static readonly _itemClass = AssetTag;\n protected leftField = 'assetId';\n protected rightField = 'tagId';\n protected sortField: string | null = null;\n protected positionField: string | null = null;\n}\n","/**\n * AssetTypeCollection - Collection manager for AssetType instances\n *\n * Manages asset type lookup table with common type initialization\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { AssetType } from './asset-type';\n\nexport class AssetTypeCollection extends SmrtCollection<AssetType> {\n static readonly _itemClass = AssetType;\n\n /**\n * Get or create an asset type by slug\n *\n * @param slug - The asset type slug\n * @param name - The display name (defaults to slug)\n * @param description - Optional description\n * @returns The existing or newly created AssetType\n */\n async getOrCreate(\n slug: string,\n name?: string,\n description?: string,\n ): Promise<AssetType> {\n const existing = await this.list({ where: { slug }, limit: 1 });\n if (existing.length > 0) {\n return existing[0];\n }\n\n return await this.create({\n slug,\n name: name || slug,\n description,\n });\n }\n\n /**\n * Initialize common asset types\n *\n * Creates standard asset types if they don't exist:\n * - image\n * - video\n * - document\n * - audio\n *\n * Note: `folder` was removed in R3-D. Folders are no longer an asset\n * subtype — they live on their own `folders` table. See `folder.ts`.\n */\n async initializeCommonTypes(): Promise<void> {\n await this.getOrCreate(\n 'image',\n 'Image',\n 'Image files (JPEG, PNG, GIF, etc.)',\n );\n await this.getOrCreate(\n 'video',\n 'Video',\n 'Video files (MP4, AVI, MOV, etc.)',\n );\n await this.getOrCreate(\n 'document',\n 'Document',\n 'Document files (PDF, DOCX, TXT, etc.)',\n );\n await this.getOrCreate(\n 'audio',\n 'Audio',\n 'Audio files (MP3, WAV, AAC, etc.)',\n );\n }\n}\n","/**\n * Folder model — hierarchical container for organizing assets.\n *\n * Folders are now their own SmrtHierarchical model with their own `folders`\n * table (R3-D). Prior to R3-D, Folder was an STI subclass of Asset and\n * folder rows lived in the `assets` table with `type_slug='folder'`. The\n * column `assets.parent_id` did double duty: structural hierarchy for\n * folder rows, derivation source for asset rows.\n *\n * Splitting Folder onto its own table eliminates the overload and lets\n * Folder participate in the framework-wide `parentId` convention as a\n * SmrtHierarchical subclass (Place, Event, Tag, Account, Zone).\n *\n * Migration path (4 steps; see changeset):\n *\n * 1. Create `folders` table.\n * 2. Copy folder rows from `assets` (`type_slug='folder'`) into `folders`.\n * 3. On `assets`, rename `parent_id` → `source_asset_id` (handled in\n * asset.ts schema generator output) and copy values for non-folder\n * rows.\n * 4. Delete folder rows from `assets`.\n *\n * @see Asset.sourceAssetId for the renamed derivation pointer.\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtHierarchical,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { FolderOptions } from './types';\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n api: { include: ['list', 'get', 'create', 'update', 'delete'] },\n mcp: { include: ['list', 'get', 'create', 'update'] },\n cli: { skipApiCheck: true },\n})\nexport class Folder extends SmrtHierarchical { static __smrtPackage__ = \"@happyvertical/smrt-assets\";\n // Tenant isolation (optional — folders can be global or tenant-specific,\n // mirroring Asset's tenancy mode).\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n // Core fields. `parentId` is inherited from SmrtHierarchical.\n @field({ description: 'A recognizable name for this group of files.' })\n name = '';\n // `slug` is inherited as an accessor from SmrtObject.\n @field({\n description:\n 'Explain the purpose of this folder and which files belong here.',\n })\n description = '';\n @crossPackageRef('@happyvertical/smrt-profiles:Profile')\n @field({\n description: 'The person or organization responsible for this folder.',\n })\n ownerProfileId: string | null = null;\n\n // Timestamps\n createdAt = new Date();\n updatedAt = new Date();\n\n constructor(options: FolderOptions = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.slug !== undefined) this.slug = options.slug;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.parentId !== undefined)\n this.parentId = options.parentId ?? null;\n if (options.ownerProfileId !== undefined)\n this.ownerProfileId = options.ownerProfileId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.createdAt) this.createdAt = options.createdAt;\n if (options.updatedAt) this.updatedAt = options.updatedAt;\n }\n\n // Hierarchy traversal (getParent / getChildren / getAncestors /\n // getDescendants / getHierarchy / moveTo) provided by SmrtHierarchical.\n\n /**\n * Look up a folder by slug.\n */\n static async getBySlug(_slug: string): Promise<Folder | null> {\n // Will be auto-implemented by SMRT\n return null;\n }\n}\n","/**\n * FolderCollection — collection manager for Folder instances.\n *\n * Provides tree-oriented helpers that wrap the inherited SmrtHierarchical\n * traversal methods, plus folder-content helpers (`getContents`,\n * `moveAsset`) for working with the assets in a folder.\n *\n * Post R3-D, Folder lives on its own `folders` table; tree queries no\n * longer have to filter by `type_slug='folder'`.\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport type { Asset } from './asset';\nimport type { AssetCollection } from './assets';\nimport { Folder } from './folder';\n\nexport class FolderCollection extends SmrtCollection<Folder> {\n static readonly _itemClass = Folder;\n\n /**\n * Get the folder tree starting from an optional root.\n *\n * When `rootId` is omitted, returns top-level folders (those with no\n * parent). When `rootId` is provided, returns all descendants of that\n * folder via the inherited SmrtHierarchical BFS traversal — same\n * cycle-safe, one-query-per-depth behaviour as Place/Event.\n *\n * @param rootId - Optional root folder ID; if omitted, returns top-level folders\n * @returns Array of folders (flat list; use parentId to reconstruct tree)\n */\n async getTree(rootId?: string): Promise<Folder[]> {\n if (!rootId) {\n return (await this.list({\n where: { parentId: null },\n orderBy: 'name ASC',\n })) as Folder[];\n }\n\n // Walk the tree BFS-by-level, sorting siblings by name within each\n // batch. This preserves two contracts from the pre-R3-D\n // implementation that `SmrtHierarchical.getDescendants` alone does\n // not — global name-sort would interleave parents and grandchildren\n // (root → Zulu → Alpha would surface as `[Alpha, Zulu]`, putting\n // grandchild before parent), and storage-order BFS would render\n // unsorted siblings.\n const result: Folder[] = [];\n const queue: string[] = [rootId];\n const visited = new Set<string>([rootId]);\n\n while (queue.length > 0) {\n const currentId = queue.shift()!;\n const children = (await this.list({\n where: { parentId: currentId },\n orderBy: 'name ASC',\n })) as Folder[];\n\n for (const child of children) {\n if (child.id && !visited.has(child.id)) {\n visited.add(child.id);\n result.push(child);\n queue.push(child.id);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get the path from root to a given folder (ancestors + self).\n *\n * @param folderId - The folder ID to get the path for\n * @returns Array of folders from root to the given folder (inclusive)\n */\n async getPath(folderId: string): Promise<Folder[]> {\n const folder = (await this.get({ id: folderId })) as Folder | null;\n if (!folder) return [];\n const ancestors = await folder.getAncestors();\n return [...ancestors, folder];\n }\n\n /**\n * Get all assets that are direct children of a folder.\n *\n * Folder membership is recorded on `Asset.folderId`, not on the folder\n * itself, so this is delegated to the asset collection.\n *\n * @param folderId - The folder ID\n * @param assetCollection - An AssetCollection instance for querying\n * @returns Array of assets in this folder\n */\n async getContents(\n folderId: string,\n assetCollection: AssetCollection,\n ): Promise<Asset[]> {\n return (await assetCollection.list({\n where: { folderId },\n })) as Asset[];\n }\n\n /**\n * Move an asset into a folder (or out, by passing `null`).\n *\n * @param asset - The asset to move\n * @param folderId - The target folder ID (or null to move to root)\n */\n async moveAsset(asset: Asset, folderId: string | null): Promise<void> {\n asset.folderId = folderId;\n await asset.save();\n }\n}\n","export type MediaSupportFileVisibility =\n | 'visible'\n | 'hidden-retained'\n | 'drop-after-extract';\n\nexport interface MediaBundleFileDescriptor {\n path: string;\n relativePath?: string;\n name?: string;\n mimeType?: string;\n sizeBytes?: number;\n sha256?: string;\n modifiedAt?: Date | string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface MediaBundleGpsTrackPoint {\n tSeconds: number;\n recordedAt?: Date | string | null;\n latitude: number;\n longitude: number;\n altitude?: number | null;\n heading?: number | null;\n speedMps?: number | null;\n sourceFilePath?: string;\n}\n\nexport interface MediaBundleNormalizedMetadata {\n captureTime?: Date | string | null;\n width?: number;\n height?: number;\n durationMs?: number;\n mimeType?: string;\n gpsTrack?: MediaBundleGpsTrackPoint[];\n raw?: Record<string, unknown>;\n private?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface MediaBundleSupportFileInspection {\n file: MediaBundleFileDescriptor;\n role: string;\n relationship: string;\n visibility?: MediaSupportFileVisibility;\n metadata?: MediaBundleNormalizedMetadata;\n}\n\nexport interface MediaBundleInspection {\n handlerId: string;\n handlerVersion: string;\n formatFamily: string;\n primary: MediaBundleFileDescriptor;\n supportFiles: MediaBundleSupportFileInspection[];\n metadata: MediaBundleNormalizedMetadata;\n capabilities: string[];\n warnings: string[];\n errors: string[];\n raw?: Record<string, unknown>;\n}\n\nexport type MediaBundleInspectionLike = MediaBundleInspection;\n\nexport interface PersistMediaBundleAssetInput {\n file: MediaBundleFileDescriptor;\n role: 'primary' | 'support';\n typeSlug?: string;\n parentAssetId?: string | null;\n relationship?: string;\n visibility?: MediaSupportFileVisibility;\n metadata: MediaBundleNormalizedMetadata;\n inspection: MediaBundleInspection;\n}\n\nexport interface PersistMediaBundleAssociationInput {\n primaryAssetId: string;\n supportAssetId: string;\n relationship: string;\n visibility: MediaSupportFileVisibility;\n metadata?: MediaBundleNormalizedMetadata;\n}\n\nexport interface PersistMediaBundleMetadataArtifactInput {\n primaryAssetId: string;\n inspection: MediaBundleInspection;\n}\n\nexport interface SmrtMediaBundlePersistenceAdapter {\n upsertAsset(input: PersistMediaBundleAssetInput): Promise<{ id: string }>;\n associateSupportFile?(\n input: PersistMediaBundleAssociationInput,\n ): Promise<void>;\n writeMetadataArtifact?(\n input: PersistMediaBundleMetadataArtifactInput,\n ): Promise<{ id: string } | null | undefined>;\n replaceGpsTrack?(\n primaryAssetId: string,\n points: MediaBundleGpsTrackPoint[],\n ): Promise<number>;\n}\n\nexport interface PersistMediaBundleInspectionOptions {\n primaryTypeSlug?: string;\n supportTypeSlug?: string;\n supportVisibility?: MediaSupportFileVisibility;\n writeGpsTrack?: boolean;\n writeMetadataArtifact?: boolean;\n}\n\nexport interface PersistMediaBundleInspectionResult {\n primaryAssetId: string;\n supportAssetIds: string[];\n metadataArtifactId?: string;\n gpsTrackPointCount: number;\n warnings: string[];\n}\n\nexport async function persistMediaBundleInspection(\n adapter: SmrtMediaBundlePersistenceAdapter,\n inspection: MediaBundleInspection,\n options: PersistMediaBundleInspectionOptions = {},\n): Promise<PersistMediaBundleInspectionResult> {\n const warnings = [...inspection.warnings];\n const primary = await adapter.upsertAsset({\n file: inspection.primary,\n role: 'primary',\n typeSlug: options.primaryTypeSlug,\n metadata: inspection.metadata,\n inspection,\n });\n\n const supportAssetIds: string[] = [];\n for (const support of inspection.supportFiles) {\n // Support metadata is retained on support assets; canonical GPS persistence\n // is attached to the primary media through inspection.metadata.gpsTrack.\n const visibility =\n support.visibility ?? options.supportVisibility ?? 'hidden-retained';\n if (visibility === 'drop-after-extract') continue;\n\n const supportAsset = await adapter.upsertAsset({\n file: support.file,\n role: 'support',\n typeSlug: options.supportTypeSlug,\n parentAssetId: primary.id,\n relationship: support.relationship,\n visibility,\n metadata: support.metadata ?? {},\n inspection,\n });\n supportAssetIds.push(supportAsset.id);\n\n await adapter.associateSupportFile?.({\n primaryAssetId: primary.id,\n supportAssetId: supportAsset.id,\n relationship: support.relationship,\n visibility,\n metadata: support.metadata,\n });\n }\n\n let metadataArtifactId: string | undefined;\n if (\n options.writeMetadataArtifact !== false &&\n adapter.writeMetadataArtifact\n ) {\n const artifact = await adapter.writeMetadataArtifact({\n primaryAssetId: primary.id,\n inspection,\n });\n metadataArtifactId = artifact?.id;\n }\n\n let gpsTrackPointCount = 0;\n const gpsTrack = inspection.metadata.gpsTrack ?? [];\n if (options.writeGpsTrack !== false && gpsTrack.length > 0) {\n if (adapter.replaceGpsTrack) {\n gpsTrackPointCount = await adapter.replaceGpsTrack(primary.id, gpsTrack);\n } else {\n warnings.push('adapter cannot persist gps track');\n }\n }\n\n return {\n primaryAssetId: primary.id,\n supportAssetIds,\n metadataArtifactId,\n gpsTrackPointCount,\n warnings,\n };\n}\n","import type { SmrtCollectionOptions } from '@happyvertical/smrt-core';\nimport { withSystemContext } from '@happyvertical/smrt-tenancy';\nimport type { Asset } from './asset';\nimport { AssetCollection } from './assets';\n\nexport const OWNED_ASSET_RELATIONSHIP_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nexport interface AssetOwnerRecord {\n getAssets(relationship?: string): Promise<Asset[]>;\n addAsset(\n asset: Asset,\n relationship?: string,\n sortOrder?: number,\n ): Promise<void>;\n removeAsset(assetId: string, relationship?: string): Promise<void>;\n}\n\nexport interface AssetOwnerCollection<OwnerType extends AssetOwnerRecord> {\n get(where: { id: string }): Promise<OwnerType | null>;\n}\n\nexport function assertValidOwnedAssetRelationship(relationship: string): void {\n if (!OWNED_ASSET_RELATIONSHIP_PATTERN.test(relationship)) {\n throw new Error(\n `Invalid relationship type \"${relationship}\"; must start with a letter or underscore and contain only letters, digits, and underscores`,\n );\n }\n}\n\nexport function assertValidOwnedAssetSortOrder(sortOrder: number): void {\n if (!Number.isInteger(sortOrder) || sortOrder < 0 || sortOrder > 2147483647) {\n throw new Error(\n `Invalid sortOrder \"${sortOrder}\"; must be a non-negative integer`,\n );\n }\n}\n\n/**\n * Refuse to link an asset to an owner (place, profile, …) of another tenant.\n *\n * The asset's tenant is read from STORAGE by id under system context, never\n * taken from the caller's object: `new Asset({ id: foreignId })` or a stale\n * copy carries no (or the wrong) tenant and would otherwise pass. Rules:\n *\n * - a tenant owner may link its own assets and global (tenantless) ones;\n * - a global owner may link only global assets — a tenant's asset linked to\n * a global owner would be listed to every tenant through the global link;\n * - an asset id with no stored row is refused.\n *\n * @param ownerLabel - plain noun for the error (\"place\", \"profile\")\n */\nexport async function assertAssetLinkable(\n db: SmrtCollectionOptions['db'],\n assetId: string,\n ownerTenantId: string | null | undefined,\n ownerLabel: string,\n): Promise<void> {\n const assets = await AssetCollection.create({ db });\n const [stored] = await withSystemContext(async () =>\n assets.listByIds([assetId]),\n );\n if (!stored) {\n throw new Error(`Cannot associate asset ${assetId}: it does not exist`);\n }\n const assetTenant = stored.tenantId ? String(stored.tenantId) : null;\n const ownerTenant = ownerTenantId ? String(ownerTenantId) : null;\n if (assetTenant === null || assetTenant === ownerTenant) return;\n throw new Error(\n ownerTenant === null\n ? `Cannot associate a tenant's asset with a shared ${ownerLabel}`\n : `Cannot associate an asset from another tenant with this ${ownerLabel}`,\n );\n}\n\nexport async function resolveOwnedAssetsById(\n db: SmrtCollectionOptions['db'],\n assetIds: string[],\n tenantId?: string | null,\n): Promise<Asset[]> {\n if (assetIds.length === 0) {\n return [];\n }\n\n const assets = await AssetCollection.create({ db });\n const resolved = tenantId\n ? await withSystemContext(async () => assets.listByIds(assetIds))\n : await assets.listByIds(assetIds);\n const visibleAssets = tenantId\n ? resolved.filter(\n (asset) => asset.tenantId === tenantId || asset.tenantId === null,\n )\n : resolved;\n const assetsById = new Map(\n visibleAssets\n .filter((asset) => asset.id)\n .map((asset) => [asset.id as string, asset]),\n );\n\n return assetIds\n .map((assetId) => assetsById.get(assetId))\n .filter(Boolean) as Asset[];\n}\n\nasync function getOwnerRecord<OwnerType extends AssetOwnerRecord>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerId: string,\n): Promise<OwnerType | null> {\n return collection.get({ id: ownerId });\n}\n\nexport async function getOwnedAssetsFromCollection<\n OwnerType extends AssetOwnerRecord,\n>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerId: string,\n relationship?: string,\n): Promise<Asset[]> {\n const owner = await getOwnerRecord(collection, ownerId);\n if (!owner) {\n return [];\n }\n\n return owner.getAssets(relationship);\n}\n\nexport async function addOwnedAssetFromCollection<\n OwnerType extends AssetOwnerRecord,\n>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerType: string,\n ownerId: string,\n asset: Asset,\n relationship = 'attachment',\n sortOrder = 0,\n): Promise<void> {\n const owner = await getOwnerRecord(collection, ownerId);\n if (!owner) {\n throw new Error(`${ownerType} '${ownerId}' not found`);\n }\n\n await owner.addAsset(asset, relationship, sortOrder);\n}\n\nexport async function removeOwnedAssetFromCollection<\n OwnerType extends AssetOwnerRecord,\n>(\n collection: AssetOwnerCollection<OwnerType>,\n ownerType: string,\n ownerId: string,\n assetId: string,\n relationship?: string,\n): Promise<void> {\n const owner = await getOwnerRecord(collection, ownerId);\n if (!owner) {\n throw new Error(`${ownerType} '${ownerId}' not found`);\n }\n\n await owner.removeAsset(assetId, relationship);\n}\n"],"mappings":"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"}
|