@econ-v1/domain 6.7.0 → 6.10.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 (286) hide show
  1. package/dist/errors/index.d.ts +96 -25
  2. package/dist/errors/index.d.ts.map +1 -1
  3. package/dist/events/index.d.ts +127 -20
  4. package/dist/events/index.d.ts.map +1 -1
  5. package/dist/index.d.ts +250 -47
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/models/agent.d.ts +25 -4
  8. package/dist/models/agent.d.ts.map +1 -1
  9. package/dist/models/app-data.d.ts +84 -0
  10. package/dist/models/app-data.d.ts.map +1 -0
  11. package/dist/models/conversation.d.ts +12 -2
  12. package/dist/models/conversation.d.ts.map +1 -1
  13. package/dist/models/delegation.d.ts +12 -3
  14. package/dist/models/delegation.d.ts.map +1 -1
  15. package/dist/models/device-boot-state.d.ts +12 -0
  16. package/dist/models/device-boot-state.d.ts.map +1 -0
  17. package/dist/models/device-identity.d.ts +11 -3
  18. package/dist/models/device-identity.d.ts.map +1 -1
  19. package/dist/models/graph.d.ts +26 -3
  20. package/dist/models/graph.d.ts.map +1 -1
  21. package/dist/models/index.d.ts +82 -14
  22. package/dist/models/index.d.ts.map +1 -1
  23. package/dist/models/json.d.ts +5 -5
  24. package/dist/models/json.d.ts.map +1 -1
  25. package/dist/models/l402.d.ts +4 -5
  26. package/dist/models/l402.d.ts.map +1 -1
  27. package/dist/models/live-query-definition.d.ts +15 -16
  28. package/dist/models/live-query-definition.d.ts.map +1 -1
  29. package/dist/models/node-app-memory.d.ts +107 -0
  30. package/dist/models/node-app-memory.d.ts.map +1 -0
  31. package/dist/models/outbox-intent.d.ts +3 -3
  32. package/dist/models/outbox-intent.d.ts.map +1 -1
  33. package/dist/models/outbox.d.ts +67 -15
  34. package/dist/models/outbox.d.ts.map +1 -1
  35. package/dist/models/owner-views.d.ts +28 -28
  36. package/dist/models/owner-views.d.ts.map +1 -1
  37. package/dist/models/payment.d.ts +28 -9
  38. package/dist/models/payment.d.ts.map +1 -1
  39. package/dist/models/query-diff.d.ts +2 -2
  40. package/dist/models/query-diff.d.ts.map +1 -1
  41. package/dist/models/release-manifest.d.ts +32 -47
  42. package/dist/models/release-manifest.d.ts.map +1 -1
  43. package/dist/models/repository-write.d.ts +3 -3
  44. package/dist/models/repository-write.d.ts.map +1 -1
  45. package/dist/models/revisioned-commands.d.ts +66 -13
  46. package/dist/models/revisioned-commands.d.ts.map +1 -1
  47. package/dist/models/revisioned.d.ts +50 -10
  48. package/dist/models/revisioned.d.ts.map +1 -1
  49. package/dist/models/settings.d.ts +2 -2
  50. package/dist/models/settings.d.ts.map +1 -1
  51. package/dist/models/sync.d.ts +18 -18
  52. package/dist/models/sync.d.ts.map +1 -1
  53. package/dist/models/wallet.d.ts +16 -16
  54. package/dist/models/wallet.d.ts.map +1 -1
  55. package/dist/queries/certificate-trust.d.ts +13 -0
  56. package/dist/queries/certificate-trust.d.ts.map +1 -0
  57. package/dist/queries/conversations.d.ts +38 -7
  58. package/dist/queries/conversations.d.ts.map +1 -1
  59. package/dist/queries/discovery.d.ts +10 -10
  60. package/dist/queries/discovery.d.ts.map +1 -1
  61. package/dist/queries/notifications.d.ts +24 -4
  62. package/dist/queries/notifications.d.ts.map +1 -1
  63. package/dist/queries/onboarding-flow.d.ts +25 -0
  64. package/dist/queries/onboarding-flow.d.ts.map +1 -0
  65. package/dist/queries/onboarding-lcd-screen.d.ts +56 -0
  66. package/dist/queries/onboarding-lcd-screen.d.ts.map +1 -0
  67. package/dist/queries/onboarding-status.d.ts +10 -10
  68. package/dist/queries/onboarding-status.d.ts.map +1 -1
  69. package/dist/queries/revisioned.d.ts +47 -13
  70. package/dist/queries/revisioned.d.ts.map +1 -1
  71. package/dist/queries/wallet.d.ts +40 -10
  72. package/dist/queries/wallet.d.ts.map +1 -1
  73. package/dist/services/abort-signal.d.ts +54 -0
  74. package/dist/services/abort-signal.d.ts.map +1 -0
  75. package/dist/services/certificate-trust-service.d.ts +42 -0
  76. package/dist/services/certificate-trust-service.d.ts.map +1 -0
  77. package/dist/services/conversation-service.d.ts +103 -15
  78. package/dist/services/conversation-service.d.ts.map +1 -1
  79. package/dist/services/conversation-sync.d.ts +9 -5
  80. package/dist/services/conversation-sync.d.ts.map +1 -1
  81. package/dist/services/device-boot-state-service.d.ts +17 -0
  82. package/dist/services/device-boot-state-service.d.ts.map +1 -0
  83. package/dist/services/device-cleanup-service.d.ts +47 -0
  84. package/dist/services/device-cleanup-service.d.ts.map +1 -0
  85. package/dist/services/discovery-probe-service.d.ts +37 -46
  86. package/dist/services/discovery-probe-service.d.ts.map +1 -1
  87. package/dist/services/gossip-service.d.ts +28 -13
  88. package/dist/services/gossip-service.d.ts.map +1 -1
  89. package/dist/services/invite-redemption-service.d.ts +43 -0
  90. package/dist/services/invite-redemption-service.d.ts.map +1 -0
  91. package/dist/services/lan-candidate-refresh-service.d.ts +22 -7
  92. package/dist/services/lan-candidate-refresh-service.d.ts.map +1 -1
  93. package/dist/services/lcd-decision-service.d.ts +58 -0
  94. package/dist/services/lcd-decision-service.d.ts.map +1 -0
  95. package/dist/services/onboarding-checkpoint-store.d.ts +54 -0
  96. package/dist/services/onboarding-checkpoint-store.d.ts.map +1 -0
  97. package/dist/services/onboarding-flow-service.d.ts +180 -0
  98. package/dist/services/onboarding-flow-service.d.ts.map +1 -0
  99. package/dist/services/onboarding-status-service.d.ts +21 -38
  100. package/dist/services/onboarding-status-service.d.ts.map +1 -1
  101. package/dist/services/outbox-service.d.ts +105 -29
  102. package/dist/services/outbox-service.d.ts.map +1 -1
  103. package/dist/services/owner-claim-service.d.ts +187 -0
  104. package/dist/services/owner-claim-service.d.ts.map +1 -0
  105. package/dist/services/owner-view-refresh-service.d.ts +40 -0
  106. package/dist/services/owner-view-refresh-service.d.ts.map +1 -0
  107. package/dist/services/owner-view-service.d.ts +49 -4
  108. package/dist/services/owner-view-service.d.ts.map +1 -1
  109. package/dist/services/peer-directory-service.d.ts +53 -17
  110. package/dist/services/peer-directory-service.d.ts.map +1 -1
  111. package/dist/services/release-manifest-verifier.d.ts +18 -20
  112. package/dist/services/release-manifest-verifier.d.ts.map +1 -1
  113. package/dist/services/revisioned-domain-service.d.ts +50 -17
  114. package/dist/services/revisioned-domain-service.d.ts.map +1 -1
  115. package/dist/services/runtime-globals.d.ts +39 -0
  116. package/dist/services/runtime-globals.d.ts.map +1 -0
  117. package/dist/services/session-service.d.ts +23 -3
  118. package/dist/services/session-service.d.ts.map +1 -1
  119. package/dist/services/sync/authoritative-mirror.d.ts +88 -15
  120. package/dist/services/sync/authoritative-mirror.d.ts.map +1 -1
  121. package/dist/services/sync/index.d.ts +17 -5
  122. package/dist/services/sync/index.d.ts.map +1 -1
  123. package/dist/services/sync/log-sync.d.ts +42 -8
  124. package/dist/services/sync/log-sync.d.ts.map +1 -1
  125. package/dist/services/sync/revision-merge.d.ts +80 -13
  126. package/dist/services/sync/revision-merge.d.ts.map +1 -1
  127. package/dist/services/sync/strategy.d.ts +11 -1
  128. package/dist/services/sync/strategy.d.ts.map +1 -1
  129. package/dist/services/transport-manager.d.ts +84 -29
  130. package/dist/services/transport-manager.d.ts.map +1 -1
  131. package/dist/services/wallet-mirror-service.d.ts +81 -18
  132. package/dist/services/wallet-mirror-service.d.ts.map +1 -1
  133. package/dist/services/wifi-provisioning-service.d.ts +146 -0
  134. package/dist/services/wifi-provisioning-service.d.ts.map +1 -0
  135. package/package.json +18 -12
  136. package/src/errors/index.js +126 -0
  137. package/src/events/index.js +124 -0
  138. package/src/index.js +566 -0
  139. package/src/models/agent.js +43 -0
  140. package/src/models/app-data.js +122 -0
  141. package/src/models/conversation.js +36 -0
  142. package/{dist → src}/models/delegation.js +18 -1
  143. package/src/models/device-boot-state.js +3 -0
  144. package/{dist → src}/models/device-identity.js +18 -2
  145. package/{dist → src}/models/graph.js +30 -3
  146. package/src/models/index.js +175 -0
  147. package/src/models/json.js +18 -0
  148. package/src/models/l402.js +9 -0
  149. package/src/models/live-query-definition.js +30 -0
  150. package/src/models/node-app-memory.js +130 -0
  151. package/src/models/outbox-intent.js +12 -0
  152. package/{dist → src}/models/outbox.js +102 -3
  153. package/src/models/owner-views.js +59 -0
  154. package/src/models/payment.js +24 -0
  155. package/src/models/query-diff.js +7 -0
  156. package/src/models/release-manifest.js +160 -0
  157. package/src/models/repository-write.js +10 -0
  158. package/src/models/revisioned-commands.js +92 -0
  159. package/src/models/revisioned.js +41 -0
  160. package/src/models/settings.js +16 -0
  161. package/src/models/sync.js +44 -0
  162. package/src/models/wallet.js +40 -0
  163. package/src/queries/certificate-trust.js +31 -0
  164. package/{dist → src}/queries/conversations.js +54 -3
  165. package/src/queries/discovery.js +34 -0
  166. package/{dist → src}/queries/notifications.js +25 -2
  167. package/src/queries/onboarding-flow.js +48 -0
  168. package/src/queries/onboarding-lcd-screen.js +100 -0
  169. package/src/queries/onboarding-status.js +34 -0
  170. package/src/queries/revisioned.js +128 -0
  171. package/{dist → src}/queries/wallet.js +35 -1
  172. package/src/services/abort-signal.js +69 -0
  173. package/src/services/certificate-trust-service.js +119 -0
  174. package/{dist → src}/services/conversation-service.js +108 -1
  175. package/src/services/conversation-sync.js +28 -0
  176. package/src/services/device-boot-state-service.js +45 -0
  177. package/src/services/device-cleanup-service.js +112 -0
  178. package/src/services/discovery-probe-service.js +196 -0
  179. package/{dist → src}/services/gossip-service.js +54 -4
  180. package/src/services/invite-redemption-service.js +64 -0
  181. package/src/services/lan-candidate-refresh-service.js +164 -0
  182. package/src/services/lcd-decision-service.js +93 -0
  183. package/src/services/onboarding-checkpoint-store.js +155 -0
  184. package/src/services/onboarding-flow-service.js +1096 -0
  185. package/src/services/onboarding-status-service.js +64 -0
  186. package/{dist → src}/services/outbox-service.js +132 -1
  187. package/src/services/owner-claim-service.js +347 -0
  188. package/src/services/owner-view-refresh-service.js +208 -0
  189. package/src/services/owner-view-service.js +87 -0
  190. package/{dist → src}/services/peer-directory-service.js +72 -3
  191. package/src/services/release-manifest-verifier.js +108 -0
  192. package/{dist → src}/services/revisioned-domain-service.js +189 -9
  193. package/src/services/runtime-globals.js +174 -0
  194. package/src/services/session-service.js +50 -0
  195. package/{dist → src}/services/sync/authoritative-mirror.js +90 -1
  196. package/src/services/sync/index.js +67 -0
  197. package/src/services/sync/log-sync.js +60 -0
  198. package/src/services/sync/revision-merge.js +119 -0
  199. package/src/services/sync/strategy.js +44 -0
  200. package/src/services/transport-manager.js +478 -0
  201. package/{dist → src}/services/wallet-mirror-service.js +226 -6
  202. package/src/services/wifi-provisioning-service.js +278 -0
  203. package/dist/errors/index.js +0 -45
  204. package/dist/errors/index.js.map +0 -1
  205. package/dist/events/index.js +0 -17
  206. package/dist/events/index.js.map +0 -1
  207. package/dist/index.js +0 -31
  208. package/dist/index.js.map +0 -1
  209. package/dist/models/agent.js +0 -17
  210. package/dist/models/agent.js.map +0 -1
  211. package/dist/models/conversation.js +0 -2
  212. package/dist/models/conversation.js.map +0 -1
  213. package/dist/models/delegation.js.map +0 -1
  214. package/dist/models/device-identity.js.map +0 -1
  215. package/dist/models/graph.js.map +0 -1
  216. package/dist/models/index.js +0 -8
  217. package/dist/models/index.js.map +0 -1
  218. package/dist/models/json.js +0 -2
  219. package/dist/models/json.js.map +0 -1
  220. package/dist/models/l402.js +0 -2
  221. package/dist/models/l402.js.map +0 -1
  222. package/dist/models/live-query-definition.js +0 -2
  223. package/dist/models/live-query-definition.js.map +0 -1
  224. package/dist/models/outbox-intent.js +0 -2
  225. package/dist/models/outbox-intent.js.map +0 -1
  226. package/dist/models/outbox.js.map +0 -1
  227. package/dist/models/owner-views.js +0 -7
  228. package/dist/models/owner-views.js.map +0 -1
  229. package/dist/models/payment.js +0 -3
  230. package/dist/models/payment.js.map +0 -1
  231. package/dist/models/query-diff.js +0 -2
  232. package/dist/models/query-diff.js.map +0 -1
  233. package/dist/models/release-manifest.js +0 -147
  234. package/dist/models/release-manifest.js.map +0 -1
  235. package/dist/models/repository-write.js +0 -2
  236. package/dist/models/repository-write.js.map +0 -1
  237. package/dist/models/revisioned-commands.js +0 -35
  238. package/dist/models/revisioned-commands.js.map +0 -1
  239. package/dist/models/revisioned.js +0 -2
  240. package/dist/models/revisioned.js.map +0 -1
  241. package/dist/models/settings.js +0 -2
  242. package/dist/models/settings.js.map +0 -1
  243. package/dist/models/sync.js +0 -2
  244. package/dist/models/sync.js.map +0 -1
  245. package/dist/models/wallet.js +0 -2
  246. package/dist/models/wallet.js.map +0 -1
  247. package/dist/queries/conversations.js.map +0 -1
  248. package/dist/queries/discovery.js +0 -27
  249. package/dist/queries/discovery.js.map +0 -1
  250. package/dist/queries/notifications.js.map +0 -1
  251. package/dist/queries/onboarding-status.js +0 -27
  252. package/dist/queries/onboarding-status.js.map +0 -1
  253. package/dist/queries/revisioned.js +0 -58
  254. package/dist/queries/revisioned.js.map +0 -1
  255. package/dist/queries/wallet.js.map +0 -1
  256. package/dist/services/conversation-service.js.map +0 -1
  257. package/dist/services/conversation-sync.js +0 -9
  258. package/dist/services/conversation-sync.js.map +0 -1
  259. package/dist/services/discovery-probe-service.js +0 -129
  260. package/dist/services/discovery-probe-service.js.map +0 -1
  261. package/dist/services/gossip-service.js.map +0 -1
  262. package/dist/services/lan-candidate-refresh-service.js +0 -79
  263. package/dist/services/lan-candidate-refresh-service.js.map +0 -1
  264. package/dist/services/onboarding-status-service.js +0 -32
  265. package/dist/services/onboarding-status-service.js.map +0 -1
  266. package/dist/services/outbox-service.js.map +0 -1
  267. package/dist/services/owner-view-service.js +0 -21
  268. package/dist/services/owner-view-service.js.map +0 -1
  269. package/dist/services/peer-directory-service.js.map +0 -1
  270. package/dist/services/release-manifest-verifier.js +0 -84
  271. package/dist/services/release-manifest-verifier.js.map +0 -1
  272. package/dist/services/revisioned-domain-service.js.map +0 -1
  273. package/dist/services/session-service.js +0 -20
  274. package/dist/services/session-service.js.map +0 -1
  275. package/dist/services/sync/authoritative-mirror.js.map +0 -1
  276. package/dist/services/sync/index.js +0 -4
  277. package/dist/services/sync/index.js.map +0 -1
  278. package/dist/services/sync/log-sync.js +0 -20
  279. package/dist/services/sync/log-sync.js.map +0 -1
  280. package/dist/services/sync/revision-merge.js +0 -46
  281. package/dist/services/sync/revision-merge.js.map +0 -1
  282. package/dist/services/sync/strategy.js +0 -2
  283. package/dist/services/sync/strategy.js.map +0 -1
  284. package/dist/services/transport-manager.js +0 -234
  285. package/dist/services/transport-manager.js.map +0 -1
  286. package/dist/services/wallet-mirror-service.js.map +0 -1
