ooxml-core 1.22.0 → 1.23.0

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 (72) hide show
  1. package/README.md +7 -2
  2. package/dist/automation/index.cjs +6 -4
  3. package/dist/automation/index.mjs +6 -4
  4. package/dist/collab/embedding.d.ts +7 -0
  5. package/dist/collab/embedding.d.ts.map +1 -1
  6. package/dist/collab/index.cjs +189 -0
  7. package/dist/collab/index.d.ts +2 -0
  8. package/dist/collab/index.d.ts.map +1 -1
  9. package/dist/collab/index.mjs +186 -1
  10. package/dist/collab/write-back-lifecycle.d.ts +33 -0
  11. package/dist/collab/write-back-lifecycle.d.ts.map +1 -0
  12. package/dist/collab/write-back.d.ts +75 -0
  13. package/dist/collab/write-back.d.ts.map +1 -0
  14. package/dist/visio/collab/baseline.d.ts +36 -0
  15. package/dist/visio/collab/baseline.d.ts.map +1 -0
  16. package/dist/visio/collab/bind.d.ts +20 -2
  17. package/dist/visio/collab/bind.d.ts.map +1 -1
  18. package/dist/visio/collab/compose-lists.d.ts +27 -0
  19. package/dist/visio/collab/compose-lists.d.ts.map +1 -0
  20. package/dist/visio/collab/compose.d.ts +6 -2
  21. package/dist/visio/collab/compose.d.ts.map +1 -1
  22. package/dist/visio/collab/fixups.d.ts +5 -0
  23. package/dist/visio/collab/fixups.d.ts.map +1 -0
  24. package/dist/visio/collab/index.cjs +1499 -362
  25. package/dist/visio/collab/index.d.ts +3 -2
  26. package/dist/visio/collab/index.d.ts.map +1 -1
  27. package/dist/visio/collab/index.mjs +1492 -363
  28. package/dist/visio/collab/keys.d.ts +30 -1
  29. package/dist/visio/collab/keys.d.ts.map +1 -1
  30. package/dist/visio/collab/package.d.ts +22 -1
  31. package/dist/visio/collab/package.d.ts.map +1 -1
  32. package/dist/visio/collab/pieces.d.ts +34 -0
  33. package/dist/visio/collab/pieces.d.ts.map +1 -0
  34. package/dist/visio/collab/read-ids.d.ts +29 -0
  35. package/dist/visio/collab/read-ids.d.ts.map +1 -0
  36. package/dist/visio/collab/read-parts.d.ts +34 -0
  37. package/dist/visio/collab/read-parts.d.ts.map +1 -0
  38. package/dist/visio/collab/read-shapes.d.ts +35 -0
  39. package/dist/visio/collab/read-shapes.d.ts.map +1 -0
  40. package/dist/visio/collab/read-util.d.ts +20 -0
  41. package/dist/visio/collab/read-util.d.ts.map +1 -0
  42. package/dist/visio/collab/read.d.ts +40 -19
  43. package/dist/visio/collab/read.d.ts.map +1 -1
  44. package/dist/visio/collab/schema.d.ts +55 -1
  45. package/dist/visio/collab/schema.d.ts.map +1 -1
  46. package/dist/visio/collab/snapshot-contents.d.ts +9 -0
  47. package/dist/visio/collab/snapshot-contents.d.ts.map +1 -0
  48. package/dist/visio/collab/snapshot-io.d.ts +26 -0
  49. package/dist/visio/collab/snapshot-io.d.ts.map +1 -0
  50. package/dist/visio/collab/snapshot-parts.d.ts +14 -0
  51. package/dist/visio/collab/snapshot-parts.d.ts.map +1 -0
  52. package/dist/visio/collab/snapshot.d.ts +31 -24
  53. package/dist/visio/collab/snapshot.d.ts.map +1 -1
  54. package/dist/visio/collab/write-parts.d.ts +8 -0
  55. package/dist/visio/collab/write-parts.d.ts.map +1 -0
  56. package/dist/visio/collab/write-shapes.d.ts +30 -0
  57. package/dist/visio/collab/write-shapes.d.ts.map +1 -0
  58. package/dist/visio/collab/write.d.ts +4 -16
  59. package/dist/visio/collab/write.d.ts.map +1 -1
  60. package/dist/visio/index.cjs +57 -4
  61. package/dist/visio/index.d.ts +1 -0
  62. package/dist/visio/index.d.ts.map +1 -1
  63. package/dist/visio/index.mjs +56 -5
  64. package/dist/visio/parse-incremental.d.ts +16 -0
  65. package/dist/visio/parse-incremental.d.ts.map +1 -0
  66. package/dist/visio/parser.d.ts +7 -0
  67. package/dist/visio/parser.d.ts.map +1 -1
  68. package/dist/visio/ui/collab-options.d.ts +10 -5
  69. package/dist/visio/ui/collab-options.d.ts.map +1 -1
  70. package/dist/visio/ui/index.cjs +6 -4
  71. package/dist/visio/ui/index.mjs +6 -4
  72. package/package.json +1 -1
