@noodleseed/one 0.162.0 → 0.163.0

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 (308) hide show
  1. package/dist/commands/billing-catalog-ops.d.ts +4 -0
  2. package/dist/commands/billing-catalog-ops.d.ts.map +1 -0
  3. package/dist/commands/billing-catalog-ops.js +58 -0
  4. package/dist/commands/billing-catalog-ops.js.map +1 -0
  5. package/dist/commands/billing-ops.d.ts.map +1 -1
  6. package/dist/commands/billing-ops.js +3 -0
  7. package/dist/commands/billing-ops.js.map +1 -1
  8. package/dist/commands/catalog.d.ts.map +1 -1
  9. package/dist/commands/catalog.js +2 -0
  10. package/dist/commands/catalog.js.map +1 -1
  11. package/dist/commands/config-values.d.ts.map +1 -1
  12. package/dist/commands/config-values.js +16 -0
  13. package/dist/commands/config-values.js.map +1 -1
  14. package/dist/commands/installation-variables.d.ts +26 -0
  15. package/dist/commands/installation-variables.d.ts.map +1 -0
  16. package/dist/commands/installation-variables.js +171 -0
  17. package/dist/commands/installation-variables.js.map +1 -0
  18. package/dist/commands/solutions-activity.d.ts +5 -0
  19. package/dist/commands/solutions-activity.d.ts.map +1 -0
  20. package/dist/commands/solutions-activity.js +116 -0
  21. package/dist/commands/solutions-activity.js.map +1 -0
  22. package/dist/commands/solutions-connections.d.ts +7 -0
  23. package/dist/commands/solutions-connections.d.ts.map +1 -0
  24. package/dist/commands/solutions-connections.js +88 -0
  25. package/dist/commands/solutions-connections.js.map +1 -0
  26. package/dist/commands/solutions-installation-options.d.ts +5 -0
  27. package/dist/commands/solutions-installation-options.d.ts.map +1 -0
  28. package/dist/commands/solutions-installation-options.js +52 -0
  29. package/dist/commands/solutions-installation-options.js.map +1 -0
  30. package/dist/commands/solutions-onboarding.d.ts +5 -0
  31. package/dist/commands/solutions-onboarding.d.ts.map +1 -0
  32. package/dist/commands/solutions-onboarding.js +113 -0
  33. package/dist/commands/solutions-onboarding.js.map +1 -0
  34. package/dist/commands/solutions-ops.d.ts.map +1 -1
  35. package/dist/commands/solutions-ops.js +278 -17
  36. package/dist/commands/solutions-ops.js.map +1 -1
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -1
  40. package/dist/index.js.map +1 -1
  41. package/node_modules/@noodle-borg/admission-limits/dist/counter-store.d.ts +21 -2
  42. package/node_modules/@noodle-borg/admission-limits/dist/counter-store.js +10 -1
  43. package/node_modules/@noodle-borg/admission-limits/dist/in-memory-counter-store.d.ts +5 -1
  44. package/node_modules/@noodle-borg/admission-limits/dist/in-memory-counter-store.js +31 -2
  45. package/node_modules/@noodle-borg/admission-limits/dist/portable.d.ts +3 -0
  46. package/node_modules/@noodle-borg/admission-limits/dist/portable.js +3 -0
  47. package/node_modules/@noodle-borg/admission-limits/dist/public-admission-assertion.d.ts +28 -0
  48. package/node_modules/@noodle-borg/admission-limits/dist/public-admission-assertion.js +124 -0
  49. package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.d.ts +54 -0
  50. package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.js +74 -0
  51. package/node_modules/@noodle-borg/admission-limits/dist/request-attribution.d.ts +11 -0
  52. package/node_modules/@noodle-borg/admission-limits/dist/request-attribution.js +19 -0
  53. package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +2 -0
  54. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +5 -5
  55. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +3 -0
  56. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +15 -3
  57. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +1 -1
  58. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  59. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.d.ts +9 -1
  60. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-configuration.js +33 -2
  61. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-execution-bound.d.ts +3 -0
  62. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-execution-bound.js +29 -0
  63. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interaction-state.d.ts +5 -3
  64. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interaction-state.js +21 -10
  65. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.d.ts +1 -1
  66. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-store.js +3 -3
  67. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +1 -0
  68. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +1 -0
  69. package/node_modules/@noodle-borg/assistant-gateway/dist/session-target.js +5 -4
  70. package/node_modules/@noodle-borg/authoring/dist/config.d.ts +20 -0
  71. package/node_modules/@noodle-borg/authoring/dist/config.js +29 -2
  72. package/node_modules/@noodle-borg/authoring/dist/connectors.d.ts +6 -1
  73. package/node_modules/@noodle-borg/authoring/dist/connectors.js +5 -0
  74. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +2 -2
  75. package/node_modules/@noodle-borg/authoring/dist/json-schema.d.ts +1 -1
  76. package/node_modules/@noodle-borg/authoring/dist/json-schema.js +15 -2
  77. package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
  78. package/node_modules/@noodle-borg/authoring/dist/managed-collection.d.ts +18 -4
  79. package/node_modules/@noodle-borg/authoring/dist/managed-collection.js +22 -3
  80. package/node_modules/@noodle-borg/authoring/dist/platform.d.ts +3 -0
  81. package/node_modules/@noodle-borg/authoring/dist/platform.js +4 -0
  82. package/node_modules/@noodle-borg/authoring/dist/server-presentation.js +43 -0
  83. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -1
  84. package/node_modules/@noodle-borg/authoring/dist/server.js +4 -44
  85. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-billing.js +18 -0
  86. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-config.js +31 -4
  87. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +778 -0
  88. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-tenant-resources.js +0 -241
  89. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-types.js +7 -0
  90. package/node_modules/@noodle-borg/cli-catalog/dist/index.js +20 -0
  91. package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +2 -1
  92. package/node_modules/@noodle-borg/compiler/dist/artifact/catalog-emission.js +3 -1
  93. package/node_modules/@noodle-borg/compiler/dist/artifact/config.js +3 -0
  94. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +17 -1
  95. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +6 -1
  96. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +6 -1
  97. package/node_modules/@noodle-borg/compiler/dist/business-variables.d.ts +27 -0
  98. package/node_modules/@noodle-borg/compiler/dist/business-variables.js +230 -0
  99. package/node_modules/@noodle-borg/compiler/dist/compile.js +7 -1
  100. package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
  101. package/node_modules/@noodle-borg/compiler/dist/index.d.ts +3 -0
  102. package/node_modules/@noodle-borg/compiler/dist/index.js +3 -0
  103. package/node_modules/@noodle-borg/compiler/dist/managed-collection-controls.d.ts +52 -0
  104. package/node_modules/@noodle-borg/compiler/dist/managed-collection-controls.js +92 -0
  105. package/node_modules/@noodle-borg/compiler/dist/managed-collections.d.ts +26 -1
  106. package/node_modules/@noodle-borg/compiler/dist/managed-collections.js +190 -1
  107. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +100 -36
  108. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +2 -0
  109. package/node_modules/@noodle-borg/compiler/dist/native-record-operations.d.ts +7 -0
  110. package/node_modules/@noodle-borg/compiler/dist/native-record-operations.js +59 -0
  111. package/node_modules/@noodle-borg/connector-defs/dist/compile-http.js +83 -8
  112. package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts +31 -1
  113. package/node_modules/@noodle-borg/connector-defs/dist/schema.js +16 -0
  114. package/node_modules/@noodle-borg/connector-http/dist/http-connector.d.ts +12 -3
  115. package/node_modules/@noodle-borg/connector-http/dist/http-connector.js +41 -2
  116. package/node_modules/@noodle-borg/connector-http/dist/http-response.js +3 -2
  117. package/node_modules/@noodle-borg/connector-http/dist/mcp/config.js +1 -1
  118. package/node_modules/@noodle-borg/connector-http/dist/url-resolution.js +1 -1
  119. package/node_modules/@noodle-borg/control-plane/dist/contracts.d.ts +2 -1
  120. package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.d.ts +3 -0
  121. package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.js +11 -0
  122. package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.d.ts +45 -0
  123. package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.js +74 -0
  124. package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +1 -0
  125. package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
  126. package/node_modules/@noodle-borg/module/dist/contract.d.ts +20 -0
  127. package/node_modules/@noodle-borg/module/dist/platform-human-identity.d.ts +2 -0
  128. package/node_modules/@noodle-borg/protocol/dist/request-deps.js +1 -0
  129. package/node_modules/@noodle-borg/protocol/dist/request-state.d.ts +1 -0
  130. package/node_modules/@noodle-borg/protocol/dist/request-state.js +11 -2
  131. package/node_modules/@noodle-borg/protocol/dist/sdk-server.d.ts +2 -0
  132. package/node_modules/@noodle-borg/protocol/dist/v2/input-required.js +2 -1
  133. package/node_modules/@noodle-borg/protocol/dist/v2/server.js +2 -1
  134. package/node_modules/@noodle-borg/runtime/dist/ambient-context.js +6 -1
  135. package/node_modules/@noodle-borg/runtime/dist/broker/types.d.ts +2 -0
  136. package/node_modules/@noodle-borg/runtime/dist/business-variables.d.ts +15 -0
  137. package/node_modules/@noodle-borg/runtime/dist/business-variables.js +75 -0
  138. package/node_modules/@noodle-borg/runtime/dist/confirmation-continuation.js +2 -1
  139. package/node_modules/@noodle-borg/runtime/dist/confirmation.js +23 -3
  140. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +17 -1
  141. package/node_modules/@noodle-borg/runtime/dist/connector-snapshot.js +6 -0
  142. package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +18 -3
  143. package/node_modules/@noodle-borg/runtime/dist/execute.js +13 -3
  144. package/node_modules/@noodle-borg/runtime/dist/index.d.ts +2 -0
  145. package/node_modules/@noodle-borg/runtime/dist/index.js +1 -0
  146. package/node_modules/@noodle-borg/runtime/dist/interactive.js +11 -0
  147. package/node_modules/@noodle-borg/runtime/dist/managed-origins.d.ts +3 -1
  148. package/node_modules/@noodle-borg/runtime/dist/managed-origins.js +32 -7
  149. package/node_modules/@noodle-borg/runtime/dist/operation-credential.js +6 -0
  150. package/node_modules/@noodle-borg/runtime/dist/operation-evidence.d.ts +30 -0
  151. package/node_modules/@noodle-borg/runtime/dist/operation-evidence.js +68 -0
  152. package/node_modules/@noodle-borg/runtime/dist/operation-execution.d.ts +2 -2
  153. package/node_modules/@noodle-borg/runtime/dist/operation-execution.js +142 -71
  154. package/node_modules/@noodle-borg/runtime/dist/result.d.ts +6 -4
  155. package/node_modules/@noodle-borg/runtime/dist/tool-dispatch.d.ts +7 -0
  156. package/node_modules/@noodle-borg/runtime/dist/tool-dispatch.js +48 -4
  157. package/node_modules/@noodle-borg/service/dist/admission.js +2 -1
  158. package/node_modules/@noodle-borg/service/dist/application-activity.js +236 -0
  159. package/node_modules/@noodle-borg/service/dist/application-business-notice.js +28 -0
  160. package/node_modules/@noodle-borg/service/dist/application-runtime-target.js +141 -0
  161. package/node_modules/@noodle-borg/service/dist/application-settings-targets.js +72 -0
  162. package/node_modules/@noodle-borg/service/dist/application-settings.js +210 -0
  163. package/node_modules/@noodle-borg/service/dist/business-api-admission.js +121 -0
  164. package/node_modules/@noodle-borg/service/dist/business-information/activity-pagination.js +73 -0
  165. package/node_modules/@noodle-borg/service/dist/business-information/business-notice.d.ts +24 -0
  166. package/node_modules/@noodle-borg/service/dist/business-information/business-notice.js +18 -0
  167. package/node_modules/@noodle-borg/service/dist/business-information/collection-controls.js +51 -0
  168. package/node_modules/@noodle-borg/service/dist/business-information/contracts.d.ts +439 -0
  169. package/node_modules/@noodle-borg/service/dist/business-information/definition-resolver.js +94 -0
  170. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-installation-lifecycle.js +136 -0
  171. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-invitations.js +160 -0
  172. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-locks.js +23 -0
  173. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-store.js +287 -132
  174. package/node_modules/@noodle-borg/service/dist/business-information/installation-capacity.js +10 -0
  175. package/node_modules/@noodle-borg/service/dist/business-information/invitations.js +68 -0
  176. package/node_modules/@noodle-borg/service/dist/business-information/legacy-request-migration.js +60 -0
  177. package/node_modules/@noodle-borg/service/dist/business-information/managed-releases.js +245 -0
  178. package/node_modules/@noodle-borg/service/dist/business-information/managed-solution-blueprints.js +51 -0
  179. package/node_modules/@noodle-borg/service/dist/business-information/managed-solution-executable.js +76 -0
  180. package/node_modules/@noodle-borg/service/dist/business-information/model.js +124 -23
  181. package/node_modules/@noodle-borg/service/dist/business-information/native-query.js +162 -0
  182. package/node_modules/@noodle-borg/service/dist/business-information/native-storage-budget.js +60 -0
  183. package/node_modules/@noodle-borg/service/dist/business-information/pagination.js +28 -0
  184. package/node_modules/@noodle-borg/service/dist/business-information/portable.js +9 -0
  185. package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.d.ts +11 -0
  186. package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.js +42 -0
  187. package/node_modules/@noodle-borg/service/dist/business-information/profiles.js +137 -6
  188. package/node_modules/@noodle-borg/service/dist/business-information/reader-floor.js +26 -0
  189. package/node_modules/@noodle-borg/service/dist/business-information/source-credential-fence.js +79 -0
  190. package/node_modules/@noodle-borg/service/dist/business-information/source-custody-budget.js +30 -0
  191. package/node_modules/@noodle-borg/service/dist/business-information/source-custody-memory.js +122 -0
  192. package/node_modules/@noodle-borg/service/dist/business-information/source-executor.js +191 -0
  193. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-contracts.js +2 -0
  194. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-coordinator.js +136 -0
  195. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-failures.js +32 -0
  196. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-memory-state.js +154 -0
  197. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-memory-store.js +568 -0
  198. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-memory-types.js +26 -0
  199. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-memory-values.js +59 -0
  200. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-pagination.js +54 -0
  201. package/node_modules/@noodle-borg/service/dist/business-information/source-ingestion-validation.js +163 -0
  202. package/node_modules/@noodle-borg/service/dist/business-information/source-refresh-retention.js +29 -0
  203. package/node_modules/@noodle-borg/service/dist/business-information-runtime.js +80 -0
  204. package/node_modules/@noodle-borg/service/dist/business-onboarding.js +73 -0
  205. package/node_modules/@noodle-borg/service/dist/connections/types.js +8 -0
  206. package/node_modules/@noodle-borg/service/dist/credential-binding-index.js +38 -33
  207. package/node_modules/@noodle-borg/service/dist/deployment-execution.js +275 -0
  208. package/node_modules/@noodle-borg/service/dist/external-credential-binding-broker.js +19 -1
  209. package/node_modules/@noodle-borg/service/dist/external-credential-exchange.d.ts +16 -1
  210. package/node_modules/@noodle-borg/service/dist/modules/host.js +4 -1
  211. package/node_modules/@noodle-borg/service/dist/native-record-connector.d.ts +20 -0
  212. package/node_modules/@noodle-borg/service/dist/native-record-connector.js +206 -0
  213. package/node_modules/@noodle-borg/service/dist/oauth/store.d.ts +3 -0
  214. package/node_modules/@noodle-borg/service/dist/operation-evidence.js +89 -0
  215. package/node_modules/@noodle-borg/service/dist/recovery-quarantine.js +49 -0
  216. package/node_modules/@noodle-borg/service/dist/registry-compile.js +60 -11
  217. package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +18 -0
  218. package/node_modules/@noodle-borg/service/dist/registry-rollback.js +2 -1
  219. package/node_modules/@noodle-borg/service/dist/registry-state.d.ts +1 -1
  220. package/node_modules/@noodle-borg/service/dist/registry-state.js +6 -3
  221. package/node_modules/@noodle-borg/service/dist/registry-targets.js +49 -0
  222. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +2 -0
  223. package/node_modules/@noodle-borg/service/dist/registry.d.ts +15 -3
  224. package/node_modules/@noodle-borg/service/dist/registry.js +177 -137
  225. package/node_modules/@noodle-borg/service/dist/routes/assistant-appearance.js +3 -2
  226. package/node_modules/@noodle-borg/service/dist/routes/assistant-clients.js +2 -1
  227. package/node_modules/@noodle-borg/service/dist/routes/assistant-doctor.js +2 -1
  228. package/node_modules/@noodle-borg/service/dist/routes/assistant-embeds.js +8 -3
  229. package/node_modules/@noodle-borg/service/dist/routes/assistant-interaction-replay.js +1 -1
  230. package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +48 -36
  231. package/node_modules/@noodle-borg/service/dist/routes/assistant-public-configuration.js +3 -2
  232. package/node_modules/@noodle-borg/service/dist/routes/assistant-public-session.js +10 -10
  233. package/node_modules/@noodle-borg/service/dist/routes/assistant-session-target.js +32 -3
  234. package/node_modules/@noodle-borg/service/dist/routes/assistant-suggestions.js +1 -1
  235. package/node_modules/@noodle-borg/service/dist/routes/assistant-transcript.js +1 -1
  236. package/node_modules/@noodle-borg/service/dist/routes/assistant.js +10 -10
  237. package/node_modules/@noodle-borg/service/dist/routes/business-information-activity.js +83 -0
  238. package/node_modules/@noodle-borg/service/dist/routes/business-information-admission.js +26 -0
  239. package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +153 -0
  240. package/node_modules/@noodle-borg/service/dist/routes/business-information-connections.js +107 -0
  241. package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +87 -4
  242. package/node_modules/@noodle-borg/service/dist/routes/business-information-external.js +79 -0
  243. package/node_modules/@noodle-borg/service/dist/routes/business-information-installation.js +24 -0
  244. package/node_modules/@noodle-borg/service/dist/routes/business-information-notice.js +69 -0
  245. package/node_modules/@noodle-borg/service/dist/routes/business-information-onboarding.js +58 -0
  246. package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +76 -0
  247. package/node_modules/@noodle-borg/service/dist/routes/business-information-public.js +151 -0
  248. package/node_modules/@noodle-borg/service/dist/routes/business-information-reader-floor.js +15 -0
  249. package/node_modules/@noodle-borg/service/dist/routes/business-information-record-create.js +29 -0
  250. package/node_modules/@noodle-borg/service/dist/routes/business-information-record-reads.js +36 -0
  251. package/node_modules/@noodle-borg/service/dist/routes/business-information-request.js +64 -0
  252. package/node_modules/@noodle-borg/service/dist/routes/business-information-settings.js +64 -0
  253. package/node_modules/@noodle-borg/service/dist/routes/business-information-source.js +249 -0
  254. package/node_modules/@noodle-borg/service/dist/routes/business-information-staff.js +258 -0
  255. package/node_modules/@noodle-borg/service/dist/routes/business-information-wire.js +226 -26
  256. package/node_modules/@noodle-borg/service/dist/routes/business-information.js +238 -197
  257. package/node_modules/@noodle-borg/service/dist/routes/config-values-dispatch.js +1 -1
  258. package/node_modules/@noodle-borg/service/dist/routes/config-values.js +26 -4
  259. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +19 -233
  260. package/node_modules/@noodle-borg/service/dist/routes/deployment-activation-response.js +13 -4
  261. package/node_modules/@noodle-borg/service/dist/service-resource-cleanup.js +4 -0
  262. package/node_modules/@noodle-borg/service/dist/service.js +57 -11
  263. package/node_modules/@noodle-borg/service/dist/solution-installation-activation.js +149 -0
  264. package/node_modules/@noodle-borg/service/dist/source-configuration-authority.js +94 -0
  265. package/node_modules/@noodle-borg/service/dist/state-catalog.js +6 -5
  266. package/node_modules/@noodle-borg/service/dist/store/config-values.d.ts +42 -17
  267. package/node_modules/@noodle-borg/service/dist/store/config-values.js +123 -24
  268. package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +1 -0
  269. package/node_modules/@noodle-borg/service/dist/store/in-memory.js +3 -0
  270. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +1 -0
  271. package/node_modules/@noodle-borg/service/dist/store/json-file.js +3 -0
  272. package/node_modules/@noodle-borg/service/dist/store.d.ts +3 -1
  273. package/node_modules/@noodle-borg/service/package.json +2 -2
  274. package/node_modules/@noodle-borg/transport-http/dist/serve-request.js +9 -0
  275. package/node_modules/@noodle-borg/transport-http/package.json +1 -0
  276. package/node_modules/@noodle-borg/wire-contracts/dist/application-activity.d.ts +143 -0
  277. package/node_modules/@noodle-borg/wire-contracts/dist/application-activity.js +90 -0
  278. package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.d.ts +130 -0
  279. package/node_modules/@noodle-borg/wire-contracts/dist/application-channels.js +77 -0
  280. package/node_modules/@noodle-borg/wire-contracts/dist/application-connections.d.ts +105 -0
  281. package/node_modules/@noodle-borg/wire-contracts/dist/application-connections.js +67 -0
  282. package/node_modules/@noodle-borg/wire-contracts/dist/application-onboarding.d.ts +27 -0
  283. package/node_modules/@noodle-borg/wire-contracts/dist/application-onboarding.js +28 -0
  284. package/node_modules/@noodle-borg/wire-contracts/dist/application-settings.d.ts +112 -0
  285. package/node_modules/@noodle-borg/wire-contracts/dist/application-settings.js +88 -0
  286. package/node_modules/@noodle-borg/wire-contracts/dist/assistant.d.ts +1 -1
  287. package/node_modules/@noodle-borg/wire-contracts/dist/billing-catalog.d.ts +62 -0
  288. package/node_modules/@noodle-borg/wire-contracts/dist/billing-catalog.js +57 -0
  289. package/node_modules/@noodle-borg/wire-contracts/dist/billing-enforcement.d.ts +12 -12
  290. package/node_modules/@noodle-borg/wire-contracts/dist/billing-metering.d.ts +4 -4
  291. package/node_modules/@noodle-borg/wire-contracts/dist/billing-org-transfer.d.ts +3 -3
  292. package/node_modules/@noodle-borg/wire-contracts/dist/business-information-query.d.ts +25 -0
  293. package/node_modules/@noodle-borg/wire-contracts/dist/business-information-query.js +37 -0
  294. package/node_modules/@noodle-borg/wire-contracts/dist/business-information.d.ts +1816 -158
  295. package/node_modules/@noodle-borg/wire-contracts/dist/business-information.js +416 -41
  296. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +2 -2
  297. package/node_modules/@noodle-borg/wire-contracts/dist/distribution.d.ts +5 -5
  298. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +8 -0
  299. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +8 -0
  300. package/node_modules/@noodle-borg/wire-contracts/dist/knowledge.d.ts +3 -3
  301. package/node_modules/@noodle-borg/wire-contracts/dist/managed-assistant-sponsorship.d.ts +7 -7
  302. package/node_modules/@noodle-borg/wire-contracts/dist/platform-account-reset.d.ts +5 -0
  303. package/node_modules/@noodle-borg/wire-contracts/dist/platform-account-reset.js +1 -0
  304. package/node_modules/@noodle-borg/wire-contracts/dist/platform-auth.d.ts +66 -66
  305. package/node_modules/@noodle-borg/wire-contracts/dist/solution-onboarding.d.ts +143 -0
  306. package/node_modules/@noodle-borg/wire-contracts/dist/solution-onboarding.js +94 -0
  307. package/node_modules/@noodleseed/assistant/README.md +5 -0
  308. package/package.json +2 -2
