@markii/host 0.17.0-next.2 → 0.17.0-next.4

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 (67) hide show
  1. package/README.md +10 -2
  2. package/dist/browser-worker.classic.js +640 -0
  3. package/dist/browser-worker.js +40 -40
  4. package/dist/browser.d.ts +7 -3
  5. package/dist/browser.js +16 -4
  6. package/dist/conformance/host/05-export-html-pack-directive/expected.json +2 -0
  7. package/dist/conformance/host/06-install-pack-then-render/expected.json +6 -1
  8. package/dist/conformance/host/README.md +4 -2
  9. package/dist/export/note-export.d.ts +1 -1
  10. package/dist/host/adapter.d.ts +22 -25
  11. package/dist/host/conformance-runner.d.ts +1 -1
  12. package/dist/host/conformance-runner.js +21 -11
  13. package/dist/host/create-host.d.ts +29 -11
  14. package/dist/host/create-host.js +34 -23
  15. package/dist/host/editor-behavior.d.ts +2 -6
  16. package/dist/host/editor-behavior.js +5 -9
  17. package/dist/host/export-behavior.d.ts +6 -7
  18. package/dist/host/export-behavior.js +1 -1
  19. package/dist/host/labels.d.ts +5 -15
  20. package/dist/host/labels.js +8 -22
  21. package/dist/host/pack-archive.d.ts +4 -10
  22. package/dist/host/pack-archive.js +12 -21
  23. package/dist/host/pack-install.d.ts +11 -23
  24. package/dist/host/pack-install.js +26 -23
  25. package/dist/host/pack-load.d.ts +1 -2
  26. package/dist/host/pack-trust-behavior.js +1 -1
  27. package/dist/host/refresh-interval.d.ts +1 -3
  28. package/dist/host/refresh-interval.js +1 -3
  29. package/dist/host/run-behavior.d.ts +3 -4
  30. package/dist/host/run-behavior.js +23 -2
  31. package/dist/host/script-execution.d.ts +1 -1
  32. package/dist/html-engine.js +4 -4
  33. package/dist/index.d.ts +15 -80
  34. package/dist/index.js +18 -63
  35. package/dist/internal.d.ts +30 -0
  36. package/dist/internal.js +31 -0
  37. package/dist/node-fs.d.ts +0 -3
  38. package/dist/node-fs.js +1 -21
  39. package/dist/node-worker.d.ts +8 -0
  40. package/dist/node-worker.js +20678 -0
  41. package/dist/node.d.ts +3 -3
  42. package/dist/node.js +5 -4
  43. package/dist/packs/discover.d.ts +2 -0
  44. package/dist/packs/discover.js +2 -1
  45. package/dist/packs/pack-diagnostics.d.ts +6 -8
  46. package/dist/packs/pack-diagnostics.js +6 -8
  47. package/dist/packs/pack-paths.js +1 -2
  48. package/dist/packs/pack-render-registry.d.ts +9 -10
  49. package/dist/run/browser-isolate.d.ts +5 -5
  50. package/dist/run/browser-isolate.js +5 -1
  51. package/dist/run/isolate.d.ts +3 -1
  52. package/dist/run/net-bridge.d.ts +5 -5
  53. package/dist/run/net-bridge.js +7 -5
  54. package/dist/run/net-pinning.d.ts +2 -0
  55. package/dist/run/net-pinning.js +17 -3
  56. package/dist/run/net-provider.d.ts +3 -1
  57. package/dist/run/net-provider.js +40 -15
  58. package/dist/run/net-refusal.d.ts +4 -0
  59. package/dist/run/net-refusal.js +24 -0
  60. package/dist/run/node-isolate.d.ts +13 -21
  61. package/dist/run/node-isolate.js +31 -80
  62. package/dist/run/run-flow.d.ts +1 -2
  63. package/dist/run/run-host.d.ts +3 -3
  64. package/dist/run/worker-entry-browser.d.ts +24 -4
  65. package/dist/run/worker-entry-browser.js +99 -4
  66. package/dist/run/worker-entry.js +2 -0
  67. package/package.json +31 -12
