@pexip/media 19.0.0 → 20.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/utils.js CHANGED
@@ -3,22 +3,22 @@ import { calculateMaxBlurPass } from '@pexip/media-processor';
3
3
  import { hasOwn, assert, isEmpty } from '@pexip/utils';
4
4
  import { internalSignals } from './signals';
5
5
  export const makeDeriveDeviceStatus = (constraints) => (audio, video, both) => {
6
- if (constraints.audio === undefined || constraints.audio) {
7
- if (constraints.video === undefined || constraints.video) {
6
+ if (constraints.audio) {
7
+ if (constraints.video) {
8
8
  return both;
9
9
  }
10
10
  return audio;
11
11
  }
12
- if (constraints.video === undefined || constraints.video) {
12
+ if (constraints.video) {
13
13
  return video;
14
14
  }
15
15
  return both;
16
16
  };
17
17
  export const createMediaTrack = (trackInit) => {
18
- assert(trackInit.track === undefined ||
18
+ const trackConsistency = trackInit.track === undefined ||
19
19
  (trackInit.kind === trackInit.input?.kind &&
20
- trackInit.kind === toMediaDeviceInputKind(trackInit.track),
21
- 'Inconsistent track kind'));
20
+ trackInit.kind === toMediaDeviceInputKind(trackInit.track));
21
+ assert(trackConsistency, `Inconsistent track kind: ${trackInit.input?.kind} ${trackInit.track && toMediaDeviceInputKind(trackInit.track)} vs ${trackInit.kind}`);
22
22
  const currentConstraints = typeof trackInit.constraints === 'boolean' ? {} : trackInit.constraints;
23
23
  // Find the original source track through the linked list
24
24
  let sourceMediaTrack = trackInit.previousMediaTrack;
@@ -41,7 +41,7 @@ export const createMediaTrack = (trackInit) => {
41
41
  ...(trackInit.getSettings?.() ?? {}),
42
42
  };
43
43
  };
44
- return {
44
+ const mediaTrack = {
45
45
  get kind() {
46
46
  return trackInit.kind;
47
47
  },
@@ -52,7 +52,9 @@ export const createMediaTrack = (trackInit) => {
52
52
  return trackInit.track;
53
53
  },
54
54
  get input() {
55
- return trackInit.input;
55
+ return trackInit.track?.readyState !== 'live'
56
+ ? undefined
57
+ : trackInit.input;
56
58
  },
57
59
  get expectedInput() {
58
60
  return trackInit.expectedInput;
@@ -74,7 +76,7 @@ export const createMediaTrack = (trackInit) => {
74
76
  isTrackMuted(trackInit.track));
75
77
  },
76
78
  get source() {
77
- return sourceMediaTrack ?? this;
79
+ return sourceMediaTrack ?? mediaTrack;
78
80
  },
79
81
  mute(toMute) {
80
82
  if (trackInit.overrideMute) {
@@ -89,11 +91,12 @@ export const createMediaTrack = (trackInit) => {
89
91
  getConstraints() {
90
92
  return getConstraints();
91
93
  },
92
- clone() {
94
+ clone(signals) {
93
95
  return createMediaTrack({
94
96
  ...trackInit,
95
97
  constraints: getConstraints(),
96
98
  track: trackInit.track?.clone(),
99
+ signals,
97
100
  });
98
101
  },
99
102
  async applyConstraints(constraints) {
@@ -152,10 +155,14 @@ export const createMediaTrack = (trackInit) => {
152
155
  };
153
156
  },
154
157
  };
158
+ return mediaTrack;
155
159
  };
156
160
  export const createTrackProcessingPipeline = (processors) => async (track) => {
157
161
  let lastTrack = track;
158
162
  for (const process of processors) {
163
+ if (lastTrack.track?.readyState !== 'live') {
164
+ return lastTrack;
165
+ }
159
166
  lastTrack = await process(lastTrack);
160
167
  }
161
168
  return lastTrack;
@@ -187,7 +194,7 @@ export const findExpectedInput = (devices, constraints, input, kind) => {
187
194
  * A utility function to check if the provided track is muted. There are
188
195
  * 2 factors to be considered: `MediaStreamTrack['muted']` and `MediaStreamTrack['enabled']`.
189
196
  *
190
- * ```
197
+ * ```md
191
198
  * | muted \ enabled | true | false |
192
199
  * |-----------------| ----- | ----- |
193
200
  * | true | true | true |
@@ -334,23 +341,23 @@ export const buildMedia = (mediaInit, onDevicesChanged = internalSignals.onDevic
334
341
  return props.originalConstraints;
335
342
  },
336
343
  isAudioInputUnavailable() {
337
- return (mediaInit.permission.audio === 'granted' &&
338
- props.devices.size('audioinput') === 0);
344
+ return !props.devices.anyAuthorizedDevice('audioinput');
339
345
  },
340
346
  isVideoInputUnavailable() {
341
- return (mediaInit.permission.video === 'granted' &&
342
- props.devices.size('videoinput') === 0);
347
+ return !props.devices.anyAuthorizedDevice('videoinput');
343
348
  },
344
349
  setOriginalConstraints(constraints) {
345
350
  props.originalConstraints = constraints;
346
351
  },
347
352
  requestedAudio() {
348
353
  return (Boolean(props.audioTrack?.track) ||
349
- Boolean(props.originalConstraints.audio));
354
+ (Boolean(props.originalConstraints.audio) &&
355
+ props.devices.size('audioinput') > 0));
350
356
  },
351
357
  requestedVideo() {
352
358
  return (Boolean(props.videoTrack?.track) ||
353
- Boolean(props.originalConstraints.video));
359
+ (Boolean(props.originalConstraints.video) &&
360
+ props.devices.size('videoinput') > 0));
354
361
  },
355
362
  muteAudio(mute) {
356
363
  if (!props.audioTrack) {
@@ -377,7 +384,9 @@ export const buildMedia = (mediaInit, onDevicesChanged = internalSignals.onDevic
377
384
  async release() {
378
385
  unsubscribe?.();
379
386
  unsubscribe = undefined;
380
- trackSubscriptions.forEach(unsubscribe => unsubscribe());
387
+ for (const unsubscribe of trackSubscriptions.values()) {
388
+ unsubscribe();
389
+ }
381
390
  trackSubscriptions.clear();
382
391
  await Promise.all([
383
392
  props.audioTrack?.release(),
@@ -388,12 +397,12 @@ export const buildMedia = (mediaInit, onDevicesChanged = internalSignals.onDevic
388
397
  audio: props.audioTrack?.getSettings(),
389
398
  video: props.videoTrack?.getSettings(),
390
399
  }),
391
- clone() {
400
+ clone(signals) {
392
401
  const tracks = [props.audioTrack, props.videoTrack].flatMap(track => {
393
402
  if (!track?.source) {
394
403
  return [];
395
404
  }
396
- const cloned = track.source.clone();
405
+ const cloned = track.source.clone(signals);
397
406
  // Restore the enabled state for all cloned track
398
407
  cloned.mute(false);
399
408
  return cloned;
@@ -516,6 +525,11 @@ export const VIDEO_SETTINGS_KEYS = [
516
525
  export const MIXING_SETTINGS_KEYS = [
517
526
  'mixWithAdditionalMedia',
518
527
  ];
528
+ export const PAN_TILT_ZOOM_SETTINGS_KEYS = [
529
+ 'pan',
530
+ 'tilt',
531
+ 'zoom',
532
+ ];
519
533
  export const hasSettingsChanged = (keysToLookFor) => {
520
534
  const cache = {};
521
535
  return (settingsA, settingsB) => {
@@ -544,6 +558,28 @@ export const hasSettingsChanged = (keysToLookFor) => {
544
558
  return cache.result;
545
559
  };
546
560
  };
561
+ export const isApplyingRenderEffect = (effect) => {
562
+ switch (effect) {
563
+ case 'blur':
564
+ case 'overlay':
565
+ return true;
566
+ default:
567
+ return false;
568
+ }
569
+ };
570
+ export const getSettingsFromKeys = (keys, settings) => {
571
+ if (!settings) {
572
+ return settings;
573
+ }
574
+ const result = {};
575
+ for (const key of keys) {
576
+ if (settings[key] !== undefined) {
577
+ // @ts-expect-error --- Type issue and need to be fixed when we have time
578
+ result[key] = settings[key];
579
+ }
580
+ }
581
+ return result;
582
+ };
547
583
  export const diffSettings = (keysToLookFor) => {
548
584
  const cache = {};
549
585
  return (settingsA, settingsB) => {
@@ -559,7 +595,16 @@ export const diffSettings = (keysToLookFor) => {
559
595
  return cache.diff;
560
596
  }
561
597
  if (settingsA === undefined || settingsB === undefined) {
562
- cache.diff = settingsB;
598
+ const settings = settingsA ?? settingsB;
599
+ for (const key of keysToLookFor) {
600
+ if (settings?.[key] !== undefined) {
601
+ if (cache.diff === undefined) {
602
+ cache.diff = {};
603
+ }
604
+ // @ts-expect-error --- Type issue and need to be fixed when we have time
605
+ cache.diff[key] = settings[key];
606
+ }
607
+ }
563
608
  return cache.diff;
564
609
  }
565
610
  for (const key of keysToLookFor) {
@@ -624,6 +669,7 @@ export const refineMediaConstraints = (state) => {
624
669
  switch (state.permission) {
625
670
  case 'denied':
626
671
  return false;
672
+ case 'prompt':
627
673
  case 'granted': {
628
674
  const requestNeeded = shouldRequestDevice({
629
675
  kind: state.kind,
@@ -641,7 +687,6 @@ export const refineMediaConstraints = (state) => {
641
687
  }
642
688
  return state.request;
643
689
  }
644
- case 'prompt':
645
690
  default:
646
691
  return state.request;
647
692
  }
@@ -649,25 +694,25 @@ export const refineMediaConstraints = (state) => {
649
694
  export const createMediaProcessor = ({ audioProcessors, videoProcessors, onProcessingError, }) => {
650
695
  const processAudioTrack = createTrackProcessingPipeline(audioProcessors);
651
696
  const processVideoTrack = createTrackProcessingPipeline(videoProcessors);
652
- return async (newTracks) => {
653
- const processTrack = async (track) => {
654
- try {
655
- switch (track.kind) {
656
- case 'audioinput':
657
- return await processAudioTrack(track);
658
- case 'videoinput':
659
- return await processVideoTrack(track);
660
- default:
661
- assert(false, `Unexpected track kind: ${track.kind}`);
662
- }
697
+ const processTrack = async (track) => {
698
+ try {
699
+ switch (track.kind) {
700
+ case 'audioinput':
701
+ return await processAudioTrack(track);
702
+ case 'videoinput':
703
+ return await processVideoTrack(track);
704
+ default:
705
+ assert(false, `Unexpected track kind: ${track.kind}`);
663
706
  }
664
- catch (error) {
665
- if (error instanceof Error) {
666
- onProcessingError?.(error, track);
667
- }
668
- return track;
707
+ }
708
+ catch (error) {
709
+ if (error instanceof Error) {
710
+ onProcessingError?.(error, track);
669
711
  }
670
- };
712
+ return track;
713
+ }
714
+ };
715
+ return async (newTracks) => {
671
716
  return await Promise.all(newTracks.map(track => processTrack(track)));
672
717
  };
673
718
  };
@@ -1,4 +1,4 @@
1
- import type { VideoProcessor, SegmentationTransform, SegmentationModel } from '@pexip/media-processor';
1
+ import type { VideoProcessor, SegmentationTransform, SegmentationModel, RenderBackend } from '@pexip/media-processor';
2
2
  import type { MediaDeviceRequest } from '@pexip/media-control';
3
3
  import type { TrackProcessor, VideoRenderParams, Segmenters, VideoStreamTrackProcessorAPIs, VideoContentHint } from './types';
4
4
  interface ProcessorDeps {
@@ -24,6 +24,8 @@ interface VideoStreamProcessOptions extends Partial<VideoRenderParams>, Omit<Pro
24
24
  width?: number;
25
25
  height?: number;
26
26
  label?: string;
27
+ dynamicProcessingDimensions?: () => boolean;
28
+ gpuAPI?: () => RenderBackend;
27
29
  }
28
30
  interface VideoStreamProcessProps extends Partial<VideoRenderParams>, Required<ProcessorDeps> {
29
31
  hasInitialized: boolean;
@@ -32,6 +34,6 @@ interface VideoStreamProcessProps extends Partial<VideoRenderParams>, Required<P
32
34
  declare const FEATURE_KEYS: readonly ["backgroundBlurAmount", "backgroundImageUrl", "maskCombineRatio", "edgeBlurAmount", "foregroundThreshold", "frameRate", "videoSegmentation", "videoSegmentationModel", "width", "height", "pan", "tilt", "zoom", "contentHint"];
33
35
  type FeaturePropKeys = (typeof FEATURE_KEYS)[number];
34
36
  type FeatureProps = Pick<Partial<VideoStreamProcessProps>, FeaturePropKeys>;
35
- export declare const updateFeatureProps: (constraints: MediaDeviceRequest['video'], props: FeatureProps) => FeatureProps;
36
- export declare const createVideoStreamProcess: ({ trackProcessorAPI, processingWidth, processingHeight, shouldEnable, frameRate, videoSegmentation, foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label, ...options }: VideoStreamProcessOptions) => TrackProcessor;
37
+ export declare const updateFeatureProps: (constraints: MediaDeviceRequest["video"], props: FeatureProps) => FeatureProps;
38
+ export declare const createVideoStreamProcess: ({ trackProcessorAPI, processingWidth, processingHeight, shouldEnable, frameRate, videoSegmentation, foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label, dynamicProcessingDimensions, gpuAPI, ...options }: VideoStreamProcessOptions) => TrackProcessor;
37
39
  export {};
@@ -30,7 +30,8 @@ export const updateFeatureProps = (constraints, props) => {
30
30
  const [[feature = ''] = []] = extracted[key];
31
31
  if (isVideoContentHint(feature) && props[key] !== feature) {
32
32
  props[key] = feature;
33
- return { ...accm, [key]: feature };
33
+ accm[key] = feature;
34
+ return accm;
34
35
  }
35
36
  return accm;
36
37
  }
@@ -38,7 +39,8 @@ export const updateFeatureProps = (constraints, props) => {
38
39
  const [[feature] = []] = extracted[key];
39
40
  if (isRenderEffects(feature) && props[key] !== feature) {
40
41
  props[key] = feature;
41
- return { ...accm, [key]: feature };
42
+ accm[key] = feature;
43
+ return accm;
42
44
  }
43
45
  return accm;
44
46
  }
@@ -46,7 +48,8 @@ export const updateFeatureProps = (constraints, props) => {
46
48
  const [[feature] = []] = extracted[key];
47
49
  if (isSegmentationModel(feature) && props[key] !== feature) {
48
50
  props[key] = feature;
49
- return { ...accm, [key]: feature };
51
+ accm[key] = feature;
52
+ return accm;
50
53
  }
51
54
  return accm;
52
55
  }
@@ -54,7 +57,8 @@ export const updateFeatureProps = (constraints, props) => {
54
57
  const [[feature] = []] = extracted[key];
55
58
  if (feature) {
56
59
  props[key] = feature;
57
- return { ...accm, [key]: feature };
60
+ accm[key] = feature;
61
+ return accm;
58
62
  }
59
63
  return accm;
60
64
  }
@@ -70,10 +74,8 @@ export const updateFeatureProps = (constraints, props) => {
70
74
  const value = getValueFromConstrainNumber(feature);
71
75
  if (props[key] !== value) {
72
76
  props[key] = value;
73
- return {
74
- ...accm,
75
- [key]: value,
76
- };
77
+ accm[key] = value;
78
+ return accm;
77
79
  }
78
80
  }
79
81
  return accm;
@@ -84,7 +86,8 @@ export const updateFeatureProps = (constraints, props) => {
84
86
  const [feature] = extracted[key];
85
87
  if (feature !== undefined && props[key] !== feature) {
86
88
  props[key] = feature;
87
- return { ...accm, [key]: feature };
89
+ accm[key] = feature;
90
+ return accm;
88
91
  }
89
92
  return accm;
90
93
  }
@@ -92,7 +95,7 @@ export const updateFeatureProps = (constraints, props) => {
92
95
  }, {});
93
96
  };
94
97
  const applyFeatures = (transformer, features) => {
95
- Object.keys(features).forEach(key => {
98
+ for (const key in features) {
96
99
  const k = key;
97
100
  switch (k) {
98
101
  case 'edgeBlurAmount':
@@ -102,66 +105,30 @@ const applyFeatures = (transformer, features) => {
102
105
  if (value !== undefined) {
103
106
  transformer[k] = value;
104
107
  }
105
- return;
108
+ break;
106
109
  }
107
110
  case 'backgroundBlurAmount': {
108
111
  const value = features[k];
109
112
  if (value !== undefined) {
110
113
  transformer[k] = getBlurKernelSize(value, transformer.height);
111
114
  }
112
- return;
115
+ break;
113
116
  }
114
117
  case 'videoSegmentation': {
115
118
  const value = features[k];
116
119
  if (value && value !== transformer.effects) {
117
120
  transformer.effects = value;
118
121
  }
119
- return;
122
+ break;
120
123
  }
121
124
  case 'backgroundImageUrl': {
122
125
  const value = features[k];
123
126
  if (value && value !== transformer.backgroundImageUrl) {
124
127
  transformer.backgroundImageUrl = value;
125
128
  }
126
- return;
127
- }
128
- default: {
129
- return;
130
- }
131
- }
132
- });
133
- };
134
- const adjustResolution = async (track, features, processingSize) => {
135
- if (features.videoSegmentation) {
136
- const videoSettings = track.getSettings();
137
- const constraints = updateFeatureProps(track.getConstraints(), {});
138
- switch (features.videoSegmentation) {
139
- case 'blur':
140
- case 'overlay': {
141
- if (videoSettings?.height !== processingSize.height) {
142
- try {
143
- await track.applyConstraints({
144
- width: processingSize.width,
145
- height: processingSize.height,
146
- });
147
- const postVideoSettings = track.getSettings();
148
- if (postVideoSettings?.height !== processingSize.height) {
149
- // Workaround Firefox 16:9 ratio https://bugzilla.mozilla.org/show_bug.cgi?id=1193640
150
- await track.applyConstraints({ height: 720 });
151
- }
152
- }
153
- catch (error) {
154
- // Workaround Firefox 16:9 ratio https://bugzilla.mozilla.org/show_bug.cgi?id=1193640
155
- await track.applyConstraints({ height: 720 });
156
- }
157
- }
158
129
  break;
159
130
  }
160
- case 'none': {
161
- if (constraints.height &&
162
- constraints.height !== videoSettings?.height) {
163
- await track.applyConstraints({ height: constraints.height });
164
- }
131
+ default: {
165
132
  break;
166
133
  }
167
134
  }
@@ -178,7 +145,8 @@ export const createVideoStreamProcess = ({ trackProcessorAPI = () => 'stream', p
178
145
  //backgroundBlurAmount,
179
146
  videoSegmentation,
180
147
  //edgeBlurAmount,
181
- foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label = PROCESSOR_LABELS.VideoProcessor, ...options }) => {
148
+ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label = PROCESSOR_LABELS.VideoProcessor, dynamicProcessingDimensions = () => false, gpuAPI = () => 'webgl', // dynamically changing the config will not be picked up, not a priority for now
149
+ ...options }) => {
182
150
  const videoSegmentationModel = options.videoSegmentationModel ?? 'selfie';
183
151
  const segmenter = options.segmenters[videoSegmentationModel];
184
152
  if (!segmenter) {
@@ -235,14 +203,34 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
235
203
  return prevMediaTrack;
236
204
  }
237
205
  if (!props.hasInitialized) {
238
- await props.videoProcessor().open();
206
+ if (dynamicProcessingDimensions()) {
207
+ const { width, height } = prevMediaTrack.getSettings();
208
+ /**
209
+ * when the processor is closed, the segmenter is not killed, but the track is released.
210
+ * this means we can't listen on the track settings changes when we change quality
211
+ * we need to update instead of open if the segmenter is already running
212
+ */
213
+ if (props.transformer.segmenter.status === 'new') {
214
+ await props.videoProcessor().open({
215
+ processingWidth: width,
216
+ processingHeight: height,
217
+ });
218
+ props.transformer.backend = gpuAPI();
219
+ }
220
+ else {
221
+ props.transformer.update({
222
+ processingWidth: width,
223
+ processingHeight: height,
224
+ });
225
+ }
226
+ }
227
+ else {
228
+ await props.videoProcessor().open();
229
+ props.transformer.backend = gpuAPI();
230
+ }
239
231
  props.hasInitialized = true;
240
232
  }
241
233
  applyFeatures(props.transformer, features);
242
- await adjustResolution(prevMediaTrack, features, {
243
- width: processingWidth,
244
- height: processingHeight,
245
- });
246
234
  const model = features.videoSegmentationModel &&
247
235
  props.segmenters[features.videoSegmentationModel];
248
236
  if (features.videoSegmentationModel &&
@@ -292,10 +280,15 @@ foregroundThreshold, backgroundImageUrl, maskCombineRatio, edgeBlurAmount, label
292
280
  return;
293
281
  }
294
282
  applyFeatures(props.transformer, features);
295
- await adjustResolution(prevMediaTrack, features, {
296
- width: processingWidth,
297
- height: processingHeight,
298
- });
283
+ if (dynamicProcessingDimensions()) {
284
+ const acceptedSettings = prevMediaTrack.source.getSettings();
285
+ if (acceptedSettings.width && acceptedSettings.height) {
286
+ props.transformer.update({
287
+ processingHeight: acceptedSettings.height,
288
+ processingWidth: acceptedSettings.width,
289
+ });
290
+ }
291
+ }
299
292
  const model = features.videoSegmentationModel &&
300
293
  props.segmenters[features.videoSegmentationModel];
301
294
  if (model &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pexip/media",
3
- "version": "19.0.0",
3
+ "version": "20.0.0",
4
4
  "description": "Home for media related stuff",
5
5
  "homepage": "https://gitlab.com/pexip/zoo",
6
6
  "bugs": "https://gitlab.com/pexip/zoo/issues",
@@ -14,6 +14,16 @@
14
14
  "author": "Terrence Lam <terrence@tlam.dev>",
15
15
  "module": "dist/index.js",
16
16
  "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ },
22
+ "./*": {
23
+ "types": "./dist/*.d.ts",
24
+ "import": "./dist/*.js"
25
+ }
26
+ },
17
27
  "directories": {
18
28
  "lib": "lib",
19
29
  "test": "__tests__"
@@ -23,30 +33,31 @@
23
33
  ],
24
34
  "scripts": {
25
35
  "build": "pexip-bundler build",
36
+ "build:sourcemap": "pexip-bundler build --sourcemap",
26
37
  "clean": "rm -fr dist",
27
38
  "dev": "pexip-bundler dev",
28
- "prepack": "yarn clean && yarn build --no-sourcemap",
39
+ "prepack": "yarn clean && yarn build",
29
40
  "test": "jest",
30
41
  "check-format": "prettier --ignore-path=../../.prettierignore --check .",
31
42
  "format": "prettier --ignore-path=../../.prettierignore --write .",
32
- "lint": "yarn run -T eslint --ext .js,.ts,.tsx --report-unused-disable-directives .",
43
+ "lint": "yarn run -T biome lint",
33
44
  "typecheck": "yarn tsc --noEmit -p ."
34
45
  },
35
46
  "dependencies": {
36
- "@pexip/media-control": "19.0.0",
37
- "@pexip/media-processor": "19.0.0",
38
- "@pexip/signal": "16.7.1",
39
- "@pexip/utils": "16.13.0"
47
+ "@pexip/media-control": "20.0.0",
48
+ "@pexip/media-processor": "20.0.0",
49
+ "@pexip/signal": "16.8.0",
50
+ "@pexip/utils": "17.0.0"
40
51
  },
41
52
  "devDependencies": {
42
53
  "@jest/globals": "^29.7.0",
43
- "@pexip/bundler": "17.1.0",
44
- "@swc/core": "^1.4.6",
45
- "@swc/jest": "^0.2.36",
54
+ "@pexip/bundler": "18.0.0",
55
+ "@swc/core": "^1.10.12",
56
+ "@swc/jest": "^0.2.37",
46
57
  "jest": "^29.5.12",
47
58
  "jest-junit": "^16.0.0",
48
59
  "prettier": "^3.2.5",
49
- "typescript": "~5.3.0"
60
+ "typescript": "~5.7.3"
50
61
  },
51
62
  "publishConfig": {
52
63
  "access": "public",