@amalgm/live 0.2.58 → 0.2.60

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 (38) hide show
  1. package/PURPOSE.md +29 -14
  2. package/dist/transfer/cargo.d.ts +11 -0
  3. package/dist/transfer/cargo.d.ts.map +1 -1
  4. package/dist/transfer/cargo.js +97 -23
  5. package/dist/transfer/cargo.js.map +1 -1
  6. package/dist/transfer/download.d.ts +65 -25
  7. package/dist/transfer/download.d.ts.map +1 -1
  8. package/dist/transfer/download.js +339 -84
  9. package/dist/transfer/download.js.map +1 -1
  10. package/dist/transfer/index.d.ts +1 -0
  11. package/dist/transfer/index.d.ts.map +1 -1
  12. package/dist/transfer/index.js +1 -0
  13. package/dist/transfer/index.js.map +1 -1
  14. package/dist/transfer/ranges.d.ts +20 -35
  15. package/dist/transfer/ranges.d.ts.map +1 -1
  16. package/dist/transfer/ranges.js +35 -121
  17. package/dist/transfer/ranges.js.map +1 -1
  18. package/dist/transfer/refs.d.ts +15 -14
  19. package/dist/transfer/refs.d.ts.map +1 -1
  20. package/dist/transfer/refs.js +33 -37
  21. package/dist/transfer/refs.js.map +1 -1
  22. package/dist/transfer/stream.d.ts +50 -0
  23. package/dist/transfer/stream.d.ts.map +1 -0
  24. package/dist/transfer/stream.js +78 -0
  25. package/dist/transfer/stream.js.map +1 -0
  26. package/dist-cjs/transfer/cargo.js +98 -23
  27. package/dist-cjs/transfer/cargo.js.map +1 -1
  28. package/dist-cjs/transfer/download.js +339 -85
  29. package/dist-cjs/transfer/download.js.map +1 -1
  30. package/dist-cjs/transfer/index.js +1 -0
  31. package/dist-cjs/transfer/index.js.map +1 -1
  32. package/dist-cjs/transfer/ranges.js +37 -124
  33. package/dist-cjs/transfer/ranges.js.map +1 -1
  34. package/dist-cjs/transfer/refs.js +34 -38
  35. package/dist-cjs/transfer/refs.js.map +1 -1
  36. package/dist-cjs/transfer/stream.js +81 -0
  37. package/dist-cjs/transfer/stream.js.map +1 -0
  38. package/package.json +1 -1
@@ -1,145 +1,59 @@
1
1
  /**
2
- * Ranged pack reading.
2
+ * Pack span planning.
3
3
  *
4
- * Sealed envelopes of many small objects live inside one immutable pack.
5
- * Requests for ranges of the same pack URL made in the same event-loop turn
6
- * merge into minimal byte spans one ranged GET per spanand every
7
- * requester receives exactly its envelope slice. A small hole between wanted
8
- * ranges is cheaper to read through than to split a request over, so spans
9
- * merge across gaps up to RANGE_MERGE_GAP_BYTES. Wrong or shifted bytes can
10
- * never leak: every dispensed slice is still an authenticated envelope the
11
- * caller opens and hash-verifies.
4
+ * Sealed envelopes of many small objects live inside one immutable pack. A
5
+ * download wave knows every member it needs before any byte moves, so the
6
+ * members of one pack are planned into a few broad byte spans one ranged
7
+ * GET per span instead of a request per member. A small hole between
8
+ * wanted ranges is cheaper to read through than to split a request over, so
9
+ * spans merge across gaps up to RANGE_MERGE_GAP_BYTES; a span never grows
10
+ * past the caller's byte cap, so one planned read always fits the wave's
11
+ * in-flight memory bound. Wrong or shifted bytes can never leak: every
12
+ * dispensed slice is still an authenticated envelope the reader opens and
13
+ * hash-verifies.
12
14
  */
13
15
  /** Read through holes up to this size rather than splitting a range request. */
14
16
  export const RANGE_MERGE_GAP_BYTES = 64 * 1024;