@@ -1,81 +1,23 @@
1
1
  /**
2
2
  * The Node half of the Run path's isolate seam: a `node:worker_threads`
3
- * worker, plus the dev/Vitest worker-entry fallback. Split out of
4
- * `./run-host.ts` so the watchdog, the exactly-once settlement and the
5
- * never-rejects contract stay in a module with no `node:*` import and run
6
- * unchanged for every host kind. Reached only through `@markii/host/node`.
3
+ * worker. Split out of `./run-host.ts` so the watchdog, the exactly-once
4
+ * settlement and the never-rejects contract stay in a module with no
5
+ * `node:*` import and run unchanged for every host kind. Reached only
6
+ * through `@markii/host/node`.
7
7
  */
8
8
  import { Worker } from 'node:worker_threads';
9
9
  import { existsSync } from 'node:fs';
10
- import * as path from 'node:path';
11
- import { spawnRun } from './run-host.js';
12
- /** `execArgv` needed to run a `.ts` worker entry directly (dev/Vitest only) — see `./worker-entry.ts`'s doc comment on why a plain `node` `.ts` import can't resolve this repo's `@markii/*` bare specifiers or its `./foo.js` -> `./foo.ts` import convention on its own. `--require tsx/cjs` (rather than `tsx/esm`) is deliberate: empirically, the ESM loader hook deadlocks Node's CJS/ESM interop translator for a worker thread requiring `.js`-suffixed relative imports that resolve to `.ts` files (this repo's convention throughout `@markii/*`), while the CJS `require` hook has no such issue. */
13
- const TSX_DEV_EXEC_ARGV = ['--require', 'tsx/cjs'];
14
- /**
15
- * The ONLY fallback this shared package supplies for an unset `workerPath`:
16
- * the sibling `worker-entry.ts`, run straight from source via `tsx`
17
- * (`TSX_DEV_EXEC_ARGV`, above). That is correct for this package's own
18
- * dev/Vitest runs — `__dirname` at that point is this real source
19
- * directory — but it says nothing about where a packaged host's bundled
20
- * worker lives, because this package does not know that host's `dist/`
21
- * layout. A packaged host (e.g. `apps/vscode`) MUST resolve its own bundled
22
- * worker path itself and pass it as `SpawnRunOptions.workerPath`; see
23
- * `apps/vscode/src/worker-path.ts` for that host's resolution and
24
- * `apps/vscode/esbuild.config.mjs`'s worker build for where it bundles to.
25
- */
26
- export function defaultWorkerPath() {
27
- // The built package is ESM, where `__dirname` does not exist: a packaged
28
- // host always passes its own bundled worker, so say so instead of
29
- // throwing a ReferenceError.
30
- const devSource = typeof __dirname === 'undefined'
31
- ? undefined
32
- : path.join(__dirname, 'worker-entry.ts');
33
- if (devSource !== undefined && existsSync(devSource))
34
- return devSource;
35
- throw new Error('spawnRun: could not resolve the worker entry file (looked for ' +
36
- `${devSource ?? 'worker-entry.ts beside the package source'}) — pass workerPath explicitly`);
37
- }
38
- function execArgvFor(workerPath) {
39
- return workerPath.endsWith('.ts') ? TSX_DEV_EXEC_ARGV : undefined;
40
- }
41
- /**
42
- * Dev/Vitest only: point `tsx` at the repo's `tsconfig.dev.json` so a
43
- * `.ts` worker entry resolves `@markii/*` to each package's SOURCE.
44
- *
45
- * Without this the worker is a plain Node resolution: a bare `@markii/lua`
46
- * goes through `node_modules` to that package's `exports`, which name the
47
- * built `dist/`. `npm run build` only type-checks (`tsc --noEmit`); the
48
- * emitting script is `build:dist`, which runs at release. So `dist/` is
49
- * whatever a past release left behind, and a real-isolate probe would
50
- * silently exercise that stale code instead of the source it is written to
51
- * test. A capability added since the last `build:dist` simply would not
52
- * exist inside the worker.
53
- *
54
- * Gated on a `.ts` entry, the same signal `execArgvFor` already uses for
55
- * the tsx hook, so a packaged host passing its own bundled `.js` worker is
56
- * never touched by this.
57
- */
58
- function devTsconfigEnv(entryPath) {
59
- if (!entryPath.endsWith('.ts') || typeof __dirname === 'undefined') {
60
- return undefined;
61
- }
62
- const tsconfig = path.resolve(__dirname, '../../../..', 'tsconfig.dev.json');
63
- if (!existsSync(tsconfig))
64
- return undefined;
65
- return { env: { ...process.env, TSX_TSCONFIG_PATH: tsconfig } };
66
- }
10
+ import { fileURLToPath } from 'node:url';
67
11
  /**
68
12
  * The default isolate: a `node:worker_threads` worker, which is what every
69
- * Node host (the VS Code extension host, this package's own tests) uses. It
70
- * is the only kind that accepts `resourceLimits`, so it is also the only
13
+ * Node host uses. It is the only kind that accepts `resourceLimits`, so it is also the only
71
14
  * kind whose V8 heap is bounded — see `./isolate.ts` for why a host might
72
15
  * still have to supply the other one.
73
16
  */
