kindy-editor 1.0.1 → 2.0.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.
@@ -0,0 +1,786 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/core/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_PAGE_STATE: () => DEFAULT_PAGE_STATE,
24
+ DOCUMENT_ERROR_CODES: () => DOCUMENT_ERROR_CODES,
25
+ DocumentLibraryClient: () => DocumentLibraryClient,
26
+ DocumentLibraryError: () => DocumentLibraryError,
27
+ KINDY_DOCUMENT_SCHEMA_VERSION: () => KINDY_DOCUMENT_SCHEMA_VERSION,
28
+ MemoryDocumentAdapter: () => MemoryDocumentAdapter,
29
+ RestDocumentAdapter: () => RestDocumentAdapter,
30
+ TypedEventEmitter: () => TypedEventEmitter,
31
+ YjsCollaborationAdapter: () => YjsCollaborationAdapter,
32
+ cloneDocumentState: () => cloneDocumentState,
33
+ createDocumentLibrary: () => createDocumentLibrary,
34
+ createEditorDocumentState: () => createEditorDocumentState,
35
+ createEmptyDocumentState: () => createEmptyDocumentState,
36
+ createLegacyCallbackAdapter: () => createLegacyCallbackAdapter,
37
+ createMemoryDocumentAdapter: () => createMemoryDocumentAdapter,
38
+ createRestDocumentAdapter: () => createRestDocumentAdapter,
39
+ createYjsCollaborationAdapter: () => createYjsCollaborationAdapter,
40
+ migrateDocumentState: () => migrateDocumentState,
41
+ stateFromEditor: () => stateFromEditor,
42
+ toCanonicalHeaderFooter: () => toCanonicalHeaderFooter,
43
+ toCanonicalSection: () => toCanonicalSection,
44
+ toDocumentLibraryError: () => toDocumentLibraryError
45
+ });
46
+ module.exports = __toCommonJS(index_exports);
47
+
48
+ // src/core/types.ts
49
+ var KINDY_DOCUMENT_SCHEMA_VERSION = "2.0";
50
+
51
+ // src/core/errors.ts
52
+ var DOCUMENT_ERROR_CODES = [
53
+ "DOCX_INVALID",
54
+ "DOCX_UNSUPPORTED",
55
+ "IMPORT_FAILED",
56
+ "EXPORT_FAILED",
57
+ "ADAPTER_ERROR",
58
+ "NETWORK_ERROR",
59
+ "VERSION_CONFLICT",
60
+ "DOCUMENT_NOT_FOUND",
61
+ "OPERATION_CANCELLED"
62
+ ];
63
+ var DocumentLibraryError = class extends Error {
64
+ code;
65
+ status;
66
+ details;
67
+ constructor(code, message, options = {}) {
68
+ super(message, options.cause ? { cause: options.cause } : void 0);
69
+ this.name = "DocumentLibraryError";
70
+ this.code = code;
71
+ this.status = options.status;
72
+ this.details = options.details;
73
+ }
74
+ };
75
+ function toDocumentLibraryError(error, fallback = "ADAPTER_ERROR") {
76
+ if (error instanceof DocumentLibraryError) return error;
77
+ if (error instanceof DOMException && error.name === "AbortError") {
78
+ return new DocumentLibraryError("OPERATION_CANCELLED", "Operation was cancelled.", { cause: error });
79
+ }
80
+ const message = error instanceof Error ? error.message : String(error || "Unknown error");
81
+ return new DocumentLibraryError(fallback, message, { cause: error });
82
+ }
83
+
84
+ // src/core/state.ts
85
+ var DEFAULT_PAGE_STATE = Object.freeze({
86
+ size: { width: 21, height: 29.7 },
87
+ orientation: "portrait",
88
+ margin: { top: 2.54, right: 2.54, bottom: 2.54, left: 2.54 },
89
+ background: "#ffffff",
90
+ header: { enabled: false, text: "" },
91
+ footer: { enabled: false, text: "" },
92
+ sections: []
93
+ });
94
+ var emptyDocument = { type: "doc", content: [{ type: "paragraph" }] };
95
+ function cloneDocumentState(value) {
96
+ if (typeof structuredClone === "function") {
97
+ try {
98
+ return structuredClone(value);
99
+ } catch {
100
+ }
101
+ }
102
+ return JSON.parse(JSON.stringify(value));
103
+ }
104
+ function createEmptyDocumentState(overrides = {}) {
105
+ const page = overrides.page || {};
106
+ return {
107
+ schemaVersion: KINDY_DOCUMENT_SCHEMA_VERSION,
108
+ content: cloneDocumentState(overrides.content || emptyDocument),
109
+ page: {
110
+ ...cloneDocumentState(DEFAULT_PAGE_STATE),
111
+ ...page,
112
+ size: page.size || cloneDocumentState(DEFAULT_PAGE_STATE.size),
113
+ orientation: page.orientation || DEFAULT_PAGE_STATE.orientation,
114
+ margin: {
115
+ ...DEFAULT_PAGE_STATE.margin,
116
+ ...page.margin || {}
117
+ }
118
+ },
119
+ assets: cloneDocumentState(overrides.assets || [])
120
+ };
121
+ }
122
+ function migrateDocumentState(input) {
123
+ if (!input || typeof input !== "object") return createEmptyDocumentState();
124
+ const value = input;
125
+ if (value.schemaVersion === KINDY_DOCUMENT_SCHEMA_VERSION && value.content) {
126
+ return createEmptyDocumentState(value);
127
+ }
128
+ if (value.type === "doc") {
129
+ return createEmptyDocumentState({ content: value });
130
+ }
131
+ const legacyDocument = value.document;
132
+ const legacyContent = value.content || legacyDocument?.content;
133
+ const content = legacyContent && typeof legacyContent === "object" ? legacyContent : emptyDocument;
134
+ return createEmptyDocumentState({
135
+ content,
136
+ page: value.page || {},
137
+ assets: Array.isArray(value.assets) ? value.assets : []
138
+ });
139
+ }
140
+ function stateFromEditor(editor, page) {
141
+ return createEmptyDocumentState({ content: editor.getJSON(), page });
142
+ }
143
+
144
+ // src/core/editor-state.ts
145
+ function toCanonicalHeaderFooter(value) {
146
+ if (!value) return void 0;
147
+ const first = value.variants?.first || {};
148
+ const even = value.variants?.even || {};
149
+ return {
150
+ enabled: Boolean(value.enable ?? value.enabled),
151
+ ...value.content ? { content: cloneDocumentState(value.content) } : {},
152
+ text: value.text || value.leftText || "",
153
+ ...first.content ? { firstContent: cloneDocumentState(first.content) } : {},
154
+ firstText: first.text || "",
155
+ ...even.content ? { evenContent: cloneDocumentState(even.content) } : {},
156
+ evenText: even.text || "",
157
+ differentFirstPage: Boolean(value.differentFirstPage),
158
+ differentOddEven: Boolean(value.differentOddEven)
159
+ };
160
+ }
161
+ function toCanonicalSection(section, page) {
162
+ const defaultState = createEmptyDocumentState().page;
163
+ return {
164
+ id: section.id || `section-${section.fromBlockId || "default"}`,
165
+ ...section.fromBlockId ? { fromBlockId: section.fromBlockId } : {},
166
+ ...Number.isFinite(section.pageNumberStart) ? { pageNumberStart: section.pageNumberStart } : {},
167
+ size: {
168
+ width: section.size?.width || page.size?.width || defaultState.size.width,
169
+ height: section.size?.height || page.size?.height || defaultState.size.height
170
+ },
171
+ orientation: section.orientation || page.orientation || defaultState.orientation,
172
+ margin: {
173
+ ...defaultState.margin,
174
+ ...page.margin || {},
175
+ ...section.margin || {}
176
+ },
177
+ ...section.header ? { header: toCanonicalHeaderFooter(section.header) } : {},
178
+ ...section.footer ? { footer: toCanonicalHeaderFooter(section.footer) } : {}
179
+ };
180
+ }
181
+ function createEditorDocumentState(input) {
182
+ const page = input.page || {};
183
+ return createEmptyDocumentState({
184
+ content: input.content,
185
+ assets: cloneDocumentState(input.assets || []),
186
+ page: {
187
+ size: page.size?.width && page.size?.height ? { width: page.size.width, height: page.size.height } : void 0,
188
+ orientation: page.orientation,
189
+ margin: page.margin,
190
+ background: page.background,
191
+ watermark: page.watermark,
192
+ header: toCanonicalHeaderFooter(page.header),
193
+ footer: toCanonicalHeaderFooter(page.footer),
194
+ sections: Array.isArray(page.sections) ? page.sections.map((section) => toCanonicalSection(section, page)) : []
195
+ }
196
+ });
197
+ }
198
+
199
+ // src/core/emitter.ts
200
+ var TypedEventEmitter = class {
201
+ listeners = /* @__PURE__ */ new Map();
202
+ on(event, listener) {
203
+ const listeners = this.listeners.get(event) || /* @__PURE__ */ new Set();
204
+ listeners.add(listener);
205
+ this.listeners.set(event, listeners);
206
+ return () => listeners.delete(listener);
207
+ }
208
+ emit(event, payload) {
209
+ this.listeners.get(event)?.forEach((listener) => listener(payload));
210
+ }
211
+ clear() {
212
+ this.listeners.clear();
213
+ }
214
+ };
215
+
216
+ // src/core/client.ts
217
+ var DocumentLibraryClient = class extends TypedEventEmitter {
218
+ adapter;
219
+ codecs;
220
+ engine;
221
+ locale;
222
+ theme;
223
+ autosaveEnabled;
224
+ autosaveDelay;
225
+ autosaveTimer = null;
226
+ saveController = null;
227
+ savePromise = null;
228
+ snapshot = null;
229
+ dirty = false;
230
+ conflicted = false;
231
+ mutation = 0;
232
+ constructor(options) {
233
+ super();
234
+ this.adapter = options.adapter;
235
+ this.codecs = options.codecs;
236
+ this.engine = options.engine;
237
+ this.locale = options.locale || "vi-VN";
238
+ this.theme = options.theme || {};
239
+ this.autosaveEnabled = options.autosave?.enabled ?? true;
240
+ this.autosaveDelay = Math.max(250, options.autosave?.delay ?? 3e4);
241
+ }
242
+ get current() {
243
+ return this.snapshot ? cloneDocumentState(this.snapshot) : null;
244
+ }
245
+ get hasUnsavedChanges() {
246
+ return this.dirty;
247
+ }
248
+ get hasConflict() {
249
+ return this.conflicted;
250
+ }
251
+ listDocuments(query, signal) {
252
+ return this.adapter.listDocuments(query, signal);
253
+ }
254
+ async open(documentId, versionId, signal) {
255
+ this.cancelPendingSave();
256
+ const snapshot = await this.adapter.loadState(documentId, versionId, signal);
257
+ snapshot.state = migrateDocumentState(snapshot.state);
258
+ this.snapshot = snapshot;
259
+ this.dirty = false;
260
+ this.conflicted = false;
261
+ const current = this.current;
262
+ this.emit("opened", current);
263
+ return current;
264
+ }
265
+ async create(input, signal) {
266
+ const document = await this.adapter.createDocument({ ...input, state: input.state || createEmptyDocumentState() }, signal);
267
+ return this.open(document.id, void 0, signal);
268
+ }
269
+ async import(input, signal) {
270
+ const document = await this.adapter.importDocument({ ...input, state: migrateDocumentState(input.state) }, signal);
271
+ this.emit("imported", { document, compatibilityReport: input.compatibilityReport });
272
+ return this.open(document.id, void 0, signal);
273
+ }
274
+ updateState(state) {
275
+ if (!this.snapshot) throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", "Open a document before updating state.");
276
+ this.snapshot.state = migrateDocumentState(state);
277
+ this.mutation += 1;
278
+ this.dirty = true;
279
+ const current = this.current;
280
+ this.emit("changed", current);
281
+ if (this.autosaveEnabled && !this.conflicted) this.scheduleAutosave();
282
+ return current;
283
+ }
284
+ async save(reason = "manual") {
285
+ if (this.savePromise) {
286
+ try {
287
+ await this.savePromise;
288
+ } catch {
289
+ }
290
+ }
291
+ const operation = this.performSave(reason);
292
+ this.savePromise = operation;
293
+ try {
294
+ return await operation;
295
+ } finally {
296
+ if (this.savePromise === operation) this.savePromise = null;
297
+ }
298
+ }
299
+ async performSave(reason) {
300
+ if (!this.snapshot) throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", "Open a document before saving.");
301
+ if (this.conflicted) throw new DocumentLibraryError("VERSION_CONFLICT", "Autosave is paused until the conflict is resolved.");
302
+ this.cancelPendingSave(false);
303
+ const controller = new AbortController();
304
+ this.saveController = controller;
305
+ const { snapshot } = this;
306
+ const savedMutation = this.mutation;
307
+ this.emit("save-started", { documentId: snapshot.document.id, reason });
308
+ try {
309
+ const result = await this.adapter.saveState(snapshot.document.id, {
310
+ state: cloneDocumentState(snapshot.state),
311
+ baseRevisionId: snapshot.revisionId,
312
+ reason,
313
+ clientMutationId: globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`
314
+ }, controller.signal);
315
+ snapshot.revisionId = result.revisionId;
316
+ snapshot.document.currentRevisionId = result.revisionId;
317
+ if (result.version) {
318
+ snapshot.version = result.version;
319
+ snapshot.document.currentVersionId = result.version.id;
320
+ }
321
+ this.dirty = savedMutation !== this.mutation;
322
+ this.emit("saved", { ...result, documentId: snapshot.document.id });
323
+ if (this.dirty && this.autosaveEnabled) this.scheduleAutosave();
324
+ return result;
325
+ } catch (error) {
326
+ const normalized = toDocumentLibraryError(error);
327
+ if (normalized.code === "VERSION_CONFLICT") this.conflicted = true;
328
+ this.emit("save-failed", normalized);
329
+ this.emit("error", normalized);
330
+ throw normalized;
331
+ } finally {
332
+ if (this.saveController === controller) this.saveController = null;
333
+ }
334
+ }
335
+ async restore(versionId, signal) {
336
+ if (!this.snapshot) throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", "Open a document before restoring a version.");
337
+ const restored = await this.adapter.restoreVersion(this.snapshot.document.id, versionId, signal);
338
+ restored.state = migrateDocumentState(restored.state);
339
+ this.snapshot = restored;
340
+ this.dirty = false;
341
+ this.conflicted = false;
342
+ const current = this.current;
343
+ this.emit("version-restored", current);
344
+ return current;
345
+ }
346
+ async storeArtifact(input, signal) {
347
+ if (!this.snapshot) throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", "Open a document before storing an artifact.");
348
+ return this.adapter.storeArtifact(this.snapshot.document.id, input, signal);
349
+ }
350
+ resolveConflict(snapshot) {
351
+ this.snapshot = { ...cloneDocumentState(snapshot), state: migrateDocumentState(snapshot.state) };
352
+ this.dirty = false;
353
+ this.conflicted = false;
354
+ }
355
+ destroy() {
356
+ this.cancelPendingSave();
357
+ this.snapshot = null;
358
+ this.clear();
359
+ }
360
+ scheduleAutosave() {
361
+ if (this.autosaveTimer) clearTimeout(this.autosaveTimer);
362
+ this.autosaveTimer = setTimeout(() => {
363
+ this.autosaveTimer = null;
364
+ if (this.dirty && !this.conflicted) void this.save("autosave").catch(() => void 0);
365
+ }, this.autosaveDelay);
366
+ }
367
+ cancelPendingSave(abort = true) {
368
+ if (this.autosaveTimer) clearTimeout(this.autosaveTimer);
369
+ this.autosaveTimer = null;
370
+ if (abort) this.saveController?.abort();
371
+ this.saveController = null;
372
+ }
373
+ };
374
+ var createDocumentLibrary = (options) => new DocumentLibraryClient(options);
375
+
376
+ // src/core/collaboration.ts
377
+ var YjsCollaborationAdapter = class {
378
+ constructor(options) {
379
+ this.options = options;
380
+ }
381
+ options;
382
+ async connect(context) {
383
+ const provider = await this.options.providerFactory({ documentId: context.documentId, user: context.user, editor: context.editor });
384
+ provider.connect?.();
385
+ return {
386
+ disconnect() {
387
+ provider.disconnect?.();
388
+ provider.destroy?.();
389
+ },
390
+ getUsers: () => provider.awareness?.getStates ? [...provider.awareness.getStates().values()] : []
391
+ };
392
+ }
393
+ };
394
+ var createYjsCollaborationAdapter = (options) => new YjsCollaborationAdapter(options);
395
+
396
+ // src/core/adapters/rest.ts
397
+ function normalizeBaseUrl(baseUrl) {
398
+ return baseUrl.replace(/\/+$/, "");
399
+ }
400
+ function queryString(query) {
401
+ if (!query) return "";
402
+ const params = new URLSearchParams();
403
+ Object.entries(query).forEach(([key, value]) => {
404
+ if (value === void 0 || value === null || value === "") return;
405
+ if (Array.isArray(value)) value.forEach((item) => params.append(key, String(item)));
406
+ else params.set(key, String(value));
407
+ });
408
+ const result = params.toString();
409
+ return result ? `?${result}` : "";
410
+ }
411
+ var RestDocumentAdapter = class {
412
+ baseUrl;
413
+ transport;
414
+ constructor(options) {
415
+ if (!options?.baseUrl) throw new TypeError("RestDocumentAdapter requires baseUrl.");
416
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
417
+ this.transport = options.transport || globalThis.fetch.bind(globalThis);
418
+ }
419
+ async request(path, init = {}) {
420
+ try {
421
+ const response = await this.transport(`${this.baseUrl}${path}`, init);
422
+ if (!response.ok) {
423
+ let details;
424
+ try {
425
+ details = await response.json();
426
+ } catch {
427
+ details = await response.text();
428
+ }
429
+ const code = response.status === 404 ? "DOCUMENT_NOT_FOUND" : response.status === 409 ? "VERSION_CONFLICT" : "ADAPTER_ERROR";
430
+ throw new DocumentLibraryError(code, `Document API returned ${response.status}.`, {
431
+ status: response.status,
432
+ details
433
+ });
434
+ }
435
+ if (response.status === 204) return void 0;
436
+ return await response.json();
437
+ } catch (error) {
438
+ if (error instanceof DocumentLibraryError) throw error;
439
+ throw toDocumentLibraryError(error, "NETWORK_ERROR");
440
+ }
441
+ }
442
+ json(path, method, body, signal) {
443
+ return this.request(path, {
444
+ method,
445
+ signal,
446
+ headers: { "content-type": "application/json" },
447
+ body: body === void 0 ? void 0 : JSON.stringify(body)
448
+ });
449
+ }
450
+ listDocuments(query, signal) {
451
+ return this.request(`/documents${queryString(query)}`, { signal });
452
+ }
453
+ getDocument(documentId, signal) {
454
+ return this.request(`/documents/${encodeURIComponent(documentId)}`, { signal });
455
+ }
456
+ createDocument(input, signal) {
457
+ return this.json("/documents", "POST", input, signal);
458
+ }
459
+ importDocument(input, signal) {
460
+ const data = new FormData();
461
+ data.set("file", input.file, input.fileName || "document.docx");
462
+ data.set("metadata", JSON.stringify({
463
+ title: input.title,
464
+ fileName: input.fileName,
465
+ folderId: input.folderId,
466
+ tags: input.tags,
467
+ metadata: input.metadata
468
+ }));
469
+ data.set("state", JSON.stringify(input.state));
470
+ if (input.compatibilityReport) data.set("compatibilityReport", JSON.stringify(input.compatibilityReport));
471
+ return this.request("/documents/import", { method: "POST", body: data, signal });
472
+ }
473
+ updateDocument(documentId, patch, signal) {
474
+ return this.json(`/documents/${encodeURIComponent(documentId)}`, "PATCH", patch, signal);
475
+ }
476
+ loadState(documentId, versionId, signal) {
477
+ const suffix = versionId ? `?versionId=${encodeURIComponent(versionId)}` : "";
478
+ return this.request(`/documents/${encodeURIComponent(documentId)}/state${suffix}`, { signal });
479
+ }
480
+ saveState(documentId, input, signal) {
481
+ return this.json(`/documents/${encodeURIComponent(documentId)}/state`, "PUT", input, signal);
482
+ }
483
+ listVersions(documentId, query, signal) {
484
+ return this.request(
485
+ `/documents/${encodeURIComponent(documentId)}/versions${queryString(query)}`,
486
+ { signal }
487
+ );
488
+ }
489
+ restoreVersion(documentId, versionId, signal) {
490
+ return this.json(
491
+ `/documents/${encodeURIComponent(documentId)}/versions/${encodeURIComponent(versionId)}/restore`,
492
+ "POST",
493
+ void 0,
494
+ signal
495
+ );
496
+ }
497
+ listFolders(query, signal) {
498
+ return this.request(`/folders${queryString(query)}`, { signal });
499
+ }
500
+ listTemplates(query, signal) {
501
+ return this.request(`/templates${queryString(query)}`, { signal });
502
+ }
503
+ storeArtifact(documentId, input, signal) {
504
+ const data = new FormData();
505
+ data.set("file", input.blob, input.fileName);
506
+ data.set("fileName", input.fileName);
507
+ data.set("format", input.format);
508
+ if (input.versionId) data.set("versionId", input.versionId);
509
+ if (input.compatibilityReport) data.set("compatibilityReport", JSON.stringify(input.compatibilityReport));
510
+ return this.request(`/documents/${encodeURIComponent(documentId)}/artifacts`, {
511
+ method: "POST",
512
+ body: data,
513
+ signal
514
+ });
515
+ }
516
+ getArtifact(documentId, artifactId, signal) {
517
+ return this.request(
518
+ `/documents/${encodeURIComponent(documentId)}/artifacts/${encodeURIComponent(artifactId)}`,
519
+ { signal }
520
+ );
521
+ }
522
+ };
523
+ var createRestDocumentAdapter = (options) => new RestDocumentAdapter(options);
524
+
525
+ // src/core/adapters/memory.ts
526
+ var id = (prefix) => `${prefix}-${globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
527
+ var now = () => (/* @__PURE__ */ new Date()).toISOString();
528
+ var throwIfAborted = (signal) => {
529
+ if (signal?.aborted) throw new DocumentLibraryError("OPERATION_CANCELLED", "Operation was cancelled.");
530
+ };
531
+ function paginate(items, query = {}) {
532
+ const page = Math.max(1, query.page || 1);
533
+ const pageSize = Math.max(1, query.pageSize || 20);
534
+ return { items: items.slice((page - 1) * pageSize, page * pageSize), total: items.length, page, pageSize };
535
+ }
536
+ function cloneArtifact(value) {
537
+ const { blob, ...metadata } = value;
538
+ return { ...cloneDocumentState(metadata), blob };
539
+ }
540
+ var MemoryDocumentAdapter = class {
541
+ documents = /* @__PURE__ */ new Map();
542
+ states = /* @__PURE__ */ new Map();
543
+ versions = /* @__PURE__ */ new Map();
544
+ versionStates = /* @__PURE__ */ new Map();
545
+ artifacts = /* @__PURE__ */ new Map();
546
+ folders;
547
+ constructor(seed = {}) {
548
+ this.folders = cloneDocumentState(seed.folders || []);
549
+ seed.documents?.forEach((snapshot) => this.seed(snapshot));
550
+ }
551
+ seed(snapshot) {
552
+ const value = cloneDocumentState(snapshot);
553
+ value.state = migrateDocumentState(value.state);
554
+ this.documents.set(value.document.id, value.document);
555
+ this.states.set(value.document.id, value);
556
+ if (value.version) {
557
+ this.versions.set(value.document.id, [value.version]);
558
+ this.versionStates.set(value.version.id, value);
559
+ }
560
+ }
561
+ requireDocument(documentId) {
562
+ const document = this.documents.get(documentId);
563
+ if (!document) throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", `Document ${documentId} was not found.`, { status: 404 });
564
+ return document;
565
+ }
566
+ async listDocuments(query = {}, signal) {
567
+ throwIfAborted(signal);
568
+ const search = query.search?.toLocaleLowerCase();
569
+ let items = [...this.documents.values()].filter((item) => query.templatesOnly ? item.isTemplate : !item.isTemplate);
570
+ if (search) items = items.filter((item) => `${item.title} ${item.fileName}`.toLocaleLowerCase().includes(search));
571
+ if (query.folderId !== void 0) items = items.filter((item) => item.folderId === query.folderId);
572
+ if (query.tags?.length) items = items.filter((item) => query.tags.every((tag) => item.tags?.includes(tag)));
573
+ items.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
574
+ return paginate(cloneDocumentState(items), query);
575
+ }
576
+ async getDocument(documentId, signal) {
577
+ throwIfAborted(signal);
578
+ return cloneDocumentState(this.requireDocument(documentId));
579
+ }
580
+ async createDocument(input, signal) {
581
+ throwIfAborted(signal);
582
+ const documentId = id("doc");
583
+ const createdAt = now();
584
+ const document = {
585
+ id: documentId,
586
+ title: input.title,
587
+ fileName: input.fileName || `${input.title || "Untitled"}.docx`,
588
+ folderId: input.folderId,
589
+ tags: input.tags || [],
590
+ isTemplate: false,
591
+ currentRevisionId: id("rev"),
592
+ createdAt,
593
+ updatedAt: createdAt,
594
+ metadata: input.metadata,
595
+ capabilities: { view: true, edit: true, comment: true, review: true, download: true, restore: true, manage: true }
596
+ };
597
+ const version = this.makeVersion(document, input.templateId ? "template" : "create");
598
+ document.currentVersionId = version.id;
599
+ this.documents.set(document.id, document);
600
+ const templateState = input.templateId ? this.states.get(input.templateId)?.state : void 0;
601
+ if (input.templateId && !templateState) throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", `Template ${input.templateId} was not found.`, { status: 404 });
602
+ const state = input.state || templateState || createEmptyDocumentState();
603
+ const snapshot = { document, state, revisionId: document.currentRevisionId, version };
604
+ this.states.set(document.id, cloneDocumentState(snapshot));
605
+ this.versionStates.set(version.id, cloneDocumentState(snapshot));
606
+ return cloneDocumentState(document);
607
+ }
608
+ async importDocument(input, signal) {
609
+ throwIfAborted(signal);
610
+ const document = await this.createDocument({ ...input, state: input.state }, signal);
611
+ const versions = this.versions.get(document.id) || [];
612
+ if (versions[0]) versions[0].reason = "import";
613
+ const original = {
614
+ format: "original-docx",
615
+ blob: input.file,
616
+ fileName: input.fileName || document.fileName,
617
+ versionId: document.currentVersionId,
618
+ compatibilityReport: input.compatibilityReport
619
+ };
620
+ const artifact = await this.storeArtifact(document.id, original, signal);
621
+ return this.updateDocument(document.id, {
622
+ originalSource: {
623
+ artifactId: artifact.id,
624
+ revisionId: document.currentRevisionId,
625
+ format: "original-docx",
626
+ fileName: artifact.fileName,
627
+ compatibilityReport: input.compatibilityReport
628
+ }
629
+ }, signal);
630
+ }
631
+ async updateDocument(documentId, patch, signal) {
632
+ throwIfAborted(signal);
633
+ const document = this.requireDocument(documentId);
634
+ const updated = { ...document, ...cloneDocumentState(patch), id: document.id, updatedAt: now() };
635
+ this.documents.set(documentId, updated);
636
+ const snapshot = this.states.get(documentId);
637
+ if (snapshot) snapshot.document = cloneDocumentState(updated);
638
+ for (const versionSnapshot of this.versionStates.values()) {
639
+ if (versionSnapshot.document.id === documentId) versionSnapshot.document = cloneDocumentState(updated);
640
+ }
641
+ return cloneDocumentState(updated);
642
+ }
643
+ async loadState(documentId, versionId, signal) {
644
+ throwIfAborted(signal);
645
+ this.requireDocument(documentId);
646
+ const snapshot = versionId ? this.versionStates.get(versionId) : this.states.get(documentId);
647
+ if (!snapshot || snapshot.document.id !== documentId) {
648
+ throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", `Version ${versionId} was not found.`, { status: 404 });
649
+ }
650
+ return cloneDocumentState(snapshot);
651
+ }
652
+ async saveState(documentId, input, signal) {
653
+ throwIfAborted(signal);
654
+ const document = this.requireDocument(documentId);
655
+ if (document.currentRevisionId !== input.baseRevisionId) {
656
+ throw new DocumentLibraryError("VERSION_CONFLICT", "The document has a newer revision.", {
657
+ status: 409,
658
+ details: { currentRevisionId: document.currentRevisionId }
659
+ });
660
+ }
661
+ const revisionId = id("rev");
662
+ document.currentRevisionId = revisionId;
663
+ document.updatedAt = now();
664
+ const version = input.reason === "manual" ? this.makeVersion(document, "manual") : void 0;
665
+ if (version) document.currentVersionId = version.id;
666
+ const snapshot = {
667
+ document: cloneDocumentState(document),
668
+ state: migrateDocumentState(input.state),
669
+ revisionId,
670
+ version
671
+ };
672
+ this.states.set(documentId, snapshot);
673
+ if (version) this.versionStates.set(version.id, cloneDocumentState(snapshot));
674
+ return { revisionId, savedAt: document.updatedAt, version: cloneDocumentState(version) };
675
+ }
676
+ async listVersions(documentId, query = {}, signal) {
677
+ throwIfAborted(signal);
678
+ this.requireDocument(documentId);
679
+ return paginate(cloneDocumentState(this.versions.get(documentId) || []).reverse(), query);
680
+ }
681
+ async restoreVersion(documentId, versionId, signal) {
682
+ throwIfAborted(signal);
683
+ const source = await this.loadState(documentId, versionId, signal);
684
+ const document = this.requireDocument(documentId);
685
+ const revisionId = id("rev");
686
+ document.currentRevisionId = revisionId;
687
+ document.updatedAt = now();
688
+ const version = this.makeVersion(document, "restore");
689
+ document.currentVersionId = version.id;
690
+ const snapshot = { document: cloneDocumentState(document), state: source.state, revisionId, version };
691
+ this.states.set(documentId, cloneDocumentState(snapshot));
692
+ this.versionStates.set(version.id, cloneDocumentState(snapshot));
693
+ return cloneDocumentState(snapshot);
694
+ }
695
+ async listFolders(_query, signal) {
696
+ throwIfAborted(signal);
697
+ return cloneDocumentState(this.folders);
698
+ }
699
+ async listTemplates(query = {}, signal) {
700
+ return this.listDocuments({ ...query, templatesOnly: true }, signal);
701
+ }
702
+ async storeArtifact(documentId, input, signal) {
703
+ throwIfAborted(signal);
704
+ this.requireDocument(documentId);
705
+ const artifact = {
706
+ id: id("artifact"),
707
+ documentId,
708
+ versionId: input.versionId,
709
+ format: input.format,
710
+ fileName: input.fileName,
711
+ mimeType: input.blob.type || "application/octet-stream",
712
+ size: input.blob.size,
713
+ blob: input.blob,
714
+ createdAt: now()
715
+ };
716
+ this.artifacts.set(artifact.id, artifact);
717
+ return cloneArtifact(artifact);
718
+ }
719
+ async getArtifact(documentId, artifactId, signal) {
720
+ throwIfAborted(signal);
721
+ this.requireDocument(documentId);
722
+ const artifact = this.artifacts.get(artifactId);
723
+ if (!artifact || artifact.documentId !== documentId) {
724
+ throw new DocumentLibraryError("DOCUMENT_NOT_FOUND", `Artifact ${artifactId} was not found.`, { status: 404 });
725
+ }
726
+ return cloneArtifact(artifact);
727
+ }
728
+ makeVersion(document, reason) {
729
+ const list = this.versions.get(document.id) || [];
730
+ const version = {
731
+ id: id("version"),
732
+ documentId: document.id,
733
+ number: list.length + 1,
734
+ revisionId: document.currentRevisionId,
735
+ reason,
736
+ createdAt: now()
737
+ };
738
+ list.push(version);
739
+ this.versions.set(document.id, list);
740
+ return version;
741
+ }
742
+ };
743
+ var createMemoryDocumentAdapter = (seed) => new MemoryDocumentAdapter(seed);
744
+
745
+ // src/core/adapters/legacy.ts
746
+ function createLegacyCallbackAdapter(options = {}) {
747
+ const memory = createMemoryDocumentAdapter();
748
+ return new Proxy(memory, {
749
+ get(target, property, receiver) {
750
+ if (property === "saveState") {
751
+ return async (documentId, input) => {
752
+ const result = await target.saveState(documentId, input);
753
+ await options.onSave?.({ json: input.state.content, state: input.state }, input.state.page);
754
+ return result;
755
+ };
756
+ }
757
+ const value = Reflect.get(target, property, target);
758
+ return typeof value === "function" ? value.bind(target) : value;
759
+ }
760
+ });
761
+ }
762
+ // Annotate the CommonJS export names for ESM import in node:
763
+ 0 && (module.exports = {
764
+ DEFAULT_PAGE_STATE,
765
+ DOCUMENT_ERROR_CODES,
766
+ DocumentLibraryClient,
767
+ DocumentLibraryError,
768
+ KINDY_DOCUMENT_SCHEMA_VERSION,
769
+ MemoryDocumentAdapter,
770
+ RestDocumentAdapter,
771
+ TypedEventEmitter,
772
+ YjsCollaborationAdapter,
773
+ cloneDocumentState,
774
+ createDocumentLibrary,
775
+ createEditorDocumentState,
776
+ createEmptyDocumentState,
777
+ createLegacyCallbackAdapter,
778
+ createMemoryDocumentAdapter,
779
+ createRestDocumentAdapter,
780
+ createYjsCollaborationAdapter,
781
+ migrateDocumentState,
782
+ stateFromEditor,
783
+ toCanonicalHeaderFooter,
784
+ toCanonicalSection,
785
+ toDocumentLibraryError
786
+ });