From 09233de34052f9c70882b92fbaf1a0fa8fd1ed17 Mon Sep 17 00:00:00 2001
From: bencmbrook <7354176+bencmbrook@users.noreply.github.com>
Date: Wed, 27 May 2026 19:29:29 -0400
Subject: [PATCH 01/17] init react-airgap
---
packages/react-airgap/package.json | 57 ++
packages/react-airgap/src/index.ts | 11 +
.../src/next/tracking-script.test.ts | 79 +++
.../react-airgap/src/next/tracking-script.tsx | 292 +++++++++
packages/react-airgap/tsconfig.json | 14 +
packages/react-airgap/tsdown.config.ts | 8 +
pnpm-lock.yaml | 564 ++++++++++++++++++
pnpm-workspace.yaml | 7 +-
tsconfig.json | 1 +
9 files changed, 1032 insertions(+), 1 deletion(-)
create mode 100644 packages/react-airgap/package.json
create mode 100644 packages/react-airgap/src/index.ts
create mode 100644 packages/react-airgap/src/next/tracking-script.test.ts
create mode 100644 packages/react-airgap/src/next/tracking-script.tsx
create mode 100644 packages/react-airgap/tsconfig.json
create mode 100644 packages/react-airgap/tsdown.config.ts
diff --git a/packages/react-airgap/package.json b/packages/react-airgap/package.json
new file mode 100644
index 00000000..a29fe23a
--- /dev/null
+++ b/packages/react-airgap/package.json
@@ -0,0 +1,57 @@
+{
+ "name": "@transcend-io/react-airgap",
+ "version": "0.0.0",
+ "description": "React components for the Transcend Airgap library.",
+ "homepage": "https://github.com/transcend-io/tools/tree/main/packages/react-airgap",
+ "license": "Apache-2.0",
+ "author": "Transcend Inc.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/transcend-io/tools.git",
+ "directory": "packages/react-airgap"
+ },
+ "files": [
+ "dist"
+ ],
+ "type": "module",
+ "sideEffects": false,
+ "types": "./dist/index.d.mts",
+ "exports": {
+ ".": {
+ "@transcend-io/source": "./src/index.ts",
+ "types": "./dist/index.d.mts",
+ "default": "./dist/index.mjs"
+ }
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "scripts": {
+ "build": "tsdown",
+ "test": "vitest run",
+ "typecheck": "tsc -p tsconfig.json --noEmit",
+ "check:exports": "attw --pack . --ignore-rules cjs-resolves-to-esm",
+ "check:publint": "publint --level warning --strict --pack pnpm"
+ },
+ "devDependencies": {
+ "@arethetypeswrong/cli": "catalog:",
+ "@types/node": "catalog:",
+ "@types/react": "catalog:",
+ "@types/react-dom": "catalog:",
+ "next": "catalog:",
+ "publint": "catalog:",
+ "react": "catalog:",
+ "react-dom": "catalog:",
+ "tsdown": "catalog:",
+ "typescript": "catalog:",
+ "vitest": "catalog:"
+ },
+ "peerDependencies": {
+ "next": "catalog:",
+ "react": "catalog:",
+ "react-dom": "catalog:"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+}
diff --git a/packages/react-airgap/src/index.ts b/packages/react-airgap/src/index.ts
new file mode 100644
index 00000000..5494e500
--- /dev/null
+++ b/packages/react-airgap/src/index.ts
@@ -0,0 +1,11 @@
+'use client';
+
+export {
+ allOf,
+ anyOf,
+ default as TrackingScript,
+ onConsent,
+ onEvent,
+ onPromise,
+} from './next/tracking-script.js';
+export type { LoadTrigger, OnEventOptions, TrackingScriptProps } from './next/tracking-script.js';
diff --git a/packages/react-airgap/src/next/tracking-script.test.ts b/packages/react-airgap/src/next/tracking-script.test.ts
new file mode 100644
index 00000000..63ffed8c
--- /dev/null
+++ b/packages/react-airgap/src/next/tracking-script.test.ts
@@ -0,0 +1,79 @@
+import { describe, expect, test, vi } from 'vitest';
+
+import { allOf, anyOf, onEvent, onPromise, type LoadTrigger } from './tracking-script.js';
+
+describe('tracking script triggers', () => {
+ test('onEvent loads once when the event fires', () => {
+ const target = new EventTarget();
+ const load = vi.fn();
+ const cleanup = onEvent('ready', { target })(load);
+
+ target.dispatchEvent(new Event('ready'));
+ target.dispatchEvent(new Event('ready'));
+
+ expect(load).toHaveBeenCalledTimes(1);
+ cleanup?.();
+ });
+
+ test('onEvent loads immediately when the latch is already open', () => {
+ const target = new EventTarget();
+ const load = vi.fn();
+
+ onEvent('ready', { target, latch: () => true })(load);
+
+ expect(load).toHaveBeenCalledTimes(1);
+ });
+
+ test('onPromise loads when the promise resolves', async () => {
+ const load = vi.fn();
+
+ onPromise(Promise.resolve())(load);
+ await Promise.resolve();
+
+ expect(load).toHaveBeenCalledTimes(1);
+ });
+
+ test('anyOf loads once when the first trigger fires and cleans up every trigger', () => {
+ const triggerCallbacks: Array<() => void> = [];
+ const firstCleanup = vi.fn();
+ const secondCleanup = vi.fn();
+ const first: LoadTrigger = (load) => {
+ triggerCallbacks[0] = load;
+ return firstCleanup;
+ };
+ const second: LoadTrigger = (load) => {
+ triggerCallbacks[1] = load;
+ return secondCleanup;
+ };
+ const load = vi.fn();
+
+ const cleanup = anyOf(first, second)(load);
+ triggerCallbacks[0]?.();
+ triggerCallbacks[1]?.();
+ cleanup?.();
+
+ expect(load).toHaveBeenCalledTimes(1);
+ expect(firstCleanup).toHaveBeenCalledTimes(1);
+ expect(secondCleanup).toHaveBeenCalledTimes(1);
+ });
+
+ test('allOf waits for every trigger and ignores duplicate trigger calls', () => {
+ const triggerCallbacks: Array<() => void> = [];
+ const first: LoadTrigger = (load) => {
+ triggerCallbacks[0] = load;
+ };
+ const second: LoadTrigger = (load) => {
+ triggerCallbacks[1] = load;
+ };
+ const load = vi.fn();
+
+ allOf(first, second)(load);
+ triggerCallbacks[0]?.();
+ triggerCallbacks[0]?.();
+ expect(load).not.toHaveBeenCalled();
+
+ triggerCallbacks[1]?.();
+
+ expect(load).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/packages/react-airgap/src/next/tracking-script.tsx b/packages/react-airgap/src/next/tracking-script.tsx
new file mode 100644
index 00000000..da8226ab
--- /dev/null
+++ b/packages/react-airgap/src/next/tracking-script.tsx
@@ -0,0 +1,292 @@
+'use client';
+
+/**
+ * TrackingScript — a drop-in wrapper around `next/script` that defers script
+ * injection until one or more triggers fire (custom events, promises, or
+ * Transcend airgap consent).
+ *
+ * Supports both forms of `next/script`:
+ *
+ * {`...inline JS...`}
+ *
+ * SSR: like `;
+}
+
+/* -------------------------------------------------------------------------- */
+/* Examples */
+/* -------------------------------------------------------------------------- */
+
+/*
+import TrackingScript, { onEvent, onConsent, onPromise, anyOf, allOf } from './tracking-script';
+
+// 1. External script gated on a custom event
+
+
+// 2. External script gated on airgap + Analytics consent
+
+
+// 3. Inline GTM bootstrap gated on consent (note: requires `id`)
+{`
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
+ var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;
+ j.src='https://www.googletagmanager.com/gtm.js?id='+i;f.parentNode.insertBefore(j,f);
+ })(window,document,'script','dataLayer','GTM-XXXX');
+`}
+
+// 4. Compound: consent AND (custom event OR 5s timeout)
+ setTimeout(r, 5000))),
+ ),
+ )}
+/>
+*/
diff --git a/packages/react-airgap/tsconfig.json b/packages/react-airgap/tsconfig.json
new file mode 100644
index 00000000..4d330f66
--- /dev/null
+++ b/packages/react-airgap/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "jsx": "react-jsx",
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "outDir": "dist",
+ "rootDir": "src",
+ "types": ["node", "vitest/globals"]
+ },
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
+}
diff --git a/packages/react-airgap/tsdown.config.ts b/packages/react-airgap/tsdown.config.ts
new file mode 100644
index 00000000..7ab4ad4c
--- /dev/null
+++ b/packages/react-airgap/tsdown.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'tsdown';
+
+import sharedConfig from '../../tsdown.config.base.ts';
+
+export default defineConfig({
+ ...sharedConfig,
+ entry: ['src/index.ts'],
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ad1dee36..2e8fcfec 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -21,12 +21,21 @@ catalogs:
'@types/node':
specifier: ^22.19.15
version: 22.19.15
+ '@types/react':
+ specifier: ^19.2.15
+ version: 19.2.15
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3
'@types/semver':
specifier: ^7.7.1
version: 7.7.1
husky:
specifier: ^9.1.7
version: 9.1.7
+ next:
+ specifier: ^16.2.6
+ version: 16.2.6
oxfmt:
specifier: ^0.38.0
version: 0.38.0
@@ -36,6 +45,12 @@ catalogs:
publint:
specifier: ^0.3.18
version: 0.3.18
+ react:
+ specifier: ^19.2.6
+ version: 19.2.6
+ react-dom:
+ specifier: ^19.2.6
+ version: 19.2.6
semver:
specifier: ^7.7.4
version: 7.7.4
@@ -733,6 +748,42 @@ importers:
specifier: 'catalog:'
version: 4.0.18(@types/node@22.19.15)(tsx@4.21.0)
+ packages/react-airgap:
+ devDependencies:
+ '@arethetypeswrong/cli':
+ specifier: 'catalog:'
+ version: 0.18.2
+ '@types/node':
+ specifier: 'catalog:'
+ version: 22.19.15
+ '@types/react':
+ specifier: 'catalog:'
+ version: 19.2.15
+ '@types/react-dom':
+ specifier: 'catalog:'
+ version: 19.2.3(@types/react@19.2.15)
+ next:
+ specifier: 'catalog:'
+ version: 16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ publint:
+ specifier: 'catalog:'
+ version: 0.3.18
+ react:
+ specifier: 'catalog:'
+ version: 19.2.6
+ react-dom:
+ specifier: 'catalog:'
+ version: 19.2.6(react@19.2.6)
+ tsdown:
+ specifier: 'catalog:'
+ version: 0.21.2(@arethetypeswrong/core@0.18.2)(publint@0.3.18)(typescript@6.0.3)
+ typescript:
+ specifier: 'catalog:'
+ version: 6.0.3
+ vitest:
+ specifier: 'catalog:'
+ version: 4.0.18(@types/node@22.19.15)(tsx@4.21.0)
+
packages/sdk:
dependencies:
'@transcend-io/airgap.js-types':
@@ -1182,6 +1233,159 @@ packages:
peerDependencies:
hono: ^4
+ '@img/colour@1.1.0':
+ resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
+ engines: {node: '>=18'}
+
+ '@img/sharp-darwin-arm64@0.34.5':
+ resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.34.5':
+ resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.2.4':
+ resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.2.4':
+ resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.2.4':
+ resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-arm@1.2.4':
+ resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-ppc64@1.2.4':
+ resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-riscv64@1.2.4':
+ resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-s390x@1.2.4':
+ resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-x64@1.2.4':
+ resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
+ resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.4':
+ resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linux-arm64@0.34.5':
+ resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-arm@0.34.5':
+ resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-ppc64@0.34.5':
+ resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-riscv64@0.34.5':
+ resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-s390x@0.34.5':
+ resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-x64@0.34.5':
+ resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linuxmusl-arm64@0.34.5':
+ resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linuxmusl-x64@0.34.5':
+ resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-wasm32@0.34.5':
+ resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.34.5':
+ resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@img/sharp-win32-ia32@0.34.5':
+ resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.34.5':
+ resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
'@inquirer/external-editor@1.0.3':
resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==}
engines: {node: '>=18'}
@@ -1229,6 +1433,61 @@ packages:
'@napi-rs/wasm-runtime@1.1.1':
resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==}
+ '@next/env@16.2.6':
+ resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==}
+
+ '@next/swc-darwin-arm64@16.2.6':
+ resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@16.2.6':
+ resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@16.2.6':
+ resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-arm64-musl@16.2.6':
+ resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-linux-x64-gnu@16.2.6':
+ resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-x64-musl@16.2.6':
+ resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-win32-arm64-msvc@16.2.6':
+ resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@16.2.6':
+ resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1752,6 +2011,9 @@ packages:
'@stricli/core@1.2.6':
resolution: {integrity: sha512-j5fa1wyOLrP9WJqqLFEJeQviqb3cK46K+FXTuISEkG/H5C820YfKDoVQ3CDVdM5WLhEx1ARdpiW6+hU4tYHjCQ==}
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
'@textlint/ast-node-types@12.6.1':
resolution: {integrity: sha512-uzlJ+ZsCAyJm+lBi7j0UeBbj+Oy6w/VWoGJ3iHRHE5eZ8Z4iK66mq+PG/spupmbllLtz77OJbY89BYqgFyjXmA==}
@@ -1900,6 +2162,14 @@ packages:
'@types/range-parser@1.2.7':
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.15':
+ resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==}
+
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
@@ -2043,6 +2313,11 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ baseline-browser-mapping@2.10.32:
+ resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
better-path-resolve@1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'}
@@ -2109,6 +2384,9 @@ packages:
camel-case@4.1.2:
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
+ caniuse-lite@1.0.30001793:
+ resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==}
+
capital-case@1.0.4:
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
@@ -2178,6 +2456,9 @@ packages:
resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==}
engines: {node: '>= 10'}
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
cliui@7.0.4:
resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
@@ -2234,6 +2515,9 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
csv-parse@5.6.0:
resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==}
@@ -2295,6 +2579,10 @@ packages:
resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
engines: {node: '>=8'}
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
detect-node@2.1.0:
resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
@@ -3197,6 +3485,27 @@ packages:
fp-ts: ^2.0.0
monocle-ts: ^2.0.0
+ next@16.2.6:
+ resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==}
+ engines: {node: '>=20.9.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
@@ -3369,6 +3678,10 @@ packages:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
@@ -3416,6 +3729,15 @@ packages:
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'}
+ react-dom@19.2.6:
+ resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==}
+ peerDependencies:
+ react: ^19.2.6
+
+ react@19.2.6:
+ resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==}
+ engines: {node: '>=0.10.0'}
+
read-yaml-file@1.1.0:
resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==}
engines: {node: '>=6'}
@@ -3554,6 +3876,9 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
semver-compare@1.0.0:
resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
@@ -3592,6 +3917,10 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+ sharp@0.34.5:
+ resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -3703,6 +4032,19 @@ packages:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
engines: {node: '>=4'}
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -4488,6 +4830,103 @@ snapshots:
dependencies:
hono: 4.12.12
+ '@img/colour@1.1.0':
+ optional: true
+
+ '@img/sharp-darwin-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-riscv64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-riscv64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-riscv64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-s390x@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-wasm32@0.34.5':
+ dependencies:
+ '@emnapi/runtime': 1.8.1
+ optional: true
+
+ '@img/sharp-win32-arm64@0.34.5':
+ optional: true
+
+ '@img/sharp-win32-ia32@0.34.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.34.5':
+ optional: true
+
'@inquirer/external-editor@1.0.3(@types/node@22.19.15)':
dependencies:
chardet: 2.1.1
@@ -4560,6 +4999,32 @@ snapshots:
'@tybys/wasm-util': 0.10.1
optional: true
+ '@next/env@16.2.6': {}
+
+ '@next/swc-darwin-arm64@16.2.6':
+ optional: true
+
+ '@next/swc-darwin-x64@16.2.6':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@16.2.6':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@16.2.6':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@16.2.6':
+ optional: true
+
+ '@next/swc-linux-x64-musl@16.2.6':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@16.2.6':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@16.2.6':
+ optional: true
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -4832,6 +5297,10 @@ snapshots:
'@stricli/core@1.2.6': {}
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
'@textlint/ast-node-types@12.6.1': {}
'@textlint/markdown-to-ast@12.6.1':
@@ -5002,6 +5471,14 @@ snapshots:
'@types/range-parser@1.2.7': {}
+ '@types/react-dom@19.2.3(@types/react@19.2.15)':
+ dependencies:
+ '@types/react': 19.2.15
+
+ '@types/react@19.2.15':
+ dependencies:
+ csstype: 3.2.3
+
'@types/semver@7.7.1': {}
'@types/send@1.2.1':
@@ -5151,6 +5628,8 @@ snapshots:
base64-js@1.5.1: {}
+ baseline-browser-mapping@2.10.32: {}
+
better-path-resolve@1.0.0:
dependencies:
is-windows: 1.0.2
@@ -5232,6 +5711,8 @@ snapshots:
pascal-case: 3.1.2
tslib: 2.8.1
+ caniuse-lite@1.0.30001793: {}
+
capital-case@1.0.4:
dependencies:
no-case: 3.0.4
@@ -5305,6 +5786,8 @@ snapshots:
cli-width@3.0.0: {}
+ client-only@0.0.1: {}
+
cliui@7.0.4:
dependencies:
string-width: 4.2.3
@@ -5358,6 +5841,8 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
+ csstype@3.2.3: {}
+
csv-parse@5.6.0: {}
data-view-buffer@1.0.2:
@@ -5414,6 +5899,9 @@ snapshots:
detect-indent@6.1.0: {}
+ detect-libc@2.1.2:
+ optional: true
+
detect-node@2.1.0: {}
dir-glob@3.0.1:
@@ -6457,6 +6945,30 @@ snapshots:
fp-ts: 2.16.11
monocle-ts: 2.3.13(fp-ts@2.16.11)
+ next@16.2.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ dependencies:
+ '@next/env': 16.2.6
+ '@swc/helpers': 0.5.15
+ baseline-browser-mapping: 2.10.32
+ caniuse-lite: 1.0.30001793
+ postcss: 8.4.31
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ styled-jsx: 5.1.6(react@19.2.6)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 16.2.6
+ '@next/swc-darwin-x64': 16.2.6
+ '@next/swc-linux-arm64-gnu': 16.2.6
+ '@next/swc-linux-arm64-musl': 16.2.6
+ '@next/swc-linux-x64-gnu': 16.2.6
+ '@next/swc-linux-x64-musl': 16.2.6
+ '@next/swc-win32-arm64-msvc': 16.2.6
+ '@next/swc-win32-x64-msvc': 16.2.6
+ sharp: 0.34.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
no-case@3.0.4:
dependencies:
lower-case: 2.0.2
@@ -6650,6 +7162,12 @@ snapshots:
possible-typed-array-names@1.1.0: {}
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
postcss@8.5.8:
dependencies:
nanoid: 3.3.11
@@ -6698,6 +7216,13 @@ snapshots:
iconv-lite: 0.7.2
unpipe: 1.0.0
+ react-dom@19.2.6(react@19.2.6):
+ dependencies:
+ react: 19.2.6
+ scheduler: 0.27.0
+
+ react@19.2.6: {}
+
read-yaml-file@1.1.0:
dependencies:
graceful-fs: 4.2.11
@@ -6910,6 +7435,8 @@ snapshots:
safer-buffer@2.1.2: {}
+ scheduler@0.27.0: {}
+
semver-compare@1.0.0: {}
semver@7.7.4: {}
@@ -6973,6 +7500,38 @@ snapshots:
setprototypeof@1.2.0: {}
+ sharp@0.34.5:
+ dependencies:
+ '@img/colour': 1.1.0
+ detect-libc: 2.1.2
+ semver: 7.7.4
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.34.5
+ '@img/sharp-darwin-x64': 0.34.5
+ '@img/sharp-libvips-darwin-arm64': 1.2.4
+ '@img/sharp-libvips-darwin-x64': 1.2.4
+ '@img/sharp-libvips-linux-arm': 1.2.4
+ '@img/sharp-libvips-linux-arm64': 1.2.4
+ '@img/sharp-libvips-linux-ppc64': 1.2.4
+ '@img/sharp-libvips-linux-riscv64': 1.2.4
+ '@img/sharp-libvips-linux-s390x': 1.2.4
+ '@img/sharp-libvips-linux-x64': 1.2.4
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.4
+ '@img/sharp-linux-arm': 0.34.5
+ '@img/sharp-linux-arm64': 0.34.5
+ '@img/sharp-linux-ppc64': 0.34.5
+ '@img/sharp-linux-riscv64': 0.34.5
+ '@img/sharp-linux-s390x': 0.34.5
+ '@img/sharp-linux-x64': 0.34.5
+ '@img/sharp-linuxmusl-arm64': 0.34.5
+ '@img/sharp-linuxmusl-x64': 0.34.5
+ '@img/sharp-wasm32': 0.34.5
+ '@img/sharp-win32-arm64': 0.34.5
+ '@img/sharp-win32-ia32': 0.34.5
+ '@img/sharp-win32-x64': 0.34.5
+ optional: true
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -7091,6 +7650,11 @@ snapshots:
strip-bom@3.0.0: {}
+ styled-jsx@5.1.6(react@19.2.6):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.2.6
+
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index c586b3e2..8de26747 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -9,11 +9,16 @@ catalog:
'@transcend-io/internationalization': ^2.3.2
'@types/json-schema': ^7.0.15
'@types/node': ^22.19.15
+ '@types/react': ^19.2.15
+ '@types/react-dom': ^19.2.3
'@types/semver': ^7.7.1
husky: ^9.1.7
+ next: ^16.2.6
oxfmt: ^0.38.0
oxlint: ^1.53.0
publint: ^0.3.18
+ react: ^19.2.6
+ react-dom: ^19.2.6
semver: ^7.7.4
syncpack: ^14.2.0
tsdown: ^0.21.2
@@ -22,8 +27,8 @@ catalog:
vitest: ^4.0.18
zod: ^4.3.6
-# Only install npm packages that are at least 3 days old
minimumReleaseAge: 4320
+
minimumReleaseAgeExclude:
- '@transcend-io/*'
diff --git a/tsconfig.json b/tsconfig.json
index 99f34d37..39e47e0f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -16,6 +16,7 @@
{ "path": "./packages/mcp/mcp-server-preferences" },
{ "path": "./packages/mcp/mcp-server-workflows" },
{ "path": "./packages/privacy-types" },
+ { "path": "./packages/react-airgap" },
{ "path": "./packages/sdk" },
{ "path": "./packages/type-utils" },
{ "path": "./packages/utils" }
From 3626887f5fdc35f4355d4972940bc43d093343b4 Mon Sep 17 00:00:00 2001
From: bencmbrook <7354176+bencmbrook@users.noreply.github.com>
Date: Wed, 27 May 2026 19:43:50 -0400
Subject: [PATCH 02/17] Simplify react-airgap script gating
---
packages/react-airgap/src/index.ts | 11 +-
.../src/next/tracking-script.test.ts | 96 ++----
.../react-airgap/src/next/tracking-script.tsx | 287 +++---------------
3 files changed, 73 insertions(+), 321 deletions(-)
diff --git a/packages/react-airgap/src/index.ts b/packages/react-airgap/src/index.ts
index 5494e500..5610c864 100644
--- a/packages/react-airgap/src/index.ts
+++ b/packages/react-airgap/src/index.ts
@@ -1,11 +1,4 @@
'use client';
-export {
- allOf,
- anyOf,
- default as TrackingScript,
- onConsent,
- onEvent,
- onPromise,
-} from './next/tracking-script.js';
-export type { LoadTrigger, OnEventOptions, TrackingScriptProps } from './next/tracking-script.js';
+export { default as TrackingScript } from './next/tracking-script.js';
+export type { LoadAfter, TrackingScriptProps } from './next/tracking-script.js';
diff --git a/packages/react-airgap/src/next/tracking-script.test.ts b/packages/react-airgap/src/next/tracking-script.test.ts
index 63ffed8c..22a8f079 100644
--- a/packages/react-airgap/src/next/tracking-script.test.ts
+++ b/packages/react-airgap/src/next/tracking-script.test.ts
@@ -1,79 +1,43 @@
-import { describe, expect, test, vi } from 'vitest';
+import { describe, expect, test } from 'vitest';
-import { allOf, anyOf, onEvent, onPromise, type LoadTrigger } from './tracking-script.js';
+import { waitForLoadAfter } from './tracking-script.js';
-describe('tracking script triggers', () => {
- test('onEvent loads once when the event fires', () => {
- const target = new EventTarget();
- const load = vi.fn();
- const cleanup = onEvent('ready', { target })(load);
+describe('waitForLoadAfter', () => {
+ test('resolves after a single promise resolves', async () => {
+ const value = {};
- target.dispatchEvent(new Event('ready'));
- target.dispatchEvent(new Event('ready'));
+ const result = await waitForLoadAfter(Promise.resolve(value));
- expect(load).toHaveBeenCalledTimes(1);
- cleanup?.();
+ expect(result).toBe(value);
});
- test('onEvent loads immediately when the latch is already open', () => {
- const target = new EventTarget();
- const load = vi.fn();
-
- onEvent('ready', { target, latch: () => true })(load);
-
- expect(load).toHaveBeenCalledTimes(1);
- });
-
- test('onPromise loads when the promise resolves', async () => {
- const load = vi.fn();
-
- onPromise(Promise.resolve())(load);
+ test('waits for promise composition from the caller', async () => {
+ let resolveFirst!: () => void;
+ let resolveSecond!: () => void;
+ const first = new Promise((resolve) => {
+ resolveFirst = resolve;
+ });
+ const second = new Promise((resolve) => {
+ resolveSecond = resolve;
+ });
+ let resolved = false;
+
+ void waitForLoadAfter(Promise.all([first, second])).then(() => {
+ resolved = true;
+ });
+ resolveFirst();
await Promise.resolve();
+ expect(resolved).toBe(false);
- expect(load).toHaveBeenCalledTimes(1);
- });
-
- test('anyOf loads once when the first trigger fires and cleans up every trigger', () => {
- const triggerCallbacks: Array<() => void> = [];
- const firstCleanup = vi.fn();
- const secondCleanup = vi.fn();
- const first: LoadTrigger = (load) => {
- triggerCallbacks[0] = load;
- return firstCleanup;
- };
- const second: LoadTrigger = (load) => {
- triggerCallbacks[1] = load;
- return secondCleanup;
- };
- const load = vi.fn();
-
- const cleanup = anyOf(first, second)(load);
- triggerCallbacks[0]?.();
- triggerCallbacks[1]?.();
- cleanup?.();
-
- expect(load).toHaveBeenCalledTimes(1);
- expect(firstCleanup).toHaveBeenCalledTimes(1);
- expect(secondCleanup).toHaveBeenCalledTimes(1);
+ resolveSecond();
+ await Promise.all([first, second]);
+ await Promise.resolve();
+ expect(resolved).toBe(true);
});
- test('allOf waits for every trigger and ignores duplicate trigger calls', () => {
- const triggerCallbacks: Array<() => void> = [];
- const first: LoadTrigger = (load) => {
- triggerCallbacks[0] = load;
- };
- const second: LoadTrigger = (load) => {
- triggerCallbacks[1] = load;
- };
- const load = vi.fn();
-
- allOf(first, second)(load);
- triggerCallbacks[0]?.();
- triggerCallbacks[0]?.();
- expect(load).not.toHaveBeenCalled();
-
- triggerCallbacks[1]?.();
+ test('rejects when a load promise rejects', async () => {
+ const error = new Error('gate failed');
- expect(load).toHaveBeenCalledTimes(1);
+ await expect(waitForLoadAfter(Promise.reject(error))).rejects.toThrow(error);
});
});
diff --git a/packages/react-airgap/src/next/tracking-script.tsx b/packages/react-airgap/src/next/tracking-script.tsx
index da8226ab..2d14b95c 100644
--- a/packages/react-airgap/src/next/tracking-script.tsx
+++ b/packages/react-airgap/src/next/tracking-script.tsx
@@ -1,198 +1,57 @@
'use client';
-/**
- * TrackingScript — a drop-in wrapper around `next/script` that defers script
- * injection until one or more triggers fire (custom events, promises, or
- * Transcend airgap consent).
- *
- * Supports both forms of `next/script`:
- *
- * {`...inline JS...`}
- *
- * SSR: like `;
}
-
-/* -------------------------------------------------------------------------- */
-/* Examples */
-/* -------------------------------------------------------------------------- */
-
-/*
-import TrackingScript, { onEvent, onConsent, onPromise, anyOf, allOf } from './tracking-script';
-
-// 1. External script gated on a custom event
-
-
-// 2. External script gated on airgap + Analytics consent
-
-
-// 3. Inline GTM bootstrap gated on consent (note: requires `id`)
-{`
- (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});
- var f=d.getElementsByTagName(s)[0],j=d.createElement(s);j.async=true;
- j.src='https://www.googletagmanager.com/gtm.js?id='+i;f.parentNode.insertBefore(j,f);
- })(window,document,'script','dataLayer','GTM-XXXX');
-`}
-
-// 4. Compound: consent AND (custom event OR 5s timeout)
- setTimeout(r, 5000))),
- ),
- )}
-/>
-*/
From 2776c3b32e379d7bb1e2a24e590e219aec1e4ed0 Mon Sep 17 00:00:00 2001
From: bencmbrook <7354176+bencmbrook@users.noreply.github.com>
Date: Wed, 27 May 2026 19:55:59 -0400
Subject: [PATCH 03/17] airgapReady
---
packages/react-airgap/README.md | 85 +++++++++++++++++++
packages/react-airgap/src/index.ts | 1 +
.../src/next/airgap-ready.test.ts | 51 +++++++++++
.../react-airgap/src/next/airgap-ready.ts | 42 +++++++++
.../react-airgap/src/next/tracking-script.tsx | 8 +-
5 files changed, 181 insertions(+), 6 deletions(-)
create mode 100644 packages/react-airgap/README.md
create mode 100644 packages/react-airgap/src/next/airgap-ready.test.ts
create mode 100644 packages/react-airgap/src/next/airgap-ready.ts
diff --git a/packages/react-airgap/README.md b/packages/react-airgap/README.md
new file mode 100644
index 00000000..77fd20b2
--- /dev/null
+++ b/packages/react-airgap/README.md
@@ -0,0 +1,85 @@
+# @transcend-io/react-airgap
+
+React helpers for loading scripts after Transcend Airgap is ready.
+
+## TrackingScript
+
+`TrackingScript` is a small client component around `next/script`. It renders
+nothing until its `loadAfter` promise resolves, then renders the underlying
+`