@wildo-ai/saas-models 1.1.1 → 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":"resources-config.shared.schemas.js","sourceRoot":"","sources":["../../../../src/resources/resources-config.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Operations Shared Schemas\n *\n * Reusable operation schemas for bulk actions and common operations\n */\n\nimport { z } from 'zod';\nimport { EnumLikeType, EnumValues, PrimitiveZodType } from '@wildo-ai/zod-decorators';\nimport { ErasureRetentionMode, ResourceDataSubjectKind } from '../compliance/privacy/impersonalization.shared.schemas';\nimport type { DataPortabilityProvenance } from '../compliance/privacy/subject-export.shared.schemas';\n/*\n * entrypoint-closure-waiver: this root-reachable module imports the external-data AUTHORING\n * contract, which only `@wildo-ai/saas-models/external-data` publishes (see\n * `external-data.exports.ts` for why that subpath exists).\n *\n * MEASURED SAFE 2026-08-29 by the probe `package-public-entrypoints.md` prescribes — a consumer\n * importing ONLY the package root, re-exporting these surfaces and deriving their parameter types\n * so declaration emit must WRITE them rather than alias them, compiled with `declaration: true`:\n * zero TS2742 / TS2883 / TS7056, and the emitted `.d.ts` names `@wildo-ai/saas-models` with no\n * `node_modules/...` path anywhere in it.\n *\n * The mechanism, so the next reader need not re-derive it: every crossing here is an EXPLICIT\n * ANNOTATION inside this package, so TypeScript emits a RELATIVE intra-package import in our own\n * `.d.ts` — which resolves by PATH for any consumer, whatever the exports map says. TS2742 bites\n * only when TypeScript must INVENT a specifier for an INFERRED type. Re-run the probe if a\n * root-exported contract here ever starts inferring one.\n */\nimport type { HttpApiResourceBinding } from '../http-api-binding/http-api-binding.shared.schemas';\nimport type { ExternalDataPipelineDeclaration } from '../external-data/external-data-pipeline.shared.schemas';\nimport { HttpMethod } from '../requests/http-requests.shared';\nimport { Roles } from '../security/authorizations/roles.shared.schemas';\nimport { TokenAuthenticationConfig, TokenGenerationConfig, TokenRevocationConfig } from '../security/authentications/consumable-token.shared.schemas';\nimport type { ConsumableTokenTypes } from '../security/authentications/consumable-token.shared.schemas';\nimport { ResourceOperation_ServiceRuntimeMode, ResourceOperationVariantType, ResourceOperationRiskLevel, ResourceOperationFrontendSurface, ResourceParentResourceRequirement, ResourcePrimaryScope, ResourceTranspositionPolicy, ResourceOperation_CustomServiceImplementationMode, ResourceRelationship, ResourceRelationshipAccessScopeStrategy, ManyToManyScopeJunctionMap, type ReferenceConstraints } from './resources.shared.schemas';\nimport { NotificationBadgeIdentifier, NotificationBadgeOperation } from '../notifications/notification-badges.schemas';\nimport { M2MNotificationDefinition, UserNotificationDefinition } from '../notifications/notifications.shared.schemas';\nimport { NotificationBadgeDefinition } from '../notifications/notification-badges.schemas';\nimport { CoreResourceOperation, ResourceFieldIdentifier, ResourceType } from './resources.shared.core.definitions';\nimport type { OrganizationUnitNarrowingDeclaration } from '../organizations/organization-unit-narrowing.shared';\nimport { LimitCountingConfig } from '../features/feature-definition.shared.schemas';\nimport { MainSchemaInfer, SchemaArrayInfer } from './utils/schema-type-inference.utils';\nimport type { RateLimitConfiguration } from '../requests/rate-limits.shared.schemas';\n\n\n\n// --------------------------------\n// Resources Pagination Response\n// --------------------------------\n\n/**\n * The pagination block a collection RESPONSE echoes back, bounded by the operation's ceiling.\n *\n * `limit` here reports the page size that was actually SERVED, so its bound has to be at least as\n * permissive as the one the repository is allowed to serve. It was a hard-coded `.max(100)`, which\n * made it the LAST of five separate pagination ceilings to ignore the operation's declaration — and\n * the one that turned a legitimately-served 120-row page into a 500 (`too_big` at\n * `pagination.limit`) after the request and data-array bounds had already been fixed.\n *\n * The five, for anyone changing one of them: the REQUEST bound\n * ({@link createPaginationRequestSchema}), the repository's `_doList` and `_doSearch` maxima, the\n * response DATA array bound, and this. They are one invariant — \"the operation's declared ceiling\n * governs\" — and a fix applied to fewer than all of them just moves which layer says no.\n */\nexport function createPaginationResponseMetadataSchema(maxLimit: number = 100): z.ZodObject<{\n page: z.ZodDefault<z.ZodNumber>;\n limit: z.ZodDefault<z.ZodNumber>;\n total: z.ZodNumber;\n totalPages: z.ZodNumber;\n}> {\n return z.object({\n page: z.number().min(1).default(1),\n limit: z.number().min(1).max(maxLimit).default(20),\n total: z.number().min(0),\n totalPages: z.number().min(0)\n });\n}\n\n/** The default-bounded response metadata. Prefer {@link createPaginationResponseMetadataSchema} where the ceiling is known. */\nexport const Resouces_PaginationResponseMetadataSchema = createPaginationResponseMetadataSchema();\n\n\n\n/**\n * The pagination block a collection request may carry, bounded by the operation's declared ceiling.\n *\n * The bound used to be a hard-coded `.max(100)` on a single shared constant, which made it the\n * STRICTEST of the three pagination ceilings in the framework — the repository serves up to the\n * operation's declared limit and the response DTO accepts up to the same, but a request asking for\n * more than 100 was rejected before either ran. An operation declaring 200 therefore advertised a\n * limit it answered 400 to.\n *\n * `maxLimit` defaults to 100 so every existing caller keeps its current contract.\n */\nexport function createPaginationRequestSchema(maxLimit: number = 100): z.ZodObject<{\n page: z.ZodDefault<z.ZodNumber>;\n limit: z.ZodDefault<z.ZodNumber>;\n}> {\n return z.object({\n page: z.number().min(1).default(1),\n limit: z.number().min(1).max(maxLimit).default(20)\n });\n}\n\n/** The default-bounded pagination block. Prefer {@link createPaginationRequestSchema} where the operation's ceiling is known. */\nexport const Resouces_PaginationRequestSchema = createPaginationRequestSchema();\n\n// Type utility for paginated response result (Zod schema based)\nexport type ResourcePaginatedResponseResultSchema<T extends z.ZodTypeAny> = {\n data: SchemaArrayInfer<T>;\n pagination: z.infer<typeof Resouces_PaginationResponseMetadataSchema>;\n};\n\n// Plain TypeScript types for pagination (non-Zod dependent)\nexport type Resources_PaginationMetadata = z.infer<typeof Resouces_PaginationResponseMetadataSchema>;\nexport type Resources_PaginationRequest = z.infer<typeof Resouces_PaginationRequestSchema>;\n\nexport type Resources_PaginatedResult<T> = {\n data: T[];\n pagination: Resources_PaginationMetadata;\n};\n\n// --------------------------------\n// Data Mode (for read/list operations)\n// --------------------------------\n\n/**\n * @wildo_source:part:start saas.models.data-mode-read-list facet:layer:shared facet:family:resource-config\n *\n * Data mode for read/list operations — controls API data shape.\n * SUMMARY = backend returns only `isSummaryField()` fields and populates `isSummaryField({ populate: true })` FKs.\n *\n * Note: \"summary\" here is about *data shape*, not FK rendering (see ForeignKeyDisplayMode.SUMMARY)\n * or operation bar density (see ResourceOperationDisplayMode.SUMMARY).\n * Values are lowercase strings matching URL/serialization format directly.\n *\n * @remarks Import from `@wildo-ai/saas-models`. Used by read/list/search configs and DTO shaping.\n */\nexport enum DataMode {\n /** All stored fields, no population */\n RAW = 'raw',\n\n /** isSummaryField fields only, populate isSummaryField({ populate: true }) FKs */\n SUMMARY = 'summary',\n\n /** All fields, populate based on contextPolicy */\n CONTEXT = 'context'\n}\n\n// --------------------------------\n// Persistence Adapter (Database Type)\n// --------------------------------\n\n/**\n * Canonical persistence adapters supported by the resource system.\n *\n * Determines which backing store serves a resource's rows:\n * - `mongodb`: MongoDB with Mongoose ORM (platform-provisioned)\n * - `postgresql`: PostgreSQL SQL adapter (platform-provisioned)\n * - `http-api`: a system reached over an authenticated HTTP API (OData or REST/JSON dialect) —\n * the platform provisions NOTHING for it; rows are queried on demand\n *\n * If not specified at resource level, uses `appConfig.database.defaultAdapter`.\n */\n/**\n * What an INTROSPECTION-backed resource projects.\n *\n * Declared BY THE AUTHOR beside the resource, exactly as `httpApiBinding` is, and for the same\n * reason: the adapter answers \"how rows are reached\", and everything specific to THIS resource's\n * reach is a typed declaration rather than runtime inference.\n *\n * Keeping it here rather than in a companion-side lookup table matters. A table keyed by resource\n * type would be a second enumeration of a population the resource configs already define — the\n * shape that silently drifts when one side gains an entry.\n */\nexport enum IntrospectionTenancyStance {\n /**\n * The rows describe the APPLICATION'S OWN STRUCTURE — its modules, resource specifications,\n * design system, compliance declarations — and carry no tenant dimension, so there is nothing for\n * a contextual scope predicate to filter on.\n *\n * This is the only stance implemented, and it is declared rather than inferred for the reason\n * `HttpApiTenancyStance` gives for its own: an unstated stance is indistinguishable from an\n * overlooked one. Every other repository enforces scoping ITSELF — Mongo and PostgreSQL through\n * `buildContextualFilter`, HTTP_API through its tenancy stance — so a read-through adapter that\n * simply returned whatever its source produced would be the one backing kind with no scoping\n * story, and nobody would be asked to notice.\n */\n APPLICATION_STRUCTURE = 'application-structure',\n\n /**\n * The rows carry a tenant dimension and a scope predicate must be pushed into the source.\n *\n * REFUSED AT REGISTRATION today: no introspection behavior takes a scope argument, so the engine\n * has nothing to push down. Declared as a member rather than omitted so the refusal is explicit\n * and a future scoped behavior is a deliberate design step — the alternative is that the first\n * author needing it discovers the gap by shipping unscoped rows.\n */\n SCOPE_FILTER_PUSHDOWN = 'scope-filter-pushdown',\n}\n\nexport interface IntrospectionResourceBinding {\n /**\n * The companion introspection behavior whose output becomes this resource's rows.\n *\n * A plain string on purpose: the behavior catalog lives in the companion (23 behaviors as of\n * 2026-08-29) and the engine must not depend on it. An unknown id is refused by the companion at\n * read time, naming the id and the supported set.\n */\n readonly behavior: string;\n\n /**\n * Path into the behavior's result that holds the rows, dot-separated. Omitted means the result\n * itself is the row collection.\n *\n * Behaviors answer their own natural shape — `{ frontendChartRegistrationRefs: [...] }` — and\n * teaching each one to answer a uniform envelope would change 23 working behaviors to suit a\n * consumer that arrived later.\n */\n readonly rowsPath?: string;\n\n /**\n * REQUIRED. Why these rows need no contextual scope predicate, or that they do.\n *\n * Not optional and not defaulted: a default would make \"nobody thought about tenancy\" and\n * \"tenancy does not apply here\" the same declaration.\n */\n readonly tenancy: IntrospectionTenancyStance;\n\n /**\n * Fields whose values COMPOSE each row's primary key, in order, when the behavior's rows carry no\n * identifier of their own.\n *\n * Most introspection behaviors answer structural projections rather than records: a module row\n * carries `moduleId` and needs nothing here, while a relationship row carries only the two\n * resource types it joins. Without a key such a resource can be LISTed and never READ, because\n * there is no value to address a row by.\n *\n * Composition is DECLARED rather than derived because only the author knows which fields are\n * jointly unique for a given behavior — and being wrong is not hypothetical. `resourceType1` +\n * `resourceType2` is unique across all 16 relationships in the dogfood application and is still\n * unsafe in general, since the framework permits several edges between the same pair (a junction\n * with two roles, a self-relationship). So the source FAILS LOUDLY when two rows compose the same\n * key rather than serving both under one id, which would make READ return an arbitrary one of\n * them and give no sign anything was wrong.\n *\n * The composed value is written to the resource's own `resourceFieldIdentifier`, which is the one\n * key name resolvable from the registry without introspecting the schema's decorators. A resource\n * declaring `identityFields` must therefore name that field as its primary key.\n *\n * The values are joined verbatim, so the key stays READABLE — `workspace__users__createdByUserId`\n * rather than an opaque hash. That is possible because an INTROSPECTION-backed resource resolves\n * to `ResourceIdFormat.INTROSPECTION_SYNTHESIZED`, which admits a URL-safe charset instead of the\n * ObjectId-or-UUID rule platform rows follow.\n *\n * It was briefly a deterministic UUIDv5, before that format existed. Hashing worked and was the\n * wrong trade: it made every id opaque, and it disguised the real property — a DERIVED id is only\n * as stable as the fields it is derived from, and hiding that behind a hash removes the reader's\n * ability to notice when a row's identity moves.\n *\n * Each value must be URL-safe (`[a-zA-Z0-9_-]`) and the whole key at most 255 characters; the\n * source refuses per row rather than emitting a key that lists and cannot be read.\n *\n * Omit when the behavior's rows already carry an identifier of their own.\n */\n readonly identityFields?: readonly string[];\n}\n\nexport enum PersistenceAdapter {\n MONGODB = 'mongodb',\n POSTGRESQL = 'postgresql',\n\n /**\n * Rows are DERIVED on demand by asking the running dev companion to introspect the application —\n * its resource registry, specifications, design system, compliance facts, assertions, market and\n * persona context. Nothing is stored: every read recomputes.\n *\n * ## Why this is an adapter and not a service call\n *\n * The companion already computes all of it — 23 named introspection behaviors — and today each is\n * reachable only through a bespoke route. Making it an adapter is what turns those behaviors into\n * ordinary RESOURCES: list, read, filter, search, labels, navigation, all generated, with no\n * per-behavior UI. That is the same argument `HTTP_API` settled on 2026-08-26 — the adapter answers\n * \"how rows are reached\", and the mechanism behind it is a binding an author declares.\n *\n * Distinct from `HTTP_API`, deliberately, and not merely because the transport differs. Retrieval\n * is a SUBPROCESS that imports the target application's built modules; it costs seconds rather than\n * milliseconds, it requires that application's `dist` to exist, and its failure mode is a behavior\n * throwing rather than a row being absent. Collapsing the two would give one declaration whose\n * discriminator chooses between an HTTP call and a subprocess — two adapters wearing one name.\n *\n * ## What every adapter decision site must honor\n *\n * - **READ-ONLY, absolutely.** There is no writable target: a row is a projection of the\n * application's own source. Every write method refuses, as `HTTP_API`'s twelve do.\n * - **No provisioning, migration or schema surface.** Nothing is stored, so there is nothing to\n * create, migrate or verify — the platform must never believe it owns a store here.\n * - **Never a legal application-wide DEFAULT adapter.** It is a per-resource declaration only;\n * an application whose default was introspection could persist nothing at all.\n * - **It must DEGRADE, never fail closed at boot.** The companion is the only thing that can run\n * an introspection behavior, so a boot path that refuses on an introspection failure would need\n * the companion to fix the companion — see `continuity-drift-and-cas-basis.md` Rule 3.\n */\n INTROSPECTION = 'introspection',\n\n /**\n * Rows live in a system reached over an authenticated HTTP API and are queried on demand\n * (read-through). Decided 2026-08-26 (ETL workstream, D1): this IS a persistence-adapter answer\n * — \"how rows are reached\" — not a separate axis; the dialect (OData vs REST/JSON-RPC) and every\n * other binding property live in the REQUIRED companion declaration\n * (`HttpApiResourceBindingSchema`, published by `@wildo-ai/saas-models/external-data`).\n *\n * ## Why that companion lives in a SHARED package at all (asked 2026-08-29, measured not argued)\n *\n * Only the backend has a runtime for it — 19 references there, none in any frontend package — so\n * \"surely this is backend-only\" is the natural reading. It is wrong, for one decisive reason: an\n * application author writes the binding in `*.resources-config.ts`, which lives in the app's\n * `shared-lib`, and that package depends on `@wildo-ai/saas-models` and deliberately NOT on\n * `@wildo-ai/saas-backend-lib` (the frontend imports `shared-lib`, so a backend dependency there\n * would pull the backend graph browser-ward — the opposite of the isolation wanted). Moving the\n * declaration to the backend would leave an author unable to type their own declaration.\n *\n * Nothing in that companion is runtime: dialects, tenancy, erasure, key fields, remote-call\n * semantics are all typed by a human. The runtime that consumes them — read client, extraction\n * source, transform, run service, batch — is already backend-only. So the tiers are already\n * split where they should be; what was owed was SURFACE hygiene, which is why the companion is\n * published by a subpath instead of the root barrel rather than moved.\n *\n * This asymmetry with MONGODB / POSTGRESQL is therefore principled, not accidental: those need\n * no AUTHORED companion at all (their detail is infra config plus a runtime planner), while\n * HTTP_API is the one adapter whose detail an app developer writes.\n *\n * Consequences every adapter decision site must honor explicitly (compile-forced via\n * `PersistenceAdapterCases`):\n * - provisioning/migration/schema surfaces have NOTHING to do — the platform does not own the\n * store and must never mutate its schema;\n * - it is never a legal application-wide DEFAULT adapter — only a per-resource declaration\n * accompanied by its binding;\n * - until the external repository ships, `createRepository()` refuses it loudly.\n */\n HTTP_API = 'http-api',\n}\n\n/**\n * String-value form accepted by authored resource configuration.\n *\n * This remains a string union so declarative configuration may use either the\n * enum members or their serialized values, while deriving the vocabulary from\n * the single canonical enum above.\n */\nexport type PersistenceAdapterType = `${PersistenceAdapter}`;\n\nconst PERSISTENCE_ADAPTER_VALUES: readonly string[] = Object.freeze(Object.values(PersistenceAdapter));\n\n/**\n * Runtime guard for persistence-adapter values entering through erased,\n * serialized, or otherwise untyped configuration boundaries.\n *\n * Keeping this beside the canonical enum ensures future adapter additions are\n * recognized by validation without duplicating a second vocabulary.\n */\nexport function isPersistenceAdapter(\n value: unknown,\n): value is PersistenceAdapterType {\n return typeof value === 'string'\n && PERSISTENCE_ADAPTER_VALUES.includes(value);\n}\n\n/**\n * Default persistence adapter when not specified.\n *\n * Typed as the ENUM member (not the string-union `PersistenceAdapterType`) so it satisfies\n * enum-typed configuration fields directly; enum member types are assignable to the string\n * union, so every union-typed consumer is unaffected.\n */\nexport const DEFAULT_PERSISTENCE_ADAPTER: PersistenceAdapter =\n PersistenceAdapter.MONGODB;\n\n/**\n * A resource's declared relationship to CROSS-ADAPTER ATOMIC WRITE BOUNDARIES.\n *\n * **Why this exists.** An application is wholly one persistence adapter by default, and the\n * resources registry REFUSES a per-resource `persistenceAdapter` that disagrees with\n * `database.defaultAdapter`. That refusal protects exactly one property: a mixed application\n * cannot commit two resources in one native transaction, so every cross-resource boundary\n * silently degrades from atomic to at-least-once.\n *\n * The refusal is right as a DEFAULT and wrong as an absolute — some resources provably never\n * participate in such a boundary, and for those the blanket refusal forbids a legitimate,\n * safe deployment (a retrieval corpus whose vector tier only PostgreSQL can serve, say) with\n * no way past it. Per `.claude/rules/design-philosophy.md`, a construct that refuses must ship\n * the door in the same change: this enum IS that door. Declaring a member is a positive claim\n * about the resource's write topology, made by its author, recorded in the config, and logged\n * at registration — never an ambient tolerance and never a flag that merely silences an error.\n *\n * **This declaration is a claim, not an exemption.** It does not weaken any runtime guarantee:\n * `ResourcePersistenceUnitOfWorkBackendService.assessCompatibility` still classifies any real\n * boundary whose ATOMIC participants resolve to more than one adapter as\n * `MIXED_PERSISTENCE_ADAPTERS`, and `executeInUnitOfWork` still THROWS on it. So a resource\n * that declares a member here and then IS reached by a cross-adapter atomic boundary fails\n * loudly at that boundary, exactly as it would have without the declaration. What the\n * declaration buys is the right to boot; what it can never buy is a silent degradation.\n *\n * assurance-control: WILDO.DATA.TRANSACTIONAL_INTEGRITY — the declaration a resource must make\n * before it may sit on a non-default persistence adapter.\n */\nexport enum ResourcePersistenceAdapterIsolation {\n /**\n * This resource participates in NO cross-adapter atomic write boundary.\n *\n * The author asserts that no operation commits this resource and a resource on another\n * adapter inside one native transaction — neither as the subject of a\n * `ResourcePersistenceUnitOfWork` boundary nor as a participant in someone else's.\n *\n * Legitimate when the resource is a self-contained store whose writes stand alone: a\n * retrieval corpus, an append-only analytical sink, a projection rebuilt from its source\n * rather than written beside it. It is NOT legitimate merely because no such boundary\n * exists *today* — the claim is about the resource's design, and a later operation that\n * makes it false must change the declaration rather than discover the refusal at runtime.\n */\n NO_CROSS_ADAPTER_TRANSACTION = 'NO_CROSS_ADAPTER_TRANSACTION',\n}\n\n/**\n * String-value form accepted by authored resource configuration, mirroring the\n * {@link PersistenceAdapterType} convention so declarative config may use either the enum\n * member or its serialized value.\n *\n * **Deliberately one member.** A second — \"participates only as a COMPENSATABLE participant\",\n * mirroring the persistence unit of work's own\n * `ResourcePersistenceParticipantCoupling.COMPENSATABLE` — is describable, but no resource is\n * asking for it, and an admission path with no caller is an admission path with no coverage.\n * Add it when a real resource needs it, with the compensating path named at its call site.\n */\nexport type ResourcePersistenceAdapterIsolationType = `${ResourcePersistenceAdapterIsolation}`;\n\nconst RESOURCE_PERSISTENCE_ADAPTER_ISOLATION_VALUES: readonly string[] =\n Object.freeze(Object.values(ResourcePersistenceAdapterIsolation));\n\n/**\n * Runtime guard for isolation declarations entering through erased, serialized, or otherwise\n * untyped configuration boundaries — which is exactly how the resources registry sees them, since\n * it reads heterogeneous resource configurations through an erased index.\n *\n * Deliberately a MEMBERSHIP test rather than a presence test: the registry admits a non-default\n * adapter only on a recognised member, so a typo, a stale value from an older vocabulary, or a\n * truthy non-string cannot open the door. Fail-closed is the point — an unrecognised declaration\n * is treated as no declaration at all, and the application is refused with the standard message.\n */\nexport function isResourcePersistenceAdapterIsolation(\n value: unknown,\n): value is ResourcePersistenceAdapterIsolationType {\n return typeof value === 'string'\n && RESOURCE_PERSISTENCE_ADAPTER_ISOLATION_VALUES.includes(value);\n}\n\n/**\n * Variant key constants for operation variants.\n * Values are identical to DataMode.SUMMARY and DataMode.CONTEXT.\n * Kept as a standalone constant for use in factory/controller code that\n * deals with variant keys independently of DataMode.\n */\nexport const OPERATION_VARIANT_KEY = {\n SUMMARY: 'summary',\n CONTEXT: 'context',\n} as const;\n\n/**\n * Convert DataMode to corresponding variant key.\n * RAW mode has no variant (returns undefined).\n * For SUMMARY/CONTEXT, the variant key equals the DataMode value directly.\n */\nexport function dataModeToVariantKey(dataMode: DataMode): string | undefined {\n if (dataMode === DataMode.RAW) {\n return undefined;\n }\n return dataMode;\n}\n\n/**\n * Options for read operations.\n */\nexport interface ReadOperationOptions {\n /**\n * Data mode controlling fields and population.\n * @default DataMode.RAW\n */\n dataMode?: DataMode;\n}\n\n/**\n * Options for list operations.\n */\nexport interface ListOperationOptions {\n /**\n * Data mode controlling fields and population.\n * @default DataMode.RAW\n */\n dataMode?: DataMode;\n\n /**\n * Whether to wrap in pagination container.\n * @default false for internal, should be true for API responses\n */\n paginated?: boolean;\n\n /**\n * Pagination parameters (required if paginated: true).\n */\n pagination?: Resources_PaginationRequest;\n}\n/** @wildo_source:part:end saas.models.data-mode-read-list */\n\n// --------------------------------\n// Auto-Variants Configuration\n// --------------------------------\n\n/**\n * Configuration for auto-generated operation variants.\n * Controls which READ/LIST variants (summary, context) are auto-generated.\n */\nexport type ResourceConfiguration_AutoVariants = {\n read?: {\n /** Enable READ.context variant. @default true */\n enableContext?: boolean;\n /** Enable READ.summary variant. @default true */\n enableSummary?: boolean;\n };\n list?: {\n /** Enable LIST.context variant. @default true */\n enableContext?: boolean;\n /** Enable LIST.summary variant. @default true */\n enableSummary?: boolean;\n };\n}\n\n// --------------------------------\n// Resource Search Query Request\n// --------------------------------\n\n// Real TypeScript enum for sort order\nexport enum SortOrder {\n ASC = 'asc',\n DESC = 'desc'\n}\nexport const Resouces_Filter_DateRangeSchema = z.object({\n startDate: z.date().optional(),\n endDate: z.date().optional()\n});\n\n\nexport const Resouces_Filter_BaseSchema = z.object({\n searchRequest: z.string().optional(),\n createdAt: Resouces_Filter_DateRangeSchema.optional(),\n updatedAt: Resouces_Filter_DateRangeSchema.optional(),\n});\n\n// Generic filter request schema (extends base filter with custom fields)\nexport const Resouces_FilterRequestSchema = z.object({\n searchRequest: z.string().optional(),\n createdAt: Resouces_Filter_DateRangeSchema.optional(),\n updatedAt: Resouces_Filter_DateRangeSchema.optional(),\n}).catchall(z.any().optional()); // Allows additional filter fields\n\n// Generic sorting field schema\nexport const Resouces_SortingFieldSchema = z.object({\n field: z.string(),\n order: z.enum(SortOrder).default(SortOrder.ASC),\n priority: z.number().min(0).default(0)\n});\n\n// Generic sorting request schema (object with field keys)\nexport const Resouces_SortingRequestSchema = z.record(\n z.string(),\n Resouces_SortingFieldSchema.optional()\n).optional();\n\n// Generic search query request schema\nexport const Resouces_SearchQueryRequestSchema = z.object({\n pagination: Resouces_PaginationRequestSchema.optional(),\n sorting: Resouces_SortingRequestSchema.optional(),\n filters: Resouces_FilterRequestSchema.optional()\n});\n\n// Type utility for search query request result\nexport type ResourceSearchQueryRequestResult<\n TFilterFields extends Record<string, PrimitiveZodType | ((filterRef: PrimitiveZodType) => string)> | undefined,\n TSortFields extends readonly string[] | undefined\n> = z.ZodObject<{\n pagination: z.ZodOptional<typeof Resouces_PaginationRequestSchema>;\n sorting: z.ZodOptional<typeof Resouces_SortingRequestSchema>;\n filters: z.ZodOptional<typeof Resouces_Filter_BaseSchema>;\n}>;\n\nexport type Ressources_RequestSortPriority<T extends EnumLikeType> = {\n [K in keyof T as T[K]]: number;\n};\n\n/**\n * Distributive omit. `UserNotificationDefinition` is a discriminated union (by\n * `channel`); a bare `Omit<Union, K>` collapses to only the COMMON keys and silently\n * drops every channel-specific field (the EMAIL variant's `recipientEmailField`, the\n * FRONT_END_SUCCESS variant's `celebration`/`nextStep`). Routing through a naked type\n * parameter distributes the omit over each union member so those per-channel fields\n * survive in the config-authoring type.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\ntype UserNotificationDefinitionWithoutPrimaryScopeAndIdentifier = DistributiveOmit<UserNotificationDefinition, 'primaryScope' | 'identifier'>;\ntype M2MNotificationDefinitionWithoutIdentifier = Omit<M2MNotificationDefinition, 'identifier'>;\n\nexport type CollectionView_SearchOptions = {\n caseSensitive? : boolean, // default false\n fullMatchOnly? : boolean, // default false\n}\n\n/**\n * Shared collection-view fields for operations that render a collection of items (LIST, SEARCH).\n * These fields are integrated directly into the variant builder type — not nested.\n * The factory flattens them onto the final operation object.\n *\n * NOTE: cards-vs-table presentation (`displayMode` / `userSwitchableDisplayMode`)\n * is intentionally NOT here. It is a pure FRONTEND concern with no backend meaning,\n * so it is authored on the operation's `operationFrontendConfig.collectionDisplayConfig`\n * in the resource UI behavior layer (`CollectionDisplayMode` in\n * `@wildo-ai/saas-frontend-lib`). The fields that remain here are the ones the\n * BACKEND also reads: `pageSize` (pagination), `filterFields` (filtering), and\n * `sortFields` (sort validation).\n */\n/**\n * The wire format a collection export is rendered into.\n *\n * ## Why this is NOT `SubjectExportFormat`\n *\n * That enum has the same two members and answers a different question. Its `JSON` member is\n * documented as \"the format an Art. 20 request should receive by default\", and its `CSV` member as\n * \"Never the default for portability\" — statements about a data subject's PORTABILITY RIGHT. A\n * listing export owes no such duty: it is an operator exporting rows they can already read, and CSV\n * is a perfectly good default for it.\n *\n * In this codebase an enum member is where the per-member \"why\" lives (that is the whole reason an\n * anonymous string union is refused), so sharing the enum would attach Art. 20 reasoning to a\n * toolbar button, where every word of it is false. It breaks the other way too: adding `XLSX` for\n * listings would land it in the subject-export vocabulary, where someone then has to reason about\n * whether a spreadsheet satisfies Art. 20.\n *\n * What IS shared is the MECHANISM — RFC 4180 cell escaping and column resolution — which is format\n * mechanics with no legal content. See `renderCollectionExportCsv`.\n */\nexport enum ResourceCollectionExportFormat {\n /**\n * Tabular, one file. Nested values are JSON-encoded into their cell and type information is lost,\n * which is the accepted trade for a file every spreadsheet opens. The usual default for a listing.\n */\n CSV = 'csv',\n /**\n * The rows exactly as the listing's response DTO carries them — nested objects, arrays, nulls and\n * types intact. Choose this when the export feeds another system rather than a human.\n */\n JSON = 'json',\n}\n\n/**\n * WHICH rows a collection export contains.\n *\n * The axis is ROWS, deliberately — not columns. An export carries the same fields the listing's read\n * DTO carries, which is already the caller's permitted projection; there is no separate\n * \"visible columns\" notion to select from.\n */\nexport enum ResourceCollectionExportRowScope {\n /**\n * Exactly the rows the caller's request would have returned — same page, same filters, same sort.\n * One service call, and what the user sees is what they get.\n */\n VISIBLE_PAGE = 'visible_page',\n /**\n * Every row the caller's filters match, with the page bound removed.\n *\n * Implemented by ITERATING pages at the operation's own declared ceiling, never by asking for one\n * huge page: the page bound is enforced at eight independent sites (see `maxPageSize` below) and\n * raising it for an export would move the effective ceiling for the whole operation.\n */\n FULL_RESULT_SET = 'full_result_set',\n}\n\n/**\n * Opt a LIST/SEARCH operation into CSV/JSON export of its own result set.\n *\n * ## What declaring this does\n *\n * The factory derives a companion operation — one `GENERATED_COLLECTION_EXPORT` per exporting\n * LIST/SEARCH operation — that runs **the same service call as the listing** and renders the result.\n * That is the load-bearing property: tenant isolation, role ACL, contextual filters, retention\n * row-hide, backend-only/secret field stripping, relationship population and sort validation all hold\n * by construction rather than through a second implementation kept in step by hand. (`_doList` and\n * `buildContextualFilter` each exist twice — once per persistence adapter — and have diverged before;\n * an export with its own query would be a third place to get the tenant boundary right.)\n *\n * The derived operation copies the source operation's `roles` and CANNOT widen them: an export is\n * exactly as reachable as the listing it exports, never more.\n *\n * ## What it deliberately does NOT inherit\n *\n * `mcp` / agent exposure. A LIST curated onto an MCP server is a PAGINATED read tool; an export tool\n * returns the whole table in one call, which is a different risk. Inheriting the flag would widen an\n * existing application's agent surface the moment it declared an export, so the derived operation is\n * never agent-exposed and an app that wants one declares it deliberately.\n *\n * ## Ceilings\n *\n * `maximumExportedRows` is OPTIONAL and has NO default — an operation that declares none exports\n * whatever its filters match. When declared and exceeded, the export FAILS with an error naming the\n * matched count and the ceiling; it never truncates. A short file is indistinguishable from a\n * complete one once it is open in a spreadsheet, and a silent partial export is the worse outcome.\n */\n/**\n * What a collection-export route answers with, described for specs and OpenAPI.\n *\n * ⚠️ The BODY is the exported FILE — a CSV or JSON byte stream, not this object. The controller owns\n * the response for this operation family and streams it, so nothing ever serialises this schema.\n * What it documents is the METADATA CONTRACT, which rides in response headers beside the bytes:\n * `Content-Type`, `Content-Disposition` (carrying the filename) and `X-Wildo-Export-Row-Count`.\n *\n * Modelling the response as \"a JSON envelope containing the file\" was rejected: a large CSV inside a\n * JSON string doubles the payload, defeats streaming, and gives the browser nothing to download.\n */\nexport const ResourceCollectionExportResponseSchema = z.object({\n /** Mirrors the `filename=` of the `Content-Disposition` header. */\n filename: z.string(),\n /** Mirrors `Content-Type`: `text/csv` or `application/json`. */\n contentType: z.string(),\n /** Mirrors `X-Wildo-Export-Row-Count` — the number of rows actually written. */\n rowCount: z.number().int().nonnegative(),\n});\n\nexport type ResourceCollectionExportConfig = {\n /** Offered formats. Non-empty — an empty array is a startup error, never a silent \"all\". */\n formats: readonly ResourceCollectionExportFormat[];\n /** Offered row scopes. Non-empty, same rule. */\n rowScopes: readonly ResourceCollectionExportRowScope[];\n /**\n * Refuse (do not truncate) a `FULL_RESULT_SET` export whose match count exceeds this.\n * Omit for no ceiling.\n */\n maximumExportedRows?: number;\n};\n\nexport type CollectionViewFields_Shared = {\n pageSize?: number;\n filterFields?: Record<string, PrimitiveZodType | ((filterRef: PrimitiveZodType) => string)>;\n sortFields?: readonly string[];\n /**\n * Opt this LIST/SEARCH operation into CSV/JSON export — see {@link ResourceCollectionExportConfig}.\n * It belongs in this bucket because it is read by the BACKEND, like its three neighbours.\n */\n collectionExport?: ResourceCollectionExportConfig;\n};\n\n/** LIST-specific collection-view fields (LIST and custom ops with resourceOperationLike: LIST) */\nexport type CollectionViewFields_List = CollectionViewFields_Shared & {\n searchEnabled?: boolean;\n filtersEnabled?: boolean;\n /**\n * Maximum page size this operation will accept, serve and return.\n *\n * ## Read this before changing any pagination bound\n *\n * ONE declaration, enforced at EIGHT independent sites. That is not an accident of style — each\n * layer validates the page from its own side, and a bound tightened at any one of them silently\n * becomes the effective ceiling for the whole operation. Getting fewer than all eight to agree\n * does not fail loudly; it just moves which layer says no:\n *\n * | # | Where | What it bounds |\n * |---|---|---|\n * | 1 | `createPaginationRequestSchema` | the `pagination.limit` a caller may ASK for (400 if exceeded) |\n * | 2 | Mongo `_doList` | the rows the query is allowed to fetch |\n * | 3 | PostgreSQL `_doList` | same, and it must MATCH #2 — the adapters diverged here once |\n * | 4 | Mongo/PostgreSQL `_doSearch` | same for the SEARCH verb |\n * | 5 | response DTO `data` array `.max()` | the rows the response schema will serialise (500 if exceeded) |\n * | 6 | `createPaginationResponseMetadataSchema` | the `limit` VALUE echoed in the envelope (500 if exceeded) |\n * | 7 | `internalDto` pagination wrappers (default + SUMMARY + CONTEXT variants) | SERVICE-layer validation, *after* the page is built |\n * | 8 | `wrapInPaginationStructure` (dataMode SUMMARY/CONTEXT) | the dataMode-selected response shape |\n *\n * ⚠️ Sites 7 and 8 are the ones that hide. They validate the SERVICE result, so they only speak\n * once the request has been accepted and the repository has already produced the page — a bound\n * left stale there surfaces as `service_resource_transformation_failed`, not as a validation\n * error, and only for collections large enough to reach it.\n *\n * ## The default is 100, and it is load-bearing\n *\n * An operation that declares nothing gets **100**, because that is the value the Mongo `_doList`\n * hard-coded before these sites were unified. Defaulting to 50 (SEARCH's own default) was tried\n * and reverted: it silently HALVED every caller asking for more — billing usage-metering\n * (`limit: 10000`), the sidebar resource browser, the flows-actors controller and the audit\n * export, none of which declare a ceiling and none of which would have errored. Symmetry with\n * SEARCH is not worth a silent truncation.\n *\n * DECLARE a value when a caller depends on it, even if it equals the default — `auditLogs | LIST`\n * does exactly that, because its export path breaks quietly if the default ever moves.\n *\n * The operation declaration is the sole pagination authority. The resolved default operation and\n * every auto-generated LIST representation inherit the same effective ceiling, so request,\n * repository, response and API-reference contracts cannot diverge by data mode.\n */\n maxPaginatedResultPerPageLimit?: number;\n};\n\n/**\n * @wildo_source:part:start saas.models.resource-config.searchable-relation facet:layer:shared facet:family:resource-config\n *\n * Relational search descriptor — lets a SEARCH operation match rows of THIS\n * resource by searching a RELATED (\"juncted\") resource's own searchable fields,\n * WITHOUT denormalizing those fields onto this resource. Author it inline inside\n * a SEARCH operation's `searchableFields` array, alongside plain own-field names.\n *\n * WHY IT EXISTS — some human-meaningful search keys live on a related resource\n * and are MUTABLE there (e.g. a user's first/last name on a user-profile\n * resource). Copying such a field onto this row would go stale and demand a\n * change-propagation sync on every mutation path of the owning resource. This\n * descriptor reuses the related resource's OWN searchable surface at query time\n * instead — single source of truth, never stale, no sync hooks to maintain.\n *\n * HOW IT WORKS (tenant-safe by construction) — at search time the framework runs\n * a secondary, system-level match on {@link relatedResource} using {@link fields}\n * (defaulting to that resource's own string `searchableFields`), collects the\n * matching rows' {@link relatedJoinField} values, and adds \"this resource's\n * {@link localField} is one of those values\" as an extra match branch, OR-ed\n * with the own-field matches. The primary query stays scoped (e.g. by\n * organization) via the normal contextual filter, so the secondary match's own\n * (often global) scope is irrelevant to safety: a row outside the caller's scope\n * can never enter the already-scoped primary result set. Because the join\n * collapses to a single-collection lookup, pagination and totals are unchanged.\n *\n * FAIL-SAFE — a relational entry only ever ADDS matches within the already-scoped\n * set, so if the related resource cannot be resolved the entry contributes\n * nothing (the search under-matches; it can never widen visibility).\n *\n * @remarks Import from `@wildo-ai/saas-models`. Supported on MongoDB-backed\n * resources today; on a PostgreSQL-backed resource the relational entries are\n * skipped and only the own-field entries apply (own-field search is unaffected).\n *\n * @example\n * // Search org members by the linked user's name. The user's firstName/lastName\n * // live on a separate USER_PROFILES resource (mutable) and are deliberately NOT\n * // copied onto the member row; both members and profiles carry the same `userId`,\n * // so we join on it and reuse the profile's own searchableFields.\n * searchableFields: [\n * 'userEmail',\n * { localField: 'userId', relatedResource: CoreResourceType.USER_PROFILES,\n * relatedJoinField: 'userId', fields: ['firstName', 'lastName'] },\n * ]\n */\nexport type SearchableRelationDescriptor = {\n /** Field on THIS resource matched against the resolved related ids (e.g. `userId`). */\n localField: string;\n /** The related resource whose own searchable surface is reused (e.g. `USER_PROFILES`). */\n relatedResource: ResourceType;\n /**\n * Field on the related resource whose values are matched against\n * {@link localField}'s values. Defaults to the related primary key (`_id`) —\n * set it explicitly when joining on a shared key other than the primary key\n * (e.g. members and profiles both carry `userId`).\n */\n relatedJoinField?: string;\n /**\n * Which related fields to match the search term against. Defaults to the\n * related resource's OWN (string) `searchableFields` — i.e. literally \"use the\n * juncted element's own search\". Provide an explicit list only to narrow it.\n */\n fields?: string[];\n};\n\n/**\n * A SEARCH `searchableFields` entry: either an OWN-resource field name (string),\n * matched on this collection, or a {@link SearchableRelationDescriptor} that\n * matches via a related resource. The two forms coexist in one array and are\n * OR-ed together at query time.\n */\nexport type SearchableFieldDescriptor = string | SearchableRelationDescriptor;\n\n/** Runtime guard distinguishing a relational descriptor from an own-field name. */\nexport function isSearchableRelationDescriptor(\n field: SearchableFieldDescriptor\n): field is SearchableRelationDescriptor {\n return typeof field === 'object' && field !== null;\n}\n/** @wildo_source:part:end saas.models.resource-config.searchable-relation */\n\n/** SEARCH-specific collection-view fields (SEARCH and custom ops with resourceOperationLike: SEARCH) */\nexport type CollectionViewFields_Search = CollectionViewFields_Shared & {\n isSearchable?: boolean;\n /**\n * Fields this SEARCH matches against. Each entry is either an own-collection\n * field name or a {@link SearchableRelationDescriptor} that reaches into a\n * related resource (see that type for the tenant-safety contract). Own-field\n * and relational entries OR together.\n */\n searchableFields?: SearchableFieldDescriptor[];\n searchableOptions?: CollectionView_SearchOptions;\n /** SEARCH's page ceiling. Same eight-site contract as the LIST field — see its JSDoc above. */\n maxPaginatedResultPerPageLimit?: number;\n};\n\n// --------------------------------\n// Resources Shared Configuration\n// --------------------------------\n\n/**\n * The wire selector every bulk operation carries.\n *\n * The DTO builder wraps EVERY `isBulkOperation` request DTO in this shape\n * (`buildEnhancedRequestDto`), so `_ids` is the ONE field name a client may use to name the rows a\n * bulk operation targets. It is a SELECTOR, not data: it never reaches persistence.\n *\n * The leading underscore is deliberate and load-bearing — it keeps the selector out of the resource's\n * own field namespace, so a resource that legitimately owns a field called `ids` cannot collide with\n * it. Read it through {@link BULK_OPERATION_SELECTOR_FIELD} rather than spelling the string, and\n * resolve it through {@link readBulkOperationSelectorIds}: a producer that spells it `ids` type-checks,\n * validates away to nothing, and then fails as \"`_ids` is required\" — which is exactly what every\n * frontend bulk surface did until 2026-08-29.\n */\nexport const Resouces_BulkOperationBaseSchema = z.object({\n _ids: z.array(z.string().min(1)).min(1),\n});\n\n/**\n * The single named source for the bulk selector's field name.\n *\n * Every producer (frontend request builders) and every consumer (the service-tier target resolution,\n * the generated relationship-bulk selector) reads the name from here, so the two halves of the wire\n * contract cannot drift apart silently.\n */\nexport const BULK_OPERATION_SELECTOR_FIELD = '_ids' as const;\n\n/**\n * Read the bulk selector out of a request payload, or `undefined` when it carries none.\n *\n * Deliberately tolerant of an unparsed body: this runs at the service tier, which is reached both by\n * the HTTP controller (where the request DTO has already validated the shape) and by MCP / batch /\n * programmatic callers (where it has not). Anything that is not a non-empty array of non-empty\n * strings yields `undefined`, so the caller's own missing-target refusal reports the failure rather\n * than a half-resolved selector reaching persistence.\n */\nexport function readBulkOperationSelectorIds(payload: unknown): string[] | undefined {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return undefined;\n }\n\n const raw = (payload as Record<string, unknown>)[BULK_OPERATION_SELECTOR_FIELD];\n if (!Array.isArray(raw)) {\n return undefined;\n }\n\n const ids = raw.filter((candidate): candidate is string => typeof candidate === 'string' && candidate.length > 0);\n return ids.length > 0 ? ids : undefined;\n}\n\n/**\n * Strip the bulk selector from a request payload.\n *\n * `_ids` is NOT in `createOrUpdateMandatoryDiscardFields`, so without this it rides the payload all\n * the way to the repository and is silently dropped there by the strict re-parse against the main\n * schema. Removing it at the point the target is resolved keeps \"who targets\" and \"what is written\"\n * separate, which is what lets the read-only-field guard reason about the payload honestly.\n */\nexport function stripBulkOperationSelector<T>(payload: T): T {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return payload;\n }\n\n if (!(BULK_OPERATION_SELECTOR_FIELD in (payload as Record<string, unknown>))) {\n return payload;\n }\n\n const { [BULK_OPERATION_SELECTOR_FIELD]: _selector, ...rest } = payload as Record<string, unknown>;\n return rest as T;\n}\n\n\n// DELETED — ResourceConfiguration_ResourceLabel_FunctionParameters\n// Transferred to @wildo-ai/saas-frontend-lib as ResourceUIBehaviorLabelParams\n\nexport type ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = {\n inputDto: MainSchemaInfer<TMainSchema>,\n currentObject: MainSchemaInfer<TMainSchema>,\n /**\n * The row as it stood BEFORE the write (the pre-image), when the framework captured one.\n *\n * Present only for a SINGLE-row UPDATE/DELETE (the framework fetches a pre-image only when\n * exactly one id is selected); `undefined` for CREATE (nothing existed) and for bulk writes.\n * It lets a consumer reason about what CHANGED rather than only the final state. Its wired\n * consumer today is the derived-badge recompute, which re-counts the scope a mutation moved a\n * row OUT of (not just the one it moved it into) — see `dispatchDerivedBadgeRecomputes`. Other\n * consumers (e.g. a `userIdsSelector` notifying the PRIOR assignee a task was taken from them)\n * would need the notification dispatcher to forward it into the selector call first; it is not\n * plumbed there yet. Never assume it is populated; branch on its presence.\n */\n previousObject?: MainSchemaInfer<TMainSchema>,\n objectContext: any,\n initiatorUserContext?: any,\n initiatorIds?: { userId?: string; organizationId?: string; applicationId?: string },\n}\n\n/**\n * Metadata for functions that can optionally require context.\n * Similar to virtual fields, these flags indicate whether objectContext/initiatorUserContext\n * should be populated when calling the function.\n */\nexport type ResourceConfiguration_FunctionContextNeeds = {\n needsObjectContext?: boolean;\n needsUserContext?: boolean;\n}\n\n\n\n// --------------------------------\n// Required Features Configuration\n// --------------------------------\n\n/**\n * Limit check entry: references a LIMIT-type feature and specifies the increment.\n * The gate uses the feature definition's `counting` config to measure current usage.\n */\nexport type RequiredFeaturesLimitCheck = {\n /** Feature identifier (must reference a LIMIT-type feature) */\n featureId: string;\n /**\n * How many units this operation would add.\n * When omitted: auto-inferred from operation type:\n * - CREATE → 1\n * - CREATE_MANY → validatedData.length\n * - All others → 0\n */\n increment?: number;\n /**\n * Override counting config from feature definition (rare).\n * Use when this specific operation needs different counting than the default.\n */\n countingOverride?: LimitCountingConfig;\n};\n\n/**\n * Full required features configuration for an operation variant.\n */\nexport type RequiredFeaturesFullConfig = {\n /** Boolean feature identifiers that gate this operation (presence check) */\n features?: string[];\n /** Combination mode for boolean features: OR = any enables (default), AND = all required */\n mode?: 'OR' | 'AND';\n /** Limit checks to perform before this operation */\n limitChecks?: RequiredFeaturesLimitCheck[];\n};\n\n/**\n * Required features config: accepts either shorthand (string[]) or full config.\n * Shorthand is auto-normalized by the factory to { features: [...], mode: 'OR' }.\n */\nexport type RequiredFeaturesConfig = string[] | RequiredFeaturesFullConfig;\n\n/**\n * Normalized required features (always the full form, after factory processing).\n */\nexport type RequiredFeaturesNormalized = RequiredFeaturesFullConfig;\n\n// --------------------------------\n// Resource Operation Preset Base Type\n// --------------------------------\n\n/**\n * Wrapper type for functions that can optionally require context.\n * Stores both the function and metadata about its context needs.\n */\nexport type ResourceConfiguration_FunctionWithContextNeeds<TFunction extends (...args: any[]) => any> =\n TFunction & ResourceConfiguration_FunctionContextNeeds;\n\nexport enum ResourceGeneratedOperationKind {\n GENERATED_FILE_REGENERATE = 'generated_file_regenerate',\n /** The CSV/JSON export companion of a LIST/SEARCH operation — see {@link ResourceCollectionExportConfig}. */\n GENERATED_COLLECTION_EXPORT = 'generated_collection_export',\n}\n\nexport type ResourceConfiguration_GeneratedFileRegenerateOperationMetadata = {\n kind: ResourceGeneratedOperationKind.GENERATED_FILE_REGENERATE;\n fieldName: string;\n};\n\n/**\n * Identifies an operation as the export companion of one LIST/SEARCH operation, and carries the\n * export contract the handler and the frontend both read.\n *\n * `sourceOperationIdentifier` is what makes the export re-runnable AS the listing: the handler\n * resolves that operation and dispatches it, rather than re-deriving a query.\n */\nexport type ResourceConfiguration_GeneratedCollectionExportOperationMetadata = {\n kind: ResourceGeneratedOperationKind.GENERATED_COLLECTION_EXPORT;\n /** The LIST/SEARCH operation whose result set this exports. */\n sourceOperationIdentifier: string;\n /** Which core verb the source is, so the handler knows which service call to make. */\n sourceCoreOperation: CoreResourceOperation.LIST | CoreResourceOperation.SEARCH;\n export: ResourceCollectionExportConfig;\n};\n\nexport type ResourceConfiguration_GeneratedOperationMetadata =\n | ResourceConfiguration_GeneratedFileRegenerateOperationMetadata\n | ResourceConfiguration_GeneratedCollectionExportOperationMetadata;\n\n/**\n * Authored acknowledgement that an operation variant is DECLARED but knowingly NOT IMPLEMENTED.\n *\n * ## Why this exists\n *\n * A custom operation whose `requestDto` no implementation consumes does not fail — it falls\n * through to the generic core path, which persists only fields the resource schema declares.\n * A DTO such as `{ newRole, reason }` names none, so the framework validates the body, writes\n * nothing, and answers **HTTP 200**. On 2026-08-01 a hostile e2e probe caught\n * `organizationMembers | promote` doing exactly that, and a catalogue sweep found the shape in\n * 41 routed operations — 8 CRITICAL, 11 HIGH, including `users | impersonate`,\n * `organizations | suspend` and `applications | transfer`. A \"successful\" demotion that never\n * happened is an operational-safety and audit-integrity defect: every runbook, script, and UI\n * that trusts the 2xx is wrong.\n *\n * ## What it does\n *\n * Presence is a fail-closed declaration with TWO effects, never one without the other:\n *\n * 1. **Startup is allowed to proceed.** Without it, an unserviceable operation is a startup\n * ERROR (`ApplicationStartupConfigValidatorService`), so a new one cannot ship silently.\n * 2. **The request fails closed.** The dispatcher refuses the operation before any persistence\n * with `ErrorType.CONFIGURATION` /\n * `ErrorCustomMessageReference.RESOURCE_OPERATION_CUSTOM_IMPLEMENTATION_NOT_IMPLEMENTED`.\n * Acknowledging a gap must never preserve the 2xx — that would document the defect instead\n * of closing it.\n *\n * The guard is BIDIRECTIONAL: an acknowledgement on an operation that IS serviceable is also a\n * startup error, so a stale marker can never brick an operation someone has since implemented.\n *\n * ## This is a stop-gap, not a resting place\n *\n * The right end states are to implement the operation (see the organization-units\n * MOVE/ARCHIVE pattern — a `prefixCoreOperations` hook returning the field patch the core\n * write persists) or to remove the declaration. Reach for this only to keep a known gap\n * honest while it is scheduled.\n *\n * assurance-control: WILDO.OPERATIONS.EXECUTION_INTEGRITY — an interface must not report success for an\n * action it did not perform; monitoring and incident response depend on the response being\n * truthful.\n */\nexport type ResourceOperation_UnimplementedAcknowledgement = {\n /** Why the operation is declared without an implementation, in the author's own words. */\n reason: string;\n /** Where the gap is tracked — a plan path, issue id, or equivalent durable reference. */\n trackingRef: string;\n};\n\n/**\n * Where the engine's own inherited implementation gaps are tracked — the 2026-08-01 catalogue\n * sweep that found 41 routed operations answering a success status having performed nothing.\n * One authority so the backlog cannot fragment across 37 hand-typed strings.\n */\nexport const UNIMPLEMENTED_ENGINE_OPERATION_BACKLOG_REF =\n '.claude/plans/routed-unimplemented-operations-fail-closed.md';\n\n/**\n * Acknowledge an engine operation as a known, fail-closed implementation gap.\n *\n * Requires a SPECIFIC reason: \"not implemented\" is what the marker already says, so a reason\n * that adds nothing is a reason not worth reading during triage. State what the operation was\n * meant to do, so whoever picks it up knows the target behaviour without re-deriving it.\n *\n * @see ResourceOperation_UnimplementedAcknowledgement for the two effects this has.\n */\nexport const acknowledgeUnimplementedEngineOperation = (\n reason: string,\n): ResourceOperation_UnimplementedAcknowledgement => ({\n reason,\n trackingRef: UNIMPLEMENTED_ENGINE_OPERATION_BACKLOG_REF,\n});\n\n/**\n * Declarative control over this variant's audit emission.\n *\n * **The baseline needs no declaration.** Every `HIGH` / `CRITICAL` variant emits\n * `RESOURCE_OPERATION_PERFORMED` automatically — `riskLevel` is already the framework's canonical\n * danger axis (it drives MCP destructive hints, frontend danger-zone placement and CRITICAL typed\n * confirmation), and a risk-driven floor is fail-closed by omission. A declarative-ONLY mechanism\n * was rejected for exactly that reason: it fails OPEN when an author forgets, which is how 20 event\n * types came to be declared in `CoreAuditableEventType` and wired to nothing.\n *\n * This declaration exists for the two cases the floor cannot express on its own.\n */\nexport type ResourceOperation_AuditableEvent = {\n /**\n * Emit for this variant even though its `riskLevel` is below the automatic floor.\n *\n * For a `MEDIUM` / `LOW` verb whose danger is not proportional to its blast radius — the risk\n * level answers \"how alarming is the UI affordance\", which is a related but not identical\n * question to \"is this evidence an auditor needs\".\n */\n auditBelowRiskFloor?: boolean;\n};\n\n/*\n * A DELIBERATELY ABSENT second knob — `suppressGenericEvent`.\n *\n * It was designed and then removed after checking whether any verb in the catalogue is actually\n * COVERED by its purpose-built event. None is. The generic row carries the operation identity\n * triple, the top-level `correlationId` that correlates a cascade, the actor's roles as they stood, and the\n * execution type — and NO purpose-built event carries any of those. So the two rows state different\n * facts about one action rather than duplicating each other:\n *\n * - `users | assign_roles` also emits `USER_APP_ROLES_CHANGED`, which uniquely carries\n * `requestedRoles` (the set the role-ceiling gate measured). The generic row uniquely carries\n * who asked, from where, under which variant, correlated to the rest of the request.\n * - `webhookConfig | testEndpoint`, the other candidate, is MEDIUM — below the floor entirely, so\n * nothing would have been suppressed.\n *\n * Shipping the flag anyway would have added authoring vocabulary with zero consumers, which is the\n * precise defect this whole mechanism exists to undo: 19 members of `CoreAuditableEventType` are\n * declared, classified, and emitted by nothing. Add it the day a verb is genuinely covered.\n */\n\ntype ResourceConfiguration_OperationVariant_Base<TMainSchema extends z.ZodTypeAny = z.ZodAny> = {\n variantType : ResourceOperationVariantType;\n resourceOperationLike? : CoreResourceOperation\n generatedOperation?: ResourceConfiguration_GeneratedOperationMetadata;\n /**\n * Feature gating for this operation variant.\n * Shorthand: `string[]` auto-normalized by factory to `{ features: [...], mode: 'OR' }`.\n * Full form: `{ features, mode?, limitChecks? }`.\n */\n requiredFeatures?: RequiredFeaturesConfig;\n isDefault?: boolean; // default true\n variantKey? : string; // default undefined - mandatory if isDefault is false\n\n riskLevel: ResourceOperationRiskLevel;\n roles: Roles[];\n /**\n * Per-foreign-key policy for REFERENCE fields this variant writes — the TARGET-side counterpart of\n * `roles` above (which gates the CALLER).\n *\n * A relationship that declares `scopeMembership` already guarantees the baseline: the referenced row\n * must be linked to the writing row's scope through a junction (that is tenant isolation, and it\n * applies to every variant). This map TIGHTENS that baseline for THIS variant, keyed by foreign-key\n * field — require an `ACTIVE` membership, an `ORG_ADMIN` assignee, a typed partnership — or `false`\n * to disable it for one foreign key on one variant.\n *\n * Omit entirely (the common case): the relationship's baseline membership applies unchanged.\n *\n * @example an \"assign reviewer\" variant whose assignee must be an ACTIVE org admin\n * referenceConstraints: {\n * assignedToUserId: {\n * qualifyingStatuses: ['ACTIVE'],\n * requiredRoles: { scope: ResourcePrimaryScope.ORGANIZATIONS, roles: [CORE_ORG_ROLES.ORG_ADMIN] },\n * },\n * }\n */\n referenceConstraints?: ReferenceConstraints;\n /**\n * Declares this variant a KNOWN, fail-closed implementation gap.\n * See {@link ResourceOperation_UnimplementedAcknowledgement} — presence both unblocks\n * startup and makes the request refuse, so an acknowledged operation never answers 2xx.\n */\n unimplementedAcknowledgement?: ResourceOperation_UnimplementedAcknowledgement;\n /**\n * Declarative control over this variant's audit emission — see\n * {@link ResourceOperation_AuditableEvent}. Omit it: `HIGH` / `CRITICAL` variants are audited\n * automatically, and everything else is deliberately not.\n */\n auditableEvent?: ResourceOperation_AuditableEvent;\n /**\n * Demand a FRESH proof of identity for this specific operation, regardless of how recently the\n * caller authenticated.\n *\n * The framework's baseline step-up is a per-USER-TYPE policy (`UserTypeAuthPolicy.stepUpAuth`):\n * opt-in, defaulting to `enabled: false`, and *freshness*-based — it challenges only once the\n * session's `lastAuthenticatedAt` is older than `maxAgeSeconds` (default 300). That is the right\n * shape for \"this tenant wants periodic re-proof across the board\", and the wrong shape for a\n * single irreversible action: a caller who signed in two minutes ago destroys the resource with\n * no credential proof at all, and an application that never configures `stepUpAuth` is never\n * challenged for anything.\n *\n * Setting this to `true` makes the challenge UNCONDITIONAL for this operation. The caller must\n * present a valid single-use `REAUTH` consumable token (minted by `POST /auth/reauth`, carried in\n * `X-Reauth-Token`); otherwise the request is refused with `STEP_UP_REQUIRED` (HTTP 403). The\n * frontend HTTP client already completes this loop — it catches the 403, runs the registered\n * step-up handler to obtain a token, and replays the request — so declaring the flag is the whole\n * integration.\n *\n * FAIL-CLOSED by design: an operation that declares this is challenged even when the user type\n * has NO `stepUpAuth` policy at all. Deriving the requirement from `riskLevel` was rejected —\n * `riskLevel` is a UI/observability signal (danger-zone placement, typed confirmation, MCP\n * destructive hints), read today by no authorization code, and silently promoting it to a security\n * control would change the behaviour of every existing `CRITICAL` operation at once.\n *\n * Applies only to principals whose identity can be re-proved: user requests that are not\n * sub-calls. Machine principals, internal calls and worker executions are exempt exactly as they\n * are for the baseline policy — there is no interactive credential to re-present.\n *\n * ⚠️ **Check who can actually satisfy it before declaring it.**\n * `AuthMethodManagementBackendService.reAuthenticate` proves identity by PASSWORD only today\n * (`AuthMethod.PASSKEY` is declared and explicitly refused as not yet supported), and it requires\n * a real `passwordHash` on the principal's credential row — a user-type policy that merely enables\n * `PASSWORD` is necessary but NOT sufficient.\n *\n * Two populations therefore cannot satisfy this gate:\n *\n * - a user type whose `authMethodsEnabled` excludes `PASSWORD` (SSO-only, passkey-only) — note\n * this is also reachable through a per-ORGANIZATION `authMethodsEnabled` override, so an\n * app-level policy is not the whole answer;\n * - **directory-managed (SCIM) users**, whose `passwordHash` AND passkeys the framework\n * DELIBERATELY DESTROYS as a compliance control\n * (`_reconcileLocalCredentialsForDirectoryManaged` — a local first factor would survive IdP\n * deprovisioning). Their TOTP is intentionally preserved, but re-auth cannot use it yet.\n *\n * For those principals the operation is permanently refused. That is fail-closed rather than\n * unsafe, and the refusal is diagnosable (the step-up round-trip ends in an explicit \"No password\n * set for this account\") — but it is a product decision, not a detail. Do not declare this flag on\n * an operation those populations are expected to perform until re-authentication covers a factor\n * they hold.\n *\n * This is also why the requirement is a per-operation flag rather than a hardcoded `password`\n * field in a request DTO: the proof is negotiated by the auth policy, not frozen into the resource\n * contract. A `{ password }` field cannot adapt to any of the above.\n *\n * assurance-control: WILDO.IDENTITY.STEP_UP_AUTHENTICATION — re-authentication before a high-impact action.\n */\n /**\n * Admits an APPLICATION-scope platform super-administrator to a resource whose primary scope is a\n * PER-INSTANCE tenant scope (today: `ORGANIZATIONS`) without requiring membership of that tenant.\n *\n * ## Why this exists, and why it is per-variant rather than global\n *\n * `validateParentScopeAccess` authorizes an organization-scoped operation by finding an\n * organization-WIDE `initiatorRoles` entry whose `relatedResourceId` is the target organization. A\n * platform super-administrator holds an APPLICATION-scope entry and therefore matches nothing — so\n * they are denied on every org-scoped resource, INCLUDING the organization row itself. That is\n * normally exactly right: it is what makes tenant isolation hold against the most privileged\n * principal in the system, and it is pinned by\n * `cross-tenant-super-admin-reach.mongo.integration.test.ts`.\n *\n * It also means a tenant that loses its last owner cannot be repaired by anyone, which is the other\n * half of the administrative-continuity pattern: protect the tenant tier and\n * repair it only from a higher authorized tier. This flag is that higher-tier\n * exception and nothing else.\n *\n * **It is deliberately NOT a global super-admin bypass.** Adding one to the authorization layer\n * would grant cross-tenant reach to EVERY org-scoped operation whose gate a super-admin's role\n * chain satisfies — an enormous, silent widening of tenant isolation. Instead each variant opts in\n * explicitly, so the admitted surface is enumerable by grep and empty by default.\n *\n * ## Conditions, all required\n *\n * - The caller must hold `APP_ADMIN_SUPER_ADMIN` at **APPLICATION** scope. An organization-scoped\n * role string that merely spells the same name confers nothing: `getRoleHierarchy` partitions the\n * two role tables, and membership `roles` is free-form (`OrgRolesSchema` admits any string), so\n * without the scope qualifier a tenant admin could type their way into cross-tenant reach.\n * - The variant must declare this flag. Absent, behaviour is unchanged and the caller is denied.\n *\n * Declare it only on operations whose PURPOSE is cross-tenant administration, and expect each one\n * to justify itself: this is the seam a reviewer should look at first.\n *\n * assurance-control: WILDO.ACCESS.CROSS_TENANT_ADMINISTRATION — an explicitly enumerated,\n * platform-tier admission across a tenant boundary that is otherwise absolute.\n */\n admitsCrossTenantPlatformAdministration?: boolean;\n\n /**\n * Admits this variant to address a USER other than the caller — the USER_SELF-scope sibling of\n * {@link admitsCrossTenantPlatformAdministration}, and it follows that flag's doctrine exactly:\n * per-variant, empty by default, enumerable by grep.\n *\n * ## Why it is needed\n *\n * `authorizeUserSelf` authorizes a USER_SELF-scoped operation by requiring the addressed row to BE\n * the caller. On `USERS` — whose `resourcePrimaryScope` IS `USER_SELF`, because the resource is a\n * self-anchor scope root — that made the entire administrative surface self-only: `assign_roles`,\n * `revoke_roles`, `suspend_user`, `unsuspend_user`, `force_password_reset`, the admin READ and the\n * core UPDATE/DELETE all refused `authorizations_access_denied` against another user, while the\n * specification documents every one of them as acting on \"another user's record\" and the admin UI\n * surfaces them as per-row actions.\n *\n * ## Why it is PER-VARIANT and not per-resource\n *\n * This is the whole reason a resource-level declaration cannot serve. Within `USERS` the two READ\n * variants need OPPOSITE answers:\n *\n * - the DEFAULT read is gated `APP_USER`. Admitting cross-subject addressing there would let ANY\n * authenticated user read ANY other user's row — `email`, `appRoles`, `status` — a privacy and\n * enumeration regression far worse than the bug being fixed.\n * - the `admin` read is gated `APP_ADMIN_VIEWER` and is precisely the surface that SHOULD address\n * others.\n *\n * One resource, one field identifier, two required answers. Only the variant can carry it.\n *\n * ## What still protects the surface\n *\n * This flag relaxes WHOSE row may be addressed. It does not touch WHO may call: the operation's\n * `roles` gate runs independently and unchanged, so an admitted variant is still reachable only by\n * the roles it declares. Role decides who; this decides whose. Declare it only where the\n * operation's PURPOSE is administering another person, and expect each one to justify itself.\n *\n * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — an administrative action on another principal\n * is authorized by the caller's ROLE, never by caller and subject being the same person.\n */\n admitsCrossSubjectUserAdministration?: boolean;\n\n requiresStepUpAuthentication?: boolean;\n /**\n * Default **frontend surface** for this operation when no app preset declares\n * one (see {@link ResourceOperationFrontendSurface}). Lets the framework mark\n * an operation headless-by-default (`API_ONLY`) — e.g. system/workflow/signup\n * -driven creates — so apps don't re-author `apiOnly()` per app. An app preset\n * that surfaces the operation (`addressable()`/`notAddressable()`/`apiOnly()`)\n * always wins; this is only the fallback. When unset, an unsurfaced operation\n * resolves to `API_ONLY` (the safe default — callable, no UI). Display-only:\n * never affects authorization.\n */\n defaultFrontendPosture?: ResourceOperationFrontendSurface;\n /**\n * @wildo_source:part:start saas.models.resource-config.operation-mcp-exposure facet:layer:shared facet:family:resource-config\n *\n * MCP (Model Context Protocol) exposure for this operation. Set `exposed: true` to\n * surface the operation as an MCP tool that authenticated machine / agent callers can\n * discover (`tools/list`) and invoke (`tools/call`). Import path: `@wildo-ai/saas-models`\n * — this is a field on an operation variant inside a resource's `operations` config.\n *\n * Opt-in + FAIL-CLOSED: absent or `false` ⇒ the operation is NOT a tool. Only DEFAULT,\n * non-bulk, URL-bearing operations (the standard `API_CALL` create / read / list / search /\n * count / update / delete and URL-bearing custom ops) of ORGANIZATION- or APPLICATION-scoped\n * resources are eligible. A machine principal cannot reach USER_SELF resources, so setting\n * `exposed` on a USER_SELF operation has no machine-callable effect.\n *\n * `description` is the agent-facing tool description — the prose the calling agent reads to\n * decide when to use the tool. When omitted, the tool description falls back to the resource\n * specification's `purpose` / `whenToUse`, then to a synthesized default; keep an authored\n * `description` reconciled with the spec.\n *\n * Exposure is a DISCOVERY gate ONLY. Every `tools/call` still re-runs the operation's own\n * role authorization against the CALLER's roles before dispatch, so exposing an operation can\n * never widen who is allowed to perform it.\n *\n * `servers` selects WHICH named MCP server(s) carry this tool when the app declares more than one\n * (each server is a separate endpoint + audience, so a token for one is rejected at another). OMIT it\n * for the DEFAULT server — the plain `/mcp` endpoint every app has. When PRESENT it must name at least\n * one declared server: an EMPTY array is a startup error, not a silent fallback, so the field always\n * reads as \"exactly these\" (the same omitted-vs-present rule an A2A agent's `actorSystemRefs` follows).\n * Naming a server the app does not declare is likewise a startup error (fail-closed), so a typo can\n * never conjure a phantom surface.\n *\n * Membership is EXCLUSIVE: an operation naming a server LEAVES the default `/mcp` catalog rather than\n * appearing on both, so curation is always a deliberate move and never an accidental duplication.\n */\n mcp?: {\n exposed?: boolean;\n description?: string;\n servers?: string[];\n };\n /** @wildo_source:part:end saas.models.resource-config.operation-mcp-exposure */\n requestDto?: z.ZodSchema<any>;\n /**\n * RepositoryDto for repository projection.\n * Excludes virtual fields and populated placeholders.\n * Calculated at factory execution time.\n */\n repositoryDto?: z.ZodSchema<any>;\n customServiceImplementationModes?: ResourceOperation_CustomServiceImplementationMode[];\n /**\n * Function to modify input before core operation.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * basicPrefixOperation.needsObjectContext = true\n * basicPrefixOperation.needsUserContext = true\n *\n * Returns a PATCH, not a replacement: the service MERGES the result onto the payload\n * (`coreInput = { ...coreInput, ...result }`) and whitelists the keys the hook ADDED into\n * `preserveServerInjectedFields`, so they survive the repository's strict re-parse. Naming\n * only the fields the verb changes is therefore both sufficient and safer than spreading the\n * current object — a spread makes every field a server-injected key, which re-writes\n * `.excludeFromUpdate()` fields such as an owning foreign key on every call.\n */\n basicPrefixOperation?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => Partial<MainSchemaInfer<TMainSchema>>\n >; // default undefined\n /**\n * Fields whose value this operation's {@link basicPrefixOperation} OWNS.\n *\n * Declare a field here when the hook must set it EVEN IF the caller also supplied it. The\n * \"keys the hook ADDED\" inference described above is derived from a snapshot of the payload\n * taken at the SERVICE tier, after a LOOSE parse (`z.looseObject`) that passes unknown keys\n * through. A caller that reaches that tier with the field already present — MCP `callTool`\n * forwards its arguments with no strict parse — puts the key into the snapshot, so the hook's\n * own value stops being treated as injected, the repository's strict re-parse drops it, and the\n * operation answers success having written nothing.\n *\n * Declaring the field makes the ownership explicit: it is always preserved, always with the\n * POST-hook value, so a caller-supplied value is overwritten rather than honored. This does NOT\n * make the field client-writable — reachability stays governed by {@link requestDto} and the\n * schema decorators.\n *\n * Any hook that forces a server-owned lifecycle value (a `status` transition, an ownership\n * stamp) should declare it. A hook that only derives a value from other authored input does not\n * need to, because a caller could not have supplied it under a strict contract anyway.\n *\n * Typed `readonly string[]` rather than `keyof MainSchemaInfer<TMainSchema>`: this type is shared\n * by the authoring parameters AND the built operation, and the built side is assembled behind a\n * type-erased generic where `keyof` collapses to `never`. The stronger check lives at BOOT\n * instead — `collectResourceOperationAuthoritativeFieldIssues` refuses startup when a declared\n * name is not a persisted field of the resource. That guard is strictly better than the\n * compile-time one would have been, because it also covers custom-implementation registrations,\n * whose declarations are plain strings with no schema generic to check against. A typo must fail\n * loudly: silently declaring nothing would restore the exact no-op this contract prevents.\n */\n authoritativeFields?: readonly string[];\n}\n\n\n// --------------------------------\n// Resource Configuration Builder Parameters\n// --------------------------------\n\n\ntype ResourceConfiguration_OperationVariant_ApiCall_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = ResourceConfiguration_OperationVariant_Base<TMainSchema> & {\n\n variantType : ResourceOperationVariantType.API_CALL;\n isDefault?: boolean; // default true\n haveBulkOperation?: boolean; // default false - not possible for CoreResourceOperation.READ or LIST\n isApiKeyAccessDisabled?: boolean; // default false\n /** Declares which consumable token types this operation accepts for authentication */\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n /** Declarative token generation config — creates a token as side-effect after successful operation */\n tokenGeneration?: TokenGenerationConfig; // default undefined\n /** Declarative token revocation config — revokes tokens before the core operation (e.g., on DELETE) */\n tokenRevocation?: TokenRevocationConfig; // default undefined\n customResponseDto?: z.ZodSchema<any>;\n /**\n * Per-endpoint rate-limit policy.\n *\n * When present, the resource controller's pre-flight check enforces this\n * policy BEFORE execution-context creation, request validation, and\n * authorization (via `RateLimitBackendService.checkPolicyForVariant`). At\n * that seam the authenticated identity is not yet known, so the client\n * identifier is the IP address (`getClientIpFromRequest`).\n *\n * When absent, only the global HTTP rate-limit\n * (`RuntimeConfig.httpServerConfiguration.rateLimiting`) gates this route\n * — that limiter is the safety net for endpoints with no per-endpoint\n * policy.\n *\n * Multiple windows AND together (most restrictive wins). On 429 the\n * `X-RateLimit-*` headers report the **tightest active window** (fewest\n * remaining slots), while `Retry-After` / `retryAfterSeconds` reports\n * the **latest tripped window** because declared windows are AND-ed.\n * These can be different windows when multiple trip simultaneously.\n *\n * The declared policy is surfaced through standard rate-limit response\n * headers and retry guidance, so callers see the same request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n}\n\ntype ResourceConfiguration_OperationVariant_ApiCallWithCallback_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = ResourceConfiguration_OperationVariant_Base<TMainSchema> & {\n\n variantType : ResourceOperationVariantType.API_CALL_WITH_CALLBACK;\n isDefault?: boolean; // default true\n haveBulkOperation?: boolean; // default false - not possible for CoreResourceOperation.READ or LIST\n isApiKeyAccessDisabled?: boolean; // default false\n /** Declares which consumable token types this operation accepts for authentication */\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n /** Declarative token generation config — creates a token as side-effect after successful operation */\n tokenGeneration?: TokenGenerationConfig; // default undefined\n /** Declarative token revocation config — revokes tokens before the core operation (e.g., on DELETE) */\n tokenRevocation?: TokenRevocationConfig; // default undefined\n customResponseDto?: z.ZodSchema<any>;\n /**\n * Per-endpoint rate-limit policy. Same semantics as on the API_CALL\n * variant — enforced pre-flight by the resource controller before EC\n * creation, validation, and authorization.\n *\n * The declared policy is surfaced through standard rate-limit response\n * headers and retry guidance, so callers see the same request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n callbackUrl?: string; // Optional callback URL for async operations\n callbackTimeout?: number; // Timeout in seconds for callback operations, default 300 (5 minutes)\n callbackRetryAttempts?: number; // Number of retry attempts for failed callbacks, default 3\n}\n\ntype ResourceConfiguration_OperationVariant_CronJob_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.CRON_JOB,\n cronExpression: string,\n /**\n * Explicit response contract for a CRON_JOB custom operation whose return shape\n * is NOT the resource row implied by `resourceOperationLike`. A scheduled\n * generator/sweeper (e.g. a recurring-occurrence generator) borrows a core verb\n * (UPDATE/CREATE) for its repo/DTO semantics but returns a RUN SUMMARY, not an\n * occurrence. The factory already reads `customResponseDto` off any variant\n * params (`resources-config.shared.factory.ts`) and warns when a borrowed-verb\n * custom op omits it — this exposes it on the CRON_JOB params so the cron op can\n * declare its true return shape instead of inheriting the verb's occurrence\n * -shaped envelope (which the executor would reject the summary against).\n */\n customResponseDto?: z.ZodSchema<any>,\n}\ntype ResourceConfiguration_OperationVariant_BatchJob_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.BATCH_JOB\n}\ntype ResourceConfiguration_OperationVariant_InternalCall_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.INTERNAL_CALL\n}\ntype ResourceConfiguration_OperationVariant_RepositoryOnly_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.REPOSITORY_ONLY\n}\n\n\n// --------------------------------\n// Resource Final Configuration\n// --------------------------------\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-base facet:layer:shared facet:family:resource-config\n *\n * Base operation configuration type.\n * NOTE: TDatabaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in mainSchema and are stripped at the service layer.\n *\n * @remarks Import from `@wildo-ai/saas-models`. App `resources-config` fills these per operation variant.\n */\nexport type ResourceConfiguration_Operation_Base<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_OperationVariant_Base<TMainSchema> & {\n isDisabled: boolean, // default false\n mainSchema: TMainSchema,\n resourcePrimaryScope : ResourcePrimaryScope, // default ResourcePrimaryScope.ORGANIZATIONS\n resourceIdentifier : ResourceType,\n resourceFieldIdentifier : ResourceFieldIdentifier,\n\n resourceRelationships : ResourceConfiguration_ResourceRelationships,\n\n isSystemResource: boolean, // default false - true for core system resources (organizations, users, etc.), false for tenant-specific resources\n operationIdentifier : TCustomOperationEnum | TCoreOperationEnum,\n isOperationDefault : boolean, // default true\n serviceRuntimeMode: ResourceOperation_ServiceRuntimeMode; //default ResourceOperation_ServiceRuntimeMode.INLINE\n generatedOperation?: ResourceConfiguration_GeneratedOperationMetadata;\n\n requestDto?: z.ZodSchema<any>;\n responseDto?: z.ZodSchema<any>;\n /** Set by factory when an explicit `requestDto` was provided in variant params.\n * Allows the DTO builder to skip heuristic comparison and trust the flag:\n * `true` ⇒ preserve the supplied schema verbatim; `false` ⇒ the requestDto is\n * the factory's auto-derived temporary placeholder, so always use the\n * inheritance-rebuilt DTO (a discriminated union for inheritance resources).\n * Absent only on legacy/hand-authored operations that predate the flag. */\n hasCustomRequestDto?: boolean;\n /** Set by factory when `customResponseDto` was explicitly provided in variant params.\n * Allows the DTO builder to skip heuristic comparison and always preserve the custom schema. */\n hasCustomResponseDto?: boolean;\n /**\n * SummaryDto contains only fields marked with isSummaryField decorator.\n * Used for LIST operations in SUMMARY mode.\n * Calculated at factory execution time.\n */\n summaryDto?: z.ZodSchema<any>;\n /**\n * ContextDto includes relationship placeholders for context-dependent operations.\n * Used for operations in CONTEXT mode.\n * Calculated at factory execution time.\n */\n contextDto?: z.ZodSchema<any>;\n /**\n * InternalDto includes backend-only fields (isBackendOnly decorator).\n * Used for backend-to-backend communication (queues, internal calls).\n * Calculated at factory execution time.\n */\n internalDto?: z.ZodSchema<any>;\n /**\n * RepositoryDto for repository projection.\n * Excludes virtual fields and populated placeholders.\n * Calculated at factory execution time.\n */\n repositoryDto?: z.ZodSchema<any>;\n\n notificationBadgeOperations: { identifier : NotificationBadgeIdentifier, operation : NotificationBadgeOperation }[]; //\n userNotifications: UserNotificationDefinition[];\n m2mNotifications: M2MNotificationDefinitionWithoutIdentifier[];\n }\n/** @wildo_source:part:end saas.models.resource-config.operation-base */\n\n/**\n * Base operation type - explicitly extracted for better type checking performance.\n * TypeScript can cache this more efficiently when it's a named type alias.\n * This helps reduce memory usage during compilation when processing union types.\n */\nexport type ResourceConfiguration_Operation_BaseType<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n> = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>;\n\n\nexport type ResourceConfiguration_Operation_CronJob<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.CRON_JOB;\n cronExpression: string,\n }\n\nexport type ResourceConfiguration_Operation_BatchJob<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.BATCH_JOB;\n }\n\nexport type ResourceConfiguration_Operation_InternalCall<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.INTERNAL_CALL;\n }\n\nexport type ResourceConfiguration_Operation_RepositoryOnly<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.REPOSITORY_ONLY;\n }\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-api-call facet:layer:shared facet:family:resource-config\n *\n * HTTP API surface for an operation: URL templates, method, tokens,\n * enabledCondition, and per-endpoint rate-limit policy (`rateLimit?`).\n *\n * @remarks Import from `@wildo-ai/saas-models`.\n */\n// URL parameter information for each URL\nexport type ResourceConfiguration_Operation_UrlParameterInfo = {\n fieldIdentifier: string;\n resourceType: ResourceType;\n /**\n * The FK relationship field if this parameter comes from a FK relationship.\n * e.g., 'assignedToUserId' for /assigned-to-users/{userId}/...\n * undefined for default parent relationships.\n */\n relationshipField?: string;\n /**\n * Scope-via-junction tag, propagated from the synthetic scope-anchored parent\n * edge (`ResourceRelationship.scopeViaJunction`). When set, this param scopes\n * the (target) resource's SEARCH/LIST through the named junction `J` rather\n * than via a direct column the resource owns — e.g. on\n * `/organizations/{organizationId}/users/search`, `organizationId` carries\n * `scopeViaJunction: 'organizationMembers'`. The backend contextual filter +\n * authorization read this off the runtime relationship context. Absent on\n * normal parent params.\n */\n scopeViaJunction?: ResourceType;\n};\n\n// URL information structure\nexport type ResourceConfiguration_Operation_UrlInitiatorInfo = {\n url: string;\n urlCallback?: string;\n parameters: ResourceConfiguration_Operation_UrlParameterInfo[];\n};\n\nexport type ResourceConfiguration_Operation_ApiCall<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.API_CALL;\n httpVerb: HttpMethod;\n isApiKeyAccessDisabled: boolean; // default false\n isBulkOperation: boolean; // default false\n initiatorInfos?: ResourceConfiguration_Operation_UrlInitiatorInfo[];\n\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n tokenGeneration?: TokenGenerationConfig; // default undefined\n tokenRevocation?: TokenRevocationConfig; // default undefined\n /**\n * Resolved per-endpoint rate-limit policy (forwarded by the resource\n * configuration factory from the variant builder parameters). Read at\n * runtime by `RateLimitBackendService.checkPolicyForVariant` at the\n * controller pre-flight seam (`controller-resource.backend.service.ts`\n * → `doOperation`).\n *\n * Auto-variants (`SUMMARY` / `CONTEXT` for READ/LIST) inherit this\n * field from their default URL-bearing variant (`API_CALL` or\n * `API_CALL_WITH_CALLBACK`) — a single declaration on the default variant\n * covers all derived auto-variants by design.\n *\n * The declaration is inherited by derived variants so every route for this\n * operation presents one consistent request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n }\n/** @wildo_source:part:end saas.models.resource-config.operation-api-call */\n\nexport type ResourceConfiguration_Operation_ApiCallWithCallback<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.API_CALL_WITH_CALLBACK;\n httpVerb: HttpMethod;\n isApiKeyAccessDisabled: boolean; // default false\n isBulkOperation: boolean; // default false\n initiatorInfos?: ResourceConfiguration_Operation_UrlInitiatorInfo[];\n\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n tokenGeneration?: TokenGenerationConfig; // default undefined\n tokenRevocation?: TokenRevocationConfig; // default undefined\n /**\n * Resolved per-endpoint rate-limit policy. Same semantics as on the\n * API_CALL variant.\n *\n * The declaration is inherited by derived variants so every route for this\n * operation presents one consistent request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n callbackUrl?: string; // Optional callback URL for async operations\n callbackTimeout: number; // Timeout in seconds for callback operations, default 300 (5 minutes)\n callbackRetryAttempts: number; // Number of retry attempts for failed callbacks, default 3\n }\n\nexport type ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> =\n ResourceConfiguration_OperationVariant_ApiCall_BuilderParameters<TMainSchema> |\n ResourceConfiguration_OperationVariant_ApiCallWithCallback_BuilderParameters<TMainSchema> |\n ResourceConfiguration_OperationVariant_CronJob_BuilderParameters |\n ResourceConfiguration_OperationVariant_BatchJob_BuilderParameters |\n ResourceConfiguration_OperationVariant_InternalCall_BuilderParameters |\n ResourceConfiguration_OperationVariant_RepositoryOnly_BuilderParameters;\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-variant-for-key facet:layer:shared facet:family:resource-config\n *\n * Variant builder type that distributes operation-specific fields based on the operation key.\n *\n * Collection-view fields are intersected with the **full** variant union — not restricted to\n * API_CALL. The variant type (\"how is this triggered?\") and collection-view fields (\"how does\n * this operation query/present a collection?\") are orthogonal concerns. A REPOSITORY_ONLY LIST\n * still lists things and may need pageSize, filterFields, sortFields for internal queries.\n *\n * Only collection-style operations get operation-specific frontend/query\n * metadata here. Action-style behavior for custom operations is modeled\n * explicitly through custom operation identifiers plus variant/runtime config,\n * not through a special core operation.\n */\nexport type OperationVariant_ForKey<\n K,\n TMainSchema extends z.ZodTypeAny = z.ZodAny\n> =\n K extends CoreResourceOperation.LIST\n ? ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema> & CollectionViewFields_List\n : K extends CoreResourceOperation.SEARCH\n ? ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema> & CollectionViewFields_Search\n : K extends CoreResourceOperation\n ? ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n : ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n | (\n ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n & { resourceOperationLike: CoreResourceOperation.LIST }\n & CollectionViewFields_List\n )\n | (\n ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n & { resourceOperationLike: CoreResourceOperation.SEARCH }\n & CollectionViewFields_Search\n );\n\n/**\n * Operation config with variant type narrowed by operation key.\n * Distributes at the operation-config level; collection-view fields live on the variant.\n */\nexport type OperationConfig_ForKey<\n K,\n TMainSchema extends z.ZodTypeAny = z.ZodAny\n> = {\n variants: OperationVariant_ForKey<K, TMainSchema>[];\n serviceRuntimeMode?: ResourceOperation_ServiceRuntimeMode;\n parentResourceAccessScopeStrategyOverrides?: { resourceType: ResourceType, accessScopeStrategy: ResourceRelationshipAccessScopeStrategy }[];\n userNotifications?: UserNotificationDefinitionWithoutPrimaryScopeAndIdentifier[];\n notificationBadgeOperations?: { identifier: NotificationBadgeIdentifier, operation: NotificationBadgeOperation }[];\n m2mNotifications?: M2MNotificationDefinitionWithoutIdentifier[];\n};\n/** @wildo_source:part:end saas.models.resource-config.operation-variant-for-key */\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-union facet:layer:shared facet:family:resource-config\n *\n * Discriminated union of resolved operation configuration shapes (variant discriminant).\n */\nexport type ResourceConfiguration_Operation<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n> =\n ResourceConfiguration_Operation_ApiCall<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_ApiCallWithCallback<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_CronJob<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_BatchJob<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_InternalCall<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_RepositoryOnly<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>;\n/** @wildo_source:part:end saas.models.resource-config.operation-union */\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-path facet:layer:shared facet:family:resource-config\n *\n * Path key for routing: resource, operation, variant, optional variantKey / bulk.\n */\nexport type ResourceConfiguration_OperationPath<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined\n> = {\n resourceIdentifier: ResourceType;\n operationIdentifier: TCustomOperationEnum | TCoreOperationEnum;\n variantType : ResourceOperationVariantType\n isOperationDefault?: boolean;\n isBulkOperation?: boolean;\n variantKey?: string;\n\n}\n/** @wildo_source:part:end saas.models.resource-config.operation-path */\n\n\n\nexport type ResourceConfiguration_OperationRepositoryPath = {\n resourceIdentifier: ResourceType;\n operationIdentifier: CoreResourceOperation;\n /**\n * Actual configured operation identifier when a custom operation is routed\n * through repository logic using `resourceOperationLike`.\n *\n * This preserves action identity for repository lookups so multiple custom\n * operations that share the same effective core behavior do not collide.\n */\n operationRef?: string;\n isBulkOperation?: boolean;\n}\n\n// --------------------------------\n// DELETED — Frontend Resource Configuration (transferred to @wildo-ai/saas-frontend-lib)\n// See: ResourceUIBehavior_EmptyState, ResourceFrontend_GuidanceElement,\n// ResourceUIBehavior_GuidanceFlow, ResourceUIBehaviorConfig\n// --------------------------------\n\n/**\n * Helper type for individual inherited schema configuration.\n * NOTE: databaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in the schema and are stripped at the service layer.\n */\nexport type ResourceConfiguration_InheritanceItemSchemaDefinition<\n TDiscriminationEnum extends EnumLikeType,\n TMainSchema extends z.ZodTypeAny,\n TInheritedSchema extends TMainSchema = TMainSchema,\n> = {\n discriminatorFieldValue: string\n schema: TInheritedSchema,\n inheritenceSchemaDefinition? : ResourceConfiguration_InheritanceSchemaDefinition<EnumLikeType, TInheritedSchema>,\n}\n\nexport type ResourceConfiguration_InheritanceSchemaDefinition<\n TDiscriminationEnum extends EnumLikeType,\n TMainSchema extends z.ZodTypeAny,\n> = {\n discriminatorField : string,\n withoutInheritanceDiscriminatorFieldValues? : string[],\n inheritedSchemas : ResourceConfiguration_InheritanceItemSchemaDefinition<TDiscriminationEnum, TMainSchema>[],\n}\n\n\nexport type ResourceConfiguration_Operation_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = {\n variants: ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>[];\n serviceRuntimeMode?: ResourceOperation_ServiceRuntimeMode; // default ResourceOperation_ServiceRuntimeMode.INLINE\n parentResourceAccessScopeStrategyOverrides?: { resourceType: ResourceType, accessScopeStrategy: ResourceRelationshipAccessScopeStrategy }[]; // default undefined\n\n userNotifications?: UserNotificationDefinitionWithoutPrimaryScopeAndIdentifier[];\n notificationBadgeOperations?: { identifier : NotificationBadgeIdentifier, operation : NotificationBadgeOperation }[];\n m2mNotifications?: M2MNotificationDefinitionWithoutIdentifier[];\n}\n/**\n * @wildo_source:part:start saas.models.resource-config.builder-parameters facet:layer:shared facet:family:resource-config\n *\n * Valid operation keys from core + custom enums, and builder params for `createResourceConfiguration`.\n */\n// Helper type to extract valid operation keys, excluding undefined\nexport type ValidOperationKeys<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined\n> = (TCustomOperationEnum extends EnumLikeType ? EnumValues<TCustomOperationEnum> : never) |\n (TCoreOperationEnum extends Partial<CoreResourceOperation> ? TCoreOperationEnum : never);\n\n/**\n * @wildo_source:part:start saas.models.resource-config.system-access-policy facet:layer:shared facet:family:resource-config\n *\n * Access mode for the ACL-bypassing system-context reads\n * (`SystemAccessBackendService.readAsSystem` / `listAsSystem`, from\n * `@wildo-ai/saas-backend-lib`).\n *\n * Those primitives run with a synthesised super-admin execution context and\n * therefore SKIP the caller's scope and role checks entirely. A resource is\n * readable that way ONLY if its resource configuration opts the verb in. Any\n * verb left undeclared defaults to `FORBIDDEN`: a system read of a resource\n * that has not opted in throws `ErrorType.CONFIGURATION` — the framework is\n * fail-closed here by construction.\n */\nexport enum ResourceSystemAccessMode {\n /** The verb's system-context primitive is rejected for this resource (default). */\n FORBIDDEN = 'FORBIDDEN',\n /** The verb's system-context primitive is permitted for this resource. */\n ALLOWED = 'ALLOWED',\n}\n\n/**\n * Per-resource opt-in policy for the ACL-bypassing system-context primitives\n * on `SystemAccessBackendService`: the reads (`readAsSystem` / `listAsSystem`)\n * and the trusted writes (`createAsSystem` / `updateAsSystem` /\n * `updateManyAsSystem`).\n *\n * A system-context call does not REMOVE the authorization decision — it\n * RELOCATES it from the resource ACL onto the calling service, which MUST\n * enforce an explicit alternative check (e.g. consumable-token or\n * email-ownership matching). Declaring this policy is the data owner's\n * statement that such a bypass is permissible at all, and for which verb. An\n * absent policy — or a verb set to (or left defaulting to) `FORBIDDEN` — makes\n * the primitive throw.\n *\n * The WRITE verbs additionally let a trusted in-process caller persist the\n * fields the client-shaped request DTO strips — `.isBackendOnly()` material\n * (e.g. a credential `passwordHash`) and the context-auto-populated FKs (e.g.\n * `userId` on a system resource that has no HTTP surface to seed it) — by\n * re-attaching exactly those fields after the strict parse. They are the\n * sanctioned path for engine flows like registration that must write system\n * resources directly; opting a verb in is the data owner's acknowledgement that\n * a super-admin-context mutation of this resource is legitimate.\n *\n * Caveats a system-context call inherits (it runs as super-admin), to weigh\n * before opting in:\n * - a READ returns the FULL record — every field, including ones a normal\n * caller's role / field-visibility would hide — so never forward the result\n * verbatim to an under-privileged caller; project only what is safe.\n * - a WRITE bypasses the caller's scope + role ACL, so the calling service\n * owns the authorization decision (only trusted engine code can reach the\n * primitive — it is never exposed to an HTTP payload).\n * - feature flags do not gate it (it is an internal request).\n * - the policy is RESOURCE-LEVEL: for a polymorphic resource it is shared by\n * every scope variant (declare it once on the shared config).\n *\n * Intentionally DISTINCT from `isSystemResource`: marking a resource as\n * framework infrastructure does NOT imply that an ACL bypass is acceptable\n * for its data. Keep the two concepts separate.\n */\nexport type ResourceSystemAccessPolicy = {\n /** Governs `readAsSystem`. Omitted ⇒ FORBIDDEN. */\n read?: ResourceSystemAccessMode,\n /** Governs `listAsSystem`. Omitted ⇒ FORBIDDEN. */\n list?: ResourceSystemAccessMode,\n /** Governs `createAsSystem`. Omitted ⇒ FORBIDDEN. */\n create?: ResourceSystemAccessMode,\n /** Governs `updateAsSystem`. Omitted ⇒ FORBIDDEN. */\n update?: ResourceSystemAccessMode,\n /** Governs `updateManyAsSystem`. Omitted ⇒ FORBIDDEN. */\n updateMany?: ResourceSystemAccessMode,\n // NOTE: there is deliberately NO `delete` verb here — there is no `deleteAsSystem` primitive. A row is\n // removed either through its own DELETE operation ACL (e.g. an owner-facing `API_CALL` DELETE under the\n // USER_SELF owner filter) or by the `onParentDelete` composition cascade (which deletes at the raw\n // repository layer, variant-agnostic — see `deleteChildrenImmediate`). If you find yourself reaching\n // for `systemAccessPolicy.delete`, model the removal as one of those two instead.\n /**\n * Governs the impersonalization see-through reads (`readRetainedAsSystem` /\n * `listRetainedAsSystem`) — the privileged, audited path that reveals\n * `retentionStatus = RETAINED` rows hidden-by-default. Omitted ⇒ FORBIDDEN.\n * Auto-derived to `ALLOWED` by the factory when a resource opts into\n * `retentionPolicy`; never authored by hand in the common case.\n *\n * **LIVE.** `readRetainedAsSystem` / `listRetainedAsSystem` exist and are enforced: the verb is\n * asserted fail-closed, a privileged human role is required, and every reveal emits a\n * human-attributed `RETAINED_DATA_ACCESS` audit event. (This block previously said the primitives\n * were \"wired in a later phase\" — false since the M5b work landed.)\n */\n seeRetained?: ResourceSystemAccessMode,\n\n /**\n * May this resource's rows be swept into a SUBJECT EXPORT bundle (GDPR Art. 15 / Art. 20)?\n *\n * Fail-closed by omission, like every other verb here. A subject export reads across the whole\n * child-direction graph of its subject, so without an explicit opt-in a resource could be\n * disclosed to a data subject because it merely sits on an edge — nobody having decided that its\n * contents are appropriate to hand over.\n *\n * **The refusal is LOUD, not a silent skip.** When an edge the classifier marked for inclusion\n * belongs to a resource that has not opted in, the export REFUSES and names it. Silently dropping\n * it would produce an access bundle that is incomplete without saying so — a disclosure failure\n * wearing the appearance of a successful response, which is the same class of lie as the routed\n * export operations this capability replaced. Refusing is a one-line fix per resource; a silent\n * omission is undetectable.\n *\n * NOT derived from `portabilityPolicy`: declaring where fields came from (Art. 20 scoping) and\n * consenting to be disclosed at all are different decisions, and a resource may legitimately want\n * the first without the second.\n */\n exportSubject?: ResourceSystemAccessMode,\n};\n/** @wildo_source:part:end saas.models.resource-config.system-access-policy */\n\n/**\n * Resource-level opt-in to the **impersonalization-on-erasure** capability: on erasure the\n * resource is impersonalized-and-retained (personal fields scrubbed, row kept for a legal/audit\n * obligation) instead of hard-deleted. When set, the factory injects a `retentionStatus` marker\n * and derives the `updateMany` + `seeRetained` system-access grants.\n *\n * RESOURCE-level (intrinsic, trigger-agnostic). The per-EDGE trigger is\n * `onParentDelete.strategy = IMPERSONALIZE_AND_RETAIN`; the per-FIELD scrub is `.impersonalizeWith()`.\n *\n * **LIVE — opting in has real effect today.** The marker + derived grants, the in-place scrub writer,\n * the `IMPERSONALIZE_AND_RETAIN` cascade mode and the hidden-by-default read filter all exist and are\n * enforced. A RETAINED row is invisible to reads, lists, counts and CHART aggregates, and immutable\n * to ordinary writes, on BOTH persistence adapters; the only door is the audited see-through path.\n *\n * Runtime-proven, not asserted — and scoped precisely, because an earlier revision of this block\n * over-claimed it. Against a real MongoDB, the erasure orchestrator reaches its terminal path and its\n * receipt is truthful over a registered subset of resources\n * (`subject-erasure-completion.mongo.integration.test.ts`); the retention OUTCOME and a SECOND erasure\n * are observed (`subject-impersonalization-retention-outcome.mongo.integration.test.ts`); and a\n * retained row is absent from a real application chart over real HTTP\n * (Wonder Todos `e2e/chart-retention-hide.e2e.ts`).\n *\n * What is NOT proven: that an erasure completes over the FULL `onParentDelete` closure, and that\n * external (provider-side) effects run. The completion lane deliberately requires\n * `declinedEdges.length > 0`, so it asserts a state in which some edges were NOT executed, and\n * `externalEffects` is stubbed empty. Do not read \"runtime-proven\" here as end-to-end completeness.\n *\n * (This block previously read \"Contract only in this phase … No record is impersonalized-and-retained\n * yet.\" That was true when written and became false without being revisited — it is the public\n * authoring contract an app developer reads before declaring this policy, so it is corrected here\n * rather than left to mislead.)\n *\n * ⚠️ NOT the anonymous-user concept (`isAnonymizable` is pre-auth ownership transposition — the\n * opposite direction). Reuse of that mechanism, never its name.\n */\n/**\n * WHO a row of this resource is with respect to an erasure, and where that person's session lives.\n * The companion of {@link ResourceRetentionPolicy}: retention says what happens to the ROW and whether\n * the row holds personal data at all; this says **whose SESSION an erasure of it must end**.\n *\n * The two are independent, and reading this one as \"is the row about a person\" is the mistake that\n * cost `NO_PRINCIPAL` its previous name — see {@link ResourceDataSubjectKind}. A CRM contact holds\n * personal data (`RETAIN_AND_IMPERSONALIZE`) and has no session (`NO_PRINCIPAL`); an invoice holds\n * none (`RETAIN_ONLY`) and also has no session. Same value here, opposite compliance meaning, and the\n * retention mode is what tells them apart.\n *\n * Required on every resource declaring a {@link ResourceRetentionPolicy}, because those are exactly\n * the resources that can be passed to `eraseSubjectAsSystem` as a subject (the orchestrator refuses a\n * subject with no retention policy). Undeclared fails at config build rather than defaulting: the safe\n * default is unknowable — guessing `NO_PRINCIPAL` silently drops a real person's revocation, and\n * guessing a linked principal revokes the wrong people. See {@link ResourceDataSubjectKind} for why\n * the linkage can never be inferred from the presence of a user foreign key.\n */\nexport interface ResourceDataSubjectPolicy {\n /** Whose login session an erasure of this row must end — never whether the row is about a person. */\n kind: ResourceDataSubjectKind;\n /**\n * The field naming the `USERS` row whose session an erasure of this row must end.\n *\n * REQUIRED when {@link kind} is `LINKED_PRINCIPAL`, and FORBIDDEN otherwise — under\n * `SELF_PRINCIPAL` the row's own id is the principal, and under `NO_PRINCIPAL` there is no\n * principal at all, so a field there would be a contradiction rather than redundancy. Both\n * directions are enforced at config build.\n */\n sessionPrincipalField?: string;\n}\n\nexport interface ResourceRetentionPolicy {\n /**\n * WHAT happens to a row of this resource when its subject is erased.\n *\n * Both modes retain the row (kept, marked `RETAINED`, hidden from normal reads and immutable,\n * readable only through the audited see-through path). They differ on whether the row's own FIELDS\n * additionally need scrubbing — which is a genuinely separate question, because a record can be\n * legally retainable while holding no personal data of its own (a financial row that merely POINTS\n * at a person). Fusing the two forced such a resource to either invent a fake scrub or forgo\n * retention entirely; keeping them distinct is what makes \"retain, nothing to scrub\" expressible.\n */\n mode: ErasureRetentionMode;\n}\n\n/**\n * Resource-level DEFAULT for where this resource's field values came from — the axis that decides\n * GDPR Art. 20 (portability) scope. Per-field overrides are `.portabilityProvenance()`.\n *\n * ## Why a resource-level default exists at all\n *\n * Art. 20 is intrinsically a per-FIELD question: one row routinely mixes data the subject supplied\n * (`firstName`) with data the controller produced about them (a computed score, a server-assigned\n * status). But requiring a decorator on EVERY field of every resource would make the capability\n * unadoptable, and an unadopted disclosure capability is worse than a coarse one — it means\n * portability requests cannot be answered at all.\n *\n * So the declaration is two-level: this default carries the resource's common case in one line, and\n * only fields that DIFFER from it need a decorator. A `USER_PROFILES` resource is\n * `SUBJECT_PROVIDED` by default with a handful of derived exceptions; an `AUDIT_LOGS` resource is\n * `DERIVED` throughout with none.\n *\n * ## Why it is optional, and what happens without it\n *\n * Omitting it entirely is legitimate: **Art. 15 (access) never consults provenance**, so an\n * application can answer access requests in full before annotating anything. Only an Art. 20\n * portability export needs the axis, and for a resource where neither the field nor this default\n * declares one, that export REFUSES rather than guessing — see\n * `SubjectExportFieldScope.UNDECLARED` for why neither guess is the safe one.\n *\n * ⚠️ NOT `retentionPolicy`. That says what happens to a value on ERASURE (destroy vs retain-and-\n * scrub); this says where the value ORIGINATED, for a DISCLOSURE decision. A field is commonly\n * `SUBJECT_PROVIDED` here and masked on erasure there — orthogonal axes on the same value.\n */\nexport interface ResourcePortabilityPolicy {\n /**\n * The provenance every field of this resource is assumed to carry unless its own\n * `.portabilityProvenance()` says otherwise.\n */\n defaultProvenance: DataPortabilityProvenance;\n}\n\n/**\n * Builder parameters for creating a resource configuration.\n * NOTE: databaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in mainSchema and are stripped at the service layer.\n */\nexport type ResourceConfiguration_BuilderParameters<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny,\n> = {\n isDisabled?: boolean, // default false\n mainSchema: TMainSchema,\n resourceIdentifier : ResourceType,\n resourceFieldIdentifier : ResourceFieldIdentifier,\n resourceRelationships : ResourceRelationship[],\n inheritenceSchemaDefinition?: ResourceConfiguration_InheritanceSchemaDefinition<EnumLikeType,TMainSchema>,\n\n /**\n * Persistence adapter (database type) for this resource.\n * - `mongodb`: MongoDB with Mongoose ORM\n * - `postgresql`: PostgreSQL (SQL adapter)\n *\n * If not specified, uses `appConfig.database.defaultAdapter` — which is the normal case and\n * should stay the normal case.\n *\n * **Declaring a value that DISAGREES with the application default is refused at registration\n * unless {@link ResourceConfiguration_BuilderParameters.persistenceAdapterIsolation} is also\n * declared.** An application is wholly one adapter by default because two adapters cannot\n * share a native transaction; the isolation declaration is the only way past that refusal,\n * and it is a claim about this resource's write topology rather than a way to silence the\n * error. Declaring an adapter that AGREES with the default is always fine and needs nothing.\n */\n persistenceAdapter?: PersistenceAdapterType,\n\n /**\n * This resource's declared relationship to cross-adapter atomic write boundaries.\n *\n * Required — and only meaningful — when {@link\n * ResourceConfiguration_BuilderParameters.persistenceAdapter} disagrees with\n * `appConfig.database.defaultAdapter`. Without it, the resources registry refuses the\n * application at startup; with it, the registry admits the resource and LOGS the admitted\n * set, so a mixed configuration is always named and never ambient.\n *\n * Read {@link ResourcePersistenceAdapterIsolation} before declaring one: the member is an\n * assertion about the resource's design that the persistence unit of work will still enforce\n * at every real boundary.\n */\n persistenceAdapterIsolation?: ResourcePersistenceAdapterIsolationType,\n\n /**\n * REQUIRED companion declaration when `persistenceAdapter` is `HTTP_API`; forbidden otherwise.\n *\n * Carries everything the external repository needs that the platform does not own: provider\n * reference (credentials resolve through `ExternalProvidersRegistryBackendService`), transport\n * dialect, remote entity, ordered key declaration (id synthesis), identity mode, tenancy\n * stance, erasure stance, and remote-call semantics. The factory enforces presence/absence\n * coherence and content-validates the declaration.\n */\n httpApiBinding?: HttpApiResourceBinding,\n /** REQUIRED when `persistenceAdapter` is INTROSPECTION; meaningless otherwise. */\n introspectionBinding?: IntrospectionResourceBinding,\n\n /**\n * OPTIONAL inbound ETL pipeline on an ordinary LOCAL resource — \"some resources local, some\n * remote, ETL between them\" (partial-resource coverage, model v3; PR-3). FORBIDDEN on an\n * HTTP_API-adapter resource: a virtual resource IS the remote side. The factory\n * content-validates the declaration and every mainSchema-dependent coherence rule (the\n * remote-key field is a declared `.isBusinessKey()` + excludeFromCreate/Update STRING; mapped\n * targets exist, accept null unless `required`, and are excludeFromCreate/Update; CLOSED\n * population refuses caller CREATE operations).\n */\n externalDataPipeline?: ExternalDataPipelineDeclaration,\n\n isSystemResource?: boolean, // default false - true for core system resources (organizations, users, etc.), false for tenant-specific resources\n\n /**\n * Does this resource's OWN primary-key URL segment name the CALLER, or the SUBJECT?\n *\n * Default `true` (the segment names the caller) — deliberately the SAFE default, so a resource\n * whose author has not considered the question keeps the identity-coherence guard rather than\n * silently losing it.\n *\n * ## Why this cannot be inferred\n *\n * The W3.6 identity-coherence guard (`handleUserJwtAuthentication` →\n * `AUTHORIZATION_USER_ID_MISMATCH`) refuses a request whose URL `userId` differs from the JWT\n * subject. That is exactly right for `USER_SELF` (`/user-self/{userId}` — the segment IS the\n * caller, pinned by the horizontal-IDOR probes) and for child routes under a user\n * (`/users/{userId}/draft-notes` — a PARENT segment naming whose rows these are).\n *\n * It is exactly WRONG for `USERS`, whose own `resourceFieldIdentifier` is also `userId` but whose\n * segment names the TARGET of an administrative action. Both resources are keyed on the same\n * field name, so no structural rule can tell them apart — the framework already had to add\n * `coreResourceShared_FieldIdentifierPriority` to disambiguate the very same collision for FK\n * resolution. This declaration is that disambiguation for IDENTITY.\n *\n * ## What it does and does not change\n *\n * Setting `false` relaxes ONLY the coherence refusal. It does not touch:\n * - `initiatorIds.userId`, which is taken from the JWT and spread LAST precisely so a URL can\n * never spoof the caller (see the `W3.6` comment at that assignment) — so permitting the\n * mismatch cannot produce impersonation;\n * - the contextual-field map, which legitimately carries the ADDRESSED resource's id;\n * - authorization, which still decides whether this caller may run this operation at all.\n *\n * Set `false` only for an administrative resource whose primary key is a person/tenant identifier\n * that a privileged caller is MEANT to address on someone else's behalf.\n */\n primaryKeyIdentifiesInitiator?: boolean,\n\n /**\n * Opt-in policy for the ACL-bypassing system-context primitives\n * (`readAsSystem` / `listAsSystem` and the trusted writes `createAsSystem` /\n * `updateAsSystem` / `updateManyAsSystem`). Omitted ⇒ EVERY verb FORBIDDEN\n * (fail-closed): a system-context call on this resource throws unless the\n * matching verb is explicitly set to ALLOWED. Declare ONLY the verb(s) that\n * genuinely need a system bypass, and ensure the calling service enforces an\n * explicit alternative authorization.\n */\n systemAccessPolicy?: ResourceSystemAccessPolicy,\n coreOperations: TCoreOperationEnum[],\n customOperation?: TCustomOperationEnum, // default undefined\n operationsConfiguration : { [K in ValidOperationKeys<TCustomOperationEnum, TCoreOperationEnum>]: OperationConfig_ForKey<K, TMainSchema> }\n /**\n * Configuration for auto-generated operation variants.\n * Controls which READ/LIST variants (summary, context) are auto-generated.\n * All variants are enabled by default.\n */\n autoVariants?: ResourceConfiguration_AutoVariants,\n // NOTE: responseSummaryDto removed - now auto-derived from isSummaryField() decorators in factory\n\n /**\n * W7.3a: Marks this resource as accessible to anonymous sessions.\n * When true, the factory auto-generates dual URL paths (USER_SELF + ANONYMOUS)\n * and a STANDALONE relationship to ANONYMOUS_USERS.\n * The resource MUST have a nullable `userId` field.\n */\n isAnonymizable?: boolean,\n\n /**\n * W7.3a: Controls how anonymous resources are handled during transposition.\n * Only meaningful when isAnonymizable is true.\n * Defaults to ADD (simple ownership reassignment).\n */\n transpositionPolicy?: ResourceTranspositionPolicy,\n\n /**\n * Opt in to impersonalization-on-erasure (retain + scrub instead of hard-delete). When set, the\n * factory injects a `retentionStatus` marker and derives the `updateMany` / `seeRetained`\n * system-access grants. Per-field scrub is `.impersonalizeWith()`; the per-edge trigger is the\n * `IMPERSONALIZE_AND_RETAIN` cascade mode.\n *\n * **LIVE.** Marker injection, grant derivation, the scrub writer, the cascade mode and the\n * hidden-by-default read filter are all enforced today — see `ResourceRetentionPolicy` for the\n * runtime evidence. Declaring this changes behaviour; it is not a forward declaration.\n *\n * Declaring this also REQUIRES {@link dataSubjectPolicy}: a retention-governed resource is exactly\n * one that can be handed to `eraseSubjectAsSystem` as a subject, so it must say whether its rows are\n * a person — and if so, whose session an erasure ends.\n */\n retentionPolicy?: ResourceRetentionPolicy,\n\n /**\n * WHO a row of this resource is with respect to an erasure — and, when it is a person, which field\n * names the `USERS` row whose session that erasure must end.\n *\n * Mandatory for any resource declaring a {@link retentionPolicy}; meaningless (and rejected) without\n * one, since a resource with no retention policy can never be an erasure subject. See\n * {@link ResourceDataSubjectPolicy}.\n */\n dataSubjectPolicy?: ResourceDataSubjectPolicy,\n\n /**\n * Resource-level DEFAULT provenance for GDPR Art. 20 (portability) scoping; per-field overrides\n * are `.portabilityProvenance()`. Optional — Art. 15 (access) never consults provenance, so an\n * application can answer access requests without declaring this. See\n * {@link ResourcePortabilityPolicy}.\n */\n portabilityPolicy?: ResourcePortabilityPolicy,\n}\n/** @wildo_source:part:end saas.models.resource-config.builder-parameters */\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.resource-relationships-buckets facet:layer:shared facet:family:resource-config\n *\n * Categorized resource relationships produced by `transformResourceRelationships()`.\n *\n * All relationships — including conditional ones (those with `discriminator` or `condition`) —\n * are stored flat in these three buckets. There is no separate `inheritanceSchemaRelationships`\n * bucket; that structure was removed because it was populated but never consumed at runtime,\n * making the conditional relationship system non-functional.\n *\n * The flat design means:\n * - **Factory/startup**: All relationships are categorized structurally (by resourceType\n * and cardinality), regardless of whether they have conditions.\n * - **Runtime**: Consumers (ResourceContext, ServicesRegistryHandler, ContainerNested)\n * evaluate `discriminator`/`condition` inline when actual parent data is available.\n *\n * M:N relationships are decomposed into synthetic 1:N relationships and placed into the\n * appropriate bucket. Synthetic relationships for entity→junction carry the original\n * `discriminator` and `condition` from the M:N declaration.\n *\n * @see ResourceRelationship — for the full type including optional `condition` function.\n * @see transformResourceRelationships — in resources-config.shared.factory.ts for categorization logic.\n */\nexport type ResourceConfiguration_ResourceRelationships = {\n parentRelationships: ResourceRelationship[],\n childRelationships: ResourceRelationship[],\n selfRelationships: ResourceRelationship[],\n}\n/** @wildo_source:part:end saas.models.resource-config.resource-relationships-buckets */\n\n/**\n * Returns all relationships where this resource acts as a child (has a parent).\n * Merges `childRelationships` with `selfRelationships` — self-refs are both\n * parent and child of themselves, so they must be included when iterating\n * \"what children does this resource have?\"\n */\nexport function getChildDirectionRelationships(\n rels: ResourceConfiguration_ResourceRelationships\n): ResourceRelationship[] {\n return [...rels.childRelationships, ...rels.selfRelationships];\n}\n\n/**\n * Returns all relationships where this resource acts as a parent (provides context).\n * Merges `parentRelationships` with `selfRelationships` — self-refs need to be\n * treated as parents when resolving FK fields, scope requirements, and URL paths.\n */\nexport function getParentDirectionRelationships(\n rels: ResourceConfiguration_ResourceRelationships\n): ResourceRelationship[] {\n return [...rels.parentRelationships, ...rels.selfRelationships];\n}\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.resource-configuration facet:layer:shared facet:family:resource-config\n *\n * Full resource configuration type.\n * NOTE: databaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in mainSchema and are stripped at the service layer.\n */\nexport type ResourceConfiguration<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny,\n> = {\n isDisabled: boolean, // default false\n\n resourcePrimaryScope : ResourcePrimaryScope, // default ResourcePrimaryScope.ORGANIZATIONS\n resourceIdentifier : ResourceType,\n resourceFieldIdentifier : ResourceFieldIdentifier,\n coreOperations: TCoreOperationEnum[],\n customOperation?: TCustomOperationEnum, // default undefined\n mainSchema: TMainSchema,\n resourceRelationships : ResourceConfiguration_ResourceRelationships,\n inheritenceSchemaDefinition?: ResourceConfiguration_InheritanceSchemaDefinition<EnumLikeType,TMainSchema>,\n\n /**\n * Persistence adapter (database type) for this resource.\n * - `mongodb`: MongoDB with Mongoose ORM\n * - `postgresql`: PostgreSQL (SQL adapter)\n *\n * Resolved at runtime from:\n * 1. This field (if specified)\n * 2. `appConfig.database.defaultAdapter` (fallback)\n *\n * A value disagreeing with the application default is admitted at registration ONLY when\n * {@link ResourceConfiguration.persistenceAdapterIsolation} is declared beside it.\n */\n persistenceAdapter?: PersistenceAdapterType,\n\n /**\n * This resource's declared relationship to cross-adapter atomic write boundaries; see the\n * authoring-side declaration and {@link ResourcePersistenceAdapterIsolation} for the full\n * rationale.\n *\n * Carried through to the resolved config UNMATERIALISED (no default is substituted) because\n * absence is semantically load-bearing here: the registry's admission asks whether the author\n * MADE the claim, and a materialised default would answer that question for them.\n */\n persistenceAdapterIsolation?: ResourcePersistenceAdapterIsolationType,\n\n /**\n * The external HTTP-API binding — present exactly when `persistenceAdapter` is `HTTP_API`\n * (factory-enforced both ways). See the authoring-side declaration for the full contract.\n */\n httpApiBinding?: HttpApiResourceBinding,\n /** REQUIRED when `persistenceAdapter` is INTROSPECTION; meaningless otherwise. */\n introspectionBinding?: IntrospectionResourceBinding,\n\n /**\n * The inbound ETL pipeline, when this LOCAL resource declared one — parsed + coherence-checked\n * by the factory (see the authoring-side declaration for the full contract).\n */\n externalDataPipeline?: ExternalDataPipelineDeclaration,\n\n isSystemResource: boolean, // default false - true for core system resources (organizations, users, etc.), false for tenant-specific resources\n\n /**\n * Whether this resource's own primary-key URL segment names the CALLER (see the authoring-side\n * declaration for the full rationale).\n *\n * Materialised by the factory to a concrete boolean, defaulting to `true`, so the consuming guard\n * never has to distinguish \"declared true\" from \"not declared\" — an un-migrated or hand-built\n * config keeps the identity-coherence refusal.\n */\n primaryKeyIdentifiesInitiator: boolean,\n\n /**\n * Opt-in policy gating the ACL-bypassing `readAsSystem` / `listAsSystem`\n * primitives for this resource. Optional on the resolved config so that\n * non-factory-built configs (mocks/tests) need not declare it; the factory\n * always materialises a concrete value (defaulting both verbs to FORBIDDEN\n * when unauthored). The system-read primitives treat an absent policy — or\n * any verb not explicitly ALLOWED — as forbidden and throw.\n */\n systemAccessPolicy?: ResourceSystemAccessPolicy,\n\n /**\n * W7.3a: Marks this resource as accessible to anonymous sessions.\n * When true, the engine generates dual URL paths and a STANDALONE relationship to ANONYMOUS_USERS.\n */\n isAnonymizable: boolean,\n\n /**\n * W7.3a: Transposition policy for anonymous→authenticated ownership transfer.\n * Only meaningful when isAnonymizable is true. Defaults to ADD.\n */\n transpositionPolicy: ResourceTranspositionPolicy,\n\n /** Resolved impersonalization opt-in (undefined when the resource did not opt into `retentionPolicy`). */\n retentionPolicy?: ResourceRetentionPolicy,\n\n /**\n * Resolved data-subject declaration. Present exactly when `retentionPolicy` is (the factory refuses\n * a retention-governed resource that declares no `dataSubjectPolicy`, and refuses a\n * `dataSubjectPolicy` on a resource with no `retentionPolicy`), so a consumer that has one may read\n * the other without a second existence check.\n */\n dataSubjectPolicy?: ResourceDataSubjectPolicy,\n\n /** Resolved portability default (undefined when the resource declared no `portabilityPolicy` — an\n * Art. 15 export is unaffected, an Art. 20 export refuses on any field that also lacks its own\n * `.portabilityProvenance()`). */\n portabilityPolicy?: ResourcePortabilityPolicy,\n\n operations : ResourceConfiguration_Operation<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>[]\n}\n/** @wildo_source:part:end saas.models.resource-config.resource-configuration */\n\nexport type ResourceConfiguration_InitializationFactory<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny,\n> = (resourcesRelationships: ResourceRelationship[]) => ResourceConfiguration<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>;\n\n\n/**\n * The factory map accepted by the RUNTIME REGISTRY initializers\n * (`ResourcesRegistryBackendService.initialize`, `ResourceRegistry_InitializerParameters`).\n *\n * ⚠️ The leading `TResourceType extends undefined` conditional is DISTRIBUTIVE, and it must stay\n * that way. A registry initializer takes this map at a position whose type argument is INFERRED\n * from the argument, and inference only reaches the mapped-type branch through that distributive\n * chain. Prefixing the chain with a non-distributive guard (`[TResourceType] extends [never] ? …`)\n * stops the inference dead: every such parameter falls back to its `= undefined` default, so a\n * perfectly good map is reported as `not assignable to parameter of type 'undefined'`. That\n * shipped on 2026-08-26 and failed the compile of BOTH `platform-apps-manager` and\n * `platform-crontabs-batches-manager` — the only two runtimes that pass a typed custom map\n * positionally — while every other consumer stayed green because it passes `undefined` or a value\n * already widened by a cast.\n *\n * The consequence of the distribution is that a union `TResourceType` yields a UNION of\n * single-key maps rather than one complete map. That is weaker than it looks, and it is why a\n * module's OWN map uses {@link ResourceConfiguration_ModuleInitializationFactoryMap} instead —\n * including for the empty-module case (`never`), which this type deliberately does not model:\n * distributing over `never` yields `never`, and no ordering of distributive branches can change\n * that.\n */\nexport type ResourceConfiguration_InitializationFactoryMap<TResourceType extends ResourceType | undefined = undefined> =\n TResourceType extends undefined\n ? undefined\n : [TResourceType] extends [undefined]\n ? undefined\n : TResourceType extends ResourceType\n ? { [key in TResourceType]: ResourceConfiguration_InitializationFactory<any, any, any> }\n : { [key : string]: ResourceConfiguration_InitializationFactory<any, any, any> }\n\n/**\n * The factory map a MODULE declares for its OWN resources: exactly one entry per member of that\n * module's resource-type enum — no missing entry, no key the enum does not declare.\n *\n * This is a plain mapped type, with no conditional in front of it, and both of its properties\n * follow from that:\n *\n * - a union of enum members produces ONE complete map (not the union of single-key maps that\n * {@link ResourceConfiguration_InitializationFactoryMap} produces), which is the truthful\n * contract for a module: it owns all of its resources;\n * - a FRESHLY SCAFFOLDED module, whose resource enum has no members yet, produces the empty map\n * `{}`. A memberless enum is numeric by default, so the scaffold writes\n * `<Module>_ResourceType & string`, which is `never` until the first string member arrives —\n * and a mapped type over `never` is `{}`, so the empty declaration type-checks without any\n * `never` special case.\n *\n * The value is assignable to every consumer that takes the registry-facing map, so a module can be\n * merged into an application registry unchanged.\n */\nexport type ResourceConfiguration_ModuleInitializationFactoryMap<TResourceType extends ResourceType> =\n { [key in TResourceType]: ResourceConfiguration_InitializationFactory<any, any, any> }\n\n\nexport type AnyResourceConfiguration = ResourceConfiguration<\n any, // TCustomOperationEnum - allow any custom operations\n any, // TCoreOperationEnum - allow any subset of core operations\n any // TMainSchema\n>;\n\n/**\n * @wildo_source:part:start saas.models.resource-config.resources-registry facet:layer:shared facet:family:resource-config\n *\n * Aggregated registry passed to app bootstrap (relationships + per-resource configs + FK maps).\n */\nexport type ResourcesRegistry = {\n notificationBadgeDefinitions: NotificationBadgeDefinition[];\n additionalUserNotificationDefinitions: UserNotificationDefinition[];\n resourceRelationships: ResourceRelationship[];\n resourceConfigurations: {\n [key: ResourceType]: AnyResourceConfiguration ;\n };\n /**\n * The resource types the APPLICATION registered — its own, never the engine's.\n *\n * The registry composes app and core factory maps into one `resourceConfigurations`, which is what\n * every runtime consumer wants. This is the one place the two are still distinguishable, so the\n * split is recorded HERE rather than re-derived by each consumer from a core key set — which would\n * be a second enumeration of a population this file already knows.\n *\n * Its consumer today is the personal-data declaration report, which asks a question only an\n * application author can act on: the engine declares its own posture on ~85 resources, and putting\n * those in front of a reader of a report about THEIR application is how a diagnostic channel stops\n * being read.\n */\n applicationResourceTypes: ResourceType[];\n /**\n * Map of ResourceType to its standard field identifier (e.g., USERS -> userId).\n * Used for FK field resolution without string manipulation.\n */\n allResourceFieldIdentifiers: Record<ResourceType, string>;\n /**\n * Reverse map of field identifier to ResourceType (e.g., userId -> USERS).\n * Used for quick lookup of resource type from a FK field name.\n */\n fieldIdentifierToResourceType: Record<string, ResourceType>;\n /**\n * Scope-required reference-search junctions, keyed by `${target}|${scope}`.\n * When a foreign key targets `T` from a resource whose primary scope is `S`,\n * and `T ↔ S` is a MANY-to-MANY relationship via junction `J`, a reference\n * people-picker searches `T` scoped to the caller's current `S` through `J`\n * (rather than enumerating every `T`). Derived from the registered M:N\n * relationships at registry init.\n */\n manyToManyScopeJunctions: ManyToManyScopeJunctionMap;\n /**\n * Resources narrowed by ORGANIZATION UNIT, keyed by resource type — the engine's opt-ins merged\n * with the application's, validated at boot against the authorization-plane containment rule.\n *\n * Opting a resource in means: a principal whose authority for it comes ONLY from a unit grant is\n * admitted by the operation gate and then confined to the rows of those units. Narrowing is\n * ADDITIVE — an organization-wide grant that already satisfies the operation is never narrowed —\n * so opting in can only widen who can reach the resource, never shrink an existing result set.\n *\n * Empty for an application that declares none, which is the default: narrowing is never inferred\n * from the presence of a unit field. See `OrganizationUnitNarrowingDeclaration`.\n */\n organizationUnitNarrowing: Readonly<Partial<Record<ResourceType, OrganizationUnitNarrowingDeclaration>>>;\n}\n/** @wildo_source:part:end saas.models.resource-config.resources-registry */\n\nexport type ResourceRegistry_InitializerParameters<TResourceType extends ResourceType | undefined = undefined> =\n [TResourceType] extends [undefined]\n ? {\n resourceRelationships: ResourceRelationship[],\n resourceConfigurationsFactoryMap: ResourceConfiguration_InitializationFactoryMap<undefined>,\n resourceFieldIdentifiers: undefined,\n }\n : TResourceType extends ResourceType\n ? {\n resourceRelationships: ResourceRelationship[],\n resourceConfigurationsFactoryMap: ResourceConfiguration_InitializationFactoryMap<TResourceType>,\n resourceFieldIdentifiers: { [key in TResourceType]: string },\n }\n : {\n resourceRelationships: ResourceRelationship[],\n resourceConfigurationsFactoryMap: ResourceConfiguration_InitializationFactoryMap<ResourceType>,\n resourceFieldIdentifiers: { } | undefined,\n }\n\n\n\n\n\n// --------------------------------\n"]}
1
+ {"version":3,"file":"resources-config.shared.schemas.js","sourceRoot":"","sources":["../../../../src/resources/resources-config.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuCxB,mCAAmC;AACnC,gCAAgC;AAChC,mCAAmC;AAEnC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sCAAsC,CAAC,WAAmB,GAAG;IAM3E,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAClC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,+HAA+H;AAC/H,MAAM,CAAC,MAAM,0CAA0C,GAAG,sCAAsC,EAAE,CAAC;AAInG;;;;;;;;;;GAUG;AACH,MAAM,UAAU,6BAA6B,CAAC,WAAmB,GAAG;IAIlE,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAClC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KACnD,CAAC,CAAC;AACL,CAAC;AAED,iIAAiI;AACjI,MAAM,CAAC,MAAM,iCAAiC,GAAG,6BAA6B,EAAE,CAAC;AAiBjF,mCAAmC;AACnC,uCAAuC;AACvC,mCAAmC;AAEnC;;;;;;;;;;;GAWG;AACH,MAAM,CAAN,IAAY,QASX;AATD,WAAY,QAAQ;IAClB,uCAAuC;IACvC,uBAAW,CAAA;IAEX,kFAAkF;IAClF,+BAAmB,CAAA;IAEnB,kDAAkD;IAClD,+BAAmB,CAAA;AACrB,CAAC,EATW,QAAQ,KAAR,QAAQ,QASnB;AAED,mCAAmC;AACnC,sCAAsC;AACtC,mCAAmC;AAEnC;;;;;;;;;;GAUG;AACH;;;;;;;;;;GAUG;AACH,MAAM,CAAN,IAAY,0BA8CX;AA9CD,WAAY,0BAA0B;IACpC;;;;;;;;;;;OAWG;IACH,6EAA+C,CAAA;IAE/C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,yEAA2C,CAAA;IAE3C;;;;;;;OAOG;IACH,6EAA+C,CAAA;AACjD,CAAC,EA9CW,0BAA0B,KAA1B,0BAA0B,QA8CrC;AAqED,MAAM,CAAN,IAAY,kBA8EX;AA9ED,WAAY,kBAAkB;IAC5B,yCAAmB,CAAA;IACnB,+CAAyB,CAAA;IAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACH,qDAA+B,CAAA;IAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,2CAAqB,CAAA;AACvB,CAAC,EA9EW,kBAAkB,KAAlB,kBAAkB,QA8E7B;AAWD,MAAM,0BAA0B,GAAsB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAEvG;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAc;IAEd,OAAO,OAAO,KAAK,KAAK,QAAQ;WAC3B,0BAA0B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GACtC,kBAAkB,CAAC,OAAO,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAN,IAAY,mCAeX;AAfD,WAAY,mCAAmC;IAC7C;;;;;;;;;;;;OAYG;IACH,oGAA6D,CAAA;AAC/D,CAAC,EAfW,mCAAmC,KAAnC,mCAAmC,QAe9C;AAeD,MAAM,6CAA6C,GACjD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,UAAU,qCAAqC,CACnD,KAAc;IAEd,OAAO,OAAO,KAAK,KAAK,QAAQ;WAC3B,6CAA6C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACV,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAkB;IACrD,IAAI,QAAQ,KAAK,QAAQ,CAAC,GAAG,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AA2DD,mCAAmC;AACnC,gCAAgC;AAChC,mCAAmC;AAEnC,sCAAsC;AACtC,MAAM,CAAN,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,wBAAW,CAAA;IACX,0BAAa,CAAA;AACf,CAAC,EAHW,SAAS,KAAT,SAAS,QAGpB;AACD,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC,MAAM,CAAC;IACvD,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,SAAS,EAAE,gCAAgC,CAAC,QAAQ,EAAE;IACtD,SAAS,EAAE,gCAAgC,CAAC,QAAQ,EAAE;CACvD,CAAC,CAAC;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACpC,SAAS,EAAE,gCAAgC,CAAC,QAAQ,EAAE;IACtD,SAAS,EAAE,gCAAgC,CAAC,QAAQ,EAAE;CACvD,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,kCAAkC;AAEnE,+BAA+B;AAC/B,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CACvC,CAAC,CAAC;AAEH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CACpD,CAAC,CAAC,MAAM,EAAE,EACV,4BAA4B,CAAC,QAAQ,EAAE,CACxC,CAAC,QAAQ,EAAE,CAAC;AAEb,sCAAsC;AACtC,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,CAAC,MAAM,CAAC;IACzD,UAAU,EAAE,iCAAiC,CAAC,QAAQ,EAAE;IACxD,OAAO,EAAE,8BAA8B,CAAC,QAAQ,EAAE;IAClD,OAAO,EAAE,6BAA6B,CAAC,QAAQ,EAAE;CAClD,CAAC,CAAC;AAiCH;;;;;;;;;;;;GAYG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAN,IAAY,8BAWX;AAXD,WAAY,8BAA8B;IACxC;;;OAGG;IACH,6CAAW,CAAA;IACX;;;OAGG;IACH,+CAAa,CAAA;AACf,CAAC,EAXW,8BAA8B,KAA9B,8BAA8B,QAWzC;AAED;;;;;;GAMG;AACH,MAAM,CAAN,IAAY,gCAcX;AAdD,WAAY,gCAAgC;IAC1C;;;OAGG;IACH,iEAA6B,CAAA;IAC7B;;;;;;OAMG;IACH,uEAAmC,CAAA;AACrC,CAAC,EAdW,gCAAgC,KAAhC,gCAAgC,QAc3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7D,mEAAmE;IACnE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB,gEAAgE;IAChE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,gFAAgF;IAChF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;CACzC,CAAC,CAAC;AAmJH,mFAAmF;AACnF,MAAM,UAAU,8BAA8B,CAC5C,KAAgC;IAEhC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAkBD,mCAAmC;AACnC,iCAAiC;AACjC,mCAAmC;AAEnC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CACxC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAe,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAgB;IAC3D,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAI,OAAmC,CAAC,6BAA6B,CAAC,CAAC;IAChF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,SAAS,EAAuB,EAAE,CAAC,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClH,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B,CAAI,OAAU;IACtD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,CAAC,CAAC,6BAA6B,IAAK,OAAmC,CAAC,EAAE,CAAC;QAC7E,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,EAAE,CAAC,6BAA6B,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,OAAkC,CAAC;IACnG,OAAO,IAAS,CAAC;AACnB,CAAC;AAmGD,MAAM,CAAN,IAAY,8BAIX;AAJD,WAAY,8BAA8B;IACxC,yFAAuD,CAAA;IACvD,6GAA6G;IAC7G,6FAA2D,CAAA;AAC7D,CAAC,EAJW,8BAA8B,KAA9B,8BAA8B,QAIzC;AA2ED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAN,IAAY,uCAOX;AAPD,WAAY,uCAAuC;IACjD,6HAA6H;IAC7H,sEAA2B,CAAA;IAC3B,oFAAoF;IACpF,0EAA+B,CAAA;IAC/B,iGAAiG;IACjG,4EAAiC,CAAA;AACnC,CAAC,EAPW,uCAAuC,KAAvC,uCAAuC,QAOlD;AAED;;;GAGG;AACH,MAAM,CAAN,IAAY,oCAOX;AAPD,WAAY,oCAAoC;IAC9C,yGAAyG;IACzG,uGAA+D,CAAA;IAC/D,0HAA0H;IAC1H,yGAAiE,CAAA;IACjE,gGAAgG;IAChG,6HAAqF,CAAA;AACvF,CAAC,EAPW,oCAAoC,KAApC,oCAAoC,QAO/C;AAED;;;;;;;;GAQG;AACH,MAAM,CAAN,IAAY,uCAKX;AALD,WAAY,uCAAuC;IACjD,yFAAyF;IACzF,gEAAqB,CAAA;IACrB,sFAAsF;IACtF,kHAAuE,CAAA;AACzE,CAAC,EALW,uCAAuC,KAAvC,uCAAuC,QAKlD;AAGD;;;;GAIG;AACH,MAAM,CAAC,MAAM,0CAA0C,GACrD,8DAA8D,CAAC;AAEjE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,uCAAuC,GAAG,CACrD,MAAc,EACkC,EAAE,CAAC,CAAC;IACpD,MAAM;IACN,WAAW,EAAE,0CAA0C;CACxD,CAAC,CAAC;AAo4BH;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAN,IAAY,wBAKX;AALD,WAAY,wBAAwB;IAClC,mFAAmF;IACnF,mDAAuB,CAAA;IACvB,0EAA0E;IAC1E,+CAAmB,CAAA;AACrB,CAAC,EALW,wBAAwB,KAAxB,wBAAwB,QAKnC;AAwaD,wFAAwF;AAExF;;;;;GAKG;AACH,MAAM,UAAU,8BAA8B,CAC5C,IAAiD;IAEjD,OAAO,CAAC,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,+BAA+B,CAC7C,IAAiD;IAEjD,OAAO,CAAC,GAAG,IAAI,CAAC,mBAAmB,EAAE,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;AAClE,CAAC;AA8QD,mCAAmC","sourcesContent":["/**\n * Operations Shared Schemas\n *\n * Reusable operation schemas for bulk actions and common operations\n */\n\nimport { z } from 'zod';\nimport { EnumLikeType, EnumValues, PrimitiveZodType } from '@wildo-ai/zod-decorators';\nimport { ErasureRetentionMode, ResourceDataSubjectKind } from '../compliance/privacy/impersonalization.shared.schemas';\nimport type { DataPortabilityProvenance } from '../compliance/privacy/subject-export.shared.schemas';\n/*\n * entrypoint-closure-waiver: this root-reachable module imports the external-data AUTHORING\n * contract, which only `@wildo-ai/saas-models/external-data` publishes (see\n * `external-data.exports.ts` for why that subpath exists).\n *\n * MEASURED SAFE 2026-08-29 by the probe `package-public-entrypoints.md` prescribes — a consumer\n * importing ONLY the package root, re-exporting these surfaces and deriving their parameter types\n * so declaration emit must WRITE them rather than alias them, compiled with `declaration: true`:\n * zero TS2742 / TS2883 / TS7056, and the emitted `.d.ts` names `@wildo-ai/saas-models` with no\n * `node_modules/...` path anywhere in it.\n *\n * The mechanism, so the next reader need not re-derive it: every crossing here is an EXPLICIT\n * ANNOTATION inside this package, so TypeScript emits a RELATIVE intra-package import in our own\n * `.d.ts` — which resolves by PATH for any consumer, whatever the exports map says. TS2742 bites\n * only when TypeScript must INVENT a specifier for an INFERRED type. Re-run the probe if a\n * root-exported contract here ever starts inferring one.\n */\nimport type { HttpApiResourceBinding, HttpApiResourceBindingAuthored } from '../http-api-binding/http-api-binding.shared.schemas';\nimport type { ExternalDataPipelineDeclaration, ExternalDataPipelineDeclarationAuthored } from '../external-data/external-data-pipeline.shared.schemas';\nimport { HttpMethod } from '../requests/http-requests.shared';\nimport { Roles } from '../security/authorizations/roles.shared.schemas';\nimport { TokenAuthenticationConfig, TokenGenerationConfig, TokenRevocationConfig } from '../security/authentications/consumable-token.shared.schemas';\nimport type { ConsumableTokenTypes } from '../security/authentications/consumable-token.shared.schemas';\nimport { ResourceOperation_ServiceRuntimeMode, ResourceOperationVariantType, ResourceOperationRiskLevel, ResourceOperationFrontendSurface, ResourceParentResourceRequirement, ResourcePrimaryScope, ResourceTranspositionPolicy, ResourceOperation_CustomServiceImplementationMode, ResourceRelationship, ResourceRelationshipAccessScopeStrategy, ManyToManyScopeJunctionMap, type ReferenceConstraints } from './resources.shared.schemas';\nimport { NotificationBadgeIdentifier, NotificationBadgeOperation } from '../notifications/notification-badges.schemas';\nimport { M2MNotificationDefinition, UserNotificationDefinition } from '../notifications/notifications.shared.schemas';\nimport { NotificationBadgeDefinition } from '../notifications/notification-badges.schemas';\nimport { CoreResourceOperation, ResourceFieldIdentifier, ResourceType } from './resources.shared.core.definitions';\nimport type { OrganizationUnitNarrowingDeclaration } from '../organizations/organization-unit-narrowing.shared';\nimport { LimitCountingConfig } from '../features/feature-definition.shared.schemas';\nimport { MainSchemaInfer, SchemaArrayInfer } from './utils/schema-type-inference.utils';\nimport type { RateLimitConfiguration } from '../requests/rate-limits.shared.schemas';\n\n\n\n// --------------------------------\n// Resources Pagination Response\n// --------------------------------\n\n/**\n * The pagination block a collection RESPONSE echoes back, bounded by the operation's ceiling.\n *\n * `limit` here reports the page size that was actually SERVED, so its bound has to be at least as\n * permissive as the one the repository is allowed to serve. It was a hard-coded `.max(100)`, which\n * made it the LAST of five separate pagination ceilings to ignore the operation's declaration — and\n * the one that turned a legitimately-served 120-row page into a 500 (`too_big` at\n * `pagination.limit`) after the request and data-array bounds had already been fixed.\n *\n * The five, for anyone changing one of them: the REQUEST bound\n * ({@link createPaginationRequestSchema}), the repository's `_doList` and `_doSearch` maxima, the\n * response DATA array bound, and this. They are one invariant — \"the operation's declared ceiling\n * governs\" — and a fix applied to fewer than all of them just moves which layer says no.\n */\nexport function createPaginationResponseMetadataSchema(maxLimit: number = 100): z.ZodObject<{\n page: z.ZodDefault<z.ZodNumber>;\n limit: z.ZodDefault<z.ZodNumber>;\n total: z.ZodNumber;\n totalPages: z.ZodNumber;\n}> {\n return z.object({\n page: z.number().min(1).default(1),\n limit: z.number().min(1).max(maxLimit).default(20),\n total: z.number().min(0),\n totalPages: z.number().min(0)\n });\n}\n\n/** The default-bounded response metadata. Prefer {@link createPaginationResponseMetadataSchema} where the ceiling is known. */\nexport const Resources_PaginationResponseMetadataSchema = createPaginationResponseMetadataSchema();\n\n\n\n/**\n * The pagination block a collection request may carry, bounded by the operation's declared ceiling.\n *\n * The bound used to be a hard-coded `.max(100)` on a single shared constant, which made it the\n * STRICTEST of the three pagination ceilings in the framework — the repository serves up to the\n * operation's declared limit and the response DTO accepts up to the same, but a request asking for\n * more than 100 was rejected before either ran. An operation declaring 200 therefore advertised a\n * limit it answered 400 to.\n *\n * `maxLimit` defaults to 100 so every existing caller keeps its current contract.\n */\nexport function createPaginationRequestSchema(maxLimit: number = 100): z.ZodObject<{\n page: z.ZodDefault<z.ZodNumber>;\n limit: z.ZodDefault<z.ZodNumber>;\n}> {\n return z.object({\n page: z.number().min(1).default(1),\n limit: z.number().min(1).max(maxLimit).default(20)\n });\n}\n\n/** The default-bounded pagination block. Prefer {@link createPaginationRequestSchema} where the operation's ceiling is known. */\nexport const Resources_PaginationRequestSchema = createPaginationRequestSchema();\n\n// Type utility for paginated response result (Zod schema based)\nexport type ResourcePaginatedResponseResultSchema<T extends z.ZodTypeAny> = {\n data: SchemaArrayInfer<T>;\n pagination: z.infer<typeof Resources_PaginationResponseMetadataSchema>;\n};\n\n// Plain TypeScript types for pagination (non-Zod dependent)\nexport type Resources_PaginationMetadata = z.infer<typeof Resources_PaginationResponseMetadataSchema>;\nexport type Resources_PaginationRequest = z.infer<typeof Resources_PaginationRequestSchema>;\n\nexport type Resources_PaginatedResult<T> = {\n data: T[];\n pagination: Resources_PaginationMetadata;\n};\n\n// --------------------------------\n// Data Mode (for read/list operations)\n// --------------------------------\n\n/**\n * @wildo_source:part:start saas.models.data-mode-read-list facet:layer:shared facet:family:resource-config\n *\n * Data mode for read/list operations — controls API data shape.\n * SUMMARY = backend returns only `isSummaryField()` fields and populates `isSummaryField({ populate: true })` FKs.\n *\n * Note: \"summary\" here is about *data shape*, not FK rendering (see ForeignKeyDisplayMode.SUMMARY)\n * or operation bar density (see ResourceOperationDisplayMode.SUMMARY).\n * Values are lowercase strings matching URL/serialization format directly.\n *\n * @remarks Import from `@wildo-ai/saas-models`. Used by read/list/search configs and DTO shaping.\n */\nexport enum DataMode {\n /** All stored fields, no population */\n RAW = 'raw',\n\n /** isSummaryField fields only, populate isSummaryField({ populate: true }) FKs */\n SUMMARY = 'summary',\n\n /** All fields, populate based on contextPolicy */\n CONTEXT = 'context'\n}\n\n// --------------------------------\n// Persistence Adapter (Database Type)\n// --------------------------------\n\n/**\n * Canonical persistence adapters supported by the resource system.\n *\n * Determines which backing store serves a resource's rows:\n * - `mongodb`: MongoDB with Mongoose ORM (platform-provisioned)\n * - `postgresql`: PostgreSQL SQL adapter (platform-provisioned)\n * - `http-api`: a system reached over an authenticated HTTP API (OData or REST/JSON dialect) —\n * the platform provisions NOTHING for it; rows are queried on demand\n *\n * If not specified at resource level, uses `appConfig.database.defaultAdapter`.\n */\n/**\n * What an INTROSPECTION-backed resource projects.\n *\n * Declared BY THE AUTHOR beside the resource, exactly as `httpApiBinding` is, and for the same\n * reason: the adapter answers \"how rows are reached\", and everything specific to THIS resource's\n * reach is a typed declaration rather than runtime inference.\n *\n * Keeping it here rather than in a companion-side lookup table matters. A table keyed by resource\n * type would be a second enumeration of a population the resource configs already define — the\n * shape that silently drifts when one side gains an entry.\n */\nexport enum IntrospectionTenancyStance {\n /**\n * The rows describe the APPLICATION'S OWN STRUCTURE — its modules, resource specifications,\n * design system, compliance declarations — and carry no tenant dimension, so there is nothing for\n * a contextual scope predicate to filter on.\n *\n * This is the only stance implemented, and it is declared rather than inferred for the reason\n * `HttpApiTenancyStance` gives for its own: an unstated stance is indistinguishable from an\n * overlooked one. Every other repository enforces scoping ITSELF — Mongo and PostgreSQL through\n * `buildContextualFilter`, HTTP_API through its tenancy stance — so a read-through adapter that\n * simply returned whatever its source produced would be the one backing kind with no scoping\n * story, and nobody would be asked to notice.\n */\n APPLICATION_STRUCTURE = 'application-structure',\n\n /**\n * The rows are the DEVELOPMENT COMPANION'S OWN RUNTIME PROJECTIONS for the one application it\n * serves — the playbook eligibility board, the build plan, the run write-register, the execution\n * traces. Like `APPLICATION_STRUCTURE` they carry no tenant dimension and no scope predicate can\n * apply; UNLIKE it, they are not derived from the application's source, and the tenancy stance is\n * where that difference must stay visible (added 2026-09-01, creation-board resources plan):\n *\n * - `APPLICATION_STRUCTURE`'s public-read posture (`APP_PUBLIC` variants on the workbench's\n * structural projections) rests on the rows being the application's OWN source, which anyone\n * with the working tree can already read. That argument does NOT extend to runtime state, so a\n * resource declaring this stance is expected to gate its variants on an authenticated role —\n * never `APP_PUBLIC`.\n * - Row-level tenant-anchor checks are bypassed for this stance exactly as for\n * `APPLICATION_STRUCTURE` (the rows carry no anchor BY CONTRACT); authorization comes from the\n * session and the operation's role gate.\n *\n * Fusing this into `APPLICATION_STRUCTURE` was rejected for the enum's own founding reason: an\n * unstated distinction is indistinguishable from an overlooked one, and the next decision site\n * keyed off the stance would inherit a claim nobody checked.\n */\n APPLICATION_RUNTIME = 'application-runtime',\n\n /**\n * The rows carry a tenant dimension and a scope predicate must be pushed into the source.\n *\n * REFUSED AT REGISTRATION today: no introspection behavior takes a scope argument, so the engine\n * has nothing to push down. Declared as a member rather than omitted so the refusal is explicit\n * and a future scoped behavior is a deliberate design step — the alternative is that the first\n * author needing it discovers the gap by shipping unscoped rows.\n */\n SCOPE_FILTER_PUSHDOWN = 'scope-filter-pushdown',\n}\n\nexport interface IntrospectionResourceBinding {\n /**\n * The companion introspection behavior whose output becomes this resource's rows.\n *\n * A plain string on purpose: the behavior catalog lives in the companion (23 behaviors as of\n * 2026-08-29) and the engine must not depend on it. An unknown id is refused by the companion at\n * read time, naming the id and the supported set.\n */\n readonly behavior: string;\n\n /**\n * Path into the behavior's result that holds the rows, dot-separated. Omitted means the result\n * itself is the row collection.\n *\n * Behaviors answer their own natural shape — `{ frontendChartRegistrationRefs: [...] }` — and\n * teaching each one to answer a uniform envelope would change 23 working behaviors to suit a\n * consumer that arrived later.\n */\n readonly rowsPath?: string;\n\n /**\n * REQUIRED. Why these rows need no contextual scope predicate, or that they do.\n *\n * Not optional and not defaulted: a default would make \"nobody thought about tenancy\" and\n * \"tenancy does not apply here\" the same declaration.\n */\n readonly tenancy: IntrospectionTenancyStance;\n\n /**\n * Fields whose values COMPOSE each row's primary key, in order, when the behavior's rows carry no\n * identifier of their own.\n *\n * Most introspection behaviors answer structural projections rather than records: a module row\n * carries `moduleId` and needs nothing here, while a relationship row carries only the two\n * resource types it joins. Without a key such a resource can be LISTed and never READ, because\n * there is no value to address a row by.\n *\n * Composition is DECLARED rather than derived because only the author knows which fields are\n * jointly unique for a given behavior — and being wrong is not hypothetical. `resourceType1` +\n * `resourceType2` is unique across all 16 relationships in the dogfood application and is still\n * unsafe in general, since the framework permits several edges between the same pair (a junction\n * with two roles, a self-relationship). So the source FAILS LOUDLY when two rows compose the same\n * key rather than serving both under one id, which would make READ return an arbitrary one of\n * them and give no sign anything was wrong.\n *\n * The composed value is written to the resource's own `resourceFieldIdentifier`, which is the one\n * key name resolvable from the registry without introspecting the schema's decorators. A resource\n * declaring `identityFields` must therefore name that field as its primary key.\n *\n * The values are joined verbatim, so the key stays READABLE — `workspace__users__createdByUserId`\n * rather than an opaque hash. That is possible because an INTROSPECTION-backed resource resolves\n * to `ResourceIdFormat.INTROSPECTION_SYNTHESIZED`, which admits a URL-safe charset instead of the\n * ObjectId-or-UUID rule platform rows follow.\n *\n * It was briefly a deterministic UUIDv5, before that format existed. Hashing worked and was the\n * wrong trade: it made every id opaque, and it disguised the real property — a DERIVED id is only\n * as stable as the fields it is derived from, and hiding that behind a hash removes the reader's\n * ability to notice when a row's identity moves.\n *\n * Each value must be URL-safe (`[a-zA-Z0-9_-]`) and the whole key at most 255 characters; the\n * source refuses per row rather than emitting a key that lists and cannot be read.\n *\n * Omit when the behavior's rows already carry an identifier of their own.\n */\n readonly identityFields?: readonly string[];\n}\n\nexport enum PersistenceAdapter {\n MONGODB = 'mongodb',\n POSTGRESQL = 'postgresql',\n\n /**\n * Rows are DERIVED on demand by asking the running dev companion to introspect the application —\n * its resource registry, specifications, design system, compliance facts, assertions, market and\n * persona context. Nothing is stored: every read recomputes.\n *\n * ## Why this is an adapter and not a service call\n *\n * The companion already computes all of it — 23 named introspection behaviors — and today each is\n * reachable only through a bespoke route. Making it an adapter is what turns those behaviors into\n * ordinary RESOURCES: list, read, filter, search, labels, navigation, all generated, with no\n * per-behavior UI. That is the same argument `HTTP_API` settled on 2026-08-26 — the adapter answers\n * \"how rows are reached\", and the mechanism behind it is a binding an author declares.\n *\n * Distinct from `HTTP_API`, deliberately, and not merely because the transport differs. Retrieval\n * is the COMPANION's decision per behavior (amended 2026-09-01): the app-import behaviors run a\n * SUBPROCESS that imports the target application's built modules — seconds per read, requiring\n * that application's `dist`, failing as a thrown behavior — while the creation-plane behaviors\n * are IN-PROCESS projections of the companion's own runtime services (playbook eligibility,\n * build plan, write register, traces). Both reach the engine through the same\n * `IntrospectionResourceSourceBackendPort`; the adapter never encodes the mechanism, which is\n * exactly why a behavior id is an engine-opaque string. Collapsing this into `HTTP_API` would\n * still be wrong for the original reason: its failure mode is a source throwing rather than a\n * row being absent, and nothing here is reached over a network API.\n *\n * ## What every adapter decision site must honor\n *\n * - **READ-ONLY, absolutely.** There is no writable target: a row is a projection of the\n * application's own source. Every write method refuses, as `HTTP_API`'s twelve do.\n * - **No provisioning, migration or schema surface.** Nothing is stored, so there is nothing to\n * create, migrate or verify — the platform must never believe it owns a store here.\n * - **Never a legal application-wide DEFAULT adapter.** It is a per-resource declaration only;\n * an application whose default was introspection could persist nothing at all.\n * - **It must DEGRADE, never fail closed at boot.** The companion is the only thing that can run\n * an introspection behavior, so a boot path that refuses on an introspection failure would need\n * the companion to fix the companion — see `continuity-drift-and-cas-basis.md` Rule 3.\n */\n INTROSPECTION = 'introspection',\n\n /**\n * Rows live in a system reached over an authenticated HTTP API and are queried on demand\n * (read-through). Decided 2026-08-26 (ETL workstream, D1): this IS a persistence-adapter answer\n * — \"how rows are reached\" — not a separate axis; the dialect (OData vs REST/JSON-RPC) and every\n * other binding property live in the REQUIRED companion declaration\n * (`HttpApiResourceBindingSchema`, published by `@wildo-ai/saas-models/external-data`).\n *\n * ## Why that companion lives in a SHARED package at all (asked 2026-08-29, measured not argued)\n *\n * Only the backend has a runtime for it — 19 references there, none in any frontend package — so\n * \"surely this is backend-only\" is the natural reading. It is wrong, for one decisive reason: an\n * application author writes the binding in `*.resources-config.ts`, which lives in the app's\n * `shared-lib`, and that package depends on `@wildo-ai/saas-models` and deliberately NOT on\n * `@wildo-ai/saas-backend-lib` (the frontend imports `shared-lib`, so a backend dependency there\n * would pull the backend graph browser-ward — the opposite of the isolation wanted). Moving the\n * declaration to the backend would leave an author unable to type their own declaration.\n *\n * Nothing in that companion is runtime: dialects, tenancy, erasure, key fields, remote-call\n * semantics are all typed by a human. The runtime that consumes them — read client, extraction\n * source, transform, run service, batch — is already backend-only. So the tiers are already\n * split where they should be; what was owed was SURFACE hygiene, which is why the companion is\n * published by a subpath instead of the root barrel rather than moved.\n *\n * This asymmetry with MONGODB / POSTGRESQL is therefore principled, not accidental: those need\n * no AUTHORED companion at all (their detail is infra config plus a runtime planner), while\n * HTTP_API is the one adapter whose detail an app developer writes.\n *\n * Consequences every adapter decision site must honor explicitly (compile-forced via\n * `PersistenceAdapterCases`):\n * - provisioning/migration/schema surfaces have NOTHING to do — the platform does not own the\n * store and must never mutate its schema;\n * - it is never a legal application-wide DEFAULT adapter — only a per-resource declaration\n * accompanied by its binding;\n * - until the external repository ships, `createRepository()` refuses it loudly.\n */\n HTTP_API = 'http-api',\n}\n\n/**\n * String-value form accepted by authored resource configuration.\n *\n * This remains a string union so declarative configuration may use either the\n * enum members or their serialized values, while deriving the vocabulary from\n * the single canonical enum above.\n */\nexport type PersistenceAdapterType = `${PersistenceAdapter}`;\n\nconst PERSISTENCE_ADAPTER_VALUES: readonly string[] = Object.freeze(Object.values(PersistenceAdapter));\n\n/**\n * Runtime guard for persistence-adapter values entering through erased,\n * serialized, or otherwise untyped configuration boundaries.\n *\n * Keeping this beside the canonical enum ensures future adapter additions are\n * recognized by validation without duplicating a second vocabulary.\n */\nexport function isPersistenceAdapter(\n value: unknown,\n): value is PersistenceAdapterType {\n return typeof value === 'string'\n && PERSISTENCE_ADAPTER_VALUES.includes(value);\n}\n\n/**\n * Default persistence adapter when not specified.\n *\n * Typed as the ENUM member (not the string-union `PersistenceAdapterType`) so it satisfies\n * enum-typed configuration fields directly; enum member types are assignable to the string\n * union, so every union-typed consumer is unaffected.\n */\nexport const DEFAULT_PERSISTENCE_ADAPTER: PersistenceAdapter =\n PersistenceAdapter.MONGODB;\n\n/**\n * A resource's declared relationship to CROSS-ADAPTER ATOMIC WRITE BOUNDARIES.\n *\n * **Why this exists.** An application is wholly one persistence adapter by default, and the\n * resources registry REFUSES a per-resource `persistenceAdapter` that disagrees with\n * `database.defaultAdapter`. That refusal protects exactly one property: a mixed application\n * cannot commit two resources in one native transaction, so every cross-resource boundary\n * silently degrades from atomic to at-least-once.\n *\n * The refusal is right as a DEFAULT and wrong as an absolute — some resources provably never\n * participate in such a boundary, and for those the blanket refusal forbids a legitimate,\n * safe deployment (a retrieval corpus whose vector tier only PostgreSQL can serve, say) with\n * no way past it. Per `.claude/rules/design-philosophy.md`, a construct that refuses must ship\n * the door in the same change: this enum IS that door. Declaring a member is a positive claim\n * about the resource's write topology, made by its author, recorded in the config, and logged\n * at registration — never an ambient tolerance and never a flag that merely silences an error.\n *\n * **This declaration is a claim, not an exemption.** It does not weaken any runtime guarantee:\n * `ResourcePersistenceUnitOfWorkBackendService.assessCompatibility` still classifies any real\n * boundary whose ATOMIC participants resolve to more than one adapter as\n * `MIXED_PERSISTENCE_ADAPTERS`, and `executeInUnitOfWork` still THROWS on it. So a resource\n * that declares a member here and then IS reached by a cross-adapter atomic boundary fails\n * loudly at that boundary, exactly as it would have without the declaration. What the\n * declaration buys is the right to boot; what it can never buy is a silent degradation.\n *\n * assurance-control: WILDO.DATA.TRANSACTIONAL_INTEGRITY — the declaration a resource must make\n * before it may sit on a non-default persistence adapter.\n */\nexport enum ResourcePersistenceAdapterIsolation {\n /**\n * This resource participates in NO cross-adapter atomic write boundary.\n *\n * The author asserts that no operation commits this resource and a resource on another\n * adapter inside one native transaction — neither as the subject of a\n * `ResourcePersistenceUnitOfWork` boundary nor as a participant in someone else's.\n *\n * Legitimate when the resource is a self-contained store whose writes stand alone: a\n * retrieval corpus, an append-only analytical sink, a projection rebuilt from its source\n * rather than written beside it. It is NOT legitimate merely because no such boundary\n * exists *today* — the claim is about the resource's design, and a later operation that\n * makes it false must change the declaration rather than discover the refusal at runtime.\n */\n NO_CROSS_ADAPTER_TRANSACTION = 'NO_CROSS_ADAPTER_TRANSACTION',\n}\n\n/**\n * String-value form accepted by authored resource configuration, mirroring the\n * {@link PersistenceAdapterType} convention so declarative config may use either the enum\n * member or its serialized value.\n *\n * **Deliberately one member.** A second — \"participates only as a COMPENSATABLE participant\",\n * mirroring the persistence unit of work's own\n * `ResourcePersistenceParticipantCoupling.COMPENSATABLE` — is describable, but no resource is\n * asking for it, and an admission path with no caller is an admission path with no coverage.\n * Add it when a real resource needs it, with the compensating path named at its call site.\n */\nexport type ResourcePersistenceAdapterIsolationType = `${ResourcePersistenceAdapterIsolation}`;\n\nconst RESOURCE_PERSISTENCE_ADAPTER_ISOLATION_VALUES: readonly string[] =\n Object.freeze(Object.values(ResourcePersistenceAdapterIsolation));\n\n/**\n * Runtime guard for isolation declarations entering through erased, serialized, or otherwise\n * untyped configuration boundaries — which is exactly how the resources registry sees them, since\n * it reads heterogeneous resource configurations through an erased index.\n *\n * Deliberately a MEMBERSHIP test rather than a presence test: the registry admits a non-default\n * adapter only on a recognised member, so a typo, a stale value from an older vocabulary, or a\n * truthy non-string cannot open the door. Fail-closed is the point — an unrecognised declaration\n * is treated as no declaration at all, and the application is refused with the standard message.\n */\nexport function isResourcePersistenceAdapterIsolation(\n value: unknown,\n): value is ResourcePersistenceAdapterIsolationType {\n return typeof value === 'string'\n && RESOURCE_PERSISTENCE_ADAPTER_ISOLATION_VALUES.includes(value);\n}\n\n/**\n * Variant key constants for operation variants.\n * Values are identical to DataMode.SUMMARY and DataMode.CONTEXT.\n * Kept as a standalone constant for use in factory/controller code that\n * deals with variant keys independently of DataMode.\n */\nexport const OPERATION_VARIANT_KEY = {\n SUMMARY: 'summary',\n CONTEXT: 'context',\n} as const;\n\n/**\n * Convert DataMode to corresponding variant key.\n * RAW mode has no variant (returns undefined).\n * For SUMMARY/CONTEXT, the variant key equals the DataMode value directly.\n */\nexport function dataModeToVariantKey(dataMode: DataMode): string | undefined {\n if (dataMode === DataMode.RAW) {\n return undefined;\n }\n return dataMode;\n}\n\n/**\n * Options for read operations.\n */\nexport interface ReadOperationOptions {\n /**\n * Data mode controlling fields and population.\n * @default DataMode.RAW\n */\n dataMode?: DataMode;\n}\n\n/**\n * Options for list operations.\n */\nexport interface ListOperationOptions {\n /**\n * Data mode controlling fields and population.\n * @default DataMode.RAW\n */\n dataMode?: DataMode;\n\n /**\n * Whether to wrap in pagination container.\n * @default false for internal, should be true for API responses\n */\n paginated?: boolean;\n\n /**\n * Pagination parameters (required if paginated: true).\n */\n pagination?: Resources_PaginationRequest;\n}\n/** @wildo_source:part:end saas.models.data-mode-read-list */\n\n// --------------------------------\n// Auto-Variants Configuration\n// --------------------------------\n\n/**\n * Configuration for auto-generated operation variants.\n * Controls which READ/LIST variants (summary, context) are auto-generated.\n */\nexport type ResourceConfiguration_AutoVariants = {\n read?: {\n /** Enable READ.context variant. @default true */\n enableContext?: boolean;\n /** Enable READ.summary variant. @default true */\n enableSummary?: boolean;\n };\n list?: {\n /** Enable LIST.context variant. @default true */\n enableContext?: boolean;\n /** Enable LIST.summary variant. @default true */\n enableSummary?: boolean;\n };\n}\n\n// --------------------------------\n// Resource Search Query Request\n// --------------------------------\n\n// Real TypeScript enum for sort order\nexport enum SortOrder {\n ASC = 'asc',\n DESC = 'desc'\n}\nexport const Resources_Filter_DateRangeSchema = z.object({\n startDate: z.date().optional(),\n endDate: z.date().optional()\n});\n\n\nexport const Resources_Filter_BaseSchema = z.object({\n searchRequest: z.string().optional(),\n createdAt: Resources_Filter_DateRangeSchema.optional(),\n updatedAt: Resources_Filter_DateRangeSchema.optional(),\n});\n\n// Generic filter request schema (extends base filter with custom fields)\nexport const Resources_FilterRequestSchema = z.object({\n searchRequest: z.string().optional(),\n createdAt: Resources_Filter_DateRangeSchema.optional(),\n updatedAt: Resources_Filter_DateRangeSchema.optional(),\n}).catchall(z.any().optional()); // Allows additional filter fields\n\n// Generic sorting field schema\nexport const Resources_SortingFieldSchema = z.object({\n field: z.string(),\n order: z.enum(SortOrder).default(SortOrder.ASC),\n priority: z.number().min(0).default(0)\n});\n\n// Generic sorting request schema (object with field keys)\nexport const Resources_SortingRequestSchema = z.record(\n z.string(),\n Resources_SortingFieldSchema.optional()\n).optional();\n\n// Generic search query request schema\nexport const Resources_SearchQueryRequestSchema = z.object({\n pagination: Resources_PaginationRequestSchema.optional(),\n sorting: Resources_SortingRequestSchema.optional(),\n filters: Resources_FilterRequestSchema.optional()\n});\n\n// Type utility for search query request result\nexport type ResourceSearchQueryRequestResult<\n TFilterFields extends Record<string, PrimitiveZodType | ((filterRef: PrimitiveZodType) => string)> | undefined,\n TSortFields extends readonly string[] | undefined\n> = z.ZodObject<{\n pagination: z.ZodOptional<typeof Resources_PaginationRequestSchema>;\n sorting: z.ZodOptional<typeof Resources_SortingRequestSchema>;\n filters: z.ZodOptional<typeof Resources_Filter_BaseSchema>;\n}>;\n\nexport type Ressources_RequestSortPriority<T extends EnumLikeType> = {\n [K in keyof T as T[K]]: number;\n};\n\n/**\n * Distributive omit. `UserNotificationDefinition` is a discriminated union (by\n * `channel`); a bare `Omit<Union, K>` collapses to only the COMMON keys and silently\n * drops every channel-specific field (the EMAIL variant's `recipientEmailField`, the\n * FRONT_END_SUCCESS variant's `celebration`/`nextStep`). Routing through a naked type\n * parameter distributes the omit over each union member so those per-channel fields\n * survive in the config-authoring type.\n */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\ntype UserNotificationDefinitionWithoutPrimaryScopeAndIdentifier = DistributiveOmit<UserNotificationDefinition, 'primaryScope' | 'identifier'>;\ntype M2MNotificationDefinitionWithoutIdentifier = Omit<M2MNotificationDefinition, 'identifier'>;\n\nexport type CollectionView_SearchOptions = {\n caseSensitive? : boolean, // default false\n fullMatchOnly? : boolean, // default false\n}\n\n/**\n * Shared collection-view fields for operations that render a collection of items (LIST, SEARCH).\n * These fields are integrated directly into the variant builder type — not nested.\n * The factory flattens them onto the final operation object.\n *\n * NOTE: cards-vs-table presentation (`displayMode` / `userSwitchableDisplayMode`)\n * is intentionally NOT here. It is a pure FRONTEND concern with no backend meaning,\n * so it is authored on the operation's `operationFrontendConfig.collectionDisplayConfig`\n * in the resource UI behavior layer (`CollectionDisplayMode` in\n * `@wildo-ai/saas-frontend-lib`). The fields that remain here are the ones the\n * BACKEND also reads: `pageSize` (pagination), `filterFields` (filtering), and\n * `sortFields` (sort validation).\n */\n/**\n * The wire format a collection export is rendered into.\n *\n * ## Why this is NOT `SubjectExportFormat`\n *\n * That enum has the same two members and answers a different question. Its `JSON` member is\n * documented as \"the format an Art. 20 request should receive by default\", and its `CSV` member as\n * \"Never the default for portability\" — statements about a data subject's PORTABILITY RIGHT. A\n * listing export owes no such duty: it is an operator exporting rows they can already read, and CSV\n * is a perfectly good default for it.\n *\n * In this codebase an enum member is where the per-member \"why\" lives (that is the whole reason an\n * anonymous string union is refused), so sharing the enum would attach Art. 20 reasoning to a\n * toolbar button, where every word of it is false. It breaks the other way too: adding `XLSX` for\n * listings would land it in the subject-export vocabulary, where someone then has to reason about\n * whether a spreadsheet satisfies Art. 20.\n *\n * What IS shared is the MECHANISM — RFC 4180 cell escaping and column resolution — which is format\n * mechanics with no legal content. See `renderCollectionExportCsv`.\n */\nexport enum ResourceCollectionExportFormat {\n /**\n * Tabular, one file. Nested values are JSON-encoded into their cell and type information is lost,\n * which is the accepted trade for a file every spreadsheet opens. The usual default for a listing.\n */\n CSV = 'csv',\n /**\n * The rows exactly as the listing's response DTO carries them — nested objects, arrays, nulls and\n * types intact. Choose this when the export feeds another system rather than a human.\n */\n JSON = 'json',\n}\n\n/**\n * WHICH rows a collection export contains.\n *\n * The axis is ROWS, deliberately — not columns. An export carries the same fields the listing's read\n * DTO carries, which is already the caller's permitted projection; there is no separate\n * \"visible columns\" notion to select from.\n */\nexport enum ResourceCollectionExportRowScope {\n /**\n * Exactly the rows the caller's request would have returned — same page, same filters, same sort.\n * One service call, and what the user sees is what they get.\n */\n VISIBLE_PAGE = 'visible_page',\n /**\n * Every row the caller's filters match, with the page bound removed.\n *\n * Implemented by ITERATING pages at the operation's own declared ceiling, never by asking for one\n * huge page: the page bound is enforced at eight independent sites (see `maxPageSize` below) and\n * raising it for an export would move the effective ceiling for the whole operation.\n */\n FULL_RESULT_SET = 'full_result_set',\n}\n\n/**\n * Opt a LIST/SEARCH operation into CSV/JSON export of its own result set.\n *\n * ## What declaring this does\n *\n * The factory derives a companion operation — one `GENERATED_COLLECTION_EXPORT` per exporting\n * LIST/SEARCH operation — that runs **the same service call as the listing** and renders the result.\n * That is the load-bearing property: tenant isolation, role ACL, contextual filters, retention\n * row-hide, backend-only/secret field stripping, relationship population and sort validation all hold\n * by construction rather than through a second implementation kept in step by hand. (`_doList` and\n * `buildContextualFilter` each exist twice — once per persistence adapter — and have diverged before;\n * an export with its own query would be a third place to get the tenant boundary right.)\n *\n * The derived operation copies the source operation's `roles` and CANNOT widen them: an export is\n * exactly as reachable as the listing it exports, never more.\n *\n * ## What it deliberately does NOT inherit\n *\n * `mcp` / agent exposure. A LIST curated onto an MCP server is a PAGINATED read tool; an export tool\n * returns the whole table in one call, which is a different risk. Inheriting the flag would widen an\n * existing application's agent surface the moment it declared an export, so the derived operation is\n * never agent-exposed and an app that wants one declares it deliberately.\n *\n * ## Ceilings\n *\n * `maximumExportedRows` is OPTIONAL and has NO default — an operation that declares none exports\n * whatever its filters match. When declared and exceeded, the export FAILS with an error naming the\n * matched count and the ceiling; it never truncates. A short file is indistinguishable from a\n * complete one once it is open in a spreadsheet, and a silent partial export is the worse outcome.\n */\n/**\n * What a collection-export route answers with, described for specs and OpenAPI.\n *\n * ⚠️ The BODY is the exported FILE — a CSV or JSON byte stream, not this object. The controller owns\n * the response for this operation family and streams it, so nothing ever serialises this schema.\n * What it documents is the METADATA CONTRACT, which rides in response headers beside the bytes:\n * `Content-Type`, `Content-Disposition` (carrying the filename) and `X-Wildo-Export-Row-Count`.\n *\n * Modelling the response as \"a JSON envelope containing the file\" was rejected: a large CSV inside a\n * JSON string doubles the payload, defeats streaming, and gives the browser nothing to download.\n */\nexport const ResourceCollectionExportResponseSchema = z.object({\n /** Mirrors the `filename=` of the `Content-Disposition` header. */\n filename: z.string(),\n /** Mirrors `Content-Type`: `text/csv` or `application/json`. */\n contentType: z.string(),\n /** Mirrors `X-Wildo-Export-Row-Count` — the number of rows actually written. */\n rowCount: z.number().int().nonnegative(),\n});\n\nexport type ResourceCollectionExportConfig = {\n /** Offered formats. Non-empty — an empty array is a startup error, never a silent \"all\". */\n formats: readonly ResourceCollectionExportFormat[];\n /** Offered row scopes. Non-empty, same rule. */\n rowScopes: readonly ResourceCollectionExportRowScope[];\n /**\n * Refuse (do not truncate) a `FULL_RESULT_SET` export whose match count exceeds this.\n * Omit for no ceiling.\n */\n maximumExportedRows?: number;\n};\n\nexport type CollectionViewFields_Shared = {\n pageSize?: number;\n filterFields?: Record<string, PrimitiveZodType | ((filterRef: PrimitiveZodType) => string)>;\n sortFields?: readonly string[];\n /**\n * Opt this LIST/SEARCH operation into CSV/JSON export — see {@link ResourceCollectionExportConfig}.\n * It belongs in this bucket because it is read by the BACKEND, like its three neighbours.\n */\n collectionExport?: ResourceCollectionExportConfig;\n};\n\n/** LIST-specific collection-view fields (LIST and custom ops with resourceOperationLike: LIST) */\nexport type CollectionViewFields_List = CollectionViewFields_Shared & {\n searchEnabled?: boolean;\n filtersEnabled?: boolean;\n /**\n * Maximum page size this operation will accept, serve and return.\n *\n * ## Read this before changing any pagination bound\n *\n * ONE declaration, enforced at EIGHT independent sites. That is not an accident of style — each\n * layer validates the page from its own side, and a bound tightened at any one of them silently\n * becomes the effective ceiling for the whole operation. Getting fewer than all eight to agree\n * does not fail loudly; it just moves which layer says no:\n *\n * | # | Where | What it bounds |\n * |---|---|---|\n * | 1 | `createPaginationRequestSchema` | the `pagination.limit` a caller may ASK for (400 if exceeded) |\n * | 2 | Mongo `_doList` | the rows the query is allowed to fetch |\n * | 3 | PostgreSQL `_doList` | same, and it must MATCH #2 — the adapters diverged here once |\n * | 4 | Mongo/PostgreSQL `_doSearch` | same for the SEARCH verb |\n * | 5 | response DTO `data` array `.max()` | the rows the response schema will serialise (500 if exceeded) |\n * | 6 | `createPaginationResponseMetadataSchema` | the `limit` VALUE echoed in the envelope (500 if exceeded) |\n * | 7 | `internalDto` pagination wrappers (default + SUMMARY + CONTEXT variants) | SERVICE-layer validation, *after* the page is built |\n * | 8 | `wrapInPaginationStructure` (dataMode SUMMARY/CONTEXT) | the dataMode-selected response shape |\n *\n * ⚠️ Sites 7 and 8 are the ones that hide. They validate the SERVICE result, so they only speak\n * once the request has been accepted and the repository has already produced the page — a bound\n * left stale there surfaces as `service_resource_transformation_failed`, not as a validation\n * error, and only for collections large enough to reach it.\n *\n * ## The default is 100, and it is load-bearing\n *\n * An operation that declares nothing gets **100**, because that is the value the Mongo `_doList`\n * hard-coded before these sites were unified. Defaulting to 50 (SEARCH's own default) was tried\n * and reverted: it silently HALVED every caller asking for more — billing usage-metering\n * (`limit: 10000`), the sidebar resource browser, the flows-actors controller and the audit\n * export, none of which declare a ceiling and none of which would have errored. Symmetry with\n * SEARCH is not worth a silent truncation.\n *\n * DECLARE a value when a caller depends on it, even if it equals the default — `auditLogs | LIST`\n * does exactly that, because its export path breaks quietly if the default ever moves.\n *\n * The operation declaration is the sole pagination authority. The resolved default operation and\n * every auto-generated LIST representation inherit the same effective ceiling, so request,\n * repository, response and API-reference contracts cannot diverge by data mode.\n */\n maxPaginatedResultPerPageLimit?: number;\n};\n\n/**\n * @wildo_source:part:start saas.models.resource-config.searchable-relation facet:layer:shared facet:family:resource-config\n *\n * Relational search descriptor — lets a SEARCH operation match rows of THIS\n * resource by searching a RELATED (\"juncted\") resource's own searchable fields,\n * WITHOUT denormalizing those fields onto this resource. Author it inline inside\n * a SEARCH operation's `searchableFields` array, alongside plain own-field names.\n *\n * WHY IT EXISTS — some human-meaningful search keys live on a related resource\n * and are MUTABLE there (e.g. a user's first/last name on a user-profile\n * resource). Copying such a field onto this row would go stale and demand a\n * change-propagation sync on every mutation path of the owning resource. This\n * descriptor reuses the related resource's OWN searchable surface at query time\n * instead — single source of truth, never stale, no sync hooks to maintain.\n *\n * HOW IT WORKS (tenant-safe by construction) — at search time the framework runs\n * a secondary, system-level match on {@link relatedResource} using {@link fields}\n * (defaulting to that resource's own string `searchableFields`), collects the\n * matching rows' {@link relatedJoinField} values, and adds \"this resource's\n * {@link localField} is one of those values\" as an extra match branch, OR-ed\n * with the own-field matches. The primary query stays scoped (e.g. by\n * organization) via the normal contextual filter, so the secondary match's own\n * (often global) scope is irrelevant to safety: a row outside the caller's scope\n * can never enter the already-scoped primary result set. Because the join\n * collapses to a single-collection lookup, pagination and totals are unchanged.\n *\n * FAIL-SAFE — a relational entry only ever ADDS matches within the already-scoped\n * set, so if the related resource cannot be resolved the entry contributes\n * nothing (the search under-matches; it can never widen visibility).\n *\n * @remarks Import from `@wildo-ai/saas-models`. Supported on MongoDB-backed\n * resources today; on a PostgreSQL-backed resource the relational entries are\n * skipped and only the own-field entries apply (own-field search is unaffected).\n *\n * @example\n * // Search org members by the linked user's name. The user's firstName/lastName\n * // live on a separate USER_PROFILES resource (mutable) and are deliberately NOT\n * // copied onto the member row; both members and profiles carry the same `userId`,\n * // so we join on it and reuse the profile's own searchableFields.\n * searchableFields: [\n * 'userEmail',\n * { localField: 'userId', relatedResource: CoreResourceType.USER_PROFILES,\n * relatedJoinField: 'userId', fields: ['firstName', 'lastName'] },\n * ]\n */\nexport type SearchableRelationDescriptor = {\n /** Field on THIS resource matched against the resolved related ids (e.g. `userId`). */\n localField: string;\n /** The related resource whose own searchable surface is reused (e.g. `USER_PROFILES`). */\n relatedResource: ResourceType;\n /**\n * Field on the related resource whose values are matched against\n * {@link localField}'s values. Defaults to the related primary key (`_id`) —\n * set it explicitly when joining on a shared key other than the primary key\n * (e.g. members and profiles both carry `userId`).\n */\n relatedJoinField?: string;\n /**\n * Which related fields to match the search term against. Defaults to the\n * related resource's OWN (string) `searchableFields` — i.e. literally \"use the\n * juncted element's own search\". Provide an explicit list only to narrow it.\n */\n fields?: string[];\n};\n\n/**\n * A SEARCH `searchableFields` entry: either an OWN-resource field name (string),\n * matched on this collection, or a {@link SearchableRelationDescriptor} that\n * matches via a related resource. The two forms coexist in one array and are\n * OR-ed together at query time.\n */\nexport type SearchableFieldDescriptor = string | SearchableRelationDescriptor;\n\n/** Runtime guard distinguishing a relational descriptor from an own-field name. */\nexport function isSearchableRelationDescriptor(\n field: SearchableFieldDescriptor\n): field is SearchableRelationDescriptor {\n return typeof field === 'object' && field !== null;\n}\n/** @wildo_source:part:end saas.models.resource-config.searchable-relation */\n\n/** SEARCH-specific collection-view fields (SEARCH and custom ops with resourceOperationLike: SEARCH) */\nexport type CollectionViewFields_Search = CollectionViewFields_Shared & {\n isSearchable?: boolean;\n /**\n * Fields this SEARCH matches against. Each entry is either an own-collection\n * field name or a {@link SearchableRelationDescriptor} that reaches into a\n * related resource (see that type for the tenant-safety contract). Own-field\n * and relational entries OR together.\n */\n searchableFields?: SearchableFieldDescriptor[];\n searchableOptions?: CollectionView_SearchOptions;\n /** SEARCH's page ceiling. Same eight-site contract as the LIST field — see its JSDoc above. */\n maxPaginatedResultPerPageLimit?: number;\n};\n\n// --------------------------------\n// Resources Shared Configuration\n// --------------------------------\n\n/**\n * The wire selector every bulk operation carries.\n *\n * The DTO builder wraps EVERY `isBulkOperation` request DTO in this shape\n * (`buildEnhancedRequestDto`), so `_ids` is the ONE field name a client may use to name the rows a\n * bulk operation targets. It is a SELECTOR, not data: it never reaches persistence.\n *\n * The leading underscore is deliberate and load-bearing — it keeps the selector out of the resource's\n * own field namespace, so a resource that legitimately owns a field called `ids` cannot collide with\n * it. Read it through {@link BULK_OPERATION_SELECTOR_FIELD} rather than spelling the string, and\n * resolve it through {@link readBulkOperationSelectorIds}: a producer that spells it `ids` type-checks,\n * validates away to nothing, and then fails as \"`_ids` is required\" — which is exactly what every\n * frontend bulk surface did until 2026-08-29.\n */\nexport const Resources_BulkOperationBaseSchema = z.object({\n _ids: z.array(z.string().min(1)).min(1),\n});\n\n/**\n * The single named source for the bulk selector's field name.\n *\n * Every producer (frontend request builders) and every consumer (the service-tier target resolution,\n * the generated relationship-bulk selector) reads the name from here, so the two halves of the wire\n * contract cannot drift apart silently.\n */\nexport const BULK_OPERATION_SELECTOR_FIELD = '_ids' as const;\n\n/**\n * Read the bulk selector out of a request payload, or `undefined` when it carries none.\n *\n * Deliberately tolerant of an unparsed body: this runs at the service tier, which is reached both by\n * the HTTP controller (where the request DTO has already validated the shape) and by MCP / batch /\n * programmatic callers (where it has not). Anything that is not a non-empty array of non-empty\n * strings yields `undefined`, so the caller's own missing-target refusal reports the failure rather\n * than a half-resolved selector reaching persistence.\n */\nexport function readBulkOperationSelectorIds(payload: unknown): string[] | undefined {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return undefined;\n }\n\n const raw = (payload as Record<string, unknown>)[BULK_OPERATION_SELECTOR_FIELD];\n if (!Array.isArray(raw)) {\n return undefined;\n }\n\n const ids = raw.filter((candidate): candidate is string => typeof candidate === 'string' && candidate.length > 0);\n return ids.length > 0 ? ids : undefined;\n}\n\n/**\n * Strip the bulk selector from a request payload.\n *\n * `_ids` is NOT in `createOrUpdateMandatoryDiscardFields`, so without this it rides the payload all\n * the way to the repository and is silently dropped there by the strict re-parse against the main\n * schema. Removing it at the point the target is resolved keeps \"who targets\" and \"what is written\"\n * separate, which is what lets the read-only-field guard reason about the payload honestly.\n */\nexport function stripBulkOperationSelector<T>(payload: T): T {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return payload;\n }\n\n if (!(BULK_OPERATION_SELECTOR_FIELD in (payload as Record<string, unknown>))) {\n return payload;\n }\n\n const { [BULK_OPERATION_SELECTOR_FIELD]: _selector, ...rest } = payload as Record<string, unknown>;\n return rest as T;\n}\n\n\n// DELETED — ResourceConfiguration_ResourceLabel_FunctionParameters\n// Transferred to @wildo-ai/saas-frontend-lib as ResourceUIBehaviorLabelParams\n\nexport type ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = {\n inputDto: MainSchemaInfer<TMainSchema>,\n currentObject: MainSchemaInfer<TMainSchema>,\n /**\n * The row as it stood BEFORE the write (the pre-image), when the framework captured one.\n *\n * Present only for a SINGLE-row UPDATE/DELETE (the framework fetches a pre-image only when\n * exactly one id is selected); `undefined` for CREATE (nothing existed) and for bulk writes.\n * It lets a consumer reason about what CHANGED rather than only the final state. Its wired\n * consumer today is the derived-badge recompute, which re-counts the scope a mutation moved a\n * row OUT of (not just the one it moved it into) — see `dispatchDerivedBadgeRecomputes`. Other\n * consumers (e.g. a `userIdsSelector` notifying the PRIOR assignee a task was taken from them)\n * would need the notification dispatcher to forward it into the selector call first; it is not\n * plumbed there yet. Never assume it is populated; branch on its presence.\n */\n previousObject?: MainSchemaInfer<TMainSchema>,\n objectContext: any,\n initiatorUserContext?: any,\n initiatorIds?: { userId?: string; organizationId?: string; applicationId?: string },\n}\n\n/**\n * Metadata for functions that can optionally require context.\n * Similar to virtual fields, these flags indicate whether objectContext/initiatorUserContext\n * should be populated when calling the function.\n */\nexport type ResourceConfiguration_FunctionContextNeeds = {\n needsObjectContext?: boolean;\n needsUserContext?: boolean;\n}\n\n\n\n// --------------------------------\n// Required Features Configuration\n// --------------------------------\n\n/**\n * Limit check entry: references a LIMIT-type feature and specifies the increment.\n * The gate uses the feature definition's `counting` config to measure current usage.\n */\nexport type RequiredFeaturesLimitCheck = {\n /** Feature identifier (must reference a LIMIT-type feature) */\n featureId: string;\n /**\n * How many units this operation would add.\n * When omitted: auto-inferred from operation type:\n * - CREATE → 1\n * - CREATE_MANY → validatedData.length\n * - All others → 0\n */\n increment?: number;\n /**\n * Override counting config from feature definition (rare).\n * Use when this specific operation needs different counting than the default.\n */\n countingOverride?: LimitCountingConfig;\n};\n\n/**\n * Full required features configuration for an operation variant.\n */\nexport type RequiredFeaturesFullConfig = {\n /** Boolean feature identifiers that gate this operation (presence check) */\n features?: string[];\n /** Combination mode for boolean features: OR = any enables (default), AND = all required */\n mode?: 'OR' | 'AND';\n /** Limit checks to perform before this operation */\n limitChecks?: RequiredFeaturesLimitCheck[];\n};\n\n/**\n * Required features config: accepts either shorthand (string[]) or full config.\n * Shorthand is auto-normalized by the factory to { features: [...], mode: 'OR' }.\n */\nexport type RequiredFeaturesConfig = string[] | RequiredFeaturesFullConfig;\n\n/**\n * Normalized required features (always the full form, after factory processing).\n */\nexport type RequiredFeaturesNormalized = RequiredFeaturesFullConfig;\n\n// --------------------------------\n// Resource Operation Preset Base Type\n// --------------------------------\n\n/**\n * Wrapper type for functions that can optionally require context.\n * Stores both the function and metadata about its context needs.\n */\nexport type ResourceConfiguration_FunctionWithContextNeeds<TFunction extends (...args: any[]) => any> =\n TFunction & ResourceConfiguration_FunctionContextNeeds;\n\nexport enum ResourceGeneratedOperationKind {\n GENERATED_FILE_REGENERATE = 'generated_file_regenerate',\n /** The CSV/JSON export companion of a LIST/SEARCH operation — see {@link ResourceCollectionExportConfig}. */\n GENERATED_COLLECTION_EXPORT = 'generated_collection_export',\n}\n\nexport type ResourceConfiguration_GeneratedFileRegenerateOperationMetadata = {\n kind: ResourceGeneratedOperationKind.GENERATED_FILE_REGENERATE;\n fieldName: string;\n};\n\n/**\n * Identifies an operation as the export companion of one LIST/SEARCH operation, and carries the\n * export contract the handler and the frontend both read.\n *\n * `sourceOperationIdentifier` is what makes the export re-runnable AS the listing: the handler\n * resolves that operation and dispatches it, rather than re-deriving a query.\n */\nexport type ResourceConfiguration_GeneratedCollectionExportOperationMetadata = {\n kind: ResourceGeneratedOperationKind.GENERATED_COLLECTION_EXPORT;\n /** The LIST/SEARCH operation whose result set this exports. */\n sourceOperationIdentifier: string;\n /** Which core verb the source is, so the handler knows which service call to make. */\n sourceCoreOperation: CoreResourceOperation.LIST | CoreResourceOperation.SEARCH;\n export: ResourceCollectionExportConfig;\n};\n\nexport type ResourceConfiguration_GeneratedOperationMetadata =\n | ResourceConfiguration_GeneratedFileRegenerateOperationMetadata\n | ResourceConfiguration_GeneratedCollectionExportOperationMetadata;\n\n/**\n * Authored acknowledgement that an operation variant is DECLARED but knowingly NOT IMPLEMENTED.\n *\n * ## Why this exists\n *\n * A custom operation whose `requestDto` no implementation consumes does not fail — it falls\n * through to the generic core path, which persists only fields the resource schema declares.\n * A DTO such as `{ newRole, reason }` names none, so the framework validates the body, writes\n * nothing, and answers **HTTP 200**. On 2026-08-01 a hostile e2e probe caught\n * `organizationMembers | promote` doing exactly that, and a catalogue sweep found the shape in\n * 41 routed operations — 8 CRITICAL, 11 HIGH, including `users | impersonate`,\n * `organizations | suspend` and `applications | transfer`. A \"successful\" demotion that never\n * happened is an operational-safety and audit-integrity defect: every runbook, script, and UI\n * that trusts the 2xx is wrong.\n *\n * ## What it does\n *\n * Presence is a fail-closed declaration with TWO effects, never one without the other:\n *\n * 1. **Startup is allowed to proceed.** Without it, an unserviceable operation is a startup\n * ERROR (`ApplicationStartupConfigValidatorService`), so a new one cannot ship silently.\n * 2. **The request fails closed.** The dispatcher refuses the operation before any persistence\n * with `ErrorType.CONFIGURATION` /\n * `ErrorCustomMessageReference.RESOURCE_OPERATION_CUSTOM_IMPLEMENTATION_NOT_IMPLEMENTED`.\n * Acknowledging a gap must never preserve the 2xx — that would document the defect instead\n * of closing it.\n *\n * The guard is BIDIRECTIONAL: an acknowledgement on an operation that IS serviceable is also a\n * startup error, so a stale marker can never brick an operation someone has since implemented.\n *\n * ## This is a stop-gap, not a resting place\n *\n * The right end states are to implement the operation (see the organization-units\n * MOVE/ARCHIVE pattern — a `prefixCoreOperations` hook returning the field patch the core\n * write persists) or to remove the declaration. Reach for this only to keep a known gap\n * honest while it is scheduled.\n *\n * assurance-control: WILDO.OPERATIONS.EXECUTION_INTEGRITY — an interface must not report success for an\n * action it did not perform; monitoring and incident response depend on the response being\n * truthful.\n */\nexport type ResourceOperation_UnimplementedAcknowledgement = {\n /** Why the operation is declared without an implementation, in the author's own words. */\n reason: string;\n /** Where the gap is tracked — a plan path, issue id, or equivalent durable reference. */\n trackingRef: string;\n};\n\n/**\n * Whether a declared operation is actually SERVICED — the verdict a shared predicate answers with.\n *\n * MOVED HERE FROM `saas-backend-lib` on 2026-09-02, and the reason is a layering one rather than\n * tidiness. These are closed VOCABULARIES, not backend behaviour: the resolvers that compute them\n * stay in the backend, but the answers they produce are what other layers must be able to name.\n * `saas-specifications` could not attest `WILDO.OPERATIONS.EXECUTION_INTEGRITY` in its assurance\n * authority basis while the vocabulary lived in a package it must not depend on — the control was\n * real, well-marked and unnameable from the layer obliged to describe it (#207).\n *\n * The single-resolver contract is unchanged and still lives with the resolvers: startup validation\n * and request-time refusal must consume the SAME predicate, because an operation blessed at boot\n * that can still answer 2xx on the first request is precisely the state that module ended.\n *\n * The defect behind all of this: a custom operation declaring a `requestDto` no implementation\n * consumes falls through to the generic core path, which persists only fields the resource schema\n * declares. A DTO such as `{ newRole, reason }` names none of them, so the framework validates the\n * body, writes nothing, and answers HTTP 200 with the unchanged row. A 2026-08-01 hostile probe\n * caught `organizationMembers | promote` doing exactly that, and a catalogue sweep found 41 routed\n * operations sharing the shape. Nothing escalates; the destructive direction is the dangerous one —\n * an admin demotes a compromised account, is told 200 OK, and the account keeps its privileges.\n *\n * assurance-control: WILDO.OPERATIONS.EXECUTION_INTEGRITY — an interface must not report success for\n * an action it did not perform; monitoring and incident response depend on truthful responses.\n */\nexport enum ResourceOperationImplementationCoverage {\n /** Something consumes the declared contract: a custom implementation, a declarative side effect, or the core path itself. */\n SERVICEABLE = 'serviceable',\n /** Nothing does. Left alone, the operation answers 2xx having performed nothing. */\n UNSERVICEABLE = 'unserviceable',\n /** Outside this guard's remit — a core operation, or a variant the service tier never enters. */\n NOT_APPLICABLE = 'not_applicable',\n}\n\n/**\n * Why an operation was judged {@link ResourceOperationImplementationCoverage.UNSERVICEABLE}.\n * Surfaced verbatim in the startup error so the fix is obvious without re-deriving the analysis.\n */\nexport enum ResourceOperationUnserviceableReason {\n /** Write-like: no declared request field is a persisted resource-schema field, so the write is empty. */\n NO_REQUEST_FIELD_IS_PERSISTED = 'no_request_field_is_persisted',\n /** READ-like: the core read addresses the row by id and consumes no request field, so the declared verb never happens. */\n READ_CONSUMES_NO_REQUEST_FIELD = 'read_consumes_no_request_field',\n /** Collection-like: no declared request field is a collection parameter the core path reads. */\n NO_REQUEST_FIELD_IS_COLLECTION_PARAMETER = 'no_request_field_is_collection_parameter',\n}\n\n/**\n * Whether a custom write-like verb authored the request contract it exposes, or silently inherited\n * the resource's whole create/update contract.\n *\n * Moved here with {@link ResourceOperationImplementationCoverage}, for the same reason.\n *\n * assurance-control: WILDO.OPERATIONS.EXECUTION_INTEGRITY — a named verb must not silently widen\n * into an unconstrained write over fields its resource never exposed for update.\n */\nexport enum ResourceOperationRequestContractBreadth {\n /** The operation authored its own request contract, or its family cannot inherit one. */\n AUTHORED = 'authored',\n /** A write-like custom verb inherited the resource's whole create/update contract. */\n OVERBROAD_INHERITED_FULL_CONTRACT = 'overbroad_inherited_full_contract',\n}\n\n\n/**\n * Where the engine's own inherited implementation gaps are tracked — the 2026-08-01 catalogue\n * sweep that found 41 routed operations answering a success status having performed nothing.\n * One authority so the backlog cannot fragment across 37 hand-typed strings.\n */\nexport const UNIMPLEMENTED_ENGINE_OPERATION_BACKLOG_REF =\n '.claude/plans/routed-unimplemented-operations-fail-closed.md';\n\n/**\n * Acknowledge an engine operation as a known, fail-closed implementation gap.\n *\n * Requires a SPECIFIC reason: \"not implemented\" is what the marker already says, so a reason\n * that adds nothing is a reason not worth reading during triage. State what the operation was\n * meant to do, so whoever picks it up knows the target behaviour without re-deriving it.\n *\n * @see ResourceOperation_UnimplementedAcknowledgement for the two effects this has.\n */\nexport const acknowledgeUnimplementedEngineOperation = (\n reason: string,\n): ResourceOperation_UnimplementedAcknowledgement => ({\n reason,\n trackingRef: UNIMPLEMENTED_ENGINE_OPERATION_BACKLOG_REF,\n});\n\n/**\n * Declarative control over this variant's audit emission.\n *\n * **The baseline needs no declaration.** Every `HIGH` / `CRITICAL` variant emits\n * `RESOURCE_OPERATION_PERFORMED` automatically — `riskLevel` is already the framework's canonical\n * danger axis (it drives MCP destructive hints, frontend danger-zone placement and CRITICAL typed\n * confirmation), and a risk-driven floor is fail-closed by omission. A declarative-ONLY mechanism\n * was rejected for exactly that reason: it fails OPEN when an author forgets, which is how 20 event\n * types came to be declared in `CoreAuditableEventType` and wired to nothing.\n *\n * This declaration exists for the two cases the floor cannot express on its own.\n */\nexport type ResourceOperation_AuditableEvent = {\n /**\n * Emit for this variant even though its `riskLevel` is below the automatic floor.\n *\n * For a `MEDIUM` / `LOW` verb whose danger is not proportional to its blast radius — the risk\n * level answers \"how alarming is the UI affordance\", which is a related but not identical\n * question to \"is this evidence an auditor needs\".\n */\n auditBelowRiskFloor?: boolean;\n};\n\n/*\n * A DELIBERATELY ABSENT second knob — `suppressGenericEvent`.\n *\n * It was designed and then removed after checking whether any verb in the catalogue is actually\n * COVERED by its purpose-built event. None is. The generic row carries the operation identity\n * triple, the top-level `correlationId` that correlates a cascade, the actor's roles as they stood, and the\n * execution type — and NO purpose-built event carries any of those. So the two rows state different\n * facts about one action rather than duplicating each other:\n *\n * - `users | assign_roles` also emits `USER_APP_ROLES_CHANGED`, which uniquely carries\n * `requestedRoles` (the set the role-ceiling gate measured). The generic row uniquely carries\n * who asked, from where, under which variant, correlated to the rest of the request.\n * - `webhookConfig | testEndpoint`, the other candidate, is MEDIUM — below the floor entirely, so\n * nothing would have been suppressed.\n *\n * Shipping the flag anyway would have added authoring vocabulary with zero consumers, which is the\n * precise defect this whole mechanism exists to undo: 19 members of `CoreAuditableEventType` are\n * declared, classified, and emitted by nothing. Add it the day a verb is genuinely covered.\n */\n\ntype ResourceConfiguration_OperationVariant_Base<TMainSchema extends z.ZodTypeAny = z.ZodAny> = {\n variantType : ResourceOperationVariantType;\n resourceOperationLike? : CoreResourceOperation\n generatedOperation?: ResourceConfiguration_GeneratedOperationMetadata;\n /**\n * Feature gating for this operation variant.\n * Shorthand: `string[]` auto-normalized by factory to `{ features: [...], mode: 'OR' }`.\n * Full form: `{ features, mode?, limitChecks? }`.\n */\n requiredFeatures?: RequiredFeaturesConfig;\n isDefault?: boolean; // default true\n variantKey? : string; // default undefined - mandatory if isDefault is false\n\n riskLevel: ResourceOperationRiskLevel;\n /**\n * @wildo_source:part:start saas.models.resource-config.operation-variant-client-timeout facet:layer:shared facet:family:resource-config\n *\n * Frontend HTTP timeout for THIS variant, in milliseconds — declare it on any operation whose\n * synchronous round trip legitimately outlives the frontend client's 30-second default (an\n * operation that calls a model and waits for the answer, a long import, a blocking generation).\n *\n * The frontend resources client applies it as the per-request timeout on every dispatch shape\n * (api-call, api-call-with-callback, search). An explicit caller-supplied\n * `RequestOptions.timeout` still wins; omitting the field keeps the client default.\n *\n * Why it matters: without it, a long-running operation's request is aborted CLIENT-side while\n * the server work succeeds — the caller renders a network error, and the operation-success\n * refresh chain never fires, so lists keep showing the pre-operation world. Declare the\n * expectation on the variant (the operation knows it is long-running); do not hard-code\n * timeouts in consumer hooks, which duplicates that knowledge at every call site.\n *\n * This is a TRANSPORT expectation only — not a UI/observability signal (that is `riskLevel`)\n * and not a server-side bound: the backend is free to answer sooner or be cut off by its own\n * limits.\n *\n * @example a custom operation that generates a document before answering\n * variants: [{\n * variantType: ResourceOperationVariantType.API_CALL,\n * roles: [CORE_ORG_ROLES.ORG_MEMBER],\n * riskLevel: ResourceOperationRiskLevel.MEDIUM,\n * clientRequestTimeoutMs: 300_000, // five minutes — the generation is synchronous\n * requestDto: GenerateReportRequest_Schema,\n * customResponseDto: GenerateReportOutcome_Schema,\n * }]\n */\n clientRequestTimeoutMs?: number;\n /** @wildo_source:part:end saas.models.resource-config.operation-variant-client-timeout */\n roles: Roles[];\n /**\n * Per-foreign-key policy for REFERENCE fields this variant writes — the TARGET-side counterpart of\n * `roles` above (which gates the CALLER).\n *\n * A relationship that declares `scopeMembership` already guarantees the baseline: the referenced row\n * must be linked to the writing row's scope through a junction (that is tenant isolation, and it\n * applies to every variant). This map TIGHTENS that baseline for THIS variant, keyed by foreign-key\n * field — require an `ACTIVE` membership, an `ORG_ADMIN` assignee, a typed partnership — or `false`\n * to disable it for one foreign key on one variant.\n *\n * Omit entirely (the common case): the relationship's baseline membership applies unchanged.\n *\n * @example an \"assign reviewer\" variant whose assignee must be an ACTIVE org admin\n * referenceConstraints: {\n * assignedToUserId: {\n * qualifyingStatuses: ['ACTIVE'],\n * requiredRoles: { scope: ResourcePrimaryScope.ORGANIZATIONS, roles: [CORE_ORG_ROLES.ORG_ADMIN] },\n * },\n * }\n */\n referenceConstraints?: ReferenceConstraints;\n /**\n * Declares this variant a KNOWN, fail-closed implementation gap.\n * See {@link ResourceOperation_UnimplementedAcknowledgement} — presence both unblocks\n * startup and makes the request refuse, so an acknowledged operation never answers 2xx.\n */\n unimplementedAcknowledgement?: ResourceOperation_UnimplementedAcknowledgement;\n /**\n * Declarative control over this variant's audit emission — see\n * {@link ResourceOperation_AuditableEvent}. Omit it: `HIGH` / `CRITICAL` variants are audited\n * automatically, and everything else is deliberately not.\n */\n auditableEvent?: ResourceOperation_AuditableEvent;\n /**\n * Demand a FRESH proof of identity for this specific operation, regardless of how recently the\n * caller authenticated.\n *\n * The framework's baseline step-up is a per-USER-TYPE policy (`UserTypeAuthPolicy.stepUpAuth`):\n * opt-in, defaulting to `enabled: false`, and *freshness*-based — it challenges only once the\n * session's `lastAuthenticatedAt` is older than `maxAgeSeconds` (default 300). That is the right\n * shape for \"this tenant wants periodic re-proof across the board\", and the wrong shape for a\n * single irreversible action: a caller who signed in two minutes ago destroys the resource with\n * no credential proof at all, and an application that never configures `stepUpAuth` is never\n * challenged for anything.\n *\n * Setting this to `true` makes the challenge UNCONDITIONAL for this operation. The caller must\n * present a valid single-use `REAUTH` consumable token (minted by `POST /auth/reauth`, carried in\n * `X-Reauth-Token`); otherwise the request is refused with `STEP_UP_REQUIRED` (HTTP 403). The\n * frontend HTTP client already completes this loop — it catches the 403, runs the registered\n * step-up handler to obtain a token, and replays the request — so declaring the flag is the whole\n * integration.\n *\n * FAIL-CLOSED by design: an operation that declares this is challenged even when the user type\n * has NO `stepUpAuth` policy at all. Deriving the requirement from `riskLevel` was rejected —\n * `riskLevel` is a UI/observability signal (danger-zone placement, typed confirmation, MCP\n * destructive hints), read today by no authorization code, and silently promoting it to a security\n * control would change the behaviour of every existing `CRITICAL` operation at once.\n *\n * Applies only to principals whose identity can be re-proved: user requests that are not\n * sub-calls. Machine principals, internal calls and worker executions are exempt exactly as they\n * are for the baseline policy — there is no interactive credential to re-present.\n *\n * ⚠️ **Check who can actually satisfy it before declaring it.**\n * `AuthMethodManagementBackendService.reAuthenticate` accepts THREE factors — `PASSWORD`, `TOTP`\n * (a code or a backup code, through the same anti-replay authority login uses) and `PASSKEY` (a\n * WebAuthn assertion produced under {@link PasskeyAuthenticationCeremony.STEP_UP}, which pins user\n * verification to REQUIRED and uses a challenge key a login assertion cannot redeem). A principal\n * satisfies this gate if they hold ANY of the three.\n *\n * That was not always true, and the earlier text here is worth knowing because it inverted the\n * advice: until TOTP and passkey re-auth landed, this said the gate was PASSWORD-only and told\n * authors not to declare the flag on operations SSO-only, passkey-only or directory-managed users\n * perform. Both populations can satisfy it now.\n *\n * - **directory-managed (SCIM) users** in particular. The framework deliberately destroys their\n * `passwordHash` AND passkeys as a compliance control\n * (`_reconcileLocalCredentialsForDirectoryManaged` — a local first factor would survive IdP\n * deprovisioning) and deliberately PRESERVES their TOTP, which sits on top of the IdP first\n * factor. TOTP re-auth is what makes that preserved factor usable, and it is why an ORG_ADMIN\n * of a directory-managed tenant can now start a teardown they were once locked out of.\n *\n * A principal holding NONE of the three still cannot satisfy the gate, and is still refused\n * fail-closed with a diagnosable message. Check the user type's `authMethodsEnabled` — and any\n * per-ORGANIZATION override of it, since an app-level policy is not the whole answer — before\n * declaring this flag on an operation that population must perform.\n *\n * This is also why the requirement is a per-operation flag rather than a hardcoded `password`\n * field in a request DTO: the proof is negotiated by the auth policy, not frozen into the resource\n * contract. A `{ password }` field cannot adapt to any of the above.\n *\n * assurance-control: WILDO.IDENTITY.STEP_UP_AUTHENTICATION — re-authentication before a high-impact action.\n */\n /**\n * Admits an APPLICATION-scope platform super-administrator to a resource whose primary scope is a\n * PER-INSTANCE tenant scope (today: `ORGANIZATIONS`) without requiring membership of that tenant.\n *\n * ## Why this exists, and why it is per-variant rather than global\n *\n * `validateParentScopeAccess` authorizes an organization-scoped operation by finding an\n * organization-WIDE `initiatorRoles` entry whose `relatedResourceId` is the target organization. A\n * platform super-administrator holds an APPLICATION-scope entry and therefore matches nothing — so\n * they are denied on every org-scoped resource, INCLUDING the organization row itself. That is\n * normally exactly right: it is what makes tenant isolation hold against the most privileged\n * principal in the system, and it is pinned by\n * `cross-tenant-super-admin-reach.mongo.integration.test.ts`.\n *\n * It also means a tenant that loses its last owner cannot be repaired by anyone, which is the other\n * half of the administrative-continuity pattern: protect the tenant tier and\n * repair it only from a higher authorized tier. This flag is that higher-tier\n * exception and nothing else.\n *\n * **It is deliberately NOT a global super-admin bypass.** Adding one to the authorization layer\n * would grant cross-tenant reach to EVERY org-scoped operation whose gate a super-admin's role\n * chain satisfies — an enormous, silent widening of tenant isolation. Instead each variant opts in\n * explicitly, so the admitted surface is enumerable by grep and empty by default.\n *\n * ## Conditions, all required\n *\n * - The caller must hold `APP_ADMIN_SUPER_ADMIN` at **APPLICATION** scope. An organization-scoped\n * role string that merely spells the same name confers nothing: `getRoleHierarchy` partitions the\n * two role tables, and membership `roles` is free-form (`OrgRolesSchema` admits any string), so\n * without the scope qualifier a tenant admin could type their way into cross-tenant reach.\n * - The variant must declare this flag. Absent, behaviour is unchanged and the caller is denied.\n *\n * Declare it only on operations whose PURPOSE is cross-tenant administration, and expect each one\n * to justify itself: this is the seam a reviewer should look at first.\n *\n * assurance-control: WILDO.ACCESS.CROSS_TENANT_ADMINISTRATION — an explicitly enumerated,\n * platform-tier admission across a tenant boundary that is otherwise absolute.\n */\n admitsCrossTenantPlatformAdministration?: boolean;\n\n /**\n * Admits this variant to address a USER other than the caller — the USER_SELF-scope sibling of\n * {@link admitsCrossTenantPlatformAdministration}, and it follows that flag's doctrine exactly:\n * per-variant, empty by default, enumerable by grep.\n *\n * ## Why it is needed\n *\n * `authorizeUserSelf` authorizes a USER_SELF-scoped operation by requiring the addressed row to BE\n * the caller. On `USERS` — whose `resourcePrimaryScope` IS `USER_SELF`, because the resource is a\n * self-anchor scope root — that made the entire administrative surface self-only: `assign_roles`,\n * `revoke_roles`, `suspend_user`, `unsuspend_user`, `force_password_reset`, the admin READ and the\n * core UPDATE/DELETE all refused `authorizations_access_denied` against another user, while the\n * specification documents every one of them as acting on \"another user's record\" and the admin UI\n * surfaces them as per-row actions.\n *\n * ## Why it is PER-VARIANT and not per-resource\n *\n * This is the whole reason a resource-level declaration cannot serve. Within `USERS` the two READ\n * variants need OPPOSITE answers:\n *\n * - the DEFAULT read is gated `APP_USER`. Admitting cross-subject addressing there would let ANY\n * authenticated user read ANY other user's row — `email`, `appRoles`, `status` — a privacy and\n * enumeration regression far worse than the bug being fixed.\n * - the `admin` read is gated `APP_ADMIN_VIEWER` and is precisely the surface that SHOULD address\n * others.\n *\n * One resource, one field identifier, two required answers. Only the variant can carry it.\n *\n * ## What still protects the surface\n *\n * This flag relaxes WHOSE row may be addressed. It does not touch WHO may call: the operation's\n * `roles` gate runs independently and unchanged, so an admitted variant is still reachable only by\n * the roles it declares. Role decides who; this decides whose. Declare it only where the\n * operation's PURPOSE is administering another person, and expect each one to justify itself.\n *\n * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — an administrative action on another principal\n * is authorized by the caller's ROLE, never by caller and subject being the same person.\n */\n admitsCrossSubjectUserAdministration?: boolean;\n\n requiresStepUpAuthentication?: boolean;\n /**\n * Default **frontend surface** for this operation when no app preset declares\n * one (see {@link ResourceOperationFrontendSurface}). Lets the framework mark\n * an operation headless-by-default (`API_ONLY`) — e.g. system/workflow/signup\n * -driven creates — so apps don't re-author `apiOnly()` per app. An app preset\n * that surfaces the operation (`addressable()`/`notAddressable()`/`apiOnly()`)\n * always wins; this is only the fallback. When unset, an unsurfaced operation\n * resolves to `API_ONLY` (the safe default — callable, no UI). Display-only:\n * never affects authorization.\n */\n defaultFrontendPosture?: ResourceOperationFrontendSurface;\n /**\n * @wildo_source:part:start saas.models.resource-config.operation-mcp-exposure facet:layer:shared facet:family:resource-config\n *\n * MCP (Model Context Protocol) exposure for this operation. Set `exposed: true` to\n * surface the operation as an MCP tool that authenticated machine / agent callers can\n * discover (`tools/list`) and invoke (`tools/call`). Import path: `@wildo-ai/saas-models`\n * — this is a field on an operation variant inside a resource's `operations` config.\n *\n * Opt-in + FAIL-CLOSED: absent or `false` ⇒ the operation is NOT a tool. Only DEFAULT,\n * non-bulk, URL-bearing operations (the standard `API_CALL` create / read / list / search /\n * count / update / delete and URL-bearing custom ops) of ORGANIZATION- or APPLICATION-scoped\n * resources are eligible. A machine principal cannot reach USER_SELF resources, so setting\n * `exposed` on a USER_SELF operation has no machine-callable effect.\n *\n * `description` is the agent-facing tool description — the prose the calling agent reads to\n * decide when to use the tool. When omitted, the tool description falls back to the resource\n * specification's `purpose` / `whenToUse`, then to a synthesized default; keep an authored\n * `description` reconciled with the spec.\n *\n * Exposure is a DISCOVERY gate ONLY. Every `tools/call` still re-runs the operation's own\n * role authorization against the CALLER's roles before dispatch, so exposing an operation can\n * never widen who is allowed to perform it.\n *\n * `servers` selects WHICH named MCP server(s) carry this tool when the app declares more than one\n * (each server is a separate endpoint + audience, so a token for one is rejected at another). OMIT it\n * for the DEFAULT server — the plain `/mcp` endpoint every app has. When PRESENT it must name at least\n * one declared server: an EMPTY array is a startup error, not a silent fallback, so the field always\n * reads as \"exactly these\" (the same omitted-vs-present rule an A2A agent's `actorSystemRefs` follows).\n * Naming a server the app does not declare is likewise a startup error (fail-closed), so a typo can\n * never conjure a phantom surface.\n *\n * Membership is EXCLUSIVE: an operation naming a server LEAVES the default `/mcp` catalog rather than\n * appearing on both, so curation is always a deliberate move and never an accidental duplication.\n */\n mcp?: {\n exposed?: boolean;\n description?: string;\n servers?: string[];\n };\n /** @wildo_source:part:end saas.models.resource-config.operation-mcp-exposure */\n requestDto?: z.ZodSchema<any>;\n /**\n * RepositoryDto for repository projection.\n * Excludes virtual fields and populated placeholders.\n * Calculated at factory execution time.\n */\n repositoryDto?: z.ZodSchema<any>;\n customServiceImplementationModes?: ResourceOperation_CustomServiceImplementationMode[];\n /**\n * Function to modify input before core operation.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * basicPrefixOperation.needsObjectContext = true\n * basicPrefixOperation.needsUserContext = true\n *\n * Returns a PATCH, not a replacement: the service MERGES the result onto the payload\n * (`coreInput = { ...coreInput, ...result }`) and whitelists the keys the hook ADDED into\n * `preserveServerInjectedFields`, so they survive the repository's strict re-parse. Naming\n * only the fields the verb changes is therefore both sufficient and safer than spreading the\n * current object — a spread makes every field a server-injected key, which re-writes\n * `.excludeFromUpdate()` fields such as an owning foreign key on every call.\n */\n basicPrefixOperation?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => Partial<MainSchemaInfer<TMainSchema>>\n >; // default undefined\n /**\n * Fields whose value this operation's {@link basicPrefixOperation} OWNS.\n *\n * Declare a field here when the hook must set it EVEN IF the caller also supplied it. The\n * \"keys the hook ADDED\" inference described above is derived from a snapshot of the payload\n * taken at the SERVICE tier, after a LOOSE parse (`z.looseObject`) that passes unknown keys\n * through. A caller that reaches that tier with the field already present — MCP `callTool`\n * forwards its arguments with no strict parse — puts the key into the snapshot, so the hook's\n * own value stops being treated as injected, the repository's strict re-parse drops it, and the\n * operation answers success having written nothing.\n *\n * Declaring the field makes the ownership explicit: it is always preserved, always with the\n * POST-hook value, so a caller-supplied value is overwritten rather than honored. This does NOT\n * make the field client-writable — reachability stays governed by {@link requestDto} and the\n * schema decorators.\n *\n * Any hook that forces a server-owned lifecycle value (a `status` transition, an ownership\n * stamp) should declare it. A hook that only derives a value from other authored input does not\n * need to, because a caller could not have supplied it under a strict contract anyway.\n *\n * Typed `readonly string[]` rather than `keyof MainSchemaInfer<TMainSchema>`: this type is shared\n * by the authoring parameters AND the built operation, and the built side is assembled behind a\n * type-erased generic where `keyof` collapses to `never`. The stronger check lives at BOOT\n * instead — `collectResourceOperationAuthoritativeFieldIssues` refuses startup when a declared\n * name is not a persisted field of the resource. That guard is strictly better than the\n * compile-time one would have been, because it also covers custom-implementation registrations,\n * whose declarations are plain strings with no schema generic to check against. A typo must fail\n * loudly: silently declaring nothing would restore the exact no-op this contract prevents.\n */\n authoritativeFields?: readonly string[];\n}\n\n\n// --------------------------------\n// Resource Configuration Builder Parameters\n// --------------------------------\n\n\ntype ResourceConfiguration_OperationVariant_ApiCall_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = ResourceConfiguration_OperationVariant_Base<TMainSchema> & {\n\n variantType : ResourceOperationVariantType.API_CALL;\n isDefault?: boolean; // default true\n haveBulkOperation?: boolean; // default false - not possible for CoreResourceOperation.READ or LIST\n isApiKeyAccessDisabled?: boolean; // default false\n /** Declares which consumable token types this operation accepts for authentication */\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n /** Declarative token generation config — creates a token as side-effect after successful operation */\n tokenGeneration?: TokenGenerationConfig; // default undefined\n /** Declarative token revocation config — revokes tokens before the core operation (e.g., on DELETE) */\n tokenRevocation?: TokenRevocationConfig; // default undefined\n customResponseDto?: z.ZodSchema<any>;\n /**\n * Per-endpoint rate-limit policy.\n *\n * When present, the resource controller's pre-flight check enforces this\n * policy BEFORE execution-context creation, request validation, and\n * authorization (via `RateLimitBackendService.checkPolicyForVariant`). At\n * that seam the authenticated identity is not yet known, so the client\n * identifier is the IP address (`getClientIpFromRequest`).\n *\n * When absent, only the global HTTP rate-limit\n * (`RuntimeConfig.httpServerConfiguration.rateLimiting`) gates this route\n * — that limiter is the safety net for endpoints with no per-endpoint\n * policy.\n *\n * Multiple windows AND together (most restrictive wins). On 429 the\n * `X-RateLimit-*` headers report the **tightest active window** (fewest\n * remaining slots), while `Retry-After` / `retryAfterSeconds` reports\n * the **latest tripped window** because declared windows are AND-ed.\n * These can be different windows when multiple trip simultaneously.\n *\n * The declared policy is surfaced through standard rate-limit response\n * headers and retry guidance, so callers see the same request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n}\n\ntype ResourceConfiguration_OperationVariant_ApiCallWithCallback_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = ResourceConfiguration_OperationVariant_Base<TMainSchema> & {\n\n variantType : ResourceOperationVariantType.API_CALL_WITH_CALLBACK;\n isDefault?: boolean; // default true\n haveBulkOperation?: boolean; // default false - not possible for CoreResourceOperation.READ or LIST\n isApiKeyAccessDisabled?: boolean; // default false\n /** Declares which consumable token types this operation accepts for authentication */\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n /** Declarative token generation config — creates a token as side-effect after successful operation */\n tokenGeneration?: TokenGenerationConfig; // default undefined\n /** Declarative token revocation config — revokes tokens before the core operation (e.g., on DELETE) */\n tokenRevocation?: TokenRevocationConfig; // default undefined\n customResponseDto?: z.ZodSchema<any>;\n /**\n * Per-endpoint rate-limit policy. Same semantics as on the API_CALL\n * variant — enforced pre-flight by the resource controller before EC\n * creation, validation, and authorization.\n *\n * The declared policy is surfaced through standard rate-limit response\n * headers and retry guidance, so callers see the same request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n callbackUrl?: string; // Optional callback URL for async operations\n callbackTimeout?: number; // Timeout in seconds for callback operations, default 300 (5 minutes)\n callbackRetryAttempts?: number; // Number of retry attempts for failed callbacks, default 3\n}\n\ntype ResourceConfiguration_OperationVariant_CronJob_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.CRON_JOB,\n cronExpression: string,\n /**\n * Explicit response contract for a CRON_JOB custom operation whose return shape\n * is NOT the resource row implied by `resourceOperationLike`. A scheduled\n * generator/sweeper (e.g. a recurring-occurrence generator) borrows a core verb\n * (UPDATE/CREATE) for its repo/DTO semantics but returns a RUN SUMMARY, not an\n * occurrence. The factory already reads `customResponseDto` off any variant\n * params (`resources-config.shared.factory.ts`) and warns when a borrowed-verb\n * custom op omits it — this exposes it on the CRON_JOB params so the cron op can\n * declare its true return shape instead of inheriting the verb's occurrence\n * -shaped envelope (which the executor would reject the summary against).\n */\n customResponseDto?: z.ZodSchema<any>,\n}\ntype ResourceConfiguration_OperationVariant_BatchJob_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.BATCH_JOB\n}\ntype ResourceConfiguration_OperationVariant_InternalCall_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.INTERNAL_CALL\n}\ntype ResourceConfiguration_OperationVariant_RepositoryOnly_BuilderParameters = ResourceConfiguration_OperationVariant_Base & {\n variantType : ResourceOperationVariantType.REPOSITORY_ONLY\n}\n\n\n// --------------------------------\n// Resource Final Configuration\n// --------------------------------\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-base facet:layer:shared facet:family:resource-config\n *\n * Base operation configuration type.\n * NOTE: TDatabaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in mainSchema and are stripped at the service layer.\n *\n * @remarks Import from `@wildo-ai/saas-models`. App `resources-config` fills these per operation variant.\n */\nexport type ResourceConfiguration_Operation_Base<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_OperationVariant_Base<TMainSchema> & {\n isDisabled: boolean, // default false\n mainSchema: TMainSchema,\n resourcePrimaryScope : ResourcePrimaryScope, // default ResourcePrimaryScope.ORGANIZATIONS\n resourceIdentifier : ResourceType,\n resourceFieldIdentifier : ResourceFieldIdentifier,\n\n resourceRelationships : ResourceConfiguration_ResourceRelationships,\n\n isSystemResource: boolean, // default false - true for core system resources (organizations, users, etc.), false for tenant-specific resources\n operationIdentifier : TCustomOperationEnum | TCoreOperationEnum,\n isOperationDefault : boolean, // default true\n serviceRuntimeMode: ResourceOperation_ServiceRuntimeMode; //default ResourceOperation_ServiceRuntimeMode.INLINE\n generatedOperation?: ResourceConfiguration_GeneratedOperationMetadata;\n\n requestDto?: z.ZodSchema<any>;\n responseDto?: z.ZodSchema<any>;\n /** Set by factory when an explicit `requestDto` was provided in variant params.\n * Allows the DTO builder to skip heuristic comparison and trust the flag:\n * `true` ⇒ preserve the supplied schema verbatim; `false` ⇒ the requestDto is\n * the factory's auto-derived temporary placeholder, so always use the\n * inheritance-rebuilt DTO (a discriminated union for inheritance resources).\n * Absent only on legacy/hand-authored operations that predate the flag. */\n hasCustomRequestDto?: boolean;\n /** Set by factory when `customResponseDto` was explicitly provided in variant params.\n * Allows the DTO builder to skip heuristic comparison and always preserve the custom schema. */\n hasCustomResponseDto?: boolean;\n /**\n * SummaryDto contains only fields marked with isSummaryField decorator.\n * Used for LIST operations in SUMMARY mode.\n * Calculated at factory execution time.\n */\n summaryDto?: z.ZodSchema<any>;\n /**\n * ContextDto includes relationship placeholders for context-dependent operations.\n * Used for operations in CONTEXT mode.\n * Calculated at factory execution time.\n */\n contextDto?: z.ZodSchema<any>;\n /**\n * InternalDto includes backend-only fields (isBackendOnly decorator).\n * Used for backend-to-backend communication (queues, internal calls).\n * Calculated at factory execution time.\n */\n internalDto?: z.ZodSchema<any>;\n /**\n * RepositoryDto for repository projection.\n * Excludes virtual fields and populated placeholders.\n * Calculated at factory execution time.\n */\n repositoryDto?: z.ZodSchema<any>;\n\n notificationBadgeOperations: { identifier : NotificationBadgeIdentifier, operation : NotificationBadgeOperation }[]; //\n userNotifications: UserNotificationDefinition[];\n m2mNotifications: M2MNotificationDefinitionWithoutIdentifier[];\n }\n/** @wildo_source:part:end saas.models.resource-config.operation-base */\n\n/**\n * Base operation type - explicitly extracted for better type checking performance.\n * TypeScript can cache this more efficiently when it's a named type alias.\n * This helps reduce memory usage during compilation when processing union types.\n */\nexport type ResourceConfiguration_Operation_BaseType<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n> = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>;\n\n\nexport type ResourceConfiguration_Operation_CronJob<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.CRON_JOB;\n cronExpression: string,\n }\n\nexport type ResourceConfiguration_Operation_BatchJob<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.BATCH_JOB;\n }\n\nexport type ResourceConfiguration_Operation_InternalCall<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.INTERNAL_CALL;\n }\n\nexport type ResourceConfiguration_Operation_RepositoryOnly<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.REPOSITORY_ONLY;\n }\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-api-call facet:layer:shared facet:family:resource-config\n *\n * HTTP API surface for an operation: URL templates, method, tokens,\n * enabledCondition, and per-endpoint rate-limit policy (`rateLimit?`).\n *\n * @remarks Import from `@wildo-ai/saas-models`.\n */\n// URL parameter information for each URL\nexport type ResourceConfiguration_Operation_UrlParameterInfo = {\n fieldIdentifier: string;\n resourceType: ResourceType;\n /**\n * The FK relationship field if this parameter comes from a FK relationship.\n * e.g., 'assignedToUserId' for /assigned-to-users/{userId}/...\n * undefined for default parent relationships.\n */\n relationshipField?: string;\n /**\n * Scope-via-junction tag, propagated from the synthetic scope-anchored parent\n * edge (`ResourceRelationship.scopeViaJunction`). When set, this param scopes\n * the (target) resource's SEARCH/LIST through the named junction `J` rather\n * than via a direct column the resource owns — e.g. on\n * `/organizations/{organizationId}/users/search`, `organizationId` carries\n * `scopeViaJunction: 'organizationMembers'`. The backend contextual filter +\n * authorization read this off the runtime relationship context. Absent on\n * normal parent params.\n */\n scopeViaJunction?: ResourceType;\n};\n\n// URL information structure\nexport type ResourceConfiguration_Operation_UrlInitiatorInfo = {\n url: string;\n urlCallback?: string;\n parameters: ResourceConfiguration_Operation_UrlParameterInfo[];\n};\n\nexport type ResourceConfiguration_Operation_ApiCall<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.API_CALL;\n httpVerb: HttpMethod;\n isApiKeyAccessDisabled: boolean; // default false\n isBulkOperation: boolean; // default false\n initiatorInfos?: ResourceConfiguration_Operation_UrlInitiatorInfo[];\n\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n tokenGeneration?: TokenGenerationConfig; // default undefined\n tokenRevocation?: TokenRevocationConfig; // default undefined\n /**\n * Resolved per-endpoint rate-limit policy (forwarded by the resource\n * configuration factory from the variant builder parameters). Read at\n * runtime by `RateLimitBackendService.checkPolicyForVariant` at the\n * controller pre-flight seam (`controller-resource.backend.service.ts`\n * → `doOperation`).\n *\n * Auto-variants (`SUMMARY` / `CONTEXT` for READ/LIST) inherit this\n * field from their default URL-bearing variant (`API_CALL` or\n * `API_CALL_WITH_CALLBACK`) — a single declaration on the default variant\n * covers all derived auto-variants by design.\n *\n * The declaration is inherited by derived variants so every route for this\n * operation presents one consistent request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n }\n/** @wildo_source:part:end saas.models.resource-config.operation-api-call */\n\nexport type ResourceConfiguration_Operation_ApiCallWithCallback<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n > = ResourceConfiguration_Operation_Base<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> & {\n variantType : ResourceOperationVariantType.API_CALL_WITH_CALLBACK;\n httpVerb: HttpMethod;\n isApiKeyAccessDisabled: boolean; // default false\n isBulkOperation: boolean; // default false\n initiatorInfos?: ResourceConfiguration_Operation_UrlInitiatorInfo[];\n\n tokenAuthentication?: TokenAuthenticationConfig; // default undefined\n tokenGeneration?: TokenGenerationConfig; // default undefined\n tokenRevocation?: TokenRevocationConfig; // default undefined\n /**\n * Resolved per-endpoint rate-limit policy. Same semantics as on the\n * API_CALL variant.\n *\n * The declaration is inherited by derived variants so every route for this\n * operation presents one consistent request budget.\n */\n rateLimit?: RateLimitConfiguration; // default undefined\n /**\n * Function to determine if operation variant is enabled.\n * Available in both frontend and backend.\n * Can optionally specify needsObjectContext/needsUserContext flags:\n * enabledCondition.needsObjectContext = true\n * enabledCondition.needsUserContext = true\n */\n enabledCondition?: ResourceConfiguration_FunctionWithContextNeeds<\n (params: ResourceConfiguration_OperationVariant_CustomImplementation_FunctionParameters<TMainSchema>) => boolean\n >; // default undefined\n callbackUrl?: string; // Optional callback URL for async operations\n callbackTimeout: number; // Timeout in seconds for callback operations, default 300 (5 minutes)\n callbackRetryAttempts: number; // Number of retry attempts for failed callbacks, default 3\n }\n\nexport type ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> =\n ResourceConfiguration_OperationVariant_ApiCall_BuilderParameters<TMainSchema> |\n ResourceConfiguration_OperationVariant_ApiCallWithCallback_BuilderParameters<TMainSchema> |\n ResourceConfiguration_OperationVariant_CronJob_BuilderParameters |\n ResourceConfiguration_OperationVariant_BatchJob_BuilderParameters |\n ResourceConfiguration_OperationVariant_InternalCall_BuilderParameters |\n ResourceConfiguration_OperationVariant_RepositoryOnly_BuilderParameters;\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-variant-for-key facet:layer:shared facet:family:resource-config\n *\n * Variant builder type that distributes operation-specific fields based on the operation key.\n *\n * Collection-view fields are intersected with the **full** variant union — not restricted to\n * API_CALL. The variant type (\"how is this triggered?\") and collection-view fields (\"how does\n * this operation query/present a collection?\") are orthogonal concerns. A REPOSITORY_ONLY LIST\n * still lists things and may need pageSize, filterFields, sortFields for internal queries.\n *\n * Only collection-style operations get operation-specific frontend/query\n * metadata here. Action-style behavior for custom operations is modeled\n * explicitly through custom operation identifiers plus variant/runtime config,\n * not through a special core operation.\n */\nexport type OperationVariant_ForKey<\n K,\n TMainSchema extends z.ZodTypeAny = z.ZodAny\n> =\n K extends CoreResourceOperation.LIST\n ? ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema> & CollectionViewFields_List\n : K extends CoreResourceOperation.SEARCH\n ? ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema> & CollectionViewFields_Search\n : K extends CoreResourceOperation\n ? ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n : ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n | (\n ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n & { resourceOperationLike: CoreResourceOperation.LIST }\n & CollectionViewFields_List\n )\n | (\n ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>\n & { resourceOperationLike: CoreResourceOperation.SEARCH }\n & CollectionViewFields_Search\n );\n\n/**\n * Operation config with variant type narrowed by operation key.\n * Distributes at the operation-config level; collection-view fields live on the variant.\n */\nexport type OperationConfig_ForKey<\n K,\n TMainSchema extends z.ZodTypeAny = z.ZodAny\n> = {\n variants: OperationVariant_ForKey<K, TMainSchema>[];\n serviceRuntimeMode?: ResourceOperation_ServiceRuntimeMode;\n parentResourceAccessScopeStrategyOverrides?: { resourceType: ResourceType, accessScopeStrategy: ResourceRelationshipAccessScopeStrategy }[];\n userNotifications?: UserNotificationDefinitionWithoutPrimaryScopeAndIdentifier[];\n notificationBadgeOperations?: { identifier: NotificationBadgeIdentifier, operation: NotificationBadgeOperation }[];\n m2mNotifications?: M2MNotificationDefinitionWithoutIdentifier[];\n};\n/** @wildo_source:part:end saas.models.resource-config.operation-variant-for-key */\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-union facet:layer:shared facet:family:resource-config\n *\n * Discriminated union of resolved operation configuration shapes (variant discriminant).\n */\nexport type ResourceConfiguration_Operation<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny = z.ZodAny,\n> =\n ResourceConfiguration_Operation_ApiCall<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_ApiCallWithCallback<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_CronJob<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_BatchJob<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_InternalCall<TCustomOperationEnum, TCoreOperationEnum, TMainSchema> |\n ResourceConfiguration_Operation_RepositoryOnly<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>;\n/** @wildo_source:part:end saas.models.resource-config.operation-union */\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.operation-path facet:layer:shared facet:family:resource-config\n *\n * Path key for routing: resource, operation, variant, optional variantKey / bulk.\n */\nexport type ResourceConfiguration_OperationPath<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined\n> = {\n resourceIdentifier: ResourceType;\n operationIdentifier: TCustomOperationEnum | TCoreOperationEnum;\n variantType : ResourceOperationVariantType\n isOperationDefault?: boolean;\n isBulkOperation?: boolean;\n variantKey?: string;\n\n}\n/** @wildo_source:part:end saas.models.resource-config.operation-path */\n\n\n\nexport type ResourceConfiguration_OperationRepositoryPath = {\n resourceIdentifier: ResourceType;\n operationIdentifier: CoreResourceOperation;\n /**\n * Actual configured operation identifier when a custom operation is routed\n * through repository logic using `resourceOperationLike`.\n *\n * This preserves action identity for repository lookups so multiple custom\n * operations that share the same effective core behavior do not collide.\n */\n operationRef?: string;\n isBulkOperation?: boolean;\n}\n\n// --------------------------------\n// DELETED — Frontend Resource Configuration (transferred to @wildo-ai/saas-frontend-lib)\n// See: ResourceUIBehavior_EmptyState, ResourceFrontend_GuidanceElement,\n// ResourceUIBehavior_GuidanceFlow, ResourceUIBehaviorConfig\n// --------------------------------\n\n/**\n * Helper type for individual inherited schema configuration.\n * NOTE: databaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in the schema and are stripped at the service layer.\n */\nexport type ResourceConfiguration_InheritanceItemSchemaDefinition<\n TDiscriminationEnum extends EnumLikeType,\n TMainSchema extends z.ZodTypeAny,\n TInheritedSchema extends TMainSchema = TMainSchema,\n> = {\n discriminatorFieldValue: string\n schema: TInheritedSchema,\n inheritenceSchemaDefinition? : ResourceConfiguration_InheritanceSchemaDefinition<EnumLikeType, TInheritedSchema>,\n}\n\nexport type ResourceConfiguration_InheritanceSchemaDefinition<\n TDiscriminationEnum extends EnumLikeType,\n TMainSchema extends z.ZodTypeAny,\n> = {\n discriminatorField : string,\n withoutInheritanceDiscriminatorFieldValues? : string[],\n inheritedSchemas : ResourceConfiguration_InheritanceItemSchemaDefinition<TDiscriminationEnum, TMainSchema>[],\n}\n\n\nexport type ResourceConfiguration_Operation_BuilderParameters<TMainSchema extends z.ZodTypeAny = z.ZodAny> = {\n variants: ResourceConfiguration_OperationVariant_BuilderParameters<TMainSchema>[];\n serviceRuntimeMode?: ResourceOperation_ServiceRuntimeMode; // default ResourceOperation_ServiceRuntimeMode.INLINE\n parentResourceAccessScopeStrategyOverrides?: { resourceType: ResourceType, accessScopeStrategy: ResourceRelationshipAccessScopeStrategy }[]; // default undefined\n\n userNotifications?: UserNotificationDefinitionWithoutPrimaryScopeAndIdentifier[];\n notificationBadgeOperations?: { identifier : NotificationBadgeIdentifier, operation : NotificationBadgeOperation }[];\n m2mNotifications?: M2MNotificationDefinitionWithoutIdentifier[];\n}\n/**\n * @wildo_source:part:start saas.models.resource-config.builder-parameters facet:layer:shared facet:family:resource-config\n *\n * Valid operation keys from core + custom enums, and builder params for `createResourceConfiguration`.\n */\n// Helper type to extract valid operation keys, excluding undefined\nexport type ValidOperationKeys<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined\n> = (TCustomOperationEnum extends EnumLikeType ? EnumValues<TCustomOperationEnum> : never) |\n (TCoreOperationEnum extends Partial<CoreResourceOperation> ? TCoreOperationEnum : never);\n\n/**\n * @wildo_source:part:start saas.models.resource-config.system-access-policy facet:layer:shared facet:family:resource-config\n *\n * Access mode for the ACL-bypassing system-context reads\n * (`SystemAccessBackendService.readAsSystem` / `listAsSystem`, from\n * `@wildo-ai/saas-backend-lib`).\n *\n * Those primitives run with a synthesised super-admin execution context and\n * therefore SKIP the caller's scope and role checks entirely. A resource is\n * readable that way ONLY if its resource configuration opts the verb in. Any\n * verb left undeclared defaults to `FORBIDDEN`: a system read of a resource\n * that has not opted in throws `ErrorType.CONFIGURATION` — the framework is\n * fail-closed here by construction.\n */\nexport enum ResourceSystemAccessMode {\n /** The verb's system-context primitive is rejected for this resource (default). */\n FORBIDDEN = 'FORBIDDEN',\n /** The verb's system-context primitive is permitted for this resource. */\n ALLOWED = 'ALLOWED',\n}\n\n/**\n * Per-resource opt-in policy for the ACL-bypassing system-context primitives\n * on `SystemAccessBackendService`: the reads (`readAsSystem` / `listAsSystem`)\n * and the trusted writes (`createAsSystem` / `updateAsSystem` /\n * `updateManyAsSystem`).\n *\n * A system-context call does not REMOVE the authorization decision — it\n * RELOCATES it from the resource ACL onto the calling service, which MUST\n * enforce an explicit alternative check (e.g. consumable-token or\n * email-ownership matching). Declaring this policy is the data owner's\n * statement that such a bypass is permissible at all, and for which verb. An\n * absent policy — or a verb set to (or left defaulting to) `FORBIDDEN` — makes\n * the primitive throw.\n *\n * The WRITE verbs additionally let a trusted in-process caller persist the\n * fields the client-shaped request DTO strips — `.isBackendOnly()` material\n * (e.g. a credential `passwordHash`) and the context-auto-populated FKs (e.g.\n * `userId` on a system resource that has no HTTP surface to seed it) — by\n * re-attaching exactly those fields after the strict parse. They are the\n * sanctioned path for engine flows like registration that must write system\n * resources directly; opting a verb in is the data owner's acknowledgement that\n * a super-admin-context mutation of this resource is legitimate.\n *\n * Caveats a system-context call inherits (it runs as super-admin), to weigh\n * before opting in:\n * - a READ returns the FULL record — every field, including ones a normal\n * caller's role / field-visibility would hide — so never forward the result\n * verbatim to an under-privileged caller; project only what is safe.\n * - a WRITE bypasses the caller's scope + role ACL, so the calling service\n * owns the authorization decision (only trusted engine code can reach the\n * primitive — it is never exposed to an HTTP payload).\n * - feature flags do not gate it (it is an internal request).\n * - the policy is RESOURCE-LEVEL: for a polymorphic resource it is shared by\n * every scope variant (declare it once on the shared config).\n *\n * Intentionally DISTINCT from `isSystemResource`: marking a resource as\n * framework infrastructure does NOT imply that an ACL bypass is acceptable\n * for its data. Keep the two concepts separate.\n */\nexport type ResourceSystemAccessPolicy = {\n /** Governs `readAsSystem`. Omitted ⇒ FORBIDDEN. */\n read?: ResourceSystemAccessMode,\n /** Governs `listAsSystem`. Omitted ⇒ FORBIDDEN. */\n list?: ResourceSystemAccessMode,\n /** Governs `createAsSystem`. Omitted ⇒ FORBIDDEN. */\n create?: ResourceSystemAccessMode,\n /** Governs `updateAsSystem`. Omitted ⇒ FORBIDDEN. */\n update?: ResourceSystemAccessMode,\n /** Governs `updateManyAsSystem`. Omitted ⇒ FORBIDDEN. */\n updateMany?: ResourceSystemAccessMode,\n // NOTE: there is deliberately NO `delete` verb here — there is no `deleteAsSystem` primitive. A row is\n // removed either through its own DELETE operation ACL (e.g. an owner-facing `API_CALL` DELETE under the\n // USER_SELF owner filter) or by the `onParentDelete` composition cascade (which deletes at the raw\n // repository layer, variant-agnostic — see `deleteChildrenImmediate`). If you find yourself reaching\n // for `systemAccessPolicy.delete`, model the removal as one of those two instead.\n /**\n * Governs the impersonalization see-through reads (`readRetainedAsSystem` /\n * `listRetainedAsSystem`) — the privileged, audited path that reveals\n * `retentionStatus = RETAINED` rows hidden-by-default. Omitted ⇒ FORBIDDEN.\n * Auto-derived to `ALLOWED` by the factory when a resource opts into\n * `retentionPolicy`; never authored by hand in the common case.\n *\n * **LIVE.** `readRetainedAsSystem` / `listRetainedAsSystem` exist and are enforced: the verb is\n * asserted fail-closed, a privileged human role is required, and every reveal emits a\n * human-attributed `RETAINED_DATA_ACCESS` audit event. (This block previously said the primitives\n * were \"wired in a later phase\" — false since the M5b work landed.)\n */\n seeRetained?: ResourceSystemAccessMode,\n\n /**\n * May this resource's rows be swept into a SUBJECT EXPORT bundle (GDPR Art. 15 / Art. 20)?\n *\n * Fail-closed by omission, like every other verb here. A subject export reads across the whole\n * child-direction graph of its subject, so without an explicit opt-in a resource could be\n * disclosed to a data subject because it merely sits on an edge — nobody having decided that its\n * contents are appropriate to hand over.\n *\n * **The refusal is LOUD, not a silent skip.** When an edge the classifier marked for inclusion\n * belongs to a resource that has not opted in, the export REFUSES and names it. Silently dropping\n * it would produce an access bundle that is incomplete without saying so — a disclosure failure\n * wearing the appearance of a successful response, which is the same class of lie as the routed\n * export operations this capability replaced. Refusing is a one-line fix per resource; a silent\n * omission is undetectable.\n *\n * NOT derived from `portabilityPolicy`: declaring where fields came from (Art. 20 scoping) and\n * consenting to be disclosed at all are different decisions, and a resource may legitimately want\n * the first without the second.\n */\n exportSubject?: ResourceSystemAccessMode,\n};\n/** @wildo_source:part:end saas.models.resource-config.system-access-policy */\n\n/**\n * Resource-level opt-in to the **impersonalization-on-erasure** capability: on erasure the\n * resource is impersonalized-and-retained (personal fields scrubbed, row kept for a legal/audit\n * obligation) instead of hard-deleted. When set, the factory injects a `retentionStatus` marker\n * and derives the `updateMany` + `seeRetained` system-access grants.\n *\n * RESOURCE-level (intrinsic, trigger-agnostic). The per-EDGE trigger is\n * `onParentDelete.strategy = IMPERSONALIZE_AND_RETAIN`; the per-FIELD scrub is `.impersonalizeWith()`.\n *\n * **LIVE — opting in has real effect today.** The marker + derived grants, the in-place scrub writer,\n * the `IMPERSONALIZE_AND_RETAIN` cascade mode and the hidden-by-default read filter all exist and are\n * enforced. A RETAINED row is invisible to reads, lists, counts and CHART aggregates, and immutable\n * to ordinary writes, on BOTH persistence adapters; the only door is the audited see-through path.\n *\n * Runtime-proven, not asserted — and scoped precisely, because an earlier revision of this block\n * over-claimed it. Against a real MongoDB, the erasure orchestrator reaches its terminal path and its\n * receipt is truthful over a registered subset of resources\n * (`subject-erasure-completion.mongo.integration.test.ts`); the retention OUTCOME and a SECOND erasure\n * are observed (`subject-impersonalization-retention-outcome.mongo.integration.test.ts`); and a\n * retained row is absent from a real application chart over real HTTP\n * (Wonder Todos `e2e/chart-retention-hide.e2e.ts`).\n *\n * What is NOT proven: that an erasure completes over the FULL `onParentDelete` closure, and that\n * external (provider-side) effects run. The completion lane deliberately requires\n * `declinedEdges.length > 0`, so it asserts a state in which some edges were NOT executed, and\n * `externalEffects` is stubbed empty. Do not read \"runtime-proven\" here as end-to-end completeness.\n *\n * (This block previously read \"Contract only in this phase … No record is impersonalized-and-retained\n * yet.\" That was true when written and became false without being revisited — it is the public\n * authoring contract an app developer reads before declaring this policy, so it is corrected here\n * rather than left to mislead.)\n *\n * ⚠️ NOT the anonymous-user concept (`isAnonymizable` is pre-auth ownership transposition — the\n * opposite direction). Reuse of that mechanism, never its name.\n */\n/**\n * WHO a row of this resource is with respect to an erasure, and where that person's session lives.\n * The companion of {@link ResourceRetentionPolicy}: retention says what happens to the ROW and whether\n * the row holds personal data at all; this says **whose SESSION an erasure of it must end**.\n *\n * The two are independent, and reading this one as \"is the row about a person\" is the mistake that\n * cost `NO_PRINCIPAL` its previous name — see {@link ResourceDataSubjectKind}. A CRM contact holds\n * personal data (`RETAIN_AND_IMPERSONALIZE`) and has no session (`NO_PRINCIPAL`); an invoice holds\n * none (`RETAIN_ONLY`) and also has no session. Same value here, opposite compliance meaning, and the\n * retention mode is what tells them apart.\n *\n * Required on every resource declaring a {@link ResourceRetentionPolicy}, because those are exactly\n * the resources that can be passed to `eraseSubjectAsSystem` as a subject (the orchestrator refuses a\n * subject with no retention policy). Undeclared fails at config build rather than defaulting: the safe\n * default is unknowable — guessing `NO_PRINCIPAL` silently drops a real person's revocation, and\n * guessing a linked principal revokes the wrong people. See {@link ResourceDataSubjectKind} for why\n * the linkage can never be inferred from the presence of a user foreign key.\n */\nexport interface ResourceDataSubjectPolicy {\n /** Whose login session an erasure of this row must end — never whether the row is about a person. */\n kind: ResourceDataSubjectKind;\n /**\n * The field naming the `USERS` row whose session an erasure of this row must end.\n *\n * REQUIRED when {@link kind} is `LINKED_PRINCIPAL`, and FORBIDDEN otherwise — under\n * `SELF_PRINCIPAL` the row's own id is the principal, and under `NO_PRINCIPAL` there is no\n * principal at all, so a field there would be a contradiction rather than redundancy. Both\n * directions are enforced at config build.\n */\n sessionPrincipalField?: string;\n}\n\nexport interface ResourceRetentionPolicy {\n /**\n * WHAT happens to a row of this resource when its subject is erased.\n *\n * Both modes retain the row (kept, marked `RETAINED`, hidden from normal reads and immutable,\n * readable only through the audited see-through path). They differ on whether the row's own FIELDS\n * additionally need scrubbing — which is a genuinely separate question, because a record can be\n * legally retainable while holding no personal data of its own (a financial row that merely POINTS\n * at a person). Fusing the two forced such a resource to either invent a fake scrub or forgo\n * retention entirely; keeping them distinct is what makes \"retain, nothing to scrub\" expressible.\n */\n mode: ErasureRetentionMode;\n}\n\n/**\n * Resource-level DEFAULT for where this resource's field values came from — the axis that decides\n * GDPR Art. 20 (portability) scope. Per-field overrides are `.portabilityProvenance()`.\n *\n * ## Why a resource-level default exists at all\n *\n * Art. 20 is intrinsically a per-FIELD question: one row routinely mixes data the subject supplied\n * (`firstName`) with data the controller produced about them (a computed score, a server-assigned\n * status). But requiring a decorator on EVERY field of every resource would make the capability\n * unadoptable, and an unadopted disclosure capability is worse than a coarse one — it means\n * portability requests cannot be answered at all.\n *\n * So the declaration is two-level: this default carries the resource's common case in one line, and\n * only fields that DIFFER from it need a decorator. A `USER_PROFILES` resource is\n * `SUBJECT_PROVIDED` by default with a handful of derived exceptions; an `AUDIT_LOGS` resource is\n * `DERIVED` throughout with none.\n *\n * ## Why it is optional, and what happens without it\n *\n * Omitting it entirely is legitimate: **Art. 15 (access) never consults provenance**, so an\n * application can answer access requests in full before annotating anything. Only an Art. 20\n * portability export needs the axis, and for a resource where neither the field nor this default\n * declares one, that export REFUSES rather than guessing — see\n * `SubjectExportFieldScope.UNDECLARED` for why neither guess is the safe one.\n *\n * ⚠️ NOT `retentionPolicy`. That says what happens to a value on ERASURE (destroy vs retain-and-\n * scrub); this says where the value ORIGINATED, for a DISCLOSURE decision. A field is commonly\n * `SUBJECT_PROVIDED` here and masked on erasure there — orthogonal axes on the same value.\n */\nexport interface ResourcePortabilityPolicy {\n /**\n * The provenance every field of this resource is assumed to carry unless its own\n * `.portabilityProvenance()` says otherwise.\n */\n defaultProvenance: DataPortabilityProvenance;\n}\n\n/**\n * Builder parameters for creating a resource configuration.\n * NOTE: databaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in mainSchema and are stripped at the service layer.\n */\nexport type ResourceConfiguration_BuilderParameters<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny,\n> = {\n isDisabled?: boolean, // default false\n mainSchema: TMainSchema,\n resourceIdentifier : ResourceType,\n resourceFieldIdentifier : ResourceFieldIdentifier,\n resourceRelationships : ResourceRelationship[],\n inheritenceSchemaDefinition?: ResourceConfiguration_InheritanceSchemaDefinition<EnumLikeType,TMainSchema>,\n\n /**\n * Persistence adapter (database type) for this resource.\n * - `mongodb`: MongoDB with Mongoose ORM\n * - `postgresql`: PostgreSQL (SQL adapter)\n *\n * If not specified, uses `appConfig.database.defaultAdapter` — which is the normal case and\n * should stay the normal case.\n *\n * **Declaring a value that DISAGREES with the application default is refused at registration\n * unless {@link ResourceConfiguration_BuilderParameters.persistenceAdapterIsolation} is also\n * declared.** An application is wholly one adapter by default because two adapters cannot\n * share a native transaction; the isolation declaration is the only way past that refusal,\n * and it is a claim about this resource's write topology rather than a way to silence the\n * error. Declaring an adapter that AGREES with the default is always fine and needs nothing.\n */\n persistenceAdapter?: PersistenceAdapterType,\n\n /**\n * This resource's declared relationship to cross-adapter atomic write boundaries.\n *\n * Required — and only meaningful — when {@link\n * ResourceConfiguration_BuilderParameters.persistenceAdapter} disagrees with\n * `appConfig.database.defaultAdapter`. Without it, the resources registry refuses the\n * application at startup; with it, the registry admits the resource and LOGS the admitted\n * set, so a mixed configuration is always named and never ambient.\n *\n * Read {@link ResourcePersistenceAdapterIsolation} before declaring one: the member is an\n * assertion about the resource's design that the persistence unit of work will still enforce\n * at every real boundary.\n */\n persistenceAdapterIsolation?: ResourcePersistenceAdapterIsolationType,\n\n /**\n * REQUIRED companion declaration when `persistenceAdapter` is `HTTP_API`; forbidden otherwise.\n *\n * Carries everything the external repository needs that the platform does not own: provider\n * reference (credentials resolve through `ExternalProvidersRegistryBackendService`), transport\n * dialect, remote entity, ordered key declaration (id synthesis), identity mode, tenancy\n * stance, erasure stance, and remote-call semantics. The factory enforces presence/absence\n * coherence and content-validates the declaration.\n */\n httpApiBinding?: HttpApiResourceBindingAuthored,\n\n /** REQUIRED when `persistenceAdapter` is INTROSPECTION; meaningless otherwise. */\n introspectionBinding?: IntrospectionResourceBinding,\n\n /**\n * OPTIONAL inbound ETL pipeline on an ordinary LOCAL resource — \"some resources local, some\n * remote, ETL between them\" (partial-resource coverage, model v3; PR-3). FORBIDDEN on an\n * HTTP_API-adapter resource: a virtual resource IS the remote side. The factory\n * content-validates the declaration and every mainSchema-dependent coherence rule (the\n * remote-key field is a declared `.isBusinessKey()` + excludeFromCreate/Update STRING; mapped\n * targets exist, accept null unless `required`, and are excludeFromCreate/Update; CLOSED\n * population refuses caller CREATE operations).\n */\n externalDataPipeline?: ExternalDataPipelineDeclarationAuthored,\n\n isSystemResource?: boolean, // default false - true for core system resources (organizations, users, etc.), false for tenant-specific resources\n\n /**\n * Does this resource's OWN primary-key URL segment name the CALLER, or the SUBJECT?\n *\n * Default `true` (the segment names the caller) — deliberately the SAFE default, so a resource\n * whose author has not considered the question keeps the identity-coherence guard rather than\n * silently losing it.\n *\n * ## Why this cannot be inferred\n *\n * The W3.6 identity-coherence guard (`handleUserJwtAuthentication` →\n * `AUTHORIZATION_USER_ID_MISMATCH`) refuses a request whose URL `userId` differs from the JWT\n * subject. That is exactly right for `USER_SELF` (`/user-self/{userId}` — the segment IS the\n * caller, pinned by the horizontal-IDOR probes) and for child routes under a user\n * (`/users/{userId}/draft-notes` — a PARENT segment naming whose rows these are).\n *\n * It is exactly WRONG for `USERS`, whose own `resourceFieldIdentifier` is also `userId` but whose\n * segment names the TARGET of an administrative action. Both resources are keyed on the same\n * field name, so no structural rule can tell them apart — the framework already had to add\n * `coreResourceShared_FieldIdentifierPriority` to disambiguate the very same collision for FK\n * resolution. This declaration is that disambiguation for IDENTITY.\n *\n * ## What it does and does not change\n *\n * Setting `false` relaxes ONLY the coherence refusal. It does not touch:\n * - `initiatorIds.userId`, which is taken from the JWT and spread LAST precisely so a URL can\n * never spoof the caller (see the `W3.6` comment at that assignment) — so permitting the\n * mismatch cannot produce impersonation;\n * - the contextual-field map, which legitimately carries the ADDRESSED resource's id;\n * - authorization, which still decides whether this caller may run this operation at all.\n *\n * Set `false` only for an administrative resource whose primary key is a person/tenant identifier\n * that a privileged caller is MEANT to address on someone else's behalf.\n */\n primaryKeyIdentifiesInitiator?: boolean,\n\n /**\n * Opt-in policy for the ACL-bypassing system-context primitives\n * (`readAsSystem` / `listAsSystem` and the trusted writes `createAsSystem` /\n * `updateAsSystem` / `updateManyAsSystem`). Omitted ⇒ EVERY verb FORBIDDEN\n * (fail-closed): a system-context call on this resource throws unless the\n * matching verb is explicitly set to ALLOWED. Declare ONLY the verb(s) that\n * genuinely need a system bypass, and ensure the calling service enforces an\n * explicit alternative authorization.\n */\n systemAccessPolicy?: ResourceSystemAccessPolicy,\n coreOperations: TCoreOperationEnum[],\n customOperation?: TCustomOperationEnum, // default undefined\n operationsConfiguration : { [K in ValidOperationKeys<TCustomOperationEnum, TCoreOperationEnum>]: OperationConfig_ForKey<K, TMainSchema> }\n /**\n * Configuration for auto-generated operation variants.\n * Controls which READ/LIST variants (summary, context) are auto-generated.\n * All variants are enabled by default.\n */\n autoVariants?: ResourceConfiguration_AutoVariants,\n // NOTE: responseSummaryDto removed - now auto-derived from isSummaryField() decorators in factory\n\n /**\n * W7.3a: Marks this resource as accessible to anonymous sessions.\n * When true, the factory auto-generates dual URL paths (USER_SELF + ANONYMOUS)\n * and a STANDALONE relationship to ANONYMOUS_USERS.\n * The resource MUST have a nullable `userId` field.\n */\n isAnonymizable?: boolean,\n\n /**\n * W7.3a: Controls how anonymous resources are handled during transposition.\n * Only meaningful when isAnonymizable is true.\n * Defaults to ADD (simple ownership reassignment).\n */\n transpositionPolicy?: ResourceTranspositionPolicy,\n\n /**\n * Opt in to impersonalization-on-erasure (retain + scrub instead of hard-delete). When set, the\n * factory injects a `retentionStatus` marker and derives the `updateMany` / `seeRetained`\n * system-access grants. Per-field scrub is `.impersonalizeWith()`; the per-edge trigger is the\n * `IMPERSONALIZE_AND_RETAIN` cascade mode.\n *\n * **LIVE.** Marker injection, grant derivation, the scrub writer, the cascade mode and the\n * hidden-by-default read filter are all enforced today — see `ResourceRetentionPolicy` for the\n * runtime evidence. Declaring this changes behaviour; it is not a forward declaration.\n *\n * Declaring this also REQUIRES {@link dataSubjectPolicy}: a retention-governed resource is exactly\n * one that can be handed to `eraseSubjectAsSystem` as a subject, so it must say whether its rows are\n * a person — and if so, whose session an erasure ends.\n */\n retentionPolicy?: ResourceRetentionPolicy,\n\n /**\n * WHO a row of this resource is with respect to an erasure — and, when it is a person, which field\n * names the `USERS` row whose session that erasure must end.\n *\n * Mandatory for any resource declaring a {@link retentionPolicy}; meaningless (and rejected) without\n * one, since a resource with no retention policy can never be an erasure subject. See\n * {@link ResourceDataSubjectPolicy}.\n */\n dataSubjectPolicy?: ResourceDataSubjectPolicy,\n\n /**\n * Resource-level DEFAULT provenance for GDPR Art. 20 (portability) scoping; per-field overrides\n * are `.portabilityProvenance()`. Optional — Art. 15 (access) never consults provenance, so an\n * application can answer access requests without declaring this. See\n * {@link ResourcePortabilityPolicy}.\n */\n portabilityPolicy?: ResourcePortabilityPolicy,\n}\n/** @wildo_source:part:end saas.models.resource-config.builder-parameters */\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.resource-relationships-buckets facet:layer:shared facet:family:resource-config\n *\n * Categorized resource relationships produced by `transformResourceRelationships()`.\n *\n * All relationships — including conditional ones (those with `discriminator` or `condition`) —\n * are stored flat in these three buckets. There is no separate `inheritanceSchemaRelationships`\n * bucket; that structure was removed because it was populated but never consumed at runtime,\n * making the conditional relationship system non-functional.\n *\n * The flat design means:\n * - **Factory/startup**: All relationships are categorized structurally (by resourceType\n * and cardinality), regardless of whether they have conditions.\n * - **Runtime**: Consumers (ResourceContext, ServicesRegistryHandler, ContainerNested)\n * evaluate `discriminator`/`condition` inline when actual parent data is available.\n *\n * M:N relationships are decomposed into synthetic 1:N relationships and placed into the\n * appropriate bucket. Synthetic relationships for entity→junction carry the original\n * `discriminator` and `condition` from the M:N declaration.\n *\n * @see ResourceRelationship — for the full type including optional `condition` function.\n * @see transformResourceRelationships — in resources-config.shared.factory.ts for categorization logic.\n */\nexport type ResourceConfiguration_ResourceRelationships = {\n parentRelationships: ResourceRelationship[],\n childRelationships: ResourceRelationship[],\n selfRelationships: ResourceRelationship[],\n}\n/** @wildo_source:part:end saas.models.resource-config.resource-relationships-buckets */\n\n/**\n * Returns all relationships where this resource acts as a child (has a parent).\n * Merges `childRelationships` with `selfRelationships` — self-refs are both\n * parent and child of themselves, so they must be included when iterating\n * \"what children does this resource have?\"\n */\nexport function getChildDirectionRelationships(\n rels: ResourceConfiguration_ResourceRelationships\n): ResourceRelationship[] {\n return [...rels.childRelationships, ...rels.selfRelationships];\n}\n\n/**\n * Returns all relationships where this resource acts as a parent (provides context).\n * Merges `parentRelationships` with `selfRelationships` — self-refs need to be\n * treated as parents when resolving FK fields, scope requirements, and URL paths.\n */\nexport function getParentDirectionRelationships(\n rels: ResourceConfiguration_ResourceRelationships\n): ResourceRelationship[] {\n return [...rels.parentRelationships, ...rels.selfRelationships];\n}\n\n\n/**\n * @wildo_source:part:start saas.models.resource-config.resource-configuration facet:layer:shared facet:family:resource-config\n *\n * Full resource configuration type.\n * NOTE: databaseSchema has been removed. Backend-only fields are now marked with\n * the isBackendOnly decorator in mainSchema and are stripped at the service layer.\n */\nexport type ResourceConfiguration<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny,\n> = {\n isDisabled: boolean, // default false\n\n resourcePrimaryScope : ResourcePrimaryScope, // default ResourcePrimaryScope.ORGANIZATIONS\n resourceIdentifier : ResourceType,\n resourceFieldIdentifier : ResourceFieldIdentifier,\n coreOperations: TCoreOperationEnum[],\n customOperation?: TCustomOperationEnum, // default undefined\n mainSchema: TMainSchema,\n resourceRelationships : ResourceConfiguration_ResourceRelationships,\n inheritenceSchemaDefinition?: ResourceConfiguration_InheritanceSchemaDefinition<EnumLikeType,TMainSchema>,\n\n /**\n * Persistence adapter (database type) for this resource.\n * - `mongodb`: MongoDB with Mongoose ORM\n * - `postgresql`: PostgreSQL (SQL adapter)\n *\n * Resolved at runtime from:\n * 1. This field (if specified)\n * 2. `appConfig.database.defaultAdapter` (fallback)\n *\n * A value disagreeing with the application default is admitted at registration ONLY when\n * {@link ResourceConfiguration.persistenceAdapterIsolation} is declared beside it.\n */\n persistenceAdapter?: PersistenceAdapterType,\n\n /**\n * This resource's declared relationship to cross-adapter atomic write boundaries; see the\n * authoring-side declaration and {@link ResourcePersistenceAdapterIsolation} for the full\n * rationale.\n *\n * Carried through to the resolved config UNMATERIALISED (no default is substituted) because\n * absence is semantically load-bearing here: the registry's admission asks whether the author\n * MADE the claim, and a materialised default would answer that question for them.\n */\n persistenceAdapterIsolation?: ResourcePersistenceAdapterIsolationType,\n\n /**\n * The external HTTP-API binding — present exactly when `persistenceAdapter` is `HTTP_API`\n * (factory-enforced both ways). See the authoring-side declaration for the full contract.\n */\n httpApiBinding?: HttpApiResourceBinding,\n /** REQUIRED when `persistenceAdapter` is INTROSPECTION; meaningless otherwise. */\n introspectionBinding?: IntrospectionResourceBinding,\n\n /**\n * The inbound ETL pipeline, when this LOCAL resource declared one — parsed + coherence-checked\n * by the factory (see the authoring-side declaration for the full contract).\n */\n externalDataPipeline?: ExternalDataPipelineDeclaration,\n\n isSystemResource: boolean, // default false - true for core system resources (organizations, users, etc.), false for tenant-specific resources\n\n /**\n * Whether this resource's own primary-key URL segment names the CALLER (see the authoring-side\n * declaration for the full rationale).\n *\n * Materialised by the factory to a concrete boolean, defaulting to `true`, so the consuming guard\n * never has to distinguish \"declared true\" from \"not declared\" — an un-migrated or hand-built\n * config keeps the identity-coherence refusal.\n */\n primaryKeyIdentifiesInitiator: boolean,\n\n /**\n * Opt-in policy gating the ACL-bypassing `readAsSystem` / `listAsSystem`\n * primitives for this resource. Optional on the resolved config so that\n * non-factory-built configs (mocks/tests) need not declare it; the factory\n * always materialises a concrete value (defaulting both verbs to FORBIDDEN\n * when unauthored). The system-read primitives treat an absent policy — or\n * any verb not explicitly ALLOWED — as forbidden and throw.\n */\n systemAccessPolicy?: ResourceSystemAccessPolicy,\n\n /**\n * W7.3a: Marks this resource as accessible to anonymous sessions.\n * When true, the engine generates dual URL paths and a STANDALONE relationship to ANONYMOUS_USERS.\n */\n isAnonymizable: boolean,\n\n /**\n * W7.3a: Transposition policy for anonymous→authenticated ownership transfer.\n * Only meaningful when isAnonymizable is true. Defaults to ADD.\n */\n transpositionPolicy: ResourceTranspositionPolicy,\n\n /** Resolved impersonalization opt-in (undefined when the resource did not opt into `retentionPolicy`). */\n retentionPolicy?: ResourceRetentionPolicy,\n\n /**\n * Resolved data-subject declaration. Present exactly when `retentionPolicy` is (the factory refuses\n * a retention-governed resource that declares no `dataSubjectPolicy`, and refuses a\n * `dataSubjectPolicy` on a resource with no `retentionPolicy`), so a consumer that has one may read\n * the other without a second existence check.\n */\n dataSubjectPolicy?: ResourceDataSubjectPolicy,\n\n /** Resolved portability default (undefined when the resource declared no `portabilityPolicy` — an\n * Art. 15 export is unaffected, an Art. 20 export refuses on any field that also lacks its own\n * `.portabilityProvenance()`). */\n portabilityPolicy?: ResourcePortabilityPolicy,\n\n operations : ResourceConfiguration_Operation<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>[]\n}\n/** @wildo_source:part:end saas.models.resource-config.resource-configuration */\n\nexport type ResourceConfiguration_InitializationFactory<\n TCustomOperationEnum extends EnumLikeType | undefined,\n TCoreOperationEnum extends Partial<CoreResourceOperation> | undefined,\n TMainSchema extends z.ZodTypeAny,\n> = (resourcesRelationships: ResourceRelationship[]) => ResourceConfiguration<TCustomOperationEnum, TCoreOperationEnum, TMainSchema>;\n\n\n/**\n * The factory map accepted by the RUNTIME REGISTRY initializers\n * (`ResourcesRegistryBackendService.initialize`, `ResourceRegistry_InitializerParameters`).\n *\n * ⚠️ The leading `TResourceType extends undefined` conditional is DISTRIBUTIVE, and it must stay\n * that way. A registry initializer takes this map at a position whose type argument is INFERRED\n * from the argument, and inference only reaches the mapped-type branch through that distributive\n * chain. Prefixing the chain with a non-distributive guard (`[TResourceType] extends [never] ? …`)\n * stops the inference dead: every such parameter falls back to its `= undefined` default, so a\n * perfectly good map is reported as `not assignable to parameter of type 'undefined'`. That\n * shipped on 2026-08-26 and failed the compile of BOTH `platform-apps-manager` and\n * `platform-crontabs-batches-manager` — the only two runtimes that pass a typed custom map\n * positionally — while every other consumer stayed green because it passes `undefined` or a value\n * already widened by a cast.\n *\n * The consequence of the distribution is that a union `TResourceType` yields a UNION of\n * single-key maps rather than one complete map. That is weaker than it looks, and it is why a\n * module's OWN map uses {@link ResourceConfiguration_ModuleInitializationFactoryMap} instead —\n * including for the empty-module case (`never`), which this type deliberately does not model:\n * distributing over `never` yields `never`, and no ordering of distributive branches can change\n * that.\n */\nexport type ResourceConfiguration_InitializationFactoryMap<TResourceType extends ResourceType | undefined = undefined> =\n TResourceType extends undefined\n ? undefined\n : [TResourceType] extends [undefined]\n ? undefined\n : TResourceType extends ResourceType\n ? { [key in TResourceType]: ResourceConfiguration_InitializationFactory<any, any, any> }\n : { [key : string]: ResourceConfiguration_InitializationFactory<any, any, any> }\n\n/**\n * The factory map a MODULE declares for its OWN resources: exactly one entry per member of that\n * module's resource-type enum — no missing entry, no key the enum does not declare.\n *\n * This is a plain mapped type, with no conditional in front of it, and both of its properties\n * follow from that:\n *\n * - a union of enum members produces ONE complete map (not the union of single-key maps that\n * {@link ResourceConfiguration_InitializationFactoryMap} produces), which is the truthful\n * contract for a module: it owns all of its resources;\n * - a FRESHLY SCAFFOLDED module, whose resource enum has no members yet, produces the empty map\n * `{}`. A memberless enum is numeric by default, so the scaffold writes\n * `<Module>_ResourceType & string`, which is `never` until the first string member arrives —\n * and a mapped type over `never` is `{}`, so the empty declaration type-checks without any\n * `never` special case.\n *\n * The value is assignable to every consumer that takes the registry-facing map, so a module can be\n * merged into an application registry unchanged.\n */\nexport type ResourceConfiguration_ModuleInitializationFactoryMap<TResourceType extends ResourceType> =\n { [key in TResourceType]: ResourceConfiguration_InitializationFactory<any, any, any> }\n\n\nexport type AnyResourceConfiguration = ResourceConfiguration<\n any, // TCustomOperationEnum - allow any custom operations\n any, // TCoreOperationEnum - allow any subset of core operations\n any // TMainSchema\n>;\n\n/**\n * @wildo_source:part:start saas.models.resource-config.resources-registry facet:layer:shared facet:family:resource-config\n *\n * Aggregated registry passed to app bootstrap (relationships + per-resource configs + FK maps).\n */\nexport type ResourcesRegistry = {\n notificationBadgeDefinitions: NotificationBadgeDefinition[];\n additionalUserNotificationDefinitions: UserNotificationDefinition[];\n resourceRelationships: ResourceRelationship[];\n resourceConfigurations: {\n [key: ResourceType]: AnyResourceConfiguration ;\n };\n /**\n * The resource types the APPLICATION registered — its own, never the engine's.\n *\n * The registry composes app and core factory maps into one `resourceConfigurations`, which is what\n * every runtime consumer wants. This is the one place the two are still distinguishable, so the\n * split is recorded HERE rather than re-derived by each consumer from a core key set — which would\n * be a second enumeration of a population this file already knows.\n *\n * Its consumer today is the personal-data declaration report, which asks a question only an\n * application author can act on: the engine declares its own posture on ~85 resources, and putting\n * those in front of a reader of a report about THEIR application is how a diagnostic channel stops\n * being read.\n */\n applicationResourceTypes: ResourceType[];\n /**\n * Map of ResourceType to its standard field identifier (e.g., USERS -> userId).\n * Used for FK field resolution without string manipulation.\n */\n allResourceFieldIdentifiers: Record<ResourceType, string>;\n /**\n * Reverse map of field identifier to ResourceType (e.g., userId -> USERS).\n * Used for quick lookup of resource type from a FK field name.\n */\n fieldIdentifierToResourceType: Record<string, ResourceType>;\n /**\n * Scope-required reference-search junctions, keyed by `${target}|${scope}`.\n * When a foreign key targets `T` from a resource whose primary scope is `S`,\n * and `T ↔ S` is a MANY-to-MANY relationship via junction `J`, a reference\n * people-picker searches `T` scoped to the caller's current `S` through `J`\n * (rather than enumerating every `T`). Derived from the registered M:N\n * relationships at registry init.\n */\n manyToManyScopeJunctions: ManyToManyScopeJunctionMap;\n /**\n * Resources narrowed by ORGANIZATION UNIT, keyed by resource type — the engine's opt-ins merged\n * with the application's, validated at boot against the authorization-plane containment rule.\n *\n * Opting a resource in means: a principal whose authority for it comes ONLY from a unit grant is\n * admitted by the operation gate and then confined to the rows of those units. Narrowing is\n * ADDITIVE — an organization-wide grant that already satisfies the operation is never narrowed —\n * so opting in can only widen who can reach the resource, never shrink an existing result set.\n *\n * Empty for an application that declares none, which is the default: narrowing is never inferred\n * from the presence of a unit field. See `OrganizationUnitNarrowingDeclaration`.\n */\n organizationUnitNarrowing: Readonly<Partial<Record<ResourceType, OrganizationUnitNarrowingDeclaration>>>;\n}\n/** @wildo_source:part:end saas.models.resource-config.resources-registry */\n\nexport type ResourceRegistry_InitializerParameters<TResourceType extends ResourceType | undefined = undefined> =\n [TResourceType] extends [undefined]\n ? {\n resourceRelationships: ResourceRelationship[],\n resourceConfigurationsFactoryMap: ResourceConfiguration_InitializationFactoryMap<undefined>,\n resourceFieldIdentifiers: undefined,\n }\n : TResourceType extends ResourceType\n ? {\n resourceRelationships: ResourceRelationship[],\n resourceConfigurationsFactoryMap: ResourceConfiguration_InitializationFactoryMap<TResourceType>,\n resourceFieldIdentifiers: { [key in TResourceType]: string },\n }\n : {\n resourceRelationships: ResourceRelationship[],\n resourceConfigurationsFactoryMap: ResourceConfiguration_InitializationFactoryMap<ResourceType>,\n resourceFieldIdentifiers: { } | undefined,\n }\n\n\n\n\n\n// --------------------------------\n"]}