-
Notifications
You must be signed in to change notification settings - Fork 199
Expand file tree
/
Copy pathinputFile.ts.twig
More file actions
144 lines (122 loc) · 4.32 KB
/
inputFile.ts.twig
File metadata and controls
144 lines (122 loc) · 4.32 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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { File } from "node-fetch-native-with-agent";
type FsPromises = {
stat: (path: string) => Promise<{ size: number }>;
open: (path: string, flags: string) => Promise<{
read: (buffer: Uint8Array, offset: number, length: number, position: number) => Promise<{ bytesRead: number }>;
close: () => Promise<void>;
}>;
readFile: (path: string) => Promise<Uint8Array>;
};
function isEdgeRuntime(): boolean {
return typeof globalThis !== 'undefined' && typeof (globalThis as { EdgeRuntime?: unknown }).EdgeRuntime !== 'undefined';
}
function assertFileSystemAvailable(): void {
if (isEdgeRuntime()) {
throw new Error('File system operations are not supported in edge runtimes. Please use InputFile.fromBuffer instead.');
}
}
async function getFs(): Promise<FsPromises> {
assertFileSystemAvailable();
try {
const fs = await import('fs');
return fs.promises;
} catch {
throw new Error('File system operations are not available in this runtime. Please use InputFile.fromBuffer instead.');
}
}
function getFilename(path: string): string {
const segments = path.replace(/\\/g, '/').split('/').filter(Boolean);
return segments.pop() ?? 'file';
}
type BlobLike = {
size: number;
slice: (start: number, end: number) => BlobLike;
arrayBuffer: () => Promise<ArrayBuffer>;
};
type InputFileSource =
| { type: 'path'; path: string }
| { type: 'buffer'; data: Uint8Array }
| { type: 'blob'; data: BlobLike };
export class InputFile {
private source: InputFileSource;
filename: string;
private constructor(source: InputFileSource, filename: string) {
this.source = source;
this.filename = filename;
}
static fromBuffer(parts: BlobLike | Uint8Array | ArrayBuffer | string, name: string): InputFile {
if (parts && !ArrayBuffer.isView(parts) && typeof (parts as BlobLike).arrayBuffer === 'function') {
return new InputFile({ type: 'blob', data: parts as BlobLike }, name);
}
if (typeof parts === 'string') {
return new InputFile({ type: 'buffer', data: new TextEncoder().encode(parts) }, name);
}
if (parts instanceof ArrayBuffer) {
return new InputFile({ type: 'buffer', data: new Uint8Array(parts) }, name);
}
if (ArrayBuffer.isView(parts)) {
return new InputFile({
type: 'buffer',
data: new Uint8Array(parts.buffer, parts.byteOffset, parts.byteLength),
}, name);
}
throw new Error('Unsupported input type for InputFile.fromBuffer');
}
static fromPath(path: string, name?: string): InputFile {
assertFileSystemAvailable();
return new InputFile({ type: 'path', path }, name ?? getFilename(path));
}
static fromPlainText(content: string, name: string): InputFile {
return new InputFile({ type: 'buffer', data: new TextEncoder().encode(content) }, name);
}
async size(): Promise<number> {
switch (this.source.type) {
case 'path': {
const fs = await getFs();
return (await fs.stat(this.source.path)).size;
}
case 'buffer':
return this.source.data.length;
case 'blob':
return this.source.data.size;
}
}
async slice(start: number, end: number): Promise<Uint8Array> {
const length = end - start;
switch (this.source.type) {
case 'path': {
const fs = await getFs();
const handle = await fs.open(this.source.path, 'r');
try {
const buffer = new Uint8Array(length);
const result = await handle.read(buffer, 0, length, start);
return result.bytesRead === buffer.length ? buffer : buffer.subarray(0, result.bytesRead);
} finally {
await handle.close();
}
}
case 'buffer':
return this.source.data.subarray(start, end);
case 'blob': {
const arrayBuffer = await this.source.data.slice(start, end).arrayBuffer();
return new Uint8Array(arrayBuffer);
}
}
}
async toFile(): Promise<File> {
const data = await this.toUint8Array();
return new File([data], this.filename);
}
private async toUint8Array(): Promise<Uint8Array> {
switch (this.source.type) {
case 'path': {
const fs = await getFs();
return await fs.readFile(this.source.path);
}
case 'buffer':
return this.source.data;
case 'blob':
return new Uint8Array(await this.source.data.arrayBuffer());
}
}
}