@onlyne/lib 3.0.0 → 3.2.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 (121) hide show
  1. package/README.md +6 -3
  2. package/dist/awaited.d.ts +43 -0
  3. package/dist/awaited.d.ts.map +1 -0
  4. package/dist/awaited.js +82 -0
  5. package/dist/awaited.js.map +1 -0
  6. package/dist/client.d.ts +140 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +1772 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/core.d.ts +36 -0
  11. package/dist/core.d.ts.map +1 -0
  12. package/dist/core.js +122 -0
  13. package/dist/core.js.map +1 -0
  14. package/dist/delivery.d.ts +120 -0
  15. package/dist/delivery.d.ts.map +1 -0
  16. package/dist/delivery.js +117 -0
  17. package/dist/delivery.js.map +1 -0
  18. package/dist/discovery.d.ts +35 -0
  19. package/dist/discovery.d.ts.map +1 -0
  20. package/dist/discovery.js +306 -0
  21. package/dist/discovery.js.map +1 -0
  22. package/dist/domains.d.ts +29 -0
  23. package/dist/domains.d.ts.map +1 -0
  24. package/dist/domains.js +267 -0
  25. package/dist/domains.js.map +1 -0
  26. package/dist/history.d.ts +13 -0
  27. package/dist/history.d.ts.map +1 -0
  28. package/dist/history.js +143 -0
  29. package/dist/history.js.map +1 -0
  30. package/dist/http.d.ts +20 -0
  31. package/dist/http.d.ts.map +1 -0
  32. package/dist/http.js +88 -0
  33. package/dist/http.js.map +1 -0
  34. package/dist/index.d.ts +20 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +15 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/keys.d.ts +97 -0
  39. package/dist/keys.d.ts.map +1 -0
  40. package/dist/keys.js +679 -0
  41. package/dist/keys.js.map +1 -0
  42. package/dist/memory.d.ts +9 -0
  43. package/dist/memory.d.ts.map +1 -0
  44. package/dist/memory.js +35 -0
  45. package/dist/memory.js.map +1 -0
  46. package/dist/messages.d.ts +64 -0
  47. package/dist/messages.d.ts.map +1 -0
  48. package/dist/messages.js +480 -0
  49. package/dist/messages.js.map +1 -0
  50. package/dist/node.d.ts +26 -0
  51. package/dist/node.d.ts.map +1 -0
  52. package/dist/node.js +59 -0
  53. package/dist/node.js.map +1 -0
  54. package/dist/resolve.d.ts +18 -0
  55. package/dist/resolve.d.ts.map +1 -0
  56. package/dist/resolve.js +37 -0
  57. package/dist/resolve.js.map +1 -0
  58. package/dist/resources.d.ts +25 -0
  59. package/dist/resources.d.ts.map +1 -0
  60. package/dist/resources.js +782 -0
  61. package/dist/resources.js.map +1 -0
  62. package/dist/routes/domains.d.ts +4 -0
  63. package/dist/routes/domains.d.ts.map +1 -0
  64. package/dist/routes/domains.js +42 -0
  65. package/dist/routes/domains.js.map +1 -0
  66. package/dist/routes/grants.d.ts +8 -0
  67. package/dist/routes/grants.d.ts.map +1 -0
  68. package/dist/routes/grants.js +32 -0
  69. package/dist/routes/grants.js.map +1 -0
  70. package/dist/routes/public.d.ts +4 -0
  71. package/dist/routes/public.d.ts.map +1 -0
  72. package/dist/routes/public.js +62 -0
  73. package/dist/routes/public.js.map +1 -0
  74. package/dist/routes/resources.d.ts +8 -0
  75. package/dist/routes/resources.d.ts.map +1 -0
  76. package/dist/routes/resources.js +87 -0
  77. package/dist/routes/resources.js.map +1 -0
  78. package/dist/routes/shared.d.ts +77 -0
  79. package/dist/routes/shared.d.ts.map +1 -0
  80. package/dist/routes/shared.js +349 -0
  81. package/dist/routes/shared.js.map +1 -0
  82. package/dist/routes/threads.d.ts +8 -0
  83. package/dist/routes/threads.d.ts.map +1 -0
  84. package/dist/routes/threads.js +113 -0
  85. package/dist/routes/threads.js.map +1 -0
  86. package/dist/routes/trust.d.ts +4 -0
  87. package/dist/routes/trust.d.ts.map +1 -0
  88. package/dist/routes/trust.js +36 -0
  89. package/dist/routes/trust.js.map +1 -0
  90. package/dist/stream.d.ts +20 -0
  91. package/dist/stream.d.ts.map +1 -0
  92. package/dist/stream.js +141 -0
  93. package/dist/stream.js.map +1 -0
  94. package/dist/threads.d.ts +79 -0
  95. package/dist/threads.d.ts.map +1 -0
  96. package/dist/threads.js +480 -0
  97. package/dist/threads.js.map +1 -0
  98. package/dist/transport.d.ts +45 -0
  99. package/dist/transport.d.ts.map +1 -0
  100. package/dist/transport.js +175 -0
  101. package/dist/transport.js.map +1 -0
  102. package/dist/types.d.ts +809 -0
  103. package/dist/types.d.ts.map +1 -0
  104. package/dist/types.js +9 -0
  105. package/dist/types.js.map +1 -0
  106. package/package.json +16 -8
  107. package/src/client.ts +0 -1549
  108. package/src/core.ts +0 -114
  109. package/src/discovery.ts +0 -267
  110. package/src/domains.ts +0 -210
  111. package/src/history.ts +0 -129
  112. package/src/http.ts +0 -841
  113. package/src/index.ts +0 -18
  114. package/src/keys.ts +0 -632
  115. package/src/memory.ts +0 -34
  116. package/src/messages.ts +0 -398
  117. package/src/node.ts +0 -56
  118. package/src/resources.ts +0 -745
  119. package/src/threads.ts +0 -350
  120. package/src/transport.ts +0 -204
  121. package/src/types.ts +0 -383
