@real-life/trust-protocol 0.3.2 → 0.5.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 (277) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +209 -78
  3. package/dist/ack.d.ts +84 -0
  4. package/dist/ack.d.ts.map +1 -0
  5. package/dist/ack.js +114 -0
  6. package/dist/ack.js.map +1 -0
  7. package/dist/adapters/vti-mediator.d.ts +169 -0
  8. package/dist/adapters/vti-mediator.d.ts.map +1 -0
  9. package/dist/adapters/vti-mediator.js +659 -0
  10. package/dist/adapters/vti-mediator.js.map +1 -0
  11. package/dist/anchor.d.ts +226 -0
  12. package/dist/anchor.d.ts.map +1 -0
  13. package/dist/anchor.js +347 -0
  14. package/dist/anchor.js.map +1 -0
  15. package/dist/{holder.d.ts → carrier-holder.d.ts} +1 -1
  16. package/dist/carrier-holder.d.ts.map +1 -0
  17. package/dist/{holder.js → carrier-holder.js} +5 -4
  18. package/dist/carrier-holder.js.map +1 -0
  19. package/dist/carrier-identity.js +4 -4
  20. package/dist/carrier-identity.js.map +1 -1
  21. package/dist/carrier.d.ts.map +1 -1
  22. package/dist/carrier.js +4 -3
  23. package/dist/carrier.js.map +1 -1
  24. package/dist/ceremony.d.ts +3 -2
  25. package/dist/ceremony.d.ts.map +1 -1
  26. package/dist/ceremony.js +16 -26
  27. package/dist/ceremony.js.map +1 -1
  28. package/dist/codec.d.ts +54 -0
  29. package/dist/codec.d.ts.map +1 -0
  30. package/dist/codec.js +494 -0
  31. package/dist/codec.js.map +1 -0
  32. package/dist/continuity.d.ts +67 -0
  33. package/dist/continuity.d.ts.map +1 -0
  34. package/dist/continuity.js +110 -0
  35. package/dist/continuity.js.map +1 -0
  36. package/dist/core.d.ts +4 -0
  37. package/dist/core.d.ts.map +1 -1
  38. package/dist/core.js.map +1 -1
  39. package/dist/crypto.d.ts +23 -7
  40. package/dist/crypto.d.ts.map +1 -1
  41. package/dist/crypto.js +54 -20
  42. package/dist/crypto.js.map +1 -1
  43. package/dist/delivery.d.ts +31 -3
  44. package/dist/delivery.d.ts.map +1 -1
  45. package/dist/delivery.js +75 -28
  46. package/dist/delivery.js.map +1 -1
  47. package/dist/encounter/ceremony.d.ts +37 -0
  48. package/dist/encounter/ceremony.d.ts.map +1 -0
  49. package/dist/encounter/ceremony.js +662 -0
  50. package/dist/encounter/ceremony.js.map +1 -0
  51. package/dist/encounter.d.ts +91 -1
  52. package/dist/encounter.d.ts.map +1 -1
  53. package/dist/encounter.js +191 -12
  54. package/dist/encounter.js.map +1 -1
  55. package/dist/engine.d.ts +303 -0
  56. package/dist/engine.d.ts.map +1 -0
  57. package/dist/engine.js +1509 -0
  58. package/dist/engine.js.map +1 -0
  59. package/dist/entropy.d.ts +28 -0
  60. package/dist/entropy.d.ts.map +1 -0
  61. package/dist/entropy.js +96 -0
  62. package/dist/entropy.js.map +1 -0
  63. package/dist/events.d.ts +198 -0
  64. package/dist/events.d.ts.map +1 -0
  65. package/dist/events.js +2 -0
  66. package/dist/events.js.map +1 -0
  67. package/dist/fixtures.d.ts +30 -0
  68. package/dist/fixtures.d.ts.map +1 -0
  69. package/dist/fixtures.js +155 -0
  70. package/dist/fixtures.js.map +1 -0
  71. package/dist/forge.d.ts +47 -0
  72. package/dist/forge.d.ts.map +1 -0
  73. package/dist/forge.js +20 -0
  74. package/dist/forge.js.map +1 -0
  75. package/dist/group-star.d.ts +251 -0
  76. package/dist/group-star.d.ts.map +1 -0
  77. package/dist/group-star.js +348 -0
  78. package/dist/group-star.js.map +1 -0
  79. package/dist/holder-api.d.ts +165 -0
  80. package/dist/holder-api.d.ts.map +1 -0
  81. package/dist/holder-api.js +142 -0
  82. package/dist/holder-api.js.map +1 -0
  83. package/dist/holder-state-v1.d.ts +801 -0
  84. package/dist/holder-state-v1.d.ts.map +1 -0
  85. package/dist/holder-state-v1.js +2 -0
  86. package/dist/holder-state-v1.js.map +1 -0
  87. package/dist/holder-state-v2.d.ts +858 -0
  88. package/dist/holder-state-v2.d.ts.map +1 -0
  89. package/dist/holder-state-v2.js +2 -0
  90. package/dist/holder-state-v2.js.map +1 -0
  91. package/dist/identity-state.d.ts +43 -0
  92. package/dist/identity-state.d.ts.map +1 -0
  93. package/dist/identity-state.js +345 -0
  94. package/dist/identity-state.js.map +1 -0
  95. package/dist/identity.d.ts +24 -2
  96. package/dist/identity.d.ts.map +1 -1
  97. package/dist/identity.js +70 -11
  98. package/dist/identity.js.map +1 -1
  99. package/dist/index.d.ts +27 -1
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +81 -29
  102. package/dist/index.js.map +1 -1
  103. package/dist/internal.d.ts +37 -0
  104. package/dist/internal.d.ts.map +1 -0
  105. package/dist/internal.js +22 -0
  106. package/dist/internal.js.map +1 -0
  107. package/dist/introduction/holder.d.ts +18 -0
  108. package/dist/introduction/holder.d.ts.map +1 -0
  109. package/dist/introduction/holder.js +764 -0
  110. package/dist/introduction/holder.js.map +1 -0
  111. package/dist/introduction.d.ts +101 -0
  112. package/dist/introduction.d.ts.map +1 -0
  113. package/dist/introduction.js +132 -0
  114. package/dist/introduction.js.map +1 -0
  115. package/dist/invariants.d.ts +43 -0
  116. package/dist/invariants.d.ts.map +1 -0
  117. package/dist/invariants.js +953 -0
  118. package/dist/invariants.js.map +1 -0
  119. package/dist/keys-seed.d.ts +7 -0
  120. package/dist/keys-seed.d.ts.map +1 -0
  121. package/dist/keys-seed.js +133 -0
  122. package/dist/keys-seed.js.map +1 -0
  123. package/dist/log-memory.d.ts +12 -0
  124. package/dist/log-memory.d.ts.map +1 -0
  125. package/dist/log-memory.js +101 -0
  126. package/dist/log-memory.js.map +1 -0
  127. package/dist/membership/holder.d.ts +33 -0
  128. package/dist/membership/holder.d.ts.map +1 -0
  129. package/dist/membership/holder.js +929 -0
  130. package/dist/membership/holder.js.map +1 -0
  131. package/dist/membership.d.ts +123 -0
  132. package/dist/membership.d.ts.map +1 -0
  133. package/dist/membership.js +180 -0
  134. package/dist/membership.js.map +1 -0
  135. package/dist/message/holder.d.ts +49 -0
  136. package/dist/message/holder.d.ts.map +1 -0
  137. package/dist/message/holder.js +290 -0
  138. package/dist/message/holder.js.map +1 -0
  139. package/dist/message.d.ts +43 -0
  140. package/dist/message.d.ts.map +1 -0
  141. package/dist/message.js +51 -0
  142. package/dist/message.js.map +1 -0
  143. package/dist/ports.d.ts +237 -0
  144. package/dist/ports.d.ts.map +1 -0
  145. package/dist/ports.js +2 -0
  146. package/dist/ports.js.map +1 -0
  147. package/dist/profile.d.ts +56 -0
  148. package/dist/profile.d.ts.map +1 -0
  149. package/dist/profile.js +193 -0
  150. package/dist/profile.js.map +1 -0
  151. package/dist/prompts.d.ts +14 -0
  152. package/dist/prompts.d.ts.map +1 -0
  153. package/dist/prompts.js +69 -0
  154. package/dist/prompts.js.map +1 -0
  155. package/dist/receive.d.ts +134 -0
  156. package/dist/receive.d.ts.map +1 -0
  157. package/dist/receive.js +317 -0
  158. package/dist/receive.js.map +1 -0
  159. package/dist/results.d.ts +173 -0
  160. package/dist/results.d.ts.map +1 -0
  161. package/dist/results.js +21 -0
  162. package/dist/results.js.map +1 -0
  163. package/dist/schemas.d.ts.map +1 -1
  164. package/dist/schemas.js +45 -38
  165. package/dist/schemas.js.map +1 -1
  166. package/dist/sha256.d.ts +3 -0
  167. package/dist/sha256.d.ts.map +1 -0
  168. package/dist/sha256.js +69 -0
  169. package/dist/sha256.js.map +1 -0
  170. package/dist/star.d.ts +31 -0
  171. package/dist/star.d.ts.map +1 -0
  172. package/dist/star.js +56 -0
  173. package/dist/star.js.map +1 -0
  174. package/dist/state-builder.d.ts +42 -0
  175. package/dist/state-builder.d.ts.map +1 -0
  176. package/dist/state-builder.js +100 -0
  177. package/dist/state-builder.js.map +1 -0
  178. package/dist/state.d.ts +914 -0
  179. package/dist/state.d.ts.map +1 -0
  180. package/dist/state.js +37 -0
  181. package/dist/state.js.map +1 -0
  182. package/dist/store-contract.d.ts +11 -0
  183. package/dist/store-contract.d.ts.map +1 -0
  184. package/dist/store-contract.js +189 -0
  185. package/dist/store-contract.js.map +1 -0
  186. package/dist/store-memory.d.ts +8 -0
  187. package/dist/store-memory.d.ts.map +1 -0
  188. package/dist/store-memory.js +17 -0
  189. package/dist/store-memory.js.map +1 -0
  190. package/dist/testing.d.ts +22 -0
  191. package/dist/testing.d.ts.map +1 -0
  192. package/dist/testing.js +21 -0
  193. package/dist/testing.js.map +1 -0
  194. package/dist/trust/continuity.d.ts +71 -0
  195. package/dist/trust/continuity.d.ts.map +1 -0
  196. package/dist/trust/continuity.js +1222 -0
  197. package/dist/trust/continuity.js.map +1 -0
  198. package/dist/trust/duties.d.ts +67 -0
  199. package/dist/trust/duties.d.ts.map +1 -0
  200. package/dist/trust/duties.js +179 -0
  201. package/dist/trust/duties.js.map +1 -0
  202. package/dist/trust/entries.d.ts +139 -0
  203. package/dist/trust/entries.d.ts.map +1 -0
  204. package/dist/trust/entries.js +795 -0
  205. package/dist/trust/entries.js.map +1 -0
  206. package/dist/trust/group-star.d.ts +43 -0
  207. package/dist/trust/group-star.d.ts.map +1 -0
  208. package/dist/trust/group-star.js +188 -0
  209. package/dist/trust/group-star.js.map +1 -0
  210. package/dist/trust/mapping.d.ts +56 -0
  211. package/dist/trust/mapping.d.ts.map +1 -0
  212. package/dist/trust/mapping.js +584 -0
  213. package/dist/trust/mapping.js.map +1 -0
  214. package/dist/trust/profile.d.ts +25 -0
  215. package/dist/trust/profile.d.ts.map +1 -0
  216. package/dist/trust/profile.js +249 -0
  217. package/dist/trust/profile.js.map +1 -0
  218. package/dist/trust/star.d.ts +208 -0
  219. package/dist/trust/star.d.ts.map +1 -0
  220. package/dist/trust/star.js +588 -0
  221. package/dist/trust/star.js.map +1 -0
  222. package/dist/trust/state.d.ts +83 -0
  223. package/dist/trust/state.d.ts.map +1 -0
  224. package/dist/trust/state.js +154 -0
  225. package/dist/trust/state.js.map +1 -0
  226. package/dist/util-freeze.d.ts +3 -0
  227. package/dist/util-freeze.d.ts.map +1 -0
  228. package/dist/util-freeze.js +11 -0
  229. package/dist/util-freeze.js.map +1 -0
  230. package/dist/views.d.ts +179 -0
  231. package/dist/views.d.ts.map +1 -0
  232. package/dist/views.js +286 -0
  233. package/dist/views.js.map +1 -0
  234. package/dist/visibility.d.ts +5 -5
  235. package/dist/visibility.d.ts.map +1 -1
  236. package/dist/visibility.js +22 -28
  237. package/dist/visibility.js.map +1 -1
  238. package/dist/wire.d.ts +218 -83
  239. package/dist/wire.d.ts.map +1 -1
  240. package/dist/wire.js.map +1 -1
  241. package/package.json +60 -10
  242. package/schemas/holder-state-v1.schema.json +3880 -0
  243. package/schemas/holder-state-v2.schema.json +4123 -0
  244. package/dist/holder.d.ts.map +0 -1
  245. package/dist/holder.js.map +0 -1
  246. package/dist/probe/acks.d.ts +0 -27
  247. package/dist/probe/acks.d.ts.map +0 -1
  248. package/dist/probe/acks.js +0 -152
  249. package/dist/probe/acks.js.map +0 -1
  250. package/dist/probe/continuity.d.ts +0 -20
  251. package/dist/probe/continuity.d.ts.map +0 -1
  252. package/dist/probe/continuity.js +0 -805
  253. package/dist/probe/continuity.js.map +0 -1
  254. package/dist/probe/deps.d.ts +0 -61
  255. package/dist/probe/deps.d.ts.map +0 -1
  256. package/dist/probe/deps.js +0 -54
  257. package/dist/probe/deps.js.map +0 -1
  258. package/dist/probe/encounter.d.ts +0 -26
  259. package/dist/probe/encounter.d.ts.map +0 -1
  260. package/dist/probe/encounter.js +0 -938
  261. package/dist/probe/encounter.js.map +0 -1
  262. package/dist/probe/introduce.d.ts +0 -62
  263. package/dist/probe/introduce.d.ts.map +0 -1
  264. package/dist/probe/introduce.js +0 -306
  265. package/dist/probe/introduce.js.map +0 -1
  266. package/dist/probe/membership.d.ts +0 -96
  267. package/dist/probe/membership.d.ts.map +0 -1
  268. package/dist/probe/membership.js +0 -467
  269. package/dist/probe/membership.js.map +0 -1
  270. package/dist/probe/trust.d.ts +0 -156
  271. package/dist/probe/trust.d.ts.map +0 -1
  272. package/dist/probe/trust.js +0 -1233
  273. package/dist/probe/trust.js.map +0 -1
  274. package/dist/probe.d.ts +0 -7
  275. package/dist/probe.d.ts.map +0 -1
  276. package/dist/probe.js +0 -42
  277. package/dist/probe.js.map +0 -1
