@holo-js/media 0.3.26 → 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 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, mediaRuntimeInternals, normalizeCollectionDefinitions, normalizeConversionDefinitions, normalizeMediaDefinition, registerMediaDefinition, requireMediaDefinition, requireMediaDefinitionForMorphClass, resetMediaPathGenerator, resetMediaRuntime, resolveMediaCollection, resolveMediaConversion, resolveMediaDefinition, runMediaGenerateConversionsJob, setMediaConversionExecutor, setMediaPathGenerator };
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 (!isImageSource(source.mimeType, source.extension)) {
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: inferMimeType(fileName)
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 = inferMimeType(generatedFileName, generated.mimeType);
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.26",
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.26",
32
- "@holo-js/db": "^0.3.26",
33
- "@holo-js/queue": "^0.3.26",
34
- "@holo-js/storage": "^0.3.26",
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.26",
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"