@wildo-ai/saas-models 1.1.0 → 1.1.2

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 (292) hide show
  1. package/dist/esm/applications/applications-context.schemas.d.ts +2 -0
  2. package/dist/esm/applications/applications-context.schemas.d.ts.map +1 -1
  3. package/dist/esm/billing/billing-account.shared.resources-config.schemas.d.ts +1 -1
  4. package/dist/esm/billing/billing-account.shared.resources-config.schemas.d.ts.map +1 -1
  5. package/dist/esm/billing/billing-account.shared.resources-config.schemas.js +36 -0
  6. package/dist/esm/billing/billing-account.shared.resources-config.schemas.js.map +1 -1
  7. package/dist/esm/billing/billing-account.shared.schemas.d.ts.map +1 -1
  8. package/dist/esm/billing/billing-account.shared.schemas.js +8 -1
  9. package/dist/esm/billing/billing-account.shared.schemas.js.map +1 -1
  10. package/dist/esm/billing/billing-types.shared.schemas.d.ts +2 -2
  11. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.d.ts +1 -1
  12. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.d.ts.map +1 -1
  13. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.js +39 -0
  14. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.js.map +1 -1
  15. package/dist/esm/billing/credit-pool.shared.schemas.d.ts +6 -6
  16. package/dist/esm/billing/credit-pool.shared.schemas.d.ts.map +1 -1
  17. package/dist/esm/billing/credit-pool.shared.schemas.js +8 -4
  18. package/dist/esm/billing/credit-pool.shared.schemas.js.map +1 -1
  19. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.d.ts +1 -1
  20. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.d.ts.map +1 -1
  21. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.js +35 -0
  22. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.js.map +1 -1
  23. package/dist/esm/billing/invoice-ref.shared.schemas.d.ts.map +1 -1
  24. package/dist/esm/billing/invoice-ref.shared.schemas.js +8 -3
  25. package/dist/esm/billing/invoice-ref.shared.schemas.js.map +1 -1
  26. package/dist/esm/billing/subscription.shared.resources-config.schemas.d.ts +1 -1
  27. package/dist/esm/billing/subscription.shared.resources-config.schemas.d.ts.map +1 -1
  28. package/dist/esm/billing/subscription.shared.resources-config.schemas.js +39 -0
  29. package/dist/esm/billing/subscription.shared.resources-config.schemas.js.map +1 -1
  30. package/dist/esm/billing/subscription.shared.schemas.d.ts +4 -4
  31. package/dist/esm/billing/subscription.shared.schemas.d.ts.map +1 -1
  32. package/dist/esm/billing/subscription.shared.schemas.js +6 -2
  33. package/dist/esm/billing/subscription.shared.schemas.js.map +1 -1
  34. package/dist/esm/billing/usage-record.shared.resources-config.schemas.d.ts +1 -1
  35. package/dist/esm/billing/usage-record.shared.resources-config.schemas.d.ts.map +1 -1
  36. package/dist/esm/billing/usage-record.shared.resources-config.schemas.js +39 -0
  37. package/dist/esm/billing/usage-record.shared.resources-config.schemas.js.map +1 -1
  38. package/dist/esm/billing/usage-record.shared.schemas.d.ts +4 -4
  39. package/dist/esm/billing/usage-record.shared.schemas.d.ts.map +1 -1
  40. package/dist/esm/billing/usage-record.shared.schemas.js +7 -3
  41. package/dist/esm/billing/usage-record.shared.schemas.js.map +1 -1
  42. package/dist/esm/compliance/audit-trails/audit-logs.shared.resources-config.schemas.d.ts.map +1 -1
  43. package/dist/esm/compliance/audit-trails/audit-logs.shared.resources-config.schemas.js +35 -2
  44. package/dist/esm/compliance/audit-trails/audit-logs.shared.resources-config.schemas.js.map +1 -1
  45. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.d.ts +61 -0
  46. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.d.ts.map +1 -1
  47. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.js +58 -0
  48. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.js.map +1 -1
  49. package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.d.ts.map +1 -1
  50. package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.js +14 -0
  51. package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.js.map +1 -1
  52. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.d.ts +49 -0
  53. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.d.ts.map +1 -1
  54. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.js +124 -1
  55. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.js.map +1 -1
  56. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.d.ts +11 -3
  57. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.d.ts.map +1 -1
  58. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.js +11 -3
  59. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.js.map +1 -1
  60. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.d.ts +93 -0
  61. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.d.ts.map +1 -0
  62. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.js +116 -0
  63. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.js.map +1 -0
  64. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.d.ts +67 -12
  65. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.d.ts.map +1 -1
  66. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.js +95 -8
  67. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.js.map +1 -1
  68. package/dist/esm/compliance/privacy/redaction.shared.schemas.d.ts +21 -1
  69. package/dist/esm/compliance/privacy/redaction.shared.schemas.d.ts.map +1 -1
  70. package/dist/esm/compliance/privacy/redaction.shared.schemas.js +20 -0
  71. package/dist/esm/compliance/privacy/redaction.shared.schemas.js.map +1 -1
  72. package/dist/esm/config/app-configuration-shared.shared.schemas.d.ts +2 -0
  73. package/dist/esm/config/app-configuration-shared.shared.schemas.d.ts.map +1 -1
  74. package/dist/esm/config/app-configuration-shared.shared.schemas.js +29 -0
  75. package/dist/esm/config/app-configuration-shared.shared.schemas.js.map +1 -1
  76. package/dist/esm/config/frontend/navigation.shared.schemas.d.ts +33 -0
  77. package/dist/esm/config/frontend/navigation.shared.schemas.d.ts.map +1 -1
  78. package/dist/esm/config/frontend/navigation.shared.schemas.js +32 -0
  79. package/dist/esm/config/frontend/navigation.shared.schemas.js.map +1 -1
  80. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.d.ts +12 -1
  81. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.d.ts.map +1 -1
  82. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.js +12 -1
  83. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.js.map +1 -1
  84. package/dist/esm/external-data/external-data-pipeline.shared.schemas.d.ts +13 -0
  85. package/dist/esm/external-data/external-data-pipeline.shared.schemas.d.ts.map +1 -1
  86. package/dist/esm/external-data/external-data-pipeline.shared.schemas.js.map +1 -1
  87. package/dist/esm/external-providers/engine-capabilities.shared.schemas.d.ts +6 -6
  88. package/dist/esm/external-providers/engine-capabilities.shared.schemas.d.ts.map +1 -1
  89. package/dist/esm/external-providers/engine-capabilities.shared.schemas.js +26 -6
  90. package/dist/esm/external-providers/engine-capabilities.shared.schemas.js.map +1 -1
  91. package/dist/esm/external-providers/engine-capability-resolution-mode.shared.d.ts.map +1 -1
  92. package/dist/esm/external-providers/engine-capability-resolution-mode.shared.js +0 -2
  93. package/dist/esm/external-providers/engine-capability-resolution-mode.shared.js.map +1 -1
  94. package/dist/esm/external-providers/frontend-bootstrap.shared.schemas.d.ts +1 -0
  95. package/dist/esm/external-providers/frontend-bootstrap.shared.schemas.d.ts.map +1 -1
  96. package/dist/esm/external-providers/provider-capability.shared.schemas.d.ts +4 -3
  97. package/dist/esm/external-providers/provider-capability.shared.schemas.d.ts.map +1 -1
  98. package/dist/esm/external-providers/provider-capability.shared.schemas.js +4 -3
  99. package/dist/esm/external-providers/provider-capability.shared.schemas.js.map +1 -1
  100. package/dist/esm/features/user-features.shared.resources-config.schemas.d.ts +1 -1
  101. package/dist/esm/features/user-features.shared.resources-config.schemas.d.ts.map +1 -1
  102. package/dist/esm/features/user-features.shared.resources-config.schemas.js +48 -0
  103. package/dist/esm/features/user-features.shared.resources-config.schemas.js.map +1 -1
  104. package/dist/esm/files/file-upload-grant.shared.schemas.d.ts +144 -0
  105. package/dist/esm/files/file-upload-grant.shared.schemas.d.ts.map +1 -0
  106. package/dist/esm/files/file-upload-grant.shared.schemas.js +136 -0
  107. package/dist/esm/files/file-upload-grant.shared.schemas.js.map +1 -0
  108. package/dist/esm/files/files.shared.schemas.d.ts +45 -4
  109. package/dist/esm/files/files.shared.schemas.d.ts.map +1 -1
  110. package/dist/esm/files/files.shared.schemas.js +48 -4
  111. package/dist/esm/files/files.shared.schemas.js.map +1 -1
  112. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.d.ts +1 -1
  113. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.d.ts.map +1 -1
  114. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.js +33 -0
  115. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.js.map +1 -1
  116. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.schemas.d.ts.map +1 -1
  117. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.schemas.js +6 -2
  118. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.schemas.js.map +1 -1
  119. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.d.ts +1 -1
  120. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.d.ts.map +1 -1
  121. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.js +53 -0
  122. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.js.map +1 -1
  123. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.d.ts +1 -1
  124. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.d.ts.map +1 -1
  125. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.js +43 -0
  126. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.js.map +1 -1
  127. package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.d.ts.map +1 -1
  128. package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.js +8 -3
  129. package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.js.map +1 -1
  130. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.d.ts +1 -1
  131. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.d.ts.map +1 -1
  132. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.js +41 -0
  133. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.js.map +1 -1
  134. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.d.ts +0 -7
  135. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.d.ts.map +1 -1
  136. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.js +23 -13
  137. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.js.map +1 -1
  138. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.d.ts +1 -1
  139. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.d.ts.map +1 -1
  140. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.js +51 -0
  141. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.js.map +1 -1
  142. package/dist/esm/guidance/guidance-state.shared.schemas.d.ts.map +1 -1
  143. package/dist/esm/guidance/guidance-state.shared.schemas.js +8 -2
  144. package/dist/esm/guidance/guidance-state.shared.schemas.js.map +1 -1
  145. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.d.ts +1 -1
  146. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.d.ts.map +1 -1
  147. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.js +51 -0
  148. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.js.map +1 -1
  149. package/dist/esm/guidance/lifecycle-state.shared.schemas.d.ts.map +1 -1
  150. package/dist/esm/guidance/lifecycle-state.shared.schemas.js +28 -3
  151. package/dist/esm/guidance/lifecycle-state.shared.schemas.js.map +1 -1
  152. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.d.ts +1 -1
  153. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.d.ts.map +1 -1
  154. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.js +51 -0
  155. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.js.map +1 -1
  156. package/dist/esm/guidance/progression-state.shared.schemas.d.ts.map +1 -1
  157. package/dist/esm/guidance/progression-state.shared.schemas.js +18 -4
  158. package/dist/esm/guidance/progression-state.shared.schemas.js.map +1 -1
  159. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.d.ts +37 -3
  160. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.d.ts.map +1 -1
  161. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.js +29 -3
  162. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.js.map +1 -1
  163. package/dist/esm/index.d.ts +6 -0
  164. package/dist/esm/index.d.ts.map +1 -1
  165. package/dist/esm/index.js +6 -0
  166. package/dist/esm/index.js.map +1 -1
  167. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.d.ts +70 -0
  168. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.d.ts.map +1 -0
  169. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.js +71 -0
  170. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.js.map +1 -0
  171. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.d.ts +1 -1
  172. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.d.ts.map +1 -1
  173. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.js +36 -0
  174. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.js.map +1 -1
  175. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.d.ts +1 -1
  176. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.d.ts.map +1 -1
  177. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.js +44 -0
  178. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.js.map +1 -1
  179. package/dist/esm/organizations/organization-members.shared.schemas.d.ts.map +1 -1
  180. package/dist/esm/organizations/organization-members.shared.schemas.js +15 -4
  181. package/dist/esm/organizations/organization-members.shared.schemas.js.map +1 -1
  182. package/dist/esm/organizations/organizations.shared.resources-config.schemas.d.ts.map +1 -1
  183. package/dist/esm/organizations/organizations.shared.resources-config.schemas.js +18 -10
  184. package/dist/esm/organizations/organizations.shared.resources-config.schemas.js.map +1 -1
  185. package/dist/esm/queue/jobs.shared.resources-config.schemas.d.ts.map +1 -1
  186. package/dist/esm/queue/jobs.shared.resources-config.schemas.js +17 -0
  187. package/dist/esm/queue/jobs.shared.resources-config.schemas.js.map +1 -1
  188. package/dist/esm/requests/websocket.shared.schemas.d.ts +59 -0
  189. package/dist/esm/requests/websocket.shared.schemas.d.ts.map +1 -1
  190. package/dist/esm/requests/websocket.shared.schemas.js +103 -0
  191. package/dist/esm/requests/websocket.shared.schemas.js.map +1 -1
  192. package/dist/esm/resources/collection-query-parameters.shared.d.ts +22 -0
  193. package/dist/esm/resources/collection-query-parameters.shared.d.ts.map +1 -0
  194. package/dist/esm/resources/collection-query-parameters.shared.js +22 -0
  195. package/dist/esm/resources/collection-query-parameters.shared.js.map +1 -0
  196. package/dist/esm/resources/resources-config.shared.factory.d.ts.map +1 -1
  197. package/dist/esm/resources/resources-config.shared.factory.js +86 -13
  198. package/dist/esm/resources/resources-config.shared.factory.js.map +1 -1
  199. package/dist/esm/resources/resources-config.shared.schemas.d.ts +162 -41
  200. package/dist/esm/resources/resources-config.shared.schemas.d.ts.map +1 -1
  201. package/dist/esm/resources/resources-config.shared.schemas.js +109 -20
  202. package/dist/esm/resources/resources-config.shared.schemas.js.map +1 -1
  203. package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.d.ts.map +1 -1
  204. package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.js +21 -6
  205. package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.js.map +1 -1
  206. package/dist/esm/security/authentications/authentication.shared.schemas.d.ts +57 -0
  207. package/dist/esm/security/authentications/authentication.shared.schemas.d.ts.map +1 -1
  208. package/dist/esm/security/authentications/authentication.shared.schemas.js +64 -0
  209. package/dist/esm/security/authentications/authentication.shared.schemas.js.map +1 -1
  210. package/dist/esm/security/authentications/consumable-token.shared.schemas.d.ts +17 -1
  211. package/dist/esm/security/authentications/consumable-token.shared.schemas.d.ts.map +1 -1
  212. package/dist/esm/security/authentications/consumable-token.shared.schemas.js +16 -0
  213. package/dist/esm/security/authentications/consumable-token.shared.schemas.js.map +1 -1
  214. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.d.ts +28 -0
  215. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.d.ts.map +1 -0
  216. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.js +28 -0
  217. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.js.map +1 -0
  218. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.d.ts +21 -0
  219. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.d.ts.map +1 -0
  220. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.js +21 -0
  221. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.js.map +1 -0
  222. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.d.ts +1 -1
  223. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.d.ts.map +1 -1
  224. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.js +43 -0
  225. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.js.map +1 -1
  226. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.d.ts +0 -8
  227. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.d.ts.map +1 -1
  228. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.js +13 -4
  229. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.js.map +1 -1
  230. package/dist/esm/security/authorizations/roles.shared.schemas.d.ts +16 -1
  231. package/dist/esm/security/authorizations/roles.shared.schemas.d.ts.map +1 -1
  232. package/dist/esm/security/authorizations/roles.shared.schemas.js +16 -1
  233. package/dist/esm/security/authorizations/roles.shared.schemas.js.map +1 -1
  234. package/dist/esm/security/authorizations/roles.shared.utils.d.ts +54 -0
  235. package/dist/esm/security/authorizations/roles.shared.utils.d.ts.map +1 -1
  236. package/dist/esm/security/authorizations/roles.shared.utils.js +59 -0
  237. package/dist/esm/security/authorizations/roles.shared.utils.js.map +1 -1
  238. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.d.ts +61 -0
  239. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.d.ts.map +1 -1
  240. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.js +13 -1
  241. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.js.map +1 -1
  242. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.d.ts +1 -1
  243. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.d.ts.map +1 -1
  244. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.js +35 -0
  245. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.js.map +1 -1
  246. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.d.ts.map +1 -1
  247. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.js +7 -1
  248. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.js.map +1 -1
  249. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.d.ts +1 -1
  250. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.d.ts.map +1 -1
  251. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.js +34 -0
  252. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.js.map +1 -1
  253. package/dist/esm/users/inbound-contacts.shared.schemas.d.ts.map +1 -1
  254. package/dist/esm/users/inbound-contacts.shared.schemas.js +7 -4
  255. package/dist/esm/users/inbound-contacts.shared.schemas.js.map +1 -1
  256. package/dist/esm/users/user-credentials.shared.resources-config.schemas.d.ts +1 -1
  257. package/dist/esm/users/user-credentials.shared.resources-config.schemas.d.ts.map +1 -1
  258. package/dist/esm/users/user-credentials.shared.resources-config.schemas.js +37 -0
  259. package/dist/esm/users/user-credentials.shared.resources-config.schemas.js.map +1 -1
  260. package/dist/esm/users/user-credentials.shared.schemas.d.ts.map +1 -1
  261. package/dist/esm/users/user-credentials.shared.schemas.js +17 -2
  262. package/dist/esm/users/user-credentials.shared.schemas.js.map +1 -1
  263. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.d.ts +1 -1
  264. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.d.ts.map +1 -1
  265. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.js +36 -0
  266. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.js.map +1 -1
  267. package/dist/esm/users/user-identity-links.shared.schemas.d.ts.map +1 -1
  268. package/dist/esm/users/user-identity-links.shared.schemas.js +15 -5
  269. package/dist/esm/users/user-identity-links.shared.schemas.js.map +1 -1
  270. package/dist/esm/users/user-preferences.shared.resources-config.schemas.d.ts +1 -1
  271. package/dist/esm/users/user-preferences.shared.resources-config.schemas.d.ts.map +1 -1
  272. package/dist/esm/users/user-preferences.shared.resources-config.schemas.js +53 -0
  273. package/dist/esm/users/user-preferences.shared.resources-config.schemas.js.map +1 -1
  274. package/dist/esm/users/user-profiles.shared.resources-config.schemas.d.ts +1 -1
  275. package/dist/esm/users/user-profiles.shared.resources-config.schemas.d.ts.map +1 -1
  276. package/dist/esm/users/user-profiles.shared.resources-config.schemas.js +62 -0
  277. package/dist/esm/users/user-profiles.shared.resources-config.schemas.js.map +1 -1
  278. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.d.ts +1 -1
  279. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.d.ts.map +1 -1
  280. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.js +53 -0
  281. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.js.map +1 -1
  282. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.d.ts +1 -1
  283. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.d.ts.map +1 -1
  284. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.js +62 -0
  285. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.js.map +1 -1
  286. package/dist/esm/users/users.shared.schemas.d.ts +19 -0
  287. package/dist/esm/users/users.shared.schemas.d.ts.map +1 -1
  288. package/dist/esm/users/users.shared.schemas.js +41 -16
  289. package/dist/esm/users/users.shared.schemas.js.map +1 -1
  290. package/dist/tsconfig.build.tsbuildinfo +1 -1
  291. package/package.json +5 -6
  292. package/dist/esm/.builder.pid +0 -9
