@happyvertical/smrt-fields 0.40.57
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/AGENTS.md +179 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +52 -0
- package/dist/index.d.ts +532 -0
- package/dist/index.js +936 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +501 -0
- package/dist/smrt-knowledge.json +347 -0
- package/dist/types.d.ts +203 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/__smrt-register__.ts","../src/cache.ts","../src/field-definitions.ts","../src/models/FieldPolicy.ts","../src/collections/FieldPolicyCollection.ts","../src/field-policy-resolver.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","import type { DatabaseInterface } from '@happyvertical/sql';\nimport type { ExplainedObjectFieldPolicy } from './types.js';\n\nconst FIELD_POLICY_CACHE_TTL_MS = 30_000;\n\ntype CacheEntry = {\n expiresAt: number;\n value: ExplainedObjectFieldPolicy;\n};\n\nconst fieldPolicyCache = new Map<string, CacheEntry>();\nconst dbInstanceIds = new WeakMap<object, string>();\nlet nextDbId = 1;\n\n/**\n * Namespace a cache key by database identity so parallel test databases (and\n * multi-database processes) never share entries. Mirrors\n * `packages/prompts/src/cache.ts` exactly.\n */\nfunction getDbNamespace(db: unknown): string {\n if (!db) {\n return 'no-db';\n }\n\n if (typeof db === 'string') {\n return `db:${db}`;\n }\n\n if (typeof db === 'object') {\n const dbObject = db as Record<string, unknown>;\n if (typeof dbObject.query === 'function') {\n if (!dbInstanceIds.has(dbObject)) {\n dbInstanceIds.set(dbObject, `db-instance:${nextDbId++}`);\n }\n const namespace = dbInstanceIds.get(dbObject);\n if (namespace) {\n return namespace;\n }\n\n return 'db-instance:unknown';\n }\n\n try {\n return `db-config:${JSON.stringify(dbObject)}`;\n } catch {\n return 'db-config:opaque';\n }\n }\n\n return 'db:unknown';\n}\n\nconst hierarchyLoaderIds = new WeakMap<object, string>();\nlet nextLoaderId = 1;\n\n/**\n * Namespace a cache key by tenant-hierarchy loader identity.\n *\n * An injected `tenantHierarchyLoader` can produce a completely different\n * ancestor chain — and therefore different resolved defaults and locks — for\n * the same `(db, objectRef, tenantId, userId)`. Without this component an\n * injected loader would serve (or be served) the DEFAULT loader's cached\n * result. Callers that pass no loader all share the `default` namespace.\n */\nfunction getHierarchyLoaderNamespace(loader: unknown): string {\n if (typeof loader !== 'function') {\n return 'loader:default';\n }\n\n const key = loader as unknown as object;\n if (!hierarchyLoaderIds.has(key)) {\n hierarchyLoaderIds.set(key, `loader:${nextLoaderId++}`);\n }\n return hierarchyLoaderIds.get(key) ?? 'loader:unknown';\n}\n\n/**\n * Loader namespace goes LAST so `invalidateFieldPolicyCache`'s\n * `(db, objectRef)` prefix scan still clears every loader's entries.\n */\nfunction buildCacheKey(\n objectRef: string,\n tenantId: string | null | undefined,\n userId: string | null | undefined,\n db: DatabaseInterface | unknown,\n hierarchyLoader?: unknown,\n): string {\n return `${getDbNamespace(db)}::${objectRef}::${tenantId ?? 'app'}::${\n userId ?? 'user:none'\n }::${getHierarchyLoaderNamespace(hierarchyLoader)}`;\n}\n\nexport function getFieldPolicyCacheTtlMs(): number {\n return FIELD_POLICY_CACHE_TTL_MS;\n}\n\nexport function getCachedFieldPolicy(\n objectRef: string,\n tenantId: string | null | undefined,\n userId: string | null | undefined,\n db: DatabaseInterface | unknown,\n hierarchyLoader?: unknown,\n): ExplainedObjectFieldPolicy | null {\n const cacheKey = buildCacheKey(\n objectRef,\n tenantId,\n userId,\n db,\n hierarchyLoader,\n );\n const cached = fieldPolicyCache.get(cacheKey);\n\n if (!cached) {\n return null;\n }\n\n if (cached.expiresAt <= Date.now()) {\n fieldPolicyCache.delete(cacheKey);\n return null;\n }\n\n return cached.value;\n}\n\nexport function setCachedFieldPolicy(\n objectRef: string,\n tenantId: string | null | undefined,\n userId: string | null | undefined,\n db: DatabaseInterface | unknown,\n value: ExplainedObjectFieldPolicy,\n hierarchyLoader?: unknown,\n): void {\n fieldPolicyCache.set(\n buildCacheKey(objectRef, tenantId, userId, db, hierarchyLoader),\n {\n expiresAt: Date.now() + FIELD_POLICY_CACHE_TTL_MS,\n value,\n },\n );\n}\n\n/**\n * Drop every cached resolution for `(db, objectRef)`.\n *\n * Deliberately coarser than the prompts precedent (which deletes a single\n * `(key, tenantId)` entry when the tenant is known): tenant HIERARCHY makes a\n * parent-tenant row change affect every descendant tenant's resolution, and a\n * lock or app-row change affects user-tier entries too, so precise\n * invalidation would have to know the whole tenant tree. Per-objectRef prefix\n * invalidation is always correct and the 30s TTL keeps the cost bounded.\n */\nexport function invalidateFieldPolicyCache(\n objectRef: string,\n db: DatabaseInterface | unknown,\n): void {\n const keyPrefix = `${getDbNamespace(db)}::${objectRef}::`;\n for (const cacheKey of fieldPolicyCache.keys()) {\n if (cacheKey.startsWith(keyPrefix)) {\n fieldPolicyCache.delete(cacheKey);\n }\n }\n}\n\nexport function clearFieldPolicyCache(): void {\n fieldPolicyCache.clear();\n}\n","/**\n * Manifest-as-definition-registry helpers.\n *\n * Field policy is validated and seeded from the LIVE `ObjectRegistry` (never\n * from checked-in `manifest.json` artifacts): the registry is the runtime\n * source of truth for which objects/fields exist, their types, their security\n * flags, and their `_meta.ui` presentation hints (#2046).\n */\n\nimport type { FieldUIHints } from '@happyvertical/smrt-core';\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\nimport type { FieldPolicyDelta, FieldPolicyVisibility } from './types.js';\n\n/**\n * Field map for one object, keyed by field name (includes inherited fields).\n * Derived structurally from the PUBLIC `ObjectRegistry.getAllFields` return\n * type — core does not export its internal `RegisteredField` name.\n */\nexport type FieldDefinitionMap = Awaited<\n ReturnType<typeof ObjectRegistry.getAllFields>\n>;\n\n/** One field's registered metadata, as returned by `getAllFields`. */\nexport type RegisteredFieldInfo =\n FieldDefinitionMap extends Map<string, infer F> ? F : never;\n\n/**\n * Resolve `objectRef` (qualified `@package/name:ClassName`) against the live\n * registry, throwing a descriptive error when the class is unknown.\n */\nexport function requireRegisteredObject(objectRef: string): void {\n if (!objectRef?.includes(':')) {\n throw new Error(\n `Field policy objectRef must be a qualified class name ` +\n `(\"@package/name:ClassName\"), got \"${objectRef}\"`,\n );\n }\n if (!ObjectRegistry.getClassByQualifiedName(objectRef)) {\n throw new Error(\n `Unknown field policy objectRef \"${objectRef}\": no class with that ` +\n `qualified name is registered`,\n );\n }\n}\n\n/** Load the full (inherited) field map for a registered objectRef. */\nexport async function getObjectFieldMap(\n objectRef: string,\n): Promise<FieldDefinitionMap> {\n requireRegisteredObject(objectRef);\n return ObjectRegistry.getAllFields(objectRef);\n}\n\n/**\n * Narrow a manifest `_meta.ui` bag to the known {@link FieldUIHints} keys with\n * their expected primitive types, dropping junk (mirrors the sanitization the\n * web emission applies in core's `web-collections.ts`).\n */\nexport function sanitizeFieldUIHints(value: unknown): FieldUIHints | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n const raw = value as Record<string, unknown>;\n const ui: FieldUIHints = {\n ...(typeof raw.basic === 'boolean' ? { basic: raw.basic } : {}),\n ...(typeof raw.group === 'string' ? { group: raw.group } : {}),\n ...(typeof raw.order === 'number' && Number.isFinite(raw.order)\n ? { order: raw.order }\n : {}),\n ...(typeof raw.locked === 'boolean' ? { locked: raw.locked } : {}),\n };\n return Object.keys(ui).length > 0 ? ui : undefined;\n}\n\n/** Security rail: `sensitive` may live top-level or under `_meta` (STI merges). */\nexport function isSensitiveField(field: RegisteredFieldInfo): boolean {\n return field.sensitive === true || field._meta?.sensitive === true;\n}\n\n/** Security rail: `readPermission` may live top-level or under `_meta`. */\nexport function getFieldReadPermission(\n field: RegisteredFieldInfo,\n): string | undefined {\n if (typeof field.readPermission === 'string') {\n return field.readPermission;\n }\n const metaPermission = field._meta?.readPermission;\n return typeof metaPermission === 'string' ? metaPermission : undefined;\n}\n\nexport function isTransientField(field: RegisteredFieldInfo): boolean {\n return field.transient === true || field._meta?.transient === true;\n}\n\n/**\n * A field is required when flagged required and not explicitly nullable.\n *\n * `nullable` is read from BOTH the top level and `_meta` for the same reason\n * `sensitive`/`readPermission`/`transient` are: registrations reach the\n * registry through several paths (decorator, manifest, STI merge) and land the\n * flag in either place. Checking only one side made the pair asymmetric —\n * `required` was read from both while `nullable` was read from one.\n */\nexport function isRequiredField(field: RegisteredFieldInfo): boolean {\n if (field.nullable === true || field._meta?.nullable === true) {\n return false;\n }\n return field.required === true || field._meta?.required === true;\n}\n\n/** The manifest/code default for a field, boxed; `undefined` when none. */\nexport function getCodeDefault(\n field: RegisteredFieldInfo,\n): { value: unknown } | undefined {\n if (field.default !== undefined) {\n return { value: field.default };\n }\n if (field._meta?.default !== undefined) {\n return { value: field._meta.default };\n }\n return undefined;\n}\n\n/**\n * A resolved default satisfies the required-field invariant only when it is a\n * value a form could actually submit: explicit `null` and the empty string do\n * not count (a required text field \"defaulting\" to `''` is still unfilled).\n */\nexport function isUsableRequiredDefault(\n defaultBox: { value: unknown } | undefined,\n): boolean {\n if (!defaultBox) {\n return false;\n }\n return defaultBox.value !== null && defaultBox.value !== '';\n}\n\n/**\n * Compute the code-seed visibility for every field of an object using the\n * cold-start rule (#2046): with no `ui.basic` markers anywhere on the object,\n * every field is `basic`; once any field is marked `basic: true`, unmarked\n * fields default to `advanced` (and an explicit `basic: false` is always\n * `advanced`).\n */\nexport function buildCodeSeedVisibility(\n fields: FieldDefinitionMap,\n): Map<string, FieldPolicyVisibility> {\n let hasBasicMarkers = false;\n const hints = new Map<string, FieldUIHints | undefined>();\n for (const [name, field] of fields) {\n const ui = sanitizeFieldUIHints(field._meta?.ui);\n hints.set(name, ui);\n if (ui?.basic === true) {\n hasBasicMarkers = true;\n }\n }\n\n const visibility = new Map<string, FieldPolicyVisibility>();\n for (const [name] of fields) {\n const ui = hints.get(name);\n if (ui?.basic === true) {\n visibility.set(name, 'basic');\n } else if (ui?.basic === false || hasBasicMarkers) {\n visibility.set(name, 'advanced');\n } else {\n visibility.set(name, 'basic');\n }\n }\n return visibility;\n}\n\n/** Code-seed delta for one field (visibility supplied by the cold-start pass). */\nexport function buildCodeSeedDelta(\n field: RegisteredFieldInfo,\n visibility: FieldPolicyVisibility,\n): FieldPolicyDelta {\n const ui = sanitizeFieldUIHints(field._meta?.ui);\n const help =\n typeof field.description === 'string'\n ? field.description\n : typeof field._meta?.description === 'string'\n ? field._meta.description\n : undefined;\n const codeDefault = getCodeDefault(field);\n\n return {\n ...(codeDefault ? { default: codeDefault } : {}),\n visibility,\n ...(help !== undefined ? { help } : {}),\n ...(ui?.order !== undefined ? { order: ui.order } : {}),\n ...(ui?.locked === true ? { locked: true } : {}),\n };\n}\n\n/** Code-seed grouping key (`ui.group`) — not overridable by stored rows. */\nexport function getCodeSeedGroup(field: RegisteredFieldInfo): string | null {\n return sanitizeFieldUIHints(field._meta?.ui)?.group ?? null;\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\n/** Whether a reference field stores text ids instead of native UUIDs. */\nfunction usesTextIdStorage(field: RegisteredFieldInfo): boolean {\n return field.idType === 'text' || field._meta?.idType === 'text';\n}\n\n/**\n * Type-check a parsed default value against the manifest field type.\n *\n * Explicit `null` is allowed only for optional fields (a required field with a\n * null default has no usable default). Unsupported field types reject defaults\n * outright (fail closed).\n */\nexport function assertDefaultValueMatchesFieldType(\n objectRef: string,\n fieldName: string,\n field: RegisteredFieldInfo,\n value: unknown,\n): void {\n const label = `Field policy default for \"${objectRef}.${fieldName}\"`;\n\n if (value === null) {\n if (isRequiredField(field)) {\n throw new Error(\n `${label} may not be null: the field is required, so a null default ` +\n `is never a usable default`,\n );\n }\n return;\n }\n\n switch (field.type) {\n case 'text':\n if (typeof value !== 'string') {\n throw new Error(`${label} must be a string (field type \"text\")`);\n }\n return;\n case 'integer':\n if (typeof value !== 'number' || !Number.isInteger(value)) {\n throw new Error(`${label} must be an integer (field type \"integer\")`);\n }\n return;\n case 'decimal':\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new Error(\n `${label} must be a finite number (field type \"decimal\")`,\n );\n }\n return;\n case 'boolean':\n if (typeof value !== 'boolean') {\n throw new Error(`${label} must be a boolean (field type \"boolean\")`);\n }\n return;\n case 'datetime':\n if (typeof value !== 'string' || Number.isNaN(Date.parse(value))) {\n throw new Error(\n `${label} must be a date-parseable string (field type \"datetime\")`,\n );\n }\n return;\n case 'json':\n // Any JSON value (it already survived JSON.parse).\n return;\n case 'foreignKey':\n case 'crossPackageRef':\n if (typeof value !== 'string') {\n throw new Error(\n `${label} must be a string id (field type \"${field.type}\")`,\n );\n }\n // Reference columns are native UUID on PostgreSQL/DuckDB unless the\n // field explicitly opts into text ids — a non-UUID default would\n // validate here but fail at insert time on those dialects.\n if (!usesTextIdStorage(field) && !UUID_PATTERN.test(value)) {\n throw new Error(\n `${label} must be a UUID string (the reference column stores ` +\n `native UUIDs; declare idType 'text' on the field to store ` +\n `arbitrary ids)`,\n );\n }\n return;\n default:\n throw new Error(\n `${label} is not supported: defaults cannot target ` +\n `\"${String(field.type)}\" fields`,\n );\n }\n}\n","import {\n crossPackageRef,\n field,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport {\n getCurrentTenant,\n isSuperAdminBypass,\n TenantIsolationError,\n tenantId,\n} from '@happyvertical/smrt-tenancy';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport { invalidateFieldPolicyCache } from '../cache.js';\nimport {\n assertDefaultValueMatchesFieldType,\n getFieldReadPermission,\n getObjectFieldMap,\n isRequiredField,\n isSensitiveField,\n isTransientField,\n isUsableRequiredDefault,\n type RegisteredFieldInfo,\n} from '../field-definitions.js';\nimport {\n APP_FIELD_POLICY_SCOPE_KEY,\n FIELD_POLICY_SCOPE_TYPES,\n FIELD_POLICY_VISIBILITIES,\n type FieldPolicyOptions,\n type FieldPolicyScopeType,\n type FieldPolicyVisibility,\n type ResolvedFieldPolicy,\n} from '../types.js';\n\ntype FieldPolicyIdentity = {\n objectRef: string;\n fieldName: string;\n scopeType: string;\n scopeKey: string;\n tenantId: string | null;\n userId: string | null;\n};\n\ntype FieldPolicyTransactionHandle = DatabaseInterface & {\n commit: () => Promise<void>;\n rollback: () => Promise<void>;\n};\n\n/**\n * Sparse, layered field policy override (epic #2045, issue #2047).\n *\n * One row personalizes a subset of `{defaultValue, visibility, help, label,\n * displayOrder, locked}` for a single `(objectRef, fieldName)` at one scope\n * tier. A NULL column means \"inherit from the lower layer\" (code seed → app →\n * tenant → user), so resetting a customization is a row DELETE — later\n * lower-layer changes then flow through (sparse-delta rationale, #1770).\n *\n * Rows are validated at write time against the live `ObjectRegistry` (the\n * manifest is the definition registry): unknown objects/fields are rejected,\n * defaults are type-checked, and the security rail (`sensitive` /\n * `readPermission` / `transient`) refuses stored defaults outright.\n */\n// The generated surfaces expose NO read verbs anywhere: list/get on this\n// non-@TenantScoped model would enumerate every tenant's and user's policy\n// rows to any authenticated principal, and the generated CLI invokes methods\n// over HTTP so CLI reads would be equally exposed (and unreachable once the\n// API closes them — the build-time cli↔api coherence gate enforces that).\n// Reads go through the context-scoped batch resolver\n// (FieldPolicyCollection.resolveBatch) and the server-side resolver/explain\n// APIs.\n@smrt({\n tableName: '_smrt_field_policies',\n conflictColumns: ['object_ref', 'field_name', 'scope_type', 'scope_key'],\n api: { include: ['create', 'update', 'delete'] },\n cli: {\n include: ['create', 'update', 'delete'],\n exclude: ['getDefaultValue', 'setDefaultValue'],\n },\n mcp: { include: [] },\n})\nexport class FieldPolicy extends SmrtObject {\n /** Qualified class name of the target object (`@package/name:ClassName`). */\n @field({ required: true })\n objectRef: string = '';\n\n /** Field name on the target object (validated against the registry). */\n @field({ required: true })\n fieldName: string = '';\n\n /** Scope tier this row belongs to ('app' | 'tenant' | 'user'). */\n @field({ required: true })\n scopeType: FieldPolicyScopeType = 'app';\n\n /** Owning tenant for tenant-scope rows; NULL otherwise (native UUID on PG). */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /** Owning user for user-scope rows; NULL otherwise. */\n @crossPackageRef('@happyvertical/smrt-users:User', { nullable: true })\n userId: string | null = null;\n\n /**\n * Computed uniqueness key (`userId ?? tenantId ?? '__app__'`), set in\n * `save()`. Used ONLY by `conflictColumns` so the unique index stays total\n * while `tenantId`/`userId` are nullable — mirrors `PromptOverride.context`.\n * Never read it for scoping logic; `scopeType` + the typed columns own that.\n */\n @field({ type: 'text', required: true })\n scopeKey: string = '';\n\n /** JSON-encoded default value; NULL = inherit. JSON `null` = \"default to null\". */\n @field({ type: 'text', nullable: true })\n defaultValue: string | null = null;\n\n /** Visibility override ('basic' | 'advanced' | 'hidden'); NULL = inherit. */\n @field({ type: 'text', nullable: true })\n visibility: FieldPolicyVisibility | null = null;\n\n /** Help text override; NULL = inherit (code seed: field description). */\n @field({ type: 'text', nullable: true })\n help: string | null = null;\n\n /** Label override; NULL = inherit (consumers derive from the field name). */\n @field({ type: 'text', nullable: true })\n label: string | null = null;\n\n /**\n * Sort-order override; NULL = inherit (code seed: `ui.order`). Named\n * `displayOrder` because a column literally named `order` is an SQL keyword\n * the runtime INSERT path does not quote; resolved output exposes `order`.\n */\n @field({ type: 'integer', nullable: true })\n displayOrder: number | null = null;\n\n /**\n * Org lock (app/tenant rows only): when the effective lock is true, the\n * user tier may not override this field. NULL = inherit (code seed:\n * `ui.locked`); org rows may set `false` to explicitly unlock.\n */\n @field({ type: 'boolean', nullable: true })\n locked: boolean | null = null;\n\n /** Audit attribution for #2050 (\"who changed what\"); not validated. */\n @crossPackageRef('@happyvertical/smrt-users:User', { nullable: true })\n updatedBy: string | null = null;\n\n constructor(options: FieldPolicyOptions = {}) {\n super(options);\n\n if (options.objectRef !== undefined) this.objectRef = options.objectRef;\n if (options.fieldName !== undefined) this.fieldName = options.fieldName;\n if (options.scopeType !== undefined) this.scopeType = options.scopeType;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.userId !== undefined) this.userId = options.userId;\n // Two explicit channels, never sniffed: `defaultValue` is already\n // JSON-encoded (the wire contract — generated write routes pass the\n // request body straight through, and the gear posts JSON.stringify'd\n // values), `defaultValueRaw` is a plain value that is always serialized.\n // One option carrying both meanings cannot distinguish `'\"TBD\"'` from\n // `'TBD'`, so supplying both is rejected rather than silently resolved.\n if (\n options.defaultValue !== undefined &&\n options.defaultValueRaw !== undefined\n ) {\n throw new Error(\n 'FieldPolicy accepts either defaultValue (already JSON-encoded) or ' +\n 'defaultValueRaw (a plain value to serialize), not both',\n );\n }\n if (options.defaultValueRaw !== undefined) {\n this.setDefaultValue(options.defaultValueRaw);\n } else if (options.defaultValue !== undefined) {\n this.defaultValue = options.defaultValue;\n }\n if (options.visibility !== undefined) this.visibility = options.visibility;\n if (options.help !== undefined) this.help = options.help;\n if (options.label !== undefined) this.label = options.label;\n if (options.displayOrder !== undefined) {\n this.displayOrder = options.displayOrder;\n }\n if (options.locked !== undefined) this.locked = options.locked;\n if (options.updatedBy !== undefined) this.updatedBy = options.updatedBy;\n }\n\n /** Parse the stored JSON default. `undefined` = no stored default (inherit). */\n getDefaultValue(): unknown {\n if (this.defaultValue === null || this.defaultValue === undefined) {\n return undefined;\n }\n try {\n return JSON.parse(this.defaultValue);\n } catch {\n return undefined;\n }\n }\n\n /** Serialize a default value; `undefined` clears the override (inherit). */\n setDefaultValue(value: unknown): void {\n this.defaultValue = value === undefined ? null : JSON.stringify(value);\n }\n\n override async save(): Promise<this> {\n // Runs BEFORE the persisted-identity lookup: that lookup falls back to\n // the natural key, which includes the scope key derived from these\n // columns.\n this.attributeScopeToAmbientContext();\n const previousIdentity = await this.getPersistedIdentity();\n // The caller must own the row AS PERSISTED before any mutation is\n // accepted — otherwise a foreign row could be re-scoped into the caller's\n // own tenant/user (and the identity-change path below would then delete\n // the original foreign row).\n if (previousIdentity) {\n this.assertScopeOwnedByAmbientContext(previousIdentity, 'save');\n }\n // Audit attribution (#2050): inside an ambient context the SERVER stamps\n // who wrote the row — a client-supplied `updatedBy` is overwritten, so\n // attribution cannot be spoofed through the generated write routes. A\n // context without a user id attributes to null; trusted context-less\n // flows keep whatever explicit attribution they set.\n const ambientContext = getCurrentTenant();\n if (ambientContext) {\n this.updatedBy = ambientContext.userId ?? null;\n }\n this.normalizeDefaultValueForPersistence();\n await this.validateFieldPolicy();\n // scopeKey makes the conflict-column tuple total even though tenantId and\n // userId are nullable (nullable columns would allow duplicate NULL rows).\n this.scopeKey = this.computeScopeKey();\n\n const identityChanged =\n previousIdentity &&\n (previousIdentity.objectRef !== this.objectRef ||\n previousIdentity.fieldName !== this.fieldName ||\n previousIdentity.scopeType !== this.scopeType ||\n previousIdentity.scopeKey !== this.scopeKey);\n\n const result =\n identityChanged && previousIdentity\n ? await this.saveAfterIdentityChange()\n : await super.save();\n\n if (identityChanged && previousIdentity) {\n invalidateFieldPolicyCache(previousIdentity.objectRef, this.db);\n }\n invalidateFieldPolicyCache(this.objectRef, this.db);\n return result;\n }\n\n private async saveAfterIdentityChange(): Promise<this> {\n if (typeof this.db.beginTransaction === 'function') {\n return this.saveAfterIdentityChangeInTransaction();\n }\n\n return this.saveAfterIdentityChangeWithDeferredDelete();\n }\n\n private async saveAfterIdentityChangeInTransaction(): Promise<this> {\n const originalDb = this._db;\n const originalOptionsDb = this.options.db;\n const tx = (await this.db.beginTransaction?.()) as\n | FieldPolicyTransactionHandle\n | undefined;\n\n if (!tx) {\n return this.saveAfterIdentityChangeWithDeferredDelete();\n }\n\n try {\n this._db = tx;\n this.options.db = tx;\n await super.delete();\n const result = await super.save();\n await tx.commit();\n return result;\n } catch (error) {\n try {\n await tx.rollback();\n } catch {\n // Preserve the original save error; rollback failures are secondary.\n }\n throw error;\n } finally {\n this._db = originalDb;\n this.options.db = originalOptionsDb;\n }\n }\n\n private async saveAfterIdentityChangeWithDeferredDelete(): Promise<this> {\n const previousId = this.id;\n if (!previousId) {\n return super.save();\n }\n\n const replacementId = crypto.randomUUID();\n let replacementSaved = false;\n this.id = replacementId;\n\n try {\n const result = await super.save();\n replacementSaved = true;\n await this.db.delete(this.tableName, { id: previousId });\n return result;\n } catch (error) {\n if (replacementSaved) {\n try {\n await this.db.delete(this.tableName, { id: replacementId });\n } catch {\n // Best effort cleanup keeps the original row as the source of truth.\n }\n }\n\n this.id = previousId;\n throw error;\n }\n }\n\n override async delete(): Promise<void> {\n // Authorize against the PERSISTED row, not in-memory state: the generated\n // DELETE route (and any caller holding a foreign row id) must not remove\n // app rows or another tenant's/user's rows from inside a tenant context.\n const persisted = await this.getPersistedIdentity();\n if (persisted) {\n this.assertScopeOwnedByAmbientContext(persisted, 'delete');\n }\n const objectRef = persisted?.objectRef ?? this.objectRef;\n await super.delete();\n invalidateFieldPolicyCache(objectRef, this.db);\n if (this.objectRef && this.objectRef !== objectRef) {\n invalidateFieldPolicyCache(this.objectRef, this.db);\n }\n }\n\n private async validateFieldPolicy(): Promise<void> {\n if (!this.objectRef || this.objectRef.trim() === '') {\n throw new Error('FieldPolicy.objectRef is required');\n }\n if (!this.fieldName || this.fieldName.trim() === '') {\n throw new Error('FieldPolicy.fieldName is required');\n }\n if (!FIELD_POLICY_SCOPE_TYPES.includes(this.scopeType)) {\n throw new Error(\n `FieldPolicy.scopeType must be one of ` +\n `${FIELD_POLICY_SCOPE_TYPES.join(', ')}; got \"${this.scopeType}\"`,\n );\n }\n if (\n this.visibility !== null &&\n !FIELD_POLICY_VISIBILITIES.includes(this.visibility)\n ) {\n throw new Error(\n `FieldPolicy.visibility must be null or one of ` +\n `${FIELD_POLICY_VISIBILITIES.join(', ')}; got \"${this.visibility}\"`,\n );\n }\n if (\n this.displayOrder !== null &&\n (typeof this.displayOrder !== 'number' ||\n !Number.isInteger(this.displayOrder))\n ) {\n throw new Error('FieldPolicy.displayOrder must be null or an integer');\n }\n\n this.validateScopeConsistency();\n this.validateTenantContextBoundary();\n\n const fields = await getObjectFieldMap(this.objectRef);\n const fieldDef = fields.get(this.fieldName);\n if (!fieldDef) {\n throw new Error(\n `Unknown field \"${this.fieldName}\" on \"${this.objectRef}\"`,\n );\n }\n if (fieldDef._meta?.__smrtSystemField === true) {\n throw new Error(\n `Field \"${this.fieldName}\" on \"${this.objectRef}\" is a framework ` +\n `system field and is not policy-addressable`,\n );\n }\n if (fieldDef.type === 'oneToMany' || fieldDef.type === 'manyToMany') {\n throw new Error(\n `Field \"${this.fieldName}\" on \"${this.objectRef}\" is a relationship ` +\n `pseudo-field and is not policy-addressable`,\n );\n }\n // The resolver excludes STI meta storage fields, so accepting a row here\n // would persist policy that silently never applies.\n if (fieldDef.type === 'meta') {\n throw new Error(\n `Field \"${this.fieldName}\" on \"${this.objectRef}\" is STI meta ` +\n `storage and is not policy-addressable`,\n );\n }\n\n if (this.locked !== null && this.scopeType === 'user') {\n throw new Error(\n 'FieldPolicy.locked may only be set on org rows (app or tenant scope)',\n );\n }\n\n if (this.defaultValue !== null) {\n this.validateDefaultAgainstSecurityRail(fieldDef);\n const parsed = this.parseDefaultValueOrThrow();\n assertDefaultValueMatchesFieldType(\n this.objectRef,\n this.fieldName,\n fieldDef,\n parsed,\n );\n }\n\n // Required-field invariant (write side): demoting a required field to\n // advanced/hidden needs a usable resolved default — either this row's own\n // default or one resolved by the org tiers (code → app → tenant chain,\n // INCLUDING cascading ancestor-tenant defaults). The resolver enforces\n // the same rule again at read time (safety net) because a DIFFERENT row's\n // later deletion can invalidate what held here.\n const demotesRequiredField =\n (this.visibility === 'advanced' || this.visibility === 'hidden') &&\n isRequiredField(fieldDef);\n const ownDefault =\n demotesRequiredField && this.defaultValue !== null\n ? { value: this.parseDefaultValueOrThrow() }\n : undefined;\n const needsOrgDefault =\n demotesRequiredField && !isUsableRequiredDefault(ownDefault);\n const needsLockCheck = this.scopeType === 'user';\n\n if (needsOrgDefault || needsLockCheck) {\n const orgPolicy = await this.resolveOrgTierFieldPolicy();\n\n if (needsOrgDefault) {\n const orgDefault = orgPolicy?.hasDefault\n ? { value: orgPolicy.defaultValue }\n : undefined;\n if (!isUsableRequiredDefault(orgDefault)) {\n throw new Error(\n `Cannot set visibility \"${this.visibility}\" for required field ` +\n `\"${this.objectRef}.${this.fieldName}\": no resolved default ` +\n `exists at or below this layer`,\n );\n }\n }\n\n // Org lock enforcement (write side): the effective lock includes\n // cascading ancestor-tenant locks, not just the direct tenant row. The\n // resolver additionally skips the user layer at read time whenever the\n // org tiers resolve locked, so stale user rows cannot bypass a lock.\n if (needsLockCheck && orgPolicy?.locked) {\n throw new Error(\n `Field \"${this.objectRef}.${this.fieldName}\" is locked by org ` +\n `policy; user-scope overrides are not allowed`,\n );\n }\n }\n }\n\n /**\n * Effective org-tier (code → app → tenant hierarchy) policy for this row's\n * field, computed by the RESOLVER so write-time checks share the one\n * precedence implementation — including ancestor-tenant cascades via the\n * default hierarchy loader. The chain tenant is the row's own tenant for\n * tenant-scope rows, the ambient context tenant for user-scope rows, and\n * none for app-scope rows (their only lower layer is the code seed).\n *\n * On updates the resolver sees this row's PERSISTED version (there is no\n * self-exclusion), so a save that removes the only default while demoting\n * can pass here; the resolver-side safety net stays authoritative at read\n * time.\n */\n private async resolveOrgTierFieldPolicy(): Promise<\n ResolvedFieldPolicy | undefined\n > {\n const tenantIdForChain =\n this.scopeType === 'tenant'\n ? this.tenantId\n : this.scopeType === 'user'\n ? (getCurrentTenant()?.tenantId ?? null)\n : null;\n\n // Dynamic import: the resolver statically imports the collection, which\n // statically imports this model.\n const { resolveFieldPolicy } = await import('../field-policy-resolver.js');\n const resolved = await resolveFieldPolicy(this.objectRef, {\n tenantId: tenantIdForChain,\n db: this.options.db ?? this.options.persistence,\n });\n return resolved.fields[this.fieldName];\n }\n\n /**\n * Exactly-one-owner scope shape: app rows carry neither id, tenant rows\n * carry only `tenantId`, user rows carry only `userId` (the user tier is\n * keyed by user alone so preferences follow the user across tenants).\n */\n private validateScopeConsistency(): void {\n if (this.scopeType === 'app') {\n if (this.tenantId !== null || this.userId !== null) {\n throw new Error(\n 'App-scope field policy rows must have tenantId and userId null',\n );\n }\n return;\n }\n if (this.scopeType === 'tenant') {\n if (!this.tenantId || this.userId !== null) {\n throw new Error(\n 'Tenant-scope field policy rows must set tenantId and leave userId null',\n );\n }\n return;\n }\n if (!this.userId || this.tenantId !== null) {\n throw new Error(\n 'User-scope field policy rows must set userId and leave tenantId null',\n );\n }\n }\n\n /** `userId ?? tenantId ?? '__app__'` — the `conflictColumns` scope key. */\n private computeScopeKey(): string {\n return this.userId ?? this.tenantId ?? APP_FIELD_POLICY_SCOPE_KEY;\n }\n\n /**\n * Derive a scope-owner column the transport could not carry (#2047).\n *\n * Core's mass-assignment guard treats `tenantId` as server-managed and\n * strips it from EVERY generated create/update body, and `FieldPolicy` is\n * deliberately not `@TenantScoped`, so the tenancy interceptor never\n * repopulates it. A `POST {scopeType:'tenant', tenantId}` therefore always\n * reached `validateScopeConsistency` with `tenantId === null`, making the\n * org tier write-dead over every generated surface — the model, as the\n * single validation authority for these rows, fills it in instead.\n *\n * This grants nothing: `assertScopeOwnedByAmbientContext` already requires\n * a non-bypass caller's tenant/user rows to name exactly the ambient\n * tenant/user, so the derived value is the ONLY value that could ever have\n * been accepted. An explicit value is never overwritten (a super-admin\n * bypass caller keeps naming other scopes), and with no ambient context\n * nothing is derived — scope-shape validation rejects the row as before.\n * Consequence: a bypass caller writing ANOTHER tenant's row must pass\n * `tenantId` through a server-side model call, because the generated routes\n * still strip it.\n */\n private attributeScopeToAmbientContext(): void {\n const context = getCurrentTenant();\n if (!context) {\n return;\n }\n if (this.scopeType === 'tenant' && this.tenantId === null) {\n this.tenantId = context.tenantId;\n }\n if (this.scopeType === 'user' && this.userId === null) {\n this.userId = context.userId ?? null;\n }\n }\n\n /**\n * Fail-closed write boundary against the ambient tenant context, applied to\n * the NEW scope on save and to the PERSISTED scope on save/delete of an\n * existing row.\n *\n * With NO ambient identity at all (no tenant context entered — e.g. a\n * runtime API deployment whose auth middleware never enters the tenancy\n * ALS), only app-scope rows are accepted: tenant- and user-scope rows are\n * unattributable without a context, so they are rejected outright rather\n * than allowed by default. Inside a non-bypass context, a caller may only\n * touch rows for its own tenant, and user rows only for its own user id —\n * a context that carries NO user id may not touch the user tier at all.\n * App-wide rows then require super-admin bypass (or a context-less/system\n * caller).\n *\n * Package rule: a missing identity component DENIES, it never skips.\n */\n private assertScopeOwnedByAmbientContext(\n scope: {\n scopeType: string;\n tenantId: string | null;\n userId: string | null;\n },\n operation: 'save' | 'delete',\n ): void {\n const context = getCurrentTenant();\n\n if (!context) {\n if (scope.scopeType === 'tenant' || scope.scopeType === 'user') {\n throw new TenantIsolationError(\n `${scope.scopeType === 'tenant' ? 'Tenant' : 'User'}-scope field ` +\n `policy ${operation}s require an ambient tenant context (or ` +\n `super-admin bypass); without one the caller identity is ` +\n `unattributable`,\n );\n }\n return;\n }\n\n if (isSuperAdminBypass()) {\n return;\n }\n\n if (scope.scopeType === 'app') {\n throw new TenantIsolationError(\n `App-scope field policy ${operation}s are not allowed inside a ` +\n 'tenant context (use a system context or super-admin bypass)',\n { tenantId: context.tenantId },\n );\n }\n\n if (scope.scopeType === 'tenant' && scope.tenantId !== context.tenantId) {\n throw new TenantIsolationError(\n `Tenant isolation violation in FieldPolicy.${operation}: context ` +\n `tenant is '${context.tenantId}' but the row belongs to ` +\n `'${scope.tenantId}'`,\n {\n tenantId: context.tenantId,\n attemptedTenantId: scope.tenantId ?? undefined,\n },\n );\n }\n\n if (scope.scopeType === 'user') {\n // A MISSING identity component denies, never skips. Tenancy adapters\n // populate `permissions` while leaving `userId` undefined whenever no\n // `resolveUserId` hook is configured (API-key auth, service principals,\n // background jobs, a bare `withTenant({ tenantId })`), and user rows\n // carry `tenantId: null` by design — so skipping the check here would\n // let any such caller create, re-scope, or delete ANY user's rows in\n // ANY tenant, with the generated PUT echoing the row back as a read\n // primitive.\n if (context.userId === undefined) {\n throw new TenantIsolationError(\n `User-scope field policy ${operation}s require an ambient context ` +\n `that carries a user id; this context cannot attribute a ` +\n `user-scope write`,\n { tenantId: context.tenantId },\n );\n }\n if (scope.userId !== context.userId) {\n throw new TenantIsolationError(\n `Tenant isolation violation in FieldPolicy.${operation}: context ` +\n `user is '${context.userId}' but the row belongs to ` +\n `'${scope.userId}'`,\n { tenantId: context.tenantId },\n );\n }\n }\n }\n\n private validateTenantContextBoundary(): void {\n this.assertScopeOwnedByAmbientContext(\n {\n scopeType: this.scopeType,\n tenantId: this.tenantId,\n userId: this.userId,\n },\n 'save',\n );\n }\n\n private validateDefaultAgainstSecurityRail(\n fieldDef: RegisteredFieldInfo,\n ): void {\n if (isTransientField(fieldDef)) {\n throw new Error(\n `Cannot store a default for transient field ` +\n `\"${this.objectRef}.${this.fieldName}\"`,\n );\n }\n if (isSensitiveField(fieldDef)) {\n throw new Error(\n `Cannot store a default for sensitive field ` +\n `\"${this.objectRef}.${this.fieldName}\"`,\n );\n }\n const readPermission = getFieldReadPermission(fieldDef);\n if (readPermission) {\n throw new Error(\n `Cannot store a default for read-permission-gated field ` +\n `\"${this.objectRef}.${this.fieldName}\" (requires \"${readPermission}\")`,\n );\n }\n }\n\n private parseDefaultValueOrThrow(): unknown {\n try {\n return JSON.parse(this.defaultValue as string);\n } catch (error) {\n // The overwhelmingly likely cause is a PLAIN string sent through the\n // encoded channel (`{ defaultValue: 'Net 30' }`), which is also the most\n // natural-looking call — so name the fix instead of only reporting the\n // parse failure.\n throw new Error(\n `FieldPolicy default for \"${this.objectRef}.${this.fieldName}\" is ` +\n `not valid JSON: ${\n error instanceof Error ? error.message : String(error)\n }. The \"defaultValue\" option is the already-encoded channel; pass a ` +\n `plain value as \"defaultValueRaw\" (or call setDefaultValue()).`,\n );\n }\n }\n\n /**\n * The persisted row this save/delete would replace, looked up by primary\n * key and — when that misses — by the NATURAL key.\n *\n * The natural-key fallback is load-bearing for authorization: every\n * generated create arrives with a freshly minted UUID, so a primary-key\n * lookup always misses, yet the `conflictColumns` upsert still overwrites\n * whatever row already occupies `(objectRef, fieldName, scopeType,\n * scopeKey)`. Authorizing on the primary key alone therefore skipped the\n * persisted-scope guard on exactly the path that can replace an existing\n * row's contents.\n */\n private async getPersistedIdentity(): Promise<FieldPolicyIdentity | null> {\n const existing =\n (await this.getPersistedRowById()) ??\n (await this.getPersistedRowByNaturalKey());\n if (!existing) {\n return null;\n }\n\n const row = existing;\n const read = (camel: string, snake: string, fallback: string): string => {\n if (row[camel] !== undefined && row[camel] !== null) {\n return String(row[camel]);\n }\n if (row[snake] !== undefined && row[snake] !== null) {\n return String(row[snake]);\n }\n return fallback;\n };\n const readNullable = (camel: string, snake: string): string | null => {\n const value = row[camel] !== undefined ? row[camel] : row[snake];\n return value === undefined || value === null ? null : String(value);\n };\n\n return {\n objectRef: read('objectRef', 'object_ref', this.objectRef),\n fieldName: read('fieldName', 'field_name', this.fieldName),\n scopeType: read('scopeType', 'scope_type', this.scopeType),\n scopeKey: read('scopeKey', 'scope_key', this.scopeKey),\n tenantId: readNullable('tenantId', 'tenant_id'),\n userId: readNullable('userId', 'user_id'),\n };\n }\n\n private async getPersistedRowById(): Promise<Record<string, unknown> | null> {\n if (!this.id) {\n return null;\n }\n const row = await this.db.get(this.tableName, { id: this.id });\n return (row as Record<string, unknown> | undefined) ?? null;\n }\n\n /**\n * The row occupying this row's `conflictColumns` tuple, if any. Column\n * names are the physical snake_case ones: this reads the driver directly\n * rather than through a collection (the model has no collection handle).\n */\n private async getPersistedRowByNaturalKey(): Promise<Record<\n string,\n unknown\n > | null> {\n if (!this.objectRef || !this.fieldName) {\n return null;\n }\n const row = await this.db.get(this.tableName, {\n object_ref: this.objectRef,\n field_name: this.fieldName,\n scope_type: this.scopeType,\n scope_key: this.computeScopeKey(),\n });\n return (row as Record<string, unknown> | undefined) ?? null;\n }\n\n private normalizeDefaultValueForPersistence(): void {\n const raw = this.defaultValue as unknown;\n\n if (raw === null || raw === undefined) {\n this.defaultValue = null;\n return;\n }\n\n if (typeof raw === 'string') {\n this.defaultValue = raw;\n return;\n }\n\n // Non-string writes (e.g. a plain value assigned directly) serialize so\n // the column always stores a JSON string.\n this.defaultValue = JSON.stringify(raw);\n }\n}\n","import { SmrtCollection, smrt } from '@happyvertical/smrt-core';\nimport { getCurrentTenant } from '@happyvertical/smrt-tenancy';\nimport {\n getFieldReadPermission,\n getObjectFieldMap,\n isSensitiveField,\n isTransientField,\n} from '../field-definitions.js';\nimport { FieldPolicy } from '../models/FieldPolicy.js';\nimport type {\n FieldPolicyBatchResult,\n ResolvedObjectFieldPolicy,\n} from '../types.js';\n\n/** Upper bound on objectRefs per batch call — bounds registry/db work per request. */\nconst MAX_BATCH_OBJECT_REFS = 100;\n\n/**\n * Collection surface for {@link FieldPolicy} rows plus the batch resolve\n * action consumed by client bootstrapping (#2048).\n *\n * `resolveBatch` is a custom collection-scoped action (NOT a system route —\n * core's generated system trio is closed): the generated route parses the\n * body, calls the method on the app-configured collection instance (which\n * carries the app database), and serializes the plain result.\n *\n * Exposure note: a decorated collection's config becomes the RUNTIME registry\n * authority for its item class (core merges the collection registration onto\n * the item slot), while build-time generation reads each manifest object's\n * own config. This config therefore mirrors FieldPolicy's API posture —\n * writes open plus the batch action, reads CLOSED (generated list/get would\n * enumerate every tenant's/user's rows) — and closes the runtime CLI/MCP\n * surfaces entirely (the ContentContributions precedent; the cli↔api\n * coherence gate does not admit standard CRUD entries on a collection's\n * `cli.include`). Keep the api include lists in lockstep with FieldPolicy's.\n */\n// `conflictColumns` MUST mirror FieldPolicy's. A decorated collection emits\n// its OWN schema for the item's table (`_smrt_field_policies`), and without\n// the natural key that schema falls back to SmrtObject's default unique\n// `(slug, context)` index. Manifest-driven migrations aggregate both schemas\n// onto the one physical table, so the stray index would reject legitimate\n// layered rows — every policy row has a NULL slug and context, and the app,\n// tenant, and user rows for a field are distinct only by the real natural key.\n@smrt({\n conflictColumns: ['object_ref', 'field_name', 'scope_type', 'scope_key'],\n api: {\n include: ['create', 'update', 'delete', 'resolveBatch'],\n routes: {\n resolveBatch: {\n scope: 'collection',\n method: 'POST',\n path: 'resolve',\n },\n },\n },\n cli: false,\n mcp: false,\n})\nexport class FieldPolicyCollection extends SmrtCollection<FieldPolicy> {\n static readonly _itemClass = FieldPolicy;\n\n /** All app-scope rows for an object, keyed by field name. */\n async getAppRows(objectRef: string): Promise<Map<string, FieldPolicy>> {\n const rows = await this.list({\n where: { objectRef, scopeType: 'app' },\n });\n const byField = new Map<string, FieldPolicy>();\n for (const row of rows) {\n byField.set(row.fieldName, row);\n }\n return byField;\n }\n\n /**\n * All tenant-scope rows for an object across a tenant chain, keyed\n * `tenantId → fieldName → row`.\n */\n async getTenantRows(\n objectRef: string,\n tenantIds: string[],\n ): Promise<Map<string, Map<string, FieldPolicy>>> {\n const byTenant = new Map<string, Map<string, FieldPolicy>>();\n if (tenantIds.length === 0) {\n return byTenant;\n }\n\n const rows = await this.list({\n where: { objectRef, scopeType: 'tenant', 'tenantId in': tenantIds },\n });\n for (const row of rows) {\n if (!row.tenantId) {\n continue;\n }\n let byField = byTenant.get(row.tenantId);\n if (!byField) {\n byField = new Map<string, FieldPolicy>();\n byTenant.set(row.tenantId, byField);\n }\n byField.set(row.fieldName, row);\n }\n return byTenant;\n }\n\n /** All user-scope rows for an object and user, keyed by field name. */\n async getUserRows(\n objectRef: string,\n userId: string,\n ): Promise<Map<string, FieldPolicy>> {\n const rows = await this.list({\n where: { objectRef, scopeType: 'user', userId },\n });\n const byField = new Map<string, FieldPolicy>();\n for (const row of rows) {\n byField.set(row.fieldName, row);\n }\n return byField;\n }\n\n /**\n * Resolve merged field policy for a set of objectRefs for the CURRENT\n * caller, gated for public consumption.\n *\n * Caller identity comes exclusively from the ambient tenant context\n * (established by the app's auth hook, e.g. smrt-users' session context) —\n * the request body cannot select another tenant or user (fail closed).\n * Server-side consumers wanting explicit identities call\n * `resolveFieldPolicy()` directly instead.\n *\n * Field gating mirrors the REST serializer's derivation (`sensitive` /\n * `readPermission` read from both the top level and `_meta`): sensitive and\n * read-permission-gated fields are ABSENT from the response for every\n * caller (the generated action route cannot convey per-caller grants to\n * this method, so gated fields fail closed), and transient fields are\n * stripped for parity with generated client field definitions.\n */\n async resolveBatch(\n options: { objectRefs?: string[] } = {},\n ): Promise<FieldPolicyBatchResult> {\n const rawRefs = options.objectRefs;\n if (!Array.isArray(rawRefs) || rawRefs.length === 0) {\n throw new Error(\n 'resolveBatch requires a non-empty \"objectRefs\" string array',\n );\n }\n if (rawRefs.some((ref) => typeof ref !== 'string' || ref.trim() === '')) {\n throw new Error('resolveBatch objectRefs must be non-empty strings');\n }\n const objectRefs = [...new Set(rawRefs)];\n if (objectRefs.length > MAX_BATCH_OBJECT_REFS) {\n throw new Error(\n `resolveBatch accepts at most ${MAX_BATCH_OBJECT_REFS} objectRefs ` +\n `per call (got ${objectRefs.length})`,\n );\n }\n\n const context = getCurrentTenant();\n const tenantId = context?.tenantId ?? null;\n const userId = context?.userId ?? null;\n\n // Dynamic import breaks the module cycle with the resolver (which imports\n // this collection statically for its row reads).\n const { resolveFieldPolicy } = await import('../field-policy-resolver.js');\n\n const policies: Record<string, ResolvedObjectFieldPolicy> = {};\n for (const objectRef of objectRefs) {\n const resolved = await resolveFieldPolicy(objectRef, {\n tenantId,\n userId,\n db: this.db,\n });\n policies[objectRef] = await this.gateResolvedForPublicResponse(resolved);\n }\n\n return { policies };\n }\n\n private async gateResolvedForPublicResponse(\n resolved: ResolvedObjectFieldPolicy,\n ): Promise<ResolvedObjectFieldPolicy> {\n const fieldMap = await getObjectFieldMap(resolved.objectRef);\n const fields: ResolvedObjectFieldPolicy['fields'] = {};\n\n for (const [fieldName, policy] of Object.entries(resolved.fields)) {\n const fieldDef = fieldMap.get(fieldName);\n if (!fieldDef) {\n continue;\n }\n if (\n isSensitiveField(fieldDef) ||\n getFieldReadPermission(fieldDef) !== undefined ||\n isTransientField(fieldDef)\n ) {\n continue;\n }\n fields[fieldName] = policy;\n }\n\n return { objectRef: resolved.objectRef, fields };\n }\n}\n","import { importWorkspaceModule } from '@happyvertical/smrt-core/utils/import-workspace-module';\nimport {\n assertTenantReadAllowed,\n getCurrentTenant,\n isSuperAdminBypass,\n TenantIsolationError,\n} from '@happyvertical/smrt-tenancy';\nimport { getCachedFieldPolicy, setCachedFieldPolicy } from './cache.js';\nimport { FieldPolicyCollection } from './collections/FieldPolicyCollection.js';\nimport {\n buildCodeSeedDelta,\n buildCodeSeedVisibility,\n type FieldDefinitionMap,\n getCodeSeedGroup,\n getObjectFieldMap,\n isRequiredField,\n isUsableRequiredDefault,\n} from './field-definitions.js';\nimport type { FieldPolicy } from './models/FieldPolicy.js';\nimport type {\n ExplainedObjectFieldPolicy,\n FieldPolicyDelta,\n FieldPolicyLayerContribution,\n FieldPolicyTenantHierarchyProvider,\n FieldPolicyTenantNode,\n FieldPolicyUsersModule,\n FieldPolicyVisibility,\n ResolvedFieldPolicy,\n ResolvedObjectFieldPolicy,\n ResolveFieldPolicyOptions,\n SmrtClassOptions,\n} from './types.js';\n\n/** Accumulated merge state for one field while layers apply. */\ninterface MergedPolicyState {\n default?: { value: unknown };\n visibility: FieldPolicyVisibility;\n help?: string;\n label?: string;\n order?: number;\n locked?: boolean;\n}\n\n/**\n * Resolve the merged field policy for `objectRef` in the given\n * `(tenantId, userId)` context: code seed → app rows → tenant rows (hierarchy\n * walk root → leaf) → user rows. Defaults AND visibility both resolve through\n * the user tier.\n *\n * Results are cached per `(database, objectRef, tenantId, userId)` with a\n * short TTL; `FieldPolicy.save()`/`.delete()` invalidate the object's entries.\n */\nexport async function resolveFieldPolicy(\n objectRef: string,\n options: ResolveFieldPolicyOptions = {},\n): Promise<ResolvedObjectFieldPolicy> {\n const explained = await resolveFieldPolicyExplained(objectRef, options);\n return { objectRef: explained.objectRef, fields: explained.fields };\n}\n\n/**\n * Explain variant: the merged result plus ordered per-layer contributions for\n * each field, so the gear UI (#2049, \"shows inherited base\") and the control\n * panel (#2050, effective-value-per-layer) never re-derive precedence.\n *\n * A user-layer row suppressed by an effective org lock is omitted from the\n * layer list too — the listed layers always reproduce the merged result.\n */\nexport async function resolveFieldPolicyExplained(\n objectRef: string,\n options: ResolveFieldPolicyOptions = {},\n): Promise<ExplainedObjectFieldPolicy> {\n const tenantId = options.tenantId ?? null;\n const userId = options.userId ?? null;\n\n assertResolutionAllowedInContext(tenantId, userId);\n\n let collection: FieldPolicyCollection | null = null;\n let cacheDb: unknown = options.db;\n if (options.db) {\n collection = await FieldPolicyCollection.create({ db: options.db });\n cacheDb = collection.db;\n }\n\n // The field map load also validates objectRef against the live registry, so\n // unknown refs throw before the cache is consulted.\n const fieldMap = await getObjectFieldMap(objectRef);\n\n const cached = getCachedFieldPolicy(\n objectRef,\n tenantId,\n userId,\n cacheDb,\n options.tenantHierarchyLoader,\n );\n if (cached) {\n return cached;\n }\n\n const policyFields = selectPolicyAddressableFields(fieldMap);\n const codeVisibility = buildCodeSeedVisibility(fieldMap);\n\n const appRows = collection\n ? await collection.getAppRows(objectRef)\n : new Map<string, FieldPolicy>();\n\n let survivingChain: FieldPolicyTenantNode[] = [];\n let tenantRows = new Map<string, Map<string, FieldPolicy>>();\n if (collection && tenantId) {\n const chain = await resolveTenantChain(tenantId, options);\n // Permission-inheritance breaks are chain-STRUCTURAL (node flags, not\n // rows), so a break at node i discards every earlier tenant contribution\n // for ALL fields — the merge baseline resets to the app-layer state\n // there. Only the suffix from the LAST break participates in merging and\n // in the explained layers, so sequentially replaying the listed deltas\n // always reproduces the merged result.\n survivingChain = selectSurvivingChainSuffix(chain);\n tenantRows = await collection.getTenantRows(\n objectRef,\n survivingChain.map((node) => node.id),\n );\n }\n\n const userRows =\n collection && userId\n ? await collection.getUserRows(objectRef, userId)\n : new Map<string, FieldPolicy>();\n\n const fields: Record<string, ResolvedFieldPolicy> = {};\n const layers: Record<string, FieldPolicyLayerContribution[]> = {};\n\n for (const [fieldName, fieldDef] of policyFields) {\n const contributions: FieldPolicyLayerContribution[] = [];\n\n const codeDelta = buildCodeSeedDelta(\n fieldDef,\n codeVisibility.get(fieldName) ?? 'basic',\n );\n contributions.push({ layer: 'code', delta: codeDelta });\n\n let state: MergedPolicyState = applyDelta(\n { visibility: 'basic' },\n codeDelta,\n );\n\n const appRow = appRows.get(fieldName);\n if (appRow) {\n const delta = rowToDelta(appRow);\n contributions.push({ layer: 'app', delta });\n state = applyDelta(state, delta);\n }\n\n // Tenant chain walk, root → leaf, over the surviving suffix only (nodes\n // before the last permission-inheritance break contribute nothing — see\n // selectSurvivingChainSuffix). Equivalent to smrt-features' baseline\n // walk, but the explained contributions never list discarded ancestors.\n for (const node of survivingChain) {\n const row = tenantRows.get(node.id)?.get(fieldName);\n if (row) {\n const delta = rowToDelta(row);\n contributions.push({ layer: 'tenant', tenantId: node.id, delta });\n state = applyDelta(state, delta);\n }\n }\n\n // Org lock: when the code/app/tenant tiers resolve locked, the user tier\n // is skipped entirely — a stale user row cannot bypass a later lock.\n const orgLocked = state.locked === true;\n const userRow = userId ? userRows.get(fieldName) : undefined;\n if (userRow && !orgLocked) {\n const delta = rowToDelta(userRow);\n contributions.push({ layer: 'user', userId: userId as string, delta });\n state = applyDelta(state, delta);\n }\n\n // Resolver-side required-field safety net: a required field with no\n // usable resolved default is ALWAYS visible, regardless of stored\n // visibility — write-time enforcement alone breaks when a DIFFERENT row's\n // deletion removes the default a demotion relied on.\n const required = isRequiredField(fieldDef);\n let visibilityForced = false;\n if (\n required &&\n !isUsableRequiredDefault(state.default) &&\n state.visibility !== 'basic'\n ) {\n state = { ...state, visibility: 'basic' };\n visibilityForced = true;\n }\n\n fields[fieldName] = {\n fieldName,\n hasDefault: state.default !== undefined,\n defaultValue: state.default?.value,\n visibility: state.visibility,\n help: state.help ?? null,\n label: state.label ?? null,\n order: state.order ?? null,\n group: getCodeSeedGroup(fieldDef),\n locked: state.locked === true,\n required,\n ...(visibilityForced ? { visibilityForced: true } : {}),\n };\n layers[fieldName] = contributions;\n }\n\n const explained: ExplainedObjectFieldPolicy = { objectRef, fields, layers };\n setCachedFieldPolicy(\n objectRef,\n tenantId,\n userId,\n cacheDb,\n explained,\n options.tenantHierarchyLoader,\n );\n return explained;\n}\n\n/**\n * Fail-closed isolation guard: an active non-bypass tenant context may only\n * resolve its own tenant and its own user. App-only resolution (`tenantId`\n * null) is always allowed — app rows are global data.\n *\n * Mirrors the write-side rule in `FieldPolicy`: a MISSING identity component\n * denies, it never skips. A context that carries permissions but no user id\n * (no `resolveUserId` hook configured — API-key auth, service principals,\n * background jobs) must not be able to read any user's resolved policy.\n * Context-LESS callers stay allowed: `resolveFieldPolicy` is a trusted\n * server-side API, and the public `resolveBatch` route never lets a request\n * body select a user — it takes identity from the ambient context alone.\n */\nfunction assertResolutionAllowedInContext(\n tenantId: string | null,\n userId: string | null,\n): void {\n if (tenantId) {\n assertTenantReadAllowed(tenantId, 'resolveFieldPolicy');\n }\n\n if (!userId) {\n return;\n }\n const context = getCurrentTenant();\n if (!context || isSuperAdminBypass()) {\n return;\n }\n if (context.userId === undefined) {\n throw new TenantIsolationError(\n `Tenant isolation violation in resolveFieldPolicy: the ambient ` +\n `context carries no user id, so user-scope resolution for ` +\n `'${userId}' is not attributable`,\n { tenantId: context.tenantId },\n );\n }\n if (context.userId !== userId) {\n throw new TenantIsolationError(\n `Tenant isolation violation in resolveFieldPolicy: context user is ` +\n `'${context.userId}' but resolution requested '${userId}'`,\n { tenantId: context.tenantId },\n );\n }\n}\n\n/**\n * Fields that participate in policy resolution: everything except injected\n * framework system fields, relationship pseudo-fields, and STI meta\n * internals (matching the exclusions of the generated web field definitions).\n */\nfunction selectPolicyAddressableFields(\n fieldMap: FieldDefinitionMap,\n): FieldDefinitionMap {\n const selected: FieldDefinitionMap = new Map();\n for (const [name, field] of fieldMap) {\n if (field._meta?.__smrtSystemField === true) {\n continue;\n }\n if (\n field.type === 'oneToMany' ||\n field.type === 'manyToMany' ||\n field.type === 'meta'\n ) {\n continue;\n }\n selected.set(name, field);\n }\n return selected;\n}\n\n/** A stored row's sparse contribution (NULL columns contribute nothing). */\nfunction rowToDelta(row: FieldPolicy): FieldPolicyDelta {\n const delta: FieldPolicyDelta = {};\n\n if (row.defaultValue !== null && row.defaultValue !== undefined) {\n try {\n delta.default = { value: JSON.parse(row.defaultValue) };\n } catch {\n // Unparseable stored JSON (should be prevented by save-time validation)\n // contributes nothing rather than poisoning resolution.\n }\n }\n if (row.visibility !== null && row.visibility !== undefined) {\n delta.visibility = row.visibility;\n }\n if (row.help !== null && row.help !== undefined) {\n delta.help = row.help;\n }\n if (row.label !== null && row.label !== undefined) {\n delta.label = row.label;\n }\n if (row.displayOrder !== null && row.displayOrder !== undefined) {\n delta.order = row.displayOrder;\n }\n if (row.locked !== null && row.locked !== undefined) {\n delta.locked = row.locked;\n }\n\n return delta;\n}\n\nfunction applyDelta(\n state: MergedPolicyState,\n delta: FieldPolicyDelta,\n): MergedPolicyState {\n return {\n default: delta.default ?? state.default,\n visibility: delta.visibility ?? state.visibility,\n help: delta.help ?? state.help,\n label: delta.label ?? state.label,\n order: delta.order ?? state.order,\n locked: delta.locked ?? state.locked,\n };\n}\n\n/**\n * The chain suffix that actually participates in merging: nodes from the\n * LAST permission-inheritance break onward (a node breaks inheritance when\n * its parent does not cascade permissions or it does not accept them —\n * smrt-features semantics). Everything before the last break is discarded\n * for every field, so it is excluded from both merging and the explained\n * layer contributions.\n */\nfunction selectSurvivingChainSuffix(\n chain: FieldPolicyTenantNode[],\n): FieldPolicyTenantNode[] {\n let survivingStart = 0;\n for (let index = 1; index < chain.length; index++) {\n const inherits =\n chain[index - 1].cascadePermissions && chain[index].inheritPermissions;\n if (!inherits) {\n survivingStart = index;\n }\n }\n return chain.slice(survivingStart);\n}\n\nasync function resolveTenantChain(\n tenantId: string,\n options: ResolveFieldPolicyOptions,\n): Promise<FieldPolicyTenantNode[]> {\n const loader = options.tenantHierarchyLoader || defaultTenantHierarchyLoader;\n const provider = await loader({ db: options.db } as SmrtClassOptions);\n\n if (provider) {\n const chain = await provider.getChain(tenantId);\n if (chain.length > 0) {\n return chain;\n }\n }\n\n // Flat-tenant fallback (no hierarchy provider, or the provider does not\n // know the tenant): treat the tenant as a single-node chain.\n return [{ id: tenantId, inheritPermissions: true, cascadePermissions: true }];\n}\n\n/**\n * Default hierarchy loader: dynamic-imports `@happyvertical/smrt-users` (the\n * smrt-features precedent — a loader function, not a container registration)\n * and returns `null` when it is not installed so resolution degrades to the\n * flat-tenant fallback.\n */\nasync function defaultTenantHierarchyLoader(\n options: SmrtClassOptions,\n): Promise<FieldPolicyTenantHierarchyProvider | null> {\n try {\n const usersModule = await importWorkspaceModule<FieldPolicyUsersModule>({\n packageName: '@happyvertical/smrt-users',\n sourceEntry: 'packages/users/src/collections/index.ts',\n purpose: 'tenant-aware field policy resolution',\n });\n\n const tenantCollection = await usersModule.TenantCollection.create(options);\n return {\n async getChain(tenantId: string): Promise<FieldPolicyTenantNode[]> {\n const tenant = await tenantCollection.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const ancestors = await tenantCollection.getAncestorsFromRoot(tenantId);\n return [...ancestors, tenant].map((node) => ({\n id: String(node.id),\n inheritPermissions: Boolean(node.inheritPermissions),\n cascadePermissions: Boolean(node.cascadePermissions),\n }));\n },\n };\n } catch (error) {\n if (isMissingUsersDependency(error)) {\n return null;\n }\n throw error;\n }\n}\n\n/** Node's missing-module message shapes, capturing the quoted specifier. */\nconst MISSING_MODULE_TARGET_PATTERN =\n /Cannot find (?:package|module) '([^']+)'/;\n\n/** Whether a missing-module TARGET specifier is smrt-users (or a subpath). */\nfunction isUsersSpecifier(target: string): boolean {\n return (\n target === '@happyvertical/smrt-users' ||\n target.startsWith('@happyvertical/smrt-users/')\n );\n}\n\n/**\n * Whether an import failure means `@happyvertical/smrt-users` is simply not\n * installed (→ flat-tenant fallback) rather than installed-but-broken\n * (→ rethrow, surfacing the problem instead of silently losing ancestor\n * locks/defaults).\n *\n * The decision is made on the missing-module TARGET parsed from Node's\n * `Cannot find package/module '<specifier>'` message (walking the full\n * `cause` chain): only a target that IS smrt-users (or one of its subpaths)\n * counts. A transitive failure INSIDE an installed smrt-users names the\n * other package as the target — with the users path merely appearing as the\n * importer — and therefore rethrows. `importWorkspaceModule`'s own\n * source-fallback wrapper (\"Failed to load @happyvertical/smrt-users for\n * ...\") is also accepted: it is thrown only when the users package itself\n * cannot be located.\n *\n * Exported for direct testing; not re-exported from the package index.\n */\nexport function isMissingUsersDependency(error: unknown): boolean {\n let current: unknown = error;\n const seen = new Set<unknown>();\n\n while (current instanceof Error && !seen.has(current)) {\n seen.add(current);\n\n const match = current.message.match(MISSING_MODULE_TARGET_PATTERN);\n if (match && isUsersSpecifier(match[1])) {\n return true;\n }\n\n if (\n current.message.includes('Failed to load @happyvertical/smrt-users for')\n ) {\n return true;\n }\n\n current = current.cause;\n }\n\n return false;\n}\n"],"mappings":"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"}
|