@holo-js/media 0.3.27 → 0.3.28
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/index.d.ts +1 -12
- package/dist/index.mjs +13 -75
- package/package.json +8 -6
package/dist/index.d.ts
CHANGED
|
@@ -216,12 +216,10 @@ type MediaSourceInput = string | BinaryContent | {
|
|
|
216
216
|
} | {
|
|
217
217
|
readonly url: string;
|
|
218
218
|
readonly fileName?: string;
|
|
219
|
-
readonly mimeType?: string;
|
|
220
219
|
readonly name?: string;
|
|
221
220
|
} | {
|
|
222
221
|
readonly contents: BinaryContent;
|
|
223
222
|
readonly fileName?: string;
|
|
224
|
-
readonly mimeType?: string;
|
|
225
223
|
readonly name?: string;
|
|
226
224
|
};
|
|
227
225
|
type MediaAddErrorCode = 'max_size_exceeded' | 'invalid_mime_type' | 'invalid_extension';
|
|
@@ -367,14 +365,5 @@ declare function dispatchQueuedMediaConversions(payload: MediaGenerateConversion
|
|
|
367
365
|
|
|
368
366
|
declare function createDefaultMediaConversionExecutor(): MediaConversionExecutor;
|
|
369
367
|
declare const defaultMediaConversionExecutor: MediaConversionExecutor;
|
|
370
|
-
interface MediaImageInspection {
|
|
371
|
-
readonly mimeType: 'image/gif' | 'image/jpeg' | 'image/png' | 'image/webp';
|
|
372
|
-
readonly width: number;
|
|
373
|
-
readonly height: number;
|
|
374
|
-
readonly pages: number;
|
|
375
|
-
}
|
|
376
|
-
declare const mediaRuntimeInternals: Readonly<{
|
|
377
|
-
inspectImage(contents: Uint8Array, maximumPixels?: number): Promise<MediaImageInspection>;
|
|
378
|
-
}>;
|
|
379
368
|
|
|
380
|
-
export { type CollectionNamesOf, type ConversionNamesOf, MEDIA_GENERATE_CONVERSIONS_JOB, Media, type MediaAddResult, MediaAdder, type MediaCollectionBuilder, type MediaCollectionDefinition, type MediaConversionBuilder, type MediaConversionDefinition, type MediaConversionExecutor, type MediaConversionExecutorInput, type MediaConversionExecutorResult, type MediaConversionExecutorSource, type MediaConversionFit, type MediaConversionFormat, type MediaConversionPathInput, type MediaDefinitionFactory, type MediaDefinitionHelpers, type MediaDefinitionInput, type MediaEnabledEntity, type MediaEnabledModel, type MediaGenerateConversionsPayload, type MediaGenerateConversionsResult, MediaItem, type MediaOriginalPathInput, type MediaPathGenerator, type MediaSourceInput, type NormalizedMediaCollectionDefinition, type NormalizedMediaConversionDefinition, type NormalizedMediaDefinition, type StoredMediaConversion, collection, conversion, createDefaultMediaConversionExecutor, defaultMediaConversionExecutor, defineMediaModel, dispatchQueuedMediaConversions, ensureMediaQueueJobRegistered, getMediaConversionExecutor, getMediaDefinition, getMediaDefinitionForMorphClass, getMediaPathGenerator,
|
|
369
|
+
export { type CollectionNamesOf, type ConversionNamesOf, MEDIA_GENERATE_CONVERSIONS_JOB, Media, type MediaAddResult, MediaAdder, type MediaCollectionBuilder, type MediaCollectionDefinition, type MediaConversionBuilder, type MediaConversionDefinition, type MediaConversionExecutor, type MediaConversionExecutorInput, type MediaConversionExecutorResult, type MediaConversionExecutorSource, type MediaConversionFit, type MediaConversionFormat, type MediaConversionPathInput, type MediaDefinitionFactory, type MediaDefinitionHelpers, type MediaDefinitionInput, type MediaEnabledEntity, type MediaEnabledModel, type MediaGenerateConversionsPayload, type MediaGenerateConversionsResult, MediaItem, type MediaOriginalPathInput, type MediaPathGenerator, type MediaSourceInput, type NormalizedMediaCollectionDefinition, type NormalizedMediaConversionDefinition, type NormalizedMediaDefinition, type StoredMediaConversion, collection, conversion, createDefaultMediaConversionExecutor, defaultMediaConversionExecutor, defineMediaModel, dispatchQueuedMediaConversions, ensureMediaQueueJobRegistered, getMediaConversionExecutor, getMediaDefinition, getMediaDefinitionForMorphClass, getMediaPathGenerator, normalizeCollectionDefinitions, normalizeConversionDefinitions, normalizeMediaDefinition, registerMediaDefinition, requireMediaDefinition, requireMediaDefinitionForMorphClass, resetMediaPathGenerator, resetMediaRuntime, resolveMediaCollection, resolveMediaConversion, resolveMediaDefinition, runMediaGenerateConversionsJob, setMediaConversionExecutor, setMediaPathGenerator };
|
package/dist/index.mjs
CHANGED
|
@@ -9,33 +9,11 @@ import { Entity as Entity2 } from "@holo-js/db";
|
|
|
9
9
|
// src/registry.ts
|
|
10
10
|
import { Entity } from "@holo-js/db";
|
|
11
11
|
|
|
12
|
+
// src/runtime/image.ts
|
|
13
|
+
import { mimeTypeFromFileName } from "@holo-js/kernel/file-content";
|
|
14
|
+
|
|
12
15
|
// src/runtime/binary.ts
|
|
13
16
|
import { basename, extname } from "path";
|
|
14
|
-
var MIME_TYPES = Object.freeze({
|
|
15
|
-
avif: "image/avif",
|
|
16
|
-
gif: "image/gif",
|
|
17
|
-
jpeg: "image/jpeg",
|
|
18
|
-
jpg: "image/jpeg",
|
|
19
|
-
json: "application/json",
|
|
20
|
-
pdf: "application/pdf",
|
|
21
|
-
png: "image/png",
|
|
22
|
-
svg: "image/svg+xml",
|
|
23
|
-
txt: "text/plain",
|
|
24
|
-
webp: "image/webp"
|
|
25
|
-
});
|
|
26
|
-
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
27
|
-
"avif",
|
|
28
|
-
"gif",
|
|
29
|
-
"heic",
|
|
30
|
-
"heif",
|
|
31
|
-
"jpeg",
|
|
32
|
-
"jpg",
|
|
33
|
-
"png",
|
|
34
|
-
"svg",
|
|
35
|
-
"tif",
|
|
36
|
-
"tiff",
|
|
37
|
-
"webp"
|
|
38
|
-
]);
|
|
39
17
|
var SHARP_OUTPUT_FORMATS = /* @__PURE__ */ new Set([
|
|
40
18
|
"avif",
|
|
41
19
|
"jpeg",
|
|
@@ -47,13 +25,6 @@ function sanitizeFileName(value) {
|
|
|
47
25
|
const sanitized = value.trim().replace(/\\/g, "/").split("/").filter(Boolean).join("-");
|
|
48
26
|
return sanitized || "media.bin";
|
|
49
27
|
}
|
|
50
|
-
function inferMimeType(fileName, explicit) {
|
|
51
|
-
if (explicit?.trim()) {
|
|
52
|
-
return explicit.trim();
|
|
53
|
-
}
|
|
54
|
-
const extension = extname(fileName).replace(/^\./, "").toLowerCase();
|
|
55
|
-
return MIME_TYPES[extension];
|
|
56
|
-
}
|
|
57
28
|
function getExtension(fileName) {
|
|
58
29
|
const extension = extname(fileName).replace(/^\./, "").toLowerCase();
|
|
59
30
|
return extension || void 0;
|
|
@@ -86,12 +57,6 @@ function getContentSize(value) {
|
|
|
86
57
|
}
|
|
87
58
|
return value.size;
|
|
88
59
|
}
|
|
89
|
-
function isImageSource(mimeType, extension) {
|
|
90
|
-
if (mimeType?.trim().toLowerCase().startsWith("image/")) {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
return extension ? IMAGE_EXTENSIONS.has(extension.trim().toLowerCase()) : false;
|
|
94
|
-
}
|
|
95
60
|
function normalizeOutputFormat(extension, conversionFormat) {
|
|
96
61
|
if (conversionFormat) {
|
|
97
62
|
return conversionFormat;
|
|
@@ -131,7 +96,7 @@ function resolveFit(mode) {
|
|
|
131
96
|
function createDefaultMediaConversionExecutor() {
|
|
132
97
|
return {
|
|
133
98
|
async generate({ source, conversion: conversion2 }) {
|
|
134
|
-
if (!
|
|
99
|
+
if (!source.mimeType?.toLowerCase().startsWith("image/")) {
|
|
135
100
|
return null;
|
|
136
101
|
}
|
|
137
102
|
const outputFormat = normalizeOutputFormat(source.extension, conversion2.format);
|
|
@@ -181,7 +146,7 @@ function createDefaultMediaConversionExecutor() {
|
|
|
181
146
|
return {
|
|
182
147
|
contents,
|
|
183
148
|
fileName,
|
|
184
|
-
mimeType:
|
|
149
|
+
mimeType: mimeTypeFromFileName(fileName) ?? void 0
|
|
185
150
|
};
|
|
186
151
|
} catch (error) {
|
|
187
152
|
throw new Error(
|
|
@@ -192,30 +157,6 @@ function createDefaultMediaConversionExecutor() {
|
|
|
192
157
|
};
|
|
193
158
|
}
|
|
194
159
|
var defaultMediaConversionExecutor = createDefaultMediaConversionExecutor();
|
|
195
|
-
var imageMimeTypes = {
|
|
196
|
-
gif: "image/gif",
|
|
197
|
-
jpeg: "image/jpeg",
|
|
198
|
-
png: "image/png",
|
|
199
|
-
webp: "image/webp"
|
|
200
|
-
};
|
|
201
|
-
var mediaRuntimeInternals = Object.freeze({
|
|
202
|
-
async inspectImage(contents, maximumPixels = 4e7) {
|
|
203
|
-
if (!Number.isSafeInteger(maximumPixels) || maximumPixels < 1) throw new TypeError("[Holo Media] Image pixel limits require a positive integer.");
|
|
204
|
-
const factory = await loadSharp();
|
|
205
|
-
try {
|
|
206
|
-
const image = factory(Buffer.from(contents), { animated: true, failOn: "warning", limitInputPixels: maximumPixels });
|
|
207
|
-
const metadata = await image.metadata();
|
|
208
|
-
const { width, height } = metadata;
|
|
209
|
-
const format = metadata.format;
|
|
210
|
-
if (format !== "gif" && format !== "jpeg" && format !== "png" && format !== "webp") throw new Error("[Holo Media] Unsupported raster image format.");
|
|
211
|
-
if (!width || !height || !Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width * height > maximumPixels) throw new Error("[Holo Media] Image exceeds the pixel limit.");
|
|
212
|
-
await image.stats();
|
|
213
|
-
return { mimeType: imageMimeTypes[format], width, height: metadata.pageHeight ?? height, pages: metadata.pages ?? 1 };
|
|
214
|
-
} catch (error) {
|
|
215
|
-
throw new Error("[Holo Media] Image could not be decoded.", { cause: error });
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
160
|
|
|
220
161
|
// src/registry.ts
|
|
221
162
|
var mediaRegistrySymbol = /* @__PURE__ */ Symbol.for("@holo-js/media.registry");
|
|
@@ -405,6 +346,7 @@ import { BlockList, isIP } from "net";
|
|
|
405
346
|
import { tmpdir } from "os";
|
|
406
347
|
import { basename as basename2, resolve, sep } from "path";
|
|
407
348
|
import { Readable } from "stream";
|
|
349
|
+
import { inspectFileContent } from "@holo-js/kernel/file-content";
|
|
408
350
|
import { Storage as Storage4 } from "@holo-js/storage/runtime";
|
|
409
351
|
|
|
410
352
|
// src/queue.ts
|
|
@@ -417,6 +359,7 @@ import {
|
|
|
417
359
|
import { connectionAsyncContext } from "@holo-js/db";
|
|
418
360
|
|
|
419
361
|
// src/model/conversions.ts
|
|
362
|
+
import { mimeTypeFromFileName as mimeTypeFromFileName2 } from "@holo-js/kernel/file-content";
|
|
420
363
|
import { Storage as Storage2 } from "@holo-js/storage/runtime";
|
|
421
364
|
|
|
422
365
|
// src/model/mutation.ts
|
|
@@ -620,7 +563,7 @@ async function generateStoredConversions(options) {
|
|
|
620
563
|
generatedFileName
|
|
621
564
|
});
|
|
622
565
|
const targetDisk = generated.disk ?? options.conversionsDisk;
|
|
623
|
-
const conversionMimeType =
|
|
566
|
+
const conversionMimeType = generated.mimeType?.trim() || mimeTypeFromFileName2(generatedFileName) || void 0;
|
|
624
567
|
const conversionContents = await toBinaryContent(generated.contents);
|
|
625
568
|
await options.mutation.put(targetDisk, conversionPath, conversionContents);
|
|
626
569
|
generatedConversions[conversion2.name] = Object.freeze({
|
|
@@ -1044,6 +987,11 @@ async function readRemoteMediaContents(response, fileName, maxSize, collectionNa
|
|
|
1044
987
|
return contents;
|
|
1045
988
|
}
|
|
1046
989
|
async function resolveMediaSource(input, overrideFileName, overrideName, options) {
|
|
990
|
+
const source = await readMediaSource(input, overrideFileName, overrideName, options);
|
|
991
|
+
const { mimeType } = await inspectFileContent(source.contents, source.fileName);
|
|
992
|
+
return mimeType ? { ...source, mimeType } : source;
|
|
993
|
+
}
|
|
994
|
+
async function readMediaSource(input, overrideFileName, overrideName, options) {
|
|
1047
995
|
if (typeof input === "string" && /^https?:\/\//i.test(input)) {
|
|
1048
996
|
return resolveRemoteMediaSource(
|
|
1049
997
|
{ url: input },
|
|
@@ -1069,7 +1017,6 @@ async function resolveMediaSource(input, overrideFileName, overrideName, options
|
|
|
1069
1017
|
return {
|
|
1070
1018
|
contents: contents2,
|
|
1071
1019
|
fileName: fileName2,
|
|
1072
|
-
mimeType: inferMimeType(fileName2),
|
|
1073
1020
|
extension: getExtension(fileName2),
|
|
1074
1021
|
size: contents2.byteLength,
|
|
1075
1022
|
name: getDisplayName(fileName2, overrideName)
|
|
@@ -1081,7 +1028,6 @@ async function resolveMediaSource(input, overrideFileName, overrideName, options
|
|
|
1081
1028
|
return {
|
|
1082
1029
|
contents: contents2,
|
|
1083
1030
|
fileName: fileName2,
|
|
1084
|
-
mimeType: inferMimeType(fileName2, input.mimeType),
|
|
1085
1031
|
extension: getExtension(fileName2),
|
|
1086
1032
|
size: getContentSize(contents2),
|
|
1087
1033
|
name: getDisplayName(fileName2, overrideName ?? input.name)
|
|
@@ -1092,7 +1038,6 @@ async function resolveMediaSource(input, overrideFileName, overrideName, options
|
|
|
1092
1038
|
return {
|
|
1093
1039
|
contents,
|
|
1094
1040
|
fileName,
|
|
1095
|
-
mimeType: inferMimeType(fileName, getBinaryMimeType(input)),
|
|
1096
1041
|
extension: getExtension(fileName),
|
|
1097
1042
|
size: getContentSize(contents),
|
|
1098
1043
|
name: getDisplayName(fileName, overrideName)
|
|
@@ -1102,10 +1047,6 @@ function getBinaryFileName(input) {
|
|
|
1102
1047
|
const name = input.name;
|
|
1103
1048
|
return typeof name === "string" && name.trim() ? name : "media.bin";
|
|
1104
1049
|
}
|
|
1105
|
-
function getBinaryMimeType(input) {
|
|
1106
|
-
const type = input.type;
|
|
1107
|
-
return typeof type === "string" && type.trim() ? type : void 0;
|
|
1108
|
-
}
|
|
1109
1050
|
function isPathInput(input) {
|
|
1110
1051
|
return typeof input === "object" && input !== null && "path" in input;
|
|
1111
1052
|
}
|
|
@@ -1126,7 +1067,6 @@ async function resolveRemoteMediaSource(input, overrideFileName, overrideName, m
|
|
|
1126
1067
|
const fileName = sanitizeFileName(
|
|
1127
1068
|
overrideFileName ?? input.fileName ?? parseRemoteFileName(remoteUrl.toString())
|
|
1128
1069
|
);
|
|
1129
|
-
const responseMimeType = response.headers.get("content-type")?.split(";")[0];
|
|
1130
1070
|
const contentLengthHeader = response.headers.get("content-length");
|
|
1131
1071
|
const contentLength = contentLengthHeader?.trim() ? Number(contentLengthHeader) : Number.NaN;
|
|
1132
1072
|
const resolvedMaxSize = typeof maxSize === "number" ? maxSize : DEFAULT_REMOTE_MEDIA_MAX_SIZE;
|
|
@@ -1142,7 +1082,6 @@ async function resolveRemoteMediaSource(input, overrideFileName, overrideName, m
|
|
|
1142
1082
|
return {
|
|
1143
1083
|
contents,
|
|
1144
1084
|
fileName,
|
|
1145
|
-
mimeType: inferMimeType(fileName, input.mimeType ?? responseMimeType ?? void 0),
|
|
1146
1085
|
extension: getExtension(fileName),
|
|
1147
1086
|
size: contents.byteLength,
|
|
1148
1087
|
name: getDisplayName(fileName, overrideName ?? input.name)
|
|
@@ -1800,7 +1739,6 @@ export {
|
|
|
1800
1739
|
getMediaDefinition,
|
|
1801
1740
|
getMediaDefinitionForMorphClass,
|
|
1802
1741
|
getMediaPathGenerator,
|
|
1803
|
-
mediaRuntimeInternals,
|
|
1804
1742
|
normalizeCollectionDefinitions,
|
|
1805
1743
|
normalizeConversionDefinitions,
|
|
1806
1744
|
normalizeMediaConfig,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/media",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.28",
|
|
4
4
|
"description": "Holo-JS Framework - media collections and conversions for Holo-JS models",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,14 +28,16 @@
|
|
|
28
28
|
"test": "vitest --run"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@holo-js/config": "^0.3.
|
|
32
|
-
"@holo-js/db": "^0.3.
|
|
33
|
-
"@holo-js/
|
|
34
|
-
"@holo-js/
|
|
31
|
+
"@holo-js/config": "^0.3.28",
|
|
32
|
+
"@holo-js/db": "^0.3.28",
|
|
33
|
+
"@holo-js/kernel": "^0.3.28",
|
|
34
|
+
"@holo-js/queue": "^0.3.28",
|
|
35
|
+
"@holo-js/storage": "^0.3.28",
|
|
36
|
+
"file-type": "^22.2.0",
|
|
35
37
|
"sharp": "^0.35.4"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|
|
38
|
-
"@holo-js/db-sqlite": "^0.3.
|
|
40
|
+
"@holo-js/db-sqlite": "^0.3.28",
|
|
39
41
|
"tsup": "^8.3.5",
|
|
40
42
|
"typescript": "^5.7.2",
|
|
41
43
|
"vitest": "^4.1.5"
|