From 4c5212f4f7190b2975e7b7e770dfc75a9f77cc20 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Tue, 6 Oct 2026 14:45:00 +0800 Subject: [PATCH 1/4] feat(socket-auth): add socket.token() to mint realtime socket tokens Adds a Socket helper on the client (fleetbase.socket.token()) that calls POST socket/token and resolves to { token, expires_in, expires_at }. Documents the server-side mint / browser authenticate flow. --- .changeset/socket-token.md | 5 +++ README.md | 64 ++++++++++++++++++++++++++++++++++++++ src/fleetbase.ts | 5 +++ src/index.ts | 4 ++- src/socket.ts | 35 +++++++++++++++++++++ tests/core.test.ts | 27 ++++++++++++++++ 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 .changeset/socket-token.md create mode 100644 src/socket.ts diff --git a/.changeset/socket-token.md b/.changeset/socket-token.md new file mode 100644 index 0000000..8fcf7ce --- /dev/null +++ b/.changeset/socket-token.md @@ -0,0 +1,5 @@ +--- +'@fleetbase/sdk': minor +--- + +Add `fleetbase.socket.token()` (`POST socket/token`) to mint a short-lived realtime socket token server-side. It resolves to `{ token, expires_in, expires_at }` (`SocketTokenResponse`) and follows `setAdapter`. Additive; no existing export, store, or method changes. diff --git a/README.md b/README.md index 2a7df74..3e01bdf 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,70 @@ const place = new Place({ The root package exports the existing resource classes, adapters, collection helpers, resolver and registry hooks, string helpers, validation utilities, and TypeScript request/configuration types. +## Realtime socket tokens + +Fleetbase publishes realtime events over SocketCluster. Channel subscriptions are authorized with a short-lived socket token, so the flow is always: + +1. **On your server**, use your secret key to mint a token with `fleetbase.socket.token()` (`POST /v1/socket/token`). +2. Send **only the token** to the browser or device. Never send the API key. +3. In the browser, connect with `socketcluster-client`, present the token (`socket.authenticate(token)` or an in-memory `authEngine`), then subscribe. + +```ts +// server.ts: runs on your backend, never in the browser +import Fleetbase from '@fleetbase/sdk'; + +const fleetbase = new Fleetbase(process.env.FLEETBASE_SECRET_KEY!); + +app.post('/realtime-token', requireSignedInUser, async (_req, res) => { + // { token, expires_in, expires_at } + res.json(await fleetbase.socket.token()); +}); +``` + +```ts +// browser.ts +import { create } from 'socketcluster-client'; + +async function fetchSocketToken(): Promise<{ token: string; expires_in: number }> { + const response = await fetch('/realtime-token', { method: 'POST', credentials: 'include' }); + if (!response.ok) throw new Error(`Token request failed: ${response.status}`); + return response.json(); +} + +// Keep the token in memory only. SocketCluster calls loadToken() before every (re)connect, +// so the token travels in the handshake and subscriptions are authorized from the start. +let current: { token: string; refreshAt: number } | null = null; +const remember = ({ token, expires_in }: { token: string; expires_in: number }) => { + current = { token, refreshAt: Date.now() + (expires_in - 60) * 1000 }; + return token; +}; +const authEngine = { + saveToken: async (_name: string, token: string) => token, + removeToken: async () => { + const token = current?.token ?? null; + current = null; + return token; + }, + loadToken: async () => (current && Date.now() < current.refreshAt ? current.token : remember(await fetchSocketToken())), +}; + +const socket = create({ hostname: 'socket.example.com', secure: true, port: 443, authEngine }); + +// Refresh about 60 seconds before expiry without dropping subscriptions. +setInterval(async () => { + if (current && Date.now() >= current.refreshAt) { + await socket.authenticate(remember(await fetchSocketToken())); + } +}, 15_000); + +const channel = socket.subscribe(`company.${companyUuid}`); +for await (const event of channel) { + console.log(event); +} +``` + +A token minted with an API key may subscribe to its company channel (`company.{company uuid}`), its own key channel (`api.{key id}`), and channels of resources that belong to the same company (for example `order.{order uuid or public id}`). A server that does not have realtime authentication configured answers the mint request with `404`; in that case connect without a token as before. + ## Custom adapters Implement the stable adapter interface when requests need to use an application-specific transport: diff --git a/src/fleetbase.ts b/src/fleetbase.ts index a58a2bc..2d56297 100644 --- a/src/fleetbase.ts +++ b/src/fleetbase.ts @@ -1,4 +1,5 @@ import { detectAdapter } from './adapters/detect.js'; +import Socket from './socket.js'; import Store from './store.js'; import { driverActions, @@ -117,6 +118,8 @@ export default class Fleetbase { fuelReports: Store; issues: Store; workOrders: WorkOrderStore; + /** Realtime helpers, e.g. `socket.token()` to mint a short-lived socket token server-side. */ + socket: Socket; constructor(publicKey: string, config: FleetbaseConfig = {}, debug = false) { if (typeof publicKey !== 'string' || publicKey.length === 0) { @@ -154,6 +157,7 @@ export default class Fleetbase { this.fuelReports = new Store('fuel-report', this.adapter); this.issues = new Store('issue', this.adapter); this.workOrders = new Store('work-order', this.adapter).extendActions(workOrderActions) as WorkOrderStore; + this.socket = new Socket(this.adapter); } static newInstance(...params: ConstructorParameters): Fleetbase { @@ -162,6 +166,7 @@ export default class Fleetbase { setAdapter(adapter: AdapterLike): void { this.adapter = adapter; + this.socket.adapter = adapter; for (const store of this.stores()) { store.adapter = adapter; } diff --git a/src/index.ts b/src/index.ts index 1124872..4521281 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import EmberJsAdapter from './adapters/ember.js'; import NodeAdapter from './adapters/node.js'; import Fleetbase from './fleetbase.js'; import Resource from './resource.js'; +import Socket from './socket.js'; import Store from './store.js'; import { register } from './registry.js'; @@ -13,7 +14,7 @@ register('adapter', 'NodeAdapter', NodeAdapter); register('adapter', 'EmberJsAdapter', EmberJsAdapter); export default Fleetbase; -export { Fleetbase, Adapter, BrowserAdapter, EmberJsAdapter, NodeAdapter, Resource, Store }; +export { Fleetbase, Adapter, BrowserAdapter, EmberJsAdapter, NodeAdapter, Resource, Socket, Store }; export { detectAdapter } from './adapters/detect.js'; export { default as Collection, createCollection, isCollection, iter, objectAt, replace, uniqBy } from './collection.js'; export { FleetbaseError } from './errors.js'; @@ -46,5 +47,6 @@ export { } from './utils.js'; export { isResource } from './resource.js'; export type * from './types.js'; +export type { SocketTokenResponse } from './socket.js'; export type { DriverStore, ManifestStore, ManifestStopStore, OrderStore, OrganizationStore, ServiceQuoteStore, TrailerStore, VehicleStore, WorkOrderStore } from './fleetbase.js'; diff --git a/src/socket.ts b/src/socket.ts new file mode 100644 index 0000000..4ae28fd --- /dev/null +++ b/src/socket.ts @@ -0,0 +1,35 @@ +import type { AdapterLike, RequestOptions } from './types.js'; + +/** Response of every Fleetbase socket token mint route. */ +export interface SocketTokenResponse { + /** Short-lived HS256 JWT to hand to the realtime client (`socket.authenticate(token)`). */ + token: string; + /** Lifetime of the token in seconds. Refresh about 60 seconds before it elapses. */ + expires_in: number; + /** Expiry as an ISO 8601 timestamp. */ + expires_at: string; +} + +/** + * Realtime (SocketCluster) helpers. + * + * Mint socket tokens on a server with a secret key, then pass only the short-lived token + * to the browser or device that opens the realtime connection. + */ +export default class Socket { + adapter: AdapterLike; + + constructor(adapter: AdapterLike) { + this.adapter = adapter; + } + + /** + * `POST socket/token` — mint a short-lived realtime token for the authenticating credential. + * For an API key the token authorizes subscriptions to the key's company channel + * (`company.{company uuid}`), its own `api.{key id}` channel, and resource channels of the + * same company. For a driver's user token it is scoped to that driver's own channels. + */ + token(options: RequestOptions = {}): Promise { + return this.adapter.post('socket/token', {}, options) as Promise; + } +} diff --git a/tests/core.test.ts b/tests/core.test.ts index c2c8561..068f18c 100644 --- a/tests/core.test.ts +++ b/tests/core.test.ts @@ -39,6 +39,7 @@ import Fleetbase, { ServiceArea, ServiceQuote, ServiceRate, + Socket, Store, StoreActions, TrackingStatus, @@ -673,6 +674,32 @@ describe('driver app stores', () => { }); }); +describe('realtime socket tokens', () => { + it('mints a socket token with POST socket/token and follows adapter replacement', async () => { + const adapter = new RecordingAdapter(); + const minted = { token: 'jwt', expires_in: 900, expires_at: '2026-01-01T00:15:00.000Z' }; + adapter.response = minted; + const sdk = new Fleetbase('pk_test', { adapter }); + expect(sdk.socket).toBeInstanceOf(Socket); + expect(sdk.socket.adapter).toBe(adapter); + await expect(sdk.socket.token()).resolves.toEqual(minted); + expect(adapter.calls.at(-1)).toEqual(['POST', 'socket/token', {}, {}]); + await sdk.socket.token({ headers: { 'X-Request-Id': 'abc' } }); + expect(adapter.calls.at(-1)).toEqual(['POST', 'socket/token', {}, { headers: { 'X-Request-Id': 'abc' } }]); + const replacement = new RecordingAdapter(); + sdk.setAdapter(replacement); + expect(sdk.socket.adapter).toBe(replacement); + await sdk.socket.token(); + expect(replacement.calls).toHaveLength(1); + }); + + it('propagates mint failures such as a 404 from a server without socket auth', async () => { + const adapter = new RecordingAdapter(); + adapter.failure = new Error('Not Found'); + await expect(new Socket(adapter).token()).rejects.toThrow('Not Found'); + }); +}); + describe('published v1 compatibility snapshot', () => { it('preserves every published root export', () => { for (const name of contract.exports) { From 8c4dc7f29c50874e0125b5f167d182bac875ee81 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 8 Oct 2026 17:48:48 +0800 Subject: [PATCH 2/4] fix(node): send Node requests through the shared Fetch transport NodeAdapter overrode request() with (method, url) while the inherited get() calls request(path, 'GET', ...), so every GET under Node went to '//GET' with the path as its method. It also read the constructor-time config, so headers set later with setHeaders (Customer-Token, Accept-Language) and host changes were dropped. NodeAdapter now only adds its User-Agent and inherits the browser transport. --- src/adapters/node.ts | 29 ++++++----------------------- tests/core.test.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/adapters/node.ts b/src/adapters/node.ts index 4dc7796..1cf1f35 100644 --- a/src/adapters/node.ts +++ b/src/adapters/node.ts @@ -1,7 +1,11 @@ import BrowserAdapter from './browser.js'; -import { requestWithFetch } from './fetch.js'; -import type { AdapterConfig, RequestOptions } from '../types.js'; +import type { AdapterConfig } from '../types.js'; +/** + * The Fetch transport for Node.js. It is the browser transport plus a User-Agent, so + * every verb, the current host and namespace, and headers set later with `setHeaders` + * work the same way in both. + */ export default class NodeAdapter extends BrowserAdapter { constructor(config: AdapterConfig = {}) { const headers = new Headers(config.headers); @@ -10,25 +14,4 @@ export default class NodeAdapter extends BrowserAdapter { } super({ ...config, headers }); } - - override request(method: string, url: string, options: RequestOptions & { data?: unknown } = {}): Promise { - const { data, ...requestOptions } = options; - return requestWithFetch(this.fetchConfig, { path: url, method, data, options: requestOptions }); - } - - override post(path: string, data: unknown = {}, options: RequestOptions = {}): Promise { - return this.request('POST', path, { ...options, data }); - } - - override put(path: string, data: unknown = {}, options: RequestOptions = {}): Promise { - return this.request('PUT', path, { ...options, data }); - } - - override patch(path: string, data: unknown = {}, options: RequestOptions = {}): Promise { - return this.request('PATCH', path, { ...options, data }); - } - - override delete(path: string, options: RequestOptions = {}, legacyOptions?: RequestOptions): Promise { - return this.request('DELETE', path, legacyOptions ?? options); - } } diff --git a/tests/core.test.ts b/tests/core.test.ts index c2c8561..5299b43 100644 --- a/tests/core.test.ts +++ b/tests/core.test.ts @@ -863,6 +863,33 @@ describe('edge and failure contracts', () => { expect(new Headers(presetNode.headers).get('User-Agent')).toBe('custom'); }); + it('sends Node requests to the right URL with the method, body and current headers', async () => { + const fetchMock = vi.fn(() => Promise.resolve(new Response('{"id":"order_1"}', { status: 200 }))); + const node = new NodeAdapter({ host: 'https://api.test', namespace: 'v1', publicKey: 'pk', fetch: fetchMock }); + node.setHeaders({ 'Customer-Token': 'token_1' }); + + await expect(node.get('orders', { status: 'active' })).resolves.toEqual({ id: 'order_1' }); + await node.post('orders', { id: 1 }); + await node.delete('orders/1', {}, { headers: { 'X-Request': 'yes' } }); + + const [getUrl, getInit] = fetchMock.mock.calls[0] as unknown as [string, RequestInit]; + expect(getUrl).toBe('https://api.test/v1/orders?status=active'); + expect(getInit.method).toBe('GET'); + const getHeaders = new Headers(getInit.headers); + expect(getHeaders.get('Authorization')).toBe('Bearer pk'); + expect(getHeaders.get('Customer-Token')).toBe('token_1'); + expect(getHeaders.get('User-Agent')).toBe('@fleetbase/sdk;node'); + + const [postUrl, postInit] = fetchMock.mock.calls[1] as unknown as [string, RequestInit]; + expect(postUrl).toBe('https://api.test/v1/orders'); + expect(postInit).toMatchObject({ method: 'POST', body: '{"id":1}' }); + + const [deleteUrl, deleteInit] = fetchMock.mock.calls[2] as unknown as [string, RequestInit]; + expect(deleteUrl).toBe('https://api.test/v1/orders/1'); + expect(deleteInit.method).toBe('DELETE'); + expect(new Headers(deleteInit.headers).get('X-Request')).toBe('yes'); + }); + it('covers UUID fallback and sparse address data', () => { const originalCrypto = globalThis.crypto; vi.stubGlobal('crypto', {}); From 137f0a0cab5393c7d29c4c4f86027000c2baf6e3 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 8 Oct 2026 17:49:23 +0800 Subject: [PATCH 3/4] chore: prepare v2.1.0 release --- .changeset/driver-app-stores.md | 7 ------- CHANGELOG.md | 11 +++++++++++ RELEASE.md | 14 ++++++-------- package.json | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 .changeset/driver-app-stores.md diff --git a/.changeset/driver-app-stores.md b/.changeset/driver-app-stores.md deleted file mode 100644 index 75a3974..0000000 --- a/.changeset/driver-app-stores.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@fleetbase/sdk': minor ---- - -Add first-class stores and resources for the Fleet-Ops driver workflow: `manifests` (with `optimize` and `drivers.manifests`), `manifestStops` (PATCH `update`), `trailers` (CRUD plus `attach`, `detach`, `connections`, `track`, and `vehicles.trailers`), `fuelReports`, `issues`, `workOrders` (with `send`), and the driver password actions `changePassword`, `forgotPassword`, and `resetPassword`. All additions are additive; no existing export, store, or method changes. - -Trailer attach/detach and work-order send preserve resource identity and return typed connection/acknowledgement responses. Public store types are exported from the package root. Inspection APIs are deferred pending a released backend contract. diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ebfbe..00cc456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to the Fleetbase JavaScript SDK are documented here. This project follows semantic versioning. +## 2.1.0 + +### Added + +- Fleet-Ops driver workflow stores and resources: `manifests` (with `optimize` and `drivers.manifests`), `manifestStops` (PATCH `update`), `trailers` (CRUD plus `attach`, `detach`, `connections`, `track` and `vehicles.trailers`), `fuelReports`, `issues` and `workOrders` (with `send`), and the driver password actions `changePassword`, `forgotPassword` and `resetPassword`. +- `fleetbase.socket.token()` to mint realtime socket tokens. + +### Fixed + +- `NodeAdapter` sent GET requests to the wrong path and ignored headers set after construction; it now shares the browser Fetch transport. + ## 2.0.0 ### Changed diff --git a/RELEASE.md b/RELEASE.md index cd068ee..fbf0fb0 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,15 +1,13 @@ -> v2.0.0 ~ "Modern JavaScript packaging and first-party compatibility" +> v2.1.0 ~ "Driver workflows, realtime socket tokens and a working Node transport" ## Highlights -- Strict TypeScript implementation with native ESM, CommonJS, browser bundles, source maps, and public declarations. -- Preserved adapter extension points, authentication/header overrides, mutation bodies, and driver GeoJSON coordinates used by Navigator and Storefront. -- Deterministic contract tests with enforced 100% statement, branch, function, and line coverage, plus packed-artifact framework and package-manager checks. -- Structured API errors, consistent resource state cleanup, and corrected collection and persistence helpers. -- Automatic tagging, npm publication, and GitHub releases after a reviewed release-branch merge, with provenance, checksum verification, and post-publication ESM/CommonJS checks. +- Fleet-Ops driver workflow stores: `manifests` (with `optimize` and `drivers.manifests`), `manifestStops`, `trailers` (CRUD, `attach`, `detach`, `connections`, `track`, `vehicles.trailers`), `fuelReports`, `issues` and `workOrders` (with `send`), plus the driver password actions `changePassword`, `forgotPassword` and `resetPassword`. +- `fleetbase.socket.token()` mints short-lived realtime socket tokens (`POST socket/token`) for server-side code that holds a secret key or user token. +- `NodeAdapter` requests work: GET requests reach the right path, and headers set after construction with `setHeaders` are sent. Node consumers of v2.0.0, including the Storefront SDK under Node, should upgrade. ## Compatibility -Node 20.19.4 remains a legacy runtime compatibility target; Node 22/24 are recommended. Building this repository requires Node 22.13 or newer. Existing v1 declaration-path consumers retain a compatibility entry. +All additions are additive; no export, store or method changes. Node 20.19.4 remains a legacy runtime target; Node 22/24 are recommended. -This is the review branch's release metadata, not evidence that v2 has been published. Native application acceptance and owner-controlled publishing configuration must pass before release. +This is the release branch's metadata, not evidence that v2.1.0 has been published. Merging this release PR into `main` tags and publishes it. diff --git a/package.json b/package.json index 821b4ec..52e7e34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/sdk", - "version": "2.0.0", + "version": "2.1.0", "description": "Official JavaScript and TypeScript SDK for the Fleetbase API", "type": "module", "main": "./dist/index.cjs", From a502a95949230836f367c2a6678fd359c815d7b9 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Thu, 8 Oct 2026 18:02:12 +0800 Subject: [PATCH 4/4] chore(deps): pin patched brace-expansion, fast-uri, shell-quote and source-map-js New high and critical advisories in development tooling (eslint, stryker, vitest) failed the dependency audit. None of these ship in the package. --- pnpm-lock.yaml | 38 +++++++++++++++++++++----------------- pnpm-workspace.yaml | 4 ++++ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9370509..bd041cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,10 @@ overrides: flatted: '>=3.4.2' picomatch@>=4.0.0 <4.0.4: '>=4.0.4' serialize-javascript: '>=7.0.3' + brace-expansion@>=4.0.0 <5.0.11: ^5.0.11 + fast-uri@>=3.0.0 <3.1.8: ^3.1.8 + shell-quote@>=1.8.4 <1.11.0: ^1.11.0 + source-map-js@<1.2.2: ^1.2.2 importers: .: @@ -1093,8 +1097,8 @@ packages: engines: { node: '>=6.0.0' } hasBin: true - brace-expansion@5.0.9: - resolution: { integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg== } + brace-expansion@5.0.12: + resolution: { integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ== } engines: { node: 20 || >=22 } browserslist@4.28.8: @@ -1332,8 +1336,8 @@ packages: fast-string-width@3.0.2: resolution: { integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg== } - fast-uri@3.1.6: - resolution: { integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q== } + fast-uri@3.1.8: + resolution: { integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg== } fast-wrap-ansi@0.2.2: resolution: { integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q== } @@ -1867,8 +1871,8 @@ packages: resolution: { integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== } engines: { node: '>=8' } - shell-quote@1.10.0: - resolution: { integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA== } + shell-quote@1.12.0: + resolution: { integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q== } engines: { node: '>= 0.4' } side-channel-list@1.0.1: @@ -1901,8 +1905,8 @@ packages: resolution: { integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA== } engines: { node: '>=8' } - source-map-js@1.2.1: - resolution: { integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== } + source-map-js@1.2.2: + resolution: { integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw== } engines: { node: '>=0.10.0' } source-map-support@0.5.21: @@ -3165,7 +3169,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.6 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -3197,7 +3201,7 @@ snapshots: baseline-browser-mapping@2.11.20: {} - brace-expansion@5.0.9: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -3437,7 +3441,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.6: {} + fast-uri@3.1.8: {} fast-wrap-ansi@0.2.2: dependencies: @@ -3601,7 +3605,7 @@ snapshots: launch-editor@2.14.1: dependencies: picocolors: 1.1.1 - shell-quote: 1.10.0 + shell-quote: 1.12.0 levn@0.4.1: dependencies: @@ -3673,7 +3677,7 @@ snapshots: dependencies: '@babel/parser': 7.29.8 '@babel/types': 7.29.8 - source-map-js: 1.2.1 + source-map-js: 1.2.2 make-dir@4.0.0: dependencies: @@ -3698,7 +3702,7 @@ snapshots: minimatch@10.2.6: dependencies: - brace-expansion: 5.0.9 + brace-expansion: 5.0.12 mri@1.2.0: {} @@ -3795,7 +3799,7 @@ snapshots: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 prelude-ls@1.2.1: {} @@ -3912,7 +3916,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.10.0: {} + shell-quote@1.12.0: {} side-channel-list@1.0.1: dependencies: @@ -3952,7 +3956,7 @@ snapshots: dependencies: unicode-emoji-modifier-base: 1.0.0 - source-map-js@1.2.1: {} + source-map-js@1.2.2: {} source-map-support@0.5.21: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 568f979..1d90dc0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,3 +5,7 @@ overrides: flatted: '>=3.4.2' 'picomatch@>=4.0.0 <4.0.4': '>=4.0.4' serialize-javascript: '>=7.0.3' + 'brace-expansion@>=4.0.0 <5.0.11': '^5.0.11' + 'fast-uri@>=3.0.0 <3.1.8': '^3.1.8' + 'shell-quote@>=1.8.4 <1.11.0': '^1.11.0' + 'source-map-js@<1.2.2': '^1.2.2'