@@ -0,0 +1,69 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {{
5
+ * readonly capture?: boolean;
6
+ * }} AbortEventListenerOptions
7
+ */
8
+
9
+ /**
10
+ * @typedef {AbortEventListenerOptions & {
11
+ * readonly once?: boolean;
12
+ * readonly passive?: boolean;
13
+ * readonly signal?: AbortSignalPort;
14
+ * }} AbortAddEventListenerOptions
15
+ */
16
+
17
+ /**
18
+ * @typedef {{
19
+ * readonly bubbles: boolean;
20
+ * cancelBubble: boolean;
21
+ * readonly cancelable: boolean;
22
+ * readonly composed: boolean;
23
+ * readonly currentTarget: AbortEventTarget | null;
24
+ * readonly defaultPrevented: boolean;
25
+ * readonly eventPhase: number;
26
+ * readonly isTrusted: boolean;
27
+ * returnValue: boolean;
28
+ * readonly srcElement: AbortEventTarget | null;
29
+ * readonly target: AbortEventTarget | null;
30
+ * readonly timeStamp: number;
31
+ * readonly type: string;
32
+ * composedPath(): AbortEventTarget[];
33
+ * initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
34
+ * preventDefault(): void;
35
+ * stopImmediatePropagation(): void;
36
+ * stopPropagation(): void;
37
+ * readonly NONE: 0;
38
+ * readonly CAPTURING_PHASE: 1;
39
+ * readonly AT_TARGET: 2;
40
+ * readonly BUBBLING_PHASE: 3;
41
+ * }} AbortEvent
42
+ */
43
+
44
+ /** @typedef {(event: AbortEvent) => void} AbortEventListener */
45
+ /** @typedef {{ handleEvent(event: AbortEvent): void }} AbortEventListenerObject */
46
+
47
+ /**
48
+ * @typedef {{
49
+ * addEventListener(type: string, listener: AbortEventListener | AbortEventListenerObject | null, options?: boolean | AbortAddEventListenerOptions): void;
50
+ * dispatchEvent(event: AbortEvent): boolean;
51
+ * removeEventListener(type: string, listener: AbortEventListener | AbortEventListenerObject | null, options?: boolean | AbortEventListenerOptions): void;
52
+ * }} AbortEventTarget
53
+ */
54
+
55
+ /**
56
+ * Platform-neutral structural equivalent of a native AbortSignal instance.
57
+ *
58
+ * This internal contract preserves the native AbortSignal surface without
59
+ * expanding the public ports package.
60
+ *
61
+ * @typedef {AbortEventTarget & {
62
+ * readonly aborted: boolean;
63
+ * onabort: ((this: AbortSignalPort, event: AbortEvent) => unknown) | null;
64
+ * readonly reason: unknown;
65
+ * throwIfAborted(): void;
66
+ * }} AbortSignalPort
67
+ */
68
+
69
+ export {};
@@ -0,0 +1,119 @@
1
+ // @ts-check
2
+ /** @typedef {import('@econ-v1/ports').CertificateTrustPlatform} CertificateTrustPlatform */
3
+ /** @typedef {import('@econ-v1/ports').CertificateTrustProbePort} CertificateTrustProbePort */
4
+ export class CertificateTrustService {
5
+ /** @readonly @type {string} */
6
+ #origin;
7
+ /** @readonly @type {CertificateTrustPlatform} */
8
+ #platform;
9
+ /** @readonly @type {CertificateTrustProbePort} */
10
+ #probe;
11
+ /** @readonly @type {(snapshot: CertificateTrustSnapshot) => void} */
12
+ #onChange;
13
+ /** @type {CertificateTrustSnapshot} */
14
+ #snapshot;
15
+ /**
16
+ * @param {CertificateTrustServiceOptions} options
17
+ */
18
+ constructor(options) {
19
+ this.#origin = options.origin;
20
+ this.#platform = options.platform;
21
+ this.#probe = options.probe;
22
+ this.#onChange = options.onChange ?? (() => undefined);
23
+ this.#snapshot = {
24
+ status: "unknown",
25
+ origin: options.origin,
26
+ platform: options.platform,
27
+ };
28
+ }
29
+ /**
30
+ * @returns {CertificateTrustSnapshot}
31
+ */
32
+ status() {
33
+ return this.#snapshot;
34
+ }
35
+ /**
36
+ * @returns {CertificateTrustSnapshot}
37
+ */
38
+ restartInstall() {
39
+ return this.#set({ status: "installing", lastError: undefined });
40
+ }
41
+ /**
42
+ * @returns {CertificateTrustSnapshot}
43
+ */
44
+ skip() {
45
+ return this.#set({ status: "skipped", lastError: undefined });
46
+ }
47
+ /**
48
+ * @returns {Promise<CertificateTrustSnapshot>}
49
+ */
50
+ async verify() {
51
+ try {
52
+ const result = await this.#probe.probe(this.#origin);
53
+ // Only an answering node reports a fingerprint; an unreachable one has
54
+ // nothing to report and must not clear a previously observed value.
55
+ const fingerprint =
56
+ result.status === "unreachable" ? undefined : result.fingerprint;
57
+ this.#snapshot = {
58
+ ...this.#snapshot,
59
+ ...(fingerprint === undefined ? {} : { fingerprint }),
60
+ };
61
+ if (result.status === "trusted") {
62
+ return this.#set({ status: "verified", lastError: undefined });
63
+ }
64
+ // A node that never answered has not refused anything. Reporting "does not
65
+ // trust the CA yet" here would tell someone to go re-install a certificate
66
+ // when the real problem is that nothing is listening — the same unmet
67
+ // prerequisite reported as the wrong one.
68
+ if (result.status === "unreachable") {
69
+ return this.#set({
70
+ status: "unavailable",
71
+ lastError: "The node could not be reached over HTTPS.",
72
+ });
73
+ }
74
+ return this.#set({
75
+ status: "failed",
76
+ lastError: "The device does not trust the node CA yet.",
77
+ });
78
+ } catch {
79
+ return this.#set({
80
+ status: "unavailable",
81
+ lastError: "The node could not be reached over HTTPS.",
82
+ });
83
+ }
84
+ }
85
+ /**
86
+ * @param {{ readonly status: CertificateTrustStatus; readonly lastError?: string | undefined }} next
87
+ * @returns {CertificateTrustSnapshot}
88
+ */
89
+ #set(next) {
90
+ this.#snapshot = {
91
+ origin: this.#origin,
92
+ platform: this.#platform,
93
+ status: next.status,
94
+ ...(this.#snapshot.fingerprint === undefined
95
+ ? {}
96
+ : { fingerprint: this.#snapshot.fingerprint }),
97
+ ...(next.lastError === undefined ? {} : { lastError: next.lastError }),
98
+ };
99
+ this.#onChange(this.#snapshot);
100
+ return this.#snapshot;
101
+ }
102
+ }
103
+ /** @typedef {"unknown" | "installing" | "verified" | "failed" | "skipped" | "unavailable"} CertificateTrustStatus */
104
+ /**
105
+ * @typedef {object} CertificateTrustSnapshot
106
+ * @property {CertificateTrustStatus} status
107
+ * @property {string} origin
108
+ * @property {CertificateTrustPlatform} platform
109
+ * @property {string} [fingerprint]
110
+ * @property {string} [lastError]
111
+ */
112
+ /**
113
+ * @typedef {object} CertificateTrustServiceOptions
114
+ * @property {string} origin
115
+ * @property {CertificateTrustPlatform} platform
116
+ * @property {CertificateTrustProbePort} probe
117
+ * @property {(snapshot: CertificateTrustSnapshot) => void} [onChange]
118
+ */
119
+ // @ts-check
@@ -1,9 +1,77 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {import("@econ-v1/ports").Conversation} Conversation
5
+ */
6
+ /**
7
+ * @typedef {import("@econ-v1/ports").ConversationListParams} ConversationListParams
8
+ */
9
+ /**
10
+ * @typedef {import("@econ-v1/ports").ConversationLogDelta} ConversationLogDelta
11
+ */
12
+ /**
13
+ * @typedef {import("@econ-v1/ports").ConversationMessage} ConversationMessage
14
+ */
15
+ /**
16
+ * @typedef {import("@econ-v1/ports").ConversationRepository} ConversationRepository
17
+ */
18
+ /**
19
+ * @typedef {import("@econ-v1/ports").Notification} Notification
20
+ */
21
+ /**
22
+ * @typedef {import("@econ-v1/ports").NotificationListParams} NotificationListParams
23
+ */
24
+ /**
25
+ * @typedef {import("@econ-v1/ports").NotificationRepository} NotificationRepository
26
+ */
27
+ /**
28
+ * @typedef {import("@econ-v1/ports").OutboxRecord} OutboxRecord
29
+ */
30
+ /**
31
+ * @typedef {import("@econ-v1/ports").OutboxRepository} OutboxRepository
32
+ */
33
+ /**
34
+ * @typedef {import("@econ-v1/ports").RepositoryEvent} RepositoryEvent
35
+ */
36
+ /**
37
+ * @typedef {{ readonly body: string; readonly conversationId: string; readonly idempotencyKey: string; readonly messageId?: string; readonly senderId: string; }} SendMessageInput
38
+ */
39
+ /**
40
+ * @typedef {{ readonly id: () => string; readonly now?: () => string; }} ConversationServiceOptions
41
+ */
42
+ /**
43
+ * @typedef {RepositoryEvent} ConversationRepositoryWrite
44
+ */
1
45
  export class ConversationService {
46
+ /**
47
+ * @readonly
48
+ * @type {ConversationRepository}
49
+ */
2
50
  #conversations;
51
+ /**
52
+ * @readonly
53
+ * @type {() => string}
54
+ */
3
55
  #id;
56
+ /**
57
+ * @readonly
58
+ * @type {NotificationRepository}
59
+ */
4
60
  #notifications;
61
+ /**
62
+ * @readonly
63
+ * @type {() => string}
64
+ */
5
65
  #now;
66
+ /**
67
+ * @readonly
68
+ * @type {OutboxRepository}
69
+ */
6
70
  #outbox;
71
+ /**
72
+ * @param {{ readonly conversations: ConversationRepository; readonly notifications: NotificationRepository; readonly outbox: OutboxRepository; }} dependencies
73
+ * @param {ConversationServiceOptions} options
74
+ */
7
75
  constructor(dependencies, options) {
8
76
  this.#conversations = dependencies.conversations;
9
77
  this.#id = options.id;
@@ -11,12 +79,17 @@ export class ConversationService {
11
79
  this.#now = options.now ?? (() => new Date().toISOString());
12
80
  this.#outbox = dependencies.outbox;
13
81
  }
82
+ /**
83
+ * @param {SendMessageInput} input
84
+ * @returns {Promise<ConversationMessage>}
85
+ */
14
86
  async sendMessage(input) {
15
87
  requireText(input.body, "message body");
16
88
  requireText(input.conversationId, "conversation id");
17
89
  requireText(input.idempotencyKey, "message idempotency key");
18
90
  requireText(input.senderId, "message sender id");
19
91
  const createdAt = this.#now();
92
+ /** @type {ConversationMessage} */
20
93
  const message = {
21
94
  body: input.body,
22
95
  ...(input.messageId === undefined ? {} : { clientMessageId: input.messageId }),
@@ -27,6 +100,7 @@ export class ConversationService {
27
100
  state: "pending",
28
101
  };
29
102
  await this.#conversations.appendPendingMessage({ message }).commit();
103
+ /** @type {OutboxRecord} */
30
104
  const outbox = {
31
105
  createdAt,
32
106
  domain: "conversation",
@@ -45,6 +119,10 @@ export class ConversationService {
45
119
  await this.#outbox.create(outbox);
46
120
  return message;
47
121
  }
122
+ /**
123
+ * @param {ConversationLogDelta} delta
124
+ * @returns {Promise<void>}
125
+ */
48
126
  async applyDelta(delta) {
49
127
  switch (delta.kind) {
50
128
  case "notification.read":
@@ -59,6 +137,10 @@ export class ConversationService {
59
137
  return;
60
138
  }
61
139
  }
140
+ /**
141
+ * @param {{ readonly acknowledgedAt?: string; readonly messageId: string; readonly serverMessageId?: string; }} input
142
+ * @returns {Promise<void>}
143
+ */
62
144
  async acknowledgeMessage(input) {
63
145
  await this.#conversations.acknowledgeMessage({
64
146
  acknowledgedAt: input.acknowledgedAt ?? this.#now(),
@@ -66,6 +148,10 @@ export class ConversationService {
66
148
  ...(input.serverMessageId === undefined ? {} : { serverMessageId: input.serverMessageId }),
67
149
  });
68
150
  }
151
+ /**
152
+ * @param {{ readonly messageId: string; readonly reason: string; readonly failedAt?: string }} input
153
+ * @returns {Promise<void>}
154
+ */
69
155
  async failMessage(input) {
70
156
  requireText(input.reason, "message failure reason");
71
157
  await this.#conversations.failMessage({
@@ -74,22 +160,43 @@ export class ConversationService {
74
160
  reason: input.reason,
75
161
  });
76
162
  }
163
+ /**
164
+ * @param {ConversationListParams} [params]
165
+ * @returns {Promise<readonly Conversation[]>}
166
+ */
77
167
  async listConversations(params) {
78
168
  return await this.#conversations.listConversations(params);
79
169
  }
170
+ /**
171
+ * @param {string} conversationId
172
+ * @param {{ readonly before?: string; readonly limit?: number }} [params]
173
+ * @returns {Promise<readonly ConversationMessage[]>}
174
+ */
80
175
  async listMessages(conversationId, params) {
81
176
  return await this.#conversations.listMessages(conversationId, params);
82
177
  }
178
+ /**
179
+ * @param {NotificationListParams} [params]
180
+ * @returns {Promise<readonly Notification[]>}
181
+ */
83
182
  async listNotifications(params) {
84
183
  return await this.#notifications.listNotifications(params);
85
184
  }
185
+ /**
186
+ * @param {string} notificationId
187
+ * @returns {Promise<void>}
188
+ */
86
189
  async markNotificationRead(notificationId, readAt = this.#now()) {
87
190
  requireText(notificationId, "notification id");
88
191
  await this.#notifications.markRead({ notificationId, readAt }).commit();
89
192
  }
90
193
  }
194
+ /**
195
+ * @param {string} value
196
+ * @param {string} label
197
+ * @returns {void}
198
+ */
91
199
  function requireText(value, label) {
92
200
  if (value.trim() === "")
93
201
  throw new TypeError(`${label} must be a non-empty string`);
94
202
  }
95
- //# sourceMappingURL=conversation-service.js.map
@@ -0,0 +1,28 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @typedef {import("../models/conversation.js").ConversationLogDelta} ConversationLogDelta
5
+ */
6
+ /**
7
+ * @typedef {import("../models/sync.js").SyncDeltaContext} SyncDeltaContext
8
+ */
9
+ /**
10
+ * @typedef {import("./conversation-service.js").ConversationService} ConversationService
11
+ */
12
+ /**
13
+ * @typedef {SyncDeltaContext} ConversationSyncContext
14
+ */
15
+ import { LogSync } from "./sync/log-sync.js";
16
+ /**
17
+ * @param {Pick<ConversationService, "applyDelta">} service
18
+ * @returns {LogSync<ConversationLogDelta>}
19
+ */
20
+ export function createConversationLogSync(service) {
21
+ return new LogSync({
22
+ apply: /**
23
+ * @param {ConversationLogDelta} payload
24
+ */ async (payload) => {
25
+ await service.applyDelta(payload);
26
+ },
27
+ });
28
+ }
@@ -0,0 +1,45 @@
1
+ // @ts-check
2
+ /** @import { IdentityPort } from "@econ-v1/ports" */
3
+ /** @import { DeviceBootState } from "../models/device-boot-state.js" */
4
+ /** @typedef {{ readonly identity: Pick<IdentityPort, "loadDelegation" | "loadLifecycle">; readonly now: () => string; }} DeviceBootStateServiceOptions */
5
+ export class DeviceBootStateService {
6
+ /** @type {DeviceBootStateServiceOptions["identity"]} */
7
+ #identity;
8
+ /** @type {() => string} */
9
+ #now;
10
+ /** @param {DeviceBootStateServiceOptions} options */
11
+ constructor(options) {
12
+ this.#identity = options.identity;
13
+ this.#now = options.now;
14
+ }
15
+ /** @returns {Promise<DeviceBootState>} */
16
+ async state() {
17
+ /** @type {"active" | "revoked" | undefined} */
18
+ let lifecycle;
19
+ try {
20
+ lifecycle = await this.#identity.loadLifecycle?.();
21
+ }
22
+ catch {
23
+ // A store we cannot read must fail toward pairing, never toward a crash or
24
+ // toward rendering cached data on an unverified device (spec §5).
25
+ return { kind: "unpaired" };
26
+ }
27
+ if (lifecycle === "revoked")
28
+ return { kind: "revoked" };
29
+ /** @type {Awaited<ReturnType<IdentityPort["loadDelegation"]>>} */
30
+ let delegation;
31
+ try {
32
+ delegation = await this.#identity.loadDelegation();
33
+ }
34
+ catch {
35
+ return { kind: "unpaired" };
36
+ }
37
+ if (delegation === undefined)
38
+ return { kind: "unpaired" };
39
+ const expiresAtMs = Date.parse(delegation.expiresAt);
40
+ const expired = Number.isNaN(expiresAtMs) || expiresAtMs <= Date.parse(this.#now());
41
+ return expired
42
+ ? { kind: "paired-expired", delegationExpiresAt: delegation.expiresAt }
43
+ : { kind: "paired", delegationExpiresAt: delegation.expiresAt };
44
+ }
45
+ }
@@ -0,0 +1,112 @@
1
+ // @ts-check
2
+ /** @typedef {import("@econ-v1/ports").DelayPort} DelayPort */
3
+ /** @typedef {"pending" | "approved" | "declined" | "expired" | "no_candidate" | "not_needed" | "failed"} DeviceCleanupState */
4
+ /** @typedef {{ cleanupStatus: (sessionId: string) => Promise<{ readonly state: DeviceCleanupState }>; requestCleanup: () => Promise<{ readonly sessionId: string; readonly state: DeviceCleanupState }> }} DeviceCleanupPort */
5
+ /** @typedef {{ cleanup: DeviceCleanupPort; delay: Pick<DelayPort, "wait">; publish: (state: DeviceCleanupState | undefined) => void }} DeviceCleanupServiceOptions */
6
+ const POLL_BASE_DELAY_MS = 2_000;
7
+ const POLL_MAX_DELAY_MS = 30_000;
8
+ /** Unchanged from the shell's original `cleanupPollDelayMs`: 2s -> 4s -> 8s -> 16s -> 30s
9
+ * ceiling. The owner may take anywhere from seconds to minutes to walk to the node's screen,
10
+ * and the pending-op TTL server-side is 5 minutes, so polling every couple of seconds the
11
+ * whole time would just be noise. */
12
+ /** @param {number} attempt */
13
+ function pollDelayMs(attempt) {
14
+ return Math.min(POLL_BASE_DELAY_MS * (2 ** attempt), POLL_MAX_DELAY_MS);
15
+ }
16
+ export class DeviceCleanupService {
17
+ /** @type {DeviceCleanupPort} */
18
+ #cleanup;
19
+ /** @type {Pick<DelayPort, "wait">} */
20
+ #delay;
21
+ /** @type {(state: DeviceCleanupState | undefined) => void} */
22
+ #publish;
23
+ /** Invalidates a stale poll continuation after `cancel()` or a fresh `start()` — the same
24
+ * generation-counter guard `OwnerClaimService`/`OnboardingFlowService` use for their own
25
+ * long-running polls. */
26
+ #generation = 0;
27
+ /** @type {DeviceCleanupState | undefined} */
28
+ #state;
29
+ /** @param {DeviceCleanupServiceOptions} options */
30
+ constructor(options) {
31
+ this.#cleanup = options.cleanup;
32
+ this.#delay = options.delay;
33
+ this.#publish = options.publish;
34
+ }
35
+ /** The live-query-facing read: `undefined` while no ceremony has ever run for the current
36
+ * generation (nothing requested yet, or `cancel()` cleared it). */
37
+ state() {
38
+ return this.#state;
39
+ }
40
+ /**
41
+ * Opens/resumes the node's one live cleanup session (`client-device.cleanup-request.v1`)
42
+ * and, if it comes back `pending`, starts the bounded-backoff poll loop. Safe to call again
43
+ * — each call invalidates whatever poll was previously in flight, matching the ceremony's
44
+ * original "Ask the owner" re-request behaviour.
45
+ *
46
+ * A `requestCleanup()` failure is deliberately NOT caught here: it propagates to the caller
47
+ * exactly as it did when `pairing-gate.ts` awaited the command directly, so the shell's own
48
+ * catch (which renders `cleanupState: "failed"` onto the still-locally-held device-limit
49
+ * card) keeps working unchanged — this service has no base error state to merge it onto.
50
+ */
51
+ async start() {
52
+ const generation = ++this.#generation;
53
+ const result = await this.#cleanup.requestCleanup();
54
+ if (generation !== this.#generation)
55
+ return result;
56
+ this.#state = result.state;
57
+ this.#publish(this.#state);
58
+ // The session id lives only in this closure and the one `#poll` call it seeds — nothing
59
+ // else ever needs to read it back, so it is not duplicated onto a field (there is no
60
+ // "current session id" accessor for the same reason `state()` has no "current attempt
61
+ // count" sibling: both are captured by the generation-guarded loop that owns them).
62
+ if (result.state === "pending")
63
+ void this.#poll(result.sessionId, generation);
64
+ return result;
65
+ }
66
+ /**
67
+ * Cancels any in-flight poll and clears the ceremony state. Called from every point that
68
+ * used to invalidate the shell's own poll loop: a client change, suspension, `showRevoked()`,
69
+ * and disconnect (`pairing-gate.ts`'s `client-device.cleanup-cancel.v1` command, fired at
70
+ * exactly those four points).
71
+ */
72
+ cancel() {
73
+ this.#generation += 1;
74
+ if (this.#state === undefined)
75
+ return;
76
+ this.#state = undefined;
77
+ this.#publish(undefined);
78
+ }
79
+ /**
80
+ * The poll loop `start()` hands off to. Only ever continues while the node keeps answering
81
+ * `pending` — every other {@link DeviceCleanupState} (including `failed`, produced here when
82
+ * `cleanupStatus()` itself throws) is terminal and stops the loop, which is the rule under
83
+ * test in `device-cleanup-service.test.ts`'s backoff/termination coverage.
84
+ */
85
+ /** @param {string} sessionId @param {number} generation */
86
+ async #poll(sessionId, generation) {
87
+ let attempt = 0;
88
+ for (;;) {
89
+ await this.#delay.wait(pollDelayMs(attempt));
90
+ if (generation !== this.#generation)
91
+ return;
92
+ let result;
93
+ try {
94
+ result = await this.#cleanup.cleanupStatus(sessionId);
95
+ }
96
+ catch {
97
+ if (generation !== this.#generation)
98
+ return;
99
+ this.#state = "failed";
100
+ this.#publish(this.#state);
101
+ return;
102
+ }
103
+ if (generation !== this.#generation)
104
+ return;
105
+ this.#state = result.state;
106
+ this.#publish(this.#state);
107
+ if (result.state !== "pending")
108
+ return;
109
+ attempt += 1;
110
+ }
111
+ }
112
+ }