rusteerio 1.0.1 → 1.1.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/Cargo.lock +42 -1
- package/Cargo.toml +8 -1
- package/README.md +90 -51
- package/THIRD_PARTY_NOTICES.md +107 -2
- package/dist/{chunk-DMRD3FGC.mjs → chunk-FPRP6UIX.mjs} +2233 -751
- package/dist/chunk-FPRP6UIX.mjs.map +1 -0
- package/dist/index.cjs +2232 -750
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/slim.cjs +2226 -745
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.d.mts +2 -2
- package/dist/slim.d.ts +2 -2
- package/dist/slim.mjs +1 -1
- package/dist/{types-BP6TpxE5.d.mts → types-Cs9UP1Vv.d.mts} +3 -3
- package/dist/{types-BP6TpxE5.d.ts → types-Cs9UP1Vv.d.ts} +3 -3
- package/dist/utils.d.mts +1 -1
- package/dist/utils.d.ts +1 -1
- package/native/src/arena.rs +219 -80
- package/native/src/attributes.rs +11 -5
- package/native/src/document_order.rs +711 -0
- package/native/src/extraction.rs +15 -3
- package/native/src/gc.rs +12 -3
- package/native/src/id_hash.rs +42 -0
- package/native/src/immutable.rs +256 -0
- package/native/src/lib.rs +1039 -171
- package/native/src/runtime.rs +189 -33
- package/native/src/selector.rs +346 -44
- package/native/src/sink.rs +14 -1
- package/native/src/snapshot.rs +509 -0
- package/native/src/stream.rs +2 -2
- package/native/src/tokenizer.rs +29 -12
- package/native/vendor/html5ever-0.35.0/src/tokenizer/char_ref/mod.rs +79 -0
- package/package.json +3 -1
- package/rusteerio.darwin-arm64.node +0 -0
- package/rusteerio.darwin-x64.node +0 -0
- package/rusteerio.linux-arm64-gnu.node +0 -0
- package/rusteerio.linux-arm64-musl.node +0 -0
- package/rusteerio.linux-x64-gnu.node +0 -0
- package/rusteerio.linux-x64-musl.node +0 -0
- package/rusteerio.win32-arm64-msvc.node +0 -0
- package/rusteerio.win32-x64-msvc.node +0 -0
- package/src/attributes.ts +78 -44
- package/src/codec.ts +22 -3
- package/src/extract.ts +86 -2
- package/src/hooks.ts +148 -2
- package/src/index.ts +51 -40
- package/src/match.ts +207 -0
- package/src/native.ts +9 -1
- package/src/nodes.ts +531 -183
- package/src/router.ts +792 -229
- package/src/selection.ts +239 -125
- package/src/snapshot.ts +255 -0
- package/src/transport.ts +51 -42
- package/dist/chunk-DMRD3FGC.mjs.map +0 -1
package/src/snapshot.ts
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { decodeString } from './codec.js';
|
|
2
|
+
import type { NativeNodeRecord } from './native.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The compact record copy returned by `NativeDocument.snapshot()`: the
|
|
6
|
+
* lengths of the node, child, attribute and string lists, whether any string
|
|
7
|
+
* holds codec escapes, then the lists.
|
|
8
|
+
*/
|
|
9
|
+
export interface NativeSnapshot {
|
|
10
|
+
data: Uint32Array;
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
const HEADER = 5;
|
|
14
|
+
|
|
15
|
+
const STRIDE = 15;
|
|
16
|
+
const ABSENT = 0xffffffff;
|
|
17
|
+
const HAS_START_INDEX = 1;
|
|
18
|
+
const HAS_END_INDEX = 2;
|
|
19
|
+
const HAS_NAMESPACE = 4;
|
|
20
|
+
const EMPTY_ATTRIBUTE_MAPS = 8;
|
|
21
|
+
const OPAQUE = 16;
|
|
22
|
+
const RELEASED = 32;
|
|
23
|
+
const noChildren: number[] = Object.freeze([]) as unknown as number[];
|
|
24
|
+
const noAttributes: Record<string, string> = Object.freeze({}) as Record<string, string>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A record read from a snapshot. It stores only its position; fields decode
|
|
28
|
+
* on read, and strings and lists are kept once decoded. Every instance has
|
|
29
|
+
* the same fields, so reads stay monomorphic. Absent fields read `undefined`.
|
|
30
|
+
*/
|
|
31
|
+
class SnapshotRecord {
|
|
32
|
+
private decodedData: string | undefined;
|
|
33
|
+
private decodedAttributes: Record<string, string> | undefined;
|
|
34
|
+
private decodedChildren: number[] | undefined;
|
|
35
|
+
|
|
36
|
+
constructor(private readonly snapshot: NodeSnapshot, readonly id: number, private readonly base: number) {
|
|
37
|
+
this.decodedData = undefined;
|
|
38
|
+
this.decodedAttributes = undefined;
|
|
39
|
+
this.decodedChildren = undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get type(): string { return this.snapshot.name(this.snapshot.nodes[this.base]); }
|
|
43
|
+
get name(): string | undefined { const name = this.snapshot.nodes[this.base + 6]; return name ? this.snapshot.name(name - 1) : undefined; }
|
|
44
|
+
get data(): string | undefined {
|
|
45
|
+
if (this.decodedData !== undefined) return this.decodedData;
|
|
46
|
+
const data = this.snapshot.nodes[this.base + 7];
|
|
47
|
+
return data ? this.decodedData = this.snapshot.string(data - 1) : undefined;
|
|
48
|
+
}
|
|
49
|
+
get parent(): number | null { const parent = this.snapshot.nodes[this.base + 1]; return parent ? parent - 1 : null; }
|
|
50
|
+
get prev(): number | null { const prev = this.snapshot.nodes[this.base + 2]; return prev ? prev - 1 : null; }
|
|
51
|
+
get next(): number | null { const next = this.snapshot.nodes[this.base + 3]; return next ? next - 1 : null; }
|
|
52
|
+
get startIndex(): number | null | undefined { return this.index(HAS_START_INDEX, 11); }
|
|
53
|
+
get endIndex(): number | null | undefined { return this.index(HAS_END_INDEX, 12); }
|
|
54
|
+
get namespace(): string | undefined { const nodes = this.snapshot.nodes; return nodes[this.base + 10] & HAS_NAMESPACE ? this.snapshot.name(nodes[this.base + 13]) : undefined; }
|
|
55
|
+
get attribsNamespace(): Record<string, string> | undefined { return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : undefined; }
|
|
56
|
+
get attribsPrefix(): Record<string, string> | undefined { return this.snapshot.nodes[this.base + 10] & EMPTY_ATTRIBUTE_MAPS ? {} : undefined; }
|
|
57
|
+
get attribs(): Record<string, string> { return this.decodedAttributes ??= this.snapshot.attributesOf(this.id); }
|
|
58
|
+
get children(): number[] { return this.decodedChildren ??= this.snapshot.childrenOf(this.id); }
|
|
59
|
+
|
|
60
|
+
private index(flag: number, offset: number): number | null | undefined {
|
|
61
|
+
const nodes = this.snapshot.nodes;
|
|
62
|
+
if (!(nodes[this.base + 10] & flag)) return undefined;
|
|
63
|
+
const value = nodes[this.base + offset];
|
|
64
|
+
return value === ABSENT ? null : value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Snapshot records extended with fields stored as JSON, which are rare. */
|
|
69
|
+
const recordFields = ['type', 'name', 'data', 'parent', 'prev', 'next', 'startIndex', 'endIndex', 'namespace', 'attribsNamespace', 'attribsPrefix', 'attribs', 'children'] as const;
|
|
70
|
+
|
|
71
|
+
/** A plain copy of a record, for callers that spread or serialize it. */
|
|
72
|
+
export function plainRecord(record: NativeNodeRecord): NativeNodeRecord {
|
|
73
|
+
if (!(record instanceof SnapshotRecord)) return record;
|
|
74
|
+
const copy: Record<string, unknown> = { id: record.id };
|
|
75
|
+
for (const field of recordFields) { const value = (record as any)[field]; if (value !== undefined) copy[field] = value; }
|
|
76
|
+
for (const key of Object.keys(record)) if (!key.startsWith('decoded') && key !== 'snapshot' && key !== 'base' && key !== 'id') copy[key] = (record as any)[key];
|
|
77
|
+
return copy as NativeNodeRecord;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Decodes node records from a snapshot on demand, exactly as `node()` returns them. */
|
|
81
|
+
export class NodeSnapshot {
|
|
82
|
+
readonly size: number;
|
|
83
|
+
readonly nodes: Uint32Array;
|
|
84
|
+
private readonly children: Uint32Array;
|
|
85
|
+
private readonly attributes: Uint32Array;
|
|
86
|
+
private readonly strings: Uint32Array;
|
|
87
|
+
private readonly text: string;
|
|
88
|
+
private readonly isEncoded: boolean;
|
|
89
|
+
private readonly names: (string | undefined)[] = [];
|
|
90
|
+
/** Node object constructors by interned type, resolved once per snapshot. */
|
|
91
|
+
viewConstructors: unknown = undefined;
|
|
92
|
+
|
|
93
|
+
constructor(raw: NativeSnapshot) {
|
|
94
|
+
const { data } = raw;
|
|
95
|
+
const childrenStart = HEADER + data[0];
|
|
96
|
+
const attributesStart = childrenStart + data[1];
|
|
97
|
+
const stringsStart = attributesStart + data[2];
|
|
98
|
+
this.text = raw.text;
|
|
99
|
+
this.isEncoded = data[4] === 1;
|
|
100
|
+
this.nodes = data.subarray(HEADER, childrenStart);
|
|
101
|
+
this.children = data.subarray(childrenStart, attributesStart);
|
|
102
|
+
this.attributes = data.subarray(attributesStart, stringsStart);
|
|
103
|
+
this.strings = data.subarray(stringsStart, stringsStart + data[3]);
|
|
104
|
+
this.size = this.nodes.length / STRIDE;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The record, `null` for a released node, or `undefined` when it must be read individually. */
|
|
108
|
+
record(local: number): NativeNodeRecord | null | undefined {
|
|
109
|
+
if (local >= this.size) return undefined;
|
|
110
|
+
const base = local * STRIDE;
|
|
111
|
+
const flags = this.nodes[base + 10];
|
|
112
|
+
if (flags & RELEASED) return null;
|
|
113
|
+
if (flags & OPAQUE) return undefined;
|
|
114
|
+
const record = new SnapshotRecord(this, local, base);
|
|
115
|
+
const extras = this.nodes[base + 14];
|
|
116
|
+
if (extras) {
|
|
117
|
+
// Fields stored as JSON, such as foreign attribute namespaces, shadow the decoded getters.
|
|
118
|
+
const fields = JSON.parse(this.string(extras - 1));
|
|
119
|
+
for (const key of Object.keys(fields)) Object.defineProperty(record, key, { value: fields[key], writable: true, enumerable: true, configurable: true });
|
|
120
|
+
}
|
|
121
|
+
return record as unknown as NativeNodeRecord;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Whether a node's fields all live in the arrays, so callers may read them without a record. */
|
|
125
|
+
isPlain(local: number): boolean {
|
|
126
|
+
if (local >= this.size) return false;
|
|
127
|
+
const base = local * STRIDE;
|
|
128
|
+
return (this.nodes[base + 10] & (OPAQUE | RELEASED)) === 0 && this.nodes[base + 14] === 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
typeOf(local: number): string {
|
|
132
|
+
return this.name(this.nodes[local * STRIDE]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
nameOf(local: number): string | undefined {
|
|
136
|
+
const name = this.nodes[local * STRIDE + 6];
|
|
137
|
+
return name ? this.name(name - 1) : undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
dataOf(local: number): string | undefined {
|
|
141
|
+
const data = this.nodes[local * STRIDE + 7];
|
|
142
|
+
return data ? this.string(data - 1) : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The parent, previous or next sibling (`offset` 1, 2 or 3), or `null`. */
|
|
146
|
+
linkOf(local: number, offset: number): number | null {
|
|
147
|
+
const link = this.nodes[local * STRIDE + offset];
|
|
148
|
+
return link ? link - 1 : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
namespaceOf(local: number): string | undefined {
|
|
152
|
+
const base = local * STRIDE;
|
|
153
|
+
return this.nodes[base + 10] & HAS_NAMESPACE ? this.name(this.nodes[base + 13]) : undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** A node's attributes as parse5 attribute objects; plain nodes have no namespaced ones. */
|
|
157
|
+
attributeObjects(local: number): { name: string; value: string }[] {
|
|
158
|
+
const base = local * STRIDE;
|
|
159
|
+
const count = this.nodes[base + 9];
|
|
160
|
+
const first = this.nodes[base + 8];
|
|
161
|
+
const attributes = this.attributes;
|
|
162
|
+
const result: { name: string; value: string }[] = new Array(count);
|
|
163
|
+
for (let index = 0; index < count; index++) {
|
|
164
|
+
const pair = (first + index) * 2;
|
|
165
|
+
result[index] = { name: this.name(attributes[pair]), value: this.string(attributes[pair + 1]) };
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Copies a node's attributes onto `target`, in order. */
|
|
171
|
+
copyAttributes(local: number, target: Record<string, string>): void {
|
|
172
|
+
const base = local * STRIDE;
|
|
173
|
+
const count = this.nodes[base + 9];
|
|
174
|
+
const first = this.nodes[base + 8];
|
|
175
|
+
const attributes = this.attributes;
|
|
176
|
+
for (let index = 0; index < count; index++) {
|
|
177
|
+
const pair = (first + index) * 2;
|
|
178
|
+
const name = this.name(attributes[pair]);
|
|
179
|
+
const value = this.string(attributes[pair + 1]);
|
|
180
|
+
if (name === '__proto__') Object.defineProperty(target, name, { value, writable: true, enumerable: true, configurable: true });
|
|
181
|
+
else target[name] = value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** The flat child id list; a node's children start at `childStart(local)`. */
|
|
186
|
+
get childIds(): Uint32Array { return this.children; }
|
|
187
|
+
|
|
188
|
+
childStart(local: number): number {
|
|
189
|
+
return this.nodes[local * STRIDE + 4];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The interned reference of a node's type, comparable across nodes of this snapshot. */
|
|
193
|
+
typeReference(local: number): number {
|
|
194
|
+
return this.nodes[local * STRIDE];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Child ids without materializing the record. */
|
|
198
|
+
childCount(local: number): number {
|
|
199
|
+
return this.nodes[local * STRIDE + 5];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
childAt(local: number, index: number): number {
|
|
203
|
+
return this.children[this.nodes[local * STRIDE + 4] + index];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
childrenOf(local: number): number[] {
|
|
207
|
+
const base = local * STRIDE;
|
|
208
|
+
const count = this.nodes[base + 5];
|
|
209
|
+
if (count === 0) return noChildren;
|
|
210
|
+
const first = this.nodes[base + 4];
|
|
211
|
+
return Array.from(this.children.subarray(first, first + count));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
attributesOf(local: number): Record<string, string> {
|
|
215
|
+
const base = local * STRIDE;
|
|
216
|
+
const count = this.nodes[base + 9];
|
|
217
|
+
if (count === 0) return noAttributes;
|
|
218
|
+
const first = this.nodes[base + 8];
|
|
219
|
+
const attributes = this.attributes;
|
|
220
|
+
const result: Record<string, string> = {};
|
|
221
|
+
for (let index = 0; index < count; index++) {
|
|
222
|
+
const pair = (first + index) * 2;
|
|
223
|
+
const name = this.name(attributes[pair]);
|
|
224
|
+
const value = this.string(attributes[pair + 1]);
|
|
225
|
+
if (name === '__proto__') Object.defineProperty(result, name, { value, writable: true, enumerable: true, configurable: true });
|
|
226
|
+
else result[name] = value;
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** One attribute's value, without decoding the rest. */
|
|
232
|
+
attributeValue(local: number, name: string): string | undefined {
|
|
233
|
+
const base = local * STRIDE;
|
|
234
|
+
const count = this.nodes[base + 9];
|
|
235
|
+
const first = this.nodes[base + 8];
|
|
236
|
+
const attributes = this.attributes;
|
|
237
|
+
// A repeated name keeps its last value, as decoded attribute maps do.
|
|
238
|
+
for (let index = count - 1; index >= 0; index--) {
|
|
239
|
+
const pair = (first + index) * 2;
|
|
240
|
+
if (this.name(attributes[pair]) === name) return this.string(attributes[pair + 1]);
|
|
241
|
+
}
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
string(reference: number): string {
|
|
246
|
+
const strings = this.strings;
|
|
247
|
+
const value = this.text.slice(strings[reference * 2], strings[reference * 2 + 1]);
|
|
248
|
+
return this.isEncoded ? decodeString(value) : value;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Names repeat across nodes, so each decodes once. */
|
|
252
|
+
name(reference: number): string {
|
|
253
|
+
return this.names[reference] ??= this.string(reference);
|
|
254
|
+
}
|
|
255
|
+
}
|
package/src/transport.ts
CHANGED
|
@@ -2,18 +2,29 @@ import { decodeJson, decodeString, encodeJson, encodeOptions, encodeString } fro
|
|
|
2
2
|
import type { NativeBinding, NativeDocument } from './native.js';
|
|
3
3
|
|
|
4
4
|
interface TransportRegistry {
|
|
5
|
-
documents: WeakMap<object, NativeDocument>;
|
|
6
|
-
originals: WeakMap<object, NativeDocument>;
|
|
7
5
|
userErrors: WeakSet<object>;
|
|
8
6
|
}
|
|
9
|
-
const registryKey = Symbol.for('rusteerio.native-transport.
|
|
7
|
+
const registryKey = Symbol.for('rusteerio.native-transport.v2');
|
|
10
8
|
const globals = globalThis as typeof globalThis & { [key: symbol]: TransportRegistry | undefined };
|
|
11
|
-
const registry = globals[registryKey] ??= {
|
|
9
|
+
const registry = globals[registryKey] ??= { userErrors: new WeakSet() };
|
|
10
|
+
const transportedKey = Symbol.for('rusteerio.native-transport.prototype');
|
|
11
|
+
|
|
12
|
+
const jsonMethods = new Set(['op', 'node', 'exportTree', 'parserDiagnostics', 'adopt']);
|
|
13
|
+
/** String arguments each document method takes, by position, and how to encode them. */
|
|
14
|
+
const argumentEncoders: Record<string, (args: any[]) => void> = {
|
|
15
|
+
op: (args) => { args[1] = encodeJson(args[1]); },
|
|
16
|
+
create: (args) => { args[0] = encodeString(args[0]); if (typeof args[2] === 'string') args[2] = encodeOptions(args[2]); },
|
|
17
|
+
select: (args) => { args[0] = encodeString(args[0]); },
|
|
18
|
+
matches: (args) => { args[0] = encodeString(args[0]); },
|
|
19
|
+
attr: (args) => { args[1] = encodeString(args[1]); },
|
|
20
|
+
setAttribute: (args) => { args[1] = encodeString(args[1]); if (typeof args[2] === 'string') args[2] = encodeString(args[2]); },
|
|
21
|
+
render: (args) => { if (typeof args[1] === 'string') args[1] = encodeJson(args[1]); },
|
|
22
|
+
};
|
|
12
23
|
|
|
13
24
|
/** Keep the codec at the private addon boundary, including callback RPC. */
|
|
14
25
|
export function wrapNativeBinding(binding: NativeBinding): NativeBinding {
|
|
15
26
|
const raw = binding as NativeBinding & Record<string, any>;
|
|
16
|
-
const {
|
|
27
|
+
const { userErrors } = registry;
|
|
17
28
|
const restoreError = (error: unknown): never => {
|
|
18
29
|
if (error instanceof Error && !userErrors.has(error)) {
|
|
19
30
|
const message = decodeString(error.message);
|
|
@@ -38,39 +49,33 @@ export function wrapNativeBinding(binding: NativeBinding): NativeBinding {
|
|
|
38
49
|
throw error;
|
|
39
50
|
}
|
|
40
51
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const result = Reflect.apply(value, target, args);
|
|
60
|
-
if (property === 'op' && args[0] === 'serialize' && typeof result === 'string' && result.startsWith('{')) {
|
|
61
|
-
const error = JSON.parse(result).__nativeError;
|
|
62
|
-
if (error?.name === 'URIError') throw new URIError(error.message);
|
|
63
|
-
}
|
|
64
|
-
return typeof result === 'string' ? jsonMethods.has(String(property)) ? decodeJson(result) : decodeString(result) : result;
|
|
65
|
-
});
|
|
66
|
-
methods.set(property, method);
|
|
67
|
-
return method;
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
documents.set(document, wrapped);
|
|
71
|
-
originals.set(wrapped, document);
|
|
72
|
-
return wrapped;
|
|
52
|
+
// Wrapping the class's methods once, rather than each document in a proxy,
|
|
53
|
+
// keeps native calls free of per-document and per-call allocations.
|
|
54
|
+
const transportMethod = (name: string, method: (...args: any[]) => unknown) => {
|
|
55
|
+
const encodeArguments = argumentEncoders[name];
|
|
56
|
+
const decode = jsonMethods.has(name) ? decodeJson : decodeString;
|
|
57
|
+
const isOperation = name === 'op';
|
|
58
|
+
return function transported(this: NativeDocument, ...args: any[]): unknown {
|
|
59
|
+
try {
|
|
60
|
+
if (encodeArguments) encodeArguments(args);
|
|
61
|
+
const result = method.apply(this, args);
|
|
62
|
+
if (typeof result !== 'string') return result;
|
|
63
|
+
if (isOperation && args[0] === 'serialize' && result.startsWith('{')) {
|
|
64
|
+
const error = JSON.parse(result).__nativeError;
|
|
65
|
+
if (error?.name === 'URIError') throw new URIError(error.message);
|
|
66
|
+
}
|
|
67
|
+
return decode(result);
|
|
68
|
+
} catch (error) { return restoreError(error); }
|
|
69
|
+
};
|
|
73
70
|
};
|
|
71
|
+
const prototype = raw.NativeDocument.prototype as Record<PropertyKey, unknown>;
|
|
72
|
+
if (!Object.hasOwn(prototype, transportedKey)) {
|
|
73
|
+
for (const name of Object.getOwnPropertyNames(prototype)) {
|
|
74
|
+
const method = prototype[name];
|
|
75
|
+
if (name !== 'constructor' && typeof method === 'function') prototype[name] = transportMethod(name, method as (...args: any[]) => unknown);
|
|
76
|
+
}
|
|
77
|
+
Object.defineProperty(prototype, transportedKey, { value: true });
|
|
78
|
+
}
|
|
74
79
|
const wrapStream = (stream: any) => {
|
|
75
80
|
let carry = '';
|
|
76
81
|
return {
|
|
@@ -83,7 +88,7 @@ export function wrapNativeBinding(binding: NativeBinding): NativeBinding {
|
|
|
83
88
|
},
|
|
84
89
|
async finish() {
|
|
85
90
|
if (carry) { const final = carry; carry = ''; await invoke(() => stream.write(encodeString(final))); }
|
|
86
|
-
return
|
|
91
|
+
return invoke(() => stream.finish());
|
|
87
92
|
},
|
|
88
93
|
abort() { carry = ''; return invoke(() => stream.abort()); },
|
|
89
94
|
};
|
|
@@ -92,12 +97,16 @@ export function wrapNativeBinding(binding: NativeBinding): NativeBinding {
|
|
|
92
97
|
construct(target, args) { return wrap(invoke(() => Reflect.construct(target, argumentsFor(args)))); },
|
|
93
98
|
});
|
|
94
99
|
const wrapped: NativeBinding & Record<string, any> = { ...raw };
|
|
95
|
-
|
|
100
|
+
const Document = raw.NativeDocument;
|
|
101
|
+
wrapped.NativeDocument = function NativeDocument(html: string, isDocument?: boolean, options?: string) {
|
|
102
|
+
try { return new Document(encodeString(html), isDocument, encodeOptions(options)); }
|
|
103
|
+
catch (error) { return restoreError(error); }
|
|
104
|
+
} as unknown as NativeBinding['NativeDocument'];
|
|
96
105
|
if (raw.NativeStream) wrapped.NativeStream = wrapConstructor(raw.NativeStream, ([options, isDocument, bridge]) => [encodeOptions(options), isDocument, bridge && wrapBridge(bridge)], wrapStream);
|
|
97
106
|
if (raw.NativeHookStream) wrapped.NativeHookStream = wrapConstructor(raw.NativeHookStream, ([isDocument, options, bridge]) => [isDocument, encodeOptions(options), wrapBridge(bridge)], wrapStream);
|
|
98
|
-
if (raw.parseWithTreeAdapter) wrapped.parseWithTreeAdapter = (source: string, isDocument: boolean, options: string, bridge: (request: string) => string) =>
|
|
99
|
-
if (raw.parseAsync) wrapped.parseAsync =
|
|
100
|
-
if (raw.parseManyAsync) wrapped.parseManyAsync =
|
|
107
|
+
if (raw.parseWithTreeAdapter) wrapped.parseWithTreeAdapter = (source: string, isDocument: boolean, options: string, bridge: (request: string) => string) => invoke(() => raw.parseWithTreeAdapter(encodeString(source), isDocument, encodeOptions(options), wrapBridge(bridge)));
|
|
108
|
+
if (raw.parseAsync) wrapped.parseAsync = (source, isDocument, options) => invoke(() => raw.parseAsync!(encodeString(source), isDocument, encodeOptions(options)));
|
|
109
|
+
if (raw.parseManyAsync) wrapped.parseManyAsync = (sources, isDocument, threads, options) => invoke(() => raw.parseManyAsync!(sources.map(encodeString), isDocument, threads, encodeOptions(options)));
|
|
101
110
|
if (raw.extractPlanManyAsync) wrapped.extractPlanManyAsync = async (sources, plan, threads, options) => decodeJson(await invoke(() => raw.extractPlanManyAsync!(sources.map(encodeString), encodeJson(plan), threads, encodeOptions(options))));
|
|
102
111
|
if (raw.extractManyAsync) wrapped.extractManyAsync = async (sources, queries, threads, isDocument, options) => decodeJson(await invoke(() => raw.extractManyAsync!(sources.map(encodeString), queries.map((query) => ({ ...query, key: encodeString(query.key), selector: encodeString(query.selector), ...(query.attr === undefined ? {} : { attr: encodeString(query.attr) }) })), threads, isDocument, encodeOptions(options))));
|
|
103
112
|
return wrapped;
|