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.
- package/README.md +7 -2
- package/dist/automation/index.cjs +6 -4
- package/dist/automation/index.mjs +6 -4
- package/dist/collab/embedding.d.ts +7 -0
- package/dist/collab/embedding.d.ts.map +1 -1
- package/dist/collab/index.cjs +189 -0
- package/dist/collab/index.d.ts +2 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.mjs +186 -1
- package/dist/collab/write-back-lifecycle.d.ts +33 -0
- package/dist/collab/write-back-lifecycle.d.ts.map +1 -0
- package/dist/collab/write-back.d.ts +75 -0
- package/dist/collab/write-back.d.ts.map +1 -0
- package/dist/visio/collab/baseline.d.ts +36 -0
- package/dist/visio/collab/baseline.d.ts.map +1 -0
- package/dist/visio/collab/bind.d.ts +20 -2
- package/dist/visio/collab/bind.d.ts.map +1 -1
- package/dist/visio/collab/compose-lists.d.ts +27 -0
- package/dist/visio/collab/compose-lists.d.ts.map +1 -0
- package/dist/visio/collab/compose.d.ts +6 -2
- package/dist/visio/collab/compose.d.ts.map +1 -1
- package/dist/visio/collab/fixups.d.ts +5 -0
- package/dist/visio/collab/fixups.d.ts.map +1 -0
- package/dist/visio/collab/index.cjs +1499 -362
- package/dist/visio/collab/index.d.ts +3 -2
- package/dist/visio/collab/index.d.ts.map +1 -1
- package/dist/visio/collab/index.mjs +1492 -363
- package/dist/visio/collab/keys.d.ts +30 -1
- package/dist/visio/collab/keys.d.ts.map +1 -1
- package/dist/visio/collab/package.d.ts +22 -1
- package/dist/visio/collab/package.d.ts.map +1 -1
- package/dist/visio/collab/pieces.d.ts +34 -0
- package/dist/visio/collab/pieces.d.ts.map +1 -0
- package/dist/visio/collab/read-ids.d.ts +29 -0
- package/dist/visio/collab/read-ids.d.ts.map +1 -0
- package/dist/visio/collab/read-parts.d.ts +34 -0
- package/dist/visio/collab/read-parts.d.ts.map +1 -0
- package/dist/visio/collab/read-shapes.d.ts +35 -0
- package/dist/visio/collab/read-shapes.d.ts.map +1 -0
- package/dist/visio/collab/read-util.d.ts +20 -0
- package/dist/visio/collab/read-util.d.ts.map +1 -0
- package/dist/visio/collab/read.d.ts +40 -19
- package/dist/visio/collab/read.d.ts.map +1 -1
- package/dist/visio/collab/schema.d.ts +55 -1
- package/dist/visio/collab/schema.d.ts.map +1 -1
- package/dist/visio/collab/snapshot-contents.d.ts +9 -0
- package/dist/visio/collab/snapshot-contents.d.ts.map +1 -0
- package/dist/visio/collab/snapshot-io.d.ts +26 -0
- package/dist/visio/collab/snapshot-io.d.ts.map +1 -0
- package/dist/visio/collab/snapshot-parts.d.ts +14 -0
- package/dist/visio/collab/snapshot-parts.d.ts.map +1 -0
- package/dist/visio/collab/snapshot.d.ts +31 -24
- package/dist/visio/collab/snapshot.d.ts.map +1 -1
- package/dist/visio/collab/write-parts.d.ts +8 -0
- package/dist/visio/collab/write-parts.d.ts.map +1 -0
- package/dist/visio/collab/write-shapes.d.ts +30 -0
- package/dist/visio/collab/write-shapes.d.ts.map +1 -0
- package/dist/visio/collab/write.d.ts +4 -16
- package/dist/visio/collab/write.d.ts.map +1 -1
- package/dist/visio/index.cjs +57 -4
- package/dist/visio/index.d.ts +1 -0
- package/dist/visio/index.d.ts.map +1 -1
- package/dist/visio/index.mjs +56 -5
- package/dist/visio/parse-incremental.d.ts +16 -0
- package/dist/visio/parse-incremental.d.ts.map +1 -0
- package/dist/visio/parser.d.ts +7 -0
- package/dist/visio/parser.d.ts.map +1 -1
- package/dist/visio/ui/collab-options.d.ts +10 -5
- package/dist/visio/ui/collab-options.d.ts.map +1 -1
- package/dist/visio/ui/index.cjs +6 -4
- package/dist/visio/ui/index.mjs +6 -4
- 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
|
|
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,
|
|
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":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../collab/embedding.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,WAAW,CAAC;AAGpE,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,mBAAmB,CAAC;AAEvE,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAEhE,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IACjC,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,IAAI,CAAC,EAAE,iBAAiB,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAC9D,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kGAAkG;IAClG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,gHAAgH;IAChH,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC3B;AAED,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACd;AAED,wFAAwF;AACxF,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,0EAA0E;IAC1E,MAAM,EAAE,gBAAgB,GAAG,KAAK,CAAC;IACjC,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,2BAA2B,CAAC;CACxC;AAED,2FAA2F;AAC3F,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AAEjE,eAAO,MAAM,uBAAuB,EAAE,kBAMpC,CAAC;AAEH;;;GAGG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAC7D,aAAa,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;IAChD,kFAAkF;IAClF,iBAAiB,IAAI,IAAI,CAAC;IAC1B,sFAAsF;IACtF,sBAAsB,IAAI,IAAI,CAAC;CAC/B;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,MAAM;IACpD,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC7D,eAAe,CAAC,EAAE,CAAC,CAAC;CACpB;AAED,eAAO,MAAM,oBAAoB,4EACyC,CAAC;AAC3E,eAAO,MAAM,oBAAoB,kEAAkE,CAAC;AAEpG;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,MAAM,EACxD,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAChC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B;IAAE,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAoC/C;AAOD,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,kBAAkB,CAqBvF;AAED,2FAA2F;AAC3F,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,QAAQ,EAAE,MAAM,IAAI,GAClB,MAAM,IAAI,CASZ;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,eAAe,CAuBjF"}
|
package/dist/collab/index.cjs
CHANGED
|
@@ -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;
|
package/dist/collab/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/collab/index.mjs
CHANGED
|
@@ -1499,4 +1499,189 @@ function freezeDeep(value) {
|
|
|
1499
1499
|
return value;
|
|
1500
1500
|
}
|
|
1501
1501
|
|
|
1502
|
-
|
|
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"}
|