diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..54e4783 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.json] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..663e695 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto eol=lf + +# Keep Windows shell scripts with CRLF for compatibility. +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf diff --git a/.github/workflows/ghp.yml b/.github/workflows/ghp.yml new file mode 100644 index 0000000..fd76e99 --- /dev/null +++ b/.github/workflows/ghp.yml @@ -0,0 +1,86 @@ +name: GitHub Pages +on: + push: + branches: [ "outlook" ] + workflow_dispatch: + inputs: + VITE_IO_BRIDGE_URL: + description: 'Optional bridge URL override for this run (e.g. https://bridge.example.com)' + required: false + type: string + +#concurrency: +# group: pages +# cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write + + steps: + - name: Checkout bridge-examples + uses: actions/checkout@v6 + with: + path: bridge-examples + - name: Checkout connect-js + uses: actions/checkout@v6 + with: + repository: InteropIO/connect-js + ref: cb-next-4.3 + token: '${{ secrets.PAT }}' + path: connect-js + + - name: Use Node 20 for connect-js build + uses: actions/setup-node@v6 + with: + node-version: 20 + cache: npm + cache-dependency-path: connect-js/package-lock.json + + - name: Build connect-js + working-directory: connect-js/ + env: + LICENSE_KEY: ${{ secrets.IO_CB_LICENSE_KEY }} + run: | + npm ci + npm run build + + - name: Use project Node version for office build + uses: actions/setup-node@v6 + with: + node-version-file: bridge-examples/.node-version + cache: npm + cache-dependency-path: bridge-examples/office/package-lock.json + - name: Install dependencies + working-directory: bridge-examples/office + run: npm ci + - name: Build + working-directory: bridge-examples/office + run: npm run build + env: + VITE_IO_BRIDGE_URL: ${{ github.event.inputs.VITE_IO_BRIDGE_URL || vars.VITE_IO_BRIDGE_URL }} + VITE_AUTH0_DOMAIN: ${{ secrets.AUTH0_DOMAIN }} + VITE_AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }} + VITE_AUTH0_AUDIENCE: ${{ secrets.AUTH0_AUDIENCE }} + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v4 + with: + path: bridge-examples/office/dist + + deploy: + needs: build + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..490bca4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +.env +.secrets/ diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..8e35034 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24.14.1 diff --git a/README.md b/README.md deleted file mode 100644 index 8fc2084..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# bridge-examples -io.Bridge Examples diff --git a/office/.env.example b/office/.env.example new file mode 100644 index 0000000..3250642 --- /dev/null +++ b/office/.env.example @@ -0,0 +1,126 @@ +## This is an example .env file for io.Bridge +## Copy this file as .env and fill in the required values + +############################################################################### +## License key for io.Bridge ## +############################################################################### + +## License key for io.Bridge (Required). Specify it as a string. +IO_BRIDGE_LICENSE_KEY= +## Alternatively, you can specify the path to a file containing the license key. +#IO_BRIDGE_LICENSE_KEY_FILE=./.secrets/io-bridge-license.key + +############################################################################### +## Server ## +############################################################################### + +## The PORT for io.Bridge server to bind on (Optional, default is 8084) +#IO_BRIDGE_SERVER_PORT=8084 + +## The host for io.Bridge server to bind on (Optional, default is '0.0.0.0' i.e. any local IP) +#IO_BRIDGE_SERVER_HOST=127.0.0.1 + +## The path to file where the resolved io.Bridge server is bound (Optional) +#IO_BRIDGE_PRINT_PORT=./io-bridge-address.json + +############################################################################### +## Auth ## +############################################################################### + +## Authentication type. For example 'none', 'basic' or 'oauth2' +#IO_BRIDGE_SERVER_AUTH_TYPE=oauth2 + +## Basic Authentication Realm +IO_BRIDGE_SERVER_AUTH_BASIC_REALM=interop.io + +## +#IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_ISSUERURI= + +## +IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_AUDIENCE= + +############################################################################### +## Mesh ## +############################################################################### + +## The ping interval for mesh (Optional, default is 30s) +#IO_BRIDGE_MESH_PING_INTERVAL=30s + +############################################################################### +## Gateway ## +############################################################################### + +## If you want to embed gateway (Optional, default is false) +#IO_BRIDGE_GATEWAY_ENABLED=true + +############################################################################### +## CORS ## +############################################################################### + +## Allow origins for CORS (Optional, default is *) +## The special value * allows all origins. +## You can also specify multiple origins separated by commas. +## You can use regular expressions to match origins, for example: +## - http:\/\/localhost(:d+)? matches http://localhost, http://localhost:8080, etc. +## - file:\/\/.* matches any file URL +## - null is a way to support specific 'null' origin set by the client, for example when using about:blank in a browser +#IO_BRIDGE_SERVER_CORS_ALLOW_ORIGIN=/http:\/\/localhost(:d+)?/,/file:\/\/.*/,null + +## Allow methods for CORS (Optional, default is GET,HEAD). The special value * allows all methods. +#IO_BRIDGE_SERVER_CORS_ALLOW_METHODS=GET,POST,DELETE + +## Which headers a pre-flight request can list as allowed for use during an actual request. (Optional, default is *) +#IO_BRIDGE_SERVER_CORS_ALLOW_HEADERS=* + +## Which headers that an actual response might have and can be exposed to the clients (Optional) +#IO_BRIDGE_SERVER_CORS_EXPOSE_HEADERS=X-Authorization + +## How long (in seconds) response from a pre-flight request can be cached by clients (Optional, default is 3600 i.e. 1 hour) +#IO_BRIDGE_SERVER_CORS_MAX_AGE=3600 + +## If you want to allow credentials in CORS (Optional, default is false), cannot be true if allow origins is set to * +## Setting this to true will impact how allow origins, methods and headers are processed +#IO_BRIDGE_SERVER_CORS_ALLOW_CREDENTIALS=true + +## If you want to allow private network access (PNA) in CORS (Optional, default is false), cannot be true if allow origins is set to * +#IO_BRIDGE_SERVER_CORS_ALLOW_PRIVATE_NETWORK=true + +## If you want to completely disable CORS handling (Optional, default is false) +#IO_BRIDGE_SERVER_CORS_DISABLED=true + +############################################################################### +## Logging +############################################################################### + +## This is the logging layout to use. The default is 'ecs' which is the Elastic Common Schema. +## Other options are 'logstash' +LOGGING_LAYOUT=ecs + +## The logging level to use. The default is 'info'. +## Supported levels are: trace, debug, info, warn, error and off +#LOGGING_LEVEL=debug + +#LOGGING_LEVEL_GATEWAY_BRIDGE=debug +#LOGGING_LEVEL_GATEWAY_SERVER=info +#LOGGING_LEVEL_GATEWAY_SERVER_HTTP=debug + +LOGGING_LEVEL_GATEWAY=info + +############################################################################### +## Web App Configuration (Vite) +## Variables prefixed with VITE_ are exposed to the browser (required by Vite) +############################################################################### + +## License key for io.Connect Browser Platform (Required for web app) +## Note: This is a DIFFERENT license than IO_BRIDGE_LICENSE_KEY above. +## - VITE_IO_CB_LICENSE_KEY is for the browser platform (client-side) +VITE_IO_CB_LICENSE_KEY= + +## io.Bridge URL for browser platform and gateway connection (Optional, default is https://gw-bridge-examples.interop.io) +#VITE_IO_BRIDGE_URL=https://localhost:8084 +VITE_AUTH0_DOMAIN= +VITE_AUTH0_CLIENT_ID= +VITE_AUTH0_AUDIENCE= + +## +#VITE_AUTH0_SCOPES=openid email diff --git a/office/.gitignore b/office/.gitignore new file mode 100644 index 0000000..353b3fa --- /dev/null +++ b/office/.gitignore @@ -0,0 +1,3 @@ +dist/ +node_modules/ +public/static/modals/ diff --git a/office/auth0/app.ts b/office/auth0/app.ts new file mode 100644 index 0000000..620cb75 --- /dev/null +++ b/office/auth0/app.ts @@ -0,0 +1,123 @@ +import type { ServerConfigurer } from "@interopio/gateway-server/web/server"; +import type { Logger } from "@interopio/gateway/logging/api"; +import { randomUUID } from "node:crypto"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +export type AuthInfo = { + user: string + username?: string, + token?: string | null + headers?: Record +} + +const successPage = readFileSync(resolve(import.meta.dirname, './success.html')).toString(); + +// --------------------------------------------------------------------------- +// Auth0 integration — Implicit Flow with response_mode=form_post +// +// GET /login/auth0 → redirects to Auth0 Universal Login +// POST /login/oauth2/code/auth0 → Auth0 POSTs tokens back here +// --------------------------------------------------------------------------- +export const app = async ( + config: { + logger: Logger, + issuerUrl: string, + clientId: string, + scopes?: string, + audience?: string, + authInfoResolver: (data: AuthInfo) => void, + }, + { + handle + }: ServerConfigurer +) => { + const { logger, issuerUrl, clientId } = config; + const scopes = config.scopes ?? "openid email"; + const loginCallbackPath = "/login/oauth2/code/auth0"; + const loginUrlPath = "/login/auth0"; + + logger.info(`auth0 login endpoint registered on [${loginUrlPath}]`); + logger.info(`auth0 callback endpoint registered on [${loginCallbackPath}]`); + + handle( + // --------------------------------------------------------------- + // GET /login/auth0 — redirect to Auth0 Universal Login + // --------------------------------------------------------------- + { + request: { method: 'GET', path: loginUrlPath }, + options: { authorize: { access: "permitted" } }, + handler: async ({ request, response }) => { + + const nonce = randomUUID(); + const state = randomUUID(); + const redirectUri = `${request.protocol}://${request.host}${loginCallbackPath}`; + logger.info(`initiating Auth0 implicit login flow ${redirectUri}`); + + const authorizeUrl = `${issuerUrl}/authorize?` + new URLSearchParams({ + response_type: 'id_token token', + response_mode: 'form_post', + client_id: clientId, + redirect_uri: redirectUri, + scope: scopes, + state, + nonce, + audience: config.audience ?? `io.bridge`, + + }).toString(); + + logger.info(`redirecting to Auth0: ${authorizeUrl}`); + response.setRawStatusCode(302); + response.headers.set('Location', authorizeUrl); + await response.end(); + } + }, + // --------------------------------------------------------------- + // POST /login/oauth2/code/auth0 — Auth0 posts tokens here + // --------------------------------------------------------------- + { + request: { method: 'POST', path: loginCallbackPath }, + options: { authorize: { access: 'permitted' } }, + handler: async ({ request, response }) => { + const formData = await request.formData(); + + const error = formData.get('error'); + const errorDescription = formData.get('error_description'); + + if (error) { + logger.warn(`auth0 error: ${error} — ${errorDescription}`); + response.setRawStatusCode(400); + await response.body(new TextEncoder().encode( + `Auth0 login failed: ${error} — ${errorDescription ?? 'unknown error'}` + )); + await response.end(); + return; + } + + const idToken = formData.get('id_token'); + let user = 'dev-user'; + let username; + if (idToken) { + const payload = JSON.parse(Buffer.from(idToken.split('.')[1], 'base64').toString()); + user = payload.email; + username = payload.name; + } + username ??= user; + + // set authInfo with the received tokens and user + config.authInfoResolver({ + token: formData.get('access_token'), + user, + username + }); + + response.setRawStatusCode(200); + response.headers.set('Content-Type', 'text/html'); + + + await response.body(new TextEncoder().encode(successPage.replace('${username}', `${username} (${user})`))); + await response.end(); + } + } + ); +} diff --git a/office/auth0/interop_licenses.js b/office/auth0/interop_licenses.js new file mode 100644 index 0000000..3f94778 --- /dev/null +++ b/office/auth0/interop_licenses.js @@ -0,0 +1,52 @@ + +const CLAIM_NAMESPACE = 'https://interop.io'; +const USE_SECRETS_LICENSE = false; +// list client ids for applications that needs licenses +const CLIENT_IDS = [ + '' //todo: YourApp's ClientId +]; + +/** + * Handler that will be called during the execution of a PostLogin flow. + * + * @param {Event} event - Details about the user and the context in which they are logging in. + * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login. + */ +exports.onExecutePostLogin = async (event, api) => { + + if (CLIENT_IDS.includes(event.client.client_id)) { + if (event.user.app_metadata && event.user.app_metadata.io_cb_license_key) { + const io_cb_license_key = event.user.app_metadata.io_cb_license_key; + + if (io_cb_license_key) { + api.idToken.setCustomClaim(`${CLAIM_NAMESPACE}/io_cb_license_key`, io_cb_license_key); + } + } + else { + // todo: replace with your form id + api.prompt.render('ap_dmiQhNpjgwtTJUnQopxjFB'); + } + } +}; + + +/** + * Handler that will be invoked when this action is resuming after an external redirect. If your + * onExecutePostLogin function does not perform a redirect, this function can be safely ignored. + * + * @param {Event} event - Details about the user and the context in which they are logging in. + * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login. + */ +exports.onContinuePostLogin = async (event, api) => { + if (CLIENT_IDS.includes(event.client.client_id)) { + const io_cb_license_key = event.prompt?.fields?.io_cb_license_key ?? (USE_SECRETS_LICENSE ? event.secrets['IO_CB_LICENSE_KEY'] : undefined); + + if (io_cb_license_key) { + api.user.setAppMetadata("io_cb_license_key", io_cb_license_key); + api.idToken.setCustomClaim(`${CLAIM_NAMESPACE}/io_cb_license_key`, io_cb_license_key); + } + else { + api.access.deny("io.Connect Browser License key required."); + } + } +}; diff --git a/office/auth0/interop_licenses.json b/office/auth0/interop_licenses.json new file mode 100644 index 0000000..6bee53e --- /dev/null +++ b/office/auth0/interop_licenses.json @@ -0,0 +1,63 @@ +{ + "version": "4.0.0", + "form": { + "name": "Interop Licenses", + "languages": { + "primary": "en" + }, + "nodes": [ + { + "id": "step_wiz0", + "type": "STEP", + "coordinates": { + "x": 500, + "y": 0 + }, + "alias": "Enter io.CB license key", + "config": { + "components": [ + { + "id": "io_cb_license_key", + "category": "FIELD", + "type": "PASSWORD", + "label": "io.Connect Browser License Key", + "required": false, + "sensitive": true, + "config": { + "hash": "NONE", + "complexity": false, + "nist": false, + "strength_meter": false + } + }, + { + "id": "next_button_RI80", + "category": "BLOCK", + "type": "NEXT_BUTTON", + "config": { + "text": "Continue" + } + } + ], + "next_node": "$ending" + } + } + ], + "start": { + "next_node": "step_wiz0", + "coordinates": { + "x": 0, + "y": 0 + } + }, + "ending": { + "resume_flow": true, + "coordinates": { + "x": 1250, + "y": 0 + } + } + }, + "flows": {}, + "connections": {} +} \ No newline at end of file diff --git a/office/auth0/readme.md b/office/auth0/readme.md new file mode 100644 index 0000000..b3af3b9 --- /dev/null +++ b/office/auth0/readme.md @@ -0,0 +1,205 @@ +# Auth0 Setup for io.Bridge Authentication + +When io.Bridge is configured with `IO_BRIDGE_SERVER_AUTH_TYPE=oauth2`, both the **desktop gateway** and the **browser platform** must authenticate with a Bearer token. This guide walks through the Auth0 setup required to make that work. + +## 1. Create an Auth0 Account + +1. Go to [https://auth0.com](https://auth0.com) and sign up for a free account. +2. During signup you will be asked to create a **tenant** — choose a name (e.g. `my-company`). This determines your Auth0 domain: `my-company.auth0.com` (or `my-company.eu.auth0.com` for EU tenants). +3. Once the dashboard loads, note your **Auth0 Domain** — you will need it for the `.env` configuration. + +## 2. Create an API (Audience) + +The API represents the resource that tokens will be issued for — in this case, **io.Bridge**. + +1. In the Auth0 Dashboard navigate to **Applications → APIs**. +2. Click **+ Create API**. +3. Fill in the details: + - **Name**: `io.Bridge` (display name — can be anything descriptive) + - **Identifier (Audience)**: `io.bridge` (this is the logical identifier, not a URL — it must match `IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_AUDIENCE` in your `.env`) + - **Signing Algorithm**: `RS256` (default) +4. Click **Create**. +5. Note the **Identifier** value — this is your **audience**. + +### Configuration mapping + +| Auth0 field | `.env` variable | +|---|---| +| Identifier (Audience) | `IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_AUDIENCE` / `VITE_AUTH0_AUDIENCE` | + +## 3. Create a Single-Page Application (SPA) + +The SPA application is used by both the **browser platform** (via `@auth0/auth0-react`) and the **desktop gateway** (via the implicit flow callback registered in `auth0/app.ts`). + +1. In the Auth0 Dashboard navigate to **Applications → Applications**. +2. Click **+ Create Application**. +3. Fill in the details: + - **Name**: `io.Connect Bridge Example` (or any descriptive name) + - **Application Type**: **Single Page Application** +4. Click **Create**. +5. Go to the **Settings** tab of the newly created application. +6. Note the **Client ID** and **Domain** — you will need them for `.env`. + +### 3.1. Configure Callback URLs and Web Origins + +Still on the **Settings** tab, scroll down to the **Application URIs** section and configure the following: + +#### Allowed Callback URLs + +These are the URLs Auth0 will redirect to after login. Add all URLs where the gateway server or the browser app might be running: + +``` +http://localhost:8385/login/oauth2/code/auth0, +http://localhost:5173, +https:// +``` + +- `http://localhost:8385/login/oauth2/code/auth0` — the desktop gateway's Auth0 callback endpoint (implicit flow with `response_mode=form_post`) +- `http://localhost:5173` — the Vite dev server (browser platform login) +- Add any additional deployed URLs (e.g. GitHub Pages) as needed + +#### Allowed Web Origins + +These origins are required for silent token renewal and CORS: + +``` +http://localhost:5173, +http://localhost:8385, +https:// +``` + +#### Allowed Logout URLs *(optional)* + +If you want logout to redirect back to the app: + +``` +http://localhost:5173, +https:// +``` + +7. Scroll down and click **Save Changes**. + +### Configuration mapping + +| Auth0 field | `.env` variable | +|---|---| +| Domain | `VITE_AUTH0_DOMAIN` | +| Client ID | `VITE_AUTH0_CLIENT_ID` | + +### 3.2. Create a Form for License Key Input + +Auth0 Forms let you collect additional information from users during the login flow. We use a form to prompt the user for their **io.Connect Browser license key**, which is then injected into the token by a post-login Action. + +1. In the Auth0 Dashboard navigate to **Actions → Forms**. +2. Click **+ Create Form** → **Import**. +3. Paste the contents of [`interop_licenses.json`](./interop_licenses.json) (included in this repository) and import it. +4. The form contains a single step with a **Password** field (`io_cb_license_key`) labelled *"io.Connect Browser License Key"* and a **Continue** button. +5. Click **Save** and **Publish** the form. + +> **Note:** The form field ID `io_cb_license_key` is important — it is referenced by the post-login Action below and maps to the token claim `https://interop.io/io_cb_license_key` that the browser platform reads. + +### 3.3. Create a Post-Login Action to Inject the License + +Auth0 Actions let you run custom logic during the login flow. We create a **Login / Post Login** action that: + +1. Only runs for specific applications (matched by **Client ID**). +2. On first login, renders the license key form (from step 3.2) to collect the key. +3. Stores the submitted license key in `user.app_metadata` so the user is not prompted again. +4. Injects the license key as a custom claim (`https://interop.io/io_cb_license_key`) in the **ID token** on every login. +5. Denies access if no license key is provided after the form. + +#### Steps + +1. In the Auth0 Dashboard navigate to **Actions → Library**. +2. Click **+ Create Action** → **Build from Scratch**. +3. Fill in: + - **Name**: `Inject io.Connect Browser License` (or any descriptive name) + - **Trigger**: **Login / Post Login** + - **Runtime**: Node 22 (or latest available) +4. Paste the contents of [`interop_licenses.js`](./interop_licenses.js) (included in this repository), or use the code below: + +```javascript +const CLAIM_NAMESPACE = 'https://interop.io'; +const USE_SECRETS_LICENSE = false; +// List client IDs for applications that need licenses. +const CLIENT_IDS = [ + '' // ← replace with your SPA's Client ID (from step 3) +]; + +exports.onExecutePostLogin = async (event, api) => { + if (CLIENT_IDS.includes(event.client.client_id)) { + if (event.user.app_metadata && event.user.app_metadata.io_cb_license_key) { + const io_cb_license_key = event.user.app_metadata.io_cb_license_key; + if (io_cb_license_key) { + api.idToken.setCustomClaim(`${CLAIM_NAMESPACE}/io_cb_license_key`, io_cb_license_key); + } + } else { + // First login — render the form to collect the license key. + api.prompt.render('FORM_ID'); // ← replace with your Form's ID + } + } +}; + +exports.onContinuePostLogin = async (event, api) => { + if (CLIENT_IDS.includes(event.client.client_id)) { + const io_cb_license_key = event.prompt?.fields?.io_cb_license_key + ?? (USE_SECRETS_LICENSE ? event.secrets['IO_CB_LICENSE_KEY'] : undefined); + + if (io_cb_license_key) { + api.user.setAppMetadata("io_cb_license_key", io_cb_license_key); + api.idToken.setCustomClaim(`${CLAIM_NAMESPACE}/io_cb_license_key`, io_cb_license_key); + } else { + api.access.deny("io.Connect Browser License key required."); + } + } +}; +``` + +5. Replace `'FORM_ID'` with the actual **Form ID** from the form created in step 3.2 (visible in **Actions → Forms → your form → Settings**). +6. Add your SPA's **Client ID** (from step 3) to the `CLIENT_IDS` array. This ensures the action only runs for your application. +7. *(Optional)* If you want to provide a fallback license via Auth0 Secrets instead of the form, set `USE_SECRETS_LICENSE = true` and add a secret named `IO_CB_LICENSE_KEY` in the action's **Secrets** tab. +8. Click **Deploy**. + +#### Add the Action to the Login Flow + +1. Navigate to **Actions → Flows → Login**. +2. Drag the **Inject io.Connect Browser License** action from the right panel into the flow, between **Start** and **Complete**. +3. Click **Apply**. + +#### How the browser platform reads the claim + +In `App.tsx`, the license key is extracted from the Auth0 user profile: + +```typescript +const licenseKey = userData.user["https://interop.io/io_cb_license_key"] ?? import.meta.env.VITE_IO_CB_LICENSE_KEY; +``` + +When Auth0 login is used, the license key comes from the ID token claim. When using simple login (no Auth0), it falls back to the `VITE_IO_CB_LICENSE_KEY` environment variable. + +## 4. Configure the `.env` File + +After completing the Auth0 setup, update your `.env` with the values from above: + +```dotenv +# io.Bridge authentication +IO_BRIDGE_SERVER_AUTH_TYPE=oauth2 +IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_ISSUERURI=https://.auth0.com/ +IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_AUDIENCE=io.bridge + +# Browser & gateway Auth0 settings +VITE_AUTH0_DOMAIN=.auth0.com +VITE_AUTH0_CLIENT_ID= +VITE_AUTH0_AUDIENCE=io.bridge +``` + +## How It Works + +On startup, the **desktop gateway** (`gateway-server.config.ts`) probes the bridge by sending `POST /api/nodes` with an empty JSON array: + +- **2xx response** → no authentication required; the gateway proceeds with the local OS username. +- **401 + `WWW-Authenticate: Bearer`** → authentication is required; the gateway registers the Auth0 login and callback routes (`/login/auth0` and `/login/oauth2/code/auth0`), then opens the login page in the system browser. After the user authenticates, Auth0 posts the tokens back to the callback endpoint. The received access token is then used for the mesh connection to io.Bridge. +- **Other / unreachable** → the gateway logs a warning and proceeds without a token (the mesh will retry once the bridge becomes available). + +The **browser platform** (`App.tsx`) uses `@auth0/auth0-react` with the `IOConnectHome` login type set to `"auth0"`, handling authentication entirely client-side. + + diff --git a/office/auth0/success.html b/office/auth0/success.html new file mode 100644 index 0000000..685b834 --- /dev/null +++ b/office/auth0/success.html @@ -0,0 +1,60 @@ + + + + + + Login Successful + + + + +
+
+