@@ -1 +1 @@
1
- {"version":3,"file":"impersonalization.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/impersonalization.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,oBAAY,eAAe;IACzB,sDAAsD;IACtD,MAAM,WAAW;IACjB;;;;;OAKG;IACH,QAAQ,aAAa;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,eAAe,CAC3C,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AACH,oBAAY,oBAAoB;IAC9B;;;;;;OAMG;IACH,WAAW,gBAAgB;IAC3B;;;;;;OAMG;IACH,wBAAwB,6BAA6B;CACtD;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,oBAAoB,CAChD,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,oBAAY,uBAAuB;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,iBAAiB;IAC7B;;;;;;;;;OASG;IACH,cAAc,mBAAmB;IACjC;;;;;;;;;;OAUG;IACH,gBAAgB,qBAAqB;CACtC;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,uBAAuB,CACnD,CAAC"}
1
+ {"version":3,"file":"impersonalization.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/impersonalization.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,oBAAY,eAAe;IACzB,sDAAsD;IACtD,MAAM,WAAW;IACjB;;;;;;;;;;;;;OAaG;IACH,QAAQ,aAAa;CACtB;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,eAAe,CAC3C,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AACH,oBAAY,oBAAoB;IAC9B;;;;;;OAMG;IACH,WAAW,gBAAgB;IAC3B;;;;;;OAMG;IACH,wBAAwB,6BAA6B;CACtD;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,oBAAoB,CAChD,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,oBAAY,uBAAuB;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,iBAAiB;IAC7B;;;;;;;;;OASG;IACH,cAAc,mBAAmB;IACjC;;;;;;;;;;OAUG;IACH,gBAAgB,qBAAqB;CACtC;AAED;;;GAGG;AACH,eAAO,MAAM,6BAA6B,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,uBAAuB,CACnD,CAAC"}
@@ -29,9 +29,17 @@ export var RetentionStatus;
29
29
  RetentionStatus["ACTIVE"] = "active";
30
30
  /**
31
31
  * Impersonalized-and-retained — personal fields scrubbed in place, the row kept for a
32
- * retention obligation. Intended semantics (enforcement wired in a later phase): hidden
33
- * and immutable to normal callers; readable only via the audited see-through path;
34
- * disposable only by a privileged system op.
32
+ * retention obligation: hidden and immutable to normal callers, readable only via the audited
33
+ * `seeRetained` see-through path, disposable only by a privileged system op.
34
+ *
35
+ * ENFORCED, not intended. This said "enforcement wired in a later phase" long after the phase
36
+ * landed, which is worth correcting rather than deleting: a reader deciding whether to trust the
37
+ * marker would have concluded the row was still visible. The rule has one semantic authority —
38
+ * `compliance/privacy/retention-hide-predicate.backend` — consumed by both persistence adapters,
39
+ * the aggregation/charts surface and RAG retrieval, and a guard test fails the suite if any site
40
+ * hand-writes the predicate again. That single-authority shape exists because the duplicated
41
+ * version produced two recovered defects: retained rows still counted in every chart, and the
42
+ * Mongo/Postgres compilers silently disagreed about which rows a NULL marker selected.
35
43
  */