@@ -1,6 +1,16 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { activityFromRecord, applyRequestOperation, cloneActivity, cloneGrant, cloneInstallation, cloneRecord, deletedRecord, idempotencyDigest, initialRecord, installationFingerprint, normalizeInstallationInput, permissionsForBusinessRole, requestFingerprint, validateCollectionEnabled, validateExpectedRevision, } from './model.js';
3
- import { afterCursor, compareRecords, decodeCursor, encodeCursor, recordKey, scopeKey, } from './pagination.js';
2
+ import { memoryActivityPage } from './activity-pagination.js';
3
+ import { collectionControlEnabled } from './collection-controls.js';
4
+ import { InMemoryInstallationLifecycle, } from './in-memory-installation-lifecycle.js';
5
+ import { InMemoryBusinessInvitations } from './in-memory-invitations.js';
6
+ import { BusinessMemoryLocks } from './in-memory-locks.js';
7
+ import { InstallationCapacityError, MAX_RETAINED_INSTALLATIONS_PER_ORG, } from './installation-capacity.js';
8
+ import { migrateLegacyRequestRecord } from './legacy-request-migration.js';
9
+ import { activityFromRecord, applyRequestOperation, cloneGrant, cloneRecord, deletedRecord, effectiveInstallation, idempotencyDigest, initialRecord, installationFingerprint, managedInstallationIntentMatches, normalizeInstallationInput, permissionsForBusinessRole, requestFingerprint, validateCollectionEnabled, validateExpectedRevision, validateStoredRecord, } from './model.js';
10
+ import { matchesNativeQueryMetadata, planNativeQuery, runNativeQuery } from './native-query.js';
11
+ import { commitNativeMemoryRecord, NativeStorageBudget } from './native-storage-budget.js';
12
+ import { afterCursor, compareRecords, decodeCursor, grantKey, installationIdKey, page, recordKey, scopeKey, } from './pagination.js';
13
+ import { BusinessPrincipalAuthority, } from './principal-authority.js';
4
14
  import { boundedExportPageSize, boundedPageSize, validateEmail, validateScalar, validateScope, } from './validation.js';
