@larkup/tool-doc-editor 0.2.30 → 0.3.1
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/editor.d.ts +2 -8
- package/dist/editor.js +50 -12
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +3 -3
- package/src/editor.ts +52 -12
- package/src/index.ts +3 -1
- package/tool.manifest.json +1 -1
package/dist/editor.d.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Document editor — orchestrates parsing, editing, and export.
|
|
3
|
-
*
|
|
4
|
-
* Uses:
|
|
5
|
-
* - pdf-lib (MIT) for native PDF form filling
|
|
6
|
-
* - Sandbox scripts for DOCX/PPTX editing via python-docx/python-pptx
|
|
7
|
-
* - In-memory session management
|
|
8
|
-
*/
|
|
9
1
|
import type { EditorSession, FieldEdit, ContentEdit, EditResult, SignatureData } from './types.js';
|
|
2
|
+
/** Hydrate isolated sessions from the host's tenant-bound persistent repository. */
|
|
3
|
+
export declare function runWithDocumentSessions<T>(operation: () => T): Promise<Awaited<T>>;
|
|
10
4
|
export declare function getSession(sessionId: string): EditorSession | undefined;
|
|
11
5
|
export declare function getAllSessions(): EditorSession[];
|
|
12
6
|
export declare function deleteSession(sessionId: string): boolean;
|
package/dist/editor.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { workspaceHost } from '@larkup/core/workspace-host';
|
|
1
3
|
/**
|
|
2
4
|
* Document editor — orchestrates parsing, editing, and export.
|
|
3
5
|
*
|
|
@@ -11,18 +13,50 @@ import { SandboxManager } from '@larkup/sandbox';
|
|
|
11
13
|
import { readConfig } from '@larkup/core/config-store';
|
|
12
14
|
import { parseDocument, enrichPDFWithText } from './parsers.js';
|
|
13
15
|
import { generateFillDOCXScript, generateFillPPTXScript, generateEditDOCXScript, generateEditPPTXScript, generateExtractPDFTextScript, } from './sandbox-scripts.js';
|
|
14
|
-
const
|
|
16
|
+
const localSessions = new Map();
|
|
17
|
+
const sessionScope = new AsyncLocalStorage();
|
|
18
|
+
function sessions() {
|
|
19
|
+
return sessionScope.getStore()?.sessions ?? localSessions;
|
|
20
|
+
}
|
|
21
|
+
async function persistSessions() {
|
|
22
|
+
await sessionScope.getStore()?.flush();
|
|
23
|
+
}
|
|
24
|
+
/** Hydrate isolated sessions from the host's tenant-bound persistent repository. */
|
|
25
|
+
export async function runWithDocumentSessions(operation) {
|
|
26
|
+
const host = workspaceHost();
|
|
27
|
+
if (!host || sessionScope.getStore())
|
|
28
|
+
return await operation();
|
|
29
|
+
const stored = ((await host.readState('document-sessions')) ?? []);
|
|
30
|
+
const scoped = new Map(stored.map((session) => [session.id, session]));
|
|
31
|
+
let previous = JSON.stringify(stored);
|
|
32
|
+
let pending = Promise.resolve();
|
|
33
|
+
const flush = () => {
|
|
34
|
+
pending = pending.then(async () => {
|
|
35
|
+
const next = JSON.stringify([...scoped.values()]);
|
|
36
|
+
if (next !== previous) {
|
|
37
|
+
await host.writeState('document-sessions', JSON.parse(next));
|
|
38
|
+
previous = next;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return pending;
|
|
42
|
+
};
|
|
43
|
+
return await sessionScope.run({ sessions: scoped, flush }, async () => {
|
|
44
|
+
const result = await operation();
|
|
45
|
+
await flush();
|
|
46
|
+
return result;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
15
49
|
export function getSession(sessionId) {
|
|
16
|
-
return sessions.get(sessionId);
|
|
50
|
+
return sessions().get(sessionId);
|
|
17
51
|
}
|
|
18
52
|
export function getAllSessions() {
|
|
19
|
-
return Array.from(sessions.values()).sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
53
|
+
return Array.from(sessions().values()).sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
20
54
|
}
|
|
21
55
|
export function deleteSession(sessionId) {
|
|
22
|
-
return sessions.delete(sessionId);
|
|
56
|
+
return sessions().delete(sessionId);
|
|
23
57
|
}
|
|
24
58
|
export function restoreSession(session) {
|
|
25
|
-
sessions.set(session.id, session);
|
|
59
|
+
sessions().set(session.id, session);
|
|
26
60
|
}
|
|
27
61
|
export async function createSession(buffer, fileName, mimeType) {
|
|
28
62
|
// Parse the document
|
|
@@ -75,13 +109,14 @@ export async function createSession(buffer, fileName, mimeType) {
|
|
|
75
109
|
createdAt: new Date().toISOString(),
|
|
76
110
|
updatedAt: new Date().toISOString(),
|
|
77
111
|
};
|
|
78
|
-
sessions.set(session.id, session);
|
|
112
|
+
sessions().set(session.id, session);
|
|
113
|
+
await persistSessions();
|
|
79
114
|
return session;
|
|
80
115
|
}
|
|
81
116
|
export async function applyFieldEdits(sessionId, edits) {
|
|
82
|
-
const session = sessions.get(sessionId);
|
|
117
|
+
const session = sessions().get(sessionId);
|
|
83
118
|
if (!session) {
|
|
84
|
-
console.error(`[doc-editor] Session not found: ${sessionId}. Available sessions:`, Array.from(sessions.keys()));
|
|
119
|
+
console.error(`[doc-editor] Session not found: ${sessionId}. Available sessions:`, Array.from(sessions().keys()));
|
|
85
120
|
return {
|
|
86
121
|
success: false,
|
|
87
122
|
parsed: null,
|
|
@@ -212,6 +247,7 @@ export async function applyFieldEdits(sessionId, edits) {
|
|
|
212
247
|
session.parsed = newParsed;
|
|
213
248
|
session.edits.push(...edits);
|
|
214
249
|
session.updatedAt = new Date().toISOString();
|
|
250
|
+
await persistSessions();
|
|
215
251
|
return {
|
|
216
252
|
success: true,
|
|
217
253
|
parsed: newParsed,
|
|
@@ -231,7 +267,7 @@ export async function applyFieldEdits(sessionId, edits) {
|
|
|
231
267
|
}
|
|
232
268
|
}
|
|
233
269
|
export async function applyContentEdits(sessionId, edits) {
|
|
234
|
-
const session = sessions.get(sessionId);
|
|
270
|
+
const session = sessions().get(sessionId);
|
|
235
271
|
if (!session) {
|
|
236
272
|
return {
|
|
237
273
|
success: false,
|
|
@@ -308,6 +344,7 @@ export async function applyContentEdits(sessionId, edits) {
|
|
|
308
344
|
session.currentFileBase64 = newBase64;
|
|
309
345
|
session.parsed = newParsed;
|
|
310
346
|
session.updatedAt = new Date().toISOString();
|
|
347
|
+
await persistSessions();
|
|
311
348
|
return {
|
|
312
349
|
success: true,
|
|
313
350
|
parsed: newParsed,
|
|
@@ -326,7 +363,7 @@ export async function applyContentEdits(sessionId, edits) {
|
|
|
326
363
|
}
|
|
327
364
|
}
|
|
328
365
|
export function exportDocument(sessionId) {
|
|
329
|
-
const session = sessions.get(sessionId);
|
|
366
|
+
const session = sessions().get(sessionId);
|
|
330
367
|
if (!session)
|
|
331
368
|
return null;
|
|
332
369
|
return {
|
|
@@ -421,7 +458,7 @@ async function fillPDFNatively(buffer, edits) {
|
|
|
421
458
|
return { buffer: Buffer.from(modifiedBytes), matchedFields };
|
|
422
459
|
}
|
|
423
460
|
export async function applySignature(sessionId, data) {
|
|
424
|
-
const session = sessions.get(sessionId);
|
|
461
|
+
const session = sessions().get(sessionId);
|
|
425
462
|
if (!session) {
|
|
426
463
|
return { success: false, error: 'Session not found' };
|
|
427
464
|
}
|
|
@@ -534,7 +571,8 @@ export async function applySignature(sessionId, data) {
|
|
|
534
571
|
session.currentFileBase64 = newBase64;
|
|
535
572
|
session.parsed = newParsed;
|
|
536
573
|
session.updatedAt = new Date().toISOString();
|
|
537
|
-
|
|
574
|
+
await persistSessions();
|
|
575
|
+
sessions().set(sessionId, session);
|
|
538
576
|
return {
|
|
539
577
|
success: true,
|
|
540
578
|
parsed: newParsed,
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export declare const TOOL_META: {
|
|
9
9
|
readonly id: "doc-editor";
|
|
10
10
|
readonly name: "Document Editor";
|
|
11
|
-
readonly version: "0.
|
|
11
|
+
readonly version: "0.3.0";
|
|
12
12
|
};
|
|
13
13
|
export declare const TOOL_EXTENSION: {
|
|
14
14
|
id: string;
|
|
@@ -18,3 +18,4 @@ export declare const TOOL_EXTENSION: {
|
|
|
18
18
|
export { createSession, getSession, getAllSessions, deleteSession, restoreSession, applyFieldEdits, applyContentEdits, applySignature, exportDocument, } from './editor.js';
|
|
19
19
|
export { parseDocument, parsePDF, parseDOCX, parsePPTX, parseTXT, detectDocumentType, enrichPDFWithText, } from './parsers.js';
|
|
20
20
|
export type { DocumentType, DocumentField, DocumentPage, ParsedDocument, FieldEdit, ContentEdit, EditorSession, EditResult, } from './types.js';
|
|
21
|
+
export { runWithDocumentSessions } from './editor.js';
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
export const TOOL_META = {
|
|
9
9
|
id: 'doc-editor',
|
|
10
10
|
name: 'Document Editor',
|
|
11
|
-
version: '0.
|
|
11
|
+
version: '0.3.0',
|
|
12
12
|
};
|
|
13
13
|
export const TOOL_EXTENSION = {
|
|
14
14
|
id: 'doc-editor',
|
|
@@ -18,3 +18,4 @@ export const TOOL_EXTENSION = {
|
|
|
18
18
|
// Public API
|
|
19
19
|
export { createSession, getSession, getAllSessions, deleteSession, restoreSession, applyFieldEdits, applyContentEdits, applySignature, exportDocument, } from './editor.js';
|
|
20
20
|
export { parseDocument, parsePDF, parseDOCX, parsePPTX, parseTXT, detectDocumentType, enrichPDFWithText, } from './parsers.js';
|
|
21
|
+
export { runWithDocumentSessions } from './editor.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larkup/tool-doc-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"jszip": "^3.10.1",
|
|
41
41
|
"mammoth": "^1.12.0",
|
|
42
42
|
"pdf-lib": "^1.17.1",
|
|
43
|
-
"@larkup/core": "0.
|
|
43
|
+
"@larkup/core": "0.8.1",
|
|
44
44
|
"@larkup/sandbox": "0.1.8"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"typescript": "^7.0.2",
|
|
56
|
-
"@larkup/marketplace": "0.3.
|
|
56
|
+
"@larkup/marketplace": "0.3.10"
|
|
57
57
|
},
|
|
58
58
|
"license": "Apache-2.0",
|
|
59
59
|
"repository": {
|
package/src/editor.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { workspaceHost } from '@larkup/core/workspace-host';
|
|
1
3
|
/**
|
|
2
4
|
* Document editor — orchestrates parsing, editing, and export.
|
|
3
5
|
*
|
|
@@ -29,24 +31,58 @@ import {
|
|
|
29
31
|
generateExtractPDFTextScript,
|
|
30
32
|
} from './sandbox-scripts.js';
|
|
31
33
|
|
|
32
|
-
const
|
|
34
|
+
const localSessions = new Map<string, EditorSession>();
|
|
35
|
+
const sessionScope = new AsyncLocalStorage<{
|
|
36
|
+
sessions: Map<string, EditorSession>;
|
|
37
|
+
flush: () => Promise<void>;
|
|
38
|
+
}>();
|
|
39
|
+
function sessions() {
|
|
40
|
+
return sessionScope.getStore()?.sessions ?? localSessions;
|
|
41
|
+
}
|
|
42
|
+
async function persistSessions() {
|
|
43
|
+
await sessionScope.getStore()?.flush();
|
|
44
|
+
}
|
|
45
|
+
/** Hydrate isolated sessions from the host's tenant-bound persistent repository. */
|
|
46
|
+
export async function runWithDocumentSessions<T>(operation: () => T): Promise<Awaited<T>> {
|
|
47
|
+
const host = workspaceHost();
|
|
48
|
+
if (!host || sessionScope.getStore()) return await operation();
|
|
49
|
+
const stored = ((await host.readState('document-sessions')) ?? []) as EditorSession[];
|
|
50
|
+
const scoped = new Map(stored.map((session) => [session.id, session]));
|
|
51
|
+
let previous = JSON.stringify(stored);
|
|
52
|
+
let pending = Promise.resolve();
|
|
53
|
+
const flush = () => {
|
|
54
|
+
pending = pending.then(async () => {
|
|
55
|
+
const next = JSON.stringify([...scoped.values()]);
|
|
56
|
+
if (next !== previous) {
|
|
57
|
+
await host.writeState('document-sessions', JSON.parse(next));
|
|
58
|
+
previous = next;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return pending;
|
|
62
|
+
};
|
|
63
|
+
return await sessionScope.run({ sessions: scoped, flush }, async () => {
|
|
64
|
+
const result = await operation();
|
|
65
|
+
await flush();
|
|
66
|
+
return result;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
33
69
|
|
|
34
70
|
export function getSession(sessionId: string): EditorSession | undefined {
|
|
35
|
-
return sessions.get(sessionId);
|
|
71
|
+
return sessions().get(sessionId);
|
|
36
72
|
}
|
|
37
73
|
|
|
38
74
|
export function getAllSessions(): EditorSession[] {
|
|
39
|
-
return Array.from(sessions.values()).sort(
|
|
75
|
+
return Array.from(sessions().values()).sort(
|
|
40
76
|
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),
|
|
41
77
|
);
|
|
42
78
|
}
|
|
43
79
|
|
|
44
80
|
export function deleteSession(sessionId: string): boolean {
|
|
45
|
-
return sessions.delete(sessionId);
|
|
81
|
+
return sessions().delete(sessionId);
|
|
46
82
|
}
|
|
47
83
|
|
|
48
84
|
export function restoreSession(session: EditorSession): void {
|
|
49
|
-
sessions.set(session.id, session);
|
|
85
|
+
sessions().set(session.id, session);
|
|
50
86
|
}
|
|
51
87
|
|
|
52
88
|
export async function createSession(
|
|
@@ -112,16 +148,17 @@ export async function createSession(
|
|
|
112
148
|
updatedAt: new Date().toISOString(),
|
|
113
149
|
};
|
|
114
150
|
|
|
115
|
-
sessions.set(session.id, session);
|
|
151
|
+
sessions().set(session.id, session);
|
|
152
|
+
await persistSessions();
|
|
116
153
|
return session;
|
|
117
154
|
}
|
|
118
155
|
|
|
119
156
|
export async function applyFieldEdits(sessionId: string, edits: FieldEdit[]): Promise<EditResult> {
|
|
120
|
-
const session = sessions.get(sessionId);
|
|
157
|
+
const session = sessions().get(sessionId);
|
|
121
158
|
if (!session) {
|
|
122
159
|
console.error(
|
|
123
160
|
`[doc-editor] Session not found: ${sessionId}. Available sessions:`,
|
|
124
|
-
Array.from(sessions.keys()),
|
|
161
|
+
Array.from(sessions().keys()),
|
|
125
162
|
);
|
|
126
163
|
return {
|
|
127
164
|
success: false,
|
|
@@ -279,6 +316,7 @@ export async function applyFieldEdits(sessionId: string, edits: FieldEdit[]): Pr
|
|
|
279
316
|
session.parsed = newParsed;
|
|
280
317
|
session.edits.push(...edits);
|
|
281
318
|
session.updatedAt = new Date().toISOString();
|
|
319
|
+
await persistSessions();
|
|
282
320
|
|
|
283
321
|
return {
|
|
284
322
|
success: true,
|
|
@@ -302,7 +340,7 @@ export async function applyContentEdits(
|
|
|
302
340
|
sessionId: string,
|
|
303
341
|
edits: ContentEdit[],
|
|
304
342
|
): Promise<EditResult> {
|
|
305
|
-
const session = sessions.get(sessionId);
|
|
343
|
+
const session = sessions().get(sessionId);
|
|
306
344
|
if (!session) {
|
|
307
345
|
return {
|
|
308
346
|
success: false,
|
|
@@ -390,6 +428,7 @@ export async function applyContentEdits(
|
|
|
390
428
|
session.currentFileBase64 = newBase64;
|
|
391
429
|
session.parsed = newParsed;
|
|
392
430
|
session.updatedAt = new Date().toISOString();
|
|
431
|
+
await persistSessions();
|
|
393
432
|
|
|
394
433
|
return {
|
|
395
434
|
success: true,
|
|
@@ -411,7 +450,7 @@ export async function applyContentEdits(
|
|
|
411
450
|
export function exportDocument(
|
|
412
451
|
sessionId: string,
|
|
413
452
|
): { buffer: Buffer; fileName: string; mimeType: string } | null {
|
|
414
|
-
const session = sessions.get(sessionId);
|
|
453
|
+
const session = sessions().get(sessionId);
|
|
415
454
|
if (!session) return null;
|
|
416
455
|
|
|
417
456
|
return {
|
|
@@ -522,7 +561,7 @@ async function fillPDFNatively(
|
|
|
522
561
|
}
|
|
523
562
|
|
|
524
563
|
export async function applySignature(sessionId: string, data: SignatureData): Promise<EditResult> {
|
|
525
|
-
const session = sessions.get(sessionId);
|
|
564
|
+
const session = sessions().get(sessionId);
|
|
526
565
|
if (!session) {
|
|
527
566
|
return { success: false, error: 'Session not found' } as any;
|
|
528
567
|
}
|
|
@@ -651,7 +690,8 @@ export async function applySignature(sessionId: string, data: SignatureData): Pr
|
|
|
651
690
|
session.currentFileBase64 = newBase64;
|
|
652
691
|
session.parsed = newParsed;
|
|
653
692
|
session.updatedAt = new Date().toISOString();
|
|
654
|
-
|
|
693
|
+
await persistSessions();
|
|
694
|
+
sessions().set(sessionId, session);
|
|
655
695
|
|
|
656
696
|
return {
|
|
657
697
|
success: true,
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
export const TOOL_META = {
|
|
10
10
|
id: 'doc-editor',
|
|
11
11
|
name: 'Document Editor',
|
|
12
|
-
version: '0.
|
|
12
|
+
version: '0.3.0',
|
|
13
13
|
} as const;
|
|
14
14
|
|
|
15
15
|
export const TOOL_EXTENSION = {
|
|
@@ -51,3 +51,5 @@ export type {
|
|
|
51
51
|
EditorSession,
|
|
52
52
|
EditResult,
|
|
53
53
|
} from './types.js';
|
|
54
|
+
|
|
55
|
+
export { runWithDocumentSessions } from './editor.js';
|
package/tool.manifest.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"description": "AI-powered form filling and document editing with Canvas-style live preview.",
|
|
6
6
|
"longDescription": "Open PDF, Word, PowerPoint, and text files in a split-view Canvas. AI fills forms using your knowledge base, edits content on request, and previews changes live. Supports PDF form fields (AcroForm), DOCX paragraphs/tables, PPTX slide text, and plain text editing. Uses pdf-lib (native) for PDF and the Docker sandbox for DOCX/PPTX via python-docx/python-pptx.",
|
|
7
7
|
"category": "utility",
|
|
8
|
-
"version": "0.
|
|
8
|
+
"version": "0.3.0",
|
|
9
9
|
"pricing": "free",
|
|
10
10
|
"emoji": "📝",
|
|
11
11
|
"icon": "FileEdit",
|