@markii/bundle 0.4.0 → 0.6.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 +18 -0
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.js +24 -1
- package/dist/storage.d.ts +12 -0
- package/dist/zip.js +10 -0
- package/package.json +1 -1
package/dist/fs.js
CHANGED
|
@@ -212,6 +212,24 @@ export function openDirBundle(rootDir) {
|
|
|
212
212
|
throw err;
|
|
213
213
|
}
|
|
214
214
|
},
|
|
215
|
+
async size(path) {
|
|
216
|
+
// Routes through the exact same `normalizeOrThrow` + `resolveInsideRoot`
|
|
217
|
+
// choke point `read`/`exists` use, so this can never be used to stat
|
|
218
|
+
// outside the bundle root or through a symlink (see `resolveInsideRoot`'s
|
|
219
|
+
// doc comment) — the whole point of C-1's fix is a size check a caller
|
|
220
|
+
// can trust as much as it trusts `read` itself.
|
|
221
|
+
const relPath = normalizeOrThrow(path);
|
|
222
|
+
const { target } = await resolveInsideRoot(rootAbs, relPath);
|
|
223
|
+
try {
|
|
224
|
+
const info = await stat(target);
|
|
225
|
+
return info.isFile() ? info.size : undefined;
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
if (isEnoent(err))
|
|
229
|
+
return undefined;
|
|
230
|
+
throw err;
|
|
231
|
+
}
|
|
232
|
+
},
|
|
215
233
|
};
|
|
216
234
|
}
|
|
217
235
|
/**
|
package/dist/manifest.d.ts
CHANGED
|
@@ -11,6 +11,17 @@ export interface BundleManifest {
|
|
|
11
11
|
mark: string;
|
|
12
12
|
permissions?: BundlePermissions;
|
|
13
13
|
uses?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Optional bundle-relative path to the document to open, overriding the
|
|
16
|
+
* conventional `note.mk.md`. `parseManifest` only checks that this is a
|
|
17
|
+
* string; it does not reject `../` or an absolute path here. Path-jailing
|
|
18
|
+
* is done once, at use time, by the consumer's `normalizeBundlePath` (see
|
|
19
|
+
* `./paths.ts`) — matching the parity of every other manifest field, none
|
|
20
|
+
* of which pre-jail path-shaped values either. Keeping the single jail
|
|
21
|
+
* point at use time avoids duplicating (and risking drift from) that
|
|
22
|
+
* logic here.
|
|
23
|
+
*/
|
|
24
|
+
document?: string;
|
|
14
25
|
[key: string]: unknown;
|
|
15
26
|
}
|
|
16
27
|
export interface BundlePermissions {
|
package/dist/manifest.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/** The current spec version this package's default manifests declare. */
|
|
2
2
|
export const CURRENT_SPEC_VERSION = '0.1.0';
|
|
3
|
-
const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
3
|
+
const KNOWN_TOP_LEVEL_KEYS = new Set([
|
|
4
|
+
'mark',
|
|
5
|
+
'permissions',
|
|
6
|
+
'uses',
|
|
7
|
+
'document',
|
|
8
|
+
]);
|
|
4
9
|
const KNOWN_FS_GRANTS = new Set(['read', 'write:cache/']);
|
|
5
10
|
// Simplified but structurally correct semver: MAJOR.MINOR.PATCH with
|
|
6
11
|
// optional prerelease/build metadata. Good enough for a "shape" check —
|
|
@@ -124,6 +129,22 @@ export function parseManifest(json) {
|
|
|
124
129
|
uses = obj.uses;
|
|
125
130
|
}
|
|
126
131
|
}
|
|
132
|
+
// --- document (optional) ---
|
|
133
|
+
// Only the type is checked here (must be a string). Whether it's a usable
|
|
134
|
+
// relative path (no `../` escape, not absolute) is left to the consumer's
|
|
135
|
+
// `normalizeBundlePath` at use time — see the type-level doc comment above
|
|
136
|
+
// for why: no other manifest field pre-jails a path-shaped value either,
|
|
137
|
+
// so enforcing it here would be inconsistent and would duplicate the one
|
|
138
|
+
// real jail point.
|
|
139
|
+
let document;
|
|
140
|
+
if (obj.document !== undefined) {
|
|
141
|
+
if (typeof obj.document !== 'string') {
|
|
142
|
+
errors.push('"document" must be a string');
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
document = obj.document;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
127
148
|
// --- unknown top-level keys: forward-compat warning, not an error ---
|
|
128
149
|
for (const key of Object.keys(obj)) {
|
|
129
150
|
if (!KNOWN_TOP_LEVEL_KEYS.has(key)) {
|
|
@@ -138,6 +159,8 @@ export function parseManifest(json) {
|
|
|
138
159
|
manifest.permissions = permissions;
|
|
139
160
|
if (uses !== undefined)
|
|
140
161
|
manifest.uses = uses;
|
|
162
|
+
if (document !== undefined)
|
|
163
|
+
manifest.document = document;
|
|
141
164
|
return { ok: true, manifest, warnings };
|
|
142
165
|
}
|
|
143
166
|
/** A minimal, valid manifest for a freshly promoted bundle: no permissions granted, no packs declared. */
|
package/dist/storage.d.ts
CHANGED
|
@@ -22,6 +22,18 @@ export interface BundleStorage {
|
|
|
22
22
|
/** All file paths currently in the bundle, bundle-relative, sorted. */
|
|
23
23
|
list(): Promise<string[]>;
|
|
24
24
|
exists(path: string): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the file's byte length, or `undefined` if no such path exists —
|
|
27
|
+
* WITHOUT reading (let alone inflating) its contents. This is what lets a
|
|
28
|
+
* caller (e.g. `buildBundleSnapshot` in `apps/vscode`) enforce a size
|
|
29
|
+
* budget by skipping an over-budget file before it is ever materialized in
|
|
30
|
+
* memory, rather than reading it whole and only then discovering it was too
|
|
31
|
+
* big. Must route through the exact same path-jail/symlink-refusal a
|
|
32
|
+
* `read` of the same path would (see the class doc comment above) — a
|
|
33
|
+
* caller must never be able to learn the size of, or prove the existence
|
|
34
|
+
* of, a path `read`/`write` would refuse.
|
|
35
|
+
*/
|
|
36
|
+
size(path: string): Promise<number | undefined>;
|
|
25
37
|
}
|
|
26
38
|
/**
|
|
27
39
|
* The shared choke point every `BundleStorage` implementation calls before
|
package/dist/zip.js
CHANGED
|
@@ -31,6 +31,16 @@ function createMapStorage(map) {
|
|
|
31
31
|
const normalized = normalizeOrThrow(path);
|
|
32
32
|
return Promise.resolve(map.has(normalized));
|
|
33
33
|
},
|
|
34
|
+
size(path) {
|
|
35
|
+
// The map already holds each entry's fully-decompressed bytes (this
|
|
36
|
+
// archive form eagerly inflates every entry up front, under
|
|
37
|
+
// `openZipBundle`'s own per-entry/total decompression-bomb caps — see
|
|
38
|
+
// that function's doc comment), so this is just the stored buffer's
|
|
39
|
+
// length: no re-inflation, no touching the archive bytes again.
|
|
40
|
+
const normalized = normalizeOrThrow(path);
|
|
41
|
+
const data = map.get(normalized);
|
|
42
|
+
return Promise.resolve(data === undefined ? undefined : data.length);
|
|
43
|
+
},
|
|
34
44
|
};
|
|
35
45
|
}
|
|
36
46
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markii/bundle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|