diff --git a/.gitignore b/.gitignore index e07bb98..e5715cd 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,10 @@ public/dist .turbo wrangler.toml **/coverage/** +.wrangler +bin/ +**/fastly/**/pkg/ # ignore all .vscode folders unless they're in the root directory .vscode diff --git a/package.json b/package.json index ae68b3d..cd0cf7b 100755 --- a/package.json +++ b/package.json @@ -26,5 +26,8 @@ "prettier": "^2.5.1", "turbo": "latest" }, - "packageManager": "yarn@3.4.1" + "packageManager": "yarn@3.4.1", + "resolutions": { + "@segment/edge-sdk": "workspace:*" + } } diff --git a/packages/edge-sdk-integration-tests/.eslintrc.js b/packages/edge-sdk-integration-tests/.eslintrc.js new file mode 100644 index 0000000..7dd406d --- /dev/null +++ b/packages/edge-sdk-integration-tests/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["@segment/edge-sdk"], +}; diff --git a/packages/edge-sdk-integration-tests/jest.config.cjs b/packages/edge-sdk-integration-tests/jest.config.cjs new file mode 100644 index 0000000..b413e10 --- /dev/null +++ b/packages/edge-sdk-integration-tests/jest.config.cjs @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/package.json b/packages/edge-sdk-integration-tests/package.json new file mode 100644 index 0000000..1805542 --- /dev/null +++ b/packages/edge-sdk-integration-tests/package.json @@ -0,0 +1,22 @@ +{ + "name": "@segment/edge-sdk-integration-tests", + "version": "0.0.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "test": "jest" + }, + "dependencies": { + "@segment/edge-sdk": "*" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20231002.0", + "@fastly/js-compute": "^3.7.3", + "@segment/eslint-config-edge-sdk": "*", + "@types/jest": "^29.5.9", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "wrangler": "^3.17.0" + } +} \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/cloudflare-workers/setup.ts b/packages/edge-sdk-integration-tests/src/cloudflare-workers/setup.ts new file mode 100644 index 0000000..35bd0c4 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/cloudflare-workers/setup.ts @@ -0,0 +1,18 @@ +import { join as joinPath } from "path"; +import { unstable_dev } from "wrangler"; +import { Service } from '../service'; + +export async function startService(): Promise { + const worker = await unstable_dev(joinPath(__dirname, "workers", "index.ts"), { + experimental: { disableExperimentalWarning: true, }, + bundle: true, + nodeCompat: false, + config: joinPath(__dirname, "workers", "wrangler.test.toml"), + }); + + return { + fetch: worker.fetch.bind(worker) as any as Service['fetch'], + stop: worker.stop.bind(worker), + edgeSdkSettings: {} + } +} \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/cloudflare-workers/workers/index.ts b/packages/edge-sdk-integration-tests/src/cloudflare-workers/workers/index.ts new file mode 100644 index 0000000..93e7e73 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/cloudflare-workers/workers/index.ts @@ -0,0 +1,18 @@ +/// +import { Segment } from '@segment/edge-sdk' + +const EdgeSettingsHeader = 'x-segment-edge-settings'; +const EdgeFeaturesHeader = "x-segment-edge-features"; + +export default { + async fetch(request: Request, _env: {}, _ctx: ExecutionContext) { + console.log(request.url) + const settingsHeader = request.headers.get(EdgeSettingsHeader); + const featuresHeader = request.headers.get(EdgeFeaturesHeader); + const settings = settingsHeader ? JSON.parse(settingsHeader) : undefined; + const features = featuresHeader ? JSON.parse(featuresHeader) : undefined; + const segment = new Segment(settings, features) + const response = await segment.handleEvent(request); + return response; + }, +} diff --git a/packages/edge-sdk-integration-tests/src/cloudflare-workers/workers/wrangler.test.toml b/packages/edge-sdk-integration-tests/src/cloudflare-workers/workers/wrangler.test.toml new file mode 100644 index 0000000..0f73536 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/cloudflare-workers/workers/wrangler.test.toml @@ -0,0 +1,6 @@ +name = "test-worker" +compatibility_date = "2022-11-30" + +[dev] +# Mock Origin server +host = "localhost:3001" \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/fastly/service/fastly.toml b/packages/edge-sdk-integration-tests/src/fastly/service/fastly.toml new file mode 100644 index 0000000..3f2083e --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/fastly/service/fastly.toml @@ -0,0 +1,14 @@ +language = "javascript" +manifest_version = 3 +name = "test-fastly" + +[local_server] + [local_server.backends.origin] + url = "http://localhost:3001" + [local_server.backends.segment-cdn] + url = "http://localhost:3002" + [local_server.backends.tracking-api] + url = "http://localhost:3003" + +[scripts] + build = "npx js-compute-runtime ./src/fastly/service/index.js ./src/fastly/service/bin/main.wasm" \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/fastly/service/index.js b/packages/edge-sdk-integration-tests/src/fastly/service/index.js new file mode 100644 index 0000000..2470309 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/fastly/service/index.js @@ -0,0 +1,22 @@ +/// +import { Segment } from '@segment/edge-sdk'; + +const EdgeSettingsHeader = 'x-segment-edge-settings'; +const EdgeFeaturesHeader = "x-segment-edge-features"; + +addEventListener('fetch', (event) => event.respondWith(handleRequest(event))); + +/** + * @param {FetchEvent} event + * @returns {Promise} + */ +async function handleRequest(event) { + const { request } = event; + const settingsHeader = request.headers.get(EdgeSettingsHeader); + const featuresHeader = request.headers.get(EdgeFeaturesHeader); + const settings = settingsHeader ? JSON.parse(settingsHeader) : undefined; + const features = featuresHeader ? JSON.parse(featuresHeader) : undefined; + const segment = new Segment(settings, features) + const response = await segment.handleEvent(request); + return response; +} \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/fastly/setup.ts b/packages/edge-sdk-integration-tests/src/fastly/setup.ts new file mode 100644 index 0000000..abfd4a7 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/fastly/setup.ts @@ -0,0 +1,60 @@ +import { spawn } from 'node:child_process'; +import { join as joinPath } from "node:path"; +import { Service } from "../service"; + +export async function startService(): Promise { + const abortController = new AbortController(); + const host = '127.0.0.1:3010'; + const fastly = spawn('fastly', ['compute', 'serve', '--addr', host, '--dir', joinPath(__dirname, 'service')], { + signal: abortController.signal + }); + + let serverRunningResolver: () => void; + const serverRunning = new Promise((resolve) => { + serverRunningResolver = resolve; + }); + + fastly.stderr.on('data', (data) => { + // console.error(`stderr: ${data}`); + }); + fastly.stdout.on('data', (data) => { + if (data.toString().includes('Listening on http://127.0.0.1:3010')) { + serverRunningResolver(); + } + console.log(`stdout: ${data}`); + }); + + await serverRunning; + + return { + fetch: (...args) => { + if (typeof args[0] === 'string') { + const url = new URL(args[0]); + url.host = host; + url.protocol = 'http'; + args[0] = url.toString(); + } else { + const request = args[0]; + const url = new URL(request.url); + url.host = host; + url.protocol = 'http'; + args[0] = new Request(url.toString(), request); + } + return fetch(...args); + }, + stop: () => { + return new Promise((resolve, reject) => { + fastly.on('close', resolve); + fastly.kill(); + }); + }, + edgeSdkSettings: { + fastly: { + segmentCdnBackend: 'segment-cdn', + segmentTrackingAPIBackend: 'tracking-api', + websiteOriginBackend: 'origin' + } + } + } +} + diff --git a/packages/edge-sdk-integration-tests/src/index.test.ts b/packages/edge-sdk-integration-tests/src/index.test.ts new file mode 100644 index 0000000..ed39917 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/index.test.ts @@ -0,0 +1,682 @@ +import type { EdgeSDKFeatures, EdgeSDKSettings } from "@segment/edge-sdk"; +import { MockCdnServer } from "./mocks/mockCdn"; +import { MockOriginServer } from "./mocks/mockOrigin"; +import { EventPayload, MockTrackingApi } from "./mocks/mockTrackingApi"; +import { startService as startCloudflareService } from "./cloudflare-workers/setup"; +import { startService as startFastlyService } from './fastly/setup'; +import { Service } from './service'; + +const EdgeSettingsHeader = "x-segment-edge-settings"; +const EdgeFeaturesHeader = "x-segment-edge-features"; + +const portMappings = { + origin: 3001, + cdn: 3002, + tapi: 3003, +}; + +const environments = new Map([ + ['Cloudflare Workers', startCloudflareService], + ['Fastly Compute@Edge', startFastlyService], +]) + +environments.forEach((startService, environment) => { + + describe(`Edge SDK in ${environment}`, () => { + let service: Service; + + beforeAll(async () => { + service = await startService(); + }); + + afterAll(async () => { + await service.stop(); + }); + + describe("origin proxy", () => { + let edgeSDKSettings: EdgeSDKSettings; + + let mockOriginServer: MockOriginServer; + beforeAll(async () => { + // Setup mock origin + mockOriginServer = new MockOriginServer({ port: portMappings.origin }); + await mockOriginServer.start(); + edgeSDKSettings = { + ...service.edgeSdkSettings, + writeKey: "FAKE_WRITE_KEY", + routePrefix: "seg", + }; + }); + + afterAll(async () => { + // Tear down mock origin + await mockOriginServer.stop(); + }); + + it('proxies origin without modifications', async () => { + const edgeSDKFeatures: Partial = { + ajsInjection: false, + proxyOrigin: true + } + const workerResponse = await service.fetch("https://customer.com/", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const html = await workerResponse.text(); + expect(html).toContain('Hello from the customer origin!'); + }); + + it('does not proxy origin if the flag is off', async () => { + const edgeSDKFeatures: Partial = { + proxyOrigin: false + } + const workerResponse = await service.fetch("https://customer.com/", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(404); + const html = await workerResponse.text(); + expect(html).toContain('Not Found'); + }); + }); + + describe("ajs snippet injection", () => { + let edgeSDKSettings: EdgeSDKSettings; + + let mockOriginServer: MockOriginServer; + beforeAll(async () => { + // Setup mock origin + mockOriginServer = new MockOriginServer({ port: portMappings.origin }); + await mockOriginServer.start(); + edgeSDKSettings = { + ...service.edgeSdkSettings, + writeKey: "FAKE_WRITE_KEY", + routePrefix: "seg", + }; + }); + + afterAll(async () => { + // Tear down mock origin + await mockOriginServer.stop(); + }); + + it('AJS snippet injection with writeKey redaction', async () => { + const edgeSDKFeatures: Partial = {}; + const workerResponse = await service.fetch("https://customer.com/", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const html = await workerResponse.text(); + expect(html).toContain('analytics.load("REDACTED");'); // write key is redacted + expect(html).toContain('analytics._writeKey="REDACTED";'); // write key is redacted + expect(html).toContain('t.src="https://customer.com/seg/ajs/'); // AJS URL excluding the randomized bit + expect(html).toContain('analytics._cdn = "https://customer.com/seg"'); // CDN is configured properly + expect(html).toContain("analytics.page()"); // There is a default page call + + expect(html).toContain('Hello from the customer origin!'); // page content is rendered + }); + + it('AJS snippet injection without writeKey redaction', async () => { + const writeKey = edgeSDKSettings.writeKey; + expect(writeKey).not.toBe('REDACTED'); + const edgeSDKFeatures: Partial = { + redactWritekey: false + }; + const workerResponse = await service.fetch("https://customer.com/", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const html = await workerResponse.text(); + expect(html).toContain(`analytics.load("${writeKey}");`); // write key is not redacted + expect(html).toContain(`analytics._writeKey="${writeKey}";`); // write key is not redacted + expect(html).toContain('t.src="https://customer.com/seg/ajs/'); // AJS URL excluding the randomized bit + expect(html).toContain('analytics._cdn = "https://customer.com/seg"'); // CDN is configured properly + expect(html).toContain("analytics.page()"); // There is a default page call + + expect(html).toContain('Hello from the customer origin!'); // page content is rendered + }); + + it('AJS snippet injection respects the feature flag', async () => { + const edgeSDKFeatures: Partial = { + ajsInjection: false + }; + const workerResponse = await service.fetch("https://customer.com/", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const html = await workerResponse.text(); + expect(html).not.toContain('analytics'); // no mention of analytics on page + expect(html).toContain('Hello from the customer origin!'); // page content is rendered + }); + + it('Avoid AJS snippet injection into non-200 responses from origin', async () => { + const edgeSDKFeatures: Partial = {}; + const workerResponse = await service.fetch("https://customer.com/some-fake-path", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(404); + const html = await workerResponse.text(); + expect(html).not.toContain('analytics'); // no mention of analytics on page + expect(html).toContain('Unknown origin resource'); // page content is rendered + }); + + it('AJS snippet injection with disabled page call', async () => { + const edgeSDKFeatures: Partial = {}; + const workerResponse = await service.fetch("https://customer.com/", { + headers: { + [EdgeSettingsHeader]: JSON.stringify({ ...edgeSDKSettings, snippetInitialPageView: false }), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const html = await workerResponse.text(); + expect(html).toContain('analytics.load("REDACTED");'); // write key is redacted + expect(html).toContain('analytics._writeKey="REDACTED";'); // write key is redacted + expect(html).toContain('t.src="https://customer.com/seg/ajs/'); // AJS URL excluding the randomized bit + expect(html).toContain('analytics._cdn = "https://customer.com/seg"'); // CDN is configured properly + expect(html).not.toContain("analytics.page()"); // There is no page call + + expect(html).toContain('Hello from the customer origin!'); // page content is rendered + }); + }); + + describe("assets proxy", () => { + let mockCdnServer: MockCdnServer; + let edgeSDKSettings: EdgeSDKSettings; + beforeAll(async () => { + // Setup mock CDN + mockCdnServer = new MockCdnServer({ port: portMappings.cdn, validWriteKeys: ['FAKE_WRITE_KEY'] }); + await mockCdnServer.start(); + edgeSDKSettings = { + ...service.edgeSdkSettings, + writeKey: "FAKE_WRITE_KEY", + baseSegmentCDN: `http://${mockCdnServer.host}`, + routePrefix: "seg", + } + }); + + afterAll(async () => { + // Tear down mock CDN + await mockCdnServer.stop(); + }); + + it('Makes AJS available on the first party domain', async () => { + const edgeSDKFeatures: Partial = {} + let workerResponse = await service.fetch("http://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + }); + expect(workerResponse.status).toBe(200); + expect(await workerResponse.text()).toContain("Analytics JS Code!"); // Original AJS Code exists + + workerResponse = await service.fetch("http://customer.com/seg/v1/projects/anything/settings", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + }); + expect(workerResponse.status).toBe(200); + expect(await workerResponse.text()).toContain("Segment.io"); // Returns settings content + + workerResponse = await service.fetch("http://customer.com/seg/analytics-next/bundles/core.bundle.1231223.js", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + }); + expect(workerResponse.status).toBe(200); + expect(await workerResponse.text()).toContain("Core bundle 👨🏻‍💻"); // Returns AJS core bundles + + workerResponse = await service.fetch("http://customer.com/seg/next-integrations/integrations/segment-integration/1.0.0/segment-integration.js.gz", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + }); + expect(workerResponse.status).toBe(200); + expect(await workerResponse.text()).toContain("Legacy destination 👴"); // Returns classic destinations + + workerResponse = await service.fetch("http://customer.com/seg/next-integrations/actions/segment-plugin/1231223.js", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + }); + expect(workerResponse.status).toBe(200); + expect(await workerResponse.text()).toContain("Action destination 💥"); // Returns action destinations + }); + + it('AJS: Configures the AJS CDN correctly in AJS code', async () => { + const edgeSDKFeatures: Partial = {} + const workerResponse = await service.fetch("https://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + expect(workerResponse.status).toBe(200); + expect(await workerResponse.text()).toContain('analytics._cdn = "https://customer.com/seg'); + }); + + it('AJS: Configures AJS with Id cookies that exist in the request', async () => { + const edgeSDKFeatures: Partial = {} + const workerResponse = await service.fetch("https://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com', + cookie: "ajs_user_id=123; ajs_anonymous_id=xyz" + }, + }); + expect(workerResponse.status).toBe(200); + const ajs = await workerResponse.text(); + expect(ajs).toContain('analytics.setAnonymousId("xyz");'); + expect(ajs).toContain('analytics.identify("123");'); + }); + + it('AJS: generates anonymousId automatically if it is not already part of request cookies', async () => { + const edgeSDKFeatures: Partial = {} + const workerResponse = await service.fetch("https://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com', + }, + }); + expect(workerResponse.status).toBe(200); + const ajs = await workerResponse.text(); + expect(ajs).toContain('analytics.setAnonymousId("'); + expect(ajs).not.toContain('analytics.identify("123");'); + }); + + it('AJS: should not configure user id if server-side cookie and client-side traits feature is disabled', async () => { + const edgeSDKFeatures: Partial = { + serverSideCookies: false, + clientSideTraits: false + } + const workerResponse = await service.fetch("https://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com', + cookie: "ajs_user_id=123; ajs_anonymous_id=xyz" + }, + }); + expect(workerResponse.status).toBe(200); + const ajs = await workerResponse.text(); + expect(ajs).toContain('analytics._cdn = "https://customer.com/seg'); + expect(ajs).not.toContain('analytics.setAnonymousId("xyz");'); + expect(ajs).not.toContain('analytics.identify("123");'); + }); + + it('AJS: should set server-side cookies when returning AJS', async () => { + const edgeSDKFeatures: Partial = {}; + const workerResponse = await service.fetch("https://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com', + cookie: "ajs_user_id=123; ajs_anonymous_id=xyz" + }, + }); + expect(workerResponse.status).toBe(200); + const ajs = await workerResponse.text(); + expect(ajs).toContain('Analytics JS Code!'); + expect(workerResponse.headers.get('set-cookie')).toContain('ajs_user_id=123'); + expect(workerResponse.headers.get('set-cookie')).toContain('ajs_anonymous_id=xyz'); + expect(workerResponse.headers.get('set-cookie')).toContain('HttpOnly'); + expect(workerResponse.headers.get('set-cookie')).toContain('Domain=customer.com'); + }); + + it('AJS: invalid responses are returned verbatim', async () => { + const edgeSDKFeatures: Partial = {}; + const ajsWorkerResponse = await service.fetch("https://customer.com/seg/ajs/111-111-111-111", { + headers: { + [EdgeSettingsHeader]: JSON.stringify({ ...edgeSDKSettings, writeKey: 'INVALID_WRITEKEY' }), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures) + }, + }); + // 404 from Segment CDN is returned without any modifications + expect(ajsWorkerResponse.status).toBe(404); + const ajs = await ajsWorkerResponse.text(); + expect(ajs).toBe('Cannot GET - Invalid path or write key provided.'); + + const settingsWorkerResponse = await service.fetch("https://customer.com/seg/v1/projects/anything/settings", { + headers: { + [EdgeSettingsHeader]: JSON.stringify({ ...edgeSDKSettings, writeKey: 'INVALID_WRITEKEY' }), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures) + }, + }); + // 404 from Segment CDN is returned without any modifications + expect(settingsWorkerResponse.status).toBe(404); + const settings = await settingsWorkerResponse.text(); + expect(settings).toBe('Cannot GET - Invalid path or write key provided.'); + }); + + it('Settings: configures API host to point to the first-party domain', async () => { + const edgeSDKFeatures: Partial = {}; + const workerResponse = await service.fetch("https://customer.com/seg/v1/projects/anything/settings", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const settings = await workerResponse.json() as any; + expect(settings.integrations['Segment.io'].apiHost).toBe('customer.com/seg/evs'); // API host is set to 1st party domain + }); + + it('Settings: redacts the writeKey in settings if the flag is set to true', async () => { + const edgeSDKFeatures: Partial = { redactWritekey: true }; + const workerResponse = await service.fetch("https://customer.com/seg/v1/projects/anything/settings", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const settings = await workerResponse.json() as any; + expect(settings.integrations['Segment.io'].apiKey).toBe('REDACTED'); + }); + + it('Settings: does not redact the writeKey in settings if the flag is set to false', async () => { + const edgeSDKFeatures: Partial = { redactWritekey: false }; + const workerResponse = await service.fetch("https://customer.com/seg/v1/projects/anything/settings", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const settings = await workerResponse.json() as any; + expect(settings.integrations['Segment.io'].apiKey).toBe(edgeSDKSettings.writeKey); + expect(settings.integrations['Segment.io'].apiKey).not.toBe('REDACTED'); + }); + + it('Settings: should allow access from any origin', async () => { + const edgeSDKFeatures: Partial = {}; + const workerResponse = await service.fetch("https://customer.com/seg/v1/projects/anything/settings", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const settings = await workerResponse.json() as any; + expect(settings.integrations['Segment.io']).toBeDefined(); + expect(workerResponse.headers.get('access-control-allow-origin')).toBe('*'); + }); + }); + + describe("tapi handler", () => { + const snapshotMatcher = { + context: expect.objectContaining({ + library: expect.objectContaining({ + version: expect.any(String), + }), + edge: expect.any(Object), + }), + }; + + const reqBodyFixture = { + timestamp: "2022-10-06T06:08:42.057Z", + integrations: { "Segment.io": true }, + userId: "🤿", + anonymousId: "👻", + type: "identify", + traits: { isCool: "no" }, + context: { + library: { name: "analytics.js", version: "next-1.43.0" }, + }, + messageId: "ajs-next-f142195b60efd67506bd5c4f7a4ffa99", + writeKey: "Shall not be revealed", + } as const; + + let mockTrackingApi: MockTrackingApi; + let edgeSDKSettings: EdgeSDKSettings; + beforeEach(async () => { + // Setup mock tracking API + mockTrackingApi = new MockTrackingApi({ port: portMappings.tapi }); + await mockTrackingApi.start(); + edgeSDKSettings = { + ...service.edgeSdkSettings, + writeKey: "", + trackingApiEndpoint: `http://${mockTrackingApi.host}/v1`, + routePrefix: "seg", + } + }); + + afterEach(async () => { + // Tear down mock tracking API + await mockTrackingApi.stop(); + }); + + + it("Proxies TAPI", async () => { + const edgeSDKFeatures: Partial = { + edgeContext: false + }; + const tapiEvents: EventPayload[] = []; + mockTrackingApi.on("event", (event) => { + tapiEvents.push(event); + }); + + const workerResponse = await service.fetch("http://customer.com/seg/evs/p", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + method: "POST", + body: JSON.stringify({ type: "page" }), + }); + await workerResponse.text(); + + expect(workerResponse.status).toBe(200); + expect(tapiEvents.length).toBe(1); + expect(tapiEvents[0].path).toBe("/v1/p"); + expect(tapiEvents[0].method).toBe("POST"); + expect(tapiEvents[0].event).toMatchInlineSnapshot( + { + context: expect.objectContaining({ + library: expect.objectContaining({ + version: expect.any(String), + }), + }) + }, + ` + { + "_metadata": { + "jsRuntime": "cloudflare-worker", + }, + "context": ObjectContaining { + "library": ObjectContaining { + "version": Any, + }, + }, + "type": "page", + } + ` + ); + }); + + it("Enrich identify calls with Edge traits", async () => { + const edgeSDKFeatures: Partial = { + edgeContext: true + }; + const tapiEvents: EventPayload[] = []; + mockTrackingApi.on("event", (event) => { + tapiEvents.push(event); + }); + + const workerResponse = await service.fetch("http://customer.com/seg/evs/i", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + }, + method: "POST", + body: JSON.stringify(reqBodyFixture), + }); + await workerResponse.text(); + + expect(workerResponse.status).toBe(200); + expect(tapiEvents.length).toBe(1); + expect(tapiEvents[0].path).toBe("/v1/i"); + expect(tapiEvents[0].method).toBe("POST"); + expect(tapiEvents[0].event).toMatchInlineSnapshot( + { + context: expect.objectContaining({ + library: expect.objectContaining({ + version: expect.any(String), + }), + edge: expect.any(Object), + }), + }, + ` + { + "_metadata": { + "jsRuntime": "cloudflare-worker", + }, + "anonymousId": "👻", + "context": ObjectContaining { + "edge": Any, + "library": ObjectContaining { + "version": Any, + }, + }, + "integrations": { + "Segment.io": true, + }, + "messageId": "ajs-next-f142195b60efd67506bd5c4f7a4ffa99", + "timestamp": "2022-10-06T06:08:42.057Z", + "traits": { + "isCool": "no", + }, + "type": "identify", + "userId": "🤿", + } + ` + ); + }); + + it("Inject writekey to the headers", async () => { + const tapiEvents: EventPayload[] = []; + mockTrackingApi.on("event", (event) => { + tapiEvents.push(event); + }); + + const workerResponse = await service.fetch("http://customer.com/seg/evs/p", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + }, + method: "POST", + body: JSON.stringify({ type: "page", writeKey: "REDACTED" }), + }); + await workerResponse.text(); + + expect(workerResponse.status).toBe(200); + expect(tapiEvents.length).toBe(1); + expect(tapiEvents[0].path).toBe("/v1/p"); + expect(tapiEvents[0].method).toBe("POST"); + expect(tapiEvents[0].headers["authorization"]).toBe("Basic Og=="); + expect(tapiEvents[0].event.writeKey).toBeUndefined(); + }); + + it("Injects metadata into the body", async () => { + const tapiEvents: EventPayload[] = []; + mockTrackingApi.on("event", (event) => { + tapiEvents.push(event); + }); + + const workerResponse = await service.fetch("http://customer.com/seg/evs/p", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + }, + method: "POST", + body: JSON.stringify(reqBodyFixture), + }); + await workerResponse.text(); + + expect(workerResponse.status).toBe(200); + expect(tapiEvents.length).toBe(1); + expect(tapiEvents[0].path).toBe("/v1/p"); + expect(tapiEvents[0].method).toBe("POST"); + expect(tapiEvents[0].event._metadata.jsRuntime).toBe("cloudflare-worker"); + const [edgeVersion, ajsVersion] = tapiEvents[0].event.context.library.version.split(':'); + expect(edgeVersion).toMatch(/edge-\d*/); + expect(ajsVersion).toMatch(reqBodyFixture.context.library.version); + }); + }); + + describe("reset endpoint", () => { + let edgeSDKSettings: EdgeSDKSettings; + + beforeAll(() => { + edgeSDKSettings = { + ...service.edgeSdkSettings, + writeKey: "FAKE_WRITE_KEY", + routePrefix: "seg", + }; + }); + + it("resets the server-side cookies", async () => { + const edgeSDKFeatures: Partial = {} + const workerResponse = await service.fetch("https://customer.com/seg/reset", { + headers: { + [EdgeSettingsHeader]: JSON.stringify(edgeSDKSettings), + [EdgeFeaturesHeader]: JSON.stringify(edgeSDKFeatures), + host: 'customer.com' + }, + }); + + expect(workerResponse.status).toBe(200); + const data = await workerResponse.text(); + expect(data).toContain('Success!'); + expect(workerResponse.headers.get("set-cookie")).toContain("ajs_anonymous_id=;"); + expect(workerResponse.headers.get("set-cookie")).toContain("ajs_user_id=;"); + expect(workerResponse.headers.get("set-cookie")).toContain("Max-Age=0;"); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/mocks/mockCdn.ts b/packages/edge-sdk-integration-tests/src/mocks/mockCdn.ts new file mode 100644 index 0000000..c77eb53 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/mocks/mockCdn.ts @@ -0,0 +1,140 @@ +import { createServer } from 'http'; + +export interface MockCdnServerProps { + port: number; + validWriteKeys: string[]; +} + +export class MockCdnServer { + private port: number; + private server: ReturnType; + private validWriteKeys: Set; + + constructor({port, validWriteKeys}: MockCdnServerProps) { + this.port = port; + this.validWriteKeys = new Set(validWriteKeys); + this.server = createServer((req, res) => { + const url = new URL(req.url!, `http://localhost:${this.port}`); + const path = url.pathname; + + if (!['get', 'options'].includes(req.method!.toLowerCase())) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end(`Cannot ${req.method!} - Invalid method.`); + return; + } + + const pathData = pathMatcher(path); + if (pathData.type === 'ajs') { + if (!this.validWriteKeys.has(pathData.writeKey)) { + res.writeHead(404); + res.end(`Cannot GET - Invalid path or write key provided.`); + return; + } + + res.writeHead(200, { 'Content-Type': 'text/javascript' }); + res.end(`Analytics JS Code!`); + return; + } else if (pathData.type === 'settings') { + if (!this.validWriteKeys.has(pathData.writeKey)) { + res.writeHead(404); + res.end(`Cannot GET - Invalid path or write key provided.`); + return; + } + + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ + "integrations": { + "Segment.io": { + "apiKey": pathData.writeKey, + "apiHost": "api.segment.io/v1" + } + }, + "metrics": { + "sampleRate": 0.1, + "host": "api.segment.io/v1" + }, + "remotePlugins": [] + })); + return; + } else if (pathData.type === 'core-bundle') { + res.writeHead(200, { 'Content-Type': 'text/javascript' }); + res.end(`Core bundle 👨🏻‍💻`); + return; + } else if (pathData.type === 'action-destination') { + res.writeHead(200, { 'Content-Type': 'text/javascript' }); + res.end(`Action destination 💥`); + return; + } else if (pathData.type === 'legacy-destination') { + res.writeHead(200, { 'Content-Type': 'text/javascript' }); + res.end(`Legacy destination 👴`); + return; + } + + res.writeHead(404); + res.end(`Unknown CDN resource.`); + }); + } + + get host(): string { + return `localhost:${this.port}`; + } + + start(): Promise { + return new Promise((resolve, reject) => { + this.server.once('error', reject); + this.server.listen({port: this.port, host: 'localhost'}, () => { + resolve(); + }); + }); + } + + stop(): Promise { + return new Promise((resolve, reject) => { + this.server.closeAllConnections(); + this.server.close((err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } +} + +type PathMatcherOutput = + { type: 'ajs', writeKey: string } | + { type: 'settings', writeKey: string } | + { type: 'core-bundle', filename: string } | + { type: 'action-destination', name: string, filename: string } | + { type: 'legacy-destination', name: string, version: string, filename: string } | + {type: 'unknown'}; + +function pathMatcher(path: string): PathMatcherOutput { + const ajsMatch = path.match(/\/analytics.js\/v1\/(.*)\/analytics.min.js/); + if (ajsMatch) { + return { type: 'ajs', writeKey: ajsMatch[1] }; + } + + const settingsMatch = path.match(/\/v1\/projects\/(.*)\/settings/); + if (settingsMatch) { + return { type: 'settings', writeKey: settingsMatch[1] }; + } + + const coreBundleMatch = path.match(/\/analytics-next\/bundles\/(.*)\.js/); + if (coreBundleMatch) { + return { type: 'core-bundle', filename: coreBundleMatch[1] }; + } + + const actionDestinationMatch = path.match(/\/next-integrations\/actions\/(.*)\/(.*\.js)/); + if (actionDestinationMatch) { + return { type: 'action-destination', name: actionDestinationMatch[1], filename: actionDestinationMatch[2] }; + } + + const legacyDestinationMatch = path.match(/\/next-integrations\/integrations\/(.*)\/(.*)\/(.*\.js\.gz)/); + if (legacyDestinationMatch) { + return { type: 'legacy-destination', name: legacyDestinationMatch[1], version: legacyDestinationMatch[2], filename: legacyDestinationMatch[3] }; + } + + return { type: 'unknown' }; +} \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/mocks/mockOrigin.ts b/packages/edge-sdk-integration-tests/src/mocks/mockOrigin.ts new file mode 100644 index 0000000..374189e --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/mocks/mockOrigin.ts @@ -0,0 +1,82 @@ +import { createServer } from 'http'; + +export interface MockOriginServerProps { + port: number; +} + +export class MockOriginServer { + private port: number; + private server: ReturnType; + + constructor({port}: MockOriginServerProps) { + this.port = port; + this.server = createServer((req, res) => { + const url = new URL(req.url!, `http://localhost:${this.port}`); + const path = url.pathname; + + if (!['get', 'options'].includes(req.method!.toLowerCase())) { + res.writeHead(404, { 'Content-Type': 'text/plain' }); + res.end(`Cannot ${req.method!} - Invalid method.`); + return; + } + + const pathData = pathMatcher(path); + if (pathData.type === 'index') { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(wrapInHTML("Hello from the customer origin!")); + return; + } + + res.writeHead(404, { 'Content-Type': 'text/html' }); + res.end(wrapInHTML(`Unknown origin resource.`)); + }); + } + + get host(): string { + return `localhost:${this.port}`; + } + + start(): Promise { + return new Promise((resolve, reject) => { + this.server.once('error', reject); + this.server.listen({port: this.port, host: 'localhost'}, () => { + resolve(); + }); + }); + } + + stop(): Promise { + return new Promise((resolve, reject) => { + this.server.closeAllConnections(); + this.server.close((err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } +} + +type PathMatcherOutput = + { type: 'index' } | + {type: 'unknown'}; + +function pathMatcher(path: string): PathMatcherOutput { + if (path === '/') { + return { type: 'index' }; + } + + return { type: 'unknown' }; +} + +const wrapInHTML = (content: string) => ` + + + + + + ${content} + +`; \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/mocks/mockTrackingApi.ts b/packages/edge-sdk-integration-tests/src/mocks/mockTrackingApi.ts new file mode 100644 index 0000000..ee7ce29 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/mocks/mockTrackingApi.ts @@ -0,0 +1,95 @@ +import { createServer, IncomingMessage } from 'http'; + +export interface EventPayload { + method: string; + path: string; + headers: Record; + event: Record; +} + +export type EventHandler = (event: EventPayload) => void; + +export interface MockTrackingApiProps { + port: number; +} + +export class MockTrackingApi { + private port: number; + private server: ReturnType; + private onEventHandlers: Set = new Set(); + private validPaths: Set = new Set(['/v1/a', '/v1/b', '/v1/g', '/v1/i', '/v1/m', '/v1/p', '/v1/s', '/v1/t']) + + constructor({ port }: MockTrackingApiProps) { + this.port = port; + this.server = createServer(async (req, res) => { + const host = req.headers.host; + const url = new URL(req.url!, `http://${host}`); + const path = url.pathname; + + if (!this.validPaths.has(path)) { + res.writeHead(404, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ success: false })); + return; + } + + try { + const payload = await getRequestText(req); + const event = JSON.parse(payload); + res.writeHead(200, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ success: true })) + this.onEventHandlers.forEach((handler) => handler({ path, headers: req.headers, method: req.method!, event })); + } catch (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: err })); + } + }) + } + + get host(): string { + return `localhost:${this.port}`; + } + + start(): Promise { + return new Promise((resolve, reject) => { + this.server.once('error', reject); + this.server.listen({ port: this.port, host: 'localhost' }, () => { + resolve(); + }); + }); + } + + stop(): Promise { + return new Promise((resolve, reject) => { + this.onEventHandlers.clear(); + this.server.closeAllConnections(); + this.server.close((err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } + + on(_event: 'event', handler: EventHandler) { + this.onEventHandlers.add(handler); + } + + off(_event: 'event', handler: EventHandler) { + this.onEventHandlers.delete(handler); + } +} + +async function getRequestText(req: IncomingMessage) { + return new Promise((resolve, reject) => { + const data: Buffer[] = [] + req.on('error', reject) + req.on('data', (chunk) => { + data.push(chunk) + }) + req.on('end', () => { + resolve(Buffer.concat(data).toString()) + }) + }) +} \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/src/service.ts b/packages/edge-sdk-integration-tests/src/service.ts new file mode 100644 index 0000000..7648750 --- /dev/null +++ b/packages/edge-sdk-integration-tests/src/service.ts @@ -0,0 +1,8 @@ +import { EdgeSDKSettings } from "@segment/edge-sdk"; + +export interface Service { + fetch(request: RequestInfo, init?: RequestInit): Promise; + stop(): Promise; + + edgeSdkSettings: Partial; +} \ No newline at end of file diff --git a/packages/edge-sdk-integration-tests/tsconfig.json b/packages/edge-sdk-integration-tests/tsconfig.json new file mode 100644 index 0000000..bee570c --- /dev/null +++ b/packages/edge-sdk-integration-tests/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@segment/tsconfig-edge-sdk/base.json", + "include": [ + "." + ], + "exclude": [ + "dist", + "build", + "node_modules" + ], +} \ No newline at end of file diff --git a/packages/edge-sdk/src/__tests__/assetsProxy.test.ts b/packages/edge-sdk/src/__tests__/assetsProxy.test.ts index 381df35..25d7e6b 100644 --- a/packages/edge-sdk/src/__tests__/assetsProxy.test.ts +++ b/packages/edge-sdk/src/__tests__/assetsProxy.test.ts @@ -87,9 +87,7 @@ describe("asset proxy", () => { { ...mockContext, host: "sushi-shop.com" } ); expect(resp?.status).toBe(200); - expect(await resp?.text()).toBe(` - analytics._cdn = "https://sushi-shop.com/seg"; - The amazing AJS minified code!`); + expect(await resp?.text()).toBe(`analytics._cdn = "https://sushi-shop.com/seg";The amazing AJS minified code!`); }); it("Enrich with anonymousId call", async () => { @@ -106,8 +104,7 @@ describe("asset proxy", () => { expect(resp?.status).toBe(200); expect(await resp?.text()).toBe(` analytics.setAnonymousId("👻"); - analytics.on('reset', function() { fetch('https://sushi-shop.com/seg/reset', {credentials:"include"}) }); - The amazing AJS minified code!`); + analytics.on('reset', function() { fetch('https://sushi-shop.com/seg/reset', {credentials:"include"}) });The amazing AJS minified code!`); }); it("Enrich with user id", async () => { @@ -124,8 +121,7 @@ describe("asset proxy", () => { expect(resp?.status).toBe(200); expect(await resp?.text()).toBe(` analytics.identify("👋"); - analytics.on('reset', function() { fetch('https://sushi-shop.com/seg/reset', {credentials:"include"}) }); - The amazing AJS minified code!`); + analytics.on('reset', function() { fetch('https://sushi-shop.com/seg/reset', {credentials:"include"}) });The amazing AJS minified code!`); }); it("Enrich with client-side traits", async () => { @@ -145,8 +141,7 @@ describe("asset proxy", () => { expect(resp?.status).toBe(200); expect(await resp?.text()).toBe(` analytics.identify("👋", {"locale":"en"}); - analytics.on('reset', function() { fetch('https://sushi-shop.com/seg/reset', {credentials:"include"}) }); - The amazing AJS minified code!`); + analytics.on('reset', function() { fetch('https://sushi-shop.com/seg/reset', {credentials:"include"}) });The amazing AJS minified code!`); }); it("Redacts the writekey in response", async () => { diff --git a/packages/edge-sdk/src/assetsProxy.ts b/packages/edge-sdk/src/assetsProxy.ts index eeb97f6..ede1992 100644 --- a/packages/edge-sdk/src/assetsProxy.ts +++ b/packages/edge-sdk/src/assetsProxy.ts @@ -1,10 +1,14 @@ +import { fetchWithSettings } from "./fetchWithSettings"; +import { getReplacementStream } from "./getReplacementStream"; import { HandlerFunction } from "./types"; +const DEFAULT_PROTOCOL = 'https'; + // Proxy AJS export const handleAJS: HandlerFunction = async (request, response, ctx) => { const url = `${ctx.settings.baseSegmentCDN}/analytics.js/v1/${ctx.settings.writeKey}/analytics.min.js`; - const resp = await fetch(url); + const resp = await fetchWithSettings(url, undefined, { fastly: { backend: ctx.settings.fastly?.segmentCdnBackend } }) return [request, resp, ctx]; }; @@ -15,7 +19,7 @@ export const handleSettings: HandlerFunction = async ( ctx ) => { const url = `${ctx.settings.baseSegmentCDN}/v1/projects/${ctx.settings.writeKey}/settings`; - const resp = await fetch(url); + const resp = await fetchWithSettings(url, undefined, { fastly: { backend: ctx.settings.fastly?.segmentCdnBackend } }) return [request, resp, ctx]; }; @@ -28,7 +32,7 @@ export const handleBundles: HandlerFunction = async ( const url = new URL(request.url); const path = url.pathname.replace(`/${ctx.settings.routePrefix}/`, "/"); const target = `${ctx.settings.baseSegmentCDN}${path}`; - const resp = await fetch(target); + const resp = await fetchWithSettings(target, undefined, { fastly: { backend: ctx.settings.fastly?.segmentCdnBackend } }) return [request, resp, ctx]; }; @@ -45,29 +49,33 @@ export const appendIdCallsToAJS: HandlerFunction = async ( const { userId, anonymousId, clientSideTraits } = ctx; - const anonymousCall = `${ - anonymousId ? `analytics.setAnonymousId("${anonymousId}");` : "" - }`; + const anonymousCall = `${anonymousId ? `analytics.setAnonymousId("${anonymousId}");` : "" + }`; // TODO: Switch to non-tracking call - const idCall = `${ - userId && clientSideTraits + const idCall = `${userId && clientSideTraits ? `analytics.identify("${userId}", ${JSON.stringify(clientSideTraits)});` : userId - ? `analytics.identify("${userId}");` - : "" - }`; - - const resetHandler = `analytics.on('reset', function() { fetch('https://${ctx.host}/${ctx.settings.routePrefix}/reset', {credentials:"include"}) });`; + ? `analytics.identify("${userId}");` + : "" + }`; - const content = await response.text(); + const resetHandler = `analytics.on('reset', function() { fetch('${ctx.settings.experimental?.protocol ?? DEFAULT_PROTOCOL}://${ctx.host}/${ctx.settings.routePrefix}/reset', {credentials:"include"}) });`; - const body = ` + const content = ` ${anonymousCall}${idCall} - ${resetHandler} - ${content}`; + ${resetHandler}`; + + let modifiedResponse = response; + if (response.body && !response.bodyUsed) { + const modifiedContent = response.body.pipeThrough(getReplacementStream({ + prependContent: content + })); + + modifiedResponse = new Response(modifiedContent, response); + } - return [request, new Response(body, response), ctx]; + return [request, modifiedResponse, ctx]; }; // Configures AJS with the custom domain and other monkey patches @@ -82,23 +90,26 @@ export const appendAJSCustomConfiguration: HandlerFunction = async ( const host = ctx.host; - const cdnConfiguration = `analytics._cdn = "https://${host}/${ctx.settings.routePrefix}";`; - - let content = await response.text(); - - // TODO: this monkey-patch is hacky. We should probably address this directly in AJS codebase instead. - // Sending (credentials:"include") is required to allow TAPI calls set cookies when TAPI and customer website are not on the same domain. - // For example, TAPI on segment.example.com and customer website on example.com - content = content.replace( - /method:"post"/g, - 'method:"post",credentials:"include"' - ); - - const body = ` - ${cdnConfiguration} - ${content}`; + const cdnConfiguration = `analytics._cdn = "${ctx.settings.experimental?.protocol ?? DEFAULT_PROTOCOL}://${host}/${ctx.settings.routePrefix}";`; + + let modifiedResponse = response; + if (response.body && !response.bodyUsed) { + const modifiedContent = response.body.pipeThrough(getReplacementStream({ + prependContent: cdnConfiguration, + replacer(content) { + // TODO: this monkey-patch is hacky. We should probably address this directly in AJS codebase instead. + // Sending (credentials:"include") is required to allow TAPI calls set cookies when TAPI and customer website are not on the same domain. + // For example, TAPI on segment.example.com and customer website on example.com + return content.replace( + /method:"post"/g, + 'method:"post",credentials:"include"' + ); + } + })); + modifiedResponse = new Response(modifiedContent, response); + } - return [request, new Response(body, response), ctx]; + return [request, modifiedResponse, ctx]; }; export const redactWritekey: HandlerFunction = async ( @@ -110,10 +121,17 @@ export const redactWritekey: HandlerFunction = async ( return [request, response, ctx]; } - const content = await response.text(); - const body = content.replace(ctx.settings.writeKey, "REDACTED"); + let modifiedResponse = response; + if (response.body && !response.bodyUsed) { + const modifiedContent = response.body.pipeThrough(getReplacementStream({ + replacer(content) { + return content.replace(ctx.settings.writeKey, "REDACTED") + } + })); + modifiedResponse = new Response(modifiedContent, response); + } - return [request, new Response(body, response), ctx]; + return [request, modifiedResponse, ctx]; }; // get rid of 'missing sourcemaps' error @@ -125,10 +143,18 @@ export const removeSourcemapReference: HandlerFunction = async ( if (response.status !== 200) { return [request, response, ctx]; } - const content = await response.text(); - const body = content.replace(new RegExp("//#.*"), ""); - return [request, new Response(body, response), ctx]; + let modifiedResponse = response; + if (response.body && !response.bodyUsed) { + const modifiedContent = response.body.pipeThrough(getReplacementStream({ + replacer(content) { + return content.replace(new RegExp("//#.*"), ""); + } + })); + modifiedResponse = new Response(modifiedContent, response); + } + + return [request, modifiedResponse, ctx]; }; export const configureApiHost: HandlerFunction = async ( @@ -142,12 +168,26 @@ export const configureApiHost: HandlerFunction = async ( const host = ctx.host; // rather than send to api.segment.io, configure analytics to proxy event calls through worker. - const settings = (await response.json()) as any; const apiHost = `${host}/${ctx.settings.routePrefix}/evs`; - // we parse settings because of bug where apiHost is missing. - settings.integrations["Segment.io"].apiHost = apiHost; - settings.metrics.host = apiHost; - return [request, new Response(JSON.stringify(settings), response), ctx]; + + let modifiedResponse = response; + if (response.body && !response.bodyUsed) { + const modifiedContent = response.body.pipeThrough(getReplacementStream({ + replacer(content) { + const settings = JSON.parse(content); + // we parse settings because of bug where apiHost is missing. + settings.integrations["Segment.io"].apiHost = apiHost; + settings.metrics.host = apiHost; + if (ctx.settings.experimental?.protocol) { + settings.integrations['Segment.io'].protocol = ctx.settings.experimental.protocol; + } + return JSON.stringify(settings); + } + })); + modifiedResponse = new Response(modifiedContent, response); + } + + return [request, modifiedResponse, ctx]; }; export const handleCORS: HandlerFunction = async (request, response, ctx) => { diff --git a/packages/edge-sdk/src/fetchWithSettings.ts b/packages/edge-sdk/src/fetchWithSettings.ts new file mode 100644 index 0000000..e486c16 --- /dev/null +++ b/packages/edge-sdk/src/fetchWithSettings.ts @@ -0,0 +1,87 @@ +export type FetchParameters = Parameters + +export type FetchInit = FetchParameters[1] & FastlyRequestOptions | undefined + +export type FastlyRequestOptions = { + backend?: string; + fastly?: { + decompressGzip?: boolean; + } +} + +export interface FetchSettings { + fastly?: { + backend?: string + } +} + +export async function fetchWithSettings(request: FetchParameters[0], init: FetchInit = {}, settings: FetchSettings) { + const modifiedInit = modifyAccepts(request, init); + modifiedInit.fastly = { decompressGzip: true }; + + if (settings.fastly?.backend) { + modifiedInit.backend = settings.fastly.backend + } + //TODO: change content encoding? + return fetch(request, modifiedInit) +} + +function modifyAccepts(request: FetchParameters[0], init: FetchInit): FetchInit & {} { + let modifiedInit = init || {}; + let existingHeaders = init?.headers; + if (!existingHeaders && typeof request !== 'string') { + existingHeaders = request.headers; + } + + if (!existingHeaders) { + return modifiedInit; + } + + const modifiedHeaders = cloneHeaders(existingHeaders); + const acceptEncoding = modifiedHeaders.get('Accept-Encoding'); + if (acceptEncoding) { + modifiedHeaders.set('Accept-Encoding', 'gzip'); + } + + // Cloudflare allows passing Request objects in initOptions + // which has readonly headers, so we need to modify those headers + // rather than replace. + if (isRequest(modifiedInit)) { + modifiedInit = new Request(modifiedInit, { headers: modifiedHeaders }) + } else { + modifiedInit.headers = modifiedHeaders; + } + + return modifiedInit; +} + +function cloneHeaders(headers: HeadersInit): Headers { + const newHeaders = new Headers(); + if (!headers) { + return newHeaders; + } + + if (Array.isArray(headers)) { + headers.forEach(([key, value]) => { + newHeaders.append(key, value); + }); + } else if (isHeaders(headers)) { + for (const [key, value] of headers.entries()) { + newHeaders.append(key, value); + } + } else if (typeof headers === 'object') { + for (const key of Object.keys(headers)) { + newHeaders.append(key, headers[key]); + } + } + + return newHeaders; +} + +function isHeaders(headers: HeadersInit): headers is Headers { + return 'entries' in headers; +} + +function isRequest(request: FetchInit): request is Request { + return Boolean(request && 'clone' in request); +} \ No newline at end of file diff --git a/packages/edge-sdk/src/getReplacementStream.ts b/packages/edge-sdk/src/getReplacementStream.ts new file mode 100644 index 0000000..0a51c0d --- /dev/null +++ b/packages/edge-sdk/src/getReplacementStream.ts @@ -0,0 +1,44 @@ +export interface ReplacementStreamProps { + prependContent?: string; + appendContent?: string; + replacer?: (content: string) => string; +} + +export function getReplacementStream({ + prependContent, + appendContent, + replacer +}: ReplacementStreamProps) { + const textEncoder = new TextEncoder(); + const textDecoder = new TextDecoder(); + + let originalLengthInBytes = 0; + let originalBytes: Uint8Array[] | null = []; + + return new TransformStream({ + start(controller) { + if (prependContent) { + controller.enqueue(textEncoder.encode(prependContent)); + } + }, + transform(chunk: Uint8Array, controller) { + if (!replacer) { + return controller.enqueue(chunk); + } + originalBytes!.push(chunk); + originalLengthInBytes += chunk.byteLength; + }, + flush(controller) { + if (replacer) { + const body = originalBytes!.map((chunk, idx) => textDecoder.decode(chunk, { stream: idx < originalBytes!.length - 2 })).join(''); + const modifiedBody = replacer(body); + controller.enqueue(textEncoder.encode(modifiedBody)); + } + + if (appendContent) { + controller.enqueue(textEncoder.encode(appendContent)); + } + originalBytes = null + } + }) +} \ No newline at end of file diff --git a/packages/edge-sdk/src/index.ts b/packages/edge-sdk/src/index.ts index e7d187d..fd670ee 100644 --- a/packages/edge-sdk/src/index.ts +++ b/packages/edge-sdk/src/index.ts @@ -1 +1,2 @@ export { Segment } from "./segment"; +export type { EdgeSDKFeatures, EdgeSDKSettings } from "./types"; \ No newline at end of file diff --git a/packages/edge-sdk/src/origin.ts b/packages/edge-sdk/src/origin.ts index a55984e..51c1c18 100644 --- a/packages/edge-sdk/src/origin.ts +++ b/packages/edge-sdk/src/origin.ts @@ -1,10 +1,11 @@ +import { fetchWithSettings } from "./fetchWithSettings"; import { HandlerFunction } from "./types"; export const handleOrigin: HandlerFunction = async (request, response, ctx) => { if (response && response.status === 200) { return [request, response, ctx]; } else { - const resp = await fetch(request); + const resp = await fetchWithSettings(request, undefined, { fastly: { backend: ctx.settings.fastly?.websiteOriginBackend } }); return [request, resp, ctx]; } }; @@ -23,7 +24,7 @@ export const handleOriginWithEarlyExit: HandlerFunction = async ( // experiment setup on the route, keep going through the pipeline return [request, response, ctx]; } else { - const resp = await fetch(request); + const resp = await fetchWithSettings(request, undefined, { fastly: { backend: ctx.settings.fastly?.websiteOriginBackend } }); if ( resp.status === 200 && resp.headers.get("content-type")?.startsWith("text/html") diff --git a/packages/edge-sdk/src/parser.ts b/packages/edge-sdk/src/parser.ts index 734077e..398703d 100644 --- a/packages/edge-sdk/src/parser.ts +++ b/packages/edge-sdk/src/parser.ts @@ -1,38 +1,7 @@ import snippet from "@segment/snippet"; import { v4 as uuidv4 } from "uuid"; -import { Handler } from "worktop"; import { EdgeSDKSettings, HandlerFunction } from "./types"; -class ElementHandler { - host: string; - writeKey: string; - routePrefix: string; - snippetInitialPageView: boolean; - constructor( - host: string, - writeKey: string, - routePrefix: string, - snippetInitialPageView: boolean = true - ) { - this.host = host; - this.writeKey = writeKey; - this.routePrefix = routePrefix; - this.snippetInitialPageView = snippetInitialPageView; - } - - element(element: Element) { - const snip = snippet.min({ - host: `${this.host}/${this.routePrefix}`, - apiKey: this.writeKey, - ajsPath: `/ajs/${uuidv4()}`, - useHostForBundles: true, - page: this.snippetInitialPageView ? {} : false, - }); - - element.append(``, { html: true }); - } -} - export const enrichWithAJS: HandlerFunction = async ( request, response, @@ -43,14 +12,21 @@ export const enrichWithAJS: HandlerFunction = async ( } = context; const host = context.host; + let ajsSnippet = snippet.min({ + host: `${host}/${routePrefix}`, + apiKey: writeKey, + ajsPath: `/ajs/${uuidv4()}`, + useHostForBundles: true, + page: snippetInitialPageView ? {} : false, + }); + + if (context.settings.experimental?.protocol) { + ajsSnippet = ajsSnippet.replace('https://', `${context.settings.experimental.protocol}://`) + } + return [ request, - new HTMLRewriter() - .on( - "head", - new ElementHandler(host, writeKey, routePrefix, snippetInitialPageView) - ) - .transform(response), + transformHTML(response, ``), context, ]; }; @@ -64,20 +40,70 @@ export const enrichWithAJSNoWriteKey: HandlerFunction = async ( settings: { routePrefix, snippetInitialPageView }, } = context; const host = context.host; + + let ajsSnippet = snippet.min({ + host: `${host}/${routePrefix}`, + apiKey: 'REDACTED', + ajsPath: `/ajs/${uuidv4()}`, + useHostForBundles: true, + page: snippetInitialPageView ? {} : false, + }); + + if (context.settings.experimental?.protocol) { + ajsSnippet = ajsSnippet.replace('https://', `${context.settings.experimental.protocol}://`) + } return [ request, - new HTMLRewriter() - .on( - "head", - new ElementHandler( - host, - "REDACTED", - routePrefix, - snippetInitialPageView - ) - ) - .transform(response), + transformHTML(response, ``), context, ]; }; + +function transformHTML( + response: Response, + content: string +): Response { + if (typeof HTMLRewriter !== 'undefined') { + // use HTMLRewriter + return new HTMLRewriter() + .on('head', { + element(element) { + element.append(content, { html: true }) + } + }).transform(response) + } else { + return getStreamingHTMLTransform(response, content) + } +} + +function getStreamingHTMLTransform(response: Response, content: string) { + const textEncoder = new TextEncoder(); + const textDecoder = new TextDecoder(); + + // TODO: Check if response body exists + if (response.body && !response.bodyUsed) { + let responseContents = ''; + const stream = new TransformStream({ + transform(chunk: Uint8Array, controller) { + responseContents += textDecoder.decode(chunk, {stream: true }); + }, + flush(controller) { + responseContents += textDecoder.decode(undefined, {stream: false }); + + const newContents = responseContents.replace('', `${content}`); + controller.enqueue(textEncoder.encode(newContents)); + } + }); + + return new Response(response.body.pipeThrough(stream), { + headers: response.headers, + status: response.status, + statusText: response.statusText + }); + } else { + console.log(`Something weird happened here for ${response.url}`) + } + + return response; +} \ No newline at end of file diff --git a/packages/edge-sdk/src/personas.ts b/packages/edge-sdk/src/personas.ts index 454b822..ec1d1b8 100644 --- a/packages/edge-sdk/src/personas.ts +++ b/packages/edge-sdk/src/personas.ts @@ -128,7 +128,7 @@ export const handleVariations: HandlerFunction = async function ( if (originPath) { url.pathname = originPath; return [ - new Request(url, { + new Request(url.toString(), { method: request.method, headers: request.headers, body: request.body, diff --git a/packages/edge-sdk/src/segment.ts b/packages/edge-sdk/src/segment.ts index 4ebd752..f96ee57 100644 --- a/packages/edge-sdk/src/segment.ts +++ b/packages/edge-sdk/src/segment.ts @@ -88,6 +88,8 @@ export class Segment { private profilesStorage?: Storage; private trackingApiEndpoint: SegmentTrackingAPIEndpoint; private snippetInitialPageView: boolean; + private fastlySettings: EdgeSDKSettings['fastly']; + private experimental: EdgeSDKSettings['experimental']; get settings(): EdgeSDKSettings { return { @@ -101,6 +103,8 @@ export class Segment { profilesStorage: this.profilesStorage, trackingApiEndpoint: this.trackingApiEndpoint, snippetInitialPageView: this.snippetInitialPageView, + fastly: this.fastlySettings, + experimental: this.experimental }; } @@ -133,6 +137,8 @@ export class Segment { profilesStorage, trackingApiEndpoint, snippetInitialPageView, + fastly, + experimental } = { ...sdkDefaultSettings, ...settings, @@ -147,6 +153,8 @@ export class Segment { this.profilesStorage = profilesStorage; this.trackingApiEndpoint = trackingApiEndpoint; this.snippetInitialPageView = snippetInitialPageView; + this.fastlySettings = fastly; + this.experimental = experimental; this._variations = []; this._traitsFunc = (traits) => undefined; this.logger = new Logger(logLevels); diff --git a/packages/edge-sdk/src/tapi.ts b/packages/edge-sdk/src/tapi.ts index 85cbef3..fb1f8cd 100644 --- a/packages/edge-sdk/src/tapi.ts +++ b/packages/edge-sdk/src/tapi.ts @@ -1,5 +1,6 @@ import { HandlerFunction } from "./types"; import { version } from "./generated/version"; +import { fetchWithSettings } from "./fetchWithSettings"; export const includeEdgeTraitsInContext: HandlerFunction = async ( request, @@ -10,22 +11,21 @@ export const includeEdgeTraitsInContext: HandlerFunction = async ( const parts = url.pathname.split("/"); const method = parts.pop(); const body: { [key: string]: any } = await request.json(); - if (method && ["i", "t", "p"].includes(method) && request.cf) { - body.context = { - ...body.context, - edge: { - region: request.cf.region, - regionCode: request.cf.regionCode, - city: request.cf.city, - country: request.cf.country, - continent: request.cf.continent, - postalCode: request.cf.postalCode, - latitude: request.cf.latitude, - longitude: request.cf.longitude, - timezone: request.cf.timezone, - }, - }; + if (method && ["i", "t", "p"].includes(method)) { + const edge = request.cf ? { + region: request.cf.region, + regionCode: request.cf.regionCode, + city: request.cf.city, + country: request.cf.country, + continent: request.cf.continent, + postalCode: request.cf.postalCode, + latitude: request.cf.latitude, + longitude: request.cf.longitude, + timezone: request.cf.timezone + } : {}; + body.context = {...body.context, edge: {...edge}} } + return [ new Request(request.url, { body: JSON.stringify(body), @@ -46,10 +46,17 @@ export const handleTAPI: HandlerFunction = async ( const url = new URL(request.url); const parts = url.pathname.split("/"); const method = parts.pop(); + const body = await request.text(); - const resp = await fetch( + const resp = await fetchWithSettings( `${context.settings.trackingApiEndpoint}/${method}`, - request + { + headers: request.headers, + cf: request.cf as RequestInitCfProperties, + method: request.method, + body: body + }, + {fastly: { backend: context.settings.fastly?.segmentTrackingAPIBackend }} ); return [request, resp, context]; @@ -86,7 +93,6 @@ export const injectWritekey: HandlerFunction = async ( ) => { // grab body and method from request const body: { [key: string]: any } = await request.json(); - // discard the redacted writekey and include the real one in the headers const headers = new Headers(request.headers); headers.append( diff --git a/packages/edge-sdk/src/types.ts b/packages/edge-sdk/src/types.ts index 2230ecc..c933cdd 100644 --- a/packages/edge-sdk/src/types.ts +++ b/packages/edge-sdk/src/types.ts @@ -107,6 +107,26 @@ export interface EdgeSDKSettings { * * */ snippetInitialPageView?: boolean; + /** + * Configuration specific to the edge sdk when ran on fastly compute. + */ + fastly?: { + /** + * The name of your Segment CDN origin as configured in origin hosts. + */ + segmentCdnBackend?: string; + /** + * The name of your Segment tracking API origin as configured in origin hosts. + */ + segmentTrackingAPIBackend?: string; + /** + * The name of your website's origin as configured in origin hosts. + */ + websiteOriginBackend?: string; + } + experimental?: { + protocol?: 'http' | 'https' + } } /** diff --git a/packages/jest-presets/jest/cf/jest-preset.js b/packages/jest-presets/jest/cf/jest-preset.js index 920e798..4703b60 100644 --- a/packages/jest-presets/jest/cf/jest-preset.js +++ b/packages/jest-presets/jest/cf/jest-preset.js @@ -2,6 +2,7 @@ module.exports = { testEnvironment: "miniflare", testEnvironmentOptions: { kvNamespaces: ["PROFILES_TEST_NAMESPACE"], + compatibilityDate: "2022-11-30" }, roots: [""], transform: { diff --git a/yarn.lock b/yarn.lock index 0b884a0..8523cb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -372,6 +372,13 @@ __metadata: languageName: node linkType: hard +"@babel/regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "@babel/regjsgen@npm:0.8.0" + checksum: 89c338fee774770e5a487382170711014d49a68eb281e74f2b5eac88f38300a4ad545516a7786a8dd5702e9cf009c94c2f582d200f077ac5decd74c56b973730 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.5.5": version: 7.21.0 resolution: "@babel/runtime@npm:7.21.0" @@ -428,6 +435,113 @@ __metadata: languageName: node linkType: hard +"@bytecodealliance/jco@npm:^0.10.0": + version: 0.10.3 + resolution: "@bytecodealliance/jco@npm:0.10.3" + dependencies: + "@bytecodealliance/preview2-shim": 0.0.12 + binaryen: ^111.0.0 + chalk-template: ^0.4.0 + commander: ^9.4.1 + mkdirp: ^1.0.4 + ora: ^6.1.2 + terser: ^5.16.1 + bin: + jco: src/jco.js + checksum: ff01236d430c42b717ca3c482e8e692b5b883fe96e061ea7d0bdf15bd652406e54464ffe5bd53a58359a3b80d4a660e4ea6eeecbe73d38dec840051cb5dad6d0 + languageName: node + linkType: hard + +"@bytecodealliance/preview2-shim@npm:0.0.12": + version: 0.0.12 + resolution: "@bytecodealliance/preview2-shim@npm:0.0.12" + checksum: fe3f26cbdee24a46e991b388892fd4be326ceae9451476bcc07e1572d0ac9f0ec14d2bfa306ee91e2d4e951e88180e987154d4c503df20eb821e270cd43d1964 + languageName: node + linkType: hard + +"@bytecodealliance/wizer-darwin-arm64@npm:3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer-darwin-arm64@npm:3.0.1" + bin: + wizer-darwin-arm64: wizer + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@bytecodealliance/wizer-darwin-x64@npm:3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer-darwin-x64@npm:3.0.1" + bin: + wizer-darwin-x64: wizer + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@bytecodealliance/wizer-linux-arm64@npm:3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer-linux-arm64@npm:3.0.1" + bin: + wizer-linux-arm64: wizer + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@bytecodealliance/wizer-linux-s390x@npm:3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer-linux-s390x@npm:3.0.1" + bin: + wizer-linux-s390x: wizer + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@bytecodealliance/wizer-linux-x64@npm:3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer-linux-x64@npm:3.0.1" + bin: + wizer-linux-x64: wizer + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@bytecodealliance/wizer-win32-x64@npm:3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer-win32-x64@npm:3.0.1" + bin: + wizer-win32-x64: wizer + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@bytecodealliance/wizer@npm:^3.0.1": + version: 3.0.1 + resolution: "@bytecodealliance/wizer@npm:3.0.1" + dependencies: + "@bytecodealliance/wizer-darwin-arm64": 3.0.1 + "@bytecodealliance/wizer-darwin-x64": 3.0.1 + "@bytecodealliance/wizer-linux-arm64": 3.0.1 + "@bytecodealliance/wizer-linux-s390x": 3.0.1 + "@bytecodealliance/wizer-linux-x64": 3.0.1 + "@bytecodealliance/wizer-win32-x64": 3.0.1 + dependenciesMeta: + "@bytecodealliance/wizer-darwin-arm64": + optional: true + "@bytecodealliance/wizer-darwin-x64": + optional: true + "@bytecodealliance/wizer-linux-arm64": + optional: true + "@bytecodealliance/wizer-linux-s390x": + optional: true + "@bytecodealliance/wizer-linux-x64": + optional: true + "@bytecodealliance/wizer-win32-x64": + optional: true + bin: + wizer: wizer.js + checksum: 097c783b0ae53539723378378285bcaf776528f1da40417406ee46ab7e117dff1dbe67c10c3426a2c1cc45984e578e46ebf256217be72fc8329799e9e653591a + languageName: node + linkType: hard + "@changesets/apply-release-plan@npm:^6.1.3": version: 6.1.3 resolution: "@changesets/apply-release-plan@npm:6.1.3" @@ -673,6 +787,41 @@ __metadata: languageName: node linkType: hard +"@cloudflare/workerd-darwin-64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-darwin-64@npm:1.20231030.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-arm64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20231030.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-linux-64@npm:1.20231030.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-arm64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-linux-arm64@npm:1.20231030.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-windows-64@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "@cloudflare/workerd-windows-64@npm:1.20231030.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@cloudflare/workers-types@npm:^3.16.0, @cloudflare/workers-types@npm:^3.18.0": version: 3.19.0 resolution: "@cloudflare/workers-types@npm:3.19.0" @@ -687,6 +836,13 @@ __metadata: languageName: node linkType: hard +"@cloudflare/workers-types@npm:^4.20231002.0": + version: 4.20231121.0 + resolution: "@cloudflare/workers-types@npm:4.20231121.0" + checksum: 62c5552d70a3bd533502a8ee92c6283456f9370c0b6e791641c2c322ee00cbf13b46a536532b35fe2238b463ac275b19b3a7500f4e96449b79a8e7aa813a9363 + languageName: node + linkType: hard + "@esbuild-plugins/node-globals-polyfill@npm:^0.1.1": version: 0.1.1 resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.1.1" @@ -696,6 +852,15 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-globals-polyfill@npm:^0.2.3": + version: 0.2.3 + resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" + peerDependencies: + esbuild: "*" + checksum: f83eeaa382680b26a3b1cf6c396450332c41d2dc0f9fd935d3f4bacf5412bef7383d2aeb4246a858781435b7c005a570dadc81051f8a038f1ef2111f17d3d8b0 + languageName: node + linkType: hard + "@esbuild-plugins/node-modules-polyfill@npm:^0.1.4": version: 0.1.4 resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.1.4" @@ -708,6 +873,88 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-modules-polyfill@npm:^0.2.2": + version: 0.2.2 + resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.2.2" + dependencies: + escape-string-regexp: ^4.0.0 + rollup-plugin-node-polyfills: ^0.2.1 + peerDependencies: + esbuild: "*" + checksum: 73c247a7559c68b7df080ab08dd3d0b0ab44b934840a4933df9626357b7183a9a5d8cf4ffa9c744f1bad8d7131bce0fde14a23203f7b262f9f14f7b3485bfdb1 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-arm64@npm:0.17.19" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-arm@npm:0.17.19" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-x64@npm:0.17.19" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/darwin-arm64@npm:0.17.19" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/darwin-x64@npm:0.17.19" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/freebsd-arm64@npm:0.17.19" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/freebsd-x64@npm:0.17.19" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-arm64@npm:0.17.19" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-arm@npm:0.17.19" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-ia32@npm:0.17.19" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.14.54": version: 0.14.54 resolution: "@esbuild/linux-loong64@npm:0.14.54" @@ -715,6 +962,90 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-loong64@npm:0.17.19" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-mips64el@npm:0.17.19" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-ppc64@npm:0.17.19" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-riscv64@npm:0.17.19" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-s390x@npm:0.17.19" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-x64@npm:0.17.19" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/netbsd-x64@npm:0.17.19" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/openbsd-x64@npm:0.17.19" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/sunos-x64@npm:0.17.19" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-arm64@npm:0.17.19" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-ia32@npm:0.17.19" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-x64@npm:0.17.19" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^0.4.3": version: 0.4.3 resolution: "@eslint/eslintrc@npm:0.4.3" @@ -756,6 +1087,30 @@ __metadata: languageName: node linkType: hard +"@fastify/busboy@npm:^2.0.0": + version: 2.1.0 + resolution: "@fastify/busboy@npm:2.1.0" + checksum: 3233abd10f73e50668cb4bb278a79b7b3fadd30215ac6458299b0e5a09a29c3586ec07597aae6bd93f5cbedfcef43a8aeea51829cd28fc13850cdbcd324c28d5 + languageName: node + linkType: hard + +"@fastly/js-compute@npm:^3.7.3": + version: 3.7.3 + resolution: "@fastly/js-compute@npm:3.7.3" + dependencies: + "@bytecodealliance/jco": ^0.10.0 + "@bytecodealliance/wizer": ^3.0.1 + acorn: ^8.8.2 + acorn-walk: ^8.2.0 + esbuild: ^0.17.18 + magic-string: ^0.30.0 + regexpu-core: ^5.3.2 + bin: + js-compute-runtime: js-compute-runtime-cli.js + checksum: f9c1e08826c6da5675c78b7f0c7d654d52a8292ad43c96d42226594bdea4ff8f7ce6a7eb0d72021852ed7b631ba488fc70bbfaca88a9a4de6e11f897c3d5ac9c + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -840,6 +1195,20 @@ __metadata: languageName: node linkType: hard +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + "@types/node": "*" + chalk: ^4.0.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + slash: ^3.0.0 + checksum: 0e3624e32c5a8e7361e889db70b170876401b7d70f509a2538c31d5cd50deb0c1ae4b92dc63fe18a0902e0a48c590c21d53787a0df41a52b34fa7cab96c384d6 + languageName: node + linkType: hard + "@jest/core@npm:^29.4.3": version: 29.4.3 resolution: "@jest/core@npm:29.4.3" @@ -881,6 +1250,47 @@ __metadata: languageName: node linkType: hard +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" + dependencies: + "@jest/console": ^29.7.0 + "@jest/reporters": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + ansi-escapes: ^4.2.1 + chalk: ^4.0.0 + ci-info: ^3.2.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-changed-files: ^29.7.0 + jest-config: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-resolve-dependencies: ^29.7.0 + jest-runner: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + jest-watcher: ^29.7.0 + micromatch: ^4.0.4 + pretty-format: ^29.7.0 + slash: ^3.0.0 + strip-ansi: ^6.0.0 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: af759c9781cfc914553320446ce4e47775ae42779e73621c438feb1e4231a5d4862f84b1d8565926f2d1aab29b3ec3dcfdc84db28608bdf5f29867124ebcfc0d + languageName: node + linkType: hard + "@jest/environment@npm:>=27, @jest/environment@npm:^29.4.3": version: 29.4.3 resolution: "@jest/environment@npm:29.4.3" @@ -893,6 +1303,18 @@ __metadata: languageName: node linkType: hard +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" + dependencies: + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + jest-mock: ^29.7.0 + checksum: 6fb398143b2543d4b9b8d1c6dbce83fa5247f84f550330604be744e24c2bd2178bb893657d62d1b97cf2f24baf85c450223f8237cccb71192c36a38ea2272934 + languageName: node + linkType: hard + "@jest/expect-utils@npm:^29.4.3": version: 29.4.3 resolution: "@jest/expect-utils@npm:29.4.3" @@ -902,6 +1324,15 @@ __metadata: languageName: node linkType: hard +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" + dependencies: + jest-get-type: ^29.6.3 + checksum: 75eb177f3d00b6331bcaa057e07c0ccb0733a1d0a1943e1d8db346779039cb7f103789f16e502f888a3096fb58c2300c38d1f3748b36a7fa762eb6f6d1b160ed + languageName: node + linkType: hard + "@jest/expect@npm:^29.4.3": version: 29.4.3 resolution: "@jest/expect@npm:29.4.3" @@ -912,6 +1343,16 @@ __metadata: languageName: node linkType: hard +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" + dependencies: + expect: ^29.7.0 + jest-snapshot: ^29.7.0 + checksum: a01cb85fd9401bab3370618f4b9013b90c93536562222d920e702a0b575d239d74cecfe98010aaec7ad464f67cf534a353d92d181646a4b792acaa7e912ae55e + languageName: node + linkType: hard + "@jest/fake-timers@npm:>=27, @jest/fake-timers@npm:^29.4.3": version: 29.4.3 resolution: "@jest/fake-timers@npm:29.4.3" @@ -926,6 +1367,20 @@ __metadata: languageName: node linkType: hard +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + "@sinonjs/fake-timers": ^10.0.2 + "@types/node": "*" + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: caf2bbd11f71c9241b458d1b5a66cbe95debc5a15d96442444b5d5c7ba774f523c76627c6931cca5e10e76f0d08761f6f1f01a608898f4751a0eee54fc3d8d00 + languageName: node + linkType: hard + "@jest/globals@npm:^29.4.3": version: 29.4.3 resolution: "@jest/globals@npm:29.4.3" @@ -938,6 +1393,18 @@ __metadata: languageName: node linkType: hard +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" + dependencies: + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/types": ^29.6.3 + jest-mock: ^29.7.0 + checksum: 97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 + languageName: node + linkType: hard + "@jest/reporters@npm:^29.4.3": version: 29.4.3 resolution: "@jest/reporters@npm:29.4.3" @@ -975,6 +1442,43 @@ __metadata: languageName: node linkType: hard +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" + dependencies: + "@bcoe/v8-coverage": ^0.2.3 + "@jest/console": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 + "@jridgewell/trace-mapping": ^0.3.18 + "@types/node": "*" + chalk: ^4.0.0 + collect-v8-coverage: ^1.0.0 + exit: ^0.1.2 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + istanbul-lib-coverage: ^3.0.0 + istanbul-lib-instrument: ^6.0.0 + istanbul-lib-report: ^3.0.0 + istanbul-lib-source-maps: ^4.0.0 + istanbul-reports: ^3.1.3 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 + slash: ^3.0.0 + string-length: ^4.0.1 + strip-ansi: ^6.0.0 + v8-to-istanbul: ^9.0.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: 7eadabd62cc344f629024b8a268ecc8367dba756152b761bdcb7b7e570a3864fc51b2a9810cd310d85e0a0173ef002ba4528d5ea0329fbf66ee2a3ada9c40455 + languageName: node + linkType: hard + "@jest/schemas@npm:^29.4.3": version: 29.4.3 resolution: "@jest/schemas@npm:29.4.3" @@ -984,6 +1488,15 @@ __metadata: languageName: node linkType: hard +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": ^0.27.8 + checksum: 910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 + languageName: node + linkType: hard + "@jest/source-map@npm:^29.4.3": version: 29.4.3 resolution: "@jest/source-map@npm:29.4.3" @@ -995,6 +1508,17 @@ __metadata: languageName: node linkType: hard +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" + dependencies: + "@jridgewell/trace-mapping": ^0.3.18 + callsites: ^3.0.0 + graceful-fs: ^4.2.9 + checksum: bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb + languageName: node + linkType: hard + "@jest/test-result@npm:^29.4.3": version: 29.4.3 resolution: "@jest/test-result@npm:29.4.3" @@ -1007,6 +1531,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" + dependencies: + "@jest/console": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/istanbul-lib-coverage": ^2.0.0 + collect-v8-coverage: ^1.0.0 + checksum: 67b6317d526e335212e5da0e768e3b8ab8a53df110361b80761353ad23b6aea4432b7c5665bdeb87658ea373b90fb1afe02ed3611ef6c858c7fba377505057fa + languageName: node + linkType: hard + "@jest/test-sequencer@npm:^29.4.3": version: 29.4.3 resolution: "@jest/test-sequencer@npm:29.4.3" @@ -1019,6 +1555,18 @@ __metadata: languageName: node linkType: hard +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" + dependencies: + "@jest/test-result": ^29.7.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.7.0 + slash: ^3.0.0 + checksum: 73f43599017946be85c0b6357993b038f875b796e2f0950487a82f4ebcb115fa12131932dd9904026b4ad8be131fe6e28bd8d0aa93b1563705185f9804bff8bd + languageName: node + linkType: hard + "@jest/transform@npm:^29.4.3": version: 29.4.3 resolution: "@jest/transform@npm:29.4.3" @@ -1042,6 +1590,29 @@ __metadata: languageName: node linkType: hard +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" + dependencies: + "@babel/core": ^7.11.6 + "@jest/types": ^29.6.3 + "@jridgewell/trace-mapping": ^0.3.18 + babel-plugin-istanbul: ^6.1.1 + chalk: ^4.0.0 + convert-source-map: ^2.0.0 + fast-json-stable-stringify: ^2.1.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 + micromatch: ^4.0.4 + pirates: ^4.0.4 + slash: ^3.0.0 + write-file-atomic: ^4.0.2 + checksum: 0f8ac9f413903b3cb6d240102db848f2a354f63971ab885833799a9964999dd51c388162106a807f810071f864302cdd8e3f0c241c29ce02d85a36f18f3f40ab + languageName: node + linkType: hard + "@jest/types@npm:>=27, @jest/types@npm:^29.4.3": version: 29.4.3 resolution: "@jest/types@npm:29.4.3" @@ -1056,6 +1627,20 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" + dependencies: + "@jest/schemas": ^29.6.3 + "@types/istanbul-lib-coverage": ^2.0.0 + "@types/istanbul-reports": ^3.0.0 + "@types/node": "*" + "@types/yargs": ^17.0.8 + chalk: ^4.0.0 + checksum: a0bcf15dbb0eca6bdd8ce61a3fb055349d40268622a7670a3b2eb3c3dbafe9eb26af59938366d520b86907b9505b0f9b29b85cec11579a9e580694b87cd90fcc + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.1.0": version: 0.1.1 resolution: "@jridgewell/gen-mapping@npm:0.1.1" @@ -1066,6 +1651,17 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.3.0": + version: 0.3.3 + resolution: "@jridgewell/gen-mapping@npm:0.3.3" + dependencies: + "@jridgewell/set-array": ^1.0.1 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 4a74944bd31f22354fc01c3da32e83c19e519e3bbadafa114f6da4522ea77dd0c2842607e923a591d60a76699d819a2fbb6f3552e277efdb9b58b081390b60ab + languageName: node + linkType: hard + "@jridgewell/gen-mapping@npm:^0.3.2": version: 0.3.2 resolution: "@jridgewell/gen-mapping@npm:0.3.2" @@ -1084,6 +1680,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.1 + resolution: "@jridgewell/resolve-uri@npm:3.1.1" + checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 + languageName: node + linkType: hard + "@jridgewell/set-array@npm:^1.0.0, @jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" @@ -1091,6 +1694,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/source-map@npm:^0.3.3": + version: 0.3.5 + resolution: "@jridgewell/source-map@npm:0.3.5" + dependencies: + "@jridgewell/gen-mapping": ^0.3.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1ad4dec0bdafbade57920a50acec6634f88a0eb735851e0dda906fa9894e7f0549c492678aad1a10f8e144bfe87f238307bf2a914a1bc85b7781d345417e9f6f + languageName: node + linkType: hard + "@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": version: 1.4.14 resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" @@ -1098,6 +1711,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.17 resolution: "@jridgewell/trace-mapping@npm:0.3.17" @@ -1108,6 +1728,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.18": + version: 0.3.20 + resolution: "@jridgewell/trace-mapping@npm:0.3.20" + dependencies: + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: cd1a7353135f385909468ff0cf20bdd37e59f2ee49a13a966dedf921943e222082c583ade2b579ff6cd0d8faafcb5461f253e1bf2a9f48fec439211fdbe788f5 + languageName: node + linkType: hard + "@manypkg/find-root@npm:^1.1.0": version: 1.1.0 resolution: "@manypkg/find-root@npm:1.1.0" @@ -1441,7 +2071,22 @@ __metadata: languageName: unknown linkType: soft -"@segment/edge-sdk@*, @segment/edge-sdk@workspace:packages/edge-sdk": +"@segment/edge-sdk-integration-tests@workspace:packages/edge-sdk-integration-tests": + version: 0.0.0-use.local + resolution: "@segment/edge-sdk-integration-tests@workspace:packages/edge-sdk-integration-tests" + dependencies: + "@cloudflare/workers-types": ^4.20231002.0 + "@fastly/js-compute": ^3.7.3 + "@segment/edge-sdk": "*" + "@segment/eslint-config-edge-sdk": "*" + "@types/jest": ^29.5.9 + jest: ^29.7.0 + ts-jest: ^29.1.1 + wrangler: ^3.17.0 + languageName: unknown + linkType: soft + +"@segment/edge-sdk@workspace:*, @segment/edge-sdk@workspace:packages/edge-sdk": version: 0.0.0-use.local resolution: "@segment/edge-sdk@workspace:packages/edge-sdk" dependencies: @@ -1517,6 +2162,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 00bd7362a3439021aa1ea51b0e0d0a0e8ca1351a3d54c606b115fdcc49b51b16db6e5f43b4fe7a28c38688523e22a94d49dd31168868b655f0d4d50f032d07a1 + languageName: node + linkType: hard + "@sinonjs/commons@npm:^2.0.0": version: 2.0.0 resolution: "@sinonjs/commons@npm:2.0.0" @@ -1645,6 +2297,16 @@ __metadata: languageName: node linkType: hard +"@types/jest@npm:^29.5.9": + version: 29.5.10 + resolution: "@types/jest@npm:29.5.10" + dependencies: + expect: ^29.0.0 + pretty-format: ^29.0.0 + checksum: ef385905787db528de9b6beb2688865c0bb276e64256ed60b9a1a6ffc0b75737456cb5e27e952a3241c5845b6a1da487470010dd30f3ca59c8581624c564a823 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.9": version: 7.0.11 resolution: "@types/json-schema@npm:7.0.11" @@ -1875,6 +2537,13 @@ __metadata: languageName: node linkType: hard +"acorn-walk@npm:^8.2.0": + version: 8.3.0 + resolution: "acorn-walk@npm:8.3.0" + checksum: 15ea56ab6529135be05e7d018f935ca80a572355dd3f6d3cd717e36df3346e0f635a93ae781b1c7942607693e2e5f3ef81af5c6fc697bbadcc377ebda7b7f5f6 + languageName: node + linkType: hard + "acorn@npm:^7.4.0": version: 7.4.1 resolution: "acorn@npm:7.4.1" @@ -1893,6 +2562,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.8.2": + version: 8.11.2 + resolution: "acorn@npm:8.11.2" + bin: + acorn: bin/acorn + checksum: 818450408684da89423e3daae24e4dc9b68692db8ab49ea4569c7c5abb7a3f23669438bf129cc81dfdada95e1c9b944ee1bfca2c57a05a4dc73834a612fbf6a7 + languageName: node + linkType: hard + "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -1970,6 +2648,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -2071,6 +2756,15 @@ __metadata: languageName: node linkType: hard +"as-table@npm:^1.0.36": + version: 1.0.55 + resolution: "as-table@npm:1.0.55" + dependencies: + printable-characters: ^1.0.42 + checksum: 341c99d9e99a702c315b3f0744d49b4764b26ef7ddd32bafb9e1706626560c0e599100521fc1b17f640e804bd0503ce70b2ba519c023da6edf06bdd9086dccd9 + languageName: node + linkType: hard + "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -2102,6 +2796,23 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" + dependencies: + "@jest/transform": ^29.7.0 + "@types/babel__core": ^7.1.14 + babel-plugin-istanbul: ^6.1.1 + babel-preset-jest: ^29.6.3 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + slash: ^3.0.0 + peerDependencies: + "@babel/core": ^7.8.0 + checksum: ee6f8e0495afee07cac5e4ee167be705c711a8cc8a737e05a587a131fdae2b3c8f9aa55dfd4d9c03009ac2d27f2de63d8ba96d3e8460da4d00e8af19ef9a83f7 + languageName: node + linkType: hard + "babel-plugin-istanbul@npm:^6.1.1": version: 6.1.1 resolution: "babel-plugin-istanbul@npm:6.1.1" @@ -2127,6 +2838,18 @@ __metadata: languageName: node linkType: hard +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" + dependencies: + "@babel/template": ^7.3.3 + "@babel/types": ^7.3.3 + "@types/babel__core": ^7.1.14 + "@types/babel__traverse": ^7.0.6 + checksum: 51250f22815a7318f17214a9d44650ba89551e6d4f47a2dc259128428324b52f5a73979d010cefd921fd5a720d8c1d55ad74ff601cd94c7bd44d5f6292fde2d1 + languageName: node + linkType: hard + "babel-preset-current-node-syntax@npm:^1.0.0": version: 1.0.1 resolution: "babel-preset-current-node-syntax@npm:1.0.1" @@ -2161,6 +2884,18 @@ __metadata: languageName: node linkType: hard +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" + dependencies: + babel-plugin-jest-hoist: ^29.6.3 + babel-preset-current-node-syntax: ^1.0.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -2168,6 +2903,13 @@ __metadata: languageName: node linkType: hard +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 + languageName: node + linkType: hard + "better-path-resolve@npm:1.0.0": version: 1.0.0 resolution: "better-path-resolve@npm:1.0.0" @@ -2184,6 +2926,27 @@ __metadata: languageName: node linkType: hard +"binaryen@npm:^111.0.0": + version: 111.0.0 + resolution: "binaryen@npm:111.0.0" + bin: + wasm-opt: bin/wasm-opt + wasm2js: bin/wasm2js + checksum: 0ecaefd77212dbb52677b82522014048b5a99865d9dc2971dfe9b890367d1e4ea58d23aed5974179eea35bb46902811473fc6500490608ce4116733ef82e08b2 + languageName: node + linkType: hard + +"bl@npm:^5.0.0": + version: 5.1.0 + resolution: "bl@npm:5.1.0" + dependencies: + buffer: ^6.0.3 + inherits: ^2.0.4 + readable-stream: ^3.4.0 + checksum: a7a438ee0bc540e80b8eb68cc1ad759a9c87df06874a99411d701d01cc0b36f30cd20050512ac3e77090138890960e07bfee724f3ee6619bb39a569f5cc3b1bc + languageName: node + linkType: hard + "blake3-wasm@npm:^2.1.5": version: 2.1.5 resolution: "blake3-wasm@npm:2.1.5" @@ -2267,6 +3030,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.2.1 + checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9 + languageName: node + linkType: hard + "builtins@npm:^5.0.0": version: 5.0.1 resolution: "builtins@npm:5.0.1" @@ -2378,6 +3151,25 @@ __metadata: languageName: node linkType: hard +"capnp-ts@npm:^0.7.0": + version: 0.7.0 + resolution: "capnp-ts@npm:0.7.0" + dependencies: + debug: ^4.3.1 + tslib: ^2.2.0 + checksum: 9ab495a887c5d5fd56afa3cc930733cd8c6c0743c52c2e79c46675eb5c7753e5578f71348628a4b3d9f03e5269bc71f811e58af18d0b0557607c4ee56189cfbb + languageName: node + linkType: hard + +"chalk-template@npm:^0.4.0": + version: 0.4.0 + resolution: "chalk-template@npm:0.4.0" + dependencies: + chalk: ^4.1.2 + checksum: 6c706802a79a7963cbce18f022b046fe86e438a67843151868852f80ea7346e975a6a9749991601e7e5d3b6a6c4852a04c53dc966a9a3d04031bd0e0ed53c819 + languageName: node + linkType: hard + "chalk@npm:^2.0.0, chalk@npm:^2.1.0": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -2399,6 +3191,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.0.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80 + languageName: node + linkType: hard + "char-regex@npm:^1.0.2": version: 1.0.2 resolution: "char-regex@npm:1.0.2" @@ -2460,6 +3259,22 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-cursor@npm:4.0.0" + dependencies: + restore-cursor: ^4.0.0 + checksum: ab3f3ea2076e2176a1da29f9d64f72ec3efad51c0960898b56c8a17671365c26e67b735920530eaf7328d61f8bd41c27f46b9cf6e4e10fe2fa44b5e8c0e392cc + languageName: node + linkType: hard + +"cli-spinners@npm:^2.6.1": + version: 2.9.1 + resolution: "cli-spinners@npm:2.9.1" + checksum: 1780618be58309c469205bc315db697934bac68bce78cd5dfd46248e507a533172d623c7348ecfd904734f597ce0a4e5538684843d2cfb7af485d4466699940c + languageName: node + linkType: hard + "cliui@npm:^6.0.0": version: 6.0.0 resolution: "cliui@npm:6.0.0" @@ -2544,6 +3359,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^2.20.0": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e + languageName: node + linkType: hard + "commander@npm:^4.0.0": version: 4.1.1 resolution: "commander@npm:4.1.1" @@ -2551,6 +3373,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^9.4.1": + version: 9.5.0 + resolution: "commander@npm:9.5.0" + checksum: c7a3e27aa59e913b54a1bafd366b88650bc41d6651f0cbe258d4ff09d43d6a7394232a4dadd0bf518b3e696fdf595db1028a0d82c785b88bd61f8a440cecfade + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -2586,6 +3415,30 @@ __metadata: languageName: node linkType: hard +"cookie@npm:^0.5.0": + version: 0.5.0 + resolution: "cookie@npm:0.5.0" + checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 + languageName: node + linkType: hard + +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + prompts: ^2.0.1 + bin: + create-jest: bin/create-jest.js + checksum: 1427d49458adcd88547ef6fa39041e1fe9033a661293aa8d2c3aa1b4967cb5bf4f0c00436c7a61816558f28ba2ba81a94d5c962e8022ea9a883978fc8e1f2945 + languageName: node + linkType: hard + "cron-schedule@npm:^3.0.4": version: 3.0.6 resolution: "cron-schedule@npm:3.0.6" @@ -2648,6 +3501,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^2.0.0": + version: 2.0.2 + resolution: "data-uri-to-buffer@npm:2.0.2" + checksum: 152bec5e77513ee253a7c686700a1723246f582dad8b614e8eaaaba7fa45a15c8671ae4b8f4843f4f3a002dae1d3e7a20f852f7d7bdc8b4c15cfe7adfdfb07f8 + languageName: node + linkType: hard + "debug@npm:4, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" @@ -2684,6 +3544,18 @@ __metadata: languageName: node linkType: hard +"dedent@npm:^1.0.0": + version: 1.5.1 + resolution: "dedent@npm:1.5.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: c3c300a14edf1bdf5a873f9e4b22e839d62490bc5c8d6169c1f15858a1a76733d06a9a56930e963d677a2ceeca4b6b0894cc5ea2f501aa382ca5b92af3413c2a + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -2752,6 +3624,13 @@ __metadata: languageName: node linkType: hard +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: f4914158e1f2276343d98ff5b31fc004e7304f5470bf0f1adb2ac6955d85a531a6458d33e87667f98f6ae52ebd3891bb47d420bb48a5bd8b7a27ee25b20e33aa + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -3262,6 +4141,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:0.17.19, esbuild@npm:^0.17.18": + version: 0.17.19 + resolution: "esbuild@npm:0.17.19" + dependencies: + "@esbuild/android-arm": 0.17.19 + "@esbuild/android-arm64": 0.17.19 + "@esbuild/android-x64": 0.17.19 + "@esbuild/darwin-arm64": 0.17.19 + "@esbuild/darwin-x64": 0.17.19 + "@esbuild/freebsd-arm64": 0.17.19 + "@esbuild/freebsd-x64": 0.17.19 + "@esbuild/linux-arm": 0.17.19 + "@esbuild/linux-arm64": 0.17.19 + "@esbuild/linux-ia32": 0.17.19 + "@esbuild/linux-loong64": 0.17.19 + "@esbuild/linux-mips64el": 0.17.19 + "@esbuild/linux-ppc64": 0.17.19 + "@esbuild/linux-riscv64": 0.17.19 + "@esbuild/linux-s390x": 0.17.19 + "@esbuild/linux-x64": 0.17.19 + "@esbuild/netbsd-x64": 0.17.19 + "@esbuild/openbsd-x64": 0.17.19 + "@esbuild/sunos-x64": 0.17.19 + "@esbuild/win32-arm64": 0.17.19 + "@esbuild/win32-ia32": 0.17.19 + "@esbuild/win32-x64": 0.17.19 + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: ac11b1a5a6008e4e37ccffbd6c2c054746fc58d0ed4a2f9ee643bd030cfcea9a33a235087bc777def8420f2eaafb3486e76adb7bdb7241a9143b43a69a10afd8 + languageName: node + linkType: hard + "esbuild@npm:^0.14.25": version: 0.14.54 resolution: "esbuild@npm:0.14.54" @@ -3668,6 +4624,13 @@ __metadata: languageName: node linkType: hard +"exit-hook@npm:^2.2.1": + version: 2.2.1 + resolution: "exit-hook@npm:2.2.1" + checksum: 1aa8359b6c5590a012d6cadf9cd337d227291bfcaa8970dc585d73dffef0582af34ed8ac56f6164f8979979fb417cff1eb49f03cdfd782f9332a30c773f0ada0 + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -3688,6 +4651,19 @@ __metadata: languageName: node linkType: hard +"expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" + dependencies: + "@jest/expect-utils": ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + checksum: 9257f10288e149b81254a0fda8ffe8d54a7061cd61d7515779998b012579d2b8c22354b0eb901daf0145f347403da582f75f359f4810c007182ad3fb318b5c0c + languageName: node + linkType: hard + "extendable-error@npm:^0.1.5": version: 0.1.7 resolution: "extendable-error@npm:0.1.7" @@ -3970,6 +4946,16 @@ __metadata: languageName: node linkType: hard +"get-source@npm:^2.0.12": + version: 2.0.12 + resolution: "get-source@npm:2.0.12" + dependencies: + data-uri-to-buffer: ^2.0.0 + source-map: ^0.6.1 + checksum: c73368fee709594ba38682ec1a96872aac6f7d766396019611d3d2358b68186a7847765a773ea0db088c42781126cc6bc09e4b87f263951c74dae5dcea50ad42 + languageName: node + linkType: hard + "get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -4005,6 +4991,13 @@ __metadata: languageName: node linkType: hard +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: e795f4e8f06d2a15e86f76e4d92751cf8bbfcf0157cea5c2f0f35678a8195a750b34096b1256e436f0cebc1883b5ff0888c47348443e69546a5a87f9e1eb1167 + languageName: node + linkType: hard + "glob@npm:7.1.6": version: 7.1.6 resolution: "glob@npm:7.1.6" @@ -4281,6 +5274,13 @@ __metadata: languageName: node linkType: hard +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e + languageName: node + linkType: hard + "ignore@npm:^4.0.6": version: 4.0.6 resolution: "ignore@npm:4.0.6" @@ -4348,7 +5348,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3": +"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -4485,6 +5485,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: e8d52ad490bed7ae665032c7675ec07732bbfe25808b0efbc4d5a76b1a1f01c165f332775c63e25e9a03d319ebb6b24f571a9e902669fc1e40b0a60b5be6e26c + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -4602,6 +5609,13 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.1.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 20a1fc161afafaf49243551a5ac33b6c4cf0bbcce369fcd8f2951fbdd000c30698ce320de3ee6830497310a8f41880f8066d440aa3eb0a853e2aa4836dd89abc + languageName: node + linkType: hard + "is-weakref@npm:^1.0.2": version: 1.0.2 resolution: "is-weakref@npm:1.0.2" @@ -4645,6 +5659,19 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": ^7.12.3 + "@babel/parser": ^7.14.7 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.2.0 + semver: ^7.5.4 + checksum: fb23472e739cfc9b027cefcd7d551d5e7ca7ff2817ae5150fab99fe42786a7f7b56a29a2aa8309c37092e18297b8003f9c274f50ca4360949094d17fbac81472 + languageName: node + linkType: hard + "istanbul-lib-report@npm:^3.0.0": version: 3.0.0 resolution: "istanbul-lib-report@npm:3.0.0" @@ -4687,6 +5714,17 @@ __metadata: languageName: node linkType: hard +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" + dependencies: + execa: ^5.0.0 + jest-util: ^29.7.0 + p-limit: ^3.1.0 + checksum: 963e203893c396c5dfc75e00a49426688efea7361b0f0e040035809cecd2d46b3c01c02be2d9e8d38b1138357d2de7719ea5b5be21f66c10f2e9685a5a73bb99 + languageName: node + linkType: hard + "jest-circus@npm:^29.4.3": version: 29.4.3 resolution: "jest-circus@npm:29.4.3" @@ -4714,6 +5752,34 @@ __metadata: languageName: node linkType: hard +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" + dependencies: + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + chalk: ^4.0.0 + co: ^4.6.0 + dedent: ^1.0.0 + is-generator-fn: ^2.0.0 + jest-each: ^29.7.0 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 + p-limit: ^3.1.0 + pretty-format: ^29.7.0 + pure-rand: ^6.0.0 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: 349437148924a5a109c9b8aad6d393a9591b4dac1918fc97d81b7fc515bc905af9918495055071404af1fab4e48e4b04ac3593477b1d5dcf48c4e71b527c70a7 + languageName: node + linkType: hard + "jest-cli@npm:^29.4.3": version: 29.4.3 resolution: "jest-cli@npm:29.4.3" @@ -4741,6 +5807,32 @@ __metadata: languageName: node linkType: hard +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" + dependencies: + "@jest/core": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 + chalk: ^4.0.0 + create-jest: ^29.7.0 + exit: ^0.1.2 + import-local: ^3.0.2 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + yargs: ^17.3.1 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 664901277a3f5007ea4870632ed6e7889db9da35b2434e7cb488443e6bf5513889b344b7fddf15112135495b9875892b156faeb2d7391ddb9e2a849dcb7b6c36 + languageName: node + linkType: hard + "jest-config@npm:^29.4.3": version: 29.4.3 resolution: "jest-config@npm:29.4.3" @@ -4779,6 +5871,44 @@ __metadata: languageName: node linkType: hard +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" + dependencies: + "@babel/core": ^7.11.6 + "@jest/test-sequencer": ^29.7.0 + "@jest/types": ^29.6.3 + babel-jest: ^29.7.0 + chalk: ^4.0.0 + ci-info: ^3.2.0 + deepmerge: ^4.2.2 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + jest-circus: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-get-type: ^29.6.3 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-runner: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + micromatch: ^4.0.4 + parse-json: ^5.2.0 + pretty-format: ^29.7.0 + slash: ^3.0.0 + strip-json-comments: ^3.1.1 + peerDependencies: + "@types/node": "*" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + ts-node: + optional: true + checksum: 4cabf8f894c180cac80b7df1038912a3fc88f96f2622de33832f4b3314f83e22b08fb751da570c0ab2b7988f21604bdabade95e3c0c041068ac578c085cf7dff + languageName: node + linkType: hard + "jest-diff@npm:^29.4.3": version: 29.4.3 resolution: "jest-diff@npm:29.4.3" @@ -4791,6 +5921,18 @@ __metadata: languageName: node linkType: hard +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" + dependencies: + chalk: ^4.0.0 + diff-sequences: ^29.6.3 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77 + languageName: node + linkType: hard + "jest-docblock@npm:^29.4.3": version: 29.4.3 resolution: "jest-docblock@npm:29.4.3" @@ -4800,6 +5942,15 @@ __metadata: languageName: node linkType: hard +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" + dependencies: + detect-newline: ^3.0.0 + checksum: 66390c3e9451f8d96c5da62f577a1dad701180cfa9b071c5025acab2f94d7a3efc2515cfa1654ebe707213241541ce9c5530232cdc8017c91ed64eea1bd3b192 + languageName: node + linkType: hard + "jest-each@npm:^29.4.3": version: 29.4.3 resolution: "jest-each@npm:29.4.3" @@ -4813,6 +5964,19 @@ __metadata: languageName: node linkType: hard +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + chalk: ^4.0.0 + jest-get-type: ^29.6.3 + jest-util: ^29.7.0 + pretty-format: ^29.7.0 + checksum: e88f99f0184000fc8813f2a0aa79e29deeb63700a3b9b7928b8a418d7d93cd24933608591dbbdea732b473eb2021c72991b5cc51a17966842841c6e28e6f691c + languageName: node + linkType: hard + "jest-environment-miniflare@npm:^2.11.0": version: 2.12.1 resolution: "jest-environment-miniflare@npm:2.12.1" @@ -4846,6 +6010,20 @@ __metadata: languageName: node linkType: hard +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" + dependencies: + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: 501a9966292cbe0ca3f40057a37587cb6def25e1e0c5e39ac6c650fe78d3c70a2428304341d084ac0cced5041483acef41c477abac47e9a290d5545fd2f15646 + languageName: node + linkType: hard + "jest-get-type@npm:^29.4.3": version: 29.4.3 resolution: "jest-get-type@npm:29.4.3" @@ -4853,6 +6031,13 @@ __metadata: languageName: node linkType: hard +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 + languageName: node + linkType: hard + "jest-haste-map@npm:^29.4.3": version: 29.4.3 resolution: "jest-haste-map@npm:29.4.3" @@ -4876,6 +6061,29 @@ __metadata: languageName: node linkType: hard +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + "@types/graceful-fs": ^4.1.3 + "@types/node": "*" + anymatch: ^3.0.3 + fb-watchman: ^2.0.0 + fsevents: ^2.3.2 + graceful-fs: ^4.2.9 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 + micromatch: ^4.0.4 + walker: ^1.0.8 + dependenciesMeta: + fsevents: + optional: true + checksum: c2c8f2d3e792a963940fbdfa563ce14ef9e14d4d86da645b96d3cd346b8d35c5ce0b992ee08593939b5f718cf0a1f5a90011a056548a1dbf58397d4356786f01 + languageName: node + linkType: hard + "jest-leak-detector@npm:^29.4.3": version: 29.4.3 resolution: "jest-leak-detector@npm:29.4.3" @@ -4886,6 +6094,16 @@ __metadata: languageName: node linkType: hard +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" + dependencies: + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 + languageName: node + linkType: hard + "jest-matcher-utils@npm:^29.4.3": version: 29.4.3 resolution: "jest-matcher-utils@npm:29.4.3" @@ -4898,6 +6116,18 @@ __metadata: languageName: node linkType: hard +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" + dependencies: + chalk: ^4.0.0 + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd + languageName: node + linkType: hard + "jest-message-util@npm:^29.4.3": version: 29.4.3 resolution: "jest-message-util@npm:29.4.3" @@ -4915,6 +6145,23 @@ __metadata: languageName: node linkType: hard +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" + dependencies: + "@babel/code-frame": ^7.12.13 + "@jest/types": ^29.6.3 + "@types/stack-utils": ^2.0.0 + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + micromatch: ^4.0.4 + pretty-format: ^29.7.0 + slash: ^3.0.0 + stack-utils: ^2.0.3 + checksum: a9d025b1c6726a2ff17d54cc694de088b0489456c69106be6b615db7a51b7beb66788bea7a59991a019d924fbf20f67d085a445aedb9a4d6760363f4d7d09930 + languageName: node + linkType: hard + "jest-mock@npm:>=27, jest-mock@npm:^29.4.3": version: 29.4.3 resolution: "jest-mock@npm:29.4.3" @@ -4926,6 +6173,17 @@ __metadata: languageName: node linkType: hard +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + "@types/node": "*" + jest-util: ^29.7.0 + checksum: 81ba9b68689a60be1482212878973700347cb72833c5e5af09895882b9eb5c4e02843a1bbdf23f94c52d42708bab53a30c45a3482952c9eec173d1eaac5b86c5 + languageName: node + linkType: hard + "jest-pnp-resolver@npm:^1.2.2": version: 1.2.3 resolution: "jest-pnp-resolver@npm:1.2.3" @@ -4945,6 +6203,13 @@ __metadata: languageName: node linkType: hard +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a + languageName: node + linkType: hard + "jest-resolve-dependencies@npm:^29.4.3": version: 29.4.3 resolution: "jest-resolve-dependencies@npm:29.4.3" @@ -4955,6 +6220,16 @@ __metadata: languageName: node linkType: hard +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" + dependencies: + jest-regex-util: ^29.6.3 + jest-snapshot: ^29.7.0 + checksum: aeb75d8150aaae60ca2bb345a0d198f23496494677cd6aefa26fc005faf354061f073982175daaf32b4b9d86b26ca928586344516e3e6969aa614cb13b883984 + languageName: node + linkType: hard + "jest-resolve@npm:^29.4.3": version: 29.4.3 resolution: "jest-resolve@npm:29.4.3" @@ -4972,6 +6247,23 @@ __metadata: languageName: node linkType: hard +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" + dependencies: + chalk: ^4.0.0 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.7.0 + jest-pnp-resolver: ^1.2.2 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + resolve: ^1.20.0 + resolve.exports: ^2.0.0 + slash: ^3.0.0 + checksum: 0ca218e10731aa17920526ec39deaec59ab9b966237905ffc4545444481112cd422f01581230eceb7e82d86f44a543d520a71391ec66e1b4ef1a578bd5c73487 + languageName: node + linkType: hard + "jest-runner@npm:^29.4.3": version: 29.4.3 resolution: "jest-runner@npm:29.4.3" @@ -5001,6 +6293,35 @@ __metadata: languageName: node linkType: hard +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" + dependencies: + "@jest/console": ^29.7.0 + "@jest/environment": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + chalk: ^4.0.0 + emittery: ^0.13.1 + graceful-fs: ^4.2.9 + jest-docblock: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-leak-detector: ^29.7.0 + jest-message-util: ^29.7.0 + jest-resolve: ^29.7.0 + jest-runtime: ^29.7.0 + jest-util: ^29.7.0 + jest-watcher: ^29.7.0 + jest-worker: ^29.7.0 + p-limit: ^3.1.0 + source-map-support: 0.5.13 + checksum: f0405778ea64812bf9b5c50b598850d94ccf95d7ba21f090c64827b41decd680ee19fcbb494007cdd7f5d0d8906bfc9eceddd8fa583e753e736ecd462d4682fb + languageName: node + linkType: hard + "jest-runtime@npm:^29.4.3": version: 29.4.3 resolution: "jest-runtime@npm:29.4.3" @@ -5031,6 +6352,36 @@ __metadata: languageName: node linkType: hard +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" + dependencies: + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/globals": ^29.7.0 + "@jest/source-map": ^29.6.3 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + chalk: ^4.0.0 + cjs-module-lexer: ^1.0.0 + collect-v8-coverage: ^1.0.0 + glob: ^7.1.3 + graceful-fs: ^4.2.9 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 + slash: ^3.0.0 + strip-bom: ^4.0.0 + checksum: d19f113d013e80691e07047f68e1e3448ef024ff2c6b586ce4f90cd7d4c62a2cd1d460110491019719f3c59bfebe16f0e201ed005ef9f80e2cf798c374eed54e + languageName: node + linkType: hard + "jest-snapshot@npm:^29.4.3": version: 29.4.3 resolution: "jest-snapshot@npm:29.4.3" @@ -5063,17 +6414,59 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:>=27, jest-util@npm:^29.0.0, jest-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-util@npm:29.4.3" +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" + dependencies: + "@babel/core": ^7.11.6 + "@babel/generator": ^7.7.2 + "@babel/plugin-syntax-jsx": ^7.7.2 + "@babel/plugin-syntax-typescript": ^7.7.2 + "@babel/types": ^7.3.3 + "@jest/expect-utils": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 + babel-preset-current-node-syntax: ^1.0.0 + chalk: ^4.0.0 + expect: ^29.7.0 + graceful-fs: ^4.2.9 + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + natural-compare: ^1.4.0 + pretty-format: ^29.7.0 + semver: ^7.5.3 + checksum: 86821c3ad0b6899521ce75ee1ae7b01b17e6dfeff9166f2cf17f012e0c5d8c798f30f9e4f8f7f5bed01ea7b55a6bc159f5eda778311162cbfa48785447c237ad + languageName: node + linkType: hard + +"jest-util@npm:>=27, jest-util@npm:^29.0.0, jest-util@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-util@npm:29.4.3" + dependencies: + "@jest/types": ^29.4.3 + "@types/node": "*" + chalk: ^4.0.0 + ci-info: ^3.2.0 + graceful-fs: ^4.2.9 + picomatch: ^2.2.3 + checksum: 606b3e6077895baf8fb4ad4d08c134f37a6b81d5ba77ae654c942b1ae4b7294ab3b5a0eb93db34f129407b367970cf3b76bf5c80897b30f215f2bc8bf20a5f3f + languageName: node + linkType: hard + +"jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: 606b3e6077895baf8fb4ad4d08c134f37a6b81d5ba77ae654c942b1ae4b7294ab3b5a0eb93db34f129407b367970cf3b76bf5c80897b30f215f2bc8bf20a5f3f + checksum: 042ab4980f4ccd4d50226e01e5c7376a8556b472442ca6091a8f102488c0f22e6e8b89ea874111d2328a2080083bf3225c86f3788c52af0bd0345a00eb57a3ca languageName: node linkType: hard @@ -5091,6 +6484,20 @@ __metadata: languageName: node linkType: hard +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + camelcase: ^6.2.0 + chalk: ^4.0.0 + jest-get-type: ^29.6.3 + leven: ^3.1.0 + pretty-format: ^29.7.0 + checksum: 191fcdc980f8a0de4dbdd879fa276435d00eb157a48683af7b3b1b98b0f7d9de7ffe12689b617779097ff1ed77601b9f7126b0871bba4f776e222c40f62e9dae + languageName: node + linkType: hard + "jest-watcher@npm:^29.4.3": version: 29.4.3 resolution: "jest-watcher@npm:29.4.3" @@ -5107,6 +6514,22 @@ __metadata: languageName: node linkType: hard +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" + dependencies: + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 + "@types/node": "*" + ansi-escapes: ^4.2.1 + chalk: ^4.0.0 + emittery: ^0.13.1 + jest-util: ^29.7.0 + string-length: ^4.0.1 + checksum: 67e6e7fe695416deff96b93a14a561a6db69389a0667e9489f24485bb85e5b54e12f3b2ba511ec0b777eca1e727235b073e3ebcdd473d68888650489f88df92f + languageName: node + linkType: hard + "jest-worker@npm:^29.4.3": version: 29.4.3 resolution: "jest-worker@npm:29.4.3" @@ -5119,6 +6542,18 @@ __metadata: languageName: node linkType: hard +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" + dependencies: + "@types/node": "*" + jest-util: ^29.7.0 + merge-stream: ^2.0.0 + supports-color: ^8.0.0 + checksum: 30fff60af49675273644d408b650fc2eb4b5dcafc5a0a455f238322a8f9d8a98d847baca9d51ff197b6747f54c7901daa2287799230b856a0f48287d131f8c13 + languageName: node + linkType: hard + "jest@npm:^29.0.3": version: 29.4.3 resolution: "jest@npm:29.4.3" @@ -5138,6 +6573,25 @@ __metadata: languageName: node linkType: hard +"jest@npm:^29.7.0": + version: 29.7.0 + resolution: "jest@npm:29.7.0" + dependencies: + "@jest/core": ^29.7.0 + "@jest/types": ^29.6.3 + import-local: ^3.0.2 + jest-cli: ^29.7.0 + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 17ca8d67504a7dbb1998cf3c3077ec9031ba3eb512da8d71cb91bcabb2b8995c4e4b292b740cb9bf1cbff5ce3e110b3f7c777b0cefb6f41ab05445f248d0ee0b + languageName: node + linkType: hard + "joycon@npm:^3.0.1": version: 3.1.1 resolution: "joycon@npm:3.1.1" @@ -5191,6 +6645,15 @@ __metadata: languageName: node linkType: hard +"jsesc@npm:~0.5.0": + version: 0.5.0 + resolution: "jsesc@npm:0.5.0" + bin: + jsesc: bin/jsesc + checksum: b8b44cbfc92f198ad972fba706ee6a1dfa7485321ee8c0b25f5cedd538dcb20cde3197de16a7265430fce8277a12db066219369e3d51055038946039f6e20e17 + languageName: node + linkType: hard + "json-parse-even-better-errors@npm:^2.3.0": version: 2.3.1 resolution: "json-parse-even-better-errors@npm:2.3.1" @@ -5364,6 +6827,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^5.1.0": + version: 5.1.0 + resolution: "log-symbols@npm:5.1.0" + dependencies: + chalk: ^5.0.0 + is-unicode-supported: ^1.1.0 + checksum: 7291b6e7f1b3df6865bdaeb9b59605c832668ac2fa0965c63b1e7dd3700349aec09c1d7d40c368d5041ff58b7f89461a56e4009471921301af7b3609cbff9a29 + languageName: node + linkType: hard + "lru-cache@npm:^4.0.1": version: 4.1.5 resolution: "lru-cache@npm:4.1.5" @@ -5408,6 +6881,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.0": + version: 0.30.5 + resolution: "magic-string@npm:0.30.5" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.15 + checksum: da10fecff0c0a7d3faf756913ce62bd6d5e7b0402be48c3b27bfd651b90e29677e279069a63b764bcdc1b8ecdcdb898f29a5c5ec510f2323e8d62ee057a6eb18 + languageName: node + linkType: hard + "make-dir@npm:^3.0.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -5544,6 +7026,28 @@ __metadata: languageName: node linkType: hard +"miniflare@npm:3.20231030.1": + version: 3.20231030.1 + resolution: "miniflare@npm:3.20231030.1" + dependencies: + acorn: ^8.8.0 + acorn-walk: ^8.2.0 + capnp-ts: ^0.7.0 + exit-hook: ^2.2.1 + glob-to-regexp: ^0.4.1 + source-map-support: 0.5.21 + stoppable: ^1.1.0 + undici: ^5.22.1 + workerd: 1.20231030.0 + ws: ^8.11.0 + youch: ^3.2.2 + zod: ^3.20.6 + bin: + miniflare: bootstrap.js + checksum: 496cc6e75d80be5d09f706358db9c818ccbdca88e7342e43efa34f0d89ede3655c09c465c4c6515f2b5ce202fe8d16c2e4acb56bcb345af26127d8d84c84b77d + languageName: node + linkType: hard + "miniflare@npm:^2.11.0, miniflare@npm:^2.8.1": version: 2.12.1 resolution: "miniflare@npm:2.12.1" @@ -5934,7 +7438,7 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^5.1.2": +"onetime@npm:^5.1.0, onetime@npm:^5.1.2": version: 5.1.2 resolution: "onetime@npm:5.1.2" dependencies: @@ -5966,6 +7470,23 @@ __metadata: languageName: node linkType: hard +"ora@npm:^6.1.2": + version: 6.3.1 + resolution: "ora@npm:6.3.1" + dependencies: + chalk: ^5.0.0 + cli-cursor: ^4.0.0 + cli-spinners: ^2.6.1 + is-interactive: ^2.0.0 + is-unicode-supported: ^1.1.0 + log-symbols: ^5.1.0 + stdin-discarder: ^0.1.0 + strip-ansi: ^7.0.1 + wcwidth: ^1.0.1 + checksum: 474c0596a35c1be1e836bb836bea8a2d9e37458fc63b020e1435c8fe2030ab224454bfb263618e3ec09fcab2008dd525e9047f4c61548c4ace7b6490a766fc1c + languageName: node + linkType: hard + "os-tmpdir@npm:~1.0.2": version: 1.0.2 resolution: "os-tmpdir@npm:1.0.2" @@ -6219,6 +7740,24 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": ^29.6.3 + ansi-styles: ^5.0.0 + react-is: ^18.0.0 + checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6 + languageName: node + linkType: hard + +"printable-characters@npm:^1.0.42": + version: 1.0.42 + resolution: "printable-characters@npm:1.0.42" + checksum: 2724aa02919d7085933af0f8f904bd0de67a6b53834f2e5b98fc7aa3650e20755c805e8c85bcf96c09f678cb16a58b55640dd3a2da843195fce06b1ccb0c8ce4 + languageName: node + linkType: hard + "progress@npm:^2.0.0": version: 2.0.3 resolution: "progress@npm:2.0.3" @@ -6267,6 +7806,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^6.0.0": + version: 6.0.4 + resolution: "pure-rand@npm:6.0.4" + checksum: e1c4e69f8bf7303e5252756d67c3c7551385cd34d94a1f511fe099727ccbab74c898c03a06d4c4a24a89b51858781057b83ebbfe740d984240cdc04fead36068 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -6323,6 +7869,17 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^3.4.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: ^2.0.3 + string_decoder: ^1.1.1 + util-deprecate: ^1.0.1 + checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d + languageName: node + linkType: hard + "readable-stream@npm:^3.6.0": version: 3.6.1 resolution: "readable-stream@npm:3.6.1" @@ -6353,6 +7910,22 @@ __metadata: languageName: node linkType: hard +"regenerate-unicode-properties@npm:^10.1.0": + version: 10.1.1 + resolution: "regenerate-unicode-properties@npm:10.1.1" + dependencies: + regenerate: ^1.4.2 + checksum: b80958ef40f125275824c2c47d5081dfaefebd80bff26c76761e9236767c748a4a95a69c053fe29d2df881177f2ca85df4a71fe70a82360388b31159ef19adcf + languageName: node + linkType: hard + +"regenerate@npm:^1.4.2": + version: 1.4.2 + resolution: "regenerate@npm:1.4.2" + checksum: 3317a09b2f802da8db09aa276e469b57a6c0dd818347e05b8862959c6193408242f150db5de83c12c3fa99091ad95fb42a6db2c3329bfaa12a0ea4cbbeb30cb0 + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.13.11": version: 0.13.11 resolution: "regenerator-runtime@npm:0.13.11" @@ -6385,6 +7958,31 @@ __metadata: languageName: node linkType: hard +"regexpu-core@npm:^5.3.2": + version: 5.3.2 + resolution: "regexpu-core@npm:5.3.2" + dependencies: + "@babel/regjsgen": ^0.8.0 + regenerate: ^1.4.2 + regenerate-unicode-properties: ^10.1.0 + regjsparser: ^0.9.1 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.1.0 + checksum: 95bb97088419f5396e07769b7de96f995f58137ad75fac5811fb5fe53737766dfff35d66a0ee66babb1eb55386ef981feaef392f9df6d671f3c124812ba24da2 + languageName: node + linkType: hard + +"regjsparser@npm:^0.9.1": + version: 0.9.1 + resolution: "regjsparser@npm:0.9.1" + dependencies: + jsesc: ~0.5.0 + bin: + regjsparser: bin/parser + checksum: 5e1b76afe8f1d03c3beaf9e0d935dd467589c3625f6d65fb8ffa14f224d783a0fed4bf49c2c1b8211043ef92b6117313419edf055a098ed8342e340586741afc + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -6436,6 +8034,13 @@ __metadata: languageName: node linkType: hard +"resolve.exports@npm:^2.0.2": + version: 2.0.2 + resolution: "resolve.exports@npm:2.0.2" + checksum: 1c7778ca1b86a94f8ab4055d196c7d87d1874b96df4d7c3e67bbf793140f0717fd506dcafd62785b079cd6086b9264424ad634fb904409764c3509c3df1653f2 + languageName: node + linkType: hard + "resolve@npm:^1.10.0, resolve@npm:^1.20.0": version: 1.22.1 resolution: "resolve@npm:1.22.1" @@ -6462,6 +8067,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "restore-cursor@npm:4.0.0" + dependencies: + onetime: ^5.1.0 + signal-exit: ^3.0.2 + checksum: 5b675c5a59763bf26e604289eab35711525f11388d77f409453904e1e69c0d37ae5889295706b2c81d23bd780165084d040f9b68fffc32cc921519031c4fa4af + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -6620,6 +8235,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.3, semver@npm:^7.5.4": + version: 7.5.4 + resolution: "semver@npm:7.5.4" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 12d8ad952fa353b0995bf180cdac205a4068b759a140e5d3c608317098b3575ac2f1e09182206bf2eb26120e1c0ed8fb92c48c592f6099680de56bb071423ca3 + languageName: node + linkType: hard + "set-blocking@npm:^2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" @@ -6763,7 +8389,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.5.20": +"source-map-support@npm:0.5.21, source-map-support@npm:^0.5.20, source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -6773,6 +8399,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:^0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 + languageName: node + linkType: hard + "source-map@npm:0.8.0-beta.0": version: 0.8.0-beta.0 resolution: "source-map@npm:0.8.0-beta.0" @@ -6782,13 +8415,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:^0.6.1": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 - languageName: node - linkType: hard - "source-map@npm:^0.7.4": version: 0.7.4 resolution: "source-map@npm:0.7.4" @@ -6879,6 +8505,32 @@ __metadata: languageName: node linkType: hard +"stacktracey@npm:^2.1.8": + version: 2.1.8 + resolution: "stacktracey@npm:2.1.8" + dependencies: + as-table: ^1.0.36 + get-source: ^2.0.12 + checksum: abd8316b4e120884108f5a47b2f61abdcaeaa118afd95f3c48317cb057fff43d697450ba00de3f9fe7fee61ee72644ccda4db990a8e4553706644f7c17522eab + languageName: node + linkType: hard + +"stdin-discarder@npm:^0.1.0": + version: 0.1.0 + resolution: "stdin-discarder@npm:0.1.0" + dependencies: + bl: ^5.0.0 + checksum: 85131f70ae2830144133b7a6211d56f9ac2603573f4af3d0b66e828af5e13fcdea351f9192f86bb7fed2c64604c8097bf36d50cb77d54e898ce4604c3b7b6b8f + languageName: node + linkType: hard + +"stoppable@npm:^1.1.0": + version: 1.1.0 + resolution: "stoppable@npm:1.1.0" + checksum: 63104fcbdece130bc4906fd982061e763d2ef48065ed1ab29895e5ad00552c625f8a4c50c9cd2e3bfa805c8a2c3bfdda0f07c5ae39694bd2d5cb0bee1618d1e9 + languageName: node + linkType: hard + "stream-transform@npm:^2.1.3": version: 2.1.3 resolution: "stream-transform@npm:2.1.3" @@ -6956,6 +8608,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: ^6.0.1 + checksum: 859c73fcf27869c22a4e4d8c6acfe690064659e84bef9458aa6d13719d09ca88dcfd40cbf31fd0be63518ea1a643fe070b4827d353e09533a5b0b9fd4553d64d + languageName: node + linkType: hard + "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -7085,6 +8746,20 @@ __metadata: languageName: node linkType: hard +"terser@npm:^5.16.1": + version: 5.24.0 + resolution: "terser@npm:5.24.0" + dependencies: + "@jridgewell/source-map": ^0.3.3 + acorn: ^8.8.2 + commander: ^2.20.0 + source-map-support: ~0.5.20 + bin: + terser: bin/terser + checksum: d88f774b6fa711a234fcecefd7657f99189c367e17dbe95a51c2776d426ad0e4d98d1ffe6edfdf299877c7602e495bdd711d21b2caaec188410795e5447d0f6c + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -7236,6 +8911,39 @@ __metadata: languageName: node linkType: hard +"ts-jest@npm:^29.1.1": + version: 29.1.1 + resolution: "ts-jest@npm:29.1.1" + dependencies: + bs-logger: 0.x + fast-json-stable-stringify: 2.x + jest-util: ^29.0.0 + json5: ^2.2.3 + lodash.memoize: 4.x + make-error: 1.x + semver: ^7.5.3 + yargs-parser: ^21.0.1 + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/types": ^29.0.0 + babel-jest: ^29.0.0 + jest: ^29.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: a8c9e284ed4f819526749f6e4dc6421ec666f20ab44d31b0f02b4ed979975f7580b18aea4813172d43e39b29464a71899f8893dd29b06b4a351a3af8ba47b402 + languageName: node + linkType: hard + "tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -7243,6 +8951,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.2.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tsup@npm:^5.10.1": version: 5.12.9 resolution: "tsup@npm:5.12.9" @@ -7481,6 +9196,46 @@ turbo@latest: languageName: node linkType: hard +"undici@npm:^5.22.1": + version: 5.27.2 + resolution: "undici@npm:5.27.2" + dependencies: + "@fastify/busboy": ^2.0.0 + checksum: 22bbdd763798700979986546d70072b67223189353d2a811efa9c6e44476161a0d1781ffe24115221f69a1b344b95d5926bd39a6eb760a2cd8804781cec0c5eb + languageName: node + linkType: hard + +"unicode-canonical-property-names-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" + checksum: 39be078afd014c14dcd957a7a46a60061bc37c4508ba146517f85f60361acf4c7539552645ece25de840e17e293baa5556268d091ca6762747fdd0c705001a45 + languageName: node + linkType: hard + +"unicode-match-property-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-match-property-ecmascript@npm:2.0.0" + dependencies: + unicode-canonical-property-names-ecmascript: ^2.0.0 + unicode-property-aliases-ecmascript: ^2.0.0 + checksum: 1f34a7434a23df4885b5890ac36c5b2161a809887000be560f56ad4b11126d433c0c1c39baf1016bdabed4ec54829a6190ee37aa24919aa116dc1a5a8a62965a + languageName: node + linkType: hard + +"unicode-match-property-value-ecmascript@npm:^2.1.0": + version: 2.1.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.1.0" + checksum: 8d6f5f586b9ce1ed0e84a37df6b42fdba1317a05b5df0c249962bd5da89528771e2d149837cad11aa26bcb84c35355cb9f58a10c3d41fa3b899181ece6c85220 + languageName: node + linkType: hard + +"unicode-property-aliases-ecmascript@npm:^2.0.0": + version: 2.1.0 + resolution: "unicode-property-aliases-ecmascript@npm:2.1.0" + checksum: 243524431893649b62cc674d877bd64ef292d6071dd2fd01ab4d5ad26efbc104ffcd064f93f8a06b7e4ec54c172bf03f6417921a0d8c3a9994161fe1f88f815b + languageName: node + linkType: hard + "unique-filename@npm:^2.0.0": version: 2.0.1 resolution: "unique-filename@npm:2.0.1" @@ -7707,6 +9462,32 @@ turbo@latest: languageName: node linkType: hard +"workerd@npm:1.20231030.0": + version: 1.20231030.0 + resolution: "workerd@npm:1.20231030.0" + dependencies: + "@cloudflare/workerd-darwin-64": 1.20231030.0 + "@cloudflare/workerd-darwin-arm64": 1.20231030.0 + "@cloudflare/workerd-linux-64": 1.20231030.0 + "@cloudflare/workerd-linux-arm64": 1.20231030.0 + "@cloudflare/workerd-windows-64": 1.20231030.0 + dependenciesMeta: + "@cloudflare/workerd-darwin-64": + optional: true + "@cloudflare/workerd-darwin-arm64": + optional: true + "@cloudflare/workerd-linux-64": + optional: true + "@cloudflare/workerd-linux-arm64": + optional: true + "@cloudflare/workerd-windows-64": + optional: true + bin: + workerd: bin/workerd + checksum: a0c7af094c05260ed07b1217fd8542789d39fef3e9b6ba4f97cc1902136c1d5a76f4870e2d04789570925761fc8ec65e7ddfbac53f35ec8c5f939e08d128f55b + languageName: node + linkType: hard + "worktop@npm:^0.8.0-next.14": version: 0.8.0-next.14 resolution: "worktop@npm:0.8.0-next.14" @@ -7744,6 +9525,35 @@ turbo@latest: languageName: node linkType: hard +"wrangler@npm:^3.17.0": + version: 3.17.0 + resolution: "wrangler@npm:3.17.0" + dependencies: + "@cloudflare/kv-asset-handler": ^0.2.0 + "@esbuild-plugins/node-globals-polyfill": ^0.2.3 + "@esbuild-plugins/node-modules-polyfill": ^0.2.2 + blake3-wasm: ^2.1.5 + chokidar: ^3.5.3 + esbuild: 0.17.19 + fsevents: ~2.3.2 + miniflare: 3.20231030.1 + nanoid: ^3.3.3 + path-to-regexp: ^6.2.0 + resolve.exports: ^2.0.2 + selfsigned: ^2.0.1 + source-map: 0.6.1 + source-map-support: 0.5.21 + xxhash-wasm: ^1.0.1 + dependenciesMeta: + fsevents: + optional: true + bin: + wrangler: bin/wrangler.js + wrangler2: bin/wrangler.js + checksum: 011c43abcdf168a9131e691045a4891c5ff89823b728c799b02690e7660570546d131aae0ba93448dc16c074d7a8116a97e72a96a4fdcbaa4608b47ea4431da7 + languageName: node + linkType: hard + "wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" @@ -7783,6 +9593,21 @@ turbo@latest: languageName: node linkType: hard +"ws@npm:^8.11.0": + version: 8.14.2 + resolution: "ws@npm:8.14.2" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 3ca0dad26e8cc6515ff392b622a1467430814c463b3368b0258e33696b1d4bed7510bc7030f7b72838b9fdeb8dbd8839cbf808367d6aae2e1d668ce741d4308b + languageName: node + linkType: hard + "ws@npm:^8.2.2": version: 8.12.1 resolution: "ws@npm:8.12.1" @@ -7916,3 +9741,21 @@ turbo@latest: checksum: e81644ad9469a26949a85cb2e1a3f9c9faf11daa67a265e9ad0361942b2c06cf02ee1dc8c85d74fe7ad25d35bbc2fee34717a54c5a0b8eeb82dc9276d139906b languageName: node linkType: hard + +"youch@npm:^3.2.2": + version: 3.3.3 + resolution: "youch@npm:3.3.3" + dependencies: + cookie: ^0.5.0 + mustache: ^4.2.0 + stacktracey: ^2.1.8 + checksum: 2b099d8f7b7579ef9d226023037d06c3c03ed6c663e03966d034505e11c026b7ea1359052d5402ded439a9e762cf72449b0db4170e6982ce8be24bbbda4e6b95 + languageName: node + linkType: hard + +"zod@npm:^3.20.6": + version: 3.22.4 + resolution: "zod@npm:3.22.4" + checksum: 80bfd7f8039b24fddeb0718a2ec7c02aa9856e4838d6aa4864335a047b6b37a3273b191ef335bf0b2002e5c514ef261ffcda5a589fb084a48c336ffc4cdbab7f + languageName: node + linkType: hard