@plurnk/plurnk-service 3.2.0 → 4.0.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 (328) hide show
  1. package/.env.defaults +3 -3
  2. package/INSTALL.md +6 -0
  3. package/README.md +2 -2
  4. package/SPEC.md +489 -186
  5. package/dist/build-info.json +1 -1
  6. package/dist/completions/_plurnk-service +1 -1
  7. package/dist/completions/plurnk-service.bash +1 -1
  8. package/dist/completions/plurnk-service.fish +2 -0
  9. package/dist/content/read-projector.js +1 -1
  10. package/dist/content/read-projector.js.map +1 -1
  11. package/dist/core/AdministrativeLoop.d.ts +1 -1
  12. package/dist/core/AdministrativeLoop.d.ts.map +1 -1
  13. package/dist/core/AdministrativeLoop.js +4 -1
  14. package/dist/core/AdministrativeLoop.js.map +1 -1
  15. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
  16. package/dist/core/AdmittedTurnExecutor.js +8 -0
  17. package/dist/core/AdmittedTurnExecutor.js.map +1 -1
  18. package/dist/core/BareBatchRunner.js +2 -1
  19. package/dist/core/BareBatchRunner.js.map +1 -1
  20. package/dist/core/BudgetReadout.d.ts +1 -3
  21. package/dist/core/BudgetReadout.d.ts.map +1 -1
  22. package/dist/core/BudgetReadout.js +43 -46
  23. package/dist/core/BudgetReadout.js.map +1 -1
  24. package/dist/core/ChannelWrite.d.ts +10 -0
  25. package/dist/core/ChannelWrite.d.ts.map +1 -1
  26. package/dist/core/ChannelWrite.js +10 -0
  27. package/dist/core/ChannelWrite.js.map +1 -1
  28. package/dist/core/ChannelWrite.sql +14 -2
  29. package/dist/core/ClientInteractions.d.ts.map +1 -1
  30. package/dist/core/ClientInteractions.js +2 -1
  31. package/dist/core/ClientInteractions.js.map +1 -1
  32. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  33. package/dist/core/CoreSchemeServices.js +6 -0
  34. package/dist/core/CoreSchemeServices.js.map +1 -1
  35. package/dist/core/DataStatementRunner.js +4 -6
  36. package/dist/core/DataStatementRunner.js.map +1 -1
  37. package/dist/core/Dispatcher.d.ts +3 -7
  38. package/dist/core/Dispatcher.d.ts.map +1 -1
  39. package/dist/core/Dispatcher.js +53 -73
  40. package/dist/core/Dispatcher.js.map +1 -1
  41. package/dist/core/Dispatcher.sql +2 -2
  42. package/dist/core/EditMutations.js +1 -0
  43. package/dist/core/EditMutations.js.map +1 -1
  44. package/dist/core/Engine.d.ts +8 -14
  45. package/dist/core/Engine.d.ts.map +1 -1
  46. package/dist/core/Engine.js +22 -24
  47. package/dist/core/Engine.js.map +1 -1
  48. package/dist/core/Engine.sql +6 -6
  49. package/dist/core/ExecutorRegistry.d.ts +0 -5
  50. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  51. package/dist/core/ExecutorRegistry.js +10 -18
  52. package/dist/core/ExecutorRegistry.js.map +1 -1
  53. package/dist/core/FileAddress.js +1 -1
  54. package/dist/core/FileAddress.js.map +1 -1
  55. package/dist/core/FilePublication.js +10 -74
  56. package/dist/core/FilePublication.js.map +1 -1
  57. package/dist/core/FileSnapshot.js +36 -100
  58. package/dist/core/FileSnapshot.js.map +1 -1
  59. package/dist/core/HostPaths.d.ts +1 -0
  60. package/dist/core/HostPaths.d.ts.map +1 -1
  61. package/dist/core/HostPaths.js +11 -1
  62. package/dist/core/HostPaths.js.map +1 -1
  63. package/dist/core/KillHandler.js +3 -1
  64. package/dist/core/KillHandler.js.map +1 -1
  65. package/dist/core/LiveSubscription.js +1 -1
  66. package/dist/core/LiveSubscription.js.map +1 -1
  67. package/dist/core/LogBody.js +1 -1
  68. package/dist/core/LogBody.js.map +1 -1
  69. package/dist/core/LogWriter.js +1 -1
  70. package/dist/core/LogWriter.js.map +1 -1
  71. package/dist/core/LoopDriver.d.ts +3 -3
  72. package/dist/core/LoopDriver.d.ts.map +1 -1
  73. package/dist/core/LoopDriver.js +15 -13
  74. package/dist/core/LoopDriver.js.map +1 -1
  75. package/dist/core/LoopLifecycle.d.ts +5 -0
  76. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  77. package/dist/core/LoopLifecycle.js +14 -1
  78. package/dist/core/LoopLifecycle.js.map +1 -1
  79. package/dist/core/LoopLifecycle.sql +11 -2
  80. package/dist/core/MembershipMaterialization.js +2 -2
  81. package/dist/core/MembershipMaterialization.js.map +1 -1
  82. package/dist/core/MessageResources.d.ts +3 -0
  83. package/dist/core/MessageResources.d.ts.map +1 -1
  84. package/dist/core/MessageResources.js +15 -1
  85. package/dist/core/MessageResources.js.map +1 -1
  86. package/dist/core/OperatorConfig.d.ts +1 -2
  87. package/dist/core/OperatorConfig.d.ts.map +1 -1
  88. package/dist/core/OperatorConfig.js +26 -92
  89. package/dist/core/OperatorConfig.js.map +1 -1
  90. package/dist/core/PacketBuilder.d.ts.map +1 -1
  91. package/dist/core/PacketBuilder.js +21 -18
  92. package/dist/core/PacketBuilder.js.map +1 -1
  93. package/dist/core/PacketBuilder.sql +18 -5
  94. package/dist/core/ProblemLog.d.ts +1 -0
  95. package/dist/core/ProblemLog.d.ts.map +1 -1
  96. package/dist/core/ProblemLog.js +2 -2
  97. package/dist/core/ProblemLog.js.map +1 -1
  98. package/dist/core/ProposalLifecycle.d.ts +1 -0
  99. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  100. package/dist/core/ProposalLifecycle.js +14 -8
  101. package/dist/core/ProposalLifecycle.js.map +1 -1
  102. package/dist/core/ProposalPolicies.d.ts +4 -1
  103. package/dist/core/ProposalPolicies.d.ts.map +1 -1
  104. package/dist/core/ProposalPolicies.js +5 -2
  105. package/dist/core/ProposalPolicies.js.map +1 -1
  106. package/dist/core/ResourceMutations.js +2 -0
  107. package/dist/core/ResourceMutations.js.map +1 -1
  108. package/dist/core/ResourceSelector.js +1 -1
  109. package/dist/core/ResourceSelector.js.map +1 -1
  110. package/dist/core/SchemeRegistry.js +1 -1
  111. package/dist/core/SchemeRegistry.js.map +1 -1
  112. package/dist/core/ServiceHelp.js +1 -1
  113. package/dist/core/ServiceHelp.js.map +1 -1
  114. package/dist/core/ServiceTeardown.js +9 -12
  115. package/dist/core/ServiceTeardown.js.map +1 -1
  116. package/dist/core/StrikeRail.d.ts +2 -1
  117. package/dist/core/StrikeRail.d.ts.map +1 -1
  118. package/dist/core/StrikeRail.js +11 -6
  119. package/dist/core/StrikeRail.js.map +1 -1
  120. package/dist/core/TerminalResult.js +1 -1
  121. package/dist/core/TerminalResult.js.map +1 -1
  122. package/dist/core/TurnDispositionHandler.d.ts +1 -1
  123. package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
  124. package/dist/core/TurnDispositionHandler.js +8 -5
  125. package/dist/core/TurnDispositionHandler.js.map +1 -1
  126. package/dist/core/TurnMaterialization.js +6 -4
  127. package/dist/core/TurnMaterialization.js.map +1 -1
  128. package/dist/core/TurnMaterialization.sql +1 -1
  129. package/dist/core/TurnOps.js +1 -0
  130. package/dist/core/TurnOps.js.map +1 -1
  131. package/dist/core/TurnRunner.d.ts +4 -2
  132. package/dist/core/TurnRunner.d.ts.map +1 -1
  133. package/dist/core/TurnRunner.js +63 -51
  134. package/dist/core/TurnRunner.js.map +1 -1
  135. package/dist/core/WorkerControlHandler.d.ts +2 -0
  136. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  137. package/dist/core/WorkerControlHandler.js +84 -16
  138. package/dist/core/WorkerControlHandler.js.map +1 -1
  139. package/dist/core/WorkerName.d.ts +0 -4
  140. package/dist/core/WorkerName.d.ts.map +1 -1
  141. package/dist/core/WorkerName.js +5 -30
  142. package/dist/core/WorkerName.js.map +1 -1
  143. package/dist/core/WorkerName.sql +4 -20
  144. package/dist/core/WorkerOwners.d.ts +1 -0
  145. package/dist/core/WorkerOwners.d.ts.map +1 -1
  146. package/dist/core/WorkerOwners.js +6 -0
  147. package/dist/core/WorkerOwners.js.map +1 -1
  148. package/dist/core/WorkerOwners.sql +7 -0
  149. package/dist/core/WorkspaceGate.d.ts +3 -1
  150. package/dist/core/WorkspaceGate.d.ts.map +1 -1
  151. package/dist/core/WorkspaceGate.js +59 -10
  152. package/dist/core/WorkspaceGate.js.map +1 -1
  153. package/dist/core/caps/SchemeCtxImpl.d.ts +3 -0
  154. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  155. package/dist/core/caps/SchemeCtxImpl.js +14 -0
  156. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  157. package/dist/core/env-catalog.js +1 -1
  158. package/dist/core/env-catalog.js.map +1 -1
  159. package/dist/core/fork.js +1 -1
  160. package/dist/core/fork.js.map +1 -1
  161. package/dist/core/fork.sql +7 -6
  162. package/dist/core/mutation-types.d.ts +1 -0
  163. package/dist/core/mutation-types.d.ts.map +1 -1
  164. package/dist/core/mutation-types.js +4 -1
  165. package/dist/core/mutation-types.js.map +1 -1
  166. package/dist/core/namespace.js +1 -1
  167. package/dist/core/namespace.js.map +1 -1
  168. package/dist/core/packet-wire.d.ts +1 -1
  169. package/dist/core/packet-wire.d.ts.map +1 -1
  170. package/dist/core/packet-wire.js +24 -15
  171. package/dist/core/packet-wire.js.map +1 -1
  172. package/dist/core/plurnk-uri.d.ts +1 -1
  173. package/dist/core/plurnk-uri.d.ts.map +1 -1
  174. package/dist/core/plurnk-uri.js +1 -1
  175. package/dist/core/plurnk-uri.js.map +1 -1
  176. package/dist/core/scheme-types.d.ts +5 -0
  177. package/dist/core/scheme-types.d.ts.map +1 -1
  178. package/dist/core/turn-signals.d.ts +4 -0
  179. package/dist/core/turn-signals.d.ts.map +1 -1
  180. package/dist/core/turn-signals.js +1 -0
  181. package/dist/core/turn-signals.js.map +1 -1
  182. package/dist/core/workspace-settings.js +1 -1
  183. package/dist/core/workspace-settings.js.map +1 -1
  184. package/dist/evidence/EvidenceReader.js +5 -5
  185. package/dist/evidence/EvidenceReader.js.map +1 -1
  186. package/dist/man/plurnk-service.1 +27 -6
  187. package/dist/schemes/Env.d.ts +7 -3
  188. package/dist/schemes/Env.d.ts.map +1 -1
  189. package/dist/schemes/Env.js +12 -6
  190. package/dist/schemes/Env.js.map +1 -1
  191. package/dist/schemes/Exec.d.ts.map +1 -1
  192. package/dist/schemes/Exec.js +37 -25
  193. package/dist/schemes/Exec.js.map +1 -1
  194. package/dist/schemes/ExecOutputScheme.js +3 -3
  195. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  196. package/dist/schemes/ExecutionInput.d.ts +2 -1
  197. package/dist/schemes/ExecutionInput.d.ts.map +1 -1
  198. package/dist/schemes/ExecutionInput.js +45 -7
  199. package/dist/schemes/ExecutionInput.js.map +1 -1
  200. package/dist/schemes/File.d.ts.map +1 -1
  201. package/dist/schemes/File.js +32 -23
  202. package/dist/schemes/File.js.map +1 -1
  203. package/dist/schemes/Log.d.ts.map +1 -1
  204. package/dist/schemes/Log.js +23 -5
  205. package/dist/schemes/Log.js.map +1 -1
  206. package/dist/schemes/Log.sql +24 -1
  207. package/dist/schemes/ResourceTreeScheme.js +2 -5
  208. package/dist/schemes/ResourceTreeScheme.js.map +1 -1
  209. package/dist/schemes/TurnSource.js +1 -1
  210. package/dist/schemes/TurnSource.js.map +1 -1
  211. package/dist/schemes/Worker.d.ts.map +1 -1
  212. package/dist/schemes/Worker.js +10 -3
  213. package/dist/schemes/Worker.js.map +1 -1
  214. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  215. package/dist/schemes/_entry-crud.js +2 -4
  216. package/dist/schemes/_entry-crud.js.map +1 -1
  217. package/dist/schemes/_entry-crud.sql +5 -3
  218. package/dist/schemes/_entry-find.js +3 -3
  219. package/dist/schemes/_entry-find.js.map +1 -1
  220. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  221. package/dist/schemes/_entry-manifest.js +3 -3
  222. package/dist/schemes/_entry-manifest.js.map +1 -1
  223. package/dist/schemes/_entry-manifest.sql +2 -0
  224. package/dist/schemes/_entry-ops.js +1 -1
  225. package/dist/schemes/_entry-ops.js.map +1 -1
  226. package/dist/schemes/_entry-send.js +1 -1
  227. package/dist/schemes/_entry-send.js.map +1 -1
  228. package/dist/server/ClientReads.d.ts +1 -0
  229. package/dist/server/ClientReads.d.ts.map +1 -1
  230. package/dist/server/ClientReads.js +7 -2
  231. package/dist/server/ClientReads.js.map +1 -1
  232. package/dist/server/Credentials.d.ts +48 -0
  233. package/dist/server/Credentials.d.ts.map +1 -0
  234. package/dist/server/Credentials.js +203 -0
  235. package/dist/server/Credentials.js.map +1 -0
  236. package/dist/server/Daemon.d.ts +44 -39
  237. package/dist/server/Daemon.d.ts.map +1 -1
  238. package/dist/server/Daemon.js +287 -172
  239. package/dist/server/Daemon.js.map +1 -1
  240. package/dist/server/DrainSupervisor.d.ts +8 -3
  241. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  242. package/dist/server/DrainSupervisor.js +44 -32
  243. package/dist/server/DrainSupervisor.js.map +1 -1
  244. package/dist/server/EnvFunctionality.d.ts.map +1 -1
  245. package/dist/server/EnvFunctionality.js +2 -1
  246. package/dist/server/EnvFunctionality.js.map +1 -1
  247. package/dist/server/Functionality.d.ts +4 -2
  248. package/dist/server/Functionality.d.ts.map +1 -1
  249. package/dist/server/Functionality.js +192 -96
  250. package/dist/server/Functionality.js.map +1 -1
  251. package/dist/server/FunctionalityManager.d.ts +1 -4
  252. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  253. package/dist/server/FunctionalityManager.js +6 -6
  254. package/dist/server/FunctionalityManager.js.map +1 -1
  255. package/dist/server/HttpListener.d.ts +1 -1
  256. package/dist/server/HttpListener.d.ts.map +1 -1
  257. package/dist/server/HttpListener.js.map +1 -1
  258. package/dist/server/MembersFunctionality.d.ts +7 -8
  259. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  260. package/dist/server/MembersFunctionality.js +90 -56
  261. package/dist/server/MembersFunctionality.js.map +1 -1
  262. package/dist/server/MessageAbandonment.d.ts +7 -0
  263. package/dist/server/MessageAbandonment.d.ts.map +1 -0
  264. package/dist/server/MessageAbandonment.js +57 -0
  265. package/dist/server/MessageAbandonment.js.map +1 -0
  266. package/dist/server/ModuleHost.d.ts +2 -1
  267. package/dist/server/ModuleHost.d.ts.map +1 -1
  268. package/dist/server/PlurnkSkill.js +4 -7
  269. package/dist/server/PlurnkSkill.js.map +1 -1
  270. package/dist/server/Rollback.d.ts +29 -0
  271. package/dist/server/Rollback.d.ts.map +1 -0
  272. package/dist/server/Rollback.js +111 -0
  273. package/dist/server/Rollback.js.map +1 -0
  274. package/dist/server/WorkerModelResolver.js +1 -0
  275. package/dist/server/WorkerModelResolver.js.map +1 -1
  276. package/dist/server/WorkerReferences.d.ts +14 -0
  277. package/dist/server/WorkerReferences.d.ts.map +1 -0
  278. package/dist/server/WorkerReferences.js +24 -0
  279. package/dist/server/WorkerReferences.js.map +1 -0
  280. package/dist/server/WorkerReferences.sql +31 -0
  281. package/dist/server/WorkspaceCapabilities.d.ts +2 -0
  282. package/dist/server/WorkspaceCapabilities.d.ts.map +1 -1
  283. package/dist/server/WorkspaceCapabilities.js +35 -18
  284. package/dist/server/WorkspaceCapabilities.js.map +1 -1
  285. package/dist/server/WorkspacePlugins.js +1 -0
  286. package/dist/server/WorkspacePlugins.js.map +1 -1
  287. package/dist/server/WorkspaceResidency.d.ts +3 -1
  288. package/dist/server/WorkspaceResidency.d.ts.map +1 -1
  289. package/dist/server/WorkspaceResidency.js +12 -5
  290. package/dist/server/WorkspaceResidency.js.map +1 -1
  291. package/dist/server/client-input.d.ts +2 -0
  292. package/dist/server/client-input.d.ts.map +1 -1
  293. package/dist/server/client-input.js +28 -0
  294. package/dist/server/client-input.js.map +1 -1
  295. package/dist/server/drain.sql +46 -40
  296. package/dist/server/envelope.d.ts +5 -9
  297. package/dist/server/envelope.d.ts.map +1 -1
  298. package/dist/server/envelope.js +21 -84
  299. package/dist/server/envelope.js.map +1 -1
  300. package/dist/server/envelope.sql +17 -17
  301. package/dist/server/lifecycle-recovery.sql +5 -15
  302. package/dist/server/logEntry.d.ts.map +1 -1
  303. package/dist/server/logEntry.js +6 -0
  304. package/dist/server/logEntry.js.map +1 -1
  305. package/dist/server/logEntry.sql +94 -6
  306. package/dist/server/module-discovery.d.ts +1 -1
  307. package/dist/server/module-discovery.d.ts.map +1 -1
  308. package/dist/server/module-discovery.js +1 -0
  309. package/dist/server/module-discovery.js.map +1 -1
  310. package/dist/server/seam-loop.sql +15 -7
  311. package/dist/server/seam-proposal-list.sql +10 -4
  312. package/dist/service.d.ts.map +1 -1
  313. package/dist/service.js +7 -1
  314. package/dist/service.js.map +1 -1
  315. package/docs/members.md +20 -13
  316. package/migrations/002_workers.sql +2 -2
  317. package/migrations/006_log.sql +1 -1
  318. package/migrations/018_worker_owners.sql +1 -1
  319. package/migrations/026_message_obligations.sql +32 -0
  320. package/migrations/027_members_identity.sql +20 -0
  321. package/migrations/028_mutation_patch.sql +20 -0
  322. package/migrations/029_default_worker.sql +9 -0
  323. package/migrations/030_client_workers.sql +105 -0
  324. package/migrations/031_message_openers.sql +83 -0
  325. package/migrations/032_worker_references.sql +29 -0
  326. package/package.json +24 -24
  327. package/plurnk.nginx.conf +71 -0
  328. package/plurnk.service +2 -0
