@amalgm/live 0.2.59 → 0.2.61
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.
- package/PURPOSE.md +17 -5
- package/dist/transfer/download.d.ts +86 -25
- package/dist/transfer/download.d.ts.map +1 -1
- package/dist/transfer/download.js +418 -80
- package/dist/transfer/download.js.map +1 -1
- package/dist/transfer/ranges.d.ts +20 -35
- package/dist/transfer/ranges.d.ts.map +1 -1
- package/dist/transfer/ranges.js +35 -121
- package/dist/transfer/ranges.js.map +1 -1
- package/dist/transfer/refs.d.ts +15 -14
- package/dist/transfer/refs.d.ts.map +1 -1
- package/dist/transfer/refs.js +33 -37
- package/dist/transfer/refs.js.map +1 -1
- package/dist-cjs/transfer/download.js +418 -81
- package/dist-cjs/transfer/download.js.map +1 -1
- package/dist-cjs/transfer/ranges.js +37 -124
- package/dist-cjs/transfer/ranges.js.map +1 -1
- package/dist-cjs/transfer/refs.js +34 -38
- package/dist-cjs/transfer/refs.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,47 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Pack span planning.
|
|
3
3
|
*
|
|
4
|
-
* Sealed envelopes of many small objects live inside one immutable pack.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* ranges is cheaper to read through than to split a request over, so
|
|
9
|
-
* merge across gaps up to RANGE_MERGE_GAP_BYTES
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
import type { ContentRef } from './refs.js';
|
|
14
15
|
/** Read through holes up to this size rather than splitting a range request. */
|
|
15
16
|
export declare const RANGE_MERGE_GAP_BYTES: number;
|
|
16
|
-
/** Merged span bytes one fetcher may hold in flight at once. Admission of
|
|
17
|
-
* spans — not just of the objects above them — is byte-bounded, so a wide
|
|
18
|
-
* same-turn fan-out can never hold every pack's sealed bytes at once. */
|
|
19
|
-
export declare const SPAN_FETCH_MAX_IN_FLIGHT_BYTES: number;
|
|
20
17
|
export interface ByteSpan {
|
|
21
18
|
readonly offset: number;
|
|
22
19
|
readonly length: number;
|
|
23
20
|
}
|
|
24
|
-
export interface
|
|
21
|
+
export interface PackSpanPlan {
|
|
25
22
|
/** Minimal disjoint fetch spans, ascending by offset. */
|
|
26
|
-
readonly spans: ByteSpan[];
|
|
27
|
-
/** Aligned with the input: which span
|
|
28
|
-
|
|
29
|
-
readonly slices: Array<{
|
|
30
|
-
readonly span: number;
|
|
31
|
-
readonly start: number;
|
|
32
|
-
}>;
|
|
23
|
+
readonly spans: readonly ByteSpan[];
|
|
24
|
+
/** Aligned with the input: which span holds each member. */
|
|
25
|
+
readonly memberSpan: readonly number[];
|
|
33
26
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
fetchSpan(url: string, span: ByteSpan | null): Promise<Uint8Array>;
|
|
40
|
-
}
|
|
41
|
-
export interface SpanFetcher {
|
|
42
|
-
/** The sealed envelope bytes a ref names — a whole standalone object, or
|
|
43
|
-
* one exact slice of a pack. */
|
|
44
|
-
fetch(ref: ContentRef): Promise<Uint8Array>;
|
|
45
|
-
}
|
|
46
|
-
export declare function createSpanFetcher(ports: SpanFetcherPorts): SpanFetcher;
|
|
27
|
+
/** Plan one pack's wanted member ranges into broad fetch spans. Pure
|
|
28
|
+
* planning — no IO. Members merge across holes up to `mergeGap`; a span
|
|
29
|
+
* stops growing at `maxSpanBytes`, except that a single member larger than
|
|
30
|
+
* the cap still gets exactly one span of its own. */
|
|
31
|
+
export declare function planPackSpans(members: readonly ByteSpan[], mergeGap: number, maxSpanBytes: number): PackSpanPlan;
|
|
47
32
|
//# sourceMappingURL=ranges.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ranges.d.ts","sourceRoot":"","sources":["../../src/transfer/ranges.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ranges.d.ts","sourceRoot":"","sources":["../../src/transfer/ranges.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAE/C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,4DAA4D;IAC5D,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAQD;;;qDAGqD;AACrD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,YAAY,CA6Bd"}
|
package/dist/transfer/ranges.js
CHANGED
|
@@ -1,145 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Pack span planning.
|
|
3
3
|
*
|
|
4
|
-
* Sealed envelopes of many small objects live inside one immutable pack.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* ranges is cheaper to read through than to split a request over, so
|
|
9
|
-
* merge across gaps up to RANGE_MERGE_GAP_BYTES
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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('
|
|
30
|
+
throw new Error('pack span plan received an invalid merge gap');
|
|
30
31
|
}
|
|
31
|
-
if (
|
|
32
|
-
throw new Error('
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
37
|
-
for (const {
|
|
38
|
-
const end =
|
|
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
|
|
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:
|
|
50
|
+
spans.push({ offset: member.offset, end });
|
|
45
51
|
}
|
|
46
|
-
|
|
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
|
-
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"ranges.js","sourceRoot":"","sources":["../../src/transfer/ranges.ts"],"names":[],"mappings":"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"}
|
package/dist/transfer/refs.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Content-ref
|
|
2
|
+
* Content-ref resolution for direct content-storage fetches.
|
|
3
3
|
*
|
|
4
|
-
* Downloads fetch every storage object straight from content storage.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
|
28
|
-
/** Resolve
|
|
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
|
-
|
|
32
|
-
|
|
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
|
|
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"}
|
package/dist/transfer/refs.js
CHANGED
|
@@ -1,45 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Content-ref
|
|
2
|
+
* Content-ref resolution for direct content-storage fetches.
|
|
3
3
|
*
|
|
4
|
-
* Downloads fetch every storage object straight from content storage.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
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"}
|