@marimo-team/marimo-export 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # @marimo-team/marimo-export
2
2
 
3
- `@marimo-team/marimo-export` opens notebook exports in browser applications. The
4
- Python package or CLI produces the export directory before the browser reads it.
3
+ `@marimo-team/marimo-export` opens **notebook exports** in browser applications,
4
+ resolves exported states, and loads named outputs. Select the states and outputs
5
+ to publish from a [marimo](https://marimo.io/) notebook. marimo-export writes a
6
+ portable, verified notebook export that browser applications and agents read
7
+ without a Python runtime or a copy of the notebook source.
5
8
 
6
9
  [pnpm](https://pnpm.io/) adds the package to a TypeScript project:
7
10
 
@@ -9,39 +12,54 @@ Python package or CLI produces the export directory before the browser reads it.
9
12
  pnpm add @marimo-team/marimo-export
10
13
  ```
11
14
 
15
+ The package targets [ECMAScript 2022](https://tc39.es/ecma262/2022/). Opening and
16
+ verifying an export require browser `fetch`, `URL`, `TextEncoder`, `TextDecoder`,
17
+ `AbortSignal`, and [Web Crypto](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API).
18
+ Serve the application over HTTPS or from a browser-recognized local development
19
+ origin so Web Crypto is available.
20
+
12
21
  ## Open one immutable export
13
22
 
14
- Assume the application serves a notebook export at `/exports/report/` and its
15
- document contains `<h1 id="title"></h1>`:
23
+ Assume the application serves the quickstart notebook export at
24
+ `/exports/report/` and its document contains `<pre id="summary"></pre>`:
16
25
 
17
26
  ```ts
18
27
  import { openExport } from "@marimo-team/marimo-export";
19
28
  import { jsonLoader } from "@marimo-team/marimo-export/loader/json";
20
29
 
21
30
  const notebookExport = await openExport("/exports/report/");
22
- const title = await notebookExport.defaultState.output("title").load(jsonLoader());
31
+ const summary = await notebookExport.defaultState.output("summary").load(jsonLoader());
23
32
 
24
- document.querySelector("#title")!.textContent = String(title);
33
+ document.querySelector("#summary")!.textContent = JSON.stringify(summary, null, 2);
25
34
  ```
26
35
 
27
36
  `openExport()` validates canonical `index.json`. Output assets remain lazy until
28
37
  a loader or complete verification requests them. Each load verifies the selected
29
38
  asset before decoding it.
30
39
 
40
+ Verification checks the notebook export against its loaded `index.json`. The
41
+ application still authenticates the publisher and delivery origin.
42
+
31
43
  The reader exposes the export identity, spec SHA-256, default state, notebook and
32
44
  producer facts, input names, control bindings, output names, aliases, and
33
45
  normalized states.
34
46
 
35
47
  ## Load and mount an output
36
48
 
37
- The next fragment continues with the `notebookExport` opened in the first
38
- example. That export also contains a `weekly` state and a `chart` output stored
39
- as Vega-Lite. The document contains an element with `id="chart"`.
49
+ The next fragment uses another export with a `weekly` state and a `chart` output
50
+ stored as Vega-Lite. Its document contains an element with `id="chart"`.
51
+
52
+ Install the peer runtime used to mount Vega-Lite charts:
53
+
54
+ ```bash
55
+ pnpm add vega-embed
56
+ ```
40
57
 
41
58
  ```ts
42
59
  import { vegaLiteLoader } from "@marimo-team/marimo-export/loader/vegalite";
43
60
 
44
- const state = notebookExport.state("weekly");
61
+ const chartExport = await openExport("/exports/market/");
62
+ const state = chartExport.state("weekly");
45
63
  const chart = await state.output("chart").load(vegaLiteLoader());
46
64
  const host = document.querySelector<HTMLElement>("#chart")!;
47
65
  const mounted = await chart.mount(host, { renderer: "svg" });
@@ -64,9 +82,8 @@ selected by a `marimo-export.prepared.v1` manifest. Applications use it when a
64
82
  server may publish a newer export generation or select another exported state.
65
83
 
66
84
  The following fragment assumes the application serves the manifest at
67
- `/runtime/prepared.json` and provides the same `#title` element as the first
68
- example. The manifest's export contains an `interval` input and a `title`
69
- output:
85
+ `/runtime/prepared.json` and provides `<h1 id="title"></h1>`. The manifest's
86
+ export contains an `interval` input and a `title` output:
70
87
 
71
88
  ```ts
72
89
  import { jsonLoader } from "@marimo-team/marimo-export/loader/json";
@@ -110,7 +127,8 @@ the page's authority. Apply the application's rendering policy,
110
127
  [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), and origin rules before
111
128
  mounting executable output.
112
129
 
113
- - [Build a browser application](https://marimo-team.github.io/marimo-export/guide/browser-applications)
130
+ - [Browser applications](https://marimo-team.github.io/marimo-export/guide/browser-applications)
114
131
  - [Browser API](https://marimo-team.github.io/marimo-export/reference/browser-api)
132
+ - [Browser compatibility and limits](https://marimo-team.github.io/marimo-export/reference/browser/errors-and-limits)
115
133
  - [Output representations](https://marimo-team.github.io/marimo-export/reference/representations)
116
134
  - [Export format](https://marimo-team.github.io/marimo-export/reference/export-format)
@@ -1,5 +1,5 @@
1
- import { c as isNotebookExportError, g as portableJsonObject, h as isStringValue, i as resolveOutputLoader, l as isCallableValue, m as isRecordValue, o as parseMediaType, p as isPropertyOwner, s as NotebookExportError } from "./loader-CDfnJ5Yb.mjs";
2
- import { a as parseStrictJson, n as compareUnicodeScalarStrings, r as parseExportIndex, t as canonicalJson } from "./schema-SJ_aIgcO.mjs";
1
+ import { c as isNotebookExportError, g as portableJsonObject, h as isStringValue, i as resolveOutputLoader, l as isCallableValue, m as isRecordValue, o as parseMediaType, p as isPropertyOwner, s as NotebookExportError } from "./loader-BTeU85BE.mjs";
2
+ import { a as parseStrictJson, n as compareUnicodeScalarStrings, r as parseExportIndex, t as canonicalJson } from "./schema-BOtMEPzp.mjs";
3
3
  import { decode } from "@msgpack/msgpack";
4
4
  //#region src/abort.ts
5
5
  function isAbortError(cause) {
@@ -1,4 +1,5 @@
1
1
  import { C as NotebookExport, I as ScalarValue, O as OpenExportOptions, b as MediaType, j as OutputLoader, k as OutputCodec, m as ExportOutput, o as BlobAssetLoadInput, s as BlobAssetLoader, t as AnyOutputLoader, x as MountableValue } from "./types-D52GHn1Q.mjs";
2
+ import { h as MarimoReplayResources, m as MarimoOutputSnapshot, o as MarimoCellSnapshot } from "./marimo-snapshot-Df66Xan0.mjs";
2
3
  //#region src/loader.d.ts
3
4
  declare function defineOutputLoader<C extends OutputCodec, T>(loader: OutputLoader<C, T>): OutputLoader<C, T>;
4
5
  declare function defineBlobAssetLoader<T>(definition: {
@@ -12,4 +13,8 @@ declare function imageLoader(): BlobAssetLoader<MountableValue>;
12
13
  //#region src/export.d.ts
13
14
  declare function openExport(base: string | URL, options?: OpenExportOptions): Promise<NotebookExport>;
14
15
  //#endregion
15
- export { resolveOutputLoader as a, imageLoader as i, defineBlobAssetLoader as n, scalarLoader as o, defineOutputLoader as r, openExport as t };
16
+ //#region src/replay-resources.d.ts
17
+ /** Compose snapshot resources while preserving every source's replay order. */
18
+ declare function mergeMarimoReplayResources(snapshots: readonly (MarimoOutputSnapshot | MarimoCellSnapshot)[]): MarimoReplayResources;
19
+ //#endregion
20
+ export { imageLoader as a, defineOutputLoader as i, openExport as n, resolveOutputLoader as o, defineBlobAssetLoader as r, scalarLoader as s, mergeMarimoReplayResources as t };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { A as OutputDescriptor, B as JsonObject, C as NotebookExport, D as NumpyDescriptor, E as NotebookProvenance, F as ScalarDescriptor, H as JsonValue, I as ScalarValue, L as VerificationResult, M as OutputPayloadMap, N as ProducerProvenance, O as OpenExportOptions, P as Provenance, R as VerifyOptions, S as MountedView, T as NotebookExportErrorCode, V as JsonPrimitive, _ as LoadOptions, a as BlobAssetDescriptor, b as MediaType, c as ControlBinding, d as ControlKeyStep, f as ControlPathStep, g as JsonDescriptor, h as ExportState, i as BlobAsset, j as OutputLoader, k as OutputCodec, l as ControlElementStep, m as ExportOutput, n as ArrowDescriptor, o as BlobAssetLoadInput, p as DescriptorFor, r as AssetDescriptor, s as BlobAssetLoader, t as AnyOutputLoader, u as ControlIndexStep, v as MarimoCellDescriptor, w as NotebookExportError, x as MountableValue, y as MarimoOutputDescriptor, z as isNotebookExportError } from "./types-D52GHn1Q.mjs";
2
- import { a as resolveOutputLoader, i as imageLoader, n as defineBlobAssetLoader, o as scalarLoader, r as defineOutputLoader, t as openExport } from "./index-Ca3yEJUD.mjs";
2
+ import { a as imageLoader, i as defineOutputLoader, n as openExport, o as resolveOutputLoader, r as defineBlobAssetLoader, s as scalarLoader, t as mergeMarimoReplayResources } from "./index-DXgOo60T.mjs";
3
3
  import { _ as parseMarimoOutputSnapshot, a as MarimoCellOutput, c as MarimoModelCloseMessage, d as MarimoModelLifecycleNotification, f as MarimoModelOpenMessage, g as parseMarimoCellSnapshot, h as MarimoReplayResources, i as MarimoCellIdentity, l as MarimoModelCustomMessage, m as MarimoOutputSnapshot, n as MarimoBufferPathToken, o as MarimoCellSnapshot, p as MarimoModelUpdateMessage, r as MarimoCellChannel, s as MarimoEsmSpec, t as MarimoBufferPath, u as MarimoModelLifecycleMessage } from "./marimo-snapshot-Df66Xan0.mjs";
4
- export { type AnyOutputLoader, type ArrowDescriptor, type AssetDescriptor, type BlobAsset, type BlobAssetDescriptor, type BlobAssetLoadInput, type BlobAssetLoader, type ControlBinding, type ControlElementStep, type ControlIndexStep, type ControlKeyStep, type ControlPathStep, type DescriptorFor, type ExportOutput, type ExportState, type JsonDescriptor, type JsonObject, type JsonPrimitive, type JsonValue, type LoadOptions, type MarimoBufferPath, type MarimoBufferPathToken, type MarimoCellChannel, type MarimoCellDescriptor, type MarimoCellIdentity, type MarimoCellOutput, type MarimoCellSnapshot, type MarimoEsmSpec, type MarimoModelCloseMessage, type MarimoModelCustomMessage, type MarimoModelLifecycleMessage, type MarimoModelLifecycleNotification, type MarimoModelOpenMessage, type MarimoModelUpdateMessage, type MarimoOutputDescriptor, type MarimoOutputSnapshot, type MarimoReplayResources, type MediaType, type MountableValue, type MountedView, type NotebookExport, NotebookExportError, type NotebookExportErrorCode, type NotebookProvenance, type NumpyDescriptor, type OpenExportOptions, type OutputCodec, type OutputDescriptor, type OutputLoader, type OutputPayloadMap, type ProducerProvenance, type Provenance, type ScalarDescriptor, type ScalarValue, type VerificationResult, type VerifyOptions, defineBlobAssetLoader, defineOutputLoader, imageLoader, isNotebookExportError, openExport, parseMarimoCellSnapshot, parseMarimoOutputSnapshot, resolveOutputLoader, scalarLoader };
4
+ export { type AnyOutputLoader, type ArrowDescriptor, type AssetDescriptor, type BlobAsset, type BlobAssetDescriptor, type BlobAssetLoadInput, type BlobAssetLoader, type ControlBinding, type ControlElementStep, type ControlIndexStep, type ControlKeyStep, type ControlPathStep, type DescriptorFor, type ExportOutput, type ExportState, type JsonDescriptor, type JsonObject, type JsonPrimitive, type JsonValue, type LoadOptions, type MarimoBufferPath, type MarimoBufferPathToken, type MarimoCellChannel, type MarimoCellDescriptor, type MarimoCellIdentity, type MarimoCellOutput, type MarimoCellSnapshot, type MarimoEsmSpec, type MarimoModelCloseMessage, type MarimoModelCustomMessage, type MarimoModelLifecycleMessage, type MarimoModelLifecycleNotification, type MarimoModelOpenMessage, type MarimoModelUpdateMessage, type MarimoOutputDescriptor, type MarimoOutputSnapshot, type MarimoReplayResources, type MediaType, type MountableValue, type MountedView, type NotebookExport, NotebookExportError, type NotebookExportErrorCode, type NotebookProvenance, type NumpyDescriptor, type OpenExportOptions, type OutputCodec, type OutputDescriptor, type OutputLoader, type OutputPayloadMap, type ProducerProvenance, type Provenance, type ScalarDescriptor, type ScalarValue, type VerificationResult, type VerifyOptions, defineBlobAssetLoader, defineOutputLoader, imageLoader, isNotebookExportError, mergeMarimoReplayResources, openExport, parseMarimoCellSnapshot, parseMarimoOutputSnapshot, resolveOutputLoader, scalarLoader };
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { a as scalarLoader, c as isNotebookExportError, i as resolveOutputLoader, n as defineOutputLoader, r as imageLoader, s as NotebookExportError, t as defineBlobAssetLoader } from "./loader-CDfnJ5Yb.mjs";
2
- import { t as openExport } from "./export-6118xT3K.mjs";
3
- import { n as parseMarimoOutputSnapshot, t as parseMarimoCellSnapshot } from "./marimo-snapshot--nNjqcq_.mjs";
4
- export { NotebookExportError, defineBlobAssetLoader, defineOutputLoader, imageLoader, isNotebookExportError, openExport, parseMarimoCellSnapshot, parseMarimoOutputSnapshot, resolveOutputLoader, scalarLoader };
1
+ import { a as scalarLoader, c as isNotebookExportError, i as resolveOutputLoader, n as defineOutputLoader, r as imageLoader, s as NotebookExportError, t as defineBlobAssetLoader } from "./loader-BTeU85BE.mjs";
2
+ import { t as openExport } from "./export-DHoEAsST.mjs";
3
+ import { n as parseMarimoOutputSnapshot, t as parseMarimoCellSnapshot } from "./marimo-snapshot-B2eNp-aW.mjs";
4
+ import { t as mergeMarimoReplayResources } from "./src-BL2Hd1uM.mjs";
5
+ export { NotebookExportError, defineBlobAssetLoader, defineOutputLoader, imageLoader, isNotebookExportError, mergeMarimoReplayResources, openExport, parseMarimoCellSnapshot, parseMarimoOutputSnapshot, resolveOutputLoader, scalarLoader };
@@ -30,7 +30,7 @@ interface PreparedWidgetGraphPort<Record, LiveState> {
30
30
  changesModule(previous: Record, next: Record): boolean;
31
31
  capture(id: string): LiveState;
32
32
  merge(record: Record, state: LiveState): Record;
33
- replay(record: Record, signal?: AbortSignal): Promise<void>;
33
+ replay(records: readonly Record[], signal?: AbortSignal): Promise<void>;
34
34
  restore(id: string, state: LiveState): void | Promise<void>;
35
35
  close(id: string): Promise<void>;
36
36
  setFiles(files: Readonly<{
@@ -1,4 +1,4 @@
1
- import { t as defineBlobAssetLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { t as defineBlobAssetLoader } from "../loader-BTeU85BE.mjs";
2
2
  //#region ../loader-anywidget/src/runtime/value-types.ts
3
3
  function isPrimitiveWithPrototype(value, prototype) {
4
4
  const boxed = Object(value);
@@ -1423,15 +1423,7 @@ var PreparedWidgetGraph = class {
1423
1423
  const rollback = () => this.#rollback(previous, changes, live, added, replaced, removed);
1424
1424
  let remount = false;
1425
1425
  try {
1426
- await eachSequential(changes.additions, async (record) => {
1427
- signal.throwIfAborted();
1428
- added.add(this.#port.id(record));
1429
- await this.#port.replay(record, signal);
1430
- });
1431
- await eachSequential(changes.stableUpdates, async (record) => {
1432
- signal.throwIfAborted();
1433
- await this.#port.replay(record, signal);
1434
- });
1426
+ const replay = /* @__PURE__ */ new Map();
1435
1427
  await eachSequential(changes.replacements, async (record) => {
1436
1428
  signal.throwIfAborted();
1437
1429
  const id = this.#port.id(record);
@@ -1439,8 +1431,20 @@ var PreparedWidgetGraph = class {
1439
1431
  replaced.add(id);
1440
1432
  remount = true;
1441
1433
  await this.#port.close(id);
1442
- await this.#port.replay(this.#port.merge(record, state), signal);
1434
+ replay.set(id, this.#port.merge(record, state));
1435
+ });
1436
+ changes.additions.forEach((record) => {
1437
+ const id = this.#port.id(record);
1438
+ added.add(id);
1439
+ replay.set(id, record);
1443
1440
  });
1441
+ changes.stableUpdates.forEach((record) => replay.set(this.#port.id(record), record));
1442
+ signal.throwIfAborted();
1443
+ const records = [...next.records.keys()].flatMap((id) => {
1444
+ const record = replay.get(id);
1445
+ return record === void 0 ? [] : [record];
1446
+ });
1447
+ if (records.length > 0) await this.#port.replay(records, signal);
1444
1448
  signal.throwIfAborted();
1445
1449
  } catch (error) {
1446
1450
  const failure = graphFailure(error);
@@ -1487,18 +1491,22 @@ var PreparedWidgetGraph = class {
1487
1491
  const errors = [];
1488
1492
  await eachSequential(added, (id) => attempt(errors, () => this.#port.close(id)));
1489
1493
  await eachSequential(live.stable, ([id, state]) => attempt(errors, () => this.#port.restore(id, state)));
1490
- await this.#replayPrevious(errors, previous, live.replacements, replaced);
1491
- await this.#replayPrevious(errors, previous, live.removals, removed);
1494
+ await this.#replayPrevious(errors, previous, new Map([...live.replacements, ...live.removals]), /* @__PURE__ */ new Set([...replaced, ...removed]));
1492
1495
  throwCleanup(errors, "Prepared AnyWidget graph rollback failed");
1493
1496
  }
1494
1497
  async #replayPrevious(errors, previous, states, ids) {
1495
- await eachSequential(ids, async (id) => {
1496
- const record = previous.records.get(id);
1498
+ await eachSequential(ids, (id) => attempt(errors, () => this.#port.close(id)));
1499
+ const records = [];
1500
+ previous.records.forEach((record, id) => {
1497
1501
  const state = states.get(id);
1498
- if (record === void 0 || state === void 0) return;
1499
- await attempt(errors, () => this.#port.close(id));
1500
- await attempt(errors, () => this.#port.replay(this.#port.merge(record, state)));
1502
+ if (!ids.has(id) || state === void 0) return;
1503
+ try {
1504
+ records.push(this.#port.merge(record, state));
1505
+ } catch (error) {
1506
+ errors.push(graphFailure(error));
1507
+ }
1501
1508
  });
1509
+ if (records.length > 0) await attempt(errors, () => this.#port.replay(records));
1502
1510
  }
1503
1511
  #replacement(mutated, remount, settle) {
1504
1512
  const replacement = settledReplacement(mutated, remount, async (commit) => {
@@ -1,4 +1,4 @@
1
- import { n as defineOutputLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { n as defineOutputLoader } from "../loader-BTeU85BE.mjs";
2
2
  import { CompressionType, setCompressionCodec, tableFromIPC } from "@uwdata/flechette";
3
3
  import { compress, decompress } from "lz4js";
4
4
  //#region ../loader-arrow/src/index.ts
@@ -1,4 +1,4 @@
1
- import { t as defineBlobAssetLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { t as defineBlobAssetLoader } from "../loader-BTeU85BE.mjs";
2
2
  import { n as hasUtf8Charset, t as decodeUtf8Blob } from "../utf8-DI-jI2Nm.mjs";
3
3
  //#region src/loader/html.ts
4
4
  /** Decode a UTF-8 HTML BlobAsset into an inert string. */
@@ -1,4 +1,4 @@
1
- import { _ as portableJsonValue, n as defineOutputLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { _ as portableJsonValue, n as defineOutputLoader } from "../loader-BTeU85BE.mjs";
2
2
  //#region src/loader/json.ts
3
3
  /** Return one detached, immutable JSON projection. */
4
4
  const jsonLoader = () => defineOutputLoader({
@@ -1,5 +1,5 @@
1
- import { n as defineOutputLoader } from "../loader-CDfnJ5Yb.mjs";
2
- import { t as parseMarimoCellSnapshot } from "../marimo-snapshot--nNjqcq_.mjs";
1
+ import { n as defineOutputLoader } from "../loader-BTeU85BE.mjs";
2
+ import { t as parseMarimoCellSnapshot } from "../marimo-snapshot-B2eNp-aW.mjs";
3
3
  //#region src/loader/marimo-cell.ts
4
4
  /** Decode one inert complete-cell snapshot. */
5
5
  const marimoCellLoader = () => defineOutputLoader({
@@ -1,5 +1,5 @@
1
- import { n as defineOutputLoader } from "../loader-CDfnJ5Yb.mjs";
2
- import { n as parseMarimoOutputSnapshot } from "../marimo-snapshot--nNjqcq_.mjs";
1
+ import { n as defineOutputLoader } from "../loader-BTeU85BE.mjs";
2
+ import { n as parseMarimoOutputSnapshot } from "../marimo-snapshot-B2eNp-aW.mjs";
3
3
  //#region src/loader/marimo-output.ts
4
4
  /** Decode one inert Marimo output snapshot. */
5
5
  const marimoOutputLoader = () => defineOutputLoader({
@@ -1,4 +1,4 @@
1
- import { n as defineOutputLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { n as defineOutputLoader } from "../loader-BTeU85BE.mjs";
2
2
  //#region ../loader-numpy/src/index.ts
3
3
  const MAGIC = [
4
4
  147,
@@ -1,4 +1,4 @@
1
- import { t as defineBlobAssetLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { t as defineBlobAssetLoader } from "../loader-BTeU85BE.mjs";
2
2
  import { parquetReadObjects } from "hyparquet";
3
3
  //#region ../loader-parquet/src/index.ts
4
4
  /** Read a verified Parquet BlobAsset into row objects. */
@@ -1,4 +1,4 @@
1
- import { t as defineBlobAssetLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { t as defineBlobAssetLoader } from "../loader-BTeU85BE.mjs";
2
2
  import { n as hasUtf8Charset, t as decodeUtf8Blob } from "../utf8-DI-jI2Nm.mjs";
3
3
  //#region src/loader/text.ts
4
4
  /** Decode a UTF-8 text BlobAsset. */
@@ -1,4 +1,4 @@
1
- import { g as portableJsonObject, t as defineBlobAssetLoader } from "../loader-CDfnJ5Yb.mjs";
1
+ import { g as portableJsonObject, t as defineBlobAssetLoader } from "../loader-BTeU85BE.mjs";
2
2
  //#region ../loader-vegalite/src/index.ts
3
3
  const MEDIA_TYPE = /^application\/vnd\.vegalite\.v[1-9]\d*\+json$/u;
4
4
  const OWNED_CLASSES = ["vega-embed", "has-actions"];
@@ -84,13 +84,17 @@ function unicodeScalar(value, path) {
84
84
  return value;
85
85
  }
86
86
  function isBoolean(value) {
87
- return Object.prototype.toString.call(value) === "[object Boolean]";
87
+ return isPrimitiveWithPrototype$1(value, Boolean.prototype);
88
88
  }
89
89
  function isString(value) {
90
- return Object.prototype.toString.call(value) === "[object String]";
90
+ return isPrimitiveWithPrototype$1(value, String.prototype);
91
91
  }
92
92
  function isNumber(value) {
93
- return Object.prototype.toString.call(value) === "[object Number]";
93
+ return isPrimitiveWithPrototype$1(value, Number.prototype);
94
+ }
95
+ function isPrimitiveWithPrototype$1(value, prototype) {
96
+ const boxed = Object(value);
97
+ return boxed !== value && Object.getPrototypeOf(boxed) === prototype;
94
98
  }
95
99
  function isJsonObject(value) {
96
100
  return value !== null && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
@@ -1,5 +1,5 @@
1
- import { _ as portableJsonValue, d as isJsonNumber, f as isJsonString, g as portableJsonObject, m as isRecordValue } from "./loader-CDfnJ5Yb.mjs";
2
- import { a as parseStrictJson, t as canonicalJson } from "./schema-SJ_aIgcO.mjs";
1
+ import { _ as portableJsonValue, d as isJsonNumber, f as isJsonString, g as portableJsonObject, m as isRecordValue } from "./loader-BTeU85BE.mjs";
2
+ import { a as parseStrictJson, t as canonicalJson } from "./schema-BOtMEPzp.mjs";
3
3
  //#region src/marimo-snapshot.ts
4
4
  const decoder = new TextDecoder("utf-8", {
5
5
  fatal: true,
@@ -102,6 +102,7 @@ function parseCellOutput(value, path) {
102
102
  });
103
103
  }
104
104
  function parseResources(value, ownerCellId, projectionSha256) {
105
+ if (!SHA256.test(projectionSha256)) throw new TypeError("Replay resources require a valid projection digest.");
105
106
  const resources = strictRecord(value, "resources", [
106
107
  "files",
107
108
  "functions",
@@ -277,4 +278,4 @@ function equalBytes(left, right) {
277
278
  return left.byteLength === right.byteLength && left.every((byte, index) => byte === right[index]);
278
279
  }
279
280
  //#endregion
280
- export { parseMarimoOutputSnapshot as n, parseMarimoCellSnapshot as t };
281
+ export { parseMarimoOutputSnapshot as n, parseResources as r, parseMarimoCellSnapshot as t };
@@ -26,6 +26,7 @@ interface PreparedManifestFetchOptions {
26
26
  readonly signal?: AbortSignal;
27
27
  }
28
28
  declare const fetchPreparedExportManifest: (url: URL, options?: PreparedManifestFetchOptions) => Promise<PreparedExportManifest>;
29
+ declare const fetchPreparedManifestDocument: (url: URL, options?: PreparedManifestFetchOptions) => Promise<JsonValue>;
29
30
  //#endregion
30
31
  //#region src/prepared/state-port.d.ts
31
32
  type PreparedStateChangeReason = "start" | "state" | "publication";
@@ -113,4 +114,4 @@ declare class PreparedExportError extends Error {
113
114
  }
114
115
  declare const isPreparedExportError: <Value>(value: Value) => value is Value & PreparedExportError;
115
116
  //#endregion
116
- export { type OpenPreparedPublicationOptions, PreparedExportError, type PreparedExportErrorCode, type PreparedExportManifest, type PreparedManifestFetchOptions, type PreparedPublication, PreparedPublicationRefresh, type PreparedPublicationRefreshDependencies, type PreparedPublicationRefreshOptions, type PreparedStateChange, type PreparedStateChangeReason, PreparedStateController, type PreparedStatePort, type PreparedStateSnapshot, fetchPreparedExportManifest, isPreparedAbort, isPreparedExportError, openPreparedPublication, parsePreparedExportManifest, preparedControlInputPatch, resolvePreparedPublication, resolvePreparedQuerySelection, resolvePreparedQueryState, samePreparedInputs };
117
+ export { type OpenPreparedPublicationOptions, PreparedExportError, type PreparedExportErrorCode, type PreparedExportManifest, type PreparedManifestFetchOptions, type PreparedPublication, PreparedPublicationRefresh, type PreparedPublicationRefreshDependencies, type PreparedPublicationRefreshOptions, type PreparedStateChange, type PreparedStateChangeReason, PreparedStateController, type PreparedStatePort, type PreparedStateSnapshot, fetchPreparedExportManifest, fetchPreparedManifestDocument, isPreparedAbort, isPreparedExportError, openPreparedPublication, parsePreparedExportManifest, preparedControlInputPatch, resolvePreparedPublication, resolvePreparedQuerySelection, resolvePreparedQueryState, samePreparedInputs };
@@ -1,6 +1,6 @@
1
- import { _ as portableJsonValue, c as isNotebookExportError, d as isJsonNumber, f as isJsonString, g as portableJsonObject, h as isStringValue, m as isRecordValue, p as isPropertyOwner } from "../loader-CDfnJ5Yb.mjs";
2
- import { i as parsePortableJson } from "../schema-SJ_aIgcO.mjs";
3
- import { n as normalizeBase, t as openExport } from "../export-6118xT3K.mjs";
1
+ import { _ as portableJsonValue, c as isNotebookExportError, d as isJsonNumber, f as isJsonString, g as portableJsonObject, h as isStringValue, m as isRecordValue, p as isPropertyOwner } from "../loader-BTeU85BE.mjs";
2
+ import { i as parsePortableJson } from "../schema-BOtMEPzp.mjs";
3
+ import { n as normalizeBase, t as openExport } from "../export-DHoEAsST.mjs";
4
4
  //#region src/prepared/cancellation.ts
5
5
  const preparedAbortReason = (reason, message = "Prepared export operation aborted") => {
6
6
  if (reason instanceof Error || reason instanceof DOMException) return reason;
@@ -157,7 +157,8 @@ function invalid(message) {
157
157
  //#endregion
158
158
  //#region src/prepared/manifest-fetch.ts
159
159
  const MANIFEST_MAX_BYTES = 262144;
160
- const fetchPreparedExportManifest = async (url, options = {}) => {
160
+ const fetchPreparedExportManifest = async (url, options = {}) => parsePreparedExportManifest(await fetchPreparedManifestDocument(url, options));
161
+ const fetchPreparedManifestDocument = async (url, options = {}) => {
161
162
  throwIfPreparedAborted(options.signal);
162
163
  let response;
163
164
  try {
@@ -182,7 +183,7 @@ const fetchPreparedExportManifest = async (url, options = {}) => {
182
183
  const bytes = await readManifestBytes(response, options.signal);
183
184
  try {
184
185
  const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
185
- return parsePreparedExportManifest(parsePortableJson(text));
186
+ return parsePortableJson(text);
186
187
  } catch (error) {
187
188
  if (error instanceof PreparedExportError) throw error;
188
189
  throw new PreparedExportError("manifest_invalid", "The prepared export manifest is not valid portable JSON.", { cause: error });
@@ -700,4 +701,4 @@ var PreparedPublicationRefresh = class {
700
701
  }
701
702
  };
702
703
  //#endregion
703
- export { PreparedExportError, PreparedPublicationRefresh, PreparedStateController, fetchPreparedExportManifest, isPreparedAbort, isPreparedExportError, openPreparedPublication, parsePreparedExportManifest, preparedControlInputPatch, resolvePreparedPublication, resolvePreparedQuerySelection, resolvePreparedQueryState, samePreparedInputs };
704
+ export { PreparedExportError, PreparedPublicationRefresh, PreparedStateController, fetchPreparedExportManifest, fetchPreparedManifestDocument, isPreparedAbort, isPreparedExportError, openPreparedPublication, parsePreparedExportManifest, preparedControlInputPatch, resolvePreparedPublication, resolvePreparedQuerySelection, resolvePreparedQueryState, samePreparedInputs };
@@ -1,4 +1,4 @@
1
- import { _ as portableJsonValue, c as isNotebookExportError, d as isJsonNumber, f as isJsonString, g as portableJsonObject, m as isRecordValue, o as parseMediaType, s as NotebookExportError, u as isJsonBoolean, v as MAX_JSON_VALUES } from "./loader-CDfnJ5Yb.mjs";
1
+ import { _ as portableJsonValue, c as isNotebookExportError, d as isJsonNumber, f as isJsonString, g as portableJsonObject, m as isRecordValue, o as parseMediaType, s as NotebookExportError, u as isJsonBoolean, v as MAX_JSON_VALUES } from "./loader-BTeU85BE.mjs";
2
2
  //#region ../portable-json/src/parse.ts
3
3
  const MAX_JSON_NUMBER_LEXEME_BYTES = 1024;
4
4
  /** Parse one JSON value while rejecting duplicate object keys at every depth. */
@@ -0,0 +1,91 @@
1
+ import { t as canonicalJson } from "./schema-BOtMEPzp.mjs";
2
+ import { r as parseResources } from "./marimo-snapshot-B2eNp-aW.mjs";
3
+ //#region src/replay-resources.ts
4
+ const notificationValue = (notification) => {
5
+ const message = notification.message;
6
+ return {
7
+ ...notification,
8
+ message: message.method === "open" || message.method === "update" ? {
9
+ ...message,
10
+ esm_spec: message.esm_spec === null ? null : { ...message.esm_spec }
11
+ } : { ...message }
12
+ };
13
+ };
14
+ /** Compose snapshot resources while preserving every source's replay order. */
15
+ function mergeMarimoReplayResources(snapshots) {
16
+ const files = /* @__PURE__ */ new Map();
17
+ const uiValues = /* @__PURE__ */ new Map();
18
+ const functions = /* @__PURE__ */ new Map();
19
+ const sequences = /* @__PURE__ */ new Map();
20
+ const events = /* @__PURE__ */ new Map();
21
+ for (const snapshot of snapshots) {
22
+ const owner = snapshot.schema === "marimo.output.v1" ? snapshot.ownerCellId : snapshot.cell.id;
23
+ const resources = parseResources({
24
+ ...snapshot.resources,
25
+ modelNotifications: snapshot.resources.modelNotifications.map(notificationValue)
26
+ }, owner, snapshot.projectionSha256);
27
+ for (const [name, value] of Object.entries(resources.files)) {
28
+ if (files.has(name) && files.get(name) !== value) throw new TypeError(`Replay resource file ${JSON.stringify(name)} has conflicting contents.`);
29
+ files.set(name, value);
30
+ }
31
+ for (const [id, value] of Object.entries(resources.uiValues)) {
32
+ const previous = uiValues.get(id);
33
+ if (previous !== void 0 && canonicalJson(previous) !== canonicalJson(value)) throw new TypeError(`Replay UI object ${JSON.stringify(id)} has conflicting UI values.`);
34
+ uiValues.set(id, value);
35
+ functions.set(id, resources.functions[id]);
36
+ }
37
+ const grouped = /* @__PURE__ */ new Map();
38
+ for (const notification of resources.modelNotifications) {
39
+ const sequence = grouped.get(notification.model_id) ?? [];
40
+ sequence.push(notification);
41
+ grouped.set(notification.model_id, sequence);
42
+ }
43
+ for (const [id, sequence] of grouped) {
44
+ const canonical = canonicalJson(sequence.map(notificationValue));
45
+ const previous = sequences.get(id);
46
+ if (previous !== void 0 && previous !== canonical) throw new TypeError(`Replay model ${JSON.stringify(id)} has conflicting lifecycle records.`);
47
+ sequences.set(id, canonical);
48
+ }
49
+ const ordinals = /* @__PURE__ */ new Map();
50
+ let previous;
51
+ for (const notification of resources.modelNotifications) {
52
+ const ordinal = ordinals.get(notification.model_id) ?? 0;
53
+ ordinals.set(notification.model_id, ordinal + 1);
54
+ const key = `${notification.model_id}:${ordinal}`;
55
+ let event = events.get(key);
56
+ if (event === void 0) {
57
+ event = {
58
+ notification,
59
+ successors: /* @__PURE__ */ new Set(),
60
+ predecessors: 0
61
+ };
62
+ events.set(key, event);
63
+ }
64
+ if (previous !== void 0 && !previous.successors.has(key)) {
65
+ previous.successors.add(key);
66
+ event.predecessors += 1;
67
+ }
68
+ previous = event;
69
+ }
70
+ }
71
+ const ready = [...events.values()].filter((event) => event.predecessors === 0);
72
+ const notifications = [];
73
+ for (let index = 0; index < ready.length; index += 1) {
74
+ const event = ready[index];
75
+ notifications.push(event.notification);
76
+ for (const key of event.successors) {
77
+ const successor = events.get(key);
78
+ successor.predecessors -= 1;
79
+ if (successor.predecessors === 0) ready.push(successor);
80
+ }
81
+ }
82
+ if (notifications.length !== events.size) throw new TypeError("Replay snapshots require conflicting model lifecycle order.");
83
+ return Object.freeze({
84
+ files: Object.freeze(Object.fromEntries(files)),
85
+ uiValues: Object.freeze(Object.fromEntries(uiValues)),
86
+ functions: Object.freeze(Object.fromEntries(functions)),
87
+ modelNotifications: Object.freeze(notifications)
88
+ });
89
+ }
90
+ //#endregion
91
+ export { mergeMarimoReplayResources as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@marimo-team/marimo-export",
3
- "version": "0.0.2",
4
- "description": "Load precomputed marimo notebook results in browser applications",
3
+ "version": "0.0.3",
4
+ "description": "Open and load verified marimo notebook exports in browser applications",
5
5
  "homepage": "https://marimo-team.github.io/marimo-export/",
6
6
  "bugs": {
7
7
  "url": "https://github.com/marimo-team/marimo-export/issues"