@markii/host 0.0.1 → 0.17.0-next.3
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 +20 -2
- package/dist/browser-worker.js +639 -0
- package/dist/browser.d.ts +61 -0
- package/dist/browser.js +172 -0
- package/dist/conformance/host/01-plain-open/expected.json +5 -0
- package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
- package/dist/conformance/host/01-plain-open/scenario.json +6 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
- package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
- package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
- package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
- package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
- package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
- package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
- package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
- package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
- package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
- package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
- package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
- package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
- package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
- package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
- package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
- package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
- package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
- package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
- package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
- package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
- package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
- package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
- package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
- package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
- package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
- package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
- package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
- package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
- package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
- package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
- package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
- package/dist/conformance/host/14-remove-pack/expected.json +14 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
- package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
- package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
- package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
- package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
- package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
- package/dist/conformance/host/16-pack-trust/expected.json +37 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
- package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
- package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
- package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
- package/dist/conformance/host/README.md +221 -0
- package/dist/conformance.d.ts +2 -0
- package/dist/conformance.js +5 -0
- package/dist/diagnostics/render-diagnostics.d.ts +41 -0
- package/dist/diagnostics/render-diagnostics.js +46 -0
- package/dist/export/cascade.d.ts +98 -0
- package/dist/export/cascade.js +159 -0
- package/dist/export/export-zip.d.ts +19 -0
- package/dist/export/export-zip.js +38 -0
- package/dist/export/image-embed.d.ts +126 -0
- package/dist/export/image-embed.js +274 -0
- package/dist/export/note-export.d.ts +332 -0
- package/dist/export/note-export.js +366 -0
- package/dist/export/note-links.d.ts +83 -0
- package/dist/export/note-links.js +241 -0
- package/dist/glue.wasm +0 -0
- package/dist/host/adapter.d.ts +218 -0
- package/dist/host/adapter.js +9 -0
- package/dist/host/cascade-export-behavior.d.ts +120 -0
- package/dist/host/cascade-export-behavior.js +151 -0
- package/dist/host/conformance-runner.d.ts +159 -0
- package/dist/host/conformance-runner.js +632 -0
- package/dist/host/create-host.d.ts +251 -0
- package/dist/host/create-host.js +329 -0
- package/dist/host/editor-behavior.d.ts +153 -0
- package/dist/host/editor-behavior.js +223 -0
- package/dist/host/export/md-plain.d.ts +15 -0
- package/dist/host/export/md-plain.js +125 -0
- package/dist/host/export-behavior.d.ts +111 -0
- package/dist/host/export-behavior.js +257 -0
- package/dist/host/labels.d.ts +23 -0
- package/dist/host/labels.js +32 -0
- package/dist/host/pack-archive.d.ts +30 -0
- package/dist/host/pack-archive.js +62 -0
- package/dist/host/pack-install.d.ts +87 -0
- package/dist/host/pack-install.js +186 -0
- package/dist/host/pack-load.d.ts +71 -0
- package/dist/host/pack-load.js +57 -0
- package/dist/host/pack-remove.d.ts +21 -0
- package/dist/host/pack-remove.js +65 -0
- package/dist/host/pack-trust-behavior.d.ts +79 -0
- package/dist/host/pack-trust-behavior.js +262 -0
- package/dist/host/refresh-interval.d.ts +37 -0
- package/dist/host/refresh-interval.js +59 -0
- package/dist/host/run-behavior.d.ts +88 -0
- package/dist/host/run-behavior.js +189 -0
- package/dist/host/run-diagnostics.d.ts +26 -0
- package/dist/host/run-diagnostics.js +43 -0
- package/dist/host/script-execution.d.ts +72 -0
- package/dist/host/script-execution.js +68 -0
- package/dist/host/terminal-packs.d.ts +39 -0
- package/dist/host/terminal-packs.js +51 -0
- package/dist/html-engine.d.ts +2 -0
- package/dist/html-engine.js +29 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +69 -0
- package/dist/insert/component-catalog.d.ts +23 -0
- package/dist/insert/component-catalog.js +96 -0
- package/dist/node-fs.d.ts +14 -0
- package/dist/node-fs.js +70 -0
- package/dist/node-worker.js +20657 -0
- package/dist/node.d.ts +6 -0
- package/dist/node.js +13 -0
- package/dist/package.json +3 -0
- package/dist/packs/discover.d.ts +119 -0
- package/dist/packs/discover.js +266 -0
- package/dist/packs/pack-diagnostics.d.ts +79 -0
- package/dist/packs/pack-diagnostics.js +103 -0
- package/dist/packs/pack-paths.d.ts +53 -0
- package/dist/packs/pack-paths.js +105 -0
- package/dist/packs/pack-render-registry.d.ts +82 -0
- package/dist/packs/pack-render-registry.js +182 -0
- package/dist/packs/pack-scripts.d.ts +20 -0
- package/dist/packs/pack-scripts.js +96 -0
- package/dist/packs/pack-trust.d.ts +57 -0
- package/dist/packs/pack-trust.js +111 -0
- package/dist/packs/prebuilt.d.ts +49 -0
- package/dist/packs/prebuilt.js +97 -0
- package/dist/path-util.d.ts +24 -0
- package/dist/path-util.js +82 -0
- package/dist/run/browser-isolate.d.ts +45 -0
- package/dist/run/browser-isolate.js +85 -0
- package/dist/run/bundle-document.d.ts +29 -0
- package/dist/run/bundle-document.js +40 -0
- package/dist/run/bundle-run.d.ts +188 -0
- package/dist/run/bundle-run.js +232 -0
- package/dist/run/grant-flow.d.ts +232 -0
- package/dist/run/grant-flow.js +390 -0
- package/dist/run/ip-address.d.ts +41 -0
- package/dist/run/ip-address.js +255 -0
- package/dist/run/isolate.d.ts +59 -0
- package/dist/run/isolate.js +22 -0
- package/dist/run/lua-resolver.d.ts +21 -0
- package/dist/run/lua-resolver.js +63 -0
- package/dist/run/net-bridge-worker.d.ts +26 -0
- package/dist/run/net-bridge-worker.js +66 -0
- package/dist/run/net-bridge.d.ts +56 -0
- package/dist/run/net-bridge.js +59 -0
- package/dist/run/net-pinning.d.ts +82 -0
- package/dist/run/net-pinning.js +137 -0
- package/dist/run/net-provider.d.ts +72 -0
- package/dist/run/net-provider.js +303 -0
- package/dist/run/node-isolate.d.ts +22 -0
- package/dist/run/node-isolate.js +68 -0
- package/dist/run/run-flow.d.ts +186 -0
- package/dist/run/run-flow.js +323 -0
- package/dist/run/run-host.d.ts +107 -0
- package/dist/run/run-host.js +241 -0
- package/dist/run/run-job.d.ts +130 -0
- package/dist/run/run-job.js +279 -0
- package/dist/run/run-progress.d.ts +55 -0
- package/dist/run/run-progress.js +42 -0
- package/dist/run/run-trace.d.ts +44 -0
- package/dist/run/run-trace.js +36 -0
- package/dist/run/script-requirements.d.ts +98 -0
- package/dist/run/script-requirements.js +212 -0
- package/dist/run/snapshot-storage.d.ts +25 -0
- package/dist/run/snapshot-storage.js +31 -0
- package/dist/run/stale-values.d.ts +25 -0
- package/dist/run/stale-values.js +32 -0
- package/dist/run/worker-entry-browser.d.ts +62 -0
- package/dist/run/worker-entry-browser.js +172 -0
- package/dist/run/worker-entry.d.ts +1 -0
- package/dist/run/worker-entry.js +98 -0
- package/dist/values-failure.d.ts +17 -0
- package/dist/values-failure.js +1 -0
- package/dist/values-merge.d.ts +39 -0
- package/dist/values-merge.js +33 -0
- package/package.json +80 -4
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { DiscoveredPack } from './discover.js';
|
|
2
|
+
export declare const PREBUILT_SCRIPT_FILENAME = "webview.js";
|
|
3
|
+
export declare const PREBUILT_STYLESHEET_FILENAME = "webview.css";
|
|
4
|
+
/** The terminal engine's prebuilt registration module, beside `webview.js` (docs/packs.md). Optional: a pack with no terminal views never has one. */
|
|
5
|
+
export declare const PREBUILT_TERMINAL_SCRIPT_FILENAME = "terminal.js";
|
|
6
|
+
/** Absolute path of a pack folder's prebuilt terminal script. No existence check. */
|
|
7
|
+
export declare function prebuiltTerminalScriptPathFor(folder: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* The `webview.js` an export writes for a pack that has terminal views but
|
|
10
|
+
* no React ones: a registration with no components, so the archive keeps
|
|
11
|
+
* its required `webview.js` and a React host loads the pack as having
|
|
12
|
+
* nothing for it (every directive falls back).
|
|
13
|
+
*/
|
|
14
|
+
export declare function emptyWebviewScript(manifestJson: string): string;
|
|
15
|
+
/** Absolute path of a pack folder's prebuilt registration script. No existence check. */
|
|
16
|
+
export declare function prebuiltScriptPathFor(folder: string): string;
|
|
17
|
+
/** Absolute path of a pack folder's prebuilt sibling stylesheet. No existence check. */
|
|
18
|
+
export declare function prebuiltStylesheetPathFor(folder: string): string;
|
|
19
|
+
/** Whether an absolute path exists. Injected so this module needs no real disk to test; may be sync or async. */
|
|
20
|
+
export type PackPathExists = (absolutePath: string) => boolean | Promise<boolean>;
|
|
21
|
+
export interface PrebuiltPackResolution {
|
|
22
|
+
/** Absolute path to the pack's prebuilt webview.js (it exists). */
|
|
23
|
+
readonly scriptPath: string;
|
|
24
|
+
/** Absolute path to the sibling webview.css, present only when that file exists. */
|
|
25
|
+
readonly stylesheetPath?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Absolute paths of the manifest-declared component sources that are
|
|
28
|
+
* present on disk and therefore shadowed by the prebuilt script. Empty
|
|
29
|
+
* when the pack ships no sources (the ordinary distribution shape:
|
|
30
|
+
* `webview.js` alone, docs/packs.md's "This is the form to ship when
|
|
31
|
+
* other people will use your pack").
|
|
32
|
+
*/
|
|
33
|
+
readonly shadowedComponentSources: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves the prebuilt artifacts of one discovered pack, or `undefined`
|
|
37
|
+
* when the pack ships no `webview.js` at all (the from-source path, which a
|
|
38
|
+
* host compiles via `./pack-build.ts` instead).
|
|
39
|
+
*
|
|
40
|
+
* `pack.scriptPath` (already `folder/webview.js` from `./discover.ts`) is
|
|
41
|
+
* used as the script path rather than recomputed, but the stylesheet path
|
|
42
|
+
* is derived from the script path's own directory so the two always stay
|
|
43
|
+
* siblings even if a caller ever hands this a `DiscoveredPack` copy whose
|
|
44
|
+
* `folder` and `scriptPath` were computed independently.
|
|
45
|
+
*
|
|
46
|
+
* Never throws: an `exists` that throws is treated as "does not exist" for
|
|
47
|
+
* every path this function checks.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolvePrebuiltPack(pack: Pick<DiscoveredPack, 'folder' | 'componentPaths' | 'scriptPath'>, exists: PackPathExists): Promise<PrebuiltPackResolution | undefined>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The prebuilt-pack convention (docs/packs.md's "Two ways to run a pack:
|
|
3
|
+
* prebuilt and from source"): a pack that ships a compiled `webview.js`
|
|
4
|
+
* sibling to its `pack.json` is used as-is, no compiler runs. This module
|
|
5
|
+
* adds the second half of that convention (issue #15): an OPTIONAL sibling
|
|
6
|
+
* `webview.css`, injected/removed by a host's own pack-context composition
|
|
7
|
+
* keyed by namespace exactly like a compiled pack's own emitted stylesheet
|
|
8
|
+
* (`./pack-build.ts`'s `stylesheetPath`). A prebuilt pack with no
|
|
9
|
+
* `webview.css` simply has no stylesheet — never an error, matching
|
|
10
|
+
* `./discover.ts`'s "the file need not actually exist" posture for every
|
|
11
|
+
* other pack artifact.
|
|
12
|
+
*
|
|
13
|
+
* Also detects the SHADOWING case docs/packs.md names: "When a pack folder
|
|
14
|
+
* holds both a `webview.js` and sources, the prebuilt script wins and the
|
|
15
|
+
* sources are ignored." `resolvePrebuiltPack` reports exactly which of the
|
|
16
|
+
* manifest-declared component sources are present on disk, so a host's own
|
|
17
|
+
* diagnostics surface can name them (host-specific wording; this module
|
|
18
|
+
* only supplies the facts).
|
|
19
|
+
*
|
|
20
|
+
* Filesystem access is injected (`PackPathExists`), not imported directly,
|
|
21
|
+
* so this module is testable with plain in-memory fakes — matching every
|
|
22
|
+
* other module in this directory.
|
|
23
|
+
*/
|
|
24
|
+
import { dirnamePath, joinPath } from '../path-util.js';
|
|
25
|
+
export const PREBUILT_SCRIPT_FILENAME = 'webview.js';
|
|
26
|
+
export const PREBUILT_STYLESHEET_FILENAME = 'webview.css';
|
|
27
|
+
/** The terminal engine's prebuilt registration module, beside `webview.js` (docs/packs.md). Optional: a pack with no terminal views never has one. */
|
|
28
|
+
export const PREBUILT_TERMINAL_SCRIPT_FILENAME = 'terminal.js';
|
|
29
|
+
/** Absolute path of a pack folder's prebuilt terminal script. No existence check. */
|
|
30
|
+
export function prebuiltTerminalScriptPathFor(folder) {
|
|
31
|
+
return joinPath(folder, PREBUILT_TERMINAL_SCRIPT_FILENAME);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The `webview.js` an export writes for a pack that has terminal views but
|
|
35
|
+
* no React ones: a registration with no components, so the archive keeps
|
|
36
|
+
* its required `webview.js` and a React host loads the pack as having
|
|
37
|
+
* nothing for it (every directive falls back).
|
|
38
|
+
*/
|
|
39
|
+
export function emptyWebviewScript(manifestJson) {
|
|
40
|
+
return [
|
|
41
|
+
"if (typeof window !== 'undefined' && typeof window.__markiiRegisterPack === 'function') {",
|
|
42
|
+
` window.__markiiRegisterPack(${JSON.stringify(manifestJson)}, {});`,
|
|
43
|
+
'}',
|
|
44
|
+
'',
|
|
45
|
+
].join('\n');
|
|
46
|
+
}
|
|
47
|
+
/** Absolute path of a pack folder's prebuilt registration script. No existence check. */
|
|
48
|
+
export function prebuiltScriptPathFor(folder) {
|
|
49
|
+
return joinPath(folder, PREBUILT_SCRIPT_FILENAME);
|
|
50
|
+
}
|
|
51
|
+
/** Absolute path of a pack folder's prebuilt sibling stylesheet. No existence check. */
|
|
52
|
+
export function prebuiltStylesheetPathFor(folder) {
|
|
53
|
+
return joinPath(folder, PREBUILT_STYLESHEET_FILENAME);
|
|
54
|
+
}
|
|
55
|
+
/** Calls `exists`, treating a thrown error the same as "does not exist" — an unreadable path is not proof a file is absent, but this module's contract (like every other filesystem-adjacent module in this directory) is to degrade quietly rather than surface a raw I/O error here. */
|
|
56
|
+
async function existsQuietly(exists, absolutePath) {
|
|
57
|
+
try {
|
|
58
|
+
return await exists(absolutePath);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the prebuilt artifacts of one discovered pack, or `undefined`
|
|
66
|
+
* when the pack ships no `webview.js` at all (the from-source path, which a
|
|
67
|
+
* host compiles via `./pack-build.ts` instead).
|
|
68
|
+
*
|
|
69
|
+
* `pack.scriptPath` (already `folder/webview.js` from `./discover.ts`) is
|
|
70
|
+
* used as the script path rather than recomputed, but the stylesheet path
|
|
71
|
+
* is derived from the script path's own directory so the two always stay
|
|
72
|
+
* siblings even if a caller ever hands this a `DiscoveredPack` copy whose
|
|
73
|
+
* `folder` and `scriptPath` were computed independently.
|
|
74
|
+
*
|
|
75
|
+
* Never throws: an `exists` that throws is treated as "does not exist" for
|
|
76
|
+
* every path this function checks.
|
|
77
|
+
*/
|
|
78
|
+
export async function resolvePrebuiltPack(pack, exists) {
|
|
79
|
+
const scriptExists = await existsQuietly(exists, pack.scriptPath);
|
|
80
|
+
if (!scriptExists)
|
|
81
|
+
return undefined;
|
|
82
|
+
const stylesheetPath = joinPath(dirnamePath(pack.scriptPath), PREBUILT_STYLESHEET_FILENAME);
|
|
83
|
+
const stylesheetExists = await existsQuietly(exists, stylesheetPath);
|
|
84
|
+
const shadowedComponentSources = [];
|
|
85
|
+
for (const sourcePath of Object.values(pack.componentPaths)) {
|
|
86
|
+
if (sourcePath === undefined)
|
|
87
|
+
continue;
|
|
88
|
+
if (await existsQuietly(exists, sourcePath)) {
|
|
89
|
+
shadowedComponentSources.push(sourcePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
scriptPath: pack.scriptPath,
|
|
94
|
+
...(stylesheetExists ? { stylesheetPath } : {}),
|
|
95
|
+
shadowedComponentSources,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small, environment-free path helpers for the paths the host layer hands to
|
|
3
|
+
* a `HostAdapter` (`readFile`, `writeFile`, `listFolder`). They are adapter
|
|
4
|
+
* paths, not OS paths: this layer never touches a file system itself, so it
|
|
5
|
+
* must not import `node:path`.
|
|
6
|
+
*
|
|
7
|
+
* They stay correct for a Windows host, where the VS Code adapter does hand
|
|
8
|
+
* over `C:\Users\ana\packs` style strings (from `os.homedir()`, a
|
|
9
|
+
* `globalStorageUri.fsPath`, or a hand-written setting). The separator is
|
|
10
|
+
* taken from the base path: a base that uses backslashes and no forward
|
|
11
|
+
* slashes is a Windows path, joined and split with backslashes (forward
|
|
12
|
+
* slashes inside later segments are accepted as separators too, as
|
|
13
|
+
* `path.win32` does). Any other base is posix.
|
|
14
|
+
*/
|
|
15
|
+
/** True for a rooted path: `/x`, `\x`, `\\server\share`, or `C:\x` / `C:/x`. */
|
|
16
|
+
export declare function isAbsolutePath(path: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Joins `base` and `parts` with the separator `base` uses, collapsing `.`,
|
|
19
|
+
* `..` and repeated separators the way `path.join` does. An empty result is
|
|
20
|
+
* `.`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function joinPath(base: string, ...parts: string[]): string;
|
|
23
|
+
/** The parent folder of `path`, `path.dirname`-style (a root's parent is itself). */
|
|
24
|
+
export declare function dirnamePath(path: string): string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small, environment-free path helpers for the paths the host layer hands to
|
|
3
|
+
* a `HostAdapter` (`readFile`, `writeFile`, `listFolder`). They are adapter
|
|
4
|
+
* paths, not OS paths: this layer never touches a file system itself, so it
|
|
5
|
+
* must not import `node:path`.
|
|
6
|
+
*
|
|
7
|
+
* They stay correct for a Windows host, where the VS Code adapter does hand
|
|
8
|
+
* over `C:\Users\ana\packs` style strings (from `os.homedir()`, a
|
|
9
|
+
* `globalStorageUri.fsPath`, or a hand-written setting). The separator is
|
|
10
|
+
* taken from the base path: a base that uses backslashes and no forward
|
|
11
|
+
* slashes is a Windows path, joined and split with backslashes (forward
|
|
12
|
+
* slashes inside later segments are accepted as separators too, as
|
|
13
|
+
* `path.win32` does). Any other base is posix.
|
|
14
|
+
*/
|
|
15
|
+
const DRIVE_PREFIX = /^[A-Za-z]:/;
|
|
16
|
+
function separatorOf(path) {
|
|
17
|
+
return path.includes('\\') && !path.includes('/') ? '\\' : '/';
|
|
18
|
+
}
|
|
19
|
+
/** True for a rooted path: `/x`, `\x`, `\\server\share`, or `C:\x` / `C:/x`. */
|
|
20
|
+
export function isAbsolutePath(path) {
|
|
21
|
+
return (path.startsWith('/') ||
|
|
22
|
+
path.startsWith('\\') ||
|
|
23
|
+
/^[A-Za-z]:[\\/]/.test(path));
|
|
24
|
+
}
|
|
25
|
+
function parse(path, sep) {
|
|
26
|
+
const splitter = sep === '\\' ? /[\\/]+/ : /\/+/;
|
|
27
|
+
let root = '';
|
|
28
|
+
let rest = path;
|
|
29
|
+
if (sep === '\\') {
|
|
30
|
+
const drive = DRIVE_PREFIX.exec(rest);
|
|
31
|
+
if (drive) {
|
|
32
|
+
root = drive[0];
|
|
33
|
+
rest = rest.slice(2);
|
|
34
|
+
}
|
|
35
|
+
if (/^[\\/]{2}[^\\/]/.test(path) && root === '') {
|
|
36
|
+
root = '\\\\';
|
|
37
|
+
rest = rest.replace(/^[\\/]+/, '');
|
|
38
|
+
}
|
|
39
|
+
else if (/^[\\/]/.test(rest)) {
|
|
40
|
+
root += '\\';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else if (rest.startsWith('/')) {
|
|
44
|
+
root = '/';
|
|
45
|
+
}
|
|
46
|
+
const segments = [];
|
|
47
|
+
for (const part of rest.split(splitter)) {
|
|
48
|
+
if (part === '' || part === '.')
|
|
49
|
+
continue;
|
|
50
|
+
if (part === '..') {
|
|
51
|
+
const last = segments[segments.length - 1];
|
|
52
|
+
if (last !== undefined && last !== '..')
|
|
53
|
+
segments.pop();
|
|
54
|
+
else if (root === '')
|
|
55
|
+
segments.push('..');
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
segments.push(part);
|
|
59
|
+
}
|
|
60
|
+
return { root, segments };
|
|
61
|
+
}
|
|
62
|
+
function format({ root, segments }, sep) {
|
|
63
|
+
const body = segments.join(sep);
|
|
64
|
+
if (body === '')
|
|
65
|
+
return root === '' ? '.' : root;
|
|
66
|
+
return root + body;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Joins `base` and `parts` with the separator `base` uses, collapsing `.`,
|
|
70
|
+
* `..` and repeated separators the way `path.join` does. An empty result is
|
|
71
|
+
* `.`.
|
|
72
|
+
*/
|
|
73
|
+
export function joinPath(base, ...parts) {
|
|
74
|
+
const sep = separatorOf(base);
|
|
75
|
+
return format(parse([base, ...parts].filter((p) => p !== '').join(sep), sep), sep);
|
|
76
|
+
}
|
|
77
|
+
/** The parent folder of `path`, `path.dirname`-style (a root's parent is itself). */
|
|
78
|
+
export function dirnamePath(path) {
|
|
79
|
+
const sep = separatorOf(path);
|
|
80
|
+
const parsed = parse(path, sep);
|
|
81
|
+
return format({ root: parsed.root, segments: parsed.segments.slice(0, -1) }, sep);
|
|
82
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `RunIsolate` backed by a Web Worker, for hosts whose runtime cannot
|
|
3
|
+
* create a worker thread (see `./isolate.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Two details are load-bearing.
|
|
6
|
+
*
|
|
7
|
+
* The worker is started from a BLOB URL built from the bundle's own bytes,
|
|
8
|
+
* read off disk by the host. Loading a worker from a `file://` URL is
|
|
9
|
+
* refused by Chromium, and an Electron app's custom protocols are not
|
|
10
|
+
* something this package can assume, so reading the file the host already
|
|
11
|
+
* has and handing it over as a blob sidesteps the question entirely.
|
|
12
|
+
* `glue.wasm` travels the same way, because wasmoon fetches it by URL and
|
|
13
|
+
* there is no `__dirname` inside a worker to find it beside the bundle.
|
|
14
|
+
*
|
|
15
|
+
* Network is served by the HOST, not the worker: `serveNetRequests` wires
|
|
16
|
+
* every `markii:net-request` to the caller's own `NetProvider`. Lua cannot
|
|
17
|
+
* reach JavaScript, and the worker entry removes its own network globals
|
|
18
|
+
* before the first script runs (`./worker-entry-browser.ts`), so the
|
|
19
|
+
* provider's allowlist is the only path to the network.
|
|
20
|
+
*
|
|
21
|
+
* What this CANNOT do, stated plainly because it is a real difference: a
|
|
22
|
+
* Web Worker takes no `maxOldGenerationSizeMb`, so `options`'s heap cap is
|
|
23
|
+
* accepted and ignored. Lua's own memory limit and the external watchdog
|
|
24
|
+
* still apply; a runaway JavaScript-side allocation inside the isolate is
|
|
25
|
+
* bounded only by the browser engine, and exhausting it can take the host
|
|
26
|
+
* renderer down. docs/security.md records this.
|
|
27
|
+
*/
|
|
28
|
+
import type { NetProvider } from '@markii/lua';
|
|
29
|
+
import type { RunIsolate, SpawnIsolateOptions } from './isolate.js';
|
|
30
|
+
/** The Web Worker surface this uses, declared structurally so the package needs no DOM lib. */
|
|
31
|
+
export interface WorkerLike {
|
|
32
|
+
postMessage: (message: unknown) => void;
|
|
33
|
+
terminate: () => void;
|
|
34
|
+
addEventListener: (type: 'message' | 'error' | 'messageerror', listener: (event: unknown) => void) => void;
|
|
35
|
+
}
|
|
36
|
+
export interface BrowserIsolateOptions {
|
|
37
|
+
/** Creates the worker. Injected so a test never needs a real one, and so a host can decide how it mints the URL. */
|
|
38
|
+
createWorker: (entryPath: string) => WorkerLike;
|
|
39
|
+
/** The host's own pinned provider. Every `net.*` call the script makes is answered by this, in the host, never in the isolate. */
|
|
40
|
+
netProvider: (netAllowlist: string[], maxFetchBytes: number, netPolicy: unknown, signal?: AbortSignal) => NetProvider;
|
|
41
|
+
/** Where the worker should fetch wasmoon's `glue.wasm`; sent along with the job. */
|
|
42
|
+
wasmUri?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Builds the spawner a host passes as `SpawnRunOptions.spawnIsolate`. */
|
|
45
|
+
export declare function createBrowserIsolate(options: BrowserIsolateOptions): (spawn: SpawnIsolateOptions) => RunIsolate;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { isNetBridgeRequest, serveNetRequest, } from './net-bridge.js';
|
|
2
|
+
/** Pulls the fields the host needs to build the same provider the worker would have built for itself. */
|
|
3
|
+
function netArgsFrom(job) {
|
|
4
|
+
const j = (job ?? {});
|
|
5
|
+
return {
|
|
6
|
+
allowlist: Array.isArray(j.netAllowlist)
|
|
7
|
+
? j.netAllowlist.filter((h) => typeof h === 'string')
|
|
8
|
+
: [],
|
|
9
|
+
maxFetchBytes: typeof j.limits?.maxFetchBytes === 'number'
|
|
10
|
+
? j.limits.maxFetchBytes
|
|
11
|
+
: 2 * 1024 * 1024,
|
|
12
|
+
// Fail closed, exactly as the worker does with an absent policy.
|
|
13
|
+
policy: j.netPolicy ?? { allowRestrictedAddresses: false },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Builds the spawner a host passes as `SpawnRunOptions.spawnIsolate`. */
|
|
17
|
+
export function createBrowserIsolate(options) {
|
|
18
|
+
return (spawn) => {
|
|
19
|
+
const worker = options.createWorker(spawn.entryPath);
|
|
20
|
+
let provider;
|
|
21
|
+
let alive = true;
|
|
22
|
+
// Aborted by `kill()`: a request still in flight when the run ends is
|
|
23
|
+
// cancelled rather than left running in the host.
|
|
24
|
+
const requests = new AbortController();
|
|
25
|
+
const messageListeners = [];
|
|
26
|
+
const errorListeners = [];
|
|
27
|
+
worker.addEventListener('message', (event) => {
|
|
28
|
+
const data = event?.data;
|
|
29
|
+
if (isNetBridgeRequest(data)) {
|
|
30
|
+
if (!provider)
|
|
31
|
+
return;
|
|
32
|
+
void serveNetRequest(data, provider, (reply) => {
|
|
33
|
+
// A reply posted to a worker that is already gone would throw and
|
|
34
|
+
// take out this handler; the run is over either way.
|
|
35
|
+
if (alive)
|
|
36
|
+
worker.postMessage(reply);
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const listener of messageListeners)
|
|
41
|
+
listener(data);
|
|
42
|
+
});
|
|
43
|
+
worker.addEventListener('error', (event) => {
|
|
44
|
+
const message = event?.message ?? 'worker error';
|
|
45
|
+
for (const listener of errorListeners) {
|
|
46
|
+
listener(new Error(String(message)));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// A message that could not be deserialized is a failed run, not silence.
|
|
50
|
+
worker.addEventListener('messageerror', () => {
|
|
51
|
+
for (const listener of errorListeners) {
|
|
52
|
+
listener(new Error('worker sent an undeserializable message'));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
send: (job) => {
|
|
57
|
+
const { allowlist, maxFetchBytes, policy } = netArgsFrom(job);
|
|
58
|
+
provider = options.netProvider(allowlist, maxFetchBytes, policy, requests.signal);
|
|
59
|
+
worker.postMessage({
|
|
60
|
+
...job,
|
|
61
|
+
...(options.wasmUri !== undefined
|
|
62
|
+
? { wasmUri: options.wasmUri }
|
|
63
|
+
: {}),
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
kill: () => {
|
|
67
|
+
if (!alive)
|
|
68
|
+
return;
|
|
69
|
+
alive = false;
|
|
70
|
+
requests.abort();
|
|
71
|
+
worker.terminate();
|
|
72
|
+
},
|
|
73
|
+
onMessage: (listener) => {
|
|
74
|
+
messageListeners.push(listener);
|
|
75
|
+
},
|
|
76
|
+
onError: (listener) => {
|
|
77
|
+
errorListeners.push(listener);
|
|
78
|
+
},
|
|
79
|
+
// A Web Worker has no exit event: `terminate()` is immediate and
|
|
80
|
+
// silent. `spawnRun`'s watchdog path settles on its own timer rather
|
|
81
|
+
// than waiting for one, so there is nothing to report here.
|
|
82
|
+
onExit: () => { },
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a bundle's note lives and what a bundle is called on disk. Every
|
|
3
|
+
* host that opens a `.mkz` (the VS Code preview, the CLI) asks these
|
|
4
|
+
* questions, so the answers live here once.
|
|
5
|
+
*/
|
|
6
|
+
import type { BundleManifest } from '@markii/bundle';
|
|
7
|
+
/** The conventional document path inside a bundle when the manifest does not name one (docs/bundles.md). */
|
|
8
|
+
export declare const DEFAULT_BUNDLE_DOCUMENT_PATH = "note.mk.md";
|
|
9
|
+
/** The current bundle extension; new bundles are always written with this one. */
|
|
10
|
+
export declare const BUNDLE_EXTENSION = ".mkz";
|
|
11
|
+
/** The legacy bundle extension, still recognized. */
|
|
12
|
+
export declare const LEGACY_BUNDLE_EXTENSION = ".mkbundle";
|
|
13
|
+
/**
|
|
14
|
+
* True when `name` ends with `.mkz` or the legacy `.mkbundle`
|
|
15
|
+
* (case-insensitively) AND has a non-empty base name before the extension.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isBundleName(name: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* The bundle-relative path of the document to open: the manifest's own
|
|
20
|
+
* `document` field when it names one, normalized through the same path jail
|
|
21
|
+
* every other bundle path goes through so a manifest cannot point outside
|
|
22
|
+
* the bundle; otherwise the conventional `note.mk.md`.
|
|
23
|
+
*
|
|
24
|
+
* Returns `undefined` when a `document` field is present but not a usable
|
|
25
|
+
* bundle-relative path (wrong type, or rejected by `normalizeBundlePath`,
|
|
26
|
+
* including any path with a `..` segment). That is a manifest authoring
|
|
27
|
+
* error, not something to ignore silently.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveBundleDocumentPath(manifest: BundleManifest): string | undefined;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { normalizeBundlePath } from '@markii/bundle';
|
|
2
|
+
/** The conventional document path inside a bundle when the manifest does not name one (docs/bundles.md). */
|
|
3
|
+
export const DEFAULT_BUNDLE_DOCUMENT_PATH = 'note.mk.md';
|
|
4
|
+
/** The current bundle extension; new bundles are always written with this one. */
|
|
5
|
+
export const BUNDLE_EXTENSION = '.mkz';
|
|
6
|
+
/** The legacy bundle extension, still recognized. */
|
|
7
|
+
export const LEGACY_BUNDLE_EXTENSION = '.mkbundle';
|
|
8
|
+
/**
|
|
9
|
+
* True when `name` ends with `.mkz` or the legacy `.mkbundle`
|
|
10
|
+
* (case-insensitively) AND has a non-empty base name before the extension.
|
|
11
|
+
*/
|
|
12
|
+
export function isBundleName(name) {
|
|
13
|
+
const lower = name.toLowerCase();
|
|
14
|
+
for (const extension of [BUNDLE_EXTENSION, LEGACY_BUNDLE_EXTENSION]) {
|
|
15
|
+
if (lower.endsWith(extension) && lower.length > extension.length) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The bundle-relative path of the document to open: the manifest's own
|
|
23
|
+
* `document` field when it names one, normalized through the same path jail
|
|
24
|
+
* every other bundle path goes through so a manifest cannot point outside
|
|
25
|
+
* the bundle; otherwise the conventional `note.mk.md`.
|
|
26
|
+
*
|
|
27
|
+
* Returns `undefined` when a `document` field is present but not a usable
|
|
28
|
+
* bundle-relative path (wrong type, or rejected by `normalizeBundlePath`,
|
|
29
|
+
* including any path with a `..` segment). That is a manifest authoring
|
|
30
|
+
* error, not something to ignore silently.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveBundleDocumentPath(manifest) {
|
|
33
|
+
const raw = manifest.document;
|
|
34
|
+
if (raw === undefined)
|
|
35
|
+
return DEFAULT_BUNDLE_DOCUMENT_PATH;
|
|
36
|
+
if (typeof raw !== 'string')
|
|
37
|
+
return undefined;
|
|
38
|
+
const normalized = normalizeBundlePath(raw);
|
|
39
|
+
return normalized.ok ? normalized.path : undefined;
|
|
40
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slice 2 of the `.mkz` Run-path arc (GitHub issue #9's locked design
|
|
3
|
+
* comment): the host-side pieces that turn a bundle's `@markii/bundle`
|
|
4
|
+
* `BundleStorage` into the SNAPSHOT a worker run is handed, and back a
|
|
5
|
+
* worker's `.cache/` output into something the host can persist. Kept
|
|
6
|
+
* `vscode`-free, like every other module under `./run` — `preview-panel.ts`
|
|
7
|
+
* (the only file allowed to import `vscode` alongside `extension.ts`)
|
|
8
|
+
* supplies the actual `BundleStorage`/`Memento`/filesystem calls.
|
|
9
|
+
*
|
|
10
|
+
* ## Why a snapshot at all
|
|
11
|
+
*
|
|
12
|
+
* The worker is a separate `worker_thread` and must never hold a live
|
|
13
|
+
* handle to a zip archive or the user's disk (docs/security.md's isolate
|
|
14
|
+
* requirement, restated in issue #9's design comment) — so the host reads
|
|
15
|
+
* whatever a run may legitimately touch into plain bytes BEFORE spawning
|
|
16
|
+
* the worker, and the worker's `@markii/bundle` `ScriptView` is backed by
|
|
17
|
+
* that in-memory snapshot alone (`./snapshot-storage.ts`), never by the
|
|
18
|
+
* real storage.
|
|
19
|
+
*
|
|
20
|
+
* ## What's in the snapshot
|
|
21
|
+
*
|
|
22
|
+
* The design comment scopes this to "the `scripts/` tree, the subset of
|
|
23
|
+
* `assets/` reachable, and current `.cache/` contents" — deliberately NOT
|
|
24
|
+
* `manifest.json` or the document itself, even though `@markii/bundle`'s
|
|
25
|
+
* `ScriptView.read` places no path restriction on a granted read (only
|
|
26
|
+
* `write` is jailed to `.cache/`). A script that specifically wants to
|
|
27
|
+
* introspect its own manifest or document text cannot do so through
|
|
28
|
+
* `bundle.read` in this slice — `bundle.read("manifest.json")` comes back
|
|
29
|
+
* `undefined`, indistinguishable from "no such file", exactly like any
|
|
30
|
+
* other path the snapshot didn't collect (AGENTS.md's cleanliness
|
|
31
|
+
* principle: a quiet miss, never a special-cased error). This is a
|
|
32
|
+
* deliberate scope line for this slice, not an oversight — flagged in the
|
|
33
|
+
* slice's own report rather than silently widened.
|
|
34
|
+
*
|
|
35
|
+
* ## Consent unification (SECURITY-RELEVANT)
|
|
36
|
+
*
|
|
37
|
+
* Two rules this module and its callers (`./run-flow.ts`, `./grant-flow.ts`)
|
|
38
|
+
* now hold to, so a bundle's consent story matches a bare `.mk.md`
|
|
39
|
+
* document's exactly:
|
|
40
|
+
*
|
|
41
|
+
* - `manifestNetHosts` (the manifest's `permissions.net` declaration) is
|
|
42
|
+
* never merged into what a run prompts for. The static scan
|
|
43
|
+
* (`./script-requirements.ts`) is the only source of prompted hostnames,
|
|
44
|
+
* for a bundle too — see `netDeclarationDiagnostics` below for how a
|
|
45
|
+
* mismatch is surfaced instead.
|
|
46
|
+
* - The `bundle` capability (`permissions.bundle`) needs no user-facing
|
|
47
|
+
* prompt at all: `@markii/bundle`'s path-jail confines every read to the
|
|
48
|
+
* snapshot and every write to `.cache/`, and the read-only tier for
|
|
49
|
+
* auto/scheduled triggers blocks `bundle.write` outright regardless of
|
|
50
|
+
* what the manifest declares. `manifestBundleFsGrants`'s result is passed
|
|
51
|
+
* straight through as the run's granted bundle-fs permissions — see
|
|
52
|
+
* `./run-flow.ts`.
|
|
53
|
+
*/
|
|
54
|
+
import type { BundleManifest, BundleStorage } from '@markii/bundle';
|
|
55
|
+
import type { BundleFsGrant } from '@markii/bundle';
|
|
56
|
+
import type { GrantClosureScript } from '@markii/runtime';
|
|
57
|
+
/**
|
|
58
|
+
* Total byte budget for one run's bundle snapshot (across `scripts/` +
|
|
59
|
+
* `.cache/` + `assets/` combined). Mirrors the same order of magnitude as
|
|
60
|
+
* `bundle-resolve.ts`'s `DEFAULT_MAX_EMBEDDED_ASSET_BYTES` (20MB) — a
|
|
61
|
+
* personal note bundle's scripts and cache are typically tiny; this budget
|
|
62
|
+
* exists so a hostile or oversized bundle degrades quietly (files beyond
|
|
63
|
+
* the cap are simply absent from the snapshot, exactly like a path the
|
|
64
|
+
* snapshot never collected) rather than exhausting worker/host memory or
|
|
65
|
+
* ballooning the `postMessage` structured-clone payload.
|
|
66
|
+
*/
|
|
67
|
+
export declare const DEFAULT_MAX_BUNDLE_SNAPSHOT_BYTES: number;
|
|
68
|
+
/**
|
|
69
|
+
* Per-file cap applied on top of the total budget (C-1, PENTEST-REPORT
|
|
70
|
+
* follow-up): a single huge file could otherwise consume the ENTIRE total
|
|
71
|
+
* budget by itself, and — before this fix — would be read into memory in
|
|
72
|
+
* full before the total-budget check ever saw it. Sized the same as the
|
|
73
|
+
* total budget by default; a caller wanting a stricter per-file limit can
|
|
74
|
+
* override it independently via `maxFileBytes`.
|
|
75
|
+
*/
|
|
76
|
+
export declare const DEFAULT_MAX_BUNDLE_SNAPSHOT_FILE_BYTES: number;
|
|
77
|
+
export interface BuildBundleSnapshotOptions {
|
|
78
|
+
readonly maxTotalBytes?: number;
|
|
79
|
+
readonly maxFileBytes?: number;
|
|
80
|
+
}
|
|
81
|
+
export interface BundleSnapshotResult {
|
|
82
|
+
/** Bundle-relative path -> file bytes, ready to hand to `spawnRun`'s `bundle.snapshot`. */
|
|
83
|
+
readonly files: Record<string, Uint8Array>;
|
|
84
|
+
/** `true` when at least one otherwise-eligible file was left out because the budget was reached — a quiet degrade, never a thrown error. */
|
|
85
|
+
readonly truncated: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Reads every file under `scripts/`, `.cache/`, and `assets/` out of
|
|
89
|
+
* `storage`, up to `maxTotalBytes` total. `storage.list()` already returns
|
|
90
|
+
* paths sorted, so which files get dropped once the budget is hit is
|
|
91
|
+
* deterministic rather than depending on iteration order.
|
|
92
|
+
*/
|
|
93
|
+
export declare function buildBundleSnapshot(storage: BundleStorage, options?: BuildBundleSnapshotOptions): Promise<BundleSnapshotResult>;
|
|
94
|
+
/**
|
|
95
|
+
* F-1 fix: the `bundleModules` half of the grant closure `computeGrantKey`
|
|
96
|
+
* (`@markii/runtime`) hashes — resolved from `snapshot` (the same in-memory
|
|
97
|
+
* bundle snapshot a run's `ScriptView` is backed by, see this module's top
|
|
98
|
+
* doc comment) rather than left empty.
|
|
99
|
+
*
|
|
100
|
+
* Before this fix, `grant-flow.ts`'s `closureFrom` always passed
|
|
101
|
+
* `bundleModules: {}`, so a `src=scripts/etl.lua` block's `code` was `""`
|
|
102
|
+
* (@markii/core's `ScriptBlock` leaves it empty for a `src=` reference — the
|
|
103
|
+
* actual source lives in the referenced file) and the grant key never
|
|
104
|
+
* reflected that file's bytes at all: swapping the file's contents while
|
|
105
|
+
* the note's own text stayed byte-identical re-ran new code under an old,
|
|
106
|
+
* un-reprompted grant.
|
|
107
|
+
*
|
|
108
|
+
* Only `src=` targets are resolved here — `require()` (bundle-local or
|
|
109
|
+
* pack) stays unwired for the whole `.mkz` Run-path arc (see
|
|
110
|
+
* `grant-flow.ts`'s top doc comment), so a script's closure has no other
|
|
111
|
+
* bundle-local code to fold in yet. A `src=` path missing from `snapshot`
|
|
112
|
+
* (file deleted, or dropped by `buildBundleSnapshot`'s own budget) is
|
|
113
|
+
* simply omitted from the result — never thrown — so a note whose script
|
|
114
|
+
* references a missing file still gets a (now-narrower, but well-defined)
|
|
115
|
+
* grant key instead of failing the whole grant flow.
|
|
116
|
+
*/
|
|
117
|
+
export declare function bundleModulesFromSnapshot(scripts: readonly GrantClosureScript[], snapshot: Record<string, Uint8Array>): Record<string, string>;
|
|
118
|
+
/** Every `.cache/`-prefixed entry in `files`, unchanged — the shape a worker's `RunResult.cacheOut` carries back and a host persists. */
|
|
119
|
+
export declare function cacheFilesFrom(files: Record<string, Uint8Array>): Record<string, Uint8Array>;
|
|
120
|
+
/**
|
|
121
|
+
* Overlays `persisted` (a prior run's `.cache/` output, read back from
|
|
122
|
+
* wherever the host keeps it) onto `base` (a freshly built snapshot),
|
|
123
|
+
* persisted entries winning on a path collision — the "seed the next run's
|
|
124
|
+
* snapshot from the persisted cache" half of the design. Used for the
|
|
125
|
+
* zip-form path, where the archive's own on-disk `.cache/` entries (if any)
|
|
126
|
+
* are stale the moment a run has written a persisted cache elsewhere (the
|
|
127
|
+
* host never rewrites the user's zip — see `docs` design comment).
|
|
128
|
+
*/
|
|
129
|
+
export declare function withPersistedCache(base: Record<string, Uint8Array>, persisted: Record<string, Uint8Array>): Record<string, Uint8Array>;
|
|
130
|
+
/** Bare, lowercased hostnames a manifest declares under `permissions.net.get`/`.post`, deduplicated. */
|
|
131
|
+
export declare function manifestNetHosts(manifest: BundleManifest): string[];
|
|
132
|
+
/**
|
|
133
|
+
* Consent unification (SECURITY-RELEVANT, GitHub issue #9 follow-up): a
|
|
134
|
+
* bundle's manifest `permissions.net` is DECLARED INTENT only. The static
|
|
135
|
+
* scan of the run's executable closure (`./script-requirements.ts`) is the
|
|
136
|
+
* ONLY source of the hostnames a run prompts for, for a bundle exactly as
|
|
137
|
+
* for a bare `.mk.md` document — the manifest's declaration never widens
|
|
138
|
+
* that prompt (a declared-but-unused host is never prompted for) and never
|
|
139
|
+
* narrows it (a host a script actually reaches is always prompted for,
|
|
140
|
+
* declared or not).
|
|
141
|
+
*
|
|
142
|
+
* A mismatch between the two is not silently absorbed: this pure function
|
|
143
|
+
* returns one diagnostics line per mismatched host, in both directions, for
|
|
144
|
+
* the host's diagnostics surface (docs/integration.md) — never the rendered
|
|
145
|
+
* page. Wording lives here, the one place both apps' adapters read it from,
|
|
146
|
+
* so it can never drift between VS Code's output channel and Obsidian's
|
|
147
|
+
* console/notice surface.
|
|
148
|
+
*
|
|
149
|
+
* Returns `[]` when the two sets agree (including when both are empty).
|
|
150
|
+
* Order: every declared-but-unused host first (in `declaredHosts` order),
|
|
151
|
+
* then every used-but-undeclared host (in `scannedHosts` order) — matching
|
|
152
|
+
* how `manifestNetHosts`/`extractRunRequirements` already dedupe and order
|
|
153
|
+
* their inputs, so this function does no case-folding or deduping of its
|
|
154
|
+
* own.
|
|
155
|
+
*/
|
|
156
|
+
export declare function netDeclarationDiagnostics(declaredHosts: readonly string[], scannedHosts: readonly string[]): string[];
|
|
157
|
+
/** The bundle-filesystem grants a manifest declares under `permissions.bundle` (`'read'` / `'write:.cache/'`), or `[]` when it declares none. */
|
|
158
|
+
export declare function manifestBundleFsGrants(manifest: BundleManifest): BundleFsGrant[];
|
|
159
|
+
/**
|
|
160
|
+
* Cap on a zip-form bundle's persisted `.cache/` state (extension storage,
|
|
161
|
+
* keyed by bundle identity — see `preview-panel.ts`'s adapters). Mirrors
|
|
162
|
+
* `run-flow.ts`'s `MAX_CACHE_SNAPSHOT_BYTES` for the unrelated `cache.get`
|
|
163
|
+
* Memento snapshot: real script-written cache files are small, so this
|
|
164
|
+
* exists purely to guarantee a runaway/adversarial bundle can never grow
|
|
165
|
+
* `workspaceState` without bound. Oversize state is DROPPED wholesale, not
|
|
166
|
+
* partially persisted — see that constant's doc comment for why a partial
|
|
167
|
+
* write is worse than none.
|
|
168
|
+
*/
|
|
169
|
+
export declare const MAX_PERSISTED_BUNDLE_CACHE_BYTES = 1000000;
|
|
170
|
+
/** A JSON-safe, base64-encoded form of a `path -> bytes` cache map, suitable for a `vscode.Memento` value. */
|
|
171
|
+
export type EncodedBundleCache = Record<string, string>;
|
|
172
|
+
/**
|
|
173
|
+
* Encodes `files` (a bundle run's `.cache/` output) for Memento storage, or
|
|
174
|
+
* `undefined` when it must be dropped instead — either it doesn't encode at
|
|
175
|
+
* all, or the encoded form exceeds `MAX_PERSISTED_BUNDLE_CACHE_BYTES`.
|
|
176
|
+
* Callers write `undefined` back to storage in that case, exactly like
|
|
177
|
+
* `run-flow.ts`'s `serializeCacheSnapshotIfSmallEnough`.
|
|
178
|
+
*/
|
|
179
|
+
export declare function encodeBundleCacheForStorage(files: Record<string, Uint8Array>): EncodedBundleCache | undefined;
|
|
180
|
+
/**
|
|
181
|
+
* Decodes a value read back from a `vscode.Memento` into a `path -> bytes`
|
|
182
|
+
* cache map — never trusts the stored shape further than "every value is a
|
|
183
|
+
* string that decodes as base64"; a corrupt/foreign/hand-edited value
|
|
184
|
+
* degrades to an empty map (no persisted cache), matching this whole
|
|
185
|
+
* module's fail-safe posture (`grant-flow.ts`'s `isStoredGrant` takes the
|
|
186
|
+
* same stance for its own Memento reads).
|
|
187
|
+
*/
|
|
188
|
+
export declare function decodeBundleCacheFromStorage(value: unknown): Record<string, Uint8Array>;
|