@midscene/shared 1.10.5-beta-20260716133905.0 → 1.10.5

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.
@@ -5,7 +5,7 @@ import { assert } from "../utils.mjs";
5
5
  import { maskConfig, parseJson } from "./helper.mjs";
6
6
  import { initDebugConfig } from "./init-debug.mjs";
7
7
  const MODEL_CONFIG_DOC_URL = 'https://midscenejs.com/model-common-config.html';
8
- const getCurrentVersion = ()=>"1.10.5-beta-20260716133905.0";
8
+ const getCurrentVersion = ()=>"1.10.5";
9
9
  const getInvalidModelFamilyMessage = (modelFamily)=>`Invalid MIDSCENE_MODEL_FAMILY value: ${modelFamily}. Current version v${getCurrentVersion()} accepts the following model families: ${MODEL_FAMILY_VALUES.join(', ')}. You can also visit ${MODEL_CONFIG_DOC_URL} for the latest configuration information.`;
10
10
  const KEYS_MAP = {
11
11
  insight: INSIGHT_MODEL_CONFIG_KEYS,
@@ -1,9 +1,7 @@
1
1
  import node_assert from "node:assert";
2
2
  import { NodeType } from "../constants/index.mjs";
3
- import { ifInNode } from "../utils.mjs";
4
3
  import get_photon from "./get-photon.mjs";
5
- import get_sharp from "./get-sharp.mjs";
6
- import { createImgBase64ByFormat, parseBase64, photonFromBase64, photonToBase64 } from "./transform.mjs";
4
+ import { photonFromBase64, photonToBase64 } from "./transform.mjs";
7
5
  const DIGIT_FONT = {
8
6
  0: [
9
7
  [
@@ -916,45 +914,8 @@ const createSvgOverlay = async (elements, imageWidth, imageHeight, boxPadding =
916
914
  }
917
915
  return overlayPixels;
918
916
  };
919
- async function decodeImageWithSharp(inputImgBase64, size) {
920
- const { body } = parseBase64(inputImgBase64);
921
- const Sharp = await get_sharp();
922
- let pipeline = Sharp(Buffer.from(body, 'base64'));
923
- if (size) pipeline = pipeline.resize(size.width, size.height, {
924
- fit: 'fill',
925
- kernel: 'nearest'
926
- });
927
- const { data, info } = await pipeline.ensureAlpha().raw().toBuffer({
928
- resolveWithObject: true
929
- });
930
- if (!info.width || !info.height || 4 !== info.channels) throw new Error('Image processing failed to produce RGBA pixels');
931
- return {
932
- pixels: new Uint8Array(data),
933
- width: info.width,
934
- height: info.height
935
- };
936
- }
937
- async function encodeRgbaWithSharp(pixels, width, height) {
938
- const Sharp = await get_sharp();
939
- const output = await Sharp(Buffer.from(pixels), {
940
- raw: {
941
- width,
942
- height,
943
- channels: 4
944
- }
945
- }).jpeg({
946
- quality: 90,
947
- chromaSubsampling: '4:4:4'
948
- }).toBuffer();
949
- return createImgBase64ByFormat('jpeg', output.toString('base64'));
950
- }
951
917
  const compositeElementInfoImg = async (options)=>{
952
918
  node_assert(options.inputImgBase64, 'inputImgBase64 is required');
953
- if (ifInNode) {
954
- const { pixels, width, height } = await decodeImageWithSharp(options.inputImgBase64, options.size);
955
- const overlayPixels = await createSvgOverlay(options.elementsPositionInfo, width, height, options.annotationPadding, options.borderThickness, options.prompt, options.centerPoint);
956
- return encodeRgbaWithSharp(blendPixels(pixels, overlayPixels, width, height), width, height);
957
- }
958
919
  const { PhotonImage, SamplingFilter, resize } = await get_photon();
959
920
  let width = 0;
960
921
  let height = 0;
@@ -994,12 +955,6 @@ const compositeElementInfoImg = async (options)=>{
994
955
  };
995
956
  const compositePointMarkerImg = async (options)=>{
996
957
  node_assert(options.inputImgBase64, 'inputImgBase64 is required');
997
- if (ifInNode) {
998
- const { pixels, width, height } = await decodeImageWithSharp(options.inputImgBase64, options.size);
999
- const overlayPixels = new Uint8Array(width * height * 4);
1000
- drawPointMarker(overlayPixels, width, height, options.point, options.radius ?? 14, options.indexId ?? 1);
1001
- return encodeRgbaWithSharp(blendPixels(pixels, overlayPixels, width, height), width, height);
1002
- }
1003
958
  const { PhotonImage, SamplingFilter, resize } = await get_photon();
1004
959
  let width = 0;
1005
960
  let height = 0;
@@ -1,12 +1,12 @@
1
1
  import { getDebug } from "../logger.mjs";
2
- import { ifInBrowser, ifInWorker } from "../utils.mjs";
2
+ import { ifInBrowser, ifInNode, ifInWorker } from "../utils.mjs";
3
3
  const debug = getDebug('img');
4
4
  let photonModule = null;
5
5
  let isInitialized = false;
6
6
  let usingCanvasFallback = false;
7
7
  async function getPhoton() {
8
8
  if (photonModule && isInitialized) return photonModule;
9
- const env = ifInBrowser ? 'browser' : ifInWorker ? 'worker' : 'unknown';
9
+ const env = ifInBrowser ? 'browser' : ifInWorker ? 'worker' : ifInNode ? 'node' : 'unknown';
10
10
  debug(`Loading photon module in ${env} environment`);
11
11
  try {
12
12
  if (ifInBrowser || ifInWorker) {
@@ -14,7 +14,10 @@ async function getPhoton() {
14
14
  if ('function' == typeof photon.default) await photon.default();
15
15
  debug('Photon loaded: @silvia-odwyer/photon (browser/worker)');
16
16
  photonModule = photon;
17
- } else throw new Error('Photon is only available in browser environments');
17
+ } else if (ifInNode) {
18
+ photonModule = await import("@silvia-odwyer/photon-node");
19
+ debug('Photon loaded: @silvia-odwyer/photon-node (node)');
20
+ }
18
21
  if (!photonModule?.PhotonImage) throw new Error('PhotonImage is not available');
19
22
  if (!photonModule.PhotonImage.new_from_byteslice && !photonModule.PhotonImage.new_from_base64) throw new Error('PhotonImage.new_from_byteslice or new_from_base64 is not available');
20
23
  isInitialized = true;
@@ -1,4 +1,4 @@
1
1
  import { imageInfoOfBase64, isValidImageBuffer, isValidJPEGImageBuffer, isValidPNGImageBuffer, validateScreenshotBuffer } from "./info.mjs";
2
- import { createImgBase64ByFormat, cropByRect, httpImg2Base64, inferBase64ImageFormat, localImg2Base64, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlockByBase64, parseBase64, preProcessImageUrl, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, zoomForGPT4o } from "./transform.mjs";
2
+ import { convertImgBufferToJpeg, createImgBase64ByFormat, cropByRect, httpImg2Base64, inferBase64ImageFormat, localImg2Base64, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlockByBase64, parseBase64, preProcessImageUrl, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, zoomForGPT4o } from "./transform.mjs";
3
3
  import { annotateRects, compositeElementInfoImg, compositePointMarkerImg, processImageElementInfo } from "./box-select.mjs";
4
- export { annotateRects, compositeElementInfoImg, compositePointMarkerImg, createImgBase64ByFormat, cropByRect, httpImg2Base64, imageInfoOfBase64, inferBase64ImageFormat, isValidImageBuffer, isValidJPEGImageBuffer, isValidPNGImageBuffer, localImg2Base64, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlockByBase64, parseBase64, preProcessImageUrl, processImageElementInfo, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, validateScreenshotBuffer, zoomForGPT4o };
4
+ export { annotateRects, compositeElementInfoImg, compositePointMarkerImg, convertImgBufferToJpeg, createImgBase64ByFormat, cropByRect, httpImg2Base64, imageInfoOfBase64, inferBase64ImageFormat, isValidImageBuffer, isValidJPEGImageBuffer, isValidPNGImageBuffer, localImg2Base64, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlockByBase64, parseBase64, preProcessImageUrl, processImageElementInfo, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, validateScreenshotBuffer, zoomForGPT4o };
@@ -1,31 +1,13 @@
1
1
  import node_assert from "node:assert";
2
2
  import { Buffer } from "node:buffer";
3
- import { ifInNode } from "../utils.mjs";
4
3
  import get_photon from "./get-photon.mjs";
5
- import get_sharp from "./get-sharp.mjs";
6
4
  async function imageInfoOfBase64(imageBase64) {
5
+ const { PhotonImage } = await get_photon();
7
6
  const base64Data = imageBase64.replace(/^data:image\/\w+;base64,/, '').replace(/\s/g, '');
8
7
  node_assert(base64Data, 'Invalid image: empty base64 data');
9
8
  node_assert(/^[A-Za-z0-9+/]+={0,2}$/.test(base64Data) && base64Data.length % 4 !== 1, 'Invalid image: malformed base64 data');
10
9
  const imageBuffer = Buffer.from(base64Data, 'base64');
11
10
  node_assert(isValidImageBuffer(imageBuffer), 'Invalid image: unsupported format');
12
- if (ifInNode) {
13
- let metadata;
14
- try {
15
- const Sharp = await get_sharp();
16
- metadata = await Sharp(imageBuffer).metadata();
17
- } catch (error) {
18
- throw new Error(`Invalid image: failed to decode base64 data (${error instanceof Error ? error.message : String(error)})`, {
19
- cause: error
20
- });
21
- }
22
- node_assert(metadata.width && metadata.height, 'Invalid image: cannot get width or height');
23
- return {
24
- width: metadata.width,
25
- height: metadata.height
26
- };
27
- }
28
- const { PhotonImage } = await get_photon();
29
11
  let result;
30
12
  try {
31
13
  result = PhotonImage.new_from_base64(base64Data);
@@ -19,7 +19,7 @@ async function resizeAndConvertImgBuffer(inputFormat, inputData, newSize) {
19
19
  node_assert(newSize && newSize.width > 0 && newSize.height > 0, 'newSize must be positive');
20
20
  const resizeStartTime = Date.now();
21
21
  imgDebug(`resizeImg start, target size: ${newSize.width}x${newSize.height}`);
22
- if (ifInNode) {
22
+ if (ifInNode) try {
23
23
  const Sharp = await get_sharp();
24
24
  const metadata = await Sharp(inputData).metadata();
25
25
  const { width: originalWidth, height: originalHeight } = metadata;
@@ -37,6 +37,8 @@ async function resizeAndConvertImgBuffer(inputFormat, inputData, newSize) {
37
37
  buffer: resizedBuffer,
38
38
  format: 'jpeg'
39
39
  };
40
+ } catch (error) {
41
+ imgDebug('Sharp failed, falling back to Photon:', error);
40
42
  }
41
43
  const { PhotonImage, SamplingFilter, resize } = await get_photon();
42
44
  const inputBytes = new Uint8Array(inputData);
@@ -68,6 +70,23 @@ async function resizeAndConvertImgBuffer(inputFormat, inputData, newSize) {
68
70
  };
69
71
  }
70
72
  const normalizeBase64Body = (body)=>body.replace(/\s/g, '');
73
+ async function convertImgBufferToJpeg(inputData, quality = 90) {
74
+ if (ifInNode) try {
75
+ const Sharp = await get_sharp();
76
+ return await Sharp(inputData).jpeg({
77
+ quality
78
+ }).toBuffer();
79
+ } catch (error) {
80
+ imgDebug('Sharp failed, falling back to Photon:', error);
81
+ }
82
+ const mimeType = detectImageMimeTypeFromBuffer(inputData) ?? 'image/png';
83
+ const photonImage = await photonFromBase64(`data:${mimeType};base64,${inputData.toString('base64')}`);
84
+ try {
85
+ return Buffer.from(photonImage.get_bytes_jpeg(quality));
86
+ } finally{
87
+ photonImage.free();
88
+ }
89
+ }
71
90
  const base64ImageDataUrlPattern = /^data:image\/[a-zA-Z0-9.+-]+;base64,/i;
72
91
  const supportedScreenshotDataUriPattern = /^data:image\/(png|jpe?g);base64,([\s\S]*)$/i;
73
92
  const rawBase64BodyPattern = /^[A-Za-z0-9+/=\s]+$/;
@@ -167,39 +186,6 @@ async function paddingToMatchBlock(image, blockSize = 28) {
167
186
  };
168
187
  }
169
188
  async function paddingToMatchBlockByBase64(imageBase64, blockSize = 28) {
170
- if (ifInNode) {
171
- const { body } = parseBase64(imageBase64);
172
- const inputBuffer = Buffer.from(body, 'base64');
173
- const Sharp = await get_sharp();
174
- const metadata = await Sharp(inputBuffer).metadata();
175
- const width = metadata.width;
176
- const height = metadata.height;
177
- if (!width || !height) throw new Error('Failed to get image dimensions');
178
- const targetWidth = Math.ceil(width / blockSize) * blockSize;
179
- const targetHeight = Math.ceil(height / blockSize) * blockSize;
180
- if (targetWidth === width && targetHeight === height) return {
181
- width,
182
- height,
183
- imageBase64
184
- };
185
- const output = await Sharp(inputBuffer).extend({
186
- right: targetWidth - width,
187
- bottom: targetHeight - height,
188
- background: {
189
- r: 255,
190
- g: 255,
191
- b: 255,
192
- alpha: 1
193
- }
194
- }).jpeg({
195
- quality: 90
196
- }).toBuffer();
197
- return {
198
- width: targetWidth,
199
- height: targetHeight,
200
- imageBase64: createImgBase64ByFormat('jpeg', output.toString('base64'))
201
- };
202
- }
203
189
  const photonImage = await photonFromBase64(imageBase64);
204
190
  try {
205
191
  const paddedResult = await paddingToMatchBlock(photonImage, blockSize);
@@ -215,27 +201,6 @@ async function paddingToMatchBlockByBase64(imageBase64, blockSize = 28) {
215
201
  }
216
202
  }
217
203
  async function cropByRect(imageBase64, rect) {
218
- if (ifInNode) {
219
- const { body } = parseBase64(imageBase64);
220
- const Sharp = await get_sharp();
221
- const left = Math.trunc(rect.left);
222
- const top = Math.trunc(rect.top);
223
- const width = Math.trunc(rect.left + rect.width) - left;
224
- const height = Math.trunc(rect.top + rect.height) - top;
225
- const output = await Sharp(Buffer.from(body, 'base64')).extract({
226
- left,
227
- top,
228
- width,
229
- height
230
- }).jpeg({
231
- quality: 90
232
- }).toBuffer();
233
- return {
234
- width,
235
- height,
236
- imageBase64: createImgBase64ByFormat('jpeg', output.toString('base64'))
237
- };
238
- }
239
204
  const { crop } = await get_photon();
240
205
  const photonImage = await photonFromBase64(imageBase64);
241
206
  const { left, top, width, height } = rect;
@@ -311,7 +276,7 @@ async function scaleImage(imageBase64, scale) {
311
276
  const buffer = Buffer.from(body, 'base64');
312
277
  const scaleStartTime = Date.now();
313
278
  imgDebug(`scaleImage start, scale factor: ${scale}`);
314
- if (ifInNode) {
279
+ if (ifInNode) try {
315
280
  const Sharp = await get_sharp();
316
281
  const metadata = await Sharp(buffer).metadata();
317
282
  const originalWidth = metadata.width || 0;
@@ -333,6 +298,8 @@ async function scaleImage(imageBase64, scale) {
333
298
  height: newHeight,
334
299
  imageBase64: base64
335
300
  };
301
+ } catch (error) {
302
+ imgDebug('Sharp failed, falling back to Photon:', error);
336
303
  }
337
304
  const { PhotonImage, SamplingFilter, resize } = await get_photon();
338
305
  const inputBytes = new Uint8Array(buffer);
@@ -360,4 +327,4 @@ async function scaleImage(imageBase64, scale) {
360
327
  imageBase64: base64
361
328
  };
362
329
  }
363
- export { createImgBase64ByFormat, cropByRect, httpImg2Base64, inferBase64ImageFormat, localImg2Base64, normalizeBase64Body, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlock, paddingToMatchBlockByBase64, parseBase64, photonFromBase64, photonToBase64, preProcessImageUrl, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, zoomForGPT4o };
330
+ export { convertImgBufferToJpeg, createImgBase64ByFormat, cropByRect, httpImg2Base64, inferBase64ImageFormat, localImg2Base64, normalizeBase64Body, normalizeBase64Image, normalizeScreenshotBase64, paddingToMatchBlock, paddingToMatchBlockByBase64, parseBase64, photonFromBase64, photonToBase64, preProcessImageUrl, resizeAndConvertImgBuffer, resizeImgBase64, saveBase64Image, scaleImage, zoomForGPT4o };
@@ -6,32 +6,21 @@ import { getMidsceneRunSubDir } from "./common.mjs";
6
6
  import { ifInNode } from "./utils.mjs";
7
7
  const topicPrefix = 'midscene';
8
8
  const logStreams = new Map();
9
- const backpressuredLogStreams = new Set();
10
- const unavailableLogStreams = new Set();
11
9
  const debugInstances = new Map();
12
10
  function getLogStream(topic) {
13
11
  const topicFileName = topic.replace(/:/g, '-');
14
- if (unavailableLogStreams.has(topicFileName)) return null;
15
12
  if (!logStreams.has(topicFileName)) {
16
13
  const logFile = node_path.join(getMidsceneRunSubDir('log'), `${topicFileName}.log`);
17
14
  const stream = node_fs.createWriteStream(logFile, {
18
15
  flags: 'a'
19
16
  });
20
- stream.on('error', ()=>{
21
- unavailableLogStreams.add(topicFileName);
22
- backpressuredLogStreams.delete(topicFileName);
23
- if (logStreams.get(topicFileName) === stream) logStreams.delete(topicFileName);
24
- });
25
17
  logStreams.set(topicFileName, stream);
26
18
  }
27
- return logStreams.get(topicFileName) ?? null;
19
+ return logStreams.get(topicFileName);
28
20
  }
29
21
  function writeLogToFile(topic, message) {
30
22
  if (!ifInNode) return;
31
- const topicFileName = topic.replace(/:/g, '-');
32
- if (backpressuredLogStreams.has(topicFileName)) return;
33
23
  const stream = getLogStream(topic);
34
- if (!stream) return;
35
24
  const now = new Date();
36
25
  const isoDate = now.toLocaleDateString('sv-SE');
37
26
  const isoTime = now.toLocaleTimeString('sv-SE');
@@ -42,17 +31,7 @@ function writeLogToFile(topic, message) {
42
31
  const minutes = (Math.abs(timezoneOffsetMinutes) % 60).toString().padStart(2, '0');
43
32
  const timezoneString = `${sign}${hours}:${minutes}`;
44
33
  const localISOTime = `${isoDate}T${isoTime}.${milliseconds}${timezoneString}`;
45
- try {
46
- if (!stream.write(`[${localISOTime}] ${message}\n`)) {
47
- backpressuredLogStreams.add(topicFileName);
48
- stream.once('drain', ()=>{
49
- backpressuredLogStreams.delete(topicFileName);
50
- });
51
- }
52
- } catch {
53
- unavailableLogStreams.add(topicFileName);
54
- backpressuredLogStreams.delete(topicFileName);
55
- }
34
+ stream.write(`[${localISOTime}] ${message}\n`);
56
35
  }
57
36
  function getDebug(topic, options) {
58
37
  const fullTopic = `${topicPrefix}:${topic}`;
@@ -37,7 +37,7 @@ const external_utils_js_namespaceObject = require("../utils.js");
37
37
  const external_helper_js_namespaceObject = require("./helper.js");
38
38
  const external_init_debug_js_namespaceObject = require("./init-debug.js");
39
39
  const MODEL_CONFIG_DOC_URL = 'https://midscenejs.com/model-common-config.html';
40
- const getCurrentVersion = ()=>"1.10.5-beta-20260716133905.0";
40
+ const getCurrentVersion = ()=>"1.10.5";
41
41
  const getInvalidModelFamilyMessage = (modelFamily)=>`Invalid MIDSCENE_MODEL_FAMILY value: ${modelFamily}. Current version v${getCurrentVersion()} accepts the following model families: ${external_types_js_namespaceObject.MODEL_FAMILY_VALUES.join(', ')}. You can also visit ${MODEL_CONFIG_DOC_URL} for the latest configuration information.`;
42
42
  const KEYS_MAP = {
43
43
  insight: external_constants_js_namespaceObject.INSIGHT_MODEL_CONFIG_KEYS,
@@ -41,11 +41,8 @@ __webpack_require__.d(__webpack_exports__, {
41
41
  const external_node_assert_namespaceObject = require("node:assert");
42
42
  var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
43
43
  const index_js_namespaceObject = require("../constants/index.js");
44
- const external_utils_js_namespaceObject = require("../utils.js");
45
44
  const external_get_photon_js_namespaceObject = require("./get-photon.js");
46
45
  var external_get_photon_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_photon_js_namespaceObject);
47
- const external_get_sharp_js_namespaceObject = require("./get-sharp.js");
48
- var external_get_sharp_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_sharp_js_namespaceObject);
49
46
  const external_transform_js_namespaceObject = require("./transform.js");
50
47
  const DIGIT_FONT = {
51
48
  0: [
@@ -959,45 +956,8 @@ const createSvgOverlay = async (elements, imageWidth, imageHeight, boxPadding =
959
956
  }
960
957
  return overlayPixels;
961
958
  };
962
- async function decodeImageWithSharp(inputImgBase64, size) {
963
- const { body } = (0, external_transform_js_namespaceObject.parseBase64)(inputImgBase64);
964
- const Sharp = await external_get_sharp_js_default()();
965
- let pipeline = Sharp(Buffer.from(body, 'base64'));
966
- if (size) pipeline = pipeline.resize(size.width, size.height, {
967
- fit: 'fill',
968
- kernel: 'nearest'
969
- });
970
- const { data, info } = await pipeline.ensureAlpha().raw().toBuffer({
971
- resolveWithObject: true
972
- });
973
- if (!info.width || !info.height || 4 !== info.channels) throw new Error('Image processing failed to produce RGBA pixels');
974
- return {
975
- pixels: new Uint8Array(data),
976
- width: info.width,
977
- height: info.height
978
- };
979
- }
980
- async function encodeRgbaWithSharp(pixels, width, height) {
981
- const Sharp = await external_get_sharp_js_default()();
982
- const output = await Sharp(Buffer.from(pixels), {
983
- raw: {
984
- width,
985
- height,
986
- channels: 4
987
- }
988
- }).jpeg({
989
- quality: 90,
990
- chromaSubsampling: '4:4:4'
991
- }).toBuffer();
992
- return (0, external_transform_js_namespaceObject.createImgBase64ByFormat)('jpeg', output.toString('base64'));
993
- }
994
959
  const compositeElementInfoImg = async (options)=>{
995
960
  external_node_assert_default()(options.inputImgBase64, 'inputImgBase64 is required');
996
- if (external_utils_js_namespaceObject.ifInNode) {
997
- const { pixels, width, height } = await decodeImageWithSharp(options.inputImgBase64, options.size);
998
- const overlayPixels = await createSvgOverlay(options.elementsPositionInfo, width, height, options.annotationPadding, options.borderThickness, options.prompt, options.centerPoint);
999
- return encodeRgbaWithSharp(blendPixels(pixels, overlayPixels, width, height), width, height);
1000
- }
1001
961
  const { PhotonImage, SamplingFilter, resize } = await external_get_photon_js_default()();
1002
962
  let width = 0;
1003
963
  let height = 0;
@@ -1037,12 +997,6 @@ const compositeElementInfoImg = async (options)=>{
1037
997
  };
1038
998
  const compositePointMarkerImg = async (options)=>{
1039
999
  external_node_assert_default()(options.inputImgBase64, 'inputImgBase64 is required');
1040
- if (external_utils_js_namespaceObject.ifInNode) {
1041
- const { pixels, width, height } = await decodeImageWithSharp(options.inputImgBase64, options.size);
1042
- const overlayPixels = new Uint8Array(width * height * 4);
1043
- drawPointMarker(overlayPixels, width, height, options.point, options.radius ?? 14, options.indexId ?? 1);
1044
- return encodeRgbaWithSharp(blendPixels(pixels, overlayPixels, width, height), width, height);
1045
- }
1046
1000
  const { PhotonImage, SamplingFilter, resize } = await external_get_photon_js_default()();
1047
1001
  let width = 0;
1048
1002
  let height = 0;
@@ -35,7 +35,7 @@ let isInitialized = false;
35
35
  let usingCanvasFallback = false;
36
36
  async function getPhoton() {
37
37
  if (photonModule && isInitialized) return photonModule;
38
- const env = external_utils_js_namespaceObject.ifInBrowser ? 'browser' : external_utils_js_namespaceObject.ifInWorker ? 'worker' : 'unknown';
38
+ const env = external_utils_js_namespaceObject.ifInBrowser ? 'browser' : external_utils_js_namespaceObject.ifInWorker ? 'worker' : external_utils_js_namespaceObject.ifInNode ? 'node' : 'unknown';
39
39
  debug(`Loading photon module in ${env} environment`);
40
40
  try {
41
41
  if (external_utils_js_namespaceObject.ifInBrowser || external_utils_js_namespaceObject.ifInWorker) {
@@ -43,7 +43,10 @@ async function getPhoton() {
43
43
  if ('function' == typeof photon.default) await photon.default();
44
44
  debug('Photon loaded: @silvia-odwyer/photon (browser/worker)');
45
45
  photonModule = photon;
46
- } else throw new Error('Photon is only available in browser environments');
46
+ } else if (external_utils_js_namespaceObject.ifInNode) {
47
+ photonModule = await import("@silvia-odwyer/photon-node");
48
+ debug('Photon loaded: @silvia-odwyer/photon-node (node)');
49
+ }
47
50
  if (!photonModule?.PhotonImage) throw new Error('PhotonImage is not available');
48
51
  if (!photonModule.PhotonImage.new_from_byteslice && !photonModule.PhotonImage.new_from_base64) throw new Error('PhotonImage.new_from_byteslice or new_from_base64 is not available');
49
52
  isInitialized = true;
@@ -24,9 +24,10 @@ var __webpack_require__ = {};
24
24
  var __webpack_exports__ = {};
25
25
  __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
- paddingToMatchBlockByBase64: ()=>external_transform_js_namespaceObject.paddingToMatchBlockByBase64,
27
+ convertImgBufferToJpeg: ()=>external_transform_js_namespaceObject.convertImgBufferToJpeg,
28
28
  inferBase64ImageFormat: ()=>external_transform_js_namespaceObject.inferBase64ImageFormat,
29
29
  localImg2Base64: ()=>external_transform_js_namespaceObject.localImg2Base64,
30
+ paddingToMatchBlockByBase64: ()=>external_transform_js_namespaceObject.paddingToMatchBlockByBase64,
30
31
  parseBase64: ()=>external_transform_js_namespaceObject.parseBase64,
31
32
  resizeAndConvertImgBuffer: ()=>external_transform_js_namespaceObject.resizeAndConvertImgBuffer,
32
33
  resizeImgBase64: ()=>external_transform_js_namespaceObject.resizeImgBase64,
@@ -55,6 +56,7 @@ const external_box_select_js_namespaceObject = require("./box-select.js");
55
56
  exports.annotateRects = __webpack_exports__.annotateRects;
56
57
  exports.compositeElementInfoImg = __webpack_exports__.compositeElementInfoImg;
57
58
  exports.compositePointMarkerImg = __webpack_exports__.compositePointMarkerImg;
59
+ exports.convertImgBufferToJpeg = __webpack_exports__.convertImgBufferToJpeg;
58
60
  exports.createImgBase64ByFormat = __webpack_exports__.createImgBase64ByFormat;
59
61
  exports.cropByRect = __webpack_exports__.cropByRect;
60
62
  exports.httpImg2Base64 = __webpack_exports__.httpImg2Base64;
@@ -80,6 +82,7 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
80
82
  "annotateRects",
81
83
  "compositeElementInfoImg",
82
84
  "compositePointMarkerImg",
85
+ "convertImgBufferToJpeg",
83
86
  "createImgBase64ByFormat",
84
87
  "cropByRect",
85
88
  "httpImg2Base64",
@@ -42,34 +42,15 @@ __webpack_require__.d(__webpack_exports__, {
42
42
  const external_node_assert_namespaceObject = require("node:assert");
43
43
  var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
44
44
  const external_node_buffer_namespaceObject = require("node:buffer");
45
- const external_utils_js_namespaceObject = require("../utils.js");
46
45
  const external_get_photon_js_namespaceObject = require("./get-photon.js");
47
46
  var external_get_photon_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_photon_js_namespaceObject);
48
- const external_get_sharp_js_namespaceObject = require("./get-sharp.js");
49
- var external_get_sharp_js_default = /*#__PURE__*/ __webpack_require__.n(external_get_sharp_js_namespaceObject);
50
47
  async function imageInfoOfBase64(imageBase64) {
48
+ const { PhotonImage } = await external_get_photon_js_default()();
51
49
  const base64Data = imageBase64.replace(/^data:image\/\w+;base64,/, '').replace(/\s/g, '');
52
50
  external_node_assert_default()(base64Data, 'Invalid image: empty base64 data');
53
51
  external_node_assert_default()(/^[A-Za-z0-9+/]+={0,2}$/.test(base64Data) && base64Data.length % 4 !== 1, 'Invalid image: malformed base64 data');
54
52
  const imageBuffer = external_node_buffer_namespaceObject.Buffer.from(base64Data, 'base64');
55
53
  external_node_assert_default()(isValidImageBuffer(imageBuffer), 'Invalid image: unsupported format');
56
- if (external_utils_js_namespaceObject.ifInNode) {
57
- let metadata;
58
- try {
59
- const Sharp = await external_get_sharp_js_default()();
60
- metadata = await Sharp(imageBuffer).metadata();
61
- } catch (error) {
62
- throw new Error(`Invalid image: failed to decode base64 data (${error instanceof Error ? error.message : String(error)})`, {
63
- cause: error
64
- });
65
- }
66
- external_node_assert_default()(metadata.width && metadata.height, 'Invalid image: cannot get width or height');
67
- return {
68
- width: metadata.width,
69
- height: metadata.height
70
- };
71
- }
72
- const { PhotonImage } = await external_get_photon_js_default()();
73
54
  let result;
74
55
  try {
75
56
  result = PhotonImage.new_from_base64(base64Data);