@skenora/document 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # @skenora/document
2
+
3
+ Immutable SceneDocument state, patches, and conflict checks for Skenora.
4
+
5
+ ```bash
6
+ pnpm add @skenora/document
7
+ ```
8
+
9
+ `SceneDocumentStore` publishes revisioned `DocumentChange` records. Patch
10
+ helpers clone, apply, invert, and validate expected previous values without
11
+ mutating the input document.
12
+
13
+ ```ts
14
+ import { applyPatchesChecked } from "@skenora/document";
15
+
16
+ const next = applyPatchesChecked(document, patches);
17
+ ```
18
+
19
+ Use `@skenora/editor` when you also need commands, transactions, history,
20
+ selection, undo, and redo.
21
+
22
+ See the [architecture guide](https://github.com/zhangjiadi225/skenora/blob/main/docs/ARCHITECTURE.md).
@@ -0,0 +1,22 @@
1
+ import type { SceneDocumentData } from "@skenora/contracts";
2
+ import { type DocumentPatch } from "./patch.js";
3
+ export interface DocumentChange {
4
+ readonly id: string;
5
+ readonly label: string;
6
+ readonly patches: readonly DocumentPatch[];
7
+ readonly revision: number;
8
+ }
9
+ export type DocumentListener = (document: Readonly<SceneDocumentData>, change: DocumentChange) => void;
10
+ export interface SceneDocumentStoreOptions {
11
+ onListenerError?: (error: unknown, change: DocumentChange) => void;
12
+ }
13
+ export declare class SceneDocumentStore {
14
+ #private;
15
+ constructor(document: SceneDocumentData, options?: SceneDocumentStoreOptions);
16
+ get revision(): number;
17
+ get snapshot(): Readonly<SceneDocumentData>;
18
+ commit(change: Omit<DocumentChange, "revision">): Readonly<SceneDocumentData>;
19
+ replace(document: SceneDocumentData, label?: string): void;
20
+ subscribe(listener: DocumentListener, signal?: AbortSignal): () => void;
21
+ }
22
+ //# sourceMappingURL=document-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-store.d.ts","sourceRoot":"","sources":["../src/document-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,CAAC,EACrC,MAAM,EAAE,cAAc,KACnB,IAAI,CAAC;AAEV,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CACpE;AAED,qBAAa,kBAAkB;;gBAO3B,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,yBAA8B;IAMzC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,iBAAiB,CAAC,CAE1C;IAED,MAAM,CACJ,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,GACvC,QAAQ,CAAC,iBAAiB,CAAC;IAiB9B,OAAO,CAAC,QAAQ,EAAE,iBAAiB,EAAE,KAAK,SAAqB,GAAG,IAAI;IAoBtE,SAAS,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,IAAI;CAwBxE"}
@@ -0,0 +1,81 @@
1
+ import { applyPatchesChecked, cloneDocumentPatches, deepFreeze, } from "./patch.js";
2
+ export class SceneDocumentStore {
3
+ #document;
4
+ #revision = 0;
5
+ #listeners = new Set();
6
+ #onListenerError;
7
+ constructor(document, options = {}) {
8
+ this.#document = ownDocument(document);
9
+ this.#onListenerError = options.onListenerError ?? (() => undefined);
10
+ }
11
+ get revision() {
12
+ return this.#revision;
13
+ }
14
+ get snapshot() {
15
+ return this.#document;
16
+ }
17
+ commit(change) {
18
+ if (change.patches.length === 0)
19
+ return this.#document;
20
+ const patches = deepFreeze(cloneDocumentPatches(change.patches));
21
+ this.#document = deepFreeze(applyPatchesChecked(this.#document, patches));
22
+ this.#revision += 1;
23
+ const committed = Object.freeze({
24
+ id: change.id,
25
+ label: change.label,
26
+ patches,
27
+ revision: this.#revision,
28
+ });
29
+ this.#notify(committed);
30
+ return this.#document;
31
+ }
32
+ replace(document, label = "Replace document") {
33
+ const previous = this.#document;
34
+ this.#document = ownDocument(document);
35
+ this.#revision += 1;
36
+ const change = Object.freeze({
37
+ id: `replace-${this.#revision}`,
38
+ label,
39
+ revision: this.#revision,
40
+ patches: deepFreeze([
41
+ {
42
+ op: "set",
43
+ path: [],
44
+ value: this.#document,
45
+ previousValue: previous,
46
+ },
47
+ ]),
48
+ });
49
+ this.#notify(change);
50
+ }
51
+ subscribe(listener, signal) {
52
+ if (signal?.aborted)
53
+ return () => undefined;
54
+ this.#listeners.add(listener);
55
+ const unsubscribe = () => {
56
+ this.#listeners.delete(listener);
57
+ signal?.removeEventListener("abort", unsubscribe);
58
+ };
59
+ signal?.addEventListener("abort", unsubscribe, { once: true });
60
+ return unsubscribe;
61
+ }
62
+ #notify(change) {
63
+ for (const listener of [...this.#listeners]) {
64
+ try {
65
+ listener(this.#document, change);
66
+ }
67
+ catch (error) {
68
+ try {
69
+ this.#onListenerError(error, change);
70
+ }
71
+ catch {
72
+ // Listener diagnostics must not turn a completed commit into a failure.
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ function ownDocument(document) {
79
+ return deepFreeze(structuredClone(document));
80
+ }
81
+ //# sourceMappingURL=document-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-store.js","sourceRoot":"","sources":["../src/document-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./document-store.js";
2
+ export * from "./patch.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./document-store.js";
2
+ export * from "./patch.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC"}
@@ -0,0 +1,31 @@
1
+ export type DocumentPath = readonly (string | number)[];
2
+ export interface SetPatch {
3
+ op: "set";
4
+ path: DocumentPath;
5
+ value: unknown;
6
+ previousValue: unknown;
7
+ }
8
+ export interface DeletePatch {
9
+ op: "delete";
10
+ path: DocumentPath;
11
+ previousValue: unknown;
12
+ }
13
+ export type DocumentPatch = SetPatch | DeletePatch;
14
+ export declare class DocumentPatchConflictError extends Error {
15
+ readonly index: number;
16
+ readonly path: DocumentPath;
17
+ readonly expected: unknown;
18
+ readonly actual: unknown;
19
+ constructor(index: number, patch: DocumentPatch, actual: unknown);
20
+ }
21
+ export declare function cloneDocumentPatches(patches: readonly DocumentPatch[]): DocumentPatch[];
22
+ export declare function deepFreeze<T>(value: T): Readonly<T>;
23
+ export declare function getAtPath(root: unknown, path: DocumentPath): unknown;
24
+ export declare function setAtPath<T>(root: T, path: DocumentPath, value: unknown): T;
25
+ export declare function deleteAtPath<T>(root: T, path: DocumentPath): T;
26
+ export declare function applyPatch<T>(document: T, patch: DocumentPatch): T;
27
+ export declare function applyPatches<T>(document: T, patches: readonly DocumentPatch[]): T;
28
+ export declare function applyPatchesChecked<T>(document: T, patches: readonly DocumentPatch[]): T;
29
+ export declare function invertPatch(patch: DocumentPatch): DocumentPatch;
30
+ export declare function invertPatches(patches: readonly DocumentPatch[]): DocumentPatch[];
31
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;AAExD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnD,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;gBAEb,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO;CAQjE;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS,aAAa,EAAE,GAChC,aAAa,EAAE,CAejB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAWnD;AAQD,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CASpE;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAgB3E;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,CAAC,CA6B9D;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,GAAG,CAAC,CAIlE;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,SAAS,aAAa,EAAE,GAChC,CAAC,CAIH;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,QAAQ,EAAE,CAAC,EACX,OAAO,EAAE,SAAS,aAAa,EAAE,GAChC,CAAC,CAaH;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAsB/D;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,aAAa,EAAE,GAChC,aAAa,EAAE,CAEjB"}
package/dist/patch.js ADDED
@@ -0,0 +1,189 @@
1
+ export class DocumentPatchConflictError extends Error {
2
+ index;
3
+ path;
4
+ expected;
5
+ actual;
6
+ constructor(index, patch, actual) {
7
+ super(`Document patch ${index} has a stale previous value`);
8
+ this.name = "DocumentPatchConflictError";
9
+ this.index = index;
10
+ this.path = [...patch.path];
11
+ this.expected = cloneDiagnosticValue(patch.previousValue);
12
+ this.actual = cloneDiagnosticValue(actual);
13
+ }
14
+ }
15
+ export function cloneDocumentPatches(patches) {
16
+ return patches.map((patch) => patch.op === "set"
17
+ ? {
18
+ op: "set",
19
+ path: [...patch.path],
20
+ value: structuredClone(patch.value),
21
+ previousValue: structuredClone(patch.previousValue),
22
+ }
23
+ : {
24
+ op: "delete",
25
+ path: [...patch.path],
26
+ previousValue: structuredClone(patch.previousValue),
27
+ });
28
+ }
29
+ export function deepFreeze(value) {
30
+ const visited = new WeakSet();
31
+ const freeze = (candidate) => {
32
+ if (candidate === null || typeof candidate !== "object")
33
+ return;
34
+ if (visited.has(candidate))
35
+ return;
36
+ visited.add(candidate);
37
+ for (const child of Object.values(candidate))
38
+ freeze(child);
39
+ Object.freeze(candidate);
40
+ };
41
+ freeze(value);
42
+ return value;
43
+ }
44
+ function cloneContainer(value, nextKey) {
45
+ if (Array.isArray(value))
46
+ return [...value];
47
+ if (value !== null && typeof value === "object")
48
+ return { ...value };
49
+ return typeof nextKey === "number" ? [] : {};
50
+ }
51
+ export function getAtPath(root, path) {
52
+ let current = root;
53
+ for (const key of path) {
54
+ if (current === null || typeof current !== "object")
55
+ return undefined;
56
+ const record = current;
57
+ if (!Object.hasOwn(record, key))
58
+ return undefined;
59
+ current = record[key];
60
+ }
61
+ return current;
62
+ }
63
+ export function setAtPath(root, path, value) {
64
+ if (path.length === 0)
65
+ return value;
66
+ const [key, ...rest] = path;
67
+ if (key === undefined)
68
+ return root;
69
+ const container = cloneContainer(root, key);
70
+ const previous = Object.hasOwn(container, key) ? container[key] : undefined;
71
+ Object.defineProperty(container, key, {
72
+ value: setAtPath(previous, rest, value),
73
+ enumerable: true,
74
+ configurable: true,
75
+ writable: true,
76
+ });
77
+ return container;
78
+ }
79
+ export function deleteAtPath(root, path) {
80
+ if (path.length === 0) {
81
+ throw new Error("The document root cannot be deleted");
82
+ }
83
+ const [key, ...rest] = path;
84
+ if (key === undefined)
85
+ return root;
86
+ if (root === null || typeof root !== "object" || !Object.hasOwn(root, key)) {
87
+ return root;
88
+ }
89
+ const container = cloneContainer(root, key);
90
+ if (rest.length === 0) {
91
+ if (Array.isArray(container) && typeof key === "number") {
92
+ container.splice(key, 1);
93
+ }
94
+ else {
95
+ delete container[key];
96
+ }
97
+ }
98
+ else {
99
+ const previous = Object.hasOwn(container, key) ? container[key] : undefined;
100
+ Object.defineProperty(container, key, {
101
+ value: deleteAtPath(previous, rest),
102
+ enumerable: true,
103
+ configurable: true,
104
+ writable: true,
105
+ });
106
+ }
107
+ return container;
108
+ }
109
+ export function applyPatch(document, patch) {
110
+ return patch.op === "set"
111
+ ? setAtPath(document, patch.path, patch.value)
112
+ : deleteAtPath(document, patch.path);
113
+ }
114
+ export function applyPatches(document, patches) {
115
+ let result = document;
116
+ for (const patch of patches)
117
+ result = applyPatch(result, patch);
118
+ return result;
119
+ }
120
+ export function applyPatchesChecked(document, patches) {
121
+ let result = document;
122
+ for (const [index, patch] of patches.entries()) {
123
+ const actual = getAtPath(result, patch.path);
124
+ if ((patch.op === "delete" && actual === undefined) ||
125
+ !documentValuesEqual(actual, patch.previousValue)) {
126
+ throw new DocumentPatchConflictError(index, patch, actual);
127
+ }
128
+ result = applyPatch(result, patch);
129
+ }
130
+ return result;
131
+ }
132
+ export function invertPatch(patch) {
133
+ if (patch.op === "delete") {
134
+ return {
135
+ op: "set",
136
+ path: patch.path,
137
+ value: patch.previousValue,
138
+ previousValue: undefined,
139
+ };
140
+ }
141
+ if (patch.previousValue === undefined) {
142
+ return {
143
+ op: "delete",
144
+ path: patch.path,
145
+ previousValue: patch.value,
146
+ };
147
+ }
148
+ return {
149
+ op: "set",
150
+ path: patch.path,
151
+ value: patch.previousValue,
152
+ previousValue: patch.value,
153
+ };
154
+ }
155
+ export function invertPatches(patches) {
156
+ return [...patches].reverse().map(invertPatch);
157
+ }
158
+ function documentValuesEqual(left, right) {
159
+ if (Object.is(left, right))
160
+ return true;
161
+ if (left === null ||
162
+ right === null ||
163
+ typeof left !== "object" ||
164
+ typeof right !== "object") {
165
+ return false;
166
+ }
167
+ if (Array.isArray(left) || Array.isArray(right)) {
168
+ return (Array.isArray(left) &&
169
+ Array.isArray(right) &&
170
+ left.length === right.length &&
171
+ left.every((item, index) => documentValuesEqual(item, right[index])));
172
+ }
173
+ const leftRecord = left;
174
+ const rightRecord = right;
175
+ const leftKeys = Object.keys(leftRecord);
176
+ const rightKeys = Object.keys(rightRecord);
177
+ return (leftKeys.length === rightKeys.length &&
178
+ leftKeys.every((key) => Object.hasOwn(rightRecord, key) &&
179
+ documentValuesEqual(leftRecord[key], rightRecord[key])));
180
+ }
181
+ function cloneDiagnosticValue(value) {
182
+ try {
183
+ return structuredClone(value);
184
+ }
185
+ catch {
186
+ return value;
187
+ }
188
+ }
189
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@skenora/document",
3
+ "version": "0.1.2",
4
+ "description": "Immutable SceneDocument patches, conflict checks, and document state for Skenora.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "dependencies": {
22
+ "@skenora/contracts": "0.1.2"
23
+ },
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json && node ../../scripts/prepare-package-output.mjs dist",
26
+ "typecheck": "tsc -p tsconfig.json --noEmit"
27
+ },
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts"
30
+ }