@poe-platform/safe-fs 0.1.721 → 0.1.723
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/README.md +67 -21
- package/dist/safe-fs/bridge/filesystem.d.ts +4 -0
- package/dist/safe-fs/bridge/filesystem.js +49 -10
- package/dist/safe-fs/bridge/types.d.ts +6 -0
- package/dist/safe-fs/contracts/descriptor.d.ts +5 -0
- package/dist/safe-fs/contracts/filesystem.d.ts +15 -0
- package/dist/safe-fs/contracts/io.d.ts +3 -1
- package/dist/safe-fs/contracts/io.js +48 -16
- package/dist/safe-fs/core.d.ts +2 -1
- package/dist/safe-fs/core.js +1 -1
- package/dist/safe-fs/fs/capabilities.js +6 -3
- package/dist/safe-fs/fs/descriptor.js +9 -2
- package/dist/safe-fs/fs/devices/index.d.ts +1 -0
- package/dist/safe-fs/fs/devices/index.js +24 -6
- package/dist/safe-fs/fs/memory/index.d.ts +2 -0
- package/dist/safe-fs/fs/memory/index.js +95 -4
- package/dist/safe-fs/fs/memory/limits.js +6 -4
- package/dist/safe-fs/fs/mount/index.d.ts +4 -0
- package/dist/safe-fs/fs/mount/index.js +76 -12
- package/dist/safe-fs/fs/object-publication/index.d.ts +4 -0
- package/dist/safe-fs/fs/object-publication/index.js +9 -8
- package/dist/safe-fs/fs/overlay/index.d.ts +4 -2
- package/dist/safe-fs/fs/overlay/index.js +92 -36
- package/dist/safe-fs/fs/quota/index.js +83 -23
- package/dist/safe-fs/fs/readonly/index.js +5 -3
- package/dist/safe-fs/fs/real/index.d.ts +21 -1
- package/dist/safe-fs/fs/real/index.js +266 -22
- package/dist/safe-fs/fs/s3/filesystem.d.ts +8 -0
- package/dist/safe-fs/fs/s3/filesystem.js +59 -12
- package/dist/safe-fs/fs/s3/http/request.d.ts +1 -1
- package/dist/safe-fs/fs/s3/http/request.js +1 -1
- package/dist/safe-fs/fs/s3/http/transport.js +8 -6
- package/dist/safe-fs/fs/s3/http/xml.d.ts +4 -1
- package/dist/safe-fs/fs/s3/http/xml.js +6 -2
- package/dist/safe-fs/fs/s3/manifest-admission.d.ts +8 -0
- package/dist/safe-fs/fs/s3/manifest-admission.js +171 -0
- package/dist/safe-fs/fs/s3/namespace.js +13 -10
- package/dist/safe-fs/fs/scoped.d.ts +1 -0
- package/dist/safe-fs/fs/scoped.js +54 -35
- package/dist/safe-fs/fs/webdav/webdav.d.ts +4 -1
- package/dist/safe-fs/fs/webdav/webdav.js +101 -23
- package/dist/safe-fs/node/filesystem.d.ts +8 -1
- package/dist/safe-fs/node/filesystem.js +7 -1
- package/dist/safe-fs/node/host.d.ts +3 -0
- package/dist/safe-fs/node/host.js +78 -0
- package/dist/safe-fs/node/index.d.ts +2 -1
- package/dist/safe-fs/node/index.js +2 -1
- package/dist/safe-fs/platform/browser.d.ts +1 -0
- package/dist/safe-fs/platform/browser.js +1 -0
- package/dist/safe-fs/platform/node.d.ts +1 -0
- package/dist/safe-fs/platform/node.js +1 -0
- package/dist/safe-fs/python/emscripten.d.ts +1 -0
- package/dist/safe-fs/python/emscripten.js +5 -5
- package/dist/safe-fs/python/filesystem.js +5 -5
- package/dist/safe-fs/python/native.d.ts +1 -0
- package/dist/safe-fs/python/native.js +3 -2
- package/dist/safe-fs/xml.js +32 -21
- package/package.json +1 -1
|
@@ -3,8 +3,12 @@ import { randomBytes } from "node:crypto";
|
|
|
3
3
|
import { posix } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { FileSystemBridge } from "../bridge/filesystem.js";
|
|
6
|
+
const providers = new WeakMap();
|
|
7
|
+
export function getNodeFsBridgeProvider(bridge) {
|
|
8
|
+
return providers.get(bridge);
|
|
9
|
+
}
|
|
6
10
|
export function createNodeFsBridge(fs, options = {}) {
|
|
7
|
-
|
|
11
|
+
const bridge = new FileSystemBridge(fs, options, {
|
|
8
12
|
codec: {
|
|
9
13
|
isEncoding: Buffer.isEncoding,
|
|
10
14
|
encode(text, encoding) {
|
|
@@ -25,4 +29,6 @@ export function createNodeFsBridge(fs, options = {}) {
|
|
|
25
29
|
randomSuffix() { return randomBytes(6).toString("hex").slice(0, 6); },
|
|
26
30
|
paths: posix
|
|
27
31
|
});
|
|
32
|
+
providers.set(bridge, fs);
|
|
33
|
+
return bridge;
|
|
28
34
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
function snapshot(stat) {
|
|
3
|
+
return {
|
|
4
|
+
type: stat.isDirectory()
|
|
5
|
+
? "directory"
|
|
6
|
+
: stat.isSymbolicLink()
|
|
7
|
+
? "symlink"
|
|
8
|
+
: stat.isFile()
|
|
9
|
+
? "file"
|
|
10
|
+
: "character",
|
|
11
|
+
size: stat.size,
|
|
12
|
+
mode: stat.mode,
|
|
13
|
+
mtimeMs: stat.mtimeMs,
|
|
14
|
+
atimeMs: stat.atimeMs,
|
|
15
|
+
ctimeMs: stat.ctimeMs,
|
|
16
|
+
birthtimeMs: stat.birthtimeMs
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Trusted host access with native paths and symlink semantics; this is not a rooted sandbox. */
|
|
20
|
+
export function createHostFileSystem() {
|
|
21
|
+
async function perform(options, action) {
|
|
22
|
+
options?.signal?.throwIfAborted();
|
|
23
|
+
const result = await action();
|
|
24
|
+
options?.signal?.throwIfAborted();
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
capabilities: {
|
|
29
|
+
read: true,
|
|
30
|
+
write: true,
|
|
31
|
+
append: true,
|
|
32
|
+
stat: true,
|
|
33
|
+
readdir: true,
|
|
34
|
+
mkdir: true,
|
|
35
|
+
recursiveMkdir: true,
|
|
36
|
+
rename: true,
|
|
37
|
+
remove: true,
|
|
38
|
+
removeDirectory: true,
|
|
39
|
+
recursiveRemove: true,
|
|
40
|
+
symlinks: true,
|
|
41
|
+
hardlinks: true,
|
|
42
|
+
permissions: true,
|
|
43
|
+
timestamps: true,
|
|
44
|
+
exclusiveCreate: true
|
|
45
|
+
},
|
|
46
|
+
readFile: (path, options) => perform(options, () => fs.readFile(path, options?.signal ? { signal: options.signal } : undefined)),
|
|
47
|
+
writeFile: (path, data, options) => perform(options, () => fs.writeFile(path, data, options)),
|
|
48
|
+
appendFile: (path, data, options) => perform(options, () => fs.appendFile(path, data, options)),
|
|
49
|
+
stat: (path, options) => perform(options, async () => snapshot(await fs.stat(path))),
|
|
50
|
+
lstat: (path, options) => perform(options, async () => snapshot(await fs.lstat(path))),
|
|
51
|
+
readdir: (path, options) => perform(options, async () => (await fs.readdir(path, { withFileTypes: true })).map((entry) => ({
|
|
52
|
+
name: entry.name,
|
|
53
|
+
type: entry.isDirectory()
|
|
54
|
+
? "directory"
|
|
55
|
+
: entry.isSymbolicLink()
|
|
56
|
+
? "symlink"
|
|
57
|
+
: entry.isFile()
|
|
58
|
+
? "file"
|
|
59
|
+
: "character"
|
|
60
|
+
}))),
|
|
61
|
+
mkdir: (path, options) => perform(options, async () => {
|
|
62
|
+
await fs.mkdir(path, options);
|
|
63
|
+
}),
|
|
64
|
+
rm: (path, options) => perform(options, () => fs.rm(path, options)),
|
|
65
|
+
unlink: (path, options) => perform(options, () => fs.unlink(path)),
|
|
66
|
+
rmdir: (path, options) => perform(options, () => fs.rmdir(path)),
|
|
67
|
+
rename: (source, destination, options) => perform(options, () => fs.rename(source, destination)),
|
|
68
|
+
copyFile: (source, destination, options) => perform(options, () => fs.copyFile(source, destination, options?.exclusive ? fs.constants.COPYFILE_EXCL : 0)),
|
|
69
|
+
realpath: (path, options) => perform(options, () => fs.realpath(path)),
|
|
70
|
+
access: (path, mode, options) => perform(options, () => fs.access(path, mode)),
|
|
71
|
+
readlink: (path, options) => perform(options, () => fs.readlink(path)),
|
|
72
|
+
symlink: (target, path, options) => perform(options, () => fs.symlink(target, path)),
|
|
73
|
+
link: (source, destination, options) => perform(options, () => fs.link(source, destination)),
|
|
74
|
+
chmod: (path, mode, options) => perform(options, () => fs.chmod(path, mode)),
|
|
75
|
+
utimes: (path, atimeMs, mtimeMs, options) => perform(options, () => fs.utimes(path, atimeMs / 1000, mtimeMs / 1000)),
|
|
76
|
+
truncate: (path, length, options) => perform(options, () => fs.truncate(path, length))
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { createNodeFsBridge } from "./filesystem.js";
|
|
1
|
+
export { createNodeFsBridge, getNodeFsBridgeProvider } from "./filesystem.js";
|
|
2
2
|
export type { NodeFsBridgeOptions, NodeFsBridgeFileSystem, NodeFsImplementation } from "./filesystem.js";
|
|
3
|
+
export { createHostFileSystem } from "./host.js";
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { createNodeFsBridge } from "./filesystem.js";
|
|
1
|
+
export { createNodeFsBridge, getNodeFsBridgeProvider } from "./filesystem.js";
|
|
2
|
+
export { createHostFileSystem } from "./host.js";
|
|
@@ -2,6 +2,7 @@ import type { FsOptions } from "../contracts/filesystem.js";
|
|
|
2
2
|
export type PlatformErrno = number | undefined;
|
|
3
3
|
export type PlatformComparisonCallback<Callback> = Callback & never;
|
|
4
4
|
export declare const platform: Readonly<{
|
|
5
|
+
maxCollectionBytes: number;
|
|
5
6
|
errno(_code: string): PlatformErrno;
|
|
6
7
|
callbackAuthorities: false;
|
|
7
8
|
randomUUID(): string;
|
|
@@ -3,6 +3,7 @@ import type { FsOptions } from "../contracts/filesystem.js";
|
|
|
3
3
|
export type PlatformErrno = number;
|
|
4
4
|
export type PlatformComparisonCallback<Callback> = Callback;
|
|
5
5
|
export declare const platform: Readonly<{
|
|
6
|
+
maxCollectionBytes: number;
|
|
6
7
|
errno(code: string): PlatformErrno;
|
|
7
8
|
callbackAuthorities: true;
|
|
8
9
|
randomUUID: typeof randomUUID;
|
|
@@ -15,6 +15,7 @@ const systemErrnos = (() => {
|
|
|
15
15
|
})();
|
|
16
16
|
const negotiating = new AsyncLocalStorage();
|
|
17
17
|
export const platform = Object.freeze({
|
|
18
|
+
maxCollectionBytes: Infinity,
|
|
18
19
|
errno(code) {
|
|
19
20
|
const errno = systemErrnos.get(code === "EOPNOTSUPP" ? "ENOTSUP" : code);
|
|
20
21
|
if (errno === undefined)
|
|
@@ -6,6 +6,7 @@ export interface PythonMountOptions {
|
|
|
6
6
|
errno: Record<string, number>;
|
|
7
7
|
synchronizationFlags: number;
|
|
8
8
|
runtimeModule: any;
|
|
9
|
+
getUmask?: () => number;
|
|
9
10
|
}
|
|
10
11
|
export declare function mountPythonFileSystem(FS: any, options: PythonMountOptions): void;
|
|
11
12
|
/** Encode the pinned wasm32 musl stat layout without truncating canonical values. */
|
|
@@ -59,9 +59,9 @@ export function mountPythonFileSystem(FS, options) {
|
|
|
59
59
|
mknod(parent, name, mode) {
|
|
60
60
|
const target = `${path(parent)}/${name}`;
|
|
61
61
|
if (FS.isDir(mode))
|
|
62
|
-
rpc('mkdir', target, { mode: mode & 4095 });
|
|
62
|
+
rpc('mkdir', target, { mode: mode & 4095 & ~(options.getUmask?.() ?? 0), exactMode: true });
|
|
63
63
|
else {
|
|
64
|
-
const handle = rpc('open', target, { access: 'write', creation: 'exclusive', mode: mode & 4095 });
|
|
64
|
+
const handle = rpc('open', target, { access: 'write', creation: 'exclusive', mode: mode & 4095 & ~(options.getUmask?.() ?? 0), exactMode: true });
|
|
65
65
|
rpc('close', handle);
|
|
66
66
|
}
|
|
67
67
|
return node(parent, name, rpc('lstat', target));
|
|
@@ -145,7 +145,7 @@ export function mountPythonFileSystem(FS, options) {
|
|
|
145
145
|
throw new FS.ErrnoError(54);
|
|
146
146
|
let openOptions;
|
|
147
147
|
try {
|
|
148
|
-
openOptions = translatePythonOpenFlags(flags, mode);
|
|
148
|
+
openOptions = { ...translatePythonOpenFlags(flags, mode & ~(options.getUmask?.() ?? 0)), exactMode: true };
|
|
149
149
|
}
|
|
150
150
|
catch (error) {
|
|
151
151
|
throw new FS.ErrnoError(errno[error.code] ?? errno.EIO);
|
|
@@ -199,7 +199,7 @@ export function mountPythonFileSystem(FS, options) {
|
|
|
199
199
|
if (bootstrapPath(target))
|
|
200
200
|
return originalMkdir(target, mode);
|
|
201
201
|
const absolute = absolutePath(target);
|
|
202
|
-
rpc('mkdir', absolute, { mode });
|
|
202
|
+
rpc('mkdir', absolute, { mode: mode & ~(options.getUmask?.() ?? 0), exactMode: true });
|
|
203
203
|
return node(FS.root, absolute.slice(1), rpc('lstat', absolute));
|
|
204
204
|
};
|
|
205
205
|
const originalRename = FS.rename.bind(FS);
|
|
@@ -238,7 +238,7 @@ export function mountPythonFileSystem(FS, options) {
|
|
|
238
238
|
return originalMknod(target, mode, device);
|
|
239
239
|
if (!FS.isFile(mode))
|
|
240
240
|
throw new FS.ErrnoError(errno.ENOTSUP);
|
|
241
|
-
const handle = rpc('open', absolutePath(target), { access: 'write', creation: 'exclusive', mode: mode & 4095 });
|
|
241
|
+
const handle = rpc('open', absolutePath(target), { access: 'write', creation: 'exclusive', mode: mode & 4095 & ~(options.getUmask?.() ?? 0), exactMode: true });
|
|
242
242
|
rpc('close', handle);
|
|
243
243
|
return node(FS.root, absolutePath(target).slice(1), rpc('lstat', absolutePath(target)));
|
|
244
244
|
};
|
|
@@ -24,10 +24,10 @@ export class PythonFileSystem {
|
|
|
24
24
|
throw new FsError("EINVAL", { syscall: "python filesystem", path: options.cwd, message: "cwd must be absolute" });
|
|
25
25
|
this.#cwd = options.cwd;
|
|
26
26
|
this.#transfer = options.maxTransferBytes ?? 65536;
|
|
27
|
-
this.#limit = options.maxOpenFiles ??
|
|
28
|
-
this.#directoryLimit = options.maxDirectoryEntries ??
|
|
29
|
-
for (const limit of [this.#transfer,
|
|
30
|
-
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
27
|
+
this.#limit = options.maxOpenFiles ?? Infinity;
|
|
28
|
+
this.#directoryLimit = options.maxDirectoryEntries ?? Infinity;
|
|
29
|
+
for (const limit of [this.#transfer, options.maxOpenFiles, options.maxDirectoryEntries])
|
|
30
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 1))
|
|
31
31
|
throw new FsError("EINVAL", { syscall: "python filesystem" });
|
|
32
32
|
this.#open = options.open;
|
|
33
33
|
this.#scope = composeAbortSignals([...(options.signal === undefined ? [] : [options.signal]), this.#abort.signal]);
|
|
@@ -153,7 +153,7 @@ export class PythonFileSystem {
|
|
|
153
153
|
case "realpath": return fs.realpath(this.#path(request.args[0]), options);
|
|
154
154
|
case "readdir": {
|
|
155
155
|
const path = this.#path(request.args[0]);
|
|
156
|
-
const entries = await fs.readdir(path, { ...options, maxEntries: this.#directoryLimit });
|
|
156
|
+
const entries = await fs.readdir(path, { ...options, ...(this.#directoryLimit === Infinity ? {} : { maxEntries: this.#directoryLimit }) });
|
|
157
157
|
signal.throwIfAborted();
|
|
158
158
|
if (entries.length > this.#directoryLimit)
|
|
159
159
|
throw new FsError("EFBIG", { syscall: "readdir", path, message: "Python directory listing exceeds configured limit" });
|
|
@@ -8,6 +8,7 @@ export interface PythonNativeSyscallOptions {
|
|
|
8
8
|
readonly cwd: string;
|
|
9
9
|
readonly runtimeMount: string;
|
|
10
10
|
readonly maxTransferBytes: number;
|
|
11
|
+
readonly getUmask?: () => number;
|
|
11
12
|
readonly original?: (name: string, args: readonly (number | bigint)[]) => number;
|
|
12
13
|
}
|
|
13
14
|
export declare function createPythonNativeSyscalls(options: PythonNativeSyscallOptions): {
|
|
@@ -195,7 +195,8 @@ export function createPythonNativeSyscalls(options) {
|
|
|
195
195
|
}
|
|
196
196
|
if (fourth)
|
|
197
197
|
range(fourth, 4);
|
|
198
|
-
const
|
|
198
|
+
const mode = (fourth ? view().getUint32(fourth, true) : 0o666) & ~(options.getUmask?.() ?? 0);
|
|
199
|
+
const openOptions = { ...translatePythonOpenFlags(third, mode), exactMode: true };
|
|
199
200
|
const handle = await options.dispatch({ op: 'open', args: [path, openOptions] });
|
|
200
201
|
try {
|
|
201
202
|
signal.throwIfAborted();
|
|
@@ -338,7 +339,7 @@ export function createPythonNativeSyscalls(options) {
|
|
|
338
339
|
if (privatePath(path))
|
|
339
340
|
fail('EROFS');
|
|
340
341
|
if (name === '__syscall_mkdirat')
|
|
341
|
-
await request('mkdir', path, { mode: third & 0o7777 });
|
|
342
|
+
await request('mkdir', path, { mode: third & 0o7777 & ~(options.getUmask?.() ?? 0), exactMode: true });
|
|
342
343
|
else {
|
|
343
344
|
if (third !== 0 && third !== 512)
|
|
344
345
|
fail('EINVAL');
|
package/dist/safe-fs/xml.js
CHANGED
|
@@ -185,22 +185,22 @@ function* validDeclaration(content, expectedEncoding) {
|
|
|
185
185
|
return offset === content.length;
|
|
186
186
|
}
|
|
187
187
|
export function* parseXmlSteps(input, limits = {}) {
|
|
188
|
-
const maxDepth = limits.maxDepth ??
|
|
189
|
-
const maxNodes = limits.maxNodes ??
|
|
190
|
-
const maxAttributes = limits.maxAttributes ??
|
|
188
|
+
const maxDepth = limits.maxDepth ?? Infinity;
|
|
189
|
+
const maxNodes = limits.maxNodes ?? Infinity;
|
|
190
|
+
const maxAttributes = limits.maxAttributes ?? Infinity;
|
|
191
191
|
const retainContent = limits.retainContent !== false;
|
|
192
|
-
const maxContentNodes = limits.maxContentNodes ??
|
|
192
|
+
const maxContentNodes = limits.maxContentNodes ?? Infinity;
|
|
193
193
|
const emptyContent = Object.freeze([]);
|
|
194
194
|
const emptyAttributes = Object.freeze([]);
|
|
195
195
|
const emptyNamespaces = new Map();
|
|
196
|
-
const maxAttributesPerElement = limits.maxAttributesPerElement ??
|
|
197
|
-
const maxNamespaces = limits.maxNamespaces ??
|
|
198
|
-
for (const limit of [maxDepth, maxNodes, maxAttributes, maxContentNodes, maxAttributesPerElement, maxNamespaces]) {
|
|
199
|
-
if (!Number.isSafeInteger(limit) || limit < 1)
|
|
196
|
+
const maxAttributesPerElement = limits.maxAttributesPerElement ?? Infinity;
|
|
197
|
+
const maxNamespaces = limits.maxNamespaces ?? Infinity;
|
|
198
|
+
for (const limit of [limits.maxDepth, limits.maxNodes, limits.maxAttributes, limits.maxContentNodes, limits.maxAttributesPerElement, limits.maxNamespaces]) {
|
|
199
|
+
if (limit !== undefined && ((limit !== Infinity && !Number.isSafeInteger(limit)) || limit < 1))
|
|
200
200
|
throw new RangeError("XML limits must be positive integers");
|
|
201
201
|
}
|
|
202
|
-
const maxTextLength = limits.maxTextLength ??
|
|
203
|
-
if (!Number.isSafeInteger(maxTextLength) || maxTextLength < 1)
|
|
202
|
+
const maxTextLength = limits.maxTextLength ?? Infinity;
|
|
203
|
+
if (limits.maxTextLength !== undefined && ((maxTextLength !== Infinity && !Number.isSafeInteger(maxTextLength)) || maxTextLength < 1))
|
|
204
204
|
throw new RangeError("XML limits must be positive integers");
|
|
205
205
|
let textLength = 0;
|
|
206
206
|
const admitText = (text) => {
|
|
@@ -209,31 +209,42 @@ export function* parseXmlSteps(input, limits = {}) {
|
|
|
209
209
|
textLength += text.length;
|
|
210
210
|
};
|
|
211
211
|
const chunks = [];
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
const start = input.charCodeAt(0) === 0xfeff ? 1 : 0;
|
|
213
|
+
let chunkStart = start;
|
|
214
|
+
let chunkLength = 0;
|
|
215
|
+
let normalizedChunk = "";
|
|
216
|
+
// Keep validated spans intact; only changed line endings need new strings.
|
|
217
|
+
for (let index = start; index < input.length; index++) {
|
|
214
218
|
const point = input.codePointAt(index);
|
|
215
219
|
if (!validCharacter(point))
|
|
216
220
|
invalid("invalid character");
|
|
217
221
|
if (point === 13) {
|
|
218
|
-
|
|
222
|
+
normalizedChunk += input.slice(chunkStart, index) + "\n";
|
|
219
223
|
if (input.charCodeAt(index + 1) === 10)
|
|
220
224
|
index++;
|
|
225
|
+
chunkStart = index + 1;
|
|
226
|
+
chunkLength++;
|
|
221
227
|
}
|
|
222
228
|
else {
|
|
223
|
-
|
|
229
|
+
chunkLength += point > 0xffff ? 2 : 1;
|
|
224
230
|
if (point > 0xffff)
|
|
225
231
|
index++;
|
|
226
232
|
}
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
233
|
+
if (chunkLength >= 512) {
|
|
234
|
+
if (normalizedChunk) {
|
|
235
|
+
chunks.push(normalizedChunk + input.slice(chunkStart, index + 1));
|
|
236
|
+
normalizedChunk = "";
|
|
237
|
+
chunkStart = index + 1;
|
|
238
|
+
}
|
|
239
|
+
chunkLength = 0;
|
|
230
240
|
yield 512;
|
|
231
241
|
}
|
|
232
242
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
243
|
+
if (chunkLength)
|
|
244
|
+
yield chunkLength;
|
|
245
|
+
if (chunks.length || normalizedChunk)
|
|
246
|
+
chunks.push(normalizedChunk + input.slice(chunkStart));
|
|
247
|
+
const source = chunks.length ? chunks.join("") : input.slice(start);
|
|
237
248
|
const stack = [];
|
|
238
249
|
let root;
|
|
239
250
|
const prolog = [];
|