@@ -0,0 +1,267 @@
1
+ import { fail, key, OnlyneError, page, requireValue } from "./core.js";
2
+ import { serviceBase } from "./transport.js";
3
+ /** A first description probe may hydrate the cache for this long; later probes do not touch storage. */
4
+ const DECLARE_HYDRATE_MS = 1_000;
5
+ export function validateDomainId(value) {
6
+ if (typeof value !== 'string' || !/^[A-Za-z][A-Za-z0-9+.-]*:[^\s]+$/.test(value)) {
7
+ fail('invalid_request', 'A domain_id must be an absolute URI.');
8
+ }
9
+ return value;
10
+ }
11
+ /**
12
+ * Shape check for a membership as published on the wire. `null` is a valid declaration of no
13
+ * membership; `undefined` means the value is not a membership at all.
14
+ */
15
+ export function membershipRecord(value) {
16
+ if (value === null)
17
+ return null;
18
+ if (typeof value !== 'object' || Array.isArray(value))
19
+ return undefined;
20
+ if (!('domain_id' in value) || !('node_id' in value) || !('state' in value) || !('revision' in value))
21
+ return undefined;
22
+ const { domain_id, node_id, state, revision } = value;
23
+ if (typeof domain_id !== 'string' || !/^[A-Za-z][A-Za-z0-9+.-]*:[^\s]+$/.test(domain_id))
24
+ return undefined;
25
+ if (typeof node_id !== 'string' || node_id.length === 0)
26
+ return undefined;
27
+ if (state !== 'active' && state !== 'left')
28
+ return undefined;
29
+ if (typeof revision !== 'string' || revision.length === 0)
30
+ return undefined;
31
+ return { domain_id, node_id, state, revision };
32
+ }
33
+ /**
34
+ * A service description is valid evidence only when it speaks this protocol, names the node that
35
+ * answered, and publishes a membership record for that same node.
36
+ */
37
+ function declared(value, node) {
38
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
39
+ return undefined;
40
+ if (!('protocol' in value) || !('node_id' in value) || !('membership' in value))
41
+ return undefined;
42
+ if (value.protocol !== 'onlyne/3' || value.node_id !== node)
43
+ return undefined;
44
+ const membership = membershipRecord(value.membership);
45
+ if (membership === undefined || (membership !== null && membership.node_id !== node))
46
+ return undefined;
47
+ return { membership };
48
+ }
49
+ async function activeRows(tx, node) {
50
+ return (await tx.scan(key('domain') + '/'))
51
+ .map(row => row.value)
52
+ .filter(row => row.node_id === node && row.state === 'active');
53
+ }
54
+ /**
55
+ * The one active membership a node may hold, read inside the caller's transaction.
56
+ * Storage written by another revision can hold several; that state is refused instead of
57
+ * silently choosing one, so the single-membership rule stays visible to the host.
58
+ */
59
+ async function activeMembership(tx, node) {
60
+ const rows = await activeRows(tx, node);
61
+ if (rows.length > 1)
62
+ fail('conflict', 'This node holds more than one active domain membership; resolve it before continuing.');
63
+ return rows[0];
64
+ }
65
+ export class DomainService {
66
+ #ctx;
67
+ #fetcher;
68
+ #declared;
69
+ constructor(ctx, fetcher = fetch) {
70
+ this.#ctx = ctx;
71
+ this.#fetcher = fetcher;
72
+ }
73
+ /**
74
+ * The node's one declared membership, kept in memory for the public description probe. Once
75
+ * it is known, this answers without entering the transaction queue; the first read hydrates
76
+ * it under a tight bound, and an unhydratable read fails instead of inventing a membership.
77
+ */
78
+ async declared(auth) {
79
+ this.authenticate(auth);
80
+ if (this.#declared !== undefined)
81
+ return this.#declared;
82
+ const read = this.#ctx.storage.transaction(tx => activeMembership(tx, this.#ctx.node));
83
+ read.then(current => { this.#declared = current ?? null; }).catch(() => undefined);
84
+ const current = await Promise.race([
85
+ read,
86
+ new Promise((_resolve, reject) => {
87
+ setTimeout(() => reject(new OnlyneError('unavailable', 'The declared membership is unavailable.')), DECLARE_HYDRATE_MS);
88
+ }),
89
+ ]);
90
+ this.#declared = current ?? null;
91
+ return this.#declared;
92
+ }
93
+ async #rememberDeclared() {
94
+ try {
95
+ this.#declared = (await this.#ctx.storage.transaction(tx => activeMembership(tx, this.#ctx.node))) ?? null;
96
+ }
97
+ catch {
98
+ this.#declared = undefined;
99
+ }
100
+ }
101
+ authenticate(auth) {
102
+ if (!auth.actor)
103
+ fail('unauthenticated', 'Authentication is required.');
104
+ }
105
+ self(auth) {
106
+ this.authenticate(auth);
107
+ if (auth.actor !== this.#ctx.node)
108
+ fail('forbidden', 'A node may only change its own domain membership.');
109
+ }
110
+ async create(auth, mutation) {
111
+ this.self(auth);
112
+ const record = await this.#ctx.mutate(auth, 'domain.create', mutation, {}, async (tx) => {
113
+ if (await activeMembership(tx, this.#ctx.node)) {
114
+ fail('conflict', 'This node already belongs to a domain; leave it before creating another.');
115
+ }
116
+ const domain_id = validateDomainId(`urn:onlyne:region:${this.#ctx.id()}`);
117
+ const slot = key('domain', domain_id);
118
+ if (await tx.get(slot))
119
+ fail('id_conflict', 'The domain identifier is already recorded here.');
120
+ const record = { domain_id, node_id: this.#ctx.node, state: 'active', revision: this.#ctx.id() };
121
+ await tx.put(slot, record);
122
+ return record;
123
+ });
124
+ await this.#rememberDeclared();
125
+ return record;
126
+ }
127
+ async join(auth, id, mutation) {
128
+ return this.change(auth, id, 'active', mutation);
129
+ }
130
+ async leave(auth, id, mutation) {
131
+ return this.change(auth, id, 'left', mutation);
132
+ }
133
+ async change(auth, id, state, mutation) {
134
+ this.self(auth);
135
+ validateDomainId(id);
136
+ const record = await this.#ctx.mutate(auth, `domain.${state}/${id}`, mutation, { expected_revision: mutation.expected_revision ?? null }, async (tx) => {
137
+ const slot = key('domain', id);
138
+ const current = await tx.get(slot);
139
+ if (!current && state === 'left')
140
+ fail('not_found', 'No membership is recorded for this domain.');
141
+ // A node belongs to one domain. Cross-domain reach is a property of threads and ingress, not of membership.
142
+ const active = await activeMembership(tx, this.#ctx.node);
143
+ if (state === 'active' && active !== undefined && active.domain_id !== id) {
144
+ fail('conflict', 'This node already belongs to another domain; leave it first.');
145
+ }
146
+ if (current?.revision !== mutation.expected_revision) {
147
+ fail('revision_conflict', 'The local domain membership changed.', current ? { current_revision: current.revision } : undefined);
148
+ }
149
+ if (current?.state === state)
150
+ return current;
151
+ const revision = this.#ctx.id();
152
+ if (revision === current?.revision)
153
+ fail('internal', 'The revision source reused the current membership revision.');
154
+ const record = { domain_id: id, node_id: this.#ctx.node, state, revision };
155
+ await tx.put(slot, record);
156
+ return record;
157
+ });
158
+ await this.#rememberDeclared();
159
+ return record;
160
+ }
161
+ async list(auth, options = {}) {
162
+ this.authenticate(auth);
163
+ return this.#ctx.storage.transaction(async (tx) => {
164
+ const current = await activeMembership(tx, this.#ctx.node);
165
+ const records = current === undefined ? [] : [current];
166
+ records.sort((a, b) => a.domain_id < b.domain_id ? -1 : a.domain_id > b.domain_id ? 1 : 0);
167
+ return page(records, options, { kind: 'domains', node: this.#ctx.node, actor: auth.actor }, row => row.domain_id);
168
+ });
169
+ }
170
+ async describe(auth, id) {
171
+ this.authenticate(auth);
172
+ validateDomainId(id);
173
+ return this.#ctx.storage.transaction(async (tx) => requireValue(await tx.get(key('domain', id))));
174
+ }
175
+ async discover(auth, id, signal) {
176
+ this.authenticate(auth);
177
+ validateDomainId(id);
178
+ if ((await this.describe(auth, id)).state !== 'active')
179
+ fail('not_a_member', 'This node has left the domain.');
180
+ const candidates = this.#ctx.options.plugins.domainCandidates;
181
+ if (!candidates)
182
+ fail('unsupported_delivery', 'The host provides no domain discovery source.');
183
+ signal?.throwIfAborted();
184
+ const nodes = [...new Set([this.#ctx.node, ...await candidates(id)])].sort();
185
+ const members = [];
186
+ const unavailable = [];
187
+ for (const node of nodes) {
188
+ signal?.throwIfAborted();
189
+ if (typeof node !== 'string' || node.length === 0)
190
+ fail('invalid_request', 'Domain candidates must be node identities.');
191
+ if (node === this.#ctx.node) {
192
+ const current = await this.describe(auth, id);
193
+ if (current.state === 'active')
194
+ members.push(current);
195
+ continue;
196
+ }
197
+ try {
198
+ // Only the candidate's own description is evidence: protocol and identity must agree, and
199
+ // a node that declares another region (or none) is excluded rather than called unreachable.
200
+ const declaration = declared(await this.query(node, signal), node);
201
+ if (declaration === undefined)
202
+ unavailable.push(node);
203
+ else if (declaration.membership !== null && declaration.membership.domain_id === id && declaration.membership.state === 'active')
204
+ members.push(declaration.membership);
205
+ }
206
+ catch {
207
+ signal?.throwIfAborted();
208
+ unavailable.push(node);
209
+ }
210
+ }
211
+ return { domain_id: id, members, unavailable };
212
+ }
213
+ async query(node, signal) {
214
+ // A candidate's service description is public: it is read before any key exchange, so
215
+ // it never goes through the signing transport (RFC 0011 §5, RFC 0017 §4).
216
+ const target = await this.#ctx.options.plugins.resolve(node);
217
+ if (target.node_id !== node)
218
+ fail('unavailable', 'The resolver returned a target for another identity.');
219
+ const base = serviceBase(target.base_url);
220
+ const url = new URL('./description', base.href.endsWith('/') ? base.href : `${base.href}/`);
221
+ const controller = new AbortController();
222
+ let timer;
223
+ const onAbort = () => controller.abort(signal?.reason);
224
+ signal?.addEventListener('abort', onAbort, { once: true });
225
+ if (signal?.aborted)
226
+ controller.abort(signal.reason);
227
+ const aborted = new Promise((_, reject) => {
228
+ controller.signal.addEventListener('abort', () => reject(controller.signal.reason), { once: true });
229
+ if (controller.signal.aborted)
230
+ reject(controller.signal.reason);
231
+ });
232
+ timer = setTimeout(() => controller.abort(new OnlyneError('unavailable', 'Service description lookup timed out.')), this.#ctx.options.wait_ms ?? 30_000);
233
+ try {
234
+ const response = await Promise.race([
235
+ this.#fetcher(url, { method: 'GET', headers: { accept: 'application/json' }, signal: controller.signal }),
236
+ aborted,
237
+ ]);
238
+ const text = await response.text().catch(() => '');
239
+ let parsed;
240
+ try {
241
+ parsed = text === '' ? undefined : JSON.parse(text);
242
+ }
243
+ catch {
244
+ parsed = undefined;
245
+ }
246
+ if (!response.ok) {
247
+ const record = parsed !== null && typeof parsed === 'object' && 'error' in parsed ? parsed.error : parsed;
248
+ if (record !== null && typeof record === 'object' && 'code' in record && 'message' in record) {
249
+ const failure = record;
250
+ const rejection = new OnlyneError(failure.code, failure.message);
251
+ if (typeof failure.retryable === 'boolean')
252
+ rejection.error.retryable = failure.retryable;
253
+ throw rejection;
254
+ }
255
+ fail('unavailable', `The candidate answered HTTP ${response.status} without a service description.`);
256
+ }
257
+ if (parsed === undefined)
258
+ fail('unavailable', 'The candidate returned no service description.');
259
+ return parsed;
260
+ }
261
+ finally {
262
+ clearTimeout(timer);
263
+ signal?.removeEventListener('abort', onAbort);
264
+ }
265
+ }
266
+ }
267
+ //# sourceMappingURL=domains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.js","sourceRoot":"","sources":["../src/domains.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Auth, Conversation, HistoryMessage, HistoryQuery, MessageRef, Page } from './types.ts';
2
+ import { type Context } from './core.ts';
3
+ /** A read-only projection of the records this node already holds. It performs no remote reads. */
4
+ export declare class HistoryService {
5
+ #private;
6
+ constructor(ctx: Context);
7
+ list(auth: Auth, query?: HistoryQuery): Promise<Page<Conversation>>;
8
+ conversation(auth: Auth, message: MessageRef): Promise<Conversation>;
9
+ message(auth: Auth, message: MessageRef): Promise<HistoryMessage>;
10
+ private authorize;
11
+ private collect;
12
+ }
13
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,IAAI,EAAE,YAAY,EAAS,cAAc,EAAE,YAAY,EAAkB,UAAU,EAAY,IAAI,EAA0B,MAAM,YAAY,CAAC;AACnL,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAKhE,kGAAkG;AAClG,qBAAa,cAAc;;gBAGb,GAAG,EAAE,OAAO;IAIlB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAYvE,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC;IASpE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC;IAOvE,OAAO,CAAC,SAAS;YAIH,OAAO;CA4FtB"}
@@ -0,0 +1,143 @@
1
+ import { fail, key, page, same } from "./core.js";
2
+ const identity = (ref) => key(ref.sender, ref.id);
3
+ const threadIdentity = (ref) => key(ref.owner, ref.id);
4
+ /** A read-only projection of the records this node already holds. It performs no remote reads. */
5
+ export class HistoryService {
6
+ #ctx;
7
+ constructor(ctx) {
8
+ this.#ctx = ctx;
9
+ }
10
+ async list(auth, query = {}) {
11
+ this.authorize(auth);
12
+ return this.#ctx.storage.transaction(async (tx) => {
13
+ const conversations = await this.collect(tx);
14
+ const selected = conversations.filter(conversation => (query.thread === undefined || conversation.thread_refs.some(ref => same(ref, query.thread)))
15
+ && (query.message === undefined || conversation.messages.some(row => same(row.message_ref, query.message)) || same(conversation.id, query.message)));
16
+ selected.sort((left, right) => identity(left.id).localeCompare(identity(right.id)));
17
+ return page(selected, query, { kind: 'history', actor: auth.actor, thread: query.thread, message: query.message }, row => identity(row.id));
18
+ });
19
+ }
20
+ async conversation(auth, message) {
21
+ this.authorize(auth);
22
+ return this.#ctx.storage.transaction(async (tx) => {
23
+ const found = (await this.collect(tx)).find(row => same(row.id, message) || row.messages.some(item => same(item.message_ref, message)));
24
+ if (found === undefined)
25
+ fail('not_found', 'This node holds no conversation for that message.');
26
+ return found;
27
+ });
28
+ }
29
+ async message(auth, message) {
30
+ const conversation = await this.conversation(auth, message);
31
+ const found = conversation.messages.find(row => same(row.message_ref, message));
32
+ if (found === undefined)
33
+ fail('not_found', 'This node holds the reply reference, but no body for that message.');
34
+ return found;
35
+ }
36
+ authorize(auth) {
37
+ if (auth.actor !== this.#ctx.node)
38
+ fail('forbidden', 'Conversation history belongs to this node.');
39
+ }
40
+ async collect(tx) {
41
+ const messages = new Map();
42
+ const addEntry = (entry, injected, settlement) => {
43
+ const message_ref = { sender: entry.message.sender, id: entry.message.id };
44
+ const id = identity(message_ref);
45
+ const previous = messages.get(id);
46
+ const closed = previous?.settlement ?? settlement;
47
+ messages.set(id, {
48
+ message_ref, message: entry.message, direction: entry.message.sender === this.#ctx.node ? 'out' : 'in',
49
+ thread_ref: entry.thread_ref, seq: entry.seq, at: entry.at, delivery: previous?.delivery ?? 'received',
50
+ ...(previous?.guarantee === undefined ? {} : { guarantee: previous.guarantee }),
51
+ // A settlement closes the row without asserting a read, so it carries no consumption.
52
+ ...(closed !== undefined ? { settlement: closed }
53
+ : injected === undefined ? previous?.consumption === undefined ? {} : { consumption: previous.consumption }
54
+ : { consumption: injected || previous?.consumption === 'consumed' ? 'consumed' : 'pending' }),
55
+ });
56
+ };
57
+ for (const { value } of await tx.scan('entry-ref/'))
58
+ addEntry(value);
59
+ for (const { value } of await tx.scan('inbox/')) {
60
+ if (value.attention.entry !== undefined && value.attention.disposition !== 'expired') {
61
+ addEntry(value.attention.entry, value.injected, value.settlement);
62
+ }
63
+ }
64
+ const confirmed = new Set();
65
+ for (const { value } of await tx.scan('consumed/')) {
66
+ if (value.confirmed)
67
+ confirmed.add(key(value.thread_ref.owner, value.thread_ref.id, value.seq));
68
+ }
69
+ for (const { value } of await tx.scan('outbound/')) {
70
+ const message = value.request.message;
71
+ const message_ref = { sender: message.sender, id: message.id };
72
+ const previous = messages.get(identity(message_ref));
73
+ messages.set(identity(message_ref), {
74
+ message_ref, message, direction: 'out', at: value.created_at ?? previous?.at ?? null,
75
+ delivery: value.state,
76
+ ...(value.receipt === undefined ? {} : { thread_ref: value.receipt.thread_ref, seq: value.receipt.seq, guarantee: value.receipt.guarantee }),
77
+ ...(value.error === undefined ? {} : { error: value.error }),
78
+ ...(previous?.settlement === undefined ? {} : { settlement: previous.settlement }),
79
+ });
80
+ }
81
+ for (const row of messages.values()) {
82
+ if (row.direction === 'in' && row.thread_ref !== undefined && row.seq !== undefined
83
+ && confirmed.has(key(row.thread_ref.owner, row.thread_ref.id, row.seq)))
84
+ row.consumption = 'consumed';
85
+ }
86
+ const parents = new Map();
87
+ const refs = new Map();
88
+ const root = (id) => {
89
+ const parent = parents.get(id);
90
+ if (parent === undefined || parent === id)
91
+ return id;
92
+ const found = root(parent);
93
+ parents.set(id, found);
94
+ return found;
95
+ };
96
+ for (const row of messages.values()) {
97
+ const id = identity(row.message_ref);
98
+ refs.set(id, row.message_ref);
99
+ const original = row.message.meta?.in_reply_to;
100
+ if (original === undefined)
101
+ continue;
102
+ const source = identity(original);
103
+ refs.set(source, original);
104
+ const left = root(id);
105
+ const right = root(source);
106
+ if (left !== right)
107
+ parents.set(left, right);
108
+ }
109
+ const grouped = new Map();
110
+ for (const [id, message] of messages) {
111
+ const group = root(id);
112
+ const rows = grouped.get(group) ?? [];
113
+ rows.push(message);
114
+ grouped.set(group, rows);
115
+ }
116
+ return [...grouped].map(([id, rows]) => {
117
+ // Parent-before-child ordering also works when one legacy outbound has no timestamp.
118
+ const ordered = [];
119
+ const seen = new Set();
120
+ const byId = new Map(rows.map(row => [identity(row.message_ref), row]));
121
+ const visit = (row) => {
122
+ const key = identity(row.message_ref);
123
+ if (seen.has(key))
124
+ return;
125
+ seen.add(key);
126
+ const source = row.message.meta?.in_reply_to;
127
+ const parent = source === undefined ? undefined : byId.get(identity(source));
128
+ if (parent !== undefined)
129
+ visit(parent);
130
+ ordered.push(row);
131
+ };
132
+ rows.sort((a, b) => (a.at ?? '').localeCompare(b.at ?? '') || identity(a.message_ref).localeCompare(identity(b.message_ref)));
133
+ rows.forEach(visit);
134
+ const threads = new Map();
135
+ for (const row of ordered)
136
+ if (row.thread_ref !== undefined)
137
+ threads.set(threadIdentity(row.thread_ref), row.thread_ref);
138
+ const subject = ordered[0].message.delivery_kind.replace(/\s+/g, ' ').trim();
139
+ return { id: refs.get(id), title: subject || ordered[0].message.msg.split(/\r?\n/, 1)[0], thread_refs: [...threads.values()], messages: ordered, last_at: rows.filter(row => row.at !== null).at(-1)?.at ?? null };
140
+ });
141
+ }
142
+ }
143
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { Context } from './core.ts';
2
+ import type { ThreadService } from './threads.ts';
3
+ import type { MessageService } from './messages.ts';
4
+ import type { ResourceService } from './resources.ts';
5
+ import type { DomainService } from './domains.ts';
6
+ import type { DiscoveryService } from './discovery.ts';
7
+ import type { KeyService, TrustService } from './keys.ts';
8
+ export interface HttpServices {
9
+ ctx: Context;
10
+ threads: ThreadService;
11
+ messages: MessageService;
12
+ resources: ResourceService;
13
+ domains: DomainService;
14
+ discovery?: DiscoveryService;
15
+ keys?: KeyService;
16
+ trust?: TrustService;
17
+ }
18
+ /** Maps `/onlyne/v3` HTTP requests onto the services. The caller owns the server lifecycle. */
19
+ export declare function createHttpHandler(services: HttpServices): (request: Request) => Promise<Response>;
20
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAY1D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,eAAe,CAAC;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAOD,+FAA+F;AAC/F,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAmEjG"}
package/dist/http.js ADDED
@@ -0,0 +1,88 @@
1
+ import { OnlyneError, fail } from "./core.js";
2
+ import { SIGNATURE_HEADER, bodyDigest } from "./keys.js";
3
+ import { DEFAULT_BYTES, respond } from "./routes/shared.js";
4
+ import { publicRoutes } from "./routes/public.js";
5
+ import { trustRoutes } from "./routes/trust.js";
6
+ import { threadRoutes } from "./routes/threads.js";
7
+ import { grantRoutes } from "./routes/grants.js";
8
+ import { domainRoutes } from "./routes/domains.js";
9
+ import { resourceRoutes } from "./routes/resources.js";
10
+ const PREFIX = '/onlyne/v3';
11
+ function failure(error) {
12
+ const onlyne = error instanceof OnlyneError ? error : new OnlyneError('internal', 'The node failed to complete the request.');
13
+ return respond({ error: onlyne.error }, onlyne.status);
14
+ }
15
+ /** Maps `/onlyne/v3` HTTP requests onto the services. The caller owns the server lifecycle. */
16
+ export function createHttpHandler(services) {
17
+ const { ctx, threads, messages, resources, domains, discovery, keys, trust } = services;
18
+ const limit = ctx.options.request_bytes ?? DEFAULT_BYTES;
19
+ /**
20
+ * RFC 0017: an `Onlyne-Sig` header is verified against the peer's stored key before the
21
+ * host plugin sees the request; a verified signature names the actor directly. The
22
+ * signature covers the body, so it is checked against the raw bytes actually received.
23
+ */
24
+ async function authenticate(request, rawBody) {
25
+ // A keyed node never falls back to an unauthenticated actor: the signature is the credential.
26
+ if (trust !== undefined && keys?.enabled === true)
27
+ return trust.authenticate(request, bodyDigest(rawBody));
28
+ const auth = await ctx.options.plugins.authenticate(request);
29
+ if (auth === null || typeof auth.actor !== 'string' || auth.actor.length === 0)
30
+ fail('unauthenticated', 'Authentication is required.');
31
+ return auth;
32
+ }
33
+ /** RFC 0017: an authenticated answer to a signed request carries the responder's signature. */
34
+ async function signed(request, response) {
35
+ if (trust === undefined || keys?.enabled !== true || request.headers.get(SIGNATURE_HEADER) === null)
36
+ return response;
37
+ await trust.signResponse(request, response, await response.clone().text());
38
+ return response;
39
+ }
40
+ async function handle(request) {
41
+ try {
42
+ const url = new URL(request.url);
43
+ // A shared listener mounts each node under its own path prefix (`/nodes/<id>/onlyne/v3`),
44
+ // so the prefix is located inside the path rather than assumed at its start. Only the
45
+ // route segments are sliced; `request.url` stays untouched, because the signature covers
46
+ // exactly the `pathname + search` the sender signed (RFC 0017).
47
+ const mount = url.pathname.lastIndexOf(`${PREFIX}/`);
48
+ if (mount < 0)
49
+ fail('not_found', 'Unknown route.');
50
+ const path = url.pathname.slice(mount + PREFIX.length).split('/').filter(segment => segment.length > 0).map(segment => {
51
+ try {
52
+ return decodeURIComponent(segment);
53
+ }
54
+ catch {
55
+ return fail('invalid_request', 'The request path is not valid percent-encoding.');
56
+ }
57
+ });
58
+ const params = url.searchParams;
59
+ const base = { request, path, params, ctx, threads, messages, resources, domains, discovery, keys, trust, limit };
60
+ const open = await publicRoutes(base);
61
+ if (open !== undefined)
62
+ return open;
63
+ // Authenticated routes read the raw body first: the signature covers exactly these bytes.
64
+ const rawBody = await request.text();
65
+ const auth = await authenticate(request, rawBody);
66
+ const route = { ...base, auth, rawBody };
67
+ for (const module of [trustRoutes, threadRoutes, grantRoutes, domainRoutes, resourceRoutes]) {
68
+ const response = await module(route);
69
+ if (response !== undefined)
70
+ return response;
71
+ }
72
+ fail('not_found', 'Unknown route.');
73
+ }
74
+ catch (error) {
75
+ return failure(error);
76
+ }
77
+ }
78
+ return async (request) => {
79
+ const response = await handle(request);
80
+ try {
81
+ return await signed(request, response);
82
+ }
83
+ catch (error) {
84
+ return failure(error);
85
+ }
86
+ };
87
+ }
88
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export type * from './types.ts';
2
+ export { trustPhase } from './types.ts';
3
+ export type { WorkRef } from './types.ts';
4
+ export { OnlyneError } from './core.ts';
5
+ export { OnlyneNode } from './node.ts';
6
+ export { ThreadService } from './threads.ts';
7
+ export { MessageService } from './messages.ts';
8
+ export { HistoryService } from './history.ts';
9
+ export { createResourceService } from './resources.ts';
10
+ export type { ResourceService } from './resources.ts';
11
+ export { DomainService } from './domains.ts';
12
+ export { DiscoveryService } from './discovery.ts';
13
+ export { OnlyneClient } from './client.ts';
14
+ export type { SendOutcome, SendOptions, MsgOptions } from './client.ts';
15
+ export { FetchTransport } from './transport.ts';
16
+ export type { SigningTransportOptions, SigningTrust, TrustFailure } from './transport.ts';
17
+ export { KeyService, TrustService, keyFingerprint, generateKeyMaterial, SIGNATURE_HEADER, CLOCK_SKEW_MS, EXCHANGE_RETRY_MS, REFUSED_RETRY_MS } from './keys.ts';
18
+ export { createHttpHandler } from './http.ts';
19
+ export { MemoryStorage } from './memory.ts';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAChK,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ export { trustPhase } from "./types.js";
2
+ export { OnlyneError } from "./core.js";
3
+ export { OnlyneNode } from "./node.js";
4
+ export { ThreadService } from "./threads.js";
5
+ export { MessageService } from "./messages.js";
6
+ export { HistoryService } from "./history.js";
7
+ export { createResourceService } from "./resources.js";
8
+ export { DomainService } from "./domains.js";
9
+ export { DiscoveryService } from "./discovery.js";
10
+ export { OnlyneClient } from "./client.js";
11
+ export { FetchTransport } from "./transport.js";
12
+ export { KeyService, TrustService, keyFingerprint, generateKeyMaterial, SIGNATURE_HEADER, CLOCK_SKEW_MS, EXCHANGE_RETRY_MS, REFUSED_RETRY_MS } from "./keys.js";
13
+ export { createHttpHandler } from "./http.js";
14
+ export { MemoryStorage } from "./memory.js";
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAChK,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}