@markii/bundle 0.4.0 → 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/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
  /**
@@ -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(['mark', 'permissions', 'uses']);
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.4.0",
3
+ "version": "0.5.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",