@kuzuma/colwork 0.1.0 → 0.2.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/dist/model.js ADDED
@@ -0,0 +1,259 @@
1
+ import * as p from "yjs";
2
+ const M = 1, g = Object.freeze({
3
+ cells: "cells",
4
+ styles: "styles",
5
+ rowHeights: "rowHeights",
6
+ columnWidths: "columnWidths",
7
+ merges: "merges",
8
+ locks: "locks",
9
+ metadata: "colwork-metadata"
10
+ });
11
+ function w(e) {
12
+ if (!/^(0|[1-9]\d*):(0|[1-9]\d*)$/.test(e)) return;
13
+ const [t, r] = e.split(":").map(Number);
14
+ return Number.isSafeInteger(t) && Number.isSafeInteger(r) ? { row: t, column: r } : void 0;
15
+ }
16
+ function _(e) {
17
+ return `${e.start.row}:${e.start.column}-${e.end.row}:${e.end.column}`;
18
+ }
19
+ function A(e) {
20
+ const t = e.split("-");
21
+ if (t.length !== 2) return;
22
+ const r = w(t[0]), n = w(t[1]);
23
+ if (!(!r || !n || r.row > n.row || r.column > n.column))
24
+ return { start: r, end: n };
25
+ }
26
+ function z(e) {
27
+ for (const t of e) {
28
+ const r = t.codePointAt(0);
29
+ if (r >= 55296 && r <= 57343) return !1;
30
+ }
31
+ return !0;
32
+ }
33
+ function I(e) {
34
+ if (e.length < 2) return !1;
35
+ const t = [...new Set(e.flatMap((i) => [i.start.column, i.end.column + 1]))].sort((i, m) => i - m), r = new Map(t.map((i, m) => [i, m])), n = e.flatMap((i) => [
36
+ { row: i.start.row, left: r.get(i.start.column), right: r.get(i.end.column + 1) - 1, delta: 1 },
37
+ { row: i.end.row + 1, left: r.get(i.start.column), right: r.get(i.end.column + 1) - 1, delta: -1 }
38
+ ]).sort((i, m) => i.row - m.row || i.delta - m.delta), f = new Int32Array(t.length * 4), d = new Int32Array(t.length * 4), l = (i, m, c, u, o, s) => {
39
+ if (u <= m && c <= o) {
40
+ f[i] += s, d[i] += s;
41
+ return;
42
+ }
43
+ const a = Math.floor((m + c) / 2);
44
+ u <= a && l(i * 2, m, a, u, o, s), o > a && l(i * 2 + 1, a + 1, c, u, o, s), f[i] = d[i] + Math.max(f[i * 2], f[i * 2 + 1]);
45
+ };
46
+ for (const i of n)
47
+ if (l(1, 0, t.length - 2, i.left, i.right, i.delta), f[1] > 1) return !0;
48
+ return !1;
49
+ }
50
+ const S = (e) => !!e && typeof e == "object" && !Array.isArray(e) && Object.getPrototypeOf(e) === Object.prototype, x = (e, t) => Object.keys(e).every((r) => t.includes(r));
51
+ function C(e) {
52
+ return S(e) && x(e, ["version", "algorithm", "iterations", "salt", "verifier"]) && e.version === 1 && e.algorithm === "PBKDF2-SHA-256" && e.iterations === 6e5 && typeof e.salt == "string" && /^[0-9a-f]{32}$/.test(e.salt) && typeof e.verifier == "string" && /^[0-9a-f]{64}$/.test(e.verifier);
53
+ }
54
+ function R(e) {
55
+ if (!S(e) || !x(e, ["bold", "italic", "underline", "strike", "wrap", "border", "fontSize", "color", "background", "align", "format"])) return !1;
56
+ for (const t of ["bold", "italic", "underline", "strike", "wrap"]) if (t in e && typeof e[t] != "boolean") return !1;
57
+ for (const t of ["color", "background"]) if (t in e && (typeof e[t] != "string" || !z(e[t]))) return !1;
58
+ if ("fontSize" in e && (typeof e.fontSize != "number" || !Number.isFinite(e.fontSize) || e.fontSize <= 0) || "align" in e && (typeof e.align != "string" || !["left", "center", "right"].includes(e.align)) || "format" in e && (typeof e.format != "string" || !["general", "number", "date", "percent"].includes(e.format))) return !1;
59
+ if ("border" in e) {
60
+ if (!S(e.border) || !x(e.border, ["top", "right", "bottom", "left"])) return !1;
61
+ for (const t of Object.keys(e.border)) if (typeof e.border[t] != "boolean") return !1;
62
+ }
63
+ return !0;
64
+ }
65
+ function k(e) {
66
+ return {
67
+ cells: e.getMap(g.cells),
68
+ styles: e.getMap(g.styles),
69
+ rowHeights: e.getMap(g.rowHeights),
70
+ columnWidths: e.getMap(g.columnWidths),
71
+ merges: e.getMap(g.merges),
72
+ locks: e.getMap(g.locks)
73
+ };
74
+ }
75
+ function j(e, t = {}) {
76
+ for (const o of ["maxRows", "maxColumns", "maxEntries", "maxStringLength", "maxMergeArea", "maxMerges"]) {
77
+ const s = t[o];
78
+ if (s !== void 0 && (!Number.isSafeInteger(s) || s < 0)) throw new RangeError(`${o} must be a non-negative safe integer`);
79
+ }
80
+ const r = [];
81
+ (e.store.pendingStructs !== null || e.store.pendingDs !== null) && r.push({ path: "$", message: "Document contains unresolved Yjs update dependencies" });
82
+ const n = (o, s) => {
83
+ r.push({ path: o, message: s });
84
+ };
85
+ if (!t.allowUnknownRoots)
86
+ for (const o of e.share.keys())
87
+ Object.values(g).includes(o) || n(o, "Unknown document root");
88
+ let f, d;
89
+ try {
90
+ f = k(e), d = e.getMap(g.metadata);
91
+ } catch {
92
+ return [...r, { path: "$", message: "Sheet roots must be Y.Map values" }];
93
+ }
94
+ for (const [o, s] of [...Object.entries(f), ["metadata", d]])
95
+ (!(s instanceof p.Map) || s._start !== null) && n(o, "Expected map entries without sequence content");
96
+ const l = Object.values(f).reduce((o, s) => o + s.size, d.size);
97
+ t.maxEntries !== void 0 && l > t.maxEntries && n("$", "Document exceeds maxEntries");
98
+ const i = (o, s) => o < Number.MAX_SAFE_INTEGER && s < Number.MAX_SAFE_INTEGER && (t.maxRows === void 0 || o < t.maxRows) && (t.maxColumns === void 0 || s < t.maxColumns), m = (o, s) => {
99
+ if (typeof o != "string") return !1;
100
+ try {
101
+ return s(JSON.parse(o));
102
+ } catch {
103
+ return !1;
104
+ }
105
+ }, c = (o, s, a) => {
106
+ typeof a == "string" && t.maxStringLength !== void 0 && a.length > t.maxStringLength && n(`${o}.${s}`, "String exceeds maxStringLength");
107
+ };
108
+ for (const o of ["cells", "styles", "locks"])
109
+ f[o].forEach((s, a) => {
110
+ const h = w(a);
111
+ (!h || !i(h.row, h.column)) && n(`${o}.${a}`, "Invalid or out-of-bounds row:column key"), c(o, a, s), o === "cells" && (typeof s != "string" || !z(s)) && n(`${o}.${a}`, "Cell values must be strings"), o === "styles" && !m(s, R) && n(`${o}.${a}`, "Expected a JSON CellStyle object"), o === "locks" && s !== "1" && !m(s, C) && n(`${o}.${a}`, "Expected a plain lock or version-one password verifier");
112
+ });
113
+ for (const o of ["rowHeights", "columnWidths"])
114
+ f[o].forEach((s, a) => {
115
+ const h = Number(a), E = o === "rowHeights" ? t.maxRows : t.maxColumns;
116
+ (!/^(0|[1-9]\d*)$/.test(a) || !Number.isSafeInteger(h) || h >= Number.MAX_SAFE_INTEGER || E !== void 0 && h >= E) && n(`${o}.${a}`, "Invalid or out-of-bounds index"), (typeof s != "number" || !Number.isFinite(s) || s <= 0) && n(`${o}.${a}`, "Sizes must be finite positive numbers");
117
+ });
118
+ t.maxMerges !== void 0 && f.merges.size > t.maxMerges && n("merges", "Document exceeds maxMerges");
119
+ const u = [];
120
+ return f.merges.forEach((o, s) => {
121
+ const a = A(s);
122
+ !a || o !== "1" || !i(a.end.row, a.end.column) ? n(`merges.${s}`, "Invalid or out-of-bounds merge range") : (t.maxMergeArea !== void 0 && a.end.row - a.start.row + 1 > t.maxMergeArea / (a.end.column - a.start.column + 1) && n(`merges.${s}`, "Merge exceeds maxMergeArea"), u.push(a));
123
+ }), I(u) && n("merges", "Merge ranges must not overlap"), d.forEach((o, s) => {
124
+ s === "schemaVersion" ? o !== M && n("metadata.schemaVersion", "Unsupported schema version") : s === "initialized" ? typeof o != "boolean" && n("metadata.initialized", "Expected a boolean") : n(`metadata.${s}`, "Unknown metadata field");
125
+ }), r;
126
+ }
127
+ function y(e) {
128
+ let t = "";
129
+ for (let n = 0; n < e.length; n += 32768)
130
+ t += String.fromCharCode(...e.subarray(n, n + 32768));
131
+ return btoa(t);
132
+ }
133
+ function $(e) {
134
+ const t = atob(e);
135
+ return Uint8Array.from(t, (r) => r.charCodeAt(0));
136
+ }
137
+ function N(e) {
138
+ if (e != null && e.trim())
139
+ return JSON.parse(e);
140
+ }
141
+ function U(e) {
142
+ if (!(!e || e.format === "colwork.update-log"))
143
+ return e.format === "colwork.archive" ? e.snapshot : e;
144
+ }
145
+ function O(e) {
146
+ return !e || e.format === "colwork.snapshot" ? [] : e.updates ?? [];
147
+ }
148
+ function W(e, t) {
149
+ const r = N(e), n = N(t), f = U(r), d = [...O(r), ...O(n)], l = new p.Doc();
150
+ try {
151
+ f != null && f.snapshot && p.applyUpdate(l, $(f.snapshot));
152
+ const i = Number((f == null ? void 0 : f.sequence) ?? 0), m = /* @__PURE__ */ new Set(), c = /* @__PURE__ */ new Set();
153
+ let u = i;
154
+ for (const o of d.sort((s, a) => Number(s.seq) - Number(a.seq))) {
155
+ const s = Number(o.seq);
156
+ !Number.isInteger(s) || typeof o.update != "string" || s <= i || m.has(s) || c.has(o.update) || (p.applyUpdate(l, $(o.update)), m.add(s), c.add(o.update), u = Math.max(u, s));
157
+ }
158
+ return {
159
+ format: "colwork.snapshot",
160
+ version: 1,
161
+ room: f == null ? void 0 : f.room,
162
+ sequence: u,
163
+ snapshot: y(p.encodeStateAsUpdate(l)),
164
+ stateVector: y(p.encodeStateVector(l))
165
+ };
166
+ } finally {
167
+ l.destroy();
168
+ }
169
+ }
170
+ function q(e, t) {
171
+ const r = e.split(":").map(Number), n = t.split(":").map(Number);
172
+ return r[0] - n[0] || (r[1] ?? 0) - (n[1] ?? 0);
173
+ }
174
+ function b(e) {
175
+ return [...e].sort(([t], [r]) => q(t, r));
176
+ }
177
+ function V(e) {
178
+ const t = {};
179
+ for (const r of ["bold", "italic", "underline", "strike", "wrap", "fontSize", "color", "background", "align", "format"])
180
+ e[r] !== void 0 && (t[r] = e[r]);
181
+ if (e.border !== void 0) {
182
+ const r = {};
183
+ for (const n of ["top", "right", "bottom", "left"]) e.border[n] !== void 0 && (r[n] = e.border[n]);
184
+ t.border = r;
185
+ }
186
+ return t;
187
+ }
188
+ function H(e, t) {
189
+ const r = j(e, t);
190
+ if (r.length) throw new TypeError(`Invalid Colwork document at ${r[0].path}: ${r[0].message}`);
191
+ const n = k(e), f = Object.fromEntries(b(n.styles).map(([c, u]) => [c, V(JSON.parse(u))])), d = Object.fromEntries(b(n.locks).map(([c, u]) => {
192
+ if (u === "1") return [c, "1"];
193
+ const o = JSON.parse(u);
194
+ return [c, { version: o.version, algorithm: o.algorithm, iterations: o.iterations, salt: o.salt, verifier: o.verifier }];
195
+ })), l = [...n.merges.keys()].map((c) => A(c)).sort((c, u) => c.start.row - u.start.row || c.start.column - u.start.column || c.end.row - u.end.row || c.end.column - u.end.column);
196
+ let i = 1, m = 1;
197
+ for (const c of [...n.cells.keys(), ...n.styles.keys(), ...n.locks.keys()]) {
198
+ const u = w(c);
199
+ i = Math.max(i, u.row + 1), m = Math.max(m, u.column + 1);
200
+ }
201
+ for (const c of l)
202
+ i = Math.max(i, c.end.row + 1), m = Math.max(m, c.end.column + 1);
203
+ for (const c of n.rowHeights.keys()) i = Math.max(i, Number(c) + 1);
204
+ for (const c of n.columnWidths.keys()) m = Math.max(m, Number(c) + 1);
205
+ return {
206
+ schemaVersion: M,
207
+ rowCount: i,
208
+ columnCount: m,
209
+ cells: Object.fromEntries(b(n.cells)),
210
+ styles: f,
211
+ rowHeights: Object.fromEntries(b(n.rowHeights)),
212
+ columnWidths: Object.fromEntries(b(n.columnWidths)),
213
+ merges: l,
214
+ locks: d
215
+ };
216
+ }
217
+ function D(e) {
218
+ const t = j(e);
219
+ if (t.length) throw new TypeError(`Invalid Colwork document at ${t[0].path}: ${t[0].message}`);
220
+ if (Object.values(k(e)).some((n) => n.size > 0)) throw new Error("Blank initialization requires an empty sheet");
221
+ const r = e.getMap(g.metadata);
222
+ e.transact(() => {
223
+ r.set("schemaVersion", M), r.set("initialized", !0);
224
+ });
225
+ }
226
+ function K(e, t = {}) {
227
+ const r = t.sequence ?? 0;
228
+ if (!Number.isSafeInteger(r) || r < 0) throw new RangeError("sequence must be a non-negative safe integer");
229
+ return {
230
+ format: "colwork.snapshot",
231
+ version: 1,
232
+ room: t.room,
233
+ sequence: r,
234
+ snapshot: y(p.encodeStateAsUpdate(e)),
235
+ stateVector: y(p.encodeStateVector(e))
236
+ };
237
+ }
238
+ function L(e, t) {
239
+ const r = new p.Doc();
240
+ try {
241
+ return p.applyUpdate(r, e), H(r, t);
242
+ } finally {
243
+ r.destroy();
244
+ }
245
+ }
246
+ export {
247
+ g as COLWORK_MAP_NAMES,
248
+ M as COLWORK_SCHEMA_VERSION,
249
+ K as exportYjsSnapshot,
250
+ k as getSheetMaps,
251
+ D as initializeBlankSheet,
252
+ _ as mergeKey,
253
+ w as parseCellKey,
254
+ A as parseMergeKey,
255
+ H as readSheetSnapshot,
256
+ L as readSheetSnapshotUpdate,
257
+ W as rebuildYjsSnapshot,
258
+ j as validateSheetDocument
259
+ };
@@ -0,0 +1,15 @@
1
+ export type CellPosition = {
2
+ row: number;
3
+ column: number;
4
+ };
5
+ export type CellRange = {
6
+ start: CellPosition;
7
+ end: CellPosition;
8
+ };
9
+ export declare function normalizeRange(start: CellPosition, end: CellPosition): CellRange;
10
+ export declare function containsPosition(range: CellRange, position: CellPosition): boolean;
11
+ export declare function rangeSize(range: CellRange): {
12
+ rows: number;
13
+ columns: number;
14
+ };
15
+ export declare function cellKey(position: CellPosition): string;
@@ -0,0 +1,15 @@
1
+ export type CellPosition = {
2
+ row: number;
3
+ column: number;
4
+ };
5
+ export type CellRange = {
6
+ start: CellPosition;
7
+ end: CellPosition;
8
+ };
9
+ export declare function normalizeRange(start: CellPosition, end: CellPosition): CellRange;
10
+ export declare function containsPosition(range: CellRange, position: CellPosition): boolean;
11
+ export declare function rangeSize(range: CellRange): {
12
+ rows: number;
13
+ columns: number;
14
+ };
15
+ export declare function cellKey(position: CellPosition): string;
@@ -0,0 +1,28 @@
1
+ export type YjsUpdateRecord = {
2
+ seq: number;
3
+ update: string;
4
+ timestamp: number;
5
+ };
6
+ export type YjsSnapshotFile = {
7
+ format: 'colwork.snapshot';
8
+ version: 1;
9
+ room?: string;
10
+ sequence: number;
11
+ snapshot: string;
12
+ stateVector: string;
13
+ };
14
+ export type YjsUpdateLogFile = {
15
+ format: 'colwork.update-log';
16
+ version: 1;
17
+ room?: string;
18
+ updates: YjsUpdateRecord[];
19
+ };
20
+ export type YjsArchiveFile = {
21
+ format: 'colwork.archive';
22
+ version: 1;
23
+ room?: string;
24
+ snapshot?: YjsSnapshotFile;
25
+ updates: YjsUpdateRecord[];
26
+ };
27
+ export declare function bytesToBase64(bytes: Uint8Array): string;
28
+ export declare function base64ToBytes(value: string): Uint8Array<ArrayBuffer>;
@@ -0,0 +1,28 @@
1
+ export type YjsUpdateRecord = {
2
+ seq: number;
3
+ update: string;
4
+ timestamp: number;
5
+ };
6
+ export type YjsSnapshotFile = {
7
+ format: 'colwork.snapshot';
8
+ version: 1;
9
+ room?: string;
10
+ sequence: number;
11
+ snapshot: string;
12
+ stateVector: string;
13
+ };
14
+ export type YjsUpdateLogFile = {
15
+ format: 'colwork.update-log';
16
+ version: 1;
17
+ room?: string;
18
+ updates: YjsUpdateRecord[];
19
+ };
20
+ export type YjsArchiveFile = {
21
+ format: 'colwork.archive';
22
+ version: 1;
23
+ room?: string;
24
+ snapshot?: YjsSnapshotFile;
25
+ updates: YjsUpdateRecord[];
26
+ };
27
+ export declare function bytesToBase64(bytes: Uint8Array): string;
28
+ export declare function base64ToBytes(value: string): Uint8Array<ArrayBuffer>;
@@ -0,0 +1,2 @@
1
+ import { YjsSnapshotFile } from './yjsArchive.cjs';
2
+ export declare function rebuildYjsSnapshot(snapshotText?: string, updateLogText?: string): YjsSnapshotFile;
@@ -0,0 +1,2 @@
1
+ import { YjsSnapshotFile } from './yjsArchive.js';
2
+ export declare function rebuildYjsSnapshot(snapshotText?: string, updateLogText?: string): YjsSnapshotFile;
package/package.json CHANGED
@@ -1,23 +1,24 @@
1
1
  {
2
2
  "name": "@kuzuma/colwork",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "npm run dev:test",
8
- "dev:test": "node server/index.mjs --mode test",
7
+ "dev": "node script/demo.mjs dev",
8
+ "dev:test": "node script/demo.mjs dev",
9
9
  "server": "node server/index.mjs",
10
10
  "server:test": "node server/index.mjs --mode test",
11
11
  "rebuild:yjs": "node script/rebuild-yjs.mjs",
12
12
  "build": "npm run build:lib",
13
- "build:lib": "tsc --noEmit && vite build --config vite.lib.config.ts",
13
+ "build:lib": "tsc --noEmit && vite build --config vite.lib.config.ts && vite build --config vite.model.config.ts",
14
14
  "test:e2e": "playwright test",
15
- "build:site": "tsc --noEmit && vite build --config vite.site.config.ts",
15
+ "build:site": "node script/demo.mjs build",
16
16
  "dev:cloudflare": "npm run build:site && wrangler dev --config config/cloudflare/wrangler.dev.json --persist-to .wrangler/state",
17
17
  "deploy:check": "node script/deploy-cloudflare.mjs --dry-run",
18
18
  "deploy": "node script/deploy-cloudflare.mjs",
19
19
  "test:cloudflare": "node --test test/cloudflare/*.test.mjs",
20
- "prepublishOnly": "npm run build:lib"
20
+ "prepublishOnly": "npm run build:lib",
21
+ "test:model": "node --test test/model/*.test.mjs"
21
22
  },
22
23
  "dependencies": {
23
24
  "y-protocols": "^1.0.7",
@@ -51,7 +52,17 @@
51
52
  "import": "./dist/colwork.js",
52
53
  "require": "./dist/colwork.umd.cjs"
53
54
  },
54
- "./colwork.css": "./dist/colwork.css"
55
+ "./colwork.css": "./dist/colwork.css",
56
+ "./model": {
57
+ "import": {
58
+ "types": "./dist/model.d.ts",
59
+ "default": "./dist/model.js"
60
+ },
61
+ "require": {
62
+ "types": "./dist/model.d.cts",
63
+ "default": "./dist/model.cjs"
64
+ }
65
+ }
55
66
  },
56
67
  "files": [
57
68
  "dist"