@markii/host 0.0.0-stage → 0.17.0-next.2
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/LICENSE +21 -0
- package/README.md +19 -2
- package/dist/browser-worker.js +639 -0
- package/dist/browser.d.ts +57 -0
- package/dist/browser.js +167 -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 +223 -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 +156 -0
- package/dist/host/conformance-runner.js +634 -0
- package/dist/host/create-host.d.ts +253 -0
- package/dist/host/create-host.js +329 -0
- package/dist/host/editor-behavior.d.ts +157 -0
- package/dist/host/editor-behavior.js +227 -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 +113 -0
- package/dist/host/export-behavior.js +257 -0
- package/dist/host/labels.d.ts +24 -0
- package/dist/host/labels.js +32 -0
- package/dist/host/pack-archive.d.ts +30 -0
- package/dist/host/pack-archive.js +68 -0
- package/dist/host/pack-install.d.ts +88 -0
- package/dist/host/pack-install.js +189 -0
- package/dist/host/pack-load.d.ts +72 -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 +39 -0
- package/dist/host/refresh-interval.js +61 -0
- package/dist/host/run-behavior.d.ts +89 -0
- package/dist/host/run-behavior.js +168 -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 +70 -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.d.ts +5 -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 +81 -0
- package/dist/packs/pack-diagnostics.js +105 -0
- package/dist/packs/pack-paths.d.ts +53 -0
- package/dist/packs/pack-paths.js +106 -0
- package/dist/packs/pack-render-registry.d.ts +83 -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 +81 -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 +57 -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 +70 -0
- package/dist/run/net-provider.js +289 -0
- package/dist/run/node-isolate.d.ts +30 -0
- package/dist/run/node-isolate.js +117 -0
- package/dist/run/run-flow.d.ts +187 -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 +42 -0
- package/dist/run/worker-entry-browser.js +77 -0
- package/dist/run/worker-entry.d.ts +1 -0
- package/dist/run/worker-entry.js +96 -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 +84 -4
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { NetProvider } from '@markii/lua';
|
|
2
|
+
import type { HostIsolate } from './host/adapter.js';
|
|
3
|
+
/** Builds the provider for one run from the job's host allowlist, byte cap and pin policy (the policy is meaningless in a browser and ignored). */
|
|
4
|
+
export type BrowserNetProviderFactory = (netAllowlist: string[], maxFetchBytes: number, netPolicy: unknown) => NetProvider;
|
|
5
|
+
export interface FetchNetProviderOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The origins (`https://api.example.com`, `http://127.0.0.1:8080`) a
|
|
8
|
+
* script may reach. Anything else is denied. Missing or empty denies
|
|
9
|
+
* every request. Entries that do not parse as an http(s) URL are ignored.
|
|
10
|
+
*/
|
|
11
|
+
allowedOrigins?: readonly string[];
|
|
12
|
+
/** How a policy refusal is built. Defaults to a plain `Error`; the isolate bridge marks every refusal as a denial on the wire either way. */
|
|
13
|
+
denial?: (message: string) => Error;
|
|
14
|
+
/** The `fetch` to use. Defaults to the global one; injectable for tests. */
|
|
15
|
+
fetch?: typeof fetch;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The page-side half of the network bridge for a browser: a request goes
|
|
19
|
+
* out with `fetch` only when its origin is in `allowedOrigins`. Everything
|
|
20
|
+
* else is refused with a denial before any request is made.
|
|
21
|
+
*
|
|
22
|
+
* The grant flow and the tier gate run in front of this (they are shared
|
|
23
|
+
* with every host), so a script still needs the user's grant for the host
|
|
24
|
+
* it names. This allowlist is the second, host-owned boundary.
|
|
25
|
+
*
|
|
26
|
+
* What a browser cannot do, stated plainly: there is NO address pinning
|
|
27
|
+
* here. A page cannot choose which IP a hostname connects to, so the
|
|
28
|
+
* DNS-rebinding and private-range protection the Node provider adds
|
|
29
|
+
* (docs/security.md) does not exist in this provider. The browser's own
|
|
30
|
+
* rules apply instead: CORS decides whether a response is readable, and
|
|
31
|
+
* the same-origin policy stops credentials leaving (requests are sent with
|
|
32
|
+
* `credentials: 'omit'`). Redirects are not followed: the browser hides a
|
|
33
|
+
* manual redirect's target, so it cannot be checked against the allowlist,
|
|
34
|
+
* and a redirect is therefore a denial.
|
|
35
|
+
*
|
|
36
|
+
* Returns a factory because the byte cap arrives per job.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createFetchNetProvider(options?: FetchNetProviderOptions): BrowserNetProviderFactory;
|
|
39
|
+
export interface BrowserHostIsolateOptions {
|
|
40
|
+
/** Where the module worker lives. Defaults to `browser-worker.js` beside this file. A host that embeds the bytes (a blob URL) passes its own. */
|
|
41
|
+
workerUrl?: string | URL;
|
|
42
|
+
/** Where wasmoon's `glue.wasm` lives. Defaults to `glue.wasm` beside this file. Sent on every job, so the Lua engine never falls back to a CDN. */
|
|
43
|
+
wasmUrl?: string | URL;
|
|
44
|
+
/** How the worker script is loaded. Defaults to `module`, which the packaged `browser-worker.js` is. A host that embeds a classic (IIFE) bundle behind a blob URL passes `classic`. */
|
|
45
|
+
workerType?: 'module' | 'classic';
|
|
46
|
+
/** Serves `net.*` for each run. Defaults to a provider with an empty allowlist, which denies every request. */
|
|
47
|
+
netProvider?: BrowserNetProviderFactory;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A `HostIsolate` of kind `browser`: each run starts a MODULE Web Worker
|
|
51
|
+
* and the job carries the `glue.wasm` URL. A Web Worker takes no heap cap,
|
|
52
|
+
* so a runaway allocation inside it is bounded only by the browser; Lua's
|
|
53
|
+
* own limits and the external watchdog still apply (docs/security.md).
|
|
54
|
+
*/
|
|
55
|
+
export declare function createBrowserHostIsolate(options?: BrowserHostIsolateOptions): Extract<HostIsolate, {
|
|
56
|
+
kind: 'browser';
|
|
57
|
+
}>;
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// @markii/host/browser: the browser PLATFORM entry, the twin of
|
|
2
|
+
// `@markii/host/node`.
|
|
3
|
+
//
|
|
4
|
+
// The main entry (`./index.ts`) is environment-neutral. This entry adds the
|
|
5
|
+
// two things a page needs to run scripts:
|
|
6
|
+
// - a Web Worker isolate that starts the packaged module worker and hands it
|
|
7
|
+
// the packaged `glue.wasm` URL on every job;
|
|
8
|
+
// - a `fetch`-backed network provider that serves `net.*` calls on the
|
|
9
|
+
// worker's behalf, against an explicit allowlist of ORIGINS.
|
|
10
|
+
//
|
|
11
|
+
// Nothing reachable from here imports `node:*` (executed probe:
|
|
12
|
+
// `apps/vscode/src/browser-entry.probe.test.ts`). In the published package
|
|
13
|
+
// `browser-worker.js` and `glue.wasm` sit next to `browser.js`, so the
|
|
14
|
+
// `new URL(..., import.meta.url)` references below resolve in a consumer's
|
|
15
|
+
// bundler without configuration where that bundler treats them as assets.
|
|
16
|
+
import { createBrowserIsolate } from './run/browser-isolate.js';
|
|
17
|
+
function originOf(entry) {
|
|
18
|
+
try {
|
|
19
|
+
const url = new URL(entry);
|
|
20
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
21
|
+
return undefined;
|
|
22
|
+
return url.origin;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function readCapped(response, maxBytes, denial) {
|
|
29
|
+
const declared = Number(response.headers.get('content-length'));
|
|
30
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
31
|
+
void response.body?.cancel();
|
|
32
|
+
throw denial(`response declares ${declared} bytes, exceeding the ${maxBytes}-byte cap`);
|
|
33
|
+
}
|
|
34
|
+
if (!response.body)
|
|
35
|
+
return '';
|
|
36
|
+
const reader = response.body.getReader();
|
|
37
|
+
const chunks = [];
|
|
38
|
+
let total = 0;
|
|
39
|
+
for (;;) {
|
|
40
|
+
const { done, value } = await reader.read();
|
|
41
|
+
if (done)
|
|
42
|
+
break;
|
|
43
|
+
total += value.byteLength;
|
|
44
|
+
if (total > maxBytes) {
|
|
45
|
+
void reader.cancel();
|
|
46
|
+
throw denial(`response exceeds the ${maxBytes}-byte cap`);
|
|
47
|
+
}
|
|
48
|
+
chunks.push(value);
|
|
49
|
+
}
|
|
50
|
+
const bytes = new Uint8Array(total);
|
|
51
|
+
let offset = 0;
|
|
52
|
+
for (const chunk of chunks) {
|
|
53
|
+
bytes.set(chunk, offset);
|
|
54
|
+
offset += chunk.byteLength;
|
|
55
|
+
}
|
|
56
|
+
return new TextDecoder().decode(bytes);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The page-side half of the network bridge for a browser: a request goes
|
|
60
|
+
* out with `fetch` only when its origin is in `allowedOrigins`. Everything
|
|
61
|
+
* else is refused with a denial before any request is made.
|
|
62
|
+
*
|
|
63
|
+
* The grant flow and the tier gate run in front of this (they are shared
|
|
64
|
+
* with every host), so a script still needs the user's grant for the host
|
|
65
|
+
* it names. This allowlist is the second, host-owned boundary.
|
|
66
|
+
*
|
|
67
|
+
* What a browser cannot do, stated plainly: there is NO address pinning
|
|
68
|
+
* here. A page cannot choose which IP a hostname connects to, so the
|
|
69
|
+
* DNS-rebinding and private-range protection the Node provider adds
|
|
70
|
+
* (docs/security.md) does not exist in this provider. The browser's own
|
|
71
|
+
* rules apply instead: CORS decides whether a response is readable, and
|
|
72
|
+
* the same-origin policy stops credentials leaving (requests are sent with
|
|
73
|
+
* `credentials: 'omit'`). Redirects are not followed: the browser hides a
|
|
74
|
+
* manual redirect's target, so it cannot be checked against the allowlist,
|
|
75
|
+
* and a redirect is therefore a denial.
|
|
76
|
+
*
|
|
77
|
+
* Returns a factory because the byte cap arrives per job.
|
|
78
|
+
*/
|
|
79
|
+
export function createFetchNetProvider(options = {}) {
|
|
80
|
+
const allowed = new Set((options.allowedOrigins ?? [])
|
|
81
|
+
.map(originOf)
|
|
82
|
+
.filter((origin) => origin !== undefined));
|
|
83
|
+
const denial = options.denial ?? ((message) => new Error(message));
|
|
84
|
+
return (netAllowlist, maxFetchBytes) => {
|
|
85
|
+
// The run's granted hosts, re-checked here on the page side: the worker
|
|
86
|
+
// checks them too, but the worker is the isolate a hostile script runs
|
|
87
|
+
// in, so the page is the boundary (the same rule the Node provider
|
|
88
|
+
// follows). A request must pass BOTH this and the origin allowlist.
|
|
89
|
+
const granted = new Set(netAllowlist.map((host) => host.toLowerCase()));
|
|
90
|
+
async function request(rawUrl, method, body) {
|
|
91
|
+
let url;
|
|
92
|
+
try {
|
|
93
|
+
url = new URL(rawUrl);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw denial(`"${rawUrl}" is not a valid URL`);
|
|
97
|
+
}
|
|
98
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
99
|
+
throw denial(`scheme "${url.protocol}" is not allowed`);
|
|
100
|
+
}
|
|
101
|
+
if (url.username !== '' || url.password !== '') {
|
|
102
|
+
throw denial(`URL "${rawUrl}" embeds credentials, which is not allowed`);
|
|
103
|
+
}
|
|
104
|
+
if (!granted.has(url.hostname.toLowerCase())) {
|
|
105
|
+
throw denial(`host "${url.hostname}" is not on the run's allowlist`);
|
|
106
|
+
}
|
|
107
|
+
if (!allowed.has(url.origin)) {
|
|
108
|
+
throw denial(`origin "${url.origin}" is not on the host's allowlist`);
|
|
109
|
+
}
|
|
110
|
+
const doFetch = options.fetch ?? fetch;
|
|
111
|
+
let response;
|
|
112
|
+
try {
|
|
113
|
+
response = await doFetch(url.href, {
|
|
114
|
+
method,
|
|
115
|
+
...(body !== undefined
|
|
116
|
+
? { body, headers: { 'content-type': 'text/plain;charset=UTF-8' } }
|
|
117
|
+
: {}),
|
|
118
|
+
redirect: 'manual',
|
|
119
|
+
credentials: 'omit',
|
|
120
|
+
referrerPolicy: 'no-referrer',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw denial(`request failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
125
|
+
}
|
|
126
|
+
if (response.type === 'opaqueredirect') {
|
|
127
|
+
throw denial('redirects are not followed in a browser');
|
|
128
|
+
}
|
|
129
|
+
if (response.status >= 300 && response.status < 400) {
|
|
130
|
+
throw denial('redirects are not followed in a browser');
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
status: response.status,
|
|
134
|
+
body: await readCapped(response, maxFetchBytes, denial),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
get: (url) => request(url, 'GET', undefined),
|
|
139
|
+
post: (url, requestBody) => request(url, 'POST', requestBody),
|
|
140
|
+
patch: (url, requestBody) => request(url, 'PATCH', requestBody),
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A `HostIsolate` of kind `browser`: each run starts a MODULE Web Worker
|
|
146
|
+
* and the job carries the `glue.wasm` URL. A Web Worker takes no heap cap,
|
|
147
|
+
* so a runaway allocation inside it is bounded only by the browser; Lua's
|
|
148
|
+
* own limits and the external watchdog still apply (docs/security.md).
|
|
149
|
+
*/
|
|
150
|
+
export function createBrowserHostIsolate(options = {}) {
|
|
151
|
+
const netProvider = options.netProvider ?? createFetchNetProvider({ allowedOrigins: [] });
|
|
152
|
+
return {
|
|
153
|
+
kind: 'browser',
|
|
154
|
+
spawner: async () => {
|
|
155
|
+
const workerUrl = options.workerUrl ?? new URL('./browser-worker.js', import.meta.url);
|
|
156
|
+
const wasmUrl = options.wasmUrl ?? new URL('./glue.wasm', import.meta.url);
|
|
157
|
+
return createBrowserIsolate({
|
|
158
|
+
createWorker: () => options.workerType === 'classic'
|
|
159
|
+
? new Worker(workerUrl)
|
|
160
|
+
: new Worker(workerUrl, { type: 'module' }),
|
|
161
|
+
netProvider,
|
|
162
|
+
wasmUri: String(wasmUrl),
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
dispose: () => { },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"outcomes": [
|
|
3
|
+
{ "kind": "opened" },
|
|
4
|
+
{
|
|
5
|
+
"kind": "ran",
|
|
6
|
+
"values": { "total": 3 },
|
|
7
|
+
"failureKinds": [],
|
|
8
|
+
"traceOk": true
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"diagnostics": [
|
|
12
|
+
"manual run: net declaration mismatch",
|
|
13
|
+
"uses net access to api.example.com"
|
|
14
|
+
],
|
|
15
|
+
"prompts": ["This note's scripts can send data to api.example.com. Allow?"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "run-manual-grant-accepted",
|
|
3
|
+
"invariant": "a manual run prompts once, worded with the canonical wording, and a granted host's value lands",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{ "type": "open" },
|
|
7
|
+
{
|
|
8
|
+
"type": "run",
|
|
9
|
+
"trigger": "manual",
|
|
10
|
+
"answers": ["allow"],
|
|
11
|
+
"simulate": { "name": "total", "value": 3, "requiresHost": "api.example.com" }
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"outcomes": [
|
|
3
|
+
{ "kind": "opened" },
|
|
4
|
+
{
|
|
5
|
+
"kind": "ran",
|
|
6
|
+
"values": {},
|
|
7
|
+
"failureKinds": ["capability-denied"],
|
|
8
|
+
"traceOk": false
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"diagnostics": [
|
|
12
|
+
"manual run finished with 1 script failure(s)",
|
|
13
|
+
"total (capability-denied)",
|
|
14
|
+
"manual run: net declaration mismatch",
|
|
15
|
+
"uses net access to api.example.com"
|
|
16
|
+
],
|
|
17
|
+
"prompts": ["This note's scripts can send data to api.example.com. Allow?"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "run-manual-grant-refused",
|
|
3
|
+
"invariant": "the same prompt, refused, leaves the host reaching nothing and reports the failure on both surfaces",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{ "type": "open" },
|
|
7
|
+
{
|
|
8
|
+
"type": "run",
|
|
9
|
+
"trigger": "manual",
|
|
10
|
+
"answers": ["deny"],
|
|
11
|
+
"simulate": { "name": "total", "value": 3, "requiresHost": "api.example.com" }
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"outcomes": [
|
|
3
|
+
{ "kind": "opened" },
|
|
4
|
+
{
|
|
5
|
+
"kind": "ran",
|
|
6
|
+
"values": {},
|
|
7
|
+
"failureKinds": ["tier-blocked"],
|
|
8
|
+
"traceOk": false
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"diagnostics": [
|
|
12
|
+
"auto run finished with 1 script failure(s)",
|
|
13
|
+
"counter (tier-blocked)"
|
|
14
|
+
],
|
|
15
|
+
"prompts": []
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Auto trigger, a write attempt
|
|
2
|
+
|
|
3
|
+
Runs on open, on a timer, or manually. This script only ever attempts an
|
|
4
|
+
effect (a write), never a network call, so the tier gate is the only
|
|
5
|
+
thing that can deny it.
|
|
6
|
+
|
|
7
|
+
```lua {name=counter}
|
|
8
|
+
store.set("counter", 1)
|
|
9
|
+
return store.get("counter")
|
|
10
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "run-auto-write-denied",
|
|
3
|
+
"invariant": "an auto trigger is read-only: a write attempt is denied by the tier gate, not by luck, and never prompts",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{ "type": "open" },
|
|
7
|
+
{
|
|
8
|
+
"type": "run",
|
|
9
|
+
"trigger": "auto",
|
|
10
|
+
"simulate": { "name": "counter", "requiresManualTier": true }
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"outcomes": [
|
|
3
|
+
{
|
|
4
|
+
"kind": "exported",
|
|
5
|
+
"contains": ["mk-unknown", "ana_timeline"],
|
|
6
|
+
"excludes": ["<script"]
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"diagnostics": [
|
|
10
|
+
"Export wrote",
|
|
11
|
+
"Rendered with the static engine because no pack components are loaded"
|
|
12
|
+
],
|
|
13
|
+
"prompts": []
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"outcomes": [
|
|
3
|
+
{ "kind": "opened" },
|
|
4
|
+
{ "kind": "installed", "namespace": "ana", "replaced": false },
|
|
5
|
+
{ "kind": "loaded", "namespaces": ["ana"] },
|
|
6
|
+
{ "kind": "installed", "namespace": "ana", "replaced": true }
|
|
7
|
+
],
|
|
8
|
+
"diagnostics": [
|
|
9
|
+
"Install pack from file installed pack \"ana\" from \"files/ana-1.0.0\"",
|
|
10
|
+
"Loaded pack \"ana\" (namespace: ana, 1 component)",
|
|
11
|
+
"replacing the previous install"
|
|
12
|
+
],
|
|
13
|
+
"prompts": [
|
|
14
|
+
"Installing \"ana\" lets its code run inside",
|
|
15
|
+
"Installing \"ana\" lets its code run inside",
|
|
16
|
+
"A pack named \"ana\" is already installed. Replacing it removes the installed copy and cannot be undone."
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
window.__markiiRegisterPack(() => ({}));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "install-pack-then-render",
|
|
3
|
+
"invariant": "installing a .mkp loads its namespace; a second install of the same namespace asks before replacing",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{ "type": "open" },
|
|
7
|
+
{ "type": "installPack", "archive": "files/ana-1.0.0", "answers": ["allow"] },
|
|
8
|
+
{ "type": "loadPacks" },
|
|
9
|
+
{
|
|
10
|
+
"type": "installPack",
|
|
11
|
+
"archive": "files/ana-1.0.0",
|
|
12
|
+
"answers": ["allow", "allow"]
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "scripts-disabled",
|
|
3
|
+
"invariant": "the device switch blocks the run before the isolate, and says so on both surfaces",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{ "type": "open" },
|
|
7
|
+
{ "type": "run", "trigger": "manual", "scriptsDisabled": true }
|
|
8
|
+
]
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "complete-closing-fence",
|
|
3
|
+
"invariant": "a bare colon run that closes an open container offers no completions, and says so without failing",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{
|
|
7
|
+
"type": "completeAt",
|
|
8
|
+
"line": 4,
|
|
9
|
+
"column": 3
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "complete-nested-container",
|
|
3
|
+
"invariant": "completion of a container inside an equal-length container carries the fence edits that keep the pair legal",
|
|
4
|
+
"clock": 1700000000000,
|
|
5
|
+
"actions": [
|
|
6
|
+
{
|
|
7
|
+
"type": "completeAt",
|
|
8
|
+
"line": 3,
|
|
9
|
+
"column": 6
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"outcomes": [
|
|
3
|
+
{ "kind": "opened" },
|
|
4
|
+
{ "kind": "declined", "step": "consent" },
|
|
5
|
+
{ "kind": "not-installed", "namespace": "ana" }
|
|
6
|
+
],
|
|
7
|
+
"diagnostics": [
|
|
8
|
+
"Install pack from file cancelled for",
|
|
9
|
+
"Remove pack found no installed pack \"ana\""
|
|
10
|
+
],
|
|
11
|
+
"prompts": ["Installing \"ana\" lets its code run inside"]
|
|
12
|
+
}
|