@namahapdf/sheets 0.1.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/LICENSE +16 -0
- package/README.md +40 -0
- package/dist/index.cjs +74 -0
- package/dist/index.d.cts +546 -0
- package/dist/index.d.ts +546 -0
- package/dist/index.js +74 -0
- package/package.json +44 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SheetModel — the parsed render model for `.xlsx` workbooks (Phase 3,
|
|
3
|
+
* PLATFORM_PLAN.md). Sparse by design: real spreadsheets are mostly empty
|
|
4
|
+
* cells, so rows and cells live in Maps keyed by 0-based indices.
|
|
5
|
+
*
|
|
6
|
+
* Formulas are **display-only**: `Cell.formula` carries the source text and
|
|
7
|
+
* `Cell.raw` the cached result Excel saved in `<v>` — there is deliberately no
|
|
8
|
+
* evaluator (the recalc contract is `fullCalcOnLoad` on write-back, Phase 4).
|
|
9
|
+
*/
|
|
10
|
+
/** `t` attribute of `<c>`: number, shared string, formula string, bool, error, inline, ISO date. */
|
|
11
|
+
type CellType = 'n' | 's' | 'str' | 'b' | 'e' | 'inlineStr' | 'd';
|
|
12
|
+
type Cell = {
|
|
13
|
+
/**
|
|
14
|
+
* Display-ready underlying value: shared/inline strings resolved to text,
|
|
15
|
+
* otherwise the raw `<v>` content (number serials as written).
|
|
16
|
+
*/
|
|
17
|
+
raw: string;
|
|
18
|
+
type: CellType;
|
|
19
|
+
/** Formula source (without leading '='), when the cell has one. */
|
|
20
|
+
formula?: string;
|
|
21
|
+
/** Index into `Workbook.styles` (resolved cellXfs). */
|
|
22
|
+
styleIdx: number;
|
|
23
|
+
};
|
|
24
|
+
type Row = {
|
|
25
|
+
cells: Map<number, Cell>;
|
|
26
|
+
heightPt?: number;
|
|
27
|
+
hidden?: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** 0-based inclusive rectangle. */
|
|
30
|
+
type CellRange = {
|
|
31
|
+
r1: number;
|
|
32
|
+
c1: number;
|
|
33
|
+
r2: number;
|
|
34
|
+
c2: number;
|
|
35
|
+
};
|
|
36
|
+
type BorderEdge = {
|
|
37
|
+
style: string;
|
|
38
|
+
color?: string;
|
|
39
|
+
};
|
|
40
|
+
/** One resolved cellXfs entry — everything the renderer needs, pre-joined. */
|
|
41
|
+
type CellStyle = {
|
|
42
|
+
/** Number-format code ('' = General). Builtin ids already resolved to codes. */
|
|
43
|
+
numFmt: string;
|
|
44
|
+
font: {
|
|
45
|
+
name: string;
|
|
46
|
+
sizePt: number;
|
|
47
|
+
bold: boolean;
|
|
48
|
+
italic: boolean;
|
|
49
|
+
underline: boolean;
|
|
50
|
+
/** CSS hex like '#1F2937', when specified. */
|
|
51
|
+
color?: string;
|
|
52
|
+
};
|
|
53
|
+
/** Solid fill CSS hex, when patternType="solid". */
|
|
54
|
+
fill?: string;
|
|
55
|
+
borders?: {
|
|
56
|
+
l?: BorderEdge;
|
|
57
|
+
r?: BorderEdge;
|
|
58
|
+
t?: BorderEdge;
|
|
59
|
+
b?: BorderEdge;
|
|
60
|
+
};
|
|
61
|
+
align?: {
|
|
62
|
+
h?: 'left' | 'center' | 'right';
|
|
63
|
+
v?: 'top' | 'center' | 'bottom';
|
|
64
|
+
wrap?: boolean;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
type Sheet = {
|
|
68
|
+
name: string;
|
|
69
|
+
/** OPC part path, e.g. "xl/worksheets/sheet1.xml" (the XlsxAnchor.sheetPart). */
|
|
70
|
+
part: string;
|
|
71
|
+
/** Sparse rows, 0-based. */
|
|
72
|
+
rows: Map<number, Row>;
|
|
73
|
+
merges: CellRange[];
|
|
74
|
+
/** Custom column widths in Excel "character" units, 0-based col index. */
|
|
75
|
+
colWidthsChars: Map<number, number>;
|
|
76
|
+
defaultColWidthChars: number;
|
|
77
|
+
defaultRowHeightPt: number;
|
|
78
|
+
/** Used extent (rows/cols), from `<dimension>` or the data scan. */
|
|
79
|
+
rowCount: number;
|
|
80
|
+
colCount: number;
|
|
81
|
+
hidden: boolean;
|
|
82
|
+
};
|
|
83
|
+
type Workbook = {
|
|
84
|
+
sheets: Sheet[];
|
|
85
|
+
/** Resolved styles; `Cell.styleIdx` indexes here. Index 0 = default. */
|
|
86
|
+
styles: CellStyle[];
|
|
87
|
+
/** 1904 date system (Mac legacy). */
|
|
88
|
+
date1904: boolean;
|
|
89
|
+
};
|
|
90
|
+
/** Parse an A1 cell reference → 0-based row/col. */
|
|
91
|
+
declare function refToRC(ref: string): {
|
|
92
|
+
row: number;
|
|
93
|
+
col: number;
|
|
94
|
+
};
|
|
95
|
+
/** 0-based row/col → A1 reference. */
|
|
96
|
+
declare function rcToRef(row: number, col: number): string;
|
|
97
|
+
/** Parse "B2:D5" (or a single "B2") → 0-based inclusive range. */
|
|
98
|
+
declare function refToRange(ref: string): CellRange;
|
|
99
|
+
|
|
100
|
+
declare class XlsxDocument {
|
|
101
|
+
private pkg;
|
|
102
|
+
private sharedStrings;
|
|
103
|
+
private styles;
|
|
104
|
+
private date1904;
|
|
105
|
+
private sheetRefs;
|
|
106
|
+
private cache;
|
|
107
|
+
private constructor();
|
|
108
|
+
static load(data: ArrayBuffer | Uint8Array): Promise<XlsxDocument>;
|
|
109
|
+
get sheetCount(): number;
|
|
110
|
+
get sheetNames(): string[];
|
|
111
|
+
get is1904(): boolean;
|
|
112
|
+
get cellStyles(): CellStyle[];
|
|
113
|
+
/** Parse (and cache) one worksheet. */
|
|
114
|
+
sheet(index: number): Promise<Sheet>;
|
|
115
|
+
/** Eagerly parse everything (small workbooks / tests / doc-model export). */
|
|
116
|
+
workbook(): Promise<Workbook>;
|
|
117
|
+
private readWorkbook;
|
|
118
|
+
private readSharedStrings;
|
|
119
|
+
private readStyles;
|
|
120
|
+
private parseWorksheet;
|
|
121
|
+
private parseCell;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Excel number-format subset — formats a cell's cached value for display.
|
|
126
|
+
*
|
|
127
|
+
* In-house on purpose (no SheetJS `ssf` vendoring): covers the formats that
|
|
128
|
+
* dominate real workbooks — General, fixed decimals, thousands separators,
|
|
129
|
+
* percent, currency literals, dates/times from serials, text section — and
|
|
130
|
+
* falls back to a General rendering for anything exotic. Formatting must
|
|
131
|
+
* never throw: worst case the raw value is shown.
|
|
132
|
+
*/
|
|
133
|
+
declare function serialToDate(serial: number, date1904: boolean): Date;
|
|
134
|
+
/** Does this format code render a date/time? */
|
|
135
|
+
declare const isDateFormat: (code: string) => boolean;
|
|
136
|
+
/** General rendering for numbers: trim floating noise, up to ~11 significant digits. */
|
|
137
|
+
declare function generalNumber(value: number): string;
|
|
138
|
+
/**
|
|
139
|
+
* Format a cell value for display.
|
|
140
|
+
* `value` is the decoded raw (`Cell.raw`); booleans/errors/text pass through.
|
|
141
|
+
*/
|
|
142
|
+
declare function formatValue(raw: string, type: 'n' | 's' | 'str' | 'b' | 'e' | 'inlineStr' | 'd', numFmt: string, date1904?: boolean): string;
|
|
143
|
+
|
|
144
|
+
declare const HEADER_W = 46;
|
|
145
|
+
declare const HEADER_H = 24;
|
|
146
|
+
type GridMetrics = {
|
|
147
|
+
rows: number;
|
|
148
|
+
cols: number;
|
|
149
|
+
totalW: number;
|
|
150
|
+
totalH: number;
|
|
151
|
+
colX: (c: number) => number;
|
|
152
|
+
rowY: (r: number) => number;
|
|
153
|
+
colW: (c: number) => number;
|
|
154
|
+
rowH: (r: number) => number;
|
|
155
|
+
colAt: (x: number) => number;
|
|
156
|
+
rowAt: (y: number) => number;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Prefix-sum metrics over sparse custom sizes: O(#custom) memory, O(log n)
|
|
160
|
+
* lookups — the sparse maps stay the source of truth.
|
|
161
|
+
*/
|
|
162
|
+
declare function buildMetrics(sheet: Sheet): GridMetrics;
|
|
163
|
+
type SheetViewport = {
|
|
164
|
+
scrollX: number;
|
|
165
|
+
scrollY: number;
|
|
166
|
+
widthPx: number;
|
|
167
|
+
heightPx: number;
|
|
168
|
+
/** devicePixelRatio (canvas backing scale). */
|
|
169
|
+
dpr: number;
|
|
170
|
+
};
|
|
171
|
+
declare class SheetRenderer {
|
|
172
|
+
private sheet;
|
|
173
|
+
private styles;
|
|
174
|
+
private date1904;
|
|
175
|
+
readonly metrics: GridMetrics;
|
|
176
|
+
/** Merge lookup: anchor "r,c" → range; covered (non-anchor) cells "r,c". */
|
|
177
|
+
private mergeAnchor;
|
|
178
|
+
private mergeCovered;
|
|
179
|
+
constructor(sheet: Sheet, styles: CellStyle[], date1904: boolean);
|
|
180
|
+
formatted(cell: Cell): string;
|
|
181
|
+
render(canvas: HTMLCanvasElement, vp: SheetViewport, dark?: boolean): void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Canonical, format-neutral document model.
|
|
186
|
+
*
|
|
187
|
+
* Both PPTX (via the existing structure projector) and PDF normalize into this
|
|
188
|
+
* shape so one set of projectors (Markdown / chunks) and the knowledge base feed
|
|
189
|
+
* off a single representation. Designed to also accept DOCX later.
|
|
190
|
+
*
|
|
191
|
+
* Geometry is optional `BBox` in PDF points, top-left origin. Image/chart
|
|
192
|
+
* elements carry KB-enrichment fields (`ocrText`, `caption`, `summary`) that the
|
|
193
|
+
* vision pipeline fills in (Track 5).
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
type BBox = {
|
|
197
|
+
xPt: number;
|
|
198
|
+
yPt: number;
|
|
199
|
+
wPt: number;
|
|
200
|
+
hPt: number;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Edit anchors — serializable locators that tie a structural element back to a
|
|
205
|
+
* concrete editable location in the native file.
|
|
206
|
+
*
|
|
207
|
+
* Anchors deliberately carry no live object references and no byte offsets:
|
|
208
|
+
* they cross the API boundary as plain JSON, and the writer/dispatcher
|
|
209
|
+
* re-derives the concrete operators (PDF content-stream indices, OOXML nodes)
|
|
210
|
+
* fresh at apply time by re-rendering / re-parsing. This mirrors how PDF edits
|
|
211
|
+
* already work (operator indices captured at render time) and how the PPTX
|
|
212
|
+
* writer will re-open the raw XML rather than trust the lossy parsed tree.
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
/** Locator into a .pptx part. Built in Track C; consumed by the writer in Track D. */
|
|
216
|
+
type PptxAnchor = {
|
|
217
|
+
fmt: 'pptx';
|
|
218
|
+
/** e.g. "ppt/slides/slide3.xml" */
|
|
219
|
+
slidePart: string;
|
|
220
|
+
/** cNvPr/@id — stable within a slide part. */
|
|
221
|
+
shapeId?: string;
|
|
222
|
+
/** Fallback locator: 0-based shape index in p:spTree. */
|
|
223
|
+
spTreeIndex?: number;
|
|
224
|
+
/** Text edits: paragraph index within the shape's txBody. */
|
|
225
|
+
paraIndex?: number;
|
|
226
|
+
/** ...then run index within that paragraph. */
|
|
227
|
+
runIndex?: number;
|
|
228
|
+
/** Table cell. */
|
|
229
|
+
cell?: {
|
|
230
|
+
row: number;
|
|
231
|
+
col: number;
|
|
232
|
+
};
|
|
233
|
+
/** Image: the blip r:embed relationship id. */
|
|
234
|
+
relId?: string;
|
|
235
|
+
/** Image: resolved media part path (e.g. "ppt/media/image2.png"). */
|
|
236
|
+
mediaPart?: string;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Locator into a PDF. The dispatcher re-renders the page at apply time and
|
|
240
|
+
* relocates the run by text (operator indices are never carried across the
|
|
241
|
+
* boundary, so they can't go stale).
|
|
242
|
+
*/
|
|
243
|
+
type PdfAnchor = {
|
|
244
|
+
fmt: 'pdf';
|
|
245
|
+
pageIndex: number;
|
|
246
|
+
/** Element/run text used to relocate the target run at apply time. */
|
|
247
|
+
text?: string;
|
|
248
|
+
/** Geometry in PDF points, top-left origin (informational / planner hints). */
|
|
249
|
+
bbox?: BBox;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Locator into a .xlsx workbook. Rows/cols are 0-based; the writer resolves the
|
|
253
|
+
* A1-style cell reference (`<c r="B3">`) inside the worksheet part at apply time.
|
|
254
|
+
*/
|
|
255
|
+
type XlsxAnchor = {
|
|
256
|
+
fmt: 'xlsx';
|
|
257
|
+
/** e.g. "xl/worksheets/sheet1.xml" */
|
|
258
|
+
sheetPart: string;
|
|
259
|
+
/** Display name of the sheet (informational / planner hint). */
|
|
260
|
+
sheetName?: string;
|
|
261
|
+
/** 0-based row index. */
|
|
262
|
+
row: number;
|
|
263
|
+
/** 0-based column index. */
|
|
264
|
+
col: number;
|
|
265
|
+
/** Optional 0-based inclusive range for region-level intents. */
|
|
266
|
+
range?: {
|
|
267
|
+
r1: number;
|
|
268
|
+
c1: number;
|
|
269
|
+
r2: number;
|
|
270
|
+
c2: number;
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
type EditAnchor = PptxAnchor | PdfAnchor | XlsxAnchor;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Format-neutral edit intents — the stable middle of the editing pipeline.
|
|
277
|
+
*
|
|
278
|
+
* Everything above the dispatcher (planner, API, bulk runner, UI) speaks only
|
|
279
|
+
* `EditIntent` / `EditPlan`. `dispatch.ts` is the only place that knows how to
|
|
280
|
+
* route an intent to a concrete engine (PDFEditSession for PDF, PptxWriter for
|
|
281
|
+
* PPTX). Intents are JSON-serializable so they cross the API boundary; the
|
|
282
|
+
* `find-replace` intent is anchor-free (located per file at apply time).
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
type EditIntent = {
|
|
286
|
+
op: 'replace-text';
|
|
287
|
+
anchor: EditAnchor;
|
|
288
|
+
newText: string;
|
|
289
|
+
} | {
|
|
290
|
+
op: 'replace-image';
|
|
291
|
+
anchor: EditAnchor;
|
|
292
|
+
bytes: Uint8Array;
|
|
293
|
+
format: 'png' | 'jpeg';
|
|
294
|
+
mode?: 'swap-part' | 'repoint';
|
|
295
|
+
} | {
|
|
296
|
+
op: 'set-cell';
|
|
297
|
+
anchor: EditAnchor;
|
|
298
|
+
row: number;
|
|
299
|
+
col: number;
|
|
300
|
+
newText: string;
|
|
301
|
+
} | {
|
|
302
|
+
op: 'find-replace';
|
|
303
|
+
find: string;
|
|
304
|
+
replace: string;
|
|
305
|
+
scope?: 'all' | number[];
|
|
306
|
+
} | {
|
|
307
|
+
op: 'move-shape';
|
|
308
|
+
anchor: EditAnchor;
|
|
309
|
+
xPt?: number;
|
|
310
|
+
yPt?: number;
|
|
311
|
+
} | {
|
|
312
|
+
op: 'resize-shape';
|
|
313
|
+
anchor: EditAnchor;
|
|
314
|
+
wPt?: number;
|
|
315
|
+
hPt?: number;
|
|
316
|
+
} | {
|
|
317
|
+
op: 'add-slide';
|
|
318
|
+
afterIndex?: number;
|
|
319
|
+
} | {
|
|
320
|
+
op: 'delete-slide';
|
|
321
|
+
index: number;
|
|
322
|
+
} | {
|
|
323
|
+
op: 'reorder-slide';
|
|
324
|
+
from: number;
|
|
325
|
+
to: number;
|
|
326
|
+
} | {
|
|
327
|
+
op: 'add-text-box';
|
|
328
|
+
anchor: EditAnchor;
|
|
329
|
+
xPt: number;
|
|
330
|
+
yPt: number;
|
|
331
|
+
wPt: number;
|
|
332
|
+
hPt: number;
|
|
333
|
+
text: string;
|
|
334
|
+
sizePt?: number;
|
|
335
|
+
} | {
|
|
336
|
+
op: 'clear-cell';
|
|
337
|
+
anchor: EditAnchor;
|
|
338
|
+
row: number;
|
|
339
|
+
col: number;
|
|
340
|
+
} | {
|
|
341
|
+
op: 'format-cell';
|
|
342
|
+
anchor: EditAnchor;
|
|
343
|
+
row: number;
|
|
344
|
+
col: number;
|
|
345
|
+
format: CellFormatPatch;
|
|
346
|
+
} | {
|
|
347
|
+
op: 'set-column-width';
|
|
348
|
+
anchor: EditAnchor;
|
|
349
|
+
col: number;
|
|
350
|
+
widthChars: number;
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* Partial formatting for `format-cell` — unset fields inherit the cell's
|
|
354
|
+
* current style. The writer never mutates a shared style entry; it appends.
|
|
355
|
+
*/
|
|
356
|
+
type CellFormatPatch = {
|
|
357
|
+
bold?: boolean;
|
|
358
|
+
italic?: boolean;
|
|
359
|
+
/** Number-format code, e.g. '0.00', '#,##0', 'm/d/yyyy'. */
|
|
360
|
+
numFmt?: string;
|
|
361
|
+
/** Solid fill as CSS hex '#RRGGBB'. */
|
|
362
|
+
fill?: string;
|
|
363
|
+
};
|
|
364
|
+
type EditPlan = {
|
|
365
|
+
source: {
|
|
366
|
+
fileName: string;
|
|
367
|
+
format: 'pptx' | 'pdf' | 'xlsx';
|
|
368
|
+
};
|
|
369
|
+
intents: EditIntent[];
|
|
370
|
+
};
|
|
371
|
+
/** Per-intent outcome reported back from the dispatcher. */
|
|
372
|
+
type IntentResult = {
|
|
373
|
+
op: EditIntent['op'];
|
|
374
|
+
ok: boolean;
|
|
375
|
+
applied: number;
|
|
376
|
+
error?: string;
|
|
377
|
+
/** Op-specific payload, e.g. the new slide part path from `add-slide` (for anchoring follow-up intents). */
|
|
378
|
+
detail?: string;
|
|
379
|
+
};
|
|
380
|
+
type EditResult = {
|
|
381
|
+
bytes: Uint8Array;
|
|
382
|
+
results: IntentResult[];
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Apply an edit plan to a `.xlsx`. Returns the edited bytes (the unchanged
|
|
387
|
+
* input when nothing applied) plus a per-intent result log.
|
|
388
|
+
*/
|
|
389
|
+
declare function applyXlsxPlan(bytes: Uint8Array, plan: EditPlan): Promise<EditResult>;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* XlsxEditSession — the framework-free editing session behind the sheet
|
|
393
|
+
* editor UI, mirroring `pptx/edit/PptxEditSession` (P1/P3 of PLATFORM_PLAN.md).
|
|
394
|
+
*
|
|
395
|
+
* Intent-sourced: the UI never mutates the parsed model. It calls
|
|
396
|
+
* `apply(intents)`; the writer splices the current bytes (`applyXlsxPlan`),
|
|
397
|
+
* the session snapshots the result, re-parses, and notifies subscribers,
|
|
398
|
+
* which re-render from the *saved* truth. Undo/redo replay byte snapshots.
|
|
399
|
+
*/
|
|
400
|
+
|
|
401
|
+
type SessionListener = () => void;
|
|
402
|
+
declare class XlsxEditSession {
|
|
403
|
+
readonly fileName: string;
|
|
404
|
+
/** Byte snapshots; `cursor` points at the current state, entries after it are redo states. */
|
|
405
|
+
private snapshots;
|
|
406
|
+
private cursor;
|
|
407
|
+
private parsed;
|
|
408
|
+
private listeners;
|
|
409
|
+
/** Every intent ever applied, in order (serializable audit / AI parity log). */
|
|
410
|
+
readonly intentLog: EditIntent[];
|
|
411
|
+
private constructor();
|
|
412
|
+
static load(fileName: string, bytes: Uint8Array): Promise<XlsxEditSession>;
|
|
413
|
+
/** Current file bytes (the saved truth). */
|
|
414
|
+
get bytes(): Uint8Array;
|
|
415
|
+
/** Current parsed workbook. */
|
|
416
|
+
get doc(): XlsxDocument;
|
|
417
|
+
get canUndo(): boolean;
|
|
418
|
+
get canRedo(): boolean;
|
|
419
|
+
subscribe(listener: SessionListener): () => void;
|
|
420
|
+
private notify;
|
|
421
|
+
/**
|
|
422
|
+
* Apply intents to the current bytes. When anything lands, a new snapshot
|
|
423
|
+
* becomes current, redo is cleared, and the workbook is re-parsed. Failed
|
|
424
|
+
* intents are reported per-intent, never thrown.
|
|
425
|
+
*/
|
|
426
|
+
apply(intents: EditIntent[]): Promise<IntentResult[]>;
|
|
427
|
+
undo(): Promise<boolean>;
|
|
428
|
+
redo(): Promise<boolean>;
|
|
429
|
+
/** Current bytes as a downloadable Blob. */
|
|
430
|
+
toBlob(): Blob;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Zod schemas for the edit contract — the validation boundary between an
|
|
435
|
+
* (untrusted) LLM-emitted `EditPlan` and the dispatcher (PLATFORM_PLAN.md
|
|
436
|
+
* Phase 5). Two consumers:
|
|
437
|
+
*
|
|
438
|
+
* 1. **Runtime validation** — `validateEditPlan(json)` at `/api/edit/apply`
|
|
439
|
+
* (and any agent host) rejects malformed plans with readable errors before
|
|
440
|
+
* a single intent runs.
|
|
441
|
+
* 2. **Tool definitions** — `editPlanJsonSchema()` emits a JSON Schema an agent
|
|
442
|
+
* framework can hand an LLM verbatim as the arguments schema for an
|
|
443
|
+
* "apply document edits" tool. The model then emits plans that validate.
|
|
444
|
+
*
|
|
445
|
+
* The schemas mirror the `EditIntent` / `EditAnchor` unions in this directory
|
|
446
|
+
* exactly; keep them in lockstep when the unions change (the round-trip test
|
|
447
|
+
* asserts every op is covered). `replace-image` carries binary bytes, so it is
|
|
448
|
+
* `z.any()` here — image replacement uses a binary channel, not pure JSON.
|
|
449
|
+
*/
|
|
450
|
+
|
|
451
|
+
type ValidatedPlan = {
|
|
452
|
+
ok: true;
|
|
453
|
+
plan: EditPlan;
|
|
454
|
+
} | {
|
|
455
|
+
ok: false;
|
|
456
|
+
errors: string[];
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* Validate an untrusted value as an `EditPlan`. On success returns the typed
|
|
460
|
+
* plan; on failure a flat list of `path: message` strings (safe to surface to
|
|
461
|
+
* the caller / feed back to the model).
|
|
462
|
+
*/
|
|
463
|
+
declare function validateEditPlan(input: unknown): ValidatedPlan;
|
|
464
|
+
/**
|
|
465
|
+
* JSON Schema for the whole `EditPlan` — hand this to an agent framework as the
|
|
466
|
+
* arguments schema of an "apply document edits" tool. Binary/unrepresentable
|
|
467
|
+
* fields (`replace-image.bytes`) render as permissive `{}`.
|
|
468
|
+
*/
|
|
469
|
+
declare function editPlanJsonSchema(): Record<string, unknown>;
|
|
470
|
+
|
|
471
|
+
/** Shared license/activation contracts used by both the engine gate and the server. */
|
|
472
|
+
/** Capabilities a license can unlock. `view`/`annotate` are the always-available base. */
|
|
473
|
+
type LicenseFeature = 'view' | 'annotate' | 'edit-text' | 'sign' | 'redact' | 'forms' | 'export-clean' | 'pptx-edit' | 'sheets-view' | 'sheets-edit';
|
|
474
|
+
type LicenseEdition = 'trial' | 'pro' | 'enterprise';
|
|
475
|
+
type LicenseStatus = 'UNLICENSED' | 'INVALID' | 'LICENSED' | 'GRACE' | 'DEGRADED';
|
|
476
|
+
interface LicenseState {
|
|
477
|
+
status: LicenseStatus;
|
|
478
|
+
edition: LicenseEdition | null;
|
|
479
|
+
features: LicenseFeature[];
|
|
480
|
+
/** True when render/export should be watermarked. */
|
|
481
|
+
watermark: boolean;
|
|
482
|
+
/** Human-readable reason (for diagnostics; never user-facing copy). */
|
|
483
|
+
reason: string;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Client-side license gate for the NamahaPDF engine.
|
|
488
|
+
*
|
|
489
|
+
* Flow (see configureLicense):
|
|
490
|
+
* 1. Verify the license key OFFLINE (signature, expiry, domain) — instant, no network.
|
|
491
|
+
* 2. If a cached activation token is still valid → LICENSED immediately.
|
|
492
|
+
* 3. Kick a NON-BLOCKING background activation that exchanges the key for a fresh,
|
|
493
|
+
* short-TTL activation token (which is also what the server can revoke).
|
|
494
|
+
*
|
|
495
|
+
* Offline grace is weighted heavily on purpose: transient network/server failures
|
|
496
|
+
* never punish a paying customer. We tolerate up to MAX_FAILURES (5) attempts AND a
|
|
497
|
+
* BOOTSTRAP_GRACE window before degrading — the SDK degrades immediately ONLY on an
|
|
498
|
+
* explicit signed `revoked`/`invalid` from the server.
|
|
499
|
+
*
|
|
500
|
+
* Everything is injectable (storage / fetch / clock / public key) so it is unit-testable
|
|
501
|
+
* without a browser or a server.
|
|
502
|
+
*/
|
|
503
|
+
|
|
504
|
+
interface StorageLike {
|
|
505
|
+
getItem(key: string): string | null;
|
|
506
|
+
setItem(key: string, value: string): void;
|
|
507
|
+
}
|
|
508
|
+
interface LicenseConfig {
|
|
509
|
+
/** The signed license key the developer received. */
|
|
510
|
+
licenseKey?: string;
|
|
511
|
+
/** Activation endpoint. Defaults to a same-origin `/api/license/activate`. */
|
|
512
|
+
activationUrl?: string;
|
|
513
|
+
/** Stable per-install id; auto-generated + persisted when omitted. */
|
|
514
|
+
deviceId?: string;
|
|
515
|
+
/** First-party bypass — our own app passes this so it never watermarks itself. */
|
|
516
|
+
owner?: boolean;
|
|
517
|
+
/** Override the embedded public key (tests only). */
|
|
518
|
+
publicKeyHex?: string;
|
|
519
|
+
/** Injected clock (tests). */
|
|
520
|
+
now?: () => number;
|
|
521
|
+
/** Injected storage (tests / Node). */
|
|
522
|
+
storage?: StorageLike;
|
|
523
|
+
/** Injected fetch (tests / Node). */
|
|
524
|
+
fetchImpl?: typeof fetch;
|
|
525
|
+
/** Host to domain-check against; defaults to location.hostname in the browser. */
|
|
526
|
+
host?: string;
|
|
527
|
+
/** Background retry backoff in ms (tests can shorten). */
|
|
528
|
+
retryDelays?: number[];
|
|
529
|
+
}
|
|
530
|
+
type Listener = (s: LicenseState) => void;
|
|
531
|
+
/**
|
|
532
|
+
* Configure (or reconfigure) the license gate. Verifies the key synchronously and
|
|
533
|
+
* starts a background activation. Returns the immediate state; subscribe with
|
|
534
|
+
* `onLicenseChange` for updates as activation completes.
|
|
535
|
+
*/
|
|
536
|
+
declare function configureLicense(config?: LicenseConfig): LicenseState;
|
|
537
|
+
/** Current license state (synchronous, cheap). */
|
|
538
|
+
declare function getLicenseState(): LicenseState;
|
|
539
|
+
declare function isFeatureEnabled(feature: LicenseFeature): boolean;
|
|
540
|
+
/** Throws if a feature is not licensed — use to guard gated engine operations. */
|
|
541
|
+
declare function assertFeature(feature: LicenseFeature): void;
|
|
542
|
+
declare function shouldWatermark(): boolean;
|
|
543
|
+
/** Subscribe to state changes (activation success/failure). Returns an unsubscribe. */
|
|
544
|
+
declare function onLicenseChange(cb: Listener): () => void;
|
|
545
|
+
|
|
546
|
+
export { type BorderEdge, type Cell, type CellFormatPatch, type CellRange, type CellStyle, type CellType, type EditAnchor, type EditIntent, type EditPlan, type EditResult, type GridMetrics, HEADER_H, HEADER_W, type IntentResult, type LicenseConfig, type LicenseEdition, type LicenseFeature, type LicenseState, type LicenseStatus, type Row, type Sheet, SheetRenderer, type SheetViewport, type Workbook, type XlsxAnchor, XlsxDocument, XlsxEditSession, applyXlsxPlan, assertFeature, buildMetrics, configureLicense, editPlanJsonSchema, formatValue, generalNumber, getLicenseState, isDateFormat, isFeatureEnabled, onLicenseChange, rcToRef, refToRC, refToRange, serialToDate, shouldWatermark, validateEditPlan };
|