@happyvertical/smrt-tenancy 0.55.16 → 0.55.18

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 CHANGED
@@ -3,7 +3,7 @@ import { n as createExpressMiddleware, r as createCliContext, t as createSvelteK
3
3
  import { C as unregisterTenantScopedClass, S as registerTenantScopedConstructor, _ as clearTenantScopedRegistry, a as setupTestTenancy, b as isTenantScopedClass, c as disableTenancy, d as queryGlobal, f as queryWithGlobals, g as auditTenantScopedRegistrations, h as isTenancyEnabled, i as resetTenancy, l as enableTenancy, m as runTenantScopedEntryPoint, n as assertTenantIsolationViolation, o as testTenantIsolation, p as withTenantGlobalRead, r as createTestTenantContext, s as createTenantInterceptor, t as assertTenantContextRequired, u as assertTenantReadAllowed, v as getAllTenantScopedClasses, x as registerTenantScopedClass, y as getTenantScopedConfig } from "./chunks/testing-BiOkIaZK.js";
4
4
  import { ObjectRegistry, SmrtCollection, SmrtObject, applyPendingDecoratorRegistrations, crossPackageRef, field, isEmbeddedDatabase, isPostgresDatabase, registerCompatibleFieldDecorator, smrt, withEmbeddedWriteTransaction } from "@happyvertical/smrt-core";
5
5
  //#region src/__smrt-register__.ts
6
- ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-tenancy","packageVersion":"0.55.16","objects":{"@happyvertical/smrt-tenancy:BillingRelationship":{"name":"billingrelationship","className":"BillingRelationship","qualifiedName":"@happyvertical/smrt-tenancy:BillingRelationship","collection":"billingrelationships","filePath":"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts","packageName":"@happyvertical/smrt-tenancy","fields":{"childTenantId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:Tenant","_meta":{"required":true}},"resellerTenantId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:Tenant","_meta":{"required":true}},"billingOwnerMode":{"type":"text","required":false,"default":"self","enum":["self","reseller"]}},"methods":{},"decoratorConfig":{"tableName":"_smrt_billing_relationships","conflictColumns":["child_tenant_id"],"api":false,"cli":false,"mcp":false},"extends":"SmrtObject","exportName":"BillingRelationship","collectionExportName":"BillingRelationshipCollection","validationRules":[{"field":"childTenantId","rule":"required","fieldType":"crossPackageRef"},{"field":"resellerTenantId","rule":"required","fieldType":"crossPackageRef"}],"schema":{"tableName":"_smrt_billing_relationships","ddl":"CREATE TABLE IF NOT EXISTS \\"_smrt_billing_relationships\\" (\\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 \\"child_tenant_id\\" UUID NOT NULL,\\n \\"reseller_tenant_id\\" UUID NOT NULL,\\n \\"billing_owner_mode\\" TEXT DEFAULT 'self'\\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"},"child_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reseller_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"billing_owner_mode":{"type":"TEXT","notNull":false,"unique":false,"default":"self"}},"indexes":[{"name":"_smrt_billing_relationships_child_tenant_id_idx","columns":["child_tenant_id"],"unique":true},{"name":"_smrt_billing_relationships_slug_context_idx","columns":["slug","context"]},{"name":"_smrt_billing_relationships_created_at_idx","columns":["created_at"]},{"name":"_smrt_billing_relationships_reseller_tenant_id_idx","columns":["reseller_tenant_id"]}],"version":"4d7f410a"},"importPath":"@happyvertical/smrt-tenancy"},"@happyvertical/smrt-tenancy:BillingRelationshipCollection":{"name":"billingrelationshipcollection","className":"BillingRelationshipCollection","qualifiedName":"@happyvertical/smrt-tenancy:BillingRelationshipCollection","collection":"billingrelationships","filePath":"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts","packageName":"@happyvertical/smrt-tenancy","fields":{},"methods":{},"decoratorConfig":{"tableName":"_smrt_billing_relationships","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"BillingRelationship","exportName":"BillingRelationshipCollection","collectionExportName":"BillingRelationshipCollectionCollection","schema":{"tableName":"_smrt_billing_relationships","ddl":"CREATE TABLE IF NOT EXISTS \\"_smrt_billing_relationships\\" (\\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 \\"child_tenant_id\\" UUID NOT NULL,\\n \\"reseller_tenant_id\\" UUID NOT NULL,\\n \\"billing_owner_mode\\" TEXT DEFAULT 'self'\\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"},"child_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reseller_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"billing_owner_mode":{"type":"TEXT","notNull":false,"unique":false,"default":"self"}},"indexes":[{"name":"_smrt_billing_relationships_child_tenant_id_idx","columns":["child_tenant_id"],"unique":true},{"name":"_smrt_billing_relationships_slug_context_idx","columns":["slug","context"]},{"name":"_smrt_billing_relationships_created_at_idx","columns":["created_at"]},{"name":"_smrt_billing_relationships_reseller_tenant_id_idx","columns":["reseller_tenant_id"]}],"version":"4d7f410a"},"importPath":"@happyvertical/smrt-tenancy"}},"moduleType":"smrt","smrtDependencies":["@happyvertical/smrt-core"],"browser":{"status":"server-only","issues":["#3624"],"reason":"node:async_hooks"},"stampsConstructors":true}`));
6
+ ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-tenancy","packageVersion":"0.55.18","objects":{"@happyvertical/smrt-tenancy:BillingRelationship":{"name":"billingrelationship","className":"BillingRelationship","qualifiedName":"@happyvertical/smrt-tenancy:BillingRelationship","collection":"billingrelationships","filePath":"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts","packageName":"@happyvertical/smrt-tenancy","fields":{"childTenantId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:Tenant","_meta":{"required":true}},"resellerTenantId":{"type":"crossPackageRef","required":true,"related":"@happyvertical/smrt-users:Tenant","_meta":{"required":true}},"billingOwnerMode":{"type":"text","required":false,"default":"self","enum":["self","reseller"]}},"methods":{},"decoratorConfig":{"tableName":"_smrt_billing_relationships","conflictColumns":["child_tenant_id"],"api":false,"cli":false,"mcp":false},"extends":"SmrtObject","exportName":"BillingRelationship","collectionExportName":"BillingRelationshipCollection","validationRules":[{"field":"childTenantId","rule":"required","fieldType":"crossPackageRef"},{"field":"resellerTenantId","rule":"required","fieldType":"crossPackageRef"}],"schema":{"tableName":"_smrt_billing_relationships","ddl":"CREATE TABLE IF NOT EXISTS \\"_smrt_billing_relationships\\" (\\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 \\"child_tenant_id\\" UUID NOT NULL,\\n \\"reseller_tenant_id\\" UUID NOT NULL,\\n \\"billing_owner_mode\\" TEXT DEFAULT 'self'\\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"},"child_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reseller_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"billing_owner_mode":{"type":"TEXT","notNull":false,"unique":false,"default":"self"}},"indexes":[{"name":"_smrt_billing_relationships_child_tenant_id_idx","columns":["child_tenant_id"],"unique":true},{"name":"_smrt_billing_relationships_slug_context_idx","columns":["slug","context"]},{"name":"_smrt_billing_relationships_created_at_idx","columns":["created_at"]},{"name":"_smrt_billing_relationships_reseller_tenant_id_idx","columns":["reseller_tenant_id"]}],"version":"4d7f410a"},"importPath":"@happyvertical/smrt-tenancy"},"@happyvertical/smrt-tenancy:BillingRelationshipCollection":{"name":"billingrelationshipcollection","className":"BillingRelationshipCollection","qualifiedName":"@happyvertical/smrt-tenancy:BillingRelationshipCollection","collection":"billingrelationships","filePath":"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts","packageName":"@happyvertical/smrt-tenancy","fields":{},"methods":{},"decoratorConfig":{"tableName":"_smrt_billing_relationships","api":false,"mcp":false,"cli":false},"extends":"SmrtCollection","extendsTypeArg":"BillingRelationship","exportName":"BillingRelationshipCollection","collectionExportName":"BillingRelationshipCollectionCollection","schema":{"tableName":"_smrt_billing_relationships","ddl":"CREATE TABLE IF NOT EXISTS \\"_smrt_billing_relationships\\" (\\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 \\"child_tenant_id\\" UUID NOT NULL,\\n \\"reseller_tenant_id\\" UUID NOT NULL,\\n \\"billing_owner_mode\\" TEXT DEFAULT 'self'\\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"},"child_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"reseller_tenant_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":true,"unique":false},"billing_owner_mode":{"type":"TEXT","notNull":false,"unique":false,"default":"self"}},"indexes":[{"name":"_smrt_billing_relationships_child_tenant_id_idx","columns":["child_tenant_id"],"unique":true},{"name":"_smrt_billing_relationships_slug_context_idx","columns":["slug","context"]},{"name":"_smrt_billing_relationships_created_at_idx","columns":["created_at"]},{"name":"_smrt_billing_relationships_reseller_tenant_id_idx","columns":["reseller_tenant_id"]}],"version":"4d7f410a"},"importPath":"@happyvertical/smrt-tenancy"}},"moduleType":"smrt","smrtDependencies":["@happyvertical/smrt-core"],"browser":{"status":"server-only","issues":["#3624"],"reason":"node:async_hooks"},"stampsConstructors":true}`));
7
7
  //#endregion