Login Successful

+

Welcome, ${username}!

+

This window will close in 3 seconds…

+
+ + + + diff --git a/office/gateway-server.config.ts b/office/gateway-server.config.ts new file mode 100644 index 0000000..bece0f2 --- /dev/null +++ b/office/gateway-server.config.ts @@ -0,0 +1,251 @@ +import { defineConfig } from "@interopio/gateway-server/config"; + +import { tmpdir } from "node:os"; +import opener from "opener"; + +import * as IOGatewayLogging from "@interopio/gateway/logging/core"; +import type { ServerConfigurer } from "@interopio/gateway-server/web/server"; + +import type { AuthInfo } from "./auth0/app.ts"; +import { app } from "./auth0/app.ts"; + +try { + process.loadEnvFile(); +} +catch (e) { + if (e.code === "ENOENT") { + // fine, no .env file + } + else { + throw e; + } +} + +const BRIDGE_URL = process.env["VITE_IO_BRIDGE_URL"] ?? "https://gw-bridge-examples.interop.io"; + +let authInfoResolver: (data: AuthInfo) => void; +const authInfoPromise: Promise = new Promise((resolve) => { + authInfoResolver = resolve; +}); + +const host = "127.0.0.1"; // accessible only locally for security reasons +const port = 8385; // same port as io.Connect Desktop's default gateway port +const IO_CD_ENV = "DEMO"; +const IO_CD_REGION = "INTEROP.IO"; + +const logger = IOGatewayLogging.getLogger("gateway.bridge"); + +// --------------------------------------------------------------------------- +// Probe io.Bridge to determine whether authentication is required. +// +// POST /api/nodes with body [] +// 2xx → no auth required +// 401 + WWW-Authenticate: Bearer → auth required +// other response / network error → unreachable / unknown +// +// Note: If the bridge uses custom/self-signed certificates, start the gateway +// server with `node --use-system-ca` so fetch trusts the system CA store. +// --------------------------------------------------------------------------- +async function probeBridgeAuth( + bridgeUrl: string, + token?: string, +): Promise<"none" | "required" | "unreachable"> { + const headers: Record = { + "Content-Type": "application/json", + }; + if (token) { + headers["Authorization"] = `Bearer ${token}`; + } + + try { + const response = await fetch(new URL("/api/nodes", bridgeUrl), { + method: "POST", + headers, + body: "[]", + signal: AbortSignal.timeout(5_000), + }); + if (response.ok) return "none"; + if ( + response.status === 401 && + response.headers.get("WWW-Authenticate")?.startsWith("Bearer") + ) { + return "required"; + } + return "unreachable"; + } catch { + return "unreachable"; + } +} + +export default defineConfig({ + host, + port, + cors: false, + auth: { + type: "none", + }, + + gateway: { + authentication: { + basic: { + usernameResolver: async (_login) => { + const { user } = await authInfoPromise; + return user; + }, + }, + }, + contexts: { + visibility: [ + { context: /___channel___.+/, restrictions: "cluster" }, + { context: /T42\..*/, restrictions: "local" }, + { context: /___workspace___.+/, restrictions: "local" }, + { context: /___window-hibernation___.+/, restrictions: "local" }, + { context: /___instance___.+/, restrictions: "local" }, + { context: /___window___.+/, restrictions: "local" }, + { restrictions: "cluster" }, + ], + }, + + methods: { + visibility: [ + { + identity: { application: "Outlook" }, + restrictions: "cluster", + }, + { + identity: { application: "IOXLAddin" }, + restrictions: "cluster", + }, + { method: /T42\..*/, restrictions: "local" }, + { restrictions: "local" }, + ], + }, + peers: { + visibility: [ + { domain: "context", restrictions: "cluster" }, + { domain: "interop", restrictions: "cluster" }, + { domain: "bus", restrictions: "local" }, + ], + }, + mesh: { + enabled: true, + auth: { + // auth0 config and data + }, + cluster: { + endpoint: BRIDGE_URL, + opts: { + getHeaders: async () => { + const headers: Record = {}; + const authInfo = await authInfoPromise; + if (authInfo.token) { + headers["Authorization"] = `Bearer ${authInfo.token}`; + } + return headers; + }, + }, + }, + }, + }, + + management: { + server: { + path: (() => { + if (process.platform === "win32") { + return `\\\\.\\pipe\\gateway-server-${IO_CD_ENV}-${IO_CD_REGION}-${process.env.USERNAME}`; + } else { + return `${tmpdir()}/gateway-server-${IO_CD_ENV}-${IO_CD_REGION}-${process.env.USER}.sock`; + } + })(), + }, + commands: { + shutdown: { + enabled: false, + }, + }, + }, + + // ----------------------------------------------------------------------- + // The `app` callback runs once the HTTP server is ready, so any routes we + // register here (e.g. the Auth0 callback) are live before we open the + // browser — solving the chicken-and-egg problem. + // ----------------------------------------------------------------------- + app: async (configurer: ServerConfigurer) => { + // 1. Probe the bridge to decide if authentication is needed. + const authResult = await probeBridgeAuth(BRIDGE_URL); + logger.info( + `io.Bridge auth probe: ${authResult} (${BRIDGE_URL})`, + ); + + // 2a. Bridge responded 2xx — no authentication required. + if (authResult === "none") { + const user = + process.env.USERNAME || process.env.USER || "dev-user"; + logger.info( + `Bridge does not require authentication — using local user "${user}"`, + ); + authInfoResolver({ user }); + return; + } + + // 2b. Auth required (or bridge unreachable — fall back to auth if + // configured, so mesh can connect once bridge becomes available). + const issuerUrl = process.env["IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_ISSUERURI"] ?? `https://${process.env["VITE_AUTH0_DOMAIN"]}`; + const clientId = process.env["VITE_AUTH0_CLIENT_ID"]; + const audience = process.env["IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_AUDIENCE"] ?? process.env["VITE_AUTH0_AUDIENCE"]; + + if (!issuerUrl || !clientId || !audience) { + if (authResult === "required") { + throw new Error( + "io.Bridge requires authentication but Auth0 is not configured. " + + "Set IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_ISSUERURI, VITE_AUTH0_CLIENT_ID, " + + "and IO_BRIDGE_SERVER_AUTH_OAUTH2_JWT_AUDIENCE in your .env file.", + ); + } + + // Bridge unreachable + no Auth0 config — proceed without a token; + // the mesh connection will retry on its own once the bridge is up. + const user = + process.env.USERNAME || process.env.USER || "dev-user"; + logger.warn( + `Bridge unreachable and Auth0 not configured — proceeding as "${user}" without token`, + ); + authInfoResolver({ user }); + return; + } + + // 3. Register Auth0 login & callback routes (must happen before we + // open the browser so the callback endpoint is ready). + await app( + { + logger: IOGatewayLogging.getLogger("gateway.bridge.auth0"), + authInfoResolver, + issuerUrl, + clientId, + audience, + scopes: process.env.VITE_AUTH0_SCOPES ?? "openid email", + }, + configurer, + ); + + // 4. Open the Auth0 login page in the default browser. + logger.info("Opening Auth0 login in system browser…"); + opener(`http://localhost:${port}/login/auth0`); + + // 5. (Optional) Once the token arrives, verify it works against the + // bridge so we get an early, clear log entry. + authInfoPromise.then(async (authInfo) => { + if (!authInfo.token) return; + const verification = await probeBridgeAuth(BRIDGE_URL, authInfo.token); + if (verification === "none") { + logger.info( + `Token verified — ioBridge accepted authentication for "${authInfo.user}"`, + ); + } else { + logger.warn( + `Token verification failed (${verification}) — io.Bridge may reject the mesh connection`, + ); + } + }); + }, +}); diff --git a/office/package-lock.json b/office/package-lock.json new file mode 100644 index 0000000..9ccb149 --- /dev/null +++ b/office/package-lock.json @@ -0,0 +1,3209 @@ +{ + "name": "@interopio/bridge-example-office", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@interopio/bridge-example-office", + "license": "MIT", + "dependencies": { + "@auth0/auth0-react": "^2.16.1", + "@interopio/browser": "file:../../connect-js/packages/browser", + "@interopio/browser-platform": "file:../../connect-js/packages/browser-platform", + "@interopio/core": "file:../../connect-js/packages/core", + "@interopio/home-ui-react": "^4.2.8-next.0", + "@interopio/modals-ui": "^4.2.8-next.0", + "@interopio/search-api": "file:../../connect-js/packages/search-api", + "@interopio/workspaces-ui-react": "^4.2.8-next.0", + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@interopio/bridge": "^1.1.2", + "@interopio/desktop": "^6.16.3", + "@interopio/gateway": "^0.25.1", + "@interopio/gateway-server": "^0.24.0", + "@types/node": "^24.12.0", + "@types/opener": "^1.4.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "concurrently": "^9.2.1", + "opener": "^1.5.2", + "rimraf": "^6.1.3", + "typescript": "^5.9.3", + "vite": "^8.0.3" + }, + "engines": { + "node": ">=24.0.0" + } + }, + "../../connect-js/packages/browser": { + "name": "@interopio/browser", + "version": "4.2.5", + "license": "MIT", + "dependencies": { + "@interopio/core": "^6.9.0", + "@interopio/desktop": "^6.14.0", + "@interopio/modals-api": "^4.2.8-next.0", + "@interopio/search-api": "^3.2.2", + "@interopio/workspaces-api": "^4.2.8-next.0", + "callback-registry": "^2.7.2" + }, + "devDependencies": { + "@interopio/utils": "^2.2.0" + }, + "peerDependencies": { + "@interopio/insights-base": "0.0.*", + "@interopio/insights-instrumentations-web": "0.0.*", + "@interopio/insights-logs": "0.0.*", + "@interopio/insights-metrics": "0.0.*", + "@interopio/insights-traces": "0.0.*" + }, + "peerDependenciesMeta": { + "@interopio/insights-base": { + "optional": true + }, + "@interopio/insights-instrumentations-web": { + "optional": true + }, + "@interopio/insights-logs": { + "optional": true + }, + "@interopio/insights-metrics": { + "optional": true + }, + "@interopio/insights-traces": { + "optional": true + } + } + }, + "../../connect-js/packages/browser-platform": { + "name": "@interopio/browser-platform", + "version": "4.2.5", + "license": "Commercial", + "dependencies": { + "@interopio/browser": "^4.2.5", + "@interopio/core": "^6.9.0", + "@interopio/desktop": "^6.14.0" + }, + "devDependencies": { + "@interopio/search-api": "^3.2.2", + "@interopio/utils": "^2.2.0" + } + }, + "../../connect-js/packages/core": { + "name": "@interopio/core", + "version": "6.9.0", + "license": "MIT", + "dependencies": { + "@interopio/otel": "0.0.*", + "callback-registry": "^2.7.2", + "ws": "^8.18.0" + }, + "peerDependencies": { + "@interopio/insights-base": "0.0.*", + "@interopio/insights-instrumentations-web": "0.0.*", + "@interopio/insights-logs": "0.0.*", + "@interopio/insights-metrics": "0.0.*", + "@interopio/insights-traces": "0.0.*" + }, + "peerDependenciesMeta": { + "@interopio/insights-base": { + "optional": true + }, + "@interopio/insights-instrumentations-web": { + "optional": true + }, + "@interopio/insights-logs": { + "optional": true + }, + "@interopio/insights-metrics": { + "optional": true + }, + "@interopio/insights-traces": { + "optional": true + } + } + }, + "../../connect-js/packages/search-api": { + "name": "@interopio/search-api", + "version": "3.2.2", + "license": "MIT", + "dependencies": { + "@interopio/core": "^6.9.0" + } + }, + "node_modules/@accessible/use-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@accessible/use-id/-/use-id-1.1.2.tgz", + "integrity": "sha512-0JcdE3a9WuR0hf8EfvoDDcKROOYEklSh9HN8nEQuAuyRQ7FrVYKDOz966nUiknzT4wy7MD4IkbdhkC4LqLhwHA==", + "license": "MIT", + "dependencies": { + "@react-hook/passive-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@auth0/auth0-auth-js": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-auth-js/-/auth0-auth-js-1.5.0.tgz", + "integrity": "sha512-shahG7sRw6VxduRvnNcmbgL8V42z+kNR9LDih9ofY4eM8ygt07RUh58x7S1M80ENx9wcMqYUsJWMaVudgQHyjQ==", + "license": "MIT", + "dependencies": { + "jose": "^6.0.8", + "openid-client": "^6.8.0" + } + }, + "node_modules/@auth0/auth0-react": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.16.1.tgz", + "integrity": "sha512-YT6ngVDh3MV5ey2zhzXVQdLN0gQQCGzvykGtQVGY49Bky/NCB/Os6sXPhqqdsoTB07umYY9Uu54GowqwUSO4sw==", + "license": "MIT", + "dependencies": { + "@auth0/auth0-spa-js": "^2.18.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17 || ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1", + "react-dom": "^16.11.0 || ^17 || ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.18.0.tgz", + "integrity": "sha512-2oKnn6cD4zQUWDfZ4bWWmLxEbOnxjMYakMFOJCJ5Z5OobVeOBtUTVkYKB8LaDksQrK3F41IRzcP97ffsqEUYEA==", + "license": "MIT", + "dependencies": { + "@auth0/auth0-auth-js": "1.5.0", + "browser-tabs-lock": "1.3.0", + "dpop": "2.1.1", + "es-cookie": "1.3.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", + "optional": true + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/serialize/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@interopio/bridge": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@interopio/bridge/-/bridge-1.1.2.tgz", + "integrity": "sha512-w+w6aQW8s5vmNcv8e3PbhCLqu/kFFt9MT3tn3sQTuSkCyqB8bCrDNS6tNQ/EG8B4haoXGqr0+W2ZzZfISYOe3A==", + "dev": true, + "license": "see license in license.md", + "dependencies": { + "@interopio/gateway-server": "^0.24.0", + "hrw-hash": "^2.0.3", + "jsrsasign": "^11.1.1", + "nanoid": "^5.1.7" + }, + "bin": { + "io-bridge": "bin/bridge.js" + }, + "engines": { + "node": "^20.18.0 || ^22.2.0 || >=24" + } + }, + "node_modules/@interopio/browser": { + "resolved": "../../connect-js/packages/browser", + "link": true + }, + "node_modules/@interopio/browser-platform": { + "resolved": "../../connect-js/packages/browser-platform", + "link": true + }, + "node_modules/@interopio/components-react": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/components-react/-/components-react-4.2.8-next.0.tgz", + "integrity": "sha512-J4wVUbheTRveVFKfX+IRlyiVs3FxP8wnZHUQEGuj6W5GeFmMNovjazoqosNXdbjUi2ckMBKZ8vj3PW3dUnHDrw==", + "license": "MIT", + "dependencies": { + "@accessible/use-id": "^1.1.2", + "@floating-ui/react": "^0.26.24", + "callback-registry": "^2.7.2", + "classnames": "^2.3.2", + "date-fns": "^4.1.0", + "decoder-validate": "^0.0.2", + "framer-motion": "^6.5.1", + "react-select": "^5.8.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@interopio/desktop": "^6.16.3", + "@interopio/react-hooks": "^4.2.8-next.0", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@interopio/react-hooks": { + "optional": true + } + } + }, + "node_modules/@interopio/core": { + "resolved": "../../connect-js/packages/core", + "link": true + }, + "node_modules/@interopio/desktop": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@interopio/desktop/-/desktop-6.18.0.tgz", + "integrity": "sha512-YqRHRiCymbY2fOCT2HHJnLY4780JSRI1fUtaWEoIa5Z3djZk/3xRgVxIjOVw9Y/TAFGCwyIeW142a7HYnSGD5Q==", + "license": "MIT", + "dependencies": { + "@interopio/core": "^6.8.1", + "@interopio/schemas": "^10.1.0", + "@interopio/utils": "^1.4.2", + "@interopio/workspaces-api": "^4.2.3", + "callback-registry": "^2.7.1" + } + }, + "node_modules/@interopio/desktop/node_modules/@interopio/workspaces-api": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@interopio/workspaces-api/-/workspaces-api-4.2.4.tgz", + "integrity": "sha512-v/KKkud+ip/FNBtecxFpzhHaoUUv3pAvH7Dk5q2s8Duxq8Wd6thzQjMbX7a6w+PKY3Vnmijx7jWrnhu6GLyuaA==", + "license": "MIT" + }, + "node_modules/@interopio/gateway": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@interopio/gateway/-/gateway-0.25.1.tgz", + "integrity": "sha512-dol92KBZsVVb/xzab48UFbeKhEfcCcP+prFg8p3L0LtLuPzzDUMuzB9cykJLbd3DYnPX0fml5XcQraH50qbSIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "antlr4": "4.13.2", + "immer": "^11.1.4", + "jsrsasign": "^11.1.1", + "serialize-error": "^13.0.1", + "type-fest": "^5.4.4" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "transit-js": "^0.8.874" + } + }, + "node_modules/@interopio/gateway-server": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@interopio/gateway-server/-/gateway-server-0.24.0.tgz", + "integrity": "sha512-ZjKL4ZTzsqYfsOODoe54KlGBRmYabnCP2gAXFU7rvgTuhYFrmMnvY5kSwbsYq1seXWGG94oZa74KXEB5Zm08zw==", + "dev": true, + "license": "see license in license.md", + "dependencies": { + "@interopio/gateway": "^0.25.1", + "http-cookie-agent": "^7.0.3", + "tough-cookie": "^6.0.1", + "ws": "^8.19.0" + }, + "bin": { + "gateway-server": "bin/gateway-server.cjs" + }, + "engines": { + "node": ">=20.18 || >=22.12 || >=24" + }, + "peerDependencies": { + "undici": "^7.24.4" + }, + "peerDependenciesMeta": { + "undici": { + "optional": true + } + } + }, + "node_modules/@interopio/home-ui-react": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/home-ui-react/-/home-ui-react-4.2.8-next.0.tgz", + "integrity": "sha512-Z0bq6VoT1xPliseq1EO66hzGuQYfzvOabI6cw/UMjMijEKXMbUcMX141OrXTOOHyXBYvkVl6g3Ns2sSuG2Pk6w==", + "license": "MIT", + "dependencies": { + "@interopio/components-react": "^4.2.8-next.0", + "decoder-validate": "^0.0.2", + "ua-parser-js": "^1.0.35" + }, + "peerDependencies": { + "@auth0/auth0-react": "^2.0.1", + "@interopio/browser": ">=3.0.0", + "@interopio/browser-platform": ">=3.0.0", + "@interopio/react-hooks": ">=4.2.8-next.0", + "@interopio/search-api": ">=3.0.0", + "@interopio/workspaces-api": ">=4.2.8-next.0", + "@interopio/workspaces-ui-react": ">=4.2.8-next.0", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@interopio/browser": { + "optional": true + }, + "@interopio/browser-platform": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@interopio/modals-api": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/modals-api/-/modals-api-4.2.8-next.0.tgz", + "integrity": "sha512-wB5vIeQDgEjZ+VXM3XLFHLW+EAeNPjAA6EZvWUkSm37n50iNQrDInqAq33pN/fqcNOSdFCVNbujR4/MRdcLP3A==", + "license": "MIT" + }, + "node_modules/@interopio/modals-ui": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/modals-ui/-/modals-ui-4.2.8-next.0.tgz", + "integrity": "sha512-w3OksIw8PqlB3b2kyfF7q0ZZ92moz8/jgF26YP7MEeKHeCaenjTL67hA7XSf0RutTlTsD+JIzOwyuXQemv9+6w==", + "dependencies": { + "@interopio/components-react": "^4.2.8-next.0", + "@interopio/modals-api": "^4.2.8-next.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@interopio/react-hooks": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/react-hooks/-/react-hooks-4.2.8-next.0.tgz", + "integrity": "sha512-4Mzv2xwzCVEvW4e9uM/JSQ0X4e2WKqUYaZ0a4OdNF4B3ZQpN1n6xhbSxmLfFeBBwa1YMW3tblZTjo73gIDS0Rg==", + "license": "MIT", + "peer": true, + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@interopio/browser": ">=3.0.0", + "@interopio/browser-platform": ">=3.0.0", + "@interopio/desktop": ">=5.0.0", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@interopio/browser": { + "optional": true + }, + "@interopio/browser-platform": { + "optional": true + }, + "@interopio/desktop": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@interopio/schemas": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@interopio/schemas/-/schemas-10.1.0.tgz", + "integrity": "sha512-8uDEoOAZenTr4a3O8vLq6JyS/LutkT/gh9EjuOrS9fOmHEUfJaS2u+qPo5em9/8MtjKgblZEJStSsW1gr34CtQ==", + "license": "ISC", + "dependencies": { + "ajv": "^6.12.6", + "ajv-keywords": "^3.4.1" + }, + "peerDependencies": { + "log4js": "^6.4.2" + }, + "peerDependenciesMeta": { + "log4js": { + "optional": true + } + } + }, + "node_modules/@interopio/search-api": { + "resolved": "../../connect-js/packages/search-api", + "link": true + }, + "node_modules/@interopio/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@interopio/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-14Bb2WI9Cwf9zjhTurP4qP9AVY4cxR1AOrwrOtHrTGAeeHp88mALFWfMEv1QnRhlQ06To2vQcRgebNrPlHDZ8Q==", + "license": "ISC", + "dependencies": { + "decoder-validate": "^0.0.2" + } + }, + "node_modules/@interopio/workspaces-api": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/workspaces-api/-/workspaces-api-4.2.8-next.0.tgz", + "integrity": "sha512-wTYhZmKm3G6X0iF7gd8iWwNbF53WIvIWNo6fuw+umWQh5Q9ULI0YREMVbGP8L00vi8364gb7E0otwqXv2Nu0/Q==", + "license": "MIT", + "peer": true + }, + "node_modules/@interopio/workspaces-ui-react": { + "version": "4.2.8-next.0", + "resolved": "https://registry.npmjs.org/@interopio/workspaces-ui-react/-/workspaces-ui-react-4.2.8-next.0.tgz", + "integrity": "sha512-7pLGs7p1/XiKToj1N9CZ25o98cglYmA+TcBbIIxWZlW8YEu0Ph++xEbINs7/akUqlfvhxYmt4rPaQzaiSLRofg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@interopio/react-hooks": ">=4.2.8-next.0", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@interopio/react-hooks": { + "optional": true + }, + "react": { + "autoinstall": false + }, + "react-dom": { + "autoinstall": false + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@motionone/animation": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", + "dependencies": { + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "license": "MIT", + "dependencies": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", + "dependencies": { + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT" + }, + "node_modules/@motionone/utils": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", + "dependencies": { + "@motionone/types": "^10.17.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@react-hook/passive-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz", + "integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/opener": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@types/opener/-/opener-1.4.3.tgz", + "integrity": "sha512-g7TYSmy2RKZkU3QT/9pMISrhVmQtMNaYq6Aojn3Y6pht29Nu9VuijJCYIjofRj7ZaFtKdxh1I8xf3vdW4l86fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.2.tgz", + "integrity": "sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browser-tabs-lock": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.3.0.tgz", + "integrity": "sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "lodash": ">=4.17.21" + } + }, + "node_modules/callback-registry": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/callback-registry/-/callback-registry-2.7.2.tgz", + "integrity": "sha512-VVrtF21DaH0VHeNMkLDd4VGuxsYM3V3l3lwYneKVMU/6X3TRtcQszUwlAcqj2HrLcbP1NyS12LsanUwCykaz/Q==", + "license": "ISC" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decoder-validate": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/decoder-validate/-/decoder-validate-0.0.2.tgz", + "integrity": "sha512-9BsqAH9Zq6CvlxKHkSrZrH2iYlhuhHcrh6uTnDvcsa9P5YEweEzt1ci+X/9STgSCE7b9BA7/QIiwhfUDDWmjxw==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dpop": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dpop/-/dpop-2.1.1.tgz", + "integrity": "sha512-J0Of2JTiM4h5si0tlbPQ/lkqfZ5wAEVkKYBhkwyyANnPJfWH4VsR5uIkZ+T+OSPIwDYUg1fbd5Mmodd25HjY1w==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-cookie": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz", + "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "license": "MIT", + "dependencies": { + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hrw-hash": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hrw-hash/-/hrw-hash-2.0.3.tgz", + "integrity": "sha512-Xkygc2lmTZyxWycx6fFkxDtfJMnauYFugwkWNnWFo0QbTexWtY6iVSm9n5nALrp3oGvoskwfXxD/YIMQDpsfOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.5.0" + } + }, + "node_modules/http-cookie-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-7.0.3.tgz", + "integrity": "sha512-EeZo7CGhfqPW6R006rJa4QtZZUpBygDa2HZH3DJqsTzTjyRE6foDBVQIv/pjVsxHC8z2GIdbB1Hvn9SRorP3WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/3846masa" + }, + "peerDependencies": { + "tough-cookie": "^4.0.0 || ^5.0.0 || ^6.0.0", + "undici": "^7.0.0" + }, + "peerDependenciesMeta": { + "undici": { + "optional": true + } + } + }, + "node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/jsrsasign": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-11.1.1.tgz", + "integrity": "sha512-6w95OOXH8DNeGxakqLndBEqqwQ6A70zGaky1oxfg8WVLWOnghTfJsc5Tknx+Z88MHSb1bGLcqQHImOF8Lk22XA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/kjur/jsrsasign#donations" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.7.tgz", + "integrity": "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/non-error": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/non-error/-/non-error-0.1.0.tgz", + "integrity": "sha512-TMB1uHiGsHRGv1uYclfhivcnf0/PdFp2pNqRxXjncaAsjYMoisaQJI+SSZCqRq+VliwRTC8tsMQfmrWjDMhkPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/oauth4webapi": { + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.5.tgz", + "integrity": "sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/openid-client": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.2.tgz", + "integrity": "sha512-uOvTCndr4udZsKihJ68H9bUICrriHdUVJ6Az+4Ns6cW55rwM5h0bjVIzDz2SxgOI84LKjFyjOFvERLzdTUROGA==", + "license": "MIT", + "dependencies": { + "jose": "^6.1.3", + "oauth4webapi": "^3.8.4" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "license": "MIT", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-select": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.2.tgz", + "integrity": "sha512-Z33nHdEFWq9tfnfVXaiM12rbJmk+QjFEztWLtmXqQhz6Al4UZZ9xc0wiatmGtUOCCnHN0WizL3tCMYRENX4rVQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-13.0.1.tgz", + "integrity": "sha512-bBZaRwLH9PN5HbLCjPId4dP5bNGEtumcErgOX952IsvOhVPrm3/AeK1y0UHA/QaPG701eg0yEnOKsCOC6X/kaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "non-error": "^0.1.0", + "type-fest": "^5.4.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "license": "MIT", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tldts": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.27" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/transit-js": { + "version": "0.8.874", + "resolved": "https://registry.npmjs.org/transit-js/-/transit-js-0.8.874.tgz", + "integrity": "sha512-IDJJGKRzUbJHmN0P15HBBa05nbKor3r2MmG6aSt0UxXIlJZZKcddTk67/U7WyAeW9Hv/VYI02IqLzolsC4sbPA==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", + "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/office/package.json b/office/package.json new file mode 100644 index 0000000..b38d0a6 --- /dev/null +++ b/office/package.json @@ -0,0 +1,60 @@ +{ + "name": "@interopio/bridge-example-office", + "private": true, + "description": "io.Bridge example — browser ↔ Office (Outlook, Excel) integration via io.Connect", + "type": "module", + "scripts": { + "build": "tsc && vite build", + "clean": "rimraf dist", + "start:bridge": "npx @interopio/bridge", + "start:gateway": "npx @interopio/gateway-server run", + "start:web": "vite --host", + "start": "concurrently \"npm:start:bridge\" \"npm:start:gateway\" \"npm:start:web\"", + "start:no-gateway": "concurrently \"npm:start:bridge\" \"npm:start:web\"", + "start:no-bridge": "concurrently \"npm:start:gateway\" \"npm:start:web\"", + "dev": "npm run start:web", + "preview": "vite preview" + }, + "keywords": [ + "io.bridge", + "office", + "outlook", + "excel", + "gateway", + "interop", + "example" + ], + "author": "InteropIO", + "license": "MIT", + "dependencies": { + "@auth0/auth0-react": "^2.16.1", + "@interopio/browser": "file:../../connect-js/packages/browser", + "@interopio/browser-platform": "file:../../connect-js/packages/browser-platform", + "@interopio/core": "file:../../connect-js/packages/core", + "@interopio/home-ui-react": "^4.2.8-next.0", + "@interopio/modals-ui": "^4.2.8-next.0", + "@interopio/search-api": "file:../../connect-js/packages/search-api", + "@interopio/workspaces-ui-react": "^4.2.8-next.0", + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@interopio/bridge": "^1.1.2", + "@interopio/desktop": "^6.16.3", + "@interopio/gateway": "^0.25.1", + "@interopio/gateway-server": "^0.24.0", + "@types/node": "^24.12.0", + "@types/opener": "^1.4.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "concurrently": "^9.2.1", + "opener": "^1.5.2", + "rimraf": "^6.1.3", + "typescript": "^5.9.3", + "vite": "^8.0.3" + }, + "engines": { + "node": ">=24.0.0" + } +} diff --git a/office/readme.md b/office/readme.md new file mode 100644 index 0000000..aa83701 --- /dev/null +++ b/office/readme.md @@ -0,0 +1,282 @@ +# Browser ↔ Office Integration via io.Bridge + +This example demonstrates how to integrate web applications with Microsoft Office (Outlook, Excel) using **io.Bridge** — the interoperability mesh that connects browser-based and desktop applications. + +## Overview + +A React web app hosted in **io.Connect Browser** communicates with Office add-ins running on the desktop — both connected to the same **io.Bridge** instance. io.Bridge can run locally (via the `@interopio/bridge` npm package) or be deployed in the cloud. The **desktop gateway server** (`@interopio/gateway-server`) must run locally on the user's machine so that Office add-ins can discover it via named pipe. The **browser platform** has an embedded gateway that is configured to connect to the same io.Bridge instance, forming a cluster where interop method calls (e.g. *create an email*, *push data to Excel*) travel seamlessly between browser and desktop. + +### Components + +| Component | Package / Source | Role | +|---|---|---| +| **io.Bridge** | `@interopio/bridge` | Interop mesh — routes messages between gateways (runs locally or in the cloud) | +| **Desktop Gateway** | `@interopio/gateway-server` | Runs locally; connects to io.Bridge; Office add-ins connect to it via WebSocket | +| **Web App** | `@interopio/home-ui-react` + `@interopio/browser-platform` | Browser platform with embedded gateway; connects to the same io.Bridge | +| **Outlook Add-in** | [outlook-v1.318.0.0](https://github.com/InteropIO/iocd-components/releases/download/outlook-v1.318.0.0-win32/outlook-v1.318.0.0-win32.zip) | .NET add-in with interop client; registers methods for email (e.g. `T42.Outlook.CreateEmail`) | +| **Excel Add-in** | [excel-v1.26.0320.1751](https://github.com/InteropIO/iocd-components/releases/download/excel-v1.26.0320.1751-win32/excel-v1.26.0320.1751-win32.zip) | .NET add-in with interop client; registers methods for spreadsheet operations | +| **Auth0** (optional) | `@auth0/auth0-react` | SSO authentication for bridge and browser | + +### Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Browser (HTTPS — GitHub Pages or localhost:5173) │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ Web App (@interopio/home-ui-react) │ │ +│ │ ├─ Embedded Gateway ────────────────────┐ │ │ +│ │ ├─ Outlook Demo (child app) │ │ │ +│ │ └─ Excel Playground (child app) │ │ │ +│ └──────────────────────────────────────────┼───────────────┘ │ +└─────────────────────────────────────────────┼──────────────────┘ + │ WSS + ▼ + ┌──────────────────────────────────────────┐ + │ io.Bridge (@interopio/bridge) │ + │ local: https://localhost:8084 │ + │ — or cloud-hosted — │ + └──────────────────────────────────────────┘ + ▲ WSS + │ +┌─────────────────────────────────────────────┼──────────────────┐ +│ Desktop (Windows — must run locally) │ │ +│ ┌──────────────────────────────────────────┼───────────────┐ │ +│ │ Gateway Server (@interopio/gateway-server) │ │ +│ │ ws://127.0.0.1:8385 │ │ │ +│ │ ├─ Mesh connection to io.Bridge ────────┘ │ │ +│ │ └─ Named pipe (optional add-in discovery) │ │ +│ └──────────────────────────────────────────┬───────────────┘ │ +│ ┌─────────────────────┼──────────┐ │ +│ │ WebSocket │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────┐ ┌──────────┐ ┌───────┐ │ +│ │ Outlook │ │ Excel │ │ ... │ │ +│ │ Add-in │ │ Add-in │ │ │ │ +│ └──────────┘ └──────────┘ └───────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Prerequisites + +- **Windows 10/11** +- **Node.js ≥ 24** +- **Microsoft Outlook** (2016+) and/or **Microsoft Excel** (2016+) +- **io.Bridge license key** (`IO_BRIDGE_LICENSE_KEY`) +- **io.Connect Browser license key** (`VITE_IO_CB_LICENSE_KEY`) +- *(Optional)* Auth0 tenant for SSO + +## Quick Start + +The demo web app is already deployed on GitHub Pages at https://interopio.github.io/bridge-examples/, pre-configured with Auth0 and connected to the cloud-hosted io.Bridge instance at `https://gw-bridge-examples.interop.io`. To try it out you only need to run a few things locally: + +### 1. Install Office Add-ins + +Download and install the add-ins. They are .NET apps with an embedded interop client that connects to the desktop gateway via WebSocket (optionally discovered via named pipe): + +```powershell +# Outlook +Invoke-WebRequest -Uri "https://github.com/InteropIO/iocd-components/releases/download/outlook-v1.318.0.0-win32/outlook-v1.318.0.0-win32.zip" -OutFile outlook.zip +Expand-Archive outlook.zip -DestinationPath .\outlook-addin +.\outlook-addin\GlueInstaller.cmd + +# Excel +Invoke-WebRequest -Uri "https://github.com/InteropIO/iocd-components/releases/download/excel-v1.26.0320.1751-win32/excel-v1.26.0320.1751-win32.zip" -OutFile excel.zip +Expand-Archive excel.zip -DestinationPath .\excel-addin +.\excel-addin\_install_gluexl.cmd +``` + +### 2. Start the desktop gateway + +```bash +cd office +npm install +npm run start:gateway +``` + +The gateway connects to the cloud-hosted io.Bridge and exposes a local WebSocket endpoint (`ws://127.0.0.1:8385`) for the Office add-ins. + +### 3. Open Outlook and/or Excel + +Launch Microsoft Outlook (and/or Excel). The installed add-ins will automatically connect to the desktop gateway. + +### 4. Open the demo web app + +Navigate to https://interopio.github.io/bridge-examples/. Log in via Auth0, then launch the **Outlook Demo** or **Excel Playground** child apps from the launcher. Emails sent from the browser will appear as drafts in Outlook. + +--- + +## Local Development + +If you want to run everything locally (including io.Bridge and the web app): + +### 1. Install dependencies + +```bash +cd office +npm install +``` + +### 2. Set up Auth0 + +Follow the [`auth0/readme.md`](./auth0/readme.md) guide to create your own Auth0 account, API (audience), SPA application, Form, and Post-Login Action. + +### 3. Configure environment + +Copy the example env file and fill in your license keys and Auth0 settings: + +```bash +cp .env.example .env +``` + +At minimum set: + +```dotenv +IO_BRIDGE_LICENSE_KEY= +VITE_IO_CB_LICENSE_KEY= + +# Auth0 (from step 2) +VITE_AUTH0_DOMAIN=.auth0.com +VITE_AUTH0_CLIENT_ID= +VITE_AUTH0_AUDIENCE=io.bridge +``` + +See [`.env.example`](.env.example) for the full list of options (CORS, logging, etc.). + +### 4. Start everything + +```bash +# Start io.Bridge + Gateway Server + Vite dev server +npm start +``` + +Or run components individually: + +| Script | What it starts | +|---|---| +| `npm run start:bridge` | io.Bridge on `https://localhost:8084` | +| `npm run start:gateway` | Desktop gateway on `ws://127.0.0.1:8385` | +| `npm run start:web` / `npm run dev` | Vite dev server on `http://localhost:5173` | +| `npm run start:no-gateway` | Bridge + Web (no desktop gateway) | +| `npm run start:no-bridge` | Gateway + Web (use remote bridge) | + +### 5. Open the browser and use + +Navigate to `http://localhost:5173`. Log in via Auth0, then launch the **Outlook Demo** or **Excel Playground** child apps from the launcher. + +## Project Structure + +``` +office/ +├── .env.example # All configurable environment variables +├── package.json # Scripts & dependencies +├── gateway-server.config.ts # Desktop gateway server configuration +├── vite.config.ts # Vite build config (multi-page: main + outlook) +├── auth0/ # Auth0 integration for gateway & license injection +│ ├── app.ts # Implicit-flow OAuth2 endpoints for gateway +│ ├── interop_licenses.json # Auth0 Form definition (license key prompt) +│ ├── interop_licenses.js # Auth0 Post-Login Action (injects license claim) +│ ├── readme.md # Auth0 setup guide +│ └── success.html # Post-login success page +├── src/ +│ ├── index.html # Main entry — io.Connect Browser platform +│ ├── main.tsx # React root +│ ├── App.tsx # IOConnectHome bootstrap & platform config +│ ├── config.json # Channels, environment +│ ├── index.css # Global reset +│ └── outlook/ # Outlook Demo child application +│ ├── index.html +│ ├── main.tsx # IOConnectProvider (browser client) +│ ├── OutlookApp.tsx # Email form + io.Connect interop +│ ├── OutlookApp.css # Outlook app styles +│ └── components/ +│ └── EmailForm.tsx # Email composition & T42.Outlook.CreateEmail invocation +└── public/ + └── static/modals/ # Modal UI assets (copied from @interopio/modals-ui) +``` + +## How It Works + +### Browser → Bridge → Desktop flow + +1. The **web app** initializes `@interopio/browser-platform` which starts an in-browser gateway and connects to io.Bridge via WebSocket. +2. The **desktop gateway** (`@interopio/gateway-server`) also connects to io.Bridge via its mesh configuration. +3. **Office add-ins** (Outlook, Excel) are .NET applications with an embedded interop client. They connect to the desktop gateway via **WebSocket** and can optionally discover it via a **named pipe**. Once connected, they register interop methods (e.g. `T42.Outlook.CreateEmail`). +4. The browser child app calls `io.interop.invoke("T42.Outlook.CreateEmail", { To, Subject, Body })` — the call travels through io.Bridge to the Outlook add-in, which creates a draft email in Outlook. + +### Method availability detection + +The Outlook Demo child app uses `io.interop.serverMethodAdded()` / `serverMethodRemoved()` to track whether the `T42.Outlook.CreateEmail` method is available. The **Send** button stays disabled until a server (Outlook add-in) registers the method, and the UI shows which machine the server is running on. + +### Visibility rules + +Both the browser platform config (`App.tsx`) and the desktop gateway config (`gateway-server.config.ts`) define **visibility rules** that control which interop methods, contexts, and peers are shared across the cluster vs. kept local: + +- Methods from `Outlook` and `IOXLAddin` identities → **cluster** (shared via bridge) +- Internal `T42.*` methods → **local** only +- Channel contexts → **cluster**; workspace/window contexts → **local** + +## Configuration Reference + +### Environment Variables + +| Variable | Required | Default | Description | +|---|---|---|---| +| **io.Bridge** |||| +| `IO_BRIDGE_LICENSE_KEY` | Yes | — | License key for io.Bridge server | +| `IO_BRIDGE_SERVER_PORT` | No | `8084` | Port for io.Bridge | +| `IO_BRIDGE_SERVER_HOST` | No | `0.0.0.0` | Bind address for io.Bridge | +| `IO_BRIDGE_SERVER_AUTH_TYPE` | No | `none` | Auth type: `none`, `basic`, `oauth2` | +| **Web App (Vite)** |||| +| `VITE_IO_CB_LICENSE_KEY` | If no Auth0 | — | License key for io.Connect Browser Platform (with Auth0, injected via token claim instead) | +| `VITE_IO_BRIDGE_URL` | No | `https://gw-bridge-examples.interop.io` | Bridge URL for browser and gateway connection | +| `VITE_AUTH0_DOMAIN` | If Auth0 | — | Auth0 tenant domain | +| `VITE_AUTH0_CLIENT_ID` | If Auth0 | — | Auth0 SPA client ID | +| `VITE_AUTH0_AUDIENCE` | If Auth0 | — | Auth0 API audience | +| **Logging** |||| +| `LOGGING_LEVEL` | No | `info` | Log level: `trace`, `debug`, `info`, `warn`, `error`, `off` | + +### Gateway Server (`gateway-server.config.ts`) + +The gateway server uses `defineConfig()` from `@interopio/gateway-server/config`. Key settings: + +- **`host`** / **`port`**: `127.0.0.1:8385` — only accessible locally +- **`gateway.mesh`**: Connects to io.Bridge at the configured URL +- **`management.server.path`**: Named pipe for gateway management (e.g. `\\.\pipe\gateway-server-DEMO-INTEROP.IO-`) +- **`gateway.methods.visibility`** / **`gateway.contexts.visibility`**: Control what's shared across the mesh + +**Auth detection:** On startup the gateway probes `POST /api/nodes` with `[]`. If io.Bridge responds 2xx, no authentication is needed and the gateway proceeds with the local OS username. If the response is 4xx/5xx (auth required), the gateway registers Auth0 routes and opens the login page in the system browser. After the token is received, it is optionally verified against the same endpoint before the mesh connection is established. + +### Browser Platform (`App.tsx`) + +Configured via `IOConnectHome` with `getIOConnectConfig` callback: + +- **`browserPlatform.config.gateway.bridge.url`**: Points to io.Bridge +- **`browserPlatform.config.gateway.bridge.interop.visibility`**: Only Outlook/Excel methods are shared cluster-wide +- **`browserPlatform.config.applications.local`**: Defines child apps (Outlook Demo, Excel Playground) + +## Auth0 Integration (Optional) + +When Auth0 is configured, both the browser and the desktop gateway authenticate via Auth0: + +- **Browser**: Uses `@auth0/auth0-react` — the `IOConnectHome` login type is set to `"auth0"` +- **Desktop Gateway**: Runs an implicit-flow OAuth2 endpoint (`/login/auth0`) that opens in the system browser; tokens are posted back to the gateway callback +- **License Injection**: An Auth0 post-login Action collects the io.Connect Browser license key (via a custom Form on first login) and injects it as a custom claim (`https://interop.io/io_cb_license_key`) in the ID token — removing the need to set `VITE_IO_CB_LICENSE_KEY` in the environment + +To enable, set all `VITE_AUTH0_*` and `IO_BRIDGE_SERVER_AUTH_*` variables in `.env`. For the full Auth0 setup walkthrough (tenant, API, SPA, Form, Action), see [`auth0/readme.md`](./auth0/readme.md). + +## Troubleshooting + +| Symptom | Cause | Fix | +|---|---|---| +| "Waiting for Outlook..." in the email form | Outlook add-in not connected | Ensure gateway server is running and Outlook add-in is installed | +| Bridge connection refused | Bridge not running or wrong port | Check `npm run start:bridge` and `VITE_IO_BRIDGE_URL` | +| "Missing user details" error | No `user` property in platform config | Ensure login flow returns user data | +| React error #525 on production build | Multiple React instances from linked packages | The `resolve.dedupe` and `resolve.alias` in `vite.config.ts` handle this | +| Auth0 login loop | Misconfigured callback URLs | Ensure Auth0 app has `http://localhost:5173` in Allowed Callback URLs | + +## Notes + +- **io.Bridge** can run locally (via `@interopio/bridge` npm package) or be deployed in the cloud. For production cluster deployment, see the [Docker](../docker/) examples. What **must** run locally is the **desktop gateway server** — Office add-ins connect to it via WebSocket (and can optionally discover it via named pipe). +- **io.Connect Browser** has an embedded gateway that connects to the same io.Bridge instance as the desktop gateway, forming a single interop cluster. +- The `index.css` provides a minimal global reset used by all pages; Outlook-specific styles are in `OutlookApp.css`. +- Some dependencies (`@interopio/browser`, `@interopio/core`, `@interopio/search-api`) use `file:` links to a local `connect-js` checkout for development. For standalone use, replace them with published npm versions. diff --git a/office/src/App.tsx b/office/src/App.tsx new file mode 100644 index 0000000..f378acd --- /dev/null +++ b/office/src/App.tsx @@ -0,0 +1,240 @@ +import IOBrowserPlatform from "@interopio/browser-platform"; +import { IOConnectHome, type UserData, type Auth0User } from "@interopio/home-ui-react"; +import type { IOConnectInitSettings } from "@interopio/react-hooks"; +import IOModals from "@interopio/modals-api"; +import IOSearch from "@interopio/search-api"; +import IOWorkspaces from "@interopio/workspaces-api"; +import "@interopio/workspaces-ui-react/dist/styles/workspaces.css"; +import "@interopio/home-ui-react/index.css"; +import config from "./config.json"; + +const appBaseUrl = new URL(import.meta.env.BASE_URL, window.location.origin); +const appBasePath = appBaseUrl.pathname; +const outlookAppUrl = new URL("outlook/", appBaseUrl).toString(); +const modalsBaseUrl = new URL("static/modals/", appBaseUrl); + +function requireNonEmptyString(value: string | undefined, name: string): string { + if (!value || value.trim() === "") { + throw new Error(`Environment variable ${name} is required and cannot be empty.`); + } + return value; +} + +const getConfig = (userData: UserData): IOConnectInitSettings => { + const bridgeUrl = import.meta.env.VITE_IO_BRIDGE_URL || "https://gw-bridge-examples.interop.io"; + const licenseKey = (userData.type === "auth0" ? userData.user["https://interop.io/io_cb_license_key"] as string : undefined) ?? import.meta.env.VITE_IO_CB_LICENSE_KEY as string; + + // Extract user details from login + const user: Auth0User = userData.user as Auth0User; + + // Platform configuration + return { + browserPlatform: { + factory: async (platformConfig) => { + const platformInit = await IOBrowserPlatform(platformConfig); + + (window as any).io = platformInit.io; + (window as any).platform = platformInit.platform; + + return platformInit; + }, + config: { + // License key from environment + licenseKey, + environment: config.environment, + applications: { + local: [ + { + name: "outlook-demo", + type: "window", + title: "Outlook Demo", + details: { + url: outlookAppUrl, + }, + customProperties: { + includeInWorkspaces: true + } + }, + { + name: "excel-playground", + type: "window", + "title": "Excel Playground", + details: { + url: "https://interopio.github.io/excel-playground/" + }, + customProperties: { + includeInWorkspaces: true + } + } + ] + }, + layouts: { + mode: "idb", + local: [{ + name: "Welcome", + type: "Global", + components: [{ + type: "workspaceFrame", + componentType: "application", + application: "workspaces-demo", + state: { + bounds: { + top: 0, + left: 0, + height: 0, + width: 0, + }, + context: { + isPlatform: true, + }, + instanceId: "g42-welcome", + selectedWorkspace: 0, + workspaces: [{ + children: [{ + type: "row", + config: {}, + children: [ + { + type: "group", + config: {}, + children: [{ + type: "window", + config: { + title: "Outlook", + appName: 'outlook-demo', + url: `${outlookAppUrl}` + } + }] + }, + { + type: "group", + config: {}, + children: [ + { + type: "window", + config: { + title: "Excel", + appName: "excel-playground", + url: "https://interopio.github.io/excel-playground/" + } + } + ] + }], + }], + config: { + name: "Office" + }, + context: {} + }] + } + }] + } + ] + }, + channels: { + definitions: config.channels + }, + browser: { + libraries: [IOWorkspaces, IOModals, IOSearch], + modals: { + dialogs: { + enabled: true + }, + alerts: { + enabled: true + } + }, + systemLogger: { + level: "warn" + }, + intentResolver: { + enable: true + } + }, + modals: { + sources: { + bundle: new URL("io-browser-modals-ui.es.js", modalsBaseUrl).toString(), + styles: [new URL("styles.css", modalsBaseUrl).toString()], + fonts: [new URL("fonts.css", modalsBaseUrl).toString()] + } + }, + + // Gateway configuration - connect to local io.Bridge + gateway: { + logging: { + level: "warn", + appender: (info) => { + console.log(`[${info.namespace}]: ${info.message}`); + }, + }, + bridge: { + url: bridgeUrl, + search: { + enabled: true, + }, + interop: { + enabled: true, + visibility: [ + { + restrictions: "cluster", + identity: { application: new RegExp(/(Outlook|IOXLAddin)/) } + }, + ] + }, + async getHeaders() { + const headers: Record = {}; + if (userData && userData.type === "auth0") { + headers["Authorization"] = `Bearer ${userData.user.token}`; + } + return headers; + }, + getWebSocketSearchParams() { + const params: Record = {}; + if (userData && userData.type === "auth0") { + params["access_token"] = `${userData.user.token}`; + } + return params; + } + } + }, + // User details required when connecting to io.Bridge + user: { + id: user.id, + username: user.name, + firstName: user.given_name, + lastName: user.family_name, + email: user.email + }, + // Workspaces App configuration + workspaces: { + src: appBasePath, + isFrame: true + } + } + } + }; +}; + +// Configuration for the IOConnectHome component +const homeConfig = { + getIOConnectConfig: getConfig, + // Auth0 authentication - users must log in via Auth0 + login: { + type: "auth0" as const, + providerOptions: { + domain: requireNonEmptyString(import.meta.env.VITE_AUTH0_DOMAIN, "VITE_AUTH0_DOMAIN"), + clientId: requireNonEmptyString(import.meta.env.VITE_AUTH0_CLIENT_ID, "VITE_AUTH0_CLIENT_ID"), + authorizationParams: { + audience: requireNonEmptyString(import.meta.env.VITE_AUTH0_AUDIENCE, "VITE_AUTH0_AUDIENCE"), + scope: import.meta.env.VITE_AUTH0_SCOPE ?? "openid profile email", + redirect_uri: appBaseUrl.toString(), + } + }, + } +}; + +function App() { + return ; +} + +export default App; diff --git a/office/src/config.json b/office/src/config.json new file mode 100644 index 0000000..1ea95a8 --- /dev/null +++ b/office/src/config.json @@ -0,0 +1,36 @@ +{ + "channels": [ + { + "name": "Red", + "meta": { + "color": "red", + "name": "Red", + "fdc3": { + "id": "fdc3.channel.1", + "displayMetadata": { + "name": "Channel 1", + "glyph": "1" + } + } + } + }, + { + "name": "Green", + "meta": { + "color": "green", + "name": "Green", + "fdc3": { + "id": "fdc3.channel.4", + "displayMetadata": { + "name": "Channel 4", + "glyph": "4" + } + } + } + } + ], + "environment": { + "environment": "DEMO", + "region": "INTEROP.IO" + } +} diff --git a/office/src/index.css b/office/src/index.css new file mode 100644 index 0000000..96ba8a3 --- /dev/null +++ b/office/src/index.css @@ -0,0 +1,5 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} diff --git a/office/src/index.html b/office/src/index.html new file mode 100644 index 0000000..da69f45 --- /dev/null +++ b/office/src/index.html @@ -0,0 +1,12 @@ + + + + + + io.Bridge Office Integration + + +
+ + + diff --git a/office/src/main.tsx b/office/src/main.tsx new file mode 100644 index 0000000..a253cbe --- /dev/null +++ b/office/src/main.tsx @@ -0,0 +1,7 @@ +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + +); diff --git a/office/src/outlook/OutlookApp.css b/office/src/outlook/OutlookApp.css new file mode 100644 index 0000000..764b3f3 --- /dev/null +++ b/office/src/outlook/OutlookApp.css @@ -0,0 +1,175 @@ +/* Outlook Child App Styles */ +.outlook-container { + max-width: 800px; + margin: 0 auto; + padding: 2rem; + min-height: 100vh; + display: flex; + flex-direction: column; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + line-height: 1.5; + color: #213547; + background-color: #f5f5f5; +} + +.outlook-header { + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 2px solid #e0e0e0; +} + +.outlook-header h1 { + font-size: 1.75rem; + margin-bottom: 0.5rem; + color: #0078d4; +} + +.outlook-header p { + color: #666; + font-size: 0.95rem; +} + +.outlook-main { + flex: 1; +} + +.outlook-container .loading { + display: flex; + justify-content: center; + align-items: center; + height: 200px; + font-size: 1.1rem; + color: #666; +} + +.outlook-container .error { + background-color: #fff5f5; + border: 1px solid #dc3545; + border-radius: 8px; + padding: 1.5rem; + color: #dc3545; +} + +.outlook-container .error h2 { + margin-bottom: 0.5rem; +} + +/* Email Form Styles */ +.email-form { + background: white; + padding: 1.5rem; + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.25rem; +} + +.form-group label { + display: block; + font-weight: 500; + margin-bottom: 0.5rem; + color: #333; +} + +.form-group input, +.form-group textarea { + width: 100%; + padding: 0.75rem; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; + font-family: inherit; + transition: border-color 0.2s, box-shadow 0.2s; +} + +.form-group input:focus, +.form-group textarea:focus { + outline: none; + border-color: #0078d4; + box-shadow: 0 0 0 3px rgba(0, 120, 212, 0.15); +} + +.form-group input:disabled, +.form-group textarea:disabled { + background-color: #f5f5f5; + cursor: not-allowed; +} + +.form-group textarea { + resize: vertical; + min-height: 150px; +} + +.form-actions { + margin-top: 1.5rem; +} + +.form-actions button { + width: 100%; + padding: 0.875rem 1.5rem; + background-color: #0078d4; + color: white; + border: none; + border-radius: 4px; + font-size: 1rem; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s; + font-family: inherit; +} + +.form-actions button:hover:not(:disabled) { + background-color: #005a9e; +} + +.form-actions button:disabled { + opacity: 0.7; + cursor: not-allowed; +} + +.form-actions button.success { + background-color: #28a745; +} + +.error-message { + margin-top: 1rem; + padding: 0.75rem 1rem; + background-color: #fff5f5; + border: 1px solid #dc3545; + border-radius: 4px; + color: #dc3545; + font-size: 0.9rem; +} + +.success-message { + margin-top: 1rem; + padding: 0.75rem 1rem; + background-color: #f0fff4; + border: 1px solid #28a745; + border-radius: 4px; + color: #28a745; + font-size: 0.9rem; +} + +.server-info { + margin-top: 1rem; + padding: 0.75rem 1rem; + background-color: #f0fff4; + border: 1px solid #28a745; + border-radius: 4px; + color: #28a745; + font-size: 0.9rem; +} + +.warning-message { + margin-top: 1rem; + padding: 0.75rem 1rem; + background-color: #fff8e1; + border: 1px solid #ffc107; + border-radius: 4px; + color: #856404; + font-size: 0.9rem; +} + diff --git a/office/src/outlook/OutlookApp.tsx b/office/src/outlook/OutlookApp.tsx new file mode 100644 index 0000000..1fed544 --- /dev/null +++ b/office/src/outlook/OutlookApp.tsx @@ -0,0 +1,92 @@ +import { useState, useEffect } from 'react'; +import { useIOConnect } from '@interopio/react-hooks'; +import EmailForm from './components/EmailForm'; +import './OutlookApp.css'; + +function getBrowserInfo(): string { + const ua = navigator.userAgent; + let browser = 'Unknown Browser'; + let version = ''; + + if (ua.includes('Firefox/')) { + browser = 'Firefox'; + version = ua.match(/Firefox\/([\d.]+)/)?.[1] || ''; + } else if (ua.includes('Edg/')) { + browser = 'Microsoft Edge'; + version = ua.match(/Edg\/([\d.]+)/)?.[1] || ''; + } else if (ua.includes('Chrome/')) { + browser = 'Chrome'; + version = ua.match(/Chrome\/([\d.]+)/)?.[1] || ''; + } else if (ua.includes('Safari/')) { + browser = 'Safari'; + version = ua.match(/Version\/([\d.]+)/)?.[1] || ''; + } + + return `${browser} ${version}`; +} + +function OutlookApp() { + // io is initialized by the provider and exposed via react-hooks helper + const io = useIOConnect((initializedIO) => initializedIO); + const [initialBody, setInitialBody] = useState(null); + + useEffect(() => { + if (!io) return; + + const loadProfileData = async () => { + const browserInfo = getBrowserInfo(); + + try { + // system.getProfileData is available in Browser platform + const system = (io as any).system; + if (system && typeof system.getProfileData === 'function') { + const profileData = await system.getProfileData(); + const platformVersion = profileData.productsInfo?.platform?.apiVersion || io.version; + + setInitialBody( + `This message is prepared from ${browserInfo} using io.Connect ${platformVersion} via Outlook.` + ); + } else { + // Fallback for Desktop or older versions + setInitialBody( + `This message is prepared from ${browserInfo} using io.Connect ${io.version} via Outlook.` + ); + } + } catch (err) { + // Fallback to io.version if getProfileData fails + setInitialBody( + `This message is prepared from ${browserInfo} using io.Connect ${io.version} via Outlook.` + ); + } + }; + + loadProfileData(); + }, [io]); + + if (!io || initialBody === null) { + return ( +
+
Connecting to io.Connect...
+
+ ); + } + + + return ( +
+
+

📧 Outlook Email Demo

+

Send emails via io.Bridge to Outlook

+
+
+ +
+
+ ); +} + +export default OutlookApp; diff --git a/office/src/outlook/components/EmailForm.tsx b/office/src/outlook/components/EmailForm.tsx new file mode 100644 index 0000000..1c9e21b --- /dev/null +++ b/office/src/outlook/components/EmailForm.tsx @@ -0,0 +1,209 @@ +import { useState, useEffect, useCallback } from 'react'; +import type { SyntheticEvent, ChangeEvent } from 'react'; +import type { IOConnectBrowser } from '@interopio/browser'; +import type { IOConnectDesktop } from '@interopio/desktop'; + +type IOApi = IOConnectBrowser.API | IOConnectDesktop.API; + +const OUTLOOK_METHOD = 'T42.Outlook.CreateEmail'; + +interface EmailFormProps { + io: IOApi; + initialSubject?: string; + initialBody?: string; +} + +interface EmailData { + to: string; + subject: string; + body: string; +} + +interface OutlookServerInfo { + available: boolean; + machine?: string; + application?: string; +} + +type SendStatus = 'idle' | 'sending' | 'success' | 'error'; + +export default function EmailForm({ io, initialSubject = '', initialBody = '' }: EmailFormProps) { + const [email, setEmail] = useState({ + to: '', + subject: initialSubject, + body: initialBody + }); + + const [outlookServer, setOutlookServer] = useState({ available: false }); + + // Check if the Outlook method is available and track server info + const updateOutlookMethodStatus = useCallback(() => { + const methods = io.interop.methods({ name: OUTLOOK_METHOD }); + if (methods.length > 0) { + // Get the first server that provides this method + const servers = methods[0].getServers?.() || []; + if (servers.length > 0) { + const server = servers[0]; + setOutlookServer({ + available: true, + machine: server.machine, + application: server.application + }); + return; + } + } + setOutlookServer({ available: false }); + }, [io]); + + useEffect(() => { + // Check initial state + updateOutlookMethodStatus(); + + // Subscribe to method added/removed events + const unsubAdded = io.interop.serverMethodAdded(({ method }) => { + if (method.name === OUTLOOK_METHOD) { + updateOutlookMethodStatus(); + } + }); + + const unsubRemoved = io.interop.serverMethodRemoved(({ method }) => { + if (method.name === OUTLOOK_METHOD) { + updateOutlookMethodStatus(); + } + }); + + return () => { + unsubAdded(); + unsubRemoved(); + }; + }, [io, updateOutlookMethodStatus]); + + // Sync initial values when props change (e.g., when initialBody is loaded async) + useEffect(() => { + setEmail(prev => ({ + ...prev, + subject: initialSubject, + body: initialBody + })); + }, [initialSubject, initialBody]); + const [status, setStatus] = useState('idle'); + const [errorMessage, setErrorMessage] = useState(''); + + const handleSubmit = async (e: SyntheticEvent) => { + e.preventDefault(); + setStatus('sending'); + setErrorMessage(''); + + try { + // Invoke the Outlook add-on's email method via interop + // T42.SendEmail accepts: To, Subject, Body, HTMLBody, Cc, Bcc, AttachFiles, SendFrom + await io.interop.invoke(OUTLOOK_METHOD, { + To: [email.to], + Subject: email.subject, + Body: email.body + }); + + setStatus('success'); + // Reset form after success + setTimeout(() => { + setEmail({ to: '', subject: initialSubject, body: initialBody }); + setStatus('idle'); + }, 3000); + } catch (err) { + setStatus('error'); + setErrorMessage(err instanceof Error ? err.message : 'Unknown error occurred'); + } + }; + + const handleChange = (field: keyof EmailData) => ( + e: ChangeEvent + ) => { + setEmail(prev => ({ ...prev, [field]: e.target.value })); + if (status === 'error') { + setStatus('idle'); + setErrorMessage(''); + } + }; + + return ( +
+
+ + +
+ +
+ + +
+ +
+ +