5
15
  /** Process-local development/test adapter. Hosted services must use the PostgreSQL adapter. */
6
16
  export class InMemoryBusinessInformationStore {
@@ -9,33 +19,69 @@ export class InMemoryBusinessInformationStore {
9
19
  #publicIds = new Map();
10
20
  #installationFingerprints = new Map();
11
21
  #grants = new Map();
22
+ #invitations;
23
+ #lifecycle;
24
+ #principals = new BusinessPrincipalAuthority();
25
+ #custody = new NativeStorageBudget();
12
26
  #records = new Map();
13
27
  #activities = new Map();
14
28
  #idempotency = new Map();
15
- #locks = new Map();
29
+ #locks = new BusinessMemoryLocks();
16
30
  #now;
17
31
  #id;
18
32
  #publicId;
33
+ #managedDefinition;
19
34
  constructor(options = {}) {
20
35
  this.#now = options.now ?? (() => new Date());
21
36
  this.#id = options.id ?? randomUUID;
22
37
  this.#publicId = options.publicId ?? (() => `sol_${randomUUID().replaceAll('-', '')}`);
38
+ this.#managedDefinition = options.managedDefinition;
39
+ this.#lifecycle = new InMemoryInstallationLifecycle({
40
+ installations: this.#installations,
41
+ getGrant: (scope, subject) => this.#grants.get(grantKey(scope, subject)),
42
+ withLock: (key, operation) => this.#locks.run(key, operation),
43
+ now: this.#now,
44
+ managedDefinition: this.#managedDefinition,
45
+ });
46
+ this.#invitations = new InMemoryBusinessInvitations({
47
+ now: this.#now,
48
+ getInstallation: (scope) => this.getInstallation(scope),
49
+ getGrant: (scope, subject) => this.getGrant(scope, subject),
50
+ putGrant: (grant) => this.#grants.set(grantKey(grant.scope, grant.subject), grant),
51
+ withGrantLock: (scope, operation) => this.#locks.run(`grants:${scopeKey(scope)}`, operation),
52
+ liveAdministratorCount: (scope) => this.#liveAdministratorCount(scope),
53
+ });
54
+ }
55
+ configurePrincipalAuthority(provider) {
56
+ this.#principals.configure(provider);
57
+ }
58
+ async listEligibleAssignees(scope) {
59
+ return this.#principals.eligible(await this.listGrants(scope));
60
+ }
61
+ getBusinessNotice(scope) {
62
+ return this.#lifecycle.getBusinessNotice(scope);
63
+ }
64
+ setBusinessNotice(input) {
65
+ return this.#lifecycle.setBusinessNotice(input);
23
66
  }
