@webex/ladon-ts 5.4.2 → 5.4.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### 5.4.4 (2025-03-27)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * copy frame data to ensure it's not destroyed before rendered ([#53](https://github.com/beyonddunbar/ladon-ts/issues/53)) ([b4b57dd](https://github.com/beyonddunbar/ladon-ts/commit/b4b57dd22365fd43918f801685118333bf5f3f65))
11
+
12
+ ### 5.4.3 (2025-03-26)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * wasm bundle issue ([#27](https://github.com/beyonddunbar/ladon-ts/issues/27)) ([dd46a40](https://github.com/beyonddunbar/ladon-ts/commit/dd46a406d83de0b0588282dbb98835bcf858707c))
18
+
5
19
  ### 5.4.2 (2025-03-26)
6
20
 
7
21
 
@@ -1,11 +1,11 @@
1
- import { WorkerGenerator as n, LocalGenerator as h } from "./generator.js";
2
- import { BlurRenderer as o } from "./renderer/blur-renderer.js";
3
- import { PassthroughRenderer as u } from "./renderer/passthrough-renderer.js";
4
- import { ReplacementRenderer as d } from "./renderer/replacement-renderer.js";
5
- import { resizeImage as m } from "./util.js";
6
- class w {
7
- constructor(e, t, s, i) {
8
- this.inputCanvas = e, this.outputCanvas = t, this.config = s, this.videoElement = i, this.loaded = !1, this.frameQueue = [], this.maskQueue = [];
1
+ import { WorkerGenerator as h, LocalGenerator as o } from "./generator.js";
2
+ import { BlurRenderer as u } from "./renderer/blur-renderer.js";
3
+ import { PassthroughRenderer as d } from "./renderer/passthrough-renderer.js";
4
+ import { ReplacementRenderer as m } from "./renderer/replacement-renderer.js";
5
+ import { resizeImage as l } from "./util.js";
6
+ class R {
7
+ constructor(e, t, s, r) {
8
+ this.inputCanvas = e, this.outputCanvas = t, this.config = s, this.videoElement = r, this.loaded = !1, this.frameQueue = [], this.maskQueue = [];
9
9
  const a = this.inputCanvas.getContext("2d", {
10
10
  alpha: !1,
11
11
  willReadFrequently: !0
@@ -25,10 +25,10 @@ class w {
25
25
  async onFrame(e, t = !0) {
26
26
  if (!this.generator || !this.renderer)
27
27
  throw new Error("[ladon-ts] - pipeline is not loaded.");
28
- const s = this.getInputFrameData(t);
29
- if (this.frameQueue.push({ timestamp: e, frameData: s }), t || !this.lastMaskResult) {
30
- const i = s, a = this.getMaskImageData(i), r = await this.generator.getLadonOutput(a, e);
31
- this.maskQueue.push({ timestamp: r.timestamp, maskResult: r }), this.lastMaskResult = r, await this.renderSyncedFrameAndMask(), await this.generator.postRender(a, e);
28
+ const s = this.getInputFrameData(t), r = s instanceof Uint8ClampedArray ? new Uint8ClampedArray(s) : s;
29
+ if (this.frameQueue.push({ timestamp: e, frameData: r }), t || !this.lastMaskResult) {
30
+ const a = s, n = this.getMaskImageData(a), i = await this.generator.getLadonOutput(n, e);
31
+ this.maskQueue.push({ timestamp: i.timestamp, maskResult: i }), this.lastMaskResult = i, await this.renderSyncedFrameAndMask(), await this.generator.postRender(n, e);
32
32
  } else
33
33
  this.maskQueue.push({ timestamp: e, maskResult: this.lastMaskResult }), await this.renderSyncedFrameAndMask();
34
34
  return this.createInferenceResult(e, t);
@@ -65,7 +65,7 @@ class w {
65
65
  * @returns {Uint8ClampedArray} - The processed mask image data.
66
66
  */
67
67
  getMaskImageData(e) {
68
- return this.config.mask.inputSize.width === this.config.input.width ? e : m(
68
+ return this.config.mask.inputSize.width === this.config.input.width ? e : l(
69
69
  e,
70
70
  this.config.input.width,
71
71
  this.config.input.height,
@@ -109,23 +109,23 @@ class w {
109
109
  async load() {
110
110
  switch (this.config.render.type) {
111
111
  case "blur":
112
- this.renderer = new o(this.outputCanvas, this.config);
112
+ this.renderer = new u(this.outputCanvas, this.config);
113
113
  break;
114
114
  case "passthrough":
115
- this.renderer = new u(this.outputCanvas, this.config);
115
+ this.renderer = new d(this.outputCanvas, this.config);
116
116
  break;
117
117
  case "replacement":
118
- this.renderer = new d(this.outputCanvas, this.config);
118
+ this.renderer = new m(this.outputCanvas, this.config);
119
119
  break;
120
120
  default:
121
121
  throw new Error("[ladon-ts] - renderer not implemented.");
122
122
  }
123
123
  switch (await this.renderer.load(), this.config.mask.generator) {
124
124
  case "local":
125
- this.generator = new h(this.config);
125
+ this.generator = new o(this.config);
126
126
  break;
127
127
  case "worker":
128
- this.generator = new n(this.config);
128
+ this.generator = new h(this.config);
129
129
  break;
130
130
  default:
131
131
  throw new Error("[ladon-ts] - generator not implemented.");
@@ -150,6 +150,6 @@ class w {
150
150
  }
151
151
  }
152
152
  export {
153
- w as Pipeline
153
+ R as Pipeline
154
154
  };
155
155
  //# sourceMappingURL=pipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport {\n FacesAndLandmarks,\n FrameData,\n LadonOutput,\n LocalGenerator,\n MaskGenerator,\n WorkerGenerator,\n} from './generator';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\nexport interface IFrameInferenceResult {\n facesAndLandmarks: FacesAndLandmarks;\n gesture: Float32Array;\n motion: number;\n skipped: boolean;\n timestamp: number;\n}\n\nexport interface IPipeline {\n onFrame(timestamp: number, maskNeeded?: boolean): Promise<IFrameInferenceResult>;\n isLoaded(): boolean;\n load(): Promise<void>;\n destroy(): void;\n}\n\nexport class Pipeline implements IPipeline {\n private generator?: MaskGenerator;\n private renderer?: LadonRenderer;\n private loaded = false;\n\n private inputCanvasCtx: CanvasRenderingContext2D;\n private lastMaskResult?: LadonOutput;\n\n private frameQueue: Array<{ timestamp: number; frameData: FrameData }> = [];\n private maskQueue: Array<{ timestamp: number; maskResult: LadonOutput }> = [];\n\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly videoElement?: HTMLVideoElement\n ) {\n const inputCanvasCtx = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasCtx) {\n throw new Error('[ladon-ts] - error getting input context 2d');\n }\n this.inputCanvasCtx = inputCanvasCtx;\n }\n\n /**\n * Processes a single frame and returns the inference result.\n *\n * @param timestamp - The timestamp of the frame.\n * @param performInference - Flag to perform mask inference.\n * @returns The result of the frame processing.\n * @throws If the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true\n ): Promise<IFrameInferenceResult> {\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] - pipeline is not loaded.');\n }\n\n // Get the current frame (raw image data or video element)\n const frameData = this.getInputFrameData(performInference);\n\n // Add the frame to the queue with its timestamp\n this.frameQueue.push({ timestamp, frameData });\n\n if (performInference || !this.lastMaskResult) {\n // Perform inference and add the new mask to the mask queue\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n const maskResult = await this.generator.getLadonOutput(maskImageData, timestamp);\n // Add the mask to the queue and use the mask timestamp to sync frames and masks.\n this.maskQueue.push({ timestamp: maskResult.timestamp, maskResult });\n this.lastMaskResult = maskResult;\n\n // Sync frames and masks to render together\n await this.renderSyncedFrameAndMask();\n\n // Post-process the mask after rendering\n await this.generator.postRender(maskImageData, timestamp);\n } else {\n // Skipping inference. Update the last mask result's timestamp to match the current frame.\n this.maskQueue.push({ timestamp, maskResult: this.lastMaskResult });\n // Sync frames and masks to render together\n await this.renderSyncedFrameAndMask();\n }\n\n // Return the inference result\n return this.createInferenceResult(timestamp, performInference);\n }\n\n /**\n * Synchronizes frames and masks to ensure they are rendered together.\n */\n private async renderSyncedFrameAndMask(): Promise<void> {\n while (this.frameQueue.length > 0 && this.maskQueue.length > 0) {\n const frame = this.frameQueue[0];\n const maskEntry = this.maskQueue[0];\n\n if (frame.timestamp === maskEntry.timestamp) {\n // Render the frame and mask together\n await this.renderer?.render(frame.frameData, maskEntry.maskResult);\n this.frameQueue.shift();\n this.maskQueue.shift();\n } else if (frame.timestamp < maskEntry.timestamp) {\n // Discard the older frame\n this.frameQueue.shift();\n } else {\n // Discard the older mask\n this.maskQueue.shift();\n }\n }\n }\n\n /**\n * Gets the raw image data from the input canvas context.\n *\n * @param needFreshImageData - Flag to indicate if fresh image data is needed.\n *\n * @returns The raw image data.\n */\n private getInputFrameData(needFreshImageData: boolean): FrameData {\n if (needFreshImageData || !this.videoElement) {\n return this.inputCanvasCtx.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n return this.videoElement;\n }\n\n /**\n * Resizes the image data if the model expects a fixed size input.\n *\n * @param {Uint8ClampedArray} rawImageData - The raw image data.\n * @returns {Uint8ClampedArray} - The processed mask image data.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n // Check if resizing is needed based on configuration.\n if (this.config.mask.inputSize.width === this.config.input.width) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n this.config.input.width,\n this.config.input.height,\n this.config.mask.inputSize.width,\n this.config.mask.inputSize.height\n );\n }\n\n /**\n * Creates the inference result object.\n *\n * @param {number} timestamp - The timestamp of the frame.\n * @param {boolean} performedInference - Indicates if the inference was performed.\n * @returns {IFrameInferenceResult} - The result object containing inference details.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n };\n }\n\n /**\n * Checks if the pipeline is loaded.\n *\n * @returns True if the pipeline is loaded, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Loads the pipeline components such as the generator and renderer.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] - renderer not implemented.');\n }\n\n await this.renderer.load();\n\n switch (this.config.mask.generator) {\n case 'local':\n this.generator = new LocalGenerator(this.config);\n break;\n case 'worker':\n this.generator = new WorkerGenerator(this.config);\n break;\n default:\n throw new Error('[ladon-ts] - generator not implemented.');\n }\n\n await this.generator.load();\n\n this.loaded = true;\n }\n\n /**\n * Resets the frame and mask queues.\n */\n private resetQueues(): void {\n this.frameQueue = [];\n this.maskQueue = [];\n }\n\n public reset(): void {\n this.resetQueues();\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroys the pipeline and cleans up resources.\n */\n public destroy(): void {\n this.reset();\n this.generator?.destroy();\n this.renderer?.destroy();\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","videoElement","inputCanvasCtx","timestamp","performInference","frameData","rawImageData","maskImageData","maskResult","frame","maskEntry","_a","needFreshImageData","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","LocalGenerator","WorkerGenerator","_b"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.js","sources":["../../../src/lib/pipeline.ts"],"sourcesContent":["import { PipelineConfig } from './config-types';\nimport {\n FacesAndLandmarks,\n FrameData,\n LadonOutput,\n LocalGenerator,\n MaskGenerator,\n WorkerGenerator,\n} from './generator';\nimport { BlurRenderer, LadonRenderer, PassthroughRenderer, ReplacementRenderer } from './renderer';\nimport { resizeImage } from './util';\n\nexport interface IFrameInferenceResult {\n facesAndLandmarks: FacesAndLandmarks;\n gesture: Float32Array;\n motion: number;\n skipped: boolean;\n timestamp: number;\n}\n\nexport interface IPipeline {\n onFrame(timestamp: number, maskNeeded?: boolean): Promise<IFrameInferenceResult>;\n isLoaded(): boolean;\n load(): Promise<void>;\n destroy(): void;\n}\n\nexport class Pipeline implements IPipeline {\n private generator?: MaskGenerator;\n private renderer?: LadonRenderer;\n private loaded = false;\n\n private inputCanvasCtx: CanvasRenderingContext2D;\n private lastMaskResult?: LadonOutput;\n\n private frameQueue: Array<{ timestamp: number; frameData: FrameData }> = [];\n private maskQueue: Array<{ timestamp: number; maskResult: LadonOutput }> = [];\n\n constructor(\n private inputCanvas: HTMLCanvasElement,\n private outputCanvas: HTMLCanvasElement,\n public readonly config: PipelineConfig,\n private readonly videoElement?: HTMLVideoElement\n ) {\n const inputCanvasCtx = this.inputCanvas.getContext('2d', {\n alpha: false,\n willReadFrequently: true,\n });\n\n if (!inputCanvasCtx) {\n throw new Error('[ladon-ts] - error getting input context 2d');\n }\n this.inputCanvasCtx = inputCanvasCtx;\n }\n\n /**\n * Processes a single frame and returns the inference result.\n *\n * @param timestamp - The timestamp of the frame.\n * @param performInference - Flag to perform mask inference.\n * @returns The result of the frame processing.\n * @throws If the pipeline (generator or renderer) is not loaded.\n */\n public async onFrame(\n timestamp: number,\n performInference = true\n ): Promise<IFrameInferenceResult> {\n if (!this.generator || !this.renderer) {\n throw new Error('[ladon-ts] - pipeline is not loaded.');\n }\n\n // Get the current frame (raw image data or video element)\n const frameData = this.getInputFrameData(performInference);\n\n // Create a copy of the frame data if it's an array\n const frameDataCopy =\n frameData instanceof Uint8ClampedArray ? new Uint8ClampedArray(frameData) : frameData;\n\n // Add the frame to the queue with its timestamp\n this.frameQueue.push({ timestamp, frameData: frameDataCopy });\n\n if (performInference || !this.lastMaskResult) {\n // Perform inference and add the new mask to the mask queue\n const rawImageData = frameData as Uint8ClampedArray;\n const maskImageData = this.getMaskImageData(rawImageData);\n const maskResult = await this.generator.getLadonOutput(maskImageData, timestamp);\n // Add the mask to the queue and use the mask timestamp to sync frames and masks.\n this.maskQueue.push({ timestamp: maskResult.timestamp, maskResult });\n this.lastMaskResult = maskResult;\n\n // Sync frames and masks to render together\n await this.renderSyncedFrameAndMask();\n\n // Post-process the mask after rendering\n await this.generator.postRender(maskImageData, timestamp);\n } else {\n // Skipping inference. Update the last mask result's timestamp to match the current frame.\n this.maskQueue.push({ timestamp, maskResult: this.lastMaskResult });\n // Sync frames and masks to render together\n await this.renderSyncedFrameAndMask();\n }\n\n // Return the inference result\n return this.createInferenceResult(timestamp, performInference);\n }\n\n /**\n * Synchronizes frames and masks to ensure they are rendered together.\n */\n private async renderSyncedFrameAndMask(): Promise<void> {\n while (this.frameQueue.length > 0 && this.maskQueue.length > 0) {\n const frame = this.frameQueue[0];\n const maskEntry = this.maskQueue[0];\n\n if (frame.timestamp === maskEntry.timestamp) {\n // Render the frame and mask together\n await this.renderer?.render(frame.frameData, maskEntry.maskResult);\n this.frameQueue.shift();\n this.maskQueue.shift();\n } else if (frame.timestamp < maskEntry.timestamp) {\n // Discard the older frame\n this.frameQueue.shift();\n } else {\n // Discard the older mask\n this.maskQueue.shift();\n }\n }\n }\n\n /**\n * Gets the raw image data from the input canvas context.\n *\n * @param needFreshImageData - Flag to indicate if fresh image data is needed.\n *\n * @returns The raw image data.\n */\n private getInputFrameData(needFreshImageData: boolean): FrameData {\n if (needFreshImageData || !this.videoElement) {\n return this.inputCanvasCtx.getImageData(\n 0,\n 0,\n this.config.input.width,\n this.config.input.height\n ).data;\n }\n\n return this.videoElement;\n }\n\n /**\n * Resizes the image data if the model expects a fixed size input.\n *\n * @param {Uint8ClampedArray} rawImageData - The raw image data.\n * @returns {Uint8ClampedArray} - The processed mask image data.\n */\n private getMaskImageData(rawImageData: Uint8ClampedArray): Uint8ClampedArray {\n // Check if resizing is needed based on configuration.\n if (this.config.mask.inputSize.width === this.config.input.width) {\n return rawImageData;\n }\n\n // Resize the image data.\n return resizeImage(\n rawImageData,\n this.config.input.width,\n this.config.input.height,\n this.config.mask.inputSize.width,\n this.config.mask.inputSize.height\n );\n }\n\n /**\n * Creates the inference result object.\n *\n * @param {number} timestamp - The timestamp of the frame.\n * @param {boolean} performedInference - Indicates if the inference was performed.\n * @returns {IFrameInferenceResult} - The result object containing inference details.\n */\n private createInferenceResult(\n timestamp: number,\n performedInference: boolean\n ): IFrameInferenceResult {\n if (!this.lastMaskResult) {\n return {\n facesAndLandmarks: { data: new Float32Array(0), dims: [] },\n gesture: new Float32Array(0),\n motion: 0,\n skipped: true,\n timestamp,\n };\n }\n\n return {\n facesAndLandmarks: this.lastMaskResult.facesAndLandmarks,\n gesture: this.lastMaskResult.gesture,\n motion: this.lastMaskResult.motion,\n skipped: !performedInference,\n timestamp,\n };\n }\n\n /**\n * Checks if the pipeline is loaded.\n *\n * @returns True if the pipeline is loaded, false otherwise.\n */\n public isLoaded(): boolean {\n return this.loaded;\n }\n\n /**\n * Loads the pipeline components such as the generator and renderer.\n */\n public async load(): Promise<void> {\n switch (this.config.render.type) {\n case 'blur':\n this.renderer = new BlurRenderer(this.outputCanvas, this.config);\n break;\n case 'passthrough':\n this.renderer = new PassthroughRenderer(this.outputCanvas, this.config);\n break;\n case 'replacement':\n this.renderer = new ReplacementRenderer(this.outputCanvas, this.config);\n break;\n default:\n throw new Error('[ladon-ts] - renderer not implemented.');\n }\n\n await this.renderer.load();\n\n switch (this.config.mask.generator) {\n case 'local':\n this.generator = new LocalGenerator(this.config);\n break;\n case 'worker':\n this.generator = new WorkerGenerator(this.config);\n break;\n default:\n throw new Error('[ladon-ts] - generator not implemented.');\n }\n\n await this.generator.load();\n\n this.loaded = true;\n }\n\n /**\n * Resets the frame and mask queues.\n */\n private resetQueues(): void {\n this.frameQueue = [];\n this.maskQueue = [];\n }\n\n public reset(): void {\n this.resetQueues();\n this.lastMaskResult = undefined;\n }\n\n /**\n * Destroys the pipeline and cleans up resources.\n */\n public destroy(): void {\n this.reset();\n this.generator?.destroy();\n this.renderer?.destroy();\n }\n}\n"],"names":["Pipeline","inputCanvas","outputCanvas","config","videoElement","inputCanvasCtx","timestamp","performInference","frameData","frameDataCopy","rawImageData","maskImageData","maskResult","frame","maskEntry","_a","needFreshImageData","resizeImage","performedInference","BlurRenderer","PassthroughRenderer","ReplacementRenderer","LocalGenerator","WorkerGenerator","_b"],"mappings":"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"}
@@ -14,8 +14,8 @@ class U {
14
14
  async initializeWorker(e) {
15
15
  if (typeof Worker > "u")
16
16
  throw new Error("[ladon-ts] web workers are not supported in this environment");
17
- const r = await this.resolveUri(e, e.mask.workerUri), t = await (await fetch(r, { cache: "force-cache" })).text();
18
- return this.workerUrl = URL.createObjectURL(new Blob([t], { type: "text/javascript" })), new Worker(this.workerUrl, { type: "module" });
17
+ const r = await this.resolveUri(e, e.mask.workerUri), s = await (await fetch(r, { cache: "force-cache" })).text();
18
+ return this.workerUrl = URL.createObjectURL(new Blob([s], { type: "text/javascript" })), new Worker(this.workerUrl, { type: "module" });
19
19
  }
20
20
  /**
21
21
  * Terminate the worker.
@@ -31,28 +31,27 @@ class U {
31
31
  * @returns A promise that resolves when the model is preloaded.
32
32
  */
33
33
  async loadModel(e) {
34
- const { mask: r, executionProviders: s } = e;
34
+ const { mask: r, executionProviders: o } = e;
35
35
  i.env.wasm.numThreads = 1;
36
- const t = await this.resolveUri(e, e.wasmUri);
37
- if (t)
36
+ const s = await this.resolveUri(e, e.wasmUri);
37
+ if (s)
38
38
  try {
39
- i.env.wasm.wasmBinary = await h(t + k);
39
+ i.env.wasm.wasmBinary = await h(s + k);
40
40
  } catch (w) {
41
- console.warn("Failed to load or cache WASM file. Using WASM paths.", w), i.env.wasm.wasmPaths = t;
41
+ console.warn("Failed to load or cache WASM file. Using WASM paths.", w), i.env.wasm.wasmPaths = s;
42
42
  }
43
43
  const a = await this.resolveUri(e, r.modelUri);
44
- let o = this.modelSessions[a];
45
- if (!o) {
46
- const n = { executionProviders: s != null && s.length ? s : ["wasm"] };
44
+ let t = this.modelSessions[a];
45
+ if (!t) {
46
+ const n = { executionProviders: o != null && o.length ? o : ["wasm"] };
47
47
  try {
48
48
  const l = await h(a);
49
- o = await i.InferenceSession.create(l, n);
49
+ t = await i.InferenceSession.create(l, n);
50
50
  } catch (l) {
51
- console.warn("Failed to load or cache model file. Using model URL.", l), o = await i.InferenceSession.create(a, n);
51
+ console.warn("Failed to load or cache model file. Using model URL.", l), t = await i.InferenceSession.create(a, n);
52
52
  }
53
- o = await i.InferenceSession.create(a, n);
54
53
  }
55
- return this.modelSessions[a] = o, await this.warmupModel(o, r.inputSize, r.modelRank), o;
54
+ return this.modelSessions[a] = t, await this.warmupModel(t, r.inputSize, r.modelRank), t;
56
55
  }
57
56
  /**
58
57
  * Preload the worker using the provided configuration.
@@ -75,15 +74,15 @@ class U {
75
74
  * @example
76
75
  * await warmupModel(session, inputSize, inputChannels, inputType);
77
76
  */
78
- async warmupModel(e, r = { width: 1280, height: 720 }, s = 3, t = "float32") {
79
- const a = r.height * r.width, o = new Float32Array(s * a).fill(0), w = new i.Tensor(t, o, [
77
+ async warmupModel(e, r = { width: 1280, height: 720 }, o = 3, s = "float32") {
78
+ const a = r.height * r.width, t = new Float32Array(o * a).fill(0), w = new i.Tensor(s, t, [
80
79
  1,
81
- s,
80
+ o,
82
81
  r.height,
83
82
  r.width
84
83
  ]), n = { [e.inputNames[0]]: w };
85
- if (s === 3) {
86
- const l = new Float32Array(a).fill(0), d = new i.Tensor(t, l, [
84
+ if (o === 3) {
85
+ const l = new Float32Array(a).fill(0), d = new i.Tensor(s, l, [
87
86
  1,
88
87
  1,
89
88
  r.height,
@@ -128,10 +127,10 @@ class U {
128
127
  throw new Error("Asset URI is undefined.");
129
128
  if (this.resolvedUriCache.has(r))
130
129
  return this.resolvedUriCache.get(r);
131
- let s;
132
- if (c(r) ? s = r : r.match(/\.[a-z0-9]+$/i) ? typeof e.assetUrlResolver == "function" && (s = await e.assetUrlResolver(r, e)) : s = m(r, e.baseUrl), !s)
130
+ let o;
131
+ if (c(r) ? o = r : r.match(/\.[a-z0-9]+$/i) ? typeof e.assetUrlResolver == "function" && (o = await e.assetUrlResolver(r, e)) : o = m(r, e.baseUrl), !o)
133
132
  throw new Error(`Failed to resolve asset URI: ${r}`);
134
- return this.resolvedUriCache.set(r, s), s;
133
+ return this.resolvedUriCache.set(r, o), o;
135
134
  }
136
135
  getWorker() {
137
136
  return this.worker;
@@ -148,7 +147,7 @@ class U {
148
147
  * @returns The worker configuration.
149
148
  */
150
149
  async prepareWorkerConfig(e) {
151
- const s = { ...{
150
+ const o = { ...{
152
151
  ...e,
153
152
  mask: {
154
153
  ...e.mask,
@@ -157,11 +156,11 @@ class U {
157
156
  },
158
157
  wasmUri: await this.resolveUri(e, e.wasmUri)
159
158
  }, assetUrlResolver: void 0 };
160
- return JSON.parse(JSON.stringify(s));
159
+ return JSON.parse(JSON.stringify(o));
161
160
  }
162
161
  }
163
- const f = new U();
162
+ const p = new U();
164
163
  export {
165
- f as sharedResourceManager
164
+ p as sharedResourceManager
166
165
  };
167
166
  //# sourceMappingURL=shared-resource-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-resource-manager.js","sources":["../../../src/lib/shared-resource-manager.ts"],"sourcesContent":["import * as ort from 'onnxruntime-web';\n\nimport { PreloadConfig, ResolutionConfig, WorkerConfig } from './config-types';\nimport { isResolvedUri, loadAndCacheFile, toAbsoluteURI } from './util';\n\n// There is only a single WASM file for all WebAssembly binaries now.\nconst ONNX_WASM_FILENAME = 'ort-wasm-simd-threaded.wasm';\n\n/**\n * Shared resource manager used to preload and share resources across different pipelines.\n * This is useful when multiple pipelines are using the same model or when the model needs\n * to be preloaded before the pipeline is created.\n */\nclass SharedResourceManager {\n private modelSessions: Record<string, ort.InferenceSession> = {};\n private worker?: Worker;\n private workerUrl?: string;\n\n // In-memory cache for resolved URLs\n private resolvedUriCache: Map<string, string> = new Map();\n\n /**\n * Initialize the worker using the provided configuration.\n *\n * @param config - The configuration object.\n * @returns A promise that resolves when the worker is initialized.\n */\n public async initializeWorker(config: WorkerConfig): Promise<Worker> {\n if (typeof Worker === 'undefined') {\n throw new Error('[ladon-ts] web workers are not supported in this environment');\n }\n\n const scriptUri = await this.resolveUri(config, config.mask.workerUri);\n const response = await fetch(scriptUri, { cache: 'force-cache' });\n const workerSource = await response.text();\n\n this.workerUrl = URL.createObjectURL(new Blob([workerSource], { type: 'text/javascript' }));\n\n return new Worker(this.workerUrl, { type: 'module' });\n }\n\n /**\n * Terminate the worker.\n */\n public terminateWorker() {\n if (this.worker) {\n this.worker.terminate();\n this.worker = undefined;\n }\n }\n\n /**\n * Load the model using the provided configuration. This gets called by the worker when using\n * the worker generator.\n *\n * @param config - The configuration object.\n * @returns A promise that resolves when the model is preloaded.\n */\n public async loadModel(config: PreloadConfig) {\n const { mask, executionProviders: providers } = config;\n\n // Lock to a single thread to avoid performance issues.\n ort.env.wasm.numThreads = 1;\n\n // Get the resolved paths for the WASM binary.\n const wasmPaths = await this.resolveUri(config, config.wasmUri);\n\n if (wasmPaths) {\n try {\n ort.env.wasm.wasmBinary = await loadAndCacheFile(wasmPaths + ONNX_WASM_FILENAME);\n } catch (error) {\n console.warn('Failed to load or cache WASM file. Using WASM paths.', error);\n ort.env.wasm.wasmPaths = wasmPaths;\n }\n }\n\n // Resolve the model URL.\n const modelUrl = await this.resolveUri(config, mask.modelUri);\n let modelSession = this.modelSessions[modelUrl];\n\n // If the model session is not already cached, create a new session and cache.\n if (!modelSession) {\n // Allows the user to specify the execution providers to use. The default is the WASM provider.\n // TODO: Add and test support for other execution providers.\n const executionProviders = providers?.length ? providers : ['wasm'];\n const sessionOptions = { executionProviders };\n\n // First try to get the model binary either from cache or by fetching and cacheing it.\n try {\n const modelBinary = await loadAndCacheFile(modelUrl);\n modelSession = await ort.InferenceSession.create(modelBinary, sessionOptions);\n } catch (error) {\n console.warn('Failed to load or cache model file. Using model URL.', error);\n modelSession = await ort.InferenceSession.create(modelUrl, sessionOptions);\n }\n modelSession = await ort.InferenceSession.create(modelUrl, sessionOptions);\n }\n\n // Cache the model session.\n this.modelSessions[modelUrl] = modelSession;\n\n // Warmup the model with a dummy input to avoid latency when running the model for the first time.\n await this.warmupModel(modelSession, mask.inputSize, mask.modelRank);\n\n return modelSession;\n }\n\n /**\n * Preload the worker using the provided configuration.\n *\n * @param config - The configuration object.\n * @returns A promise that resolves when the worker is preloaded.\n */\n public async preloadWorker(config: PreloadConfig) {\n const workerConfig = await this.prepareWorkerConfig(config);\n\n this.worker = await this.initializeWorker(workerConfig);\n this.worker.postMessage({ type: 'preload', config: workerConfig });\n }\n\n /**\n * Warmup the model session with a dummy input to avoid latency when running the model for the first time.\n *\n * @param {ort.InferenceSession} session The model session.\n * @param {ResolutionConfig} inputSize The input size of the model.\n * @param {number} inputChannels The number of input channels (e.g., RGB is 3).\n * @param {ort.Tensor.Type} inputType The input tensor type.\n * @returns {Promise<void>} A promise that resolves when the model is warmed up.\n * @example\n * await warmupModel(session, inputSize, inputChannels, inputType);\n */\n public async warmupModel(\n session: ort.InferenceSession,\n inputSize: ResolutionConfig = { width: 1280, height: 720 },\n inputChannels = 3,\n inputType: ort.Tensor.Type = 'float32'\n ): Promise<void> {\n const numPixels = inputSize.height * inputSize.width;\n const dummyInputData = new Float32Array(inputChannels * numPixels).fill(0);\n const dummyInput = new ort.Tensor(inputType, dummyInputData, [\n 1,\n inputChannels,\n inputSize.height,\n inputSize.width,\n ]);\n const feed = { [session.inputNames[0]]: dummyInput };\n\n // Use an additional dummy tensor if the model expects a previous mask.\n if (inputChannels === 3) {\n const dummyMaskData = new Float32Array(numPixels).fill(0);\n const prevMaskTensor = new ort.Tensor(inputType, dummyMaskData, [\n 1,\n 1,\n inputSize.height,\n inputSize.width,\n ]);\n feed[session.inputNames[1]] = prevMaskTensor;\n }\n\n // Run the model with the dummy input to warm it up.\n await session.run(feed);\n }\n\n /**\n * Get the model session for the provided model URL.\n *\n * @param {string} modelUrl The model URL.\n * @returns {ort.InferenceSession} The model session.\n * @example\n * const session = getModelSession(modelUrl);\n */\n public getModelSession(modelUrl: string) {\n return this.modelSessions[modelUrl];\n }\n\n /**\n * Resolves the full URL for a given asset. Non-file URIs are treated as paths\n * and resolved using the `toAbsoluteURI` method.\n *\n * @param config - The preload configuration.\n * @param assetUri - The asset URI from the config.\n * @returns The resolved full URL.\n */\n /**\n * Resolves the full URL for a given asset. Non-file URIs are treated as paths and resolved\n * using the `toAbsoluteURI` method. If the assetUri is already resolved, or if it's found in\n * cache, it will be returned directly. If an assetUrlResolver function is provided in the\n * configuration, it will be used to resolve the URL if the assetUri is not fully qualified,\n * a blob URL, or in the cache.\n *\n * @param config - The preload configuration.\n * @param assetUri - The asset URI from the config.\n * @returns The resolved full URL.\n */\n public async resolveUri(config: PreloadConfig, assetUri?: string): Promise<string> {\n if (!assetUri) {\n throw new Error('Asset URI is undefined.');\n }\n\n // Check if the URL is already cached\n if (this.resolvedUriCache.has(assetUri)) {\n return this.resolvedUriCache.get(assetUri) as string;\n }\n\n let resolvedUrl: string | undefined;\n\n if (isResolvedUri(assetUri)) {\n // If the assetUri is fully qualified or a blob URL, return it directly.\n // (Assets uris are resolved before being sent to the worker.)\n resolvedUrl = assetUri;\n } else if (!assetUri.match(/\\.[a-z0-9]+$/i)) {\n // Resolve as an absolute path to a non-file endpoint (e.g., wasmPaths).\n resolvedUrl = toAbsoluteURI(assetUri, config.baseUrl);\n } else if (typeof config.assetUrlResolver === 'function') {\n // Resolve via assetUrlResolver. This delegates the resolution logic to the caller.\n resolvedUrl = await config.assetUrlResolver(assetUri, config);\n }\n\n if (!resolvedUrl) {\n throw new Error(`Failed to resolve asset URI: ${assetUri}`);\n }\n\n // Cache the resolved URL\n this.resolvedUriCache.set(assetUri, resolvedUrl);\n\n return resolvedUrl;\n }\n\n public getWorker() {\n return this.worker;\n }\n\n public getWorkerUrl() {\n return this.workerUrl;\n }\n\n /**\n * Prepare the worker configuration by resolving the final URLs and paths to send to the\n * worker. This is necessary because we can't send functions to the worker. We also need to\n * remove the assetUrlResolver function from the configuration.\n *\n * @param config - The preload configuration.\n * @returns The worker configuration.\n */\n public async prepareWorkerConfig(config: PreloadConfig) {\n // Resolve and cache the URIs before sending the config to the worker. Future calls to the\n // resolveUri method will use the cache.\n const resolvedConfig: PreloadConfig = {\n ...config,\n mask: {\n ...config.mask,\n modelUri: await this.resolveUri(config, config.mask.modelUri),\n workerUri: await this.resolveUri(config, config.mask.workerUri),\n },\n wasmUri: await this.resolveUri(config, config.wasmUri),\n };\n\n // We can't send the assetUrlResolver function to the worker so we remove it from the config.\n const workerConfig = { ...resolvedConfig, assetUrlResolver: undefined };\n\n // This is a workaround to ensure the worker configuration can be sent to the worker.\n return JSON.parse(JSON.stringify(workerConfig));\n }\n}\n\n// Global instance of the shared resource manager.\nexport const sharedResourceManager = new SharedResourceManager();\n"],"names":["ONNX_WASM_FILENAME","SharedResourceManager","config","scriptUri","workerSource","mask","providers","ort","wasmPaths","loadAndCacheFile","error","modelUrl","modelSession","sessionOptions","modelBinary","workerConfig","session","inputSize","inputChannels","inputType","numPixels","dummyInputData","dummyInput","feed","dummyMaskData","prevMaskTensor","assetUri","resolvedUrl","isResolvedUri","toAbsoluteURI","sharedResourceManager"],"mappings":"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"}
1
+ {"version":3,"file":"shared-resource-manager.js","sources":["../../../src/lib/shared-resource-manager.ts"],"sourcesContent":["import * as ort from 'onnxruntime-web';\n\nimport { PreloadConfig, ResolutionConfig, WorkerConfig } from './config-types';\nimport { isResolvedUri, loadAndCacheFile, toAbsoluteURI } from './util';\n\n// There is only a single WASM file for all WebAssembly binaries now.\nconst ONNX_WASM_FILENAME = 'ort-wasm-simd-threaded.wasm';\n\n/**\n * Shared resource manager used to preload and share resources across different pipelines.\n * This is useful when multiple pipelines are using the same model or when the model needs\n * to be preloaded before the pipeline is created.\n */\nclass SharedResourceManager {\n private modelSessions: Record<string, ort.InferenceSession> = {};\n private worker?: Worker;\n private workerUrl?: string;\n\n // In-memory cache for resolved URLs\n private resolvedUriCache: Map<string, string> = new Map();\n\n /**\n * Initialize the worker using the provided configuration.\n *\n * @param config - The configuration object.\n * @returns A promise that resolves when the worker is initialized.\n */\n public async initializeWorker(config: WorkerConfig): Promise<Worker> {\n if (typeof Worker === 'undefined') {\n throw new Error('[ladon-ts] web workers are not supported in this environment');\n }\n\n const scriptUri = await this.resolveUri(config, config.mask.workerUri);\n const response = await fetch(scriptUri, { cache: 'force-cache' });\n const workerSource = await response.text();\n\n this.workerUrl = URL.createObjectURL(new Blob([workerSource], { type: 'text/javascript' }));\n\n return new Worker(this.workerUrl, { type: 'module' });\n }\n\n /**\n * Terminate the worker.\n */\n public terminateWorker() {\n if (this.worker) {\n this.worker.terminate();\n this.worker = undefined;\n }\n }\n\n /**\n * Load the model using the provided configuration. This gets called by the worker when using\n * the worker generator.\n *\n * @param config - The configuration object.\n * @returns A promise that resolves when the model is preloaded.\n */\n public async loadModel(config: PreloadConfig) {\n const { mask, executionProviders: providers } = config;\n\n // Lock to a single thread to avoid performance issues.\n ort.env.wasm.numThreads = 1;\n\n // Get the resolved paths for the WASM binary.\n const wasmPaths = await this.resolveUri(config, config.wasmUri);\n\n if (wasmPaths) {\n try {\n ort.env.wasm.wasmBinary = await loadAndCacheFile(wasmPaths + ONNX_WASM_FILENAME);\n } catch (error) {\n console.warn('Failed to load or cache WASM file. Using WASM paths.', error);\n ort.env.wasm.wasmPaths = wasmPaths;\n }\n }\n\n // Resolve the model URL.\n const modelUrl = await this.resolveUri(config, mask.modelUri);\n let modelSession = this.modelSessions[modelUrl];\n\n // If the model session is not already cached, create a new session and cache.\n if (!modelSession) {\n // Allows the user to specify the execution providers to use. The default is the WASM provider.\n // TODO: Add and test support for other execution providers.\n const executionProviders = providers?.length ? providers : ['wasm'];\n const sessionOptions = { executionProviders };\n\n // First try to get the model binary either from cache or by fetching and cacheing it.\n try {\n const modelBinary = await loadAndCacheFile(modelUrl);\n modelSession = await ort.InferenceSession.create(modelBinary, sessionOptions);\n } catch (error) {\n console.warn('Failed to load or cache model file. Using model URL.', error);\n modelSession = await ort.InferenceSession.create(modelUrl, sessionOptions);\n }\n }\n\n // Cache the model session.\n this.modelSessions[modelUrl] = modelSession;\n\n // Warmup the model with a dummy input to avoid latency when running the model for the first time.\n await this.warmupModel(modelSession, mask.inputSize, mask.modelRank);\n\n return modelSession;\n }\n\n /**\n * Preload the worker using the provided configuration.\n *\n * @param config - The configuration object.\n * @returns A promise that resolves when the worker is preloaded.\n */\n public async preloadWorker(config: PreloadConfig) {\n const workerConfig = await this.prepareWorkerConfig(config);\n\n this.worker = await this.initializeWorker(workerConfig);\n this.worker.postMessage({ type: 'preload', config: workerConfig });\n }\n\n /**\n * Warmup the model session with a dummy input to avoid latency when running the model for the first time.\n *\n * @param {ort.InferenceSession} session The model session.\n * @param {ResolutionConfig} inputSize The input size of the model.\n * @param {number} inputChannels The number of input channels (e.g., RGB is 3).\n * @param {ort.Tensor.Type} inputType The input tensor type.\n * @returns {Promise<void>} A promise that resolves when the model is warmed up.\n * @example\n * await warmupModel(session, inputSize, inputChannels, inputType);\n */\n public async warmupModel(\n session: ort.InferenceSession,\n inputSize: ResolutionConfig = { width: 1280, height: 720 },\n inputChannels = 3,\n inputType: ort.Tensor.Type = 'float32'\n ): Promise<void> {\n const numPixels = inputSize.height * inputSize.width;\n const dummyInputData = new Float32Array(inputChannels * numPixels).fill(0);\n const dummyInput = new ort.Tensor(inputType, dummyInputData, [\n 1,\n inputChannels,\n inputSize.height,\n inputSize.width,\n ]);\n const feed = { [session.inputNames[0]]: dummyInput };\n\n // Use an additional dummy tensor if the model expects a previous mask.\n if (inputChannels === 3) {\n const dummyMaskData = new Float32Array(numPixels).fill(0);\n const prevMaskTensor = new ort.Tensor(inputType, dummyMaskData, [\n 1,\n 1,\n inputSize.height,\n inputSize.width,\n ]);\n feed[session.inputNames[1]] = prevMaskTensor;\n }\n\n // Run the model with the dummy input to warm it up.\n await session.run(feed);\n }\n\n /**\n * Get the model session for the provided model URL.\n *\n * @param {string} modelUrl The model URL.\n * @returns {ort.InferenceSession} The model session.\n * @example\n * const session = getModelSession(modelUrl);\n */\n public getModelSession(modelUrl: string) {\n return this.modelSessions[modelUrl];\n }\n\n /**\n * Resolves the full URL for a given asset. Non-file URIs are treated as paths\n * and resolved using the `toAbsoluteURI` method.\n *\n * @param config - The preload configuration.\n * @param assetUri - The asset URI from the config.\n * @returns The resolved full URL.\n */\n /**\n * Resolves the full URL for a given asset. Non-file URIs are treated as paths and resolved\n * using the `toAbsoluteURI` method. If the assetUri is already resolved, or if it's found in\n * cache, it will be returned directly. If an assetUrlResolver function is provided in the\n * configuration, it will be used to resolve the URL if the assetUri is not fully qualified,\n * a blob URL, or in the cache.\n *\n * @param config - The preload configuration.\n * @param assetUri - The asset URI from the config.\n * @returns The resolved full URL.\n */\n public async resolveUri(config: PreloadConfig, assetUri?: string): Promise<string> {\n if (!assetUri) {\n throw new Error('Asset URI is undefined.');\n }\n\n // Check if the URL is already cached\n if (this.resolvedUriCache.has(assetUri)) {\n return this.resolvedUriCache.get(assetUri) as string;\n }\n\n let resolvedUrl: string | undefined;\n\n if (isResolvedUri(assetUri)) {\n // If the assetUri is fully qualified or a blob URL, return it directly.\n // (Assets uris are resolved before being sent to the worker.)\n resolvedUrl = assetUri;\n } else if (!assetUri.match(/\\.[a-z0-9]+$/i)) {\n // Resolve as an absolute path to a non-file endpoint (e.g., wasmPaths).\n resolvedUrl = toAbsoluteURI(assetUri, config.baseUrl);\n } else if (typeof config.assetUrlResolver === 'function') {\n // Resolve via assetUrlResolver. This delegates the resolution logic to the caller.\n resolvedUrl = await config.assetUrlResolver(assetUri, config);\n }\n\n if (!resolvedUrl) {\n throw new Error(`Failed to resolve asset URI: ${assetUri}`);\n }\n\n // Cache the resolved URL\n this.resolvedUriCache.set(assetUri, resolvedUrl);\n\n return resolvedUrl;\n }\n\n public getWorker() {\n return this.worker;\n }\n\n public getWorkerUrl() {\n return this.workerUrl;\n }\n\n /**\n * Prepare the worker configuration by resolving the final URLs and paths to send to the\n * worker. This is necessary because we can't send functions to the worker. We also need to\n * remove the assetUrlResolver function from the configuration.\n *\n * @param config - The preload configuration.\n * @returns The worker configuration.\n */\n public async prepareWorkerConfig(config: PreloadConfig) {\n // Resolve and cache the URIs before sending the config to the worker. Future calls to the\n // resolveUri method will use the cache.\n const resolvedConfig: PreloadConfig = {\n ...config,\n mask: {\n ...config.mask,\n modelUri: await this.resolveUri(config, config.mask.modelUri),\n workerUri: await this.resolveUri(config, config.mask.workerUri),\n },\n wasmUri: await this.resolveUri(config, config.wasmUri),\n };\n\n // We can't send the assetUrlResolver function to the worker so we remove it from the config.\n const workerConfig = { ...resolvedConfig, assetUrlResolver: undefined };\n\n // This is a workaround to ensure the worker configuration can be sent to the worker.\n return JSON.parse(JSON.stringify(workerConfig));\n }\n}\n\n// Global instance of the shared resource manager.\nexport const sharedResourceManager = new SharedResourceManager();\n"],"names":["ONNX_WASM_FILENAME","SharedResourceManager","config","scriptUri","workerSource","mask","providers","ort","wasmPaths","loadAndCacheFile","error","modelUrl","modelSession","sessionOptions","modelBinary","workerConfig","session","inputSize","inputChannels","inputType","numPixels","dummyInputData","dummyInput","feed","dummyMaskData","prevMaskTensor","assetUri","resolvedUrl","isResolvedUri","toAbsoluteURI","sharedResourceManager"],"mappings":"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"}
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "5.4.2";
1
+ export declare const LIB_VERSION = "5.4.4";
@@ -1,4 +1,4 @@
1
- const o = "5.4.2";
1
+ const o = "5.4.4";
2
2
  export {
3
3
  o as LIB_VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sources":["../../../src/lib/version.ts"],"sourcesContent":["export const LIB_VERSION = '5.4.2';\n"],"names":["LIB_VERSION"],"mappings":"AAAO,MAAMA,IAAc;"}
1
+ {"version":3,"file":"version.js","sources":["../../../src/lib/version.ts"],"sourcesContent":["export const LIB_VERSION = '5.4.4';\n"],"names":["LIB_VERSION"],"mappings":"AAAO,MAAMA,IAAc;"}