@useupup/core 3.3.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 +21 -0
- package/NOTICE +8 -0
- package/README.md +75 -0
- package/dist/browser-SXR426OQ.cjs +10 -0
- package/dist/browser-WD6T5ABJ.js +1 -0
- package/dist/chunk-2H5ICKFT.cjs +122 -0
- package/dist/chunk-6HF3I2JS.cjs +39 -0
- package/dist/chunk-6S5DQDQH.js +212 -0
- package/dist/chunk-BMQC27OE.cjs +92 -0
- package/dist/chunk-BSGQGINR.cjs +219 -0
- package/dist/chunk-CPKZ2CXD.cjs +179 -0
- package/dist/chunk-DTKDIMKT.cjs +573 -0
- package/dist/chunk-DYE5XJ6Z.cjs +2134 -0
- package/dist/chunk-E3ZCNOXG.cjs +74 -0
- package/dist/chunk-FEQY2WYY.cjs +2377 -0
- package/dist/chunk-FMURBCZ7.js +120 -0
- package/dist/chunk-FYQQRULL.js +37 -0
- package/dist/chunk-GE4LIVXP.js +133 -0
- package/dist/chunk-HFR5HZJZ.js +56 -0
- package/dist/chunk-I7ULPCA2.js +72 -0
- package/dist/chunk-MHYXDLZR.js +569 -0
- package/dist/chunk-N2SLHVAW.cjs +61 -0
- package/dist/chunk-OOPDEOTM.js +169 -0
- package/dist/chunk-PMBR3OUW.js +2359 -0
- package/dist/chunk-QK7HZIVY.cjs +144 -0
- package/dist/chunk-T2AQHY7V.cjs +59 -0
- package/dist/chunk-TRCP5FUR.js +90 -0
- package/dist/chunk-U7KL5M2H.cjs +374 -0
- package/dist/chunk-UXNPM54W.js +2108 -0
- package/dist/chunk-VV6QCQLL.js +365 -0
- package/dist/chunk-ZMS2HXLB.js +56 -0
- package/dist/context-shapes-123dd3b6.d.cts +207 -0
- package/dist/context-shapes-27b24c8f.d.ts +207 -0
- package/dist/contracts-pipeline-cf177270.d.cts +78 -0
- package/dist/contracts-pipeline-cf177270.d.ts +78 -0
- package/dist/contracts-strategies-378a16b7.d.cts +168 -0
- package/dist/contracts-strategies-378a16b7.d.ts +168 -0
- package/dist/contracts.cjs +181 -0
- package/dist/contracts.d.cts +11 -0
- package/dist/contracts.d.ts +11 -0
- package/dist/contracts.js +4 -0
- package/dist/core-141335ac.d.ts +651 -0
- package/dist/core-bea2ceca.d.cts +651 -0
- package/dist/create-worker-provider-AA5SRII6.js +58 -0
- package/dist/create-worker-provider-KJNUSSTW.cjs +60 -0
- package/dist/eligibility-ONLNJ6FB.cjs +10 -0
- package/dist/eligibility-ULFC3E4G.js +8 -0
- package/dist/error-code-map-9c44a425.d.ts +62 -0
- package/dist/error-code-map-dd5becfa.d.cts +62 -0
- package/dist/i18n/index.cjs +90 -0
- package/dist/i18n/index.d.cts +3 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +1 -0
- package/dist/index.cjs +1770 -0
- package/dist/index.d.cts +280 -0
- package/dist/index.d.ts +280 -0
- package/dist/index.js +1589 -0
- package/dist/internal.cjs +2407 -0
- package/dist/internal.d.cts +595 -0
- package/dist/internal.d.ts +595 -0
- package/dist/internal.js +2275 -0
- package/dist/pipeline/index.cjs +10 -0
- package/dist/pipeline/index.d.cts +11 -0
- package/dist/pipeline/index.d.ts +11 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline-worker.cjs +199 -0
- package/dist/pipeline-worker.d.cts +2 -0
- package/dist/pipeline-worker.d.ts +2 -0
- package/dist/pipeline-worker.js +197 -0
- package/dist/resolve-locale-81feef21.d.ts +20 -0
- package/dist/resolve-locale-9cb63600.d.cts +20 -0
- package/dist/resolve-theme-01758105.d.ts +53 -0
- package/dist/resolve-theme-66cf8d98.d.cts +53 -0
- package/dist/server-mode-drive-controller-b5f8ee12.d.cts +180 -0
- package/dist/server-mode-drive-controller-b5f8ee12.d.ts +180 -0
- package/dist/steps/compress.cjs +66 -0
- package/dist/steps/compress.d.cts +10 -0
- package/dist/steps/compress.d.ts +10 -0
- package/dist/steps/compress.js +64 -0
- package/dist/steps/exif.cjs +44 -0
- package/dist/steps/exif.d.cts +5 -0
- package/dist/steps/exif.d.ts +5 -0
- package/dist/steps/exif.js +42 -0
- package/dist/steps/hash.cjs +44 -0
- package/dist/steps/hash.d.cts +5 -0
- package/dist/steps/hash.d.ts +5 -0
- package/dist/steps/hash.js +42 -0
- package/dist/steps/heic.cjs +59 -0
- package/dist/steps/heic.d.cts +5 -0
- package/dist/steps/heic.d.ts +5 -0
- package/dist/steps/heic.js +57 -0
- package/dist/steps/thumbnail.cjs +54 -0
- package/dist/steps/thumbnail.d.cts +10 -0
- package/dist/steps/thumbnail.d.ts +10 -0
- package/dist/steps/thumbnail.js +52 -0
- package/dist/strategies/index.cjs +82 -0
- package/dist/strategies/index.d.cts +187 -0
- package/dist/strategies/index.d.ts +187 -0
- package/dist/strategies/index.js +60 -0
- package/dist/strategies/tus-upload.cjs +14 -0
- package/dist/strategies/tus-upload.d.cts +15 -0
- package/dist/strategies/tus-upload.d.ts +15 -0
- package/dist/strategies/tus-upload.js +5 -0
- package/dist/theme/index.cjs +38 -0
- package/dist/theme/index.d.cts +3 -0
- package/dist/theme/index.d.ts +3 -0
- package/dist/theme/index.js +1 -0
- package/dist/theme-store-2e56a674.d.cts +39 -0
- package/dist/theme-store-f13e0315.d.ts +39 -0
- package/dist/token-endpoint-5fdb4a35.d.ts +20 -0
- package/dist/token-endpoint-a7d15ab6.d.cts +20 -0
- package/dist/ui-translations-c5d00392.d.cts +455 -0
- package/dist/ui-translations-c5d00392.d.ts +455 -0
- package/dist/uploader-props-72242002.d.ts +340 -0
- package/dist/uploader-props-7f956e60.d.cts +340 -0
- package/dist/vars-d3421f6c.d.cts +240 -0
- package/dist/vars-d3421f6c.d.ts +240 -0
- package/package.json +182 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/steps/animated-image.ts
|
|
2
|
+
function ascii(bytes, start, length) {
|
|
3
|
+
let out = "";
|
|
4
|
+
for (let i = start; i < start + length; i += 1) {
|
|
5
|
+
const byte = bytes[i];
|
|
6
|
+
if (byte === void 0) return out;
|
|
7
|
+
out += String.fromCharCode(byte);
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
function byteAt(bytes, index) {
|
|
12
|
+
return bytes[index] ?? 0;
|
|
13
|
+
}
|
|
14
|
+
function skipSubBlocks(bytes, start) {
|
|
15
|
+
let offset = start;
|
|
16
|
+
while (offset < bytes.length) {
|
|
17
|
+
const size = byteAt(bytes, offset);
|
|
18
|
+
offset += 1;
|
|
19
|
+
if (size === 0) return offset;
|
|
20
|
+
offset += size;
|
|
21
|
+
}
|
|
22
|
+
return bytes.length;
|
|
23
|
+
}
|
|
24
|
+
function isAnimatedGif(bytes) {
|
|
25
|
+
if (bytes.length < 13) return false;
|
|
26
|
+
if (ascii(bytes, 0, 3) !== "GIF") return false;
|
|
27
|
+
const screenPacked = byteAt(bytes, 10);
|
|
28
|
+
let offset = 13;
|
|
29
|
+
if ((screenPacked & 128) !== 0) {
|
|
30
|
+
offset += 3 * (1 << (screenPacked & 7) + 1);
|
|
31
|
+
}
|
|
32
|
+
let frames = 0;
|
|
33
|
+
while (offset < bytes.length) {
|
|
34
|
+
const block = byteAt(bytes, offset);
|
|
35
|
+
if (block === 59) return false;
|
|
36
|
+
if (block === 33) {
|
|
37
|
+
const label = byteAt(bytes, offset + 1);
|
|
38
|
+
offset += 2;
|
|
39
|
+
if (label === 255) {
|
|
40
|
+
const identifier = ascii(
|
|
41
|
+
bytes,
|
|
42
|
+
offset + 1,
|
|
43
|
+
byteAt(bytes, offset)
|
|
44
|
+
);
|
|
45
|
+
if (identifier === "NETSCAPE2.0" || identifier === "ANIMEXTS1.0") {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
offset = skipSubBlocks(bytes, offset);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (block === 44) {
|
|
53
|
+
frames += 1;
|
|
54
|
+
if (frames > 1) return true;
|
|
55
|
+
const imagePacked = byteAt(bytes, offset + 9);
|
|
56
|
+
offset += 10;
|
|
57
|
+
if ((imagePacked & 128) !== 0) {
|
|
58
|
+
offset += 3 * (1 << (imagePacked & 7) + 1);
|
|
59
|
+
}
|
|
60
|
+
offset += 1;
|
|
61
|
+
offset = skipSubBlocks(bytes, offset);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
var PNG_SIGNATURE = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
69
|
+
function isAnimatedPng(bytes) {
|
|
70
|
+
if (bytes.length < PNG_SIGNATURE.length) return false;
|
|
71
|
+
for (const [index, expected] of PNG_SIGNATURE.entries()) {
|
|
72
|
+
if (byteAt(bytes, index) !== expected) return false;
|
|
73
|
+
}
|
|
74
|
+
let offset = PNG_SIGNATURE.length;
|
|
75
|
+
while (offset + 8 <= bytes.length) {
|
|
76
|
+
const length = byteAt(bytes, offset) * 16777216 + byteAt(bytes, offset + 1) * 65536 + byteAt(bytes, offset + 2) * 256 + byteAt(bytes, offset + 3);
|
|
77
|
+
const type = ascii(bytes, offset + 4, 4);
|
|
78
|
+
if (type === "acTL") return true;
|
|
79
|
+
if (type === "IDAT") return false;
|
|
80
|
+
offset += 12 + length;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
function isAnimatedWebp(bytes) {
|
|
85
|
+
if (bytes.length < 16) return false;
|
|
86
|
+
if (ascii(bytes, 0, 4) !== "RIFF" || ascii(bytes, 8, 4) !== "WEBP") {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
let offset = 12;
|
|
90
|
+
while (offset + 8 <= bytes.length) {
|
|
91
|
+
const fourCC = ascii(bytes, offset, 4);
|
|
92
|
+
const size = byteAt(bytes, offset + 4) + byteAt(bytes, offset + 5) * 256 + byteAt(bytes, offset + 6) * 65536 + byteAt(bytes, offset + 7) * 16777216;
|
|
93
|
+
if (fourCC === "ANIM" || fourCC === "ANMF") return true;
|
|
94
|
+
if (fourCC === "VP8X" && (byteAt(bytes, offset + 8) & 2) !== 0) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
offset += 8 + size + size % 2;
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
var SNIFFERS = {
|
|
102
|
+
"image/gif": isAnimatedGif,
|
|
103
|
+
"image/png": isAnimatedPng,
|
|
104
|
+
"image/apng": isAnimatedPng,
|
|
105
|
+
"image/webp": isAnimatedWebp
|
|
106
|
+
};
|
|
107
|
+
function baseMimeType(type) {
|
|
108
|
+
return type.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
109
|
+
}
|
|
110
|
+
async function isAnimatedImage(file) {
|
|
111
|
+
const sniff = SNIFFERS[baseMimeType(file.type)];
|
|
112
|
+
if (!sniff) return false;
|
|
113
|
+
try {
|
|
114
|
+
return sniff(new Uint8Array(await file.arrayBuffer()));
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export { isAnimatedImage };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/pipeline/engine.ts
|
|
2
|
+
var PipelineEngine = class {
|
|
3
|
+
constructor(steps) {
|
|
4
|
+
this.steps = steps;
|
|
5
|
+
}
|
|
6
|
+
get stepCount() {
|
|
7
|
+
return this.steps.length;
|
|
8
|
+
}
|
|
9
|
+
async process(file, context) {
|
|
10
|
+
context.emit("pipeline-start", {
|
|
11
|
+
fileId: file.id,
|
|
12
|
+
steps: this.steps.map((s) => s.name)
|
|
13
|
+
});
|
|
14
|
+
let current = file;
|
|
15
|
+
for (const step of this.steps) {
|
|
16
|
+
if (step.shouldProcess && !step.shouldProcess(current)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
current = await step.process(current, context);
|
|
20
|
+
context.emit("pipeline-step", {
|
|
21
|
+
fileId: file.id,
|
|
22
|
+
step: step.name
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
context.emit("pipeline-complete", { fileId: file.id });
|
|
26
|
+
return current;
|
|
27
|
+
}
|
|
28
|
+
async processAll(files, context) {
|
|
29
|
+
const results = [];
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
results.push(await this.process(file, context));
|
|
32
|
+
}
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { PipelineEngine };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var UpupErrorCode = /* @__PURE__ */ ((UpupErrorCode2) => {
|
|
3
|
+
UpupErrorCode2["AUTH_EXPIRED"] = "AUTH_EXPIRED";
|
|
4
|
+
UpupErrorCode2["AUTH_DENIED"] = "AUTH_DENIED";
|
|
5
|
+
UpupErrorCode2["AUTH_PROVIDER_ERROR"] = "AUTH_PROVIDER_ERROR";
|
|
6
|
+
UpupErrorCode2["FILE_TOO_LARGE"] = "FILE_TOO_LARGE";
|
|
7
|
+
UpupErrorCode2["FILE_TOO_SMALL"] = "FILE_TOO_SMALL";
|
|
8
|
+
UpupErrorCode2["TYPE_MISMATCH"] = "TYPE_MISMATCH";
|
|
9
|
+
UpupErrorCode2["LIMIT_EXCEEDED"] = "LIMIT_EXCEEDED";
|
|
10
|
+
UpupErrorCode2["TOTAL_SIZE_EXCEEDED"] = "TOTAL_SIZE_EXCEEDED";
|
|
11
|
+
UpupErrorCode2["DUPLICATE"] = "DUPLICATE";
|
|
12
|
+
UpupErrorCode2["MIN_FILES_NOT_MET"] = "MIN_FILES_NOT_MET";
|
|
13
|
+
UpupErrorCode2["UPLOAD_FAILED"] = "UPLOAD_FAILED";
|
|
14
|
+
UpupErrorCode2["UPLOAD_ABORTED"] = "UPLOAD_ABORTED";
|
|
15
|
+
UpupErrorCode2["PRESIGN_FAILED"] = "PRESIGN_FAILED";
|
|
16
|
+
UpupErrorCode2["CORS_ERROR"] = "CORS_ERROR";
|
|
17
|
+
UpupErrorCode2["PIPELINE_STEP_FAILED"] = "PIPELINE_STEP_FAILED";
|
|
18
|
+
UpupErrorCode2["HEIC_CONVERSION_FAILED"] = "HEIC_CONVERSION_FAILED";
|
|
19
|
+
UpupErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
20
|
+
UpupErrorCode2["TIMEOUT"] = "TIMEOUT";
|
|
21
|
+
UpupErrorCode2["STORAGE_ERROR"] = "STORAGE_ERROR";
|
|
22
|
+
UpupErrorCode2["QUOTA_EXCEEDED"] = "QUOTA_EXCEEDED";
|
|
23
|
+
UpupErrorCode2["NO_UPLOAD_TARGET"] = "NO_UPLOAD_TARGET";
|
|
24
|
+
UpupErrorCode2["BAD_REQUEST"] = "BAD_REQUEST";
|
|
25
|
+
UpupErrorCode2["AUTH_REQUIRED"] = "AUTH_REQUIRED";
|
|
26
|
+
UpupErrorCode2["NOT_FOUND"] = "NOT_FOUND";
|
|
27
|
+
return UpupErrorCode2;
|
|
28
|
+
})(UpupErrorCode || {});
|
|
29
|
+
var UpupError = class extends Error {
|
|
30
|
+
constructor(message, code, retryable = false) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "UpupError";
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.retryable = retryable;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var UpupAuthError = class extends UpupError {
|
|
38
|
+
constructor(message, provider) {
|
|
39
|
+
super(message, "AUTH_PROVIDER_ERROR" /* AUTH_PROVIDER_ERROR */, false);
|
|
40
|
+
this.name = "UpupAuthError";
|
|
41
|
+
this.provider = provider;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var UpupNetworkError = class extends UpupError {
|
|
45
|
+
constructor(message, status) {
|
|
46
|
+
super(message, "NETWORK_ERROR" /* NETWORK_ERROR */, true);
|
|
47
|
+
this.name = "UpupNetworkError";
|
|
48
|
+
if (status !== void 0) this.status = status;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var UpupValidationError = class extends UpupError {
|
|
52
|
+
constructor(message, reason, file) {
|
|
53
|
+
super(message, reason, false);
|
|
54
|
+
this.name = "UpupValidationError";
|
|
55
|
+
this.reason = reason;
|
|
56
|
+
this.file = file;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var UpupQuotaError = class extends UpupError {
|
|
60
|
+
constructor(message, limit, used) {
|
|
61
|
+
super(message, "QUOTA_EXCEEDED" /* QUOTA_EXCEEDED */, false);
|
|
62
|
+
this.name = "UpupQuotaError";
|
|
63
|
+
this.limit = limit;
|
|
64
|
+
this.used = used;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var UpupStorageError = class extends UpupError {
|
|
68
|
+
constructor(message, provider, operation) {
|
|
69
|
+
super(message, "STORAGE_ERROR" /* STORAGE_ERROR */, false);
|
|
70
|
+
this.name = "UpupStorageError";
|
|
71
|
+
this.provider = provider;
|
|
72
|
+
this.operation = operation;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var UpupConfigError = class extends UpupError {
|
|
76
|
+
constructor(message, code = "NO_UPLOAD_TARGET" /* NO_UPLOAD_TARGET */) {
|
|
77
|
+
super(message, code, false);
|
|
78
|
+
this.name = "UpupConfigError";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var MAX_ERROR_BODY_SNIPPET = 200;
|
|
82
|
+
function parseErrorBody(body) {
|
|
83
|
+
if (!body) return { message: "" };
|
|
84
|
+
try {
|
|
85
|
+
const parsed = JSON.parse(body);
|
|
86
|
+
if (parsed && typeof parsed === "object") {
|
|
87
|
+
const {
|
|
88
|
+
code,
|
|
89
|
+
error,
|
|
90
|
+
message: msg
|
|
91
|
+
} = parsed;
|
|
92
|
+
const message = typeof error === "string" ? error : msg;
|
|
93
|
+
if (typeof message === "string" || typeof code === "string") {
|
|
94
|
+
return {
|
|
95
|
+
message: message ?? "",
|
|
96
|
+
...code !== void 0 ? { code } : {}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
const xmlCode = /<Code>([^<]*)<\/Code>/.exec(body)?.[1];
|
|
103
|
+
const xmlMessage = /<Message>([^<]*)<\/Message>/.exec(body)?.[1];
|
|
104
|
+
if (xmlCode || xmlMessage) {
|
|
105
|
+
return {
|
|
106
|
+
message: xmlMessage ?? "",
|
|
107
|
+
...xmlCode !== void 0 ? { code: xmlCode } : {}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return { message: body.slice(0, MAX_ERROR_BODY_SNIPPET) };
|
|
111
|
+
}
|
|
112
|
+
function uploadErrorFromResponse(args) {
|
|
113
|
+
const { status, statusText, body, kind, operation, provider } = args;
|
|
114
|
+
const parsed = parseErrorBody(body);
|
|
115
|
+
const message = parsed.message || `${status} ${statusText}`.trim();
|
|
116
|
+
let err;
|
|
117
|
+
if (kind === "auth") {
|
|
118
|
+
err = new UpupAuthError(message, provider ?? "server");
|
|
119
|
+
} else if (kind === "storage") {
|
|
120
|
+
err = new UpupStorageError(
|
|
121
|
+
message,
|
|
122
|
+
provider ?? "server",
|
|
123
|
+
operation ?? "upload"
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
err = new UpupNetworkError(message, status);
|
|
127
|
+
}
|
|
128
|
+
if (parsed.code) err.code = parsed.code;
|
|
129
|
+
err.status = status;
|
|
130
|
+
return err;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export { UpupAuthError, UpupConfigError, UpupError, UpupErrorCode, UpupNetworkError, UpupQuotaError, UpupStorageError, UpupValidationError, parseErrorBody, uploadErrorFromResponse };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/runtime/browser.ts
|
|
2
|
+
var BrowserRuntime = {
|
|
3
|
+
async computeHash(data) {
|
|
4
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
5
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
6
|
+
},
|
|
7
|
+
createImageBitmap: typeof createImageBitmap !== "undefined" ? (blob) => createImageBitmap(blob) : void 0,
|
|
8
|
+
createWorker: typeof Worker !== "undefined" ? () => {
|
|
9
|
+
try {
|
|
10
|
+
return new Worker(
|
|
11
|
+
new URL("./pipeline-worker.js", import.meta.url),
|
|
12
|
+
{
|
|
13
|
+
type: "module"
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
} : void 0,
|
|
20
|
+
async upload(url, body, options) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const xhr = new XMLHttpRequest();
|
|
23
|
+
xhr.upload.addEventListener("progress", (e) => {
|
|
24
|
+
if (e.lengthComputable) options.onProgress(e.loaded, e.total);
|
|
25
|
+
});
|
|
26
|
+
xhr.addEventListener("load", () => {
|
|
27
|
+
const headers = {};
|
|
28
|
+
xhr.getAllResponseHeaders().split("\r\n").forEach((line) => {
|
|
29
|
+
const [key, ...vals] = line.split(": ");
|
|
30
|
+
if (key) headers[key.toLowerCase()] = vals.join(": ");
|
|
31
|
+
});
|
|
32
|
+
resolve({ status: xhr.status, headers, body: xhr.responseText });
|
|
33
|
+
});
|
|
34
|
+
xhr.addEventListener("error", () => {
|
|
35
|
+
reject(new Error("Network error"));
|
|
36
|
+
});
|
|
37
|
+
options.signal.addEventListener("abort", () => {
|
|
38
|
+
xhr.abort();
|
|
39
|
+
});
|
|
40
|
+
xhr.open(options.method, url);
|
|
41
|
+
for (const [k, v] of Object.entries(options.headers)) {
|
|
42
|
+
xhr.setRequestHeader(k, v);
|
|
43
|
+
}
|
|
44
|
+
xhr.send(body);
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
async readAsArrayBuffer(file) {
|
|
48
|
+
return file.arrayBuffer();
|
|
49
|
+
},
|
|
50
|
+
createObjectURL: typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? (blob) => URL.createObjectURL(blob) : void 0,
|
|
51
|
+
revokeObjectURL: typeof URL !== "undefined" && typeof URL.revokeObjectURL === "function" ? (url) => {
|
|
52
|
+
URL.revokeObjectURL(url);
|
|
53
|
+
} : void 0
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export { BrowserRuntime };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createCanvas, canvasToBlob } from './chunk-6S5DQDQH.js';
|
|
2
|
+
import { UpupError } from './chunk-GE4LIVXP.js';
|
|
3
|
+
|
|
4
|
+
// src/steps/heic-decode.ts
|
|
5
|
+
var libheifPromise = null;
|
|
6
|
+
function loadLibheif() {
|
|
7
|
+
if (!libheifPromise) {
|
|
8
|
+
libheifPromise = (async () => {
|
|
9
|
+
let mod;
|
|
10
|
+
try {
|
|
11
|
+
mod = await import('libheif-js/libheif-wasm/libheif-bundle.mjs');
|
|
12
|
+
} catch {
|
|
13
|
+
libheifPromise = null;
|
|
14
|
+
throw new UpupError(
|
|
15
|
+
'HEIC support requires the optional dependency "libheif-js". Install it: npm i libheif-js',
|
|
16
|
+
"HEIC_CONVERSION_FAILED" /* HEIC_CONVERSION_FAILED */
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const factory = mod.default ?? mod;
|
|
20
|
+
return factory();
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
return libheifPromise;
|
|
24
|
+
}
|
|
25
|
+
function canvasBackendAvailable() {
|
|
26
|
+
return typeof OffscreenCanvas !== "undefined" || typeof document !== "undefined" && "createElement" in document;
|
|
27
|
+
}
|
|
28
|
+
async function heicToJpegBlob(input, options = {}) {
|
|
29
|
+
if (!canvasBackendAvailable()) return null;
|
|
30
|
+
const quality = typeof options.quality === "number" ? options.quality : 0.92;
|
|
31
|
+
const libheif = await loadLibheif();
|
|
32
|
+
const buffer = await input.arrayBuffer();
|
|
33
|
+
const decoder = new libheif.HeifDecoder();
|
|
34
|
+
let images = [];
|
|
35
|
+
try {
|
|
36
|
+
images = decoder.decode(new Uint8Array(buffer));
|
|
37
|
+
const image = images[0];
|
|
38
|
+
if (!image)
|
|
39
|
+
throw new UpupError(
|
|
40
|
+
"HEIC: no image found in file",
|
|
41
|
+
"HEIC_CONVERSION_FAILED" /* HEIC_CONVERSION_FAILED */
|
|
42
|
+
);
|
|
43
|
+
const width = image.get_width();
|
|
44
|
+
const height = image.get_height();
|
|
45
|
+
const canvas = createCanvas(width, height);
|
|
46
|
+
const ctx = canvas?.getContext("2d");
|
|
47
|
+
if (!canvas || !ctx) return null;
|
|
48
|
+
const imageData = ctx.createImageData(width, height);
|
|
49
|
+
await new Promise((resolve, reject) => {
|
|
50
|
+
image.display(imageData, (result) => {
|
|
51
|
+
if (result) resolve();
|
|
52
|
+
else reject(new Error("HEIC: display failed"));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
ctx.putImageData(imageData, 0, 0);
|
|
56
|
+
return await canvasToBlob(canvas, "image/jpeg", quality);
|
|
57
|
+
} finally {
|
|
58
|
+
for (const img of images) {
|
|
59
|
+
try {
|
|
60
|
+
img.free();
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
libheif.heif_context_free(decoder.decoder);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
decoder.decoder = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { heicToJpegBlob };
|