package/README.md CHANGED
@@ -24,7 +24,7 @@ Part of [ooxml](https://github.com/ChristopherVR/ooxml#readme): [Office](https:/
24
24
 
25
25
  ## Why ooxml-core?
26
26
 
27
- - **No UI, no framework.** Everything runs in browsers, Node.js, Bun, workers and serverless functions. The viewer apps ([docx](https://christophervr.github.io/ooxml/docx/), [xlsx](https://christophervr.github.io/ooxml/xlsx/), [visio](https://christophervr.github.io/ooxml/visio/), [teams](https://christophervr.github.io/ooxml/teams/) and [pptx-viewer](https://github.com/ChristopherVR/pptx-viewer)) are thin interfaces on top of it, and so can your application be.
27
+ - **No UI, no framework.** Everything runs in browsers, Node.js, Bun, workers and serverless functions. The viewer apps ([docx](https://christophervr.github.io/ooxml/docx/), [xlsx](https://christophervr.github.io/ooxml/xlsx/), [visio](https://christophervr.github.io/ooxml/visio/), [teams](https://christophervr.github.io/ooxml/teams/) and [pptx](https://christophervr.github.io/ooxml/pptx/)) are thin interfaces on top of it, and so can your application be.
28
28
  - **One package, one structure.** All formats share the same XML model, packaging layer, units, colours and geometry. Each format is an _area_ of this one package, not a separate dependency to keep in step.
29
29
  - **Round-trips without losing what it does not understand.** Documents are loaded into a model, edited, and written back. Untouched parts stay byte-for-byte, unknown markup is preserved, and edits that would damage unsupported content are rejected instead of silently dropped.
30
30
  - **Strict by default.** New code is strict TypeScript with branded measurement units, the shared `xml` area parses strictly (no DTD or entity expansion), and the `docx` area is checked against the ECMA-376 schemas in the test suite.
@@ -47,12 +47,17 @@ import { xml, opc } from 'ooxml-core'; // or by namespace (shared building block
47
47
  | `color` | Hex, RGB, HSL and linear colour primitives, OOXML percent and angle parsing. |
48
48
  | `geometry` | DrawingML preset shapes, connection sites, clip paths, callouts and boolean shape operations. |
49
49
  | `diagram` | SmartArt (DiagramML), format-neutral: data model, layout/colour/quick-style parts, the cached `dsp:drawing` shape tree, relationship resolution and a loader. Used by `docx`; `pptx` re-imports its parsers. |
50
+ | `drawingml` | Format-neutral DrawingML: colour, fill, text-body and geometry readers, the colour and fill writers and the theme model that docx and xlsx read their themes through. |
51
+ | `chart` | Format-neutral chart algorithms and formatting helpers: data calculations, axes, number formats, the Change Colors palettes and the SVG chart painter. |
52
+ | `text` | Shared text rules: script-category segmentation, tab leaders and styled-run wrapping. |
53
+ | `i18n` | Locale normalisation, dictionary merging and the translator every product UI uses. |
54
+ | `ribbon` | DOM-free ribbon contracts every product shares, today the add-in tab descriptor a host uses to add its own tabs. |
50
55
  | `digest` | Synchronous pure-TypeScript SHA-1, SHA-256, SHA-384 and SHA-512, ECMA-376 digest name normalisation (`SHA-512`, `SHA512`, `sha_512`, ...) and the agile password hash Office protection elements store (`hashPassword`, `verifyPasswordHash`). No Web Crypto, so it works on `http://`. Used by `xlsx` sheet and workbook protection. |
51
56
  | `crypto` | ECMA-376 package encryption ([MS-OFFCRYPTO]) for every format: `decryptOoxmlPackage`, `encryptOoxmlPackage` (agile AES-256/SHA-512 by default, or Standard), `isEncryptedOoxmlPackage`, and typed errors with a `code` (`password-required`, `incorrect-password`, `data-integrity`). The compound-file container comes from ole2 (inlined); not in the root entry. |
52
57
  | `collab` | Format-neutral real-time collaboration on Yjs: session and provider lifecycle, awareness/presence, transport-neutral sync (WebSocket, in-memory, any byte channel), update codecs, ordering helpers, asset sync and the product adapter seam. See `docs/collab-area.md`. |
53
58
  | `docx` | WordprocessingML: model, parser, preserving serializer, editing, validation. Also `/docx/embedded`. `/docx/layout` is the DOM-free pagination engine (an approximation of Word, not parity) and `/docx/load` detects and loads DOCX (also password-protected, `{ password }`) and legacy .doc (ole2 inlined). |
54
59
  | `pptx` | PresentationML: model, parser, serializer, editing, charts, SmartArt, converters, CLI, signatures. Subpaths `/pptx/converter`, `/pptx/cli`, `/pptx/signature-node`. |
55
- | `xlsx` | SpreadsheetML: workbook model, `.xlsx`/`.xlsm` reader and preserving writer, formula engine (dependency graph, dynamic arrays, 380+ functions), Excel number formats, editing commands with undo, and the DOM-free grid layout (sizes, colours, conditional formats, charts as SVG, drawing anchors). SmartArt is shown from the cached drawing (not re-laid out) and kept verbatim on save. `/xlsx/load` detects and loads `.xlsx`, `.xlsm` (also password-protected: `loadWorkbook(bytes, { password })`, `saveWorkbook(workbook, 'xlsx', { password })`), legacy `.xls` (ole2 inlined) and CSV, and refuses `.xlsb` with a typed error. `/xlsx/collab` shares a workbook over Yjs (cells, formats, rows, columns, merges, sheets and defined names; see `docs/collab-area.md`). Digital signatures are detected and reported, never kept on save. An approximation of Excel, not parity. |
60
+ | `xlsx` | SpreadsheetML: workbook model, `.xlsx`/`.xlsm` reader and preserving writer, formula engine (dependency graph, dynamic arrays, 490+ functions), Excel number formats, editing commands with undo, and the DOM-free grid layout (sizes, colours, conditional formats, charts as SVG, drawing anchors). SmartArt is shown from the cached drawing (not re-laid out) and kept verbatim on save. `/xlsx/load` detects and loads `.xlsx`, `.xlsm` (also password-protected: `loadWorkbook(bytes, { password })`, `saveWorkbook(workbook, 'xlsx', { password })`), legacy `.xls` (ole2 inlined) and CSV, and refuses `.xlsb` with a typed error. `/xlsx/collab` shares a workbook over Yjs (cells, formats, rows, columns, merges, sheets and defined names; see `docs/collab-area.md`). Digital signatures are detected and reported, never kept on save. An approximation of Excel, not parity. |
56
61
  | `visio` | Visio: `.vsdx` parsing, loading and bounded editing, page layers and visibility, and a conservative preview of legacy `.vsd` (through ole2). See `docs/visio-legacy-vsd.md`. |
57
62
  | `math` | Office Math: OMML to and from LaTeX and MathML, with colour and size helpers. |
58
63
  | `teams` | The logic of the OpenTeams workspace: channels and chat on Yjs, presence, WebRTC calls with pluggable signalling, server configuration and the `createTeamsClient` store every UI binds to. Built on `collab`. See `docs/teams-area.md`. |
@@ -69917,6 +69917,7 @@ async function parseVsdx(input2, options2 = {}) {
69917
69917
  throw new VisioPackageError("INVALID_DOCUMENT", "Missing Visio pages part.");
69918
69918
  const pages = [], pageIds2 = /* @__PURE__ */ new Set();
69919
69919
  const comments = [];
69920
+ const only = options2.pages ? new Set(options2.pages) : void 0;
69920
69921
  const pageNodes = pagesPart ? children3(await visioXml(pkg, pagesPart, "Pages"), "Page") : [];
69921
69922
  const foreground = pageNodes.filter((page) => !yes(attribute3(page, "Background")));
69922
69923
  const fieldProperties = await readVisioFieldProperties(pkg);
@@ -69926,6 +69927,7 @@ async function parseVsdx(input2, options2 = {}) {
69926
69927
  if (!id4 || pageIds2.has(id4))
69927
69928
  throw new VisioPackageError("INVALID_PAGE_ID", "Page IDs must be present and unique.");
69928
69929
  pageIds2.add(id4);
69930
+ if (only && !only.has(id4)) continue;
69929
69931
  const part = await indexedPart(pkg, pagesPart, page, "page");
69930
69932
  const root = await visioXml(pkg, part, "PageContents");
69931
69933
  const localReport = (code, message, extra) => report(code, message, { part, pageId: id4, ...extra });
@@ -70035,16 +70037,16 @@ async function parseVsdx(input2, options2 = {}) {
70035
70037
  });
70036
70038
  }
70037
70039
  checkTime();
70038
- validateBackgrounds(pages, report);
70040
+ if (!only) validateBackgrounds(pages, report);
70039
70041
  try {
70040
- comments.push(...await readVisioComments(pkg, documentPart, report));
70042
+ if (!only) comments.push(...await readVisioComments(pkg, documentPart, report));
70041
70043
  } catch (error62) {
70042
70044
  if (!(error62 instanceof VisioPackageError) || error62.code === "LIMIT_RUNTIME") throw error62;
70043
70045
  report("invalid-comments", "The comments part could not be read; comments are not shown.");
70044
70046
  }
70045
70047
  let dataRecordsets = [];
70046
70048
  try {
70047
- dataRecordsets = await readDataRecordsets(pkg, documentPart, checkTime);
70049
+ if (!only) dataRecordsets = await readDataRecordsets(pkg, documentPart, checkTime);
70048
70050
  } catch (error62) {
70049
70051
  if (error62 instanceof VisioPackageError && error62.code === "LIMIT_RUNTIME") throw error62;
70050
70052
  report(
@@ -70052,7 +70054,7 @@ async function parseVsdx(input2, options2 = {}) {
70052
70054
  `Saved external data could not be read and is preserved unchanged: ${error62 instanceof Error ? error62.message : String(error62)}`
70053
70055
  );
70054
70056
  }
70055
- const stencils = await readVisioStencilWindows(pkg, documentPart);
70057
+ const stencils = only ? [] : await readVisioStencilWindows(pkg, documentPart);
70056
70058
  report(
70057
70059
  "cached-values-only",
70058
70060
  "ShapeSheet formulas, automatic connector routing, and external data are not evaluated; saved cached values and supported theme records are used.",
@@ -69911,6 +69911,7 @@ async function parseVsdx(input2, options2 = {}) {
69911
69911
  throw new VisioPackageError("INVALID_DOCUMENT", "Missing Visio pages part.");
69912
69912
  const pages = [], pageIds2 = /* @__PURE__ */ new Set();
69913
69913
  const comments = [];
69914
+ const only = options2.pages ? new Set(options2.pages) : void 0;
69914
69915
  const pageNodes = pagesPart ? children3(await visioXml(pkg, pagesPart, "Pages"), "Page") : [];
69915
69916
  const foreground = pageNodes.filter((page) => !yes(attribute3(page, "Background")));
69916
69917
  const fieldProperties = await readVisioFieldProperties(pkg);
@@ -69920,6 +69921,7 @@ async function parseVsdx(input2, options2 = {}) {
69920
69921
  if (!id4 || pageIds2.has(id4))
69921
69922
  throw new VisioPackageError("INVALID_PAGE_ID", "Page IDs must be present and unique.");
69922
69923
  pageIds2.add(id4);
69924
+ if (only && !only.has(id4)) continue;
69923
69925
  const part = await indexedPart(pkg, pagesPart, page, "page");
69924
69926
  const root = await visioXml(pkg, part, "PageContents");
69925
69927
  const localReport = (code, message, extra) => report(code, message, { part, pageId: id4, ...extra });
@@ -70029,16 +70031,16 @@ async function parseVsdx(input2, options2 = {}) {
70029
70031
  });
70030
70032
  }
70031
70033
  checkTime();
70032
- validateBackgrounds(pages, report);
70034
+ if (!only) validateBackgrounds(pages, report);
70033
70035
  try {
70034
- comments.push(...await readVisioComments(pkg, documentPart, report));
70036
+ if (!only) comments.push(...await readVisioComments(pkg, documentPart, report));
70035
70037
  } catch (error62) {
70036
70038
  if (!(error62 instanceof VisioPackageError) || error62.code === "LIMIT_RUNTIME") throw error62;
70037
70039
  report("invalid-comments", "The comments part could not be read; comments are not shown.");
70038
70040
  }
70039
70041
  let dataRecordsets = [];
70040
70042
  try {
70041
- dataRecordsets = await readDataRecordsets(pkg, documentPart, checkTime);
70043
+ if (!only) dataRecordsets = await readDataRecordsets(pkg, documentPart, checkTime);
70042
70044
  } catch (error62) {
70043
70045
  if (error62 instanceof VisioPackageError && error62.code === "LIMIT_RUNTIME") throw error62;
70044
70046
  report(
@@ -70046,7 +70048,7 @@ async function parseVsdx(input2, options2 = {}) {
70046
70048
  `Saved external data could not be read and is preserved unchanged: ${error62 instanceof Error ? error62.message : String(error62)}`
70047
70049
  );
70048
70050
  }
70049
- const stencils = await readVisioStencilWindows(pkg, documentPart);
70051
+ const stencils = only ? [] : await readVisioStencilWindows(pkg, documentPart);
70050
70052
  report(
70051
70053
  "cached-values-only",
70052
70054
  "ShapeSheet formulas, automatic connector routing, and external data are not evaluated; saved cached values and supported theme records are used.",
@@ -3,6 +3,7 @@ import type { CollaborationRole } from './identity.js';
3
3
  import type { ConnectionStatus, ProviderFactory } from './provider.js';
4
4
  import { type CollabSession } from './session.js';
5
5
  import { type WebrtcOptions } from './webrtc-provider.js';
6
+ import type { CollaborationWriteBackState } from './write-back.js';
6
7
  /** Who this window is in the room. */
7
8
  export interface CollaborationUser {
8
9
  /** Display name; each editor falls back to its own author name. */
@@ -58,6 +59,12 @@ export interface CollaborationState {
58
59
  /** The first exchange with the room finished. */
59
60
  synced: boolean;
60
61
  people: Collaborator[];
62
+ /**
63
+ * The owner's write-back (`onWriteBack`), present only in the window that writes back. `saved`
64
+ * means the host's callback returned (or its promise resolved): the host's acknowledgement and
65
+ * nothing more.
66
+ */
67
+ writeBack?: CollaborationWriteBackState;
61
68
  }
62
69
  /** The event every editor element dispatches, with a {@link CollaborationState} detail. */
63
70
  export declare const COLLABORATION_CHANGE_EVENT = "collaboration-change";
@@ -1 +1 @@
1
- {"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../collab/embedding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../collab/embedding.ts"],"names":[],"mappings":"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"}
@@ -1523,6 +1523,191 @@ function freezeDeep(value) {
1523
1523
  return value;
1524
1524
  }
1525
1525
 
1526
+ // collab/write-back.ts
1527
+ var DEFAULT_WRITE_BACK_DEBOUNCE_MS = 5e3;
1528
+ var DEFAULT_RETRY_BASE_MS = 1e3;
1529
+ var DEFAULT_RETRY_MAX_MS = 3e4;
1530
+ var globalTimers = {
1531
+ setTimeout: (callback, ms) => globalThis.setTimeout(callback, ms),
1532
+ clearTimeout: (handle) => globalThis.clearTimeout(handle)
1533
+ };
1534
+ var isThenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
1535
+ function writeBackStateOf(state) {
1536
+ const out = { status: state.status };
1537
+ if (state.lastSavedAt !== void 0) out.lastSavedAt = state.lastSavedAt;
1538
+ if (state.status === "failed" && state.lastError !== void 0) {
1539
+ out.error = state.lastError instanceof Error ? state.lastError.message : String(state.lastError);
1540
+ }
1541
+ return out;
1542
+ }
1543
+ function createWriteBackScheduler(options) {
1544
+ const timers = options.timers ?? globalTimers;
1545
+ const now = options.now ?? Date.now;
1546
+ const configured = options.debounceMs;
1547
+ const debounceMs = configured !== void 0 && Number.isFinite(configured) && configured >= 0 ? configured : DEFAULT_WRITE_BACK_DEBOUNCE_MS;
1548
+ const attempts = Math.max(1, Math.floor(options.retry?.attempts ?? 1) || 1);
1549
+ const baseDelay = Math.max(0, options.retry?.baseDelayMs ?? DEFAULT_RETRY_BASE_MS);
1550
+ const maxDelay = Math.max(baseDelay, options.retry?.maxDelayMs ?? DEFAULT_RETRY_MAX_MS);
1551
+ let state = { status: "idle" };
1552
+ let generation = 0;
1553
+ let disposed = false;
1554
+ let dirty = false;
1555
+ let timer = null;
1556
+ let running = null;
1557
+ let wake = null;
1558
+ let ended = false;
1559
+ let rushing = 0;
1560
+ const patch = (status, extra = {}) => {
1561
+ const next = { status };
1562
+ const saved = extra.lastSavedAt ?? state.lastSavedAt;
1563
+ if (saved !== void 0) next.lastSavedAt = saved;
1564
+ if (extra.lastError !== void 0) next.lastError = extra.lastError;
1565
+ state = next;
1566
+ options.onStatus?.(state);
1567
+ };
1568
+ const clearTimer = () => {
1569
+ if (timer !== null) timers.clearTimeout(timer);
1570
+ timer = null;
1571
+ };
1572
+ const arm = () => {
1573
+ clearTimer();
1574
+ timer = timers.setTimeout(() => {
1575
+ timer = null;
1576
+ void start();
1577
+ }, debounceMs);
1578
+ if (state.status !== "pending") patch("pending");
1579
+ };
1580
+ const sleep = (ms) => new Promise((resolve) => {
1581
+ if (rushing > 0) return resolve();
1582
+ const handle = timers.setTimeout(done, ms);
1583
+ function done() {
1584
+ timers.clearTimeout(handle);
1585
+ wake = null;
1586
+ resolve();
1587
+ }
1588
+ wake = done;
1589
+ });
1590
+ const settledStatus = () => state.lastSavedAt === void 0 ? "idle" : "saved";
1591
+ async function run() {
1592
+ const gen = generation;
1593
+ const isCurrent = () => gen === generation;
1594
+ patch("saving");
1595
+ try {
1596
+ for (let attempt = 1; ; attempt++) {
1597
+ dirty = false;
1598
+ try {
1599
+ let bytes = options.serialize({ isCurrent });
1600
+ if (isThenable(bytes)) bytes = await bytes;
1601
+ if (!isCurrent()) return;
1602
+ if (bytes === null || bytes === void 0) {
1603
+ if (dirty) arm();
1604
+ else patch(settledStatus());
1605
+ return;
1606
+ }
1607
+ const acknowledged = options.write(bytes);
1608
+ if (isThenable(acknowledged)) await acknowledged;
1609
+ if (!isCurrent()) return;
1610
+ const lastSavedAt = now();
1611
+ if (dirty) {
1612
+ patch("pending", { lastSavedAt });
1613
+ arm();
1614
+ } else {
1615
+ patch("saved", { lastSavedAt });
1616
+ }
1617
+ return;
1618
+ } catch (error) {
1619
+ if (!isCurrent()) return;
1620
+ if (attempt >= attempts) {
1621
+ dirty = true;
1622
+ patch("failed", { lastError: error });
1623
+ return;
1624
+ }
1625
+ await sleep(Math.min(maxDelay, baseDelay * 2 ** (attempt - 1)));
1626
+ if (!isCurrent()) return;
1627
+ }
1628
+ }
1629
+ } finally {
1630
+ ended = true;
1631
+ running = null;
1632
+ if (dirty && timer === null && !disposed && state.status !== "failed") arm();
1633
+ }
1634
+ }
1635
+ function start() {
1636
+ if (running) return running;
1637
+ ended = false;
1638
+ const promise = run();
1639
+ if (!ended) running = promise;
1640
+ return promise;
1641
+ }
1642
+ function cancel() {
1643
+ generation++;
1644
+ clearTimer();
1645
+ dirty = false;
1646
+ const waiting = wake;
1647
+ wake = null;
1648
+ waiting?.();
1649
+ if (state.status === "pending" || state.status === "saving") patch(settledStatus());
1650
+ }
1651
+ return {
1652
+ schedule() {
1653
+ if (disposed) return;
1654
+ dirty = true;
1655
+ if (running) return;
1656
+ arm();
1657
+ },
1658
+ async flush() {
1659
+ if (disposed) return;
1660
+ clearTimer();
1661
+ rushing++;
1662
+ try {
1663
+ wake?.();
1664
+ for (; ; ) {
1665
+ if (running) await running;
1666
+ else if (dirty) await start();
1667
+ else return;
1668
+ if (disposed) return;
1669
+ if (state.status === "failed") throw state.lastError;
1670
+ clearTimer();
1671
+ }
1672
+ } finally {
1673
+ rushing--;
1674
+ }
1675
+ },
1676
+ cancel,
1677
+ dispose() {
1678
+ if (disposed) return;
1679
+ cancel();
1680
+ disposed = true;
1681
+ },
1682
+ get state() {
1683
+ return state;
1684
+ },
1685
+ get dirty() {
1686
+ return dirty;
1687
+ }
1688
+ };
1689
+ }
1690
+
1691
+ // collab/write-back-lifecycle.ts
1692
+ function flushWriteBackOnPageClose(scheduler, options = {}) {
1693
+ const globals = globalThis;
1694
+ const win = options.window === void 0 ? typeof globals.addEventListener === "function" ? globalThis : null : options.window;
1695
+ const doc = options.document === void 0 ? globals.document ?? null : options.document;
1696
+ const flush = () => {
1697
+ if (!scheduler.dirty) return;
1698
+ scheduler.flush().catch((error) => options.onError?.(error));
1699
+ };
1700
+ const hidden = () => {
1701
+ if (doc?.visibilityState === "hidden") flush();
1702
+ };
1703
+ win?.addEventListener("pagehide", flush);
1704
+ doc?.addEventListener("visibilitychange", hidden);
1705
+ return () => {
1706
+ win?.removeEventListener("pagehide", flush);
1707
+ doc?.removeEventListener("visibilitychange", hidden);
1708
+ };
1709
+ }
1710
+
1526
1711
  exports.BROADCAST_THROTTLE_MS = BROADCAST_THROTTLE_MS;
1527
1712
  exports.BoundedMap = BoundedMap;
1528
1713
  exports.COLLABORATION_CHANGE_EVENT = COLLABORATION_CHANGE_EVENT;
@@ -1533,6 +1718,7 @@ exports.CONNECTION_TIMEOUT_MS = CONNECTION_TIMEOUT_MS;
1533
1718
  exports.CURSOR_PALETTE = CURSOR_PALETTE;
1534
1719
  exports.DEFAULT_COLOR = DEFAULT_COLOR;
1535
1720
  exports.DEFAULT_MAX_PACKAGE_BYTES = DEFAULT_MAX_PACKAGE_BYTES;
1721
+ exports.DEFAULT_WRITE_BACK_DEBOUNCE_MS = DEFAULT_WRITE_BACK_DEBOUNCE_MS;
1536
1722
  exports.DEPARTURE_CHANNEL = DEPARTURE_CHANNEL;
1537
1723
  exports.Emitter = Emitter;
1538
1724
  exports.INITIAL_SYNC_GRACE_MS = INITIAL_SYNC_GRACE_MS;
@@ -1575,11 +1761,13 @@ exports.createPresencePublisher = createPresencePublisher;
1575
1761
  exports.createSyncGate = createSyncGate;
1576
1762
  exports.createTransportProvider = createTransportProvider;
1577
1763
  exports.createWebSocketTransport = createWebSocketTransport;
1764
+ exports.createWriteBackScheduler = createWriteBackScheduler;
1578
1765
  exports.deriveCanvasPresence = deriveCanvasPresence;
1579
1766
  exports.derivePresence = derivePresence;
1580
1767
  exports.encodeDiff = encodeDiff;
1581
1768
  exports.encodeSnapshot = encodeSnapshot;
1582
1769
  exports.encodeStateVector = encodeStateVector2;
1770
+ exports.flushWriteBackOnPageClose = flushWriteBackOnPageClose;
1583
1771
  exports.formatCursorLabel = formatCursorLabel;
1584
1772
  exports.freezeDeep = freezeDeep;
1585
1773
  exports.fromBase64 = fromBase64;
@@ -1617,3 +1805,4 @@ exports.validateDisplayName = validateDisplayName;
1617
1805
  exports.validateRoomId = validateRoomId;
1618
1806
  exports.validateUpdate = validateUpdate;
1619
1807
  exports.webrtcProvider = webrtcProvider;
1808
+ exports.writeBackStateOf = writeBackStateOf;
@@ -19,4 +19,6 @@ export * from './transport-provider.js';
19
19
  export * from './validation.js';
20
20
  export * from './webrtc-provider.js';
21
21
  export * from './websocket-transport.js';
22
+ export * from './write-back.js';
23
+ export * from './write-back-lifecycle.js';
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../collab/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../collab/index.ts"],"names":[],"mappings":"AAIA,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC"}
@@ -1499,4 +1499,189 @@ function freezeDeep(value) {
1499
1499
  return value;
1500
1500
  }
1501
1501
 
1502
- export { BROADCAST_THROTTLE_MS, BoundedMap, COLLABORATION_CHANGE_EVENT, COLLABORATION_OFF_STATE, COLLABORATION_ROLES, COLLAB_LEAVE_MESSAGE, CONNECTION_TIMEOUT_MS, CURSOR_PALETTE, DEFAULT_COLOR, DEFAULT_MAX_PACKAGE_BYTES, DEPARTURE_CHANNEL, Emitter, INITIAL_SYNC_GRACE_MS, INVALID_ROOM_MESSAGE, IdempotencyCache, LEGACY_PPTX_DEPARTURE_CHANNEL, LEGACY_PPTX_LEAVE_MESSAGE, LOCAL_ORIGIN, MAX_LABEL_CHARS, MAX_UPDATE_BYTES, MESSAGE_AWARENESS, MESSAGE_QUERY_AWARENESS, MESSAGE_SYNC, NO_BROADCAST_MESSAGE, PRESENCE_FIELD, PRESENCE_HEARTBEAT_MS, STALE_PRESENCE_MS, SequenceTracker, adaptYjsProvider, applyUpdateSafe, asSelectionIds, assignUserColor, bindDocument, borrowAwareness, buildRoster, canBroadcast, canWrite, clampCoordinate, classifyVersion, clearLocalAwareness, collaborationStateOf, createAssetSync, createBroadcastTransport, createClientId, createCollabSession, createDepartureChannel, createIdGenerator, createMemoryHub, createPresencePublisher, createSyncGate, createTransportProvider, createWebSocketTransport, deriveCanvasPresence, derivePresence, encodeDiff, encodeSnapshot, encodeStateVector2 as encodeStateVector, formatCursorLabel, freezeDeep, fromBase64, getUserInitials, isMixedContentBlocked, isNonNegativeSafeInteger, isPresenceFresh, isValidId, isValidRoomId, mergeUpdates2 as mergeUpdates, observeExternalSession, onCollaborationStateChange, openCollaborationSession, packageAdapter, presenceToCursors, randomToken, registerTeardown, removeAwarenessStatesLocally, restoreSnapshot, roomUrl, sanitizeAvatarUrl, sanitizeCanvasPayload, sanitizeColor, sanitizeIdentity, sanitizeIndex, sanitizeRole, sanitizeUserName, shouldAutoFollowBroadcaster, shouldRoomReplaceLoad, stripHtmlTags, toBase64, toExternalSession, transportProvider, validateDisplayName, validateRoomId, validateUpdate, webrtcProvider };
1502
+ // collab/write-back.ts
1503
+ var DEFAULT_WRITE_BACK_DEBOUNCE_MS = 5e3;
1504
+ var DEFAULT_RETRY_BASE_MS = 1e3;
1505
+ var DEFAULT_RETRY_MAX_MS = 3e4;
1506
+ var globalTimers = {
1507
+ setTimeout: (callback, ms) => globalThis.setTimeout(callback, ms),
1508
+ clearTimeout: (handle) => globalThis.clearTimeout(handle)
1509
+ };
1510
+ var isThenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
1511
+ function writeBackStateOf(state) {
1512
+ const out = { status: state.status };
1513
+ if (state.lastSavedAt !== void 0) out.lastSavedAt = state.lastSavedAt;
1514
+ if (state.status === "failed" && state.lastError !== void 0) {
1515
+ out.error = state.lastError instanceof Error ? state.lastError.message : String(state.lastError);
1516
+ }
1517
+ return out;
1518
+ }
1519
+ function createWriteBackScheduler(options) {
1520
+ const timers = options.timers ?? globalTimers;
1521
+ const now = options.now ?? Date.now;
1522
+ const configured = options.debounceMs;
1523
+ const debounceMs = configured !== void 0 && Number.isFinite(configured) && configured >= 0 ? configured : DEFAULT_WRITE_BACK_DEBOUNCE_MS;
1524
+ const attempts = Math.max(1, Math.floor(options.retry?.attempts ?? 1) || 1);
1525
+ const baseDelay = Math.max(0, options.retry?.baseDelayMs ?? DEFAULT_RETRY_BASE_MS);
1526
+ const maxDelay = Math.max(baseDelay, options.retry?.maxDelayMs ?? DEFAULT_RETRY_MAX_MS);
1527
+ let state = { status: "idle" };
1528
+ let generation = 0;
1529
+ let disposed = false;
1530
+ let dirty = false;
1531
+ let timer = null;
1532
+ let running = null;
1533
+ let wake = null;
1534
+ let ended = false;
1535
+ let rushing = 0;
1536
+ const patch = (status, extra = {}) => {
1537
+ const next = { status };
1538
+ const saved = extra.lastSavedAt ?? state.lastSavedAt;
1539
+ if (saved !== void 0) next.lastSavedAt = saved;
1540
+ if (extra.lastError !== void 0) next.lastError = extra.lastError;
1541
+ state = next;
1542
+ options.onStatus?.(state);
1543
+ };
1544
+ const clearTimer = () => {
1545
+ if (timer !== null) timers.clearTimeout(timer);
1546
+ timer = null;
1547
+ };
1548
+ const arm = () => {
1549
+ clearTimer();
1550
+ timer = timers.setTimeout(() => {
1551
+ timer = null;
1552
+ void start();
1553
+ }, debounceMs);
1554
+ if (state.status !== "pending") patch("pending");
1555
+ };
1556
+ const sleep = (ms) => new Promise((resolve) => {
1557
+ if (rushing > 0) return resolve();
1558
+ const handle = timers.setTimeout(done, ms);
1559
+ function done() {
1560
+ timers.clearTimeout(handle);
1561
+ wake = null;
1562
+ resolve();
1563
+ }
1564
+ wake = done;
1565
+ });
1566
+ const settledStatus = () => state.lastSavedAt === void 0 ? "idle" : "saved";
1567
+ async function run() {
1568
+ const gen = generation;
1569
+ const isCurrent = () => gen === generation;
1570
+ patch("saving");
1571
+ try {
1572
+ for (let attempt = 1; ; attempt++) {
1573
+ dirty = false;
1574
+ try {
1575
+ let bytes = options.serialize({ isCurrent });
1576
+ if (isThenable(bytes)) bytes = await bytes;
1577
+ if (!isCurrent()) return;
1578
+ if (bytes === null || bytes === void 0) {
1579
+ if (dirty) arm();
1580
+ else patch(settledStatus());
1581
+ return;
1582
+ }
1583
+ const acknowledged = options.write(bytes);
1584
+ if (isThenable(acknowledged)) await acknowledged;
1585
+ if (!isCurrent()) return;
1586
+ const lastSavedAt = now();
1587
+ if (dirty) {
1588
+ patch("pending", { lastSavedAt });
1589
+ arm();
1590
+ } else {
1591
+ patch("saved", { lastSavedAt });
1592
+ }
1593
+ return;
1594
+ } catch (error) {
1595
+ if (!isCurrent()) return;
1596
+ if (attempt >= attempts) {
1597
+ dirty = true;
1598
+ patch("failed", { lastError: error });
1599
+ return;
1600
+ }
1601
+ await sleep(Math.min(maxDelay, baseDelay * 2 ** (attempt - 1)));
1602
+ if (!isCurrent()) return;
1603
+ }
1604
+ }
1605
+ } finally {
1606
+ ended = true;
1607
+ running = null;
1608
+ if (dirty && timer === null && !disposed && state.status !== "failed") arm();
1609
+ }
1610
+ }
1611
+ function start() {
1612
+ if (running) return running;
1613
+ ended = false;
1614
+ const promise = run();
1615
+ if (!ended) running = promise;
1616
+ return promise;
1617
+ }
1618
+ function cancel() {
1619
+ generation++;
1620
+ clearTimer();
1621
+ dirty = false;
1622
+ const waiting = wake;
1623
+ wake = null;
1624
+ waiting?.();
1625
+ if (state.status === "pending" || state.status === "saving") patch(settledStatus());
1626
+ }
1627
+ return {
1628
+ schedule() {
1629
+ if (disposed) return;
1630
+ dirty = true;
1631
+ if (running) return;
1632
+ arm();
1633
+ },
1634
+ async flush() {
1635
+ if (disposed) return;
1636
+ clearTimer();
1637
+ rushing++;
1638
+ try {
1639
+ wake?.();
1640
+ for (; ; ) {
1641
+ if (running) await running;
1642
+ else if (dirty) await start();
1643
+ else return;
1644
+ if (disposed) return;
1645
+ if (state.status === "failed") throw state.lastError;
1646
+ clearTimer();
1647
+ }
1648
+ } finally {
1649
+ rushing--;
1650
+ }
1651
+ },
1652
+ cancel,
1653
+ dispose() {
1654
+ if (disposed) return;
1655
+ cancel();
1656
+ disposed = true;
1657
+ },
1658
+ get state() {
1659
+ return state;
1660
+ },
1661
+ get dirty() {
1662
+ return dirty;
1663
+ }
1664
+ };
1665
+ }
1666
+
1667
+ // collab/write-back-lifecycle.ts
1668
+ function flushWriteBackOnPageClose(scheduler, options = {}) {
1669
+ const globals = globalThis;
1670
+ const win = options.window === void 0 ? typeof globals.addEventListener === "function" ? globalThis : null : options.window;
1671
+ const doc = options.document === void 0 ? globals.document ?? null : options.document;
1672
+ const flush = () => {
1673
+ if (!scheduler.dirty) return;
1674
+ scheduler.flush().catch((error) => options.onError?.(error));
1675
+ };
1676
+ const hidden = () => {
1677
+ if (doc?.visibilityState === "hidden") flush();
1678
+ };
1679
+ win?.addEventListener("pagehide", flush);
1680
+ doc?.addEventListener("visibilitychange", hidden);
1681
+ return () => {
1682
+ win?.removeEventListener("pagehide", flush);
1683
+ doc?.removeEventListener("visibilitychange", hidden);
1684
+ };
1685
+ }
1686
+
1687
+ export { BROADCAST_THROTTLE_MS, BoundedMap, COLLABORATION_CHANGE_EVENT, COLLABORATION_OFF_STATE, COLLABORATION_ROLES, COLLAB_LEAVE_MESSAGE, CONNECTION_TIMEOUT_MS, CURSOR_PALETTE, DEFAULT_COLOR, DEFAULT_MAX_PACKAGE_BYTES, DEFAULT_WRITE_BACK_DEBOUNCE_MS, DEPARTURE_CHANNEL, Emitter, INITIAL_SYNC_GRACE_MS, INVALID_ROOM_MESSAGE, IdempotencyCache, LEGACY_PPTX_DEPARTURE_CHANNEL, LEGACY_PPTX_LEAVE_MESSAGE, LOCAL_ORIGIN, MAX_LABEL_CHARS, MAX_UPDATE_BYTES, MESSAGE_AWARENESS, MESSAGE_QUERY_AWARENESS, MESSAGE_SYNC, NO_BROADCAST_MESSAGE, PRESENCE_FIELD, PRESENCE_HEARTBEAT_MS, STALE_PRESENCE_MS, SequenceTracker, adaptYjsProvider, applyUpdateSafe, asSelectionIds, assignUserColor, bindDocument, borrowAwareness, buildRoster, canBroadcast, canWrite, clampCoordinate, classifyVersion, clearLocalAwareness, collaborationStateOf, createAssetSync, createBroadcastTransport, createClientId, createCollabSession, createDepartureChannel, createIdGenerator, createMemoryHub, createPresencePublisher, createSyncGate, createTransportProvider, createWebSocketTransport, createWriteBackScheduler, deriveCanvasPresence, derivePresence, encodeDiff, encodeSnapshot, encodeStateVector2 as encodeStateVector, flushWriteBackOnPageClose, formatCursorLabel, freezeDeep, fromBase64, getUserInitials, isMixedContentBlocked, isNonNegativeSafeInteger, isPresenceFresh, isValidId, isValidRoomId, mergeUpdates2 as mergeUpdates, observeExternalSession, onCollaborationStateChange, openCollaborationSession, packageAdapter, presenceToCursors, randomToken, registerTeardown, removeAwarenessStatesLocally, restoreSnapshot, roomUrl, sanitizeAvatarUrl, sanitizeCanvasPayload, sanitizeColor, sanitizeIdentity, sanitizeIndex, sanitizeRole, sanitizeUserName, shouldAutoFollowBroadcaster, shouldRoomReplaceLoad, stripHtmlTags, toBase64, toExternalSession, transportProvider, validateDisplayName, validateRoomId, validateUpdate, webrtcProvider, writeBackStateOf };
@@ -0,0 +1,33 @@
1
+ import type { WriteBackScheduler } from './write-back.js';
2
+ /** The part of `window` used: `pagehide` is listened on it. */
3
+ export interface PageHideTarget {
4
+ addEventListener(type: 'pagehide', listener: () => void): void;
5
+ removeEventListener(type: 'pagehide', listener: () => void): void;
6
+ }
7
+ /** The part of `document` used: `visibilitychange` is listened on it. */
8
+ export interface VisibilityTarget {
9
+ readonly visibilityState?: string;
10
+ addEventListener(type: 'visibilitychange', listener: () => void): void;
11
+ removeEventListener(type: 'visibilitychange', listener: () => void): void;
12
+ }
13
+ export interface FlushOnPageCloseOptions {
14
+ /** Default `globalThis` when it can listen (a browser window), otherwise no `pagehide` hook. */
15
+ window?: PageHideTarget | null;
16
+ /** Default `globalThis.document` when present. */
17
+ document?: VisibilityTarget | null;
18
+ /** Receives a flush that failed (it never throws). Default ignores it. */
19
+ onError?: (error: unknown) => void;
20
+ }
21
+ /**
22
+ * Flushes `scheduler` on `pagehide` and when the page turns hidden, and returns the function that
23
+ * removes the listeners. Only unsaved work is written.
24
+ *
25
+ * Browsers do not wait for asynchronous work when a page unloads: the flush starts the write, but
26
+ * a write that needs a network round trip can still be lost on close. A host that must survive a
27
+ * close should send the bytes with `navigator.sendBeacon(url, bytes)` or
28
+ * `fetch(url, { method: 'PUT', body: bytes, keepalive: true })` (the browser caps keepalive bodies
29
+ * at about 64 KiB, so a large document may still not make it), which the browser completes after
30
+ * the page is gone.
31
+ */
32
+ export declare function flushWriteBackOnPageClose(scheduler: Pick<WriteBackScheduler, 'flush' | 'dirty'>, options?: FlushOnPageCloseOptions): () => void;
33
+ //# sourceMappingURL=write-back-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-back-lifecycle.d.ts","sourceRoot":"","sources":["../../collab/write-back-lifecycle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC9B,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC/D,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAClE;AAED,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACvE,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAC1E;AAED,MAAM,WAAW,uBAAuB;IACvC,gGAAgG;IAChG,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,kDAAkD;IAClD,QAAQ,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACxC,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,OAAO,CAAC,EACtD,OAAO,GAAE,uBAA4B,GACnC,MAAM,IAAI,CAyBZ"}