15
- /** Merged span bytes one fetcher may hold in flight at once. Admission of
16
- * spans — not just of the objects above them — is byte-bounded, so a wide
17
- * same-turn fan-out can never hold every pack's sealed bytes at once. */
18
- export const SPAN_FETCH_MAX_IN_FLIGHT_BYTES = 64 * 1024 * 1024;
19
17
  function invalidSpan(span) {
20
18
  return !Number.isSafeInteger(span.offset) || span.offset < 0
21
19
  || !Number.isSafeInteger(span.length) || span.length < 1
22
20
  || !Number.isSafeInteger(span.offset + span.length);
23
21
  }
24
- /** Merge wanted ranges into minimal fetch spans. Pure planning — no IO. */
25
- export function planSpans(requests, mergeGap) {
26
- if (requests.length === 0)
27
- return { spans: [], slices: [] };
22
+ /** Plan one pack's wanted member ranges into broad fetch spans. Pure
23
+ * planning — no IO. Members merge across holes up to `mergeGap`; a span
24
+ * stops growing at `maxSpanBytes`, except that a single member larger than
25
+ * the cap still gets exactly one span of its own. */
26
+ export function planPackSpans(members, mergeGap, maxSpanBytes) {
27
+ if (members.length === 0)
28
+ return { spans: [], memberSpan: [] };
28
29
  if (!Number.isSafeInteger(mergeGap) || mergeGap < 0) {
29
- throw new Error('range plan received an invalid merge gap');
30
+ throw new Error('pack span plan received an invalid merge gap');
30
31
  }
31
- if (requests.some(invalidSpan))
32
- throw new Error('range plan received an invalid byte span');
33
- const order = requests.map((request, index) => ({ request, index }))
34
- .sort((a, b) => a.request.offset - b.request.offset || a.request.length - b.request.length);
32
+ if (!Number.isSafeInteger(maxSpanBytes) || maxSpanBytes < 1) {
33
+ throw new Error('pack span plan received an invalid span byte cap');
34
+ }
35
+ if (members.some(invalidSpan))
36
+ throw new Error('pack span plan received an invalid byte span');
37
+ const order = members.map((member, index) => ({ member, index }))
38
+ .sort((a, b) => a.member.offset - b.member.offset || a.member.length - b.member.length);
35
39
  const spans = [];
36
- const slices = new Array(requests.length);
37
- for (const { request, index } of order) {
38
- const end = request.offset + request.length;
40
+ const memberSpan = new Array(members.length);
41
+ for (const { member, index } of order) {
42
+ const end = member.offset + member.length;
39
43
  const current = spans[spans.length - 1];
40
- if (current && request.offset <= current.end + mergeGap) {
44
+ if (current
45
+ && member.offset <= current.end + mergeGap
46
+ && (Math.max(current.end, end) - current.offset <= maxSpanBytes || end <= current.end)) {
41
47
  current.end = Math.max(current.end, end);
42
48
  }
43
49
  else {
44
- spans.push({ offset: request.offset, end });
50
+ spans.push({ offset: member.offset, end });
45
51
  }
46
- slices[index] = { span: spans.length - 1, start: request.offset - spans[spans.length - 1].offset };
52
+ memberSpan[index] = spans.length - 1;
47
53
  }
48
54
  return {
49
55
  spans: spans.map((span) => ({ offset: span.offset, length: span.end - span.offset })),
50
- slices,
51
- };
52
- }
53
- export function createSpanFetcher(ports) {
54
- const pending = new Map();
55
- let scheduled = false;
56
- // The memory law: merged span bytes in flight are admission-bounded. A
57
- // span above the cap admits alone rather than deadlocking.
58
- let inFlightSpanBytes = 0;
59
- const waitingSpans = [];
60
- const spanWeight = (length) => Math.min(length, SPAN_FETCH_MAX_IN_FLIGHT_BYTES);
61
- const admitSpan = (length) => new Promise((admit) => {
62
- const weight = spanWeight(length);
63
- if (inFlightSpanBytes === 0 || inFlightSpanBytes + weight <= SPAN_FETCH_MAX_IN_FLIGHT_BYTES) {
64
- inFlightSpanBytes += weight;
65
- admit();
66
- return;
67
- }
68
- waitingSpans.push({ weight, admit });
69
- });
70
- const releaseSpan = (length) => {
71
- inFlightSpanBytes -= spanWeight(length);
72
- while (waitingSpans.length > 0) {
73
- const next = waitingSpans[0];
74
- if (inFlightSpanBytes !== 0
75
- && inFlightSpanBytes + next.weight > SPAN_FETCH_MAX_IN_FLIGHT_BYTES)
76
- return;
77
- waitingSpans.shift();
78
- inFlightSpanBytes += next.weight;
79
- next.admit();
80
- }
81
- };
82
- const flushUrl = async (url, waiters) => {
83
- let plan;
84
- try {
85
- plan = planSpans(waiters.map((waiter) => waiter.range), RANGE_MERGE_GAP_BYTES);
86
- }
87
- catch (error) {
88
- waiters.forEach((waiter) => waiter.reject(error));
89
- return;
90
- }
91
- await Promise.all(plan.spans.map(async (span, spanIndex) => {
92
- const served = [];
93
- plan.slices.forEach((slice, index) => {
94
- if (slice.span === spanIndex)
95
- served.push({ waiter: waiters[index], start: slice.start });
96
- });
97
- await admitSpan(span.length);
98
- try {
99
- const bytes = await ports.fetchSpan(url, span);
100
- if (bytes.length !== span.length) {
101
- throw new Error(`pack range answered ${bytes.length} bytes where ${span.length} were requested`);
102
- }
103
- // Copies, not views: a dispensed slice must never pin the whole
104
- // merged span buffer for as long as its consumer holds it.
105
- served.forEach(({ waiter, start }) => waiter.resolve(bytes.slice(start, start + waiter.range.length)));
106
- }
107
- catch (error) {
108
- served.forEach(({ waiter }) => waiter.reject(error));
109
- }
110
- finally {
111
- releaseSpan(span.length);
112
- }
113
- }));
114
- };
115
- const flush = () => {
116
- scheduled = false;
117
- const batches = [...pending.entries()];
118
- pending.clear();
119
- for (const [url, waiters] of batches)
120
- void flushUrl(url, waiters);
121
- };
122
- return {
123
- fetch(ref) {
124
- if (ref.offset == null && ref.length == null) {
125
- return ports.fetchSpan(ref.url, null);
126
- }
127
- const range = { offset: ref.offset ?? -1, length: ref.length ?? -1 };
128
- if (invalidSpan(range)) {
129
- return Promise.reject(new Error('content ref carries an invalid pack range'));
130
- }
131
- return new Promise((resolve, reject) => {
132
- const waiters = pending.get(ref.url);
133
- if (waiters)
134
- waiters.push({ range, resolve, reject });
135
- else
136
- pending.set(ref.url, [{ range, resolve, reject }]);
137
- if (!scheduled) {
138
- scheduled = true;
139
- setTimeout(flush, 0);
140
- }
141
- });
142
- },
56
+ memberSpan,
143
57
  };
144
58
  }
145
59
  //# sourceMappingURL=ranges.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ranges.js","sourceRoot":"","sources":["../../src/transfer/ranges.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ranges.js","sourceRoot":"","sources":["../../src/transfer/ranges.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,14 @@
1
1
  /**
2
- * Content-ref brokering for direct content-storage fetches.
2
+ * Content-ref resolution for direct content-storage fetches.
3
3
  *
4
- * Downloads fetch every storage object straight from content storage. Callers
5
- * ask where one object lives at a time; the broker coalesces every request
6
- * made in the same event-loop turn into one batched refs call. The authority
7
- * answers each object with a short-lived URL and, when the object's sealed
8
- * envelope rides inside an immutable pack, the exact byte range holding it.
9
- * URLs are used immediately and never cached their expiry is the gateway's
10
- * revocation lever.
4
+ * Downloads fetch every storage object straight from content storage. A
5
+ * planned wave resolves the locations of its entire want-list up front a
6
+ * handful of batched refs calls, never one per object so pack members can
7
+ * be fetched as a few broad spans instead of a request per member. The
8
+ * authority answers each object with a short-lived URL and, when the
9
+ * object's sealed envelope rides inside an immutable pack, the exact byte
10
+ * range holding it. URLs are used immediately and never cached — their
11
+ * expiry is the gateway's revocation lever.
11
12
  */
12
13
  /** One law shared with the gateway: the most objects one refs call may name. */
13
14
  export declare const CONTENT_REFS_BATCH_OBJECTS = 2048;
@@ -24,12 +25,12 @@ export interface ContentRef {
24
25
  readonly offset?: number;
25
26
  readonly length?: number;
26
27
  }
27
- export interface RefsBrokerPorts {
28
- /** Resolve every named object, returning refs aligned by index. */
28
+ export interface ResolveRefsPorts {
29
+ /** Resolve one batch of at most CONTENT_REFS_BATCH_OBJECTS named objects,
30
+ * returning refs aligned by index. */
29
31
  requestRefs(objects: readonly ContentObjectName[]): Promise<readonly ContentRef[]>;
30
32
  }
31
- export interface RefsBroker {
32
- ref(object: ContentObjectName): Promise<ContentRef>;
33
- }
34
- export declare function createRefsBroker(ports: RefsBrokerPorts): RefsBroker;
33
+ /** Resolve every named object's location, batching by the shared wire law.
34
+ * Answers align with the input. */
35
+ export declare function resolveContentRefs(ports: ResolveRefsPorts, objects: readonly ContentObjectName[]): Promise<readonly ContentRef[]>;
35
36
  //# sourceMappingURL=refs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../../src/transfer/refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;qCAEqC;AACrC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,WAAW,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACrD;AAQD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,CA+BnE"}
1
+ {"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../../src/transfer/refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAM/C,MAAM,WAAW,iBAAiB;IAChC,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;qCAEqC;AACrC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B;0CACsC;IACtC,WAAW,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;CACpF;AAED;mCACmC;AACnC,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACpC,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAkBhC"}
@@ -1,45 +1,41 @@
1
1
  /**
2
- * Content-ref brokering for direct content-storage fetches.
2
+ * Content-ref resolution for direct content-storage fetches.
3
3
  *
4
- * Downloads fetch every storage object straight from content storage. Callers
5
- * ask where one object lives at a time; the broker coalesces every request
6
- * made in the same event-loop turn into one batched refs call. The authority
7
- * answers each object with a short-lived URL and, when the object's sealed
8
- * envelope rides inside an immutable pack, the exact byte range holding it.
9
- * URLs are used immediately and never cached their expiry is the gateway's
10
- * revocation lever.
4
+ * Downloads fetch every storage object straight from content storage. A
5
+ * planned wave resolves the locations of its entire want-list up front a
6
+ * handful of batched refs calls, never one per object so pack members can
7
+ * be fetched as a few broad spans instead of a request per member. The
8
+ * authority answers each object with a short-lived URL and, when the
9
+ * object's sealed envelope rides inside an immutable pack, the exact byte
10
+ * range holding it. URLs are used immediately and never cached — their
11
+ * expiry is the gateway's revocation lever.
11
12
  */
13
+ import { parallelForEach } from './parallel.js';
12
14
  /** One law shared with the gateway: the most objects one refs call may name. */
13
15
  export const CONTENT_REFS_BATCH_OBJECTS = 2048;
14
- export function createRefsBroker(ports) {
15
- let pending = [];
16
- let scheduled = false;
17
- const flush = async () => {
18
- scheduled = false;
19
- while (pending.length > 0) {
20
- const batch = pending.splice(0, CONTENT_REFS_BATCH_OBJECTS);
21
- try {
22
- const refs = await ports.requestRefs(batch.map((waiter) => waiter.object));
23
- if (refs.length !== batch.length) {
24
- throw new Error('content refs answered the wrong number of objects');
25
- }
26
- batch.forEach((waiter, index) => waiter.resolve(refs[index]));
27
- }
28
- catch (error) {
29
- batch.forEach((waiter) => waiter.reject(error));
30
- }
16
+ /** Refs batches a wave resolves concurrently. Batches are control frames —
17
+ * small either way — so this only bounds simultaneous authority questions. */
18
+ const REFS_RESOLVE_CONCURRENCY = 4;
19
+ /** Resolve every named object's location, batching by the shared wire law.
20
+ * Answers align with the input. */
21
+ export async function resolveContentRefs(ports, objects) {
22
+ if (objects.length === 0)
23
+ return [];
24
+ const refs = new Array(objects.length);
25
+ const starts = [];
26
+ for (let start = 0; start < objects.length; start += CONTENT_REFS_BATCH_OBJECTS) {
27
+ starts.push(start);
28
+ }
29
+ await parallelForEach(starts, REFS_RESOLVE_CONCURRENCY, async (start) => {
30
+ const batch = objects.slice(start, start + CONTENT_REFS_BATCH_OBJECTS);
31
+ const answered = await ports.requestRefs(batch);
32
+ if (answered.length !== batch.length) {
33
+ throw new Error('content refs answered the wrong number of objects');
31
34
  }
32
- };
33
- return {
34
- ref(object) {
35
- return new Promise((resolve, reject) => {
36
- pending.push({ object, resolve, reject });
37
- if (!scheduled) {
38
- scheduled = true;
39
- setTimeout(() => { void flush(); }, 0);
40
- }
41
- });
42
- },
43
- };
35
+ answered.forEach((ref, index) => {
36
+ refs[start + index] = ref;
37
+ });
38
+ });
39
+ return refs;
44
40
  }
45
41
  //# sourceMappingURL=refs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../src/transfer/refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAgC/C,MAAM,UAAU,gBAAgB,CAAC,KAAsB;IACrD,IAAI,OAAO,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,SAAS,GAAG,KAAK,CAAC;QAClB,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,0BAA0B,CAAC,CAAC;YAC5D,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;gBACvE,CAAC;gBACD,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAE,CAAC,CAAC,CAAC;YACjE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,CAAC,MAAyB;YAC3B,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACjD,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC1C,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,SAAS,GAAG,IAAI,CAAC;oBACjB,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../src/transfer/refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,gFAAgF;AAChF,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAE/C;8EAC8E;AAC9E,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAuBnC;mCACmC;AACnC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAuB,EACvB,OAAqC;IAErC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAa,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,0BAA0B,EAAE,CAAC;QAChF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,MAAM,eAAe,CAAC,MAAM,EAAE,wBAAwB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACtE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,0BAA0B,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QACD,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * One-read upload — capture and transport fused.
3
+ *
4
+ * The classic upload (`upload.ts`) needs the whole-file identity up front
5
+ * and a seekable source it may reread. This primitive serves the moment the
6
+ * bytes are first read: the identity is DISCOVERED while one sequential
7
+ * read proceeds. Each full transfer batch of verified blocks — addressed by
8
+ * their own digests, bound to no whole-file identity yet — rides the wire
9
+ * while the next batch is read, and only after the last byte proves the
10
+ * whole-file digest is the manifest sealed. An interrupted or torn stream
11
+ * leaves unreferenced blocks the authority already tolerates, never a
12
+ * sealed artifact; whether the streamed digest matches current ground is
13
+ * the host's stat law, exactly as for the classic capture. Registration
14
+ * owns entity identity — this primitive knows only content.
15
+ *
16
+ * Memory is bounded by construction: one batch on the wire while one batch
17
+ * is read, so a stream holds at most two transfer batches at once.
18
+ */
19
+ import { type ContentChunk, type ContentManifest } from '../contracts/content.js';
20
+ import type { Sha256Hex } from '../entities/types.js';
21
+ export interface StreamSource {
22
+ /** Exact byte count the source will produce; a short read is refused. */
23
+ readonly bytes: number;
24
+ /** Return exactly `bytes` bytes beginning at `offset`. Called strictly in
25
+ * ascending offset order, never twice for the same span. */
26
+ read(offset: number, bytes: number): Promise<Uint8Array>;
27
+ }
28
+ /** Incremental whole-file digest, since the identity outlives any one call. */
29
+ export interface Sha256Stream {
30
+ update(bytes: Uint8Array): void;
31
+ digestHex(): string;
32
+ }
33
+ export interface StreamUploadPorts {
34
+ readonly sha256Hex: Sha256Hex;
35
+ createSha256(): Sha256Stream;
36
+ /** Store adjacent verified blocks addressed by their own digests. The
37
+ * blocks bind to no artifact yet; resolving means they are durable. */
38
+ putBlocks(chunks: readonly ContentChunk[], bytes: Uint8Array): Promise<void>;
39
+ /** Bind the discovered identity to its block list. The authority seals
40
+ * only when it holds every named block. */
41
+ sealManifest(manifest: ContentManifest): Promise<void>;
42
+ }
43
+ export interface StreamUploadReceipt {
44
+ readonly manifest: ContentManifest;
45
+ readonly uploadedChunks: number;
46
+ }
47
+ /** Upload one file's content from a single sequential read, discovering its
48
+ * identity along the way and sealing the manifest last. */
49
+ export declare function uploadStream(source: StreamSource, ports: StreamUploadPorts): Promise<StreamUploadReceipt>;
50
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/transfer/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;gEAC4D;IAC5D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1D;AAED,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,SAAS,IAAI,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,YAAY,IAAI,YAAY,CAAC;IAC7B;2EACuE;IACvE,SAAS,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;+CAC2C;IAC3C,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAwBD;2DAC2D;AAC3D,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,mBAAmB,CAAC,CAoC9B"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * One-read upload — capture and transport fused.
3
+ *
4
+ * The classic upload (`upload.ts`) needs the whole-file identity up front
5
+ * and a seekable source it may reread. This primitive serves the moment the
6
+ * bytes are first read: the identity is DISCOVERED while one sequential
7
+ * read proceeds. Each full transfer batch of verified blocks — addressed by
8
+ * their own digests, bound to no whole-file identity yet — rides the wire
9
+ * while the next batch is read, and only after the last byte proves the
10
+ * whole-file digest is the manifest sealed. An interrupted or torn stream
11
+ * leaves unreferenced blocks the authority already tolerates, never a
12
+ * sealed artifact; whether the streamed digest matches current ground is
13
+ * the host's stat law, exactly as for the classic capture. Registration
14
+ * owns entity identity — this primitive knows only content.
15
+ *
16
+ * Memory is bounded by construction: one batch on the wire while one batch
17
+ * is read, so a stream holds at most two transfer batches at once.
18
+ */
19
+ import { chunkSizes, manifestFromChunks, } from '../contracts/content.js';
20
+ import { FILE_TRANSFER_BATCH_BYTES } from './batches.js';
21
+ const planStreamBatches = (totalBytes) => {
22
+ const batches = [];
23
+ let offset = 0;
24
+ for (const size of chunkSizes(totalBytes)) {
25
+ const prior = batches.at(-1);
26
+ if (prior && prior.bytes + size <= FILE_TRANSFER_BATCH_BYTES) {
27
+ prior.bytes += size;
28
+ prior.sizes.push(size);
29
+ }
30
+ else {
31
+ batches.push({ offset, bytes: size, sizes: [size] });
32
+ }
33
+ offset += size;
34
+ }
35
+ return batches;
36
+ };
37
+ /** Upload one file's content from a single sequential read, discovering its
38
+ * identity along the way and sealing the manifest last. */
39
+ export async function uploadStream(source, ports) {
40
+ if (!Number.isSafeInteger(source.bytes) || source.bytes <= 0) {
41
+ throw new Error('stream upload source byte count is invalid');
42
+ }
43
+ const whole = ports.createSha256();
44
+ const chunks = [];
45
+ let inFlight;
46
+ try {
47
+ for (const batch of planStreamBatches(source.bytes)) {
48
+ const bytes = await source.read(batch.offset, batch.bytes);
49
+ if (!(bytes instanceof Uint8Array) || bytes.byteLength !== batch.bytes) {
50
+ throw new Error(`stream upload source returned the wrong byte count at offset ${batch.offset}`);
51
+ }
52
+ whole.update(bytes);
53
+ const batchChunks = [];
54
+ let within = 0;
55
+ for (const size of batch.sizes) {
56
+ batchChunks.push({ sha256: ports.sha256Hex(bytes.subarray(within, within + size)), bytes: size });
57
+ within += size;
58
+ }
59
+ chunks.push(...batchChunks);
60
+ await inFlight;
61
+ inFlight = ports.putBlocks(batchChunks, bytes);
62
+ }
63
+ await inFlight;
64
+ inFlight = undefined;
65
+ const checked = manifestFromChunks(whole.digestHex(), chunks);
66
+ if (!checked.ok)
67
+ throw new Error(checked.error);
68
+ await ports.sealManifest(checked.value);
69
+ return { manifest: checked.value, uploadedChunks: chunks.length };
70
+ }
71
+ catch (error) {
72
+ // In-flight blocks settle before control returns, so the host may
73
+ // safely close file and network handles after rejection.
74
+ await inFlight?.catch(() => { });
75
+ throw error;
76
+ }
77
+ }
78
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/transfer/stream.ts"],"names":[],"mappings":"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"}