@wildo-ai/saas-models 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/dist/esm/applications/applications-context.schemas.d.ts +2 -0
  2. package/dist/esm/applications/applications-context.schemas.d.ts.map +1 -1
  3. package/dist/esm/billing/billing-account.shared.resources-config.schemas.d.ts +1 -1
  4. package/dist/esm/billing/billing-account.shared.resources-config.schemas.d.ts.map +1 -1
  5. package/dist/esm/billing/billing-account.shared.resources-config.schemas.js +36 -0
  6. package/dist/esm/billing/billing-account.shared.resources-config.schemas.js.map +1 -1
  7. package/dist/esm/billing/billing-account.shared.schemas.d.ts.map +1 -1
  8. package/dist/esm/billing/billing-account.shared.schemas.js +8 -1
  9. package/dist/esm/billing/billing-account.shared.schemas.js.map +1 -1
  10. package/dist/esm/billing/billing-types.shared.schemas.d.ts +2 -2
  11. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.d.ts +1 -1
  12. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.d.ts.map +1 -1
  13. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.js +39 -0
  14. package/dist/esm/billing/credit-pool.shared.resources-config.schemas.js.map +1 -1
  15. package/dist/esm/billing/credit-pool.shared.schemas.d.ts +6 -6
  16. package/dist/esm/billing/credit-pool.shared.schemas.d.ts.map +1 -1
  17. package/dist/esm/billing/credit-pool.shared.schemas.js +8 -4
  18. package/dist/esm/billing/credit-pool.shared.schemas.js.map +1 -1
  19. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.d.ts +1 -1
  20. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.d.ts.map +1 -1
  21. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.js +35 -0
  22. package/dist/esm/billing/invoice-ref.shared.resources-config.schemas.js.map +1 -1
  23. package/dist/esm/billing/invoice-ref.shared.schemas.d.ts.map +1 -1
  24. package/dist/esm/billing/invoice-ref.shared.schemas.js +8 -3
  25. package/dist/esm/billing/invoice-ref.shared.schemas.js.map +1 -1
  26. package/dist/esm/billing/subscription.shared.resources-config.schemas.d.ts +1 -1
  27. package/dist/esm/billing/subscription.shared.resources-config.schemas.d.ts.map +1 -1
  28. package/dist/esm/billing/subscription.shared.resources-config.schemas.js +39 -0
  29. package/dist/esm/billing/subscription.shared.resources-config.schemas.js.map +1 -1
  30. package/dist/esm/billing/subscription.shared.schemas.d.ts +4 -4
  31. package/dist/esm/billing/subscription.shared.schemas.d.ts.map +1 -1
  32. package/dist/esm/billing/subscription.shared.schemas.js +6 -2
  33. package/dist/esm/billing/subscription.shared.schemas.js.map +1 -1
  34. package/dist/esm/billing/usage-record.shared.resources-config.schemas.d.ts +1 -1
  35. package/dist/esm/billing/usage-record.shared.resources-config.schemas.d.ts.map +1 -1
  36. package/dist/esm/billing/usage-record.shared.resources-config.schemas.js +39 -0
  37. package/dist/esm/billing/usage-record.shared.resources-config.schemas.js.map +1 -1
  38. package/dist/esm/billing/usage-record.shared.schemas.d.ts +4 -4
  39. package/dist/esm/billing/usage-record.shared.schemas.d.ts.map +1 -1
  40. package/dist/esm/billing/usage-record.shared.schemas.js +7 -3
  41. package/dist/esm/billing/usage-record.shared.schemas.js.map +1 -1
  42. package/dist/esm/compliance/audit-trails/audit-logs.shared.resources-config.schemas.d.ts.map +1 -1
  43. package/dist/esm/compliance/audit-trails/audit-logs.shared.resources-config.schemas.js +35 -2
  44. package/dist/esm/compliance/audit-trails/audit-logs.shared.resources-config.schemas.js.map +1 -1
  45. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.d.ts +61 -0
  46. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.d.ts.map +1 -1
  47. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.js +58 -0
  48. package/dist/esm/compliance/audit-trails/auditable-events.shared.schema.js.map +1 -1
  49. package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.d.ts.map +1 -1
  50. package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.js +14 -0
  51. package/dist/esm/compliance/audit-trails/security-audit-event-envelope.shared.schema.js.map +1 -1
  52. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.d.ts +49 -0
  53. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.d.ts.map +1 -1
  54. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.js +124 -1
  55. package/dist/esm/compliance/privacy/impersonalization-scrub.shared.utils.js.map +1 -1
  56. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.d.ts +11 -3
  57. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.d.ts.map +1 -1
  58. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.js +11 -3
  59. package/dist/esm/compliance/privacy/impersonalization.shared.schemas.js.map +1 -1
  60. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.d.ts +93 -0
  61. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.d.ts.map +1 -0
  62. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.js +116 -0
  63. package/dist/esm/compliance/privacy/operating-jurisdictions.shared.schemas.js.map +1 -0
  64. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.d.ts +67 -12
  65. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.d.ts.map +1 -1
  66. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.js +95 -8
  67. package/dist/esm/compliance/privacy/operator-compliance-identity.shared.schemas.js.map +1 -1
  68. package/dist/esm/compliance/privacy/redaction.shared.schemas.d.ts +21 -1
  69. package/dist/esm/compliance/privacy/redaction.shared.schemas.d.ts.map +1 -1
  70. package/dist/esm/compliance/privacy/redaction.shared.schemas.js +20 -0
  71. package/dist/esm/compliance/privacy/redaction.shared.schemas.js.map +1 -1
  72. package/dist/esm/config/app-configuration-shared.shared.schemas.d.ts +2 -0
  73. package/dist/esm/config/app-configuration-shared.shared.schemas.d.ts.map +1 -1
  74. package/dist/esm/config/app-configuration-shared.shared.schemas.js +29 -0
  75. package/dist/esm/config/app-configuration-shared.shared.schemas.js.map +1 -1
  76. package/dist/esm/config/frontend/navigation.shared.schemas.d.ts +33 -0
  77. package/dist/esm/config/frontend/navigation.shared.schemas.d.ts.map +1 -1
  78. package/dist/esm/config/frontend/navigation.shared.schemas.js +32 -0
  79. package/dist/esm/config/frontend/navigation.shared.schemas.js.map +1 -1
  80. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.d.ts +12 -1
  81. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.d.ts.map +1 -1
  82. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.js +12 -1
  83. package/dist/esm/errors/errors.custom-message-ref.shared.definitions.js.map +1 -1
  84. package/dist/esm/external-data/external-data-pipeline.shared.schemas.d.ts +13 -0
  85. package/dist/esm/external-data/external-data-pipeline.shared.schemas.d.ts.map +1 -1
  86. package/dist/esm/external-data/external-data-pipeline.shared.schemas.js.map +1 -1
  87. package/dist/esm/external-providers/engine-capabilities.shared.schemas.d.ts +6 -6
  88. package/dist/esm/external-providers/engine-capabilities.shared.schemas.d.ts.map +1 -1
  89. package/dist/esm/external-providers/engine-capabilities.shared.schemas.js +26 -6
  90. package/dist/esm/external-providers/engine-capabilities.shared.schemas.js.map +1 -1
  91. package/dist/esm/external-providers/engine-capability-resolution-mode.shared.d.ts.map +1 -1
  92. package/dist/esm/external-providers/engine-capability-resolution-mode.shared.js +0 -2
  93. package/dist/esm/external-providers/engine-capability-resolution-mode.shared.js.map +1 -1
  94. package/dist/esm/external-providers/frontend-bootstrap.shared.schemas.d.ts +1 -0
  95. package/dist/esm/external-providers/frontend-bootstrap.shared.schemas.d.ts.map +1 -1
  96. package/dist/esm/external-providers/provider-capability.shared.schemas.d.ts +4 -3
  97. package/dist/esm/external-providers/provider-capability.shared.schemas.d.ts.map +1 -1
  98. package/dist/esm/external-providers/provider-capability.shared.schemas.js +4 -3
  99. package/dist/esm/external-providers/provider-capability.shared.schemas.js.map +1 -1
  100. package/dist/esm/features/user-features.shared.resources-config.schemas.d.ts +1 -1
  101. package/dist/esm/features/user-features.shared.resources-config.schemas.d.ts.map +1 -1
  102. package/dist/esm/features/user-features.shared.resources-config.schemas.js +48 -0
  103. package/dist/esm/features/user-features.shared.resources-config.schemas.js.map +1 -1
  104. package/dist/esm/files/file-upload-grant.shared.schemas.d.ts +144 -0
  105. package/dist/esm/files/file-upload-grant.shared.schemas.d.ts.map +1 -0
  106. package/dist/esm/files/file-upload-grant.shared.schemas.js +136 -0
  107. package/dist/esm/files/file-upload-grant.shared.schemas.js.map +1 -0
  108. package/dist/esm/files/files.shared.schemas.d.ts +45 -4
  109. package/dist/esm/files/files.shared.schemas.d.ts.map +1 -1
  110. package/dist/esm/files/files.shared.schemas.js +48 -4
  111. package/dist/esm/files/files.shared.schemas.js.map +1 -1
  112. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.d.ts +1 -1
  113. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.d.ts.map +1 -1
  114. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.js +33 -0
  115. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.js.map +1 -1
  116. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.schemas.d.ts.map +1 -1
  117. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.schemas.js +6 -2
  118. package/dist/esm/flows-actors/a2a-push-delivery-log.shared.schemas.js.map +1 -1
  119. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.d.ts +1 -1
  120. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.d.ts.map +1 -1
  121. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.js +53 -0
  122. package/dist/esm/flows-actors/a2a-token-budget-state.shared.resources-config.schemas.js.map +1 -1
  123. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.d.ts +1 -1
  124. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.d.ts.map +1 -1
  125. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.js +43 -0
  126. package/dist/esm/flows-actors/flows-actors-execution.shared.resources-config.schemas.js.map +1 -1
  127. package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.d.ts.map +1 -1
  128. package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.js +8 -3
  129. package/dist/esm/flows-actors/flows-actors-execution.shared.schemas.js.map +1 -1
  130. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.d.ts +1 -1
  131. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.d.ts.map +1 -1
  132. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.js +41 -0
  133. package/dist/esm/flows-actors/flows-actors-task.shared.resources-config.schemas.js.map +1 -1
  134. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.d.ts +0 -7
  135. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.d.ts.map +1 -1
  136. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.js +23 -13
  137. package/dist/esm/flows-actors/flows-actors-task.shared.schemas.js.map +1 -1
  138. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.d.ts +1 -1
  139. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.d.ts.map +1 -1
  140. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.js +51 -0
  141. package/dist/esm/guidance/guidance-state.shared.resources-config.schemas.js.map +1 -1
  142. package/dist/esm/guidance/guidance-state.shared.schemas.d.ts.map +1 -1
  143. package/dist/esm/guidance/guidance-state.shared.schemas.js +8 -2
  144. package/dist/esm/guidance/guidance-state.shared.schemas.js.map +1 -1
  145. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.d.ts +1 -1
  146. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.d.ts.map +1 -1
  147. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.js +51 -0
  148. package/dist/esm/guidance/lifecycle-state.shared.resources-config.schemas.js.map +1 -1
  149. package/dist/esm/guidance/lifecycle-state.shared.schemas.d.ts.map +1 -1
  150. package/dist/esm/guidance/lifecycle-state.shared.schemas.js +28 -3
  151. package/dist/esm/guidance/lifecycle-state.shared.schemas.js.map +1 -1
  152. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.d.ts +1 -1
  153. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.d.ts.map +1 -1
  154. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.js +51 -0
  155. package/dist/esm/guidance/progression-state.shared.resources-config.schemas.js.map +1 -1
  156. package/dist/esm/guidance/progression-state.shared.schemas.d.ts.map +1 -1
  157. package/dist/esm/guidance/progression-state.shared.schemas.js +18 -4
  158. package/dist/esm/guidance/progression-state.shared.schemas.js.map +1 -1
  159. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.d.ts +37 -3
  160. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.d.ts.map +1 -1
  161. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.js +29 -3
  162. package/dist/esm/http-api-binding/http-api-binding.shared.schemas.js.map +1 -1
  163. package/dist/esm/index.d.ts +6 -0
  164. package/dist/esm/index.d.ts.map +1 -1
  165. package/dist/esm/index.js +6 -0
  166. package/dist/esm/index.js.map +1 -1
  167. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.d.ts +70 -0
  168. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.d.ts.map +1 -0
  169. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.js +71 -0
  170. package/dist/esm/notifications/m2m/m2m-webhook-delivery-contract.shared.js.map +1 -0
  171. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.d.ts +1 -1
  172. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.d.ts.map +1 -1
  173. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.js +36 -0
  174. package/dist/esm/notifications/notification-badges.shared.resources-config.schemas.js.map +1 -1
  175. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.d.ts +1 -1
  176. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.d.ts.map +1 -1
  177. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.js +44 -0
  178. package/dist/esm/organizations/organization-members.shared.resources-config.schemas.js.map +1 -1
  179. package/dist/esm/organizations/organization-members.shared.schemas.d.ts.map +1 -1
  180. package/dist/esm/organizations/organization-members.shared.schemas.js +15 -4
  181. package/dist/esm/organizations/organization-members.shared.schemas.js.map +1 -1
  182. package/dist/esm/organizations/organizations.shared.resources-config.schemas.d.ts.map +1 -1
  183. package/dist/esm/organizations/organizations.shared.resources-config.schemas.js +18 -10
  184. package/dist/esm/organizations/organizations.shared.resources-config.schemas.js.map +1 -1
  185. package/dist/esm/queue/jobs.shared.resources-config.schemas.d.ts.map +1 -1
  186. package/dist/esm/queue/jobs.shared.resources-config.schemas.js +17 -0
  187. package/dist/esm/queue/jobs.shared.resources-config.schemas.js.map +1 -1
  188. package/dist/esm/requests/websocket.shared.schemas.d.ts +59 -0
  189. package/dist/esm/requests/websocket.shared.schemas.d.ts.map +1 -1
  190. package/dist/esm/requests/websocket.shared.schemas.js +103 -0
  191. package/dist/esm/requests/websocket.shared.schemas.js.map +1 -1
  192. package/dist/esm/resources/collection-query-parameters.shared.d.ts +22 -0
  193. package/dist/esm/resources/collection-query-parameters.shared.d.ts.map +1 -0
  194. package/dist/esm/resources/collection-query-parameters.shared.js +22 -0
  195. package/dist/esm/resources/collection-query-parameters.shared.js.map +1 -0
  196. package/dist/esm/resources/resources-config.shared.factory.d.ts.map +1 -1
  197. package/dist/esm/resources/resources-config.shared.factory.js +86 -13
  198. package/dist/esm/resources/resources-config.shared.factory.js.map +1 -1
  199. package/dist/esm/resources/resources-config.shared.schemas.d.ts +162 -41
  200. package/dist/esm/resources/resources-config.shared.schemas.d.ts.map +1 -1
  201. package/dist/esm/resources/resources-config.shared.schemas.js +109 -20
  202. package/dist/esm/resources/resources-config.shared.schemas.js.map +1 -1
  203. package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.d.ts.map +1 -1
  204. package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.js +21 -6
  205. package/dist/esm/resources/utils/resources-config-operations.dtos-builder.utils.js.map +1 -1
  206. package/dist/esm/security/authentications/authentication.shared.schemas.d.ts +57 -0
  207. package/dist/esm/security/authentications/authentication.shared.schemas.d.ts.map +1 -1
  208. package/dist/esm/security/authentications/authentication.shared.schemas.js +64 -0
  209. package/dist/esm/security/authentications/authentication.shared.schemas.js.map +1 -1
  210. package/dist/esm/security/authentications/consumable-token.shared.schemas.d.ts +17 -1
  211. package/dist/esm/security/authentications/consumable-token.shared.schemas.d.ts.map +1 -1
  212. package/dist/esm/security/authentications/consumable-token.shared.schemas.js +16 -0
  213. package/dist/esm/security/authentications/consumable-token.shared.schemas.js.map +1 -1
  214. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.d.ts +28 -0
  215. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.d.ts.map +1 -0
  216. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.js +28 -0
  217. package/dist/esm/security/authentications/mfa-backup-code-contract.shared.js.map +1 -0
  218. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.d.ts +21 -0
  219. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.d.ts.map +1 -0
  220. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.js +21 -0
  221. package/dist/esm/security/authentications/siem/siem-delivery-reliability-contract.shared.js.map +1 -0
  222. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.d.ts +1 -1
  223. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.d.ts.map +1 -1
  224. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.js +43 -0
  225. package/dist/esm/security/authorizations/platform-access-grants.shared.resources-config.schemas.js.map +1 -1
  226. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.d.ts +0 -8
  227. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.d.ts.map +1 -1
  228. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.js +13 -4
  229. package/dist/esm/security/authorizations/platform-access-grants.shared.schemas.js.map +1 -1
  230. package/dist/esm/security/authorizations/roles.shared.schemas.d.ts +16 -1
  231. package/dist/esm/security/authorizations/roles.shared.schemas.d.ts.map +1 -1
  232. package/dist/esm/security/authorizations/roles.shared.schemas.js +16 -1
  233. package/dist/esm/security/authorizations/roles.shared.schemas.js.map +1 -1
  234. package/dist/esm/security/authorizations/roles.shared.utils.d.ts +54 -0
  235. package/dist/esm/security/authorizations/roles.shared.utils.d.ts.map +1 -1
  236. package/dist/esm/security/authorizations/roles.shared.utils.js +59 -0
  237. package/dist/esm/security/authorizations/roles.shared.utils.js.map +1 -1
  238. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.d.ts +61 -0
  239. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.d.ts.map +1 -1
  240. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.js +13 -1
  241. package/dist/esm/security/oauth-clients/oauth-clients.shared.schemas.js.map +1 -1
  242. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.d.ts +1 -1
  243. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.d.ts.map +1 -1
  244. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.js +35 -0
  245. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.resources-config.schemas.js.map +1 -1
  246. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.d.ts.map +1 -1
  247. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.js +7 -1
  248. package/dist/esm/security/oauth-clients/oauth-consent-grants.shared.schemas.js.map +1 -1
  249. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.d.ts +1 -1
  250. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.d.ts.map +1 -1
  251. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.js +34 -0
  252. package/dist/esm/users/inbound-contacts.shared.resources-config.schemas.js.map +1 -1
  253. package/dist/esm/users/inbound-contacts.shared.schemas.d.ts.map +1 -1
  254. package/dist/esm/users/inbound-contacts.shared.schemas.js +7 -4
  255. package/dist/esm/users/inbound-contacts.shared.schemas.js.map +1 -1
  256. package/dist/esm/users/user-credentials.shared.resources-config.schemas.d.ts +1 -1
  257. package/dist/esm/users/user-credentials.shared.resources-config.schemas.d.ts.map +1 -1
  258. package/dist/esm/users/user-credentials.shared.resources-config.schemas.js +37 -0
  259. package/dist/esm/users/user-credentials.shared.resources-config.schemas.js.map +1 -1
  260. package/dist/esm/users/user-credentials.shared.schemas.d.ts.map +1 -1
  261. package/dist/esm/users/user-credentials.shared.schemas.js +17 -2
  262. package/dist/esm/users/user-credentials.shared.schemas.js.map +1 -1
  263. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.d.ts +1 -1
  264. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.d.ts.map +1 -1
  265. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.js +36 -0
  266. package/dist/esm/users/user-identity-links.shared.resources-config.schemas.js.map +1 -1
  267. package/dist/esm/users/user-identity-links.shared.schemas.d.ts.map +1 -1
  268. package/dist/esm/users/user-identity-links.shared.schemas.js +15 -5
  269. package/dist/esm/users/user-identity-links.shared.schemas.js.map +1 -1
  270. package/dist/esm/users/user-preferences.shared.resources-config.schemas.d.ts +1 -1
  271. package/dist/esm/users/user-preferences.shared.resources-config.schemas.d.ts.map +1 -1
  272. package/dist/esm/users/user-preferences.shared.resources-config.schemas.js +53 -0
  273. package/dist/esm/users/user-preferences.shared.resources-config.schemas.js.map +1 -1
  274. package/dist/esm/users/user-profiles.shared.resources-config.schemas.d.ts +1 -1
  275. package/dist/esm/users/user-profiles.shared.resources-config.schemas.d.ts.map +1 -1
  276. package/dist/esm/users/user-profiles.shared.resources-config.schemas.js +62 -0
  277. package/dist/esm/users/user-profiles.shared.resources-config.schemas.js.map +1 -1
  278. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.d.ts +1 -1
  279. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.d.ts.map +1 -1
  280. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.js +53 -0
  281. package/dist/esm/users/user-self-preferences.shared.resources-config.schemas.js.map +1 -1
  282. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.d.ts +1 -1
  283. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.d.ts.map +1 -1
  284. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.js +62 -0
  285. package/dist/esm/users/user-self-profiles.shared.resources-config.schemas.js.map +1 -1
  286. package/dist/esm/users/users.shared.schemas.d.ts +19 -0
  287. package/dist/esm/users/users.shared.schemas.d.ts.map +1 -1
  288. package/dist/esm/users/users.shared.schemas.js +41 -16
  289. package/dist/esm/users/users.shared.schemas.js.map +1 -1
  290. package/dist/tsconfig.build.tsbuildinfo +1 -1
  291. package/package.json +5 -6
  292. package/dist/esm/.builder.pid +0 -9
