@markii/bundle 0.12.1 → 0.14.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/dist/fs.js +7 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/manifest.js +17 -3
- package/dist/paths.d.ts +7 -3
- package/dist/paths.js +8 -4
- package/dist/script-view.d.ts +3 -3
- package/dist/script-view.js +3 -3
- package/dist/storage.d.ts +28 -0
- package/dist/storage.js +57 -0
- package/dist/zip.d.ts +2 -2
- package/dist/zip.js +3 -3
- package/package.json +1 -1
package/dist/fs.js
CHANGED
|
@@ -15,8 +15,8 @@ function isEnoent(err) {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* `ENOTDIR` shows up when an *earlier* path component turns out to be a
|
|
18
|
-
* plain file instead of a directory (e.g. writing
|
|
19
|
-
* when
|
|
18
|
+
* plain file instead of a directory (e.g. writing `.cache/data.json/sub`
|
|
19
|
+
* when `.cache/data.json` already exists as a file) — treated the same as
|
|
20
20
|
* "doesn't exist yet" by the lstat walk below, deferring the actual clear
|
|
21
21
|
* error to the subsequent `mkdir`/`writeFile`/`open` call.
|
|
22
22
|
*/
|
|
@@ -31,7 +31,7 @@ function isEnoentLike(err) {
|
|
|
31
31
|
* in `./paths` only reasons about the *logical* path string, but the actual
|
|
32
32
|
* filesystem write/read *follows symlinks*, so a symlink planted anywhere
|
|
33
33
|
* inside the bundle (even one that resolves to somewhere still nominally
|
|
34
|
-
* "inside the bundle root", like
|
|
34
|
+
* "inside the bundle root", like `.cache/pwn -> ../manifest.json`) can
|
|
35
35
|
* silently retarget an otherwise-innocuous-looking write.
|
|
36
36
|
*
|
|
37
37
|
* Two layers, in order:
|
|
@@ -41,10 +41,10 @@ function isEnoentLike(err) {
|
|
|
41
41
|
* `stat`, which would itself follow a symlink). The moment any
|
|
42
42
|
* *existing* component is a symlink — file or directory, leaf or
|
|
43
43
|
* ancestor, whether or not it resolves inside or outside the root — the
|
|
44
|
-
* whole operation is rejected. This alone defeats ESCAPE 1 (
|
|
44
|
+
* whole operation is rejected. This alone defeats ESCAPE 1 (`.cache/pwn`
|
|
45
45
|
* symlinked to `../manifest.json`) and its directory variant
|
|
46
|
-
* (
|
|
47
|
-
*
|
|
46
|
+
* (`.cache/up` symlinked to `..`, then `.cache/up/manifest.json` or even
|
|
47
|
+
* `.cache/up/not-yet-created.txt`): the symlinked component is caught
|
|
48
48
|
* before we ever ask whether its target exists.
|
|
49
49
|
* 2. **Defense in depth — resolved-path re-check.** Once the walk finds
|
|
50
50
|
* the nearest existing ancestor (the leaf itself may not exist yet,
|
|
@@ -116,7 +116,7 @@ async function resolveInsideRoot(rootAbs, relPath) {
|
|
|
116
116
|
* Writes `data` to an already symlink-checked `target` that is known to
|
|
117
117
|
* exist, refusing to write through a hard link (ESCAPE 3: `st_nlink > 1`
|
|
118
118
|
* means some other path — possibly outside the bundle entirely, e.g.
|
|
119
|
-
* `ln b.mkz/../victim.txt b.mkz
|
|
119
|
+
* `ln b.mkz/../victim.txt b.mkz/.cache/hard` — refers to the exact same
|
|
120
120
|
* inode, so writing here would also silently modify that other path).
|
|
121
121
|
*
|
|
122
122
|
* Opens the file first *without* truncating (`'r+'`), `fstat`s the open
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { CURRENT_SPEC_VERSION, createDefaultManifest, parseManifest, } from './m
|
|
|
3
3
|
export type { BundleFsGrant, BundleWritePolicy, NormalizePathResult, } from './paths.js';
|
|
4
4
|
export { isWriteAllowed, normalizeBundlePath } from './paths.js';
|
|
5
5
|
export type { BundleStorage } from './storage.js';
|
|
6
|
-
export { normalizeOrThrow } from './storage.js';
|
|
6
|
+
export { createMemoryBundleStorage, normalizeOrThrow } from './storage.js';
|
|
7
7
|
export { BundlePathError, BundleZipError, ScriptCapabilityError, } from './errors.js';
|
|
8
8
|
export type { OpenZipBundleOptions } from './zip.js';
|
|
9
9
|
export { DEFAULT_MAX_ZIP_ENTRY_BYTES, DEFAULT_MAX_ZIP_TOTAL_BYTES, exportZipBundle, openZipBundle, } from './zip.js';
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// entry never has to reason about Node builtins reachable from it.
|
|
7
7
|
export { CURRENT_SPEC_VERSION, createDefaultManifest, parseManifest, } from './manifest.js';
|
|
8
8
|
export { isWriteAllowed, normalizeBundlePath } from './paths.js';
|
|
9
|
-
export { normalizeOrThrow } from './storage.js';
|
|
9
|
+
export { createMemoryBundleStorage, normalizeOrThrow } from './storage.js';
|
|
10
10
|
export { BundlePathError, BundleZipError, ScriptCapabilityError, } from './errors.js';
|
|
11
11
|
export { DEFAULT_MAX_ZIP_ENTRY_BYTES, DEFAULT_MAX_ZIP_TOTAL_BYTES, exportZipBundle, openZipBundle, } from './zip.js';
|
|
12
12
|
export { createScriptView, grantAllDeclaredPermissions, } from './script-view.js';
|
package/dist/manifest.js
CHANGED
|
@@ -7,7 +7,13 @@ const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
|
7
7
|
'uses',
|
|
8
8
|
'document',
|
|
9
9
|
]);
|
|
10
|
-
const KNOWN_FS_GRANTS = new Set(['read', 'write
|
|
10
|
+
const KNOWN_FS_GRANTS = new Set(['read', 'write:.cache/']);
|
|
11
|
+
/**
|
|
12
|
+
* The retired, undotted spelling of the write grant. Never accepted as an
|
|
13
|
+
* alias: no released bundle declares it, so a manifest naming it gets a
|
|
14
|
+
* diagnostic pointing at the correct token instead of a silent rewrite.
|
|
15
|
+
*/
|
|
16
|
+
const RETIRED_WRITE_GRANT = 'write:cache/';
|
|
11
17
|
// Simplified but structurally correct semver: MAJOR.MINOR.PATCH with
|
|
12
18
|
// optional prerelease/build metadata. Good enough for a "shape" check —
|
|
13
19
|
// this package does not need to compare or range-match versions.
|
|
@@ -145,8 +151,16 @@ export function parseManifest(json) {
|
|
|
145
151
|
else {
|
|
146
152
|
const badGrants = permsObj.bundle.filter((grant) => !KNOWN_FS_GRANTS.has(grant));
|
|
147
153
|
if (badGrants.length > 0) {
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
const retiredGrants = badGrants.filter((grant) => grant === RETIRED_WRITE_GRANT);
|
|
155
|
+
const otherGrants = badGrants.filter((grant) => grant !== RETIRED_WRITE_GRANT);
|
|
156
|
+
if (retiredGrants.length > 0) {
|
|
157
|
+
errors.push(`"permissions.bundle" uses the retired grant "write:cache/"; ` +
|
|
158
|
+
`the writable directory is "write:.cache/"`);
|
|
159
|
+
}
|
|
160
|
+
if (otherGrants.length > 0) {
|
|
161
|
+
errors.push(`"permissions.bundle" contains invalid grant(s): ${otherGrants.join(', ')} ` +
|
|
162
|
+
`(expected "read" or "write:.cache/")`);
|
|
163
|
+
}
|
|
150
164
|
}
|
|
151
165
|
else {
|
|
152
166
|
perms.bundle = permsObj.bundle;
|
package/dist/paths.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* module exists to prevent.
|
|
14
14
|
*/
|
|
15
15
|
/** The two write grants a manifest may declare under `permissions.bundle`. */
|
|
16
|
-
export type BundleFsGrant = 'read' | 'write
|
|
16
|
+
export type BundleFsGrant = 'read' | 'write:.cache/';
|
|
17
17
|
export type NormalizePathResult = {
|
|
18
18
|
ok: true;
|
|
19
19
|
path: string;
|
|
@@ -48,7 +48,11 @@ export interface BundleWritePolicy {
|
|
|
48
48
|
* `policy` — this is load-bearing, not a default: a script that could edit
|
|
49
49
|
* the manifest could grant itself further permissions, and a script that
|
|
50
50
|
* could edit `note.mk.md` would make the document self-modifying, which §8
|
|
51
|
-
* explicitly rules out. Every other path requires the `write
|
|
52
|
-
* and must normalize to a path under
|
|
51
|
+
* explicitly rules out. Every other path requires the `write:.cache/` grant
|
|
52
|
+
* and must normalize to a path under `.cache/`. The dot prefix is the one
|
|
53
|
+
* spelling: `normalizeBundlePath` collapses a bare `.` segment away, so
|
|
54
|
+
* `.cache/x` normalizes to a first segment of `.cache`, an ordinary
|
|
55
|
+
* dot-prefixed directory name rather than a traversal segment (only a
|
|
56
|
+
* literal `..` segment is rejected).
|
|
53
57
|
*/
|
|
54
58
|
export declare function isWriteAllowed(path: string, policy: BundleWritePolicy): boolean;
|
package/dist/paths.js
CHANGED
|
@@ -67,8 +67,12 @@ export function normalizeBundlePath(path) {
|
|
|
67
67
|
* `policy` — this is load-bearing, not a default: a script that could edit
|
|
68
68
|
* the manifest could grant itself further permissions, and a script that
|
|
69
69
|
* could edit `note.mk.md` would make the document self-modifying, which §8
|
|
70
|
-
* explicitly rules out. Every other path requires the `write
|
|
71
|
-
* and must normalize to a path under
|
|
70
|
+
* explicitly rules out. Every other path requires the `write:.cache/` grant
|
|
71
|
+
* and must normalize to a path under `.cache/`. The dot prefix is the one
|
|
72
|
+
* spelling: `normalizeBundlePath` collapses a bare `.` segment away, so
|
|
73
|
+
* `.cache/x` normalizes to a first segment of `.cache`, an ordinary
|
|
74
|
+
* dot-prefixed directory name rather than a traversal segment (only a
|
|
75
|
+
* literal `..` segment is rejected).
|
|
72
76
|
*/
|
|
73
77
|
export function isWriteAllowed(path, policy) {
|
|
74
78
|
const normalized = normalizeBundlePath(path);
|
|
@@ -78,7 +82,7 @@ export function isWriteAllowed(path, policy) {
|
|
|
78
82
|
// Unconditional denial: no policy input can override this.
|
|
79
83
|
if (p === 'note.mk.md' || p === 'manifest.json')
|
|
80
84
|
return false;
|
|
81
|
-
if (!policy.grants.includes('write
|
|
85
|
+
if (!policy.grants.includes('write:.cache/'))
|
|
82
86
|
return false;
|
|
83
|
-
return p.startsWith('cache/') && p.length > 'cache/'.length;
|
|
87
|
+
return p.startsWith('.cache/') && p.length > '.cache/'.length;
|
|
84
88
|
}
|
package/dist/script-view.d.ts
CHANGED
|
@@ -48,9 +48,9 @@ export declare function grantAllDeclaredPermissions(manifest: BundleManifest): B
|
|
|
48
48
|
*
|
|
49
49
|
* - No effective bundle grants at all: every call throws `ScriptCapabilityError`.
|
|
50
50
|
* - `'read'` in the intersection: `read`/`exists` work bundle-wide; `write` still fails.
|
|
51
|
-
* - `'write
|
|
52
|
-
* `isWriteAllowed` accepts —
|
|
53
|
-
* if both the manifest and the granted set include `'write
|
|
51
|
+
* - `'write:.cache/'` in the intersection: `write` works, but only for paths
|
|
52
|
+
* `isWriteAllowed` accepts — `.cache/` only. Critically, this holds even
|
|
53
|
+
* if both the manifest and the granted set include `'write:.cache/'` and
|
|
54
54
|
* the script asks for `manifest.json` or `note.mk.md`: `isWriteAllowed`
|
|
55
55
|
* denies those two paths unconditionally, regardless of what's granted
|
|
56
56
|
* or declared (see `./paths`).
|
package/dist/script-view.js
CHANGED
|
@@ -35,9 +35,9 @@ export function grantAllDeclaredPermissions(manifest) {
|
|
|
35
35
|
*
|
|
36
36
|
* - No effective bundle grants at all: every call throws `ScriptCapabilityError`.
|
|
37
37
|
* - `'read'` in the intersection: `read`/`exists` work bundle-wide; `write` still fails.
|
|
38
|
-
* - `'write
|
|
39
|
-
* `isWriteAllowed` accepts —
|
|
40
|
-
* if both the manifest and the granted set include `'write
|
|
38
|
+
* - `'write:.cache/'` in the intersection: `write` works, but only for paths
|
|
39
|
+
* `isWriteAllowed` accepts — `.cache/` only. Critically, this holds even
|
|
40
|
+
* if both the manifest and the granted set include `'write:.cache/'` and
|
|
41
41
|
* the script asks for `manifest.json` or `note.mk.md`: `isWriteAllowed`
|
|
42
42
|
* denies those two paths unconditionally, regardless of what's granted
|
|
43
43
|
* or declared (see `./paths`).
|
package/dist/storage.d.ts
CHANGED
|
@@ -41,3 +41,31 @@ export interface BundleStorage {
|
|
|
41
41
|
* `BundlePathError` describing why it was rejected.
|
|
42
42
|
*/
|
|
43
43
|
export declare function normalizeOrThrow(path: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* A third `BundleStorage` form, alongside the zip form (`./zip`) and the
|
|
46
|
+
* Node-only directory form (`./fs`): a plain in-memory bundle, backed by
|
|
47
|
+
* nothing more than a `Map`. No zip bytes to parse, no real filesystem to
|
|
48
|
+
* guard against symlinks or hard links — this form exists for a host that
|
|
49
|
+
* already has a bundle's files as plain values (a browser tab building a
|
|
50
|
+
* bundle from scratch, a test harness) and wants the same `BundleStorage`
|
|
51
|
+
* contract without a Node dependency, so it lives on the browser-safe main
|
|
52
|
+
* entry (`./index`) rather than the `./fs` subpath.
|
|
53
|
+
*
|
|
54
|
+
* `files` maps a bundle-relative path to its content, either already as
|
|
55
|
+
* bytes or as a string encoded here as UTF-8 — the same encoding every
|
|
56
|
+
* other `BundleStorage` form uses for text. Every key is routed through the
|
|
57
|
+
* exact same `normalizeOrThrow` choke point every other form uses (never a
|
|
58
|
+
* reimplemented jail): an individually invalid path throws `BundlePathError`
|
|
59
|
+
* immediately, and two distinct keys that normalize to the same path (e.g.
|
|
60
|
+
* `"note.mk.md"` and `"./note.mk.md"`) are rejected the same way `openZipBundle`
|
|
61
|
+
* rejects colliding zip entries, rather than silently letting one shadow the
|
|
62
|
+
* other.
|
|
63
|
+
*
|
|
64
|
+
* Like the zip and directory forms, this storage enforces only the
|
|
65
|
+
* structural path-jail — never `isWriteAllowed`. Confining writes to
|
|
66
|
+
* `.cache/` is `ScriptView`'s job (`./script-view`), which wraps whatever
|
|
67
|
+
* `BundleStorage` it is given; a caller wanting that policy applied wraps
|
|
68
|
+
* this storage in `createScriptView` exactly as it would the zip or
|
|
69
|
+
* directory form.
|
|
70
|
+
*/
|
|
71
|
+
export declare function createMemoryBundleStorage(files?: Readonly<Record<string, string | Uint8Array>>): BundleStorage;
|
package/dist/storage.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BundlePathError } from './errors.js';
|
|
2
2
|
import { normalizeBundlePath } from './paths.js';
|
|
3
|
+
const utf8Encoder = new TextEncoder();
|
|
3
4
|
/**
|
|
4
5
|
* The shared choke point every `BundleStorage` implementation calls before
|
|
5
6
|
* touching an archive or the filesystem: normalizes `path` or throws a
|
|
@@ -12,3 +13,59 @@ export function normalizeOrThrow(path) {
|
|
|
12
13
|
}
|
|
13
14
|
return result.path;
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* A third `BundleStorage` form, alongside the zip form (`./zip`) and the
|
|
18
|
+
* Node-only directory form (`./fs`): a plain in-memory bundle, backed by
|
|
19
|
+
* nothing more than a `Map`. No zip bytes to parse, no real filesystem to
|
|
20
|
+
* guard against symlinks or hard links — this form exists for a host that
|
|
21
|
+
* already has a bundle's files as plain values (a browser tab building a
|
|
22
|
+
* bundle from scratch, a test harness) and wants the same `BundleStorage`
|
|
23
|
+
* contract without a Node dependency, so it lives on the browser-safe main
|
|
24
|
+
* entry (`./index`) rather than the `./fs` subpath.
|
|
25
|
+
*
|
|
26
|
+
* `files` maps a bundle-relative path to its content, either already as
|
|
27
|
+
* bytes or as a string encoded here as UTF-8 — the same encoding every
|
|
28
|
+
* other `BundleStorage` form uses for text. Every key is routed through the
|
|
29
|
+
* exact same `normalizeOrThrow` choke point every other form uses (never a
|
|
30
|
+
* reimplemented jail): an individually invalid path throws `BundlePathError`
|
|
31
|
+
* immediately, and two distinct keys that normalize to the same path (e.g.
|
|
32
|
+
* `"note.mk.md"` and `"./note.mk.md"`) are rejected the same way `openZipBundle`
|
|
33
|
+
* rejects colliding zip entries, rather than silently letting one shadow the
|
|
34
|
+
* other.
|
|
35
|
+
*
|
|
36
|
+
* Like the zip and directory forms, this storage enforces only the
|
|
37
|
+
* structural path-jail — never `isWriteAllowed`. Confining writes to
|
|
38
|
+
* `.cache/` is `ScriptView`'s job (`./script-view`), which wraps whatever
|
|
39
|
+
* `BundleStorage` it is given; a caller wanting that policy applied wraps
|
|
40
|
+
* this storage in `createScriptView` exactly as it would the zip or
|
|
41
|
+
* directory form.
|
|
42
|
+
*/
|
|
43
|
+
export function createMemoryBundleStorage(files = {}) {
|
|
44
|
+
const map = new Map();
|
|
45
|
+
for (const [rawPath, content] of Object.entries(files)) {
|
|
46
|
+
const normalized = normalizeOrThrow(rawPath);
|
|
47
|
+
if (map.has(normalized)) {
|
|
48
|
+
throw new BundlePathError(rawPath, `collides with another entry after normalization (both resolve to ${JSON.stringify(normalized)})`);
|
|
49
|
+
}
|
|
50
|
+
map.set(normalized, typeof content === 'string' ? utf8Encoder.encode(content) : content);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
read(path) {
|
|
54
|
+
return Promise.resolve(map.get(normalizeOrThrow(path)));
|
|
55
|
+
},
|
|
56
|
+
write(path, data) {
|
|
57
|
+
map.set(normalizeOrThrow(path), data);
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
},
|
|
60
|
+
list() {
|
|
61
|
+
return Promise.resolve(Array.from(map.keys()).sort());
|
|
62
|
+
},
|
|
63
|
+
exists(path) {
|
|
64
|
+
return Promise.resolve(map.has(normalizeOrThrow(path)));
|
|
65
|
+
},
|
|
66
|
+
size(path) {
|
|
67
|
+
const data = map.get(normalizeOrThrow(path));
|
|
68
|
+
return Promise.resolve(data === undefined ? undefined : data.length);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
package/dist/zip.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare const DEFAULT_MAX_ZIP_TOTAL_BYTES: number;
|
|
|
37
37
|
*
|
|
38
38
|
* Collision protection (DEFECT 5): two distinct raw entry names that
|
|
39
39
|
* *normalize* to the same bundle path (e.g. `manifest.json` and
|
|
40
|
-
* `./manifest.json`, or
|
|
40
|
+
* `./manifest.json`, or `.cache/x` and `.cache//x`) are rejected outright
|
|
41
41
|
* rather than silently last-wins — a bundle could otherwise show a benign
|
|
42
42
|
* file to one reader and a hostile one to another depending on which
|
|
43
43
|
* implementation's normalization/iteration order "wins".
|
|
@@ -64,7 +64,7 @@ export declare function openZipBundle(bytes: Uint8Array, options?: OpenZipBundle
|
|
|
64
64
|
* `{}` — `fflate`'s `zipSync` flattens its input with a bracket-assignment
|
|
65
65
|
* loop (`t[key] = ...`) that inherits the same `__proto__` special-case
|
|
66
66
|
* problem when `key` is nested under a directory prefix; giving it a
|
|
67
|
-
* null-prototype object here means a bundle path like
|
|
67
|
+
* null-prototype object here means a bundle path like `.cache/__proto__`
|
|
68
68
|
* (a *nested* `__proto__`, i.e. everything except a bare top-level path)
|
|
69
69
|
* round-trips correctly instead of silently corrupting our own dict.
|
|
70
70
|
*
|
package/dist/zip.js
CHANGED
|
@@ -213,7 +213,7 @@ export const DEFAULT_MAX_ZIP_TOTAL_BYTES = 256 * 1024 * 1024;
|
|
|
213
213
|
*
|
|
214
214
|
* Collision protection (DEFECT 5): two distinct raw entry names that
|
|
215
215
|
* *normalize* to the same bundle path (e.g. `manifest.json` and
|
|
216
|
-
* `./manifest.json`, or
|
|
216
|
+
* `./manifest.json`, or `.cache/x` and `.cache//x`) are rejected outright
|
|
217
217
|
* rather than silently last-wins — a bundle could otherwise show a benign
|
|
218
218
|
* file to one reader and a hostile one to another depending on which
|
|
219
219
|
* implementation's normalization/iteration order "wins".
|
|
@@ -308,7 +308,7 @@ export function openZipBundle(bytes, options = {}) {
|
|
|
308
308
|
* `{}` — `fflate`'s `zipSync` flattens its input with a bracket-assignment
|
|
309
309
|
* loop (`t[key] = ...`) that inherits the same `__proto__` special-case
|
|
310
310
|
* problem when `key` is nested under a directory prefix; giving it a
|
|
311
|
-
* null-prototype object here means a bundle path like
|
|
311
|
+
* null-prototype object here means a bundle path like `.cache/__proto__`
|
|
312
312
|
* (a *nested* `__proto__`, i.e. everything except a bare top-level path)
|
|
313
313
|
* round-trips correctly instead of silently corrupting our own dict.
|
|
314
314
|
*
|
|
@@ -327,7 +327,7 @@ export async function exportZipBundle(storage) {
|
|
|
327
327
|
if (topLevelProtoLike.length > 0) {
|
|
328
328
|
throw new BundleZipError(`zip export rejected: bundle path(s) ${topLevelProtoLike.map((p) => JSON.stringify(p)).join(', ')} cannot be represented as a top-level zip entry ` +
|
|
329
329
|
`(fflate's zip writer cannot serialize a top-level entry literally named "__proto__", "constructor", or "prototype"); ` +
|
|
330
|
-
`nest the file under a directory (e.g. "cache/__proto__") to work around this`, topLevelProtoLike);
|
|
330
|
+
`nest the file under a directory (e.g. ".cache/__proto__") to work around this`, topLevelProtoLike);
|
|
331
331
|
}
|
|
332
332
|
const files = Object.create(null);
|
|
333
333
|
for (const path of paths) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/bundle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Bundle (.mkz, formerly .mkbundle) storage and policy layer for Markii: manifest handling, the bundle-relative path-jail, zip (fflate) and Node directory storage forms, and a capability-restricted script view. No React, no parsing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|