8
8
  //#region src/billing-relationship.ts
9
9
  var __defProp = Object.defineProperty;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/__smrt-register__.ts","../src/billing-relationship.ts","../src/decorators.ts","../src/fields.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-tenancy\\\",\\\"packageVersion\\\":\\\"0.55.16\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-tenancy:BillingRelationship\\\":{\\\"name\\\":\\\"billingrelationship\\\",\\\"className\\\":\\\"BillingRelationship\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-tenancy:BillingRelationship\\\",\\\"collection\\\":\\\"billingrelationships\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-tenancy\\\",\\\"fields\\\":{\\\"childTenantId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":true,\\\"related\\\":\\\"@happyvertical/smrt-users:Tenant\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"resellerTenantId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":true,\\\"related\\\":\\\"@happyvertical/smrt-users:Tenant\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"billingOwnerMode\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"self\\\",\\\"enum\\\":[\\\"self\\\",\\\"reseller\\\"]}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"conflictColumns\\\":[\\\"child_tenant_id\\\"],\\\"api\\\":false,\\\"cli\\\":false,\\\"mcp\\\":false},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"BillingRelationship\\\",\\\"collectionExportName\\\":\\\"BillingRelationshipCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"childTenantId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"crossPackageRef\\\"},{\\\"field\\\":\\\"resellerTenantId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"crossPackageRef\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"_smrt_billing_relationships\\\\\\\" (\\\\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 \\\\\\\"child_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"reseller_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"billing_owner_mode\\\\\\\" TEXT DEFAULT 'self'\\\\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\\\"},\\\"child_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"reseller_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"billing_owner_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"self\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"_smrt_billing_relationships_child_tenant_id_idx\\\",\\\"columns\\\":[\\\"child_tenant_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"_smrt_billing_relationships_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_reseller_tenant_id_idx\\\",\\\"columns\\\":[\\\"reseller_tenant_id\\\"]}],\\\"version\\\":\\\"4d7f410a\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-tenancy\\\"},\\\"@happyvertical/smrt-tenancy:BillingRelationshipCollection\\\":{\\\"name\\\":\\\"billingrelationshipcollection\\\",\\\"className\\\":\\\"BillingRelationshipCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-tenancy:BillingRelationshipCollection\\\",\\\"collection\\\":\\\"billingrelationships\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-tenancy\\\",\\\"fields\\\":{},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"BillingRelationship\\\",\\\"exportName\\\":\\\"BillingRelationshipCollection\\\",\\\"collectionExportName\\\":\\\"BillingRelationshipCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"_smrt_billing_relationships\\\\\\\" (\\\\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 \\\\\\\"child_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"reseller_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"billing_owner_mode\\\\\\\" TEXT DEFAULT 'self'\\\\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\\\"},\\\"child_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"reseller_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"billing_owner_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"self\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"_smrt_billing_relationships_child_tenant_id_idx\\\",\\\"columns\\\":[\\\"child_tenant_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"_smrt_billing_relationships_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_reseller_tenant_id_idx\\\",\\\"columns\\\":[\\\"reseller_tenant_id\\\"]}],\\\"version\\\":\\\"4d7f410a\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-tenancy\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3624\\\"],\\\"reason\\\":\\\"node:async_hooks\\\"},\\\"stampsConstructors\\\":true}\"));\n","/** Reseller parentage and billing ownership, independent of user permissions. */\nimport {\n crossPackageRef,\n field,\n isEmbeddedDatabase,\n isPostgresDatabase,\n type SmrtClassOptions,\n SmrtCollection,\n SmrtObject,\n smrt,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport {\n getTenantId,\n isSuperAdminBypass,\n isSystemContext,\n TenantIsolationError,\n} from './context.js';\n\nexport type BillingOwnerMode = 'self' | 'reseller';\n\nexport interface BillingRelationshipView {\n childTenantId: string;\n resellerTenantId: string;\n billingOwnerMode: BillingOwnerMode;\n billingOwnerTenantId: string;\n}\n\nexport type BillingRelationshipAction = 'read' | 'manage';\n\nexport interface BillingRelationshipServiceOptions extends SmrtClassOptions {\n /** Resolve tenant IDs against the host tenant directory. */\n tenantExists: (tenantId: string) => Promise<boolean>;\n /** Optional host permission check for access beyond the built-in context rules. */\n authorize?: (request: {\n action: BillingRelationshipAction;\n childTenantId: string;\n resellerTenantId?: string;\n }) => Promise<boolean>;\n}\n\nexport class BillingRelationshipError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'INVALID_TENANT'\n | 'INVALID_OWNER_MODE'\n | 'TENANT_NOT_FOUND'\n | 'CIRCULAR_RELATIONSHIP',\n ) {\n super(message);\n this.name = 'BillingRelationshipError';\n }\n}\n\n@smrt({\n tableName: '_smrt_billing_relationships',\n conflictColumns: ['child_tenant_id'],\n api: false,\n cli: false,\n mcp: false,\n})\nexport class BillingRelationship extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-tenancy\";\n @crossPackageRef('@happyvertical/smrt-users:Tenant')\n @field({ required: true })\n childTenantId: string = '';\n @crossPackageRef('@happyvertical/smrt-users:Tenant')\n @field({ required: true })\n resellerTenantId: string = '';\n billingOwnerMode: BillingOwnerMode = 'self';\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n this.childTenantId = canonicalTenantId(this.childTenantId);\n this.resellerTenantId = canonicalTenantId(this.resellerTenantId);\n assertMode(this.billingOwnerMode);\n if (this.childTenantId === this.resellerTenantId) {\n throw new BillingRelationshipError(\n 'A tenant cannot resell to itself.',\n 'CIRCULAR_RELATIONSHIP',\n );\n }\n }\n}\n\nexport class BillingRelationshipCollection extends SmrtCollection<BillingRelationship> {\n static readonly _itemClass = BillingRelationship;\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction assertTenantId(id: string): void {\n if (typeof id !== 'string' || !UUID_PATTERN.test(id)) {\n throw new BillingRelationshipError(\n 'Tenant IDs must be UUIDs.',\n 'INVALID_TENANT',\n );\n }\n}\n\nfunction canonicalTenantId(id: string): string {\n assertTenantId(id);\n return id.toLowerCase();\n}\n\nfunction assertMode(mode: BillingOwnerMode): void {\n if (mode !== 'self' && mode !== 'reseller') {\n throw new BillingRelationshipError(\n 'Billing owner mode must be self or reseller.',\n 'INVALID_OWNER_MODE',\n );\n }\n}\n\nfunction view(row: BillingRelationship): BillingRelationshipView {\n const childTenantId = canonicalTenantId(row.childTenantId);\n const resellerTenantId = canonicalTenantId(row.resellerTenantId);\n assertMode(row.billingOwnerMode);\n return {\n childTenantId,\n resellerTenantId,\n billingOwnerMode: row.billingOwnerMode,\n billingOwnerTenantId:\n row.billingOwnerMode === 'reseller' ? resellerTenantId : childTenantId,\n };\n}\n\n/**\n * The one-row-per-child billing graph. The host supplies tenant existence and\n * any additional authorization; absent a host authorizer, only an explicit\n * system context can mutate relationships.\n */\nexport class BillingRelationshipService {\n private constructor(\n private readonly relationships: BillingRelationshipCollection,\n private readonly options: BillingRelationshipServiceOptions,\n ) {}\n\n static async create(\n options: BillingRelationshipServiceOptions,\n ): Promise<BillingRelationshipService> {\n if (typeof options.tenantExists !== 'function') {\n throw new Error('Billing relationships require a tenantExists resolver.');\n }\n const relationships = await BillingRelationshipCollection.create(options);\n if (/^(?:https?|libsql):\\/\\//iu.test(relationships.db.url ?? '')) {\n throw new Error(\n 'Billing relationships require a local SQL or PostgreSQL database with serialized transactions.',\n );\n }\n if (!relationships.db.transaction) {\n throw new Error('Billing relationships require database transactions.');\n }\n return new BillingRelationshipService(relationships, options);\n }\n\n /** A tenant without a relationship always pays for itself. */\n async resolveBillingOwner(childTenantId: string): Promise<string> {\n childTenantId = canonicalTenantId(childTenantId);\n const row = await this.relationships.get({ childTenantId });\n await this.assertReadable(childTenantId, row?.resellerTenantId);\n await this.assertExists(childTenantId);\n if (row) await this.assertExists(row.resellerTenantId);\n return row ? view(row).billingOwnerTenantId : childTenantId;\n }\n\n async getRelationship(\n childTenantId: string,\n ): Promise<BillingRelationshipView | null> {\n childTenantId = canonicalTenantId(childTenantId);\n const row = await this.relationships.get({ childTenantId });\n await this.assertReadable(childTenantId, row?.resellerTenantId);\n await this.assertExists(childTenantId);\n if (row) await this.assertExists(row.resellerTenantId);\n return row ? view(row) : null;\n }\n\n /** List direct children billed to an owner, with no collection page limit. */\n async listChildrenBilledTo(ownerTenantId: string): Promise<string[]> {\n ownerTenantId = canonicalTenantId(ownerTenantId);\n await this.assertReadable(ownerTenantId);\n await this.assertExists(ownerTenantId);\n const result = await this.relationships.db.query(\n 'SELECT child_tenant_id FROM _smrt_billing_relationships WHERE reseller_tenant_id = ? AND billing_owner_mode = ? ORDER BY child_tenant_id',\n ownerTenantId,\n 'reseller',\n );\n return result.rows.map((row) => {\n return canonicalTenantId(String(row.child_tenant_id));\n });\n }\n\n /** Create or replace parentage and its default billing owner in one row. */\n async setRelationship(input: {\n childTenantId: string;\n resellerTenantId: string;\n billingOwnerMode: BillingOwnerMode;\n }): Promise<BillingRelationshipView> {\n const childTenantId = canonicalTenantId(input.childTenantId);\n const resellerTenantId = canonicalTenantId(input.resellerTenantId);\n const { billingOwnerMode } = input;\n assertMode(billingOwnerMode);\n await this.assertManage(childTenantId, resellerTenantId);\n await this.assertExists(childTenantId);\n await this.assertExists(resellerTenantId);\n if (childTenantId === resellerTenantId) {\n throw new BillingRelationshipError(\n 'A tenant cannot resell to itself.',\n 'CIRCULAR_RELATIONSHIP',\n );\n }\n return withEmbeddedWriteTransaction(\n this.relationships.db,\n isEmbeddedDatabase(this.relationships.db),\n async (db) => {\n if (isPostgresDatabase(db)) {\n await db.query(\n \"SELECT pg_advisory_xact_lock(hashtext('smrt-billing-relationships'))\",\n );\n }\n const relationships = await BillingRelationshipCollection.create({\n db,\n });\n await this.assertAcyclic(\n relationships,\n childTenantId,\n resellerTenantId,\n );\n const existing = await relationships.get({ childTenantId });\n if (existing && existing.resellerTenantId !== resellerTenantId) {\n await this.assertManage(childTenantId, existing.resellerTenantId);\n }\n const row = existing\n ? Object.assign(existing, { resellerTenantId, billingOwnerMode })\n : await relationships.create({\n childTenantId,\n resellerTenantId,\n billingOwnerMode,\n });\n if (existing) await row.save();\n return view(row);\n },\n );\n }\n\n /** Remove reseller parentage. The child becomes self-billed. */\n async clearRelationship(childTenantId: string): Promise<void> {\n childTenantId = canonicalTenantId(childTenantId);\n await withEmbeddedWriteTransaction(\n this.relationships.db,\n isEmbeddedDatabase(this.relationships.db),\n async (db) => {\n if (isPostgresDatabase(db)) {\n await db.query(\n \"SELECT pg_advisory_xact_lock(hashtext('smrt-billing-relationships'))\",\n );\n }\n const relationships = await BillingRelationshipCollection.create({\n db,\n });\n const current = await relationships.get({ childTenantId });\n await this.assertManage(childTenantId, current?.resellerTenantId);\n if (current?.id) {\n // SmrtObject.delete() starts its own cascade transaction, which\n // DuckDB cannot nest. This row has no dependents, so delete it on\n // the already-bound transaction handle.\n await db.delete('_smrt_billing_relationships', { id: current.id });\n }\n },\n );\n }\n\n private async assertAcyclic(\n relationships: BillingRelationshipCollection,\n childTenantId: string,\n resellerTenantId: string,\n ): Promise<void> {\n const visited = new Set([childTenantId]);\n let cursor: string | undefined = resellerTenantId;\n while (cursor) {\n if (visited.has(cursor)) {\n throw new BillingRelationshipError(\n 'Reseller relationships cannot form a cycle.',\n 'CIRCULAR_RELATIONSHIP',\n );\n }\n visited.add(cursor);\n const row = await relationships.get({ childTenantId: cursor });\n cursor = row ? canonicalTenantId(row.resellerTenantId) : undefined;\n }\n }\n\n private async assertExists(tenantId: string): Promise<void> {\n assertTenantId(tenantId);\n if (!(await this.options.tenantExists(tenantId))) {\n throw new BillingRelationshipError(\n `Tenant not found: ${tenantId}`,\n 'TENANT_NOT_FOUND',\n );\n }\n }\n\n private async assertReadable(\n childTenantId: string,\n resellerTenantId?: string,\n ): Promise<void> {\n if (\n isSystemContext() ||\n isSuperAdminBypass() ||\n getTenantId()?.toLowerCase() === childTenantId ||\n (resellerTenantId && getTenantId()?.toLowerCase() === resellerTenantId) ||\n (await this.options.authorize?.({\n action: 'read',\n childTenantId,\n resellerTenantId,\n }))\n ) {\n return;\n }\n throw new TenantIsolationError('Billing relationship access denied.');\n }\n\n private async assertManage(\n childTenantId: string,\n resellerTenantId?: string,\n ): Promise<void> {\n if (\n isSystemContext() ||\n isSuperAdminBypass() ||\n (await this.options.authorize?.({\n action: 'manage',\n childTenantId,\n resellerTenantId,\n }))\n ) {\n return;\n }\n throw new TenantIsolationError('Billing relationship mutation denied.');\n }\n}\n","/**\n * Tenancy Decorators\n *\n * Provides class and property decorators for tenant-scoped SMRT objects.\n *\n * @example\n * ```typescript\n * import { smrt, SmrtObject } from '@happyvertical/smrt-core';\n * import { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n *\n * @smrt()\n * @TenantScoped({ mode: 'optional' })\n * class Document extends SmrtObject {\n * @tenantId({ nullable: true })\n * tenantId: string | null = null; // null = global document\n *\n * title: string = '';\n * }\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n * @see https://github.com/happyvertical/smrt/issues/829\n */\n\nimport {\n applyPendingDecoratorRegistrations,\n type CompatiblePropertyDecorator,\n type CompatiblePropertyDecoratorContext,\n type LegacyPropertyDecoratorTarget,\n ObjectRegistry,\n registerCompatibleFieldDecorator,\n} from '@happyvertical/smrt-core';\nimport type { TenantIdFieldOptions } from './fields.js';\nimport {\n registerTenantScopedConstructor,\n type TenantScopedConfig,\n} from './registry.js';\n\n/**\n * Options accepted by the `@TenantScoped()` class decorator.\n *\n * All fields are optional; defaults match the most restrictive safe behaviour\n * (required mode, auto-filter and auto-populate enabled, no super-admin bypass).\n *\n * @see TenantScoped\n * @see TenantScopedConfig\n */\nexport interface TenantScopedOptions {\n /**\n * Tenancy mode for this class\n * - 'required': Must have tenant context for all operations (default)\n * - 'optional': Works with or without tenant context\n */\n mode?: 'required' | 'optional';\n\n /**\n * Field name containing tenant ID\n * @default 'tenantId'\n */\n field?: string;\n\n /**\n * Auto-filter all queries by tenant\n * @default true\n */\n autoFilter?: boolean;\n\n /**\n * Auto-populate tenant ID from context on create\n * @default true\n */\n autoPopulate?: boolean;\n\n /**\n * Allow super admin bypass for this class\n * @default false - must be explicitly enabled\n */\n allowSuperAdminBypass?: boolean;\n}\n\n/**\n * Mark a class as tenant-scoped\n *\n * This decorator registers the class with the tenancy system so that:\n * - list()/get() queries are automatically filtered by tenant\n * - save() validates tenant ID matches current context\n * - delete() validates tenant ownership\n * - Raw SQL queries trigger policy enforcement\n *\n * @param options - Configuration options\n *\n * @example Basic usage (required tenancy)\n * ```typescript\n * @smrt()\n * @TenantScoped()\n * class Document extends SmrtObject {\n * @tenantId()\n * tenantId: string = '';\n *\n * title: string = '';\n * }\n * ```\n *\n * @example With super admin bypass enabled\n * ```typescript\n * @smrt()\n * @TenantScoped({ allowSuperAdminBypass: true })\n * class AuditLog extends SmrtObject {\n * @tenantId()\n * tenantId: string = '';\n *\n * action: string = '';\n * }\n * ```\n *\n * @example Optional tenancy (works with or without context)\n * ```typescript\n * @smrt()\n * @TenantScoped({ mode: 'optional' })\n * class GlobalConfig extends SmrtObject {\n * @tenantId({ nullable: true })\n * tenantId: string | null = null; // null = global, string = tenant-specific\n *\n * key: string = '';\n * value: string = '';\n * }\n * ```\n */\nexport function TenantScoped(options: TenantScopedOptions = {}) {\n return <T extends Function>(\n target: T,\n decoratorContext?: ClassDecoratorContext,\n ): T => {\n applyPendingDecoratorRegistrations(target, decoratorContext);\n\n // Merge with defaults\n const config: Partial<TenantScopedConfig> = {\n mode: options.mode ?? 'required',\n field: options.field ?? 'tenantId',\n autoFilter: options.autoFilter ?? true,\n autoPopulate: options.autoPopulate ?? true,\n allowSuperAdminBypass: options.allowSuperAdminBypass ?? false,\n };\n\n // Register with the tenancy system\n registerTenantScopedConstructor(target, config);\n\n // Support either class-decorator order. This declaration replaces only a\n // provisional field fallback; explicit @smrt and manifest policy retain\n // their documented precedence.\n ObjectRegistry.reconcileTenantScopedConfig(target, {\n mode: config.mode ?? 'required',\n field: config.field ?? 'tenantId',\n autoFilter: config.autoFilter ?? true,\n autoPopulate: config.autoPopulate ?? true,\n allowSuperAdminBypass: config.allowSuperAdminBypass ?? false,\n });\n\n // Return the class unchanged\n return target;\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Property Decorator: @tenantId\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Tenant ID property decorator\n *\n * Marks a property as the tenant identifier field. This decorator registers\n * the field metadata with ObjectRegistry, keeping the property value clean\n * (no descriptor objects that could be accidentally saved to the database).\n *\n * @param options - Field options (nullable, autoFilter, autoPopulate, etc.)\n * @returns Property decorator\n *\n * @example Basic usage (required tenancy)\n * ```typescript\n * @smrt()\n * @TenantScoped()\n * class Document extends SmrtObject {\n * @tenantId()\n * tenantId: string = '';\n *\n * title: string = '';\n * }\n * ```\n *\n * @example Nullable tenant ID (for global resources)\n * ```typescript\n * @smrt()\n * @TenantScoped({ mode: 'optional' })\n * class GlobalConfig extends SmrtObject {\n * @tenantId({ nullable: true })\n * tenantId: string | null = null; // null = global, string = tenant-specific\n *\n * key: string = '';\n * }\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/829 - Why decorators over field helpers\n */\nexport function tenantId(options: TenantIdFieldOptions = {}) {\n const opts = {\n autoFilter: true,\n required: true,\n autoPopulate: true,\n nullable: false,\n ...options,\n };\n\n return ((\n targetOrValue: LegacyPropertyDecoratorTarget | undefined,\n propertyKeyOrContext: CompatiblePropertyDecoratorContext<unknown, unknown>,\n ) => {\n registerCompatibleFieldDecorator(\n targetOrValue,\n propertyKeyOrContext,\n (className, propertyKey, ctor) => {\n const fieldOptions: Parameters<\n typeof ObjectRegistry.registerFieldDecorator\n >[2] = {\n type: 'foreignKey',\n related: 'Tenant',\n sqlType: 'UUID',\n required: opts.required,\n nullable: opts.nullable,\n __tenancy: {\n ...opts,\n isTenantIdField: true,\n },\n };\n ObjectRegistry.registerFieldDecorator(\n className,\n propertyKey,\n fieldOptions,\n ctor,\n );\n },\n );\n }) as CompatiblePropertyDecorator;\n}\n","/**\n * Tenancy Field Types and Utilities\n *\n * This module provides types and utility functions for tenant ID fields.\n * The actual field decorator is in decorators.ts.\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n * @see https://github.com/happyvertical/smrt/issues/829\n */\n\n/**\n * Options for the `@tenantId()` property decorator.\n *\n * Controls how the decorated field interacts with the tenancy interceptor.\n * All options default to the strictest safe values: auto-filter on, required,\n * auto-populate on, not nullable.\n *\n * @see tenantId\n * @see TenantScopedOptions\n */\nexport interface TenantIdFieldOptions {\n /**\n * Auto-filter queries by this field\n * @default true\n */\n autoFilter?: boolean;\n\n /**\n * Require this field to have a value on save\n * @default true\n */\n required?: boolean;\n\n /**\n * Auto-populate from context on create if not set\n * @default true\n */\n autoPopulate?: boolean;\n\n /**\n * Allow null values (for global resources)\n * @default false\n */\n nullable?: boolean;\n}\n\n// Symbol to identify tenantId fields\nexport const TENANT_ID_SYMBOL = Symbol('tenantId');\n\n/**\n * Internal field descriptor stored in `ObjectRegistry` when `@tenantId()` is\n * applied to a property.\n *\n * Consumers should use `isTenantIdField()` and `getTenantIdFieldOptions()`\n * to inspect these descriptors rather than reading the raw properties directly.\n *\n * @see isTenantIdField\n * @see getTenantIdFieldOptions\n */\nexport interface TenantIdFieldDefinition {\n /** Field type marker */\n type: 'foreignKey';\n /** Reference to Tenant class (placeholder - actual class resolved at runtime) */\n reference: 'Tenant';\n /** SQL type */\n sqlType: 'UUID';\n /** Field is required */\n required: boolean;\n /** Field allows null */\n nullable: boolean;\n /** Tenancy-specific options */\n __tenancy: TenantIdFieldOptions & { isTenantIdField: true };\n}\n\n/**\n * Return `true` if the given field definition was produced by the `@tenantId()`\n * decorator (i.e., it has an `__tenancy.isTenantIdField` marker).\n *\n * Used internally by the interceptor and code generators to locate the tenant\n * ID field on a class without knowing its property name in advance.\n *\n * @param field - A raw field definition object, typically from `ObjectRegistry`.\n * @returns `true` if `field` is a tenant ID field definition, `false` otherwise.\n *\n * @example\n * ```typescript\n * const fields = ObjectRegistry.getFields('Document');\n * const tenantField = Object.entries(fields).find(([, def]) => isTenantIdField(def));\n * ```\n *\n * @see getTenantIdFieldOptions\n * @see TenantIdFieldDefinition\n */\nexport function isTenantIdField(field: unknown): boolean {\n if (!field || typeof field !== 'object') {\n return false;\n }\n const def = field as Record<string, unknown>;\n const tenancy = def.__tenancy as Record<string, unknown> | undefined;\n return tenancy?.isTenantIdField === true;\n}\n\n/**\n * Extract the `TenantIdFieldOptions` from a field definition.\n *\n * Returns the tenancy-specific options (autoFilter, required, autoPopulate,\n * nullable) stored inside the field descriptor's `__tenancy` property.\n * Returns `null` if the field was not produced by `@tenantId()`.\n *\n * @param field - A raw field definition object, typically from `ObjectRegistry`.\n * @returns The `TenantIdFieldOptions` if the field is a tenant ID field,\n * `null` otherwise.\n *\n * @see isTenantIdField\n * @see TenantIdFieldOptions\n */\nexport function getTenantIdFieldOptions(\n field: unknown,\n): TenantIdFieldOptions | null {\n if (!isTenantIdField(field)) {\n return null;\n }\n const def = field as { __tenancy: TenantIdFieldOptions };\n return def.__tenancy;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/__smrt-register__.ts","../src/billing-relationship.ts","../src/decorators.ts","../src/fields.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-tenancy\\\",\\\"packageVersion\\\":\\\"0.55.18\\\",\\\"objects\\\":{\\\"@happyvertical/smrt-tenancy:BillingRelationship\\\":{\\\"name\\\":\\\"billingrelationship\\\",\\\"className\\\":\\\"BillingRelationship\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-tenancy:BillingRelationship\\\",\\\"collection\\\":\\\"billingrelationships\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-tenancy\\\",\\\"fields\\\":{\\\"childTenantId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":true,\\\"related\\\":\\\"@happyvertical/smrt-users:Tenant\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"resellerTenantId\\\":{\\\"type\\\":\\\"crossPackageRef\\\",\\\"required\\\":true,\\\"related\\\":\\\"@happyvertical/smrt-users:Tenant\\\",\\\"_meta\\\":{\\\"required\\\":true}},\\\"billingOwnerMode\\\":{\\\"type\\\":\\\"text\\\",\\\"required\\\":false,\\\"default\\\":\\\"self\\\",\\\"enum\\\":[\\\"self\\\",\\\"reseller\\\"]}},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"conflictColumns\\\":[\\\"child_tenant_id\\\"],\\\"api\\\":false,\\\"cli\\\":false,\\\"mcp\\\":false},\\\"extends\\\":\\\"SmrtObject\\\",\\\"exportName\\\":\\\"BillingRelationship\\\",\\\"collectionExportName\\\":\\\"BillingRelationshipCollection\\\",\\\"validationRules\\\":[{\\\"field\\\":\\\"childTenantId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"crossPackageRef\\\"},{\\\"field\\\":\\\"resellerTenantId\\\",\\\"rule\\\":\\\"required\\\",\\\"fieldType\\\":\\\"crossPackageRef\\\"}],\\\"schema\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"_smrt_billing_relationships\\\\\\\" (\\\\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 \\\\\\\"child_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"reseller_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"billing_owner_mode\\\\\\\" TEXT DEFAULT 'self'\\\\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\\\"},\\\"child_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"reseller_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"billing_owner_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"self\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"_smrt_billing_relationships_child_tenant_id_idx\\\",\\\"columns\\\":[\\\"child_tenant_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"_smrt_billing_relationships_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_reseller_tenant_id_idx\\\",\\\"columns\\\":[\\\"reseller_tenant_id\\\"]}],\\\"version\\\":\\\"4d7f410a\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-tenancy\\\"},\\\"@happyvertical/smrt-tenancy:BillingRelationshipCollection\\\":{\\\"name\\\":\\\"billingrelationshipcollection\\\",\\\"className\\\":\\\"BillingRelationshipCollection\\\",\\\"qualifiedName\\\":\\\"@happyvertical/smrt-tenancy:BillingRelationshipCollection\\\",\\\"collection\\\":\\\"billingrelationships\\\",\\\"filePath\\\":\\\"/home/runner/work/smrt/smrt/packages/tenancy/src/billing-relationship.ts\\\",\\\"packageName\\\":\\\"@happyvertical/smrt-tenancy\\\",\\\"fields\\\":{},\\\"methods\\\":{},\\\"decoratorConfig\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"api\\\":false,\\\"mcp\\\":false,\\\"cli\\\":false},\\\"extends\\\":\\\"SmrtCollection\\\",\\\"extendsTypeArg\\\":\\\"BillingRelationship\\\",\\\"exportName\\\":\\\"BillingRelationshipCollection\\\",\\\"collectionExportName\\\":\\\"BillingRelationshipCollectionCollection\\\",\\\"schema\\\":{\\\"tableName\\\":\\\"_smrt_billing_relationships\\\",\\\"ddl\\\":\\\"CREATE TABLE IF NOT EXISTS \\\\\\\"_smrt_billing_relationships\\\\\\\" (\\\\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 \\\\\\\"child_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"reseller_tenant_id\\\\\\\" UUID NOT NULL,\\\\n \\\\\\\"billing_owner_mode\\\\\\\" TEXT DEFAULT 'self'\\\\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\\\"},\\\"child_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"reseller_tenant_id\\\":{\\\"type\\\":\\\"UUID\\\",\\\"referenceKind\\\":\\\"crossPackageRef\\\",\\\"notNull\\\":true,\\\"unique\\\":false},\\\"billing_owner_mode\\\":{\\\"type\\\":\\\"TEXT\\\",\\\"notNull\\\":false,\\\"unique\\\":false,\\\"default\\\":\\\"self\\\"}},\\\"indexes\\\":[{\\\"name\\\":\\\"_smrt_billing_relationships_child_tenant_id_idx\\\",\\\"columns\\\":[\\\"child_tenant_id\\\"],\\\"unique\\\":true},{\\\"name\\\":\\\"_smrt_billing_relationships_slug_context_idx\\\",\\\"columns\\\":[\\\"slug\\\",\\\"context\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_created_at_idx\\\",\\\"columns\\\":[\\\"created_at\\\"]},{\\\"name\\\":\\\"_smrt_billing_relationships_reseller_tenant_id_idx\\\",\\\"columns\\\":[\\\"reseller_tenant_id\\\"]}],\\\"version\\\":\\\"4d7f410a\\\"},\\\"importPath\\\":\\\"@happyvertical/smrt-tenancy\\\"}},\\\"moduleType\\\":\\\"smrt\\\",\\\"smrtDependencies\\\":[\\\"@happyvertical/smrt-core\\\"],\\\"browser\\\":{\\\"status\\\":\\\"server-only\\\",\\\"issues\\\":[\\\"#3624\\\"],\\\"reason\\\":\\\"node:async_hooks\\\"},\\\"stampsConstructors\\\":true}\"));\n","/** Reseller parentage and billing ownership, independent of user permissions. */\nimport {\n crossPackageRef,\n field,\n isEmbeddedDatabase,\n isPostgresDatabase,\n type SmrtClassOptions,\n SmrtCollection,\n SmrtObject,\n smrt,\n withEmbeddedWriteTransaction,\n} from '@happyvertical/smrt-core';\nimport {\n getTenantId,\n isSuperAdminBypass,\n isSystemContext,\n TenantIsolationError,\n} from './context.js';\n\nexport type BillingOwnerMode = 'self' | 'reseller';\n\nexport interface BillingRelationshipView {\n childTenantId: string;\n resellerTenantId: string;\n billingOwnerMode: BillingOwnerMode;\n billingOwnerTenantId: string;\n}\n\nexport type BillingRelationshipAction = 'read' | 'manage';\n\nexport interface BillingRelationshipServiceOptions extends SmrtClassOptions {\n /** Resolve tenant IDs against the host tenant directory. */\n tenantExists: (tenantId: string) => Promise<boolean>;\n /** Optional host permission check for access beyond the built-in context rules. */\n authorize?: (request: {\n action: BillingRelationshipAction;\n childTenantId: string;\n resellerTenantId?: string;\n }) => Promise<boolean>;\n}\n\nexport class BillingRelationshipError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'INVALID_TENANT'\n | 'INVALID_OWNER_MODE'\n | 'TENANT_NOT_FOUND'\n | 'CIRCULAR_RELATIONSHIP',\n ) {\n super(message);\n this.name = 'BillingRelationshipError';\n }\n}\n\n@smrt({\n tableName: '_smrt_billing_relationships',\n conflictColumns: ['child_tenant_id'],\n api: false,\n cli: false,\n mcp: false,\n})\nexport class BillingRelationship extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-tenancy\";\n @crossPackageRef('@happyvertical/smrt-users:Tenant')\n @field({ required: true })\n childTenantId: string = '';\n @crossPackageRef('@happyvertical/smrt-users:Tenant')\n @field({ required: true })\n resellerTenantId: string = '';\n billingOwnerMode: BillingOwnerMode = 'self';\n\n protected async validateBeforeSave(): Promise<void> {\n await super.validateBeforeSave();\n this.childTenantId = canonicalTenantId(this.childTenantId);\n this.resellerTenantId = canonicalTenantId(this.resellerTenantId);\n assertMode(this.billingOwnerMode);\n if (this.childTenantId === this.resellerTenantId) {\n throw new BillingRelationshipError(\n 'A tenant cannot resell to itself.',\n 'CIRCULAR_RELATIONSHIP',\n );\n }\n }\n}\n\nexport class BillingRelationshipCollection extends SmrtCollection<BillingRelationship> {\n static readonly _itemClass = BillingRelationship;\n}\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction assertTenantId(id: string): void {\n if (typeof id !== 'string' || !UUID_PATTERN.test(id)) {\n throw new BillingRelationshipError(\n 'Tenant IDs must be UUIDs.',\n 'INVALID_TENANT',\n );\n }\n}\n\nfunction canonicalTenantId(id: string): string {\n assertTenantId(id);\n return id.toLowerCase();\n}\n\nfunction assertMode(mode: BillingOwnerMode): void {\n if (mode !== 'self' && mode !== 'reseller') {\n throw new BillingRelationshipError(\n 'Billing owner mode must be self or reseller.',\n 'INVALID_OWNER_MODE',\n );\n }\n}\n\nfunction view(row: BillingRelationship): BillingRelationshipView {\n const childTenantId = canonicalTenantId(row.childTenantId);\n const resellerTenantId = canonicalTenantId(row.resellerTenantId);\n assertMode(row.billingOwnerMode);\n return {\n childTenantId,\n resellerTenantId,\n billingOwnerMode: row.billingOwnerMode,\n billingOwnerTenantId:\n row.billingOwnerMode === 'reseller' ? resellerTenantId : childTenantId,\n };\n}\n\n/**\n * The one-row-per-child billing graph. The host supplies tenant existence and\n * any additional authorization; absent a host authorizer, only an explicit\n * system context can mutate relationships.\n */\nexport class BillingRelationshipService {\n private constructor(\n private readonly relationships: BillingRelationshipCollection,\n private readonly options: BillingRelationshipServiceOptions,\n ) {}\n\n static async create(\n options: BillingRelationshipServiceOptions,\n ): Promise<BillingRelationshipService> {\n if (typeof options.tenantExists !== 'function') {\n throw new Error('Billing relationships require a tenantExists resolver.');\n }\n const relationships = await BillingRelationshipCollection.create(options);\n if (/^(?:https?|libsql):\\/\\//iu.test(relationships.db.url ?? '')) {\n throw new Error(\n 'Billing relationships require a local SQL or PostgreSQL database with serialized transactions.',\n );\n }\n if (!relationships.db.transaction) {\n throw new Error('Billing relationships require database transactions.');\n }\n return new BillingRelationshipService(relationships, options);\n }\n\n /** A tenant without a relationship always pays for itself. */\n async resolveBillingOwner(childTenantId: string): Promise<string> {\n childTenantId = canonicalTenantId(childTenantId);\n const row = await this.relationships.get({ childTenantId });\n await this.assertReadable(childTenantId, row?.resellerTenantId);\n await this.assertExists(childTenantId);\n if (row) await this.assertExists(row.resellerTenantId);\n return row ? view(row).billingOwnerTenantId : childTenantId;\n }\n\n async getRelationship(\n childTenantId: string,\n ): Promise<BillingRelationshipView | null> {\n childTenantId = canonicalTenantId(childTenantId);\n const row = await this.relationships.get({ childTenantId });\n await this.assertReadable(childTenantId, row?.resellerTenantId);\n await this.assertExists(childTenantId);\n if (row) await this.assertExists(row.resellerTenantId);\n return row ? view(row) : null;\n }\n\n /** List direct children billed to an owner, with no collection page limit. */\n async listChildrenBilledTo(ownerTenantId: string): Promise<string[]> {\n ownerTenantId = canonicalTenantId(ownerTenantId);\n await this.assertReadable(ownerTenantId);\n await this.assertExists(ownerTenantId);\n const result = await this.relationships.db.query(\n 'SELECT child_tenant_id FROM _smrt_billing_relationships WHERE reseller_tenant_id = ? AND billing_owner_mode = ? ORDER BY child_tenant_id',\n ownerTenantId,\n 'reseller',\n );\n return result.rows.map((row) => {\n return canonicalTenantId(String(row.child_tenant_id));\n });\n }\n\n /** Create or replace parentage and its default billing owner in one row. */\n async setRelationship(input: {\n childTenantId: string;\n resellerTenantId: string;\n billingOwnerMode: BillingOwnerMode;\n }): Promise<BillingRelationshipView> {\n const childTenantId = canonicalTenantId(input.childTenantId);\n const resellerTenantId = canonicalTenantId(input.resellerTenantId);\n const { billingOwnerMode } = input;\n assertMode(billingOwnerMode);\n await this.assertManage(childTenantId, resellerTenantId);\n await this.assertExists(childTenantId);\n await this.assertExists(resellerTenantId);\n if (childTenantId === resellerTenantId) {\n throw new BillingRelationshipError(\n 'A tenant cannot resell to itself.',\n 'CIRCULAR_RELATIONSHIP',\n );\n }\n return withEmbeddedWriteTransaction(\n this.relationships.db,\n isEmbeddedDatabase(this.relationships.db),\n async (db) => {\n if (isPostgresDatabase(db)) {\n await db.query(\n \"SELECT pg_advisory_xact_lock(hashtext('smrt-billing-relationships'))\",\n );\n }\n const relationships = await BillingRelationshipCollection.create({\n db,\n });\n await this.assertAcyclic(\n relationships,\n childTenantId,\n resellerTenantId,\n );\n const existing = await relationships.get({ childTenantId });\n if (existing && existing.resellerTenantId !== resellerTenantId) {\n await this.assertManage(childTenantId, existing.resellerTenantId);\n }\n const row = existing\n ? Object.assign(existing, { resellerTenantId, billingOwnerMode })\n : await relationships.create({\n childTenantId,\n resellerTenantId,\n billingOwnerMode,\n });\n if (existing) await row.save();\n return view(row);\n },\n );\n }\n\n /** Remove reseller parentage. The child becomes self-billed. */\n async clearRelationship(childTenantId: string): Promise<void> {\n childTenantId = canonicalTenantId(childTenantId);\n await withEmbeddedWriteTransaction(\n this.relationships.db,\n isEmbeddedDatabase(this.relationships.db),\n async (db) => {\n if (isPostgresDatabase(db)) {\n await db.query(\n \"SELECT pg_advisory_xact_lock(hashtext('smrt-billing-relationships'))\",\n );\n }\n const relationships = await BillingRelationshipCollection.create({\n db,\n });\n const current = await relationships.get({ childTenantId });\n await this.assertManage(childTenantId, current?.resellerTenantId);\n if (current?.id) {\n // SmrtObject.delete() starts its own cascade transaction, which\n // DuckDB cannot nest. This row has no dependents, so delete it on\n // the already-bound transaction handle.\n await db.delete('_smrt_billing_relationships', { id: current.id });\n }\n },\n );\n }\n\n private async assertAcyclic(\n relationships: BillingRelationshipCollection,\n childTenantId: string,\n resellerTenantId: string,\n ): Promise<void> {\n const visited = new Set([childTenantId]);\n let cursor: string | undefined = resellerTenantId;\n while (cursor) {\n if (visited.has(cursor)) {\n throw new BillingRelationshipError(\n 'Reseller relationships cannot form a cycle.',\n 'CIRCULAR_RELATIONSHIP',\n );\n }\n visited.add(cursor);\n const row = await relationships.get({ childTenantId: cursor });\n cursor = row ? canonicalTenantId(row.resellerTenantId) : undefined;\n }\n }\n\n private async assertExists(tenantId: string): Promise<void> {\n assertTenantId(tenantId);\n if (!(await this.options.tenantExists(tenantId))) {\n throw new BillingRelationshipError(\n `Tenant not found: ${tenantId}`,\n 'TENANT_NOT_FOUND',\n );\n }\n }\n\n private async assertReadable(\n childTenantId: string,\n resellerTenantId?: string,\n ): Promise<void> {\n if (\n isSystemContext() ||\n isSuperAdminBypass() ||\n getTenantId()?.toLowerCase() === childTenantId ||\n (resellerTenantId && getTenantId()?.toLowerCase() === resellerTenantId) ||\n (await this.options.authorize?.({\n action: 'read',\n childTenantId,\n resellerTenantId,\n }))\n ) {\n return;\n }\n throw new TenantIsolationError('Billing relationship access denied.');\n }\n\n private async assertManage(\n childTenantId: string,\n resellerTenantId?: string,\n ): Promise<void> {\n if (\n isSystemContext() ||\n isSuperAdminBypass() ||\n (await this.options.authorize?.({\n action: 'manage',\n childTenantId,\n resellerTenantId,\n }))\n ) {\n return;\n }\n throw new TenantIsolationError('Billing relationship mutation denied.');\n }\n}\n","/**\n * Tenancy Decorators\n *\n * Provides class and property decorators for tenant-scoped SMRT objects.\n *\n * @example\n * ```typescript\n * import { smrt, SmrtObject } from '@happyvertical/smrt-core';\n * import { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\n *\n * @smrt()\n * @TenantScoped({ mode: 'optional' })\n * class Document extends SmrtObject {\n * @tenantId({ nullable: true })\n * tenantId: string | null = null; // null = global document\n *\n * title: string = '';\n * }\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n * @see https://github.com/happyvertical/smrt/issues/829\n */\n\nimport {\n applyPendingDecoratorRegistrations,\n type CompatiblePropertyDecorator,\n type CompatiblePropertyDecoratorContext,\n type LegacyPropertyDecoratorTarget,\n ObjectRegistry,\n registerCompatibleFieldDecorator,\n} from '@happyvertical/smrt-core';\nimport type { TenantIdFieldOptions } from './fields.js';\nimport {\n registerTenantScopedConstructor,\n type TenantScopedConfig,\n} from './registry.js';\n\n/**\n * Options accepted by the `@TenantScoped()` class decorator.\n *\n * All fields are optional; defaults match the most restrictive safe behaviour\n * (required mode, auto-filter and auto-populate enabled, no super-admin bypass).\n *\n * @see TenantScoped\n * @see TenantScopedConfig\n */\nexport interface TenantScopedOptions {\n /**\n * Tenancy mode for this class\n * - 'required': Must have tenant context for all operations (default)\n * - 'optional': Works with or without tenant context\n */\n mode?: 'required' | 'optional';\n\n /**\n * Field name containing tenant ID\n * @default 'tenantId'\n */\n field?: string;\n\n /**\n * Auto-filter all queries by tenant\n * @default true\n */\n autoFilter?: boolean;\n\n /**\n * Auto-populate tenant ID from context on create\n * @default true\n */\n autoPopulate?: boolean;\n\n /**\n * Allow super admin bypass for this class\n * @default false - must be explicitly enabled\n */\n allowSuperAdminBypass?: boolean;\n}\n\n/**\n * Mark a class as tenant-scoped\n *\n * This decorator registers the class with the tenancy system so that:\n * - list()/get() queries are automatically filtered by tenant\n * - save() validates tenant ID matches current context\n * - delete() validates tenant ownership\n * - Raw SQL queries trigger policy enforcement\n *\n * @param options - Configuration options\n *\n * @example Basic usage (required tenancy)\n * ```typescript\n * @smrt()\n * @TenantScoped()\n * class Document extends SmrtObject {\n * @tenantId()\n * tenantId: string = '';\n *\n * title: string = '';\n * }\n * ```\n *\n * @example With super admin bypass enabled\n * ```typescript\n * @smrt()\n * @TenantScoped({ allowSuperAdminBypass: true })\n * class AuditLog extends SmrtObject {\n * @tenantId()\n * tenantId: string = '';\n *\n * action: string = '';\n * }\n * ```\n *\n * @example Optional tenancy (works with or without context)\n * ```typescript\n * @smrt()\n * @TenantScoped({ mode: 'optional' })\n * class GlobalConfig extends SmrtObject {\n * @tenantId({ nullable: true })\n * tenantId: string | null = null; // null = global, string = tenant-specific\n *\n * key: string = '';\n * value: string = '';\n * }\n * ```\n */\nexport function TenantScoped(options: TenantScopedOptions = {}) {\n return <T extends Function>(\n target: T,\n decoratorContext?: ClassDecoratorContext,\n ): T => {\n applyPendingDecoratorRegistrations(target, decoratorContext);\n\n // Merge with defaults\n const config: Partial<TenantScopedConfig> = {\n mode: options.mode ?? 'required',\n field: options.field ?? 'tenantId',\n autoFilter: options.autoFilter ?? true,\n autoPopulate: options.autoPopulate ?? true,\n allowSuperAdminBypass: options.allowSuperAdminBypass ?? false,\n };\n\n // Register with the tenancy system\n registerTenantScopedConstructor(target, config);\n\n // Support either class-decorator order. This declaration replaces only a\n // provisional field fallback; explicit @smrt and manifest policy retain\n // their documented precedence.\n ObjectRegistry.reconcileTenantScopedConfig(target, {\n mode: config.mode ?? 'required',\n field: config.field ?? 'tenantId',\n autoFilter: config.autoFilter ?? true,\n autoPopulate: config.autoPopulate ?? true,\n allowSuperAdminBypass: config.allowSuperAdminBypass ?? false,\n });\n\n // Return the class unchanged\n return target;\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Property Decorator: @tenantId\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Tenant ID property decorator\n *\n * Marks a property as the tenant identifier field. This decorator registers\n * the field metadata with ObjectRegistry, keeping the property value clean\n * (no descriptor objects that could be accidentally saved to the database).\n *\n * @param options - Field options (nullable, autoFilter, autoPopulate, etc.)\n * @returns Property decorator\n *\n * @example Basic usage (required tenancy)\n * ```typescript\n * @smrt()\n * @TenantScoped()\n * class Document extends SmrtObject {\n * @tenantId()\n * tenantId: string = '';\n *\n * title: string = '';\n * }\n * ```\n *\n * @example Nullable tenant ID (for global resources)\n * ```typescript\n * @smrt()\n * @TenantScoped({ mode: 'optional' })\n * class GlobalConfig extends SmrtObject {\n * @tenantId({ nullable: true })\n * tenantId: string | null = null; // null = global, string = tenant-specific\n *\n * key: string = '';\n * }\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/829 - Why decorators over field helpers\n */\nexport function tenantId(options: TenantIdFieldOptions = {}) {\n const opts = {\n autoFilter: true,\n required: true,\n autoPopulate: true,\n nullable: false,\n ...options,\n };\n\n return ((\n targetOrValue: LegacyPropertyDecoratorTarget | undefined,\n propertyKeyOrContext: CompatiblePropertyDecoratorContext<unknown, unknown>,\n ) => {\n registerCompatibleFieldDecorator(\n targetOrValue,\n propertyKeyOrContext,\n (className, propertyKey, ctor) => {\n const fieldOptions: Parameters<\n typeof ObjectRegistry.registerFieldDecorator\n >[2] = {\n type: 'foreignKey',\n related: 'Tenant',\n sqlType: 'UUID',\n required: opts.required,\n nullable: opts.nullable,\n __tenancy: {\n ...opts,\n isTenantIdField: true,\n },\n };\n ObjectRegistry.registerFieldDecorator(\n className,\n propertyKey,\n fieldOptions,\n ctor,\n );\n },\n );\n }) as CompatiblePropertyDecorator;\n}\n","/**\n * Tenancy Field Types and Utilities\n *\n * This module provides types and utility functions for tenant ID fields.\n * The actual field decorator is in decorators.ts.\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n * @see https://github.com/happyvertical/smrt/issues/829\n */\n\n/**\n * Options for the `@tenantId()` property decorator.\n *\n * Controls how the decorated field interacts with the tenancy interceptor.\n * All options default to the strictest safe values: auto-filter on, required,\n * auto-populate on, not nullable.\n *\n * @see tenantId\n * @see TenantScopedOptions\n */\nexport interface TenantIdFieldOptions {\n /**\n * Auto-filter queries by this field\n * @default true\n */\n autoFilter?: boolean;\n\n /**\n * Require this field to have a value on save\n * @default true\n */\n required?: boolean;\n\n /**\n * Auto-populate from context on create if not set\n * @default true\n */\n autoPopulate?: boolean;\n\n /**\n * Allow null values (for global resources)\n * @default false\n */\n nullable?: boolean;\n}\n\n// Symbol to identify tenantId fields\nexport const TENANT_ID_SYMBOL = Symbol('tenantId');\n\n/**\n * Internal field descriptor stored in `ObjectRegistry` when `@tenantId()` is\n * applied to a property.\n *\n * Consumers should use `isTenantIdField()` and `getTenantIdFieldOptions()`\n * to inspect these descriptors rather than reading the raw properties directly.\n *\n * @see isTenantIdField\n * @see getTenantIdFieldOptions\n */\nexport interface TenantIdFieldDefinition {\n /** Field type marker */\n type: 'foreignKey';\n /** Reference to Tenant class (placeholder - actual class resolved at runtime) */\n reference: 'Tenant';\n /** SQL type */\n sqlType: 'UUID';\n /** Field is required */\n required: boolean;\n /** Field allows null */\n nullable: boolean;\n /** Tenancy-specific options */\n __tenancy: TenantIdFieldOptions & { isTenantIdField: true };\n}\n\n/**\n * Return `true` if the given field definition was produced by the `@tenantId()`\n * decorator (i.e., it has an `__tenancy.isTenantIdField` marker).\n *\n * Used internally by the interceptor and code generators to locate the tenant\n * ID field on a class without knowing its property name in advance.\n *\n * @param field - A raw field definition object, typically from `ObjectRegistry`.\n * @returns `true` if `field` is a tenant ID field definition, `false` otherwise.\n *\n * @example\n * ```typescript\n * const fields = ObjectRegistry.getFields('Document');\n * const tenantField = Object.entries(fields).find(([, def]) => isTenantIdField(def));\n * ```\n *\n * @see getTenantIdFieldOptions\n * @see TenantIdFieldDefinition\n */\nexport function isTenantIdField(field: unknown): boolean {\n if (!field || typeof field !== 'object') {\n return false;\n }\n const def = field as Record<string, unknown>;\n const tenancy = def.__tenancy as Record<string, unknown> | undefined;\n return tenancy?.isTenantIdField === true;\n}\n\n/**\n * Extract the `TenantIdFieldOptions` from a field definition.\n *\n * Returns the tenancy-specific options (autoFilter, required, autoPopulate,\n * nullable) stored inside the field descriptor's `__tenancy` property.\n * Returns `null` if the field was not produced by `@tenantId()`.\n *\n * @param field - A raw field definition object, typically from `ObjectRegistry`.\n * @returns The `TenantIdFieldOptions` if the field is a tenant ID field,\n * `null` otherwise.\n *\n * @see isTenantIdField\n * @see TenantIdFieldOptions\n */\nexport function getTenantIdFieldOptions(\n field: unknown,\n): TenantIdFieldOptions | null {\n if (!isTenantIdField(field)) {\n return null;\n }\n const def = field as { __tenancy: TenantIdFieldOptions };\n return def.__tenancy;\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-tenancy",