@@ -0,0 +1,144 @@
1
+ import { z } from 'zod';
2
+ import { CoreResourceOperation } from '../resources/resources.shared.core.definitions';
3
+ import { FileStatus } from './files.shared.schemas';
4
+ /**
5
+ * File upload grant — the shared contract between the two mint doors (REST, MCP), the upload route,
6
+ * the status read, and the browser drop page (issue #312).
7
+ *
8
+ * A grant is a single-use consumable token. Nothing durable is created at mint but the token: an
9
+ * abandoned grant leaves a dead token and nothing else. The upload itself goes through the resource's
10
+ * EXISTING file-upload route, authenticated by the grant alone, and the ordinary authorizer decides.
11
+ */
12
+ /**
13
+ * Public frontend route the browser drop page lives on; the token follows as the next path segment
14
+ * (`/upload-grant/<token>`). Declared HERE so the backend (which builds `uploadPageUrl`) and the
15
+ * frontend (which mounts the route) read one source — the backend cannot import frontend route
16
+ * constants.
17
+ */
18
+ export declare const FILE_UPLOAD_GRANT_PAGE_PATH = "/upload-grant";
19
+ /** @wildo_source:part:start saas.models.file-upload-grant facet:layer:shared facet:family:files
20
+ *
21
+ * Lifetime bounds of an upload grant, in minutes. The ceiling is fixed by the framework and enforced
22
+ * when the grant is minted — a request above it is REFUSED, never clamped, so a caller that asked for
23
+ * two hours learns from an error that the bound is sixty minutes rather than from an expiry it did not
24
+ * expect. The bound is the framework's to own because the grant is designed to leave the trust
25
+ * boundary: it lands in an AI agent's transcript, a model provider's logs, a shell history.
26
+ */
27
+ export declare const FILE_UPLOAD_GRANT_DEFAULT_MINUTES = 15;
28
+ export declare const FILE_UPLOAD_GRANT_ABSOLUTE_MAX_MINUTES = 60;
29
+ /**
30
+ * Which write the grant is for. A grant minted on the CREATE upload route uploads a file that a
31
+ * later CREATE will reference; one minted on the UPDATE route (`…/{resourceId}/files/<field>`) is
32
+ * bound to that row. The two routes authorize differently, so a grant may not cross from one to the
33
+ * other — the upload middleware checks this against the route it is presented on.
34
+ */
35
+ export declare const FILE_UPLOAD_GRANT_OPERATIONS: readonly [CoreResourceOperation.CREATE, CoreResourceOperation.UPDATE];
36
+ export type FileUploadGrantOperation = (typeof FILE_UPLOAD_GRANT_OPERATIONS)[number];
37
+ /** Derived from `CoreResourceOperation` — the named source — rather than a second enum spelling the same two words. */
38
+ export declare const FileUploadGrantOperationSchema: z.ZodEnum<{
39
+ [K in FileUploadGrantOperation]: K;
40
+ }>;
41
+ /**
42
+ * Lifecycle of a grant as the status read reports it. Derived from the token row — `isConsumed`,
43
+ * `expiresAt`, `revokedAt` — never stored, so it cannot drift from the row.
44
+ */
45
+ export declare enum FileUploadGrantState {
46
+ /** Minted, unexpired, unrevoked, not yet redeemed: an upload will be accepted. */
47
+ PENDING = "pending",
48
+ /** Redeemed by exactly one upload. `file` names what it produced when the write-back succeeded. */
49
+ REDEEMED = "redeemed",
50
+ /** Past `expiresAt` without being redeemed. Mint a new one. */
51
+ EXPIRED = "expired",
52
+ /** Revoked by the minter or an administrator before redemption. */
53
+ REVOKED = "revoked"
54
+ }
55
+ export declare const FileUploadGrantStateSchema: z.ZodEnum<typeof FileUploadGrantState>;
56
+ /**
57
+ * The `metadata` a FILE_UPLOAD_GRANT token carries — the ROUTE binding, as opposed to the identity
58
+ * the typed token fields carry. The upload middleware reads this; the execution-context creator
59
+ * deliberately does not.
60
+ */
61
+ export declare const FileUploadGrantMetadataSchema: z.ZodObject<{
62
+ fieldName: z.ZodString;
63
+ operation: z.ZodEnum<{
64
+ [K in FileUploadGrantOperation]: K;
65
+ }>;
66
+ minter: z.ZodObject<{
67
+ entityType: z.ZodString;
68
+ id: z.ZodString;
69
+ }>;
70
+ uploadUrl: z.ZodString;
71
+ producedFileId: z.ZodOptional<z.ZodString>;
72
+ }>;
73
+ export type FileUploadGrantMetadata = z.infer<typeof FileUploadGrantMetadataSchema>;
74
+ /**
75
+ * What a mint returns — identical on the REST door and the MCP tool. Two affordances for one
76
+ * token: `uploadCommand` for a caller that can execute, `uploadPageUrl` for one that must hand a
77
+ * human a link. The token appears in `uploadHeader.value` and inside both commands and the page URL;
78
+ * it is the credential and cannot be omitted. It never appears in any audit row.
79
+ */
80
+ export declare const FileUploadGrantSchema: z.ZodObject<{
81
+ grantId: z.ZodString;
82
+ state: z.ZodEnum<typeof FileUploadGrantState>;
83
+ expiresAt: z.ZodString;
84
+ singleUse: z.ZodLiteral<true>;
85
+ target: z.ZodObject<{
86
+ resourceType: z.ZodString;
87
+ fieldName: z.ZodString;
88
+ operation: z.ZodEnum<{
89
+ [K in FileUploadGrantOperation]: K;
90
+ }>;
91
+ resourceId: z.ZodNullable<z.ZodString>;
92
+ }>;
93
+ constraints: z.ZodObject<{
94
+ maxBytes: z.ZodNumber;
95
+ allowedMimeTypes: z.ZodArray<z.ZodString>;
96
+ }>;
97
+ uploadUrl: z.ZodString;
98
+ uploadHeader: z.ZodObject<{
99
+ name: z.ZodString;
100
+ value: z.ZodString;
101
+ }>;
102
+ uploadCommand: z.ZodString;
103
+ uploadCommandPowerShell: z.ZodString;
104
+ uploadPageUrl: z.ZodNullable<z.ZodString>;
105
+ statusUrl: z.ZodString;
106
+ note: z.ZodString;
107
+ }>;
108
+ export type FileUploadGrant = z.infer<typeof FileUploadGrantSchema>;
109
+ /**
110
+ * What the public status read returns. The token is the credential, so the read is unauthenticated
111
+ * and returns only what the drop page needs to render and the agent needs to learn the `fileId`:
112
+ * never the file's name (the status URL may be pasted around).
113
+ */
114
+ export declare const FileUploadGrantStatusSchema: z.ZodObject<{
115
+ grantId: z.ZodString;
116
+ state: z.ZodEnum<typeof FileUploadGrantState>;
117
+ expiresAt: z.ZodString;
118
+ target: z.ZodObject<{
119
+ resourceType: z.ZodString;
120
+ fieldName: z.ZodString;
121
+ operation: z.ZodEnum<{
122
+ [K in FileUploadGrantOperation]: K;
123
+ }>;
124
+ resourceId: z.ZodNullable<z.ZodString>;
125
+ }>;
126
+ constraints: z.ZodObject<{
127
+ maxBytes: z.ZodNumber;
128
+ allowedMimeTypes: z.ZodArray<z.ZodString>;
129
+ }>;
130
+ uploadUrl: z.ZodString;
131
+ uploadHeaderName: z.ZodString;
132
+ file: z.ZodNullable<z.ZodObject<{
133
+ fileId: z.ZodString;
134
+ status: z.ZodEnum<typeof FileStatus>;
135
+ }>>;
136
+ }>;
137
+ export type FileUploadGrantStatus = z.infer<typeof FileUploadGrantStatusSchema>;
138
+ /** Body accepted by both mint doors. */
139
+ export declare const FileUploadGrantMintRequestSchema: z.ZodObject<{
140
+ expiresInMinutes: z.ZodOptional<z.ZodNumber>;
141
+ }>;
142
+ export type FileUploadGrantMintRequest = z.infer<typeof FileUploadGrantMintRequestSchema>;
143
+ /** @wildo_source:part:end saas.models.file-upload-grant */
144
+ //# sourceMappingURL=file-upload-grant.shared.schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-upload-grant.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/files/file-upload-grant.shared.schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ import { z } from 'zod';
2
+ import { CoreResourceOperation } from '../resources/resources.shared.core.definitions.js';
3
+ import { FileStatus } from './files.shared.schemas.js';
4
+ /**
5
+ * File upload grant — the shared contract between the two mint doors (REST, MCP), the upload route,
6
+ * the status read, and the browser drop page (issue #312).
7
+ *
8
+ * A grant is a single-use consumable token. Nothing durable is created at mint but the token: an
9
+ * abandoned grant leaves a dead token and nothing else. The upload itself goes through the resource's
10
+ * EXISTING file-upload route, authenticated by the grant alone, and the ordinary authorizer decides.
11
+ */
12
+ /**
13
+ * Public frontend route the browser drop page lives on; the token follows as the next path segment
14
+ * (`/upload-grant/<token>`). Declared HERE so the backend (which builds `uploadPageUrl`) and the
15
+ * frontend (which mounts the route) read one source — the backend cannot import frontend route
16
+ * constants.
17
+ */
18
+ export const FILE_UPLOAD_GRANT_PAGE_PATH = '/upload-grant';
19
+ /** @wildo_source:part:start saas.models.file-upload-grant facet:layer:shared facet:family:files
20
+ *
21
+ * Lifetime bounds of an upload grant, in minutes. The ceiling is fixed by the framework and enforced
22
+ * when the grant is minted — a request above it is REFUSED, never clamped, so a caller that asked for
23
+ * two hours learns from an error that the bound is sixty minutes rather than from an expiry it did not
24
+ * expect. The bound is the framework's to own because the grant is designed to leave the trust
25
+ * boundary: it lands in an AI agent's transcript, a model provider's logs, a shell history.
26
+ */
27
+ export const FILE_UPLOAD_GRANT_DEFAULT_MINUTES = 15;
28
+ export const FILE_UPLOAD_GRANT_ABSOLUTE_MAX_MINUTES = 60;
29
+ /**
30
+ * Which write the grant is for. A grant minted on the CREATE upload route uploads a file that a
31
+ * later CREATE will reference; one minted on the UPDATE route (`…/{resourceId}/files/<field>`) is
32
+ * bound to that row. The two routes authorize differently, so a grant may not cross from one to the
33
+ * other — the upload middleware checks this against the route it is presented on.
34
+ */
35
+ export const FILE_UPLOAD_GRANT_OPERATIONS = [CoreResourceOperation.CREATE, CoreResourceOperation.UPDATE];
36
+ /** Derived from `CoreResourceOperation` — the named source — rather than a second enum spelling the same two words. */
37
+ export const FileUploadGrantOperationSchema = z.enum(FILE_UPLOAD_GRANT_OPERATIONS);
38
+ /**
39
+ * Lifecycle of a grant as the status read reports it. Derived from the token row — `isConsumed`,
40
+ * `expiresAt`, `revokedAt` — never stored, so it cannot drift from the row.
41
+ */
42
+ export var FileUploadGrantState;
43
+ (function (FileUploadGrantState) {
44
+ /** Minted, unexpired, unrevoked, not yet redeemed: an upload will be accepted. */
45
+ FileUploadGrantState["PENDING"] = "pending";
46
+ /** Redeemed by exactly one upload. `file` names what it produced when the write-back succeeded. */
47
+ FileUploadGrantState["REDEEMED"] = "redeemed";
48
+ /** Past `expiresAt` without being redeemed. Mint a new one. */
49
+ FileUploadGrantState["EXPIRED"] = "expired";
50
+ /** Revoked by the minter or an administrator before redemption. */
51
+ FileUploadGrantState["REVOKED"] = "revoked";
52
+ })(FileUploadGrantState || (FileUploadGrantState = {}));
53
+ export const FileUploadGrantStateSchema = z.enum(FileUploadGrantState);
54
+ /**
55
+ * The `metadata` a FILE_UPLOAD_GRANT token carries — the ROUTE binding, as opposed to the identity
56
+ * the typed token fields carry. The upload middleware reads this; the execution-context creator
57
+ * deliberately does not.
58
+ */
59
+ export const FileUploadGrantMetadataSchema = z.object({
60
+ /** The `z_file()` field the upload lands on, as authored (dotted for nested fields). */
61
+ fieldName: z.string().min(1),
62
+ operation: FileUploadGrantOperationSchema,
63
+ /**
64
+ * Who minted — recorded so the uploaded file is attributed to a PERSON or MACHINE, not to the
65
+ * grant. `entityType` is a `FileUploaderEntityType` value; kept as a string here because the
66
+ * uploader enum lives in `zod-decorators`, upstream of this package.
67
+ */
68
+ minter: z.object({ entityType: z.string().min(1), id: z.string().min(1) }),
69
+ /** The concrete upload URL the grant was minted for — recorded so the status read can hand it to the drop page. */
70
+ uploadUrl: z.string().url(),
71
+ /** Written after a successful redemption; absent until then. */
72
+ producedFileId: z.string().min(1).optional(),
73
+ });
74
+ /**
75
+ * What a mint returns — identical on the REST door and the MCP tool. Two affordances for one
76
+ * token: `uploadCommand` for a caller that can execute, `uploadPageUrl` for one that must hand a
77
+ * human a link. The token appears in `uploadHeader.value` and inside both commands and the page URL;
78
+ * it is the credential and cannot be omitted. It never appears in any audit row.
79
+ */
80
+ export const FileUploadGrantSchema = z.object({
81
+ grantId: z.string().min(1),
82
+ state: FileUploadGrantStateSchema,
83
+ expiresAt: z.string().datetime(),
84
+ singleUse: z.literal(true),
85
+ target: z.object({
86
+ resourceType: z.string().min(1),
87
+ fieldName: z.string().min(1),
88
+ operation: FileUploadGrantOperationSchema,
89
+ resourceId: z.string().min(1).nullable(),
90
+ }),
91
+ /** From the field's own constraint resolver — the same one the upload enforces, so the two cannot disagree. */
92
+ constraints: z.object({
93
+ maxBytes: z.number().int().positive(),
94
+ /** Empty means every type is accepted. */
95
+ allowedMimeTypes: z.array(z.string()),
96
+ }),
97
+ uploadUrl: z.string().url(),
98
+ /** The credential travels in a HEADER on the curl form: paths are logged by every proxy, headers are not. */
99
+ uploadHeader: z.object({ name: z.string().min(1), value: z.string().min(1) }),
100
+ /** No `-f`: on a refusal the JSON error body is the only thing that tells the caller why. */
101
+ uploadCommand: z.string().min(1),
102
+ /** In PowerShell `curl` aliases Invoke-WebRequest, which does not understand `-F`; the real binary is `curl.exe`. */
103
+ uploadCommandPowerShell: z.string().min(1),
104
+ /** `null` when the application declares no frontend service — a headless API has no page to hand a human. */
105
+ uploadPageUrl: z.string().url().nullable(),
106
+ statusUrl: z.string().url(),
107
+ note: z.string().min(1),
108
+ });
109
+ /**
110
+ * What the public status read returns. The token is the credential, so the read is unauthenticated
111
+ * and returns only what the drop page needs to render and the agent needs to learn the `fileId`:
112
+ * never the file's name (the status URL may be pasted around).
113
+ */
114
+ export const FileUploadGrantStatusSchema = z.object({
115
+ grantId: z.string().min(1),
116
+ state: FileUploadGrantStateSchema,
117
+ expiresAt: z.string().datetime(),
118
+ target: z.object({
119
+ resourceType: z.string().min(1),
120
+ fieldName: z.string().min(1),
121
+ operation: FileUploadGrantOperationSchema,
122
+ resourceId: z.string().min(1).nullable(),
123
+ }),
124
+ constraints: z.object({ maxBytes: z.number().int().positive(), allowedMimeTypes: z.array(z.string()) }),
125
+ uploadUrl: z.string().url(),
126
+ /** The page sets this header itself; the token it already holds is the value. */
127
+ uploadHeaderName: z.string().min(1),
128
+ /** `null` until redeemed, or when the post-redemption write-back failed (re-read the resource instead). */
129
+ file: z.object({ fileId: z.string().min(1), status: z.enum(FileStatus) }).nullable(),
130
+ });
131
+ /** Body accepted by both mint doors. */
132
+ export const FileUploadGrantMintRequestSchema = z.object({
133
+ expiresInMinutes: z.number().int().min(1).max(FILE_UPLOAD_GRANT_ABSOLUTE_MAX_MINUTES).optional(),
134
+ }).strict();
135
+ /** @wildo_source:part:end saas.models.file-upload-grant */
136
+ //# sourceMappingURL=file-upload-grant.shared.schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-upload-grant.shared.schemas.js","sourceRoot":"","sources":["../../../../src/files/file-upload-grant.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { CoreResourceOperation } from '../resources/resources.shared.core.definitions';\nimport { FileStatus } from './files.shared.schemas';\n\n/**\n * File upload grant — the shared contract between the two mint doors (REST, MCP), the upload route,\n * the status read, and the browser drop page (issue #312).\n *\n * A grant is a single-use consumable token. Nothing durable is created at mint but the token: an\n * abandoned grant leaves a dead token and nothing else. The upload itself goes through the resource's\n * EXISTING file-upload route, authenticated by the grant alone, and the ordinary authorizer decides.\n */\n\n/**\n * Public frontend route the browser drop page lives on; the token follows as the next path segment\n * (`/upload-grant/<token>`). Declared HERE so the backend (which builds `uploadPageUrl`) and the\n * frontend (which mounts the route) read one source — the backend cannot import frontend route\n * constants.\n */\nexport const FILE_UPLOAD_GRANT_PAGE_PATH = '/upload-grant';\n\n/** @wildo_source:part:start saas.models.file-upload-grant facet:layer:shared facet:family:files\n *\n * Lifetime bounds of an upload grant, in minutes. The ceiling is fixed by the framework and enforced\n * when the grant is minted — a request above it is REFUSED, never clamped, so a caller that asked for\n * two hours learns from an error that the bound is sixty minutes rather than from an expiry it did not\n * expect. The bound is the framework's to own because the grant is designed to leave the trust\n * boundary: it lands in an AI agent's transcript, a model provider's logs, a shell history.\n */\nexport const FILE_UPLOAD_GRANT_DEFAULT_MINUTES = 15;\nexport const FILE_UPLOAD_GRANT_ABSOLUTE_MAX_MINUTES = 60;\n\n/**\n * Which write the grant is for. A grant minted on the CREATE upload route uploads a file that a\n * later CREATE will reference; one minted on the UPDATE route (`…/{resourceId}/files/<field>`) is\n * bound to that row. The two routes authorize differently, so a grant may not cross from one to the\n * other — the upload middleware checks this against the route it is presented on.\n */\nexport const FILE_UPLOAD_GRANT_OPERATIONS = [CoreResourceOperation.CREATE, CoreResourceOperation.UPDATE] as const;\nexport type FileUploadGrantOperation = (typeof FILE_UPLOAD_GRANT_OPERATIONS)[number];\n/** Derived from `CoreResourceOperation` — the named source — rather than a second enum spelling the same two words. */\nexport const FileUploadGrantOperationSchema: z.ZodEnum<{ [K in FileUploadGrantOperation]: K }> = z.enum(FILE_UPLOAD_GRANT_OPERATIONS);\n\n/**\n * Lifecycle of a grant as the status read reports it. Derived from the token row — `isConsumed`,\n * `expiresAt`, `revokedAt` — never stored, so it cannot drift from the row.\n */\nexport enum FileUploadGrantState {\n /** Minted, unexpired, unrevoked, not yet redeemed: an upload will be accepted. */\n PENDING = 'pending',\n /** Redeemed by exactly one upload. `file` names what it produced when the write-back succeeded. */\n REDEEMED = 'redeemed',\n /** Past `expiresAt` without being redeemed. Mint a new one. */\n EXPIRED = 'expired',\n /** Revoked by the minter or an administrator before redemption. */\n REVOKED = 'revoked',\n}\nexport const FileUploadGrantStateSchema: z.ZodEnum<typeof FileUploadGrantState> = z.enum(FileUploadGrantState);\n\n/**\n * The `metadata` a FILE_UPLOAD_GRANT token carries — the ROUTE binding, as opposed to the identity\n * the typed token fields carry. The upload middleware reads this; the execution-context creator\n * deliberately does not.\n */\nexport const FileUploadGrantMetadataSchema: z.ZodObject<{\n fieldName: z.ZodString;\n operation: z.ZodEnum<{ [K in FileUploadGrantOperation]: K }>;\n minter: z.ZodObject<{ entityType: z.ZodString; id: z.ZodString }>;\n uploadUrl: z.ZodString;\n producedFileId: z.ZodOptional<z.ZodString>;\n}> = z.object({\n /** The `z_file()` field the upload lands on, as authored (dotted for nested fields). */\n fieldName: z.string().min(1),\n operation: FileUploadGrantOperationSchema,\n /**\n * Who minted — recorded so the uploaded file is attributed to a PERSON or MACHINE, not to the\n * grant. `entityType` is a `FileUploaderEntityType` value; kept as a string here because the\n * uploader enum lives in `zod-decorators`, upstream of this package.\n */\n minter: z.object({ entityType: z.string().min(1), id: z.string().min(1) }),\n /** The concrete upload URL the grant was minted for — recorded so the status read can hand it to the drop page. */\n uploadUrl: z.string().url(),\n /** Written after a successful redemption; absent until then. */\n producedFileId: z.string().min(1).optional(),\n});\nexport type FileUploadGrantMetadata = z.infer<typeof FileUploadGrantMetadataSchema>;\n\n/**\n * What a mint returns — identical on the REST door and the MCP tool. Two affordances for one\n * token: `uploadCommand` for a caller that can execute, `uploadPageUrl` for one that must hand a\n * human a link. The token appears in `uploadHeader.value` and inside both commands and the page URL;\n * it is the credential and cannot be omitted. It never appears in any audit row.\n */\nexport const FileUploadGrantSchema: z.ZodObject<{\n grantId: z.ZodString;\n state: z.ZodEnum<typeof FileUploadGrantState>;\n expiresAt: z.ZodString;\n singleUse: z.ZodLiteral<true>;\n target: z.ZodObject<{\n resourceType: z.ZodString;\n fieldName: z.ZodString;\n operation: z.ZodEnum<{ [K in FileUploadGrantOperation]: K }>;\n resourceId: z.ZodNullable<z.ZodString>;\n }>;\n constraints: z.ZodObject<{\n maxBytes: z.ZodNumber;\n allowedMimeTypes: z.ZodArray<z.ZodString>;\n }>;\n uploadUrl: z.ZodString;\n uploadHeader: z.ZodObject<{ name: z.ZodString; value: z.ZodString }>;\n uploadCommand: z.ZodString;\n uploadCommandPowerShell: z.ZodString;\n uploadPageUrl: z.ZodNullable<z.ZodString>;\n statusUrl: z.ZodString;\n note: z.ZodString;\n}> = z.object({\n grantId: z.string().min(1),\n state: FileUploadGrantStateSchema,\n expiresAt: z.string().datetime(),\n singleUse: z.literal(true),\n target: z.object({\n resourceType: z.string().min(1),\n fieldName: z.string().min(1),\n operation: FileUploadGrantOperationSchema,\n resourceId: z.string().min(1).nullable(),\n }),\n /** From the field's own constraint resolver — the same one the upload enforces, so the two cannot disagree. */\n constraints: z.object({\n maxBytes: z.number().int().positive(),\n /** Empty means every type is accepted. */\n allowedMimeTypes: z.array(z.string()),\n }),\n uploadUrl: z.string().url(),\n /** The credential travels in a HEADER on the curl form: paths are logged by every proxy, headers are not. */\n uploadHeader: z.object({ name: z.string().min(1), value: z.string().min(1) }),\n /** No `-f`: on a refusal the JSON error body is the only thing that tells the caller why. */\n uploadCommand: z.string().min(1),\n /** In PowerShell `curl` aliases Invoke-WebRequest, which does not understand `-F`; the real binary is `curl.exe`. */\n uploadCommandPowerShell: z.string().min(1),\n /** `null` when the application declares no frontend service — a headless API has no page to hand a human. */\n uploadPageUrl: z.string().url().nullable(),\n statusUrl: z.string().url(),\n note: z.string().min(1),\n});\nexport type FileUploadGrant = z.infer<typeof FileUploadGrantSchema>;\n\n/**\n * What the public status read returns. The token is the credential, so the read is unauthenticated\n * and returns only what the drop page needs to render and the agent needs to learn the `fileId`:\n * never the file's name (the status URL may be pasted around).\n */\nexport const FileUploadGrantStatusSchema: z.ZodObject<{\n grantId: z.ZodString;\n state: z.ZodEnum<typeof FileUploadGrantState>;\n expiresAt: z.ZodString;\n target: z.ZodObject<{\n resourceType: z.ZodString;\n fieldName: z.ZodString;\n operation: z.ZodEnum<{ [K in FileUploadGrantOperation]: K }>;\n resourceId: z.ZodNullable<z.ZodString>;\n }>;\n constraints: z.ZodObject<{ maxBytes: z.ZodNumber; allowedMimeTypes: z.ZodArray<z.ZodString> }>;\n uploadUrl: z.ZodString;\n uploadHeaderName: z.ZodString;\n file: z.ZodNullable<z.ZodObject<{ fileId: z.ZodString; status: z.ZodEnum<typeof FileStatus> }>>;\n}> = z.object({\n grantId: z.string().min(1),\n state: FileUploadGrantStateSchema,\n expiresAt: z.string().datetime(),\n target: z.object({\n resourceType: z.string().min(1),\n fieldName: z.string().min(1),\n operation: FileUploadGrantOperationSchema,\n resourceId: z.string().min(1).nullable(),\n }),\n constraints: z.object({ maxBytes: z.number().int().positive(), allowedMimeTypes: z.array(z.string()) }),\n uploadUrl: z.string().url(),\n /** The page sets this header itself; the token it already holds is the value. */\n uploadHeaderName: z.string().min(1),\n /** `null` until redeemed, or when the post-redemption write-back failed (re-read the resource instead). */\n file: z.object({ fileId: z.string().min(1), status: z.enum(FileStatus) }).nullable(),\n});\nexport type FileUploadGrantStatus = z.infer<typeof FileUploadGrantStatusSchema>;\n\n/** Body accepted by both mint doors. */\nexport const FileUploadGrantMintRequestSchema: z.ZodObject<{ expiresInMinutes: z.ZodOptional<z.ZodNumber> }> = z.object({\n expiresInMinutes: z.number().int().min(1).max(FILE_UPLOAD_GRANT_ABSOLUTE_MAX_MINUTES).optional(),\n}).strict();\nexport type FileUploadGrantMintRequest = z.infer<typeof FileUploadGrantMintRequestSchema>;\n/** @wildo_source:part:end saas.models.file-upload-grant */\n"]}
@@ -78,10 +78,19 @@ export declare function parseFileServeVariant(raw: unknown): FileServeVariant;
78
78
  /**
79
79
  * Statuses in which a file's bytes are safe to serve for download.
80
80
  *
81
- * Wave 1 rule: AV scanning is not enforced yet, so UPLOADED and LINKED
82
- * remain downloadable (optimistic). When AV is active, only CLEAN / LINKED
83
- * (post-scan) files should be downloadable; SCANNING / INFECTED /
84
- * QUARANTINED must be blocked.
81
+ * AV scanning IS wired: `FileScanningBackendService` is bound in the container and the resource
82
+ * upload handler calls `shouldScan` then `initiateScan`, so a scanned file moves through SCANNING
83
+ * to CLEAN / INFECTED / QUARANTINED. `SCANNING`, `INFECTED` and `QUARANTINED` are absent from this
84
+ * set and therefore blocked, which is the post-scan half of the original Wave 1 note.
85
+ *
86
+ * ⚠️ `UPLOADED` is still admitted, and that is the OPEN question this comment used to hide behind
87
+ * "scanning is not enforced yet". Two reasons it is not simply a leftover, and neither is settled:
88
+ * scanning is opt-in per field (`shouldScan` reads the field's `fileMetadata`), so an unscanned
89
+ * file legitimately terminates at UPLOADED and removing it from this set would make every such
90
+ * field undownloadable; and `initiateScan` is fired without being awaited, so even for a scanned
91
+ * field there is a window between the upload committing and the status reaching SCANNING in which
92
+ * the bytes are servable. Closing that window is a behaviour change with its own decision — see
93
+ * the tracker — not something to infer from this comment.
85
94
  */
