@happyvertical/smrt-fields 0.40.60 → 0.40.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/AGENTS.md +58 -12
  2. package/README.md +143 -42
  3. package/dist/__smrt-register__.d.ts +2 -0
  4. package/dist/__smrt-register__.d.ts.map +1 -0
  5. package/dist/cache.d.ts +18 -0
  6. package/dist/cache.d.ts.map +1 -0
  7. package/dist/chunks/FieldPolicyCollection--RxukfCX.js +1218 -0
  8. package/dist/chunks/FieldPolicyCollection--RxukfCX.js.map +1 -0
  9. package/dist/collections/FieldPolicyCollection.d.ts +95 -0
  10. package/dist/collections/FieldPolicyCollection.d.ts.map +1 -0
  11. package/dist/collections/FieldPolicySuggestionCollection.d.ts +160 -0
  12. package/dist/collections/FieldPolicySuggestionCollection.d.ts.map +1 -0
  13. package/dist/collections/FieldUsageCounterCollection.d.ts +143 -0
  14. package/dist/collections/FieldUsageCounterCollection.d.ts.map +1 -0
  15. package/dist/deterministic-id.d.ts +18 -0
  16. package/dist/deterministic-id.d.ts.map +1 -0
  17. package/dist/field-definitions.d.ts +85 -0
  18. package/dist/field-definitions.d.ts.map +1 -0
  19. package/dist/field-policy-resolver.d.ts +46 -0
  20. package/dist/field-policy-resolver.d.ts.map +1 -0
  21. package/dist/index.d.ts +15 -532
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +1813 -628
  24. package/dist/index.js.map +1 -1
  25. package/dist/manifest.json +1487 -191
  26. package/dist/models/FieldPolicy.d.ts +163 -0
  27. package/dist/models/FieldPolicy.d.ts.map +1 -0
  28. package/dist/models/FieldPolicySuggestion.d.ts +132 -0
  29. package/dist/models/FieldPolicySuggestion.d.ts.map +1 -0
  30. package/dist/models/FieldUsageCounter.d.ts +178 -0
  31. package/dist/models/FieldUsageCounter.d.ts.map +1 -0
  32. package/dist/models/FieldUsageReportReceipt.d.ts +25 -0
  33. package/dist/models/FieldUsageReportReceipt.d.ts.map +1 -0
  34. package/dist/permissions.d.ts +9 -0
  35. package/dist/permissions.d.ts.map +1 -0
  36. package/dist/playground.d.ts +2 -0
  37. package/dist/playground.d.ts.map +1 -0
  38. package/dist/playground.js +39 -0
  39. package/dist/playground.js.map +1 -0
  40. package/dist/settings-catalog.d.ts +73 -0
  41. package/dist/settings-catalog.d.ts.map +1 -0
  42. package/dist/smrt-knowledge.json +449 -9
  43. package/dist/svelte/__tests__/FieldPolicyControlPanel.test.js +277 -0
  44. package/dist/svelte/__tests__/FieldPolicyGear.test.js +503 -0
  45. package/dist/svelte/__tests__/FormHelp.test.js +138 -0
  46. package/dist/svelte/__tests__/ModeUX.test.js +109 -0
  47. package/dist/svelte/__tests__/ObjectForm.test.js +687 -0
  48. package/dist/svelte/__tests__/ObjectFormGeneratedApi.integration.test.js +436 -0
  49. package/dist/svelte/__tests__/PolicyField.test.js +373 -0
  50. package/dist/svelte/__tests__/UsageLearning.test.js +140 -0
  51. package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte +30 -0
  52. package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte.d.ts +9 -0
  53. package/dist/svelte/__tests__/fixtures/AdvancedFieldsFixture.svelte.d.ts.map +1 -0
  54. package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte +33 -0
  55. package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte.d.ts +10 -0
  56. package/dist/svelte/__tests__/fixtures/DomPrefillFixture.svelte.d.ts.map +1 -0
  57. package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte +19 -0
  58. package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte.d.ts +10 -0
  59. package/dist/svelte/__tests__/fixtures/FormHelpFixture.svelte.d.ts.map +1 -0
  60. package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte +18 -0
  61. package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte.d.ts +11 -0
  62. package/dist/svelte/__tests__/fixtures/GeneratedApiGearFixture.svelte.d.ts.map +1 -0
  63. package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte +11 -0
  64. package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte.d.ts +19 -0
  65. package/dist/svelte/__tests__/fixtures/GracefulDegradeFixture.svelte.d.ts.map +1 -0
  66. package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte +18 -0
  67. package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte.d.ts +8 -0
  68. package/dist/svelte/__tests__/fixtures/ModeSwitchFixture.svelte.d.ts.map +1 -0
  69. package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte +24 -0
  70. package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte.d.ts +13 -0
  71. package/dist/svelte/__tests__/fixtures/NoIdFixture.svelte.d.ts.map +1 -0
  72. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte +37 -0
  73. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts +15 -0
  74. package/dist/svelte/__tests__/fixtures/ObjectFormActionsFixture.svelte.d.ts.map +1 -0
  75. package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte +17 -0
  76. package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte.d.ts +10 -0
  77. package/dist/svelte/__tests__/fixtures/ObjectFormFixture.svelte.d.ts.map +1 -0
  78. package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte +25 -0
  79. package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte.d.ts +10 -0
  80. package/dist/svelte/__tests__/fixtures/ObjectFormRegistryFixture.svelte.d.ts.map +1 -0
  81. package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte +34 -0
  82. package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte.d.ts +10 -0
  83. package/dist/svelte/__tests__/fixtures/ObjectFormSnippetFixture.svelte.d.ts.map +1 -0
  84. package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte +29 -0
  85. package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte.d.ts +11 -0
  86. package/dist/svelte/__tests__/fixtures/ObjectFormSourceFixture.svelte.d.ts.map +1 -0
  87. package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte +33 -0
  88. package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte.d.ts +9 -0
  89. package/dist/svelte/__tests__/fixtures/ObjectFormSourceSnippetFixture.svelte.d.ts.map +1 -0
  90. package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte +40 -0
  91. package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte.d.ts +8 -0
  92. package/dist/svelte/__tests__/fixtures/PolicyDataTableFixture.svelte.d.ts.map +1 -0
  93. package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte +26 -0
  94. package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte.d.ts +13 -0
  95. package/dist/svelte/__tests__/fixtures/PrefillFixture.svelte.d.ts.map +1 -0
  96. package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte +39 -0
  97. package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte.d.ts +17 -0
  98. package/dist/svelte/__tests__/fixtures/ProviderFieldFixture.svelte.d.ts.map +1 -0
  99. package/dist/svelte/__tests__/fixtures/RegistryInput.svelte +15 -0
  100. package/dist/svelte/__tests__/fixtures/RegistryInput.svelte.d.ts +5 -0
  101. package/dist/svelte/__tests__/fixtures/RegistryInput.svelte.d.ts.map +1 -0
  102. package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte +29 -0
  103. package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte.d.ts +14 -0
  104. package/dist/svelte/__tests__/fixtures/RevealPrefillFixture.svelte.d.ts.map +1 -0
  105. package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte +24 -0
  106. package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte.d.ts +14 -0
  107. package/dist/svelte/__tests__/fixtures/SettingsCatalogStub.svelte.d.ts.map +1 -0
  108. package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte +28 -0
  109. package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte.d.ts +13 -0
  110. package/dist/svelte/__tests__/fixtures/SnippetFixture.svelte.d.ts.map +1 -0
  111. package/dist/svelte/__tests__/fixtures/SuggestionGearFixture.svelte +23 -0
  112. package/dist/svelte/__tests__/fixtures/SuggestionGearFixture.svelte.d.ts +10 -0
  113. package/dist/svelte/__tests__/fixtures/SuggestionGearFixture.svelte.d.ts.map +1 -0
  114. package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte +14 -0
  115. package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte.d.ts +19 -0
  116. package/dist/svelte/__tests__/fixtures/ToggleModeButton.svelte.d.ts.map +1 -0
  117. package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte +25 -0
  118. package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte.d.ts +14 -0
  119. package/dist/svelte/__tests__/fixtures/TooltipFixture.svelte.d.ts.map +1 -0
  120. package/dist/svelte/__tests__/settings-catalog.test.js +161 -0
  121. package/dist/svelte/components/AdvancedFields.svelte +49 -0
  122. package/dist/svelte/components/AdvancedFields.svelte.d.ts +17 -0
  123. package/dist/svelte/components/AdvancedFields.svelte.d.ts.map +1 -0
  124. package/dist/svelte/components/FieldInput.svelte +159 -0
  125. package/dist/svelte/components/FieldInput.svelte.d.ts +5 -0
  126. package/dist/svelte/components/FieldInput.svelte.d.ts.map +1 -0
  127. package/dist/svelte/components/FieldPolicyControlPanel.svelte +288 -0
  128. package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts +29 -0
  129. package/dist/svelte/components/FieldPolicyControlPanel.svelte.d.ts.map +1 -0
  130. package/dist/svelte/components/FieldPolicyEditor.svelte +417 -0
  131. package/dist/svelte/components/FieldPolicyEditor.svelte.d.ts +18 -0
  132. package/dist/svelte/components/FieldPolicyEditor.svelte.d.ts.map +1 -0
  133. package/dist/svelte/components/FieldPolicyGearButton.svelte +24 -0
  134. package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts +8 -0
  135. package/dist/svelte/components/FieldPolicyGearButton.svelte.d.ts.map +1 -0
  136. package/dist/svelte/components/FieldPolicyGearProvider.svelte +169 -0
  137. package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts +20 -0
  138. package/dist/svelte/components/FieldPolicyGearProvider.svelte.d.ts.map +1 -0
  139. package/dist/svelte/components/FieldPolicyProvider.svelte +89 -0
  140. package/dist/svelte/components/FieldPolicyProvider.svelte.d.ts +35 -0
  141. package/dist/svelte/components/FieldPolicyProvider.svelte.d.ts.map +1 -0
  142. package/dist/svelte/components/FieldPolicySuggestionQueue.svelte +135 -0
  143. package/dist/svelte/components/FieldPolicySuggestionQueue.svelte.d.ts +11 -0
  144. package/dist/svelte/components/FieldPolicySuggestionQueue.svelte.d.ts.map +1 -0
  145. package/dist/svelte/components/FormHelp.svelte +157 -0
  146. package/dist/svelte/components/FormHelp.svelte.d.ts +15 -0
  147. package/dist/svelte/components/FormHelp.svelte.d.ts.map +1 -0
  148. package/dist/svelte/components/ModeSwitch.svelte +47 -0
  149. package/dist/svelte/components/ModeSwitch.svelte.d.ts +10 -0
  150. package/dist/svelte/components/ModeSwitch.svelte.d.ts.map +1 -0
  151. package/dist/svelte/components/ObjectForm.svelte +440 -0
  152. package/dist/svelte/components/ObjectForm.svelte.d.ts +50 -0
  153. package/dist/svelte/components/ObjectForm.svelte.d.ts.map +1 -0
  154. package/dist/svelte/components/ObjectFormSourceProvider.svelte +18 -0
  155. package/dist/svelte/components/ObjectFormSourceProvider.svelte.d.ts +11 -0
  156. package/dist/svelte/components/ObjectFormSourceProvider.svelte.d.ts.map +1 -0
  157. package/dist/svelte/components/PolicyField.svelte +338 -0
  158. package/dist/svelte/components/PolicyField.svelte.d.ts +80 -0
  159. package/dist/svelte/components/PolicyField.svelte.d.ts.map +1 -0
  160. package/dist/svelte/context.svelte.d.ts +54 -0
  161. package/dist/svelte/context.svelte.d.ts.map +1 -0
  162. package/dist/svelte/context.svelte.js +55 -0
  163. package/dist/svelte/data-table.d.ts +17 -0
  164. package/dist/svelte/data-table.d.ts.map +1 -0
  165. package/dist/svelte/data-table.js +19 -0
  166. package/dist/svelte/field-policy-editor.d.ts +81 -0
  167. package/dist/svelte/field-policy-editor.d.ts.map +1 -0
  168. package/dist/svelte/field-policy-editor.js +120 -0
  169. package/dist/svelte/gear-context.svelte.d.ts +16 -0
  170. package/dist/svelte/gear-context.svelte.d.ts.map +1 -0
  171. package/dist/svelte/gear-context.svelte.js +15 -0
  172. package/dist/svelte/index.d.ts +63 -0
  173. package/dist/svelte/index.d.ts.map +1 -0
  174. package/dist/svelte/index.js +41 -0
  175. package/dist/svelte/input-registry.d.ts +21 -0
  176. package/dist/svelte/input-registry.d.ts.map +1 -0
  177. package/dist/svelte/input-registry.js +26 -0
  178. package/dist/svelte/object-form-source-context.svelte.d.ts +4 -0
  179. package/dist/svelte/object-form-source-context.svelte.d.ts.map +1 -0
  180. package/dist/svelte/object-form-source-context.svelte.js +8 -0
  181. package/dist/svelte/object-form-source.svelte.d.ts +18 -0
  182. package/dist/svelte/object-form-source.svelte.d.ts.map +1 -0
  183. package/dist/svelte/object-form-source.svelte.js +99 -0
  184. package/dist/svelte/object-form.d.ts +6 -0
  185. package/dist/svelte/object-form.d.ts.map +1 -0
  186. package/dist/svelte/object-form.js +38 -0
  187. package/dist/svelte/playground/FieldPolicyFormPreview.svelte +212 -0
  188. package/dist/svelte/playground/FieldPolicyFormPreview.svelte.d.ts +4 -0
  189. package/dist/svelte/playground/FieldPolicyFormPreview.svelte.d.ts.map +1 -0
  190. package/dist/svelte/playground/ObjectFormPreview.svelte +129 -0
  191. package/dist/svelte/playground/ObjectFormPreview.svelte.d.ts +4 -0
  192. package/dist/svelte/playground/ObjectFormPreview.svelte.d.ts.map +1 -0
  193. package/dist/svelte/playground.d.ts +20 -0
  194. package/dist/svelte/playground.d.ts.map +1 -0
  195. package/dist/svelte/playground.js +38 -0
  196. package/dist/svelte/settings-catalog.d.ts +54 -0
  197. package/dist/svelte/settings-catalog.d.ts.map +1 -0
  198. package/dist/svelte/settings-catalog.js +116 -0
  199. package/dist/svelte/suggestions.d.ts +38 -0
  200. package/dist/svelte/suggestions.d.ts.map +1 -0
  201. package/dist/svelte/suggestions.js +68 -0
  202. package/dist/svelte/types.d.ts +101 -0
  203. package/dist/svelte/types.d.ts.map +1 -0
  204. package/dist/svelte/types.js +4 -0
  205. package/dist/svelte/usage-capture.d.ts +63 -0
  206. package/dist/svelte/usage-capture.d.ts.map +1 -0
  207. package/dist/svelte/usage-capture.js +61 -0
  208. package/dist/types.d.ts +316 -203
  209. package/dist/types.d.ts.map +1 -0
  210. package/dist/types.js +7 -1
  211. package/dist/types.js.map +1 -1
  212. package/dist/usage-learning.d.ts +201 -0
  213. package/dist/usage-learning.d.ts.map +1 -0
  214. package/dist/usage-schedules.d.ts +110 -0
  215. package/dist/usage-schedules.d.ts.map +1 -0
  216. package/dist/users-module.d.ts +30 -0
  217. package/dist/users-module.d.ts.map +1 -0
  218. package/package.json +33 -8
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldPolicyCollection--RxukfCX.js","names":[],"sources":["../../src/permissions.ts","../../src/cache.ts","../../src/field-definitions.ts","../../src/models/FieldPolicy.ts","../../src/collections/FieldPolicyCollection.ts"],"sourcesContent":["import {\n type PermissionDefinition,\n registerPermissionDefinitions,\n} from '@happyvertical/smrt-users';\n\n/** Authorizes app- and tenant-scope field-policy administration. */\nexport const MANAGE_FIELD_POLICY_PERMISSION = 'fields.policy.manage';\n\n/** Authorizes a principal to maintain their own user-scope field policy. */\nexport const PERSONALIZE_FIELD_POLICY_PERMISSION = 'fields.policy.personalize';\n\nexport const FIELD_POLICY_PERMISSION_DEFINITIONS: PermissionDefinition[] = [\n {\n slug: MANAGE_FIELD_POLICY_PERMISSION,\n category: 'fields',\n name: 'Manage Field Policies',\n description: 'Manage application and tenant field-policy overrides',\n },\n {\n slug: PERSONALIZE_FIELD_POLICY_PERMISSION,\n category: 'fields',\n name: 'Personalize Field Policies',\n description: 'Manage the caller’s own field-policy overrides',\n },\n];\n\nlet registered = false;\n\n/** Register field-policy permissions once when smrt-fields is loaded. */\nexport function ensureFieldPolicyPermissionsRegistered(): void {\n if (registered) {\n return;\n }\n registered = true;\n registerPermissionDefinitions(FIELD_POLICY_PERMISSION_DEFINITIONS);\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 * True when a registered field can receive a policy row. Keep this shared by\n * the resolver and the catalog audit: otherwise a row that never resolves can\n * accidentally be presented as an editable setting.\n */\nexport function isPolicyAddressableField(field: RegisteredFieldInfo): boolean {\n return (\n field._meta?.__smrtSystemField !== true &&\n field.type !== 'oneToMany' &&\n field.type !== 'manyToMany' &&\n field.type !== 'meta'\n );\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 * True when a reference identifier can be stored in the field's backing\n * column. Native UUID columns accept UUIDs only; `idType: 'text'` opts into\n * arbitrary non-empty string identifiers. Shared by policy defaults and the\n * usage histogram so a poisoned sample can never become an unwriteable\n * suggestion.\n */\nexport function isStorableReferenceId(\n field: RegisteredFieldInfo,\n value: unknown,\n): value is string {\n if (typeof value !== 'string' || value === '') return false;\n return usesTextIdStorage(field) || UUID_PATTERN.test(value);\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 (!isStorableReferenceId(field, 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 {\n assertOperationPermission,\n getCurrentSessionPermissionContext,\n} from '@happyvertical/smrt-users';\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 ensureFieldPolicyPermissionsRegistered,\n MANAGE_FIELD_POLICY_PERMISSION,\n PERSONALIZE_FIELD_POLICY_PERMISSION,\n} from '../permissions.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\n// Direct model imports (rather than the package entrypoint) are common in\n// server-side integrations and tests. Register before any guard asks the\n// shared users catalog about either custom capability.\nensureFieldPolicyPermissionsRegistered();\n\ntype FieldPolicyIdentity = {\n id: string;\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\nconst FIELD_POLICY_SCOPE_DENIAL_MESSAGE =\n 'Field policy scope is not permitted for this principal.';\n\n/**\n * The policy-write identity is normally supplied by tenancy ALS. Generated\n * SvelteKit routes are also valid under `createSessionHandler()` with\n * `enterTenantContext` left unset, however: that handler deliberately enters\n * only the users request-permission ALS. Use its already authenticated,\n * request-bound principal as a fallback so route bodies never become an owner\n * source. A tenant context always wins when one exists.\n */\ntype FieldPolicyAmbientContext = {\n permissions: ReadonlySet<string>;\n superAdminBypass: boolean;\n tenantId: string;\n userId?: string;\n};\n\nfunction getFieldPolicyAmbientContext(): FieldPolicyAmbientContext | undefined {\n const tenantContext = getCurrentTenant();\n if (tenantContext) {\n return {\n permissions: tenantContext.permissions,\n superAdminBypass: isSuperAdminBypass(),\n tenantId: tenantContext.tenantId,\n userId: tenantContext.userId,\n };\n }\n\n const sessionContext = getCurrentSessionPermissionContext();\n if (!sessionContext?.tenantId) {\n return undefined;\n }\n\n return {\n permissions: sessionContext.permissionSet,\n superAdminBypass: sessionContext.superAdminBypass,\n tenantId: sessionContext.tenantId,\n userId: sessionContext.userId ?? undefined,\n };\n}\n\n/** Expected authorization denial that generated REST serializes as HTTP 403. */\nclass FieldPolicyAuthorizationError extends Error {\n readonly code = 'permission_denied';\n readonly status = 403;\n\n constructor(message: string) {\n super(message);\n this.name = 'FieldPolicyAuthorizationError';\n }\n}\n\n/**\n * Scope isolation is an expected request denial, not an internal failure.\n * Preserve the tenancy error type for callers that distinguish isolation\n * failures while giving generated transports the bounded 4xx signal they need\n * to serialize it safely.\n */\nclass FieldPolicyTenantIsolationError extends TenantIsolationError {\n readonly publicMessage = FIELD_POLICY_SCOPE_DENIAL_MESSAGE;\n readonly status = 403;\n\n constructor(\n diagnosticMessage: string,\n details?: { attemptedTenantId?: string; tenantId?: string },\n ) {\n // `normalizeTypedHttpError()` serializes Error.message for all expected\n // 4xx errors. Scope diagnostics contain tenant/user identifiers, so keep\n // this client-facing message intentionally bounded and retain the useful\n // detail only as non-enumerable server-side error metadata.\n super(FIELD_POLICY_SCOPE_DENIAL_MESSAGE);\n Object.defineProperty(this, 'cause', {\n configurable: true,\n enumerable: false,\n value: new Error(diagnosticMessage),\n writable: false,\n });\n Object.defineProperty(this, 'diagnostic', {\n configurable: true,\n enumerable: false,\n value: { ...details, message: diagnosticMessage },\n writable: false,\n });\n }\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: {\n include: ['create', 'update', 'delete'],\n // Generated SvelteKit policy routes need a trusted principal-derived\n // tenancy context even though this sparse cross-scope table itself is not\n // @TenantScoped. Owner ids remain server-derived in this model.\n principalContext: true,\n },\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 await this.assertScopePermission(previousIdentity);\n }\n this.assertScopeOwnedByAmbientContext(this, 'save');\n await this.assertScopePermission(this);\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 = getFieldPolicyAmbientContext();\n if (ambientContext) {\n this.updatedBy = ambientContext.userId ?? null;\n }\n this.normalizeDefaultValueForPersistence();\n await this.validateFieldPolicy(previousIdentity);\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 await this.assertScopePermission(persisted ?? this);\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(\n previousIdentity: FieldPolicyIdentity | null,\n ): 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(previousIdentity);\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 FieldPolicyAuthorizationError(\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 * During an update, the persisted row is excluded from the lower-layer\n * projection. That makes a clear-default + required-field demotion validate\n * the post-write policy rather than accidentally counting the row being\n * replaced.\n */\n private async resolveOrgTierFieldPolicy(\n previousIdentity: FieldPolicyIdentity | null,\n ): Promise<ResolvedFieldPolicy | undefined> {\n const tenantIdForChain =\n this.scopeType === 'tenant'\n ? this.tenantId\n : this.scopeType === 'user'\n ? (getFieldPolicyAmbientContext()?.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 ...(previousIdentity\n ? { excludePolicyIds: new Set([previousIdentity.id]) }\n : {}),\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 neither tenancy nor\n * authenticated-session context nothing is derived — scope-shape validation\n * 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 = getFieldPolicyAmbientContext();\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 (neither tenant ALS nor authenticated\n * session-permission ALS), only app-scope rows are accepted: tenant- and\n * user-scope rows are unattributable without a context, so they are\n * rejected outright rather than allowed by default. Inside a non-bypass\n * context, a caller may only touch rows for its own tenant, and user rows\n * only for its own user id — a context that carries NO user id may not\n * 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 = getFieldPolicyAmbientContext();\n\n if (!context) {\n if (scope.scopeType === 'tenant' || scope.scopeType === 'user') {\n throw new FieldPolicyTenantIsolationError(\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 (context.superAdminBypass) {\n return;\n }\n\n if (scope.scopeType === 'app') {\n throw new FieldPolicyTenantIsolationError(\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 FieldPolicyTenantIsolationError(\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 FieldPolicyTenantIsolationError(\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 FieldPolicyTenantIsolationError(\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 /**\n * Permission boundary for policy mutations. Scope ownership is checked\n * separately (and first) because permission never grants cross-tenant or\n * cross-user access. Existing rows are checked against their persisted\n * scope before an identity-changing save can mutate or delete them.\n */\n private async assertScopePermission(scope: {\n scopeType: string;\n }): Promise<void> {\n const context = getFieldPolicyAmbientContext();\n const action =\n scope.scopeType === 'user'\n ? PERSONALIZE_FIELD_POLICY_PERMISSION\n : MANAGE_FIELD_POLICY_PERMISSION;\n const [, , operation] = action.split('.');\n\n await assertOperationPermission({\n collection: 'fields.policy',\n action: operation ?? 'manage',\n db: this.options.db ?? this.options.persistence,\n tenantId: context?.tenantId ?? null,\n userId: context?.userId ?? null,\n permissionSet: context?.permissions,\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 id: read('id', 'id', String(this.id ?? '')),\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 {\n getCurrentTenant,\n isSuperAdminBypass,\n} from '@happyvertical/smrt-tenancy';\nimport {\n checkOperationPermission,\n MembershipCollection,\n} from '@happyvertical/smrt-users';\nimport {\n getFieldReadPermission,\n getObjectFieldMap,\n isPolicyAddressableField,\n isSensitiveField,\n isTransientField,\n isUsableRequiredDefault,\n} from '../field-definitions.js';\nimport { FieldPolicy } from '../models/FieldPolicy.js';\nimport {\n MANAGE_FIELD_POLICY_PERMISSION,\n PERSONALIZE_FIELD_POLICY_PERMISSION,\n} from '../permissions.js';\nimport type {\n ExplainedObjectFieldPolicy,\n FieldPolicyAuditRow,\n FieldPolicyAuditSnapshot,\n FieldPolicyBatchResult,\n FieldPolicyDriftReason,\n FieldPolicyDriftRow,\n FieldPolicyEditorRow,\n FieldPolicyEditorStateResult,\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/** Public cap shared by the server catalog and browser adapter. */\nexport const MAX_FIELD_POLICY_AUDIT_OBJECT_REFS = MAX_BATCH_OBJECT_REFS;\n/** Count queries are projected, so they can cover a page plus an off-page selection. */\nexport const MAX_FIELD_POLICY_AUDIT_COUNT_REFS = MAX_BATCH_OBJECT_REFS * 2;\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: [\n 'create',\n 'update',\n 'delete',\n 'resolveBatch',\n 'getEditorState',\n 'policyAudit',\n ],\n // The collection-scoped editor action shares the sparse cross-scope\n // policy table, so generated SvelteKit routes establish identity from\n // authenticated locals even though FieldPolicy is not @TenantScoped.\n principalContext: true,\n routes: {\n resolveBatch: {\n scope: 'collection',\n method: 'POST',\n path: 'resolve',\n },\n getEditorState: {\n scope: 'collection',\n method: 'POST',\n path: 'editor-state',\n },\n policyAudit: {\n scope: 'collection',\n method: 'POST',\n path: 'policy-audit',\n },\n },\n },\n cli: false,\n mcp: false,\n})\nexport class FieldPolicyCollection extends SmrtCollection<FieldPolicy> {\n static readonly _itemClass = FieldPolicy;\n\n /**\n * Permission-gated, tenant-scoped audit read for the defaults control\n * panel. It returns editable own-tenant rows, read-only app summaries,\n * projected user override counts, and identity-only manifest drift rows.\n */\n async policyAudit(\n options: {\n objectRefs?: string[];\n countObjectRefs?: string[];\n /** Capability-only bootstrap for catalog builders before page selection. */\n summaryOnly?: boolean;\n /** Return row/count roll-ups without resolving field-policy layers. */\n countsOnly?: boolean;\n /** Return the first bounded page of manifest drift candidates. */\n includeDrift?: boolean;\n } = {},\n ): Promise<FieldPolicyAuditSnapshot> {\n const objectRefs = normalizeAuditRefs(\n options.objectRefs,\n 'objectRefs',\n MAX_FIELD_POLICY_AUDIT_OBJECT_REFS,\n );\n const countObjectRefs = normalizeAuditRefs(\n options.countObjectRefs ?? objectRefs,\n 'countObjectRefs',\n MAX_FIELD_POLICY_AUDIT_COUNT_REFS,\n );\n const context = getCurrentTenant();\n const tenantId = context?.tenantId ?? null;\n const userId = context?.userId ?? null;\n const permissionOptions = {\n collection: 'fields.policy',\n db: this.db,\n tenantId,\n userId,\n permissionSet: context?.permissions,\n onDeny: 'return' as const,\n };\n const manage = tenantId\n ? await checkOperationPermission({\n ...permissionOptions,\n action: MANAGE_FIELD_POLICY_PERMISSION.split('.').at(-1) ?? 'manage',\n })\n : { allowed: false };\n const personalize = userId\n ? await checkOperationPermission({\n ...permissionOptions,\n action:\n PERSONALIZE_FIELD_POLICY_PERMISSION.split('.').at(-1) ??\n 'personalize',\n })\n : { allowed: false };\n const caller = {\n tenantId,\n userId,\n canManageOrg: manage.allowed,\n canPersonalize: personalize.allowed && userId !== null,\n };\n if (!caller.canManageOrg || tenantId === null) return emptyAudit(caller);\n if (options.summaryOnly === true) return emptyAudit(caller);\n\n const fieldMaps = new Map<\n string,\n Promise<Awaited<ReturnType<typeof getObjectFieldMap>> | null>\n >();\n const fieldMapFor = (objectRef: string) => {\n let loaded = fieldMaps.get(objectRef);\n if (!loaded) {\n loaded = getObjectFieldMap(objectRef).catch(() => null);\n fieldMaps.set(objectRef, loaded);\n }\n return loaded;\n };\n const classify = async (\n row: Pick<FieldPolicy, 'objectRef' | 'fieldName'>,\n ): Promise<'ok' | 'gated' | FieldPolicyDriftReason> => {\n const fields = await fieldMapFor(row.objectRef);\n if (!fields) return 'unknown-object';\n const definition = fields.get(row.fieldName);\n if (!definition) return 'unknown-field';\n if (\n isSensitiveField(definition) ||\n getFieldReadPermission(definition) !== undefined ||\n isTransientField(definition)\n ) {\n return 'gated';\n }\n return isPolicyAddressableField(definition) ? 'ok' : 'excluded-field';\n };\n\n const [appRows, orgRows, ownUserRows] = objectRefs.length\n ? await Promise.all([\n this.list({\n where: { scopeType: 'app', 'objectRef in': objectRefs },\n }),\n this.list({\n where: {\n scopeType: 'tenant',\n tenantId,\n 'objectRef in': objectRefs,\n },\n }),\n userId\n ? this.list({\n where: {\n scopeType: 'user',\n userId,\n 'objectRef in': objectRefs,\n },\n })\n : Promise.resolve([] as FieldPolicy[]),\n ])\n : ([[], [], []] as [FieldPolicy[], FieldPolicy[], FieldPolicy[]]);\n const audit: FieldPolicyAuditSnapshot = {\n ...emptyAudit(caller),\n policies: options.countsOnly\n ? {}\n : await this.resolveAuditPolicies(objectRefs, tenantId, fieldMapFor),\n };\n const append = async (\n row: FieldPolicy,\n target: FieldPolicyAuditRow[],\n driftPrunable: boolean,\n ) => {\n const state = await classify(row);\n if (state === 'ok') target.push(toAuditRow(row));\n else if (state !== 'gated') {\n audit.driftRows.push({\n ...toDriftIdentity(row),\n reason: state,\n prunable: driftPrunable,\n });\n }\n };\n for (const row of orgRows) await append(row, audit.orgRows, true);\n for (const row of appRows)\n await append(row, audit.appRows, isSuperAdminBypass());\n for (const row of ownUserRows) {\n const state = await classify(row);\n if (state !== 'ok' && state !== 'gated') {\n audit.driftRows.push({\n ...toDriftIdentity(row),\n reason: state,\n prunable: caller.canPersonalize,\n });\n }\n }\n\n if (options.includeDrift === true) {\n // Drift is a separate bounded list: catalog browsing must not scan every\n // policy row merely to populate a secondary maintenance affordance.\n const [driftAppRows, driftOrgRows, driftUserRows] = await Promise.all([\n this.list({ where: { scopeType: 'app' }, limit: 100 }),\n this.list({ where: { scopeType: 'tenant', tenantId }, limit: 100 }),\n userId\n ? this.list({ where: { scopeType: 'user', userId }, limit: 100 })\n : Promise.resolve([] as FieldPolicy[]),\n ]);\n const knownIds = new Set(\n [...appRows, ...orgRows, ...ownUserRows].map((row) => String(row.id)),\n );\n for (const [rows, prunable] of [\n [driftAppRows, isSuperAdminBypass()],\n [driftOrgRows, true],\n [driftUserRows, caller.canPersonalize],\n ] as const) {\n for (const row of rows) {\n if (knownIds.has(String(row.id))) continue;\n const state = await classify(row);\n if (state !== 'ok' && state !== 'gated') {\n audit.driftRows.push({\n ...toDriftIdentity(row),\n reason: state,\n prunable,\n });\n }\n }\n }\n }\n\n // User policy rows intentionally have no tenantId because preferences\n // follow the user. Resolve tenant membership first so this roll-up cannot\n // leak whether users belonging only to another tenant customized a field.\n const memberships = await MembershipCollection.create({ db: this.db });\n const memberIds = [\n ...new Set(\n (await memberships.findActiveByTenant(tenantId))\n .map((membership) => membership.userId?.trim())\n .filter((id): id is string => Boolean(id)),\n ),\n ];\n const projected =\n countObjectRefs.length && memberIds.length\n ? await this.list({\n where: {\n scopeType: 'user',\n 'objectRef in': countObjectRefs,\n 'userId in': memberIds,\n },\n select: ['objectRef', 'fieldName'],\n })\n : [];\n for (const row of projected) {\n if ((await classify(row)) !== 'ok') continue;\n const byField = audit.userOverrideCounts[row.objectRef] ?? {};\n audit.userOverrideCounts[row.objectRef] = byField;\n byField[row.fieldName] = (byField[row.fieldName] ?? 0) + 1;\n }\n\n const { resolveSurvivingTenantChainIds } = await import(\n '../field-policy-resolver.js'\n );\n const ancestorIds = (\n await resolveSurvivingTenantChainIds(tenantId, {\n db: this.db,\n })\n ).filter((id) => id !== tenantId);\n if (ancestorIds.length && objectRefs.length) {\n const ancestors = await this.list({\n where: {\n scopeType: 'tenant',\n 'tenantId in': ancestorIds,\n 'objectRef in': objectRefs,\n },\n });\n for (const row of ancestors) {\n if ((await classify(row)) !== 'ok') continue;\n const names = audit.inheritedOrgKeys[row.objectRef] ?? [];\n audit.inheritedOrgKeys[row.objectRef] = names;\n if (!names.includes(row.fieldName)) names.push(row.fieldName);\n }\n }\n audit.orgRows.sort(sortAuditRows);\n audit.appRows.sort(sortAuditRows);\n audit.driftRows.sort(sortDriftRows);\n for (const names of Object.values(audit.inheritedOrgKeys)) names.sort();\n return audit;\n }\n\n private async resolveAuditPolicies(\n objectRefs: string[],\n tenantId: string,\n fieldMapFor: (\n objectRef: string,\n ) => Promise<Awaited<ReturnType<typeof getObjectFieldMap>> | null>,\n ): Promise<Record<string, ExplainedObjectFieldPolicy>> {\n const { resolveFieldPolicyExplained } = await import(\n '../field-policy-resolver.js'\n );\n const policies: Record<string, ExplainedObjectFieldPolicy> = {};\n for (const objectRef of objectRefs) {\n if (!(await fieldMapFor(objectRef))) continue;\n const resolved = await resolveFieldPolicyExplained(objectRef, {\n tenantId,\n db: this.db,\n });\n policies[objectRef] = await this.gateExplainedForEditorResponse(\n resolved,\n {\n manage: true,\n personalize: false,\n },\n true,\n );\n }\n return policies;\n }\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 /**\n * Context-derived bootstrap data for the policy gear. This is deliberately\n * a collection action instead of reopening list/get: a caller can inspect\n * only their safe effective policy and rows/layers they are permitted to\n * edit, never arbitrary tenant or user policy rows.\n */\n async getEditorState(\n options: { objectRef?: string } = {},\n ): Promise<FieldPolicyEditorStateResult> {\n const objectRef = options.objectRef;\n if (typeof objectRef !== 'string' || objectRef.trim() === '') {\n throw new Error('getEditorState requires a non-empty \"objectRef\" string');\n }\n\n const context = getCurrentTenant();\n const permissionOptions = {\n collection: 'fields.policy',\n db: this.db,\n tenantId: context?.tenantId ?? null,\n userId: context?.userId ?? null,\n permissionSet: context?.permissions,\n onDeny: 'return' as const,\n };\n const manage = await checkOperationPermission({\n ...permissionOptions,\n action: MANAGE_FIELD_POLICY_PERMISSION.split('.').at(-1) ?? 'manage',\n });\n const personalize = await checkOperationPermission({\n ...permissionOptions,\n action:\n PERSONALIZE_FIELD_POLICY_PERMISSION.split('.').at(-1) ?? 'personalize',\n });\n\n // A permission alone cannot make a personal layer usable: service/API-key\n // contexts without a user identity must never receive a personal tab (or\n // an affirmative capability they cannot safely act on).\n const personalizeAllowed =\n personalize.allowed &&\n typeof context?.userId === 'string' &&\n context.userId.length > 0;\n\n if (!manage.allowed && !personalizeAllowed) {\n // The runtime custom-action transport maps explicit failures to their\n // requested non-2xx status. Returning one avoids its generic catch-all\n // turning an authorization denial into a misleading 500.\n return {\n code: 'permission_denied',\n message: `Permission denied for '${personalize.permission ?? MANAGE_FIELD_POLICY_PERMISSION}'.`,\n ok: false,\n status: 403,\n };\n }\n\n const { resolveFieldPolicyExplained } = await import(\n '../field-policy-resolver.js'\n );\n const resolved = await resolveFieldPolicyExplained(objectRef, {\n tenantId: context?.tenantId ?? null,\n userId: context?.userId ?? null,\n db: this.db,\n });\n const policy = await this.gateExplainedForEditorResponse(resolved, {\n manage: manage.allowed,\n personalize: personalizeAllowed,\n });\n const fieldNames = new Set(Object.keys(policy.fields));\n // A personal editor must be able to preserve the required-field invariant\n // when it clears its own default, but a personalize-only caller cannot\n // receive app/tenant rows or explanation layers. Return only the boolean\n // answer for code/app/tenant resolution; never the contributing rows or\n // their default values.\n const personalLowerDefaultUsable = personalizeAllowed\n ? await this.getPersonalLowerDefaultUsability(\n objectRef,\n context?.tenantId ?? null,\n fieldNames,\n )\n : {};\n\n const appRows = manage.allowed\n ? this.toEditorRows(await this.getAppRows(objectRef), fieldNames)\n : [];\n const tenantRows =\n manage.allowed && context?.tenantId\n ? this.toEditorRows(\n (await this.getTenantRows(objectRef, [context.tenantId])).get(\n context.tenantId,\n ) ?? new Map(),\n fieldNames,\n )\n : [];\n const userRows =\n personalizeAllowed && context?.userId\n ? this.toEditorRows(\n await this.getUserRows(objectRef, context.userId),\n fieldNames,\n )\n : [];\n\n return {\n capabilities: {\n manage: manage.allowed,\n personalize: personalizeAllowed,\n },\n personalLowerDefaultUsable,\n policy,\n rows: { app: appRows, tenant: tenantRows, user: userRows },\n };\n }\n\n /**\n * Compute the non-disclosing fallback signal used only by personal drafts.\n * Passing no user id resolves exactly code/app/tenant precedence, including\n * the real tenant hierarchy, while keeping every lower-layer value server\n * side. The public map is limited to fields already admitted by the editor\n * response's security rail.\n */\n private async getPersonalLowerDefaultUsability(\n objectRef: string,\n tenantId: string | null,\n fieldNames: ReadonlySet<string>,\n ): Promise<Record<string, boolean>> {\n const { resolveFieldPolicy } = await import('../field-policy-resolver.js');\n const lowerPolicy = await resolveFieldPolicy(objectRef, {\n tenantId,\n db: this.db,\n });\n return Object.fromEntries(\n Array.from(fieldNames, (fieldName) => {\n const field = lowerPolicy.fields[fieldName];\n return [\n fieldName,\n isUsableRequiredDefault(\n field?.hasDefault ? { value: field.defaultValue } : undefined,\n ),\n ];\n }),\n );\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 private async gateExplainedForEditorResponse(\n resolved: ExplainedObjectFieldPolicy,\n capabilities: { manage: boolean; personalize: boolean },\n includeCode = false,\n ): Promise<ExplainedObjectFieldPolicy> {\n const fieldMap = await getObjectFieldMap(resolved.objectRef);\n const fields: ExplainedObjectFieldPolicy['fields'] = {};\n const layers: ExplainedObjectFieldPolicy['layers'] = {};\n\n for (const [fieldName, policy] of Object.entries(resolved.fields)) {\n const fieldDef = fieldMap.get(fieldName);\n if (\n !fieldDef ||\n isSensitiveField(fieldDef) ||\n getFieldReadPermission(fieldDef) !== undefined ||\n isTransientField(fieldDef)\n ) {\n continue;\n }\n\n fields[fieldName] = policy;\n layers[fieldName] = (resolved.layers[fieldName] ?? []).filter(\n (layer) =>\n (includeCode && layer.layer === 'code') ||\n (capabilities.manage &&\n (layer.layer === 'app' || layer.layer === 'tenant')) ||\n (capabilities.personalize && layer.layer === 'user'),\n );\n }\n\n return { objectRef: resolved.objectRef, fields, layers };\n }\n\n private toEditorRows(\n rows: Map<string, FieldPolicy>,\n allowedFieldNames: ReadonlySet<string>,\n ): FieldPolicyEditorRow[] {\n return Array.from(rows.values())\n .filter((row) => allowedFieldNames.has(row.fieldName))\n .map((row) => ({\n defaultValue: row.defaultValue,\n displayOrder: row.displayOrder,\n fieldName: row.fieldName,\n help: row.help,\n id: String(row.id),\n label: row.label,\n locked: row.locked,\n scopeType: row.scopeType,\n tenantId: row.tenantId,\n updatedBy: row.updatedBy,\n userId: row.userId,\n visibility: row.visibility,\n }))\n .sort((left, right) => left.fieldName.localeCompare(right.fieldName));\n }\n}\n\nfunction normalizeAuditRefs(\n value: unknown,\n label: string,\n maximum: number,\n): string[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) {\n throw new Error(`policyAudit ${label} must be a string array`);\n }\n if (value.some((ref) => typeof ref !== 'string' || ref.trim() === '')) {\n throw new Error(`policyAudit ${label} must contain non-empty strings`);\n }\n const refs = [...new Set(value)];\n if (refs.length > maximum) {\n throw new Error(\n `policyAudit ${label} accepts at most ${maximum} objectRefs`,\n );\n }\n return refs;\n}\n\nfunction emptyAudit(\n caller: FieldPolicyAuditSnapshot['caller'],\n): FieldPolicyAuditSnapshot {\n return {\n orgRows: [],\n appRows: [],\n inheritedOrgKeys: {},\n userOverrideCounts: {},\n driftRows: [],\n policies: {},\n caller,\n };\n}\n\nfunction dateText(value: unknown): string | null {\n if (value instanceof Date) return value.toISOString();\n return typeof value === 'string' ? value : null;\n}\n\nfunction toAuditRow(row: FieldPolicy): FieldPolicyAuditRow {\n return {\n objectRef: row.objectRef,\n defaultValue: row.defaultValue,\n displayOrder: row.displayOrder,\n fieldName: row.fieldName,\n help: row.help,\n id: String(row.id),\n label: row.label,\n locked: row.locked,\n scopeType: row.scopeType,\n tenantId: row.tenantId,\n updatedBy: row.updatedBy,\n userId: row.userId,\n visibility: row.visibility,\n createdAt: dateText((row as { createdAt?: unknown }).createdAt),\n updatedAt: dateText((row as { updatedAt?: unknown }).updatedAt),\n };\n}\n\nfunction toDriftIdentity(\n row: FieldPolicy,\n): Omit<FieldPolicyDriftRow, 'reason' | 'prunable'> {\n return {\n id: String(row.id),\n objectRef: row.objectRef,\n fieldName: row.fieldName,\n scopeType: row.scopeType,\n tenantId: row.tenantId,\n userId: row.userId,\n updatedBy: row.updatedBy,\n createdAt: dateText((row as { createdAt?: unknown }).createdAt),\n updatedAt: dateText((row as { updatedAt?: unknown }).updatedAt),\n };\n}\n\nfunction sortAuditRows(\n left: FieldPolicyAuditRow,\n right: FieldPolicyAuditRow,\n): number {\n return (\n left.objectRef.localeCompare(right.objectRef) ||\n left.fieldName.localeCompare(right.fieldName)\n );\n}\n\nfunction sortDriftRows(\n left: FieldPolicyDriftRow,\n right: FieldPolicyDriftRow,\n): number {\n return (\n left.objectRef.localeCompare(right.objectRef) ||\n left.fieldName.localeCompare(right.fieldName) ||\n left.scopeType.localeCompare(right.scopeType)\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { SmrtCollection } from '@happyvertical/smrt-core';
2
+ import { FieldPolicy } from '../models/FieldPolicy.js';
3
+ import { FieldPolicyAuditSnapshot, FieldPolicyBatchResult, FieldPolicyEditorStateResult } from '../types.js';
4
+ /** Public cap shared by the server catalog and browser adapter. */
5
+ export declare const MAX_FIELD_POLICY_AUDIT_OBJECT_REFS = 100;
6
+ /** Count queries are projected, so they can cover a page plus an off-page selection. */
7
+ export declare const MAX_FIELD_POLICY_AUDIT_COUNT_REFS: number;
8
+ /**
9
+ * Collection surface for {@link FieldPolicy} rows plus the batch resolve
10
+ * action consumed by client bootstrapping (#2048).
11
+ *
12
+ * `resolveBatch` is a custom collection-scoped action (NOT a system route —
13
+ * core's generated system trio is closed): the generated route parses the
14
+ * body, calls the method on the app-configured collection instance (which
15
+ * carries the app database), and serializes the plain result.
16
+ *
17
+ * Exposure note: a decorated collection's config becomes the RUNTIME registry
18
+ * authority for its item class (core merges the collection registration onto
19
+ * the item slot), while build-time generation reads each manifest object's
20
+ * own config. This config therefore mirrors FieldPolicy's API posture —
21
+ * writes open plus the batch action, reads CLOSED (generated list/get would
22
+ * enumerate every tenant's/user's rows) — and closes the runtime CLI/MCP
23
+ * surfaces entirely (the ContentContributions precedent; the cli↔api
24
+ * coherence gate does not admit standard CRUD entries on a collection's
25
+ * `cli.include`). Keep the api include lists in lockstep with FieldPolicy's.
26
+ */
27
+ export declare class FieldPolicyCollection extends SmrtCollection<FieldPolicy> {
28
+ static readonly _itemClass: typeof FieldPolicy;
29
+ /**
30
+ * Permission-gated, tenant-scoped audit read for the defaults control
31
+ * panel. It returns editable own-tenant rows, read-only app summaries,
32
+ * projected user override counts, and identity-only manifest drift rows.
33
+ */
34
+ policyAudit(options?: {
35
+ objectRefs?: string[];
36
+ countObjectRefs?: string[];
37
+ /** Capability-only bootstrap for catalog builders before page selection. */
38
+ summaryOnly?: boolean;
39
+ /** Return row/count roll-ups without resolving field-policy layers. */
40
+ countsOnly?: boolean;
41
+ /** Return the first bounded page of manifest drift candidates. */
42
+ includeDrift?: boolean;
43
+ }): Promise<FieldPolicyAuditSnapshot>;
44
+ private resolveAuditPolicies;
45
+ /** All app-scope rows for an object, keyed by field name. */
46
+ getAppRows(objectRef: string): Promise<Map<string, FieldPolicy>>;
47
+ /**
48
+ * All tenant-scope rows for an object across a tenant chain, keyed
49
+ * `tenantId → fieldName → row`.
50
+ */
51
+ getTenantRows(objectRef: string, tenantIds: string[]): Promise<Map<string, Map<string, FieldPolicy>>>;
52
+ /** All user-scope rows for an object and user, keyed by field name. */
53
+ getUserRows(objectRef: string, userId: string): Promise<Map<string, FieldPolicy>>;
54
+ /**
55
+ * Resolve merged field policy for a set of objectRefs for the CURRENT
56
+ * caller, gated for public consumption.
57
+ *
58
+ * Caller identity comes exclusively from the ambient tenant context
59
+ * (established by the app's auth hook, e.g. smrt-users' session context) —
60
+ * the request body cannot select another tenant or user (fail closed).
61
+ * Server-side consumers wanting explicit identities call
62
+ * `resolveFieldPolicy()` directly instead.
63
+ *
64
+ * Field gating mirrors the REST serializer's derivation (`sensitive` /
65
+ * `readPermission` read from both the top level and `_meta`): sensitive and
66
+ * read-permission-gated fields are ABSENT from the response for every
67
+ * caller (the generated action route cannot convey per-caller grants to
68
+ * this method, so gated fields fail closed), and transient fields are
69
+ * stripped for parity with generated client field definitions.
70
+ */
71
+ resolveBatch(options?: {
72
+ objectRefs?: string[];
73
+ }): Promise<FieldPolicyBatchResult>;
74
+ /**
75
+ * Context-derived bootstrap data for the policy gear. This is deliberately
76
+ * a collection action instead of reopening list/get: a caller can inspect
77
+ * only their safe effective policy and rows/layers they are permitted to
78
+ * edit, never arbitrary tenant or user policy rows.
79
+ */
80
+ getEditorState(options?: {
81
+ objectRef?: string;
82
+ }): Promise<FieldPolicyEditorStateResult>;
83
+ /**
84
+ * Compute the non-disclosing fallback signal used only by personal drafts.
85
+ * Passing no user id resolves exactly code/app/tenant precedence, including
86
+ * the real tenant hierarchy, while keeping every lower-layer value server
87
+ * side. The public map is limited to fields already admitted by the editor
88
+ * response's security rail.
89
+ */
90
+ private getPersonalLowerDefaultUsability;
91
+ private gateResolvedForPublicResponse;
92
+ private gateExplainedForEditorResponse;
93
+ private toEditorRows;
94
+ }
95
+ //# sourceMappingURL=FieldPolicyCollection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldPolicyCollection.d.ts","sourceRoot":"","sources":["../../src/collections/FieldPolicyCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAQ,MAAM,0BAA0B,CAAC;AAiBhE,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAKvD,OAAO,KAAK,EAGV,wBAAwB,EACxB,sBAAsB,EAItB,4BAA4B,EAE7B,MAAM,aAAa,CAAC;AAIrB,mEAAmE;AACnE,eAAO,MAAM,kCAAkC,MAAwB,CAAC;AACxE,wFAAwF;AACxF,eAAO,MAAM,iCAAiC,QAA4B,CAAC;AAE3E;;;;;;;;;;;;;;;;;;GAkBG;AAQH,qBAoCa,qBAAsB,SAAQ,cAAc,CAAC,WAAW,CAAC;IACpE,MAAM,CAAC,QAAQ,CAAC,UAAU,qBAAe;IAEzC;;;;OAIG;IACG,WAAW,CACf,OAAO,GAAE;QACP,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,4EAA4E;QAC5E,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,uEAAuE;QACvE,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,kEAAkE;QAClE,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,wBAAwB,CAAC;YA+NtB,oBAAoB;IA6BlC,6DAA6D;IACvD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAWtE;;;OAGG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAuBjD,uEAAuE;IACjE,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAWpC;;;;;;;;;;;;;;;;OAgBG;IACG,YAAY,CAChB,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACtC,OAAO,CAAC,sBAAsB,CAAC;IAuClC;;;;;OAKG;IACG,cAAc,CAClB,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACnC,OAAO,CAAC,4BAA4B,CAAC;IAsGxC;;;;;;OAMG;YACW,gCAAgC;YAuBhC,6BAA6B;YAwB7B,8BAA8B;IAiC5C,OAAO,CAAC,YAAY;CAsBrB"}
@@ -0,0 +1,160 @@
1
+ import { SmrtCollection } from '@happyvertical/smrt-core';
2
+ import { FieldPolicySuggestion } from '../models/FieldPolicySuggestion.js';
3
+ import { AcceptFieldPolicySuggestionResult, DismissFieldPolicySuggestionResult, PendingFieldPolicySuggestionsResult } from '../types.js';
4
+ /**
5
+ * Raised when a pending→settled transition loses its race: another caller
6
+ * (accept or dismiss) already settled the suggestion — whether the loss is
7
+ * detected by the compare-and-set or by the load that preceded it, so
8
+ * overlapping decisions get ONE error type regardless of timing.
9
+ *
10
+ * Carries BOTH `httpStatus` and `status` = 409. `httpStatus` is the property
11
+ * core's generated REST error mapping honors (an integer OWN property, checked
12
+ * with `Object.hasOwn`); `status` mirrors the users-style shape this package
13
+ * already uses on `FieldPolicyPermissionError`. NOTE: that mapping arrived with
14
+ * #2049 and is NOT in this branch's core yet, so until #2049 lands ahead of
15
+ * this work the generated routes still surface these as 500s — the property is
16
+ * set now so the 409 becomes real the moment it does.
17
+ */
18
+ export declare class FieldPolicySuggestionConflictError extends Error {
19
+ /** Core's generated-REST status contract (own integer property). */
20
+ readonly httpStatus = 409;
21
+ /** The users-style shape mirrored by this package's authorization errors. */
22
+ readonly status = 409;
23
+ constructor(operation: string, suggestionId: string);
24
+ }
25
+ /**
26
+ * Raised when a non-transactional accept could neither complete its policy
27
+ * write NOR undo its claim. Both causes are carried: the row may be sitting
28
+ * `accepted` without a policy, which an operator must reconcile — silently
29
+ * reporting only the policy error would imply the suggestion is still pending.
30
+ */
31
+ export declare class FieldPolicySuggestionCompensationError extends Error {
32
+ readonly httpStatus = 500;
33
+ readonly status = 500;
34
+ readonly revertCause: unknown;
35
+ constructor(suggestionId: string, cause: unknown, revertCause: unknown);
36
+ }
37
+ /**
38
+ * Stable id for the org-scope policy row an acceptance creates, derived from
39
+ * the policy natural key.
40
+ *
41
+ * With a random id, two concurrent acceptances for the same field would each
42
+ * mint a row and the natural-key upsert would resolve them by REPLACING one
43
+ * (dropping its column and dangling its id). A deterministic id turns that into
44
+ * a primary-key collision the loser can detect and fall back from.
45
+ */
46
+ export declare function fieldPolicyRowId(tenantId: string, objectRef: string, fieldName: string): Promise<string>;
47
+ /** Default cool-down a dismissal applies (30 days). */
48
+ export declare const DEFAULT_SUGGESTION_COOL_DOWN_MS: number;
49
+ /** Bounds for a caller-supplied dismissal cool-down. */
50
+ export declare const MIN_SUGGESTION_COOL_DOWN_MS: number;
51
+ export declare const MAX_SUGGESTION_COOL_DOWN_MS: number;
52
+ /**
53
+ * Collection surface for {@link FieldPolicySuggestion} plus the three
54
+ * manage-gated actions the gear badge / control-panel queue consume (#2051;
55
+ * UI integration is #2050-follow-up territory — this is the minimal seam).
56
+ *
57
+ * All three actions are custom collection-scoped routes (the resolveBatch
58
+ * mechanism — single-segment paths, both transports dispatch). Identity is
59
+ * ambient-context-only and every action requires `fields.policy.manage` (or
60
+ * super-admin bypass) within an ambient tenant: pending suggestions describe
61
+ * org-wide usage, and accept/dismiss are org policy decisions.
62
+ */
63
+ export declare class FieldPolicySuggestionCollection extends SmrtCollection<FieldPolicySuggestion> {
64
+ static readonly _itemClass: typeof FieldPolicySuggestion;
65
+ /**
66
+ * The caller's tenant's PENDING suggestions (optionally filtered to a set
67
+ * of objectRefs), newest first, plus the total for the gear badge.
68
+ */
69
+ pendingSuggestions(options?: {
70
+ objectRefs?: string[];
71
+ }): Promise<PendingFieldPolicySuggestionsResult>;
72
+ /**
73
+ * Accept a pending suggestion: CLAIM the pending→accepted transition, then
74
+ * write the corresponding org-scope `FieldPolicy` row THROUGH NORMAL
75
+ * VALIDATION (registry check, type check, security rail, required-field
76
+ * invariant, ownership boundary, and the #2049 permission split — the
77
+ * ambient caller must hold `fields.policy.manage`, which this action also
78
+ * asserts up front).
79
+ *
80
+ * An existing tenant row for the same `(objectRef, fieldName)` is UPDATED
81
+ * (read-modify-write), preserving its other sparse columns — never
82
+ * duplicated through the natural-key upsert.
83
+ *
84
+ * Concurrency: the claim is a compare-and-set on `status` (the jobs
85
+ * `writeOwnedJob` precedent — a guarded UPDATE with `RETURNING id`), so an
86
+ * overlapping accept/dismiss pair cannot both win; the loser throws
87
+ * {@link FieldPolicySuggestionConflictError} (409). Claim and policy write
88
+ * run in ONE transaction when the driver supports it, so a rejected policy
89
+ * write can never leave the suggestion settled without its policy (and vice
90
+ * versa). Drivers without transactions get an explicit compensating revert.
91
+ */
92
+ acceptSuggestion(options?: {
93
+ id?: string;
94
+ }): Promise<AcceptFieldPolicySuggestionResult>;
95
+ /**
96
+ * Dismiss a pending suggestion: set status + a cool-down until which the
97
+ * generation job will NOT regenerate the same
98
+ * `(objectRef, fieldName, tenantId, kind)` suggestion.
99
+ *
100
+ * Validates OWNERSHIP and PENDING STATUS ONLY — deliberately NOT continued
101
+ * proposal eligibility. A queued suggestion whose field has since been
102
+ * removed, turned sensitive/`readPermission`-gated/transient, or retyped is
103
+ * exactly the row an operator most needs to clear; re-running proposal
104
+ * validation on the way out would reject the dismissal and, because pending
105
+ * rows are never pruned, wedge it in the queue forever. The claim is the same
106
+ * compare-and-set as accept, so it also bypasses the model's proposal
107
+ * validation by construction.
108
+ */
109
+ dismissSuggestion(options?: {
110
+ id?: string;
111
+ coolDownMs?: number;
112
+ }): Promise<DismissFieldPolicySuggestionResult>;
113
+ /**
114
+ * Write the org-scope policy column an accepted suggestion implies, bound to
115
+ * `db` so it participates in the caller's transaction.
116
+ *
117
+ * A `promote` and a `default` suggestion for the SAME field share one sparse
118
+ * `FieldPolicy` row but own DIFFERENT columns, and both may be accepted
119
+ * concurrently. A read-modify-save of the whole row therefore loses updates:
120
+ * each acceptance reads the row (or its absence) before the other's save
121
+ * lands, and the later full-row write erases the sibling's column — and, via
122
+ * the natural-key upsert, can even replace the row id, dangling the earlier
123
+ * caller's `policyRowId`. (Distinct from the suggestion-row race fixed
124
+ * earlier: that one guards the pending→settled transition; this one is on the
125
+ * shared policy row.) So this NEVER writes a whole row over an existing one:
126
+ *
127
+ * 1. An ATOMIC partial UPDATE sets only this acceptance's column, keyed by
128
+ * the policy natural key and `RETURNING id` — one statement, so no
129
+ * interleaving can drop the sibling's column, and the returned id is
130
+ * always the surviving row.
131
+ * 2. Only when no row exists yet does it CREATE through the model, so full
132
+ * validation (registry, type check, security rail, required-field
133
+ * invariant, ownership + permission split) runs on the row that is
134
+ * actually inserted. The row carries a DETERMINISTIC id under strict
135
+ * insert, so two concurrent creates cannot each mint a row: the loser
136
+ * collides and falls back to step 1, applying only its own column.
137
+ *
138
+ * The update path skips the model, so BOTH kinds re-check live target
139
+ * addressability through the shared helpers (a field can turn sensitive,
140
+ * gated, transient, or disappear between queueing and acceptance). Defaults
141
+ * additionally re-check their value/type rail; a promotion to `basic` has no
142
+ * value payload, and the required-field invariant restricts only
143
+ * advanced/hidden. Org locks constrain the user tier, not org rows.
144
+ */
145
+ private applyAcceptedPolicy;
146
+ /**
147
+ * A driver transaction handle, or `null` when the driver has none (the
148
+ * `FieldPolicy.saveAfterIdentityChange` probe, same shape).
149
+ */
150
+ private beginTransactionIfSupported;
151
+ /**
152
+ * Ambient manage gate shared by the three actions: an ambient tenant
153
+ * context is required (no context ⇒ no identity ⇒ fail closed — the
154
+ * suggestion queue is never an anonymous surface), and the caller must hold
155
+ * `fields.policy.manage` unless running under super-admin bypass.
156
+ */
157
+ private requireManageContext;
158
+ private loadOwnedPendingSuggestion;
159
+ }
160
+ //# sourceMappingURL=FieldPolicySuggestionCollection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldPolicySuggestionCollection.d.ts","sourceRoot":"","sources":["../../src/collections/FieldPolicySuggestionCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAQ,MAAM,0BAA0B,CAAC;AAkBhE,OAAO,EAEL,qBAAqB,EACtB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,KAAK,EACV,iCAAiC,EACjC,kCAAkC,EAClC,mCAAmC,EACpC,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;GAaG;AACH,qBAAa,kCAAmC,SAAQ,KAAK;IAC3D,oEAAoE;IACpE,QAAQ,CAAC,UAAU,OAAO;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,OAAO;gBAEV,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAOpD;AAwHD;;;;;GAKG;AACH,qBAAa,sCAAuC,SAAQ,KAAK;IAC/D,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,MAAM,OAAO;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;gBAElB,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO;CAgBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAQjB;AA2GD,uDAAuD;AACvD,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,eAAO,MAAM,2BAA2B,QAAiB,CAAC;AAC1D,eAAO,MAAM,2BAA2B,QAA4B,CAAC;AAKrE;;;;;;;;;;GAUG;AACH,qBAsCa,+BAAgC,SAAQ,cAAc,CAAC,qBAAqB,CAAC;IACxF,MAAM,CAAC,QAAQ,CAAC,UAAU,+BAAyB;IAEnD;;;OAGG;IACG,kBAAkB,CACtB,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACtC,OAAO,CAAC,mCAAmC,CAAC;IAgB/C;;;;;;;;;;;;;;;;;;;OAmBG;IACG,gBAAgB,CACpB,OAAO,GAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAO,GAC5B,OAAO,CAAC,iCAAiC,CAAC;IA6H7C;;;;;;;;;;;;;OAaG;IACG,iBAAiB,CACrB,OAAO,GAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,GACjD,OAAO,CAAC,kCAAkC,CAAC;IAkC9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;YACW,mBAAmB;IAsEjC;;;OAGG;YACW,2BAA2B;IAUzC;;;;;OAKG;YACW,oBAAoB;YAqBpB,0BAA0B;CA+BzC"}