5
- "packageVersion": "0.55.16",
5
+ "packageVersion": "0.55.18",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-tenancy:BillingRelationship": {
8
8
  "name": "billingrelationship",
@@ -3,12 +3,12 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-tenancy",
6
- "packageVersion": "0.55.16",
6
+ "packageVersion": "0.55.18",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "3d9b8e113b955662a6b27839b1f3a9e424f1b6b465e81ba82c034d667e8b003a",
11
- "packageJson": "50d65787686ff046411e9bce44ad74c048a195d15c991fc903c74b4c1b7287eb",
10
+ "manifest": "c9ad04c7925b5fdd860a54d7cb1f26f28b37b26b531c18ecc78291a823ae622f",
11
+ "packageJson": "12718582b69fa4d93aa336f7645af7f6afa908c86511e60f87cefafb633a83f1",
12
12
  "agents": "deb2f3230dc4f46a9af61215673fe17cb943b0564ac6873187df458767e2e9be"
13
13
  },
14
14
  "exports": [
@@ -89,7 +89,7 @@ function getColor(name: string): string {
89
89
  tabindex={onclick ? 0 : undefined}
90
90
  onclick={onclick}
91
91
  onkeydown={(event: KeyboardEvent) => event.key === 'Enter' && onclick?.()}
92
- use:ripple
92
+ use:ripple data-smrt-component="TenantCard"
93
93
  >
94
94
  <div class="avatar" style:background-color={getColor(tenant.name ?? '')}>
95
95
  {getInitials(tenant.name ?? 'T')}
@@ -139,13 +139,15 @@ function getColor(name: string): string {
139
139
  </div>
140
140
 
141
141
  <style>
142
+ @layer smrt.reset, smrt.structure, smrt.theme;
143
+ @layer smrt.theme {
142
144
  .tenant-card {
143
145
  display: flex;
144
146
  align-items: center;
145
- gap: var(--smrt-spacing-4, 16px);
146
- padding: var(--smrt-spacing-4, 16px);
147
+ gap: var(--smrt-spacing-4);
148
+ padding: var(--smrt-spacing-4);
147
149
  background-color: var(--smrt-color-surface-container-low);
148
- border-radius: var(--smrt-radius-lg, 12px);
150
+ border-radius: var(--smrt-radius-lg);
149
151
  color: var(--smrt-color-on-surface);
150
152
  transition: all 200ms cubic-bezier(0.2, 0, 0, 1);
151
153
  position: relative;
@@ -173,7 +175,7 @@ function getColor(name: string): string {
173
175
  justify-content: center;
174
176
  width: 48px;
175
177
  height: 48px;
176
- border-radius: var(--smrt-radius-lg, 12px);
178
+ border-radius: var(--smrt-radius-lg);
177
179
  color: white;
178
180
  font: var(--smrt-typography-title-medium-font);
179
181
  font-weight: var(--smrt-typography-weight-semibold, 600);
@@ -188,7 +190,7 @@ function getColor(name: string): string {
188
190
  .header {
189
191
  display: flex;
190
192
  align-items: center;
191
- gap: var(--smrt-spacing-2, 8px);
193
+ gap: var(--smrt-spacing-2);
192
194
  }
193
195
 
194
196
  .name {
@@ -201,11 +203,11 @@ function getColor(name: string): string {
201
203
 
202
204
  .status {
203
205
  font: var(--smrt-typography-label-small-font);
204
- padding: 0 var(--smrt-spacing-2, 8px);
206
+ padding: 0 var(--smrt-spacing-2);
205
207
  height: 18px;
206
208
  display: inline-flex;
207
209
  align-items: center;
208
- border-radius: var(--smrt-radius-md, 8px);
210
+ border-radius: var(--smrt-radius-md);
209
211
  text-transform: uppercase;
210
212
  font-weight: var(--smrt-typography-weight-semibold, 600);
211
213
  flex-shrink: 0;
@@ -229,7 +231,7 @@ function getColor(name: string): string {
229
231
  .slug {
230
232
  font: var(--smrt-typography-body-small-font);
231
233
  color: var(--smrt-color-on-surface-variant);
232
- margin-top: var(--smrt-spacing-1, 4px);
234
+ margin-top: var(--smrt-spacing-1);
233
235
  }
234
236
 
235
237
  .selected .slug {
@@ -238,7 +240,7 @@ function getColor(name: string): string {
238
240
  }
239
241
 
240
242
  .meta {
241
- margin-top: var(--smrt-spacing-1, 4px);
243
+ margin-top: var(--smrt-spacing-1);
242
244
  }
243
245
 
244
246
  .members {
@@ -248,7 +250,7 @@ function getColor(name: string): string {
248
250
 
249
251
  .actions {
250
252
  display: flex;
251
- gap: var(--smrt-spacing-1, 4px);
253
+ gap: var(--smrt-spacing-1);
252
254
  flex-shrink: 0;
253
255
  }
254
256
 
@@ -267,11 +269,12 @@ function getColor(name: string): string {
267
269
  width: 32px;
268
270
  height: 32px;
269
271
  padding: 0;
270
- border-radius: var(--smrt-radius-full, 9999px);
272
+ border-radius: var(--smrt-radius-full);
271
273
  color: var(--smrt-color-on-surface-variant);
272
274
  }
273
275
 
274
276
  .actions :global(.action-btn--danger):hover {
275
277
  color: var(--smrt-color-error);
276
278
  }
279
+ }
277
280
  </style>
@@ -22,7 +22,7 @@ function handleChange(event: Event) {
22
22
  }
23
23
  </script>
24
24
 
25
- <div class="tenant-switcher">
25
+ <div class="tenant-switcher" data-smrt-component="TenantSwitcher">
26
26
  {#if memberships.length <= 1}
27
27
  <span class="tenant-name">{currentTenant?.name ?? 'Unknown'}</span>
28
28
  {:else}
@@ -41,6 +41,8 @@ function handleChange(event: Event) {
41
41
  </div>
42
42
 
43
43
  <style>
44
+ @layer smrt.reset, smrt.structure, smrt.theme;
45
+ @layer smrt.theme {
44
46
  .tenant-switcher {
45
47
  display: inline-flex;
46
48
  align-items: center;
@@ -52,7 +54,7 @@ function handleChange(event: Event) {
52
54
 
53
55
  .tenant-select {
54
56
  padding: var(--smrt-spacing-sm, 0.5rem) var(--smrt-spacing-md, 1rem);
55
- border: 1px solid var(--smrt-color-outline-variant, #c4c6cf);
57
+ border: 1px solid var(--smrt-color-outline-variant);
56
58
  border-radius: var(--smrt-radius-medium, 0.5rem);
57
59
  background: var(--smrt-color-surface, white);
58
60
  font: var(--smrt-typography-body-medium-font, 0.875rem / 1.25 sans-serif);
@@ -61,7 +63,7 @@ function handleChange(event: Event) {
61
63
  }
62
64
 
63
65
  .tenant-select:focus {
64
- outline: 2px solid var(--smrt-color-primary, #005ac1);
66
+ outline: 2px solid var(--smrt-color-primary);
65
67
  outline-offset: 2px;
66
68
  }
67
69
 
@@ -70,4 +72,5 @@ function handleChange(event: Event) {
70
72
  transition: none;
71
73
  }
72
74
  }
75
+ }
73
76
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-tenancy",
3
- "version": "0.55.16",
3
+ "version": "0.55.18",
4
4
  "smrtJsdoc": "strict",
5
5
  "description": "Production-ready multi-tenancy framework for SMRT with automatic tenant isolation and enforcement",
6
6
  "type": "module",
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@happyvertical/logger": "^0.103.4",
46
- "@happyvertical/smrt-core": "0.55.16",
47
- "@happyvertical/smrt-types": "0.55.16",
48
- "@happyvertical/smrt-ui": "0.55.16",
46
+ "@happyvertical/smrt-core": "0.55.18",
47
+ "@happyvertical/smrt-types": "0.55.18",
48
+ "@happyvertical/smrt-ui": "0.55.18",
49
49
  "@happyvertical/sql": "^0.103.4",
50
50
  "@happyvertical/utils": "^0.103.4"
51
51
  },
@@ -58,7 +58,7 @@
58
58
  }
59
59
  },
60
60
  "devDependencies": {
61
- "@happyvertical/smrt-vitest": "0.55.16",
61
+ "@happyvertical/smrt-vitest": "0.55.18",
62
62
  "@sveltejs/package": "^2.5.8",
63
63
  "@sveltejs/vite-plugin-svelte": "^7.3.0",
64
64
  "@types/node": "24.13.6",