@@ -1,4 +1,3 @@
1
- var _a;
2
1
  // {§functionality-coordinator} — the one owner of the workspace Functionality
3
2
  // lifecycle above the family adapters (Agent Skills, MCP, outbound A2A). It owns
4
3
  // durable workspace state, lifecycle ordering, serialization, atomic
@@ -42,7 +41,7 @@ const discoverInput = ({ inputs, emptyListsAll }) => ({
42
41
  });
43
42
  const failure = (family, code, status, detail, extensions = {}) => new OperationFailureError(Results.failure("functionality", code, status, detail, {}, { family, ...extensions }));
44
43
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
45
- class Functionality {
44
+ export default class Functionality {
46
45
  #host;
47
46
  #adapters = new Map();
48
47
  #owners = new Set();
@@ -93,31 +92,40 @@ class Functionality {
93
92
  throw new Error(`Functionality ${family} input has no property map.`);
94
93
  return { ...source, properties: { ...source.properties, ...scopeProperties } };
95
94
  };
96
- const addInput = Object.freeze({
97
- type: "object",
98
- additionalProperties: false,
99
- required: ["definition"],
100
- properties: {
101
- ...scopeProperties,
102
- alias: { type: "string", minLength: 1 },
103
- definition: adapter.definitionSchema,
104
- },
105
- });
95
+ // {§functionality-addressing} — a family that names definitions by their content supplies the
96
+ // inputs of add and of the select verbs itself, and serves no replace.
97
+ const { addressing } = adapter;
98
+ if (addressing !== undefined && [addressing.add, addressing.select].some((schema) => !isRecord(schema) || !isRecord(schema.properties))) {
99
+ throw new Error(`Functionality family '${family}' declares addressing without object input schemas.`);
100
+ }
101
+ const addInput = addressing === undefined
102
+ ? Object.freeze({
103
+ type: "object",
104
+ additionalProperties: false,
105
+ required: ["definition"],
106
+ properties: {
107
+ ...scopeProperties,
108
+ alias: { type: "string", minLength: 1 },
109
+ definition: adapter.definitionSchema,
110
+ },
111
+ })
112
+ : scopedInput(addressing.add);
113
+ const selectInput = scopedInput(addressing?.select ?? ALIAS_INPUT);
106
114
  // {§functionality-discover-advertisement} A family without discovery has no discover verb.
107
115
  const schemas = Object.freeze({
108
116
  list: scopedInput(EMPTY_INPUT),
109
117
  ...(discovery === undefined ? {} : { discover: scopedInput(discoverInput(discovery)) }),
110
118
  add: addInput,
111
- replace: { ...addInput, required: ["alias", "definition"] },
112
- enable: scopedInput(ALIAS_INPUT),
113
- disable: scopedInput(ALIAS_INPUT),
114
- remove: scopedInput(ALIAS_INPUT),
119
+ ...(addressing === undefined ? { replace: { ...addInput, required: ["alias", "definition"] } } : {}),
120
+ enable: selectInput,
121
+ disable: selectInput,
122
+ remove: selectInput,
115
123
  });
116
124
  // {§functionality-model-projection} — the taught `add` example must satisfy the schema it teaches.
117
125
  if (adapter.example !== undefined) {
118
126
  const example = Validator.validateJsonSchemaInstance(addInput, adapter.example);
119
127
  if (!example.valid) {
120
- throw new Error(`Functionality family '${family}' teaches an add example that violates its own definition schema: ${JSON.stringify(example.errors)}`);
128
+ throw new Error(`Functionality family '${family}' teaches an add example that violates its own add input: ${JSON.stringify(example.errors)}`);
121
129
  }
122
130
  }
123
131
  this.#adapters.set(family, adapter);
@@ -144,7 +152,7 @@ class Functionality {
144
152
  ? "FunctionalityDiscoverResult"
145
153
  : "FunctionalityMutationResult"),
146
154
  handler: async (params, context) => {
147
- if (context.scope === "worldless" || context.scope !== scope)
155
+ if (context.scope === "daemon" || context.scope !== scope)
148
156
  throw new Error(`${scope}.${family}.${verb} requires a ${scope}-scoped context.`);
149
157
  const identity = context.scope === "worker"
150
158
  ? { workspaceId: context.workspaceId, workerId: context.workerId, scope }
@@ -167,14 +175,16 @@ class Functionality {
167
175
  // `ifChanged` republishes only when the enabled definitions differ from the ones the resident
168
176
  // publication prepared ({§functionality-hotload}).
169
177
  async refresh(family, identity, options = {}) {
178
+ this.#host.signal.throwIfAborted();
170
179
  const adapter = this.#adapter(family);
171
180
  await this.#withWorkspaceGate(identity.workspaceId, adapter.namespaceOwner, options.gate ?? "wait", () => this.#serialize(this.#key(identity.workspaceId, family), async () => {
181
+ this.#host.signal.throwIfAborted();
172
182
  const current = this.#families.get(this.#key(identity.workspaceId, family));
173
183
  if (current === undefined)
174
184
  return;
175
185
  if (options.ifChanged === true && current.configurationFailure === null) {
176
186
  try {
177
- if (_a.#sameDefinitions(await this.#publishable(adapter, identity, current.state), current.enabled))
187
+ if (Functionality.#sameDefinitions(await this.#publishable(adapter, identity, current.state), current.enabled))
178
188
  return;
179
189
  }
180
190
  catch (cause) {
@@ -261,14 +271,21 @@ class Functionality {
261
271
  return out;
262
272
  }
263
273
  // The one lifecycle entry for both projections ({§functionality-model-mutation}).
264
- async invoke(family, verb, params, identity, caller, options = {}) {
274
+ async invoke(family, verb, params, identity, caller, options = {}, signal = this.#host.signal) {
275
+ const lifetime = signal === this.#host.signal ? signal : AbortSignal.any([this.#host.signal, signal]);
276
+ lifetime.throwIfAborted();
277
+ const pending = this.#invoke(family, verb, params, identity, caller, options, lifetime);
278
+ this.#admissions.set(pending, identity.workspaceId);
265
279
  try {
266
- return await this.#invoke(family, verb, params, identity, caller, options);
280
+ return await pending;
267
281
  }
268
282
  catch (cause) {
269
283
  if (!(cause instanceof ConfigurationError))
270
284
  throw cause;
271
- throw _a.#configurationFailure(family, cause);
285
+ throw Functionality.#configurationFailure(family, cause);
286
+ }
287
+ finally {
288
+ this.#admissions.delete(pending);
272
289
  }
273
290
  }
274
291
  static #configurationFailure(family, cause) {
@@ -279,7 +296,7 @@ class Functionality {
279
296
  recovery: "Correct the named configuration input. Other capabilities remain available.",
280
297
  }), { cause });
281
298
  }
282
- async #invoke(family, verb, params, identity, caller, options) {
299
+ async #invoke(family, verb, params, identity, caller, options, signal) {
283
300
  const adapter = this.#adapter(family);
284
301
  const schema = this.#schemas.get(family)[verb];
285
302
  // Neither projection offers a verb the family does not serve ({§functionality-discover-advertisement}).
@@ -308,12 +325,19 @@ class Functionality {
308
325
  // Worker's own lane and take no workspace exclusivity: a Worker shapes its environment while
309
326
  // its siblings run.
310
327
  if (scope === "worker") {
311
- const workerId = _a.#workerOf(adapter, identity);
312
- return this.#serialize(`${this.#key(identity.workspaceId, family)}:${workerId}`, () => this.#invokeForWorker(adapter, verb, input, identity, workerId, caller, options));
328
+ const workerId = Functionality.#workerOf(adapter, identity);
329
+ return this.#serialize(`${this.#key(identity.workspaceId, family)}:${workerId}`, () => this.#invokeForWorker(adapter, verb, input, identity, workerId, caller, options, signal));
313
330
  }
314
331
  if (verb === "list")
315
332
  return { status: 200, body: await this.#list(adapter, identity) };
316
- return this.#withWorkspaceGate(identity.workspaceId, adapter.namespaceOwner, caller === "operation" ? "wait" : "try", () => this.#serialize(this.#key(identity.workspaceId, family), () => this.#mutate(adapter, verb, input, identity, caller, options)));
333
+ const mutate = () => this.#serialize(this.#key(identity.workspaceId, family), () => this.#mutate(adapter, verb, input, identity, caller, options, signal));
334
+ // A context family (one that declares the worker scope) is read where it is used, so its workspace
335
+ // defaults change nothing resident either: they serialize on the family's lane, as a Worker's own do,
336
+ // and an operation inside its own held turn never waits on itself.
337
+ if (scopesOf(adapter).includes("worker"))
338
+ return mutate();
339
+ const publish = () => this.#withWorkspaceGate(identity.workspaceId, adapter.namespaceOwner, caller === "operation" ? "wait" : "try", mutate, signal);
340
+ return caller === "operation" ? this.#host.atWorkspaceBoundary(identity.workspaceId, publish) : publish();
317
341
  }
318
342
  // A worker-scoped verb that names no Worker is a defect at the projection, never a client
319
343
  // failure: both projections name the Worker ({§functionality-model-projection}).
@@ -342,8 +366,8 @@ class Functionality {
342
366
  this.#queues.set(key, next);
343
367
  return next;
344
368
  }
345
- async #withWorkspaceGate(workspaceId, owner, gate, run) {
346
- const pending = this.#host.withWorkspaceGate(workspaceId, owner, gate, run);
369
+ async #withWorkspaceGate(workspaceId, owner, gate, run, signal = this.#host.signal) {
370
+ const pending = this.#host.withWorkspaceGate(workspaceId, owner, gate, run, signal);
347
371
  this.#admissions.set(pending, workspaceId);
348
372
  try {
349
373
  return await pending;
@@ -355,6 +379,7 @@ class Functionality {
355
379
  async #activate(adapter, context) {
356
380
  const identity = { workspaceId: context.workspaceId };
357
381
  await this.#serialize(this.#key(identity.workspaceId, adapter.family), async () => {
382
+ this.#host.signal.throwIfAborted();
358
383
  const state = await this.#loadState(adapter, identity.workspaceId);
359
384
  await this.#publish(adapter, identity, state, { failure: "publish-unavailable", configurationFailure: "contain", retain: context.retain });
360
385
  });
@@ -370,10 +395,10 @@ class Functionality {
370
395
  });
371
396
  }
372
397
  async #loadState(adapter, workspaceId) {
373
- return _a.#parseState(adapter, await this.#host.readWorkspaceModuleState(workspaceId, adapter.namespaceOwner), `workspace ${workspaceId}`, "workspace");
398
+ return Functionality.#parseState(adapter, await this.#host.readWorkspaceModuleState(workspaceId, adapter.namespaceOwner), `workspace ${workspaceId}`, "workspace");
374
399
  }
375
400
  async #loadWorkerState(adapter, workerId) {
376
- return _a.#parseState(adapter, await this.#host.readWorkerModuleState(workerId, adapter.namespaceOwner), `worker ${workerId}`, "worker");
401
+ return Functionality.#parseState(adapter, await this.#host.readWorkerModuleState(workerId, adapter.namespaceOwner), `worker ${workerId}`, "worker");
377
402
  }
378
403
  static #parseState(adapter, raw, where, scope) {
379
404
  if (raw === null)
@@ -415,7 +440,7 @@ class Functionality {
415
440
  }
416
441
  async #effective(adapter, identity, state) {
417
442
  const effective = new Map();
418
- const local = _a.#localOrigin(identity);
443
+ const local = Functionality.#localOrigin(identity);
419
444
  const inheritsWorkspace = identity.scope === "worker" && scopesOf(adapter).includes("workspace");
420
445
  const lower = inheritsWorkspace
421
446
  ? [...(await this.#effective(adapter, { workspaceId: identity.workspaceId, scope: "workspace" }, await this.#loadState(adapter, identity.workspaceId))).values()]
@@ -471,7 +496,7 @@ class Functionality {
471
496
  return Validator.assertFunctionalityListResult({
472
497
  family: adapter.family,
473
498
  definitions: [...effective.values()].map((definition) => {
474
- const outcome = isDeepStrictEqual(family?.enabled.get(definition.alias), _a.#binding(definition))
499
+ const outcome = isDeepStrictEqual(family?.enabled.get(definition.alias), Functionality.#binding(definition))
475
500
  ? outcomes?.get(definition.alias)
476
501
  : undefined;
477
502
  return this.#projection(definition, outcome ?? { state: "dormant" });
@@ -483,45 +508,66 @@ class Functionality {
483
508
  const candidates = await adapter.discover(query, identity, options);
484
509
  return Validator.assertFunctionalityDiscoverResult({ family: adapter.family, candidates: [...candidates] });
485
510
  }
511
+ // The alias a select verb names: the input's `alias`, or what an addressing family reads from its
512
+ // own select input ({§functionality-addressing}).
513
+ static #selected(adapter, input) {
514
+ return adapter.addressing === undefined ? input.alias : adapter.addressing.alias(input);
515
+ }
486
516
  // The verb semantics, one implementation for every family ({§functionality-scope}): the next
487
- // state, the alias it concerns, and how the mutation reports. Only publication differs by scope.
517
+ // state, the aliases it concerns, and how the mutation reports. Only publication differs by scope.
488
518
  async #transition(adapter, verb, input, identity, caller, state, effective, options) {
489
- const local = _a.#localOrigin(identity);
519
+ const local = Functionality.#localOrigin(identity);
490
520
  const here = local === "worker" ? "this worker" : "this workspace";
491
521
  const definitions = { ...state.definitions };
492
- let alias;
522
+ const aliases = [];
493
523
  let status = 200;
494
524
  let removed = false;
495
- let admission;
525
+ let admissions = [];
496
526
  switch (verb) {
497
527
  case "add":
498
528
  case "replace": {
499
529
  const admitted = await adapter.admit(input, identity, caller, options);
500
- admission = admitted;
501
- alias = admitted.alias;
502
- if (!aliasPattern(adapter).test(alias))
503
- throw failure(adapter.family, "alias-invalid", 400, `Alias '${alias}' must match ${aliasPattern(adapter)}.`, { alias, retryable: false });
504
- const current = effective.get(alias);
505
- if (verb === "add" && current?.origin === local && !isDeepStrictEqual(current.definition, admitted.definition)) {
506
- throw failure(adapter.family, "alias-exists", 409, `'${alias}' already has a different ${local} definition.`, { alias, recovery: "Use replace with the complete definition to change it.", retryable: false });
530
+ admissions = Array.isArray(admitted) ? [...admitted] : [admitted];
531
+ // {§functionality-addressing} — an alias family admits exactly one definition; an addressing
532
+ // family's add admits each the body names, and all of them or none.
533
+ if (adapter.addressing === undefined ? admissions.length !== 1 : admissions.length === 0) {
534
+ throw new Error(`${adapter.family} admitted ${admissions.length} definitions for one ${verb}.`);
535
+ }
536
+ for (const admission of admissions) {
537
+ const { alias } = admission;
538
+ if (!aliasPattern(adapter).test(alias))
539
+ throw failure(adapter.family, "alias-invalid", 400, `Alias '${alias}' must match ${aliasPattern(adapter)}.`, { alias, retryable: false });
540
+ if (aliases.includes(alias))
541
+ throw new Error(`${adapter.family} admitted '${alias}' twice in one ${verb}.`);
542
+ const current = effective.get(alias);
543
+ if (verb === "add" && current?.origin === local && !isDeepStrictEqual(current.definition, admission.definition)) {
544
+ throw failure(adapter.family, "alias-exists", 409, `'${alias}' already has a different ${local} definition.`, {
545
+ alias,
546
+ recovery: adapter.addressing === undefined ? "Use replace with the complete definition to change it." : "Remove it, then add it again.",
547
+ retryable: false,
548
+ });
549
+ }
550
+ definitions[alias] = { origin: local, definition: admission.definition, enabled: verb === "replace" ? current?.enabled ?? true : true };
551
+ if (current?.origin !== local)
552
+ status = 201;
553
+ aliases.push(alias);
507
554
  }
508
- definitions[alias] = { origin: local, definition: admitted.definition, enabled: verb === "replace" ? current?.enabled ?? true : true };
509
- status = current?.origin === local ? 200 : 201;
510
555
  break;
511
556
  }
512
557
  case "enable":
513
558
  case "disable": {
514
- alias = input.alias;
559
+ const alias = Functionality.#selected(adapter, input);
515
560
  const current = effective.get(alias);
516
561
  if (current === undefined)
517
562
  throw failure(adapter.family, "alias-unknown", 404, `'${alias}' is not available to ${here}.`, { alias, retryable: false });
518
563
  definitions[alias] = current.origin === local
519
564
  ? { origin: local, definition: current.definition, enabled: verb === "enable" }
520
565
  : { origin: current.origin, enabled: verb === "enable" };
566
+ aliases.push(alias);
521
567
  break;
522
568
  }
523
569
  case "remove": {
524
- alias = input.alias;
570
+ const alias = Functionality.#selected(adapter, input);
525
571
  const current = effective.get(alias);
526
572
  if (current === undefined)
527
573
  throw failure(adapter.family, "alias-unknown", 404, `'${alias}' is not available to ${here}.`, { alias, retryable: false });
@@ -532,25 +578,34 @@ class Functionality {
532
578
  await adapter.forget?.({ alias, definition: current.definition }, identity);
533
579
  delete definitions[alias];
534
580
  removed = true;
581
+ aliases.push(alias);
535
582
  break;
536
583
  }
537
584
  default: throw new Error(`unreachable verb ${verb}`);
538
585
  }
539
- return { definitions, alias, status, removed, ...(admission === undefined ? {} : { admission }) };
586
+ return { definitions, aliases, status, removed, admissions };
540
587
  }
541
- #mutationResult(adapter, { alias, status, removed }, effectiveAfter, outcomes) {
542
- const definition = effectiveAfter.get(alias);
543
- const projection = definition === undefined ? undefined : this.#projection(definition, outcomes.get(alias));
544
- const body = Validator.assertFunctionalityMutationResult({
545
- status: projection?.state === "authorization-required" ? 202 : status,
546
- family: adapter.family,
547
- alias,
548
- ...(projection === undefined ? {} : { definition: projection }),
549
- ...(removed ? { removed: true } : {}),
588
+ // {§functionality-mutation-status} — the receipt: the one alias a mutation concerned, or every
589
+ // definition an addressing family's add admitted ({§functionality-addressing}).
590
+ #mutationResult(adapter, verb, { aliases, status, removed }, effectiveAfter, outcomes) {
591
+ const projections = aliases.map((alias) => {
592
+ const definition = effectiveAfter.get(alias);
593
+ return definition === undefined ? undefined : this.#projection(definition, outcomes.get(alias));
550
594
  });
595
+ const authorization = projections.some((projection) => projection?.state === "authorization-required");
596
+ const body = Validator.assertFunctionalityMutationResult(adapter.addressing !== undefined && verb === "add"
597
+ ? { status: authorization ? 202 : status, family: adapter.family, definitions: projections.map((projection) => projection) }
598
+ : {
599
+ status: authorization ? 202 : status,
600
+ family: adapter.family,
601
+ alias: aliases[0],
602
+ ...(projections[0] === undefined ? {} : { definition: projections[0] }),
603
+ ...(removed ? { removed: true } : {}),
604
+ });
551
605
  return { status: body.status, body };
552
606
  }
553
- async #mutate(adapter, verb, input, identity, caller, options) {
607
+ async #mutate(adapter, verb, input, identity, caller, options, signal) {
608
+ signal.throwIfAborted();
554
609
  const key = this.#key(identity.workspaceId, adapter.family);
555
610
  const family = this.#families.get(key);
556
611
  if (family === undefined)
@@ -560,25 +615,33 @@ class Functionality {
560
615
  const nextState = { version: STATE_VERSION, definitions: transition.definitions };
561
616
  // Re-enabling an alias that is not active retries its preparation; an
562
617
  // already-active alias keeps its live attachment.
563
- const retry = verb === "enable" && family.prepared?.outcomes.get(transition.alias)?.state !== "active";
618
+ const retry = verb === "enable" && family.prepared?.outcomes.get(transition.aliases[0])?.state !== "active";
564
619
  const publication = await this.#publish(adapter, identity, nextState, {
565
620
  failure: caller === "action" || verb === "replace" ? "reject" : "publish-unavailable",
566
621
  configurationFailure: "reject",
567
622
  retain: () => this.#host.retainWorkspace(identity.workspaceId),
568
- forceAlias: retry ? transition.alias : null,
569
- admission: transition.admission,
623
+ forceAlias: retry ? transition.aliases[0] : null,
624
+ admissions: transition.admissions,
625
+ signal,
570
626
  });
571
627
  const effectiveAfter = await this.#effective(adapter, identity, nextState);
572
- return this.#mutationResult(adapter, transition, effectiveAfter, publication.outcomes);
628
+ return this.#mutationResult(adapter, verb, transition, effectiveAfter, publication.outcomes);
573
629
  }
574
630
  // {§functionality-scope} — the Worker's own lane. State is read at each verb rather than held: the
575
631
  // spawn reads the same row, and a Worker's row is written at its creation, so nothing in memory
576
632
  // could be more current than the table.
577
- async #invokeForWorker(adapter, verb, input, identity, workerId, caller, options) {
633
+ async #invokeForWorker(adapter, verb, input, identity, workerId, caller, options, signal) {
634
+ signal.throwIfAborted();
578
635
  const state = await this.#loadWorkerState(adapter, workerId);
579
636
  const effective = await this.#effective(adapter, identity, state);
580
637
  if (verb === "list") {
581
- const prepared = await this.#prepareForWorker(adapter, identity, effective, "publish-unavailable");
638
+ const prepared = await this.#prepareForWorker(adapter, identity, effective, "publish-unavailable", [], signal);
639
+ try {
640
+ signal.throwIfAborted();
641
+ }
642
+ catch (cause) {
643
+ return Functionality.#abort(prepared, cause);
644
+ }
582
645
  await prepared.commit();
583
646
  return { status: 200, body: Validator.assertFunctionalityListResult({
584
647
  family: adapter.family,
@@ -588,35 +651,35 @@ class Functionality {
588
651
  const transition = await this.#transition(adapter, verb, input, identity, caller, state, effective, options);
589
652
  const nextState = { version: STATE_VERSION, definitions: transition.definitions };
590
653
  const effectiveAfter = await this.#effective(adapter, identity, nextState);
591
- const prepared = await this.#prepareForWorker(adapter, identity, effectiveAfter, caller === "action" || verb === "replace" ? "reject" : "publish-unavailable", transition.admission);
654
+ const prepared = await this.#prepareForWorker(adapter, identity, effectiveAfter, caller === "action" || verb === "replace" ? "reject" : "publish-unavailable", transition.admissions, signal);
592
655
  try {
593
- await this.#host.replaceWorkerModuleState(workerId, adapter.namespaceOwner, _a.#persisted(nextState));
656
+ signal.throwIfAborted();
657
+ await this.#host.replaceWorkerModuleState(workerId, adapter.namespaceOwner, Functionality.#persisted(nextState));
594
658
  }
595
659
  catch (cause) {
596
- return _a.#abort(prepared, cause);
660
+ return Functionality.#abort(prepared, cause);
597
661
  }
598
662
  await prepared.commit();
599
- return this.#mutationResult(adapter, transition, effectiveAfter, prepared.outcomes);
663
+ return this.#mutationResult(adapter, verb, transition, effectiveAfter, prepared.outcomes);
600
664
  }
601
665
  // A worker-scoped family's preparation yields outcomes only. It holds no residency — no runtimes,
602
666
  // documents or snapshot ({§module-workspace-residency}) — because its definitions are read at the
603
667
  // spawn that uses them rather than leased and cooled; the coordinator enforces that here.
604
- async #prepareForWorker(adapter, identity, effective, failureMode, admission) {
605
- const enabled = _a.#enabled(effective);
606
- const prepared = await adapter.prepare({
607
- workspaceId: identity.workspaceId, enabled, previous: null, failure: failureMode,
608
- ...(admission === undefined ? {} : { admission }),
668
+ async #prepareForWorker(adapter, identity, effective, failureMode, admissions = [], signal = this.#host.signal) {
669
+ const enabled = Functionality.#enabled(effective);
670
+ const prepared = await this.#prepare(adapter, {
671
+ workspaceId: identity.workspaceId, enabled, previous: null, failure: failureMode, admissions,
609
672
  progress: () => undefined,
610
673
  retain: () => this.#host.retainWorkspace(identity.workspaceId),
611
- });
674
+ }, signal);
612
675
  try {
613
- _a.#checkOutcomes(adapter, enabled, prepared);
676
+ Functionality.#checkOutcomes(adapter, enabled, prepared);
614
677
  if ((prepared.runtimes?.length ?? 0) > 0 || prepared.documents.length > 0 || prepared.snapshot !== null) {
615
678
  throw new Error(`${adapter.family} is worker-scoped and holds no residency, yet its preparation published runtimes, documents or a snapshot.`);
616
679
  }
617
680
  }
618
681
  catch (cause) {
619
- return _a.#abort(prepared, cause);
682
+ return Functionality.#abort(prepared, cause);
620
683
  }
621
684
  return prepared;
622
685
  }
@@ -626,7 +689,7 @@ class Functionality {
626
689
  async #publishable(adapter, identity, state) {
627
690
  if (!scopesOf(adapter).includes("workspace"))
628
691
  return new Map();
629
- return _a.#enabled(await this.#effective(adapter, { ...identity, scope: "workspace" }, state));
692
+ return Functionality.#enabled(await this.#effective(adapter, { ...identity, scope: "workspace" }, state));
630
693
  }
631
694
  static #sameDefinitions(left, right) {
632
695
  if (left.size !== right.size)
@@ -644,7 +707,7 @@ class Functionality {
644
707
  const enabled = new Map();
645
708
  for (const definition of effective.values()) {
646
709
  if (definition.enabled)
647
- enabled.set(definition.alias, structuredClone(_a.#binding(definition)));
710
+ enabled.set(definition.alias, structuredClone(Functionality.#binding(definition)));
648
711
  }
649
712
  return enabled;
650
713
  }
@@ -662,19 +725,36 @@ class Functionality {
662
725
  return [...(this.#preparations.get(workspaceId)?.values() ?? [])].map((activity) => ({ ...activity }));
663
726
  }
664
727
  async #publish(adapter, identity, nextState, options) {
728
+ (options.signal ?? this.#host.signal).throwIfAborted();
665
729
  const { workspaceId } = identity;
666
730
  const current = this.#preparations.get(workspaceId) ?? new Map();
667
731
  this.#preparations.set(workspaceId, current);
668
- const report = (alias, phase) => {
669
- current.set(adapter.family, { family: adapter.family, alias, phase, since: new Date().toISOString() });
732
+ const forget = () => {
733
+ for (const [key, activity] of current)
734
+ if (activity.family === adapter.family)
735
+ current.delete(key);
736
+ };
737
+ // {§functionality-preparation-visibility} — one activity for the family, or one per alias it reports
738
+ // preparing, each keeping the time its alias began.
739
+ const report = (aliases, phase) => {
740
+ const now = new Date().toISOString();
741
+ const began = new Map([...current.values()]
742
+ .filter((activity) => activity.family === adapter.family && activity.alias !== null)
743
+ .map((activity) => [activity.alias, activity.since]));
744
+ forget();
745
+ if (aliases.length === 0)
746
+ current.set(adapter.family, { family: adapter.family, alias: null, phase, since: now });
747
+ for (const alias of aliases) {
748
+ current.set(`${adapter.family}/${alias}`, { family: adapter.family, alias, phase, since: began.get(alias) ?? now });
749
+ }
670
750
  this.#host.preparationChanged(workspaceId, this.preparationStatus(workspaceId));
671
751
  };
672
- report(null, "preparing");
752
+ report([], "preparing");
673
753
  try {
674
754
  return await this.#publishPrepared(adapter, identity, nextState, options, report);
675
755
  }
676
756
  finally {
677
- current.delete(adapter.family);
757
+ forget();
678
758
  if (current.size === 0)
679
759
  this.#preparations.delete(workspaceId);
680
760
  this.#host.preparationChanged(workspaceId, this.preparationStatus(workspaceId));
@@ -682,6 +762,7 @@ class Functionality {
682
762
  }
683
763
  async #publishPrepared(adapter, identity, nextState, options, report) {
684
764
  const key = this.#key(identity.workspaceId, adapter.family);
765
+ const signal = options.signal ?? this.#host.signal;
685
766
  const previous = this.#families.get(key)?.prepared ?? null;
686
767
  let enabled = new Map();
687
768
  let prepared = null;
@@ -690,20 +771,23 @@ class Functionality {
690
771
  try {
691
772
  enabled = await this.#publishable(adapter, identity, nextState);
692
773
  phase = "preparation";
693
- prepared = await adapter.prepare({
774
+ prepared = await this.#prepare(adapter, {
694
775
  workspaceId: identity.workspaceId,
695
776
  enabled,
696
777
  previous: previous?.snapshot ?? null,
697
778
  failure: options.failure,
698
- ...(options.admission === undefined ? {} : { admission: options.admission }),
699
- progress: (alias) => {
700
- if (!enabled.has(alias))
701
- throw new Error(`${adapter.family} reported preparation of unknown alias '${alias}'.`);
702
- report(alias, "preparing");
779
+ admissions: options.admissions ?? [],
780
+ progress: (aliases) => {
781
+ signal.throwIfAborted();
782
+ for (const alias of aliases) {
783
+ if (!enabled.has(alias))
784
+ throw new Error(`${adapter.family} reported preparation of unknown alias '${alias}'.`);
785
+ }
786
+ report(aliases, "preparing");
703
787
  },
704
788
  retain: options.retain,
705
789
  ...(options.forceAlias ? { force: options.forceAlias } : {}),
706
- });
790
+ }, signal);
707
791
  }
708
792
  catch (cause) {
709
793
  if (!(cause instanceof ConfigurationError) || options.configurationFailure === "reject")
@@ -711,11 +795,12 @@ class Functionality {
711
795
  configurationFailure = { phase, error: cause };
712
796
  enabled = new Map();
713
797
  }
714
- report(null, "publishing");
715
798
  let runtimes;
716
799
  try {
800
+ signal.throwIfAborted();
801
+ report([], "publishing");
717
802
  if (prepared !== null)
718
- _a.#checkOutcomes(adapter, enabled, prepared);
803
+ Functionality.#checkOutcomes(adapter, enabled, prepared);
719
804
  for (const runtime of prepared?.runtimes ?? []) {
720
805
  if (runtime.namespaceOwner !== adapter.namespaceOwner) {
721
806
  throw new Error(`${adapter.family} prepared a runtime owned by '${runtime.namespaceOwner}' instead of '${adapter.namespaceOwner}'.`);
@@ -736,15 +821,16 @@ class Functionality {
736
821
  runtimes = [manager, ...(prepared?.runtimes ?? [])];
737
822
  }
738
823
  catch (cause) {
739
- return _a.#abort(prepared, cause);
824
+ return Functionality.#abort(prepared, cause);
740
825
  }
741
826
  const commit = async () => {
742
827
  const before = this.#families.get(key);
743
828
  try {
829
+ signal.throwIfAborted();
744
830
  await this.#host.replaceWorkspaceCapabilities({
745
831
  workspaceId: identity.workspaceId,
746
832
  namespaceOwner: adapter.namespaceOwner,
747
- state: _a.#persisted(nextState),
833
+ state: Functionality.#persisted(nextState),
748
834
  runtimes,
749
835
  }, {
750
836
  gate: "none",
@@ -760,7 +846,7 @@ class Functionality {
760
846
  });
761
847
  }
762
848
  catch (cause) {
763
- return _a.#abort(prepared, cause);
849
+ return Functionality.#abort(prepared, cause);
764
850
  }
765
851
  if (prepared !== null)
766
852
  await prepared.commit();
@@ -770,6 +856,18 @@ class Functionality {
770
856
  await commit();
771
857
  return { outcomes: prepared?.outcomes ?? new Map() };
772
858
  }
859
+ // {§functionality-preparation-cancellation} Join the adapter; abort a returned candidate before publication.
860
+ async #prepare(adapter, preparation, signal = this.#host.signal) {
861
+ signal.throwIfAborted();
862
+ const prepared = await adapter.prepare({ ...preparation, signal });
863
+ try {
864
+ signal.throwIfAborted();
865
+ }
866
+ catch (cause) {
867
+ return Functionality.#abort(prepared, cause);
868
+ }
869
+ return prepared;
870
+ }
773
871
  static async #abort(prepared, cause) {
774
872
  try {
775
873
  await prepared?.abort();
@@ -780,6 +878,4 @@ class Functionality {
780
878
  throw cause;
781
879
  }
782
880
  }
783
- _a = Functionality;
784
- export default Functionality;
785
881
  //# sourceMappingURL=Functionality.js.map