@imgly/plugin-print-ready-pdfs-web 1.1.2 → 1.68.0-rc.2

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.mjs CHANGED
@@ -1,14 +1,6 @@
1
- var j=Object.defineProperty;var F=(r,t)=>()=>(r&&(t=r(r=0)),t);var B=(r,t)=>{for(var e in t)j(r,e,{get:t[e],enumerable:!0})};function S(r){let t=r.endsWith("/")?r:r+"/";return t.startsWith("http://")||t.startsWith("https://")?t:(typeof document<"u"&&document.location?.origin||"")+t}var T=F(()=>{"use strict"});var L={};B(L,{BrowserAssetLoader:()=>g});var g,h=F(()=>{"use strict";T();g=class{baseUrl;constructor(t){this.baseUrl=S(t)}async loadGhostscriptModule(){let e=await import(new URL("gs.js",this.baseUrl).href);return e.default||e}getWasmPath(){return new URL("gs.wasm",this.baseUrl).href}async loadICCProfile(t){let e=new URL(t,this.baseUrl).href,o=await fetch(e);if(!o.ok)throw new Error(`Failed to load ICC profile ${t}: ${o.status} ${o.statusText}`);return o.blob()}}});var O={};B(O,{NodeAssetLoader:()=>w});var w,E=F(()=>{"use strict";w=class{moduleDir=null;gsPath=null;initPromise=null;constructor(){this.initPromise=this.initialize()}async initialize(){let t=await import("module"),e=await import("path"),i=(t.createRequire||t.default?.createRequire)(import.meta.url);this.gsPath=i.resolve("./gs.js");let n=e.dirname||e.default?.dirname;this.moduleDir=n(this.gsPath)}async ensureInitialized(){this.initPromise&&(await this.initPromise,this.initPromise=null)}async loadGhostscriptModule(){await this.ensureInitialized();let t=await import(this.gsPath);return t.default||t}getWasmPath(){if(!this.moduleDir)throw new Error("NodeAssetLoader not initialized. Call loadGhostscriptModule() first and await its completion.");return`${this.moduleDir}/gs.wasm`}async loadICCProfile(t){await this.ensureInitialized();let e=await import("fs"),o=await import("path"),i=o.join||o.default?.join,n=e.readFileSync||e.default?.readFileSync,s=i(this.moduleDir,t),a=n(s);return new Blob([a],{type:"application/vnd.iccprofile"})}}});var c=class r{constructor(t){this.component=t}static globalLogLevel="warn";static logs=[];static maxLogs=1e3;static setLogLevel(t){r.globalLogLevel=t}static getLogs(){return[...r.logs]}static clearLogs(){r.logs=[]}debug(t,e){this.log("debug",t,e)}info(t,e){this.log("info",t,e)}warn(t,e){this.log("warn",t,e)}error(t,e){this.log("error",t,e)}log(t,e,o){let i={debug:0,info:1,warn:2,error:3};if(i[t]<i[r.globalLogLevel])return;let n={timestamp:Date.now(),level:t,component:this.component,message:e,data:o};r.logs.push(n),r.logs.length>r.maxLogs&&(r.logs=r.logs.slice(-r.maxLogs));let s=`[${new Date().toISOString()}] [${t.toUpperCase()}] [${this.component}]`,a=o?`${e} ${JSON.stringify(o)}`:e;switch(t){case"debug":console.debug(s,a);break;case"info":console.info(s,a);break;case"warn":console.warn(s,a);break;case"error":console.error(s,a);break;default:break}}};var p=class{supportsWebAssembly(){try{return typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"}catch{return!1}}};async function v(r){let{silent:t=!0,assetLoader:e}=r,o=await e.loadGhostscriptModule(),i=e.getWasmPath(),n={locateFile:a=>a==="gs.wasm"?i:a};return t&&(n.print=()=>{},n.printErr=()=>{}),await o(n)}var f=class{static instance=null;static TIMEOUT_MS=3e4;static async load(t){return this.instance?this.instance:(this.instance=this.loadInternal(t),this.instance)}static async loadInternal(t){let e=new c("GhostscriptLoader");if(!new p().supportsWebAssembly())throw new Error("WebAssembly not supported in this browser");let i=t.timeout||this.TIMEOUT_MS;try{return e.info("Loading bundled Ghostscript (AGPL-3.0 licensed)"),e.info("Source available at: https://github.com/imgly/plugins"),await this.loadWithTimeout(()=>this.loadFromBundle(t.assetLoader),i)}catch(n){throw e.error("Ghostscript loading failed",{error:n.message}),new Error(`Failed to load Ghostscript: ${n.message}`)}}static async loadFromBundle(t){let e=new c("GhostscriptInit");try{e.info("Initializing Ghostscript module");let o=await v({assetLoader:t});return e.info("Ghostscript module initialized successfully",{version:o.version||"unknown",hasFS:!!o.FS,hasCallMain:!!o.callMain}),o}catch(o){throw e.error("Ghostscript initialization failed",{error:o}),new Error(`Ghostscript initialization failed: ${o.message}`)}}static async loadWithTimeout(t,e){return Promise.race([t(),new Promise((o,i)=>{setTimeout(()=>i(new Error(`Loading timeout after ${e}ms`)),e)})])}static reset(){this.instance=null}};var u=class{static async toUint8Array(t){if(t.arrayBuffer){let e=await t.arrayBuffer();return new Uint8Array(e)}return new Promise((e,o)=>{let i=new FileReader;i.onload=()=>{i.result instanceof ArrayBuffer?e(new Uint8Array(i.result)):o(new Error("Failed to read blob as ArrayBuffer"))},i.onerror=()=>o(i.error),i.readAsArrayBuffer(t)})}static async validatePDF(t){try{let e=await this.toUint8Array(t),o=new Uint8Array([37,80,68,70]);if(e.length<4)return!1;for(let i=0;i<4;i++)if(e[i]!==o[i])return!1;return!0}catch{return!1}}};var m=class{constructor(t,e="/tmp/pdfx"){this.module=t;this.fs=t.FS,this.logger=new c("VirtualFileSystem"),this.workingDir=e,this.initialize()}fs;logger;managedFiles=new Set;workingDir;initialize(){try{this.createDirIfNotExists(this.workingDir),this.createDirIfNotExists(`${this.workingDir}/input`),this.createDirIfNotExists(`${this.workingDir}/output`),this.createDirIfNotExists(`${this.workingDir}/profiles`),this.createDirIfNotExists(`${this.workingDir}/temp`),this.logger.info("Virtual filesystem initialized",{workingDir:this.workingDir})}catch(t){throw this.logger.error("Failed to initialize virtual filesystem",{error:t}),new Error(`VFS initialization failed: ${t}`)}}createDirIfNotExists(t){try{this.fs.mkdir(t)}catch(e){if(e.code!=="EEXIST")throw e}}async writeBlob(t,e,o=!0){try{let i=await u.toUint8Array(e);this.fs.writeFile(t,i),o&&this.managedFiles.add(t),this.logger.debug("File written",{path:t,size:i.length})}catch(i){throw this.logger.error("Failed to write blob",{path:t,error:i}),new Error(`Failed to write file ${t}: ${i}`)}}writeText(t,e,o=!0){try{this.fs.writeFile(t,e),o&&this.managedFiles.add(t),this.logger.debug("Text file written",{path:t,length:e.length})}catch(i){throw this.logger.error("Failed to write text",{path:t,error:i}),new Error(`Failed to write text file ${t}: ${i}`)}}readFile(t){try{let e=this.fs.readFile(t);return this.logger.debug("File read",{path:t,size:e.length}),e}catch(e){throw this.logger.error("Failed to read file",{path:t,error:e}),new Error(`Failed to read file ${t}: ${e}`)}}exists(t){try{return this.fs.stat(t),!0}catch{return!1}}cleanup(){let t=0;for(let e of this.managedFiles)try{this.fs.unlink(e),t++}catch(o){this.logger.warn("Failed to cleanup file",{path:e,error:o})}this.managedFiles.clear(),this.logger.info("Filesystem cleanup completed",{cleanedCount:t})}};function q(){try{let r=import.meta.url;if(!r||r.startsWith("file://"))return null;let t=r.lastIndexOf("/");return t===-1?null:r.substring(0,t+1)}catch{return null}}async function V(r){if(r.assetLoader)return r.assetLoader;if(r.assetPath){let{BrowserAssetLoader:o}=await Promise.resolve().then(()=>(h(),L));return new o(r.assetPath)}if(typeof window<"u"||typeof document<"u"){let o=q();if(o){let{BrowserAssetLoader:i}=await Promise.resolve().then(()=>(h(),L));return new i(o)}throw new Error(`In browser environments with Webpack 5 or Angular, you must provide the \`assetPath\` option.
2
-
3
- Example:
4
- convertToPDFX3(blob, {
5
- outputProfile: 'fogra39',
6
- assetPath: '/assets/print-ready-pdfs/'
7
- });
8
-
9
- See: https://github.com/imgly/plugins/tree/main/packages/plugin-print-ready-pdfs-web#bundler-setup-webpack-5--angular`)}let{NodeAssetLoader:e}=await Promise.resolve().then(()=>(E(),O));return new e}async function K(r,t){if(Array.isArray(r)){if(r.length===0)return[];let e=[];for(let o=0;o<r.length;o++)try{let i=await M(r[o],t);e.push(i)}catch(i){throw new Error(`Failed to convert PDF ${o+1} of ${r.length}: ${i instanceof Error?i.message:String(i)}`)}return e}return M(r,t)}async function M(r,t){if(!(r instanceof Blob))throw new Error("Input must be a Blob");if(r.size===0)throw new Error("Input PDF is empty");if(r.size>100*1024*1024)throw new Error(`Input PDF too large (${r.size} bytes). Maximum: 100MB`);if(!t.outputProfile)throw new Error('outputProfile is required. Must be one of: "gracol", "fogra39", "srgb", "custom"');let e=["gracol","fogra39","srgb","custom"];if(!e.includes(t.outputProfile))throw new Error(`Invalid outputProfile "${t.outputProfile}". Must be one of: ${e.join(", ")}`);if(t.outputProfile==="custom"&&!t.customProfile)throw new Error('customProfile Blob is required when outputProfile is "custom"');if(t.customProfile&&!(t.customProfile instanceof Blob))throw new Error("customProfile must be a Blob");if(!await u.validatePDF(r))throw new Error("Invalid PDF format");let i=await V(t),n=await f.load({assetLoader:i}),s=new m(n);try{let a="/tmp/input.pdf",D="/tmp/output.pdf",C="/tmp/pdfx_def.ps",x="/tmp/custom.icc";if(await s.writeBlob(a,r),t.outputProfile==="custom"&&t.customProfile)await s.writeBlob(x,t.customProfile);else if(t.outputProfile!=="custom"){let l=k[t.outputProfile],N=`/tmp/${l.file}`,_=await i.loadICCProfile(l.file);await s.writeBlob(N,_)}let y,P,b;if(t.outputProfile==="custom")y=x,P=t.outputConditionIdentifier||"Custom Profile",b=t.outputCondition||"Custom ICC Profile";else{let l=k[t.outputProfile];y=`/tmp/${l.file}`,P=t.outputConditionIdentifier||l.identifier,b=t.outputCondition||l.info}let I=s.readFile(y),G=Array.from(I).map(l=>l.toString(16).padStart(2,"0")).join(""),$=I.slice(16,20),U=String.fromCharCode(...$).trim()==="CMYK",X=Y(t,G,P,b);s.writeText(C,X);let d=["-dBATCH","-dNOPAUSE","-sDEVICE=pdfwrite","-dCompatibilityLevel=1.4","-dWriteObjStms=false","-dWriteXRefStm=false","-dPDFSETTINGS=/prepress","-dSetPageSize=false","-dAutoRotatePages=/None"];t.flattenTransparency!==!1&&d.push("-dHaveTransparency=false"),U?d.push("-sColorConversionStrategy=CMYK","-dProcessColorModel=/DeviceCMYK","-dConvertCMYKImagesToRGB=false"):d.push("-sColorConversionStrategy=RGB"),d.push(`-sOutputFile=${D}`,C,a);let A=await n.callMain(d);if(A!==0)throw new Error(`Ghostscript conversion failed with exit code ${A}`);let R=s.readFile(D),z=new Uint8Array(R),W=new Blob([z],{type:"application/pdf"});return s.cleanup(),W}catch(a){throw s.cleanup(),a}}var k={gracol:{file:"GRACoL2013_CRPC6.icc",identifier:"CGATS 21.2",info:"GRACoL 2013 CRPC6"},fogra39:{file:"ISOcoated_v2_eci.icc",identifier:"FOGRA39",info:"ISO Coated v2 (ECI)"},srgb:{file:"sRGB_IEC61966-2-1.icc",identifier:"sRGB IEC61966-2.1",info:"sRGB IEC61966-2.1"}};function Y(r,t,e,o){return`%!
1
+ var f=class o{constructor(t){this.component=t}static globalLogLevel="warn";static logs=[];static maxLogs=1e3;static setLogLevel(t){o.globalLogLevel=t}static getLogs(){return[...o.logs]}static clearLogs(){o.logs=[]}debug(t,e){this.log("debug",t,e)}info(t,e){this.log("info",t,e)}warn(t,e){this.log("warn",t,e)}error(t,e){this.log("error",t,e)}log(t,e,i){let r={debug:0,info:1,warn:2,error:3};if(r[t]<r[o.globalLogLevel])return;let s={timestamp:Date.now(),level:t,component:this.component,message:e,data:i};o.logs.push(s),o.logs.length>o.maxLogs&&(o.logs=o.logs.slice(-o.maxLogs));let n=`[${new Date().toISOString()}] [${t.toUpperCase()}] [${this.component}]`,l=i?`${e} ${JSON.stringify(i)}`:e;switch(t){case"debug":console.debug(n,l);break;case"info":console.info(n,l);break;case"warn":console.warn(n,l);break;case"error":console.error(n,l);break;default:break}}};var F=class{supportsWebAssembly(){try{return typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"}catch{return!1}}};async function L(o={}){let{silent:t=!0}=o,e=typeof process<"u"&&process.versions!=null&&process.versions.node!=null,i,r;if(e){let c=typeof process<"u"&&(process.env.VITEST==="true"||!1),w=new Function("s","return import(s)"),u=c?p=>import(p):w,g=await u("module"),d=await u("path"),y=g.createRequire,v=d.dirname,D=d.join,P=y(import.meta.url).resolve("./gs.js"),C=v(P);r=D(C,"gs.wasm"),i=await u(P)}else i=await import(new URL("./gs.js",import.meta.url).href),r=new URL("./gs.wasm",import.meta.url).href;let s=i.default||i,n={locateFile:c=>c==="gs.wasm"?r:c};return t&&(n.print=()=>{},n.printErr=()=>{}),await s(n)}var b=class{static instance=null;static TIMEOUT_MS=3e4;static async load(t={}){return this.instance?this.instance:(this.instance=this.loadInternal(t),this.instance)}static async loadInternal(t){let e=new f("GhostscriptLoader");if(!new F().supportsWebAssembly())throw new Error("WebAssembly not supported in this browser");let r=t.timeout||this.TIMEOUT_MS;try{return e.info("Loading bundled Ghostscript (AGPL-3.0 licensed)"),e.info("Source available at: https://img.ly/docs/cesdk"),await this.loadWithTimeout(()=>this.loadFromBundle(),r)}catch(s){throw e.error("Ghostscript loading failed",{error:s.message}),new Error(`Failed to load Ghostscript: ${s.message}`)}}static async loadFromBundle(){let t=new f("GhostscriptInit");try{t.info("Initializing Ghostscript module");let e=await L();return t.info("Ghostscript module initialized successfully",{version:e.version||"unknown",hasFS:!!e.FS,hasCallMain:!!e.callMain}),e}catch(e){throw t.error("Ghostscript initialization failed",{error:e}),new Error(`Ghostscript initialization failed: ${e.message}`)}}static async loadWithTimeout(t,e){return Promise.race([t(),new Promise((i,r)=>{setTimeout(()=>r(new Error(`Loading timeout after ${e}ms`)),e)})])}static reset(){this.instance=null}};var m=class{static async toUint8Array(t){if(t.arrayBuffer){let e=await t.arrayBuffer();return new Uint8Array(e)}return new Promise((e,i)=>{let r=new FileReader;r.onload=()=>{r.result instanceof ArrayBuffer?e(new Uint8Array(r.result)):i(new Error("Failed to read blob as ArrayBuffer"))},r.onerror=()=>i(r.error),r.readAsArrayBuffer(t)})}static async validatePDF(t){try{let e=await this.toUint8Array(t),i=new Uint8Array([37,80,68,70]);if(e.length<4)return!1;for(let r=0;r<4;r++)if(e[r]!==i[r])return!1;return!0}catch{return!1}}};var E=class{constructor(t,e="/tmp/pdfx"){this.module=t;this.fs=t.FS,this.logger=new f("VirtualFileSystem"),this.workingDir=e,this.initialize()}fs;logger;managedFiles=new Set;workingDir;initialize(){try{this.createDirIfNotExists(this.workingDir),this.createDirIfNotExists(`${this.workingDir}/input`),this.createDirIfNotExists(`${this.workingDir}/output`),this.createDirIfNotExists(`${this.workingDir}/profiles`),this.createDirIfNotExists(`${this.workingDir}/temp`),this.logger.info("Virtual filesystem initialized",{workingDir:this.workingDir})}catch(t){throw this.logger.error("Failed to initialize virtual filesystem",{error:t}),new Error(`VFS initialization failed: ${t}`)}}createDirIfNotExists(t){try{this.fs.mkdir(t)}catch(e){if(e.code!=="EEXIST")throw e}}async writeBlob(t,e,i=!0){try{let r=await m.toUint8Array(e);this.fs.writeFile(t,r),i&&this.managedFiles.add(t),this.logger.debug("File written",{path:t,size:r.length})}catch(r){throw this.logger.error("Failed to write blob",{path:t,error:r}),new Error(`Failed to write file ${t}: ${r}`)}}writeText(t,e,i=!0){try{this.fs.writeFile(t,e),i&&this.managedFiles.add(t),this.logger.debug("Text file written",{path:t,length:e.length})}catch(r){throw this.logger.error("Failed to write text",{path:t,error:r}),new Error(`Failed to write text file ${t}: ${r}`)}}readFile(t){try{let e=this.fs.readFile(t);return this.logger.debug("File read",{path:t,size:e.length}),e}catch(e){throw this.logger.error("Failed to read file",{path:t,error:e}),new Error(`Failed to read file ${t}: ${e}`)}}exists(t){try{return this.fs.stat(t),!0}catch{return!1}}cleanup(){let t=0;for(let e of this.managedFiles)try{this.fs.unlink(e),t++}catch(i){this.logger.warn("Failed to cleanup file",{path:e,error:i})}this.managedFiles.clear(),this.logger.info("Filesystem cleanup completed",{cleanedCount:t})}};async function q(o,t){if(Array.isArray(o)){if(o.length===0)return[];let e=[];for(let i=0;i<o.length;i++)try{let r=await B(o[i],t);e.push(r)}catch(r){throw new Error(`Failed to convert PDF ${i+1} of ${o.length}: ${r instanceof Error?r.message:String(r)}`)}return e}return B(o,t)}async function B(o,t){if(!(o instanceof Blob))throw new Error("Input must be a Blob");if(o.size===0)throw new Error("Input PDF is empty");if(o.size>100*1024*1024)throw new Error(`Input PDF too large (${o.size} bytes). Maximum: 100MB`);if(!t.outputProfile)throw new Error('outputProfile is required. Must be one of: "gracol", "fogra39", "srgb", "custom"');let e=["gracol","fogra39","srgb","custom"];if(!e.includes(t.outputProfile))throw new Error(`Invalid outputProfile "${t.outputProfile}". Must be one of: ${e.join(", ")}`);if(t.outputProfile==="custom"&&!t.customProfile)throw new Error('customProfile Blob is required when outputProfile is "custom"');if(t.customProfile&&!(t.customProfile instanceof Blob))throw new Error("customProfile must be a Blob");if(!await m.validatePDF(o))throw new Error("Invalid PDF format");let r=await b.load(),s=new E(r);try{let n="/tmp/input.pdf",l="/tmp/output.pdf",c="/tmp/pdfx_def.ps",w="/tmp/custom.icc";if(await s.writeBlob(n,o),t.outputProfile==="custom"&&t.customProfile)await s.writeBlob(w,t.customProfile);else if(t.outputProfile!=="custom"){let a=O[t.outputProfile],G=`/tmp/${a.file}`,X=typeof process<"u"&&process.versions?.node!=null,I;if(X){let S=process.env.VITEST==="true"||!1,h=new Function("s","return import(s)"),x=S?W=>import(W):h,{readFileSync:R}=await x("fs"),{fileURLToPath:U}=await x("url"),{dirname:N,join:z}=await x("path"),_=N(U(import.meta.url)),V=z(_,a.file),j=R(V);I=new Blob([j],{type:"application/vnd.iccprofile"})}else{let S=new URL(a.file,import.meta.url).href,h=await fetch(S);if(!h.ok)throw new Error(`Failed to load ICC profile ${a.file}: ${h.statusText}`);I=await h.blob()}await s.writeBlob(G,I)}let u,g,d;if(t.outputProfile==="custom")u=w,g=t.outputConditionIdentifier||"Custom Profile",d=t.outputCondition||"Custom ICC Profile";else{let a=O[t.outputProfile];u=`/tmp/${a.file}`,g=t.outputConditionIdentifier||a.identifier,d=t.outputCondition||a.info}let y=s.readFile(u),v=Array.from(y).map(a=>a.toString(16).padStart(2,"0")).join(""),D=y.slice(16,20),P=String.fromCharCode(...D).trim()==="CMYK",C=K(t,v,g,d);s.writeText(c,C);let p=["-dBATCH","-dNOPAUSE","-sDEVICE=pdfwrite","-dCompatibilityLevel=1.4","-dWriteObjStms=false","-dWriteXRefStm=false","-dPDFSETTINGS=/prepress","-dSetPageSize=false","-dAutoRotatePages=/None"];t.flattenTransparency!==!1&&p.push("-dHaveTransparency=false"),P?p.push("-sColorConversionStrategy=CMYK","-dProcessColorModel=/DeviceCMYK","-dConvertCMYKImagesToRGB=false"):p.push("-sColorConversionStrategy=RGB"),p.push(`-sOutputFile=${l}`,c,n);let T=await r.callMain(p);if(T!==0)throw new Error(`Ghostscript conversion failed with exit code ${T}`);let $=s.readFile(l),A=new Uint8Array($),k=new Blob([A],{type:"application/pdf"});return s.cleanup(),k}catch(n){throw s.cleanup(),n}}var O={gracol:{file:"GRACoL2013_CRPC6.icc",identifier:"CGATS 21.2",info:"GRACoL 2013 CRPC6"},fogra39:{file:"ISOcoated_v2_eci.icc",identifier:"FOGRA39",info:"ISO Coated v2 (ECI)"},srgb:{file:"sRGB_IEC61966-2-1.icc",identifier:"sRGB IEC61966-2.1",info:"sRGB IEC61966-2.1"}};function K(o,t,e,i){return`%!
10
2
  % PDF/X-3 Definition File
11
- [ /Title (${r.title||"Untitled"}) /DOCINFO pdfmark
3
+ [ /Title (${o.title||"Untitled"}) /DOCINFO pdfmark
12
4
  [ /Trapped /False /DOCINFO pdfmark
13
5
 
14
6
  % Set PDF/X-3 conformance
@@ -27,12 +19,12 @@ See: https://github.com/imgly/plugins/tree/main/packages/plugin-print-ready-pdfs
27
19
  [{OutputIntent_PDFX} <<
28
20
  /Type /OutputIntent
29
21
  /S /GTS_PDFX
30
- /OutputCondition (${o})
22
+ /OutputCondition (${i})
31
23
  /OutputConditionIdentifier (${e})
32
24
  /RegistryName (http://www.color.org)
33
25
  /DestOutputProfile {icc_PDFX}
34
26
  >> /PUT pdfmark
35
27
 
36
28
  % Add OutputIntent to Catalog
37
- [{Catalog} <</OutputIntents [{OutputIntent_PDFX}]>> /PUT pdfmark`}h();E();export{g as BrowserAssetLoader,c as Logger,w as NodeAssetLoader,K as convertToPDFX3};
29
+ [{Catalog} <</OutputIntents [{OutputIntent_PDFX}]>> /PUT pdfmark`}export{f as Logger,q as convertToPDFX3};
38
30
  //# sourceMappingURL=index.mjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/utils/asset-path.ts", "../src/loaders/browser-loader.ts", "../src/loaders/node-loader.ts", "../src/utils/logger.ts", "../src/utils/browser-detection.ts", "../src/wasm/ghostscript-module.ts", "../src/core/ghostscript-loader.ts", "../src/utils/blob-utils.ts", "../src/core/virtual-filesystem.ts", "../src/pdfx.ts", "../src/index.ts"],
4
- "sourcesContent": ["/**\n * Asset path utilities for resolving plugin assets in different environments.\n * Handles the Webpack 5 / Angular CLI issue where import.meta.url is transformed\n * to a file:// URL that doesn't work in browsers.\n */\n\n/**\n * Normalizes an asset path to ensure it has a trailing slash and is a valid URL.\n * Converts relative paths (e.g., '/assets/wasm/') to absolute URLs using the current origin.\n */\nexport function normalizeAssetPath(path: string): string {\n const normalizedPath = path.endsWith('/') ? path : path + '/';\n\n // If it's already an absolute URL, return as-is\n if (\n normalizedPath.startsWith('http://') ||\n normalizedPath.startsWith('https://')\n ) {\n return normalizedPath;\n }\n\n // Convert relative path to absolute URL using document.location.origin\n const origin =\n typeof document !== 'undefined' ? document.location?.origin || '' : '';\n\n return origin + normalizedPath;\n}\n\n/**\n * Resolves the base URL for loading plugin assets in browser environments.\n * Throws a helpful error if assetPath is required but not provided.\n *\n * @param assetPath - Explicit asset path provided by the user\n * @param currentModuleUrl - The import.meta.url of the calling module\n */\nexport function resolveAssetBasePath(\n assetPath: string | undefined,\n currentModuleUrl: string\n): string {\n // 1. Explicit assetPath always wins\n if (assetPath) {\n return normalizeAssetPath(assetPath);\n }\n\n // 2. Try import.meta.url (works in Vite, native ESM)\n if (!currentModuleUrl.startsWith('file://')) {\n // Valid browser URL - use it directly\n return new URL('./', currentModuleUrl).href;\n }\n\n // 3. Bundled environment (Webpack 5 transforms to file://)\n // assetPath is required - throw helpful error\n throw new Error(\n `Could not locate plugin assets. The assetPath option is required.\\n\\n` +\n `This typically happens when using a bundler (like Webpack 5 or Angular CLI) ` +\n `that transforms import.meta.url to a file:// URL.\\n\\n` +\n `To fix this, copy the plugin assets to your public folder and provide the assetPath option:\\n\\n` +\n `Option A: Configure your bundler to copy assets automatically\\n\\n` +\n ` Angular CLI - add to angular.json \"assets\" array:\\n` +\n ` { \"glob\": \"{gs.js,gs.wasm,*.icc}\", \"input\": \"node_modules/@imgly/plugin-print-ready-pdfs-web/dist\", \"output\": \"/assets/\" }\\n\\n` +\n ` Webpack - use copy-webpack-plugin:\\n` +\n ` new CopyPlugin({ patterns: [{ from: \"node_modules/@imgly/plugin-print-ready-pdfs-web/dist/*.{js,wasm,icc}\", to: \"assets/[name][ext]\" }] })\\n\\n` +\n `Option B: Copy manually\\n\\n` +\n ` cp node_modules/@imgly/plugin-print-ready-pdfs-web/dist/{gs.js,gs.wasm,*.icc} public/assets/\\n\\n` +\n `Then pass the assetPath option:\\n\\n` +\n ` convertToPDFX3(blob, {\\n` +\n ` outputProfile: 'fogra39',\\n` +\n ` assetPath: '/assets/' // adjust to match your output path\\n` +\n ` });\\n\\n` +\n `See: https://github.com/imgly/plugins/tree/main/packages/plugin-print-ready-pdfs-web#bundler-setup-webpack-5--angular`\n );\n}\n", "import type { AssetLoader } from '../types/asset-loader';\nimport type { GhostscriptModuleFactory } from '../types/ghostscript';\nimport { normalizeAssetPath } from '../utils/asset-path';\n\n/**\n * Browser-specific asset loader implementation.\n * Loads assets via HTTP fetch and dynamic import.\n *\n * @example\n * ```typescript\n * const loader = new BrowserAssetLoader('/assets/print-ready-pdfs/');\n * const result = await convertToPDFX3(pdfBlob, {\n * outputProfile: 'fogra39',\n * assetLoader: loader\n * });\n * ```\n */\nexport class BrowserAssetLoader implements AssetLoader {\n private baseUrl: string;\n\n /**\n * Create a new BrowserAssetLoader.\n * @param assetPath - Base URL path where plugin assets are served from.\n * Must be an absolute path (e.g., '/assets/') or full URL.\n */\n constructor(assetPath: string) {\n this.baseUrl = normalizeAssetPath(assetPath);\n }\n\n async loadGhostscriptModule(): Promise<GhostscriptModuleFactory> {\n const moduleUrl = new URL('gs.js', this.baseUrl).href;\n // webpackIgnore comment prevents Webpack from trying to bundle this dynamic import\n // The URL is determined at runtime based on assetPath configuration\n const module = await import(/* webpackIgnore: true */ moduleUrl);\n return module.default || module;\n }\n\n getWasmPath(): string {\n return new URL('gs.wasm', this.baseUrl).href;\n }\n\n async loadICCProfile(profileName: string): Promise<Blob> {\n const profileUrl = new URL(profileName, this.baseUrl).href;\n const response = await fetch(profileUrl);\n if (!response.ok) {\n throw new Error(\n `Failed to load ICC profile ${profileName}: ${response.status} ${response.statusText}`\n );\n }\n return response.blob();\n }\n}\n", "import type { AssetLoader } from '../types/asset-loader';\nimport type { GhostscriptModuleFactory } from '../types/ghostscript';\n\n/**\n * Node.js-specific asset loader implementation.\n * Loads assets from the filesystem relative to the module's location.\n *\n * This loader automatically finds assets relative to the built module,\n * so no configuration is needed in Node.js environments.\n *\n * @example\n * ```typescript\n * // In Node.js, the loader is used automatically when no assetPath is provided\n * const result = await convertToPDFX3(pdfBlob, {\n * outputProfile: 'fogra39'\n * });\n *\n * // Or explicitly:\n * const loader = new NodeAssetLoader();\n * const result = await convertToPDFX3(pdfBlob, {\n * outputProfile: 'fogra39',\n * assetLoader: loader\n * });\n * ```\n */\nexport class NodeAssetLoader implements AssetLoader {\n private moduleDir: string | null = null;\n private gsPath: string | null = null;\n private initPromise: Promise<void> | null = null;\n\n constructor() {\n // Initialization is deferred to first use because we need async imports\n this.initPromise = this.initialize();\n }\n\n private async initialize(): Promise<void> {\n // Dynamic imports for Node.js built-in modules\n // These use /* webpackIgnore: true */ comments to prevent bundlers from processing them\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const moduleLib: any = await import(/* webpackIgnore: true */ 'module');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pathLib: any = await import(/* webpackIgnore: true */ 'path');\n\n const createRequire = moduleLib.createRequire || moduleLib.default?.createRequire;\n const requireForESM = createRequire(import.meta.url);\n // gs.js is copied to dist/ alongside the bundle\n this.gsPath = requireForESM.resolve('./gs.js');\n\n const dirname = pathLib.dirname || pathLib.default?.dirname;\n this.moduleDir = dirname(this.gsPath);\n }\n\n private async ensureInitialized(): Promise<void> {\n if (this.initPromise) {\n await this.initPromise;\n this.initPromise = null;\n }\n }\n\n async loadGhostscriptModule(): Promise<GhostscriptModuleFactory> {\n await this.ensureInitialized();\n // Dynamic import for ESM compatibility\n const module = await import(this.gsPath!);\n return module.default || module;\n }\n\n /**\n * Get the path to the WASM file.\n *\n * **Important:** This method must be called after `loadGhostscriptModule()` has completed,\n * as initialization happens lazily during that call. The library's internal usage\n * guarantees this ordering, but if you're using `NodeAssetLoader` directly, ensure\n * you call `loadGhostscriptModule()` first.\n *\n * @throws Error if called before initialization completes\n */\n getWasmPath(): string {\n if (!this.moduleDir) {\n throw new Error(\n 'NodeAssetLoader not initialized. Call loadGhostscriptModule() first and await its completion.'\n );\n }\n return `${this.moduleDir}/gs.wasm`;\n }\n\n async loadICCProfile(profileName: string): Promise<Blob> {\n await this.ensureInitialized();\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fsLib: any = await import(/* webpackIgnore: true */ 'fs');\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pathLib: any = await import(/* webpackIgnore: true */ 'path');\n\n const join = pathLib.join || pathLib.default?.join;\n const readFileSync = fsLib.readFileSync || fsLib.default?.readFileSync;\n\n const profilePath = join(this.moduleDir!, profileName);\n const data = readFileSync(profilePath);\n return new Blob([data], { type: 'application/vnd.iccprofile' });\n }\n}\n", "/* eslint-disable no-console */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface LogEntry {\n timestamp: number;\n level: LogLevel;\n component: string;\n message: string;\n data?: any;\n}\n\nexport class Logger {\n private static globalLogLevel: LogLevel = 'warn';\n\n private static logs: LogEntry[] = [];\n\n private static maxLogs = 1000;\n\n constructor(private component: string) {}\n\n static setLogLevel(level: LogLevel): void {\n Logger.globalLogLevel = level;\n }\n\n static getLogs(): LogEntry[] {\n return [...Logger.logs];\n }\n\n static clearLogs(): void {\n Logger.logs = [];\n }\n\n debug(message: string, data?: any): void {\n this.log('debug', message, data);\n }\n\n info(message: string, data?: any): void {\n this.log('info', message, data);\n }\n\n warn(message: string, data?: any): void {\n this.log('warn', message, data);\n }\n\n error(message: string, data?: any): void {\n this.log('error', message, data);\n }\n\n private log(level: LogLevel, message: string, data?: any): void {\n const levelOrder: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n };\n\n if (levelOrder[level] < levelOrder[Logger.globalLogLevel]) {\n return;\n }\n\n const entry: LogEntry = {\n timestamp: Date.now(),\n level,\n component: this.component,\n message,\n data,\n };\n\n Logger.logs.push(entry);\n\n // Keep logs under the limit\n if (Logger.logs.length > Logger.maxLogs) {\n Logger.logs = Logger.logs.slice(-Logger.maxLogs);\n }\n\n // Console output\n const prefix = `[${new Date().toISOString()}] [${level.toUpperCase()}] [${\n this.component\n }]`;\n const logMessage = data ? `${message} ${JSON.stringify(data)}` : message;\n\n switch (level) {\n case 'debug':\n console.debug(prefix, logMessage);\n break;\n case 'info':\n console.info(prefix, logMessage);\n break;\n case 'warn':\n console.warn(prefix, logMessage);\n break;\n case 'error':\n console.error(prefix, logMessage);\n break;\n default:\n // All log levels handled above\n break;\n }\n }\n}\n", "export class BrowserDetection {\n supportsWebAssembly(): boolean {\n try {\n return (\n typeof WebAssembly === 'object' &&\n typeof WebAssembly.instantiate === 'function'\n );\n } catch {\n return false;\n }\n }\n}\n", "// Wrapper for the Ghostscript WASM module to ensure proper loading\nimport type {\n EmscriptenModule,\n GhostscriptModuleFactory,\n} from '../types/ghostscript';\nimport type { AssetLoader } from '../types/asset-loader';\n\nexport interface GhostscriptModuleOptions {\n /**\n * Whether to suppress Ghostscript's stdout/stderr output.\n * Default: true (silent mode)\n */\n silent?: boolean;\n\n /**\n * Asset loader for loading gs.js and gs.wasm.\n * Required - use BrowserAssetLoader or NodeAssetLoader.\n */\n assetLoader: AssetLoader;\n}\n\nexport default async function createGhostscriptModule(\n options: GhostscriptModuleOptions\n): Promise<EmscriptenModule> {\n const { silent = true, assetLoader } = options;\n\n // Load the Ghostscript module using the provided loader\n const factory = await assetLoader.loadGhostscriptModule();\n const wasmPath = assetLoader.getWasmPath();\n\n // Configure the module to load WASM from the specified location\n const moduleConfig: Record<string, unknown> = {\n locateFile: (filename: string) => {\n if (filename === 'gs.wasm') {\n return wasmPath;\n }\n return filename;\n },\n };\n\n // Suppress Ghostscript stdout/stderr output in silent mode\n if (silent) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n moduleConfig.print = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n moduleConfig.printErr = () => {};\n }\n\n const module = await factory(moduleConfig);\n\n return module;\n}\n", "import type { EmscriptenModule } from '../types/ghostscript';\nimport type { AssetLoader } from '../types/asset-loader';\nimport { Logger } from '../utils/logger';\nimport { BrowserDetection } from '../utils/browser-detection';\nimport createGhostscriptModule from '../wasm/ghostscript-module';\n\nexport interface LoaderOptions {\n timeout?: number;\n /**\n * Asset loader for loading plugin assets.\n * Required - use BrowserAssetLoader or NodeAssetLoader.\n */\n assetLoader: AssetLoader;\n}\n\nexport class GhostscriptLoader {\n private static instance: Promise<EmscriptenModule> | null = null;\n\n private static readonly TIMEOUT_MS = 30000;\n\n static async load(options: LoaderOptions): Promise<EmscriptenModule> {\n // If already loaded, return the cached instance\n // (the WASM module is a singleton and assetLoader should be consistent within an app)\n if (this.instance) {\n return this.instance;\n }\n\n this.instance = this.loadInternal(options);\n return this.instance;\n }\n\n private static async loadInternal(\n options: LoaderOptions\n ): Promise<EmscriptenModule> {\n const logger = new Logger('GhostscriptLoader');\n const browser = new BrowserDetection();\n\n // Check browser compatibility\n if (!browser.supportsWebAssembly()) {\n throw new Error('WebAssembly not supported in this browser');\n }\n\n const timeout = options.timeout || this.TIMEOUT_MS;\n\n try {\n logger.info('Loading bundled Ghostscript (AGPL-3.0 licensed)');\n logger.info('Source available at: https://github.com/imgly/plugins');\n return await this.loadWithTimeout(\n () => this.loadFromBundle(options.assetLoader),\n timeout\n );\n } catch (error) {\n logger.error('Ghostscript loading failed', {\n error: (error as Error).message,\n });\n throw new Error(\n `Failed to load Ghostscript: ${(error as Error).message}`\n );\n }\n }\n\n private static async loadFromBundle(\n assetLoader: AssetLoader\n ): Promise<EmscriptenModule> {\n // Use the bundled WASM module with proper configuration\n const logger = new Logger('GhostscriptInit');\n\n try {\n logger.info('Initializing Ghostscript module');\n\n const module = await createGhostscriptModule({ assetLoader });\n\n logger.info('Ghostscript module initialized successfully', {\n version: module.version || 'unknown',\n hasFS: !!module.FS,\n hasCallMain: !!module.callMain,\n });\n\n return module;\n } catch (error) {\n logger.error('Ghostscript initialization failed', { error });\n throw new Error(\n `Ghostscript initialization failed: ${(error as Error).message}`\n );\n }\n }\n\n private static async loadWithTimeout<T>(\n operation: () => Promise<T>,\n timeoutMs: number\n ): Promise<T> {\n return Promise.race([\n operation(),\n new Promise<never>((_, reject) => {\n setTimeout(\n () => reject(new Error(`Loading timeout after ${timeoutMs}ms`)),\n timeoutMs\n );\n }),\n ]);\n }\n\n static reset(): void {\n this.instance = null;\n }\n}\n", "export class BlobUtils {\n static async toUint8Array(blob: Blob): Promise<Uint8Array> {\n if (blob.arrayBuffer) {\n const buffer = await blob.arrayBuffer();\n return new Uint8Array(buffer);\n }\n\n // Fallback for older browsers\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n resolve(new Uint8Array(reader.result));\n } else {\n reject(new Error('Failed to read blob as ArrayBuffer'));\n }\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n }\n\n static async validatePDF(blob: Blob): Promise<boolean> {\n try {\n const data = await this.toUint8Array(blob);\n\n // Check PDF magic number\n const pdfHeader = new Uint8Array([0x25, 0x50, 0x44, 0x46]); // %PDF\n\n if (data.length < 4) {\n return false;\n }\n\n for (let i = 0; i < 4; i++) {\n if (data[i] !== pdfHeader[i]) {\n return false;\n }\n }\n\n return true;\n } catch {\n return false;\n }\n }\n}\n", "import type { EmscriptenModule, EmscriptenFS } from '../types/ghostscript';\nimport { Logger } from '../utils/logger';\nimport { BlobUtils } from '../utils/blob-utils';\n\nexport interface FileEntry {\n path: string;\n data: Uint8Array;\n cleanup: boolean;\n}\n\nexport class VirtualFileSystem {\n private readonly fs: EmscriptenFS;\n\n private readonly logger: Logger;\n\n private readonly managedFiles: Set<string> = new Set();\n\n private readonly workingDir: string;\n\n constructor(\n private readonly module: EmscriptenModule,\n workingDir = '/tmp/pdfx'\n ) {\n this.fs = module.FS;\n this.logger = new Logger('VirtualFileSystem');\n this.workingDir = workingDir;\n this.initialize();\n }\n\n private initialize(): void {\n try {\n // Create working directory structure (handle existing directories gracefully)\n this.createDirIfNotExists(this.workingDir);\n this.createDirIfNotExists(`${this.workingDir}/input`);\n this.createDirIfNotExists(`${this.workingDir}/output`);\n this.createDirIfNotExists(`${this.workingDir}/profiles`);\n this.createDirIfNotExists(`${this.workingDir}/temp`);\n\n this.logger.info('Virtual filesystem initialized', {\n workingDir: this.workingDir,\n });\n } catch (error) {\n this.logger.error('Failed to initialize virtual filesystem', { error });\n throw new Error(`VFS initialization failed: ${error}`);\n }\n }\n\n private createDirIfNotExists(path: string): void {\n try {\n this.fs.mkdir(path);\n } catch (error: any) {\n // Ignore \"File exists\" errors, re-throw others\n if (error.code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n async writeBlob(path: string, blob: Blob, managed = true): Promise<void> {\n try {\n const data = await BlobUtils.toUint8Array(blob);\n this.fs.writeFile(path, data);\n\n if (managed) {\n this.managedFiles.add(path);\n }\n\n this.logger.debug('File written', { path, size: data.length });\n } catch (error) {\n this.logger.error('Failed to write blob', { path, error });\n throw new Error(`Failed to write file ${path}: ${error}`);\n }\n }\n\n writeText(path: string, content: string, managed = true): void {\n try {\n this.fs.writeFile(path, content);\n\n if (managed) {\n this.managedFiles.add(path);\n }\n\n this.logger.debug('Text file written', { path, length: content.length });\n } catch (error) {\n this.logger.error('Failed to write text', { path, error });\n throw new Error(`Failed to write text file ${path}: ${error}`);\n }\n }\n\n readFile(path: string): Uint8Array {\n try {\n const data = this.fs.readFile(path) as Uint8Array;\n this.logger.debug('File read', { path, size: data.length });\n return data;\n } catch (error) {\n this.logger.error('Failed to read file', { path, error });\n throw new Error(`Failed to read file ${path}: ${error}`);\n }\n }\n\n exists(path: string): boolean {\n try {\n this.fs.stat(path);\n return true;\n } catch {\n return false;\n }\n }\n\n cleanup(): void {\n let cleanedCount = 0;\n\n for (const path of this.managedFiles) {\n try {\n this.fs.unlink(path);\n cleanedCount++;\n } catch (error) {\n this.logger.warn('Failed to cleanup file', { path, error });\n }\n }\n\n this.managedFiles.clear();\n this.logger.info('Filesystem cleanup completed', { cleanedCount });\n }\n}\n", "import type { PDFX3Options, AssetLoader } from './types';\nimport { GhostscriptLoader } from './core/ghostscript-loader';\nimport { VirtualFileSystem } from './core/virtual-filesystem';\nimport { BlobUtils } from './utils/blob-utils';\n\n/**\n * Try to get a usable base URL from import.meta.url.\n * Returns null if the URL is not usable (e.g., file:// protocol in browser).\n */\nfunction tryGetBaseUrlFromImportMeta(): string | null {\n try {\n // import.meta.url points to the bundled JS file location\n // In Vite: \"http://localhost:5173/node_modules/.vite/deps/...\"\n // In Webpack 5: \"file:///path/to/dist/index.mjs\" (not usable in browser)\n const url = import.meta.url;\n if (!url) return null;\n\n // file:// URLs don't work for dynamic imports in browsers\n if (url.startsWith('file://')) return null;\n\n // Extract the directory from the URL\n const lastSlash = url.lastIndexOf('/');\n if (lastSlash === -1) return null;\n\n return url.substring(0, lastSlash + 1);\n } catch {\n return null;\n }\n}\n\n/**\n * Get the appropriate AssetLoader based on options and environment.\n */\nasync function getAssetLoader(options: PDFX3Options): Promise<AssetLoader> {\n // 1. Explicit loader takes precedence\n if (options.assetLoader) {\n return options.assetLoader;\n }\n\n // 2. assetPath creates a BrowserAssetLoader\n if (options.assetPath) {\n const { BrowserAssetLoader } = await import('./loaders/browser-loader');\n return new BrowserAssetLoader(options.assetPath);\n }\n\n // 3. Auto-detect environment\n const isBrowser =\n typeof window !== 'undefined' || typeof document !== 'undefined';\n\n if (isBrowser) {\n // Try to auto-detect base URL from import.meta.url (works in Vite)\n const autoBaseUrl = tryGetBaseUrlFromImportMeta();\n if (autoBaseUrl) {\n const { BrowserAssetLoader } = await import('./loaders/browser-loader');\n return new BrowserAssetLoader(autoBaseUrl);\n }\n\n // Can't auto-detect - require explicit configuration (Webpack 5, Angular, etc.)\n throw new Error(\n 'In browser environments with Webpack 5 or Angular, you must provide the `assetPath` option.\\n\\n' +\n 'Example:\\n' +\n ' convertToPDFX3(blob, {\\n' +\n \" outputProfile: 'fogra39',\\n\" +\n \" assetPath: '/assets/print-ready-pdfs/'\\n\" +\n ' });\\n\\n' +\n 'See: https://github.com/imgly/plugins/tree/main/packages/plugin-print-ready-pdfs-web#bundler-setup-webpack-5--angular'\n );\n }\n\n // Node.js - use NodeAssetLoader\n const { NodeAssetLoader } = await import('./loaders/node-loader');\n return new NodeAssetLoader();\n}\n\n/**\n * PDF/X-3 conversion function\n * Converts RGB PDF(s) to PDF/X-3 format using specified output profile\n *\n * @overload\n * @param inputPDF Single PDF blob to convert\n * @param options Conversion configuration\n * @returns Promise resolving to converted PDF blob\n *\n * @overload\n * @param inputPDFs Array of PDF blobs to convert\n * @param options Conversion configuration\n * @returns Promise resolving to array of converted PDF blobs\n */\nexport function convertToPDFX3(\n inputPDF: Blob,\n options: PDFX3Options\n): Promise<Blob>;\nexport function convertToPDFX3(\n inputPDFs: Blob[],\n options: PDFX3Options\n): Promise<Blob[]>;\nexport async function convertToPDFX3(\n input: Blob | Blob[],\n options: PDFX3Options\n): Promise<Blob | Blob[]> {\n // Handle array input (batch processing)\n if (Array.isArray(input)) {\n if (input.length === 0) {\n return [];\n }\n\n // Process each blob sequentially to avoid overwhelming the WASM module\n const results: Blob[] = [];\n for (let i = 0; i < input.length; i++) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const converted = await convertToPDFX3Single(input[i], options);\n results.push(converted);\n } catch (error) {\n throw new Error(\n `Failed to convert PDF ${i + 1} of ${input.length}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n return results;\n }\n\n // Handle single blob input\n return convertToPDFX3Single(input, options);\n}\n\n/**\n * Internal function to convert a single PDF\n */\nasync function convertToPDFX3Single(\n inputPDF: Blob,\n options: PDFX3Options\n): Promise<Blob> {\n // Validate inputs\n if (!(inputPDF instanceof Blob)) {\n throw new Error('Input must be a Blob');\n }\n\n if (inputPDF.size === 0) {\n throw new Error('Input PDF is empty');\n }\n\n if (inputPDF.size > 100 * 1024 * 1024) {\n throw new Error(\n `Input PDF too large (${inputPDF.size} bytes). Maximum: 100MB`\n );\n }\n\n // Validate output profile is provided\n if (!options.outputProfile) {\n throw new Error(\n 'outputProfile is required. Must be one of: \"gracol\", \"fogra39\", \"srgb\", \"custom\"'\n );\n }\n\n // Validate output profile value\n const validProfiles = ['gracol', 'fogra39', 'srgb', 'custom'];\n if (!validProfiles.includes(options.outputProfile)) {\n throw new Error(\n `Invalid outputProfile \"${\n options.outputProfile\n }\". Must be one of: ${validProfiles.join(', ')}`\n );\n }\n\n // Validate custom profile requirement\n if (options.outputProfile === 'custom' && !options.customProfile) {\n throw new Error(\n 'customProfile Blob is required when outputProfile is \"custom\"'\n );\n }\n\n // Validate custom profile if provided\n if (options.customProfile && !(options.customProfile instanceof Blob)) {\n throw new Error('customProfile must be a Blob');\n }\n\n // Validate PDF format\n const isValidPDF = await BlobUtils.validatePDF(inputPDF);\n if (!isValidPDF) {\n throw new Error('Invalid PDF format');\n }\n\n // Get the asset loader for this conversion\n const assetLoader = await getAssetLoader(options);\n\n // Load Ghostscript with the asset loader\n const module = await GhostscriptLoader.load({ assetLoader });\n const vfs = new VirtualFileSystem(module);\n\n try {\n // Setup file paths\n const inputPath = '/tmp/input.pdf';\n const outputPath = '/tmp/output.pdf';\n const pdfxDefPath = '/tmp/pdfx_def.ps';\n const customProfilePath = '/tmp/custom.icc';\n\n // Write input PDF to virtual filesystem\n await vfs.writeBlob(inputPath, inputPDF);\n\n // Write ICC profiles to virtual filesystem\n if (options.outputProfile === 'custom' && options.customProfile) {\n await vfs.writeBlob(customProfilePath, options.customProfile);\n } else if (options.outputProfile !== 'custom') {\n // Load the bundled ICC profile using the asset loader\n const profileInfo =\n PROFILE_PRESETS[options.outputProfile as keyof typeof PROFILE_PRESETS];\n const profilePath = `/tmp/${profileInfo.file}`;\n\n const profileBlob = await assetLoader.loadICCProfile(profileInfo.file);\n await vfs.writeBlob(profilePath, profileBlob);\n }\n\n // Determine ICC profile path and metadata for Ghostscript\n let iccProfilePath: string;\n let outputConditionIdentifier: string;\n let outputCondition: string;\n\n if (options.outputProfile === 'custom') {\n iccProfilePath = customProfilePath;\n // Use custom values or defaults\n outputConditionIdentifier =\n options.outputConditionIdentifier || 'Custom Profile';\n outputCondition = options.outputCondition || 'Custom ICC Profile';\n } else {\n const preset =\n PROFILE_PRESETS[options.outputProfile as keyof typeof PROFILE_PRESETS];\n iccProfilePath = `/tmp/${preset.file}`;\n // Allow overriding preset values\n outputConditionIdentifier =\n options.outputConditionIdentifier || preset.identifier;\n outputCondition = options.outputCondition || preset.info;\n }\n\n // Read ICC profile data and convert to hex for embedding\n // WASM doesn't support (file) (r) file syntax, so we embed directly\n const iccData = vfs.readFile(iccProfilePath);\n const iccHex = Array.from(iccData)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n // Determine color conversion strategy based on profile type\n // ICC profile color space is at bytes 16-19\n // 'RGB ' = 0x52474220, 'CMYK' = 0x434D594B\n const colorSpaceBytes = iccData.slice(16, 20);\n const colorSpaceStr = String.fromCharCode(...colorSpaceBytes);\n const isCMYKProfile = colorSpaceStr.trim() === 'CMYK';\n\n // Generate PDF/X-3 definition with ICC profile hex data\n const pdfxDefinition = generatePDFXDef(\n options,\n iccHex,\n outputConditionIdentifier,\n outputCondition\n );\n vfs.writeText(pdfxDefPath, pdfxDefinition);\n\n // Execute Ghostscript conversion\n // For CMYK profiles, convert all colors to CMYK using the ICC profile\n // Note: Transparency flattening will rasterize transparent content\n const gsArgs = [\n '-dBATCH',\n '-dNOPAUSE',\n '-sDEVICE=pdfwrite',\n '-dCompatibilityLevel=1.4',\n // Disable PDF 1.5+ features to avoid warnings with PDF 1.4 compatibility\n '-dWriteObjStms=false',\n '-dWriteXRefStm=false',\n // PDF/X-3 settings\n '-dPDFSETTINGS=/prepress',\n '-dSetPageSize=false',\n '-dAutoRotatePages=/None',\n ];\n\n // Flatten transparency for PDF/X-3 compliance (default: true)\n // HaveTransparency=false tells Ghostscript the target viewer cannot handle\n // PDF 1.4 transparency, so pages with transparency are converted to bitmaps\n const shouldFlattenTransparency = options.flattenTransparency !== false;\n if (shouldFlattenTransparency) {\n gsArgs.push('-dHaveTransparency=false');\n }\n\n // Add color conversion for CMYK profiles\n if (isCMYKProfile) {\n gsArgs.push(\n '-sColorConversionStrategy=CMYK',\n '-dProcessColorModel=/DeviceCMYK',\n '-dConvertCMYKImagesToRGB=false'\n );\n } else {\n // For RGB profiles, we still need a color conversion strategy\n gsArgs.push('-sColorConversionStrategy=RGB');\n }\n\n // Add output file and input files\n gsArgs.push(`-sOutputFile=${outputPath}`, pdfxDefPath, inputPath);\n\n const exitCode = await module.callMain(gsArgs);\n if (exitCode !== 0) {\n throw new Error(\n `Ghostscript conversion failed with exit code ${exitCode}`\n );\n }\n\n // Read output\n const outputData = vfs.readFile(outputPath);\n // Create a copy of the data to ensure it's an ArrayBuffer, not SharedArrayBuffer\n const outputBuffer = new Uint8Array(outputData);\n const outputPDF = new Blob([outputBuffer], { type: 'application/pdf' });\n\n // Cleanup\n vfs.cleanup();\n\n return outputPDF;\n } catch (error) {\n vfs.cleanup();\n throw error;\n }\n}\n\n// Profile presets from the spec\nconst PROFILE_PRESETS = {\n gracol: {\n file: 'GRACoL2013_CRPC6.icc',\n identifier: 'CGATS 21.2',\n info: 'GRACoL 2013 CRPC6',\n },\n fogra39: {\n file: 'ISOcoated_v2_eci.icc',\n identifier: 'FOGRA39',\n info: 'ISO Coated v2 (ECI)',\n },\n srgb: {\n file: 'sRGB_IEC61966-2-1.icc',\n identifier: 'sRGB IEC61966-2.1',\n info: 'sRGB IEC61966-2.1',\n },\n};\n\nfunction generatePDFXDef(\n options: PDFX3Options,\n iccProfileHex: string,\n outputConditionIdentifier: string,\n outputCondition: string\n): string {\n return `%!\n% PDF/X-3 Definition File\n[ /Title (${options.title || 'Untitled'}) /DOCINFO pdfmark\n[ /Trapped /False /DOCINFO pdfmark\n\n% Set PDF/X-3 conformance\n[ /GTS_PDFXVersion (PDF/X-3:2003) /GTS_PDFXConformance (PDF/X-3:2003) /DOCINFO pdfmark\n\n% Set TrimBox to match MediaBox for all pages (required for PDF/X)\n[/TrimBox [0 0 0 0] /PAGE pdfmark\n\n% Embed ICC profile as hex stream\n[/_objdef {icc_PDFX} /type /stream /OBJ pdfmark\n[{icc_PDFX} <</N 4>> /PUT pdfmark\n[{icc_PDFX} <${iccProfileHex}> /PUT pdfmark\n\n% Define OutputIntent with embedded ICC profile\n[/_objdef {OutputIntent_PDFX} /type /dict /OBJ pdfmark\n[{OutputIntent_PDFX} <<\n /Type /OutputIntent\n /S /GTS_PDFX\n /OutputCondition (${outputCondition})\n /OutputConditionIdentifier (${outputConditionIdentifier})\n /RegistryName (http://www.color.org)\n /DestOutputProfile {icc_PDFX}\n>> /PUT pdfmark\n\n% Add OutputIntent to Catalog\n[{Catalog} <</OutputIntents [{OutputIntent_PDFX}]>> /PUT pdfmark`;\n}\n", "/**\n * @imgly/plugin-pdfx-web - Print-Ready PDF conversion for CE.SDK\n * Universal entry point (auto-detects environment)\n *\n * IMPORTANT: This package includes Ghostscript WASM binaries licensed under AGPL-3.0.\n * Commercial users must ensure license compliance. See README.md for details.\n *\n * For optimal bundle size, consider using the platform-specific entry points:\n * - `@imgly/plugin-print-ready-pdfs-web/browser` for browser environments\n * - `@imgly/plugin-print-ready-pdfs-web/node` for Node.js environments\n */\n\n// Export the main conversion function (supports both single blob and array of blobs)\nexport { convertToPDFX3 } from './pdfx';\n\n// Export asset loaders for both environments\nexport { BrowserAssetLoader } from './loaders/browser-loader';\nexport { NodeAssetLoader } from './loaders/node-loader';\n\n// Export Logger for controlling log verbosity\nexport { Logger } from './utils/logger';\n\n// Export types for TypeScript users\nexport type { PDFX3Options, AssetLoader } from './types';\nexport type { LogLevel } from './utils/logger';\n"],
5
- "mappings": "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",
6
- "names": ["normalizeAssetPath", "path", "normalizedPath", "init_asset_path", "__esmMin", "browser_loader_exports", "__export", "BrowserAssetLoader", "init_browser_loader", "__esmMin", "init_asset_path", "assetPath", "normalizeAssetPath", "module", "profileName", "profileUrl", "response", "node_loader_exports", "__export", "NodeAssetLoader", "init_node_loader", "__esmMin", "moduleLib", "pathLib", "requireForESM", "dirname", "module", "profileName", "fsLib", "join", "readFileSync", "profilePath", "data", "Logger", "_Logger", "component", "level", "message", "data", "levelOrder", "entry", "prefix", "logMessage", "BrowserDetection", "createGhostscriptModule", "options", "silent", "assetLoader", "factory", "wasmPath", "moduleConfig", "filename", "GhostscriptLoader", "options", "logger", "Logger", "BrowserDetection", "timeout", "error", "assetLoader", "module", "createGhostscriptModule", "operation", "timeoutMs", "_", "reject", "BlobUtils", "blob", "buffer", "resolve", "reject", "reader", "data", "pdfHeader", "VirtualFileSystem", "module", "workingDir", "Logger", "error", "path", "blob", "managed", "data", "BlobUtils", "content", "cleanedCount", "tryGetBaseUrlFromImportMeta", "url", "lastSlash", "getAssetLoader", "options", "BrowserAssetLoader", "autoBaseUrl", "NodeAssetLoader", "convertToPDFX3", "input", "results", "i", "converted", "convertToPDFX3Single", "error", "inputPDF", "validProfiles", "BlobUtils", "assetLoader", "module", "GhostscriptLoader", "vfs", "VirtualFileSystem", "inputPath", "outputPath", "pdfxDefPath", "customProfilePath", "profileInfo", "PROFILE_PRESETS", "profilePath", "profileBlob", "iccProfilePath", "outputConditionIdentifier", "outputCondition", "preset", "iccData", "iccHex", "b", "colorSpaceBytes", "isCMYKProfile", "pdfxDefinition", "generatePDFXDef", "gsArgs", "exitCode", "outputData", "outputBuffer", "outputPDF", "iccProfileHex", "init_browser_loader", "init_node_loader"]
3
+ "sources": ["../src/utils/logger.ts", "../src/utils/browser-detection.ts", "../src/wasm/ghostscript-module.ts", "../src/core/ghostscript-loader.ts", "../src/utils/blob-utils.ts", "../src/core/virtual-filesystem.ts", "../src/pdfx.ts"],
4
+ "sourcesContent": ["/* eslint-disable no-console */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface LogEntry {\n timestamp: number;\n level: LogLevel;\n component: string;\n message: string;\n data?: any;\n}\n\nexport class Logger {\n private static globalLogLevel: LogLevel = 'warn';\n\n private static logs: LogEntry[] = [];\n\n private static maxLogs = 1000;\n\n constructor(private component: string) {}\n\n static setLogLevel(level: LogLevel): void {\n Logger.globalLogLevel = level;\n }\n\n static getLogs(): LogEntry[] {\n return [...Logger.logs];\n }\n\n static clearLogs(): void {\n Logger.logs = [];\n }\n\n debug(message: string, data?: any): void {\n this.log('debug', message, data);\n }\n\n info(message: string, data?: any): void {\n this.log('info', message, data);\n }\n\n warn(message: string, data?: any): void {\n this.log('warn', message, data);\n }\n\n error(message: string, data?: any): void {\n this.log('error', message, data);\n }\n\n private log(level: LogLevel, message: string, data?: any): void {\n const levelOrder: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n };\n\n if (levelOrder[level] < levelOrder[Logger.globalLogLevel]) {\n return;\n }\n\n const entry: LogEntry = {\n timestamp: Date.now(),\n level,\n component: this.component,\n message,\n data,\n };\n\n Logger.logs.push(entry);\n\n // Keep logs under the limit\n if (Logger.logs.length > Logger.maxLogs) {\n Logger.logs = Logger.logs.slice(-Logger.maxLogs);\n }\n\n // Console output\n const prefix = `[${new Date().toISOString()}] [${level.toUpperCase()}] [${\n this.component\n }]`;\n const logMessage = data ? `${message} ${JSON.stringify(data)}` : message;\n\n switch (level) {\n case 'debug':\n console.debug(prefix, logMessage);\n break;\n case 'info':\n console.info(prefix, logMessage);\n break;\n case 'warn':\n console.warn(prefix, logMessage);\n break;\n case 'error':\n console.error(prefix, logMessage);\n break;\n default:\n // All log levels handled above\n break;\n }\n }\n}\n", "export class BrowserDetection {\n supportsWebAssembly(): boolean {\n try {\n return (\n typeof WebAssembly === 'object' &&\n typeof WebAssembly.instantiate === 'function'\n );\n } catch {\n return false;\n }\n }\n}\n", "// Wrapper for the Ghostscript WASM module to ensure proper loading\nimport type {\n EmscriptenModule,\n GhostscriptModuleFactory,\n} from '../types/ghostscript';\n\nexport interface GhostscriptModuleOptions {\n /**\n * Whether to suppress Ghostscript's stdout/stderr output.\n * Default: true (silent mode)\n */\n silent?: boolean;\n}\n\nexport default async function createGhostscriptModule(\n options: GhostscriptModuleOptions = {}\n): Promise<EmscriptenModule> {\n const { silent = true } = options;\n // Check if we're in Node.js\n const isNode =\n typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n let GhostscriptModule: any;\n let wasmPath: string;\n\n if (isNode) {\n // Node.js: Use require.resolve to find gs.js relative to this module\n // Use indirect dynamic import to prevent Webpack 5 from statically analyzing these imports\n // But use direct imports in test environments (vitest) where indirect imports bypass mocking\n // See: https://github.com/imgly/ubq/issues/11471\n const isTestEnv =\n typeof process !== 'undefined' &&\n (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test');\n\n // Helper for dynamic imports - uses indirect import in production to avoid Webpack static analysis\n // Note: new Function() could fail in CSP-restricted environments, but CSP is a browser\n // security mechanism and doesn't apply to Node.js. This code only runs in Node.js (isNode check above).\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const indirectImport = new Function('s', 'return import(s)') as (\n s: string\n ) => Promise<any>;\n const dynamicImport = isTestEnv ? (s: string) => import(s) : indirectImport;\n\n const moduleLib = await dynamicImport('module');\n const pathLib = await dynamicImport('path');\n const createRequire = moduleLib.createRequire;\n const dirname = pathLib.dirname;\n const join = pathLib.join;\n\n const requireForESM = createRequire(import.meta.url);\n\n // Resolve gs.js directly (it's copied to dist/ alongside the bundle)\n const gsPath = requireForESM.resolve('./gs.js');\n const moduleDir = dirname(gsPath);\n wasmPath = join(moduleDir, 'gs.wasm');\n\n GhostscriptModule = await dynamicImport(gsPath);\n } else {\n // Browser: Use URL-based imports\n const moduleUrl = new URL('./gs.js', import.meta.url).href;\n GhostscriptModule = await import(/* webpackIgnore: true */ moduleUrl);\n wasmPath = new URL('./gs.wasm', import.meta.url).href;\n }\n\n const factory = (GhostscriptModule.default ||\n GhostscriptModule) as GhostscriptModuleFactory;\n\n // Configure the module to load WASM from the bundled location\n const moduleConfig: Record<string, unknown> = {\n locateFile: (filename: string) => {\n if (filename === 'gs.wasm') {\n return wasmPath;\n }\n return filename;\n },\n };\n\n // Suppress Ghostscript stdout/stderr output in silent mode\n if (silent) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n moduleConfig.print = () => {};\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n moduleConfig.printErr = () => {};\n }\n\n const module = await factory(moduleConfig);\n\n return module;\n}\n", "import type { EmscriptenModule } from '../types/ghostscript';\nimport { Logger } from '../utils/logger';\nimport { BrowserDetection } from '../utils/browser-detection';\nimport createGhostscriptModule from '../wasm/ghostscript-module';\n\nexport interface LoaderOptions {\n timeout?: number;\n}\n\nexport class GhostscriptLoader {\n private static instance: Promise<EmscriptenModule> | null = null;\n\n private static readonly TIMEOUT_MS = 30000;\n\n static async load(options: LoaderOptions = {}): Promise<EmscriptenModule> {\n if (this.instance) {\n return this.instance;\n }\n\n this.instance = this.loadInternal(options);\n return this.instance;\n }\n\n private static async loadInternal(\n options: LoaderOptions\n ): Promise<EmscriptenModule> {\n const logger = new Logger('GhostscriptLoader');\n const browser = new BrowserDetection();\n\n // Check browser compatibility\n if (!browser.supportsWebAssembly()) {\n throw new Error('WebAssembly not supported in this browser');\n }\n\n const timeout = options.timeout || this.TIMEOUT_MS;\n\n try {\n logger.info('Loading bundled Ghostscript (AGPL-3.0 licensed)');\n logger.info('Source available at: https://img.ly/docs/cesdk');\n return await this.loadWithTimeout(() => this.loadFromBundle(), timeout);\n } catch (error) {\n logger.error('Ghostscript loading failed', {\n error: (error as Error).message,\n });\n throw new Error(\n `Failed to load Ghostscript: ${(error as Error).message}`\n );\n }\n }\n\n private static async loadFromBundle(): Promise<EmscriptenModule> {\n // Use the bundled WASM module with proper configuration\n const logger = new Logger('GhostscriptInit');\n\n try {\n logger.info('Initializing Ghostscript module');\n\n const module = await createGhostscriptModule();\n\n logger.info('Ghostscript module initialized successfully', {\n version: module.version || 'unknown',\n hasFS: !!module.FS,\n hasCallMain: !!module.callMain,\n });\n\n return module;\n } catch (error) {\n logger.error('Ghostscript initialization failed', { error });\n throw new Error(\n `Ghostscript initialization failed: ${(error as Error).message}`\n );\n }\n }\n\n private static async loadWithTimeout<T>(\n operation: () => Promise<T>,\n timeoutMs: number\n ): Promise<T> {\n return Promise.race([\n operation(),\n new Promise<never>((_, reject) => {\n setTimeout(\n () => reject(new Error(`Loading timeout after ${timeoutMs}ms`)),\n timeoutMs\n );\n }),\n ]);\n }\n\n static reset(): void {\n this.instance = null;\n }\n}\n", "export class BlobUtils {\n static async toUint8Array(blob: Blob): Promise<Uint8Array> {\n if (blob.arrayBuffer) {\n const buffer = await blob.arrayBuffer();\n return new Uint8Array(buffer);\n }\n\n // Fallback for older browsers\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n resolve(new Uint8Array(reader.result));\n } else {\n reject(new Error('Failed to read blob as ArrayBuffer'));\n }\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n }\n\n static async validatePDF(blob: Blob): Promise<boolean> {\n try {\n const data = await this.toUint8Array(blob);\n\n // Check PDF magic number\n const pdfHeader = new Uint8Array([0x25, 0x50, 0x44, 0x46]); // %PDF\n\n if (data.length < 4) {\n return false;\n }\n\n for (let i = 0; i < 4; i++) {\n if (data[i] !== pdfHeader[i]) {\n return false;\n }\n }\n\n return true;\n } catch {\n return false;\n }\n }\n}\n", "import type { EmscriptenModule, EmscriptenFS } from '../types/ghostscript';\nimport { Logger } from '../utils/logger';\nimport { BlobUtils } from '../utils/blob-utils';\n\nexport interface FileEntry {\n path: string;\n data: Uint8Array;\n cleanup: boolean;\n}\n\nexport class VirtualFileSystem {\n private readonly fs: EmscriptenFS;\n\n private readonly logger: Logger;\n\n private readonly managedFiles: Set<string> = new Set();\n\n private readonly workingDir: string;\n\n constructor(\n private readonly module: EmscriptenModule,\n workingDir = '/tmp/pdfx'\n ) {\n this.fs = module.FS;\n this.logger = new Logger('VirtualFileSystem');\n this.workingDir = workingDir;\n this.initialize();\n }\n\n private initialize(): void {\n try {\n // Create working directory structure (handle existing directories gracefully)\n this.createDirIfNotExists(this.workingDir);\n this.createDirIfNotExists(`${this.workingDir}/input`);\n this.createDirIfNotExists(`${this.workingDir}/output`);\n this.createDirIfNotExists(`${this.workingDir}/profiles`);\n this.createDirIfNotExists(`${this.workingDir}/temp`);\n\n this.logger.info('Virtual filesystem initialized', {\n workingDir: this.workingDir,\n });\n } catch (error) {\n this.logger.error('Failed to initialize virtual filesystem', { error });\n throw new Error(`VFS initialization failed: ${error}`);\n }\n }\n\n private createDirIfNotExists(path: string): void {\n try {\n this.fs.mkdir(path);\n } catch (error: any) {\n // Ignore \"File exists\" errors, re-throw others\n if (error.code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n async writeBlob(path: string, blob: Blob, managed = true): Promise<void> {\n try {\n const data = await BlobUtils.toUint8Array(blob);\n this.fs.writeFile(path, data);\n\n if (managed) {\n this.managedFiles.add(path);\n }\n\n this.logger.debug('File written', { path, size: data.length });\n } catch (error) {\n this.logger.error('Failed to write blob', { path, error });\n throw new Error(`Failed to write file ${path}: ${error}`);\n }\n }\n\n writeText(path: string, content: string, managed = true): void {\n try {\n this.fs.writeFile(path, content);\n\n if (managed) {\n this.managedFiles.add(path);\n }\n\n this.logger.debug('Text file written', { path, length: content.length });\n } catch (error) {\n this.logger.error('Failed to write text', { path, error });\n throw new Error(`Failed to write text file ${path}: ${error}`);\n }\n }\n\n readFile(path: string): Uint8Array {\n try {\n const data = this.fs.readFile(path) as Uint8Array;\n this.logger.debug('File read', { path, size: data.length });\n return data;\n } catch (error) {\n this.logger.error('Failed to read file', { path, error });\n throw new Error(`Failed to read file ${path}: ${error}`);\n }\n }\n\n exists(path: string): boolean {\n try {\n this.fs.stat(path);\n return true;\n } catch {\n return false;\n }\n }\n\n cleanup(): void {\n let cleanedCount = 0;\n\n for (const path of this.managedFiles) {\n try {\n this.fs.unlink(path);\n cleanedCount++;\n } catch (error) {\n this.logger.warn('Failed to cleanup file', { path, error });\n }\n }\n\n this.managedFiles.clear();\n this.logger.info('Filesystem cleanup completed', { cleanedCount });\n }\n}\n", "import type { PDFX3Options } from './types/pdfx';\nimport { GhostscriptLoader } from './core/ghostscript-loader';\nimport { VirtualFileSystem } from './core/virtual-filesystem';\nimport { BlobUtils } from './utils/blob-utils';\n\n/**\n * PDF/X-3 conversion function\n * Converts RGB PDF(s) to PDF/X-3 format using specified output profile\n *\n * @overload\n * @param inputPDF Single PDF blob to convert\n * @param options Conversion configuration\n * @returns Promise resolving to converted PDF blob\n *\n * @overload\n * @param inputPDFs Array of PDF blobs to convert\n * @param options Conversion configuration\n * @returns Promise resolving to array of converted PDF blobs\n */\nexport function convertToPDFX3(\n inputPDF: Blob,\n options: PDFX3Options\n): Promise<Blob>;\nexport function convertToPDFX3(\n inputPDFs: Blob[],\n options: PDFX3Options\n): Promise<Blob[]>;\nexport async function convertToPDFX3(\n input: Blob | Blob[],\n options: PDFX3Options\n): Promise<Blob | Blob[]> {\n // Handle array input (batch processing)\n if (Array.isArray(input)) {\n if (input.length === 0) {\n return [];\n }\n\n // Process each blob sequentially to avoid overwhelming the WASM module\n const results: Blob[] = [];\n for (let i = 0; i < input.length; i++) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const converted = await convertToPDFX3Single(input[i], options);\n results.push(converted);\n } catch (error) {\n throw new Error(\n `Failed to convert PDF ${i + 1} of ${input.length}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n return results;\n }\n\n // Handle single blob input\n return convertToPDFX3Single(input, options);\n}\n\n/**\n * Internal function to convert a single PDF\n */\nasync function convertToPDFX3Single(\n inputPDF: Blob,\n options: PDFX3Options\n): Promise<Blob> {\n // Validate inputs\n if (!(inputPDF instanceof Blob)) {\n throw new Error('Input must be a Blob');\n }\n\n if (inputPDF.size === 0) {\n throw new Error('Input PDF is empty');\n }\n\n if (inputPDF.size > 100 * 1024 * 1024) {\n throw new Error(\n `Input PDF too large (${inputPDF.size} bytes). Maximum: 100MB`\n );\n }\n\n // Validate output profile is provided\n if (!options.outputProfile) {\n throw new Error(\n 'outputProfile is required. Must be one of: \"gracol\", \"fogra39\", \"srgb\", \"custom\"'\n );\n }\n\n // Validate output profile value\n const validProfiles = ['gracol', 'fogra39', 'srgb', 'custom'];\n if (!validProfiles.includes(options.outputProfile)) {\n throw new Error(\n `Invalid outputProfile \"${\n options.outputProfile\n }\". Must be one of: ${validProfiles.join(', ')}`\n );\n }\n\n // Validate custom profile requirement\n if (options.outputProfile === 'custom' && !options.customProfile) {\n throw new Error(\n 'customProfile Blob is required when outputProfile is \"custom\"'\n );\n }\n\n // Validate custom profile if provided\n if (options.customProfile && !(options.customProfile instanceof Blob)) {\n throw new Error('customProfile must be a Blob');\n }\n\n // Validate PDF format\n const isValidPDF = await BlobUtils.validatePDF(inputPDF);\n if (!isValidPDF) {\n throw new Error('Invalid PDF format');\n }\n\n // Load Ghostscript\n const module = await GhostscriptLoader.load();\n const vfs = new VirtualFileSystem(module);\n\n try {\n // Setup file paths\n const inputPath = '/tmp/input.pdf';\n const outputPath = '/tmp/output.pdf';\n const pdfxDefPath = '/tmp/pdfx_def.ps';\n const customProfilePath = '/tmp/custom.icc';\n\n // Write input PDF to virtual filesystem\n await vfs.writeBlob(inputPath, inputPDF);\n\n // Write ICC profiles to virtual filesystem\n if (options.outputProfile === 'custom' && options.customProfile) {\n await vfs.writeBlob(customProfilePath, options.customProfile);\n } else if (options.outputProfile !== 'custom') {\n // Load the bundled ICC profile\n const profileInfo =\n PROFILE_PRESETS[options.outputProfile as keyof typeof PROFILE_PRESETS];\n const profilePath = `/tmp/${profileInfo.file}`;\n\n // Load ICC profile - different approach for Node.js vs browser\n const isNode =\n typeof process !== 'undefined' && process.versions?.node != null;\n\n let profileBlob: Blob;\n\n if (isNode) {\n // Node.js: Load from filesystem using readFileSync\n // Use indirect dynamic import to prevent Webpack 5 from statically analyzing these imports\n // But use direct imports in test environments (vitest) where indirect imports bypass mocking\n // See: https://github.com/imgly/ubq/issues/11471\n const isTestEnv =\n process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';\n\n // Note: new Function() could fail in CSP-restricted environments, but CSP is a browser\n // security mechanism and doesn't apply to Node.js. This code only runs in Node.js (isNode check above).\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const indirectImport = new Function('s', 'return import(s)') as (\n s: string\n ) => Promise<any>;\n const dynamicImport = isTestEnv\n ? (s: string) => import(s)\n : indirectImport;\n\n const { readFileSync } = await dynamicImport('fs');\n const { fileURLToPath } = await dynamicImport('url');\n const { dirname, join } = await dynamicImport('path');\n\n // Get the directory of the built module\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const profileFilePath = join(moduleDir, profileInfo.file);\n\n const profileData = readFileSync(profileFilePath);\n profileBlob = new Blob([profileData], {\n type: 'application/vnd.iccprofile',\n });\n } else {\n // Browser: Use fetch\n const profileUrl = new URL(profileInfo.file, import.meta.url).href;\n const profileResponse = await fetch(profileUrl);\n if (!profileResponse.ok) {\n throw new Error(\n `Failed to load ICC profile ${profileInfo.file}: ${profileResponse.statusText}`\n );\n }\n profileBlob = await profileResponse.blob();\n }\n\n await vfs.writeBlob(profilePath, profileBlob);\n }\n\n // Determine ICC profile path and metadata for Ghostscript\n let iccProfilePath: string;\n let outputConditionIdentifier: string;\n let outputCondition: string;\n\n if (options.outputProfile === 'custom') {\n iccProfilePath = customProfilePath;\n // Use custom values or defaults\n outputConditionIdentifier =\n options.outputConditionIdentifier || 'Custom Profile';\n outputCondition = options.outputCondition || 'Custom ICC Profile';\n } else {\n const preset =\n PROFILE_PRESETS[options.outputProfile as keyof typeof PROFILE_PRESETS];\n iccProfilePath = `/tmp/${preset.file}`;\n // Allow overriding preset values\n outputConditionIdentifier =\n options.outputConditionIdentifier || preset.identifier;\n outputCondition = options.outputCondition || preset.info;\n }\n\n // Read ICC profile data and convert to hex for embedding\n // WASM doesn't support (file) (r) file syntax, so we embed directly\n const iccData = vfs.readFile(iccProfilePath);\n const iccHex = Array.from(iccData)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n // Determine color conversion strategy based on profile type\n // ICC profile color space is at bytes 16-19\n // 'RGB ' = 0x52474220, 'CMYK' = 0x434D594B\n const colorSpaceBytes = iccData.slice(16, 20);\n const colorSpaceStr = String.fromCharCode(...colorSpaceBytes);\n const isCMYKProfile = colorSpaceStr.trim() === 'CMYK';\n\n // Generate PDF/X-3 definition with ICC profile hex data\n const pdfxDefinition = generatePDFXDef(\n options,\n iccHex,\n outputConditionIdentifier,\n outputCondition\n );\n vfs.writeText(pdfxDefPath, pdfxDefinition);\n\n // Execute Ghostscript conversion\n // For CMYK profiles, convert all colors to CMYK using the ICC profile\n // Note: Transparency flattening will rasterize transparent content\n const gsArgs = [\n '-dBATCH',\n '-dNOPAUSE',\n '-sDEVICE=pdfwrite',\n '-dCompatibilityLevel=1.4',\n // Disable PDF 1.5+ features to avoid warnings with PDF 1.4 compatibility\n '-dWriteObjStms=false',\n '-dWriteXRefStm=false',\n // PDF/X-3 settings\n '-dPDFSETTINGS=/prepress',\n '-dSetPageSize=false',\n '-dAutoRotatePages=/None',\n ];\n\n // Flatten transparency for PDF/X-3 compliance (default: true)\n // HaveTransparency=false tells Ghostscript the target viewer cannot handle\n // PDF 1.4 transparency, so pages with transparency are converted to bitmaps\n const shouldFlattenTransparency = options.flattenTransparency !== false;\n if (shouldFlattenTransparency) {\n gsArgs.push('-dHaveTransparency=false');\n }\n\n // Add color conversion for CMYK profiles\n if (isCMYKProfile) {\n gsArgs.push(\n '-sColorConversionStrategy=CMYK',\n '-dProcessColorModel=/DeviceCMYK',\n '-dConvertCMYKImagesToRGB=false'\n );\n } else {\n // For RGB profiles, we still need a color conversion strategy\n gsArgs.push('-sColorConversionStrategy=RGB');\n }\n\n // Add output file and input files\n gsArgs.push(`-sOutputFile=${outputPath}`, pdfxDefPath, inputPath);\n\n const exitCode = await module.callMain(gsArgs);\n if (exitCode !== 0) {\n throw new Error(\n `Ghostscript conversion failed with exit code ${exitCode}`\n );\n }\n\n // Read output\n const outputData = vfs.readFile(outputPath);\n // Create a copy of the data to ensure it's an ArrayBuffer, not SharedArrayBuffer\n const outputBuffer = new Uint8Array(outputData);\n const outputPDF = new Blob([outputBuffer], { type: 'application/pdf' });\n\n // Cleanup\n vfs.cleanup();\n\n return outputPDF;\n } catch (error) {\n vfs.cleanup();\n throw error;\n }\n}\n\n// Profile presets from the spec\nconst PROFILE_PRESETS = {\n gracol: {\n file: 'GRACoL2013_CRPC6.icc',\n identifier: 'CGATS 21.2',\n info: 'GRACoL 2013 CRPC6',\n },\n fogra39: {\n file: 'ISOcoated_v2_eci.icc',\n identifier: 'FOGRA39',\n info: 'ISO Coated v2 (ECI)',\n },\n srgb: {\n file: 'sRGB_IEC61966-2-1.icc',\n identifier: 'sRGB IEC61966-2.1',\n info: 'sRGB IEC61966-2.1',\n },\n};\n\nfunction generatePDFXDef(\n options: PDFX3Options,\n iccProfileHex: string,\n outputConditionIdentifier: string,\n outputCondition: string\n): string {\n return `%!\n% PDF/X-3 Definition File\n[ /Title (${options.title || 'Untitled'}) /DOCINFO pdfmark\n[ /Trapped /False /DOCINFO pdfmark\n\n% Set PDF/X-3 conformance\n[ /GTS_PDFXVersion (PDF/X-3:2003) /GTS_PDFXConformance (PDF/X-3:2003) /DOCINFO pdfmark\n\n% Set TrimBox to match MediaBox for all pages (required for PDF/X)\n[/TrimBox [0 0 0 0] /PAGE pdfmark\n\n% Embed ICC profile as hex stream\n[/_objdef {icc_PDFX} /type /stream /OBJ pdfmark\n[{icc_PDFX} <</N 4>> /PUT pdfmark\n[{icc_PDFX} <${iccProfileHex}> /PUT pdfmark\n\n% Define OutputIntent with embedded ICC profile\n[/_objdef {OutputIntent_PDFX} /type /dict /OBJ pdfmark\n[{OutputIntent_PDFX} <<\n /Type /OutputIntent\n /S /GTS_PDFX\n /OutputCondition (${outputCondition})\n /OutputConditionIdentifier (${outputConditionIdentifier})\n /RegistryName (http://www.color.org)\n /DestOutputProfile {icc_PDFX}\n>> /PUT pdfmark\n\n% Add OutputIntent to Catalog\n[{Catalog} <</OutputIntents [{OutputIntent_PDFX}]>> /PUT pdfmark`;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["Logger", "_Logger", "component", "level", "message", "data", "levelOrder", "entry", "prefix", "logMessage", "BrowserDetection", "createGhostscriptModule", "options", "silent", "isNode", "GhostscriptModule", "wasmPath", "isTestEnv", "indirectImport", "dynamicImport", "s", "moduleLib", "pathLib", "createRequire", "dirname", "join", "gsPath", "moduleDir", "factory", "moduleConfig", "filename", "GhostscriptLoader", "options", "logger", "Logger", "BrowserDetection", "timeout", "error", "module", "createGhostscriptModule", "operation", "timeoutMs", "_", "reject", "BlobUtils", "blob", "buffer", "resolve", "reject", "reader", "data", "pdfHeader", "i", "VirtualFileSystem", "module", "workingDir", "Logger", "error", "path", "blob", "managed", "data", "BlobUtils", "content", "cleanedCount", "convertToPDFX3", "input", "options", "results", "converted", "convertToPDFX3Single", "error", "inputPDF", "validProfiles", "BlobUtils", "module", "GhostscriptLoader", "vfs", "VirtualFileSystem", "inputPath", "outputPath", "pdfxDefPath", "customProfilePath", "profileInfo", "PROFILE_PRESETS", "profilePath", "isNode", "profileBlob", "isTestEnv", "indirectImport", "dynamicImport", "s", "readFileSync", "fileURLToPath", "dirname", "join", "moduleDir", "profileFilePath", "profileData", "profileUrl", "profileResponse", "iccProfilePath", "outputConditionIdentifier", "outputCondition", "preset", "iccData", "iccHex", "b", "colorSpaceBytes", "isCMYKProfile", "pdfxDefinition", "generatePDFXDef", "gsArgs", "exitCode", "outputData", "outputBuffer", "outputPDF", "iccProfileHex"]
7
7
  }
package/dist/pdfx.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { PDFX3Options } from './types/pdfx';
2
+ /**
3
+ * PDF/X-3 conversion function
4
+ * Converts RGB PDF(s) to PDF/X-3 format using specified output profile
5
+ *
6
+ * @overload
7
+ * @param inputPDF Single PDF blob to convert
8
+ * @param options Conversion configuration
9
+ * @returns Promise resolving to converted PDF blob
10
+ *
11
+ * @overload
12
+ * @param inputPDFs Array of PDF blobs to convert
13
+ * @param options Conversion configuration
14
+ * @returns Promise resolving to array of converted PDF blobs
15
+ */
16
+ export declare function convertToPDFX3(inputPDF: Blob, options: PDFX3Options): Promise<Blob>;
17
+ export declare function convertToPDFX3(inputPDFs: Blob[], options: PDFX3Options): Promise<Blob[]>;
18
+ //# sourceMappingURL=pdfx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdfx.d.ts","sourceRoot":"","sources":["../src/pdfx.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKjD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,IAAI,EACd,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAAC;AACjB,wBAAgB,cAAc,CAC5B,SAAS,EAAE,IAAI,EAAE,EACjB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './types/pdfx';
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare class BlobUtils {
2
+ static toUint8Array(blob: Blob): Promise<Uint8Array>;
3
+ static validatePDF(blob: Blob): Promise<boolean>;
4
+ }
5
+ //# sourceMappingURL=blob-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob-utils.d.ts","sourceRoot":"","sources":["../../src/utils/blob-utils.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAS;WACP,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;WAqB7C,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;CAsBvD"}
@@ -0,0 +1,4 @@
1
+ export declare class BrowserDetection {
2
+ supportsWebAssembly(): boolean;
3
+ }
4
+ //# sourceMappingURL=browser-detection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-detection.d.ts","sourceRoot":"","sources":["../../src/utils/browser-detection.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;IAC3B,mBAAmB,IAAI,OAAO;CAU/B"}
@@ -0,0 +1,24 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export interface LogEntry {
3
+ timestamp: number;
4
+ level: LogLevel;
5
+ component: string;
6
+ message: string;
7
+ data?: any;
8
+ }
9
+ export declare class Logger {
10
+ private component;
11
+ private static globalLogLevel;
12
+ private static logs;
13
+ private static maxLogs;
14
+ constructor(component: string);
15
+ static setLogLevel(level: LogLevel): void;
16
+ static getLogs(): LogEntry[];
17
+ static clearLogs(): void;
18
+ debug(message: string, data?: any): void;
19
+ info(message: string, data?: any): void;
20
+ warn(message: string, data?: any): void;
21
+ error(message: string, data?: any): void;
22
+ private log;
23
+ }
24
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,qBAAa,MAAM;IAOL,OAAO,CAAC,SAAS;IAN7B,OAAO,CAAC,MAAM,CAAC,cAAc,CAAoB;IAEjD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAkB;IAErC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAQ;gBAEV,SAAS,EAAE,MAAM;IAErC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAIzC,MAAM,CAAC,OAAO,IAAI,QAAQ,EAAE;IAI5B,MAAM,CAAC,SAAS,IAAI,IAAI;IAIxB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIxC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIvC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIvC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIxC,OAAO,CAAC,GAAG;CAmDZ"}
@@ -0,0 +1,10 @@
1
+ import type { EmscriptenModule } from '../types/ghostscript';
2
+ export interface GhostscriptModuleOptions {
3
+ /**
4
+ * Whether to suppress Ghostscript's stdout/stderr output.
5
+ * Default: true (silent mode)
6
+ */
7
+ silent?: boolean;
8
+ }
9
+ export default function createGhostscriptModule(options?: GhostscriptModuleOptions): Promise<EmscriptenModule>;
10
+ //# sourceMappingURL=ghostscript-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghostscript-module.d.ts","sourceRoot":"","sources":["../../src/wasm/ghostscript-module.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAA8B,uBAAuB,CACnD,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,gBAAgB,CAAC,CA0E3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imgly/plugin-print-ready-pdfs-web",
3
- "version": "1.1.2",
3
+ "version": "1.68.0-rc.2",
4
4
  "description": "Print-Ready PDFs plugin for CE.SDK editor - PDF/X conversion and export functionality. Contains AGPL-3.0 licensed Ghostscript WASM.",
5
5
  "keywords": [
6
6
  "CE.SDK",
@@ -9,10 +9,6 @@
9
9
  "pdfx",
10
10
  "export"
11
11
  ],
12
- "repository": {
13
- "type": "git",
14
- "url": "git+https://github.com/imgly/plugin-print-ready-pdfs-web.git"
15
- },
16
12
  "license": "SEE LICENSE IN LICENSE.md",
17
13
  "author": {
18
14
  "name": "IMG.LY GmbH",
@@ -27,29 +23,11 @@
27
23
  "types": "./dist/index.d.ts",
28
24
  "exports": {
29
25
  ".": {
30
- "node": {
31
- "import": "./dist/index.node.mjs",
32
- "types": "./dist/index.node.d.ts"
33
- },
34
- "browser": {
35
- "import": "./dist/index.browser.mjs",
36
- "types": "./dist/index.browser.d.ts"
37
- },
38
- "default": {
39
- "import": "./dist/index.mjs",
40
- "types": "./dist/index.d.ts"
41
- }
42
- },
43
- "./browser": {
44
- "import": "./dist/index.browser.mjs",
45
- "types": "./dist/index.browser.d.ts"
46
- },
47
- "./node": {
48
- "import": "./dist/index.node.mjs",
49
- "types": "./dist/index.node.d.ts"
26
+ "import": "./dist/index.mjs",
27
+ "types": "./dist/index.d.ts"
50
28
  }
51
29
  },
52
- "homepage": "https://img.ly/products/creative-sdk",
30
+ "homepage": "https://img.ly/docs/cesdk/",
53
31
  "files": [
54
32
  "LICENSE.md",
55
33
  "README.md",
@@ -71,7 +49,6 @@
71
49
  "eslint": "^8.57.1",
72
50
  "http-server": "^14.1.1",
73
51
  "playwright": "^1.54.1",
74
- "pnpm-sync-dependencies-meta-injected": "^0.0.14",
75
52
  "prettier": "^2.8.8",
76
53
  "rimraf": "^6.0.1",
77
54
  "typescript": "^5.7.3",
@@ -79,17 +56,12 @@
79
56
  "wait-on": "8.0.2",
80
57
  "@imgly/plugin-utils": "0.0.0"
81
58
  },
82
- "dependenciesMeta": {
83
- "@imgly/plugin-utils": {
84
- "injected": true
85
- }
86
- },
87
59
  "scripts": {
88
60
  "start": "pnpm dev",
89
61
  "clean": "pnpm exec rimraf dist",
90
62
  "purge": "pnpm exec rimraf node_modules",
91
- "build": "pnpm run clean && pnpm _syncPnpm && pnpm exec node scripts/build.mjs",
92
- "dev": "pnpm exec concurrently 'node scripts/watch.mjs' 'pnpm _syncPnpm --watch' --names 'build,sync deps'",
63
+ "build": "pnpm run clean && pnpm exec node scripts/build.mjs",
64
+ "dev": "pnpm exec concurrently 'node scripts/watch.mjs' --names 'build'",
93
65
  "dev:wait": "pnpm exec wait-on ./dist/index.mjs ./dist/index.d.ts --window 250 --timeout 60000",
94
66
  "dev:types": "tsc --emitDeclarationOnly --watch --preserveWatchOutput",
95
67
  "publish:latest": "pnpm run build && pnpm publish --tag latest --access public",
@@ -99,7 +71,6 @@
99
71
  "check:pretty": "prettier --list-different './src/**/*.{ts,tsx}'",
100
72
  "check:types": "tsc --noEmit",
101
73
  "types:create": "tsc --emitDeclarationOnly",
102
- "_syncPnpm": "pnpm sync-dependencies-meta-injected",
103
74
  "demo:build": "pnpm run build && node scripts/build-demo.mjs",
104
75
  "demo:dev": "pnpm run build && node scripts/build-demo.mjs && cd demo-dist && python3 -m http.server 8080",
105
76
  "test:convert": "node test/convert-test.mjs",
@@ -113,10 +84,6 @@
113
84
  "test:silent": "pnpm run build && vitest run silent-conversion",
114
85
  "test:all": "pnpm run test:browser && pnpm run test:integration",
115
86
  "test:visual": "pnpm run build && node test/visual-test.mjs",
116
- "test:webpack5": "bash test/webpack5-compatibility-test.sh",
117
- "test:webpack5:angular": "playwright test test/webpack5-angular-runtime.spec.ts --timeout 180000",
118
- "test:webpack5:angular:setup": "cd test/webpack5-angular-project && npm install",
119
- "test:webpack5:angular:cesdk": "playwright test test/webpack5-angular-cesdk.spec.ts --timeout 300000",
120
- "test:webpack5:angular:cesdk:create": "bash scripts/create-angular-cesdk-test-project.sh"
87
+ "test:webpack5": "bash test/webpack5-compatibility-test.sh"
121
88
  }
122
89
  }
@@ -3,7 +3,6 @@ import type {
3
3
  EmscriptenModule,
4
4
  GhostscriptModuleFactory,
5
5
  } from '../types/ghostscript';
6
- import type { AssetLoader } from '../types/asset-loader';
7
6
 
8
7
  export interface GhostscriptModuleOptions {
9
8
  /**
@@ -11,24 +10,64 @@ export interface GhostscriptModuleOptions {
11
10
  * Default: true (silent mode)
12
11
  */
13
12
  silent?: boolean;
14
-
15
- /**
16
- * Asset loader for loading gs.js and gs.wasm.
17
- * Required - use BrowserAssetLoader or NodeAssetLoader.
18
- */
19
- assetLoader: AssetLoader;
20
13
  }
21
14
 
22
15
  export default async function createGhostscriptModule(
23
- options: GhostscriptModuleOptions
16
+ options: GhostscriptModuleOptions = {}
24
17
  ): Promise<EmscriptenModule> {
25
- const { silent = true, assetLoader } = options;
18
+ const { silent = true } = options;
19
+ // Check if we're in Node.js
20
+ const isNode =
21
+ typeof process !== 'undefined' &&
22
+ process.versions != null &&
23
+ process.versions.node != null;
24
+
25
+ let GhostscriptModule: any;
26
+ let wasmPath: string;
27
+
28
+ if (isNode) {
29
+ // Node.js: Use require.resolve to find gs.js relative to this module
30
+ // Use indirect dynamic import to prevent Webpack 5 from statically analyzing these imports
31
+ // But use direct imports in test environments (vitest) where indirect imports bypass mocking
32
+ // See: https://github.com/imgly/ubq/issues/11471
33
+ const isTestEnv =
34
+ typeof process !== 'undefined' &&
35
+ (process.env.VITEST === 'true' || process.env.NODE_ENV === 'test');
36
+
37
+ // Helper for dynamic imports - uses indirect import in production to avoid Webpack static analysis
38
+ // Note: new Function() could fail in CSP-restricted environments, but CSP is a browser
39
+ // security mechanism and doesn't apply to Node.js. This code only runs in Node.js (isNode check above).
40
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
41
+ const indirectImport = new Function('s', 'return import(s)') as (
42
+ s: string
43
+ ) => Promise<any>;
44
+ const dynamicImport = isTestEnv ? (s: string) => import(s) : indirectImport;
45
+
46
+ const moduleLib = await dynamicImport('module');
47
+ const pathLib = await dynamicImport('path');
48
+ const createRequire = moduleLib.createRequire;
49
+ const dirname = pathLib.dirname;
50
+ const join = pathLib.join;
51
+
52
+ const requireForESM = createRequire(import.meta.url);
53
+
54
+ // Resolve gs.js directly (it's copied to dist/ alongside the bundle)
55
+ const gsPath = requireForESM.resolve('./gs.js');
56
+ const moduleDir = dirname(gsPath);
57
+ wasmPath = join(moduleDir, 'gs.wasm');
58
+
59
+ GhostscriptModule = await dynamicImport(gsPath);
60
+ } else {
61
+ // Browser: Use URL-based imports
62
+ const moduleUrl = new URL('./gs.js', import.meta.url).href;
63
+ GhostscriptModule = await import(/* webpackIgnore: true */ moduleUrl);
64
+ wasmPath = new URL('./gs.wasm', import.meta.url).href;
65
+ }
26
66
 
27
- // Load the Ghostscript module using the provided loader
28
- const factory = await assetLoader.loadGhostscriptModule();
29
- const wasmPath = assetLoader.getWasmPath();
67
+ const factory = (GhostscriptModule.default ||
68
+ GhostscriptModule) as GhostscriptModuleFactory;
30
69
 
31
- // Configure the module to load WASM from the specified location
70
+ // Configure the module to load WASM from the bundled location
32
71
  const moduleConfig: Record<string, unknown> = {
33
72
  locateFile: (filename: string) => {
34
73
  if (filename === 'gs.wasm') {