36
44
  RetentionStatus["RETAINED"] = "retained";
37
45
  })(RetentionStatus || (RetentionStatus = {}));
@@ -1 +1 @@
1
- {"version":3,"file":"impersonalization.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/impersonalization.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAN,IAAY,eAUX;AAVD,WAAY,eAAe;IACzB,sDAAsD;IACtD,oCAAiB,CAAA;IACjB;;;;;OAKG;IACH,wCAAqB,CAAA;AACvB,CAAC,EAVW,eAAe,KAAf,eAAe,QAU1B;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAN,IAAY,oBAiBX;AAjBD,WAAY,oBAAoB;IAC9B;;;;;;OAMG;IACH,mDAA2B,CAAA;IAC3B;;;;;;OAMG;IACH,6EAAqD,CAAA;AACvD,CAAC,EAjBW,oBAAoB,KAApB,oBAAoB,QAiB/B;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GACrC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,MAAM,CAAN,IAAY,uBA4CX;AA5CD,WAAY,uBAAuB;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,wDAA6B,CAAA;IAC7B;;;;;;;;;OASG;IACH,4DAAiC,CAAA;IACjC;;;;;;;;;;OAUG;IACH,gEAAqC,CAAA;AACvC,CAAC,EA5CW,uBAAuB,KAAvB,uBAAuB,QA4ClC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * # Impersonalization — per-record retention lifecycle state\n *\n * `impersonalization` = scrubbing the personal data OUT of a stored record while\n * KEEPING the row, so it can be retained for a legal/audit obligation (GDPR\n * Art. 17(3)(b) legal-obligation carve-out; tax/SOX retention). This is the third\n * erasure outcome alongside hard-delete and leave-as-is.\n *\n * ⚠️ **Not** the anonymous-user concept. `isAnonymizable` / `anonymousUserId` /\n * anonymous-session ownership transposition is an unrelated, opposite-direction\n * mechanism (carrying a pre-auth anonymous session's rows forward to a real user).\n * Never conflate the two. See `.claude/plans/retention-impersonalization-architecture.md`.\n *\n * `RetentionStatus` is the per-record marker the resource factory injects (as the\n * `retentionStatus` field) onto any resource that opts into `retentionPolicy`. The\n * INTENDED contract for a `RETAINED` record is **hidden and immutable by default**\n * (behave-as-deleted), reachable only through the audited `seeRetained` see-through path.\n *\n * **LIVE.** The factory injects the marker (defaulting to `ACTIVE`), the visibility/immutability\n * enforcement and the audited see-through read primitive are all wired and runtime-proven. A\n * `RETAINED` record is hidden from every ordinary read, list, count and chart aggregate, and is\n * immutable to ordinary writes. (This block previously said the enforcement was \"wired in a later\n * phase\"; that became false without being revisited.)\n */\nexport enum RetentionStatus {\n /** Live record — normal visibility and mutability. */\n ACTIVE = 'active',\n /**\n * Impersonalized-and-retained — personal fields scrubbed in place, the row kept for a\n * retention obligation. Intended semantics (enforcement wired in a later phase): hidden\n * and immutable to normal callers; readable only via the audited see-through path;\n * disposable only by a privileged system op.\n */\n RETAINED = 'retained',\n}\n\n/**\n * Zod schema for {@link RetentionStatus}. Explicitly annotated for the\n * `isolatedDeclarations` emit constraint.\n */\nexport const RetentionStatusSchema: z.ZodEnum<typeof RetentionStatus> =\n z.enum(RetentionStatus);\n\n/**\n * # What erasure DOES to a retention-governed row\n *\n * Retention (the row survives, is marked `RETAINED`, becomes hidden + immutable) and\n * impersonalization (its personal FIELDS are scrubbed) are ORTHOGONAL. A record can be legally\n * retainable while holding no personal data of its own — a financial row that merely POINTS at a\n * person is the canonical case: erasing the subject must not destroy the invoice, but there is\n * nothing on the invoice to scrub, because the name and address live on the linked user.\n *\n * Fusing the two axes forced such a resource to either declare a fake scrub (shipping a record\n * stamped \"impersonalized\" that never was) or forgo retention entirely (letting the cascade destroy a\n * legally-required record). Naming the two outcomes separately removes that false choice AND makes\n * the author's claim auditable: \"this resource holds no personal data\" becomes an explicit, reviewable\n * declaration rather than something a compliance reviewer must infer from an absence of treatments.\n */\nexport enum ErasureRetentionMode {\n /**\n * Keep and FREEZE the row; scrub nothing — this resource holds no personal data of its own.\n *\n * Declaring a `.impersonalizeWith()` treatment under this mode is a contradiction and fails at\n * config build: if a field does hold personal data, the resource wants\n * {@link ErasureRetentionMode.RETAIN_AND_IMPERSONALIZE} instead.\n */\n RETAIN_ONLY = 'retain_only',\n /**\n * Keep and freeze the row AND scrub the fields marked with `.impersonalizeWith()`.\n *\n * Requires at least one field that the writer would ACTUALLY scrub (a `KEEP` treatment, or one on a\n * never-scrub floor field such as a scope FK, does not count) — otherwise the row would flip to\n * `RETAINED` with every field intact while claiming to have been impersonalized.\n */\n RETAIN_AND_IMPERSONALIZE = 'retain_and_impersonalize',\n}\n\n/**\n * Zod schema for {@link ErasureRetentionMode}. Explicitly annotated for the\n * `isolatedDeclarations` emit constraint.\n */\nexport const ErasureRetentionModeSchema: z.ZodEnum<typeof ErasureRetentionMode> =\n z.enum(ErasureRetentionMode);\n\n/**\n * **WHOSE SESSION ENDS** when a row of this resource is erased. The second axis of the erasure\n * contract, beside {@link ErasureRetentionMode} (which says what happens to the row).\n *\n * ## This axis is NOT \"is this row about a person?\" — read this before adding a member\n *\n * The two questions look like one and are not, and conflating them cost this vocabulary a member name\n * for months (see the rename note below). Wildo answers them on SEPARATE axes:\n *\n * | question | the axis that answers it |\n * |---|---|\n * | Does this resource hold personal data? | {@link ErasureRetentionMode} — `RETAIN_AND_IMPERSONALIZE` requires at least one field that would actually be scrubbed; `RETAIN_ONLY` means *\"holds no personal data of its own\"*, and a scrub treatment under it fails at config build |\n * | Can a subject-access or portability export answer for it? | `systemAccessPolicy.exportSubject` |\n * | **Whose login session must be revoked?** | **this enum** |\n *\n * A CRM contact, a sales lead, a newsletter subscriber and a patient with no portal account are all\n * **data subjects with no session**. They declare `RETAIN_AND_IMPERSONALIZE` (they hold personal data)\n * and {@link ResourceDataSubjectKind.NO_PRINCIPAL} (there is no session to end) — two independent,\n * truthful answers. Adding a fourth member here for them would make this enum a cross-product of two\n * axes, which is two fields wearing one name.\n *\n * ## Why the third member is `NO_PRINCIPAL` and not `NOT_A_DATA_SUBJECT`\n *\n * It was `NOT_A_DATA_SUBJECT` until 2026-08-28, and the name was the defect. Operationally it always\n * meant *\"revoke nobody\"* — which is exactly right for a CRM contact — while its NAME claimed the row\n * was not about a person, which is false for one. Nothing misbehaved at runtime (the single consumer,\n * `resolveScrubSessionPrincipals`, only ever asked the session question), so the cost landed entirely\n * on readers: a human, and a compliance-document generator, both read the name and would have stated\n * in a published privacy notice that the application holds no personal data in precisely the resource\n * its customers care most about.\n *\n * The industry model this now matches is unambiguous — Intercom deletes \"a user **or lead**\", HubSpot\n * GDPR-deletes a Contact by id or by email, Zendesk \"forgets\" an end-user: an account and a data\n * subject are different objects, and the erasure verb applies to both. Wildo's `eraseSubjectAsSystem`\n * already takes `(resourceType, rowIds)` with no principal, so the capability was never missing.\n *\n * ## Why this must be DECLARED and can never be inferred\n *\n * An erasure of a person must also end that person's session, or a \"deleted\" person keeps browsing on\n * a live token until it expires. The tempting shortcut is to look for a `userId`-shaped foreign key on\n * the erased row and revoke it. **That shortcut is a defect, not an optimisation.** On almost every\n * resource a user foreign key names the OWNER or the ACTOR, not the subject: Wonder Todos' `TODOS` is\n * retention-governed and carries `createdByUserId`, `assignedToUserId` and `escalationContactUserId`,\n * so an inferring implementation would log out the author, the assignee and the escalation contact —\n * three people, none of them the data subject — every time a todo was erased.\n *\n * So the linkage is authored, never guessed, and a resource that can be an erasure subject (i.e. one\n * declaring a `retentionPolicy`) must say which of these it is. Undeclared is refused at config build:\n * nobody has decided yet, and refusing is what forces the decision — the same doctrine the subject\n * EXPORT model applies to an undeclared resource.\n */\nexport enum ResourceDataSubjectKind {\n /**\n * Erasing a row of this resource revokes **no session**, because no login is reachable from it.\n *\n * Two very different populations share this answer, and the enum is deliberately silent about which\n * one a given resource is — {@link ErasureRetentionMode} says that:\n *\n * - **Not a person at all.** A todo, an invoice, a log line. Any user foreign keys it carries name\n * ACTORS — an author, an assignee, an owner, an escalation contact — and revoking their sessions\n * because a record was erased would log out three uninvolved people. Such a resource pairs this\n * with `RETAIN_ONLY`.\n * - **A person with no account.** A CRM contact, a sales lead, a subscriber, a patient with no\n * portal login. Their rows ARE personal data and carry the full weight of Arts 15–17 and 20 —\n * there is simply no session to end. Such a resource pairs this with `RETAIN_AND_IMPERSONALIZE`\n * and per-field `.impersonalizeWith()` treatments, and its subject-rights path is the ordinary\n * administrative one: `eraseSubjectAsSystem(resourceType, rowIds)` and a subject export.\n *\n * The second population is the majority of what a B2B SaaS customer stores about third parties, and\n * it is what most of a privacy notice describes. Do not read this member as \"nothing to disclose\".\n */\n NO_PRINCIPAL = 'no_principal',\n /**\n * Rows of this resource ARE the person, and the row's own id IS the session principal: erasing row\n * `X` ends the session of user `X`. This is `USERS` / `USER_SELF`.\n *\n * Note this makes EXPLICIT a revocation that is otherwise only incidental. Erasing a `USERS` row\n * already logs the user out today — but only as a side effect of the retention row-hide (the auth\n * fronts re-read the user, find the row hidden, and 401) plus the scrub write bumping\n * `authorizationVersion`. Nothing on the erasure path ever WROTE a revocation, so the guarantee\n * lived entirely in the behaviour of two unrelated mechanisms.\n */\n SELF_PRINCIPAL = 'self_principal',\n /**\n * Rows of this resource are a person whose login lives in a DIFFERENT `USERS` row — a Patient, an\n * Employee, a Member record that a human signs in as. `sessionPrincipalField` names the field\n * holding that user's id.\n *\n * The named field must carry no `.impersonalizeWith()` treatment. Not because the revocation would\n * break — the principal is resolved BEFORE the scrub, so a scrubbed field is still readable when it\n * matters — but because declaring one field to be BOTH the link to a person and personal data to\n * destroy is self-contradictory, and it leaves the retained row with no way to say whose session was\n * ended. Enforced at config build.\n */\n LINKED_PRINCIPAL = 'linked_principal',\n}\n\n/**\n * Zod schema for {@link ResourceDataSubjectKind}. Explicitly annotated for the\n * `isolatedDeclarations` emit constraint.\n */\nexport const ResourceDataSubjectKindSchema: z.ZodEnum<typeof ResourceDataSubjectKind> =\n z.enum(ResourceDataSubjectKind);\n"]}
1
+ {"version":3,"file":"impersonalization.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/impersonalization.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAN,IAAY,eAkBX;AAlBD,WAAY,eAAe;IACzB,sDAAsD;IACtD,oCAAiB,CAAA;IACjB;;;;;;;;;;;;;OAaG;IACH,wCAAqB,CAAA;AACvB,CAAC,EAlBW,eAAe,KAAf,eAAe,QAkB1B;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAE1B;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAN,IAAY,oBAiBX;AAjBD,WAAY,oBAAoB;IAC9B;;;;;;OAMG;IACH,mDAA2B,CAAA;IAC3B;;;;;;OAMG;IACH,6EAAqD,CAAA;AACvD,CAAC,EAjBW,oBAAoB,KAApB,oBAAoB,QAiB/B;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GACrC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,MAAM,CAAN,IAAY,uBA4CX;AA5CD,WAAY,uBAAuB;IACjC;;;;;;;;;;;;;;;;;;OAkBG;IACH,wDAA6B,CAAA;IAC7B;;;;;;;;;OASG;IACH,4DAAiC,CAAA;IACjC;;;;;;;;;;OAUG;IACH,gEAAqC,CAAA;AACvC,CAAC,EA5CW,uBAAuB,KAAvB,uBAAuB,QA4ClC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * # Impersonalization — per-record retention lifecycle state\n *\n * `impersonalization` = scrubbing the personal data OUT of a stored record while\n * KEEPING the row, so it can be retained for a legal/audit obligation (GDPR\n * Art. 17(3)(b) legal-obligation carve-out; tax/SOX retention). This is the third\n * erasure outcome alongside hard-delete and leave-as-is.\n *\n * ⚠️ **Not** the anonymous-user concept. `isAnonymizable` / `anonymousUserId` /\n * anonymous-session ownership transposition is an unrelated, opposite-direction\n * mechanism (carrying a pre-auth anonymous session's rows forward to a real user).\n * Never conflate the two. See `.claude/plans/retention-impersonalization-architecture.md`.\n *\n * `RetentionStatus` is the per-record marker the resource factory injects (as the\n * `retentionStatus` field) onto any resource that opts into `retentionPolicy`. The\n * INTENDED contract for a `RETAINED` record is **hidden and immutable by default**\n * (behave-as-deleted), reachable only through the audited `seeRetained` see-through path.\n *\n * **LIVE.** The factory injects the marker (defaulting to `ACTIVE`), the visibility/immutability\n * enforcement and the audited see-through read primitive are all wired and runtime-proven. A\n * `RETAINED` record is hidden from every ordinary read, list, count and chart aggregate, and is\n * immutable to ordinary writes. (This block previously said the enforcement was \"wired in a later\n * phase\"; that became false without being revisited.)\n */\nexport enum RetentionStatus {\n /** Live record — normal visibility and mutability. */\n ACTIVE = 'active',\n /**\n * Impersonalized-and-retained — personal fields scrubbed in place, the row kept for a\n * retention obligation: hidden and immutable to normal callers, readable only via the audited\n * `seeRetained` see-through path, disposable only by a privileged system op.\n *\n * ENFORCED, not intended. This said \"enforcement wired in a later phase\" long after the phase\n * landed, which is worth correcting rather than deleting: a reader deciding whether to trust the\n * marker would have concluded the row was still visible. The rule has one semantic authority —\n * `compliance/privacy/retention-hide-predicate.backend` — consumed by both persistence adapters,\n * the aggregation/charts surface and RAG retrieval, and a guard test fails the suite if any site\n * hand-writes the predicate again. That single-authority shape exists because the duplicated\n * version produced two recovered defects: retained rows still counted in every chart, and the\n * Mongo/Postgres compilers silently disagreed about which rows a NULL marker selected.\n */\n RETAINED = 'retained',\n}\n\n/**\n * Zod schema for {@link RetentionStatus}. Explicitly annotated for the\n * `isolatedDeclarations` emit constraint.\n */\nexport const RetentionStatusSchema: z.ZodEnum<typeof RetentionStatus> =\n z.enum(RetentionStatus);\n\n/**\n * # What erasure DOES to a retention-governed row\n *\n * Retention (the row survives, is marked `RETAINED`, becomes hidden + immutable) and\n * impersonalization (its personal FIELDS are scrubbed) are ORTHOGONAL. A record can be legally\n * retainable while holding no personal data of its own — a financial row that merely POINTS at a\n * person is the canonical case: erasing the subject must not destroy the invoice, but there is\n * nothing on the invoice to scrub, because the name and address live on the linked user.\n *\n * Fusing the two axes forced such a resource to either declare a fake scrub (shipping a record\n * stamped \"impersonalized\" that never was) or forgo retention entirely (letting the cascade destroy a\n * legally-required record). Naming the two outcomes separately removes that false choice AND makes\n * the author's claim auditable: \"this resource holds no personal data\" becomes an explicit, reviewable\n * declaration rather than something a compliance reviewer must infer from an absence of treatments.\n */\nexport enum ErasureRetentionMode {\n /**\n * Keep and FREEZE the row; scrub nothing — this resource holds no personal data of its own.\n *\n * Declaring a `.impersonalizeWith()` treatment under this mode is a contradiction and fails at\n * config build: if a field does hold personal data, the resource wants\n * {@link ErasureRetentionMode.RETAIN_AND_IMPERSONALIZE} instead.\n */\n RETAIN_ONLY = 'retain_only',\n /**\n * Keep and freeze the row AND scrub the fields marked with `.impersonalizeWith()`.\n *\n * Requires at least one field that the writer would ACTUALLY scrub (a `KEEP` treatment, or one on a\n * never-scrub floor field such as a scope FK, does not count) — otherwise the row would flip to\n * `RETAINED` with every field intact while claiming to have been impersonalized.\n */\n RETAIN_AND_IMPERSONALIZE = 'retain_and_impersonalize',\n}\n\n/**\n * Zod schema for {@link ErasureRetentionMode}. Explicitly annotated for the\n * `isolatedDeclarations` emit constraint.\n */\nexport const ErasureRetentionModeSchema: z.ZodEnum<typeof ErasureRetentionMode> =\n z.enum(ErasureRetentionMode);\n\n/**\n * **WHOSE SESSION ENDS** when a row of this resource is erased. The second axis of the erasure\n * contract, beside {@link ErasureRetentionMode} (which says what happens to the row).\n *\n * ## This axis is NOT \"is this row about a person?\" — read this before adding a member\n *\n * The two questions look like one and are not, and conflating them cost this vocabulary a member name\n * for months (see the rename note below). Wildo answers them on SEPARATE axes:\n *\n * | question | the axis that answers it |\n * |---|---|\n * | Does this resource hold personal data? | {@link ErasureRetentionMode} — `RETAIN_AND_IMPERSONALIZE` requires at least one field that would actually be scrubbed; `RETAIN_ONLY` means *\"holds no personal data of its own\"*, and a scrub treatment under it fails at config build |\n * | Can a subject-access or portability export answer for it? | `systemAccessPolicy.exportSubject` |\n * | **Whose login session must be revoked?** | **this enum** |\n *\n * A CRM contact, a sales lead, a newsletter subscriber and a patient with no portal account are all\n * **data subjects with no session**. They declare `RETAIN_AND_IMPERSONALIZE` (they hold personal data)\n * and {@link ResourceDataSubjectKind.NO_PRINCIPAL} (there is no session to end) — two independent,\n * truthful answers. Adding a fourth member here for them would make this enum a cross-product of two\n * axes, which is two fields wearing one name.\n *\n * ## Why the third member is `NO_PRINCIPAL` and not `NOT_A_DATA_SUBJECT`\n *\n * It was `NOT_A_DATA_SUBJECT` until 2026-08-28, and the name was the defect. Operationally it always\n * meant *\"revoke nobody\"* — which is exactly right for a CRM contact — while its NAME claimed the row\n * was not about a person, which is false for one. Nothing misbehaved at runtime (the single consumer,\n * `resolveScrubSessionPrincipals`, only ever asked the session question), so the cost landed entirely\n * on readers: a human, and a compliance-document generator, both read the name and would have stated\n * in a published privacy notice that the application holds no personal data in precisely the resource\n * its customers care most about.\n *\n * The industry model this now matches is unambiguous — Intercom deletes \"a user **or lead**\", HubSpot\n * GDPR-deletes a Contact by id or by email, Zendesk \"forgets\" an end-user: an account and a data\n * subject are different objects, and the erasure verb applies to both. Wildo's `eraseSubjectAsSystem`\n * already takes `(resourceType, rowIds)` with no principal, so the capability was never missing.\n *\n * ## Why this must be DECLARED and can never be inferred\n *\n * An erasure of a person must also end that person's session, or a \"deleted\" person keeps browsing on\n * a live token until it expires. The tempting shortcut is to look for a `userId`-shaped foreign key on\n * the erased row and revoke it. **That shortcut is a defect, not an optimisation.** On almost every\n * resource a user foreign key names the OWNER or the ACTOR, not the subject: Wonder Todos' `TODOS` is\n * retention-governed and carries `createdByUserId`, `assignedToUserId` and `escalationContactUserId`,\n * so an inferring implementation would log out the author, the assignee and the escalation contact —\n * three people, none of them the data subject — every time a todo was erased.\n *\n * So the linkage is authored, never guessed, and a resource that can be an erasure subject (i.e. one\n * declaring a `retentionPolicy`) must say which of these it is. Undeclared is refused at config build:\n * nobody has decided yet, and refusing is what forces the decision — the same doctrine the subject\n * EXPORT model applies to an undeclared resource.\n */\nexport enum ResourceDataSubjectKind {\n /**\n * Erasing a row of this resource revokes **no session**, because no login is reachable from it.\n *\n * Two very different populations share this answer, and the enum is deliberately silent about which\n * one a given resource is — {@link ErasureRetentionMode} says that:\n *\n * - **Not a person at all.** A todo, an invoice, a log line. Any user foreign keys it carries name\n * ACTORS — an author, an assignee, an owner, an escalation contact — and revoking their sessions\n * because a record was erased would log out three uninvolved people. Such a resource pairs this\n * with `RETAIN_ONLY`.\n * - **A person with no account.** A CRM contact, a sales lead, a subscriber, a patient with no\n * portal login. Their rows ARE personal data and carry the full weight of Arts 15–17 and 20 —\n * there is simply no session to end. Such a resource pairs this with `RETAIN_AND_IMPERSONALIZE`\n * and per-field `.impersonalizeWith()` treatments, and its subject-rights path is the ordinary\n * administrative one: `eraseSubjectAsSystem(resourceType, rowIds)` and a subject export.\n *\n * The second population is the majority of what a B2B SaaS customer stores about third parties, and\n * it is what most of a privacy notice describes. Do not read this member as \"nothing to disclose\".\n */\n NO_PRINCIPAL = 'no_principal',\n /**\n * Rows of this resource ARE the person, and the row's own id IS the session principal: erasing row\n * `X` ends the session of user `X`. This is `USERS` / `USER_SELF`.\n *\n * Note this makes EXPLICIT a revocation that is otherwise only incidental. Erasing a `USERS` row\n * already logs the user out today — but only as a side effect of the retention row-hide (the auth\n * fronts re-read the user, find the row hidden, and 401) plus the scrub write bumping\n * `authorizationVersion`. Nothing on the erasure path ever WROTE a revocation, so the guarantee\n * lived entirely in the behaviour of two unrelated mechanisms.\n */\n SELF_PRINCIPAL = 'self_principal',\n /**\n * Rows of this resource are a person whose login lives in a DIFFERENT `USERS` row — a Patient, an\n * Employee, a Member record that a human signs in as. `sessionPrincipalField` names the field\n * holding that user's id.\n *\n * The named field must carry no `.impersonalizeWith()` treatment. Not because the revocation would\n * break — the principal is resolved BEFORE the scrub, so a scrubbed field is still readable when it\n * matters — but because declaring one field to be BOTH the link to a person and personal data to\n * destroy is self-contradictory, and it leaves the retained row with no way to say whose session was\n * ended. Enforced at config build.\n */\n LINKED_PRINCIPAL = 'linked_principal',\n}\n\n/**\n * Zod schema for {@link ResourceDataSubjectKind}. Explicitly annotated for the\n * `isolatedDeclarations` emit constraint.\n */\nexport const ResourceDataSubjectKindSchema: z.ZodEnum<typeof ResourceDataSubjectKind> =\n z.enum(ResourceDataSubjectKind);\n"]}
@@ -0,0 +1,93 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * WHERE this application operates, as the one source every compliance surface reads (issue #229).
4
+ *
5
+ * ## The hole this closes
6
+ *
7
+ * Measured 2026-09-02: nothing in application configuration said which jurisdictions an application
8
+ * reaches. `jurisdiction` appeared only as prose, as the Art. 27 origin of a representative, and as
9
+ * `jurisdictionRefs` on a governed-document release. The assurance programme's `jurisdictionSlugs`
10
+ * were free text supplied per run BY THE PRODUCING MODEL — which is the opposite of a source: it is
11
+ * unverifiable, it can differ between two runs of the same application, and nothing downstream could
12
+ * disagree with it.
13
+ *
14
+ * ## Why a sibling of `operatorComplianceIdentity` rather than a field inside it
15
+ *
16
+ * Both describe legal posture, so folding this in is tempting. It is the wrong shape for one reason
17
+ * that outranks the tidiness: that schema is all-or-nothing inside, so a jurisdiction could not be
18
+ * recorded until the lawful bases were settled with counsel. Where you sell is known long before why
19
+ * you may process — and issue #228 exists precisely because filling the entity must never block.
20
+ *
21
+ * Keeping them siblings means each is answerable the day it is known.
22
+ *
23
+ * ## Why an open slug and not an enum
24
+ *
25
+ * A jurisdiction is a genuinely open identifier: an application may operate anywhere, and an enum
26
+ * would have to be widened before an operator could state the truth about their own product. That is
27
+ * the "genuinely open app/provider/resource identifier" exemption in the semantic-literal rule, not a
28
+ * shortcut around it. The shape is constrained; the membership is not.
29
+ *
30
+ * The framework's ability to KNOW what a declared jurisdiction requires is a separate question, and
31
+ * a deliberately visible one — see {@link JURISDICTION_CONTAINMENT} and the coverage report that
32
+ * consumes it. An application may always declare where it operates; Wildo says plainly where it has
33
+ * nothing to offer.
34
+ */
35
+ export declare const JurisdictionSlugSchema: z.ZodString;
36
+ export type JurisdictionSlug = z.infer<typeof JurisdictionSlugSchema>;
37
+ /**
38
+ * One jurisdiction this application operates in, and why it reaches the product.
39
+ *
40
+ * `basis` is REQUIRED, and it is the half that makes the declaration mean anything — the same
41
+ * argument the sibling schemas make for `assessmentNote` on a legitimate-interests basis and for
42
+ * `gatedProcessing` on a consent gate. "eu" alone does not say whether the operator is established
43
+ * there, sells there, or merely holds data about people there, and those imply different obligations.
44
+ *
45
+ * It is free prose deliberately. Nothing structured exists to key it to, and inventing a closed
46
+ * vocabulary of reasons would be a legal taxonomy nobody reviewed.
47
+ */
48
+ export declare const OperatingJurisdictionSchema: z.ZodObject<{
49
+ jurisdictionSlug: z.ZodString;
50
+ /** Why this jurisdiction reaches the product — "customers established here", "data subjects resident here". */
51
+ basis: z.ZodString;
52
+ }, z.core.$strict>;
53
+ export type OperatingJurisdiction = z.infer<typeof OperatingJurisdictionSchema>;
54
+ /**
55
+ * The jurisdictions this application operates in.
56
+ *
57
+ * ## Absent is UNDECLARED, and an empty list is not offered
58
+ *
59
+ * The sibling `clientStorage` inventory distinguishes absent ("not inventoried") from an empty array
60
+ * ("this surface sets nothing"), because there both are meaningful. Here only one is: every product
61
+ * operates somewhere, so an empty list would be a claim nobody could mean. `min(1)` therefore refuses
62
+ * it, and the whole declaration stays optional at the configuration level — absence is the honest
63
+ * "nobody has answered", and every consumer must render it as that rather than as "nowhere".
64
+ */
65
+ export declare const ApplicationOperatingJurisdictionsSchema: z.ZodObject<{
66
+ jurisdictions: z.ZodArray<z.ZodObject<{
67
+ jurisdictionSlug: z.ZodString;
68
+ /** Why this jurisdiction reaches the product — "customers established here", "data subjects resident here". */
69
+ basis: z.ZodString;
70
+ }, z.core.$strict>>;
71
+ }, z.core.$strict>;
72
+ export type ApplicationOperatingJurisdictions = z.infer<typeof ApplicationOperatingJurisdictionsSchema>;
73
+ /**
74
+ * Which jurisdictions a broader one contains, so a source addressing the broader one answers for them.
75
+ *
76
+ * Without this, an application declaring `fr` would be reported as uncovered while the GDPR source
77
+ * plainly reaches it — over-warning, which is the failure mode that trains a reader to skim past the
78
+ * warnings that matter. Subdivisions need no table: `us-ca` is contained by `us` by construction, and
79
+ * that rule is applied in code rather than enumerated here.
80
+ *
81
+ * ⚠️ EU membership is a legal fact with a review trigger, not a convenience list: it changes when a
82
+ * state accedes or withdraws, and it has done both. Verified against the 27 members as of 2026-09-02.
83
+ * Nothing derives it, so a change is a deliberate edit here.
84
+ */
85
+ export declare const JURISDICTION_CONTAINMENT: Readonly<Record<string, readonly string[]>>;
86
+ /**
87
+ * Does a source addressing `reach` answer for an application operating in `declared`?
88
+ *
89
+ * Three ways, and the order is deliberate — exact first, because it is the common case and the other
90
+ * two are refinements of it.
91
+ */
92
+ export declare function jurisdictionReachCovers(reach: string, declared: string): boolean;
93
+ //# sourceMappingURL=operating-jurisdictions.shared.schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operating-jurisdictions.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operating-jurisdictions.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,sBAAsB,aAQhC,CAAC;AACJ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B;;IAEtC,+GAA+G;;kBAE/G,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uCAAuC;;;QAhBlD,+GAA+G;;;kBAgC7G,CAAC;AACL,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uCAAuC,CAAC,CAAC;AAExG;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAK/E,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOhF"}
@@ -0,0 +1,116 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * WHERE this application operates, as the one source every compliance surface reads (issue #229).
4
+ *
5
+ * ## The hole this closes
6
+ *
7
+ * Measured 2026-09-02: nothing in application configuration said which jurisdictions an application
8
+ * reaches. `jurisdiction` appeared only as prose, as the Art. 27 origin of a representative, and as
9
+ * `jurisdictionRefs` on a governed-document release. The assurance programme's `jurisdictionSlugs`
10
+ * were free text supplied per run BY THE PRODUCING MODEL — which is the opposite of a source: it is
11
+ * unverifiable, it can differ between two runs of the same application, and nothing downstream could
12
+ * disagree with it.
13
+ *
14
+ * ## Why a sibling of `operatorComplianceIdentity` rather than a field inside it
15
+ *
16
+ * Both describe legal posture, so folding this in is tempting. It is the wrong shape for one reason
17
+ * that outranks the tidiness: that schema is all-or-nothing inside, so a jurisdiction could not be
18
+ * recorded until the lawful bases were settled with counsel. Where you sell is known long before why
19
+ * you may process — and issue #228 exists precisely because filling the entity must never block.
20
+ *
21
+ * Keeping them siblings means each is answerable the day it is known.
22
+ *
23
+ * ## Why an open slug and not an enum
24
+ *
25
+ * A jurisdiction is a genuinely open identifier: an application may operate anywhere, and an enum
26
+ * would have to be widened before an operator could state the truth about their own product. That is
27
+ * the "genuinely open app/provider/resource identifier" exemption in the semantic-literal rule, not a
28
+ * shortcut around it. The shape is constrained; the membership is not.
29
+ *
30
+ * The framework's ability to KNOW what a declared jurisdiction requires is a separate question, and
31
+ * a deliberately visible one — see {@link JURISDICTION_CONTAINMENT} and the coverage report that
32
+ * consumes it. An application may always declare where it operates; Wildo says plainly where it has
33
+ * nothing to offer.
34
+ */
35
+ export const JurisdictionSlugSchema = z
36
+ .string()
37
+ .trim()
38
+ .regex(/^[a-z]{2}(-[a-z0-9]{1,8})?$/u, 'a jurisdiction slug is an ISO 3166-1 alpha-2 country code, a supranational code such as "eu", '
39
+ + 'optionally with a subdivision suffix such as "us-ca" — lowercase, because a slug that differs '
40
+ + 'only by case is a second identifier for one place');
41
+ /**
42
+ * One jurisdiction this application operates in, and why it reaches the product.
43
+ *
44
+ * `basis` is REQUIRED, and it is the half that makes the declaration mean anything — the same
45
+ * argument the sibling schemas make for `assessmentNote` on a legitimate-interests basis and for
46
+ * `gatedProcessing` on a consent gate. "eu" alone does not say whether the operator is established
47
+ * there, sells there, or merely holds data about people there, and those imply different obligations.
48
+ *
49
+ * It is free prose deliberately. Nothing structured exists to key it to, and inventing a closed
50
+ * vocabulary of reasons would be a legal taxonomy nobody reviewed.
51
+ */
52
+ export const OperatingJurisdictionSchema = z.strictObject({
53
+ jurisdictionSlug: JurisdictionSlugSchema,
54
+ /** Why this jurisdiction reaches the product — "customers established here", "data subjects resident here". */
55
+ basis: z.string().trim().min(1),
56
+ });
57
+ /**
58
+ * The jurisdictions this application operates in.
59
+ *
60
+ * ## Absent is UNDECLARED, and an empty list is not offered
61
+ *
62
+ * The sibling `clientStorage` inventory distinguishes absent ("not inventoried") from an empty array
63
+ * ("this surface sets nothing"), because there both are meaningful. Here only one is: every product
64
+ * operates somewhere, so an empty list would be a claim nobody could mean. `min(1)` therefore refuses
65
+ * it, and the whole declaration stays optional at the configuration level — absence is the honest
66
+ * "nobody has answered", and every consumer must render it as that rather than as "nowhere".
67
+ */
68
+ export const ApplicationOperatingJurisdictionsSchema = z
69
+ .strictObject({
70
+ jurisdictions: z.array(OperatingJurisdictionSchema).min(1),
71
+ })
72
+ .superRefine((declaration, context) => {
73
+ const slugs = declaration.jurisdictions.map((entry) => entry.jurisdictionSlug);
74
+ const duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);
75
+ if (duplicates.length > 0) {
76
+ context.addIssue({
77
+ code: 'custom',
78
+ path: ['jurisdictions'],
79
+ message: `jurisdiction ${[...new Set(duplicates)].map((slug) => `"${slug}"`).join(', ')} is declared more than once — `
80
+ + 'one place has one basis, and two entries would let a document cite whichever suits the clause',
81
+ });
82
+ }
83
+ });
84
+ /**
85
+ * Which jurisdictions a broader one contains, so a source addressing the broader one answers for them.
86
+ *
87
+ * Without this, an application declaring `fr` would be reported as uncovered while the GDPR source
88
+ * plainly reaches it — over-warning, which is the failure mode that trains a reader to skim past the
89
+ * warnings that matter. Subdivisions need no table: `us-ca` is contained by `us` by construction, and
90
+ * that rule is applied in code rather than enumerated here.
91
+ *
92
+ * ⚠️ EU membership is a legal fact with a review trigger, not a convenience list: it changes when a
93
+ * state accedes or withdraws, and it has done both. Verified against the 27 members as of 2026-09-02.
94
+ * Nothing derives it, so a change is a deliberate edit here.
95
+ */
96
+ export const JURISDICTION_CONTAINMENT = Object.freeze({
97
+ eu: Object.freeze([
98
+ 'at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gr', 'hr', 'hu',
99
+ 'ie', 'it', 'lt', 'lu', 'lv', 'mt', 'nl', 'pl', 'pt', 'ro', 'se', 'si', 'sk',
100
+ ]),
101
+ });
102
+ /**
103
+ * Does a source addressing `reach` answer for an application operating in `declared`?
104
+ *
105
+ * Three ways, and the order is deliberate — exact first, because it is the common case and the other
106
+ * two are refinements of it.
107
+ */
108
+ export function jurisdictionReachCovers(reach, declared) {
109
+ if (reach === declared)
110
+ return true;
111
+ // A source addressing the whole of `us` answers for `us-ca`; one addressing only `us-ca` does not answer for `us`.
112
+ if (declared.startsWith(`${reach}-`))
113
+ return true;
114
+ return (JURISDICTION_CONTAINMENT[reach] ?? []).some((member) => member === declared || declared.startsWith(`${member}-`));
115
+ }
116
+ //# sourceMappingURL=operating-jurisdictions.shared.schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operating-jurisdictions.shared.schemas.js","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operating-jurisdictions.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,EAAE;KACR,IAAI,EAAE;KACN,KAAK,CACJ,8BAA8B,EAC9B,gGAAgG;MAC5F,gGAAgG;MAChG,mDAAmD,CACxD,CAAC;AAGJ;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,YAAY,CAAC;IACxD,gBAAgB,EAAE,sBAAsB;IACxC,+GAA+G;IAC/G,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAChC,CAAC,CAAC;AAGH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC;KACrD,YAAY,CAAC;IACZ,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3D,CAAC;KACD,WAAW,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE;IACpC,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC/E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;IAChF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,QAAQ,CAAC;YACf,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,eAAe,CAAC;YACvB,OAAO,EACL,gBAAgB,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC;kBAC5G,+FAA+F;SACpG,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAGL;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAgD,MAAM,CAAC,MAAM,CAAC;IACjG,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC;QAChB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;QAClF,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;KAC7E,CAAC;CACH,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,QAAgB;IACrE,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACpC,mHAAmH;IACnH,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,CAAC,wBAAwB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CACjD,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC,CACrE,CAAC;AACJ,CAAC","sourcesContent":["import { z } from 'zod';\n\n/**\n * WHERE this application operates, as the one source every compliance surface reads (issue #229).\n *\n * ## The hole this closes\n *\n * Measured 2026-09-02: nothing in application configuration said which jurisdictions an application\n * reaches. `jurisdiction` appeared only as prose, as the Art. 27 origin of a representative, and as\n * `jurisdictionRefs` on a governed-document release. The assurance programme's `jurisdictionSlugs`\n * were free text supplied per run BY THE PRODUCING MODEL — which is the opposite of a source: it is\n * unverifiable, it can differ between two runs of the same application, and nothing downstream could\n * disagree with it.\n *\n * ## Why a sibling of `operatorComplianceIdentity` rather than a field inside it\n *\n * Both describe legal posture, so folding this in is tempting. It is the wrong shape for one reason\n * that outranks the tidiness: that schema is all-or-nothing inside, so a jurisdiction could not be\n * recorded until the lawful bases were settled with counsel. Where you sell is known long before why\n * you may process — and issue #228 exists precisely because filling the entity must never block.\n *\n * Keeping them siblings means each is answerable the day it is known.\n *\n * ## Why an open slug and not an enum\n *\n * A jurisdiction is a genuinely open identifier: an application may operate anywhere, and an enum\n * would have to be widened before an operator could state the truth about their own product. That is\n * the \"genuinely open app/provider/resource identifier\" exemption in the semantic-literal rule, not a\n * shortcut around it. The shape is constrained; the membership is not.\n *\n * The framework's ability to KNOW what a declared jurisdiction requires is a separate question, and\n * a deliberately visible one — see {@link JURISDICTION_CONTAINMENT} and the coverage report that\n * consumes it. An application may always declare where it operates; Wildo says plainly where it has\n * nothing to offer.\n */\nexport const JurisdictionSlugSchema = z\n .string()\n .trim()\n .regex(\n /^[a-z]{2}(-[a-z0-9]{1,8})?$/u,\n 'a jurisdiction slug is an ISO 3166-1 alpha-2 country code, a supranational code such as \"eu\", '\n + 'optionally with a subdivision suffix such as \"us-ca\" — lowercase, because a slug that differs '\n + 'only by case is a second identifier for one place',\n );\nexport type JurisdictionSlug = z.infer<typeof JurisdictionSlugSchema>;\n\n/**\n * One jurisdiction this application operates in, and why it reaches the product.\n *\n * `basis` is REQUIRED, and it is the half that makes the declaration mean anything — the same\n * argument the sibling schemas make for `assessmentNote` on a legitimate-interests basis and for\n * `gatedProcessing` on a consent gate. \"eu\" alone does not say whether the operator is established\n * there, sells there, or merely holds data about people there, and those imply different obligations.\n *\n * It is free prose deliberately. Nothing structured exists to key it to, and inventing a closed\n * vocabulary of reasons would be a legal taxonomy nobody reviewed.\n */\nexport const OperatingJurisdictionSchema = z.strictObject({\n jurisdictionSlug: JurisdictionSlugSchema,\n /** Why this jurisdiction reaches the product — \"customers established here\", \"data subjects resident here\". */\n basis: z.string().trim().min(1),\n});\nexport type OperatingJurisdiction = z.infer<typeof OperatingJurisdictionSchema>;\n\n/**\n * The jurisdictions this application operates in.\n *\n * ## Absent is UNDECLARED, and an empty list is not offered\n *\n * The sibling `clientStorage` inventory distinguishes absent (\"not inventoried\") from an empty array\n * (\"this surface sets nothing\"), because there both are meaningful. Here only one is: every product\n * operates somewhere, so an empty list would be a claim nobody could mean. `min(1)` therefore refuses\n * it, and the whole declaration stays optional at the configuration level — absence is the honest\n * \"nobody has answered\", and every consumer must render it as that rather than as \"nowhere\".\n */\nexport const ApplicationOperatingJurisdictionsSchema = z\n .strictObject({\n jurisdictions: z.array(OperatingJurisdictionSchema).min(1),\n })\n .superRefine((declaration, context) => {\n const slugs = declaration.jurisdictions.map((entry) => entry.jurisdictionSlug);\n const duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);\n if (duplicates.length > 0) {\n context.addIssue({\n code: 'custom',\n path: ['jurisdictions'],\n message:\n `jurisdiction ${[...new Set(duplicates)].map((slug) => `\"${slug}\"`).join(', ')} is declared more than once — `\n + 'one place has one basis, and two entries would let a document cite whichever suits the clause',\n });\n }\n });\nexport type ApplicationOperatingJurisdictions = z.infer<typeof ApplicationOperatingJurisdictionsSchema>;\n\n/**\n * Which jurisdictions a broader one contains, so a source addressing the broader one answers for them.\n *\n * Without this, an application declaring `fr` would be reported as uncovered while the GDPR source\n * plainly reaches it — over-warning, which is the failure mode that trains a reader to skim past the\n * warnings that matter. Subdivisions need no table: `us-ca` is contained by `us` by construction, and\n * that rule is applied in code rather than enumerated here.\n *\n * ⚠️ EU membership is a legal fact with a review trigger, not a convenience list: it changes when a\n * state accedes or withdraws, and it has done both. Verified against the 27 members as of 2026-09-02.\n * Nothing derives it, so a change is a deliberate edit here.\n */\nexport const JURISDICTION_CONTAINMENT: Readonly<Record<string, readonly string[]>> = Object.freeze({\n eu: Object.freeze([\n 'at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'ee', 'es', 'fi', 'fr', 'gr', 'hr', 'hu',\n 'ie', 'it', 'lt', 'lu', 'lv', 'mt', 'nl', 'pl', 'pt', 'ro', 'se', 'si', 'sk',\n ]),\n});\n\n/**\n * Does a source addressing `reach` answer for an application operating in `declared`?\n *\n * Three ways, and the order is deliberate — exact first, because it is the common case and the other\n * two are refinements of it.\n */\nexport function jurisdictionReachCovers(reach: string, declared: string): boolean {\n if (reach === declared) return true;\n // A source addressing the whole of `us` answers for `us-ca`; one addressing only `us-ca` does not answer for `us`.\n if (declared.startsWith(`${reach}-`)) return true;\n return (JURISDICTION_CONTAINMENT[reach] ?? []).some(\n (member) => member === declared || declared.startsWith(`${member}-`),\n );\n}\n"]}
@@ -142,19 +142,36 @@ export type ServiceConsentRequirement = z.infer<typeof ServiceConsentRequirement
142
142
  * A DPO appointment record, a processing-activities register (Art. 30), a retention schedule
143
143
  * (`dataRetention`), a processor register (#88). This is the operator's identity and legal posture —
144
144
  * the answers a notice needs in its opening sections — not a compliance programme.
145
+ *
146
+ * ## Every part is independently declarable, and that is the point (issue #228)
147
+ *
148
+ * This schema was optional AS A WHOLE and all-or-nothing INSIDE until 2026-09-02: the entity name,
149
+ * address, contact, role allocations and lawful bases were each required, so an operator who knew
150
+ * the registered name but had not settled lawful bases with counsel could record NOTHING. They
151
+ * answered by editing the produced document, and a landing REPLACES it — losing the answer on the
152
+ * next regeneration, which is the exact failure this schema was created to end.
153
+ *
154
+ * Paul's decision (#225) is that filling the entity must never block, and that generation is
155
+ * resilient to what is missing while saying plainly that the result is incomplete. So every part is
156
+ * optional and {@link outstandingOperatorComplianceIdentityParts} names what is still owed.
157
+ *
158
+ * What did NOT weaken: every refinement guarding a STATED claim. A legitimate-interests basis still
159
+ * requires its assessment, and a consent level above `NONE` still requires what it gates. Those
160
+ * refuse an INCOHERENT answer, never an ABSENT one, and the distinction is the whole design — a
161
+ * partial declaration is honest, a self-contradicting one is not.
145
162
  */
146
163
  export declare const ApplicationOperatorComplianceIdentitySchema: z.ZodObject<{
147
- /** The legal entity operating the service, as registered. */
148
- legalEntityName: z.ZodString;
149
- /** Its registered address, as a reader would need it to serve a request. */
150
- registeredAddress: z.ZodString;
164
+ /** The legal entity operating the service, as registered. Absent means unanswered — see the partial-filling note above. */
165
+ legalEntityName: z.ZodOptional<z.ZodString>;
166
+ /** Its registered address, as a reader would need it to serve a request. Absent means unanswered. */
167
+ registeredAddress: z.ZodOptional<z.ZodString>;
151
168
  /**
152
169
  * How a data subject reaches the operator about privacy — an address, an email, or a URL.
153
170
  *
154
171
  * Open string because all three are legitimate and the law names none: what matters is that a route
155
172
  * exists and is stated, not what shape it takes.
156
173
  */
157
- privacyContact: z.ZodString;
174
+ privacyContact: z.ZodOptional<z.ZodString>;
158
175
  /**
159
176
  * An Art. 27 representative in the origin jurisdiction, where the operator is established outside it.
160
177
  *
@@ -171,11 +188,11 @@ export declare const ApplicationOperatorComplianceIdentitySchema: z.ZodObject<{
171
188
  *
172
189
  * See {@link OperatorRoleAllocationSchema} for why this is a list rather than a field.
173
190
  */
174
- roleAllocations: z.ZodArray<z.ZodObject<{
191
+ roleAllocations: z.ZodOptional<z.ZodArray<z.ZodObject<{
175
192
  /** Which data this role covers, in the operator's own words — "customer records in a workspace". */
176
193
  dataScope: z.ZodString;
177
194
  role: z.ZodEnum<typeof OperatorProcessingRole>;
178
- }, z.core.$strict>>;
195
+ }, z.core.$strict>>>;
179
196
  /**
180
197
  * The lawful basis relied on, per processing purpose. At least one.
181
198
  *
@@ -183,7 +200,7 @@ export declare const ApplicationOperatorComplianceIdentitySchema: z.ZodObject<{
183
200
  * declaring no basis at all is a refusal wearing a declaration's clothes, and it would land as a
184
201
  * complete answer.
185
202
  */
186
- lawfulBases: z.ZodArray<z.ZodObject<{
203
+ lawfulBases: z.ZodOptional<z.ZodArray<z.ZodObject<{
187
204
  /** The purpose, in the operator's own words. Matches how the notice states purposes: prose, not a ref. */
188
205
  purpose: z.ZodString;
189
206
  basis: z.ZodEnum<typeof LawfulBasis>;
@@ -192,7 +209,7 @@ export declare const ApplicationOperatorComplianceIdentitySchema: z.ZodObject<{
192
209
  * that needs a balancing test — see the superRefine below.
193
210
  */
194
211
  assessmentNote: z.ZodOptional<z.ZodString>;
195
- }, z.core.$strict>>;
212
+ }, z.core.$strict>>>;
196
213
  /**
197
214
  * What a person must consent to in order to use the service at all (issue #139).
198
215
  *
@@ -220,11 +237,49 @@ export declare const ApplicationOperatorComplianceIdentitySchema: z.ZodObject<{
220
237
  }, z.core.$strict>>;
221
238
  }, z.core.$strict>;
222
239
  export type ApplicationOperatorComplianceIdentity = z.infer<typeof ApplicationOperatorComplianceIdentitySchema>;
240
+ /**
241
+ * The parts of an operator's compliance identity, so "what is still owed" has one named home.
242
+ *
243
+ * A closed vocabulary rather than free strings: every consumer that reports an incomplete identity —
244
+ * a drafter's brief, a document's marked gap, an operator-facing check — must name the same parts by
245
+ * the same names, or two surfaces tell an operator to fill two different things.
246
+ *
247
+ * `representative` and `serviceConsentRequirement` are deliberately absent from this list. Both are
248
+ * legitimately unset on a COMPLETE identity: an operator established where its subjects are needs no
249
+ * Art. 27 representative, and a declared absence of a consent gate is expressed by
250
+ * `serviceConsentRequirement.requiredLevel = NONE`, which is a value rather than an omission. Listing
251
+ * them would report every correct identity as incomplete.
252
+ */
253
+ export declare enum OperatorComplianceIdentityPart {
254
+ LEGAL_ENTITY_NAME = "legalEntityName",
255
+ REGISTERED_ADDRESS = "registeredAddress",
256
+ PRIVACY_CONTACT = "privacyContact",
257
+ ROLE_ALLOCATIONS = "roleAllocations",
258
+ LAWFUL_BASES = "lawfulBases"
259
+ }
260
+ /**
261
+ * Which parts of the identity are still owed, in declaration order.
262
+ *
263
+ * Empty means complete. An UNDECLARED identity is not this function's case — pass it and you get every
264
+ * part back, which is correct but says less than the caller needs: "nobody has been asked" and "four of
265
+ * five parts are outstanding" are different things to tell an operator, and the caller distinguishes
266
+ * them by checking for the identity itself first.
267
+ */
268
+ export declare function outstandingOperatorComplianceIdentityParts(identity: ApplicationOperatorComplianceIdentity | undefined): readonly OperatorComplianceIdentityPart[];
223
269
  /**
224
270
  * The lawful bases an operator declares, deduplicated — what a notice enumerates when it says which
225
271
  * bases the service relies on.
272
+ *
273
+ * `undefined` when none are declared, and NOT an empty array. A notice rendering an empty list would
274
+ * say the service relies on no lawful basis, which is a claim nobody made and one no processing could
275
+ * survive; the caller has to meet the absent case rather than fall into it.
276
+ */
277
+ export declare function declaredLawfulBases(identity: ApplicationOperatorComplianceIdentity): readonly LawfulBasis[] | undefined;
278
+ /**
279
+ * True when the operator declares more than one role, so a notice must not assign a single one.
280
+ *
281
+ * `undefined` when no roles are declared — the question cannot be answered, and `false` would be read
282
+ * as "one role", licensing exactly the collapse {@link OperatorRoleAllocationSchema} exists to prevent.
226
283
  */
227
- export declare function declaredLawfulBases(identity: ApplicationOperatorComplianceIdentity): readonly LawfulBasis[];
228
- /** True when the operator declares more than one role, so a notice must not assign a single one. */
229
- export declare function operatorHoldsSeveralProcessingRoles(identity: ApplicationOperatorComplianceIdentity): boolean;
284
+ export declare function operatorHoldsSeveralProcessingRoles(identity: ApplicationOperatorComplianceIdentity): boolean | undefined;
230
285
  //# sourceMappingURL=operator-compliance-identity.shared.schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"operator-compliance-identity.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operator-compliance-identity.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;GAQG;AACH,oBAAY,WAAW;IACrB,gGAAgG;IAChG,OAAO,YAAY;IACnB,iGAAiG;IACjG,QAAQ,aAAa;IACrB,+FAA+F;IAC/F,gBAAgB,qBAAqB;IACrC,4FAA4F;IAC5F,eAAe,oBAAoB;IACnC,wFAAwF;IACxF,WAAW,gBAAgB;IAC3B;;;;OAIG;IACH,oBAAoB,yBAAyB;CAC9C;AAED,6GAA6G;AAC7G,oBAAY,sBAAsB;IAChC,sDAAsD;IACtD,UAAU,eAAe;IACzB,mHAAmH;IACnH,SAAS,cAAc;IACvB,oHAAoH;IACpH,gBAAgB,qBAAqB;CACtC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B;IACvC,oGAAoG;;;kBAGpG,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB;IACpC,0GAA0G;;;IAG1G;;;OAGG;;kBAgBH,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,+BAA+B;IAC1C;;;;;;OAMG;;IAEH;;;;;;OAMG;;kBAgBH,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,2CAA2C;IACtD,6DAA6D;;IAE7D,4EAA4E;;IAE5E;;;;;OAKG;;IAEH;;;;;;OAMG;;;;;IAKH;;;;OAIG;;QAxJH,oGAAoG;;;;IA0JpG;;;;;;OAMG;;QAxJH,0GAA0G;;;QAG1G;;;WAGG;;;IAoJH;;;;;;OAMG;;QAxGH;;;;;;WAMG;;QAEH;;;;;;WAMG;;;kBA4FH,CAAC;AACH,MAAM,MAAM,qCAAqC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2CAA2C,CAAC,CAAC;AAGhH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,qCAAqC,GAAG,SAAS,WAAW,EAAE,CAI3G;AAED,oGAAoG;AACpG,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,qCAAqC,GAAG,OAAO,CAE5G"}
1
+ {"version":3,"file":"operator-compliance-identity.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../../src/compliance/privacy/operator-compliance-identity.shared.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;GAQG;AACH,oBAAY,WAAW;IACrB,gGAAgG;IAChG,OAAO,YAAY;IACnB,iGAAiG;IACjG,QAAQ,aAAa;IACrB,+FAA+F;IAC/F,gBAAgB,qBAAqB;IACrC,4FAA4F;IAC5F,eAAe,oBAAoB;IACnC,wFAAwF;IACxF,WAAW,gBAAgB;IAC3B;;;;OAIG;IACH,oBAAoB,yBAAyB;CAC9C;AAED,6GAA6G;AAC7G,oBAAY,sBAAsB;IAChC,sDAAsD;IACtD,UAAU,eAAe;IACzB,mHAAmH;IACnH,SAAS,cAAc;IACvB,oHAAoH;IACpH,gBAAgB,qBAAqB;CACtC;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,4BAA4B;IACvC,oGAAoG;;;kBAGpG,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB;IACpC,0GAA0G;;;IAG1G;;;OAGG;;kBAgBH,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,+BAA+B;IAC1C;;;;;;OAMG;;IAEH;;;;;;OAMG;;kBAgBH,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,2CAA2C;IACtD,2HAA2H;;IAE3H,qGAAqG;;IAErG;;;;;OAKG;;IAEH;;;;;;OAMG;;;;;IAKH;;;;OAIG;;QAzKH,oGAAoG;;;;IA2KpG;;;;;;OAMG;;QAzKH,0GAA0G;;;QAG1G;;;WAGG;;;IAqKH;;;;;;OAMG;;QAzHH;;;;;;WAMG;;QAEH;;;;;;WAMG;;;kBAiIH,CAAC;AACH,MAAM,MAAM,qCAAqC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2CAA2C,CAAC,CAAC;AAGhH;;;;;;;;;;;;GAYG;AACH,oBAAY,8BAA8B;IACxC,iBAAiB,oBAAoB;IACrC,kBAAkB,sBAAsB;IACxC,eAAe,mBAAmB;IAClC,gBAAgB,oBAAoB;IACpC,YAAY,gBAAgB;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,0CAA0C,CACxD,QAAQ,EAAE,qCAAqC,GAAG,SAAS,GAC1D,SAAS,8BAA8B,EAAE,CAQ3C;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,qCAAqC,GAAG,SAAS,WAAW,EAAE,GAAG,SAAS,CAKvH;AAED;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,QAAQ,EAAE,qCAAqC,GAAG,OAAO,GAAG,SAAS,CAGxH"}
@@ -169,19 +169,36 @@ export const ServiceConsentRequirementSchema = z.strictObject({
169
169
  * A DPO appointment record, a processing-activities register (Art. 30), a retention schedule
170
170
  * (`dataRetention`), a processor register (#88). This is the operator's identity and legal posture —
171
171
  * the answers a notice needs in its opening sections — not a compliance programme.
172
+ *
173
+ * ## Every part is independently declarable, and that is the point (issue #228)
174
+ *
175
+ * This schema was optional AS A WHOLE and all-or-nothing INSIDE until 2026-09-02: the entity name,
176
+ * address, contact, role allocations and lawful bases were each required, so an operator who knew
177
+ * the registered name but had not settled lawful bases with counsel could record NOTHING. They
178
+ * answered by editing the produced document, and a landing REPLACES it — losing the answer on the
179
+ * next regeneration, which is the exact failure this schema was created to end.
180
+ *
181
+ * Paul's decision (#225) is that filling the entity must never block, and that generation is
182
+ * resilient to what is missing while saying plainly that the result is incomplete. So every part is
183
+ * optional and {@link outstandingOperatorComplianceIdentityParts} names what is still owed.
184
+ *
185
+ * What did NOT weaken: every refinement guarding a STATED claim. A legitimate-interests basis still
186
+ * requires its assessment, and a consent level above `NONE` still requires what it gates. Those
187
+ * refuse an INCOHERENT answer, never an ABSENT one, and the distinction is the whole design — a
188
+ * partial declaration is honest, a self-contradicting one is not.
172
189
  */
173
190
  export const ApplicationOperatorComplianceIdentitySchema = z.strictObject({
174
- /** The legal entity operating the service, as registered. */
175
- legalEntityName: z.string().trim().min(1),
176
- /** Its registered address, as a reader would need it to serve a request. */
177
- registeredAddress: z.string().trim().min(1),
191
+ /** The legal entity operating the service, as registered. Absent means unanswered — see the partial-filling note above. */
192
+ legalEntityName: z.string().trim().min(1).optional(),
193
+ /** Its registered address, as a reader would need it to serve a request. Absent means unanswered. */
194
+ registeredAddress: z.string().trim().min(1).optional(),
178
195
  /**
179
196
  * How a data subject reaches the operator about privacy — an address, an email, or a URL.
180
197
  *
181
198
  * Open string because all three are legitimate and the law names none: what matters is that a route
182
199
  * exists and is stated, not what shape it takes.
183
200
  */
184
- privacyContact: z.string().trim().min(1),
201
+ privacyContact: z.string().trim().min(1).optional(),
185
202
  /**
186
203
  * An Art. 27 representative in the origin jurisdiction, where the operator is established outside it.
187
204
  *
@@ -198,7 +215,7 @@ export const ApplicationOperatorComplianceIdentitySchema = z.strictObject({
198
215
  *
199
216
  * See {@link OperatorRoleAllocationSchema} for why this is a list rather than a field.
200
217
  */
201
- roleAllocations: z.array(OperatorRoleAllocationSchema).min(1),
218
+ roleAllocations: z.array(OperatorRoleAllocationSchema).min(1).optional(),
202
219
  /**
203
220
  * The lawful basis relied on, per processing purpose. At least one.
204
221
  *
@@ -206,7 +223,7 @@ export const ApplicationOperatorComplianceIdentitySchema = z.strictObject({
206
223
  * declaring no basis at all is a refusal wearing a declaration's clothes, and it would land as a
207
224
  * complete answer.
208
225
  */
209
- lawfulBases: z.array(OperatorLawfulBasisSchema).min(1),
226
+ lawfulBases: z.array(OperatorLawfulBasisSchema).min(1).optional(),
210
227
  /**
211
228
  * What a person must consent to in order to use the service at all (issue #139).
212
229
  *
@@ -215,18 +232,88 @@ export const ApplicationOperatorComplianceIdentitySchema = z.strictObject({
215
232
  * absence is nobody having been asked, and a notice must not render the second as the first.
216
233
  */
217
234
  serviceConsentRequirement: ServiceConsentRequirementSchema.optional(),
235
+ }).superRefine((identity, context) => {
236
+ /*
237
+ * A declaration with nothing in it is a no-op that reads as an answer.
238
+ *
239
+ * Every part is independently optional so an operator can record the half they know (#228), and the
240
+ * cost of that is a new shape the old schema could not express: `operatorComplianceIdentity: {}`.
241
+ * Absent and empty must not mean the same thing — absence is "nobody was asked", which every
242
+ * consumer already renders as outstanding, while an empty object would satisfy the field and
243
+ * disclose nothing. Same defect the file refuses twice already, for a legitimate-interests basis
244
+ * with no assessment and a consent gate that names nothing.
245
+ */
246
+ if (Object.values(identity).every((part) => part === undefined)) {
247
+ context.addIssue({
248
+ code: 'custom',
249
+ path: [],
250
+ message: 'an operator compliance identity that declares nothing is not a partial answer, it is an empty one — '
251
+ + 'record the part you know, or omit `operatorComplianceIdentity` entirely so it reads as unanswered',
252
+ });
253
+ }
218
254
  });
255
+ /**
256
+ * The parts of an operator's compliance identity, so "what is still owed" has one named home.
257
+ *
258
+ * A closed vocabulary rather than free strings: every consumer that reports an incomplete identity —
259
+ * a drafter's brief, a document's marked gap, an operator-facing check — must name the same parts by
260
+ * the same names, or two surfaces tell an operator to fill two different things.
261
+ *
262
+ * `representative` and `serviceConsentRequirement` are deliberately absent from this list. Both are
263
+ * legitimately unset on a COMPLETE identity: an operator established where its subjects are needs no
264
+ * Art. 27 representative, and a declared absence of a consent gate is expressed by
265
+ * `serviceConsentRequirement.requiredLevel = NONE`, which is a value rather than an omission. Listing
266
+ * them would report every correct identity as incomplete.
267
+ */
268
+ export var OperatorComplianceIdentityPart;
269
+ (function (OperatorComplianceIdentityPart) {
270
+ OperatorComplianceIdentityPart["LEGAL_ENTITY_NAME"] = "legalEntityName";
271
+ OperatorComplianceIdentityPart["REGISTERED_ADDRESS"] = "registeredAddress";
272
+ OperatorComplianceIdentityPart["PRIVACY_CONTACT"] = "privacyContact";
273
+ OperatorComplianceIdentityPart["ROLE_ALLOCATIONS"] = "roleAllocations";
274
+ OperatorComplianceIdentityPart["LAWFUL_BASES"] = "lawfulBases";
275
+ })(OperatorComplianceIdentityPart || (OperatorComplianceIdentityPart = {}));
276
+ /**
277
+ * Which parts of the identity are still owed, in declaration order.
278
+ *
279
+ * Empty means complete. An UNDECLARED identity is not this function's case — pass it and you get every
280
+ * part back, which is correct but says less than the caller needs: "nobody has been asked" and "four of
281
+ * five parts are outstanding" are different things to tell an operator, and the caller distinguishes
282
+ * them by checking for the identity itself first.
283
+ */
284
+ export function outstandingOperatorComplianceIdentityParts(identity) {
285
+ return [
286
+ identity?.legalEntityName === undefined ? OperatorComplianceIdentityPart.LEGAL_ENTITY_NAME : undefined,
287
+ identity?.registeredAddress === undefined ? OperatorComplianceIdentityPart.REGISTERED_ADDRESS : undefined,
288
+ identity?.privacyContact === undefined ? OperatorComplianceIdentityPart.PRIVACY_CONTACT : undefined,
289
+ identity?.roleAllocations === undefined ? OperatorComplianceIdentityPart.ROLE_ALLOCATIONS : undefined,
290
+ identity?.lawfulBases === undefined ? OperatorComplianceIdentityPart.LAWFUL_BASES : undefined,
291
+ ].filter((part) => part !== undefined);
292
+ }
219
293
  /**
220
294
  * The lawful bases an operator declares, deduplicated — what a notice enumerates when it says which
221
295
  * bases the service relies on.
296
+ *
297
+ * `undefined` when none are declared, and NOT an empty array. A notice rendering an empty list would
298
+ * say the service relies on no lawful basis, which is a claim nobody made and one no processing could
299
+ * survive; the caller has to meet the absent case rather than fall into it.
222
300
  */
223
301
  export function declaredLawfulBases(identity) {
302
+ if (identity.lawfulBases === undefined)
303
+ return undefined;
224
304
  const order = Object.values(LawfulBasis);
225
305
  return [...new Set(identity.lawfulBases.map((entry) => entry.basis))]
226
306
  .sort((left, right) => order.indexOf(left) - order.indexOf(right));
227
307
  }
228
- /** True when the operator declares more than one role, so a notice must not assign a single one. */
308
+ /**
309
+ * True when the operator declares more than one role, so a notice must not assign a single one.
310
+ *
311
+ * `undefined` when no roles are declared — the question cannot be answered, and `false` would be read
312
+ * as "one role", licensing exactly the collapse {@link OperatorRoleAllocationSchema} exists to prevent.
313
+ */
229
314
  export function operatorHoldsSeveralProcessingRoles(identity) {
315
+ if (identity.roleAllocations === undefined)
316
+ return undefined;
230
317
  return new Set(identity.roleAllocations.map((allocation) => allocation.role)).size > 1;
231
318
  }
232
319
  //# sourceMappingURL=operator-compliance-identity.shared.schemas.js.map