@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.
Files changed (193) hide show
  1. package/README.md +20 -2
  2. package/dist/browser-worker.js +639 -0
  3. package/dist/browser.d.ts +61 -0
  4. package/dist/browser.js +172 -0
  5. package/dist/conformance/host/01-plain-open/expected.json +5 -0
  6. package/dist/conformance/host/01-plain-open/note.mk.md +8 -0
  7. package/dist/conformance/host/01-plain-open/scenario.json +6 -0
  8. package/dist/conformance/host/02-run-manual-grant-accepted/expected.json +16 -0
  9. package/dist/conformance/host/02-run-manual-grant-accepted/note.mk.md +5 -0
  10. package/dist/conformance/host/02-run-manual-grant-accepted/scenario.json +14 -0
  11. package/dist/conformance/host/03-run-manual-grant-refused/expected.json +18 -0
  12. package/dist/conformance/host/03-run-manual-grant-refused/note.mk.md +5 -0
  13. package/dist/conformance/host/03-run-manual-grant-refused/scenario.json +14 -0
  14. package/dist/conformance/host/04-run-auto-write-denied/expected.json +16 -0
  15. package/dist/conformance/host/04-run-auto-write-denied/note.mk.md +10 -0
  16. package/dist/conformance/host/04-run-auto-write-denied/scenario.json +13 -0
  17. package/dist/conformance/host/05-export-html-pack-directive/expected.json +14 -0
  18. package/dist/conformance/host/05-export-html-pack-directive/note.mk.md +8 -0
  19. package/dist/conformance/host/05-export-html-pack-directive/scenario.json +6 -0
  20. package/dist/conformance/host/06-install-pack-then-render/expected.json +18 -0
  21. package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/pack.json +13 -0
  22. package/dist/conformance/host/06-install-pack-then-render/files/ana-1.0.0/webview.js +1 -0
  23. package/dist/conformance/host/06-install-pack-then-render/note.mk.md +5 -0
  24. package/dist/conformance/host/06-install-pack-then-render/scenario.json +15 -0
  25. package/dist/conformance/host/07-complete-in-directive/expected.json +14 -0
  26. package/dist/conformance/host/07-complete-in-directive/note.mk.md +3 -0
  27. package/dist/conformance/host/07-complete-in-directive/scenario.json +6 -0
  28. package/dist/conformance/host/08-hover-standard-component/expected.json +5 -0
  29. package/dist/conformance/host/08-hover-standard-component/note.mk.md +5 -0
  30. package/dist/conformance/host/08-hover-standard-component/scenario.json +6 -0
  31. package/dist/conformance/host/09-insert-skeleton/expected.json +5 -0
  32. package/dist/conformance/host/09-insert-skeleton/note.mk.md +4 -0
  33. package/dist/conformance/host/09-insert-skeleton/scenario.json +6 -0
  34. package/dist/conformance/host/10-scripts-disabled/expected.json +11 -0
  35. package/dist/conformance/host/10-scripts-disabled/note.mk.md +5 -0
  36. package/dist/conformance/host/10-scripts-disabled/scenario.json +9 -0
  37. package/dist/conformance/host/11-complete-closing-fence/expected.json +5 -0
  38. package/dist/conformance/host/11-complete-closing-fence/note.mk.md +5 -0
  39. package/dist/conformance/host/11-complete-closing-fence/scenario.json +12 -0
  40. package/dist/conformance/host/12-complete-nested-container/expected.json +5 -0
  41. package/dist/conformance/host/12-complete-nested-container/note.mk.md +5 -0
  42. package/dist/conformance/host/12-complete-nested-container/scenario.json +12 -0
  43. package/dist/conformance/host/13-install-pack-declined/expected.json +12 -0
  44. package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/pack.json +13 -0
  45. package/dist/conformance/host/13-install-pack-declined/files/ana-1.0.0/webview.js +1 -0
  46. package/dist/conformance/host/13-install-pack-declined/note.mk.md +5 -0
  47. package/dist/conformance/host/13-install-pack-declined/scenario.json +10 -0
  48. package/dist/conformance/host/14-remove-pack/expected.json +14 -0
  49. package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/pack.json +13 -0
  50. package/dist/conformance/host/14-remove-pack/files/ana-1.0.0/webview.js +1 -0
  51. package/dist/conformance/host/14-remove-pack/note.mk.md +5 -0
  52. package/dist/conformance/host/14-remove-pack/scenario.json +11 -0
  53. package/dist/conformance/host/15-load-packs-engine-views/expected.json +19 -0
  54. package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/pack.json +9 -0
  55. package/dist/conformance/host/15-load-packs-engine-views/files/duo-1.0.0/webview.js +1 -0
  56. package/dist/conformance/host/15-load-packs-engine-views/note.mk.md +9 -0
  57. package/dist/conformance/host/15-load-packs-engine-views/scenario.json +10 -0
  58. package/dist/conformance/host/16-pack-trust/expected.json +37 -0
  59. package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/pack.json +12 -0
  60. package/dist/conformance/host/16-pack-trust/files/ana-1.0.0/webview.js +1 -0
  61. package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/pack.json +12 -0
  62. package/dist/conformance/host/16-pack-trust/files/sneaky-1.0.0/webview.js +1 -0
  63. package/dist/conformance/host/16-pack-trust/note.mk.md +5 -0
  64. package/dist/conformance/host/16-pack-trust/scenario.json +26 -0
  65. package/dist/conformance/host/README.md +221 -0
  66. package/dist/conformance.d.ts +2 -0
  67. package/dist/conformance.js +5 -0
  68. package/dist/diagnostics/render-diagnostics.d.ts +41 -0
  69. package/dist/diagnostics/render-diagnostics.js +46 -0
  70. package/dist/export/cascade.d.ts +98 -0
  71. package/dist/export/cascade.js +159 -0
  72. package/dist/export/export-zip.d.ts +19 -0
  73. package/dist/export/export-zip.js +38 -0
  74. package/dist/export/image-embed.d.ts +126 -0
  75. package/dist/export/image-embed.js +274 -0
  76. package/dist/export/note-export.d.ts +332 -0
  77. package/dist/export/note-export.js +366 -0
  78. package/dist/export/note-links.d.ts +83 -0
  79. package/dist/export/note-links.js +241 -0
  80. package/dist/glue.wasm +0 -0
  81. package/dist/host/adapter.d.ts +218 -0
  82. package/dist/host/adapter.js +9 -0
  83. package/dist/host/cascade-export-behavior.d.ts +120 -0
  84. package/dist/host/cascade-export-behavior.js +151 -0
  85. package/dist/host/conformance-runner.d.ts +159 -0
  86. package/dist/host/conformance-runner.js +632 -0
  87. package/dist/host/create-host.d.ts +251 -0
  88. package/dist/host/create-host.js +329 -0
  89. package/dist/host/editor-behavior.d.ts +153 -0
  90. package/dist/host/editor-behavior.js +223 -0
  91. package/dist/host/export/md-plain.d.ts +15 -0
  92. package/dist/host/export/md-plain.js +125 -0
  93. package/dist/host/export-behavior.d.ts +111 -0
  94. package/dist/host/export-behavior.js +257 -0
  95. package/dist/host/labels.d.ts +23 -0
  96. package/dist/host/labels.js +32 -0
  97. package/dist/host/pack-archive.d.ts +30 -0
  98. package/dist/host/pack-archive.js +62 -0
  99. package/dist/host/pack-install.d.ts +87 -0
  100. package/dist/host/pack-install.js +186 -0
  101. package/dist/host/pack-load.d.ts +71 -0
  102. package/dist/host/pack-load.js +57 -0
  103. package/dist/host/pack-remove.d.ts +21 -0
  104. package/dist/host/pack-remove.js +65 -0
  105. package/dist/host/pack-trust-behavior.d.ts +79 -0
  106. package/dist/host/pack-trust-behavior.js +262 -0
  107. package/dist/host/refresh-interval.d.ts +37 -0
  108. package/dist/host/refresh-interval.js +59 -0
  109. package/dist/host/run-behavior.d.ts +88 -0
  110. package/dist/host/run-behavior.js +189 -0
  111. package/dist/host/run-diagnostics.d.ts +26 -0
  112. package/dist/host/run-diagnostics.js +43 -0
  113. package/dist/host/script-execution.d.ts +72 -0
  114. package/dist/host/script-execution.js +68 -0
  115. package/dist/host/terminal-packs.d.ts +39 -0
  116. package/dist/host/terminal-packs.js +51 -0
  117. package/dist/html-engine.d.ts +2 -0
  118. package/dist/html-engine.js +29 -0
  119. package/dist/index.d.ts +95 -0
  120. package/dist/index.js +69 -0
  121. package/dist/insert/component-catalog.d.ts +23 -0
  122. package/dist/insert/component-catalog.js +96 -0
  123. package/dist/node-fs.d.ts +14 -0
  124. package/dist/node-fs.js +70 -0
  125. package/dist/node-worker.js +20657 -0
  126. package/dist/node.d.ts +6 -0
  127. package/dist/node.js +13 -0
  128. package/dist/package.json +3 -0
  129. package/dist/packs/discover.d.ts +119 -0
  130. package/dist/packs/discover.js +266 -0
  131. package/dist/packs/pack-diagnostics.d.ts +79 -0
  132. package/dist/packs/pack-diagnostics.js +103 -0
  133. package/dist/packs/pack-paths.d.ts +53 -0
  134. package/dist/packs/pack-paths.js +105 -0
  135. package/dist/packs/pack-render-registry.d.ts +82 -0
  136. package/dist/packs/pack-render-registry.js +182 -0
  137. package/dist/packs/pack-scripts.d.ts +20 -0
  138. package/dist/packs/pack-scripts.js +96 -0
  139. package/dist/packs/pack-trust.d.ts +57 -0
  140. package/dist/packs/pack-trust.js +111 -0
  141. package/dist/packs/prebuilt.d.ts +49 -0
  142. package/dist/packs/prebuilt.js +97 -0
  143. package/dist/path-util.d.ts +24 -0
  144. package/dist/path-util.js +82 -0
  145. package/dist/run/browser-isolate.d.ts +45 -0
  146. package/dist/run/browser-isolate.js +85 -0
  147. package/dist/run/bundle-document.d.ts +29 -0
  148. package/dist/run/bundle-document.js +40 -0
  149. package/dist/run/bundle-run.d.ts +188 -0
  150. package/dist/run/bundle-run.js +232 -0
  151. package/dist/run/grant-flow.d.ts +232 -0
  152. package/dist/run/grant-flow.js +390 -0
  153. package/dist/run/ip-address.d.ts +41 -0
  154. package/dist/run/ip-address.js +255 -0
  155. package/dist/run/isolate.d.ts +59 -0
  156. package/dist/run/isolate.js +22 -0
  157. package/dist/run/lua-resolver.d.ts +21 -0
  158. package/dist/run/lua-resolver.js +63 -0
  159. package/dist/run/net-bridge-worker.d.ts +26 -0
  160. package/dist/run/net-bridge-worker.js +66 -0
  161. package/dist/run/net-bridge.d.ts +56 -0
  162. package/dist/run/net-bridge.js +59 -0
  163. package/dist/run/net-pinning.d.ts +82 -0
  164. package/dist/run/net-pinning.js +137 -0
  165. package/dist/run/net-provider.d.ts +72 -0
  166. package/dist/run/net-provider.js +303 -0
  167. package/dist/run/node-isolate.d.ts +22 -0
  168. package/dist/run/node-isolate.js +68 -0
  169. package/dist/run/run-flow.d.ts +186 -0
  170. package/dist/run/run-flow.js +323 -0
  171. package/dist/run/run-host.d.ts +107 -0
  172. package/dist/run/run-host.js +241 -0
  173. package/dist/run/run-job.d.ts +130 -0
  174. package/dist/run/run-job.js +279 -0
  175. package/dist/run/run-progress.d.ts +55 -0
  176. package/dist/run/run-progress.js +42 -0
  177. package/dist/run/run-trace.d.ts +44 -0
  178. package/dist/run/run-trace.js +36 -0
  179. package/dist/run/script-requirements.d.ts +98 -0
  180. package/dist/run/script-requirements.js +212 -0
  181. package/dist/run/snapshot-storage.d.ts +25 -0
  182. package/dist/run/snapshot-storage.js +31 -0
  183. package/dist/run/stale-values.d.ts +25 -0
  184. package/dist/run/stale-values.js +32 -0
  185. package/dist/run/worker-entry-browser.d.ts +62 -0
  186. package/dist/run/worker-entry-browser.js +172 -0
  187. package/dist/run/worker-entry.d.ts +1 -0
  188. package/dist/run/worker-entry.js +98 -0
  189. package/dist/values-failure.d.ts +17 -0
  190. package/dist/values-failure.js +1 -0
  191. package/dist/values-merge.d.ts +39 -0
  192. package/dist/values-merge.js +33 -0
  193. 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>;