@econ-v1/domain 0.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 (173) hide show
  1. package/dist/errors/index.d.ts +41 -0
  2. package/dist/errors/index.d.ts.map +1 -0
  3. package/dist/errors/index.js +45 -0
  4. package/dist/errors/index.js.map +1 -0
  5. package/dist/events/index.d.ts +55 -0
  6. package/dist/events/index.d.ts.map +1 -0
  7. package/dist/events/index.js +17 -0
  8. package/dist/events/index.js.map +1 -0
  9. package/dist/index.d.ts +47 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +24 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/models/agent.d.ts +12 -0
  14. package/dist/models/agent.d.ts.map +1 -0
  15. package/dist/models/agent.js +17 -0
  16. package/dist/models/agent.js.map +1 -0
  17. package/dist/models/conversation.d.ts +3 -0
  18. package/dist/models/conversation.d.ts.map +1 -0
  19. package/dist/models/conversation.js +2 -0
  20. package/dist/models/conversation.js.map +1 -0
  21. package/dist/models/delegation.d.ts +4 -0
  22. package/dist/models/delegation.d.ts.map +1 -0
  23. package/dist/models/delegation.js +23 -0
  24. package/dist/models/delegation.js.map +1 -0
  25. package/dist/models/device-identity.d.ts +4 -0
  26. package/dist/models/device-identity.d.ts.map +1 -0
  27. package/dist/models/device-identity.js +53 -0
  28. package/dist/models/device-identity.js.map +1 -0
  29. package/dist/models/graph.d.ts +20 -0
  30. package/dist/models/graph.d.ts.map +1 -0
  31. package/dist/models/graph.js +30 -0
  32. package/dist/models/graph.js.map +1 -0
  33. package/dist/models/index.d.ts +18 -0
  34. package/dist/models/index.d.ts.map +1 -0
  35. package/dist/models/index.js +8 -0
  36. package/dist/models/index.js.map +1 -0
  37. package/dist/models/json.d.ts +8 -0
  38. package/dist/models/json.d.ts.map +1 -0
  39. package/dist/models/json.js +2 -0
  40. package/dist/models/json.js.map +1 -0
  41. package/dist/models/l402.d.ts +15 -0
  42. package/dist/models/l402.d.ts.map +1 -0
  43. package/dist/models/l402.js +2 -0
  44. package/dist/models/l402.js.map +1 -0
  45. package/dist/models/live-query-definition.d.ts +22 -0
  46. package/dist/models/live-query-definition.d.ts.map +1 -0
  47. package/dist/models/live-query-definition.js +2 -0
  48. package/dist/models/live-query-definition.js.map +1 -0
  49. package/dist/models/outbox-intent.d.ts +10 -0
  50. package/dist/models/outbox-intent.d.ts.map +1 -0
  51. package/dist/models/outbox-intent.js +2 -0
  52. package/dist/models/outbox-intent.js.map +1 -0
  53. package/dist/models/outbox.d.ts +50 -0
  54. package/dist/models/outbox.d.ts.map +1 -0
  55. package/dist/models/outbox.js +133 -0
  56. package/dist/models/outbox.js.map +1 -0
  57. package/dist/models/owner-views.d.ts +67 -0
  58. package/dist/models/owner-views.d.ts.map +1 -0
  59. package/dist/models/owner-views.js +7 -0
  60. package/dist/models/owner-views.js.map +1 -0
  61. package/dist/models/payment.d.ts +32 -0
  62. package/dist/models/payment.d.ts.map +1 -0
  63. package/dist/models/payment.js +3 -0
  64. package/dist/models/payment.js.map +1 -0
  65. package/dist/models/query-diff.d.ts +7 -0
  66. package/dist/models/query-diff.d.ts.map +1 -0
  67. package/dist/models/query-diff.js +2 -0
  68. package/dist/models/query-diff.js.map +1 -0
  69. package/dist/models/repository-write.d.ts +7 -0
  70. package/dist/models/repository-write.d.ts.map +1 -0
  71. package/dist/models/repository-write.js +2 -0
  72. package/dist/models/repository-write.js.map +1 -0
  73. package/dist/models/revisioned-commands.d.ts +24 -0
  74. package/dist/models/revisioned-commands.d.ts.map +1 -0
  75. package/dist/models/revisioned-commands.js +35 -0
  76. package/dist/models/revisioned-commands.js.map +1 -0
  77. package/dist/models/revisioned.d.ts +19 -0
  78. package/dist/models/revisioned.d.ts.map +1 -0
  79. package/dist/models/revisioned.js +2 -0
  80. package/dist/models/revisioned.js.map +1 -0
  81. package/dist/models/settings.d.ts +5 -0
  82. package/dist/models/settings.d.ts.map +1 -0
  83. package/dist/models/settings.js +2 -0
  84. package/dist/models/settings.js.map +1 -0
  85. package/dist/models/sync.d.ts +50 -0
  86. package/dist/models/sync.d.ts.map +1 -0
  87. package/dist/models/sync.js +2 -0
  88. package/dist/models/sync.js.map +1 -0
  89. package/dist/models/wallet.d.ts +52 -0
  90. package/dist/models/wallet.d.ts.map +1 -0
  91. package/dist/models/wallet.js +2 -0
  92. package/dist/models/wallet.js.map +1 -0
  93. package/dist/queries/conversations.d.ts +12 -0
  94. package/dist/queries/conversations.d.ts.map +1 -0
  95. package/dist/queries/conversations.js +59 -0
  96. package/dist/queries/conversations.js.map +1 -0
  97. package/dist/queries/notifications.d.ts +5 -0
  98. package/dist/queries/notifications.d.ts.map +1 -0
  99. package/dist/queries/notifications.js +27 -0
  100. package/dist/queries/notifications.js.map +1 -0
  101. package/dist/queries/revisioned.d.ts +17 -0
  102. package/dist/queries/revisioned.d.ts.map +1 -0
  103. package/dist/queries/revisioned.js +58 -0
  104. package/dist/queries/revisioned.js.map +1 -0
  105. package/dist/queries/wallet.d.ts +11 -0
  106. package/dist/queries/wallet.d.ts.map +1 -0
  107. package/dist/queries/wallet.js +42 -0
  108. package/dist/queries/wallet.js.map +1 -0
  109. package/dist/services/conversation-service.d.ts +42 -0
  110. package/dist/services/conversation-service.d.ts.map +1 -0
  111. package/dist/services/conversation-service.js +95 -0
  112. package/dist/services/conversation-service.js.map +1 -0
  113. package/dist/services/conversation-sync.d.ts +7 -0
  114. package/dist/services/conversation-sync.d.ts.map +1 -0
  115. package/dist/services/conversation-sync.js +9 -0
  116. package/dist/services/conversation-sync.js.map +1 -0
  117. package/dist/services/gossip-service.d.ts +18 -0
  118. package/dist/services/gossip-service.d.ts.map +1 -0
  119. package/dist/services/gossip-service.js +53 -0
  120. package/dist/services/gossip-service.js.map +1 -0
  121. package/dist/services/lan-candidate-refresh-service.d.ts +11 -0
  122. package/dist/services/lan-candidate-refresh-service.d.ts.map +1 -0
  123. package/dist/services/lan-candidate-refresh-service.js +32 -0
  124. package/dist/services/lan-candidate-refresh-service.js.map +1 -0
  125. package/dist/services/outbox-service.d.ts +47 -0
  126. package/dist/services/outbox-service.d.ts.map +1 -0
  127. package/dist/services/outbox-service.js +151 -0
  128. package/dist/services/outbox-service.js.map +1 -0
  129. package/dist/services/owner-view-service.d.ts +10 -0
  130. package/dist/services/owner-view-service.d.ts.map +1 -0
  131. package/dist/services/owner-view-service.js +21 -0
  132. package/dist/services/owner-view-service.js.map +1 -0
  133. package/dist/services/peer-directory-service.d.ts +26 -0
  134. package/dist/services/peer-directory-service.d.ts.map +1 -0
  135. package/dist/services/peer-directory-service.js +79 -0
  136. package/dist/services/peer-directory-service.js.map +1 -0
  137. package/dist/services/revisioned-domain-service.d.ts +46 -0
  138. package/dist/services/revisioned-domain-service.d.ts.map +1 -0
  139. package/dist/services/revisioned-domain-service.js +205 -0
  140. package/dist/services/revisioned-domain-service.js.map +1 -0
  141. package/dist/services/session-service.d.ts +10 -0
  142. package/dist/services/session-service.d.ts.map +1 -0
  143. package/dist/services/session-service.js +20 -0
  144. package/dist/services/session-service.js.map +1 -0
  145. package/dist/services/sync/authoritative-mirror.d.ts +24 -0
  146. package/dist/services/sync/authoritative-mirror.d.ts.map +1 -0
  147. package/dist/services/sync/authoritative-mirror.js +71 -0
  148. package/dist/services/sync/authoritative-mirror.js.map +1 -0
  149. package/dist/services/sync/index.d.ts +8 -0
  150. package/dist/services/sync/index.d.ts.map +1 -0
  151. package/dist/services/sync/index.js +4 -0
  152. package/dist/services/sync/index.js.map +1 -0
  153. package/dist/services/sync/log-sync.d.ts +12 -0
  154. package/dist/services/sync/log-sync.d.ts.map +1 -0
  155. package/dist/services/sync/log-sync.js +20 -0
  156. package/dist/services/sync/log-sync.js.map +1 -0
  157. package/dist/services/sync/revision-merge.d.ts +20 -0
  158. package/dist/services/sync/revision-merge.d.ts.map +1 -0
  159. package/dist/services/sync/revision-merge.js +46 -0
  160. package/dist/services/sync/revision-merge.js.map +1 -0
  161. package/dist/services/sync/strategy.d.ts +2 -0
  162. package/dist/services/sync/strategy.d.ts.map +1 -0
  163. package/dist/services/sync/strategy.js +2 -0
  164. package/dist/services/sync/strategy.js.map +1 -0
  165. package/dist/services/transport-manager.d.ts +22 -0
  166. package/dist/services/transport-manager.d.ts.map +1 -0
  167. package/dist/services/transport-manager.js +133 -0
  168. package/dist/services/transport-manager.js.map +1 -0
  169. package/dist/services/wallet-mirror-service.d.ts +46 -0
  170. package/dist/services/wallet-mirror-service.d.ts.map +1 -0
  171. package/dist/services/wallet-mirror-service.js +334 -0
  172. package/dist/services/wallet-mirror-service.js.map +1 -0
  173. package/package.json +56 -0