86
95
  export declare const FILE_DOWNLOADABLE_STATUSES: ReadonlySet<FileStatus>;
87
96
  /**
@@ -121,6 +130,38 @@ export declare const FILE_BLOCKED_STATUSES: ReadonlySet<FileStatus>;
121
130
  * transition to. Any transition not listed here is invalid.
122
131
  */
123
132
  export declare const FILE_STATUS_TRANSITIONS: Readonly<Record<FileStatus, readonly FileStatus[]>>;
133
+ /**
134
+ * Statuses from which a file may be LINKED to a resource field — the set the link-time checks
135
+ * (`FileOperationHandlerBackendService.validateFileFieldsForCreate/Update`,
136
+ * `FilesBackendService.linkFileToResource`) consult.
137
+ *
138
+ * ## Why this set exists (issue #320)
139
+ *
140
+ * Those checks used to compare against the literal `FileStatus.UPLOADED`, while
141
+ * {@link FILE_STATUS_TRANSITIONS} — the authority on what a status MEANS — already allowed
142
+ * `CLEAN → LINKED`. So a file the scanner had just certified safe was refused at the one moment it was
143
+ * needed, with an error naming `UPLOADED` as the expected state, and whether a scanned file could be
144
+ * attached at all depended on how fast the caller was relative to the scanner. Two `$in` queries in
145
+ * `FilesBackendService` had meanwhile grown their own `[UPLOADED, CLEAN]` pairs — three copies of one
146
+ * answer, one of them wrong. This is the single copy.
147
+ *
148
+ * ## Derived-CHECKED against the map, not computed from it
149
+ *
150
+ * Every member here has `LINKED` among its outgoing transitions (pinned by
151
+ * `files.shared.status-sets.test.ts`), so this set can never admit a transition the map forbids.
152
+ * It is nonetheless written out rather than computed, because the map is a superset of what linking
153
+ * should admit today:
154
+ *
155
+ * - `ORPHANED → LINKED` is legal in the map (a re-link after the parent was deleted with the file
156
+ * kept) and is DELIBERATELY NOT admitted here. Re-linking an orphan is a distinct capability with
157
+ * its own questions — whose scope does the file re-enter, what happens to its old key — and
158
+ * admitting it as a side effect of fixing the `CLEAN` refusal would decide those questions by
159
+ * accident. The pin asserts this exclusion by name so widening it is a choice, not drift.
160
+ * - `SCANNING` is absent because the map forbids `SCANNING → LINKED`: a file whose scan is in
161
+ * flight is not yet known to be safe. The link path names that case with
162
+ * `FILE_SCAN_IN_PROGRESS` so the caller learns to retry, rather than the generic refusal.
163
+ */
164
+ export declare const FILE_LINKABLE_STATUSES: ReadonlySet<FileStatus>;
124
165
  export declare const FILE_PRIMARY_SCOPES: readonly [ResourcePrimaryScope.APPLICATION, ResourcePrimaryScope.ORGANIZATIONS, ResourcePrimaryScope.USER_SELF];
