@maravilla-labs/platform 0.3.11 → 0.4.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/config.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -1
- package/dist/transforms-B3XZ8wYs.d.ts +300 -0
- package/package.json +1 -1
- package/src/remote-client.ts +33 -1
- package/src/transforms.ts +172 -1
- package/dist/transforms-KzpoYW43.d.ts +0 -156
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Media transforms — async ffmpeg / image / OCR jobs.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Rust `platform.media.transforms` surface. All mutating
|
|
5
|
+
* methods (transcode/thumbnail/resize/ocr) return a {@link JobHandle}
|
|
6
|
+
* whose `outputKey` is content-addressed via {@link keyFor} and known
|
|
7
|
+
* up front — clients can render placeholder UI for the derived asset
|
|
8
|
+
* before the worker even starts the job.
|
|
9
|
+
*
|
|
10
|
+
* The runtime injects the live JS object as `platform.media.transforms`
|
|
11
|
+
* (see `crates/runtime/src/ops/platform/js_bindings/platform_js.rs`).
|
|
12
|
+
* The static helper {@link keyFor} is exported separately and runs
|
|
13
|
+
* client-side — it must produce byte-for-byte identical output to the
|
|
14
|
+
* Rust `derive_key`. The shared golden-vector fixture
|
|
15
|
+
* `crates/platform/tests/derive_key_vectors.json` is consumed from both
|
|
16
|
+
* the Rust integration test (`crates/platform/tests/derive_key_golden.rs`)
|
|
17
|
+
* and the TS test (`packages/platform/tests/derive-key.test.ts`) to keep
|
|
18
|
+
* the two in lockstep.
|
|
19
|
+
*/
|
|
20
|
+
/** Video container targets supported by v1. */
|
|
21
|
+
type VideoFormat = 'mp4' | 'webm';
|
|
22
|
+
/** Image output formats supported by v1. */
|
|
23
|
+
type ImageFormat = 'jpg' | 'png' | 'webp';
|
|
24
|
+
/** Lifecycle state of a transform job. */
|
|
25
|
+
type JobStatus = 'pending' | 'running' | 'complete' | 'failed';
|
|
26
|
+
/** Options for `transforms.transcode`. */
|
|
27
|
+
interface TranscodeOpts {
|
|
28
|
+
format: VideoFormat;
|
|
29
|
+
codec?: string;
|
|
30
|
+
max_width?: number;
|
|
31
|
+
max_height?: number;
|
|
32
|
+
audio_codec?: string;
|
|
33
|
+
bitrate_kbps?: number;
|
|
34
|
+
}
|
|
35
|
+
/** Options for `transforms.thumbnail` — extract a single video frame. */
|
|
36
|
+
interface ThumbnailOpts {
|
|
37
|
+
/** Offset into the source. Accepts `"00:00:01"`, `"1s"`, or a plain number-as-string. */
|
|
38
|
+
at: string;
|
|
39
|
+
width?: number;
|
|
40
|
+
height?: number;
|
|
41
|
+
/** Defaults to `"jpg"` server-side when omitted. */
|
|
42
|
+
format?: ImageFormat;
|
|
43
|
+
quality?: number;
|
|
44
|
+
}
|
|
45
|
+
/** Options for `transforms.resize`. */
|
|
46
|
+
interface ResizeOpts {
|
|
47
|
+
width?: number;
|
|
48
|
+
height?: number;
|
|
49
|
+
format: ImageFormat;
|
|
50
|
+
quality?: number;
|
|
51
|
+
strip_metadata?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Options for `transforms.ocr`. */
|
|
54
|
+
interface OcrOpts {
|
|
55
|
+
/** Tesseract language code(s). Defaults to `"eng"` server-side when omitted. */
|
|
56
|
+
lang?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* LibreOffice-supported output container for `doc_convert`. Each value
|
|
60
|
+
* maps to a `--convert-to` filter and a derived-key extension.
|
|
61
|
+
*/
|
|
62
|
+
type DocFormat = 'pdf' | 'docx' | 'odt' | 'xlsx' | 'html' | 'txt' | 'rtf';
|
|
63
|
+
/** Options for `transforms.docToPdf`. */
|
|
64
|
+
interface DocToPdfOpts {
|
|
65
|
+
}
|
|
66
|
+
/** Options for `transforms.docThumbnail` — single-page raster of a document. */
|
|
67
|
+
interface DocThumbnailOpts {
|
|
68
|
+
width?: number;
|
|
69
|
+
height?: number;
|
|
70
|
+
/** Defaults to `"png"` server-side when omitted. */
|
|
71
|
+
format?: ImageFormat;
|
|
72
|
+
/** 1-indexed page. Defaults to `1` server-side when omitted. */
|
|
73
|
+
page?: number;
|
|
74
|
+
quality?: number;
|
|
75
|
+
}
|
|
76
|
+
/** Options for `transforms.docConvert` — generic LibreOffice conversion. */
|
|
77
|
+
interface DocConvertOpts {
|
|
78
|
+
to: DocFormat;
|
|
79
|
+
}
|
|
80
|
+
/** Options for `transforms.docToMarkdown` — RAG / LLM-friendly extraction. */
|
|
81
|
+
interface DocToMarkdownOpts {
|
|
82
|
+
/** Keep GFM tables in the output. Defaults to `true` server-side when omitted. */
|
|
83
|
+
preserve_tables?: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Options for `transforms.docToHtml` — single-file HTML with images
|
|
87
|
+
* inlined as base64 `data:` URIs and CSS inlined as `<style>` blocks.
|
|
88
|
+
* The output is a self-contained `.html` the caller can email, embed
|
|
89
|
+
* in an `<iframe>`, or hand to an LLM as one string.
|
|
90
|
+
*/
|
|
91
|
+
interface DocToHtmlOpts {
|
|
92
|
+
/** Inline `<img>` references as base64 `data:` URIs. Defaults `true` server-side. */
|
|
93
|
+
inline_images?: boolean;
|
|
94
|
+
/** Inline CSS as a single `<style>` block. Defaults `true` server-side. */
|
|
95
|
+
inline_styles?: boolean;
|
|
96
|
+
}
|
|
97
|
+
/** Reference to a replacement image — its key in tenant `STORAGE`. */
|
|
98
|
+
interface ImageRef {
|
|
99
|
+
src_key: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Options for `transforms.docReplaceImages` — substitute placeholder
|
|
103
|
+
* text-tags and/or named drawing objects with caller-supplied images.
|
|
104
|
+
*
|
|
105
|
+
* Keys in `placeholders` are the literal text the user types in their
|
|
106
|
+
* template (e.g. `"{{USER_LOGO}}"`). Keys in `named_objects` are
|
|
107
|
+
* LibreOffice draw-object Name properties (set in Word/Writer via
|
|
108
|
+
* Format → Anchor → Properties → Name) — named-object swap preserves
|
|
109
|
+
* the template's exact frame size, border, anchor, and wrap.
|
|
110
|
+
*/
|
|
111
|
+
interface DocReplaceImagesOpts {
|
|
112
|
+
/** Optional output format. When unset, the worker keeps the input format. */
|
|
113
|
+
output_format?: DocFormat;
|
|
114
|
+
/** `{{TAG}}` → image. The text is matched verbatim; missing tags are silently skipped. */
|
|
115
|
+
placeholders?: Record<string, ImageRef>;
|
|
116
|
+
/** `objectName` → image. */
|
|
117
|
+
named_objects?: Record<string, ImageRef>;
|
|
118
|
+
/** Pre-resize each image to the target frame's dimensions. Defaults `true` server-side. */
|
|
119
|
+
auto_resize?: boolean;
|
|
120
|
+
}
|
|
121
|
+
/** A single QR code to embed. Exactly one of `placeholder` or `named_object` must be set. */
|
|
122
|
+
interface QrCodeSpec {
|
|
123
|
+
placeholder?: string;
|
|
124
|
+
named_object?: string;
|
|
125
|
+
/** Payload to encode — typically a URL. ≤ 1500 bytes; the server rejects larger. */
|
|
126
|
+
payload: string;
|
|
127
|
+
/** Output PNG width in pixels. QR codes are square. Defaults to 256 server-side. */
|
|
128
|
+
size?: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Options for `transforms.docInsertQrCode` — generate QR PNG(s)
|
|
132
|
+
* server-side and inject them via the same pipeline as
|
|
133
|
+
* `docReplaceImages`.
|
|
134
|
+
*/
|
|
135
|
+
interface DocInsertQrCodeOpts {
|
|
136
|
+
output_format?: DocFormat;
|
|
137
|
+
codes: QrCodeSpec[];
|
|
138
|
+
}
|
|
139
|
+
/** Inline QR-code spec for `DocTemplateMergeOpts.qr_codes`. The placeholder
|
|
140
|
+
* is the map key, so we only carry payload + size here. */
|
|
141
|
+
interface QrPayload {
|
|
142
|
+
payload: string;
|
|
143
|
+
/** Output PNG width in pixels (square). Defaults to 256 server-side. */
|
|
144
|
+
size?: number;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Options for `transforms.docTemplateMerge` — the unified single-call
|
|
148
|
+
* template-fill: text + images + QR codes in one render. The headline
|
|
149
|
+
* templating API; one call → one server-side render → one round-trip.
|
|
150
|
+
*
|
|
151
|
+
* `data` runs first (string find-and-replace), then `images` (text-tag →
|
|
152
|
+
* image insertion), then `named_objects` (preserves frame size/border),
|
|
153
|
+
* then `qr_codes` (server generates each PNG and injects via the same
|
|
154
|
+
* placeholder pipeline as `images`). All inside one document load.
|
|
155
|
+
*/
|
|
156
|
+
interface DocTemplateMergeOpts {
|
|
157
|
+
/** Output format. Defaults to keeping the input format. */
|
|
158
|
+
output_format?: DocFormat;
|
|
159
|
+
/** Text-tag → string. e.g. `{ '{{NAME}}': 'Senol Aktas' }`. */
|
|
160
|
+
data?: Record<string, string>;
|
|
161
|
+
/** Text-tag → image. Same semantics as `DocReplaceImagesOpts.placeholders`. */
|
|
162
|
+
images?: Record<string, ImageRef>;
|
|
163
|
+
/** Object-name → image. Preserves the template's frame size/border/wrap. */
|
|
164
|
+
named_objects?: Record<string, ImageRef>;
|
|
165
|
+
/** Text-tag → QR code spec. Server generates the PNG; ≤ 1500-byte payload. */
|
|
166
|
+
qr_codes?: Record<string, QrPayload>;
|
|
167
|
+
/** Pre-resize images / QR PNGs to anchor frames. Defaults `true`. */
|
|
168
|
+
auto_resize?: boolean;
|
|
169
|
+
}
|
|
170
|
+
/** Tagged union of all transform requests — matches the Rust `TransformSpec`. */
|
|
171
|
+
type TransformSpec = ({
|
|
172
|
+
kind: 'transcode';
|
|
173
|
+
} & TranscodeOpts) | ({
|
|
174
|
+
kind: 'thumbnail';
|
|
175
|
+
} & ThumbnailOpts) | ({
|
|
176
|
+
kind: 'resize';
|
|
177
|
+
} & ResizeOpts) | ({
|
|
178
|
+
kind: 'ocr';
|
|
179
|
+
} & OcrOpts) | ({
|
|
180
|
+
kind: 'doc_to_pdf';
|
|
181
|
+
} & DocToPdfOpts) | ({
|
|
182
|
+
kind: 'doc_thumbnail';
|
|
183
|
+
} & DocThumbnailOpts) | ({
|
|
184
|
+
kind: 'doc_convert';
|
|
185
|
+
} & DocConvertOpts) | ({
|
|
186
|
+
kind: 'doc_to_markdown';
|
|
187
|
+
} & DocToMarkdownOpts) | ({
|
|
188
|
+
kind: 'doc_to_html';
|
|
189
|
+
} & DocToHtmlOpts) | ({
|
|
190
|
+
kind: 'doc_replace_images';
|
|
191
|
+
} & DocReplaceImagesOpts) | ({
|
|
192
|
+
kind: 'doc_insert_qr_code';
|
|
193
|
+
} & DocInsertQrCodeOpts) | ({
|
|
194
|
+
kind: 'doc_template_merge';
|
|
195
|
+
} & DocTemplateMergeOpts);
|
|
196
|
+
/** Probe output. All fields optional — ffprobe doesn't fill every one for every input. */
|
|
197
|
+
interface MediaInfo {
|
|
198
|
+
duration_secs?: number;
|
|
199
|
+
width?: number;
|
|
200
|
+
height?: number;
|
|
201
|
+
video_codec?: string;
|
|
202
|
+
audio_codec?: string;
|
|
203
|
+
bitrate_bps?: number;
|
|
204
|
+
container?: string;
|
|
205
|
+
}
|
|
206
|
+
/** Returned by every mutating transforms method. */
|
|
207
|
+
interface JobHandle {
|
|
208
|
+
id: string;
|
|
209
|
+
src_key: string;
|
|
210
|
+
output_key: string;
|
|
211
|
+
status: JobStatus;
|
|
212
|
+
}
|
|
213
|
+
/** Returned by `transforms.job(id)`. */
|
|
214
|
+
interface JobStatusResponse {
|
|
215
|
+
id: string;
|
|
216
|
+
status: JobStatus;
|
|
217
|
+
}
|
|
218
|
+
interface TransformsService {
|
|
219
|
+
transcode(srcKey: string, opts: TranscodeOpts): Promise<JobHandle>;
|
|
220
|
+
thumbnail(srcKey: string, opts: ThumbnailOpts): Promise<JobHandle>;
|
|
221
|
+
resize(srcKey: string, opts: ResizeOpts): Promise<JobHandle>;
|
|
222
|
+
probe(srcKey: string): Promise<MediaInfo>;
|
|
223
|
+
ocr(srcKey: string, opts?: OcrOpts | null): Promise<JobHandle>;
|
|
224
|
+
/** Convert a LibreOffice-supported document to PDF. */
|
|
225
|
+
docToPdf(srcKey: string, opts?: DocToPdfOpts | null): Promise<JobHandle>;
|
|
226
|
+
/** Render a single page of a document as a raster thumbnail. */
|
|
227
|
+
docThumbnail(srcKey: string, opts: DocThumbnailOpts): Promise<JobHandle>;
|
|
228
|
+
/** Generic LibreOffice format conversion. */
|
|
229
|
+
docConvert(srcKey: string, opts: DocConvertOpts): Promise<JobHandle>;
|
|
230
|
+
/** Extract Markdown from a document (HTML → pandoc pipeline). */
|
|
231
|
+
docToMarkdown(srcKey: string, opts?: DocToMarkdownOpts | null): Promise<JobHandle>;
|
|
232
|
+
/** Convert to single-file HTML with base64-inlined images and inlined CSS. */
|
|
233
|
+
docToHtml(srcKey: string, opts?: DocToHtmlOpts | null): Promise<JobHandle>;
|
|
234
|
+
/** Swap placeholder text-tags and/or named drawing objects with caller-supplied images. */
|
|
235
|
+
docReplaceImages(srcKey: string, opts: DocReplaceImagesOpts): Promise<JobHandle>;
|
|
236
|
+
/** Generate QR codes server-side and inject them via the image-replace pipeline. */
|
|
237
|
+
docInsertQrCode(srcKey: string, opts: DocInsertQrCodeOpts): Promise<JobHandle>;
|
|
238
|
+
/** Unified template fill: text + images + QR codes in one render. The headline templating API. */
|
|
239
|
+
docTemplateMerge(srcKey: string, opts: DocTemplateMergeOpts): Promise<JobHandle>;
|
|
240
|
+
job(id: string): Promise<JobStatusResponse>;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Compute the canonical derived-asset key for `(srcKey, spec)`.
|
|
244
|
+
*
|
|
245
|
+
* Shape: `__derived/<srcHash>/<variantHash>.<ext>` where each hash is
|
|
246
|
+
* the first 16 hex chars (8 bytes / 64 bits) of `SHA-256(...)`.
|
|
247
|
+
*
|
|
248
|
+
* **MUST** match Rust `platform::media::transforms::derive_key` byte-for-byte.
|
|
249
|
+
* Cross-language golden vectors live at
|
|
250
|
+
* `crates/platform/tests/derive_key_vectors.json`.
|
|
251
|
+
*/
|
|
252
|
+
declare function keyFor(srcKey: string, spec: TransformSpec): string;
|
|
253
|
+
/** Convenience namespace mirroring the Rust `transforms::keyFor` re-export. */
|
|
254
|
+
declare const transforms: {
|
|
255
|
+
keyFor: typeof keyFor;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Per-pattern transforms declaration used inside the `transforms` block of
|
|
259
|
+
* `maravilla.config.ts`. Each field accepts a single opts object or an array.
|
|
260
|
+
*
|
|
261
|
+
* `variants` is sugar for `resize` arrays — convenient for the common
|
|
262
|
+
* "image → multiple resized renditions" case.
|
|
263
|
+
*/
|
|
264
|
+
interface TransformsPatternSpec {
|
|
265
|
+
transcode?: TranscodeOpts | TranscodeOpts[];
|
|
266
|
+
thumbnail?: ThumbnailOpts | ThumbnailOpts[];
|
|
267
|
+
resize?: ResizeOpts | ResizeOpts[];
|
|
268
|
+
/** Sugar for `resize` arrays — same shape, same semantics. */
|
|
269
|
+
variants?: ResizeOpts[];
|
|
270
|
+
ocr?: OcrOpts | OcrOpts[];
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Top-level `transforms` block. Keys are glob path patterns matched against
|
|
274
|
+
* the storage key of every uploaded object. The adapter compiles each entry
|
|
275
|
+
* into a synthetic `storage.put` event handler that fans out the declared
|
|
276
|
+
* transforms via `Promise.all`.
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* import { defineConfig } from '@maravilla-labs/platform/config';
|
|
281
|
+
*
|
|
282
|
+
* export default defineConfig({
|
|
283
|
+
* transforms: {
|
|
284
|
+
* 'uploads/videos/**': {
|
|
285
|
+
* transcode: [{ format: 'mp4' }, { format: 'webm' }],
|
|
286
|
+
* thumbnail: { at: '1s', width: 640, format: 'jpg' },
|
|
287
|
+
* },
|
|
288
|
+
* 'uploads/photos/**': {
|
|
289
|
+
* variants: [
|
|
290
|
+
* { width: 1600, format: 'webp', quality: 85 },
|
|
291
|
+
* { width: 400, format: 'webp', quality: 80 },
|
|
292
|
+
* ],
|
|
293
|
+
* },
|
|
294
|
+
* },
|
|
295
|
+
* });
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
type TransformsConfig = Record<string, TransformsPatternSpec>;
|
|
299
|
+
|
|
300
|
+
export { type DocConvertOpts as D, type ImageFormat as I, type JobHandle as J, type MediaInfo as M, type OcrOpts as O, type QrCodeSpec as Q, type ResizeOpts as R, type TransformsConfig as T, type VideoFormat as V, type TransformsPatternSpec as a, type DocFormat as b, type DocInsertQrCodeOpts as c, type DocReplaceImagesOpts as d, type DocTemplateMergeOpts as e, type DocThumbnailOpts as f, type DocToHtmlOpts as g, type DocToMarkdownOpts as h, type DocToPdfOpts as i, type ImageRef as j, type JobStatus as k, type JobStatusResponse as l, type QrPayload as m, type ThumbnailOpts as n, type TranscodeOpts as o, type TransformSpec as p, type TransformsService as q, keyFor as r, transforms as t };
|
package/package.json
CHANGED
package/src/remote-client.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KvNamespace, KvListResult, Database, DbFindOptions, Storage, RealtimeService, PresenceService, AuthService, AuthCaller, AuthUser, AuthSession, AuthField, RegisterOptions, LoginOptions, UserListFilter, UserListResponse, UpdateUserOptions, PolicyService, VectorIndexSpec, VectorIndexDescriptor, VectorQueryWithFilter, VectorSearchHit, IndexSpec, IndexDescriptor, Workflows, WorkflowHandle, WorkflowRun, WorkflowStepRecord } from './types.js';
|
|
2
|
-
import type { TransformsService, TranscodeOpts, ThumbnailOpts, ResizeOpts, OcrOpts, JobHandle, JobStatusResponse, MediaInfo } from './transforms.js';
|
|
2
|
+
import type { TransformsService, TranscodeOpts, ThumbnailOpts, ResizeOpts, OcrOpts, DocToPdfOpts, DocThumbnailOpts, DocConvertOpts, DocToMarkdownOpts, DocToHtmlOpts, DocReplaceImagesOpts, DocInsertQrCodeOpts, DocTemplateMergeOpts, JobHandle, JobStatusResponse, MediaInfo } from './transforms.js';
|
|
3
3
|
import { RemoteMediaService } from './media.js';
|
|
4
4
|
import { getRequestAuthHeader } from './request-scope.js';
|
|
5
5
|
|
|
@@ -1110,6 +1110,38 @@ class RemoteTransformsService implements TransformsService {
|
|
|
1110
1110
|
return this.post<JobHandle>('/ocr', { srcKey, opts: opts ?? {} });
|
|
1111
1111
|
}
|
|
1112
1112
|
|
|
1113
|
+
docToPdf(srcKey: string, opts?: DocToPdfOpts | null): Promise<JobHandle> {
|
|
1114
|
+
return this.post<JobHandle>('/doc_to_pdf', { srcKey, opts: opts ?? {} });
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
docThumbnail(srcKey: string, opts: DocThumbnailOpts): Promise<JobHandle> {
|
|
1118
|
+
return this.post<JobHandle>('/doc_thumbnail', { srcKey, opts });
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
docConvert(srcKey: string, opts: DocConvertOpts): Promise<JobHandle> {
|
|
1122
|
+
return this.post<JobHandle>('/doc_convert', { srcKey, opts });
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
docToMarkdown(srcKey: string, opts?: DocToMarkdownOpts | null): Promise<JobHandle> {
|
|
1126
|
+
return this.post<JobHandle>('/doc_to_markdown', { srcKey, opts: opts ?? {} });
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
docToHtml(srcKey: string, opts?: DocToHtmlOpts | null): Promise<JobHandle> {
|
|
1130
|
+
return this.post<JobHandle>('/doc_to_html', { srcKey, opts: opts ?? {} });
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
docReplaceImages(srcKey: string, opts: DocReplaceImagesOpts): Promise<JobHandle> {
|
|
1134
|
+
return this.post<JobHandle>('/doc_replace_images', { srcKey, opts });
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
docInsertQrCode(srcKey: string, opts: DocInsertQrCodeOpts): Promise<JobHandle> {
|
|
1138
|
+
return this.post<JobHandle>('/doc_insert_qr_code', { srcKey, opts });
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
docTemplateMerge(srcKey: string, opts: DocTemplateMergeOpts): Promise<JobHandle> {
|
|
1142
|
+
return this.post<JobHandle>('/doc_template_merge', { srcKey, opts });
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1113
1145
|
probe(srcKey: string): Promise<MediaInfo> {
|
|
1114
1146
|
return this.post<MediaInfo>('/probe', { srcKey });
|
|
1115
1147
|
}
|
package/src/transforms.ts
CHANGED
|
@@ -71,12 +71,144 @@ export interface OcrOpts {
|
|
|
71
71
|
lang?: string;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* LibreOffice-supported output container for `doc_convert`. Each value
|
|
76
|
+
* maps to a `--convert-to` filter and a derived-key extension.
|
|
77
|
+
*/
|
|
78
|
+
export type DocFormat = 'pdf' | 'docx' | 'odt' | 'xlsx' | 'html' | 'txt' | 'rtf';
|
|
79
|
+
|
|
80
|
+
/** Options for `transforms.docToPdf`. */
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
82
|
+
export interface DocToPdfOpts {}
|
|
83
|
+
|
|
84
|
+
/** Options for `transforms.docThumbnail` — single-page raster of a document. */
|
|
85
|
+
export interface DocThumbnailOpts {
|
|
86
|
+
width?: number;
|
|
87
|
+
height?: number;
|
|
88
|
+
/** Defaults to `"png"` server-side when omitted. */
|
|
89
|
+
format?: ImageFormat;
|
|
90
|
+
/** 1-indexed page. Defaults to `1` server-side when omitted. */
|
|
91
|
+
page?: number;
|
|
92
|
+
quality?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Options for `transforms.docConvert` — generic LibreOffice conversion. */
|
|
96
|
+
export interface DocConvertOpts {
|
|
97
|
+
to: DocFormat;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Options for `transforms.docToMarkdown` — RAG / LLM-friendly extraction. */
|
|
101
|
+
export interface DocToMarkdownOpts {
|
|
102
|
+
/** Keep GFM tables in the output. Defaults to `true` server-side when omitted. */
|
|
103
|
+
preserve_tables?: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Options for `transforms.docToHtml` — single-file HTML with images
|
|
108
|
+
* inlined as base64 `data:` URIs and CSS inlined as `<style>` blocks.
|
|
109
|
+
* The output is a self-contained `.html` the caller can email, embed
|
|
110
|
+
* in an `<iframe>`, or hand to an LLM as one string.
|
|
111
|
+
*/
|
|
112
|
+
export interface DocToHtmlOpts {
|
|
113
|
+
/** Inline `<img>` references as base64 `data:` URIs. Defaults `true` server-side. */
|
|
114
|
+
inline_images?: boolean;
|
|
115
|
+
/** Inline CSS as a single `<style>` block. Defaults `true` server-side. */
|
|
116
|
+
inline_styles?: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Reference to a replacement image — its key in tenant `STORAGE`. */
|
|
120
|
+
export interface ImageRef {
|
|
121
|
+
src_key: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Options for `transforms.docReplaceImages` — substitute placeholder
|
|
126
|
+
* text-tags and/or named drawing objects with caller-supplied images.
|
|
127
|
+
*
|
|
128
|
+
* Keys in `placeholders` are the literal text the user types in their
|
|
129
|
+
* template (e.g. `"{{USER_LOGO}}"`). Keys in `named_objects` are
|
|
130
|
+
* LibreOffice draw-object Name properties (set in Word/Writer via
|
|
131
|
+
* Format → Anchor → Properties → Name) — named-object swap preserves
|
|
132
|
+
* the template's exact frame size, border, anchor, and wrap.
|
|
133
|
+
*/
|
|
134
|
+
export interface DocReplaceImagesOpts {
|
|
135
|
+
/** Optional output format. When unset, the worker keeps the input format. */
|
|
136
|
+
output_format?: DocFormat;
|
|
137
|
+
/** `{{TAG}}` → image. The text is matched verbatim; missing tags are silently skipped. */
|
|
138
|
+
placeholders?: Record<string, ImageRef>;
|
|
139
|
+
/** `objectName` → image. */
|
|
140
|
+
named_objects?: Record<string, ImageRef>;
|
|
141
|
+
/** Pre-resize each image to the target frame's dimensions. Defaults `true` server-side. */
|
|
142
|
+
auto_resize?: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** A single QR code to embed. Exactly one of `placeholder` or `named_object` must be set. */
|
|
146
|
+
export interface QrCodeSpec {
|
|
147
|
+
placeholder?: string;
|
|
148
|
+
named_object?: string;
|
|
149
|
+
/** Payload to encode — typically a URL. ≤ 1500 bytes; the server rejects larger. */
|
|
150
|
+
payload: string;
|
|
151
|
+
/** Output PNG width in pixels. QR codes are square. Defaults to 256 server-side. */
|
|
152
|
+
size?: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Options for `transforms.docInsertQrCode` — generate QR PNG(s)
|
|
157
|
+
* server-side and inject them via the same pipeline as
|
|
158
|
+
* `docReplaceImages`.
|
|
159
|
+
*/
|
|
160
|
+
export interface DocInsertQrCodeOpts {
|
|
161
|
+
output_format?: DocFormat;
|
|
162
|
+
codes: QrCodeSpec[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Inline QR-code spec for `DocTemplateMergeOpts.qr_codes`. The placeholder
|
|
166
|
+
* is the map key, so we only carry payload + size here. */
|
|
167
|
+
export interface QrPayload {
|
|
168
|
+
payload: string;
|
|
169
|
+
/** Output PNG width in pixels (square). Defaults to 256 server-side. */
|
|
170
|
+
size?: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Options for `transforms.docTemplateMerge` — the unified single-call
|
|
175
|
+
* template-fill: text + images + QR codes in one render. The headline
|
|
176
|
+
* templating API; one call → one server-side render → one round-trip.
|
|
177
|
+
*
|
|
178
|
+
* `data` runs first (string find-and-replace), then `images` (text-tag →
|
|
179
|
+
* image insertion), then `named_objects` (preserves frame size/border),
|
|
180
|
+
* then `qr_codes` (server generates each PNG and injects via the same
|
|
181
|
+
* placeholder pipeline as `images`). All inside one document load.
|
|
182
|
+
*/
|
|
183
|
+
export interface DocTemplateMergeOpts {
|
|
184
|
+
/** Output format. Defaults to keeping the input format. */
|
|
185
|
+
output_format?: DocFormat;
|
|
186
|
+
/** Text-tag → string. e.g. `{ '{{NAME}}': 'Senol Aktas' }`. */
|
|
187
|
+
data?: Record<string, string>;
|
|
188
|
+
/** Text-tag → image. Same semantics as `DocReplaceImagesOpts.placeholders`. */
|
|
189
|
+
images?: Record<string, ImageRef>;
|
|
190
|
+
/** Object-name → image. Preserves the template's frame size/border/wrap. */
|
|
191
|
+
named_objects?: Record<string, ImageRef>;
|
|
192
|
+
/** Text-tag → QR code spec. Server generates the PNG; ≤ 1500-byte payload. */
|
|
193
|
+
qr_codes?: Record<string, QrPayload>;
|
|
194
|
+
/** Pre-resize images / QR PNGs to anchor frames. Defaults `true`. */
|
|
195
|
+
auto_resize?: boolean;
|
|
196
|
+
}
|
|
197
|
+
|
|
74
198
|
/** Tagged union of all transform requests — matches the Rust `TransformSpec`. */
|
|
75
199
|
export type TransformSpec =
|
|
76
200
|
| ({ kind: 'transcode' } & TranscodeOpts)
|
|
77
201
|
| ({ kind: 'thumbnail' } & ThumbnailOpts)
|
|
78
202
|
| ({ kind: 'resize' } & ResizeOpts)
|
|
79
|
-
| ({ kind: 'ocr' } & OcrOpts)
|
|
203
|
+
| ({ kind: 'ocr' } & OcrOpts)
|
|
204
|
+
| ({ kind: 'doc_to_pdf' } & DocToPdfOpts)
|
|
205
|
+
| ({ kind: 'doc_thumbnail' } & DocThumbnailOpts)
|
|
206
|
+
| ({ kind: 'doc_convert' } & DocConvertOpts)
|
|
207
|
+
| ({ kind: 'doc_to_markdown' } & DocToMarkdownOpts)
|
|
208
|
+
| ({ kind: 'doc_to_html' } & DocToHtmlOpts)
|
|
209
|
+
| ({ kind: 'doc_replace_images' } & DocReplaceImagesOpts)
|
|
210
|
+
| ({ kind: 'doc_insert_qr_code' } & DocInsertQrCodeOpts)
|
|
211
|
+
| ({ kind: 'doc_template_merge' } & DocTemplateMergeOpts);
|
|
80
212
|
|
|
81
213
|
/** Probe output. All fields optional — ffprobe doesn't fill every one for every input. */
|
|
82
214
|
export interface MediaInfo {
|
|
@@ -111,6 +243,22 @@ export interface TransformsService {
|
|
|
111
243
|
resize(srcKey: string, opts: ResizeOpts): Promise<JobHandle>;
|
|
112
244
|
probe(srcKey: string): Promise<MediaInfo>;
|
|
113
245
|
ocr(srcKey: string, opts?: OcrOpts | null): Promise<JobHandle>;
|
|
246
|
+
/** Convert a LibreOffice-supported document to PDF. */
|
|
247
|
+
docToPdf(srcKey: string, opts?: DocToPdfOpts | null): Promise<JobHandle>;
|
|
248
|
+
/** Render a single page of a document as a raster thumbnail. */
|
|
249
|
+
docThumbnail(srcKey: string, opts: DocThumbnailOpts): Promise<JobHandle>;
|
|
250
|
+
/** Generic LibreOffice format conversion. */
|
|
251
|
+
docConvert(srcKey: string, opts: DocConvertOpts): Promise<JobHandle>;
|
|
252
|
+
/** Extract Markdown from a document (HTML → pandoc pipeline). */
|
|
253
|
+
docToMarkdown(srcKey: string, opts?: DocToMarkdownOpts | null): Promise<JobHandle>;
|
|
254
|
+
/** Convert to single-file HTML with base64-inlined images and inlined CSS. */
|
|
255
|
+
docToHtml(srcKey: string, opts?: DocToHtmlOpts | null): Promise<JobHandle>;
|
|
256
|
+
/** Swap placeholder text-tags and/or named drawing objects with caller-supplied images. */
|
|
257
|
+
docReplaceImages(srcKey: string, opts: DocReplaceImagesOpts): Promise<JobHandle>;
|
|
258
|
+
/** Generate QR codes server-side and inject them via the image-replace pipeline. */
|
|
259
|
+
docInsertQrCode(srcKey: string, opts: DocInsertQrCodeOpts): Promise<JobHandle>;
|
|
260
|
+
/** Unified template fill: text + images + QR codes in one render. The headline templating API. */
|
|
261
|
+
docTemplateMerge(srcKey: string, opts: DocTemplateMergeOpts): Promise<JobHandle>;
|
|
114
262
|
job(id: string): Promise<JobStatusResponse>;
|
|
115
263
|
}
|
|
116
264
|
|
|
@@ -164,6 +312,29 @@ function outputExtension(spec: TransformSpec): string {
|
|
|
164
312
|
}
|
|
165
313
|
case 'ocr':
|
|
166
314
|
return 'txt';
|
|
315
|
+
case 'doc_to_pdf':
|
|
316
|
+
return 'pdf';
|
|
317
|
+
case 'doc_thumbnail': {
|
|
318
|
+
// `format` defaults to png server-side — mirror so the canonical
|
|
319
|
+
// JSON the Rust impl hashes matches the JS one for default-shape
|
|
320
|
+
// requests.
|
|
321
|
+
const fmt = (spec as { format?: ImageFormat }).format ?? 'png';
|
|
322
|
+
return fmt;
|
|
323
|
+
}
|
|
324
|
+
case 'doc_convert':
|
|
325
|
+
return spec.to;
|
|
326
|
+
case 'doc_to_markdown':
|
|
327
|
+
return 'md';
|
|
328
|
+
case 'doc_to_html':
|
|
329
|
+
return 'html';
|
|
330
|
+
case 'doc_replace_images':
|
|
331
|
+
case 'doc_insert_qr_code':
|
|
332
|
+
case 'doc_template_merge':
|
|
333
|
+
// Mirror the Rust fallback: when output_format is omitted the
|
|
334
|
+
// result preserves the input format, which we don't know without
|
|
335
|
+
// inspecting bytes — fall back to docx (the dominant template
|
|
336
|
+
// format).
|
|
337
|
+
return spec.output_format ?? 'docx';
|
|
167
338
|
default: {
|
|
168
339
|
const _exhaust: never = spec;
|
|
169
340
|
throw new Error(`unknown transform kind: ${(_exhaust as { kind: string }).kind}`);
|