74
17
  export const workerThreadIsolate = (options) => {
75
- const execArgv = options.execArgv ?? execArgvFor(options.entryPath);
76
18
  const worker = new Worker(options.entryPath, {
77
- ...(execArgv ? { execArgv } : {}),
78
- ...(devTsconfigEnv(options.entryPath) ?? {}),
19
+ ...(options.execArgv ? { execArgv: options.execArgv } : {}),
20
+ ...(options.env ? { env: options.env } : {}),
79
21
  resourceLimits: {
80
22
  maxOldGenerationSizeMb: options.maxOldGenerationSizeMb,
81
23
  },
@@ -88,11 +30,11 @@ export const workerThreadIsolate = (options) => {
88
30
  void worker.terminate();
89
31
  },
90
32
  onMessage: (listener) => {
91
- // `on`, not `once`: a run now sends one progress message per script
92
- // (GitHub issue #35) ahead of its single result message, and
93
- // `spawnRun` is what tells the two apart. Messages arriving after
94
- // the run settles are harmless — `settled` guards every path — and
95
- // the worker is terminated on settle anyway.
33
+ // `on`, not `once`: a run sends one progress message per script
34
+ // ahead of its single result message, and `spawnRun` tells the two
35
+ // apart. Messages arriving after the run settles are harmless
36
+ // (`settled` guards every path) and the worker is terminated on
37
+ // settle anyway.
96
38
  worker.on('message', listener);
97
39
  },
98
40
  onError: (listener) => {
@@ -104,14 +46,23 @@ export const workerThreadIsolate = (options) => {
104
46
  };
105
47
  };
106
48
  /**
107
- * `spawnRun` with the Node defaults filled in: the worker-thread isolate and
108
- * the dev/Vitest worker entry. For this package's own tests and any Node
109
- * caller that wants them; a packaged host passes its own `workerPath`.
49
+ * A `HostIsolate` of kind `node`: each run starts a `worker_threads` worker
50
+ * from the packaged, self-contained `node-worker.js`. A host that bundles
51
+ * the worker itself passes its own `workerPath`. The default is resolved
52
+ * when a run asks for it, so constructing the isolate never throws.
110
53
  */
111
- export function spawnRunInThread(options) {
112
- return spawnRun({
113
- ...options,
114
- workerPath: options.workerPath ?? defaultWorkerPath(),
115
- spawnIsolate: options.spawnIsolate ?? workerThreadIsolate,
116
- });
54
+ export function createNodeHostIsolate(options = {}) {
55
+ return {
56
+ kind: 'node',
57
+ workerPath: () => {
58
+ if (options.workerPath !== undefined)
59
+ return options.workerPath;
60
+ const packaged = fileURLToPath(new URL('../node-worker.js', import.meta.url));
61
+ if (!existsSync(packaged)) {
62
+ throw new Error(`the packaged worker was not found at ${packaged}: pass workerPath`);
63
+ }
64
+ return packaged;
65
+ },
66
+ spawnIsolate: workerThreadIsolate,
67
+ };
117
68
  }
@@ -163,8 +163,7 @@ export interface RunOnceResult {
163
163
  /**
164
164
  * Consent unification (SECURITY-RELEVANT): lines describing any mismatch
165
165
  * between what is DECLARED (a bundle manifest's `permissions.net`, and/or
166
- * an individual script's own `permissions` fence attribute — batch 7
167
- * #47) and the hosts this run's scripts actually reach, from
166
+ * an individual script's own `permissions` fence attribute) and the hosts this run's scripts actually reach, from
168
167
  * `./bundle-run.ts`'s `netDeclarationDiagnostics`. `[]` when there is
169
168
  * nothing to compare against (no bundle manifest and no script declares
170
169
  * `permissions`) or every declared host agrees with the scan. For the
@@ -85,9 +85,9 @@ export interface SpawnRunOptions {
85
85
  /**
86
86
  * The entry the isolate loads. This package cannot know how a given host
87
87
  * bundles or lays out its own `dist/` (see e.g.
88
- * `apps/vscode/src/worker-path.ts`), so the host always passes it. The
89
- * Node-only dev/Vitest fallback lives in `@markii/host/node`
90
- * (`defaultWorkerPath`).
88
+ * `apps/vscode/src/worker-path.ts`), so the host always passes it.
89
+ * `createNodeHostIsolate` from `@markii/host/node` supplies the packaged
90
+ * worker's path.
91
91
  */
92
92
  workerPath: string;
93
93
  }
@@ -10,10 +10,11 @@
10
10
  * the failure classification cannot drift between hosts. This file supplies
11
11
  * only the two things a Web Worker has to do differently:
12
12
  *
13
- * 1. Network goes through `./net-bridge.ts` to the host, which still has
14
- * Node and runs the same pinned provider as before. The isolate itself
15
- * has no network stack, which makes the allowlist unbypassable from
16
- * inside it.
13
+ * 1. Network goes through `./net-bridge.ts` to the host, which runs the
14
+ * same pinned provider as before. A Web Worker does have network
15
+ * globals, so `lockdownWorkerGlobals` (below) removes them before any
16
+ * script runs. Lua cannot reach JavaScript at all; the removal is a
17
+ * second wall for the case where an interpreter escape does.
17
18
  * 2. wasmoon's `glue.wasm` arrives as a URL on the job, because there is
18
19
  * no `__dirname` here to find it beside the bundle.
19
20
  *
@@ -29,6 +30,25 @@ interface WorkerGlobal {
29
30
  data: unknown;
30
31
  }) => void) => void;
31
32
  }
33
+ /**
34
+ * Removes the worker's own ways to reach a network, once, before the first
35
+ * script runs.
36
+ *
37
+ * - Every name in `REMOVED_GLOBALS` (the connection and storage
38
+ * constructors, and `Worker`, which would start an unlocked scope) is
39
+ * shadowed with `undefined`.
40
+ * - `importScripts` becomes a function that throws. It stays a function
41
+ * because the Lua engine's start-up detects a worker by
42
+ * `typeof importScripts`.
43
+ * - `fetch` keeps working for exactly one URL, the `glue.wasm` the job
44
+ * named, because the engine loads its WebAssembly through it for every
45
+ * script. Any other URL is refused.
46
+ *
47
+ * Net traffic is unaffected: it goes to the host with `postMessage`.
48
+ * Idempotent. Covered by `worker-entry-browser.test.ts` and, in a real
49
+ * browser, by the e2e suite.
50
+ */
51
+ export declare function lockdownWorkerGlobals(scope: object, wasmUri: string | undefined): void;
32
52
  /**
33
53
  * A job for this entry additionally carries where `glue.wasm` can be
34
54
  * fetched from — a blob URL the host minted from the file on disk. Absent,
@@ -10,10 +10,11 @@
10
10
  * the failure classification cannot drift between hosts. This file supplies
11
11
  * only the two things a Web Worker has to do differently:
12
12
  *
13
- * 1. Network goes through `./net-bridge.ts` to the host, which still has
14
- * Node and runs the same pinned provider as before. The isolate itself
15
- * has no network stack, which makes the allowlist unbypassable from
16
- * inside it.
13
+ * 1. Network goes through `./net-bridge.ts` to the host, which runs the
14
+ * same pinned provider as before. A Web Worker does have network
15
+ * globals, so `lockdownWorkerGlobals` (below) removes them before any
16
+ * script runs. Lua cannot reach JavaScript at all; the removal is a
17
+ * second wall for the case where an interpreter escape does.
17
18
  * 2. wasmoon's `glue.wasm` arrives as a URL on the job, because there is
18
19
  * no `__dirname` here to find it beside the bundle.
19
20
  *
@@ -23,6 +24,99 @@
23
24
  */
24
25
  import { isRunJob, resultForInternalError, runJob, } from './run-job.js';
25
26
  import { createNetBridge } from './net-bridge-worker.js';
27
+ /**
28
+ * Globals through which a worker can open a connection or keep state outside
29
+ * the run. `fetch` and `importScripts` are handled separately because the
30
+ * Lua engine's start-up needs them (see `lockdownWorkerGlobals`).
31
+ */
32
+ const REMOVED_GLOBALS = [
33
+ 'XMLHttpRequest',
34
+ 'WebSocket',
35
+ 'EventSource',
36
+ 'indexedDB',
37
+ 'caches',
38
+ 'BroadcastChannel',
39
+ // A nested worker would start with a fresh, unlocked global scope.
40
+ 'Worker',
41
+ 'SharedWorker',
42
+ 'WebTransport',
43
+ 'WebSocketStream',
44
+ ];
45
+ /** Reads the URL out of a `fetch` argument without assuming DOM types. */
46
+ function requestUrl(input, base) {
47
+ try {
48
+ if (typeof input === 'string')
49
+ return new URL(input, base).href;
50
+ if (input instanceof URL)
51
+ return input.href;
52
+ const url = input?.url;
53
+ if (typeof url === 'string')
54
+ return new URL(url, base).href;
55
+ }
56
+ catch {
57
+ // an unparseable URL is not the allowed one
58
+ }
59
+ return undefined;
60
+ }
61
+ /**
62
+ * Removes the worker's own ways to reach a network, once, before the first
63
+ * script runs.
64
+ *
65
+ * - Every name in `REMOVED_GLOBALS` (the connection and storage
66
+ * constructors, and `Worker`, which would start an unlocked scope) is
67
+ * shadowed with `undefined`.
68
+ * - `importScripts` becomes a function that throws. It stays a function
69
+ * because the Lua engine's start-up detects a worker by
70
+ * `typeof importScripts`.
71
+ * - `fetch` keeps working for exactly one URL, the `glue.wasm` the job
72
+ * named, because the engine loads its WebAssembly through it for every
73
+ * script. Any other URL is refused.
74
+ *
75
+ * Net traffic is unaffected: it goes to the host with `postMessage`.
76
+ * Idempotent. Covered by `worker-entry-browser.test.ts` and, in a real
77
+ * browser, by the e2e suite.
78
+ */
79
+ export function lockdownWorkerGlobals(scope, wasmUri) {
80
+ const target = scope;
81
+ if (target.__markiiLocked)
82
+ return;
83
+ const base = target.location?.href ?? 'http://localhost/';
84
+ const allowed = wasmUri === undefined ? undefined : requestUrl(wasmUri, base);
85
+ const realFetch = target.fetch;
86
+ const shadow = (name, value) => {
87
+ try {
88
+ Object.defineProperty(scope, name, {
89
+ value,
90
+ configurable: false,
91
+ writable: false,
92
+ });
93
+ }
94
+ catch {
95
+ // A non-configurable property cannot be shadowed; assign instead.
96
+ try {
97
+ target[name] = value;
98
+ }
99
+ catch {
100
+ // nothing more can be done for this name
101
+ }
102
+ }
103
+ };
104
+ for (const name of REMOVED_GLOBALS)
105
+ shadow(name, undefined);
106
+ shadow('importScripts', () => {
107
+ throw new Error('importScripts is not available in the run isolate');
108
+ });
109
+ if (typeof realFetch === 'function') {
110
+ shadow('fetch', (input, init) => {
111
+ const url = requestUrl(input, base);
112
+ if (allowed !== undefined && url === allowed) {
113
+ return realFetch.call(scope, input, init);
114
+ }
115
+ return Promise.reject(new TypeError('network access is not available in the run isolate'));
116
+ });
117
+ }
118
+ Object.defineProperty(scope, '__markiiLocked', { value: true });
119
+ }
26
120
  function start(scope) {
27
121
  const bridge = createNetBridge((message) => {
28
122
  scope.postMessage(message);
@@ -47,6 +141,7 @@ function start(scope) {
47
141
  return;
48
142
  }
49
143
  const job = message;
144
+ lockdownWorkerGlobals(scope, job.wasmUri);
50
145
  let result;
51
146
  try {
52
147
  result = await runJob(job, {
@@ -53,6 +53,8 @@ import { isRunJob, resultForInternalError, runJob } from './run-job.js';
53
53
  * pass here.
54
54
  */
55
55
  function resolveWasmUri() {
56
+ if (typeof __dirname === 'undefined')
57
+ return undefined;
56
58
  const candidate = path.join(__dirname, 'glue.wasm');
57
59
  return existsSync(candidate) ? candidate : undefined;
58
60
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markii/host",
3
- "version": "0.17.0-next.2",
4
- "description": "Shared, host-neutral script-running layer for Markii hosts: tier gating, the grant model, address pinning, and the terminatable-isolate watchdog, plus the HostAdapter contract and createMarkiiHost. Entries: the neutral main entry, /node, /browser, /conformance.",
3
+ "version": "0.17.0-next.4",
4
+ "description": "Shared, host-neutral script-running layer for Markii hosts: tier gating, the grant model, address pinning, and the terminatable-isolate watchdog, plus the HostAdapter contract and createMarkiiHost. Entries: the neutral main entry, /internal (no semver promise), /node, /html-engine, /browser, /browser-worker-classic, /conformance.",
5
5
  "keywords": [
6
6
  "markdown",
7
7
  "mk.md",
@@ -27,22 +27,41 @@
27
27
  "import": "./dist/index.js",
28
28
  "default": "./dist/index.js"
29
29
  },
30
+ "./internal": {
31
+ "types": "./dist/internal.d.ts",
32
+ "import": "./dist/internal.js",
33
+ "default": "./dist/internal.js"
34
+ },
30
35
  "./node": {
31
36
  "types": "./dist/node.d.ts",
32
37
  "import": "./dist/node.js",
33
38
  "default": "./dist/node.js"
34
39
  },
40
+ "./html-engine": {
41
+ "types": "./dist/html-engine.d.ts",
42
+ "import": "./dist/html-engine.js",
43
+ "default": "./dist/html-engine.js"
44
+ },
45
+ "./node-worker": {
46
+ "default": "./dist/node-worker.js"
47
+ },
35
48
  "./browser": {
36
49
  "types": "./dist/browser.d.ts",
37
50
  "import": "./dist/browser.js",
38
51
  "default": "./dist/browser.js"
39
52
  },
53
+ "./browser-worker-classic": {
54
+ "default": "./dist/browser-worker.classic.js"
55
+ },
40
56
  "./conformance": {
41
57
  "types": "./dist/conformance.d.ts",
42
58
  "import": "./dist/conformance.js",
43
59
  "default": "./dist/conformance.js"
44
60
  }
45
61
  },
62
+ "engines": {
63
+ "node": ">=22"
64
+ },
46
65
  "scripts": {
47
66
  "pretest": "npm run generate:doc-css --workspace @markii/html",
48
67
  "test": "vitest run",
@@ -52,20 +71,20 @@
52
71
  "build:dist": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/build-dist-assets.mjs"
53
72
  },
54
73
  "dependencies": {
55
- "@markii/bundle": "^0.17.0-next.2",
56
- "@markii/core": "^0.17.0-next.2",
57
- "@markii/lua": "^0.17.0-next.2",
58
- "@markii/pack": "^0.17.0-next.2",
59
- "@markii/runtime": "^0.17.0-next.2",
60
- "@markii/stdlib": "^0.17.0-next.2",
74
+ "@markii/bundle": "^0.17.0-next.4",
75
+ "@markii/core": "^0.17.0-next.4",
76
+ "@markii/lua": "^0.17.0-next.4",
77
+ "@markii/pack": "^0.17.0-next.4",
78
+ "@markii/runtime": "^0.17.0-next.4",
79
+ "@markii/stdlib": "^0.17.0-next.4",
61
80
  "fflate": "^0.8.3"
62
81
  },
63
82
  "files": [
64
83
  "dist"
65
84
  ],
66
85
  "peerDependencies": {
67
- "@markii/html": "^0.17.0-next.2",
68
- "@markii/react": "^0.17.0-next.2"
86
+ "@markii/html": "^0.17.0-next.4",
87
+ "@markii/react": "^0.17.0-next.4"
69
88
  },
70
89
  "peerDependenciesMeta": {
71
90
  "@markii/html": {
@@ -76,8 +95,8 @@
76
95
  }
77
96
  },
78
97
  "devDependencies": {
79
- "@markii/html": "^0.17.0-next.2",
80
- "@markii/react": "^0.17.0-next.2",
98
+ "@markii/html": "^0.17.0-next.4",
99
+ "@markii/react": "^0.17.0-next.4",
81
100
  "esbuild": "^0.28.2"
82
101
  },
83
102
  "publishConfig": {