@@ -0,0 +1,11 @@
1
+ import type { HomeNodeCandidate, HomeNodeClient, IdentityPort } from "@econ-v1/ports";
2
+ export interface LanCandidateRefreshServiceOptions {
3
+ readonly identity: IdentityPort;
4
+ readonly now?: () => string;
5
+ }
6
+ export declare class LanCandidateRefreshService {
7
+ #private;
8
+ constructor(options: LanCandidateRefreshServiceOptions);
9
+ refresh(home: HomeNodeClient): Promise<readonly HomeNodeCandidate[]>;
10
+ }
11
+ //# sourceMappingURL=lan-candidate-refresh-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lan-candidate-refresh-service.d.ts","sourceRoot":"","sources":["../../src/services/lan-candidate-refresh-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACb,MAAM,gBAAgB,CAAC;AAIxB,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,qBAAa,0BAA0B;;gBAIzB,OAAO,EAAE,iCAAiC;IAKhD,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC;CAqB3E"}
@@ -0,0 +1,32 @@
1
+ import { nodeMetadataCandidates, secureAddresses } from "./peer-directory-service.js";
2
+ export class LanCandidateRefreshService {
3
+ #identity;
4
+ #now;
5
+ constructor(options) {
6
+ this.#identity = options.identity;
7
+ this.#now = options.now ?? (() => new Date().toISOString());
8
+ }
9
+ async refresh(home) {
10
+ const metadata = await home.invoke({
11
+ operation: "GET /api/v2/node/metadata",
12
+ payload: {},
13
+ });
14
+ const current = await this.#identity.loadConnectionMetadata();
15
+ const refreshed = nodeMetadataCandidates(metadata);
16
+ const directAddresses = secureAddresses([
17
+ ...(current?.directAddresses ?? []),
18
+ ...refreshed.map((candidate) => candidate.address),
19
+ ]);
20
+ const next = {
21
+ directAddresses,
22
+ lastConnectedAt: this.#now(),
23
+ ...(current?.relayUrl === undefined ? {} : { relayUrl: current.relayUrl }),
24
+ };
25
+ await this.#identity.storeConnectionMetadata(next);
26
+ const id = refreshed[0]?.id;
27
+ if (id === undefined)
28
+ return [];
29
+ return directAddresses.map((address) => ({ address, id, path: "lan-direct" }));
30
+ }
31
+ }
32
+ //# sourceMappingURL=lan-candidate-refresh-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lan-candidate-refresh-service.js","sourceRoot":"","sources":["../../src/services/lan-candidate-refresh-service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAyB,MAAM,6BAA6B,CAAC;AAO7G,MAAM,OAAO,0BAA0B;IAC5B,SAAS,CAAe;IACxB,IAAI,CAAe;IAE5B,YAAY,OAA0C;QACpD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAoB;QAChC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAmB;YACnD,SAAS,EAAE,2BAA2B;YACtC,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,sBAAsB,EAAE,CAAC;QAC9D,MAAM,SAAS,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,eAAe,GAAG,eAAe,CAAC;YACtC,GAAG,CAAC,OAAO,EAAE,eAAe,IAAI,EAAE,CAAC;YACnC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC;SACnD,CAAC,CAAC;QACH,MAAM,IAAI,GAA6B;YACrC,eAAe;YACf,eAAe,EAAE,IAAI,CAAC,IAAI,EAAE;YAC5B,GAAG,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;SAC3E,CAAC;QACF,MAAM,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAChC,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,YAAqB,EAAE,CAAC,CAAC,CAAC;IAC1F,CAAC;CACF"}
@@ -0,0 +1,47 @@
1
+ import { type OutboxEntry, type OutboxIntentInput } from "../models/outbox.js";
2
+ export interface OutboxPersistence {
3
+ create(entry: OutboxEntry): Promise<void>;
4
+ delete(id: string): Promise<void>;
5
+ list(domain?: string): Promise<readonly OutboxEntry[]>;
6
+ read(id: string): Promise<OutboxEntry | undefined>;
7
+ update(entry: OutboxEntry): Promise<void>;
8
+ }
9
+ export interface OutboxServiceOptions {
10
+ readonly maxAttempts?: number;
11
+ readonly now?: () => string;
12
+ }
13
+ export interface ClaimOptions {
14
+ readonly leaseMs?: number;
15
+ readonly now?: string;
16
+ readonly revalidateAuthoritative?: () => Promise<boolean>;
17
+ }
18
+ export interface FailureOptions {
19
+ readonly now?: string;
20
+ readonly owner?: string;
21
+ readonly retryable: boolean;
22
+ }
23
+ export declare class OutboxIdempotencyConflictError extends Error {
24
+ readonly idempotencyKey: string;
25
+ constructor(idempotencyKey: string);
26
+ }
27
+ export declare class OutboxTransitionError extends Error {
28
+ constructor(message: string);
29
+ }
30
+ export declare class OutboxRevalidationRequiredError extends OutboxTransitionError {
31
+ constructor(id: string);
32
+ }
33
+ export declare class OutboxService {
34
+ #private;
35
+ constructor(persistence: OutboxPersistence, options?: OutboxServiceOptions);
36
+ enqueue(input: OutboxIntentInput): Promise<OutboxEntry>;
37
+ claimNext(domain: string, owner: string, options?: ClaimOptions): Promise<OutboxEntry | undefined>;
38
+ acknowledge(id: string, owner: string, now?: string): Promise<OutboxEntry>;
39
+ fail(id: string, reason: string, options: FailureOptions): Promise<OutboxEntry>;
40
+ conflict(id: string, reason: string, owner?: string, now?: string): Promise<OutboxEntry>;
41
+ cancel(id: string): Promise<boolean>;
42
+ recoverExpiredLeases(now?: string, domain?: string): Promise<number>;
43
+ private requireEntry;
44
+ private assertInflightOwner;
45
+ private save;
46
+ }
47
+ //# sourceMappingURL=outbox-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-service.d.ts","sourceRoot":"","sources":["../../src/services/outbox-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EAGvB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IACnD,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;gBAEpB,cAAc,EAAE,MAAM;CAKnC;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,+BAAgC,SAAQ,qBAAqB;gBAC5D,EAAE,EAAE,MAAM;CAIvB;AAED,qBAAa,aAAa;;gBAKZ,WAAW,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB;IASxE,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC;IAavD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IA2BtG,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAM/E,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAW/E,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ7F,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQpC,oBAAoB,CAAC,GAAG,SAAc,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAUjE,YAAY;IAM1B,OAAO,CAAC,mBAAmB;YAMb,IAAI;CAWnB"}
@@ -0,0 +1,151 @@
1
+ import { canonicalOutboxPayload, createOutboxIntent, validateOutboxIntent, } from "../models/outbox.js";
2
+ export class OutboxIdempotencyConflictError extends Error {
3
+ idempotencyKey;
4
+ constructor(idempotencyKey) {
5
+ super("Outbox idempotency key belongs to a different command");
6
+ this.name = "OutboxIdempotencyConflictError";
7
+ this.idempotencyKey = idempotencyKey;
8
+ }
9
+ }
10
+ export class OutboxTransitionError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "OutboxTransitionError";
14
+ }
15
+ }
16
+ export class OutboxRevalidationRequiredError extends OutboxTransitionError {
17
+ constructor(id) {
18
+ super(`Authoritative outbox intent ${id} requires fresh-state revalidation`);
19
+ this.name = "OutboxRevalidationRequiredError";
20
+ }
21
+ }
22
+ export class OutboxService {
23
+ #maxAttempts;
24
+ #now;
25
+ #persistence;
26
+ constructor(persistence, options = {}) {
27
+ this.#maxAttempts = options.maxAttempts ?? 3;
28
+ if (!Number.isInteger(this.#maxAttempts) || this.#maxAttempts < 1) {
29
+ throw new RangeError("Outbox maxAttempts must be a positive integer");
30
+ }
31
+ this.#now = options.now ?? (() => new Date().toISOString());
32
+ this.#persistence = persistence;
33
+ }
34
+ async enqueue(input) {
35
+ const entry = createOutboxIntent(input, { maxAttempts: this.#maxAttempts, now: this.#now() });
36
+ const existing = (await this.#persistence.list()).find((candidate) => candidate.idempotencyKey === entry.idempotencyKey);
37
+ if (existing !== undefined) {
38
+ if (sameCommand(existing, entry))
39
+ return existing;
40
+ throw new OutboxIdempotencyConflictError(entry.idempotencyKey);
41
+ }
42
+ await this.#persistence.create(entry);
43
+ return entry;
44
+ }
45
+ async claimNext(domain, owner, options = {}) {
46
+ requireNonEmpty(owner, "lease owner");
47
+ const now = options.now ?? this.#now();
48
+ await this.recoverExpiredLeases(now, domain);
49
+ const entries = [...await this.#persistence.list(domain)].sort(compareEntries);
50
+ for (const entry of entries) {
51
+ if (isTerminal(entry.state))
52
+ continue;
53
+ if (entry.state === "inflight")
54
+ return undefined;
55
+ if (entry.authority === "authoritative") {
56
+ if (options.revalidateAuthoritative === undefined || !await options.revalidateAuthoritative()) {
57
+ throw new OutboxRevalidationRequiredError(entry.id);
58
+ }
59
+ }
60
+ const claimed = validateOutboxIntent({
61
+ ...entry,
62
+ attemptCount: entry.attemptCount + 1,
63
+ lease: { expiresAt: new Date(Date.parse(now) + (options.leaseMs ?? 30_000)).toISOString(), owner },
64
+ state: "inflight",
65
+ updatedAt: now,
66
+ });
67
+ if (claimed.lease === undefined)
68
+ throw new OutboxTransitionError("Failed to create outbox lease");
69
+ await this.#persistence.update(claimed);
70
+ return claimed;
71
+ }
72
+ return undefined;
73
+ }
74
+ async acknowledge(id, owner, now = this.#now()) {
75
+ const current = await this.requireEntry(id);
76
+ this.assertInflightOwner(current, owner);
77
+ return await this.save(current, { lease: undefined, state: "acked", updatedAt: now });
78
+ }
79
+ async fail(id, reason, options) {
80
+ requireNonEmpty(reason, "failure reason");
81
+ const current = await this.requireEntry(id);
82
+ if (options.owner !== undefined)
83
+ this.assertInflightOwner(current, options.owner);
84
+ else if (current.state !== "inflight")
85
+ throw new OutboxTransitionError(`Outbox intent ${id} is not inflight`);
86
+ const terminal = !options.retryable || current.attemptCount >= current.maxAttempts;
87
+ return await this.save(current, terminal
88
+ ? { lease: undefined, state: "failed", terminalReason: reason, updatedAt: options.now ?? this.#now() }
89
+ : { lease: undefined, state: "pending", updatedAt: options.now ?? this.#now() });
90
+ }
91
+ async conflict(id, reason, owner, now = this.#now()) {
92
+ requireNonEmpty(reason, "conflict reason");
93
+ const current = await this.requireEntry(id);
94
+ if (owner !== undefined)
95
+ this.assertInflightOwner(current, owner);
96
+ else if (current.state !== "inflight")
97
+ throw new OutboxTransitionError(`Outbox intent ${id} is not inflight`);
98
+ return await this.save(current, { conflictReason: reason, lease: undefined, state: "conflict", updatedAt: now });
99
+ }
100
+ async cancel(id) {
101
+ const current = await this.#persistence.read(id);
102
+ if (current === undefined)
103
+ return false;
104
+ if (current.state !== "pending")
105
+ throw new OutboxTransitionError(`Only pending outbox intents can be cancelled: ${id}`);
106
+ await this.#persistence.delete(id);
107
+ return true;
108
+ }
109
+ async recoverExpiredLeases(now = this.#now(), domain) {
110
+ let recovered = 0;
111
+ for (const entry of await this.#persistence.list(domain)) {
112
+ if (entry.state !== "inflight" || (entry.lease !== undefined && Date.parse(entry.lease.expiresAt) > Date.parse(now)))
113
+ continue;
114
+ await this.save(entry, { lease: undefined, state: "pending", updatedAt: now });
115
+ recovered += 1;
116
+ }
117
+ return recovered;
118
+ }
119
+ async requireEntry(id) {
120
+ const entry = await this.#persistence.read(id);
121
+ if (entry === undefined)
122
+ throw new OutboxTransitionError(`Outbox intent was not found: ${id}`);
123
+ return validateOutboxIntent(entry);
124
+ }
125
+ assertInflightOwner(entry, owner) {
126
+ if (entry.state !== "inflight" || entry.lease?.owner !== owner) {
127
+ throw new OutboxTransitionError(`Outbox lease is not held by ${owner}`);
128
+ }
129
+ }
130
+ async save(current, changes) {
131
+ const updated = validateOutboxIntent({ ...current, ...changes });
132
+ await this.#persistence.update(updated);
133
+ return updated;
134
+ }
135
+ }
136
+ function compareEntries(left, right) {
137
+ return left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id);
138
+ }
139
+ function isTerminal(state) {
140
+ return state === "acked" || state === "failed" || state === "conflict";
141
+ }
142
+ function sameCommand(left, right) {
143
+ return left.domain === right.domain &&
144
+ left.authority === right.authority &&
145
+ canonicalOutboxPayload(left.payload) === canonicalOutboxPayload(right.payload);
146
+ }
147
+ function requireNonEmpty(value, label) {
148
+ if (value.trim() === "")
149
+ throw new TypeError(`${label} must be a non-empty string`);
150
+ }
151
+ //# sourceMappingURL=outbox-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-service.js","sourceRoot":"","sources":["../../src/services/outbox-service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAIlB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AA2B7B,MAAM,OAAO,8BAA+B,SAAQ,KAAK;IAC9C,cAAc,CAAS;IAEhC,YAAY,cAAsB;QAChC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC/D,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;QAC7C,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,+BAAgC,SAAQ,qBAAqB;IACxE,YAAY,EAAU;QACpB,KAAK,CAAC,+BAA+B,EAAE,oCAAoC,CAAC,CAAC;QAC7E,IAAI,CAAC,IAAI,GAAG,iCAAiC,CAAC;IAChD,CAAC;CACF;AAED,MAAM,OAAO,aAAa;IACf,YAAY,CAAS;IACrB,IAAI,CAAe;IACnB,YAAY,CAAoB;IAEzC,YAAY,WAA8B,EAAE,UAAgC,EAAE;QAC5E,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,YAAY,GAAG,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,UAAU,CAAC,+CAA+C,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAwB;QACpC,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9F,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CACpD,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc,CACjE,CAAC;QACF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC;YAClD,MAAM,IAAI,8BAA8B,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,KAAa,EAAE,UAAwB,EAAE;QACvE,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;gBAAE,SAAS;YACtC,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU;gBAAE,OAAO,SAAS,CAAC;YACjD,IAAI,KAAK,CAAC,SAAS,KAAK,eAAe,EAAE,CAAC;gBACxC,IAAI,OAAO,CAAC,uBAAuB,KAAK,SAAS,IAAI,CAAC,MAAM,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC;oBAC9F,MAAM,IAAI,+BAA+B,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GAAG,oBAAoB,CAAC;gBACnC,GAAG,KAAK;gBACR,YAAY,EAAE,KAAK,CAAC,YAAY,GAAG,CAAC;gBACpC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE;gBAClG,KAAK,EAAE,UAAU;gBACjB,SAAS,EAAE,GAAG;aACf,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;gBAAE,MAAM,IAAI,qBAAqB,CAAC,+BAA+B,CAAC,CAAC;YAClG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACxC,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,KAAa,EAAE,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE;QAC5D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAU,EAAE,MAAc,EAAE,OAAuB;QAC5D,eAAe,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;aAC7E,IAAI,OAAO,CAAC,KAAK,KAAK,UAAU;YAAE,MAAM,IAAI,qBAAqB,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;QAC9G,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;QACnF,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ;YACtC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE;YACtG,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU,EAAE,MAAc,EAAE,KAAc,EAAE,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE;QAC1E,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;aAC7D,IAAI,OAAO,CAAC,KAAK,KAAK,UAAU;YAAE,MAAM,IAAI,qBAAqB,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;QAC9G,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACnH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QACxC,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,qBAAqB,CAAC,iDAAiD,EAAE,EAAE,CAAC,CAAC;QACxH,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,MAAe;QAC3D,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,KAAK,MAAM,KAAK,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACzD,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAAE,SAAS;YAC/H,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;YAC/E,SAAS,IAAI,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,EAAU;QACnC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,qBAAqB,CAAC,gCAAgC,EAAE,EAAE,CAAC,CAAC;QAC/F,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAEO,mBAAmB,CAAC,KAAkB,EAAE,KAAa;QAC3D,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,IAAI,KAAK,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,EAAE,CAAC;YAC/D,MAAM,IAAI,qBAAqB,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAI,CAChB,OAAoB,EACpB,OAGC;QAED,MAAM,OAAO,GAAG,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;QACjE,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,SAAS,cAAc,CAAC,IAAiB,EAAE,KAAkB;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,UAAU,CAAC,KAAkB;IACpC,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,CAAC;AACzE,CAAC;AAED,SAAS,WAAW,CAAC,IAAiB,EAAE,KAAkB;IACxD,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;QACjC,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;QAClC,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,eAAe,CAAC,KAAa,EAAE,KAAa;IACnD,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,GAAG,KAAK,6BAA6B,CAAC,CAAC;AACtF,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { FeedItem, FeedQuery, HomeSummary, OperationalSummary, OwnerViewRepository, PeerDirectoryEntry, PeerDirectoryQuery } from "../models/owner-views.js";
2
+ export declare class OwnerViewService {
3
+ #private;
4
+ constructor(repository: OwnerViewRepository);
5
+ readHome(): Promise<readonly HomeSummary[]>;
6
+ listPeers(params: PeerDirectoryQuery): Promise<readonly PeerDirectoryEntry[]>;
7
+ listFeed(params: FeedQuery): Promise<readonly FeedItem[]>;
8
+ readOperational(): Promise<readonly OperationalSummary[]>;
9
+ }
10
+ //# sourceMappingURL=owner-view-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-view-service.d.ts","sourceRoot":"","sources":["../../src/services/owner-view-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAElC,qBAAa,gBAAgB;;gBAGf,UAAU,EAAE,mBAAmB;IAIrC,QAAQ,IAAI,OAAO,CAAC,SAAS,WAAW,EAAE,CAAC;IAK3C,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC;IAI7E,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,QAAQ,EAAE,CAAC;IAIzD,eAAe,IAAI,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC;CAIhE"}
@@ -0,0 +1,21 @@
1
+ export class OwnerViewService {
2
+ #repository;
3
+ constructor(repository) {
4
+ this.#repository = repository;
5
+ }
6
+ async readHome() {
7
+ const summary = await this.#repository.readHomeSummary();
8
+ return summary === undefined ? [] : [summary];
9
+ }
10
+ async listPeers(params) {
11
+ return await this.#repository.listPeers(params);
12
+ }
13
+ async listFeed(params) {
14
+ return await this.#repository.listFeed(params);
15
+ }
16
+ async readOperational() {
17
+ const summary = await this.#repository.readOperationalSummary();
18
+ return summary === undefined ? [] : [summary];
19
+ }
20
+ }
21
+ //# sourceMappingURL=owner-view-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-view-service.js","sourceRoot":"","sources":["../../src/services/owner-view-service.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,gBAAgB;IAClB,WAAW,CAAsB;IAE1C,YAAY,UAA+B;QACzC,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC;QACzD,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAA0B;QACxC,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAiB;QAC9B,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,sBAAsB,EAAE,CAAC;QAChE,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ import type { HomeNodeCandidate, HomeNodeClient } from "@econ-v1/ports";
2
+ export interface PeerAdvertisement {
3
+ readonly directAddresses: readonly string[];
4
+ readonly did: string;
5
+ readonly irohNodeId: string;
6
+ readonly observedAt: string;
7
+ }
8
+ export interface NodeMetadataWire {
9
+ readonly iroh_direct_addresses?: unknown;
10
+ readonly iroh_endpoint_id?: unknown;
11
+ readonly listening_addresses?: unknown;
12
+ readonly node_id?: unknown;
13
+ }
14
+ export declare class PeerDirectoryService {
15
+ #private;
16
+ upsert(advertisement: PeerAdvertisement): boolean;
17
+ remove(irohNodeId: string): boolean;
18
+ get(irohNodeId: string): PeerAdvertisement | undefined;
19
+ list(): readonly PeerAdvertisement[];
20
+ refreshLanCandidates(home: HomeNodeClient): Promise<readonly HomeNodeCandidate[]>;
21
+ }
22
+ export declare function normalizeAdvertisement(advertisement: PeerAdvertisement): PeerAdvertisement;
23
+ export declare function nodeMetadataCandidates(metadata: NodeMetadataWire): readonly HomeNodeCandidate[];
24
+ export declare function uniqueAddresses(addresses: readonly string[]): readonly string[];
25
+ export declare function secureAddresses(addresses: readonly string[]): readonly string[];
26
+ //# sourceMappingURL=peer-directory-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-directory-service.d.ts","sourceRoot":"","sources":["../../src/services/peer-directory-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAID,qBAAa,oBAAoB;;IAG/B,MAAM,CAAC,aAAa,EAAE,iBAAiB,GAAG,OAAO;IAQjD,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAInC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAItD,IAAI,IAAI,SAAS,iBAAiB,EAAE;IAI9B,oBAAoB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC;CAOxF;AAED,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,iBAAiB,GAAG,iBAAiB,CAU1F;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,iBAAiB,EAAE,CAQ/F;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAE/E;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAK/E"}
@@ -0,0 +1,79 @@
1
+ const MAX_DIRECT_ADDRESSES = 8;
2
+ export class PeerDirectoryService {
3
+ #peers = new Map();
4
+ upsert(advertisement) {
5
+ const normalized = normalizeAdvertisement(advertisement);
6
+ const current = this.#peers.get(normalized.irohNodeId);
7
+ if (current !== undefined && current.observedAt >= normalized.observedAt)
8
+ return false;
9
+ this.#peers.set(normalized.irohNodeId, normalized);
10
+ return true;
11
+ }
12
+ remove(irohNodeId) {
13
+ return this.#peers.delete(irohNodeId);
14
+ }
15
+ get(irohNodeId) {
16
+ return this.#peers.get(irohNodeId);
17
+ }
18
+ list() {
19
+ return [...this.#peers.values()].sort((left, right) => right.observedAt.localeCompare(left.observedAt));
20
+ }
21
+ async refreshLanCandidates(home) {
22
+ const metadata = await home.invoke({
23
+ operation: "GET /api/v2/node/metadata",
24
+ payload: {},
25
+ });
26
+ return nodeMetadataCandidates(metadata);
27
+ }
28
+ }
29
+ export function normalizeAdvertisement(advertisement) {
30
+ requireText(advertisement.did, "Peer advertisement did");
31
+ requireText(advertisement.irohNodeId, "Peer advertisement irohNodeId");
32
+ requireText(advertisement.observedAt, "Peer advertisement observedAt");
33
+ return {
34
+ directAddresses: secureAddresses(advertisement.directAddresses),
35
+ did: advertisement.did,
36
+ irohNodeId: advertisement.irohNodeId,
37
+ observedAt: advertisement.observedAt,
38
+ };
39
+ }
40
+ export function nodeMetadataCandidates(metadata) {
41
+ const id = textOrUndefined(metadata.iroh_endpoint_id) ?? textOrUndefined(metadata.node_id);
42
+ if (id === undefined)
43
+ throw new TypeError("Node metadata is missing an iroh endpoint id");
44
+ const addresses = secureAddresses([
45
+ ...stringArray(metadata.listening_addresses),
46
+ ...stringArray(metadata.iroh_direct_addresses),
47
+ ]).slice(0, MAX_DIRECT_ADDRESSES);
48
+ return addresses.map((address) => ({ address, id, path: "lan-direct" }));
49
+ }
50
+ export function uniqueAddresses(addresses) {
51
+ return [...new Set(addresses.map((address) => address.trim()).filter((address) => address.length > 0))];
52
+ }
53
+ export function secureAddresses(addresses) {
54
+ return uniqueAddresses(addresses)
55
+ .map(toSecureAddress)
56
+ .filter((address) => address !== undefined)
57
+ .slice(0, MAX_DIRECT_ADDRESSES);
58
+ }
59
+ function stringArray(value) {
60
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
61
+ }
62
+ function textOrUndefined(value) {
63
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
64
+ }
65
+ function requireText(value, label) {
66
+ if (typeof value !== "string" || value.trim().length === 0)
67
+ throw new TypeError(`${label} must be non-empty`);
68
+ }
69
+ function toSecureAddress(address) {
70
+ const value = address.includes("://") ? address : `https://${address}`;
71
+ const match = /^https:\/\/([^/?#:\s]+)(?::(\d{1,5}))?(?:\/)?$/u.exec(value);
72
+ if (match === null)
73
+ return undefined;
74
+ const port = match[2] === undefined ? undefined : Number(match[2]);
75
+ if (port !== undefined && (port < 1 || port > 65535))
76
+ return undefined;
77
+ return port === undefined ? `https://${match[1]}` : `https://${match[1]}:${port}`;
78
+ }
79
+ //# sourceMappingURL=peer-directory-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-directory-service.js","sourceRoot":"","sources":["../../src/services/peer-directory-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { OutboxRepository } from "@econ-v1/ports";
2
+ import { type RevisionCommand, type RevisionCommandResult, type RevisionConflictRecord, type RevisionEntity, type RevisionResolution, type RevisionedDomain } from "../models/revisioned.js";
3
+ import type { JsonValue } from "../models/json.js";
4
+ export interface RevisionedDomainRepository<TValue extends JsonValue> {
5
+ apply(entity: RevisionEntity<TValue>): Promise<void>;
6
+ command(input: RevisionCommand<TValue>): Promise<RevisionCommandResult<TValue>>;
7
+ read(id: string): Promise<RevisionEntity<TValue> | undefined>;
8
+ }
9
+ export interface RevisionedDomainServiceOptions<TValue extends JsonValue> {
10
+ readonly domain: RevisionedDomain;
11
+ readonly now?: () => string;
12
+ readonly outbox: OutboxRepository;
13
+ readonly repository: RevisionedDomainRepository<TValue>;
14
+ }
15
+ export type RevisionedWriteResult<TValue extends JsonValue> = {
16
+ readonly entity: RevisionEntity<TValue>;
17
+ readonly status: "accepted";
18
+ } | {
19
+ readonly conflict: RevisionConflictRecord<TValue>;
20
+ readonly status: "conflict";
21
+ };
22
+ export type RevisionResolutionResult<TValue extends JsonValue> = {
23
+ readonly entity: RevisionEntity<TValue>;
24
+ readonly status: "accepted";
25
+ } | {
26
+ readonly entity: RevisionEntity<TValue>;
27
+ readonly status: "discarded";
28
+ } | {
29
+ readonly conflict: RevisionConflictRecord<TValue>;
30
+ readonly status: "conflict";
31
+ };
32
+ export interface RevisionedWriteInput<TValue extends JsonValue> {
33
+ readonly baseRev?: number;
34
+ readonly id: string;
35
+ readonly idempotencyKey: string;
36
+ readonly value: TValue;
37
+ }
38
+ export declare class RevisionedDomainService<TValue extends JsonValue> {
39
+ #private;
40
+ constructor(options: RevisionedDomainServiceOptions<TValue>);
41
+ update(input: RevisionedWriteInput<TValue>): Promise<RevisionedWriteResult<TValue>>;
42
+ listConflicts(): Promise<readonly RevisionConflictRecord<TValue>[]>;
43
+ resolveConflict(conflictIdValue: string, resolution: RevisionResolution): Promise<RevisionResolutionResult<TValue>>;
44
+ }
45
+ export declare function parseRevisionConflictRecord<TValue extends JsonValue>(value: unknown, expectedDomain?: RevisionedDomain): RevisionConflictRecord<TValue>;
46
+ //# sourceMappingURL=revisioned-domain-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revisioned-domain-service.d.ts","sourceRoot":"","sources":["../../src/services/revisioned-domain-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,gBAAgB,EAEjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAE1B,KAAK,sBAAsB,EAE3B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,MAAM,WAAW,0BAA0B,CAAC,MAAM,SAAS,SAAS;IAClE,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAChF,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,8BAA8B,CAAC,MAAM,SAAS,SAAS;IACtE,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,0BAA0B,CAAC,MAAM,CAAC,CAAC;CACzD;AAED,MAAM,MAAM,qBAAqB,CAAC,MAAM,SAAS,SAAS,IACtD;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACxE;IAAE,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEvF,MAAM,MAAM,wBAAwB,CAAC,MAAM,SAAS,SAAS,IACzD;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GACxE;IAAE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEvF,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,SAAS;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,uBAAuB,CAAC,MAAM,SAAS,SAAS;;gBAM/C,OAAO,EAAE,8BAA8B,CAAC,MAAM,CAAC;IAOrD,MAAM,CAAC,KAAK,EAAE,oBAAoB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAgBnF,aAAa,IAAI,OAAO,CAAC,SAAS,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC;IAKnE,eAAe,CACnB,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,kBAAkB,GAC7B,OAAO,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC;CAkF7C;AAED,wBAAgB,2BAA2B,CAAC,MAAM,SAAS,SAAS,EAClE,KAAK,EAAE,OAAO,EACd,cAAc,CAAC,EAAE,gBAAgB,GAChC,sBAAsB,CAAC,MAAM,CAAC,CAsBhC"}