diff --git a/apps/web-backend/src/app/web-backend-app.spec.ts b/apps/web-backend/src/app/web-backend-app.spec.ts index 83f66ed29..c44eaa147 100644 --- a/apps/web-backend/src/app/web-backend-app.spec.ts +++ b/apps/web-backend/src/app/web-backend-app.spec.ts @@ -200,11 +200,18 @@ https://stream.example/news.m3u8`); const httpClient = new StubHttpClient(); const kid = '00112233445566778899aabbccddeeff'; const key = 'ffeeddccbbaa99887766554433221100'; + // First channel uses the KODIPROP-before-#EXTINF layout (supported + // since the 0.15.2-iptvnator.2 parser pin), the second the common + // between-#EXTINF-and-URL layout. httpClient.queueResponse(`#EXTM3U -#EXTINF:-1 tvg-id="ck" group-title="DASH",Encrypted DASH #KODIPROP:inputstream.adaptive.license_type=clearkey #KODIPROP:inputstream.adaptive.license_key=${kid}:${key} +#EXTINF:-1 tvg-id="ck" group-title="DASH",Encrypted DASH https://stream.example/enc.mpd +#EXTINF:-1 tvg-id="ck2" group-title="DASH",Encrypted DASH 2 +#KODIPROP:inputstream.adaptive.license_type=clearkey +#KODIPROP:inputstream.adaptive.license_key=${kid}:${key} +https://stream.example/enc2.mpd #EXTINF:-1 tvg-id="plain",Plain Channel https://stream.example/live.m3u8`); @@ -229,13 +236,18 @@ https://stream.example/live.m3u8`); }; expect(response.status).toBe(200); - expect(body.playlist.items).toHaveLength(2); + expect(body.playlist.items).toHaveLength(3); expect(body.playlist.items[0]['drm']).toEqual({ licenseType: 'clearkey', supported: true, clearKeys: { [kid]: key }, }); - expect(body.playlist.items[1]['drm']).toBeUndefined(); + expect(body.playlist.items[1]['drm']).toEqual({ + licenseType: 'clearkey', + supported: true, + clearKeys: { [kid]: key }, + }); + expect(body.playlist.items[2]['drm']).toBeUndefined(); } ); }); diff --git a/apps/web/src/app/iptv-playlist-parser.contract.spec.ts b/apps/web/src/app/iptv-playlist-parser.contract.spec.ts index 85f2338cc..fbb1370af 100644 --- a/apps/web/src/app/iptv-playlist-parser.contract.spec.ts +++ b/apps/web/src/app/iptv-playlist-parser.contract.spec.ts @@ -106,4 +106,30 @@ describe('iptv-playlist-parser contract (4gray fork)', () => { 'https://epg.example/guide.xml' ); }); + + it('preserves #KODIPROP lines placed before #EXTINF in item raw', () => { + // Kodi property lines apply to the NEXT entry; the DASH/ClearKey + // support extracts license config from item.raw (#1225). + const result = parse( + [ + '#EXTM3U', + '#KODIPROP:inputstream.adaptive.license_type=clearkey', + '#KODIPROP:inputstream.adaptive.license_key=kid123:key456', + '#EXTINF:-1,Encrypted', + 'http://example.com/enc.mpd', + '#EXTINF:-1,Plain', + 'http://example.com/plain.m3u8', + '', + ].join('\n') + ); + + expect(result.items.length).toBe(2); + expect(result.items[0].raw).toContain( + '#KODIPROP:inputstream.adaptive.license_type=clearkey' + ); + expect(result.items[0].raw).toContain( + '#KODIPROP:inputstream.adaptive.license_key=kid123:key456' + ); + expect(result.items[1].raw).not.toContain('#KODIPROP'); + }); }); diff --git a/docs/architecture/m3u-playlist-module.md b/docs/architecture/m3u-playlist-module.md index 477c5c355..08c5dd84e 100644 --- a/docs/architecture/m3u-playlist-module.md +++ b/docs/architecture/m3u-playlist-module.md @@ -46,10 +46,11 @@ The M3U playlist module provides: All four parse call sites (Electron `playlist-source.ts` import, `playlist-refresh.worker.ts`, `web-backend` `/parse`, PWA `playlists.service.ts`) use the [4gray/iptv-playlist-parser](https://github.com/4gray/iptv-playlist-parser) fork, pinned by commit SHA in `package.json`. The fork tracks upstream -`freearhey/iptv-playlist-parser` (currently synced to v0.15.2) plus two deliberate deltas iptvnator depends on: +`freearhey/iptv-playlist-parser` (currently synced to v0.15.2) plus three deliberate deltas iptvnator depends on: - **`radio` attribute** — `item.radio` (string, `'true'` triggers the radio player, EPG suppression, and external-player gating app-wide). Upstream does not have this field; it must survive every upstream sync. - **Pipe stripping** — `item.url` is cut at the first `|`; `|User-Agent=` / `|Referer=` params still land in `item.http`. Upstream 0.15.0 stopped stripping, but iptvnator consumes `item.url` verbatim in hls.js/mpv/vlc, catch-up URL building, and url-keyed favorites. +- **`#KODIPROP` lines before `#EXTINF` are preserved** (since `v0.15.2-iptvnator.2`) — Kodi property lines apply to the *next* list entry, so ones placed above the `#EXTINF` are buffered and attached to that item's `raw` in file order (case-insensitive prefix); other stray `#` lines outside an open item are still dropped. The DASH + ClearKey feature extracts `inputstream.adaptive.license_*` config from `item.raw`, so this delta must survive every upstream sync. There is intentionally **no URL validation** (upstream removed it in 0.15.0): any non-empty non-`#` line after `#EXTINF` becomes the item URL. This is what fixes issue #1189 (Pluto TV JWT URLs longer than validator's 2084-char IE-era limit used to be rejected, and the stalled item index collapsed the whole playlist into one channel). `#` comment lines and unknown directives are appended to `item.raw` and never treated as URLs. @@ -745,11 +746,13 @@ player in settings. **DRM data flow** (M3U module only; Xtream/Stalker have no DRM concept): -1. The playlist parser fork does not understand `#KODIPROP:` lines, but keeps - every unknown line between `#EXTINF` and the stream URL in `item.raw` (the - dominant Kodi/TiviMate layout; `#KODIPROP` lines _before_ `#EXTINF` are - dropped by the parser — fixing that requires a parser-fork patch and is - deferred). +1. The playlist parser fork does not interpret `#KODIPROP:` lines, but + preserves them in `item.raw` for **both** layouts: unknown lines between + `#EXTINF` and the stream URL are kept as before, and since parser pin + `v0.15.2-iptvnator.2` `#KODIPROP` lines placed *before* the `#EXTINF` are + buffered and attached to the **next** entry's `raw` in file order (Kodi + semantics, case-insensitive prefix). Other stray `#` lines outside an open + item are still dropped, matching upstream. 2. `extractDrmFromRaw()` (`libs/shared/m3u-utils/src/lib/kodiprop.utils.ts`) post-processes `raw` inside `createPlaylistObject()` — the single funnel for all four import paths (Electron URL/file import, refresh worker, diff --git a/package.json b/package.json index 7bfe078ef..5601cb22f 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "epg-parser": "^0.1.6", "fix-path": "5.0.0", "hls.js": "1.6.13", - "iptv-playlist-parser": "github:4gray/iptv-playlist-parser#v0.15.2-iptvnator.1", + "iptv-playlist-parser": "github:4gray/iptv-playlist-parser#v0.15.2-iptvnator.2", "marked": "18.0.5", "mpegts.js": "1.8.0", "ms": "2.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9277a2785..94ef0f18f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,8 +123,8 @@ importers: specifier: 1.6.13 version: 1.6.13 iptv-playlist-parser: - specifier: github:4gray/iptv-playlist-parser#v0.15.2-iptvnator.1 - version: https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/33f5e9c09539524d758901c02a6f29302833c0a4 + specifier: github:4gray/iptv-playlist-parser#v0.15.2-iptvnator.2 + version: https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d marked: specifier: 18.0.5 version: 18.0.5 @@ -398,7 +398,7 @@ importers: version: 30.2.0 jest-preset-angular: specifier: ~15.0.0 - version: 15.0.3(c17320d959ee89222a5c83bff087787c) + version: 15.0.3(75c2f4d3e02d43367177c3c83cdeef78) jest-util: specifier: ^30.0.2 version: 30.2.0 @@ -431,7 +431,7 @@ importers: version: 3.4.19(tsx@4.21.0)(yaml@2.8.2) ts-jest: specifier: ^29.4.5 - version: 29.4.6(@babel/core@7.29.7)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.1))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(esbuild@0.28.1)(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.1))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3) ts-node: specifier: 10.9.2 version: 10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3) @@ -7578,9 +7578,9 @@ packages: resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} - iptv-playlist-parser@https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/33f5e9c09539524d758901c02a6f29302833c0a4: - resolution: {tarball: https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/33f5e9c09539524d758901c02a6f29302833c0a4} - version: 0.15.2-iptvnator.1 + iptv-playlist-parser@https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d: + resolution: {tarball: https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d} + version: 0.15.2-iptvnator.2 engines: {node: '>=18'} iron-webcrypto@1.2.1: @@ -10508,7 +10508,6 @@ packages: shaka-player@5.2.1: resolution: {integrity: sha512-Sj8Kg94Ws8hUxzjHnC570o1jQcZwB7OxIwi25vLI3mOVmL8xpso3HyIqM7hjbgAhsOgdFojpJYwtcvR/t0a9vA==} - engines: {node: '>=18'} shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} @@ -12877,12 +12876,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12893,12 +12886,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12909,12 +12896,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12925,12 +12906,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12951,12 +12926,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12967,12 +12936,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12983,12 +12946,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13009,12 +12966,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13025,12 +12976,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13041,12 +12986,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13057,12 +12996,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13073,12 +13006,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13089,12 +13016,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13105,12 +13026,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -13121,12 +13036,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.27.1 - optional: true - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -17719,13 +17628,13 @@ snapshots: transitivePeerDependencies: - supports-color - babel-jest@30.2.0(@babel/core@7.29.7): + babel-jest@30.2.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.0 '@jest/transform': 30.2.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.2.0(@babel/core@7.29.7) + babel-preset-jest: 30.2.0(@babel/core@7.29.0) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -17838,37 +17747,17 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7): - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.29.7) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7) - optional: true - babel-preset-jest@30.2.0(@babel/core@7.28.5): dependencies: '@babel/core': 7.28.5 babel-plugin-jest-hoist: 30.2.0 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) - babel-preset-jest@30.2.0(@babel/core@7.29.7): + babel-preset-jest@30.2.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.0 babel-plugin-jest-hoist: 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) optional: true bail@2.0.2: {} @@ -20652,7 +20541,7 @@ snapshots: ipaddr.js@2.3.0: {} - iptv-playlist-parser@https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/33f5e9c09539524d758901c02a6f29302833c0a4: {} + iptv-playlist-parser@https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d: {} iron-webcrypto@1.2.1: {} @@ -21095,7 +20984,7 @@ snapshots: optionalDependencies: jest-resolve: 30.2.0 - jest-preset-angular@15.0.3(c17320d959ee89222a5c83bff087787c): + jest-preset-angular@15.0.3(75c2f4d3e02d43367177c3c83cdeef78): dependencies: '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.1) @@ -21108,7 +20997,7 @@ snapshots: jest-util: 30.2.0 jsdom: 26.1.0 pretty-format: 30.2.0 - ts-jest: 29.4.6(@babel/core@7.29.7)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.1))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3) + ts-jest: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(esbuild@0.28.1)(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.1))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3) typescript: 5.9.3 optionalDependencies: esbuild: 0.28.1 @@ -24994,7 +24883,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.6(@babel/core@7.29.7)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.1))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.29.0))(esbuild@0.28.1)(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.1))(ts-node@10.9.2(@swc/core@1.15.8(@swc/helpers@0.5.18))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -25008,10 +24897,10 @@ snapshots: typescript: 5.9.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.29.7) + babel-jest: 30.2.0(@babel/core@7.29.0) esbuild: 0.28.1 jest-util: 30.2.0