@@ -0,0 +1,69 @@
1
+ import { handlerPrepared } from './receive.js';
2
+ /** The command name of the decision. */
3
+ export const DECIDE = 'prompt.decide';
4
+ const KINDS = ['encounter-confirm', 'group-invite', 'introduction'];
5
+ /** The decision handlers of the modules, one per kind at most. */
6
+ export function collectDecisions(modules) {
7
+ const out = new Map();
8
+ for (const m of modules) {
9
+ for (const [kind, h] of Object.entries(m?.decisions ?? {})) {
10
+ if (!KINDS.includes(kind))
11
+ throw new Error(`unknown prompt kind ${kind} (module ${m.name})`);
12
+ if (out.has(kind))
13
+ throw new Error(`duplicate decision handler ${kind} (module ${m.name})`);
14
+ if (typeof h !== 'function' && (h === null || typeof h !== 'object' || typeof h.apply !== 'function' || (h.prepare !== undefined && typeof h.prepare !== 'function'))) {
15
+ throw new TypeError(`decision handler ${kind} (module ${m.name}) must be a function or {prepare?, apply}`);
16
+ }
17
+ out.set(kind, h);
18
+ }
19
+ }
20
+ return out;
21
+ }
22
+ const validInput = (input) => input !== null && typeof input === 'object' && typeof input.promptId === 'string' && (input.choice === 'accept' || input.choice === 'decline');
23
+ const decideDef = (decisions) => ({
24
+ human: true,
25
+ // a prepared handler's preparation, against the snapshot apply sees;
26
+ // nothing for an unknown, expired or malformed decision (apply answers it)
27
+ async prepare(ctx, input) {
28
+ if (!validInput(input) || !Object.hasOwn(ctx.snapshot.prompts, input.promptId))
29
+ return undefined;
30
+ const p = ctx.snapshot.prompts[input.promptId];
31
+ if (p.expiresAt !== undefined && p.expiresAt <= ctx.when)
32
+ return undefined;
33
+ const h = decisions.get(p.kind);
34
+ return h && typeof h !== 'function' && h.prepare ? h.prepare(ctx, p, input.choice) : undefined;
35
+ },
36
+ apply(state, prepared, input, when) {
37
+ if (input === null || typeof input !== 'object' || typeof input.promptId !== 'string')
38
+ throw new TypeError('prompt.decide needs {promptId: string, choice}');
39
+ if (input.choice !== 'accept' && input.choice !== 'decline')
40
+ throw new TypeError(`prompt.decide: choice must be accept or decline, got ${String(input.choice)}`);
41
+ const { promptId, choice } = input;
42
+ if (!Object.hasOwn(state.prompts, promptId))
43
+ return { ok: false, code: 'unknown-prompt' };
44
+ const p = state.prompts[promptId];
45
+ if (p.expiresAt !== undefined && p.expiresAt <= when)
46
+ return { ok: false, code: 'prompt-expired' };
47
+ const handler = decisions.get(p.kind);
48
+ const r = !handler ? { state: state }
49
+ : typeof handler === 'function' ? handler(state, p, choice, when)
50
+ : handler.apply(state, p, choice, when, handlerPrepared(prepared, prepared.value));
51
+ if (r.ok === false)
52
+ return r;
53
+ const done = r;
54
+ const prompts = { ...done.state.prompts };
55
+ delete prompts[promptId];
56
+ const decided = { type: 'prompt.decided', promptId, kind: p.kind, choice };
57
+ return {
58
+ state: { ...done.state, prompts },
59
+ ...(done.outbound ? { outbound: done.outbound } : {}),
60
+ ...(done.duties ? { duties: done.duties } : {}),
61
+ events: [...(done.events ?? []), decided],
62
+ };
63
+ },
64
+ });
65
+ /** The prompts module createHolder registers, over the modules' decision handlers. */
66
+ export function promptsModule(decisions = new Map()) {
67
+ return Object.freeze({ name: 'prompts', commands: Object.freeze({ [DECIDE]: decideDef(decisions) }) });
68
+ }
69
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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","sourcesContent":["// prompts — the human's open decisions and the act that closes them\n// (lib Stage 2, note §2.1 prompts, §4.4, §4.5).\n//\n// Domain modules open prompts (encounter-confirm, group-invite,\n// introduction) inside their own transitions and announce prompt.opened;\n// this module closes them. `prompt.decide` is one human act (journaled\n// under the app's actId). createHolder registers this module beside the\n// domain modules, so its command name is taken.\n//\n// What a choice does in its domain (joining the group, confirming the\n// encounter, accepting the introduction) belongs to the module that opens\n// that kind of prompt: it registers a DecisionHandler for the kind\n// (HolderModule.decisions). The decision runs that handler in its own\n// transition, so the domain effect, its outbound and duties, and the\n// closing of the prompt commit together or not at all; a handler's\n// refusal refuses the decision. A kind without a handler is only closed.\n// The events are the handler's, then prompt.decided. At most one handler\n// per kind across all modules; a second, an unknown kind or a handler\n// that is no function throws when the holder is built.\n//\n// Expiry: a prompt with expiresAt <= when is refused as prompt-expired;\n// the refusal commits nothing, so the prompt stays until tick removes it\n// (engine.ts, built-in tick). An id without an open prompt is\n// unknown-prompt. A malformed input (a promptId that is no string, a\n// choice other than accept or decline) is a programming error and throws.\n//\n// A handler that signs or seals (PR 7: the counter-step, the membership\n// accept, the introduction's consent) is the prepared form {prepare?,\n// apply}: its preparation runs in the decision's own preparation, against\n// the snapshot its apply then sees (engine.ts, step 2).\nimport type { Applied, CommandDef, DecisionDef, DecisionHandler, EventDraft, HolderModule, Refusal } from './engine.js'\nimport { handlerPrepared } from './receive.js'\nimport type { HolderState, PromptChoice, PromptKind } from './state.js'\n\n/** The command name of the decision. */\nexport const DECIDE = 'prompt.decide'\n\nexport interface DecideInput { promptId: string, choice: PromptChoice }\n\nconst KINDS: readonly PromptKind[] = ['encounter-confirm', 'group-invite', 'introduction']\n\nexport type Decisions = ReadonlyMap<PromptKind, DecisionHandler>\n\n/** The decision handlers of the modules, one per kind at most. */\nexport function collectDecisions (modules: readonly HolderModule[]): Decisions {\n const out = new Map<PromptKind, DecisionHandler>()\n for (const m of modules) {\n for (const [kind, h] of Object.entries(m?.decisions ?? {})) {\n if (!(KINDS as readonly string[]).includes(kind)) throw new Error(`unknown prompt kind ${kind} (module ${m.name})`)\n if (out.has(kind as PromptKind)) throw new Error(`duplicate decision handler ${kind} (module ${m.name})`)\n if (typeof h !== 'function' && (h === null || typeof h !== 'object' || typeof (h as DecisionDef).apply !== 'function' || ((h as DecisionDef).prepare !== undefined && typeof (h as DecisionDef).prepare !== 'function'))) {\n throw new TypeError(`decision handler ${kind} (module ${m.name}) must be a function or {prepare?, apply}`)\n }\n out.set(kind as PromptKind, h)\n }\n }\n return out\n}\n\nconst validInput = (input: DecideInput): boolean =>\n input !== null && typeof input === 'object' && typeof input.promptId === 'string' && (input.choice === 'accept' || input.choice === 'decline')\n\nconst decideDef = (decisions: Decisions): CommandDef<DecideInput, unknown> => ({\n human: true,\n // a prepared handler's preparation, against the snapshot apply sees;\n // nothing for an unknown, expired or malformed decision (apply answers it)\n async prepare (ctx, input) {\n if (!validInput(input) || !Object.hasOwn(ctx.snapshot.prompts, input.promptId)) return undefined\n const p = ctx.snapshot.prompts[input.promptId]\n if (p.expiresAt !== undefined && p.expiresAt <= ctx.when) return undefined\n const h = decisions.get(p.kind)\n return h && typeof h !== 'function' && h.prepare ? h.prepare(ctx, p, input.choice) : undefined\n },\n apply (state, prepared, input, when) {\n if (input === null || typeof input !== 'object' || typeof input.promptId !== 'string') throw new TypeError('prompt.decide needs {promptId: string, choice}')\n if (input.choice !== 'accept' && input.choice !== 'decline') throw new TypeError(`prompt.decide: choice must be accept or decline, got ${String(input.choice)}`)\n const { promptId, choice } = input\n if (!Object.hasOwn(state.prompts, promptId)) return { ok: false, code: 'unknown-prompt' }\n const p = state.prompts[promptId]\n if (p.expiresAt !== undefined && p.expiresAt <= when) return { ok: false, code: 'prompt-expired' }\n const handler = decisions.get(p.kind)\n const r: Applied | Refusal = !handler ? { state: state as HolderState }\n : typeof handler === 'function' ? handler(state, p, choice, when)\n : handler.apply(state, p, choice, when, handlerPrepared(prepared, prepared.value))\n if ((r as Refusal).ok === false) return r as Refusal\n const done = r as Applied\n const prompts = { ...done.state.prompts }\n delete prompts[promptId]\n const decided: EventDraft = { type: 'prompt.decided', promptId, kind: p.kind, choice }\n return {\n state: { ...done.state, prompts },\n ...(done.outbound ? { outbound: done.outbound } : {}),\n ...(done.duties ? { duties: done.duties } : {}),\n events: [...(done.events ?? []), decided],\n }\n },\n})\n\n/** The prompts module createHolder registers, over the modules' decision handlers. */\nexport function promptsModule (decisions: Decisions = new Map()): HolderModule {\n return Object.freeze({ name: 'prompts', commands: Object.freeze({ [DECIDE]: decideDef(decisions) }) })\n}\n"]}
@@ -0,0 +1,134 @@
1
+ import type { Json } from './core.js';
2
+ import type { HolderState, Millis, Relationship, StoredAck, Tuple } from './state.js';
3
+ /** An ack as sealed in the preparation; the engine adds its schedule. */
4
+ export type SealedAck = Pick<StoredAck, 'digest' | 'envelope' | 'threadId' | 'recipient'>;
5
+ import type { Outbound } from './ports.js';
6
+ import type { ReceiveResult } from './results.js';
7
+ import type { EventDraft, PrepareCtx, Prepared } from './engine.js';
8
+ /** Where and when a document arrived: the own rkid it was sealed to, its
9
+ * thread, its digest and the receive's `when`. */
10
+ export interface Arrival {
11
+ rkid: string;
12
+ threadId: string;
13
+ digest: string;
14
+ when: Millis;
15
+ }
16
+ /** Why a task handler refuses a document; the stage follows from where:
17
+ * decode 'malformed' → 7, decode or prepare otherwise → 8, apply → 9. */
18
+ export type TaskFailure = 'malformed' | 'validation-failed' | 'stale-issuance' | 'gate-future' | 'consumed-challenge' | 'rate-limited';
19
+ /** A handler's refusal: nothing commits, the digest stays uncached and
20
+ * a redelivery is evaluated afresh (Delivery 6.2 stage 4) — with one
21
+ * narrow, enumerated exception, the Encounter aging latch: an apply that
22
+ * refuses because its authoritative resolution found an own challenge past
23
+ * its age bound (Encounter 5260, 5270, 5290; Delivery 4.1 check 5) returns
24
+ * `latch`, the state with that challenge removed, and the engine commits
25
+ * exactly it (and the raised lastWhen): no cache entry, no ack, no event;
26
+ * the disposition stays failed. A latch that touches anything beyond
27
+ * encounter.challenges is a programming error and throws (engine.ts). */
28
+ export interface TaskRefusal {
29
+ ok: false;
30
+ reason: TaskFailure;
31
+ ref?: string;
32
+ latch?: HolderState;
33
+ }
34
+ /** A discard at a resource limit (Trust 5365/5366), answered by apply:
35
+ * `state` commits (assembly dropped, lower bound held), the disposition
36
+ * is `discarded`, with no cache entry, no ack and no event. */
37
+ export interface TaskDiscarded {
38
+ discarded: true;
39
+ reason: DiscardReason;
40
+ state: HolderState;
41
+ ref?: string;
42
+ }
43
+ /** Why an assembly was discarded: a local resource limit (5365), or an
44
+ * authenticated violation of the assembly rules that rejects the delivery. */
45
+ export type DiscardReason = 'resource-limit' | 'rejected';
46
+ export declare const isTaskDiscarded: (x: unknown) => x is TaskDiscarded;
47
+ /** A received document after stage 7: the profile fields, its payload
48
+ * as the handler's decode returned it. Read-only. */
49
+ export interface ReceivedDocument<T = unknown> {
50
+ readonly id: string;
51
+ readonly type: string;
52
+ readonly issuer: string;
53
+ readonly recipient: string;
54
+ readonly threadId: string;
55
+ readonly issuedAt: string;
56
+ readonly payload: T;
57
+ readonly ceremony?: Json;
58
+ readonly proof?: Json;
59
+ }
60
+ /** A sealed ack as the outbound the app sends, at the given attempt. */
61
+ export declare const ackOutbound: (a: Readonly<SealedAck>, attempt: number) => Outbound;
62
+ /** Signature-class types (Delivery 4.2/4.4): payloads whose authenticity
63
+ * is a transferable signature; their acks are signed, not MACed. The
64
+ * `x-` slugs are this library's own experimental, unregistered forms
65
+ * for what the specifications leave to the application (the welcome until the Access envelope is
66
+ * built; the introduction offer that conveys the target designator,
67
+ * Trust 8.3): signed documents as well. A receiver that does not know
68
+ * them answers unknown-type (Delivery §6.2). */
69
+ export declare const SIGNATURE_CLASS: ReadonlySet<string>;
70
+ /** The counterpart a `channel` binding names: the key agreement the ack
71
+ * is sealed to and the contact it is filed under. */
72
+ /** The binding a document arrives under: the counterpart's key and the
73
+ * contact; `via`, where the arrival key is no tuple's (a member key), the
74
+ * own rkid of the tuple the contact was named through (Recipient.via). */
75
+ export interface Channel {
76
+ keyAgreement: string;
77
+ contactId: string;
78
+ via?: string;
79
+ }
80
+ export declare const isTaskRefusal: (x: unknown) => x is TaskRefusal;
81
+ type Failed = Extract<ReceiveResult, {
82
+ disposition: 'failed';
83
+ }>;
84
+ /** What stages 1–8 decided, bound to the snapshot they ran against. */
85
+ export type ReceivePlan = {
86
+ kind: 'failed';
87
+ result: Failed;
88
+ } | {
89
+ kind: 'duplicate';
90
+ digest: string;
91
+ } | {
92
+ kind: 'ack';
93
+ digest: string;
94
+ ref: string;
95
+ doc: ReceivedDocument;
96
+ arrival: Arrival;
97
+ } | {
98
+ kind: 'task';
99
+ digest: string;
100
+ type: string;
101
+ doc: ReceivedDocument;
102
+ arrival: Arrival;
103
+ value: unknown;
104
+ ack: SealedAck;
105
+ };
106
+ /** The handler surface the core needs (engine.ts TaskHandler). */
107
+ export interface TaskEntry {
108
+ proof: 'absent' | 'required';
109
+ decode(payload: unknown): unknown;
110
+ channel?(state: Readonly<HolderState>, doc: ReceivedDocument, rkid: string): Channel | TaskRefusal;
111
+ /** may arrive on a deactivated tuple an act pinned (engine TaskHandler) */
112
+ pinned?: true;
113
+ prepare?(ctx: PrepareCtx, doc: ReceivedDocument, arrival: Arrival): Promise<unknown>;
114
+ }
115
+ /** The tuple whose own rkid is `rkid`, with its relationship. */
116
+ export declare function tupleByRkid(state: Readonly<HolderState>, rkid: string): {
117
+ rel: Relationship;
118
+ tuple: Tuple;
119
+ } | null;
120
+ /**
121
+ * Stages 1–8 against the preparation's snapshot. Never throws for
122
+ * anything the envelope carries; port failures surface as InfraError.
123
+ */
124
+ export declare function prepareReceive(ctx: PrepareCtx, env: unknown, tasks: ReadonlyMap<string, TaskEntry>): Promise<ReceivePlan>;
125
+ /** The outbox after a valid ack of `ref` (4.2, 6.1): delivered, late after
126
+ * failed; unchanged when already delivered. Shared with report(). */
127
+ export declare function markDelivered(state: Readonly<HolderState>, ref: string, when: Millis): {
128
+ outbox: HolderState['outbox'];
129
+ event: EventDraft | null;
130
+ };
131
+ /** Prepared for a handler: its own value under the receive's binding. */
132
+ export declare const handlerPrepared: <P>(p: Prepared<unknown>, value: P) => Prepared<P>;
133
+ export {};
134
+ //# sourceMappingURL=receive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receive.d.ts","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,317 @@
1
+ // receive — the holder's receive core (lib Stage 2, note §4.3; Delivery
2
+ // §6.2, §4.2, §4.4). The engine runs it as its built-in `receive`: the
3
+ // preparation (async) walks stages 1–8, the transition (sync) is stage 9.
4
+ //
5
+ // 1–4 once, here, through delivery.receiveByAgreement: the key agreement
6
+ // is the KeyProvider's `agree` under the engine's release from
7
+ // `addresses` (a tombstone answers retired: stage 3; an rkid never
8
+ // issued answers unknown: stage 2); the completed-effect cache is
9
+ // `state.effects`
10
+ // 5 the document profile (schema, calendar-valid issuedAt) and
11
+ // recipient = the own anchor of the label the arrival rkid derives
12
+ // under (addresses)
13
+ // 6 dispatch by the full type URI with version: the built-in
14
+ // delivery-ack path, else the task register, else unknown-type;
15
+ // then the entry's proof declaration. Delivery §3 wants `proof`
16
+ // absent on every type but delivery-ack and registry-declaration,
17
+ // and §4.4 puts the declaration into the registry entry; the
18
+ // profile schema can name only the types it knows, so for
19
+ // companion types the rule is enforced here, the earliest stage at
20
+ // which the receiver holds the entry: a mismatch is
21
+ // failed(validation-failed)
22
+ // 7–8 the handler's decode (malformed at 7, any other refusal at 8),
23
+ // the core's closed acceptance binding (Delivery 4.4: the arrival
24
+ // tuple is selected by rkid, the document's issuer is its
25
+ // counterpart anchor), the handler's prepare, and the ack: sealed
26
+ // here, before the transaction, from fresh entropy
27
+ // 9 the handler's apply; the core files the effect-cache entry with
28
+ // the ack in the same commit (the handler never writes `effects`)
29
+ // and hands the ack out first among the outbound
30
+ //
31
+ // Acks (delivery-ack/0.1) are no registered task: one common path checks
32
+ // the form (ack.ts macAckForm), then the consistency of 4.2 against the
33
+ // outbox — `ref` is a document this holder sent on that thread, on the
34
+ // arrival tuple (outbox item rkid = arrival rkid), the issuer is that
35
+ // tuple's counterpart, the referenced document's type is of the
36
+ // designated-verifier class (a MAC ack of a signature-class document is
37
+ // the wrong class), and the MAC verifies under the arrival tuple's ack
38
+ // key. A valid ack marks the item delivered (late after failed) and
39
+ // earns a terminal cache entry, no ack of its own.
40
+ //
41
+ // The stage-9 lock set (Delivery 6.2): the engine serializes every
42
+ // operation of a holder in one queue and commits by CAS — a strictly
43
+ // stronger serialization than the lock set asks for (it holds every key
44
+ // of the holder, not only the overlapping ones), which is how this
45
+ // library implements it under Stage 2's one-writer contract. The lock set
46
+ // — the digest, and for star and group-star chunks additionally the
47
+ // assembly key (tuple, type, salt) of 4.4 — is held for the whole
48
+ // evaluation; a CAS conflict re-reads and re-enters at stage 1, which
49
+ // re-checks the cache at stage 4. The chunk types (trust/star.ts,
50
+ // trust/group-star.ts, PR 4b) need no key of their own here: every
51
+ // chunk of one tuple and type is evaluated against, and committed over,
52
+ // the one state version its preparation read, so the completing chunk's
53
+ // effect (validated assembly, accepted snapshot, completed-salt advance)
54
+ // commits with the cache entry and the ack in one transaction.
55
+ //
56
+ // The lock keys a registering specification adds (Delivery RLTP-DEL-6210
57
+ // to 6230): the relationship key of a message or an attributed invitation,
58
+ // the holder-wide credential-digest key of every invitation, the
59
+ // (genesis digest, inviter) key of the prelude (Membership 3950–3957,
60
+ // 3980), and the writers that take them (a block, a chaining, a merge or
61
+ // split, a deletion of a completion-record entry or a binding) are all
62
+ // held by the same whole-state serialization.
63
+ //
64
+ // A refusal (TaskRefusal) never commits anything, at any stage. One of
65
+ // them is transient: `rate-limited` (Delivery RLTP-DEL-5820, 6270, 6280),
66
+ // a document beyond a bound its type declares; it earns no ack and no
67
+ // cache entry, the carrier keeps it, and a redelivery is evaluated in
68
+ // full. A rate the type places inside stage 9 is checked here in the
69
+ // handler's preparation (stage 8), against the snapshot stage 9 commits
70
+ // on — the same state, by the CAS above.
71
+ //
72
+ // A handler's apply may answer a TaskDiscarded: the receiver discarded an
73
+ // open assembly and holds its salt as the lower bound (5366), for one of
74
+ // two reasons: `resource-limit` (Trust 5365), or `rejected` — an
75
+ // authenticated chunk (its MAC verified) violates the assembly rules
76
+ // (5260–5310, 5500, 5510), and the spec rejects the whole delivery, so it
77
+ // must not complete from any later chunk. That is a spec-mandated state
78
+ // effect, not a failure of the document: its state commits and the result
79
+ // is the disposition `discarded` with the reason and the rule — no
80
+ // effect-cache entry (a redelivery is evaluated afresh and meets the lower
81
+ // bound), no ack (the delivery must not complete), no event; the completed
82
+ // high-water mark stays. A chunk that does not authenticate is a plain
83
+ // refusal and touches nothing. DELIVERY GAP: Delivery §6.2 names no
84
+ // disposition for a document whose processing ends in such a discard;
85
+ // `discarded` (both reasons) is this library's, to be registered in
86
+ // Delivery 0.82.
87
+ //
88
+ // A verified ack runs the ack handler a module registered for the
89
+ // acknowledged item's type, in the ack's transaction (engine.ts
90
+ // AckHandler): the sender side of 5660, completion by verified acks only.
91
+ //
92
+ // Ack class (4.4): the class follows the payload's audience class. The
93
+ // types of SIGNATURE_CLASS carry transferable signatures (the encounter
94
+ // carriers, the membership documents, the signed introduction artifacts,
95
+ // the registry declaration): their ack is SIGNED, an eddsa-jcs-2022 proof
96
+ // under the ack's issuer anchor through the KeyProvider (`sign` with the
97
+ // arrival key's label). Every other type is designated-verifier and earns
98
+ // the MAC form under the channel ack key of the arrival rkid and the
99
+ // counterpart key. A received ack is checked in the form its referenced
100
+ // outbox item's class selects; the other form is validation-failed.
101
+ //
102
+ // The acceptance binding before a tuple exists (PR 7): a handler with a
103
+ // `channel` binds its document itself, from the document, where it may
104
+ // arrive on an own key no tuple carries yet — the encounter bundle on the
105
+ // displayed card's key (Delivery 4.1: the bundle establishes the tuple,
106
+ // its issuer bound to card.anchor = credential.issuer), the membership
107
+ // documents on member keys (Membership 3.1, 3.2). `channel` names the
108
+ // counterpart's key agreement the ack is sealed to and the contact it is
109
+ // filed under, or refuses (stage 7 for malformed, else 8). Every other
110
+ // handler keeps the tuple binding of Delivery 4.4.
111
+ import { calOK, iso, makeValidator, toU } from './core.js';
112
+ import { diSignWith, diVerify, digestDoc, xRawOfMk } from './crypto.js';
113
+ import { receiveByAgreement, seal } from './delivery.js';
114
+ import { ACK_TYPE, ackDocument, ackKeyOf, ackProofForm, macAck, macAckForm, macAckValid, signedAckForm } from './ack.js';
115
+ import { SCHEMAS } from './schemas.js';
116
+ import { triasOf } from './state.js';
117
+ import { entryOf, liveRel } from './trust/state.js';
118
+ import { InfraError } from './results.js';
119
+ export const isTaskDiscarded = (x) => x !== null && typeof x === 'object' && x.discarded === true;
120
+ /** A sealed ack as the outbound the app sends, at the given attempt. */
121
+ export const ackOutbound = (a, attempt) => structuredClone({ digest: a.digest, envelope: a.envelope, type: ACK_TYPE, threadId: a.threadId, recipient: a.recipient, attempt });
122
+ const TT = 'https://real-life.org/trust-tasks/';
123
+ /** Signature-class types (Delivery 4.2/4.4): payloads whose authenticity
124
+ * is a transferable signature; their acks are signed, not MACed. The
125
+ * `x-` slugs are this library's own experimental, unregistered forms
126
+ * for what the specifications leave to the application (the welcome until the Access envelope is
127
+ * built; the introduction offer that conveys the target designator,
128
+ * Trust 8.3): signed documents as well. A receiver that does not know
129
+ * them answers unknown-type (Delivery §6.2). */
130
+ export const SIGNATURE_CLASS = new Set([
131
+ TT + 'encounter-bundle/0.1',
132
+ TT + 'encounter-credential-delivery/0.1',
133
+ TT + 'membership-invite/0.2',
134
+ TT + 'membership-accept/0.2',
135
+ TT + 'membership-evidence/0.1',
136
+ TT + 'membership-evidence/0.2',
137
+ TT + 'registry-declaration/0.1',
138
+ TT + 'introduction-request/0.1',
139
+ TT + 'introduction-forward/0.1',
140
+ TT + 'introduction-reply/0.1',
141
+ TT + 'membership-prelude-request/0.1',
142
+ TT + 'membership-prelude-reply/0.1',
143
+ // lib 0.4.0's prelude forms: never received any more (unknown-type), kept
144
+ // for the class of an outbox item a codec-1 state still holds, whose
145
+ // signed ack may yet arrive
146
+ TT + 'x-membership-prelude-request/0.1',
147
+ TT + 'x-membership-prelude-reply/0.1',
148
+ TT + 'x-membership-welcome/0.1',
149
+ TT + 'x-introduction-offer/0.1',
150
+ ]);
151
+ export const isTaskRefusal = (x) => x !== null && typeof x === 'object' && x.ok === false && typeof x.reason === 'string';
152
+ const V = makeValidator(SCHEMAS);
153
+ const profileOk = (doc) => V.validate(doc, SCHEMAS['rltp-delivery-document.schema.json'], SCHEMAS['rltp-delivery-document.schema.json']).length === 0 &&
154
+ calOK(doc.issuedAt);
155
+ const failed = (reason, stage, extra = {}) => ({ kind: 'failed', result: { disposition: 'failed', reason, stage, ...extra } });
156
+ /** The tuple whose own rkid is `rkid`, with its relationship. */
157
+ export function tupleByRkid(state, rkid) {
158
+ for (const id of Object.keys(state.relationships).sort()) {
159
+ const rel = state.relationships[id];
160
+ for (const tuple of rel.tuples)
161
+ if (tuple.rkid === rkid)
162
+ return { rel, tuple };
163
+ }
164
+ return null;
165
+ }
166
+ /** The arrival channel's ack key (4.2): X25519 of the own key behind
167
+ * `rkid` and the counterpart key, HKDF under issuer/recipient. */
168
+ async function ackKey(ctx, rkid, counterpartKeyAgreement, issuer, recipient) {
169
+ const raw = xRawOfMk(counterpartKeyAgreement);
170
+ if (!raw)
171
+ return null;
172
+ const a = await ctx.keys.agree(rkid, raw, ctx.release);
173
+ if (a.status !== 'available')
174
+ return null;
175
+ try {
176
+ return await ackKeyOf(a.shared, issuer, recipient, ctx.crypto.subtle);
177
+ }
178
+ catch {
179
+ return null;
180
+ }
181
+ }
182
+ /**
183
+ * Stages 1–8 against the preparation's snapshot. Never throws for
184
+ * anything the envelope carries; port failures surface as InfraError.
185
+ */
186
+ export async function prepareReceive(ctx, env, tasks) {
187
+ const S = ctx.crypto.subtle;
188
+ const state = ctx.snapshot;
189
+ // 1–4
190
+ const agree = (rkid, epk) => ctx.keys.agree(rkid, epk, ctx.release);
191
+ const r = await receiveByAgreement(env, agree, (d) => Object.hasOwn(state.effects, d), S);
192
+ if (r.disposition === 'duplicate-known')
193
+ return { kind: 'duplicate', digest: r.digest };
194
+ if (r.disposition !== 'unique') {
195
+ const stage = r.stages[r.stages.length - 1].stage;
196
+ const reason = r.disposition.slice('failed('.length, -1);
197
+ return failed(reason, stage, r.key ? { key: r.key } : {});
198
+ }
199
+ const doc = r.document;
200
+ const digest = r.digest;
201
+ const rkid = env.rkid;
202
+ // 5 — profile, then recipient = own anchor under the arrival key's label
203
+ if (!profileOk(doc))
204
+ return failed('malformed', 5, { digest });
205
+ const release = ctx.release(rkid);
206
+ if (release.status !== 'live')
207
+ throw new InfraError('key-provider', 'agree answered for an rkid the release does not hold live');
208
+ const own = await ctx.keys.anchorOf(release.label);
209
+ if (doc.recipient !== own.anchor)
210
+ return failed('wrong-recipient', 5, { digest });
211
+ const at = tupleByRkid(state, rkid);
212
+ // 6 — the built-in ack path, in the form the acked document's class selects
213
+ if (doc.type === ACK_TYPE) {
214
+ const signed = ackProofForm(doc) === 'signed';
215
+ const f = signed ? signedAckForm(doc) : macAckForm(doc);
216
+ if (!f.ok)
217
+ return failed(f.reason, f.stage, { digest });
218
+ // 8 — consistency against what this holder sent (4.2): the document on
219
+ // that thread, sent from the arrival rkid, of the class the proof form
220
+ // claims; the issuer its recipient (the tuple's counterpart, or the
221
+ // anchor it went to where no tuple carried it)
222
+ // the ref in either rendering (u or z, 4.2) names the outbox item by its digest bytes
223
+ const ref = toU(f.ref) ?? f.ref;
224
+ const item = Object.hasOwn(state.outbox, ref) ? state.outbox[ref] : undefined;
225
+ if (!item || item.threadId !== f.threadId || item.recipient.rkid !== rkid || SIGNATURE_CLASS.has(item.type) !== signed)
226
+ return failed('validation-failed', 8, { digest });
227
+ const expected = at ? at.tuple.counterpartAnchor : item.recipient.anchor;
228
+ if (expected === undefined || doc.issuer !== expected)
229
+ return failed('validation-failed', 8, { digest });
230
+ if (signed) {
231
+ if (!(await diVerify(doc, doc.issuer, S)))
232
+ return failed('validation-failed', 8, { digest });
233
+ }
234
+ else {
235
+ const key = await ackKey(ctx, rkid, item.recipient.keyAgreement, doc.issuer, doc.recipient);
236
+ if (!key || !(await macAckValid(doc, key, S)))
237
+ return failed('validation-failed', 8, { digest });
238
+ }
239
+ const arrival = Object.freeze({ rkid, threadId: doc.threadId, digest, when: ctx.when });
240
+ return { kind: 'ack', digest, ref, doc: Object.freeze({ ...doc }), arrival };
241
+ }
242
+ // 6 — the task register, by the full type URI (no prefix, no fallback)
243
+ const h = tasks.get(doc.type);
244
+ if (!h)
245
+ return failed('unknown-type', 6, { digest });
246
+ if ((h.proof === 'absent') !== (doc.proof === undefined))
247
+ return failed('validation-failed', 6, { digest });
248
+ // 7–8 — decode, the closed acceptance binding, the handler's preparation
249
+ const payload = h.decode(doc.payload);
250
+ const refused = (r, stage) => failed(r.reason, stage, { digest, ...(r.ref !== undefined ? { ref: r.ref } : {}) });
251
+ if (isTaskRefusal(payload))
252
+ return refused(payload, payload.reason === 'malformed' ? 7 : 8);
253
+ const rdoc = Object.freeze({ ...doc, payload });
254
+ // the binding: the arrival tuple (4.4), or the handler's own channel
255
+ // where its document may arrive before any tuple (4.1, Membership)
256
+ let peer;
257
+ if (h.channel) {
258
+ const c = h.channel(state, rdoc, rkid);
259
+ if (isTaskRefusal(c))
260
+ return refused(c, c.reason === 'malformed' ? 7 : 8);
261
+ peer = c;
262
+ }
263
+ else {
264
+ if (!at || doc.issuer !== at.tuple.counterpartAnchor)
265
+ return failed('validation-failed', 8, { digest });
266
+ // a deactivated tuple takes nothing new (6190, 6240): the arrival tuple is
267
+ // the active head of a live relationship, unless the handler binds its
268
+ // artifacts to the tuples an act pinned before a chaining (8270)
269
+ if (!h.pinned && (!liveRel(at.rel) || at.rel.activeHead !== at.tuple.ownPairLabel))
270
+ return failed('validation-failed', 8, { digest, ref: 'RLTP-TRU-6240' });
271
+ // the entry the arrival's relationship sends under (2146), by its current id
272
+ peer = { keyAgreement: at.tuple.counterpartKeyAgreement, contactId: entryOf(state, at.rel)?.id ?? at.rel.contactId };
273
+ }
274
+ const arrival = Object.freeze({ rkid, threadId: doc.threadId, digest, when: ctx.when });
275
+ const value = h.prepare ? await h.prepare(ctx, rdoc, arrival) : undefined;
276
+ if (isTaskRefusal(value))
277
+ return refused(value, 8);
278
+ // the ack, sealed before the transaction (4.2: created inside the
279
+ // effect's transaction — it commits with the cache entry or not at all),
280
+ // in the form the document's class selects (4.4)
281
+ const base = ackDocument({
282
+ id: ctx.entropy.id('ack.id'), issuer: own.anchor, recipient: doc.issuer,
283
+ threadId: arrival.threadId, issuedAt: iso(ctx.when), ref: digest,
284
+ });
285
+ let ackDoc;
286
+ if (SIGNATURE_CLASS.has(doc.type)) {
287
+ ackDoc = await diSignWith(own.anchor, (b) => ctx.keys.sign(release.label, b), base, base.issuedAt, S);
288
+ }
289
+ else {
290
+ const key = await ackKey(ctx, rkid, peer.keyAgreement, own.anchor, doc.issuer);
291
+ if (!key)
292
+ return failed('validation-failed', 8, { digest });
293
+ ackDoc = await macAck(base, key, S);
294
+ }
295
+ const envelope = await seal(ackDoc, peer.keyAgreement, { ephSeed: ctx.entropy.bytes('seal.eph', 32), nonce: ctx.entropy.bytes('seal.nonce', 12) }, S);
296
+ const ack = {
297
+ digest: await digestDoc(ackDoc, S), envelope, threadId: arrival.threadId,
298
+ recipient: { contactId: peer.contactId, rkid, keyAgreement: peer.keyAgreement, ...(peer.via !== undefined ? { via: peer.via } : {}) },
299
+ };
300
+ return { kind: 'task', digest, type: doc.type, doc: rdoc, arrival, value, ack };
301
+ }
302
+ /** The outbox after a valid ack of `ref` (4.2, 6.1): delivered, late after
303
+ * failed; unchanged when already delivered. Shared with report(). */
304
+ export function markDelivered(state, ref, when) {
305
+ const it = state.outbox[ref];
306
+ if (it.history.some((s) => s.status === 'delivered'))
307
+ return { outbox: state.outbox, event: null };
308
+ const late = triasOf(it) === 'failed';
309
+ const { nextAttemptAt: _, ...rest } = it;
310
+ return {
311
+ outbox: { ...state.outbox, [ref]: { ...rest, history: [...it.history, { status: 'delivered', at: when, late }] } },
312
+ event: { type: 'delivery.status', digest: ref, threadId: it.threadId, status: 'delivered', late },
313
+ };
314
+ }
315
+ /** Prepared for a handler: its own value under the receive's binding. */
316
+ export const handlerPrepared = (p, value) => Object.freeze({ value, version: p.version, commitSeq: p.commitSeq });
317
+ //# sourceMappingURL=receive.js.map