-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathartifacts.ts
More file actions
88 lines (76 loc) · 2.5 KB
/
Copy pathartifacts.ts
File metadata and controls
88 lines (76 loc) · 2.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import path from 'node:path';
import type { RenderArtifact } from '../types.js';
/**
* Normalize a `tasks.down()` payload into ordered artifacts.
*
* DeckOps returns three different shapes depending on the task
* (see DeckTaskTypeResult in @deckops/sdk):
*
* ConvertFileResult[] — multi-frame converters (ppt2image, pdf2image, ...)
* FileResult — single-file converters (html2png, convertWebp, ...)
* { target: FileResult } — html2pptx
*
* A file tuple is `[path, bytes, hash, bounds?]`, where `bounds` carries the
* page geometry, so width/height never have to be measured locally.
*/
export function normalizeTaskResult(result: unknown): RenderArtifact[] {
const tuples = collectTuples(result);
return tuples.map((tuple, index) => toArtifact(tuple, index));
}
type FileTuple = [string, ...unknown[]];
function collectTuples(result: unknown): FileTuple[] {
if (isFileTuple(result)) {
return [result];
}
if (Array.isArray(result)) {
return result.filter(isFileTuple);
}
if (result && typeof result === 'object') {
const record = result as Record<string, unknown>;
// html2pptx wraps its output; other single-file payloads may too.
for (const key of ['target', 'file', 'output']) {
const value = record[key];
if (isFileTuple(value)) {
return [value];
}
}
if (typeof record.downloadUrl === 'string') {
return [[record.downloadUrl]];
}
}
return [];
}
function isFileTuple(value: unknown): value is FileTuple {
return Array.isArray(value) && typeof value[0] === 'string';
}
interface Bounds {
w?: number;
h?: number;
total?: number;
}
function toArtifact(tuple: FileTuple, index: number): RenderArtifact {
const [source, bytes, , bounds] = tuple;
const geometry = (bounds ?? undefined) as Bounds | undefined;
return {
page: index + 1,
source,
ext: extensionOf(source),
...(typeof bytes === 'number' ? { bytes } : {}),
...(typeof geometry?.w === 'number' ? { width: geometry.w } : {}),
...(typeof geometry?.h === 'number' ? { height: geometry.h } : {}),
};
}
/** Extension from a URL or path, ignoring query strings. Defaults to `.bin`. */
export function extensionOf(source: string): string {
let pathname = source;
try {
pathname = new URL(source).pathname;
} catch {
pathname = source.split('?')[0] ?? source;
}
const ext = path.extname(pathname).toLowerCase();
return ext || '.bin';
}
export function isHttpUrl(value: string): boolean {
return /^https?:\/\//i.test(value);
}