@brass-build/client 0.4.1 → 0.5.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.
- package/AGENTS.md +124 -45
- package/CHANGELOG.md +80 -4
- package/README.md +666 -179
- package/dist/api-fetch.d.ts +8 -0
- package/dist/api-fetch.d.ts.map +1 -1
- package/dist/api-fetch.js +35 -8
- package/dist/api-fetch.js.map +1 -1
- package/dist/batched-get.d.ts +11 -0
- package/dist/batched-get.d.ts.map +1 -0
- package/dist/batched-get.js +92 -0
- package/dist/batched-get.js.map +1 -0
- package/dist/call-service.d.ts +4 -0
- package/dist/call-service.d.ts.map +1 -1
- package/dist/call-service.js +66 -3
- package/dist/call-service.js.map +1 -1
- package/dist/client.d.ts +48 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +918 -115
- package/dist/client.js.map +1 -1
- package/dist/document-checkpoints.d.ts +25 -2
- package/dist/document-checkpoints.d.ts.map +1 -1
- package/dist/document-checkpoints.js +258 -61
- package/dist/document-checkpoints.js.map +1 -1
- package/dist/document-commands.d.ts.map +1 -1
- package/dist/document-commands.js +5 -3
- package/dist/document-commands.js.map +1 -1
- package/dist/document-contents.d.ts +10 -0
- package/dist/document-contents.d.ts.map +1 -1
- package/dist/document-contents.js +23 -1
- package/dist/document-contents.js.map +1 -1
- package/dist/document-set-subscription.d.ts +56 -0
- package/dist/document-set-subscription.d.ts.map +1 -0
- package/dist/document-set-subscription.js +240 -0
- package/dist/document-set-subscription.js.map +1 -0
- package/dist/document-signals.d.ts +6 -0
- package/dist/document-signals.d.ts.map +1 -1
- package/dist/document-signals.js +31 -1
- package/dist/document-signals.js.map +1 -1
- package/dist/document-streams.d.ts +36 -2
- package/dist/document-streams.d.ts.map +1 -1
- package/dist/document-streams.js +69 -18
- package/dist/document-streams.js.map +1 -1
- package/dist/document-thumbnail-capture.d.ts +12 -2
- package/dist/document-thumbnail-capture.d.ts.map +1 -1
- package/dist/document-thumbnail-capture.js +103 -27
- package/dist/document-thumbnail-capture.js.map +1 -1
- package/dist/documents-namespace.d.ts +92 -10
- package/dist/documents-namespace.d.ts.map +1 -1
- package/dist/documents-namespace.js +285 -39
- package/dist/documents-namespace.js.map +1 -1
- package/dist/documents.d.ts +173 -8
- package/dist/documents.d.ts.map +1 -1
- package/dist/documents.js +1778 -310
- package/dist/documents.js.map +1 -1
- package/dist/errors.d.ts +3 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fetch-retry.d.ts +1 -0
- package/dist/fetch-retry.d.ts.map +1 -1
- package/dist/fetch-retry.js +2 -0
- package/dist/fetch-retry.js.map +1 -1
- package/dist/file-choice.d.ts +2 -0
- package/dist/file-choice.d.ts.map +1 -0
- package/dist/file-choice.js +27 -0
- package/dist/file-choice.js.map +1 -0
- package/dist/guest-link.d.ts +18 -1
- package/dist/guest-link.d.ts.map +1 -1
- package/dist/guest-link.js +329 -1
- package/dist/guest-link.js.map +1 -1
- package/dist/handle-types.d.ts +67 -5
- package/dist/handle-types.d.ts.map +1 -1
- package/dist/handles.d.ts +15 -19
- package/dist/handles.d.ts.map +1 -1
- package/dist/handles.js +231 -76
- package/dist/handles.js.map +1 -1
- package/dist/hosted-ui.d.ts +61 -0
- package/dist/hosted-ui.d.ts.map +1 -1
- package/dist/hosted-ui.js +94 -42
- package/dist/hosted-ui.js.map +1 -1
- package/dist/import-contract.d.ts +74 -0
- package/dist/import-contract.d.ts.map +1 -0
- package/dist/import-contract.js +163 -0
- package/dist/import-contract.js.map +1 -0
- package/dist/import-guest.d.ts +9 -0
- package/dist/import-guest.d.ts.map +1 -0
- package/dist/import-guest.js +155 -0
- package/dist/import-guest.js.map +1 -0
- package/dist/import-report.d.ts +17 -0
- package/dist/import-report.d.ts.map +1 -0
- package/dist/import-report.js +7 -0
- package/dist/import-report.js.map +1 -0
- package/dist/importer-wait.d.ts +2 -0
- package/dist/importer-wait.d.ts.map +1 -0
- package/dist/importer-wait.js +5 -0
- package/dist/importer-wait.js.map +1 -0
- package/dist/importer.d.ts +18 -4
- package/dist/importer.d.ts.map +1 -1
- package/dist/importer.js +322 -105
- package/dist/importer.js.map +1 -1
- package/dist/in-development.d.ts +2 -0
- package/dist/in-development.d.ts.map +1 -0
- package/dist/in-development.js +10 -0
- package/dist/in-development.js.map +1 -0
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -8
- package/dist/index.js.map +1 -1
- package/dist/init-keys.d.ts +1 -1
- package/dist/init-keys.d.ts.map +1 -1
- package/dist/init-keys.js +6 -1
- package/dist/init-keys.js.map +1 -1
- package/dist/input-documents.d.ts +67 -0
- package/dist/input-documents.d.ts.map +1 -0
- package/dist/input-documents.js +693 -0
- package/dist/input-documents.js.map +1 -0
- package/dist/inputs-status.d.ts +80 -0
- package/dist/inputs-status.d.ts.map +1 -0
- package/dist/inputs-status.js +460 -0
- package/dist/inputs-status.js.map +1 -0
- package/dist/inputs.d.ts +2 -0
- package/dist/inputs.d.ts.map +1 -0
- package/dist/inputs.js +793 -0
- package/dist/inputs.js.map +1 -0
- package/dist/internal.d.ts +6 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -1
- package/dist/internal.js.map +1 -1
- package/dist/join-content.d.ts +33 -0
- package/dist/join-content.d.ts.map +1 -0
- package/dist/join-content.js +114 -0
- package/dist/join-content.js.map +1 -0
- package/dist/join-stamp.d.ts +3 -0
- package/dist/join-stamp.d.ts.map +1 -0
- package/dist/join-stamp.js +12 -0
- package/dist/join-stamp.js.map +1 -0
- package/dist/manifest-drift.d.ts +9 -0
- package/dist/manifest-drift.d.ts.map +1 -0
- package/dist/manifest-drift.js +46 -0
- package/dist/manifest-drift.js.map +1 -0
- package/dist/open-document.d.ts +109 -2
- package/dist/open-document.d.ts.map +1 -1
- package/dist/open-document.js +1822 -811
- package/dist/open-document.js.map +1 -1
- package/dist/page-restore.d.ts +12 -0
- package/dist/page-restore.d.ts.map +1 -0
- package/dist/page-restore.js +23 -0
- package/dist/page-restore.js.map +1 -0
- package/dist/placement.d.ts +13 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +34 -0
- package/dist/placement.js.map +1 -0
- package/dist/presence.d.ts +1 -0
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +16 -4
- package/dist/presence.js.map +1 -1
- package/dist/read-only-document.d.ts +87 -0
- package/dist/read-only-document.d.ts.map +1 -0
- package/dist/read-only-document.js +4 -0
- package/dist/read-only-document.js.map +1 -0
- package/dist/record-paging.d.ts +63 -6
- package/dist/record-paging.d.ts.map +1 -1
- package/dist/record-paging.js +122 -29
- package/dist/record-paging.js.map +1 -1
- package/dist/references.d.ts +40 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +135 -0
- package/dist/references.js.map +1 -0
- package/dist/request-lane.d.ts +5 -0
- package/dist/request-lane.d.ts.map +1 -0
- package/dist/request-lane.js +48 -0
- package/dist/request-lane.js.map +1 -0
- package/dist/sealed-arrow.d.ts +170 -0
- package/dist/sealed-arrow.d.ts.map +1 -0
- package/dist/sealed-arrow.js +769 -0
- package/dist/sealed-arrow.js.map +1 -0
- package/dist/sealed-cache.d.ts +21 -0
- package/dist/sealed-cache.d.ts.map +1 -0
- package/dist/sealed-cache.js +247 -0
- package/dist/sealed-cache.js.map +1 -0
- package/dist/sealed-encode.d.ts +87 -0
- package/dist/sealed-encode.d.ts.map +1 -0
- package/dist/sealed-encode.js +876 -0
- package/dist/sealed-encode.js.map +1 -0
- package/dist/sealed-handles.d.ts +45 -0
- package/dist/sealed-handles.d.ts.map +1 -0
- package/dist/sealed-handles.js +408 -0
- package/dist/sealed-handles.js.map +1 -0
- package/dist/sealed-store.d.ts +242 -0
- package/dist/sealed-store.d.ts.map +1 -0
- package/dist/sealed-store.js +1155 -0
- package/dist/sealed-store.js.map +1 -0
- package/dist/serializer.d.ts +6 -15
- package/dist/serializer.d.ts.map +1 -1
- package/dist/session-toolbar.d.ts +20 -5
- package/dist/session-toolbar.d.ts.map +1 -1
- package/dist/session-toolbar.js +419 -67
- package/dist/session-toolbar.js.map +1 -1
- package/dist/session.d.ts +38 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +562 -79
- package/dist/session.js.map +1 -1
- package/dist/sha256.d.ts +3 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +74 -0
- package/dist/sha256.js.map +1 -0
- package/dist/stable-handles.d.ts +19 -0
- package/dist/stable-handles.d.ts.map +1 -0
- package/dist/stable-handles.js +193 -0
- package/dist/stable-handles.js.map +1 -0
- package/dist/staged-seal.d.ts +2 -0
- package/dist/staged-seal.d.ts.map +1 -0
- package/dist/staged-seal.js +76 -0
- package/dist/staged-seal.js.map +1 -0
- package/dist/subscriber-delivery.d.ts +8 -0
- package/dist/subscriber-delivery.d.ts.map +1 -0
- package/dist/subscriber-delivery.js +31 -0
- package/dist/subscriber-delivery.js.map +1 -0
- package/dist/tail-op-import.d.ts +10 -0
- package/dist/tail-op-import.d.ts.map +1 -0
- package/dist/tail-op-import.js +33 -0
- package/dist/tail-op-import.js.map +1 -0
- package/dist/topic-lifecycle.d.ts +6 -0
- package/dist/topic-lifecycle.d.ts.map +1 -1
- package/dist/topic-lifecycle.js +32 -2
- package/dist/topic-lifecycle.js.map +1 -1
- package/dist/trial-replace-dialog.d.ts +10 -0
- package/dist/trial-replace-dialog.d.ts.map +1 -0
- package/dist/trial-replace-dialog.js +153 -0
- package/dist/trial-replace-dialog.js.map +1 -0
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/undo.d.ts +60 -5
- package/dist/undo.d.ts.map +1 -1
- package/dist/undo.js +344 -75
- package/dist/undo.js.map +1 -1
- package/dist/version-bar.d.ts +1 -0
- package/dist/version-bar.d.ts.map +1 -1
- package/dist/version-bar.js +2 -1
- package/dist/version-bar.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/dist/ws-client.d.ts +21 -0
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +192 -8
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-frame-limit.d.ts +1 -0
- package/dist/ws-frame-limit.d.ts.map +1 -1
- package/dist/ws-frame-limit.js.map +1 -1
- package/docs/app-manifest.md +121 -17
- package/docs/custom-chrome.md +24 -16
- package/docs/hosted-ui-advanced.md +38 -6
- package/docs/imported-documents.md +104 -51
- package/docs/presence.md +16 -4
- package/docs/schemas.md +8 -5
- package/docs/services.md +6 -6
- package/docs/streams.md +139 -150
- package/docs/text-fields.md +26 -19
- package/package.json +5 -2
- package/src/api-fetch.ts +53 -3
- package/src/auth-flow.ts +1 -1
- package/src/batched-get.ts +117 -0
- package/src/call-service.ts +78 -3
- package/src/client.ts +1022 -112
- package/src/document-checkpoints.ts +307 -65
- package/src/document-commands.ts +5 -5
- package/src/document-contents.ts +27 -1
- package/src/document-set-subscription.ts +274 -0
- package/src/document-signals.ts +47 -5
- package/src/document-streams.ts +85 -16
- package/src/document-thumbnail-capture.ts +118 -32
- package/src/documents-namespace.ts +539 -84
- package/src/documents.ts +2017 -348
- package/src/errors.ts +17 -6
- package/src/fetch-retry.ts +5 -0
- package/src/file-choice.ts +26 -0
- package/src/guest-link.ts +362 -1
- package/src/handle-types.ts +73 -47
- package/src/handles.ts +253 -83
- package/src/hosted-ui.ts +211 -50
- package/src/import-contract.ts +227 -0
- package/src/import-guest.ts +193 -0
- package/src/import-report.ts +24 -0
- package/src/importer-wait.ts +4 -0
- package/src/importer.ts +386 -131
- package/src/in-development.ts +10 -0
- package/src/index.ts +76 -16
- package/src/init-keys.ts +6 -1
- package/src/input-documents.ts +819 -0
- package/src/inputs-status.ts +598 -0
- package/src/inputs.ts +1090 -0
- package/src/internal.ts +7 -0
- package/src/join-content.ts +135 -0
- package/src/join-stamp.ts +12 -0
- package/src/manifest-drift.ts +54 -0
- package/src/open-document.ts +2346 -930
- package/src/page-restore.ts +38 -0
- package/src/placement.ts +54 -0
- package/src/presence.ts +21 -5
- package/src/read-only-document.ts +133 -0
- package/src/record-paging.ts +147 -25
- package/src/references.ts +207 -0
- package/src/request-lane.ts +59 -0
- package/src/sealed-arrow.ts +843 -0
- package/src/sealed-cache.ts +267 -0
- package/src/sealed-encode.ts +1015 -0
- package/src/sealed-handles.ts +486 -0
- package/src/sealed-store.ts +1330 -0
- package/src/serializer.ts +8 -20
- package/src/session-toolbar.ts +463 -72
- package/src/session.ts +740 -99
- package/src/sha256.ts +65 -0
- package/src/stable-handles.ts +235 -0
- package/src/staged-seal.ts +90 -0
- package/src/subscriber-delivery.ts +37 -0
- package/src/tail-op-import.ts +30 -0
- package/src/topic-lifecycle.ts +38 -2
- package/src/trial-replace-dialog.ts +163 -0
- package/src/types.ts +6 -6
- package/src/undo.ts +386 -73
- package/src/version-bar.ts +5 -1
- package/src/version.ts +1 -1
- package/src/ws-client.ts +208 -16
- package/src/ws-frame-limit.ts +1 -0
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
// Frame 4's reader: a stream's maps as typed Arrow tables, inferred from the
|
|
2
|
+
// values alone by the platform's encoder, compressed with the rest of the
|
|
3
|
+
// stream as one ZSTD frame.
|
|
4
|
+
//
|
|
5
|
+
// A value sits in a slot, and a slot holds one or more variants. A variant is
|
|
6
|
+
// a JSON kind, and for an object its ordered key list plus a slot per field,
|
|
7
|
+
// so values that differ in kind or key set sit side by side rather than in one
|
|
8
|
+
// widened column. A slot of several variants carries a `t` column naming each
|
|
9
|
+
// row's variant.
|
|
10
|
+
//
|
|
11
|
+
// null no column
|
|
12
|
+
// bool uint8, or a constant in the schema
|
|
13
|
+
// num int32 where every value is one, else float64; or a constant
|
|
14
|
+
// str dictionary utf8, or a constant, or (`derive`) a code into
|
|
15
|
+
// rules that rebuild the text from a sibling number field
|
|
16
|
+
// tuple a struct of position slots (one array length)
|
|
17
|
+
// list a list of one item slot
|
|
18
|
+
// obj a struct of field slots
|
|
19
|
+
// json JSON text, where no other variant can carry the value
|
|
20
|
+
//
|
|
21
|
+
// A map is one or more tables. Each holds its rows' keys, their positions in
|
|
22
|
+
// the map as gaps from the row before, and the root slot, so the reader puts
|
|
23
|
+
// every entry back at its position and the map's order is its own.
|
|
24
|
+
//
|
|
25
|
+
// What a read returns is what `JSON.parse` of the value's JSON text returns:
|
|
26
|
+
// the same keys in the same order. Objects are built by cloning a per-variant
|
|
27
|
+
// template; no generated code runs, which a page whose CSP withholds
|
|
28
|
+
// `unsafe-eval` requires.
|
|
29
|
+
//
|
|
30
|
+
// Nothing here is exported from the package. Arrow is loaded only when a
|
|
31
|
+
// frame-4 stream is opened (`loadSealedCodec` in `sealed-store.ts`).
|
|
32
|
+
import { DictionaryBatch } from 'apache-arrow/fb/dictionary-batch';
|
|
33
|
+
import { Footer } from 'apache-arrow/fb/footer';
|
|
34
|
+
import { Message } from 'apache-arrow/fb/message';
|
|
35
|
+
import { MessageHeader } from 'apache-arrow/fb/message-header';
|
|
36
|
+
import { RecordBatch } from 'apache-arrow/fb/record-batch';
|
|
37
|
+
import { ByteBuffer } from 'flatbuffers';
|
|
38
|
+
/** The derived-string code for a row whose text no rule rebuilds. */
|
|
39
|
+
export const HELD = 255;
|
|
40
|
+
/** The table metadata's key in an IPC file's schema metadata. */
|
|
41
|
+
export const TABLE_META_KEY = 'brass:table';
|
|
42
|
+
/** Whether a variant stores a column. */
|
|
43
|
+
export function variantStored(v) {
|
|
44
|
+
switch (v.k) {
|
|
45
|
+
case 'null':
|
|
46
|
+
return false;
|
|
47
|
+
case 'bool':
|
|
48
|
+
case 'num':
|
|
49
|
+
case 'str':
|
|
50
|
+
return v.c === undefined;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// --- parts ---------------------------------------------------------------
|
|
55
|
+
// Each row decodes on its own, so a decoder that strips a leading BOM would
|
|
56
|
+
// take U+FEFF off the start of every value that begins with one.
|
|
57
|
+
const utf8Decoder = new TextDecoder('utf-8', { ignoreBOM: true });
|
|
58
|
+
/** Named parts behind a JSON table of names and lengths. */
|
|
59
|
+
export function unpackParts(bytes) {
|
|
60
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
61
|
+
const headLength = view.getUint32(0, true);
|
|
62
|
+
const table = JSON.parse(utf8Decoder.decode(bytes.subarray(4, 4 + headLength)));
|
|
63
|
+
let at = 4 + headLength;
|
|
64
|
+
return table.map(([name, length]) => {
|
|
65
|
+
const part = bytes.subarray(at, at + length);
|
|
66
|
+
at += length;
|
|
67
|
+
if (part.byteLength !== length)
|
|
68
|
+
throw new RangeError(`part ${name} is truncated`);
|
|
69
|
+
return [name, part];
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// --- checking ------------------------------------------------------------
|
|
73
|
+
const ARROW_MAGIC = [0x41, 0x52, 0x52, 0x4f, 0x57, 0x31];
|
|
74
|
+
/** Throws unless `count` items of `stride` bytes fit in the buffer. */
|
|
75
|
+
function fits(count, stride, bb) {
|
|
76
|
+
if (!(count >= 0 && count * stride <= bb.capacity())) {
|
|
77
|
+
throw new RangeError('an IPC vector runs past its bytes');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function checkField(f, bb, depth, budget) {
|
|
81
|
+
if (depth > 64 || (budget.left -= 1) < 0)
|
|
82
|
+
throw new RangeError('an IPC schema nests too deep');
|
|
83
|
+
fits(f.childrenLength(), 4, bb);
|
|
84
|
+
fits(f.customMetadataLength(), 4, bb);
|
|
85
|
+
for (let i = 0; i < f.childrenLength(); i += 1) {
|
|
86
|
+
const child = f.children(i);
|
|
87
|
+
if (child === null)
|
|
88
|
+
throw new RangeError('an IPC field is missing');
|
|
89
|
+
checkField(child, bb, depth + 1, budget);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function checkSchema(s, bb) {
|
|
93
|
+
fits(s.fieldsLength(), 4, bb);
|
|
94
|
+
fits(s.customMetadataLength(), 4, bb);
|
|
95
|
+
fits(s.featuresLength(), 8, bb);
|
|
96
|
+
const budget = { left: bb.capacity() >> 2 };
|
|
97
|
+
for (let i = 0; i < s.fieldsLength(); i += 1) {
|
|
98
|
+
const field = s.fields(i);
|
|
99
|
+
if (field === null)
|
|
100
|
+
throw new RangeError('an IPC field is missing');
|
|
101
|
+
checkField(field, bb, 0, budget);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function checkBatch(rb, bb) {
|
|
105
|
+
fits(rb.nodesLength(), 16, bb);
|
|
106
|
+
fits(rb.buffersLength(), 16, bb);
|
|
107
|
+
fits(rb.variadicBufferCountsLength(), 8, bb);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Throws unless an Arrow IPC file's footer and every message it points at
|
|
111
|
+
* stay inside the file. Arrow reads a file by its own offsets and vector
|
|
112
|
+
* lengths, so a footer block naming a message of the wrong kind makes its
|
|
113
|
+
* reader loop, and a declared length makes it allocate that much; both are
|
|
114
|
+
* held to the bytes here before Arrow reads anything.
|
|
115
|
+
*/
|
|
116
|
+
export function checkIpcFile(bytes) {
|
|
117
|
+
const n = bytes.byteLength;
|
|
118
|
+
if (n < 22)
|
|
119
|
+
throw new RangeError('an IPC file is truncated');
|
|
120
|
+
for (let i = 0; i < ARROW_MAGIC.length; i += 1) {
|
|
121
|
+
if (bytes[i] !== ARROW_MAGIC[i] || bytes[n - 6 + i] !== ARROW_MAGIC[i]) {
|
|
122
|
+
throw new RangeError('an IPC file lacks its magic');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, n);
|
|
126
|
+
const footerLength = view.getInt32(n - 10, true);
|
|
127
|
+
if (!(footerLength > 0 && footerLength <= n - 18)) {
|
|
128
|
+
throw new RangeError('an IPC footer runs past its file');
|
|
129
|
+
}
|
|
130
|
+
const bodyEnd = n - 10 - footerLength;
|
|
131
|
+
const fbb = new ByteBuffer(bytes.subarray(bodyEnd, n - 10));
|
|
132
|
+
const footer = Footer.getRootAsFooter(fbb);
|
|
133
|
+
const schema = footer.schema();
|
|
134
|
+
if (schema === null)
|
|
135
|
+
throw new RangeError('an IPC file has no schema');
|
|
136
|
+
checkSchema(schema, fbb);
|
|
137
|
+
fits(footer.recordBatchesLength(), 24, fbb);
|
|
138
|
+
fits(footer.dictionariesLength(), 24, fbb);
|
|
139
|
+
fits(footer.customMetadataLength(), 4, fbb);
|
|
140
|
+
const blocks = [
|
|
141
|
+
[MessageHeader.DictionaryBatch, footer.dictionariesLength(), (i) => footer.dictionaries(i)],
|
|
142
|
+
[MessageHeader.RecordBatch, footer.recordBatchesLength(), (i) => footer.recordBatches(i)],
|
|
143
|
+
];
|
|
144
|
+
for (const [want, count, blockAt] of blocks) {
|
|
145
|
+
for (let i = 0; i < count; i += 1) {
|
|
146
|
+
const block = blockAt(i);
|
|
147
|
+
if (block === null)
|
|
148
|
+
throw new RangeError('an IPC block is missing');
|
|
149
|
+
const offset = Number(block.offset());
|
|
150
|
+
const metaLength = block.metaDataLength();
|
|
151
|
+
const bodyLength = Number(block.bodyLength());
|
|
152
|
+
if (!(offset >= 8 && metaLength > 8 && bodyLength >= 0 && offset + metaLength + bodyLength <= bodyEnd)) {
|
|
153
|
+
throw new RangeError('an IPC block runs past its file');
|
|
154
|
+
}
|
|
155
|
+
if (view.getInt32(offset, true) !== -1)
|
|
156
|
+
throw new RangeError('an IPC block names no message');
|
|
157
|
+
const length = view.getInt32(offset + 4, true);
|
|
158
|
+
if (!(length > 0 && length <= metaLength - 8)) {
|
|
159
|
+
throw new RangeError('an IPC message runs past its block');
|
|
160
|
+
}
|
|
161
|
+
const mbb = new ByteBuffer(bytes.subarray(offset + 8, offset + 8 + length));
|
|
162
|
+
const message = Message.getRootAsMessage(mbb);
|
|
163
|
+
if (message.headerType() !== want)
|
|
164
|
+
throw new RangeError('an IPC block names the wrong message');
|
|
165
|
+
if (!(Number(message.bodyLength()) <= bodyLength)) {
|
|
166
|
+
throw new RangeError('an IPC message body runs past its block');
|
|
167
|
+
}
|
|
168
|
+
fits(message.customMetadataLength(), 4, mbb);
|
|
169
|
+
const rb = want === MessageHeader.RecordBatch
|
|
170
|
+
? message.header(new RecordBatch())
|
|
171
|
+
: message.header(new DictionaryBatch())?.data() ?? null;
|
|
172
|
+
if (rb === null)
|
|
173
|
+
throw new RangeError('an IPC message has no batch');
|
|
174
|
+
checkBatch(rb, mbb);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Arrow's type ids for the column types a sealed table is written with.
|
|
179
|
+
const TYPE_INT = 2;
|
|
180
|
+
const TYPE_FLOAT = 3;
|
|
181
|
+
const TYPE_UTF8 = 5;
|
|
182
|
+
const TYPE_LIST = 12;
|
|
183
|
+
const TYPE_STRUCT = 13;
|
|
184
|
+
const TYPE_DICTIONARY = -1;
|
|
185
|
+
/**
|
|
186
|
+
* Throws unless a stored table's buffers carry every row it declares. A
|
|
187
|
+
* column's length is the uploader's claim, and the reader allocates and loops
|
|
188
|
+
* on it, so each claim is held to the bytes behind it: a fixed-width column
|
|
189
|
+
* has a value per row, offsets rise and stay inside the bytes or child they
|
|
190
|
+
* index (so no two rows share a range, and decoding every row reads each
|
|
191
|
+
* byte once), a struct's fields are each as long as it, and a dictionary is
|
|
192
|
+
* no longer than the column that indexes it. A table's own struct carries
|
|
193
|
+
* the key, gap and value columns.
|
|
194
|
+
*/
|
|
195
|
+
export function checkTableData(data) {
|
|
196
|
+
if (data.type.typeId !== TYPE_STRUCT || data.children.length !== 3) {
|
|
197
|
+
throw new RangeError('a table is not a key, gap and value struct');
|
|
198
|
+
}
|
|
199
|
+
checkData(data);
|
|
200
|
+
}
|
|
201
|
+
function checkData(d) {
|
|
202
|
+
const n = d.length;
|
|
203
|
+
if (d.offset !== 0 || !(n >= 0))
|
|
204
|
+
throw new RangeError('a column starts past its buffer');
|
|
205
|
+
switch (d.type.typeId) {
|
|
206
|
+
case TYPE_INT:
|
|
207
|
+
case TYPE_FLOAT:
|
|
208
|
+
if (d.values.length < n)
|
|
209
|
+
throw new RangeError('a column is shorter than its rows');
|
|
210
|
+
return;
|
|
211
|
+
case TYPE_UTF8:
|
|
212
|
+
checkOffsets(d.valueOffsets, n, d.values.length);
|
|
213
|
+
return;
|
|
214
|
+
case TYPE_LIST: {
|
|
215
|
+
const child = d.children[0];
|
|
216
|
+
if (child === undefined)
|
|
217
|
+
throw new RangeError('a list has no items');
|
|
218
|
+
checkOffsets(d.valueOffsets, n, child.length);
|
|
219
|
+
checkData(child);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
case TYPE_STRUCT:
|
|
223
|
+
for (const child of d.children) {
|
|
224
|
+
if (child.length < n)
|
|
225
|
+
throw new RangeError('a field is shorter than its struct');
|
|
226
|
+
checkData(child);
|
|
227
|
+
}
|
|
228
|
+
return;
|
|
229
|
+
case TYPE_DICTIONARY: {
|
|
230
|
+
const dictionary = d.dictionary;
|
|
231
|
+
if (d.values.length < n || dictionary === undefined || dictionary.length > n) {
|
|
232
|
+
throw new RangeError('a dictionary column does not cover its rows');
|
|
233
|
+
}
|
|
234
|
+
checkIndices(d.values, n, dictionary.length);
|
|
235
|
+
for (const chunk of dictionary.data) {
|
|
236
|
+
if (chunk.type.typeId !== TYPE_UTF8)
|
|
237
|
+
throw new RangeError('a dictionary is not text');
|
|
238
|
+
checkData(chunk);
|
|
239
|
+
}
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
throw new RangeError(`a column has type ${d.type.typeId}, which no sealed table is written with`);
|
|
244
|
+
}
|
|
245
|
+
/** Throws unless each of the first `n` values indexes into `extent`. */
|
|
246
|
+
function checkIndices(values, n, extent) {
|
|
247
|
+
for (let row = 0; row < n; row += 1) {
|
|
248
|
+
const i = values[row];
|
|
249
|
+
if (!(i >= 0 && i < extent))
|
|
250
|
+
throw new RangeError('an index runs past what it indexes');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function checkOffsets(offsets, n, extent) {
|
|
254
|
+
if (offsets.length < n + 1)
|
|
255
|
+
throw new RangeError('a column is shorter than its rows');
|
|
256
|
+
let last = offsets[0];
|
|
257
|
+
if (last < 0)
|
|
258
|
+
throw new RangeError('an offset is negative');
|
|
259
|
+
for (let row = 1; row <= n; row += 1) {
|
|
260
|
+
const next = offsets[row];
|
|
261
|
+
if (next < last)
|
|
262
|
+
throw new RangeError('offsets fall');
|
|
263
|
+
last = next;
|
|
264
|
+
}
|
|
265
|
+
if (last > extent)
|
|
266
|
+
throw new RangeError('an offset runs past its bytes');
|
|
267
|
+
}
|
|
268
|
+
/** What one decode shares across its tables: each object key list's
|
|
269
|
+
* construction site, and each dictionary decoded once. A `lazy` context
|
|
270
|
+
* decodes a dictionary's entries as reads reach them, which suits a keyed
|
|
271
|
+
* read; a walk reads every entry, and decoding them up front is faster. */
|
|
272
|
+
export class ReadContext {
|
|
273
|
+
lazy;
|
|
274
|
+
shapes = new Map();
|
|
275
|
+
dictionaries = new WeakMap();
|
|
276
|
+
lazyDictionaries = new WeakMap();
|
|
277
|
+
constructor(lazy = false) {
|
|
278
|
+
this.lazy = lazy;
|
|
279
|
+
}
|
|
280
|
+
strings(dictionary) {
|
|
281
|
+
let strings = this.dictionaries.get(dictionary);
|
|
282
|
+
if (strings === undefined) {
|
|
283
|
+
strings = [];
|
|
284
|
+
for (const chunk of dictionary.data) {
|
|
285
|
+
const at = utf8At(chunk);
|
|
286
|
+
for (let row = 0; row < chunk.length; row += 1)
|
|
287
|
+
strings.push(at(row));
|
|
288
|
+
}
|
|
289
|
+
this.dictionaries.set(dictionary, strings);
|
|
290
|
+
}
|
|
291
|
+
return strings;
|
|
292
|
+
}
|
|
293
|
+
lazyStrings(dictionary) {
|
|
294
|
+
let strings = this.lazyDictionaries.get(dictionary);
|
|
295
|
+
if (strings === undefined) {
|
|
296
|
+
strings = new LazyDictionary(dictionary.data);
|
|
297
|
+
this.lazyDictionaries.set(dictionary, strings);
|
|
298
|
+
}
|
|
299
|
+
return strings;
|
|
300
|
+
}
|
|
301
|
+
/** A flag per dictionary entry equal to `s`, or null where none is. */
|
|
302
|
+
matches(dictionary, s) {
|
|
303
|
+
return this.lazy ? this.lazyStrings(dictionary).matches(s) : dictionaryMatches(this.strings(dictionary), s);
|
|
304
|
+
}
|
|
305
|
+
shapeId(keys) {
|
|
306
|
+
const sig = keys.join('\u0000');
|
|
307
|
+
let id = this.shapes.get(sig);
|
|
308
|
+
if (id === undefined) {
|
|
309
|
+
id = this.shapes.size;
|
|
310
|
+
this.shapes.set(sig, id);
|
|
311
|
+
}
|
|
312
|
+
return id;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/** A dictionary whose entries are decoded on first read and kept. */
|
|
316
|
+
export class LazyDictionary {
|
|
317
|
+
chunks;
|
|
318
|
+
length;
|
|
319
|
+
decoded;
|
|
320
|
+
/** Each chunk's first entry. */
|
|
321
|
+
starts = [];
|
|
322
|
+
constructor(chunks) {
|
|
323
|
+
this.chunks = chunks;
|
|
324
|
+
let length = 0;
|
|
325
|
+
for (const chunk of chunks) {
|
|
326
|
+
this.starts.push(length);
|
|
327
|
+
length += chunk.length;
|
|
328
|
+
}
|
|
329
|
+
this.length = length;
|
|
330
|
+
this.decoded = Array.from({ length });
|
|
331
|
+
}
|
|
332
|
+
at(i) {
|
|
333
|
+
let s = this.decoded[i];
|
|
334
|
+
if (s === undefined) {
|
|
335
|
+
let c = this.chunks.length - 1;
|
|
336
|
+
while (c > 0 && this.starts[c] > i)
|
|
337
|
+
c -= 1;
|
|
338
|
+
const chunk = this.chunks[c];
|
|
339
|
+
const row = i - this.starts[c];
|
|
340
|
+
const offsets = chunk.valueOffsets;
|
|
341
|
+
s = utf8Decoder.decode(chunk.values.subarray(offsets[row], offsets[row + 1]));
|
|
342
|
+
this.decoded[i] = s;
|
|
343
|
+
}
|
|
344
|
+
return s;
|
|
345
|
+
}
|
|
346
|
+
/** A flag per entry equal to `s`, found by comparing bytes, or null where
|
|
347
|
+
* none is. */
|
|
348
|
+
matches(s) {
|
|
349
|
+
// An entry reads with replacement characters where its bytes are not
|
|
350
|
+
// UTF-8, so a wanted string carrying one is compared as text.
|
|
351
|
+
if (s.includes('\uFFFD'))
|
|
352
|
+
return dictionaryMatches(Array.from({ length: this.length }, (_, i) => this.at(i)), s);
|
|
353
|
+
// Every entry decodes well formed, so a string that is not equals none.
|
|
354
|
+
if (!wellFormed(s))
|
|
355
|
+
return null;
|
|
356
|
+
const want = utf8Encoder.encode(s);
|
|
357
|
+
const length = want.length;
|
|
358
|
+
let flags = null;
|
|
359
|
+
this.chunks.forEach((chunk, c) => {
|
|
360
|
+
const bytes = chunk.values;
|
|
361
|
+
const offsets = chunk.valueOffsets;
|
|
362
|
+
for (let row = 0; row < chunk.length; row += 1) {
|
|
363
|
+
const start = offsets[row];
|
|
364
|
+
if (offsets[row + 1] - start !== length)
|
|
365
|
+
continue;
|
|
366
|
+
let i = 0;
|
|
367
|
+
while (i < length && bytes[start + i] === want[i])
|
|
368
|
+
i += 1;
|
|
369
|
+
if (i === length)
|
|
370
|
+
(flags ??= new Uint8Array(this.length))[this.starts[c] + row] = 1;
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
return flags;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function utf8At(d) {
|
|
377
|
+
const bytes = d.values;
|
|
378
|
+
const offsets = d.valueOffsets;
|
|
379
|
+
return (row) => utf8Decoder.decode(bytes.subarray(offsets[row], offsets[row + 1]));
|
|
380
|
+
}
|
|
381
|
+
// Keys are compared by their bytes on a keyed read, so a key whose bytes are
|
|
382
|
+
// not UTF-8 is refused rather than read with replacement characters.
|
|
383
|
+
const keyDecoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
384
|
+
/**
|
|
385
|
+
* A table's keys as text, row by row. Each key reads only in the form the
|
|
386
|
+
* encoder writes it (a non-negative integer id, a separator that falls after
|
|
387
|
+
* the first half, UTF-8 text, JSON text as `JSON.stringify` writes it), so a
|
|
388
|
+
* walk and a keyed read (`rowsWithKey`) agree on every key; any other form
|
|
389
|
+
* throws.
|
|
390
|
+
*/
|
|
391
|
+
export function keyReader(meta, d, cx) {
|
|
392
|
+
const key = meta.key;
|
|
393
|
+
if ('prefix' in key) {
|
|
394
|
+
const prefix = key.prefix;
|
|
395
|
+
const ids = d.values;
|
|
396
|
+
if (typeof prefix !== 'string')
|
|
397
|
+
throw new RangeError('a key prefix is not text');
|
|
398
|
+
for (let row = 0; row < d.length; row += 1) {
|
|
399
|
+
const id = ids[row];
|
|
400
|
+
if (!(Number.isInteger(id) && id >= 0))
|
|
401
|
+
throw new RangeError('a key id is not a whole number');
|
|
402
|
+
}
|
|
403
|
+
return (row) => prefix + String(ids[row]);
|
|
404
|
+
}
|
|
405
|
+
if ('split' in key) {
|
|
406
|
+
const [a, b] = d.children;
|
|
407
|
+
if (cx.lazy)
|
|
408
|
+
return lazySplitKeyReader(key.split, a, b, cx);
|
|
409
|
+
const as = cx.strings(a.dictionary);
|
|
410
|
+
const bs = cx.strings(b.dictionary);
|
|
411
|
+
const ai = a.values;
|
|
412
|
+
const bi = b.values;
|
|
413
|
+
const sep = key.split;
|
|
414
|
+
if (typeof sep !== 'string' || sep === '' || as.some((s) => (s + sep).indexOf(sep) !== s.length)) {
|
|
415
|
+
throw new RangeError('a key separator does not split its keys');
|
|
416
|
+
}
|
|
417
|
+
return (row) => as[ai[row]] + sep + bs[bi[row]];
|
|
418
|
+
}
|
|
419
|
+
const bytes = d.values;
|
|
420
|
+
const offsets = d.valueOffsets;
|
|
421
|
+
const at = (row) => keyDecoder.decode(bytes.subarray(offsets[row], offsets[row + 1]));
|
|
422
|
+
if ('json' in key) {
|
|
423
|
+
return (row) => {
|
|
424
|
+
const text = at(row);
|
|
425
|
+
const k = JSON.parse(text);
|
|
426
|
+
if (typeof k !== 'string' || JSON.stringify(k) !== text)
|
|
427
|
+
throw new RangeError('a JSON key is not as written');
|
|
428
|
+
return k;
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
return at;
|
|
432
|
+
}
|
|
433
|
+
/** Split keys read through lazy dictionaries. Each first half is checked
|
|
434
|
+
* against the separator when a read first reaches it. */
|
|
435
|
+
function lazySplitKeyReader(sep, a, b, cx) {
|
|
436
|
+
if (typeof sep !== 'string' || sep === '')
|
|
437
|
+
throw new RangeError('a key separator does not split its keys');
|
|
438
|
+
const as = cx.lazyStrings(a.dictionary);
|
|
439
|
+
const bs = cx.lazyStrings(b.dictionary);
|
|
440
|
+
const ai = a.values;
|
|
441
|
+
const bi = b.values;
|
|
442
|
+
const checked = new Uint8Array(as.length);
|
|
443
|
+
return (row) => {
|
|
444
|
+
const i = ai[row];
|
|
445
|
+
const first = as.at(i);
|
|
446
|
+
if (checked[i] === 0) {
|
|
447
|
+
if ((first + sep).indexOf(sep) !== first.length)
|
|
448
|
+
throw new RangeError('a key separator does not split its keys');
|
|
449
|
+
checked[i] = 1;
|
|
450
|
+
}
|
|
451
|
+
return first + sep + bs.at(bi[row]);
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
const utf8Encoder = new TextEncoder();
|
|
455
|
+
/** Whether a string is well-formed UTF-16, which UTF-8 can carry. */
|
|
456
|
+
const wellFormed = (s) => s.isWellFormed();
|
|
457
|
+
/**
|
|
458
|
+
* The rows of a table whose key reads as `key`, found from the key column
|
|
459
|
+
* alone: an id compared as a number, a dictionary half by its index, text by
|
|
460
|
+
* its bytes. Nothing is decoded for a row that does not match.
|
|
461
|
+
*/
|
|
462
|
+
export function rowsWithKey(meta, d, key, cx) {
|
|
463
|
+
const rows = [];
|
|
464
|
+
const n = d.length;
|
|
465
|
+
const k = meta.key;
|
|
466
|
+
if ('prefix' in k) {
|
|
467
|
+
if (!key.startsWith(k.prefix))
|
|
468
|
+
return rows;
|
|
469
|
+
const digits = key.slice(k.prefix.length);
|
|
470
|
+
const id = Number(digits);
|
|
471
|
+
if (digits === '' || String(id) !== digits)
|
|
472
|
+
return rows;
|
|
473
|
+
const ids = d.values;
|
|
474
|
+
for (let row = 0; row < n; row += 1)
|
|
475
|
+
if (ids[row] === id)
|
|
476
|
+
rows.push(row);
|
|
477
|
+
return rows;
|
|
478
|
+
}
|
|
479
|
+
if ('split' in k) {
|
|
480
|
+
const at = key.indexOf(k.split);
|
|
481
|
+
if (at < 0)
|
|
482
|
+
return rows;
|
|
483
|
+
const [a, b] = d.children;
|
|
484
|
+
const inA = cx.matches(a.dictionary, key.slice(0, at));
|
|
485
|
+
const inB = cx.matches(b.dictionary, key.slice(at + k.split.length));
|
|
486
|
+
if (inA === null || inB === null)
|
|
487
|
+
return rows;
|
|
488
|
+
const ai = a.values;
|
|
489
|
+
const bi = b.values;
|
|
490
|
+
for (let row = 0; row < n; row += 1)
|
|
491
|
+
if (inA[ai[row]] === 1 && inB[bi[row]] === 1)
|
|
492
|
+
rows.push(row);
|
|
493
|
+
return rows;
|
|
494
|
+
}
|
|
495
|
+
// Text keys are written well formed, and JSON keys as `JSON.stringify` writes them.
|
|
496
|
+
if ('text' in k && !wellFormed(key))
|
|
497
|
+
return rows;
|
|
498
|
+
const want = utf8Encoder.encode('json' in k ? JSON.stringify(key) : key);
|
|
499
|
+
const bytes = d.values;
|
|
500
|
+
const offsets = d.valueOffsets;
|
|
501
|
+
const length = want.length;
|
|
502
|
+
for (let row = 0; row < n; row += 1) {
|
|
503
|
+
const start = offsets[row];
|
|
504
|
+
if (offsets[row + 1] - start !== length)
|
|
505
|
+
continue;
|
|
506
|
+
let i = 0;
|
|
507
|
+
while (i < length && bytes[start + i] === want[i])
|
|
508
|
+
i += 1;
|
|
509
|
+
if (i === length)
|
|
510
|
+
rows.push(row);
|
|
511
|
+
}
|
|
512
|
+
return rows;
|
|
513
|
+
}
|
|
514
|
+
/** A flag per dictionary entry equal to `s`, or null where none is. */
|
|
515
|
+
function dictionaryMatches(strings, s) {
|
|
516
|
+
let flags = null;
|
|
517
|
+
for (let i = 0; i < strings.length; i += 1) {
|
|
518
|
+
if (strings[i] === s)
|
|
519
|
+
(flags ??= new Uint8Array(strings.length))[i] = 1;
|
|
520
|
+
}
|
|
521
|
+
return flags;
|
|
522
|
+
}
|
|
523
|
+
export function readSlot(slot, d, cx) {
|
|
524
|
+
if (d.offset !== 0)
|
|
525
|
+
throw new RangeError('a slot column starts past its buffer');
|
|
526
|
+
let c = 0;
|
|
527
|
+
const tags = slot.v.length > 1 ? d.children[c++].values : null;
|
|
528
|
+
const readers = slot.v.map((v) => readVariant(v, variantStored(v) ? d.children[c++] : null, cx));
|
|
529
|
+
if (tags === null)
|
|
530
|
+
return readers[0];
|
|
531
|
+
checkIndices(tags, d.length, readers.length);
|
|
532
|
+
return (row) => readers[tags[row]](row);
|
|
533
|
+
}
|
|
534
|
+
function readVariant(v, d, cx) {
|
|
535
|
+
switch (v.k) {
|
|
536
|
+
case 'null':
|
|
537
|
+
return () => null;
|
|
538
|
+
case 'bool': {
|
|
539
|
+
if (v.c !== undefined) {
|
|
540
|
+
const c = v.c;
|
|
541
|
+
return () => c;
|
|
542
|
+
}
|
|
543
|
+
const values = d.values;
|
|
544
|
+
return (row) => values[row] === 1;
|
|
545
|
+
}
|
|
546
|
+
case 'num': {
|
|
547
|
+
if (v.c !== undefined) {
|
|
548
|
+
const c = v.c;
|
|
549
|
+
return () => c;
|
|
550
|
+
}
|
|
551
|
+
const values = d.values;
|
|
552
|
+
return (row) => values[row];
|
|
553
|
+
}
|
|
554
|
+
case 'str': {
|
|
555
|
+
if (v.c !== undefined) {
|
|
556
|
+
const c = v.c;
|
|
557
|
+
return () => c;
|
|
558
|
+
}
|
|
559
|
+
if (v.derive)
|
|
560
|
+
throw new TypeError('a derived string is read through its object');
|
|
561
|
+
const idx = d.values;
|
|
562
|
+
if (cx.lazy) {
|
|
563
|
+
const lazy = cx.lazyStrings(d.dictionary);
|
|
564
|
+
return (row) => lazy.at(idx[row]);
|
|
565
|
+
}
|
|
566
|
+
const strings = cx.strings(d.dictionary);
|
|
567
|
+
return (row) => strings[idx[row]];
|
|
568
|
+
}
|
|
569
|
+
case 'json': {
|
|
570
|
+
const at = utf8At(d);
|
|
571
|
+
return (row) => JSON.parse(at(row));
|
|
572
|
+
}
|
|
573
|
+
case 'tuple':
|
|
574
|
+
return tupleReader(v.items.map((s, p) => readSlot(s, d.children[p], cx)));
|
|
575
|
+
case 'list': {
|
|
576
|
+
const offsets = d.valueOffsets;
|
|
577
|
+
const item = readSlot(v.item, d.children[0], cx);
|
|
578
|
+
return (row) => {
|
|
579
|
+
const out = [];
|
|
580
|
+
for (let j = offsets[row]; j < offsets[row + 1]; j += 1)
|
|
581
|
+
out.push(item(j));
|
|
582
|
+
return out;
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
case 'obj': {
|
|
586
|
+
// The encoder writes an object whose keys assign onto `{}` in order as
|
|
587
|
+
// JSON text instead, so a field named `__proto__` is not a table's.
|
|
588
|
+
if (v.keys.includes('__proto__') || v.keys.length !== v.fields.length) {
|
|
589
|
+
throw new RangeError('an object variant names a key no table carries');
|
|
590
|
+
}
|
|
591
|
+
// Fields every row agrees on ride in the template the object is cloned
|
|
592
|
+
// from; the rest are read per row.
|
|
593
|
+
const template = {};
|
|
594
|
+
const varying = [];
|
|
595
|
+
const readers = [];
|
|
596
|
+
v.fields.forEach((s, f) => {
|
|
597
|
+
const key = v.keys[f];
|
|
598
|
+
const only = s.v.length === 1 ? s.v[0] : null;
|
|
599
|
+
if (only?.k === 'null')
|
|
600
|
+
template[key] = null;
|
|
601
|
+
else if ((only?.k === 'num' || only?.k === 'str' || only?.k === 'bool') &&
|
|
602
|
+
only.c !== undefined) {
|
|
603
|
+
template[key] = only.c;
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
template[key] = null;
|
|
607
|
+
varying.push(key);
|
|
608
|
+
const derive = only?.k === 'str' ? only.derive : undefined;
|
|
609
|
+
readers.push(derive === undefined
|
|
610
|
+
? readSlot(s, d.children[f], cx)
|
|
611
|
+
: derivedReader(derive, d.children[f].children[0], d.children[derive.from].children[0].values));
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
return objectReader(cx.shapeId(v.keys), template, varying, readers);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function derivedReader(derive, d, numbers) {
|
|
619
|
+
const codes = d.children[0].values;
|
|
620
|
+
for (let row = 0; row < d.length; row += 1) {
|
|
621
|
+
const code = codes[row];
|
|
622
|
+
if (code !== HELD && !(code >= 0 && code < derive.rules.length)) {
|
|
623
|
+
throw new RangeError('a derived string names no rule');
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
const held = utf8At(d.children[1]);
|
|
627
|
+
const upper = derive.rules.map((r) => r[0] === 1);
|
|
628
|
+
const suffix = derive.rules.map((r) => r[1]);
|
|
629
|
+
return (row) => {
|
|
630
|
+
const code = codes[row];
|
|
631
|
+
if (code === HELD)
|
|
632
|
+
return held(row);
|
|
633
|
+
const s = String(numbers[row]);
|
|
634
|
+
return (upper[code] ? s.toUpperCase() : s) + suffix[code];
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
// An array literal allocates at its length, where pushing grows the array.
|
|
638
|
+
const TUPLES = [
|
|
639
|
+
() => () => [],
|
|
640
|
+
([r0]) => (row) => [r0(row)],
|
|
641
|
+
([r0, r1]) => (row) => [r0(row), r1(row)],
|
|
642
|
+
([r0, r1, r2]) => (row) => [r0(row), r1(row), r2(row)],
|
|
643
|
+
([r0, r1, r2, r3]) => (row) => [r0(row), r1(row), r2(row), r3(row)],
|
|
644
|
+
([r0, r1, r2, r3, r4]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row)],
|
|
645
|
+
([r0, r1, r2, r3, r4, r5]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row)],
|
|
646
|
+
([r0, r1, r2, r3, r4, r5, r6]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row), r6(row)],
|
|
647
|
+
([r0, r1, r2, r3, r4, r5, r6, r7]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row), r6(row), r7(row)],
|
|
648
|
+
([r0, r1, r2, r3, r4, r5, r6, r7, r8]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row), r6(row), r7(row), r8(row)],
|
|
649
|
+
([r0, r1, r2, r3, r4, r5, r6, r7, r8, r9]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row), r6(row), r7(row), r8(row), r9(row)],
|
|
650
|
+
([r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row), r6(row), r7(row), r8(row), r9(row), r10(row)],
|
|
651
|
+
([r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11]) => (row) => [r0(row), r1(row), r2(row), r3(row), r4(row), r5(row), r6(row), r7(row), r8(row), r9(row), r10(row), r11(row)],
|
|
652
|
+
];
|
|
653
|
+
function tupleReader(rs) {
|
|
654
|
+
const literal = TUPLES[rs.length];
|
|
655
|
+
if (literal !== undefined)
|
|
656
|
+
return literal(rs);
|
|
657
|
+
const n = rs.length;
|
|
658
|
+
return (row) => {
|
|
659
|
+
const out = [];
|
|
660
|
+
for (let p = 0; p < n; p += 1)
|
|
661
|
+
out.push(rs[p](row));
|
|
662
|
+
return out;
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
// One construction site per object shape. V8 keeps type feedback per source
|
|
666
|
+
// site, and a site that clones one template and stores one set of keys stays
|
|
667
|
+
// monomorphic, where one shared site sees every shape in the stream and runs
|
|
668
|
+
// at about half the speed. Each array holds distinct copies of one site, by
|
|
669
|
+
// the number of fields a row reads; shape `id` takes copy `id`, and a shape
|
|
670
|
+
// past the copies takes the shared loop.
|
|
671
|
+
const SITES0 = [
|
|
672
|
+
(t) => () => ({ ...t }),
|
|
673
|
+
(t) => () => ({ ...t }),
|
|
674
|
+
(t) => () => ({ ...t }),
|
|
675
|
+
(t) => () => ({ ...t }),
|
|
676
|
+
(t) => () => ({ ...t }),
|
|
677
|
+
(t) => () => ({ ...t }),
|
|
678
|
+
(t) => () => ({ ...t }),
|
|
679
|
+
(t) => () => ({ ...t }),
|
|
680
|
+
(t) => () => ({ ...t }),
|
|
681
|
+
(t) => () => ({ ...t }),
|
|
682
|
+
(t) => () => ({ ...t }),
|
|
683
|
+
(t) => () => ({ ...t }),
|
|
684
|
+
(t) => () => ({ ...t }),
|
|
685
|
+
(t) => () => ({ ...t }),
|
|
686
|
+
(t) => () => ({ ...t }),
|
|
687
|
+
(t) => () => ({ ...t }),
|
|
688
|
+
];
|
|
689
|
+
const SITES1 = [
|
|
690
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
691
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
692
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
693
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
694
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
695
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
696
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
697
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
698
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
699
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
700
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
701
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
702
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
703
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
704
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
705
|
+
(t, [k0], [r0]) => (row) => { const o = { ...t }; o[k0] = r0(row); return o; },
|
|
706
|
+
];
|
|
707
|
+
const SITES2 = [
|
|
708
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
709
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
710
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
711
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
712
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
713
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
714
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
715
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
716
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
717
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
718
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
719
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
720
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
721
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
722
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
723
|
+
(t, [k0, k1], [r0, r1]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); return o; },
|
|
724
|
+
];
|
|
725
|
+
const SITES3 = [
|
|
726
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
727
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
728
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
729
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
730
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
731
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
732
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
733
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
734
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
735
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
736
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
737
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
738
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
739
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
740
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
741
|
+
(t, [k0, k1, k2], [r0, r1, r2]) => (row) => { const o = { ...t }; o[k0] = r0(row); o[k1] = r1(row); o[k2] = r2(row); return o; },
|
|
742
|
+
];
|
|
743
|
+
const SITES = [SITES0, SITES1, SITES2, SITES3];
|
|
744
|
+
function objectReader(id, template, varying, rs) {
|
|
745
|
+
const site = SITES[varying.length]?.[id];
|
|
746
|
+
if (site !== undefined)
|
|
747
|
+
return site(template, varying, rs);
|
|
748
|
+
const n = varying.length;
|
|
749
|
+
return (row) => {
|
|
750
|
+
const o = { ...template };
|
|
751
|
+
for (let f = 0; f < n; f += 1)
|
|
752
|
+
o[varying[f]] = rs[f](row);
|
|
753
|
+
return o;
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
/** A stored table's rows prepared for reading. Positions are absolute, and a
|
|
757
|
+
* table's gap column runs from zero. */
|
|
758
|
+
export function readyTable(meta, data, cx) {
|
|
759
|
+
const [keyCol, gapCol, rootCol] = data.children;
|
|
760
|
+
const positions = new Int32Array(data.length);
|
|
761
|
+
const gaps = gapCol.values;
|
|
762
|
+
let at = 0;
|
|
763
|
+
for (let row = 0; row < data.length; row += 1) {
|
|
764
|
+
at += gaps[row];
|
|
765
|
+
positions[row] = at;
|
|
766
|
+
}
|
|
767
|
+
return { positions, key: keyReader(meta, keyCol, cx), value: readSlot(meta.slot, rootCol, cx) };
|
|
768
|
+
}
|
|
769
|
+
//# sourceMappingURL=sealed-arrow.js.map
|