125
166
  export type FilePrimaryScope = (typeof FILE_PRIMARY_SCOPES)[number];
126
167
  export declare function validateFileScopeIdentifiers(scope: {
@@ -1 +1 @@
1
- {"version":3,"file":"files.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/files/files.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtI,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAG7E,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACvF,YAAY,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;;;;;;;;GAYG;AACH,oBAAY,UAAU;IACpB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IAEjB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,WAAW,gBAAgB;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,oBAAY,gBAAgB;IAC1B,QAAQ,aAAa;IACrB,SAAS,cAAc;CACxB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAIpE;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,UAAU,CAK7D,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,UAAU,CAElE,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,aAAa,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,UAAU,CAOxD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC,CAWvF,CAAC;AAEF,eAAO,MAAM,mBAAmB,iHAItB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAyBpE,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAqBV;AAuGD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,GAAG,qBAAqB,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,qGAAqG;IACrG,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"files.shared.schemas.d.ts","sourceRoot":"","sources":["../../../../src/files/files.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACtI,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAG7E,YAAY,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACvF,YAAY,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;;;;;;;;GAYG;AACH,oBAAY,UAAU;IACpB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IAEjB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,WAAW,gBAAgB;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,oBAAY,gBAAgB;IAC1B,QAAQ,aAAa;IACrB,SAAS,cAAc;CACxB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,UAAU,CAK7D,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,UAAU,CAElE,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,aAAa,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,UAAU,CAOxD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC,CAWvF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,UAAU,CAGzD,CAAC;AAGH,eAAO,MAAM,mBAAmB,iHAItB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAyBpE,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAqBV;AAuGD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,GAAG,qBAAqB,CAAC;AAEtE;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,qGAAqG;IACrG,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -83,10 +83,19 @@ export function parseFileServeVariant(raw) {
83
83
  /**
84
84
  * Statuses in which a file's bytes are safe to serve for download.
85
85
  *
86
- * Wave 1 rule: AV scanning is not enforced yet, so UPLOADED and LINKED
87
- * remain downloadable (optimistic). When AV is active, only CLEAN / LINKED
88
- * (post-scan) files should be downloadable; SCANNING / INFECTED /
89
- * QUARANTINED must be blocked.
86
+ * AV scanning IS wired: `FileScanningBackendService` is bound in the container and the resource
87
+ * upload handler calls `shouldScan` then `initiateScan`, so a scanned file moves through SCANNING
88
+ * to CLEAN / INFECTED / QUARANTINED. `SCANNING`, `INFECTED` and `QUARANTINED` are absent from this
89
+ * set and therefore blocked, which is the post-scan half of the original Wave 1 note.
90
+ *
91
+ * ⚠️ `UPLOADED` is still admitted, and that is the OPEN question this comment used to hide behind
92
+ * "scanning is not enforced yet". Two reasons it is not simply a leftover, and neither is settled:
93
+ * scanning is opt-in per field (`shouldScan` reads the field's `fileMetadata`), so an unscanned
94
+ * file legitimately terminates at UPLOADED and removing it from this set would make every such
95
+ * field undownloadable; and `initiateScan` is fired without being awaited, so even for a scanned
96
+ * field there is a window between the upload committing and the status reaching SCANNING in which
97
+ * the bytes are servable. Closing that window is a behaviour change with its own decision — see
98
+ * the tracker — not something to infer from this comment.
90
99
  */
91
100
  export const FILE_DOWNLOADABLE_STATUSES = new Set([
92
101
  FileStatus.UPLOADED,
@@ -151,6 +160,41 @@ export const FILE_STATUS_TRANSITIONS = {
151
160
  [FileStatus.DELETED]: [],
152
161
  [FileStatus.FAILED]: [FileStatus.DELETED],
153
162
  };
163
+ /**
164
+ * Statuses from which a file may be LINKED to a resource field — the set the link-time checks
165
+ * (`FileOperationHandlerBackendService.validateFileFieldsForCreate/Update`,
166
+ * `FilesBackendService.linkFileToResource`) consult.
167
+ *
168
+ * ## Why this set exists (issue #320)
169
+ *
170
+ * Those checks used to compare against the literal `FileStatus.UPLOADED`, while
171
+ * {@link FILE_STATUS_TRANSITIONS} — the authority on what a status MEANS — already allowed
172
+ * `CLEAN → LINKED`. So a file the scanner had just certified safe was refused at the one moment it was
173
+ * needed, with an error naming `UPLOADED` as the expected state, and whether a scanned file could be
174
+ * attached at all depended on how fast the caller was relative to the scanner. Two `$in` queries in
175
+ * `FilesBackendService` had meanwhile grown their own `[UPLOADED, CLEAN]` pairs — three copies of one
176
+ * answer, one of them wrong. This is the single copy.
177
+ *
178
+ * ## Derived-CHECKED against the map, not computed from it
179
+ *
180
+ * Every member here has `LINKED` among its outgoing transitions (pinned by
181
+ * `files.shared.status-sets.test.ts`), so this set can never admit a transition the map forbids.
182
+ * It is nonetheless written out rather than computed, because the map is a superset of what linking
183
+ * should admit today:
184
+ *
185
+ * - `ORPHANED → LINKED` is legal in the map (a re-link after the parent was deleted with the file
186
+ * kept) and is DELIBERATELY NOT admitted here. Re-linking an orphan is a distinct capability with
187
+ * its own questions — whose scope does the file re-enter, what happens to its old key — and
188
+ * admitting it as a side effect of fixing the `CLEAN` refusal would decide those questions by
189
+ * accident. The pin asserts this exclusion by name so widening it is a choice, not drift.
190
+ * - `SCANNING` is absent because the map forbids `SCANNING → LINKED`: a file whose scan is in
191
+ * flight is not yet known to be safe. The link path names that case with
192
+ * `FILE_SCAN_IN_PROGRESS` so the caller learns to retry, rather than the generic refusal.
193
+ */
194
+ export const FILE_LINKABLE_STATUSES = new Set([
195
+ FileStatus.UPLOADED,
196
+ FileStatus.CLEAN,
197
+ ]);
154
198
  export const FILE_PRIMARY_SCOPES = [
155
199
  ResourcePrimaryScope.APPLICATION,
156
200
  ResourcePrimaryScope.ORGANIZATIONS,
@@ -1 +1 @@
1
- {"version":3,"file":"files.shared.schemas.js","sourceRoot":"","sources":["../../../../src/files/files.shared.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6DAA6D,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,sBAAsB,EAA8B,MAAM,0BAA0B,CAAC;AACtI,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAI7E,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGvF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAN,IAAY,UAYX;AAZD,WAAY,UAAU;IACpB,iCAAmB,CAAA;IACnB,mCAAqB,CAAA;IACrB,+BAAiB,CAAA;IACjB,iCAAmB,CAAA;IACnB,mCAAqB,CAAA;IACrB,+BAAiB,CAAA;IAEjB,mCAAqB,CAAA;IACrB,6BAAe,CAAA;IACf,mCAAqB,CAAA;IACrB,yCAA2B,CAAA;AAC7B,CAAC,EAZW,UAAU,KAAV,UAAU,QAYrB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAN,IAAY,gBAGX;AAHD,WAAY,gBAAgB;IAC1B,yCAAqB,CAAA;IACrB,2CAAuB,CAAA;AACzB,CAAC,EAHW,gBAAgB,KAAhB,gBAAgB,QAG3B;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,OAAO,GAAG,KAAK,gBAAgB,CAAC,SAAS;QACvC,CAAC,CAAC,gBAAgB,CAAC,SAAS;QAC5B,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAA4B,IAAI,GAAG,CAAC;IACzE,UAAU,CAAC,QAAQ;IACnB,UAAU,CAAC,MAAM;IACjB,UAAU,CAAC,QAAQ;IACnB,UAAU,CAAC,KAAK;CACjB,CAAC,CAAC;AAEH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAA4B,IAAI,GAAG,CAAC;IAC9E,UAAU,CAAC,OAAO;CACnB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,UAAU,CAAC;AAEvD;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA4B,IAAI,GAAG,CAAC;IACpE,UAAU,CAAC,OAAO;IAClB,UAAU,CAAC,OAAO;IAClB,UAAU,CAAC,MAAM;IACjB,UAAU,CAAC,QAAQ;IACnB,UAAU,CAAC,QAAQ;IACnB,UAAU,CAAC,WAAW;CACvB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAwD;IAC1F,CAAC,UAAU,CAAC,OAAO,CAAC,EAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC;IACnE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAM,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC;IAC/H,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAM,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC;IACrF,CAAC,UAAU,CAAC,KAAK,CAAC,EAAS,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC;IACvF,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAM,CAAC,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC;IACvE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAG,CAAC,UAAU,CAAC,OAAO,CAAC;IAC/C,CAAC,UAAU,CAAC,MAAM,CAAC,EAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC;IACpE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAM,CAAC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC;IAClE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAO,EAAE;IAC7B,CAAC,UAAU,CAAC,MAAM,CAAC,EAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;CAChD,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,oBAAoB,CAAC,WAAW;IAChC,oBAAoB,CAAC,aAAa;IAClC,oBAAoB,CAAC,SAAS;CACtB,CAAC;AAIX,SAAS,2BAA2B,CAAC,KAAyB;IAC5D,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,oBAAoB,CAC3B,IAGC,EACD,GAAoB;IAEpB,MAAM,aAAa,GAAG,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnE,MAAM,uBAAuB,GAAG,IAAI,CAAC,oBAAoB,KAAK,SAAS,CAAC;IAExE,IAAI,aAAa,KAAK,uBAAuB,EAAE,CAAC;QAC9C,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;YAC/D,OAAO,EAAE,gFAAgF;SAC1F,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAK5C;IACC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC1E,MAAM,iBAAiB,GAAG,2BAA2B,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC5E,MAAM,SAAS,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE5D,QAAQ,KAAK,CAAC,YAAY,EAAE,CAAC;QAC3B,KAAK,oBAAoB,CAAC,WAAW;YACnC,OAAO,gBAAgB,IAAI,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC;QAC9D,KAAK,oBAAoB,CAAC,aAAa;YACrC,kGAAkG;YAClG,OAAO,gBAAgB,IAAI,iBAAiB,IAAI,CAAC,SAAS,CAAC;QAC7D,KAAK,oBAAoB,CAAC,SAAS;YACjC,8EAA8E;YAC9E,4EAA4E;YAC5E,+EAA+E;YAC/E,gFAAgF;YAChF,yCAAyC;YACzC,OAAO,gBAAgB,IAAI,SAAS,IAAI,CAAC,iBAAiB,CAAC;QAC7D;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,cAAc,EAAE;IAEzD,sCAAsC;IACtC,+FAA+F;IAC/F,8FAA8F;IAC9F,oGAAoG;IACpG,sDAAsD;IACtD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,cAAc,EAAE,CAAC,cAAc,CAAC,oBAAoB,CAAC,YAAY,CAAC;IAC5G,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,cAAc,CAAC,oBAAoB,CAAC,YAAY,CAAC;IACtG,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,cAAc,EAAE;IAC1D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE;IACxC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAE7C,gCAAgC;IAChC,4EAA4E;IAC5E,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IACrE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IACtD,uFAAuF;IACvF,wFAAwF;IACxF,wFAAwF;IACxF,0FAA0F;IAC1F,gBAAgB;IAChB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IACtD,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,gCAAgC;IAEzD,2CAA2C;IAC3C,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,cAAc,EAAE;IAErF,iDAAiD;IACjD,8DAA8D;IAC9D,kGAAkG;IAClG,sGAAsG;IACtG,oGAAoG;IACpG,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAC1D,iBAAiB;IACjB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACvC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxC,YAAY,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAChC,8EAA8E;IAC9E,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,sBAAsB;IAClE,yDAAyD;IACzD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAE3C,yCAAyC;IACzC,4EAA4E;IAC5E,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IACvD,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IACrD,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAEtC,sCAAsC;IACtC,mEAAmE;IACnE,6CAA6C;IAC7C,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAElE,0CAA0C;IAC1C,oFAAoF;IACpF,4FAA4F;IAC5F,2FAA2F;IAE3F,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;IAEvC,mFAAmF;IACnF,oFAAoF;IACpF,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAEnD,uFAAuF;IACvF,sFAAsF;IACtF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAE3C,wCAAwC;IACxC,oEAAoE;IACpE,oEAAoE;IACpE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAC7D,oBAAoB,EAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE;IAE7E,mCAAmC;IACnC,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,cAAc,EAAE,CAAC,iBAAiB,EAAE,CAAC,iBAAiB,EAAE;IACrH,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,iBAAiB,EAAE,CAAC,iBAAiB,EAAE;IACnG,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC,iBAAiB,EAAE;CACjE,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACjE,oBAAoB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAEhC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;QACvB,+FAA+F;QAC/F,kFAAkF;QAClF,IAAI,2BAA2B,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,2BAA2B,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACjG,GAAG,CAAC,QAAQ,CAAC;gBACX,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,cAAc,CAAC;gBACtB,OAAO,EAAE,oEAAoE;aAC9E,CAAC,CAAC;QACL,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,CAAC,4BAA4B,CAAC;QAChC,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC,EAAE,CAAC;QACH,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,cAAc,CAAC;YACtB,OAAO,EAAE,6DAA6D;SACvE,CAAC,CAAC;IACL,CAAC;AAEH,CAAC,CAAC,CAAC","sourcesContent":["/**\n * Files Shared Schemas\n *\n * Reusable schemas for file uploads and file handling.\n *\n * ## Scope Model\n *\n * Files use a **segregated scope model** where scope identifiers are separate fields\n * (not hierarchical). A file belongs to exactly one primary scope:\n * - APPLICATION: App-level shared files (e.g., branding assets)\n * - ORGANIZATIONS: Org-scoped files (e.g., org documents)\n * - USER_SELF: User-private files owned by a single user (NO org — USER_SELF and\n * ORGANIZATIONS are independent primary-scope axes; `applicationId` is the bucket anchor)\n *\n * `ANONYMOUS` is intentionally excluded from the file contract for now. The\n * file layer does not yet define anonymous upload, storage, and access semantics\n * end to end, so file resources fail closed instead of pretending support.\n *\n * Scope is **derived from the parent resource's resourcePrimaryScope** when linking.\n * Access rights are **inherited from the z_file() field** on the parent resource.\n *\n * Note: FileMetadata is defined in @wildo-ai/zod-decorators\n */\n\nimport { z } from 'zod';\nimport { PersonalDataCategory } from '../compliance/privacy/personal-data-category.shared.schemas';\nimport { FileDisplayNature, FileStorageAccepted, FileUploaderEntityType, type FileUploaderOwnership } from '@wildo-ai/zod-decorators';\nimport { ResourcePrimaryScope } from '../resources/resources.shared.schemas';\n\n// Re-export FileMetadata from zod-decorators for convenience\nexport type { FileMetadata } from '@wildo-ai/zod-decorators';\nexport { FileUploaderEntityType, FileStorageAccepted } from '@wildo-ai/zod-decorators';\nexport type { FileUploaderOwnership } from '@wildo-ai/zod-decorators';\n\n/**\n * File status lifecycle enum\n *\n * NOTE — no extraction/indexing members. `EXTRACTING` / `EXTRACTED` / `INDEXED` were removed\n * 2026-08-26 with the resource-native RAG reshape (plan `resource-rag-capability`, Phase 1):\n * text extraction and embedding state are per-FIELD, per-CHUNK facts, and the RAG chunk rows\n * (Phase 2's derived-data tier, keyed `(resourceType, resourceId, fieldName, chunkIndex)` with a\n * NULLABLE `vector` as the \"needs embedding\" ledger) carry them. A single file-level status could\n * never say WHICH field's corpus was current — it was the wrong shape, not merely unused — and\n * `INDEXED` had already shipped one false claim (written to mean \"text was copied\", read as\n * \"searchable\": https://github.com/wildo-ai/wildo-ai/issues/46). The file lifecycle now ends at\n * upload/scan/link/erasure concerns, which really are per-file.\n */\nexport enum FileStatus {\n PENDING = 'pending',\n UPLOADED = 'uploaded',\n LINKED = 'linked',\n DELETED = 'deleted',\n ORPHANED = 'orphaned',\n FAILED = 'failed',\n\n SCANNING = 'scanning',\n CLEAN = 'clean',\n INFECTED = 'infected',\n QUARANTINED = 'quarantined',\n}\n\n/**\n * Rendition of a file's bytes requested from the serve route via `?variant=`.\n *\n * - `ORIGINAL` — the stored bytes, unchanged (the default when the param is absent).\n * - `THUMBNAIL` — a small, downscaled `webp` derived on the fly for preview surfaces\n * (nav avatar, list/card image) so a preview never pulls a multi-MB original. Only\n * raster images are eligible; for any other file the serve route transparently\n * falls back to the original bytes.\n *\n * This is the WIRE CONTRACT for the serve route's `variant` query parameter — shared\n * by the frontend (which builds `?variant=thumbnail` preview URLs) and the backend\n * serve route (which parses it). Keep the string values stable across releases.\n */\nexport enum FileServeVariant {\n ORIGINAL = 'original',\n THUMBNAIL = 'thumbnail',\n}\n\n/**\n * Parse an untrusted `?variant=` query value into a {@link FileServeVariant}.\n * Absent or unrecognized values fall back to `ORIGINAL` (fail-safe: an unknown\n * variant serves the untransformed bytes rather than erroring).\n */\nexport function parseFileServeVariant(raw: unknown): FileServeVariant {\n return raw === FileServeVariant.THUMBNAIL\n ? FileServeVariant.THUMBNAIL\n : FileServeVariant.ORIGINAL;\n}\n\n/**\n * Statuses in which a file's bytes are safe to serve for download.\n *\n * Wave 1 rule: AV scanning is not enforced yet, so UPLOADED and LINKED\n * remain downloadable (optimistic). When AV is active, only CLEAN / LINKED\n * (post-scan) files should be downloadable; SCANNING / INFECTED /\n * QUARANTINED must be blocked.\n */\nexport const FILE_DOWNLOADABLE_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.UPLOADED,\n FileStatus.LINKED,\n FileStatus.ORPHANED,\n FileStatus.CLEAN,\n]);\n\n/**\n * Statuses in which a file's own METADATA must no longer be disclosed on the GLOBAL\n * `GET /files/:fileId/metadata` route (gate **G-FILE**).\n *\n * Blocking the BYTES is not enough. A privacy erasure marks the file `DELETED`, but physical cleanup\n * is a batch that runs much later — so between the two, `originalFilename` (`passport-jane-doe.pdf`)\n * and `uploadedBy` stayed readable by any member of the file's scope. \"Erased\" that leaves the\n * filename readable for weeks is not erased.\n *\n * DELIBERATELY NARROWER than {@link FILE_BLOCKED_STATUSES}: `PENDING`, `SCANNING`, `FAILED` and\n * `INFECTED` files are not servable either, but their metadata IS the diagnostic surface an uploader\n * legitimately polls (\"upload failed\", \"scan in progress\"). Withholding those would break upload UX\n * while protecting nothing — none of them is an erasure outcome. `DELETED` is the terminal state (it\n * has no outgoing transition in {@link FILE_STATUS_TRANSITIONS}) and the one an erasure produces.\n */\nexport const FILE_METADATA_WITHHELD_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.DELETED,\n]);\n\n/**\n * Value written over a file's `filename` / `originalFilename` when the file is erased for privacy.\n *\n * Both fields are REQUIRED (`z.string().min(1)`), so an erasure cannot null them without a schema\n * reshape; overwriting with one shared, non-identifying constant erases the personal data while\n * keeping the row valid, greppable and obviously-erased in logs and admin surfaces. Deliberately a\n * fixed constant rather than a per-file token: two erased files must not be distinguishable by name.\n */\nexport const ERASED_FILE_NAME_PLACEHOLDER = '[erased]';\n\n/**\n * Statuses that represent terminal or non-servable states.\n * Files in these states must not be served for download.\n */\nexport const FILE_BLOCKED_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.PENDING,\n FileStatus.DELETED,\n FileStatus.FAILED,\n FileStatus.SCANNING,\n FileStatus.INFECTED,\n FileStatus.QUARANTINED,\n]);\n\n/**\n * Valid lifecycle transitions.\n *\n * Each key is a source status; the value is the set of statuses it can\n * transition to. Any transition not listed here is invalid.\n */\nexport const FILE_STATUS_TRANSITIONS: Readonly<Record<FileStatus, readonly FileStatus[]>> = {\n [FileStatus.PENDING]: [FileStatus.UPLOADED, FileStatus.FAILED],\n [FileStatus.UPLOADED]: [FileStatus.LINKED, FileStatus.SCANNING, FileStatus.ORPHANED, FileStatus.DELETED, FileStatus.FAILED],\n [FileStatus.SCANNING]: [FileStatus.CLEAN, FileStatus.INFECTED, FileStatus.FAILED],\n [FileStatus.CLEAN]: [FileStatus.LINKED, FileStatus.ORPHANED, FileStatus.DELETED],\n [FileStatus.INFECTED]: [FileStatus.QUARANTINED, FileStatus.DELETED],\n [FileStatus.QUARANTINED]: [FileStatus.DELETED],\n [FileStatus.LINKED]: [FileStatus.ORPHANED, FileStatus.DELETED],\n [FileStatus.ORPHANED]: [FileStatus.LINKED, FileStatus.DELETED],\n [FileStatus.DELETED]: [],\n [FileStatus.FAILED]: [FileStatus.DELETED],\n};\n\nexport const FILE_PRIMARY_SCOPES = [\n ResourcePrimaryScope.APPLICATION,\n ResourcePrimaryScope.ORGANIZATIONS,\n ResourcePrimaryScope.USER_SELF,\n] as const;\n\nexport type FilePrimaryScope = (typeof FILE_PRIMARY_SCOPES)[number];\n\nfunction hasFileScopeIdentifierValue(value: string | undefined): boolean {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction validateUploaderPair(\n file: {\n uploadedBy?: string;\n uploadedByEntityType?: FileUploaderEntityType;\n },\n ctx: z.RefinementCtx,\n): void {\n const hasUploadedBy = hasFileScopeIdentifierValue(file.uploadedBy);\n const hasUploadedByEntityType = file.uploadedByEntityType !== undefined;\n\n if (hasUploadedBy !== hasUploadedByEntityType) {\n ctx.addIssue({\n code: 'custom',\n path: hasUploadedBy ? ['uploadedByEntityType'] : ['uploadedBy'],\n message: 'uploadedBy and uploadedByEntityType must either both be set or both be omitted',\n });\n }\n}\n\nexport function validateFileScopeIdentifiers(scope: {\n primaryScope: FilePrimaryScope;\n applicationId: string;\n organizationId?: string;\n userId?: string;\n}): boolean {\n const hasApplicationId = hasFileScopeIdentifierValue(scope.applicationId);\n const hasOrganizationId = hasFileScopeIdentifierValue(scope.organizationId);\n const hasUserId = hasFileScopeIdentifierValue(scope.userId);\n\n switch (scope.primaryScope) {\n case ResourcePrimaryScope.APPLICATION:\n return hasApplicationId && !hasOrganizationId && !hasUserId;\n case ResourcePrimaryScope.ORGANIZATIONS:\n // applicationId is always required (tenant isolation); organizationId is the scope discriminator.\n return hasApplicationId && hasOrganizationId && !hasUserId;\n case ResourcePrimaryScope.USER_SELF:\n // Primary scopes are a SEPARATION OF CONCERN: USER_SELF and ORGANIZATIONS are\n // independent axes. A user's self-owned file is user-owned, NOT org-owned —\n // `userId` is the scope discriminator and `applicationId` is the tenant/bucket\n // anchor. `organizationId` MUST be absent (a present org would conflate the two\n // scopes). Fail closed on any org value.\n return hasApplicationId && hasUserId && !hasOrganizationId;\n default:\n return false;\n }\n}\n\n/**\n * File resource schema\n *\n * Stores file metadata and links files to resources.\n * Files are stored via app-managed storage providers (MinIO/S3, local directory, etc.),\n * this schema tracks metadata only.\n *\n * ## Storage Identity Convention\n * `storageProvider` + `storageRef` are the persisted routing truth used to\n * reach the active storage backend. `storageKey` remains framework metadata\n * that captures the file's derived scope path for bookkeeping, diagnostics,\n * and future storage contract evolution.\n *\n * `storageKey` includes scope path derived from primaryScope:\n * - APPLICATION: `app/{applicationId}/{fileId}`\n * - ORGANIZATIONS: `org/{organizationId}/{fileId}`\n * - USER_SELF: `user/{userId}/{fileId}` (owner is the user; no org segment)\n */\nconst FileSchemaBase = z.object({\n fileId: z.string().min(1).isPrimaryKey().isSummaryField(),\n\n // ========== File Metadata ==========\n // USER_CONTENT on both names. A filename is chosen by the person and routinely IS the personal\n // datum — \"scan-2026-medical.pdf\" discloses a special category before the file is opened. The\n // file's BYTES are not a field here (this resource is metadata over object storage), so this is the\n // only place the content axis can be declared at all.\n filename: z.string().min(1).isDBIndexed().isSummaryField().dataCategories(PersonalDataCategory.USER_CONTENT),\n originalFilename: z.string().min(1).isSummaryField().dataCategories(PersonalDataCategory.USER_CONTENT),\n mimeType: z.string().min(1).isDBIndexed().isSummaryField(),\n size: z.number().min(0).isSummaryField(),\n extension: z.string().optional(),\n checksum: z.string().optional().isDBIndexed(),\n\n // ========== Storage ==========\n // `storageProvider` + `storageRef` are the persisted backend-routing truth.\n storageProvider: z.enum(FileStorageAccepted).optional().isDBIndexed(),\n storageRef: z.string().min(1).optional().isDBIndexed(),\n // `storageKey` is still scoped metadata, not the storage backend handle. It embeds the\n // file identity (`_id`, surfaced as the `fileId` virtual), so it is derived + persisted\n // AFTER the row is created (insert-first) — hence optional at create time. `createFile`\n // sets it once `_id` exists, and `linkFileToResource` re-derives it when the file's scope\n // is finalized.\n storageKey: z.string().min(1).optional().isDBIndexed(),\n url: z.url().optional(), // Cached URL (may be presigned)\n\n // ========== Status & Lifecycle ==========\n status: z.enum(FileStatus).default(FileStatus.PENDING).isDBIndexed().isSummaryField(),\n\n // ========== Nature-specific Metadata ==========\n // Inferred from MIME type or set by analysis services (F4-03)\n // Resolved display nature (IMAGE/VIDEO/AUDIO/DOCUMENT/GENERIC) — NOT the schema-level FileNature.\n // Set by resolveFileDisplayNature() during upload or inferred from MIME type in toMetadataResponse().\n // Schema declares FileNature (e.g., PDF, OFFICE), but the DB stores the resolved FileDisplayNature.\n nature: z.enum(FileDisplayNature).optional().isDBIndexed(),\n // Image-specific\n width: z.number().positive().optional(),\n height: z.number().positive().optional(),\n thumbnailUrl: z.url().optional(),\n // Video/Audio-specific (populated by IFileVideoAnalyzer / IFileAudioAnalyzer)\n duration: z.number().positive().optional(), // Duration in seconds\n // Document-specific (populated by IFileDocumentAnalyzer)\n pageCount: z.number().positive().optional(),\n\n // ========== Resource Linking ==========\n // When linked, scope is derived from parent resource's resourcePrimaryScope\n linkedResourceType: z.string().optional().isDBIndexed(),\n linkedResourceId: z.string().optional().isDBIndexed(),\n linkedFieldName: z.string().optional(),\n\n // ========== Primary Scope ==========\n // Derived from parent resource's resourcePrimaryScope when linking\n // Determines access control and storage path\n primaryScope: z.enum(FILE_PRIMARY_SCOPES).optional().isDBIndexed(),\n\n // ========== Scope Identifiers ==========\n // applicationId is ALWAYS set (from execution context at creation time) — it is the\n // top-level tenant isolation field present on every file record regardless of primaryScope.\n // organizationId and userId are populated only when the file's primaryScope requires them.\n\n applicationId: z.string().isDBIndexed(),\n\n // ORGANIZATIONS scope: org-scoped files. Set ONLY for ORGANIZATIONS-scoped files —\n // never for USER_SELF (the two scopes are independent axes, separation of concern).\n organizationId: z.string().optional().isDBIndexed(),\n\n // USER_SELF scope: user-private files owned by one user. Set ONLY for USER_SELF-scoped\n // files; the owner is the user (no org). `applicationId` is the tenant/bucket anchor.\n userId: z.string().optional().isDBIndexed(),\n\n // ========== Upload Metadata ==========\n // The uploader principal is persisted as a raw id + type pair only.\n // Any richer label/entity hydration is intentionally client-driven.\n uploadedBy: z.string().trim().min(1).optional().isDBIndexed(),\n uploadedByEntityType: z.enum(FileUploaderEntityType).optional().isDBIndexed(),\n\n // ========== Timestamps ==========\n uploadedAt: z.date().default(() => new Date()).isDBIndexed().isSummaryField().excludeFromCreate().excludeFromUpdate(),\n updatedAt: z.date().default(() => new Date()).isDBIndexed().excludeFromCreate().excludeFromUpdate(),\n deletedAt: z.date().optional().isDBIndexed().excludeFromCreate(),\n});\n\nexport const FileSchema = FileSchemaBase.superRefine((file, ctx) => {\n validateUploaderPair(file, ctx);\n\n if (!file.primaryScope) {\n // Pre-link state: applicationId is always present (set from execution context at upload time),\n // but organizationId/userId must not be set without a primaryScope discriminator.\n if (hasFileScopeIdentifierValue(file.organizationId) || hasFileScopeIdentifierValue(file.userId)) {\n ctx.addIssue({\n code: 'custom',\n path: ['primaryScope'],\n message: 'primaryScope is required when organizationId or userId are present',\n });\n }\n return;\n }\n\n if (!validateFileScopeIdentifiers({\n primaryScope: file.primaryScope,\n applicationId: file.applicationId,\n organizationId: file.organizationId,\n userId: file.userId,\n })) {\n ctx.addIssue({\n code: 'custom',\n path: ['primaryScope'],\n message: 'File scope identifiers must match the declared primaryScope',\n });\n }\n\n});\nexport type File = z.infer<typeof FileSchema> & FileUploaderOwnership;\n\n/**\n * Scope identifiers for file access control.\n * Populated based on primaryScope when file is created/linked.\n */\nexport interface FileScopeIdentifiers {\n primaryScope: FilePrimaryScope;\n /** Always required — top-level tenant isolation key, set from execution context at file creation. */\n applicationId: string;\n organizationId?: string;\n userId?: string;\n}\n"]}
1
+ {"version":3,"file":"files.shared.schemas.js","sourceRoot":"","sources":["../../../../src/files/files.shared.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Files Shared Schemas\n *\n * Reusable schemas for file uploads and file handling.\n *\n * ## Scope Model\n *\n * Files use a **segregated scope model** where scope identifiers are separate fields\n * (not hierarchical). A file belongs to exactly one primary scope:\n * - APPLICATION: App-level shared files (e.g., branding assets)\n * - ORGANIZATIONS: Org-scoped files (e.g., org documents)\n * - USER_SELF: User-private files owned by a single user (NO org — USER_SELF and\n * ORGANIZATIONS are independent primary-scope axes; `applicationId` is the bucket anchor)\n *\n * `ANONYMOUS` is intentionally excluded from the file contract for now. The\n * file layer does not yet define anonymous upload, storage, and access semantics\n * end to end, so file resources fail closed instead of pretending support.\n *\n * Scope is **derived from the parent resource's resourcePrimaryScope** when linking.\n * Access rights are **inherited from the z_file() field** on the parent resource.\n *\n * Note: FileMetadata is defined in @wildo-ai/zod-decorators\n */\n\nimport { z } from 'zod';\nimport { PersonalDataCategory } from '../compliance/privacy/personal-data-category.shared.schemas';\nimport { FileDisplayNature, FileStorageAccepted, FileUploaderEntityType, type FileUploaderOwnership } from '@wildo-ai/zod-decorators';\nimport { ResourcePrimaryScope } from '../resources/resources.shared.schemas';\n\n// Re-export FileMetadata from zod-decorators for convenience\nexport type { FileMetadata } from '@wildo-ai/zod-decorators';\nexport { FileUploaderEntityType, FileStorageAccepted } from '@wildo-ai/zod-decorators';\nexport type { FileUploaderOwnership } from '@wildo-ai/zod-decorators';\n\n/**\n * File status lifecycle enum\n *\n * NOTE — no extraction/indexing members. `EXTRACTING` / `EXTRACTED` / `INDEXED` were removed\n * 2026-08-26 with the resource-native RAG reshape (plan `resource-rag-capability`, Phase 1):\n * text extraction and embedding state are per-FIELD, per-CHUNK facts, and the RAG chunk rows\n * (Phase 2's derived-data tier, keyed `(resourceType, resourceId, fieldName, chunkIndex)` with a\n * NULLABLE `vector` as the \"needs embedding\" ledger) carry them. A single file-level status could\n * never say WHICH field's corpus was current — it was the wrong shape, not merely unused — and\n * `INDEXED` had already shipped one false claim (written to mean \"text was copied\", read as\n * \"searchable\": https://github.com/wildo-ai/wildo-ai/issues/46). The file lifecycle now ends at\n * upload/scan/link/erasure concerns, which really are per-file.\n */\nexport enum FileStatus {\n PENDING = 'pending',\n UPLOADED = 'uploaded',\n LINKED = 'linked',\n DELETED = 'deleted',\n ORPHANED = 'orphaned',\n FAILED = 'failed',\n\n SCANNING = 'scanning',\n CLEAN = 'clean',\n INFECTED = 'infected',\n QUARANTINED = 'quarantined',\n}\n\n/**\n * Rendition of a file's bytes requested from the serve route via `?variant=`.\n *\n * - `ORIGINAL` — the stored bytes, unchanged (the default when the param is absent).\n * - `THUMBNAIL` — a small, downscaled `webp` derived on the fly for preview surfaces\n * (nav avatar, list/card image) so a preview never pulls a multi-MB original. Only\n * raster images are eligible; for any other file the serve route transparently\n * falls back to the original bytes.\n *\n * This is the WIRE CONTRACT for the serve route's `variant` query parameter — shared\n * by the frontend (which builds `?variant=thumbnail` preview URLs) and the backend\n * serve route (which parses it). Keep the string values stable across releases.\n */\nexport enum FileServeVariant {\n ORIGINAL = 'original',\n THUMBNAIL = 'thumbnail',\n}\n\n/**\n * Parse an untrusted `?variant=` query value into a {@link FileServeVariant}.\n * Absent or unrecognized values fall back to `ORIGINAL` (fail-safe: an unknown\n * variant serves the untransformed bytes rather than erroring).\n */\nexport function parseFileServeVariant(raw: unknown): FileServeVariant {\n return raw === FileServeVariant.THUMBNAIL\n ? FileServeVariant.THUMBNAIL\n : FileServeVariant.ORIGINAL;\n}\n\n/**\n * Statuses in which a file's bytes are safe to serve for download.\n *\n * AV scanning IS wired: `FileScanningBackendService` is bound in the container and the resource\n * upload handler calls `shouldScan` then `initiateScan`, so a scanned file moves through SCANNING\n * to CLEAN / INFECTED / QUARANTINED. `SCANNING`, `INFECTED` and `QUARANTINED` are absent from this\n * set and therefore blocked, which is the post-scan half of the original Wave 1 note.\n *\n * ⚠️ `UPLOADED` is still admitted, and that is the OPEN question this comment used to hide behind\n * \"scanning is not enforced yet\". Two reasons it is not simply a leftover, and neither is settled:\n * scanning is opt-in per field (`shouldScan` reads the field's `fileMetadata`), so an unscanned\n * file legitimately terminates at UPLOADED and removing it from this set would make every such\n * field undownloadable; and `initiateScan` is fired without being awaited, so even for a scanned\n * field there is a window between the upload committing and the status reaching SCANNING in which\n * the bytes are servable. Closing that window is a behaviour change with its own decision — see\n * the tracker — not something to infer from this comment.\n */\nexport const FILE_DOWNLOADABLE_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.UPLOADED,\n FileStatus.LINKED,\n FileStatus.ORPHANED,\n FileStatus.CLEAN,\n]);\n\n/**\n * Statuses in which a file's own METADATA must no longer be disclosed on the GLOBAL\n * `GET /files/:fileId/metadata` route (gate **G-FILE**).\n *\n * Blocking the BYTES is not enough. A privacy erasure marks the file `DELETED`, but physical cleanup\n * is a batch that runs much later — so between the two, `originalFilename` (`passport-jane-doe.pdf`)\n * and `uploadedBy` stayed readable by any member of the file's scope. \"Erased\" that leaves the\n * filename readable for weeks is not erased.\n *\n * DELIBERATELY NARROWER than {@link FILE_BLOCKED_STATUSES}: `PENDING`, `SCANNING`, `FAILED` and\n * `INFECTED` files are not servable either, but their metadata IS the diagnostic surface an uploader\n * legitimately polls (\"upload failed\", \"scan in progress\"). Withholding those would break upload UX\n * while protecting nothing — none of them is an erasure outcome. `DELETED` is the terminal state (it\n * has no outgoing transition in {@link FILE_STATUS_TRANSITIONS}) and the one an erasure produces.\n */\nexport const FILE_METADATA_WITHHELD_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.DELETED,\n]);\n\n/**\n * Value written over a file's `filename` / `originalFilename` when the file is erased for privacy.\n *\n * Both fields are REQUIRED (`z.string().min(1)`), so an erasure cannot null them without a schema\n * reshape; overwriting with one shared, non-identifying constant erases the personal data while\n * keeping the row valid, greppable and obviously-erased in logs and admin surfaces. Deliberately a\n * fixed constant rather than a per-file token: two erased files must not be distinguishable by name.\n */\nexport const ERASED_FILE_NAME_PLACEHOLDER = '[erased]';\n\n/**\n * Statuses that represent terminal or non-servable states.\n * Files in these states must not be served for download.\n */\nexport const FILE_BLOCKED_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.PENDING,\n FileStatus.DELETED,\n FileStatus.FAILED,\n FileStatus.SCANNING,\n FileStatus.INFECTED,\n FileStatus.QUARANTINED,\n]);\n\n/**\n * Valid lifecycle transitions.\n *\n * Each key is a source status; the value is the set of statuses it can\n * transition to. Any transition not listed here is invalid.\n */\nexport const FILE_STATUS_TRANSITIONS: Readonly<Record<FileStatus, readonly FileStatus[]>> = {\n [FileStatus.PENDING]: [FileStatus.UPLOADED, FileStatus.FAILED],\n [FileStatus.UPLOADED]: [FileStatus.LINKED, FileStatus.SCANNING, FileStatus.ORPHANED, FileStatus.DELETED, FileStatus.FAILED],\n [FileStatus.SCANNING]: [FileStatus.CLEAN, FileStatus.INFECTED, FileStatus.FAILED],\n [FileStatus.CLEAN]: [FileStatus.LINKED, FileStatus.ORPHANED, FileStatus.DELETED],\n [FileStatus.INFECTED]: [FileStatus.QUARANTINED, FileStatus.DELETED],\n [FileStatus.QUARANTINED]: [FileStatus.DELETED],\n [FileStatus.LINKED]: [FileStatus.ORPHANED, FileStatus.DELETED],\n [FileStatus.ORPHANED]: [FileStatus.LINKED, FileStatus.DELETED],\n [FileStatus.DELETED]: [],\n [FileStatus.FAILED]: [FileStatus.DELETED],\n};\n\n/**\n * Statuses from which a file may be LINKED to a resource field — the set the link-time checks\n * (`FileOperationHandlerBackendService.validateFileFieldsForCreate/Update`,\n * `FilesBackendService.linkFileToResource`) consult.\n *\n * ## Why this set exists (issue #320)\n *\n * Those checks used to compare against the literal `FileStatus.UPLOADED`, while\n * {@link FILE_STATUS_TRANSITIONS} — the authority on what a status MEANS — already allowed\n * `CLEAN → LINKED`. So a file the scanner had just certified safe was refused at the one moment it was\n * needed, with an error naming `UPLOADED` as the expected state, and whether a scanned file could be\n * attached at all depended on how fast the caller was relative to the scanner. Two `$in` queries in\n * `FilesBackendService` had meanwhile grown their own `[UPLOADED, CLEAN]` pairs — three copies of one\n * answer, one of them wrong. This is the single copy.\n *\n * ## Derived-CHECKED against the map, not computed from it\n *\n * Every member here has `LINKED` among its outgoing transitions (pinned by\n * `files.shared.status-sets.test.ts`), so this set can never admit a transition the map forbids.\n * It is nonetheless written out rather than computed, because the map is a superset of what linking\n * should admit today:\n *\n * - `ORPHANED → LINKED` is legal in the map (a re-link after the parent was deleted with the file\n * kept) and is DELIBERATELY NOT admitted here. Re-linking an orphan is a distinct capability with\n * its own questions — whose scope does the file re-enter, what happens to its old key — and\n * admitting it as a side effect of fixing the `CLEAN` refusal would decide those questions by\n * accident. The pin asserts this exclusion by name so widening it is a choice, not drift.\n * - `SCANNING` is absent because the map forbids `SCANNING → LINKED`: a file whose scan is in\n * flight is not yet known to be safe. The link path names that case with\n * `FILE_SCAN_IN_PROGRESS` so the caller learns to retry, rather than the generic refusal.\n */\nexport const FILE_LINKABLE_STATUSES: ReadonlySet<FileStatus> = new Set([\n FileStatus.UPLOADED,\n FileStatus.CLEAN,\n]);\n\n\nexport const FILE_PRIMARY_SCOPES = [\n ResourcePrimaryScope.APPLICATION,\n ResourcePrimaryScope.ORGANIZATIONS,\n ResourcePrimaryScope.USER_SELF,\n] as const;\n\nexport type FilePrimaryScope = (typeof FILE_PRIMARY_SCOPES)[number];\n\nfunction hasFileScopeIdentifierValue(value: string | undefined): boolean {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction validateUploaderPair(\n file: {\n uploadedBy?: string;\n uploadedByEntityType?: FileUploaderEntityType;\n },\n ctx: z.RefinementCtx,\n): void {\n const hasUploadedBy = hasFileScopeIdentifierValue(file.uploadedBy);\n const hasUploadedByEntityType = file.uploadedByEntityType !== undefined;\n\n if (hasUploadedBy !== hasUploadedByEntityType) {\n ctx.addIssue({\n code: 'custom',\n path: hasUploadedBy ? ['uploadedByEntityType'] : ['uploadedBy'],\n message: 'uploadedBy and uploadedByEntityType must either both be set or both be omitted',\n });\n }\n}\n\nexport function validateFileScopeIdentifiers(scope: {\n primaryScope: FilePrimaryScope;\n applicationId: string;\n organizationId?: string;\n userId?: string;\n}): boolean {\n const hasApplicationId = hasFileScopeIdentifierValue(scope.applicationId);\n const hasOrganizationId = hasFileScopeIdentifierValue(scope.organizationId);\n const hasUserId = hasFileScopeIdentifierValue(scope.userId);\n\n switch (scope.primaryScope) {\n case ResourcePrimaryScope.APPLICATION:\n return hasApplicationId && !hasOrganizationId && !hasUserId;\n case ResourcePrimaryScope.ORGANIZATIONS:\n // applicationId is always required (tenant isolation); organizationId is the scope discriminator.\n return hasApplicationId && hasOrganizationId && !hasUserId;\n case ResourcePrimaryScope.USER_SELF:\n // Primary scopes are a SEPARATION OF CONCERN: USER_SELF and ORGANIZATIONS are\n // independent axes. A user's self-owned file is user-owned, NOT org-owned —\n // `userId` is the scope discriminator and `applicationId` is the tenant/bucket\n // anchor. `organizationId` MUST be absent (a present org would conflate the two\n // scopes). Fail closed on any org value.\n return hasApplicationId && hasUserId && !hasOrganizationId;\n default:\n return false;\n }\n}\n\n/**\n * File resource schema\n *\n * Stores file metadata and links files to resources.\n * Files are stored via app-managed storage providers (MinIO/S3, local directory, etc.),\n * this schema tracks metadata only.\n *\n * ## Storage Identity Convention\n * `storageProvider` + `storageRef` are the persisted routing truth used to\n * reach the active storage backend. `storageKey` remains framework metadata\n * that captures the file's derived scope path for bookkeeping, diagnostics,\n * and future storage contract evolution.\n *\n * `storageKey` includes scope path derived from primaryScope:\n * - APPLICATION: `app/{applicationId}/{fileId}`\n * - ORGANIZATIONS: `org/{organizationId}/{fileId}`\n * - USER_SELF: `user/{userId}/{fileId}` (owner is the user; no org segment)\n */\nconst FileSchemaBase = z.object({\n fileId: z.string().min(1).isPrimaryKey().isSummaryField(),\n\n // ========== File Metadata ==========\n // USER_CONTENT on both names. A filename is chosen by the person and routinely IS the personal\n // datum — \"scan-2026-medical.pdf\" discloses a special category before the file is opened. The\n // file's BYTES are not a field here (this resource is metadata over object storage), so this is the\n // only place the content axis can be declared at all.\n filename: z.string().min(1).isDBIndexed().isSummaryField().dataCategories(PersonalDataCategory.USER_CONTENT),\n originalFilename: z.string().min(1).isSummaryField().dataCategories(PersonalDataCategory.USER_CONTENT),\n mimeType: z.string().min(1).isDBIndexed().isSummaryField(),\n size: z.number().min(0).isSummaryField(),\n extension: z.string().optional(),\n checksum: z.string().optional().isDBIndexed(),\n\n // ========== Storage ==========\n // `storageProvider` + `storageRef` are the persisted backend-routing truth.\n storageProvider: z.enum(FileStorageAccepted).optional().isDBIndexed(),\n storageRef: z.string().min(1).optional().isDBIndexed(),\n // `storageKey` is still scoped metadata, not the storage backend handle. It embeds the\n // file identity (`_id`, surfaced as the `fileId` virtual), so it is derived + persisted\n // AFTER the row is created (insert-first) — hence optional at create time. `createFile`\n // sets it once `_id` exists, and `linkFileToResource` re-derives it when the file's scope\n // is finalized.\n storageKey: z.string().min(1).optional().isDBIndexed(),\n url: z.url().optional(), // Cached URL (may be presigned)\n\n // ========== Status & Lifecycle ==========\n status: z.enum(FileStatus).default(FileStatus.PENDING).isDBIndexed().isSummaryField(),\n\n // ========== Nature-specific Metadata ==========\n // Inferred from MIME type or set by analysis services (F4-03)\n // Resolved display nature (IMAGE/VIDEO/AUDIO/DOCUMENT/GENERIC) — NOT the schema-level FileNature.\n // Set by resolveFileDisplayNature() during upload or inferred from MIME type in toMetadataResponse().\n // Schema declares FileNature (e.g., PDF, OFFICE), but the DB stores the resolved FileDisplayNature.\n nature: z.enum(FileDisplayNature).optional().isDBIndexed(),\n // Image-specific\n width: z.number().positive().optional(),\n height: z.number().positive().optional(),\n thumbnailUrl: z.url().optional(),\n // Video/Audio-specific (populated by IFileVideoAnalyzer / IFileAudioAnalyzer)\n duration: z.number().positive().optional(), // Duration in seconds\n // Document-specific (populated by IFileDocumentAnalyzer)\n pageCount: z.number().positive().optional(),\n\n // ========== Resource Linking ==========\n // When linked, scope is derived from parent resource's resourcePrimaryScope\n linkedResourceType: z.string().optional().isDBIndexed(),\n linkedResourceId: z.string().optional().isDBIndexed(),\n linkedFieldName: z.string().optional(),\n\n // ========== Primary Scope ==========\n // Derived from parent resource's resourcePrimaryScope when linking\n // Determines access control and storage path\n primaryScope: z.enum(FILE_PRIMARY_SCOPES).optional().isDBIndexed(),\n\n // ========== Scope Identifiers ==========\n // applicationId is ALWAYS set (from execution context at creation time) — it is the\n // top-level tenant isolation field present on every file record regardless of primaryScope.\n // organizationId and userId are populated only when the file's primaryScope requires them.\n\n applicationId: z.string().isDBIndexed(),\n\n // ORGANIZATIONS scope: org-scoped files. Set ONLY for ORGANIZATIONS-scoped files —\n // never for USER_SELF (the two scopes are independent axes, separation of concern).\n organizationId: z.string().optional().isDBIndexed(),\n\n // USER_SELF scope: user-private files owned by one user. Set ONLY for USER_SELF-scoped\n // files; the owner is the user (no org). `applicationId` is the tenant/bucket anchor.\n userId: z.string().optional().isDBIndexed(),\n\n // ========== Upload Metadata ==========\n // The uploader principal is persisted as a raw id + type pair only.\n // Any richer label/entity hydration is intentionally client-driven.\n uploadedBy: z.string().trim().min(1).optional().isDBIndexed(),\n uploadedByEntityType: z.enum(FileUploaderEntityType).optional().isDBIndexed(),\n\n // ========== Timestamps ==========\n uploadedAt: z.date().default(() => new Date()).isDBIndexed().isSummaryField().excludeFromCreate().excludeFromUpdate(),\n updatedAt: z.date().default(() => new Date()).isDBIndexed().excludeFromCreate().excludeFromUpdate(),\n deletedAt: z.date().optional().isDBIndexed().excludeFromCreate(),\n});\n\nexport const FileSchema = FileSchemaBase.superRefine((file, ctx) => {\n validateUploaderPair(file, ctx);\n\n if (!file.primaryScope) {\n // Pre-link state: applicationId is always present (set from execution context at upload time),\n // but organizationId/userId must not be set without a primaryScope discriminator.\n if (hasFileScopeIdentifierValue(file.organizationId) || hasFileScopeIdentifierValue(file.userId)) {\n ctx.addIssue({\n code: 'custom',\n path: ['primaryScope'],\n message: 'primaryScope is required when organizationId or userId are present',\n });\n }\n return;\n }\n\n if (!validateFileScopeIdentifiers({\n primaryScope: file.primaryScope,\n applicationId: file.applicationId,\n organizationId: file.organizationId,\n userId: file.userId,\n })) {\n ctx.addIssue({\n code: 'custom',\n path: ['primaryScope'],\n message: 'File scope identifiers must match the declared primaryScope',\n });\n }\n\n});\nexport type File = z.infer<typeof FileSchema> & FileUploaderOwnership;\n\n/**\n * Scope identifiers for file access control.\n * Populated based on primaryScope when file is created/linked.\n */\nexport interface FileScopeIdentifiers {\n primaryScope: FilePrimaryScope;\n /** Always required — top-level tenant isolation key, set from execution context at file creation. */\n applicationId: string;\n organizationId?: string;\n userId?: string;\n}\n"]}
@@ -36,5 +36,5 @@
36
36
  * @see a2a-token-budget-state.shared.resources-config.schemas.ts — the 3-scope role precedent
37
37
  */
38
38
  import { CoreResourceOperation } from '../resources/resources.shared.core.definitions';
39
- export declare const a2aPushDeliveryLogPolymorphicFactories: Record<string, import("..").ResourceConfiguration_InitializationFactory<undefined, CoreResourceOperation.READ | CoreResourceOperation.UPDATE | CoreResourceOperation.DELETE | CoreResourceOperation.DELETE_MANY | CoreResourceOperation.CREATE | CoreResourceOperation.LIST | CoreResourceOperation.SEARCH, any>>;
39
+ export declare const a2aPushDeliveryLogPolymorphicFactories: Record<string, import("..").ResourceConfiguration_InitializationFactory<undefined, CoreResourceOperation.READ | CoreResourceOperation.UPDATE | CoreResourceOperation.UPDATE_MANY | CoreResourceOperation.DELETE | CoreResourceOperation.DELETE_MANY | CoreResourceOperation.CREATE | CoreResourceOperation.LIST | CoreResourceOperation.SEARCH, any>>;
40
40
  //# sourceMappingURL=a2a-push-delivery-log.shared.resources-config.schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"a2a-push-delivery-log.shared.resources-config.schemas.d.ts","sourceRoot":"","sources":["../../../../src/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EACL,qBAAqB,EAEtB,MAAM,gDAAgD,CAAC;AAsCxD,eAAO,MAAM,sCAAsC,mTA4IlD,CAAC"}
1
+ {"version":3,"file":"a2a-push-delivery-log.shared.resources-config.schemas.d.ts","sourceRoot":"","sources":["../../../../src/flows-actors/a2a-push-delivery-log.shared.resources-config.schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EACL,qBAAqB,EAEtB,MAAM,gDAAgD,CAAC;AAuCxD,eAAO,MAAM,sCAAsC,uVA+KlD,CAAC"}