@lamberl-lee/file-preview 0.2.0 → 0.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.
Files changed (48) hide show
  1. package/README.md +6 -0
  2. package/dist/LargeFileGate.d.ts +23 -3
  3. package/dist/LargeFileGate.js +44 -40
  4. package/dist/LargeFileGate.js.map +1 -1
  5. package/dist/PluginPreviewRenderer.d.ts +20 -1
  6. package/dist/PluginPreviewRenderer.js +51 -10
  7. package/dist/PluginPreviewRenderer.js.map +1 -1
  8. package/dist/PreviewErrorBoundary.d.ts +2 -0
  9. package/dist/PreviewErrorBoundary.js +11 -2
  10. package/dist/PreviewErrorBoundary.js.map +1 -1
  11. package/dist/core/detect-meta.d.ts +73 -0
  12. package/dist/core/detect-meta.js +81 -0
  13. package/dist/core/detect-meta.js.map +1 -0
  14. package/dist/core/magic-bytes.d.ts +56 -0
  15. package/dist/core/magic-bytes.js +97 -0
  16. package/dist/core/magic-bytes.js.map +1 -0
  17. package/dist/core/plugin.d.ts +2 -2
  18. package/dist/core/plugin.js +5 -3
  19. package/dist/core/plugin.js.map +1 -1
  20. package/dist/core/preview-error.d.ts +35 -0
  21. package/dist/core/preview-error.js +39 -0
  22. package/dist/core/preview-error.js.map +1 -0
  23. package/dist/core/registry.d.ts +1 -0
  24. package/dist/index.d.ts +4 -1
  25. package/dist/index.js +21 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/plugins/audio-plugin.d.ts +1 -0
  28. package/dist/plugins/builtin-plugins.d.ts +1 -0
  29. package/dist/plugins/csv-plugin.d.ts +1 -0
  30. package/dist/plugins/docx-plugin.d.ts +1 -0
  31. package/dist/plugins/epub-plugin.d.ts +1 -0
  32. package/dist/plugins/html-plugin.d.ts +1 -0
  33. package/dist/plugins/image-plugin.d.ts +1 -0
  34. package/dist/plugins/markdown-plugin.d.ts +1 -0
  35. package/dist/plugins/pdf-plugin.d.ts +1 -0
  36. package/dist/plugins/pptx-plugin.d.ts +1 -0
  37. package/dist/plugins/rtf-plugin.d.ts +1 -0
  38. package/dist/plugins/source-code-plugin.d.ts +1 -0
  39. package/dist/plugins/svg-plugin.d.ts +1 -0
  40. package/dist/plugins/text-plugin.d.ts +1 -0
  41. package/dist/plugins/video-plugin.d.ts +1 -0
  42. package/dist/plugins/xlsx-plugin.d.ts +1 -0
  43. package/dist/plugins/zip-plugin.d.ts +1 -0
  44. package/dist/remote-url.d.ts +20 -5
  45. package/dist/remote-url.js +52 -128
  46. package/dist/remote-url.js.map +1 -1
  47. package/docs/supported-formats.md +143 -0
  48. package/package.json +12 -11
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Magic-byte sniffers.
3
+ *
4
+ * Pulled out of `remote-url.ts` so both the remote-URL pipeline and the
5
+ * synchronous `detectFileMeta` can share one source of truth for byte-level
6
+ * format detection. Add a new format here, and every entry point that uses
7
+ * detection picks it up automatically.
8
+ *
9
+ * The two layers:
10
+ *
11
+ * 1. `sniffMagic(buffer)` — sync, reads the first ~32 bytes only.
12
+ * Cheap, deterministic, no peer deps.
13
+ * 2. `sniffZipContainer(buffer)` — async, loads the buffer through
14
+ * jszip and inspects the entry list to
15
+ * distinguish docx / pptx / xlsx / epub
16
+ * (all of which are zip files at the
17
+ * magic level). Requires the optional
18
+ * `jszip` peer; returns null if
19
+ * unavailable.
20
+ */
21
+ interface MagicSniffResult {
22
+ /** Conventional extension (no leading dot), or null when no signature matched. */
23
+ ext: string | null;
24
+ /** MIME type for the matched signature, or null. */
25
+ mimeType: string | null;
26
+ }
27
+ interface ContainerSniffResult {
28
+ ext: string;
29
+ mimeType: string;
30
+ }
31
+ /**
32
+ * Inspect the first 32 bytes of `buffer` and identify the format by signature.
33
+ *
34
+ * Covered: PDF, ZIP (any of three local-file-header variants), PNG, JPEG, GIF
35
+ * (87a/89a), WebP, MP4 (ISO BMFF "ftyp"), OLE compound (legacy doc/xls/ppt).
36
+ *
37
+ * For ZIP-based office formats (docx/pptx/xlsx/epub) `sniffMagic` returns
38
+ * `ext: "zip"` — the caller must then run {@link sniffZipContainer} to
39
+ * disambiguate. Keeping it two-layered lets the cheap sync path handle 90%
40
+ * of files without paying jszip's load cost.
41
+ */
42
+ declare function sniffMagic(buffer: ArrayBuffer): MagicSniffResult;
43
+ /**
44
+ * Look inside a ZIP buffer and tell which OOXML / EPUB format it actually is.
45
+ *
46
+ * - `word/document.xml` → docx
47
+ * - `ppt/presentation.xml` → pptx
48
+ * - `xl/workbook.xml` → xlsx
49
+ * - `mimetype` == `application/epub+zip` → epub
50
+ *
51
+ * Returns `null` when none of those marker entries are present (i.e. just a
52
+ * plain zip), or when jszip can't be loaded / the buffer isn't a valid zip.
53
+ */
54
+ declare function sniffZipContainer(buffer: ArrayBuffer): Promise<ContainerSniffResult | null>;
55
+
56
+ export { type ContainerSniffResult, type MagicSniffResult, sniffMagic, sniffZipContainer };
@@ -0,0 +1,97 @@
1
+ function startsWithBytes(bytes, signature) {
2
+ if (bytes.length < signature.length) return false;
3
+ return signature.every((value, index) => bytes[index] === value);
4
+ }
5
+ function readAscii(bytes, start, length) {
6
+ return String.fromCharCode(...bytes.subarray(start, start + length));
7
+ }
8
+ function sniffMagic(buffer) {
9
+ const bytes = new Uint8Array(buffer.slice(0, 32));
10
+ if (readAscii(bytes, 0, 5) === "%PDF-") {
11
+ return { ext: "pdf", mimeType: "application/pdf" };
12
+ }
13
+ if (startsWithBytes(bytes, [80, 75, 3, 4]) || startsWithBytes(bytes, [80, 75, 5, 6]) || startsWithBytes(bytes, [80, 75, 7, 8])) {
14
+ return { ext: "zip", mimeType: "application/zip" };
15
+ }
16
+ if (startsWithBytes(bytes, [
17
+ 137,
18
+ 80,
19
+ 78,
20
+ 71,
21
+ 13,
22
+ 10,
23
+ 26,
24
+ 10
25
+ ])) {
26
+ return { ext: "png", mimeType: "image/png" };
27
+ }
28
+ if (startsWithBytes(bytes, [255, 216, 255])) {
29
+ return { ext: "jpg", mimeType: "image/jpeg" };
30
+ }
31
+ const gifHeader = readAscii(bytes, 0, 6);
32
+ if (gifHeader === "GIF87a" || gifHeader === "GIF89a") {
33
+ return { ext: "gif", mimeType: "image/gif" };
34
+ }
35
+ if (readAscii(bytes, 0, 4) === "RIFF" && readAscii(bytes, 8, 4) === "WEBP") {
36
+ return { ext: "webp", mimeType: "image/webp" };
37
+ }
38
+ if (bytes.length >= 12 && readAscii(bytes, 4, 4) === "ftyp") {
39
+ return { ext: "mp4", mimeType: "video/mp4" };
40
+ }
41
+ if (startsWithBytes(bytes, [
42
+ 208,
43
+ 207,
44
+ 17,
45
+ 224,
46
+ 161,
47
+ 177,
48
+ 26,
49
+ 225
50
+ ])) {
51
+ return { ext: "ole", mimeType: "application/x-ole-storage" };
52
+ }
53
+ return { ext: null, mimeType: null };
54
+ }
55
+ async function sniffZipContainer(buffer) {
56
+ try {
57
+ const { default: JSZip } = await import("jszip");
58
+ const zip = await JSZip.loadAsync(buffer);
59
+ const fileNames = Object.keys(zip.files).map(
60
+ (name) => name.replace(/\\/g, "/").toLowerCase()
61
+ );
62
+ const hasFile = (target) => fileNames.includes(target);
63
+ if (hasFile("word/document.xml")) {
64
+ return {
65
+ ext: "docx",
66
+ mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
67
+ };
68
+ }
69
+ if (hasFile("ppt/presentation.xml")) {
70
+ return {
71
+ ext: "pptx",
72
+ mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
73
+ };
74
+ }
75
+ if (hasFile("xl/workbook.xml")) {
76
+ return {
77
+ ext: "xlsx",
78
+ mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
79
+ };
80
+ }
81
+ const mimetypeFile = zip.file("mimetype");
82
+ if (mimetypeFile) {
83
+ const mimetype = (await mimetypeFile.async("string")).trim();
84
+ if (mimetype === "application/epub+zip") {
85
+ return { ext: "epub", mimeType: "application/epub+zip" };
86
+ }
87
+ }
88
+ return null;
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+ export {
94
+ sniffMagic,
95
+ sniffZipContainer
96
+ };
97
+ //# sourceMappingURL=magic-bytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/magic-bytes.ts"],"sourcesContent":["/**\n * Magic-byte sniffers.\n *\n * Pulled out of `remote-url.ts` so both the remote-URL pipeline and the\n * synchronous `detectFileMeta` can share one source of truth for byte-level\n * format detection. Add a new format here, and every entry point that uses\n * detection picks it up automatically.\n *\n * The two layers:\n *\n * 1. `sniffMagic(buffer)` — sync, reads the first ~32 bytes only.\n * Cheap, deterministic, no peer deps.\n * 2. `sniffZipContainer(buffer)` — async, loads the buffer through\n * jszip and inspects the entry list to\n * distinguish docx / pptx / xlsx / epub\n * (all of which are zip files at the\n * magic level). Requires the optional\n * `jszip` peer; returns null if\n * unavailable.\n */\n\nexport interface MagicSniffResult {\n /** Conventional extension (no leading dot), or null when no signature matched. */\n ext: string | null;\n /** MIME type for the matched signature, or null. */\n mimeType: string | null;\n}\n\nexport interface ContainerSniffResult {\n ext: string;\n mimeType: string;\n}\n\nfunction startsWithBytes(bytes: Uint8Array, signature: number[]): boolean {\n if (bytes.length < signature.length) return false;\n return signature.every((value, index) => bytes[index] === value);\n}\n\nfunction readAscii(bytes: Uint8Array, start: number, length: number): string {\n return String.fromCharCode(...bytes.subarray(start, start + length));\n}\n\n/**\n * Inspect the first 32 bytes of `buffer` and identify the format by signature.\n *\n * Covered: PDF, ZIP (any of three local-file-header variants), PNG, JPEG, GIF\n * (87a/89a), WebP, MP4 (ISO BMFF \"ftyp\"), OLE compound (legacy doc/xls/ppt).\n *\n * For ZIP-based office formats (docx/pptx/xlsx/epub) `sniffMagic` returns\n * `ext: \"zip\"` — the caller must then run {@link sniffZipContainer} to\n * disambiguate. Keeping it two-layered lets the cheap sync path handle 90%\n * of files without paying jszip's load cost.\n */\nexport function sniffMagic(buffer: ArrayBuffer): MagicSniffResult {\n const bytes = new Uint8Array(buffer.slice(0, 32));\n\n if (readAscii(bytes, 0, 5) === \"%PDF-\") {\n return { ext: \"pdf\", mimeType: \"application/pdf\" };\n }\n\n // Three valid ZIP local-file-header variants. The first is by far the most\n // common; the latter two appear in spanned/empty zips but we still want to\n // recognize them.\n if (\n startsWithBytes(bytes, [0x50, 0x4b, 0x03, 0x04]) ||\n startsWithBytes(bytes, [0x50, 0x4b, 0x05, 0x06]) ||\n startsWithBytes(bytes, [0x50, 0x4b, 0x07, 0x08])\n ) {\n return { ext: \"zip\", mimeType: \"application/zip\" };\n }\n\n if (\n startsWithBytes(bytes, [\n 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,\n ])\n ) {\n return { ext: \"png\", mimeType: \"image/png\" };\n }\n\n if (startsWithBytes(bytes, [0xff, 0xd8, 0xff])) {\n return { ext: \"jpg\", mimeType: \"image/jpeg\" };\n }\n\n const gifHeader = readAscii(bytes, 0, 6);\n if (gifHeader === \"GIF87a\" || gifHeader === \"GIF89a\") {\n return { ext: \"gif\", mimeType: \"image/gif\" };\n }\n\n if (readAscii(bytes, 0, 4) === \"RIFF\" && readAscii(bytes, 8, 4) === \"WEBP\") {\n return { ext: \"webp\", mimeType: \"image/webp\" };\n }\n\n // ISO Base Media File Format (\"ftyp\" at offset 4). Could be MP4, MOV, M4A,\n // 3GP, etc. — we report mp4 as the umbrella; finer-grained classification\n // would require parsing the major-brand box, which we don't need for the\n // current preview pipeline.\n if (bytes.length >= 12 && readAscii(bytes, 4, 4) === \"ftyp\") {\n return { ext: \"mp4\", mimeType: \"video/mp4\" };\n }\n\n // OLE2 compound document signature — legacy doc / xls / ppt all use this.\n // We report \"ole\" generically; the file type is then resolved from the\n // filename extension upstream (binary OLE distinguishes by stream layout\n // which we don't parse).\n if (\n startsWithBytes(bytes, [\n 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,\n ])\n ) {\n return { ext: \"ole\", mimeType: \"application/x-ole-storage\" };\n }\n\n return { ext: null, mimeType: null };\n}\n\n/**\n * Look inside a ZIP buffer and tell which OOXML / EPUB format it actually is.\n *\n * - `word/document.xml` → docx\n * - `ppt/presentation.xml` → pptx\n * - `xl/workbook.xml` → xlsx\n * - `mimetype` == `application/epub+zip` → epub\n *\n * Returns `null` when none of those marker entries are present (i.e. just a\n * plain zip), or when jszip can't be loaded / the buffer isn't a valid zip.\n */\nexport async function sniffZipContainer(\n buffer: ArrayBuffer\n): Promise<ContainerSniffResult | null> {\n try {\n const { default: JSZip } = await import(\"jszip\");\n const zip = await JSZip.loadAsync(buffer);\n\n // Path separators in zip entries are spec'd as forward slashes, but\n // Windows-built archives sometimes use backslashes. Normalize so\n // `hasFile` checks are reliable.\n const fileNames = Object.keys(zip.files).map((name) =>\n name.replace(/\\\\/g, \"/\").toLowerCase()\n );\n const hasFile = (target: string) => fileNames.includes(target);\n\n if (hasFile(\"word/document.xml\")) {\n return {\n ext: \"docx\",\n mimeType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n };\n }\n\n if (hasFile(\"ppt/presentation.xml\")) {\n return {\n ext: \"pptx\",\n mimeType:\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n };\n }\n\n if (hasFile(\"xl/workbook.xml\")) {\n return {\n ext: \"xlsx\",\n mimeType:\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n };\n }\n\n const mimetypeFile = zip.file(\"mimetype\");\n if (mimetypeFile) {\n const mimetype = (await mimetypeFile.async(\"string\")).trim();\n if (mimetype === \"application/epub+zip\") {\n return { ext: \"epub\", mimeType: \"application/epub+zip\" };\n }\n }\n\n return null;\n } catch {\n return null;\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  import { ComponentType } from 'react';
2
2
  import { FileInfo } from './types.js';
3
+ import { PreviewError } from './preview-error.js';
3
4
 
4
5
  interface PreviewPlugin {
5
6
  id: string;
@@ -28,10 +29,9 @@ declare function loadWithOptionalDep<T>(loader: () => Promise<T>, meta: {
28
29
  package: string;
29
30
  featureLabel: string;
30
31
  }): () => Promise<T>;
31
- declare class MissingPeerDependencyError extends Error {
32
+ declare class MissingPeerDependencyError extends PreviewError {
32
33
  packageName: string;
33
34
  featureLabel: string;
34
- cause: unknown;
35
35
  constructor(packageName: string, featureLabel: string, cause: unknown);
36
36
  }
37
37
 
@@ -1,3 +1,4 @@
1
+ import { PreviewError } from "./preview-error";
1
2
  function loadWithOptionalDep(loader, meta) {
2
3
  return async () => {
3
4
  try {
@@ -10,9 +11,10 @@ function loadWithOptionalDep(loader, meta) {
10
11
  }
11
12
  };
12
13
  }
13
- class MissingPeerDependencyError extends Error {
14
+ class MissingPeerDependencyError extends PreviewError {
14
15
  constructor(packageName, featureLabel, cause) {
15
16
  super(
17
+ "MISSING_PEER_DEPENDENCY",
16
18
  `${featureLabel} requires the optional peer dependency "${packageName}".
17
19
 
18
20
  Install it in your app:
@@ -20,11 +22,11 @@ Install it in your app:
20
22
  # or: npm install ${packageName}
21
23
  # or: yarn add ${packageName}
22
24
 
23
- See: https://github.com/CoderLambert/filevista/tree/main/packages/file-preview#optional-peer-dependencies`
25
+ See: https://github.com/CoderLambert/filevista/tree/main/packages/file-preview#optional-peer-dependencies`,
26
+ { cause, details: { packageName, featureLabel } }
24
27
  );
25
28
  this.packageName = packageName;
26
29
  this.featureLabel = featureLabel;
27
- this.cause = cause;
28
30
  this.name = "MissingPeerDependencyError";
29
31
  }
30
32
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/plugin.ts"],"sourcesContent":["import type { ComponentType } from \"react\";\nimport type { FileInfo } from \"../utils\";\n\nexport interface PreviewPlugin {\n id: string;\n name: string;\n priority?: number;\n match(file: FileInfo): boolean;\n load(): Promise<{ default: ComponentType<{ file: FileInfo }> }>;\n}\n\n/**\n * Wrap a dynamic-import loader so that, if the import fails because an\n * optional peer dependency is missing (e.g. `pdfjs-dist`, `exceljs`), the\n * user sees a clear \"install this package\" message instead of the raw\n * bundler error.\n *\n * @example\n * load: loadWithOptionalDep(\n * () => import(\"../preview-adapters/PdfPreviewAdapter\"),\n * { package: \"pdfjs-dist\", featureLabel: \"PDF preview\" },\n * )\n */\nexport function loadWithOptionalDep<T>(\n loader: () => Promise<T>,\n meta: { package: string; featureLabel: string },\n): () => Promise<T> {\n return async () => {\n try {\n return await loader();\n } catch (cause) {\n if (isModuleNotFoundError(cause, meta.package)) {\n throw new MissingPeerDependencyError(meta.package, meta.featureLabel, cause);\n }\n throw cause;\n }\n };\n}\n\nexport class MissingPeerDependencyError extends Error {\n constructor(\n public packageName: string,\n public featureLabel: string,\n public cause: unknown,\n ) {\n super(\n `${featureLabel} requires the optional peer dependency \"${packageName}\".\\n\\n` +\n `Install it in your app:\\n` +\n ` pnpm add ${packageName}\\n` +\n ` # or: npm install ${packageName}\\n` +\n ` # or: yarn add ${packageName}\\n\\n` +\n `See: https://github.com/CoderLambert/filevista/tree/main/packages/file-preview#optional-peer-dependencies`,\n );\n this.name = \"MissingPeerDependencyError\";\n }\n}\n\n/**\n * Detect \"module not found\" failures across the bundlers/runtimes our users\n * are likely on — Webpack, Turbopack, Vite, native ESM, esbuild.\n *\n * We deliberately match loosely. False positives are cheap (the install hint\n * is always actionable) but false negatives swallow the affordance.\n */\nfunction isModuleNotFoundError(error: unknown, packageName: string): boolean {\n if (!(error instanceof Error)) return false;\n const message = error.message ?? \"\";\n if (!message) return false;\n\n // The package name must appear in the message — otherwise we're attributing\n // an unrelated error to the wrong dependency.\n if (!message.includes(packageName)) return false;\n\n return (\n message.includes(\"Cannot find module\") || // Node ESM, Webpack\n message.includes(\"Failed to fetch dynamically imported module\") || // Native browser ESM\n message.includes(\"Failed to resolve module specifier\") || // Vite dev\n message.includes(\"Module not found\") || // Webpack production\n message.includes(\"Unable to resolve\") // Metro / some bundlers\n );\n}\n"],"mappings":"AAuBO,SAAS,oBACd,QACA,MACkB;AAClB,SAAO,YAAY;AACjB,QAAI;AACF,aAAO,MAAM,OAAO;AAAA,IACtB,SAAS,OAAO;AACd,UAAI,sBAAsB,OAAO,KAAK,OAAO,GAAG;AAC9C,cAAM,IAAI,2BAA2B,KAAK,SAAS,KAAK,cAAc,KAAK;AAAA,MAC7E;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEO,MAAM,mCAAmC,MAAM;AAAA,EACpD,YACS,aACA,cACA,OACP;AACA;AAAA,MACE,GAAG,YAAY,2CAA2C,WAAW;AAAA;AAAA;AAAA,aAErD,WAAW;AAAA,sBACF,WAAW;AAAA,mBACd,WAAW;AAAA;AAAA;AAAA,IAEnC;AAXO;AACA;AACA;AAUP,SAAK,OAAO;AAAA,EACd;AACF;AASA,SAAS,sBAAsB,OAAgB,aAA8B;AAC3E,MAAI,EAAE,iBAAiB,OAAQ,QAAO;AACtC,QAAM,UAAU,MAAM,WAAW;AACjC,MAAI,CAAC,QAAS,QAAO;AAIrB,MAAI,CAAC,QAAQ,SAAS,WAAW,EAAG,QAAO;AAE3C,SACE,QAAQ,SAAS,oBAAoB;AAAA,EACrC,QAAQ,SAAS,6CAA6C;AAAA,EAC9D,QAAQ,SAAS,oCAAoC;AAAA,EACrD,QAAQ,SAAS,kBAAkB;AAAA,EACnC,QAAQ,SAAS,mBAAmB;AAExC;","names":[]}
1
+ {"version":3,"sources":["../../src/core/plugin.ts"],"sourcesContent":["import type { ComponentType } from \"react\";\nimport type { FileInfo } from \"../utils\";\nimport { PreviewError } from \"./preview-error\";\n\nexport interface PreviewPlugin {\n id: string;\n name: string;\n priority?: number;\n match(file: FileInfo): boolean;\n load(): Promise<{ default: ComponentType<{ file: FileInfo }> }>;\n}\n\n/**\n * Wrap a dynamic-import loader so that, if the import fails because an\n * optional peer dependency is missing (e.g. `pdfjs-dist`, `exceljs`), the\n * user sees a clear \"install this package\" message instead of the raw\n * bundler error.\n *\n * @example\n * load: loadWithOptionalDep(\n * () => import(\"../preview-adapters/PdfPreviewAdapter\"),\n * { package: \"pdfjs-dist\", featureLabel: \"PDF preview\" },\n * )\n */\nexport function loadWithOptionalDep<T>(\n loader: () => Promise<T>,\n meta: { package: string; featureLabel: string },\n): () => Promise<T> {\n return async () => {\n try {\n return await loader();\n } catch (cause) {\n if (isModuleNotFoundError(cause, meta.package)) {\n throw new MissingPeerDependencyError(meta.package, meta.featureLabel, cause);\n }\n throw cause;\n }\n };\n}\n\nexport class MissingPeerDependencyError extends PreviewError {\n constructor(\n public packageName: string,\n public featureLabel: string,\n cause: unknown,\n ) {\n super(\n \"MISSING_PEER_DEPENDENCY\",\n `${featureLabel} requires the optional peer dependency \"${packageName}\".\\n\\n` +\n `Install it in your app:\\n` +\n ` pnpm add ${packageName}\\n` +\n ` # or: npm install ${packageName}\\n` +\n ` # or: yarn add ${packageName}\\n\\n` +\n `See: https://github.com/CoderLambert/filevista/tree/main/packages/file-preview#optional-peer-dependencies`,\n { cause, details: { packageName, featureLabel } },\n );\n this.name = \"MissingPeerDependencyError\";\n }\n}\n\n/**\n * Detect \"module not found\" failures across the bundlers/runtimes our users\n * are likely on — Webpack, Turbopack, Vite, native ESM, esbuild.\n *\n * We deliberately match loosely. False positives are cheap (the install hint\n * is always actionable) but false negatives swallow the affordance.\n */\nfunction isModuleNotFoundError(error: unknown, packageName: string): boolean {\n if (!(error instanceof Error)) return false;\n const message = error.message ?? \"\";\n if (!message) return false;\n\n // The package name must appear in the message — otherwise we're attributing\n // an unrelated error to the wrong dependency.\n if (!message.includes(packageName)) return false;\n\n return (\n message.includes(\"Cannot find module\") || // Node ESM, Webpack\n message.includes(\"Failed to fetch dynamically imported module\") || // Native browser ESM\n message.includes(\"Failed to resolve module specifier\") || // Vite dev\n message.includes(\"Module not found\") || // Webpack production\n message.includes(\"Unable to resolve\") // Metro / some bundlers\n );\n}\n"],"mappings":"AAEA,SAAS,oBAAoB;AAsBtB,SAAS,oBACd,QACA,MACkB;AAClB,SAAO,YAAY;AACjB,QAAI;AACF,aAAO,MAAM,OAAO;AAAA,IACtB,SAAS,OAAO;AACd,UAAI,sBAAsB,OAAO,KAAK,OAAO,GAAG;AAC9C,cAAM,IAAI,2BAA2B,KAAK,SAAS,KAAK,cAAc,KAAK;AAAA,MAC7E;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEO,MAAM,mCAAmC,aAAa;AAAA,EAC3D,YACS,aACA,cACP,OACA;AACA;AAAA,MACE;AAAA,MACA,GAAG,YAAY,2CAA2C,WAAW;AAAA;AAAA;AAAA,aAErD,WAAW;AAAA,sBACF,WAAW;AAAA,mBACd,WAAW;AAAA;AAAA;AAAA,MAEjC,EAAE,OAAO,SAAS,EAAE,aAAa,aAAa,EAAE;AAAA,IAClD;AAbO;AACA;AAaP,SAAK,OAAO;AAAA,EACd;AACF;AASA,SAAS,sBAAsB,OAAgB,aAA8B;AAC3E,MAAI,EAAE,iBAAiB,OAAQ,QAAO;AACtC,QAAM,UAAU,MAAM,WAAW;AACjC,MAAI,CAAC,QAAS,QAAO;AAIrB,MAAI,CAAC,QAAQ,SAAS,WAAW,EAAG,QAAO;AAE3C,SACE,QAAQ,SAAS,oBAAoB;AAAA,EACrC,QAAQ,SAAS,6CAA6C;AAAA,EAC9D,QAAQ,SAAS,oCAAoC;AAAA,EACrD,QAAQ,SAAS,kBAAkB;AAAA,EACnC,QAAQ,SAAS,mBAAmB;AAExC;","names":[]}
@@ -0,0 +1,35 @@
1
+ type PreviewErrorCode = "MISSING_PEER_DEPENDENCY" | "UNSUPPORTED_FILE_TYPE" | "FILE_TOO_LARGE" | "REMOTE_CORS_ERROR" | "REMOTE_HTTP_ERROR" | "INVALID_URL" | "UNSUPPORTED_PROTOCOL" | "ABORTED" | "PARSE_FAILED" | "RENDER_FAILED" | "SECURITY_BLOCKED";
2
+ interface PreviewErrorOptions {
3
+ cause?: unknown;
4
+ url?: string;
5
+ pluginId?: string;
6
+ pluginName?: string;
7
+ fileName?: string;
8
+ details?: Record<string, unknown>;
9
+ }
10
+ /**
11
+ * Stable, SDK-facing error shape.
12
+ *
13
+ * Messages can change as UX improves; `code` is the contract consumers should
14
+ * switch on for telemetry, retry policy, and custom fallback UI.
15
+ */
16
+ declare class PreviewError extends Error {
17
+ readonly code: PreviewErrorCode;
18
+ readonly cause?: unknown;
19
+ readonly url?: string;
20
+ readonly pluginId?: string;
21
+ readonly pluginName?: string;
22
+ readonly fileName?: string;
23
+ readonly details?: Record<string, unknown>;
24
+ constructor(code: PreviewErrorCode, message: string, options?: PreviewErrorOptions);
25
+ }
26
+ declare function isPreviewError(error: unknown): error is PreviewError;
27
+ declare function normalizePreviewError(error: unknown, fallback: {
28
+ code: PreviewErrorCode;
29
+ message: string;
30
+ pluginId?: string;
31
+ pluginName?: string;
32
+ fileName?: string;
33
+ }): PreviewError;
34
+
35
+ export { PreviewError, type PreviewErrorCode, type PreviewErrorOptions, isPreviewError, normalizePreviewError };
@@ -0,0 +1,39 @@
1
+ class PreviewError extends Error {
2
+ constructor(code, message, options = {}) {
3
+ super(message);
4
+ this.name = "PreviewError";
5
+ this.code = code;
6
+ this.cause = options.cause;
7
+ this.url = options.url;
8
+ this.pluginId = options.pluginId;
9
+ this.pluginName = options.pluginName;
10
+ this.fileName = options.fileName;
11
+ this.details = options.details;
12
+ }
13
+ }
14
+ function isPreviewError(error) {
15
+ return error instanceof PreviewError;
16
+ }
17
+ function normalizePreviewError(error, fallback) {
18
+ if (error instanceof PreviewError) return error;
19
+ if (error instanceof Error) {
20
+ return new PreviewError(fallback.code, error.message || fallback.message, {
21
+ cause: error,
22
+ pluginId: fallback.pluginId,
23
+ pluginName: fallback.pluginName,
24
+ fileName: fallback.fileName
25
+ });
26
+ }
27
+ return new PreviewError(fallback.code, fallback.message, {
28
+ cause: error,
29
+ pluginId: fallback.pluginId,
30
+ pluginName: fallback.pluginName,
31
+ fileName: fallback.fileName
32
+ });
33
+ }
34
+ export {
35
+ PreviewError,
36
+ isPreviewError,
37
+ normalizePreviewError
38
+ };
39
+ //# sourceMappingURL=preview-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/preview-error.ts"],"sourcesContent":["export type PreviewErrorCode =\n | \"MISSING_PEER_DEPENDENCY\"\n | \"UNSUPPORTED_FILE_TYPE\"\n | \"FILE_TOO_LARGE\"\n | \"REMOTE_CORS_ERROR\"\n | \"REMOTE_HTTP_ERROR\"\n | \"INVALID_URL\"\n | \"UNSUPPORTED_PROTOCOL\"\n | \"ABORTED\"\n | \"PARSE_FAILED\"\n | \"RENDER_FAILED\"\n | \"SECURITY_BLOCKED\";\n\nexport interface PreviewErrorOptions {\n cause?: unknown;\n url?: string;\n pluginId?: string;\n pluginName?: string;\n fileName?: string;\n details?: Record<string, unknown>;\n}\n\n/**\n * Stable, SDK-facing error shape.\n *\n * Messages can change as UX improves; `code` is the contract consumers should\n * switch on for telemetry, retry policy, and custom fallback UI.\n */\nexport class PreviewError extends Error {\n readonly code: PreviewErrorCode;\n readonly cause?: unknown;\n readonly url?: string;\n readonly pluginId?: string;\n readonly pluginName?: string;\n readonly fileName?: string;\n readonly details?: Record<string, unknown>;\n\n constructor(code: PreviewErrorCode, message: string, options: PreviewErrorOptions = {}) {\n super(message);\n this.name = \"PreviewError\";\n this.code = code;\n this.cause = options.cause;\n this.url = options.url;\n this.pluginId = options.pluginId;\n this.pluginName = options.pluginName;\n this.fileName = options.fileName;\n this.details = options.details;\n }\n}\n\nexport function isPreviewError(error: unknown): error is PreviewError {\n return error instanceof PreviewError;\n}\n\nexport function normalizePreviewError(\n error: unknown,\n fallback: {\n code: PreviewErrorCode;\n message: string;\n pluginId?: string;\n pluginName?: string;\n fileName?: string;\n }\n): PreviewError {\n if (error instanceof PreviewError) return error;\n\n if (error instanceof Error) {\n return new PreviewError(fallback.code, error.message || fallback.message, {\n cause: error,\n pluginId: fallback.pluginId,\n pluginName: fallback.pluginName,\n fileName: fallback.fileName,\n });\n }\n\n return new PreviewError(fallback.code, fallback.message, {\n cause: error,\n pluginId: fallback.pluginId,\n pluginName: fallback.pluginName,\n fileName: fallback.fileName,\n });\n}\n"],"mappings":"AA4BO,MAAM,qBAAqB,MAAM;AAAA,EAStC,YAAY,MAAwB,SAAiB,UAA+B,CAAC,GAAG;AACtF,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,QAAQ,QAAQ;AACrB,SAAK,MAAM,QAAQ;AACnB,SAAK,WAAW,QAAQ;AACxB,SAAK,aAAa,QAAQ;AAC1B,SAAK,WAAW,QAAQ;AACxB,SAAK,UAAU,QAAQ;AAAA,EACzB;AACF;AAEO,SAAS,eAAe,OAAuC;AACpE,SAAO,iBAAiB;AAC1B;AAEO,SAAS,sBACd,OACA,UAOc;AACd,MAAI,iBAAiB,aAAc,QAAO;AAE1C,MAAI,iBAAiB,OAAO;AAC1B,WAAO,IAAI,aAAa,SAAS,MAAM,MAAM,WAAW,SAAS,SAAS;AAAA,MACxE,OAAO;AAAA,MACP,UAAU,SAAS;AAAA,MACnB,YAAY,SAAS;AAAA,MACrB,UAAU,SAAS;AAAA,IACrB,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,aAAa,SAAS,MAAM,SAAS,SAAS;AAAA,IACvD,OAAO;AAAA,IACP,UAAU,SAAS;AAAA,IACnB,YAAY,SAAS;AAAA,IACrB,UAAU,SAAS;AAAA,EACrB,CAAC;AACH;","names":[]}
@@ -1,6 +1,7 @@
1
1
  import { FileInfo } from './types.js';
2
2
  import { PreviewPlugin } from './plugin.js';
3
3
  import 'react';
4
+ import './preview-error.js';
4
5
 
5
6
  declare class PreviewPluginRegistry {
6
7
  private plugins;
package/dist/index.d.ts CHANGED
@@ -2,12 +2,15 @@ export { PluginPreviewRenderer, PluginPreviewRendererProps } from './PluginPrevi
2
2
  export { ALL_FILE_TYPES, FileInfo, FileType, NormalizedFile, PreviewSource } from './core/types.js';
3
3
  export { PreviewPlugin } from './core/plugin.js';
4
4
  export { PreviewPluginRegistry, createPreviewPluginRegistry } from './core/registry.js';
5
+ export { PreviewError, PreviewErrorCode, PreviewErrorOptions, isPreviewError, normalizePreviewError } from './core/preview-error.js';
5
6
  export { ReadSourceOptions, createObjectUrlFromSource, getSourceMimeType, getSourceName, getSourceSize, readSourceAsArrayBuffer, readSourceAsBase64, readSourceAsText } from './core/source.js';
6
7
  export { BinaryPreviewInput, readBinaryPreviewAsArrayBuffer, readBinaryPreviewAsUint8Array } from './core/binary.js';
7
8
  export { downloadSource } from './core/download.js';
8
9
  export { getAssetBasePath, resolveAssetPath, setAssetBasePath } from './core/config.js';
9
10
  export { LocaleMessages, LocaleProvider, enUS, getDefaultLocale, useLocale, zhCN } from './core/i18n.js';
10
11
  export { detectFileType } from './utils.js';
12
+ export { DetectFileMetaOptions, FileMeta, FileMetaConfidence, FileMetaDetectBy, detectFileMeta } from './core/detect-meta.js';
13
+ export { ContainerSniffResult, MagicSniffResult, sniffMagic, sniffZipContainer } from './core/magic-bytes.js';
11
14
  export { PREVIEW_SUPPORT_MATRIX, PreviewSupportMeta, PreviewSupportStatus, RendererSupportState, getPreviewSupportMeta, isDegradedFileType, isPluginSupportedFileType, isUnsupportedFileType } from './support-status.js';
12
15
  export { PREVIEW_SIZE_LIMITS, PreviewSizeLevel, PreviewSizePolicy, getPreviewSizeLevel, getPreviewSizePolicy } from './performance-limits.js';
13
16
  export { FILE_PREVIEW_LIMITS, XLSX_PREVIEW_LIMITS, shouldHighlight, truncateContent } from './limits.js';
@@ -29,7 +32,7 @@ export { textPlugin } from './plugins/text-plugin.js';
29
32
  export { videoPlugin } from './plugins/video-plugin.js';
30
33
  export { xlsxPlugin } from './plugins/xlsx-plugin.js';
31
34
  export { zipPlugin } from './plugins/zip-plugin.js';
32
- export { ProcessRemoteUrlOptions, RemoteLoadProgress, RemoteUrlError, RemoteUrlErrorCode, processRemoteUrl } from './remote-url.js';
35
+ export { DEFAULT_REMOTE_MAX_BYTES, ProcessRemoteUrlOptions, RemoteLoadProgress, RemoteUrlError, RemoteUrlErrorCode, processRemoteUrl } from './remote-url.js';
33
36
  export { SourceTextState, useSourceText } from './hooks/useSourceText.js';
34
37
  export { SourceBase64State, useSourceBase64 } from './hooks/useSourceBase64.js';
35
38
  export { useObjectUrlFromSource } from './hooks/useObjectUrlFromSource.js';
package/dist/index.js CHANGED
@@ -6,6 +6,11 @@ import {
6
6
  PreviewPluginRegistry,
7
7
  createPreviewPluginRegistry
8
8
  } from "./core/registry";
9
+ import {
10
+ PreviewError,
11
+ isPreviewError,
12
+ normalizePreviewError
13
+ } from "./core/preview-error";
9
14
  import {
10
15
  readSourceAsArrayBuffer,
11
16
  readSourceAsText,
@@ -33,6 +38,13 @@ import {
33
38
  getDefaultLocale
34
39
  } from "./core/i18n";
35
40
  import { detectFileType } from "./utils";
41
+ import {
42
+ detectFileMeta
43
+ } from "./core/detect-meta";
44
+ import {
45
+ sniffMagic,
46
+ sniffZipContainer
47
+ } from "./core/magic-bytes";
36
48
  import {
37
49
  PREVIEW_SUPPORT_MATRIX,
38
50
  getPreviewSupportMeta,
@@ -74,7 +86,8 @@ import { xlsxPlugin } from "./plugins/xlsx-plugin";
74
86
  import { zipPlugin } from "./plugins/zip-plugin";
75
87
  import {
76
88
  processRemoteUrl,
77
- RemoteUrlError
89
+ RemoteUrlError,
90
+ DEFAULT_REMOTE_MAX_BYTES
78
91
  } from "./remote-url";
79
92
  import {
80
93
  useSourceText
@@ -85,12 +98,14 @@ import {
85
98
  import { useObjectUrlFromSource } from "./hooks/useObjectUrlFromSource";
86
99
  export {
87
100
  ALL_FILE_TYPES,
101
+ DEFAULT_REMOTE_MAX_BYTES,
88
102
  FILE_PREVIEW_LIMITS,
89
103
  LargeFileGate,
90
104
  LocaleProvider,
91
105
  PREVIEW_SIZE_LIMITS,
92
106
  PREVIEW_SUPPORT_MATRIX,
93
107
  PluginPreviewRenderer,
108
+ PreviewError,
94
109
  PreviewPluginRegistry,
95
110
  RemoteUrlError,
96
111
  XLSX_PREVIEW_LIMITS,
@@ -100,6 +115,7 @@ export {
100
115
  createObjectUrlFromSource,
101
116
  createPreviewPluginRegistry,
102
117
  csvPlugin,
118
+ detectFileMeta,
103
119
  detectFileType,
104
120
  docxPlugin,
105
121
  downloadSource,
@@ -117,8 +133,10 @@ export {
117
133
  imagePlugin,
118
134
  isDegradedFileType,
119
135
  isPluginSupportedFileType,
136
+ isPreviewError,
120
137
  isUnsupportedFileType,
121
138
  markdownPlugin,
139
+ normalizePreviewError,
122
140
  pdfPlugin,
123
141
  pptxPlugin,
124
142
  processRemoteUrl,
@@ -131,6 +149,8 @@ export {
131
149
  rtfPlugin,
132
150
  setAssetBasePath,
133
151
  shouldHighlight,
152
+ sniffMagic,
153
+ sniffZipContainer,
134
154
  sourceCodePlugin,
135
155
  svgPlugin,
136
156
  textPlugin,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @lamberl-lee/file-preview — public API\n *\n * Quick start:\n *\n * import { PluginPreviewRenderer, setAssetBasePath } from \"@lamberl-lee/file-preview\";\n * import \"@lamberl-lee/file-preview/styles/index.css\";\n *\n * setAssetBasePath(\"/static\"); // where you serve PDF.js worker + RTF.js bundles\n * <PluginPreviewRenderer file={fileInfo} />\n */\n\n// ─── Top-level renderer ───────────────────────────────────────────────────\nexport {\n PluginPreviewRenderer,\n type PluginPreviewRendererProps,\n} from \"./PluginPreviewRenderer\";\n\n// ─── Core abstractions ────────────────────────────────────────────────────\nexport type {\n PreviewSource,\n NormalizedFile,\n FileType,\n FileInfo,\n} from \"./core/types\";\nexport { ALL_FILE_TYPES } from \"./core/types\";\nexport type { PreviewPlugin } from \"./core/plugin\";\nexport {\n PreviewPluginRegistry,\n createPreviewPluginRegistry,\n} from \"./core/registry\";\n\n// Source utilities — read PreviewSource as text / ArrayBuffer / base64 / object URL.\nexport {\n readSourceAsArrayBuffer,\n readSourceAsText,\n readSourceAsBase64,\n createObjectUrlFromSource,\n getSourceName,\n getSourceMimeType,\n getSourceSize,\n type ReadSourceOptions,\n} from \"./core/source\";\nexport {\n readBinaryPreviewAsArrayBuffer,\n readBinaryPreviewAsUint8Array,\n type BinaryPreviewInput,\n} from \"./core/binary\";\nexport { downloadSource } from \"./core/download\";\n\n// Runtime configuration — base path for static assets (PDF.js worker, RTF.js bundles).\nexport {\n setAssetBasePath,\n getAssetBasePath,\n resolveAssetPath,\n} from \"./core/config\";\n\n// Internationalization — locale messages + provider hook.\nexport {\n zhCN,\n enUS,\n LocaleProvider,\n useLocale,\n getDefaultLocale,\n type LocaleMessages,\n} from \"./core/i18n\";\n\n// ─── File detection ───────────────────────────────────────────────────────\nexport { detectFileType } from \"./utils\";\n\n// Support matrix — which renderers cover which file types.\nexport {\n PREVIEW_SUPPORT_MATRIX,\n getPreviewSupportMeta,\n isPluginSupportedFileType,\n isUnsupportedFileType,\n isDegradedFileType,\n type PreviewSupportStatus,\n type PreviewSupportMeta,\n type RendererSupportState,\n} from \"./support-status\";\n\n// ─── Performance / size limits ────────────────────────────────────────────\nexport {\n PREVIEW_SIZE_LIMITS,\n getPreviewSizeLevel,\n getPreviewSizePolicy,\n type PreviewSizeLevel,\n type PreviewSizePolicy,\n} from \"./performance-limits\";\nexport {\n XLSX_PREVIEW_LIMITS,\n FILE_PREVIEW_LIMITS,\n shouldHighlight,\n truncateContent,\n} from \"./limits\";\nexport { LargeFileGate } from \"./LargeFileGate\";\n\n// ─── Built-in plugins (full registry) ─────────────────────────────────────\nexport {\n builtinPreviewPlugins,\n createBuiltinPreviewRegistry,\n} from \"./plugins/builtin-plugins\";\n\n// Individual plugins — for custom registries that drop or replace formats.\nexport { audioPlugin } from \"./plugins/audio-plugin\";\nexport { csvPlugin } from \"./plugins/csv-plugin\";\nexport { docxPlugin } from \"./plugins/docx-plugin\";\nexport { epubPlugin } from \"./plugins/epub-plugin\";\nexport { htmlPlugin } from \"./plugins/html-plugin\";\nexport { imagePlugin } from \"./plugins/image-plugin\";\nexport { markdownPlugin } from \"./plugins/markdown-plugin\";\nexport { pdfPlugin } from \"./plugins/pdf-plugin\";\nexport { pptxPlugin } from \"./plugins/pptx-plugin\";\nexport { rtfPlugin } from \"./plugins/rtf-plugin\";\nexport { sourceCodePlugin } from \"./plugins/source-code-plugin\";\nexport { svgPlugin } from \"./plugins/svg-plugin\";\nexport { textPlugin } from \"./plugins/text-plugin\";\nexport { videoPlugin } from \"./plugins/video-plugin\";\nexport { xlsxPlugin } from \"./plugins/xlsx-plugin\";\nexport { zipPlugin } from \"./plugins/zip-plugin\";\n\n// ─── Remote URL loader ────────────────────────────────────────────────────\nexport {\n processRemoteUrl,\n RemoteUrlError,\n type RemoteUrlErrorCode,\n type RemoteLoadProgress,\n type ProcessRemoteUrlOptions,\n} from \"./remote-url\";\n\n// ─── React hooks ──────────────────────────────────────────────────────────\nexport {\n useSourceText,\n type SourceTextState,\n} from \"./hooks/useSourceText\";\nexport {\n useSourceBase64,\n type SourceBase64State,\n} from \"./hooks/useSourceBase64\";\nexport { useObjectUrlFromSource } from \"./hooks/useObjectUrlFromSource\";\n"],"mappings":"AAaA;AAAA,EACE;AAAA,OAEK;AASP,SAAS,sBAAsB;AAE/B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,sBAAsB;AAG/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP,SAAS,sBAAsB;AAG/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGP,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AACjC,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAG1B;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP,SAAS,8BAA8B;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @lamberl-lee/file-preview — public API\n *\n * Quick start:\n *\n * import { PluginPreviewRenderer, setAssetBasePath } from \"@lamberl-lee/file-preview\";\n * import \"@lamberl-lee/file-preview/styles/index.css\";\n *\n * setAssetBasePath(\"/static\"); // where you serve PDF.js worker + RTF.js bundles\n * <PluginPreviewRenderer file={fileInfo} />\n */\n\n// ─── Top-level renderer ───────────────────────────────────────────────────\nexport {\n PluginPreviewRenderer,\n type PluginPreviewRendererProps,\n} from \"./PluginPreviewRenderer\";\n\n// ─── Core abstractions ────────────────────────────────────────────────────\nexport type {\n PreviewSource,\n NormalizedFile,\n FileType,\n FileInfo,\n} from \"./core/types\";\nexport { ALL_FILE_TYPES } from \"./core/types\";\nexport type { PreviewPlugin } from \"./core/plugin\";\nexport {\n PreviewPluginRegistry,\n createPreviewPluginRegistry,\n} from \"./core/registry\";\nexport {\n PreviewError,\n isPreviewError,\n normalizePreviewError,\n type PreviewErrorCode,\n type PreviewErrorOptions,\n} from \"./core/preview-error\";\n\n// Source utilities — read PreviewSource as text / ArrayBuffer / base64 / object URL.\nexport {\n readSourceAsArrayBuffer,\n readSourceAsText,\n readSourceAsBase64,\n createObjectUrlFromSource,\n getSourceName,\n getSourceMimeType,\n getSourceSize,\n type ReadSourceOptions,\n} from \"./core/source\";\nexport {\n readBinaryPreviewAsArrayBuffer,\n readBinaryPreviewAsUint8Array,\n type BinaryPreviewInput,\n} from \"./core/binary\";\nexport { downloadSource } from \"./core/download\";\n\n// Runtime configuration — base path for static assets (PDF.js worker, RTF.js bundles).\nexport {\n setAssetBasePath,\n getAssetBasePath,\n resolveAssetPath,\n} from \"./core/config\";\n\n// Internationalization — locale messages + provider hook.\nexport {\n zhCN,\n enUS,\n LocaleProvider,\n useLocale,\n getDefaultLocale,\n type LocaleMessages,\n} from \"./core/i18n\";\n\n// ─── File detection ───────────────────────────────────────────────────────\nexport { detectFileType } from \"./utils\";\nexport {\n detectFileMeta,\n type FileMeta,\n type FileMetaConfidence,\n type FileMetaDetectBy,\n type DetectFileMetaOptions,\n} from \"./core/detect-meta\";\nexport {\n sniffMagic,\n sniffZipContainer,\n type MagicSniffResult,\n type ContainerSniffResult,\n} from \"./core/magic-bytes\";\n\n// Support matrix — which renderers cover which file types.\nexport {\n PREVIEW_SUPPORT_MATRIX,\n getPreviewSupportMeta,\n isPluginSupportedFileType,\n isUnsupportedFileType,\n isDegradedFileType,\n type PreviewSupportStatus,\n type PreviewSupportMeta,\n type RendererSupportState,\n} from \"./support-status\";\n\n// ─── Performance / size limits ────────────────────────────────────────────\nexport {\n PREVIEW_SIZE_LIMITS,\n getPreviewSizeLevel,\n getPreviewSizePolicy,\n type PreviewSizeLevel,\n type PreviewSizePolicy,\n} from \"./performance-limits\";\nexport {\n XLSX_PREVIEW_LIMITS,\n FILE_PREVIEW_LIMITS,\n shouldHighlight,\n truncateContent,\n} from \"./limits\";\nexport { LargeFileGate } from \"./LargeFileGate\";\n\n// ─── Built-in plugins (full registry) ─────────────────────────────────────\nexport {\n builtinPreviewPlugins,\n createBuiltinPreviewRegistry,\n} from \"./plugins/builtin-plugins\";\n\n// Individual plugins — for custom registries that drop or replace formats.\nexport { audioPlugin } from \"./plugins/audio-plugin\";\nexport { csvPlugin } from \"./plugins/csv-plugin\";\nexport { docxPlugin } from \"./plugins/docx-plugin\";\nexport { epubPlugin } from \"./plugins/epub-plugin\";\nexport { htmlPlugin } from \"./plugins/html-plugin\";\nexport { imagePlugin } from \"./plugins/image-plugin\";\nexport { markdownPlugin } from \"./plugins/markdown-plugin\";\nexport { pdfPlugin } from \"./plugins/pdf-plugin\";\nexport { pptxPlugin } from \"./plugins/pptx-plugin\";\nexport { rtfPlugin } from \"./plugins/rtf-plugin\";\nexport { sourceCodePlugin } from \"./plugins/source-code-plugin\";\nexport { svgPlugin } from \"./plugins/svg-plugin\";\nexport { textPlugin } from \"./plugins/text-plugin\";\nexport { videoPlugin } from \"./plugins/video-plugin\";\nexport { xlsxPlugin } from \"./plugins/xlsx-plugin\";\nexport { zipPlugin } from \"./plugins/zip-plugin\";\n\n// ─── Remote URL loader ────────────────────────────────────────────────────\nexport {\n processRemoteUrl,\n RemoteUrlError,\n DEFAULT_REMOTE_MAX_BYTES,\n type RemoteUrlErrorCode,\n type RemoteLoadProgress,\n type ProcessRemoteUrlOptions,\n} from \"./remote-url\";\n\n// ─── React hooks ──────────────────────────────────────────────────────────\nexport {\n useSourceText,\n type SourceTextState,\n} from \"./hooks/useSourceText\";\nexport {\n useSourceBase64,\n type SourceBase64State,\n} from \"./hooks/useSourceBase64\";\nexport { useObjectUrlFromSource } from \"./hooks/useObjectUrlFromSource\";\n"],"mappings":"AAaA;AAAA,EACE;AAAA,OAEK;AASP,SAAS,sBAAsB;AAE/B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,sBAAsB;AAG/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAGP,SAAS,sBAAsB;AAC/B;AAAA,EACE;AAAA,OAKK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,qBAAqB;AAG9B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAGP,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,wBAAwB;AACjC,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAG1B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP;AAAA,EACE;AAAA,OAEK;AACP;AAAA,EACE;AAAA,OAEK;AACP,SAAS,8BAA8B;","names":[]}
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const audioPlugin: PreviewPlugin;
6
7
 
@@ -2,6 +2,7 @@ import { PreviewPluginRegistry } from '../core/registry.js';
2
2
  import { PreviewPlugin } from '../core/plugin.js';
3
3
  import '../core/types.js';
4
4
  import 'react';
5
+ import '../core/preview-error.js';
5
6
 
6
7
  declare const builtinPreviewPlugins: PreviewPlugin[];
7
8
  declare function createBuiltinPreviewRegistry(): PreviewPluginRegistry;
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const csvPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const docxPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const epubPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const htmlPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const imagePlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const markdownPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const pdfPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const pptxPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const rtfPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const sourceCodePlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const svgPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const textPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const videoPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const xlsxPlugin: PreviewPlugin;
6
7
 
@@ -1,6 +1,7 @@
1
1
  import { PreviewPlugin } from '../core/plugin.js';
2
2
  import 'react';
3
3
  import '../core/types.js';
4
+ import '../core/preview-error.js';
4
5
 
5
6
  declare const zipPlugin: PreviewPlugin;
6
7