24
67
  async createInstallation(input) {
25
68
  const normalized = normalizeInstallationInput(input);
26
69
  const key = scopeKey(normalized.scope);
27
- return this.#withLock(`installation:${normalized.scope.org}:${normalized.scope.installationId}`, async () => {
70
+ return this.#locks.run(`installations:${normalized.scope.org}`, async () => {
28
71
  const existingKey = this.#installationIds.get(installationIdKey(normalized.scope.org, normalized.scope.installationId));
29
72
  const existing = existingKey === undefined ? undefined : this.#installations.get(existingKey);
30
73
  if (existing !== undefined) {
31
74
  return {
32
75
  disposition: existingKey === key &&
33
- this.#installationFingerprints.get(key) === installationFingerprint(normalized)
76
+ (this.#installationFingerprints.get(key) === installationFingerprint(normalized) ||
77
+ managedInstallationIntentMatches(existing, normalized))
34
78
  ? 'replayed'
35
79
  : 'conflict',
36
- installation: cloneInstallation(existing),
80
+ installation: effectiveInstallation(existing, this.#managedDefinition),
37
81
  };
38
82
  }
83
+ if ([...this.#installations.values()].filter((entry) => entry.scope.org === normalized.scope.org).length >= MAX_RETAINED_INSTALLATIONS_PER_ORG)
84
+ throw new InstallationCapacityError();
39
85
  const now = this.#now().toISOString();
40
86
  const installation = {
41
87
  scope: { ...normalized.scope },
@@ -43,7 +89,10 @@ export class InMemoryBusinessInformationStore {
43
89
  profileKey: normalized.profileKey,
44
90
  profileVersion: normalized.profileVersion,
45
91
  managedCollections: [...normalized.managedCollections],
92
+ definition: structuredClone(normalized.definition),
46
93
  retentionDays: normalized.retentionDays,
94
+ intakeActive: true,
95
+ applicationGeneration: 'pending',
47
96
  revision: 1,
48
97
  createdAt: now,
49
98
  createdBySubject: normalized.actorSubject,
@@ -66,29 +115,54 @@ export class InMemoryBusinessInformationStore {
66
115
  updatedBySubject: normalized.actorSubject,
67
116
  };
68
117
  this.#grants.set(grantKey(normalized.scope, normalized.actorSubject), administrator);
69
- return { disposition: 'created', installation: cloneInstallation(installation) };
118
+ return {
119
+ disposition: 'created',
120
+ installation: effectiveInstallation(installation, this.#managedDefinition),
121
+ };
70
122
  });
71
123
  }
72
124
  getInstallation(scope) {
73
- const found = this.#installations.get(scopeKey(validateScope(scope)));
74
- return Promise.resolve(found === undefined ? undefined : cloneInstallation(found));
125
+ return this.#lifecycle.getInstallation(scope);
75
126
  }
76
127
  getInstallationById(org, installationId) {
77
128
  const key = this.#installationIds.get(installationIdKey(validateScalar('organization', org, 63), validateScalar('installation', installationId, 63)));
78
129
  const found = key === undefined ? undefined : this.#installations.get(key);
79
- return Promise.resolve(found === undefined ? undefined : cloneInstallation(found));
130
+ return Promise.resolve(found === undefined ? undefined : effectiveInstallation(found, this.#managedDefinition));
80
131
  }
81
132
  resolveInstallationByPublicId(publicId) {
82
133
  const key = this.#publicIds.get(validateScalar('public installation id', publicId, 128));
83
134
  const found = key === undefined ? undefined : this.#installations.get(key);
84
- return Promise.resolve(found === undefined ? undefined : cloneInstallation(found));
135
+ return Promise.resolve(found === undefined ? undefined : effectiveInstallation(found, this.#managedDefinition));
85
136
  }
86
137
  listInstallations(org) {
87
- const normalized = validateScalar('organization', org, 63);
88
- return Promise.resolve([...this.#installations.values()]
89
- .filter((installation) => installation.scope.org === normalized)
90
- .sort((left, right) => left.createdAt.localeCompare(right.createdAt))
91
- .map(cloneInstallation));
138
+ return this.#lifecycle.listInstallations(org);
139
+ }
140
+ listInstallationsForSubject(subject) {
141
+ const normalized = validateScalar('identity subject', subject, 256);
142
+ return Promise.resolve([...this.#grants.values()]
143
+ .filter((grant) => grant.subject === normalized && grant.revokedAt === undefined)
144
+ .map((grant) => {
145
+ const installation = this.#installations.get(scopeKey(grant.scope));
146
+ if (installation === undefined)
147
+ throw new Error('business grant installation is missing');
148
+ return {
149
+ installation: effectiveInstallation(installation, this.#managedDefinition),
150
+ grant: cloneGrant(grant),
151
+ };
152
+ })
153
+ .sort((left, right) => left.installation.createdAt.localeCompare(right.installation.createdAt)));
154
+ }
155
+ configureApplicationLifecycle(resolve) {
156
+ this.#lifecycle.configureApplicationLifecycle(resolve);
157
+ }
158
+ bindApplication(scope, generation) {
159
+ return this.#lifecycle.bindApplication(scope, generation);
160
+ }
161
+ pauseApplication(org, app, at, retired = false) {
162
+ return this.#lifecycle.pauseApplication(org, app, at, retired);
163
+ }
164
+ setIntakeState(input) {
165
+ return this.#lifecycle.setIntakeState(input);
92
166
  }
93
167
  getGrant(scope, subject) {
94
168
  const found = this.#grants.get(grantKey(validateScope(scope), subject));
@@ -108,7 +182,7 @@ export class InMemoryBusinessInformationStore {
108
182
  permissionsForBusinessRole(input.role);
109
183
  const scope = validateScope(input.scope);
110
184
  const key = grantKey(scope, subject);
111
- return this.#withLock(`grants:${scopeKey(scope)}`, async () => {
185
+ return this.#locks.run(`grants:${scopeKey(scope)}`, async () => {
112
186
  if (!(await this.getInstallation(input.scope)))
113
187
  return { ok: false, reason: 'not_found', currentRevision: 0 };
114
188
  const current = this.#grants.get(key);
@@ -142,7 +216,7 @@ export class InMemoryBusinessInformationStore {
142
216
  validateExpectedRevision(input.expectedRevision);
143
217
  const scope = validateScope(input.scope);
144
218
  const key = grantKey(scope, input.subject);
145
- return this.#withLock(`grants:${scopeKey(scope)}`, async () => {
219
+ return this.#locks.run(`grants:${scopeKey(scope)}`, async () => {
146
220
  const current = this.#grants.get(key);
147
221
  if (current === undefined)
148
222
  return { ok: false, reason: 'not_found', currentRevision: 0 };
@@ -170,44 +244,96 @@ export class InMemoryBusinessInformationStore {
170
244
  return { ok: true, grant: cloneGrant(grant) };
171
245
  });
172
246
  }
247
+ async createInvitation(input) {
248
+ return this.#invitations.create(input);
249
+ }
250
+ listInvitations(scope) {
251
+ return this.#invitations.list(scope);
252
+ }
253
+ async revokeInvitation(input) {
254
+ return this.#invitations.revoke(input);
255
+ }
256
+ async claimInvitation(input) {
257
+ return this.#invitations.claim(input);
258
+ }
173
259
  async createRequest(input) {
174
260
  const idempotencyKey = idempotencyDigest(input.idempotencyKey);
175
- const installation = await this.#requiredInstallation(input.scope);
176
- validateCollectionEnabled(installation, input.collectionKey);
177
261
  const key = `${scopeKey(input.scope)}\0${input.collectionKey}\0${idempotencyKey}`;
178
- return this.#withLock(`request-create:${key}`, async () => {
179
- const candidate = initialRecord({
180
- installation,
181
- collectionKey: input.collectionKey,
182
- id: this.#id(),
183
- payload: input.payload,
184
- origin: input.origin,
185
- actorSubject: input.actorSubject,
186
- now: this.#now(),
187
- });
188
- const fingerprint = requestFingerprint({
189
- collectionKey: candidate.collectionKey,
190
- payload: candidate.content?.payload,
191
- origin: candidate.origin,
192
- actorSubject: candidate.createdBySubject,
262
+ return this.#locks.run(`request-create:${key}`, async () => {
263
+ return this.#locks.run(`intake:${scopeKey(input.scope)}`, async () => {
264
+ const installation = await this.#requiredInstallation(input.scope);
265
+ validateCollectionEnabled(installation, input.collectionKey);
266
+ const candidate = initialRecord({
267
+ installation,
268
+ collectionKey: input.collectionKey,
269
+ id: this.#id(),
270
+ payload: input.payload,
271
+ ...(input.publicInput === undefined ? {} : { publicInput: input.publicInput }),
272
+ origin: input.origin,
273
+ actorSubject: input.actorSubject,
274
+ now: this.#now(),
275
+ });
276
+ const fingerprint = requestFingerprint({
277
+ collectionKey: candidate.collectionKey,
278
+ payload: input.payload,
279
+ origin: candidate.origin,
280
+ actorSubject: candidate.createdBySubject,
281
+ });
282
+ const replay = this.#idempotency.get(key);
283
+ if (replay !== undefined) {
284
+ const existing = this.#expireRecordIfNeeded(replay.recordKey, this.#now());
285
+ if (existing === undefined)
286
+ throw new Error('idempotency record points to a missing request');
287
+ validateStoredRecord(installation, existing);
288
+ return {
289
+ disposition: replay.fingerprint === fingerprint ? 'replayed' : 'conflict',
290
+ record: cloneRecord(existing),
291
+ };
292
+ }
293
+ if (!(await this.#lifecycle.applicationAllows(installation)) ||
294
+ ((input.origin.kind === 'embedded' || input.publicInput === true) &&
295
+ !installation.intakeActive)) {
296
+ return { disposition: 'paused' };
297
+ }
298
+ const currentKey = recordKey(input.scope, candidate.collectionKey, candidate.id);
299
+ this.#commitRecord(currentKey, candidate, 'created');
300
+ this.#idempotency.set(key, { fingerprint, recordKey: currentKey });
301
+ return { disposition: 'created', record: cloneRecord(candidate) };
193
302
  });
194
- const replay = this.#idempotency.get(key);
195
- if (replay !== undefined) {
196
- const existing = this.#records.get(replay.recordKey);
197
- if (existing === undefined)
198
- throw new Error('idempotency record points to a missing request');
199
- return {
200
- disposition: replay.fingerprint === fingerprint ? 'replayed' : 'conflict',
201
- record: cloneRecord(existing),
202
- };
203
- }
204
- const currentKey = recordKey(input.scope, candidate.collectionKey, candidate.id);
205
- this.#records.set(currentKey, candidate);
206
- this.#activities.set(currentKey, [activityFromRecord(candidate, 'created')]);
207
- this.#idempotency.set(key, { fingerprint, recordKey: currentKey });
208
- return { disposition: 'created', record: cloneRecord(candidate) };
209
303
  });
210
304
  }
305
+ async probeRequest(input) {
306
+ const installation = await this.#requiredInstallation(input.scope);
307
+ const candidate = initialRecord({
308
+ installation,
309
+ collectionKey: input.collectionKey,
310
+ id: this.#id(),
311
+ payload: input.payload,
312
+ ...(input.publicInput === undefined ? {} : { publicInput: input.publicInput }),
313
+ origin: input.origin,
314
+ actorSubject: input.actorSubject,
315
+ now: this.#now(),
316
+ });
317
+ const digest = idempotencyDigest(input.idempotencyKey);
318
+ const key = `${scopeKey(input.scope)}\0${input.collectionKey}\0${digest}`;
319
+ const replay = this.#idempotency.get(key);
320
+ if (replay === undefined)
321
+ return { disposition: 'missing' };
322
+ const existing = this.#expireRecordIfNeeded(replay.recordKey, this.#now());
323
+ if (existing === undefined)
324
+ throw new Error('idempotency record points to a missing request');
325
+ validateStoredRecord(installation, existing);
326
+ const fingerprint = requestFingerprint({
327
+ collectionKey: candidate.collectionKey,
328
+ payload: input.payload,
329
+ origin: candidate.origin,
330
+ actorSubject: candidate.createdBySubject,
331
+ });
332
+ return {
333
+ disposition: replay.fingerprint === fingerprint ? 'replayed' : 'conflict',
334
+ record: cloneRecord(existing),
335
+ };
336
+ }
211
337
  #liveAdministratorCount(scope) {
212
338
  const prefix = `${scopeKey(scope)}\0`;
213
339
  return [...this.#grants.entries()].filter(([key, grant]) => key.startsWith(prefix) && grant.role === 'administrator' && grant.revokedAt === undefined).length;
@@ -215,61 +341,124 @@ export class InMemoryBusinessInformationStore {
215
341
  async getRequest(scope, collectionKey, id, options = {}) {
216
342
  const installation = await this.#requiredInstallation(scope);
217
343
  const collection = validateCollectionEnabled(installation, collectionKey);
218
- const record = this.#records.get(recordKey(scope, collection.key, validateScalar('record id', id, 128)));
344
+ const key = recordKey(scope, collection.key, validateScalar('record id', id, 128));
345
+ const record = this.#expireRecordIfNeeded(key, this.#now());
219
346
  if (record === undefined || (record.deletedAt !== undefined && options.includeDeleted !== true))
220
347
  return undefined;
348
+ validateStoredRecord(installation, record);
221
349
  return cloneRecord(record);
222
350
  }
351
+ async listAcceptedSchemaInventory() {
352
+ const inventory = new Map();
353
+ for (const record of this.#records.values()) {
354
+ const identity = {
355
+ profileKey: record.profileKey,
356
+ profileVersion: record.profileVersion,
357
+ collectionKey: record.collectionKey,
358
+ schemaVersion: record.schemaVersion,
359
+ schemaDigest: record.schemaDigest,
360
+ };
361
+ inventory.set(JSON.stringify(identity), identity);
362
+ if (record.originalSchema !== undefined) {
363
+ const original = { ...identity, ...record.originalSchema };
364
+ inventory.set(JSON.stringify(original), original);
365
+ }
366
+ }
367
+ return [...inventory.entries()]
368
+ .sort(([left], [right]) => left.localeCompare(right))
369
+ .map(([, identity]) => ({ ...identity }));
370
+ }
223
371
  async listRequests(input) {
224
372
  const installation = await this.#requiredInstallation(input.scope);
225
373
  const collection = validateCollectionEnabled(installation, input.collectionKey);
226
- const limit = boundedPageSize(input.limit);
227
- const cursor = input.cursor === undefined
228
- ? undefined
229
- : decodeCursor(input.cursor, {
230
- kind: 'list',
231
- scope: input.scope,
232
- collectionKey: collection.key,
233
- });
234
- const records = this.#matchingRecords(input.scope, collection.key)
235
- .filter((record) => input.includeDeleted === true || record.deletedAt === undefined)
236
- .filter((record) => input.status === undefined || record.status === input.status)
237
- .filter((record) => input.assigneeSubject === undefined || record.assigneeSubject === input.assigneeSubject)
238
- .filter((record) => cursor === undefined || afterCursor(record, cursor));
239
- return page(records, limit, 'list', input.scope, collection.key);
374
+ const now = this.#now();
375
+ const plan = await planNativeQuery(input, collection, now, (id) => this.getRequest(input.scope, collection.key, id, { includeDeleted: true }));
376
+ const records = this.#matchingRecords(input.scope, collection.key).filter((record) => matchesNativeQueryMetadata(record, plan, now));
377
+ const result = await runNativeQuery(plan, records);
378
+ for (const record of result.records)
379
+ validateStoredRecord(installation, record);
380
+ return result;
240
381
  }
241
382
  async mutateRequest(input) {
242
383
  validateExpectedRevision(input.expectedRevision);
243
384
  const installation = await this.#requiredInstallation(input.scope);
244
385
  const collection = validateCollectionEnabled(installation, input.collectionKey);
245
386
  const key = recordKey(input.scope, collection.key, validateScalar('record id', input.id, 128));
246
- return this.#withLock(`request:${key}`, async () => {
247
- const current = this.#records.get(key);
387
+ const mutate = () => this.#locks.run(`request:${key}`, async () => {
388
+ const current = this.#expireRecordIfNeeded(key, this.#now());
248
389
  if (current === undefined || current.deletedAt !== undefined) {
249
390
  return { ok: false, reason: 'not_found', currentRevision: 0 };
250
391
  }
251
392
  if (current.revision !== input.expectedRevision) {
252
393
  return { ok: false, reason: 'conflict', currentRevision: current.revision };
253
394
  }
254
- const applied = applyRequestOperation(current, input.operation, input.actorSubject, this.#now(), this.#id);
395
+ if (input.operation.kind === 'assign' &&
396
+ !collectionControlEnabled(collection, 'assignment')) {
397
+ return { ok: false, reason: 'invalid_transition', currentRevision: current.revision };
398
+ }
399
+ if (input.operation.kind === 'assign' && input.operation.assigneeSubject !== undefined) {
400
+ const assignee = this.#grants.get(grantKey(input.scope, input.operation.assigneeSubject));
401
+ if (assignee === undefined ||
402
+ assignee.revokedAt !== undefined ||
403
+ assignee.role === 'viewer' ||
404
+ !(await this.#principals.allows(assignee.subject))) {
405
+ return { ok: false, reason: 'invalid_assignee', currentRevision: current.revision };
406
+ }
407
+ }
408
+ const historicalCollection = validateStoredRecord(installation, current);
409
+ const applied = applyRequestOperation(current, {
410
+ ...historicalCollection,
411
+ ...(collection.management === undefined ? {} : { management: collection.management }),
412
+ ...(collection.editableFields === undefined
413
+ ? {}
414
+ : { editableFields: collection.editableFields }),
415
+ }, input.operation, input.actorSubject, this.#now(), this.#id);
255
416
  if (applied === undefined) {
256
417
  return { ok: false, reason: 'invalid_transition', currentRevision: current.revision };
257
418
  }
258
- this.#records.set(key, applied.record);
259
- this.#activities.get(key)?.push(activityFromRecord(applied.record, applied.activityKind));
419
+ this.#commitRecord(key, applied.record, applied.activityKind);
260
420
  return { ok: true, record: cloneRecord(applied.record) };
261
421
  });
422
+ return input.operation.kind === 'assign'
423
+ ? this.#locks.run(`grants:${scopeKey(input.scope)}`, mutate)
424
+ : mutate();
425
+ }
426
+ async migrateLegacyRequest(input) {
427
+ validateExpectedRevision(input.expectedRevision);
428
+ const installation = await this.#requiredInstallation(input.scope);
429
+ validateCollectionEnabled(installation, input.collectionKey);
430
+ const key = recordKey(input.scope, input.collectionKey, validateScalar('record id', input.id, 128));
431
+ return this.#locks.run(`request:${key}`, async () => {
432
+ const current = this.#expireRecordIfNeeded(key, this.#now());
433
+ if (current === undefined || current.deletedAt !== undefined)
434
+ return { ok: false, reason: 'not_found', currentRevision: current?.revision ?? 0 };
435
+ if (current.revision !== input.expectedRevision)
436
+ return { ok: false, reason: 'conflict', currentRevision: current.revision };
437
+ const previous = validateStoredRecord(installation, current);
438
+ const migrated = migrateLegacyRequestRecord(current, previous, installation, input.actorSubject, this.#now());
439
+ if (migrated === undefined)
440
+ return { ok: false, reason: 'invalid_transition', currentRevision: current.revision };
441
+ if (migrated !== current) {
442
+ this.#commitRecord(key, migrated, 'schema_migrated');
443
+ }
444
+ return { ok: true, record: cloneRecord(migrated) };
445
+ });
262
446
  }
263
447
  async deleteRequest(input) {
264
448
  validateExpectedRevision(input.expectedRevision);
265
449
  const installation = await this.#requiredInstallation(input.scope);
266
450
  const collection = validateCollectionEnabled(installation, input.collectionKey);
267
451
  const key = recordKey(input.scope, collection.key, validateScalar('record id', input.id, 128));
268
- return this.#withLock(`request:${key}`, () => Promise.resolve(this.#eraseRecord(key, input.expectedRevision, input.actorSubject, 'customer_request')));
452
+ return this.#locks.run(`request:${key}`, () => Promise.resolve(this.#eraseRecord(key, input.expectedRevision, input.actorSubject, 'customer_request')));
269
453
  }
270
- listActivity(scope, collectionKey, id) {
271
- const key = recordKey(validateScope(scope), collectionKey, validateScalar('record id', id, 128));
272
- return Promise.resolve((this.#activities.get(key) ?? []).map(cloneActivity));
454
+ async listActivity(scope, collectionKey, id, input) {
455
+ const installation = await this.#requiredInstallation(scope);
456
+ const collection = validateCollectionEnabled(installation, collectionKey);
457
+ const key = recordKey(validateScope(scope), collection.key, validateScalar('record id', id, 128));
458
+ const record = this.#expireRecordIfNeeded(key, this.#now());
459
+ if (record !== undefined)
460
+ validateStoredRecord(installation, record);
461
+ return memoryActivityPage(scope, collection.key, id, this.#activities.get(key) ?? [], input);
273
462
  }
274
463
  async exportRequests(input) {
275
464
  const installation = await this.#requiredInstallation(input.scope);
@@ -289,6 +478,8 @@ export class InMemoryBusinessInformationStore {
289
478
  .filter((record) => record.createdAt <= snapshotAt)
290
479
  .filter((record) => input.includeDeleted === true || record.deletedAt === undefined)
291
480
  .filter((record) => cursor === undefined || afterCursor(record, cursor));
481
+ for (const record of records)
482
+ validateStoredRecord(installation, record);
292
483
  return {
293
484
  ...page(records, limit, 'export', input.scope, collection.key, snapshotAt),
294
485
  snapshotAt,
@@ -305,7 +496,7 @@ export class InMemoryBusinessInformationStore {
305
496
  .slice(0, limit);
306
497
  let purged = 0;
307
498
  for (const [key, record] of candidates) {
308
- const result = await this.#withLock(`request:${key}`, () => Promise.resolve(this.#eraseRecord(key, record.revision, 'system:retention', 'retention_expired')));
499
+ const result = await this.#locks.run(`request:${key}`, () => Promise.resolve(this.#eraseRecord(key, record.revision, 'system:retention', 'retention_expired')));
309
500
  if (result.ok)
310
501
  purged += 1;
311
502
  }
@@ -313,13 +504,28 @@ export class InMemoryBusinessInformationStore {
313
504
  }
314
505
  #matchingRecords(scope, collectionKey) {
315
506
  const prefix = `${scopeKey(scope)}\0${collectionKey}\0`;
507
+ const now = this.#now().toISOString();
316
508
  return [...this.#records.entries()]
317
- .filter(([key]) => key.startsWith(prefix))
509
+ .filter(([key, record]) => key.startsWith(prefix) &&
510
+ (record.deletedAt !== undefined || record.retentionExpiresAt > now))
318
511
  .map(([, record]) => record)
319
512
  .sort(compareRecords);
320
513
  }
321
- #eraseRecord(key, expectedRevision, actorSubject, reason) {
514
+ #expireRecordIfNeeded(key, now) {
322
515
  const current = this.#records.get(key);
516
+ if (current === undefined ||
517
+ current.deletedAt !== undefined ||
518
+ current.retentionExpiresAt > now.toISOString()) {
519
+ return current;
520
+ }
521
+ const erased = deletedRecord(current, 'system:retention', now, 'retention_expired');
522
+ this.#commitRecord(key, erased, 'retention_expired');
523
+ return erased;
524
+ }
525
+ #eraseRecord(key, expectedRevision, actorSubject, reason) {
526
+ const current = reason === 'retention_expired'
527
+ ? this.#records.get(key)
528
+ : this.#expireRecordIfNeeded(key, this.#now());
323
529
  if (current === undefined || current.deletedAt !== undefined) {
324
530
  return { ok: false, reason: 'not_found', currentRevision: current?.revision ?? 0 };
325
531
  }
@@ -327,18 +533,12 @@ export class InMemoryBusinessInformationStore {
327
533
  return { ok: false, reason: 'conflict', currentRevision: current.revision };
328
534
  }
329
535
  const erased = deletedRecord(current, actorSubject, this.#now(), reason);
330
- this.#records.set(key, erased);
331
- const erasedHistory = (this.#activities.get(key) ?? []).map((activity) => {
332
- const { content: ignored, ...metadata } = activity;
333
- void ignored;
334
- return metadata;
335
- });
336
- this.#activities.set(key, [
337
- ...erasedHistory,
338
- activityFromRecord(erased, reason === 'customer_request' ? 'deleted' : 'retention_expired'),
339
- ]);
536
+ this.#commitRecord(key, erased, reason === 'customer_request' ? 'deleted' : 'retention_expired');
340
537
  return { ok: true, record: cloneRecord(erased) };
341
538
  }
539
+ #commitRecord(key, record, kind) {
540
+ commitNativeMemoryRecord(this.#records, this.#activities, this.#custody, key, record, activityFromRecord(record, kind));
541
+ }
342
542
  async #requiredInstallation(scope) {
343
543
  const installation = await this.getInstallation(scope);
344
544
  if (installation === undefined)
@@ -353,50 +553,5 @@ export class InMemoryBusinessInformationStore {
353
553
  }
354
554
  throw new Error('could not allocate a unique public installation id');
355
555
  }
356
- async #withLock(key, operation) {
357
- const previous = this.#locks.get(key) ?? Promise.resolve();
358
- let release = () => void 0;
359
- const current = new Promise((resolve) => {
360
- release = resolve;
361
- });
362
- const queued = previous.then(() => current);
363
- this.#locks.set(key, queued);
364
- await previous;
365
- try {
366
- return await operation();
367
- }
368
- finally {
369
- release(undefined);
370
- if (this.#locks.get(key) === queued)
371
- this.#locks.delete(key);
372
- }
373
- }
374
- }
375
- function installationIdKey(org, installationId) {
376
- return `${org}\0${installationId}`;
377
- }
378
- function grantKey(scope, subject) {
379
- return `${scopeKey(scope)}\0${validateScalar('grant subject', subject, 256)}`;
380
- }
381
- function page(records, limit, kind, scope, collectionKey, snapshotAt) {
382
- const selected = records.slice(0, limit).map(cloneRecord);
383
- const last = selected.at(-1);
384
- const hasMore = records.length > limit;
385
- return {
386
- records: selected,
387
- ...(hasMore && last !== undefined
388
- ? {
389
- nextCursor: encodeCursor({
390
- version: 1,
391
- kind,
392
- scope: scopeKey(scope),
393
- collectionKey,
394
- lastCreatedAt: last.createdAt,
395
- lastId: last.id,
396
- ...(snapshotAt === undefined ? {} : { snapshotAt }),
397
- }),
398
- }
399
- : {}),
400
- };
401
556
  }
402
557
  //# sourceMappingURL=in-memory-store.js.map
@@ -0,0 +1,10 @@
1
+ /** Technical retained-metadata guard, independent of app or commercial entitlements. */
2
+ export const MAX_RETAINED_INSTALLATIONS_PER_ORG = 1_000;
3
+ export class InstallationCapacityError extends Error {
4
+ code = 'installation_capacity_exceeded';
5
+ constructor() {
6
+ super('This organization has reached its retained installation limit. Reuse an existing application and environment; existing installations remain accessible.');
7
+ this.name = 'InstallationCapacityError';
8
+ }
9
+ }
10
+ //# sourceMappingURL=installation-capacity.js.map
@@ -0,0 +1,68 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { validateExpectedRevision } from './model.js';
3
+ import { validateEmail, validateScalar, validateScope } from './validation.js';
4
+ export function normalizeInvitationInput(input) {
5
+ const expiresAt = input.expiresAt;
6
+ if (!Number.isFinite(expiresAt.getTime()))
7
+ throw new Error('invitation expiry is invalid');
8
+ const tokenDigest = validateDigest('invitation token digest', input.tokenDigest);
9
+ const idempotencyDigest = createHash('sha256')
10
+ .update(validateScalar('invitation idempotency key', input.idempotencyKey, 128))
11
+ .digest('hex');
12
+ const normalized = {
13
+ scope: validateScope(input.scope),
14
+ invitationId: validateScalar('invitation id', input.invitationId, 128),
15
+ email: validateEmail(input.email),
16
+ role: input.role,
17
+ tokenDigest,
18
+ idempotencyDigest,
19
+ expiresAt,
20
+ actorSubject: validateScalar('actor subject', input.actorSubject, 256),
21
+ };
22
+ return {
23
+ ...normalized,
24
+ createFingerprint: createHash('sha256')
25
+ .update(JSON.stringify({
26
+ scope: normalized.scope,
27
+ email: normalized.email,
28
+ role: normalized.role,
29
+ tokenDigest: normalized.tokenDigest,
30
+ }))
31
+ .digest('hex'),
32
+ };
33
+ }
34
+ export function validateInvitationMutation(input) {
35
+ validateExpectedRevision(input.expectedRevision);
36
+ return {
37
+ scope: validateScope(input.scope),
38
+ invitationId: validateScalar('invitation id', input.invitationId, 128),
39
+ expectedRevision: input.expectedRevision,
40
+ actorSubject: validateScalar('actor subject', input.actorSubject, 256),
41
+ };
42
+ }
43
+ export function validateInvitationClaim(input) {
44
+ return {
45
+ tokenDigest: validateDigest('invitation token digest', input.tokenDigest),
46
+ subject: validateScalar('identity subject', input.subject, 256),
47
+ email: validateEmail(input.email),
48
+ };
49
+ }
50
+ export function invitationStatus(invitation, now) {
51
+ if (invitation.acceptedAt !== undefined)
52
+ return 'accepted';
53
+ if (invitation.revokedAt !== undefined)
54
+ return 'revoked';
55
+ if (Date.parse(invitation.expiresAt) <= now.getTime())
56
+ return 'expired';
57
+ return 'pending';
58
+ }
59
+ export function cloneInvitation(invitation) {
60
+ return { ...invitation, scope: { ...invitation.scope } };
61
+ }
62
+ function validateDigest(name, value) {
63
+ const digest = validateScalar(name, value, 64);
64
+ if (!/^[a-f0-9]{64}$/.test(digest))
65
+ throw new Error(`${name} is invalid`);
66
+ return digest;
67
+ }
68
+ //# sourceMappingURL=invitations.js.map