diff --git a/.changeset/better-near-auth-1-10-0-ephemeral-marker.md b/.changeset/better-near-auth-1-10-0-ephemeral-marker.md new file mode 100644 index 00000000..cb31e969 --- /dev/null +++ b/.changeset/better-near-auth-1-10-0-ephemeral-marker.md @@ -0,0 +1,5 @@ +--- +"everything-dev": patch +--- + +Bump `better-near-auth` from 1.9.0 to 1.10.0 across the catalog and pin it exactly. The new release adds an optional `ephemeral?: true` marker on `RelayerEphemeralConfig`; mark the SIWN relayer block in `bos.config.json → app.auth.variables.siwn` with that explicit flag while keeping the existing `whitelistedContracts`, `maxGasPerTransaction`, and `maxDepositPerTransaction` constraints. Runtime behavior is unchanged — the marker is documentation only and `getRelayerInfo().mode` still reports `"ephemeral"`. diff --git a/.changeset/bos-typecheck-command.md b/.changeset/bos-typecheck-command.md new file mode 100644 index 00000000..4d3420e8 --- /dev/null +++ b/.changeset/bos-typecheck-command.md @@ -0,0 +1,9 @@ +--- +"everything-dev": minor +--- + +Add a `bos typecheck` command that runs TypeScript type checking across all local workspaces (host, ui, api, auth, and every plugin with a `tsconfig.json`), streaming errors inline and failing with a non-zero exit if any workspace fails. Root `bun run typecheck` now delegates to it. + +- New `bos typecheck [packages]` aggregates pass/fail across all configured local workspaces instead of stopping at the first error. +- Framework source hardened for strict consumer configs (`noUncheckedIndexedAccess`): safe non-null assertions in `contract.ts`, `fastkv.ts`, and `api-contract.ts`. +- `plugins/apps` tsconfig aligned with the plugin template (`types: ["node"]`, DOM lib) so its typecheck passes. diff --git a/.changeset/curly-ghosts-sync.md b/.changeset/curly-ghosts-sync.md new file mode 100644 index 00000000..f11913c4 --- /dev/null +++ b/.changeset/curly-ghosts-sync.md @@ -0,0 +1,5 @@ +--- +"everything-dev": patch +--- + +Fix `bos sync` leaking parent-only root `package.json` scripts into child projects. When syncing the root package, scripts are now filtered to the child-appropriate set generated by `buildChildRootScripts`, so parent-specific commands (regression tests, etc.) no longer appear in child projects and child script values (e.g. `typecheck`) stay correct. diff --git a/.changeset/deploy-lock-and-infra-export.md b/.changeset/deploy-lock-and-infra-export.md new file mode 100644 index 00000000..8a435f5b --- /dev/null +++ b/.changeset/deploy-lock-and-infra-export.md @@ -0,0 +1,10 @@ +--- +"everything-dev": patch +--- + +Remove deploy lock feature and add `bos infra export` command. + +- Removed `bos deploy lock acquire/release/inspect` commands and FastKV-backed deploy lock logic. Concurrent deploys now follow last-write-wins semantics (harmless redundancy for Railway redeploy). +- `bos infra export [--target ci|local] [--network mainnet|testnet]` emits `{env, services, account, gateway, project, generatedAt}` JSON. The deploy workflow consumes this to populate `$GITHUB_ENV` instead of repeating `API_DATABASE_URL`, `AUTH_DATABASE_URL`, and `CORS_ORIGIN` literals. Host port comes from `BOS_CI_HOST_PORT` env or `runtimeConfig.host.port`. +- `buildOriginMap` now derives plugin origins from `runtimeConfig.plugins[id].extendsRef` / `runtimeConfig.auth?.extendsRef` (already populated by `loadResolvedConfig`), removing the duplicate raw-JSON read and the parent-runtime fallback logic. +- New tests cover the CI plan builder and the resolved-config origin lookup. diff --git a/.changeset/four-bags-hear.md b/.changeset/four-bags-hear.md new file mode 100644 index 00000000..c6db30fd --- /dev/null +++ b/.changeset/four-bags-hear.md @@ -0,0 +1,4 @@ +--- +--- + +Add a `backcompat` regression test mode (`bun run test:regression:backcompat`) that boots a local host while loading the last published UI/API/plugin bundles via Module Federation, verifying the new host works against existing published bundles. Regression commands (`dev`, `prod`, `backcompat`) now run both HTTP and browser suites each time instead of stopping when the HTTP suite fails. diff --git a/.changeset/siwn-ephemeral-relayer.md b/.changeset/siwn-ephemeral-relayer.md new file mode 100644 index 00000000..afd14429 --- /dev/null +++ b/.changeset/siwn-ephemeral-relayer.md @@ -0,0 +1,8 @@ +--- +--- + +Confirm the SIWN auth relayer is in `RelayerEphemeralConfig` ("Ephemeral with settings") mode: a rich-object `relayer` block in `bos.config.json → app.auth.variables.siwn` with `whitelistedContracts`, `maxGasPerTransaction`, and `maxDepositPerTransaction` and no `accountId` / `privateKey`. better-near-auth 1.9.0's `initRelayer` resolves this to an auto-generated ED25519 keypair on first startup, encrypted with `BETTER_AUTH_SECRET` (HKDF-SHA256 → AES-256-GCM) and persisted in the `relayerKey` table. + +The vestigial `NEAR_RELAYER_PRIVATE_KEY` line is removed from `.env.example` in favor of an inline comment pointing operators at `/admin/relayer` (which surfaces a "needs funding" prompt using `getRelayerInfo().enabled === false` once the auto-generated implicit account has zero balance). Operators funding the implicit account via `authClient.near.getNearClient().transfer()` enables relay without ever leaving the existing ephemeral-mode config. + +AGENTS.md gains a "SIWN Auth Relayer" subsection under "Common Patterns" documenting the operational rules (funding flow, parent-key requirement for sub-account creation, why the implicit relayer account can't own sub-accounts, and the path back to `RelayerExplicitConfig` if a named-account relayer is needed). diff --git a/.changeset/ui-anon-orgs.md b/.changeset/ui-anon-orgs.md new file mode 100644 index 00000000..d1e2a805 --- /dev/null +++ b/.changeset/ui-anon-orgs.md @@ -0,0 +1,9 @@ +--- +"ui": minor +--- + +Add a dedicated anonymous mount and reorganize organization routes. + +- Add a `/_layout/_anon` pathless layout for pre-auth pages. Move login from the public layout into it; the layout redirects authenticated users to `/dashboard` and provides the theme toggle header. +- Rename the organization route group from `/organizations` to `/orgs` (`/orgs`, `/orgs/new`, `/orgs/$slug`) and move invitation acceptance to `/orgs/invites/$id`. +- Remove the stale nostr entry from the authenticated sidebar. \ No newline at end of file diff --git a/.changeset/ui-layout-mounts.md b/.changeset/ui-layout-mounts.md new file mode 100644 index 00000000..b9e8dd19 --- /dev/null +++ b/.changeset/ui-layout-mounts.md @@ -0,0 +1,9 @@ +--- +"ui": minor +--- + +Reorganize UI routes into mount-point layouts and rename the authenticated workspace. + +- Move admin routes under a new `/_layout/_admin` pathless layout that gates on the admin role and redirects non-admins to `/dashboard`. The tenant admin dashboard (`admin/admin/index.tsx`) and system page (`admin/admin/system.tsx`) now render as children of the admin layout through an `Outlet`. +- Rename the authenticated `/home` route to `/dashboard`, updating the sidebar, mobile tab bar, user nav, and login redirect fallbacks. +- Move the apps and things routes under the public layout (`_layout/_public/apps`, `_layout/_public/things`) so they render inside the shared public shell instead of the top-level layout. diff --git a/.env.example b/.env.example index 450772d4..379979f4 100644 --- a/.env.example +++ b/.env.example @@ -2,16 +2,15 @@ # Update values as needed for your local environment # app.host -CORS_ORIGIN=http://localhost:4100 -TENANT_WHITELIST= -ALLOW_OVERRIDE= -ALLOW_UNTRUSTED_SSR= +CORS_ORIGIN=http://localhost:3000 CSP_STRICT= # app.api API_DATABASE_URL=postgres://everythingdev:everythingdev@localhost:5432/api_db # app.auth +NEAR_SUB_ACCOUNT_PARENT_KEY_MAINNET= +NEAR_SUB_ACCOUNT_PARENT_KEY_TESTNET= AUTH_DATABASE_URL=postgres://everythingdev:everythingdev@localhost:5433/auth_db BETTER_AUTH_SECRET= GITHUB_CLIENT_SECRET= @@ -22,9 +21,4 @@ TWILIO_AUTH_TOKEN= TWILIO_PHONE_NUMBER= RESEND_API_KEY= NEAR_RELAYER_PRIVATE_KEY= -NEAR_SUB_ACCOUNT_PARENT_KEY_MAINNET= -NEAR_SUB_ACCOUNT_PARENT_KEY_TESTNET= - -# plugins.template -TEMPLATE_DATABASE_URL=postgres://everythingdev:everythingdev@localhost:5434/template_db diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 300ff4e6..d5d82faf 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -32,9 +32,7 @@ jobs: ZE_USER_EMAIL: ${{ secrets.ZEPHYR_USER_EMAIL }} RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} TARGET_BRANCH: ${{ github.ref_name }} - API_DATABASE_URL: postgres://everythingdev:everythingdev@127.0.0.1:5432/api_db - AUTH_DATABASE_URL: postgres://everythingdev:everythingdev@127.0.0.1:5433/auth_db - CORS_ORIGIN: http://127.0.0.1:4100 + BOS_CI_HOST_PORT: "4100" services: postgres-api: image: postgres:17-alpine @@ -95,6 +93,14 @@ jobs: run: | echo "ZE_CI_TOKEN=$ZEPHYR_CI_TOKEN" >> "$GITHUB_ENV" + - name: Emit CI infra plan + id: infra + run: | + bun run bos infra export --target ci --network mainnet > .ci-infra.json + jq -r '.env | to_entries[] | "\(.key)=\(.value)"' .ci-infra.json >> "$GITHUB_ENV" + echo "## CI infra plan" >> "$GITHUB_STEP_SUMMARY" + jq -r '"Services: " + (.services | length | tostring) + "\nEnv vars: " + (.env | keys | length | tostring)' .ci-infra.json >> "$GITHUB_STEP_SUMMARY" + - name: Publish with deploy timeout-minutes: 20 run: bun run bos publish --deploy${{ inputs.verbose == true && ' --verbose' || '' }} diff --git a/AGENTS.md b/AGENTS.md index 8453be7f..8a174c20 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -168,7 +168,7 @@ Current fixed-core host rules: - the shared host still boots once from one base runtime snapshot - child runtime config must extend the active BOS runtime - supported request-scoped overrides are `ui` and existing `plugins..ui` -- tenant SSR is gated by `TENANT_WHITELIST` and `ALLOW_UNTRUSTED_SSR` +- tenant SSR is gated per-tenant by the `allowSsr` column on the tenant record; the host's BindingResolver reads permissions from the API's `GET /tenants/bindings` endpoint (cached for 30s) - nested label routing and account-relative tenant derivation are the intended architecture direction, but not the complete resolver behavior today For full per-request host/plugin/auth/api swapping, start from `plans/runtime-config-hot-swap.md`. @@ -351,6 +351,21 @@ const { runtimeConfig } = Route.useLoaderData(); const appName = getActiveRuntime(runtimeConfig)?.title ?? getAccount(runtimeConfig); ``` +### SIWN Auth Relayer (gasless NEP-366 relay) + +The auth plugin's `siwn({ relayer: ... })` block in `bos.config.json → app.auth.variables.siwn` is **ephemeral mode** — the rich-object shape with `whitelistedContracts`, `maxGasPerTransaction`, and `maxDepositPerTransaction` but no `accountId` / `privateKey`. From the better-near-auth skill: that's `RelayerEphemeralConfig` ("Ephemeral with settings"). + +**Operational rules:** + +- On first startup the server generates an ED25519 keypair per network, derives an implicit hex account from the public key, and encrypts the private key with `BETTER_AUTH_SECRET` (HKDF-SHA256 → AES-256-GCM) into the `relayerKey` table. Same keypair recovers on every restart. +- After first startup the server logs the implicit account id. **Fund that account with NEAR** to enable relay — otherwise every relay attempt fails with insufficient balance from the RPC. +- Funding workflow: admins hit `getRelayerInfo`; the `/admin/relayer` page surfaces a "needs funding" banner on `/admin` when `enabled === false` and `accountId` is set, then the admin's connected wallet transfers NEAR to the implicit account via `authClient.near.getNearClient().transfer()`. +- The implicit relayer account is *not* a `.near` named account, so it cannot own sub-accounts. `siwn.subAccount.parentAccount` must be a named account (this project uses `v1.citynode.near` / `v1.citynode.testnet`), and the parent key is supplied via `NEAR_SUB_ACCOUNT_PARENT_KEY_MAINNET` / `NEAR_SUB_ACCOUNT_PARENT_KEY_TESTNET` secrets. Without the parent key the sub-account endpoint explains why in the error message and returns a `not-configured` reason. +- `NEAR_RELAYER_PRIVATE_KEY` is vestigial in ephemeral mode and is omitted from `.env.example`. Only reintroduce (plus explicit `relayer: { accountId, privateKey }`) when moving to `RelayerExplicitConfig`. +- The mode is observable at runtime: `getRelayerInfo()` returns `{ accountId, mode: "ephemeral", publicKey, balance, enabled }`. + +To switch to `RelayerExplicitConfig`, replace the rich-object shape with `relayer: { accountId: "relayer..near", privateKey: process.env.RELAYER_PRIVATE_KEY, whitelistedContracts: [...], maxGasPerTransaction: "...", maxDepositPerTransaction: "0" }` and re-add the env var. The ephemeral key in the `relayerKey` table is ignored once an explicit key is provided. + ## Security ### Shared Singleton Trust Model diff --git a/LLM.txt b/LLM.txt index 3b5f2820..5eb2fff3 100644 --- a/LLM.txt +++ b/LLM.txt @@ -48,7 +48,7 @@ Practical consequence: - Path-based runtime metadata works today. - Shared-host tenant UI mode now works today: the host can resolve a tenant config per request, enforce that it extends the base BOS runtime, and apply request-scoped UI overrides while keeping auth/API/plugin routers fixed. - Supported tenant overrides today are `app.ui`, existing `plugins..ui`, and existing `plugins..sidebar`. -- Tenant SSR is gated by `TENANT_WHITELIST` and `ALLOW_UNTRUSTED_SSR`. +- Tenant SSR is gated per-tenant by the `allow_ssr` column on the tenant record; the host's BindingResolver reads these permissions from the API's `GET /tenants/bindings` endpoint (cached for 30s). - True wildcard-domain full runtime swapping for host/plugin/auth/api still needs dynamic scoped app rebuilding in the host. - The active design doc for that work is `plans/runtime-config-hot-swap.md`. diff --git a/README.md b/README.md index 25b62e71..cf47c93f 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ What works today: - `app.ui` - existing `plugins..ui` - existing `plugins..sidebar` -- Tenant SSR is gated by `TENANT_WHITELIST` and `ALLOW_UNTRUSTED_SSR`. +- Tenant SSR is gated per-tenant by the `allow_ssr` column on the tenant record; the host's BindingResolver reads these permissions from the API's `GET /tenants/bindings` endpoint (cached for 30s). Design direction: - nested labels compose onto the active runtime account, such as `chicago.pizza.com -> bos://chicago.pizza.pingpayio.near/pizza.com` diff --git a/api/src/contract.ts b/api/src/contract.ts index 5887408c..d08e97b4 100644 --- a/api/src/contract.ts +++ b/api/src/contract.ts @@ -17,9 +17,12 @@ export const TenantSchema = z.object({ id: z.string(), subdomain: z.string(), accountId: z.string(), - orgId: z.string(), + orgId: z.string().nullable(), name: z.string(), status: TenantStatusSchema, + allowUiOverrides: z.boolean(), + allowBackendOverrides: z.boolean(), + allowSsr: z.boolean(), createdAt: z.string(), updatedAt: z.string(), deletedAt: z.string().nullable(), @@ -27,6 +30,32 @@ export const TenantSchema = z.object({ export type Tenant = z.infer; +export const TenantBindingSchema = z.object({ + hostname: z + .string() + .describe("Subdomain hostname that routes to this tenant on the parent domain"), + accountId: z.string(), + allowUiOverrides: z.boolean(), + allowBackendOverrides: z.boolean(), + allowSsr: z.boolean(), + status: TenantStatusSchema, +}); + +export const CityNodeSchema = z.object({ + id: z.string(), + tenantId: z.string(), + orgId: z.string(), + validatorPool: z.string(), + hostname: z.string(), + accountId: z.string(), + name: z.string(), + tenantStatus: z.string(), + createdAt: z.string(), + updatedAt: z.string(), +}); + +export type CityNode = z.infer; + const ThingSchema = z.object({ thingId: z.string().describe("Unique identifier for the thing"), type: z.string().describe("Plugin-derived thing type"), @@ -80,6 +109,9 @@ export const contract = oc.router({ name: z.string(), accountId: z.string(), status: z.enum(["active", "pending"]).optional(), + allowUiOverrides: z.boolean().default(true), + allowBackendOverrides: z.boolean().default(false), + allowSsr: z.boolean().default(false), }), ) .output(TenantSchema) @@ -94,6 +126,9 @@ export const contract = oc.router({ subdomain: z.string().optional(), accountId: z.string().optional(), status: TenantStatusSchema.optional(), + allowUiOverrides: z.boolean().optional(), + allowBackendOverrides: z.boolean().optional(), + allowSsr: z.boolean().optional(), }), ) .output(TenantSchema) @@ -128,6 +163,16 @@ export const contract = oc.router({ .output(TenantSchema) .errors({ NOT_FOUND }), + listTenantBindings: oc + .route({ + method: "GET", + path: "/tenants/bindings", + summary: "List all active tenant domain bindings", + description: + "Public — returns the subdomain-to-config mapping used by the host's BindingResolver.", + }) + .output(z.array(TenantBindingSchema)), + tenantPreflight: oc .route({ method: "POST", path: "/tenants/preflight" }) .input( @@ -150,6 +195,53 @@ export const contract = oc.router({ ) .errors({ UNAUTHORIZED, BAD_REQUEST }), + listCityNodes: oc + .route({ + method: "GET", + path: "/citynodes", + summary: "List all city nodes", + description: "Public — returns all city nodes with their linked tenant bindings.", + }) + .output(z.array(CityNodeSchema)), + + resolveCityNode: oc + .route({ + method: "GET", + path: "/citynodes/resolve", + summary: "Resolve a city node by tenant account ID", + description: "Public — returns the city node for a tenant account (used by the stake route).", + }) + .input(z.object({ accountId: z.string() })) + .output(CityNodeSchema.nullable()), + + createCityNode: oc + .route({ method: "POST", path: "/citynodes" }) + .input( + z.object({ + tenantId: z.string(), + validatorPool: z.string(), + }), + ) + .output(CityNodeSchema) + .errors({ UNAUTHORIZED, FORBIDDEN, BAD_REQUEST }), + + updateCityNode: oc + .route({ method: "PATCH", path: "/citynodes/{cityNodeId}" }) + .input( + z.object({ + cityNodeId: z.string(), + validatorPool: z.string().optional(), + }), + ) + .output(CityNodeSchema) + .errors({ UNAUTHORIZED, FORBIDDEN, NOT_FOUND, BAD_REQUEST }), + + deleteCityNode: oc + .route({ method: "POST", path: "/citynodes/{cityNodeId}/delete" }) + .input(z.object({ cityNodeId: z.string() })) + .output(z.object({ success: z.literal(true) })) + .errors({ UNAUTHORIZED, FORBIDDEN, NOT_FOUND }), + createThing: oc .route({ method: "POST", diff --git a/api/src/db/migrations/0002_awesome_madame_web.sql b/api/src/db/migrations/0002_awesome_madame_web.sql new file mode 100644 index 00000000..e39a3327 --- /dev/null +++ b/api/src/db/migrations/0002_awesome_madame_web.sql @@ -0,0 +1,3 @@ +ALTER TABLE "tenants" ADD COLUMN "allow_ui_overrides" boolean DEFAULT true NOT NULL;--> statement-breakpoint +ALTER TABLE "tenants" ADD COLUMN "allow_backend_overrides" boolean DEFAULT false NOT NULL;--> statement-breakpoint +ALTER TABLE "tenants" ADD COLUMN "allow_ssr" boolean DEFAULT false NOT NULL; \ No newline at end of file diff --git a/api/src/db/migrations/0003_brief_freak.sql b/api/src/db/migrations/0003_brief_freak.sql new file mode 100644 index 00000000..1a294453 --- /dev/null +++ b/api/src/db/migrations/0003_brief_freak.sql @@ -0,0 +1 @@ +ALTER TABLE "tenants" ALTER COLUMN "org_id" DROP NOT NULL; \ No newline at end of file diff --git a/api/src/db/migrations/0004_majestic_jamie_braddock.sql b/api/src/db/migrations/0004_majestic_jamie_braddock.sql new file mode 100644 index 00000000..a5d98b95 --- /dev/null +++ b/api/src/db/migrations/0004_majestic_jamie_braddock.sql @@ -0,0 +1,11 @@ +CREATE TABLE "citynodes" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tenant_id" uuid NOT NULL, + "org_id" text NOT NULL, + "validator_pool" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "citynodes_tenant_id_unique" UNIQUE("tenant_id") +); +--> statement-breakpoint +ALTER TABLE "citynodes" ADD CONSTRAINT "citynodes_tenant_id_tenants_id_fk" FOREIGN KEY ("tenant_id") REFERENCES "public"."tenants"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/api/src/db/migrations/meta/0002_snapshot.json b/api/src/db/migrations/meta/0002_snapshot.json new file mode 100644 index 00000000..4351ee45 --- /dev/null +++ b/api/src/db/migrations/meta/0002_snapshot.json @@ -0,0 +1,165 @@ +{ + "id": "7b8a421a-d0c7-414a-96e8-0c259935178b", + "prevId": "3e88c17e-53d5-4809-b872-33d2f87a8a90", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.tenants": { + "name": "tenants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subdomain": { + "name": "subdomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "tenant_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "allow_ui_overrides": { + "name": "allow_ui_overrides", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_backend_overrides": { + "name": "allow_backend_overrides", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_ssr": { + "name": "allow_ssr", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "tenants_subdomain_idx": { + "name": "tenants_subdomain_idx", + "columns": [ + { + "expression": "subdomain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_account_id_idx": { + "name": "tenants_account_id_idx", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tenants_subdomain_unique": { + "name": "tenants_subdomain_unique", + "nullsNotDistinct": false, + "columns": ["subdomain"] + }, + "tenants_account_id_unique": { + "name": "tenants_account_id_unique", + "nullsNotDistinct": false, + "columns": ["account_id"] + }, + "tenants_org_id_unique": { + "name": "tenants_org_id_unique", + "nullsNotDistinct": false, + "columns": ["org_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.tenant_status": { + "name": "tenant_status", + "schema": "public", + "values": ["active", "pending", "suspended", "pending_deletion"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/api/src/db/migrations/meta/0003_snapshot.json b/api/src/db/migrations/meta/0003_snapshot.json new file mode 100644 index 00000000..b013ab2b --- /dev/null +++ b/api/src/db/migrations/meta/0003_snapshot.json @@ -0,0 +1,165 @@ +{ + "id": "85c9d130-a4b6-4026-a2df-890fddfa7518", + "prevId": "7b8a421a-d0c7-414a-96e8-0c259935178b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.tenants": { + "name": "tenants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subdomain": { + "name": "subdomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "tenant_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "allow_ui_overrides": { + "name": "allow_ui_overrides", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_backend_overrides": { + "name": "allow_backend_overrides", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_ssr": { + "name": "allow_ssr", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "tenants_subdomain_idx": { + "name": "tenants_subdomain_idx", + "columns": [ + { + "expression": "subdomain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_account_id_idx": { + "name": "tenants_account_id_idx", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tenants_subdomain_unique": { + "name": "tenants_subdomain_unique", + "nullsNotDistinct": false, + "columns": ["subdomain"] + }, + "tenants_account_id_unique": { + "name": "tenants_account_id_unique", + "nullsNotDistinct": false, + "columns": ["account_id"] + }, + "tenants_org_id_unique": { + "name": "tenants_org_id_unique", + "nullsNotDistinct": false, + "columns": ["org_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.tenant_status": { + "name": "tenant_status", + "schema": "public", + "values": ["active", "pending", "suspended", "pending_deletion"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/api/src/db/migrations/meta/0004_snapshot.json b/api/src/db/migrations/meta/0004_snapshot.json new file mode 100644 index 00000000..847e8af8 --- /dev/null +++ b/api/src/db/migrations/meta/0004_snapshot.json @@ -0,0 +1,233 @@ +{ + "id": "5ad5d4c7-b7a9-4b97-a8b6-c718c0ae1b6d", + "prevId": "85c9d130-a4b6-4026-a2df-890fddfa7518", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.citynodes": { + "name": "citynodes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tenant_id": { + "name": "tenant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "validator_pool": { + "name": "validator_pool", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "citynodes_tenant_id_tenants_id_fk": { + "name": "citynodes_tenant_id_tenants_id_fk", + "tableFrom": "citynodes", + "tableTo": "tenants", + "columnsFrom": ["tenant_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "citynodes_tenant_id_unique": { + "name": "citynodes_tenant_id_unique", + "nullsNotDistinct": false, + "columns": ["tenant_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tenants": { + "name": "tenants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "subdomain": { + "name": "subdomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "tenant_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "allow_ui_overrides": { + "name": "allow_ui_overrides", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "allow_backend_overrides": { + "name": "allow_backend_overrides", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "allow_ssr": { + "name": "allow_ssr", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "tenants_subdomain_idx": { + "name": "tenants_subdomain_idx", + "columns": [ + { + "expression": "subdomain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "tenants_account_id_idx": { + "name": "tenants_account_id_idx", + "columns": [ + { + "expression": "account_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tenants_subdomain_unique": { + "name": "tenants_subdomain_unique", + "nullsNotDistinct": false, + "columns": ["subdomain"] + }, + "tenants_account_id_unique": { + "name": "tenants_account_id_unique", + "nullsNotDistinct": false, + "columns": ["account_id"] + }, + "tenants_org_id_unique": { + "name": "tenants_org_id_unique", + "nullsNotDistinct": false, + "columns": ["org_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.tenant_status": { + "name": "tenant_status", + "schema": "public", + "values": ["active", "pending", "suspended", "pending_deletion"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/api/src/db/migrations/meta/_journal.json b/api/src/db/migrations/meta/_journal.json index c939ec4a..cddb5478 100644 --- a/api/src/db/migrations/meta/_journal.json +++ b/api/src/db/migrations/meta/_journal.json @@ -15,6 +15,27 @@ "when": 1786078137908, "tag": "0001_brief_magik", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1786645659094, + "tag": "0002_awesome_madame_web", + "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1786653436589, + "tag": "0003_brief_freak", + "breakpoints": true + }, + { + "idx": 4, + "version": "7", + "when": 1786721978147, + "tag": "0004_majestic_jamie_braddock", + "breakpoints": true } ] } diff --git a/api/src/db/schema.ts b/api/src/db/schema.ts index b141f76b..ae0e6a70 100644 --- a/api/src/db/schema.ts +++ b/api/src/db/schema.ts @@ -1,4 +1,4 @@ -import { pgEnum, pgTable, text, timestamp, uniqueIndex, uuid } from "drizzle-orm/pg-core"; +import { boolean, pgEnum, pgTable, text, timestamp, uniqueIndex, uuid } from "drizzle-orm/pg-core"; export const tenantStatus = pgEnum("tenant_status", [ "active", @@ -13,9 +13,12 @@ export const tenants = pgTable( id: uuid("id").defaultRandom().primaryKey(), subdomain: text("subdomain").notNull().unique(), accountId: text("account_id").notNull().unique(), - orgId: text("org_id").notNull().unique(), + orgId: text("org_id").unique(), name: text("name").notNull(), status: tenantStatus("status").default("active").notNull(), + allowUiOverrides: boolean("allow_ui_overrides").default(true).notNull(), + allowBackendOverrides: boolean("allow_backend_overrides").default(false).notNull(), + allowSsr: boolean("allow_ssr").default(false).notNull(), createdAt: timestamp("created_at", { mode: "date", withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date", withTimezone: true }).defaultNow().notNull(), deletedAt: timestamp("deleted_at", { mode: "date", withTimezone: true }), @@ -25,3 +28,15 @@ export const tenants = pgTable( accountIdIdx: uniqueIndex("tenants_account_id_idx").on(table.accountId), }), ); + +export const citynodes = pgTable("citynodes", { + id: uuid("id").defaultRandom().primaryKey(), + tenantId: uuid("tenant_id") + .references(() => tenants.id, { onDelete: "cascade" }) + .notNull() + .unique(), + orgId: text("org_id").notNull(), + validatorPool: text("validator_pool").notNull(), + createdAt: timestamp("created_at", { mode: "date", withTimezone: true }).defaultNow().notNull(), + updatedAt: timestamp("updated_at", { mode: "date", withTimezone: true }).defaultNow().notNull(), +}); diff --git a/api/src/index.ts b/api/src/index.ts index df235000..858140ec 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -7,6 +7,7 @@ import { DatabaseLive } from "./db/layer"; import { createAuthMiddleware } from "./lib/auth"; import { ContextSchema } from "./lib/context"; import type { PluginsClient } from "./lib/plugins-types.gen"; +import { CityNodesLive, CityNodesTag } from "./services/citynodes"; import { TenantsLive, TenantsTag } from "./services/tenants"; const SUBDOMAIN_SEGMENT_REGEX = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/; @@ -53,6 +54,15 @@ function validateAccountId(accountId: string): void { } } +function validateValidatorPool(pool: string): void { + if (!ACCOUNT_ID_REGEX.test(pool)) { + throw new ORPCError("BAD_REQUEST", { + message: "Invalid validator pool", + data: { hint: "Must be a valid NEAR staking pool account ID, e.g. city-node-3.pool.near" }, + }); + } +} + export default createPlugin.withPlugins()({ variables: z.object({}), @@ -68,8 +78,10 @@ export default createPlugin.withPlugins()({ Effect.gen(function* () { const database = DatabaseLive(config.secrets.API_DATABASE_URL); const tenantsLayer = TenantsLive.pipe(Layer.provide(database)); + const cityNodesLayer = CityNodesLive.pipe(Layer.provide(database)); const tenantsService = yield* tools.buildService(TenantsTag, tenantsLayer); + const cityNodesService = yield* tools.buildService(CityNodesTag, cityNodesLayer); const templateClient = plugins.template?.(); @@ -77,6 +89,7 @@ export default createPlugin.withPlugins()({ return { tenants: tenantsService, + cityNodes: cityNodesService, templateClient, }; }), @@ -87,9 +100,32 @@ export default createPlugin.withPlugins()({ const { templateClient } = services; const { requireAuth, requireOrganization, requireOrgRole } = createAuthMiddleware(builder); + const authorizedCityNode = async ( + input: { cityNodeId: string }, + context: { organization: { activeOrganizationId: string } }, + ) => { + const activeOrgId = context.organization.activeOrganizationId; + const cityNode = await services.cityNodes.resolveById(input.cityNodeId); + if (!cityNode) { + throw new ORPCError("NOT_FOUND", { + message: "City node not found", + data: { resource: "citynode", resourceId: input.cityNodeId }, + }); + } + if (cityNode.orgId !== activeOrgId) { + throw new ORPCError("FORBIDDEN", { + message: "You are not a member of this city node's organization", + }); + } + return cityNode; + }; + const authorizedTenant = async ( input: { tenantId: string }, - context: { organization: { activeOrganizationId: string } }, + context: { + organization: { activeOrganizationId: string }; + near?: { primaryAccountId: string | null }; + }, ) => { const activeOrgId = context.organization.activeOrganizationId; const tenant = await services.tenants.resolveTenantById(input.tenantId); @@ -99,6 +135,16 @@ export default createPlugin.withPlugins()({ data: { resource: "tenant", resourceId: input.tenantId }, }); } + if (tenant.orgId === null) { + const isOwner = + !!context.near?.primaryAccountId && context.near.primaryAccountId === tenant.accountId; + if (!isOwner) { + throw new ORPCError("FORBIDDEN", { + message: "You do not own this personal tenant", + }); + } + return tenant; + } if (tenant.orgId !== activeOrgId) { throw new ORPCError("FORBIDDEN", { message: "You are not a member of this tenant's organization", @@ -144,6 +190,9 @@ export default createPlugin.withPlugins()({ accountId: input.accountId, orgId: context.organization.activeOrganizationId, status: input.status, + allowUiOverrides: input.allowUiOverrides, + allowBackendOverrides: input.allowBackendOverrides, + allowSsr: input.allowSsr, }); }), @@ -159,6 +208,9 @@ export default createPlugin.withPlugins()({ subdomain: input.subdomain, accountId: input.accountId, status: input.status, + allowUiOverrides: input.allowUiOverrides, + allowBackendOverrides: input.allowBackendOverrides, + allowSsr: input.allowSsr, }); }), @@ -223,6 +275,10 @@ export default createPlugin.withPlugins()({ return tenant; }), + listTenantBindings: builder.listTenantBindings.handler(async () => + services.tenants.listBindings(), + ), + tenantPreflight: builder.tenantPreflight.use(requireAuth).handler(async ({ input }) => { const subdomainValid = SUBDOMAIN_SEGMENT_REGEX.test(input.subdomain); const accountId = `${input.subdomain}.${input.parentAccount}`; @@ -245,6 +301,66 @@ export default createPlugin.withPlugins()({ }; }), + listCityNodes: builder.listCityNodes.handler(async () => services.cityNodes.list()), + + resolveCityNode: builder.resolveCityNode.handler(async ({ input }) => { + const cityNode = await services.cityNodes.resolveByAccountId(input.accountId); + return cityNode ?? null; + }), + + createCityNode: builder.createCityNode + .use(requireAuth) + .use(requireOrganization) + .use(requireOrgRole("admin")) + .handler(async ({ input, context }) => { + validateValidatorPool(input.validatorPool); + const tenant = await services.tenants.resolveTenantById(input.tenantId); + if (!tenant) { + throw new ORPCError("NOT_FOUND", { + message: "Tenant not found", + data: { resource: "tenant", resourceId: input.tenantId }, + }); + } + if (tenant.orgId !== context.organization.activeOrganizationId) { + throw new ORPCError("FORBIDDEN", { + message: "This tenant does not belong to your organization", + }); + } + return await services.cityNodes.create({ + tenantId: input.tenantId, + orgId: context.organization.activeOrganizationId, + validatorPool: input.validatorPool, + }); + }), + + updateCityNode: builder.updateCityNode + .use(requireAuth) + .use(requireOrganization) + .use(requireOrgRole("admin")) + .handler(async ({ input, context }) => { + if (input.validatorPool !== undefined) validateValidatorPool(input.validatorPool); + const cityNode = await authorizedCityNode(input, context); + return await services.cityNodes.update(cityNode.id, { + validatorPool: input.validatorPool, + }); + }), + + deleteCityNode: builder.deleteCityNode + .use(requireAuth) + .use(requireOrganization) + .use(requireOrgRole("admin")) + .handler(async ({ input, context }) => { + const cityNode = await authorizedCityNode(input, context); + const deleted = await services.cityNodes.delete(cityNode.id); + if (!deleted) { + throw new ORPCError("NOT_FOUND", { + message: "City node not found", + data: { resource: "citynode", resourceId: input.cityNodeId }, + }); + } + return { success: true as const }; + }), + createThing: builder.createThing.use(requireAuth).handler(async ({ input }) => { if (!templateClient) { throw new ORPCError("BAD_REQUEST", { diff --git a/api/src/services/citynodes.ts b/api/src/services/citynodes.ts new file mode 100644 index 00000000..53579a03 --- /dev/null +++ b/api/src/services/citynodes.ts @@ -0,0 +1,238 @@ +import { eq } from "drizzle-orm"; +import { Context, Effect, Layer } from "every-plugin/effect"; +import { ORPCError } from "every-plugin/orpc"; +import { DatabaseTag } from "../db/layer"; +import { citynodes as citynodesTable, tenants as tenantsTable } from "../db/schema"; + +export interface CityNodeRecord { + id: string; + tenantId: string; + orgId: string; + validatorPool: string; + createdAt: string; + updatedAt: string; +} + +export interface CityNodeWithTenant extends CityNodeRecord { + hostname: string; + accountId: string; + name: string; + tenantStatus: string; +} + +export interface CityNodeInput { + tenantId: string; + orgId: string; + validatorPool: string; +} + +export interface CityNodeUpdateInput { + orgId?: string; + validatorPool?: string; +} + +export interface CityNodesService { + list(): Promise; + resolveById(id: string): Promise; + resolveByTenantId(tenantId: string): Promise; + resolveByAccountId(accountId: string): Promise; + create(input: CityNodeInput): Promise; + update(id: string, input: CityNodeUpdateInput): Promise; + delete(id: string): Promise; +} + +export class CityNodesTag extends Context.Tag("api/CityNodes")< + CityNodesService, + CityNodesService +>() {} + +type CityNodeRow = typeof citynodesTable.$inferSelect; + +const TENANT_SELECT = { + hostname: tenantsTable.subdomain, + accountId: tenantsTable.accountId, + name: tenantsTable.name, + tenantStatus: tenantsTable.status, +}; + +function toCityNodeRecord(row: CityNodeRow): CityNodeRecord { + return { + id: row.id, + tenantId: row.tenantId, + orgId: row.orgId, + validatorPool: row.validatorPool, + createdAt: row.createdAt instanceof Date ? row.createdAt.toISOString() : String(row.createdAt), + updatedAt: row.updatedAt instanceof Date ? row.updatedAt.toISOString() : String(row.updatedAt), + }; +} + +type CityNodeJoinRow = CityNodeRow & { + hostname: string; + accountId: string; + name: string; + tenantStatus: string; +}; + +function toCityNodeWithTenant(row: CityNodeJoinRow): CityNodeWithTenant { + return { + ...toCityNodeRecord(row), + hostname: row.hostname, + accountId: row.accountId, + name: row.name, + tenantStatus: row.tenantStatus, + }; +} + +function toOrpcError(error: unknown): ORPCError { + return error instanceof ORPCError + ? error + : new ORPCError("INTERNAL_SERVER_ERROR", { + message: error instanceof Error ? error.message : String(error), + }); +} + +export const CityNodesLive = Layer.effect( + CityNodesTag, + Effect.gen(function* () { + const db = yield* DatabaseTag; + + const service: CityNodesService = { + list: async () => { + try { + const rows = await db + .select({ ...TENANT_SELECT, citynode: citynodesTable }) + .from(citynodesTable) + .innerJoin(tenantsTable, eq(citynodesTable.tenantId, tenantsTable.id)); + return rows.map(({ citynode, ...tenant }) => + toCityNodeWithTenant({ ...citynode, ...tenant }), + ); + } catch (error) { + throw toOrpcError(error); + } + }, + + resolveById: async (id) => { + try { + const rows = await db + .select({ ...TENANT_SELECT, citynode: citynodesTable }) + .from(citynodesTable) + .innerJoin(tenantsTable, eq(citynodesTable.tenantId, tenantsTable.id)) + .where(eq(citynodesTable.id, id)) + .limit(1); + const row = rows[0]; + if (!row) return null; + const { citynode, ...tenant } = row; + return toCityNodeWithTenant({ ...citynode, ...tenant }); + } catch (error) { + throw toOrpcError(error); + } + }, + + resolveByTenantId: async (tenantId) => { + try { + const rows = await db + .select({ ...TENANT_SELECT, citynode: citynodesTable }) + .from(citynodesTable) + .innerJoin(tenantsTable, eq(citynodesTable.tenantId, tenantsTable.id)) + .where(eq(citynodesTable.tenantId, tenantId)) + .limit(1); + const row = rows[0]; + if (!row) return null; + const { citynode, ...tenant } = row; + return toCityNodeWithTenant({ ...citynode, ...tenant }); + } catch (error) { + throw toOrpcError(error); + } + }, + + resolveByAccountId: async (accountId) => { + try { + const rows = await db + .select({ ...TENANT_SELECT, citynode: citynodesTable }) + .from(citynodesTable) + .innerJoin(tenantsTable, eq(citynodesTable.tenantId, tenantsTable.id)) + .where(eq(tenantsTable.accountId, accountId)) + .limit(1); + const row = rows[0]; + if (!row) return null; + const { citynode, ...tenant } = row; + return toCityNodeWithTenant({ ...citynode, ...tenant }); + } catch (error) { + throw toOrpcError(error); + } + }, + + create: async (input) => { + try { + const [row] = await db + .insert(citynodesTable) + .values({ + tenantId: input.tenantId, + orgId: input.orgId, + validatorPool: input.validatorPool, + }) + .onConflictDoNothing() + .returning(); + + if (!row) { + throw new ORPCError("BAD_REQUEST", { + message: "A city node for this tenant already exists", + data: { invalidFields: ["tenantId"] }, + }); + } + + const resolved = await service.resolveByTenantId(row.tenantId); + if (!resolved) { + throw new ORPCError("INTERNAL_SERVER_ERROR", { + message: "City node created but could not be resolved", + }); + } + return resolved; + } catch (error) { + throw toOrpcError(error); + } + }, + + update: async (id, input) => { + try { + const [row] = await db + .update(citynodesTable) + .set({ ...input, updatedAt: new Date() }) + .where(eq(citynodesTable.id, id)) + .returning(); + + if (!row) { + throw new ORPCError("NOT_FOUND", { + message: "City node not found", + data: { resource: "citynode", resourceId: id }, + }); + } + + const resolved = await service.resolveByTenantId(row.tenantId); + if (!resolved) { + throw new ORPCError("INTERNAL_SERVER_ERROR", { + message: "City node updated but could not be resolved", + }); + } + return resolved; + } catch (error) { + throw toOrpcError(error); + } + }, + + delete: async (id) => { + try { + const rows = await db + .delete(citynodesTable) + .where(eq(citynodesTable.id, id)) + .returning({ deletedId: citynodesTable.id }); + return rows.length > 0; + } catch (error) { + throw toOrpcError(error); + } + }, + }; + + return service; + }), +); diff --git a/api/src/services/tenants.ts b/api/src/services/tenants.ts index 2754489d..34f6231b 100644 --- a/api/src/services/tenants.ts +++ b/api/src/services/tenants.ts @@ -10,28 +10,55 @@ export interface TenantRecord { id: string; subdomain: string; accountId: string; - orgId: string; + orgId: string | null; name: string; status: TenantStatus; + allowUiOverrides: boolean; + allowBackendOverrides: boolean; + allowSsr: boolean; createdAt: string; updatedAt: string; deletedAt: string | null; } +export interface TenantBinding { + hostname: string; + accountId: string; + allowUiOverrides: boolean; + allowBackendOverrides: boolean; + allowSsr: boolean; + status: TenantStatus; +} + export interface TenantInput { subdomain: string; name: string; accountId: string; - orgId: string; + orgId: string | null; status?: TenantStatus; + allowUiOverrides?: boolean; + allowBackendOverrides?: boolean; + allowSsr?: boolean; } export interface TenantsService { listTenantsByOrgIds(orgIds: string[]): Promise; + listBindings(): Promise; createTenant(input: TenantInput): Promise; updateTenant( id: string, - input: Partial>, + input: Partial< + Pick< + TenantInput, + | "name" + | "subdomain" + | "accountId" + | "status" + | "allowUiOverrides" + | "allowBackendOverrides" + | "allowSsr" + > + >, ): Promise; softDeleteTenant(id: string): Promise; suspendTenant(id: string): Promise; @@ -55,6 +82,9 @@ function toTenantRecord(row: TenantRow): TenantRecord { orgId: row.orgId, name: row.name, status: row.status, + allowUiOverrides: row.allowUiOverrides, + allowBackendOverrides: row.allowBackendOverrides, + allowSsr: row.allowSsr, createdAt: row.createdAt instanceof Date ? row.createdAt.toISOString() : String(row.createdAt), updatedAt: row.updatedAt instanceof Date ? row.updatedAt.toISOString() : String(row.updatedAt), deletedAt: row.deletedAt instanceof Date ? row.deletedAt.toISOString() : null, @@ -88,6 +118,27 @@ export const TenantsLive = Layer.effect( } }, + listBindings: async () => { + try { + const rows = await db + .select({ + subdomain: tenantsTable.subdomain, + accountId: tenantsTable.accountId, + allowUiOverrides: tenantsTable.allowUiOverrides, + allowBackendOverrides: tenantsTable.allowBackendOverrides, + allowSsr: tenantsTable.allowSsr, + status: tenantsTable.status, + }) + .from(tenantsTable); + return rows.map(({ subdomain, ...binding }) => ({ + ...binding, + hostname: subdomain, + })); + } catch (error) { + throw toOrpcError(error); + } + }, + createTenant: async (input) => { try { const [row] = await db @@ -98,6 +149,13 @@ export const TenantsLive = Layer.effect( accountId: input.accountId, orgId: input.orgId, ...(input.status !== undefined && { status: input.status }), + ...(input.allowUiOverrides !== undefined && { + allowUiOverrides: input.allowUiOverrides, + }), + ...(input.allowBackendOverrides !== undefined && { + allowBackendOverrides: input.allowBackendOverrides, + }), + ...(input.allowSsr !== undefined && { allowSsr: input.allowSsr }), }) .onConflictDoNothing() .returning(); diff --git a/api/tests/unit/tenants-service.test.ts b/api/tests/unit/tenants-service.test.ts index 427ad07f..fd04bd4b 100644 --- a/api/tests/unit/tenants-service.test.ts +++ b/api/tests/unit/tenants-service.test.ts @@ -147,6 +147,62 @@ describe("TenantsService", () => { expect(await runService(layer, (svc) => svc.listTenantsByOrgIds([]))).toEqual([]); }); + it("lists active and non-deleted bindings with default permissions", async () => { + const layer = freshLayer(); + await runService(layer, (svc) => + svc.createTenant({ + ...baseInput, + subdomain: "acme", + accountId: "acme.example.near", + }), + ); + const suspended = await runService(layer, (svc) => + svc.createTenant({ + ...baseInput, + subdomain: "beta", + accountId: "beta.example.near", + orgId: "org-2", + }), + ); + await runService(layer, (svc) => svc.suspendTenant(suspended.id)); + + const bindings = await runService(layer, (svc) => svc.listBindings()); + + expect(bindings).toHaveLength(2); + expect(bindings.find((b) => b.hostname === "acme")).toMatchObject({ + hostname: "acme", + accountId: "acme.example.near", + allowUiOverrides: true, + allowBackendOverrides: false, + allowSsr: false, + status: "active", + }); + expect(bindings.find((b) => b.hostname === "beta")?.status).toBe("suspended"); + }); + + it("persists allow_* overrides on create and update", async () => { + const layer = freshLayer(); + const created = await runService(layer, (svc) => + svc.createTenant({ + ...baseInput, + allowUiOverrides: false, + allowBackendOverrides: true, + allowSsr: true, + }), + ); + expect(created).toMatchObject({ + allowUiOverrides: false, + allowBackendOverrides: true, + allowSsr: true, + }); + + const updated = await runService(layer, (svc) => + svc.updateTenant(created.id, { allowSsr: false }), + ); + expect(updated.allowSsr).toBe(false); + expect(updated.allowUiOverrides).toBe(false); + }); + it("updates a tenant name", async () => { const layer = freshLayer(); const created = await runService(layer, (svc) => svc.createTenant(baseInput)); diff --git a/bos.config.json b/bos.config.json index b31993ec..92f467af 100644 --- a/bos.config.json +++ b/bos.config.json @@ -1,11 +1,8 @@ { - "account": "dev.everything.near", - "domain": "dev.everything.dev", - "title": "everything.dev", - "description": "Open runtime for apps on NEAR, composed from published config and loaded through a shared host, UI, and API runtime.", - "staging": { - "domain": "dev.everything.dev" - }, + "account": "v1.citynode.near", + "domain": "citynode.app", + "title": "City Nodes", + "description": "Decentralized city nodes on NEAR — each city is a tenant with its own validator pool you can stake to.", "repository": "https://github.com/nearbuilders/everything-dev", "ci": { "railway": { @@ -15,54 +12,59 @@ "app": { "host": { "development": "local:host", - "production": "https://elliot-braem-7253-host-everything-dev-nearbuilder-aa7d0803c-ze.zephyrcloud.app", + "production": "https://elliot-braem-7404-host-everything-dev-nearbuilder-171db3e27-ze.zephyrcloud.app", "secrets": [ "CORS_ORIGIN", - "TENANT_WHITELIST", - "ALLOW_OVERRIDE", - "ALLOW_UNTRUSTED_SSR", "CSP_STRICT" - ] + ], + "integrity": "sha384-P+nSquVEC2WgC57dgScP8BYvBEyBoHQYz5XjQE62U+ZM3LzVBg5NvZ8yGjW+7NfK" }, "ui": { "development": "local:ui", - "production": "https://elliot-braem-7248-ui-everything-dev-nearbuilders-3ca152b89-ze.zephyrcloud.app", - "ssr": "https://elliot-braem-7252-ui-everything-dev-nearbuilders-cf9ebe900-ze.zephyrcloud.app", - "integrity": "sha384-YIdODKfCUjfFZe6M/QaXxylOp6Skko79Rw0CSUi51VTU5aWGCWa0q45M8ftORaOE", - "ssrIntegrity": "sha384-Iz+Xgf2eA5DAVSP052AvrLw9I9V1eA1PTiMkc79BF+F5uKB4q3ATgyRCx5600guP" + "production": "https://elliot-braem-7400-ui-everything-dev-nearbuilders-1b6dbfe53-ze.zephyrcloud.app", + "ssr": "https://elliot-braem-7398-ui-everything-dev-nearbuilders-e8019531b-ze.zephyrcloud.app", + "integrity": "sha384-UQX/yKubluHrdSEKhNtBs39QAbtXHs/iJ0aqkyPIW/ey/k26xRVvCEKsL2iK+cmb", + "ssrIntegrity": "sha384-99Rxrhwagd4U98ryETO5m987SDaqG2DBzRA76gh5bJHqP1x1rTJlapRhC6zASS6j" }, "api": { "development": "local:api", - "production": "https://elliot-braem-7251-api-everything-dev-nearbuilders-4d144331e-ze.zephyrcloud.app", + "production": "https://elliot-braem-7402-api-everything-dev-nearbuilders-79d1a502b-ze.zephyrcloud.app", "secrets": [ "API_DATABASE_URL" ], - "integrity": "sha384-g5kMC6dA5l7l9NBq2U5yWgntreOEqT4+ifIH7a6RMC2JSj6tIuGPG6Xq3ZIAPUAi" + "integrity": "sha384-uvl8gDlaWJ15msNwsloiR0fUP+cvikNVR1YnZPr7T6ojvpkw4gPogwy0AbaSRm27" }, "auth": { "extends": "bos://auth.everything.near/auth.everything.dev#app.auth", "secrets": [ - "NEAR_SUB_ACCOUNT_PARENT_KEY_MAINNET", - "NEAR_SUB_ACCOUNT_PARENT_KEY_TESTNET" + "NEAR_SUB_ACCOUNT_PARENT_KEY_MAINNET" ], "variables": { "passkey": { - "rpID": "everything.dev", - "rpName": "everything.dev" + "rpID": "citynode.app", + "rpName": "City Nodes" }, "siwn": { "recipients": { - "mainnet": "dev.everything.near", - "testnet": "dev.allthethings.testnet" + "mainnet": "v1.citynode.near", + "testnet": "v1.citynode.testnet" + }, + "relayer": { + "ephemeral": true, + "whitelistedContracts": [ + "v1.citynode.near" + ], + "maxGasPerTransaction": "300000000000000", + "maxDepositPerTransaction": "0" }, "subAccount": { "mainnet": { - "parentAccount": "dev.everything.near", + "parentAccount": "v1.citynode.near", "parentHasFullAccess": true, "minDeposit": "0.1 NEAR" }, "testnet": { - "parentAccount": "dev.allthethings.testnet", + "parentAccount": "v1.citynode.testnet", "parentHasFullAccess": true, "minDeposit": "0.1 NEAR" } @@ -74,22 +76,11 @@ "plugins": { "apps": { "development": "local:plugins/apps", - "production": "https://elliot-braem-7250-everything-dev-apps-plugin-ever-5d7ad5daa-ze.zephyrcloud.app", + "production": "https://elliot-braem-7401-everything-dev-apps-plugin-ever-e71038daa-ze.zephyrcloud.app", "variables": { - "registryNamespace": "dev.everything.near" + "registryNamespace": "v1.citynode.near" }, - "routes": [ - "ui/src/routes/_layout/apps/**" - ], - "integrity": "sha384-ehJsiIaaGQb5NjyNKWKS1+4ct479UK+n7MGaB6ZVdOsPSZd7hklMLZcIX5EkXECK" - }, - "template": { - "development": "local:plugins/_template", - "production": "https://elliot-braem-7249-every-plugin-template-everythin-85724dd94-ze.zephyrcloud.app", - "secrets": [ - "TEMPLATE_DATABASE_URL" - ], - "integrity": "sha384-7LAf3x/t4S0nQI0l0rtuvPzpJC81tAHljZOpa1Bc3Lr5pma9jEaM/eClDCvWpnVK" + "integrity": "sha384-3pCa9PXMHz3uCE20GEoLeewt0sBZ1otg8CN7GOQromcQcX0Dfurp42kRDA7aYNCO" } } } diff --git a/bun.lock b/bun.lock index 17ee7b3f..46d876c9 100644 --- a/bun.lock +++ b/bun.lock @@ -39,7 +39,7 @@ }, "api": { "name": "api", - "version": "2.8.0", + "version": "2.8.1", "dependencies": { "@electric-sql/pglite": "catalog:", "@orpc/contract": "catalog:", @@ -71,7 +71,7 @@ }, "host": { "name": "host", - "version": "1.16.0", + "version": "1.16.1", "dependencies": { "@electric-sql/pglite": "catalog:", "@hono/node-server": "^2.0.1", @@ -264,7 +264,7 @@ }, "ui": { "name": "ui", - "version": "1.9.0", + "version": "1.9.1", "dependencies": { "@better-auth/api-key": "catalog:", "@better-auth/core": "catalog:", @@ -272,6 +272,8 @@ "@orpc/client": "catalog:", "@orpc/contract": "catalog:", "@orpc/tanstack-query": "catalog:", + "@pingpay/onramp-sdk": "^0.2.0", + "@pingpay/onramp-types": "^0.0.3", "@radix-ui/react-aspect-ratio": "^1.1.8", "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-checkbox": "^1.3.3", @@ -389,7 +391,7 @@ "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "better-auth": "1.6.25", - "better-near-auth": "1.8.2", + "better-near-auth": "1.10.0", "drizzle-kit": "^0.31.8", "drizzle-orm": "^0.45.1", "effect": "3.21.2", @@ -406,6 +408,8 @@ "zod": "4.4.3", }, "packages": { + "@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.5", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -416,7 +420,7 @@ "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], - "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + "@babel/generator": ["@babel/generator@7.29.8", "", { "dependencies": { "@babel/parser": "^7.29.8", "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg=="], "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], @@ -436,7 +440,7 @@ "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], - "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + "@babel/parser": ["@babel/parser@7.29.8", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA=="], "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="], @@ -446,9 +450,9 @@ "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + "@babel/traverse": ["@babel/traverse@7.29.8", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", "@babel/types": "^7.29.8", "debug": "^4.3.1" } }, "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg=="], - "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="], "@better-auth/api-key": ["@better-auth/api-key@1.6.25", "", { "dependencies": { "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/core": "^1.6.25", "@better-auth/utils": "0.4.2", "better-auth": "^1.6.25", "better-call": "1.3.7" } }, "sha512-A6f3YLN8Ve+D4R7f8jrSKh8Mpu9+bmc8PIb9BFgGgspcedM1PjpSEI5JKAWPUhiGgMeJdCGeCpRP+27jQmW9/w=="], @@ -554,67 +558,67 @@ "@electric-sql/pglite": ["@electric-sql/pglite@0.4.6", "", {}, "sha512-qmlmfN8UyKCee35qkV0r/MBp+Znl8FjBz7OpoglNvww3GJpw0/DLP0o1ZymvLNmcD5DTLOQdzKPtF8Hd3mdl1w=="], - "@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="], + "@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" } }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], "@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.2", "", { "os": "android", "cpu": "arm" }, "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.2", "", { "os": "android", "cpu": "arm64" }, "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.2", "", { "os": "android", "cpu": "x64" }, "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.2", "", { "os": "linux", "cpu": "arm" }, "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.2", "", { "os": "linux", "cpu": "none" }, "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.2", "", { "os": "linux", "cpu": "x64" }, "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.2", "", { "os": "none", "cpu": "x64" }, "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.2", "", { "os": "none", "cpu": "arm64" }, "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g=="], "@every-plugin/template": ["@every-plugin/template@workspace:plugins/_template"], @@ -630,7 +634,7 @@ "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], - "@hono/node-server": ["@hono/node-server@2.0.12", "", { "peerDependencies": { "hono": "^4" } }, "sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg=="], + "@hono/node-server": ["@hono/node-server@2.1.1", "", { "peerDependencies": { "hono": "^4" } }, "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg=="], "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], @@ -704,21 +708,21 @@ "@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@1.0.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g=="], - "@jsonjoy.com/fs-core": ["@jsonjoy.com/fs-core@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-zs2TAq7Six5jgMuoMNjpspAvOP3mhtgq/k1UyQodEzCtQi/N83y2/y+zcvnZSGp/Rxq96DBN+bValOBQAyn/ew=="], + "@jsonjoy.com/fs-core": ["@jsonjoy.com/fs-core@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.68.1", "@jsonjoy.com/fs-node-utils": "4.68.1", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-V5oZ4Gt9WJKyQef0n9cAd0N9qjSkIBm3E4MYsgNIWBk5aINCDPKxMPo1i29rBxqiT4Ixf1epklqV9VJMKIxwlw=="], - "@jsonjoy.com/fs-fsa": ["@jsonjoy.com/fs-fsa@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-nMWOVbkLFyEgmXZih3wyvxA9XpgyyqyfrINMHvEFqhi7uqfRl7c9ERJt6yX7vgMPrB9Uo+OJO+Spa0cFzPD01w=="], + "@jsonjoy.com/fs-fsa": ["@jsonjoy.com/fs-fsa@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.68.1", "@jsonjoy.com/fs-node-builtins": "4.68.1", "@jsonjoy.com/fs-node-utils": "4.68.1", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-HCG72UioncuO7Gw09XNVG+S85e3cq2hrUC/mexBrsWsa3mI7eePkkqWie3uVYbtsb64OR9YGQs5SqaufDRYBcg=="], - "@jsonjoy.com/fs-node": ["@jsonjoy.com/fs-node@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "@jsonjoy.com/fs-print": "4.64.0", "@jsonjoy.com/fs-snapshot": "4.64.0", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-dO+NNkODbUli4uV42bcNrrLvq5rE7SNpdZ5TNd0dtbLsAaNK3MDiIC9lUi+brboGoIjW6vd2fB1qao60nrk5xA=="], + "@jsonjoy.com/fs-node": ["@jsonjoy.com/fs-node@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.68.1", "@jsonjoy.com/fs-node-builtins": "4.68.1", "@jsonjoy.com/fs-node-utils": "4.68.1", "@jsonjoy.com/fs-print": "4.68.1", "@jsonjoy.com/fs-snapshot": "4.68.1", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-R5D9mWtqdURzcOWj1vdXr3APCwX0xchtFT+kmW7fXLNDifWdDrnh26jSID8pdnUfFBxTyfHtFtTL/NWKzIH7kQ=="], - "@jsonjoy.com/fs-node-builtins": ["@jsonjoy.com/fs-node-builtins@4.64.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-/o7WRFhUWaM/fOrslwLZGnzn4RmRILykn+lAL+mNObqqRNw+CQSiij6hpCeZ+C7buhdoVo7go/OYqzaSUfDYmA=="], + "@jsonjoy.com/fs-node-builtins": ["@jsonjoy.com/fs-node-builtins@4.68.1", "", { "peerDependencies": { "tslib": "2" } }, "sha512-HK1BTksysokNZxNspqDH0yPaqN9YgR/AYIlYiIaU2Ys4BOk5CdybI7r6BgiZuiiPiV8n4sK/kZdice7Znpy2Kw=="], - "@jsonjoy.com/fs-node-to-fsa": ["@jsonjoy.com/fs-node-to-fsa@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-fsa": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-WDD9WVs0hb7UAEKTgZW2f66WDrbj7gIIWwpP3spbLyXa0rghtUaFTB8L4gdR3ZCWwiKIsj38/CNijpVmpnuPUw=="], + "@jsonjoy.com/fs-node-to-fsa": ["@jsonjoy.com/fs-node-to-fsa@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-fsa": "4.68.1", "@jsonjoy.com/fs-node-builtins": "4.68.1", "@jsonjoy.com/fs-node-utils": "4.68.1" }, "peerDependencies": { "tslib": "2" } }, "sha512-lpKmU4X9e/oh8GIuAI7EXaS5QiLNM3KD15CkdhfS6PYmrGvoJqKQcyEfnLgnnaGslh/PFUMYSIZBCf2ejJGw8g=="], - "@jsonjoy.com/fs-node-utils": ["@jsonjoy.com/fs-node-utils@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.64.0", "glob-to-regex.js": "^1.0.1" }, "peerDependencies": { "tslib": "2" } }, "sha512-k5Indsx9hWW9xSF7Y6oSKKwtCUNhzZxadub3owhIlitc+iMRVlPPdX2duTKQWBL3qNWpXya8jykgaaWpheeS4w=="], + "@jsonjoy.com/fs-node-utils": ["@jsonjoy.com/fs-node-utils@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-node-builtins": "4.68.1", "glob-to-regex.js": "^1.0.1" }, "peerDependencies": { "tslib": "2" } }, "sha512-/GxfW1DWm9SCdkfbvqevLO/P5duobQfmKkHXxdMIDbcZMQeAgooAstIfZhkXpATzq9QbCQsnoWFM/dGHdZfndw=="], - "@jsonjoy.com/fs-print": ["@jsonjoy.com/fs-print@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-node-utils": "4.64.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-PHZFccchvkhWrwPWHjmVAhbC3vSHCtyZvlZfJJ3ho2bnzl450hXri6/8e6pbkWdH+SkmLXNml0sV8e5HDAfxKw=="], + "@jsonjoy.com/fs-print": ["@jsonjoy.com/fs-print@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-node-utils": "4.68.1", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-oGeZOGPYKK9v1CgeVeEDsLomH1lCnslSpqUN5GmPzrmAVGQlsmsdcXNA2O4lV8Y4xkuSuynx2ITBkUHJVaTbow=="], - "@jsonjoy.com/fs-snapshot": ["@jsonjoy.com/fs-snapshot@4.64.0", "", { "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-oM7UDeL83q6NBzzsfKAsYKXKVXlykKFqqOLh4xZZKAzzROTlInkPbc6LTDGThEOnPiFiUzA7tYziHG9xavd76Q=="], + "@jsonjoy.com/fs-snapshot": ["@jsonjoy.com/fs-snapshot@4.68.1", "", { "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", "@jsonjoy.com/fs-node-utils": "4.68.1", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-XZfP0FDZN32bbc4t2bZN2qRrYHg5AktJnzk22HRoKGK4BprrbNRH2k5ceSNS/kupKYcofCs+O841+xaAbjnxwQ=="], "@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@1.21.0", "", { "dependencies": { "@jsonjoy.com/base64": "^1.1.2", "@jsonjoy.com/buffers": "^1.2.0", "@jsonjoy.com/codegen": "^1.0.0", "@jsonjoy.com/json-pointer": "^1.0.2", "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg=="], @@ -766,39 +770,39 @@ "@module-federation/automatic-vendor-federation": ["@module-federation/automatic-vendor-federation@1.2.1", "", { "dependencies": { "find-package-json": "^1.2.0" }, "peerDependencies": { "webpack": "^5.0.0-beta.16" } }, "sha512-73wxkXM7pbRZ6GGM90JP5IPTPvY3fvrhQyTVdMCUx85cQRWqnbzbibcsz3pkOMOeXyYAO4tXXsG13yNaEEGhJA=="], - "@module-federation/bridge-react-webpack-plugin": ["@module-federation/bridge-react-webpack-plugin@2.8.0", "", { "dependencies": { "@module-federation/sdk": "2.8.0" } }, "sha512-7AaaiE4YOXFb+st6xlVDK65aNKZYR9S9ykH0q2mkHAHTgquXciAjypS8JuhmKn7Lob/2rkplMOc4YsGxG3Ng9Q=="], + "@module-federation/bridge-react-webpack-plugin": ["@module-federation/bridge-react-webpack-plugin@2.8.2", "", { "dependencies": { "@module-federation/sdk": "2.8.2" } }, "sha512-cEhnpCsWHqUndQC6WKtwat5BGz+IU0UdCjzyXrZtx1UqHX1jRB0+DZxB4DKYKtxTko8fsV0FUCJ/0FjKZY6z8g=="], - "@module-federation/cli": ["@module-federation/cli@2.8.0", "", { "dependencies": { "@module-federation/dts-plugin": "2.8.0", "@module-federation/sdk": "2.8.0", "commander": "11.1.0", "jiti": "2.4.2" }, "bin": { "mf": "bin/mf.js" } }, "sha512-yTxdWkCJPPo+IGASz+NqdW13cw3DhjSBEn9r85aYn1ahckDwB1WcZe0OjDWMqCcR6yi0BMLedk0SWrUeUj0fWw=="], + "@module-federation/cli": ["@module-federation/cli@2.8.2", "", { "dependencies": { "@module-federation/dts-plugin": "2.8.2", "@module-federation/sdk": "2.8.2", "commander": "11.1.0", "jiti": "2.4.2" }, "bin": { "mf": "bin/mf.js" } }, "sha512-SrRe2UOzjYux/9Zf7AYymGGsYpJgrIHUPF+T9JQ+rZ7MC9Uiy5rNUtSYdxNrdFpVaRZYXMrI4b82iUy66CU6UA=="], - "@module-federation/dts-plugin": ["@module-federation/dts-plugin@2.8.0", "", { "dependencies": { "@module-federation/error-codes": "2.8.0", "@module-federation/managers": "2.8.0", "@module-federation/sdk": "2.8.0", "@module-federation/third-party-dts-extractor": "2.8.0", "adm-zip": "0.5.10", "isomorphic-ws": "5.0.0", "undici": "7.28.0", "ws": "8.21.0" }, "peerDependencies": { "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24" }, "optionalPeers": ["vue-tsc"] }, "sha512-defjq4jOWMEfeejezPWLP5sc8kw0O6FqTT7/E5rbZPEVyjB1A0U3ynhW6GDE5/6hk9/TzdbWS+fBNi4MqUOY6Q=="], + "@module-federation/dts-plugin": ["@module-federation/dts-plugin@2.8.2", "", { "dependencies": { "@module-federation/error-codes": "2.8.2", "@module-federation/managers": "2.8.2", "@module-federation/sdk": "2.8.2", "@module-federation/third-party-dts-extractor": "2.8.2", "adm-zip": "0.6.0", "isomorphic-ws": "5.0.0", "undici": "7.29.0", "ws": "8.21.0" }, "peerDependencies": { "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24" }, "optionalPeers": ["vue-tsc"] }, "sha512-pwZFW8b2LZTymMMC+o2M9xMXDIQKAHGtCRqj/IkOp0jHRYrKjK9cma9xoUNst8/R3sEn878/i9wgv/43BnCEyg=="], - "@module-federation/enhanced": ["@module-federation/enhanced@2.8.0", "", { "dependencies": { "@module-federation/bridge-react-webpack-plugin": "2.8.0", "@module-federation/cli": "2.8.0", "@module-federation/dts-plugin": "2.8.0", "@module-federation/error-codes": "2.8.0", "@module-federation/inject-external-runtime-core-plugin": "2.8.0", "@module-federation/managers": "2.8.0", "@module-federation/manifest": "2.8.0", "@module-federation/rspack": "2.8.0", "@module-federation/runtime-tools": "2.8.0", "@module-federation/sdk": "2.8.0", "@module-federation/webpack-bundler-runtime": "2.8.0", "schema-utils": "4.3.0", "tapable": "2.3.0" }, "peerDependencies": { "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24", "webpack": "^5.0.0" }, "optionalPeers": ["typescript", "vue-tsc", "webpack"], "bin": { "mf": "bin/mf.js" } }, "sha512-h8vkLdhK7tlcSPmyYNGfGyt0pSzfDB0tYVYdyUt2tXwQRfaJAi3bsIpujMXElw3MXtOfSHESa6M/hPKrtWTHBw=="], + "@module-federation/enhanced": ["@module-federation/enhanced@2.8.2", "", { "dependencies": { "@module-federation/bridge-react-webpack-plugin": "2.8.2", "@module-federation/cli": "2.8.2", "@module-federation/dts-plugin": "2.8.2", "@module-federation/error-codes": "2.8.2", "@module-federation/inject-external-runtime-core-plugin": "2.8.2", "@module-federation/managers": "2.8.2", "@module-federation/manifest": "2.8.2", "@module-federation/rspack": "2.8.2", "@module-federation/runtime-tools": "2.8.2", "@module-federation/sdk": "2.8.2", "@module-federation/webpack-bundler-runtime": "2.8.2", "schema-utils": "4.3.0", "tapable": "2.3.0" }, "peerDependencies": { "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24", "webpack": "^5.0.0" }, "optionalPeers": ["typescript", "vue-tsc", "webpack"], "bin": { "mf": "bin/mf.js" } }, "sha512-XVCp1dz7ADd2YgjuvGVsS7IVJ8ViuAUCEz9gAb4M0qMJCjcPfzXQ1CzV1/Me1lKaPPwNj/cJ3NXTRvXjMHanGg=="], - "@module-federation/error-codes": ["@module-federation/error-codes@2.8.0", "", {}, "sha512-Gaog9904EmxYOQV0hli3XQ7jXeFaADfh5bnBtTCtbZ37Qd/Sz9kQfd+gYQRyIj7RGmkv9DPiN/SsmrTMrTymKw=="], + "@module-federation/error-codes": ["@module-federation/error-codes@2.8.2", "", {}, "sha512-8inlDv48QOjA//CLQ3epjoHEiMQGsz1Pmtu2N+s7gQVggn6AYHpjnMe8AsyGxtpaPg3wbX0HmBZtRFggpXUB9A=="], - "@module-federation/inject-external-runtime-core-plugin": ["@module-federation/inject-external-runtime-core-plugin@2.8.0", "", { "peerDependencies": { "@module-federation/runtime-tools": "2.8.0" } }, "sha512-fW3jD1ZVds6r/Ul8TtUA42RsB0LfT1yjo5KjqgirH9QrmEMH21x44e3e6BC6IN820XKawRDSmz2kFA5YHIQp7Q=="], + "@module-federation/inject-external-runtime-core-plugin": ["@module-federation/inject-external-runtime-core-plugin@2.8.2", "", { "peerDependencies": { "@module-federation/runtime-tools": "2.8.2" } }, "sha512-RetbaupJGiT2FtmA3WWGm72xZkNSJ6Xyy53jSgtu9HRnR/5xt/wiQl+Pe/iEfzgfLsUnUAkOsSfiPBXrDtNizw=="], - "@module-federation/managers": ["@module-federation/managers@2.8.0", "", { "dependencies": { "@module-federation/sdk": "2.8.0" } }, "sha512-SnVBCwmi962WGg6hLFElxZUCnrRJdR6glE2ZKPBY/iK07AHUN2ZxuaBCBsVzyws+xLZGHZxBHmVstijTh8dSUA=="], + "@module-federation/managers": ["@module-federation/managers@2.8.2", "", { "dependencies": { "@module-federation/sdk": "2.8.2" } }, "sha512-OQfnoUwy1IUfn6DaI2S/DPKgnElkYlP+gG8mbUQlJVEW1Zg/8V/dZbNgJUKikTJly6yR1r08PK59g7239ITYMw=="], - "@module-federation/manifest": ["@module-federation/manifest@2.8.0", "", { "dependencies": { "@module-federation/dts-plugin": "2.8.0", "@module-federation/managers": "2.8.0", "@module-federation/sdk": "2.8.0" } }, "sha512-wfVeBXc4/C2F70nRFSPqJhkcwbDgo+wQyEn3jbjJTDoUqxxhYBfHFs1ACBYOk3Qm97L7hHclHGtUG0/nvDEfAA=="], + "@module-federation/manifest": ["@module-federation/manifest@2.8.2", "", { "dependencies": { "@module-federation/dts-plugin": "2.8.2", "@module-federation/managers": "2.8.2", "@module-federation/sdk": "2.8.2" } }, "sha512-mJUZo7QFL46NXoEWNby3CfPFFm2235J7LO6bXAAMrEIT4qF8QHLiqfoo5dZmrisRgM6e+muriWtGvPFMprnXyA=="], - "@module-federation/node": ["@module-federation/node@2.7.47", "", { "dependencies": { "@module-federation/enhanced": "2.8.0", "@module-federation/runtime": "2.8.0", "@module-federation/sdk": "2.8.0", "encoding": "0.1.13", "node-fetch": "2.7.0", "tapable": "2.3.0" }, "peerDependencies": { "webpack": "^5.40.0" }, "optionalPeers": ["webpack"] }, "sha512-mifMvCjWmLl53GS+badQws0j2bsu1ICpdGzCbez4I6kSpaYA8v86L6dwcHtVHIZtkUC6cjAZBDcgpxs4fK3nFQ=="], + "@module-federation/node": ["@module-federation/node@2.7.49", "", { "dependencies": { "@module-federation/enhanced": "2.8.2", "@module-federation/runtime": "2.8.2", "@module-federation/sdk": "2.8.2", "encoding": "0.1.13", "node-fetch": "2.7.0", "tapable": "2.3.0" }, "peerDependencies": { "webpack": "^5.40.0" }, "optionalPeers": ["webpack"] }, "sha512-xNGYfhA2aqFpogb/uq6lwBeEbnmDLV6PwHzSe97mRrSSr00eUKAMwlLG6PcQP6ynbkPeDG86RYj/YUC7EWgLMA=="], - "@module-federation/rsbuild-plugin": ["@module-federation/rsbuild-plugin@2.8.0", "", { "dependencies": { "@module-federation/enhanced": "2.8.0", "@module-federation/node": "2.7.47", "@module-federation/sdk": "2.8.0" }, "peerDependencies": { "@rsbuild/core": "^1.3.21 || ^2.0.0-0" }, "optionalPeers": ["@rsbuild/core"] }, "sha512-rul5OPvLx599rWoAhCtKJ3UYqyM3Dxg0RWEfad3JdnJFqkcSLBojZXgHJE1vbF7DRdGQNmNscKw34iEyn89NwQ=="], + "@module-federation/rsbuild-plugin": ["@module-federation/rsbuild-plugin@2.8.2", "", { "dependencies": { "@module-federation/enhanced": "2.8.2", "@module-federation/node": "2.7.49", "@module-federation/sdk": "2.8.2" }, "peerDependencies": { "@rsbuild/core": "^1.3.21 || ^2.0.0-0" }, "optionalPeers": ["@rsbuild/core"] }, "sha512-rUzx5quE/pqEiZk0ESyPj4QCDCvSpqhgoq/+zjuz9vyHAuMvXL/U0si6bOclBg4UGGFlLmq5+XVIJR43iAADJg=="], - "@module-federation/rspack": ["@module-federation/rspack@2.8.0", "", { "dependencies": { "@module-federation/bridge-react-webpack-plugin": "2.8.0", "@module-federation/dts-plugin": "2.8.0", "@module-federation/inject-external-runtime-core-plugin": "2.8.0", "@module-federation/managers": "2.8.0", "@module-federation/manifest": "2.8.0", "@module-federation/runtime-tools": "2.8.0", "@module-federation/sdk": "2.8.0" }, "peerDependencies": { "@rspack/core": "^0.7.0 || ^1.0.0 || ^2.0.0-0", "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24" }, "optionalPeers": ["typescript", "vue-tsc"] }, "sha512-TPcrkHpaZgL25Vx3c8oSNwyv7/KktC7uo6HTQdVWlFzbq5RSoMMGkWoir5pY5124isae2/p6v5xAuqICi4r0Zg=="], + "@module-federation/rspack": ["@module-federation/rspack@2.8.2", "", { "dependencies": { "@module-federation/bridge-react-webpack-plugin": "2.8.2", "@module-federation/dts-plugin": "2.8.2", "@module-federation/inject-external-runtime-core-plugin": "2.8.2", "@module-federation/managers": "2.8.2", "@module-federation/manifest": "2.8.2", "@module-federation/runtime-tools": "2.8.2", "@module-federation/sdk": "2.8.2" }, "peerDependencies": { "@rspack/core": "^0.7.0 || ^1.0.0 || ^2.0.0-0", "typescript": "^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "vue-tsc": ">=1.0.24" }, "optionalPeers": ["typescript", "vue-tsc"] }, "sha512-HEDirYhVYvx7IzP9jes6KLPMqSoSQwuLfBzPSOgBqY7sIH/e9zRSRO1qh5C8OXYKHi23WcQpGY+EeecIK9wxWw=="], - "@module-federation/runtime": ["@module-federation/runtime@2.8.0", "", { "dependencies": { "@module-federation/error-codes": "2.8.0", "@module-federation/runtime-core": "2.8.0", "@module-federation/sdk": "2.8.0" } }, "sha512-cGtUBQ1/TVy7KrXy6xPgy3FEmOGyIYkBA2T4iGH3ZH5PNPPTmqN9jF2AfneTSOj0RtBr7Pxq3CUt81E/UCvK1A=="], + "@module-federation/runtime": ["@module-federation/runtime@2.8.2", "", { "dependencies": { "@module-federation/error-codes": "2.8.2", "@module-federation/runtime-core": "2.8.2", "@module-federation/sdk": "2.8.2" } }, "sha512-SUoP+PD5EjSPSi6FxEPGIZoRkFifxdeYcVQbJE9mO0VEjF51gAk3/TgX8k0vzUryOBPmXekLr9SfQXU6DqUtvA=="], - "@module-federation/runtime-core": ["@module-federation/runtime-core@2.8.0", "", { "dependencies": { "@module-federation/error-codes": "2.8.0", "@module-federation/sdk": "2.8.0" } }, "sha512-Tf98+epGGiPSHqmQHuXa2uXZMMvjGf1IqJDR1/FpXfmobv5ECN0mGZCjUHGNSyxvoDyXKIkKwJu7IwEoh0ouQA=="], + "@module-federation/runtime-core": ["@module-federation/runtime-core@2.8.2", "", { "dependencies": { "@module-federation/error-codes": "2.8.2", "@module-federation/sdk": "2.8.2" } }, "sha512-PEkkK9MUp+nUCeQMS4ox3QGZfwwxgfjGA7P4umEnr5c3y8DLNDR+26tyHf/Gkjen2VsjlcyL+mEAQo1Zi4IE3g=="], "@module-federation/runtime-tools": ["@module-federation/runtime-tools@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/webpack-bundler-runtime": "0.22.0" } }, "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA=="], - "@module-federation/sdk": ["@module-federation/sdk@2.8.0", "", {}, "sha512-yBP+9+0Z8nlvKEXAZS3AsQVy7bFbZf8eMivGk4q4ZdwG3TsLMlsPjb1dQb2i7gcAG6ux9y2LWLkj/0LVk74cnQ=="], + "@module-federation/sdk": ["@module-federation/sdk@2.8.2", "", {}, "sha512-OPS/lbQjraLXoWniQpCwQ/vqgURHTrhsackSNcOPmcJHM3LyR+DabxUc0pl8jAqExsW2l+uepQq7+/Gkei871w=="], - "@module-federation/third-party-dts-extractor": ["@module-federation/third-party-dts-extractor@2.8.0", "", {}, "sha512-nAMlr74OKIylkfRwlunOhytQbmsgb3gCqdXWnPQhG+ZtqWXGELLfMT4a1Q1ht3cS+sRpWj2SZRqK2M7GadI6tA=="], + "@module-federation/third-party-dts-extractor": ["@module-federation/third-party-dts-extractor@2.8.2", "", {}, "sha512-Xf3iZ4iDi972XMOMbmUm/c5Vwwb6cTsU+Jpoz96lUom6Yps9FQX48elIdhgcQsL7/K64PqdOONsrlZVo8zphKA=="], - "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@2.8.0", "", { "dependencies": { "@module-federation/error-codes": "2.8.0", "@module-federation/runtime": "2.8.0", "@module-federation/sdk": "2.8.0" } }, "sha512-82fDy9v+7qV5fiN8TKVhOdrxhmAZnUIX/IKivYX5ulCt8aoOzVFTiwm/P1GQUDD8z6dqR48xgJdZdf0548Mc9w=="], + "@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@2.8.2", "", { "dependencies": { "@module-federation/error-codes": "2.8.2", "@module-federation/runtime": "2.8.2", "@module-federation/sdk": "2.8.2" } }, "sha512-g4xQgfgMMCKgJjVMBh7nIYGjLGNDYwSZ4lfpTdkVyWDnxmR3SL6VPQTJEZHRYPyqvrTtZE1zdDhDd++yNRvLdA=="], "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ=="], @@ -840,13 +844,17 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], + "@neodrag/core": ["@neodrag/core@3.0.0-next.11", "", {}, "sha512-3WQWxyrbxiaK9zS5JU2wJsW2gpoQlZBXVghduBh61JpqaeE0T0cte8R0qYK2RuJo3J2TYQYqxO19CpG/C1i5eg=="], + + "@neodrag/solid": ["@neodrag/solid@3.0.0-next.11", "", { "peerDependencies": { "@neodrag/core": "3.0.0-next.11", "solid-js": "^1.0.0" } }, "sha512-vCBIn/pimjWMQ6vhTS2/O1XNAwzVtc4eUhdbQ91WykbZWWqQ5NocDXt/1OdYrEkeRzJcpCv8wEz5PnMkgKP81Q=="], + "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], - "@noble/ciphers": ["@noble/ciphers@2.2.0", "", {}, "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA=="], + "@noble/ciphers": ["@noble/ciphers@2.3.0", "", {}, "sha512-Clu/xdfgVTf9o7ngLOURaxePwR0j8sjclKEtVij10/jGulwFsPWCvvRgG/XjUVf8Nei+jLG6uwyXzUTGY1DQrw=="], - "@noble/curves": ["@noble/curves@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0" } }, "sha512-T/BoHgFXirb0ENSPBquzX0rcjXeM6Lo892a2jlYJkqk83LqZx0l1Of7DzlKJ6jkpvMrkHSnAcgb5JegL8SeIkQ=="], + "@noble/curves": ["@noble/curves@2.3.0", "", { "dependencies": { "@noble/hashes": "2.3.0" } }, "sha512-v7cY+4oWYPQszRj6ZFGzTVL7uP2TaLo1xMhWHzYC5wj0ZhOXQ5x+sBre8rF3hi8cAoi0bh1qXoovoOkdFtvqEg=="], - "@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="], + "@noble/hashes": ["@noble/hashes@2.3.0", "", {}, "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -858,7 +866,7 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.43.0", "", {}, "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg=="], - "@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + "@orpc/client": ["@orpc/client@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0", "@orpc/standard-server-fetch": "1.15.0", "@orpc/standard-server-peer": "1.15.0" } }, "sha512-Qt0FdPSGySdQwy83iUWOq+Iqhw2gM9qHtyxfBbcw1mwOz1s4O2BwUFA3ymVTLIRRNYRgPrQLaBZhp8CacqeePg=="], "@orpc/contract": ["@orpc/contract@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/shared": "1.14.3", "@standard-schema/spec": "^1.1.0", "openapi-types": "^12.1.3" } }, "sha512-docXs4ALK3TADAnscEywjqvV1Dy+4+B6ihfo33hayvJdxZdpVmxjHOf7pcAYaJFJ6+LgKYoskaVVKad6LLxFlg=="], @@ -868,9 +876,9 @@ "@orpc/json-schema": ["@orpc/json-schema@1.14.3", "", { "dependencies": { "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/openapi": "1.14.3", "@orpc/server": "1.14.3", "@orpc/shared": "1.14.3", "json-schema-typed": "^8.0.2" } }, "sha512-Qcz2PzyZG2etpfB8ywy4Upf4SaI2x6x4fA8utVQXf5GcTPWfbTVz78MDDWnNtYEXSPj1BS95HqHC0AeGvYIB+g=="], - "@orpc/openapi": ["@orpc/openapi@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/openapi-client": "1.14.3", "@orpc/server": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-0tZN91VoT6MEkOfw+ERKyozsnDXzmDSsBeMgEHN3Hl1WVU97T9l4aZzLlZILSNl3fat3HmduEDy/boNGmAWJkQ=="], + "@orpc/openapi": ["@orpc/openapi@1.15.0", "", { "dependencies": { "@orpc/client": "1.15.0", "@orpc/contract": "1.15.0", "@orpc/interop": "1.15.0", "@orpc/openapi-client": "1.15.0", "@orpc/server": "1.15.0", "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-nwdsVkIRTVhW+2kjtEwHQqfkYnmbJN6HTHvBfnt0KYrPftJ7qvwDo4WnoxfS7Q1jdUlVu8griT5cKZ1gAIYcpQ=="], - "@orpc/openapi-client": ["@orpc/openapi-client@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3" } }, "sha512-1vp+hi858XDrCwYdhONl15YKNlHtj5F5gI3dq/McRRZ45tZ9/Ma03hxzABOOcayaT1L9nX7gPxhX7l5EuRf2zw=="], + "@orpc/openapi-client": ["@orpc/openapi-client@1.15.0", "", { "dependencies": { "@orpc/client": "1.15.0", "@orpc/contract": "1.15.0", "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0" } }, "sha512-MGyMAC6dvQ/YoUARpTN4L1MAnK4L2T3+Z7LA8PVva2rZ8XpKABZRcfqufyPfAC0nu/0uPeebnxHaXZ/OeUIOFg=="], "@orpc/server": ["@orpc/server@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-aws-lambda": "1.14.3", "@orpc/standard-server-fastify": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-node": "1.14.3", "@orpc/standard-server-peer": "1.14.3", "cookie": "^1.1.1" }, "peerDependencies": { "crossws": ">=0.3.4", "ws": ">=8.18.1" }, "optionalPeers": ["crossws", "ws"] }, "sha512-VQG1sgruPhWdzT/ChltJ5Ju9v1A8F+s8EQ1MMSI33z0AthZ3IuuMZdqMIOo5YSuHROoFxzMJgCShOWYR9qXhQA=="], @@ -892,7 +900,7 @@ "@orpc/zod": ["@orpc/zod@1.14.3", "", { "dependencies": { "@orpc/json-schema": "1.14.3", "@orpc/openapi": "1.14.3", "@orpc/shared": "1.14.3", "escape-string-regexp": "^5.0.0", "wildcard-match": "^5.1.4" }, "peerDependencies": { "@orpc/contract": "1.14.3", "@orpc/server": "1.14.3", "zod": ">=3.25.0" } }, "sha512-+SIDmqfkTLCeeZVN6Cic4aWeiBqf2O9F4Vto9npqOEXT1szIpHKJtCmdZBsTOSD46LV5Tcg1emOde9eUeY2EBg=="], - "@oxc-project/types": ["@oxc-project/types@0.140.0", "", {}, "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ=="], + "@oxc-project/types": ["@oxc-project/types@0.144.0", "", {}, "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg=="], "@parcel/watcher": ["@parcel/watcher@2.6.0", "", { "dependencies": { "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", "picomatch": "^4.0.4" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.6.0", "@parcel/watcher-darwin-arm64": "2.6.0", "@parcel/watcher-darwin-x64": "2.6.0", "@parcel/watcher-freebsd-x64": "2.6.0", "@parcel/watcher-linux-arm-glibc": "2.6.0", "@parcel/watcher-linux-arm-musl": "2.6.0", "@parcel/watcher-linux-arm64-glibc": "2.6.0", "@parcel/watcher-linux-arm64-musl": "2.6.0", "@parcel/watcher-linux-x64-glibc": "2.6.0", "@parcel/watcher-linux-x64-musl": "2.6.0", "@parcel/watcher-win32-arm64": "2.6.0", "@parcel/watcher-win32-x64": "2.6.0" } }, "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w=="], @@ -920,33 +928,37 @@ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.6.0", "", { "os": "win32", "cpu": "x64" }, "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A=="], - "@peculiar/asn1-android": ["@peculiar/asn1-android@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-skLbS+IOGv1lUgDqtChr8xvtvEr3HMse/JGBaL2r1J1o/n7a8wqOrovMtlRq/UXLhxvmLaONP67hwtshgzwfzA=="], + "@peculiar/asn1-android": ["@peculiar/asn1-android@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ErGsrDbWYhMu/H43L3qohwCwPrjdinn8GGm4Q52evA1u2juJxqkPXv+26vBC35hhMoUkzYIOTLrFzSbXanRU+A=="], - "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA=="], + "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "@peculiar/asn1-x509-attr": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-VKQz6sJYgSxtGaK6UdnNBUx7hmSdg0K331qrEWh5qxpQsyZGWBjxbq05AJ2bTWWjd7d+nJIxFzwvsR5T7DWC/A=="], - "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg=="], + "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-SbxRzHiWnRdiDuiiji/RLsJxu2au4hmSZSKK7GQOgNr2BVvheAlFQST9qqzRchUcZ6wvcyRuPXIfYXVzLoZ/5g=="], - "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ=="], + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-vNspHtTd9h6e8c2lMW+B/VHEUD+HRFV0fj/Gvz7SaJbwiecA8dxd96UTFPYI1PR8k7qwjjW9AFEyI+LuyVi4Kw=="], - "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-rsa": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg=="], + "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.9.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.9.0", "@peculiar/asn1-pkcs8": "^2.9.0", "@peculiar/asn1-rsa": "^2.9.0", "@peculiar/asn1-schema": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-A6bX+gZr69U38Pg1mWvPrM1eRba6L6kLR8iVG+bJtKj3qSv2rSNmlXLtej7ZOkEWt6xL6PiJD73uFEdQI3BXCg=="], - "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA=="], + "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-1JH4FliKQ3trkMD17X+bKGsph5TsiM+AiiqnVKr1wxA/GoUSDHiWG/zROzLAbDIA7eclBCjQsp8hrBEJk7LRUQ=="], - "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.8.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pfx": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ=="], + "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.9.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.9.0", "@peculiar/asn1-pfx": "^2.9.0", "@peculiar/asn1-pkcs8": "^2.9.0", "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "@peculiar/asn1-x509-attr": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-igArY6bpCI6tOPm2EU9QPrXuKq2s1iraLCTym4UlooYdzcRDIPCRUN9TAEcqZ5rZhA+HiPdFKTbDP9vneN/tsg=="], - "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg=="], + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-vOD7Q4UmQWhlMYuWJawS2sD+/JcPKJNtyQuFZx09r+KFhm5/HxfGak63y/m56cpBjmb5k6PeRlQf1fvLQAieUA=="], - "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.8.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q=="], + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.9.0", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-AKvPMOM7LfK0uFe1m7o7+veOa8xQGPqsqOrKi3QKgCElzwjGp39mbhr2g7mt/v/mXQHiIvJmDj5cJS173x8Q9Q=="], - "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg=="], + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-b9Na83rhRFQBd5CuMmuEqokYhmyWUbG7mNZl/thGhBwLpCdiZ85TZ3WFhF7OVgOekC5uKhLk4C3HKtdiScCMdQ=="], - "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.8.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA=="], + "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.9.0", "", { "dependencies": { "@peculiar/asn1-schema": "^2.9.0", "@peculiar/asn1-x509": "^2.9.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-f+u+EyGjfPvPzvr0+rlh/TFEO7LWt84mEwQynCUYr4F6urf/8s6+ESJ23qfSn1aamkZR6AuBNaC62iEsGvp0+w=="], "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], "@peculiar/x509": ["@peculiar/x509@1.14.3", "", { "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA=="], - "@playwright/test": ["@playwright/test@1.62.0", "", { "dependencies": { "playwright": "1.62.0" }, "bin": { "playwright": "cli.js" } }, "sha512-9zOJ6ZQRAena31MpOH9VSzIz8Ou3YJ/wtY/eQm5T2uhfhG7/U3COrMS8xOtUrZrp9OgdmzEnIYODye3nY1VqzA=="], + "@pingpay/onramp-sdk": ["@pingpay/onramp-sdk@0.2.0", "", { "dependencies": { "@wagmi/core": "^2.17.2", "dotenv": "^16.5.0", "zod": "^3.25.32" }, "peerDependencies": { "typescript": "^5" } }, "sha512-JPm7+eldXDevWqkhBeRyhbcB8OjuQdHxJcEIARX9KQbvcH/4D1z2XrsZDomQz6UgATxiEDkGRoHQJX/I3prnZQ=="], + + "@pingpay/onramp-types": ["@pingpay/onramp-types@0.0.3", "", { "dependencies": { "zod": "^3.25.67" }, "peerDependencies": { "typescript": "^5" } }, "sha512-qo1BCCgd9vCd3RXkE34Sl1XP2yD8opbsf3r/cgWjM6/JBXzdpcN9p2ysec4xjo2SoHNUQ+ZfEH49eYLjpbmHOQ=="], + + "@playwright/test": ["@playwright/test@1.62.1", "", { "dependencies": { "playwright": "1.62.1" }, "bin": { "playwright": "cli.js" } }, "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], @@ -1074,35 +1086,33 @@ "@radix-ui/rect": ["@radix-ui/rect@1.1.3", "", {}, "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.0", "", { "os": "android", "cpu": "arm64" }, "sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw=="], - - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.4", "", { "os": "android", "cpu": "arm64" }, "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.0", "", { "os": "none", "cpu": "arm64" }, "sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.2.0", "", { "dependencies": { "@emnapi/core": "1.11.2", "@emnapi/runtime": "1.11.2", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.4", "", { "os": "none", "cpu": "arm64" }, "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], @@ -1142,11 +1152,11 @@ "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.6.2", "", { "dependencies": { "error-stack-parser": "^2.1.4" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-k+/VrfTNgo+KirjI6V+8CWRj6y+DH9jOUWv8JorYY4vKf/9xfnZ8xHzuB4iqCwTtoZl9YnxOaOuoyjJipc2tiQ=="], - "@scure/base": ["@scure/base@2.2.0", "", {}, "sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg=="], + "@scure/base": ["@scure/base@2.3.0", "", {}, "sha512-NsG6Y03tY6R5BUis4FdVtHVkur0U6FOzskgs9ZXNl78CUc9fkZ78HmENUle1nSOkCasDmbubmWD9qwB7mm4PZA=="], - "@scure/bip32": ["@scure/bip32@2.2.0", "", { "dependencies": { "@noble/curves": "2.2.0", "@noble/hashes": "2.2.0", "@scure/base": "2.2.0" } }, "sha512-zFr7t2F+a9+5tB7QbarF2HQNYrgjCNaoLAupZdKkrFMYMozJf5zqH2WJCQibMzm1qQ0QogrxVGO3qXfQDYMaQg=="], + "@scure/bip32": ["@scure/bip32@2.3.0", "", { "dependencies": { "@noble/curves": "2.3.0", "@noble/hashes": "2.3.0", "@scure/base": "2.3.0" } }, "sha512-mMPjNcXxJsvNveIgRIXrnwd4omu0wdXo4JowAUZO7/82+/bpAwVltclx6MG6nv2M3dA6IbfVv4xwWxaREm3OcA=="], - "@scure/bip39": ["@scure/bip39@2.2.0", "", { "dependencies": { "@noble/hashes": "2.2.0", "@scure/base": "2.2.0" } }, "sha512-T/Bj/YvYMNkIPq6EENO6/rcs2e7qTNuyoUXf0KBFDmp0ZDu0H2X4Lq6yC3i0c8PcWkov5EbW+yQZZbdMmk154A=="], + "@scure/bip39": ["@scure/bip39@2.3.0", "", { "dependencies": { "@noble/hashes": "2.3.0" } }, "sha512-qdyWuxoYwi3+YmqIsfkpz1I029m980WkVPilj+kG7VxSm+gKQ2BmQru3nv3LbMtpSUXuyZwdFT65JkpKu5OHOQ=="], "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], @@ -1202,7 +1212,7 @@ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "postcss": "^8.5.16", "tailwindcss": "4.3.3" } }, "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg=="], - "@tanstack/devtools": ["@tanstack/devtools@0.13.0", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/keyboard": "^1.3.3", "@solid-primitives/resize-observer": "^2.1.3", "@tanstack/devtools-client": "0.0.8", "@tanstack/devtools-event-bus": "0.4.2", "@tanstack/devtools-ui": "0.6.0", "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.9" }, "bin": { "intent": "./bin/intent.js" } }, "sha512-p/nOH9bS/OO/u3402zPjoGu+Mz6Fzi/iRqJuYghuuYRUY32kZt+C0/d+pP/bi6/2JTi1FdT6oEXI2lWlA5tXxw=="], + "@tanstack/devtools": ["@tanstack/devtools@0.14.0", "", { "dependencies": { "@neodrag/solid": "3.0.0-next.11", "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/keyboard": "^1.3.3", "@solid-primitives/resize-observer": "^2.1.3", "@tanstack/devtools-client": "0.0.8", "@tanstack/devtools-event-bus": "0.4.2", "@tanstack/devtools-ui": "0.7.0", "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.9" }, "bin": { "intent": "./bin/intent.js" } }, "sha512-tN0SEi1BVaJYtkZbgYpvLsInjwNRAZkR3r6slSvz9Jm+bfkhcdjuOSrZp0cAwOGwdnauHZ1mYgtfe2AfC/V1NQ=="], "@tanstack/devtools-client": ["@tanstack/devtools-client@0.0.8", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.5.0" } }, "sha512-cG3iZkGWCwN330bLBKa8+9r4Of2AXNoz2zUqcsy/4XsD3105ghVBx78cGyvJj9fSclNomPxoqAnDGXXhg1WLvA=="], @@ -1210,9 +1220,9 @@ "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.4.4", "", { "bin": { "intent": "./bin/intent.js" } }, "sha512-6T5Yop/793YI+H+5J8Hsyj4kCih9sl4t3ElLgKioW5hk3ocn+ZdSJ94tT7vL7uabxSugWYBZlOTMPzEw2puvQw=="], - "@tanstack/devtools-ui": ["@tanstack/devtools-ui@0.6.0", "", { "dependencies": { "clsx": "^2.1.1", "dayjs": "^1.11.19", "goober": "^2.1.16", "solid-js": "^1.9.9" } }, "sha512-CVaM6rT6Nl5ijo83vJYFa2SjofvpuOl/uOvbYGhBrRgUhhelNHhx8zZX+hnZCHmIr0/lzM65hsocnZ72592Rvg=="], + "@tanstack/devtools-ui": ["@tanstack/devtools-ui@0.7.0", "", { "dependencies": { "clsx": "^2.1.1", "dayjs": "^1.11.19", "goober": "^2.1.16", "solid-js": "^1.9.9" } }, "sha512-px/a+JgRSVHDj/hID1cwfsIi+ly5l7t3Yw7fLw4G556HXCNgDri36fupt9h7oBeEKntpsx3ep/XtZn51rrCv2g=="], - "@tanstack/form-core": ["@tanstack/form-core@1.33.2", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1", "@tanstack/pacer-lite": "^0.1.1", "@tanstack/store": "^0.11.0" } }, "sha512-F60zJd15bGrXKonc1kpRYnNRNfiES7F+hgvrPMrsZznPLqZtO2DIg76OU6R25kCYkqYQY5xvuKteuWcUsc587A=="], + "@tanstack/form-core": ["@tanstack/form-core@1.33.5", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1", "@tanstack/pacer-lite": "^0.1.1", "@tanstack/store": "^0.11.0" } }, "sha512-3dfx9MBP0aq5sXKteikG629X9oviptrQj0IFRk9YGcb+lB7Kv5x8S17oOSk1wUWgjQZ4xVJEMbKwOAODymocgA=="], "@tanstack/history": ["@tanstack/history@1.154.14", "", {}, "sha512-xyIfof8eHBuub1CkBnbKNKQXeRZC4dClhmzePHVOEel4G7lk/dW+TQ16da7CFdeNLv6u6Owf5VoBQxoo6DFTSA=="], @@ -1224,9 +1234,9 @@ "@tanstack/query-devtools": ["@tanstack/query-devtools@5.101.4", "", {}, "sha512-z5IPHnDX3aUWeTWlRKLyooBQekaCAw4xRpZqPQ390RiWTDBcTynjpPT221BArw0u2+pnQMdGvPQI9YNNubBcmA=="], - "@tanstack/react-devtools": ["@tanstack/react-devtools@0.10.9", "", { "dependencies": { "@tanstack/devtools": "0.13.0" }, "peerDependencies": { "@types/react": ">=16.8", "@types/react-dom": ">=16.8", "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-lS6mtccEmUaodsWiRORGM/MGKT0jgzcy5v+eY6pzOPxEgzTHUDhca+WGxShFqKxmF4oneRxXjww1gkvMrWq6uw=="], + "@tanstack/react-devtools": ["@tanstack/react-devtools@0.10.10", "", { "dependencies": { "@tanstack/devtools": "0.14.0" }, "peerDependencies": { "@types/react": ">=16.8", "@types/react-dom": ">=16.8", "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-hYH34MSVbajs1pUc22ftSapyKQp2gOh0w34a7ouYOdIcbXD6YPckSR2YpAWGq1rrs6N0l/rvV6LM/G1pgN5ARg=="], - "@tanstack/react-form": ["@tanstack/react-form@1.33.2", "", { "dependencies": { "@tanstack/form-core": "1.33.2", "@tanstack/react-store": "^0.11.0" }, "peerDependencies": { "@tanstack/react-start": "*", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@tanstack/react-start"] }, "sha512-nEfayOu+27q5cZ5E0G5dmnddqLcLjdFCatbL/LCs/iLD469a1o1yYJlr8RISV3GfnqsBpm0hf+8kM4okh5fPCw=="], + "@tanstack/react-form": ["@tanstack/react-form@1.33.5", "", { "dependencies": { "@tanstack/form-core": "1.33.5", "@tanstack/react-store": "^0.11.0" }, "peerDependencies": { "@tanstack/react-start": "*", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@tanstack/react-start"] }, "sha512-LlRB28qJwO/QCGaHvWnbdh4haBgTFiZVmzA2uzxSBS3YA7/IqrQ6HOBK70CkFQ+DbflZ7NawsmSln13h5iIdTA=="], "@tanstack/react-query": ["@tanstack/react-query@5.90.20", "", { "dependencies": { "@tanstack/query-core": "5.90.20" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw=="], @@ -1312,15 +1322,17 @@ "@types/node-forge": ["@types/node-forge@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw=="], - "@types/pg": ["@types/pg@8.20.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow=="], + "@types/parse-path": ["@types/parse-path@7.1.0", "", { "dependencies": { "parse-path": "*" } }, "sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q=="], + + "@types/pg": ["@types/pg@8.23.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-gPGYzOgqj8wcPJ3GSojYWD1i3wjym3ezWgis/Zk2gY8u29x9tEsP3oaBYFwqsFYmKOm6sawDu3IHNwladQc0ig=="], "@types/qs": ["@types/qs@6.15.1", "", {}, "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw=="], "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], - "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], + "@types/react": ["@types/react@19.2.18", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w=="], - "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + "@types/react-dom": ["@types/react-dom@19.2.4", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw=="], "@types/retry": ["@types/retry@0.12.2", "", {}, "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow=="], @@ -1358,6 +1370,8 @@ "@vitest/utils": ["@vitest/utils@4.1.10", "", { "dependencies": { "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA=="], + "@wagmi/core": ["@wagmi/core@2.22.1", "", { "dependencies": { "eventemitter3": "5.0.1", "mipd": "0.0.7", "zustand": "5.0.0" }, "peerDependencies": { "@tanstack/query-core": ">=5.0.0", "typescript": ">=5.0.4", "viem": "2.x" }, "optionalPeers": ["@tanstack/query-core", "typescript"] }, "sha512-cG/xwQWsBEcKgRTkQVhH29cbpbs/TdcUJVFXCyri3ZknxhMyGv0YEjTcrNpRgt2SaswL1KrvslSNYKKo+5YEAg=="], + "@webassemblyjs/ast": ["@webassemblyjs/ast@1.14.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ=="], "@webassemblyjs/floating-point-hex-parser": ["@webassemblyjs/floating-point-hex-parser@1.13.2", "", {}, "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA=="], @@ -1392,61 +1406,67 @@ "@xtuc/long": ["@xtuc/long@4.2.2", "", {}, "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="], - "@yuku-codegen/binding-darwin-arm64": ["@yuku-codegen/binding-darwin-arm64@0.8.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7cSJH6PaKLRBdCfiB4pM6EukvgOk5xV4tyuLOIOEqrHsbnV7brtyff7CjhZbeGozdIHoOnKOi5R7rrmCWN3QSw=="], + "@yuku-codegen/binding-android-arm64": ["@yuku-codegen/binding-android-arm64@0.8.7", "", { "os": "android", "cpu": "arm64" }, "sha512-C/0zV5IhgVdYhGJTwrY0v8dknxlhiKwtVJkMUaexu9/QvRmzlV4vfU3hZlUSgqc2BxQHntL1mCVbDq8j0FRFDw=="], + + "@yuku-codegen/binding-darwin-arm64": ["@yuku-codegen/binding-darwin-arm64@0.8.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/u+REDMI4a0/lsJXTM4c53/w31OGZLOReZIyg62uhgLs0kc8NHsj/nOcxTdlQjq5gi0zhdkccD9LaLTXcdzPvw=="], - "@yuku-codegen/binding-darwin-x64": ["@yuku-codegen/binding-darwin-x64@0.8.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-mhooLL+L5ytMxgz4ueXCIirU796X2xj97d4KSQW1HxZGzX6h8wOk5bIAhGqcmOL2bqAmMZ+UkBTbPC9VpzKb/g=="], + "@yuku-codegen/binding-darwin-x64": ["@yuku-codegen/binding-darwin-x64@0.8.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-sMMzFOwCo4WXR+/6zIBThOocSC50iIIZZdfiIDbaLvj0Ax/rWt/iavyfEAqajyvzydLyCqR/ZItdLWSRlu1umw=="], - "@yuku-codegen/binding-freebsd-x64": ["@yuku-codegen/binding-freebsd-x64@0.8.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MHLOAlgGhdOh0ZfnWWnno4ljlFB04/Lox/7MIGYIvISMKFvejfC9Atb1t9G7pTVBvy+l5uqxzHGBSJUsDOOkTA=="], + "@yuku-codegen/binding-freebsd-x64": ["@yuku-codegen/binding-freebsd-x64@0.8.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-MpdpKXix9P+Y1rKgjvcNeNtGjXeL1CmttNhYINrWls8kRpm4xM/oBGTmn6w7to8lAlwj5jm8q03dQPl5mRv4Qw=="], - "@yuku-codegen/binding-linux-arm-gnu": ["@yuku-codegen/binding-linux-arm-gnu@0.8.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Ur3Awo45Sc5/Fglr8WN5XIf4IwAsq8wLd917Du8ow6mStxsBTHqFiH+tT7d5jV0FqcJnwy8EHVczmgde0zTo1A=="], + "@yuku-codegen/binding-linux-arm-gnu": ["@yuku-codegen/binding-linux-arm-gnu@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-rr1srFLlPAmC1vtxfc9C1YLDe3iH09YjfSeeIidBqKhzx1MATjOAq4mjlRUOnhr/L27MotWIYOFKwVsd5JZFOg=="], - "@yuku-codegen/binding-linux-arm-musl": ["@yuku-codegen/binding-linux-arm-musl@0.8.0", "", { "os": "linux", "cpu": "arm" }, "sha512-FIy7Ttx8oeUCd/8Y6IjnOsu+lRc6En+V/H67BlVphOeCySZAo5LU8VWrb4tv0DvjaSOzdm3DmdmRzmzN7NCqWQ=="], + "@yuku-codegen/binding-linux-arm-musl": ["@yuku-codegen/binding-linux-arm-musl@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-eAufXh8qBRpiSO6ueaMDL+yyoXIGLhpUce72YbcACtZU2qhExwBIJyEtQf5kH2Ki0X2aqkSjSfog4OPtKXan3Q=="], - "@yuku-codegen/binding-linux-arm64-gnu": ["@yuku-codegen/binding-linux-arm64-gnu@0.8.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-s+25wl1TLvf+7LzasPEi1RR1sDfVAU0i1QH601mn3vj+HudFYBYNZtUBKFaZvl645QR6vcaDaGHnOaMZhBR3ig=="], + "@yuku-codegen/binding-linux-arm64-gnu": ["@yuku-codegen/binding-linux-arm64-gnu@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-gw4w6wPoHObBrdIC4duVWLmJOvpdE25j5D7yrM5mACNlK4klRz/lv8hK+ssQk9EJHBgZjSaqZIJVFgqNYbfv7A=="], - "@yuku-codegen/binding-linux-arm64-musl": ["@yuku-codegen/binding-linux-arm64-musl@0.8.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-pRha3Cjm4AnA5wEuhpg+8XXoGfwz5X61/9a/VNxeau47+kH6xjJspkEloIy/HbHgUnvVRqVHoEHczdGZT2J9NA=="], + "@yuku-codegen/binding-linux-arm64-musl": ["@yuku-codegen/binding-linux-arm64-musl@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-L68N6Y4XkqcIaKo3Ra88JEvBEH4AHff44A4INcrxeVWZ8CZtu2tCpfVxe3hR8qQQMcvBSQlDn24KpkCKEhVvfA=="], - "@yuku-codegen/binding-linux-x64-gnu": ["@yuku-codegen/binding-linux-x64-gnu@0.8.0", "", { "os": "linux", "cpu": "x64" }, "sha512-GySXmiw5Dw99Ba3GMV2ExQVckihuangnrIpSJagPx8RFUNtwfmzpr2ibf8k31eEAE4YUofV7mEfJN5tN6+POnQ=="], + "@yuku-codegen/binding-linux-x64-gnu": ["@yuku-codegen/binding-linux-x64-gnu@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-dzyAbltJmf3Cqlb8HcFuYIf5Yn0fl1vTr3XJ9HiVNNvOlhqPSArOqtw9vI1p6/VTXTjrMLXlU+s+/kNHiIy/Cw=="], - "@yuku-codegen/binding-linux-x64-musl": ["@yuku-codegen/binding-linux-x64-musl@0.8.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8YFfcPZz44v8YyekWVKkNz/cD4t6DW62/dr03OVtMfwUtHfmo/8wpby0JKMleAOXbgWItSGw81LtwqM4I9oS0A=="], + "@yuku-codegen/binding-linux-x64-musl": ["@yuku-codegen/binding-linux-x64-musl@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-Otw4MH3404q0Bbvl+YTdW9aoUV5vXmUw8260bWvt1XlaoIX/ceSgI4ygheRDMfBPoHt6FDayQaO9OLVUZAgkFA=="], - "@yuku-codegen/binding-win32-arm64": ["@yuku-codegen/binding-win32-arm64@0.8.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-YhENbgkuzjsil+zDNV35oU3PQMDg2RXh8BPt915WCNAbIIHcqgYupHJF3564206+DbPkZtcDvcoa34Wb5B8tbQ=="], + "@yuku-codegen/binding-win32-arm64": ["@yuku-codegen/binding-win32-arm64@0.8.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-qo/jyrzryiBuKEsFiuWaBCBe3tRMynQ0qFWFgOEjcCMQeZfBm+wKiVEUEFXXLc7bh8YezguAWp0Mtnhq4ARNyA=="], - "@yuku-codegen/binding-win32-x64": ["@yuku-codegen/binding-win32-x64@0.8.0", "", { "os": "win32", "cpu": "x64" }, "sha512-qvzSRABXe6/ndubx+RNwgbFVbs7Pqroz/q/UR6vm++xsmfcpkMF43B23jgNl2xi2IUrEt8C/L1bBslXp+LtgnA=="], + "@yuku-codegen/binding-win32-x64": ["@yuku-codegen/binding-win32-x64@0.8.7", "", { "os": "win32", "cpu": "x64" }, "sha512-D5lDsVDx6m00E6bWySlWdH72Ca4TPSaphDqB6QjU6MpuNLIJqoGoatYyq2rOmBE8Zv/kunot/o58KGL03P3eiA=="], - "@yuku-parser/binding-darwin-arm64": ["@yuku-parser/binding-darwin-arm64@0.8.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-04AakSJhI4mPrqhZzXdFyaEDh0YkfeqbnyYY3aCrmxeWfR/Xr8+kFn5sh+wZYN/5HatPniELKHixJuUCUyfBvg=="], + "@yuku-parser/binding-android-arm64": ["@yuku-parser/binding-android-arm64@0.8.7", "", { "os": "android", "cpu": "arm64" }, "sha512-eGKYiUDX7Y0V7tDTmg+JTVnXnjMqfXXsorZ+EDf5kxwchQ3Or1HS14MzI2fw+jFhHR85fCWt+mtX33Yao73hIQ=="], - "@yuku-parser/binding-darwin-x64": ["@yuku-parser/binding-darwin-x64@0.8.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-BQJGI9bDeyb/X2rwhtXoBTQ9EtbkJtteX6C7cZ9jow0pqqmoOufgHPP2+m65GLk2eVNCBcfl3xlTGpJ7RFcviw=="], + "@yuku-parser/binding-darwin-arm64": ["@yuku-parser/binding-darwin-arm64@0.8.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re0RHelKLnjEURulY2/KxW+Ngb8zuNA4BRZuMwgGQNzVumT6u4U2N2hc01oeYVNVof0i7GrXE4UCNBgbpRRnjQ=="], - "@yuku-parser/binding-freebsd-x64": ["@yuku-parser/binding-freebsd-x64@0.8.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-04hmgnU152wya88raI+RQhxZPgwXcHbfdNZLu3x5ggKnJVHLD8xZZLcWIKSs59CiEXL6PKVX1/cx8GoTYlaCaQ=="], + "@yuku-parser/binding-darwin-x64": ["@yuku-parser/binding-darwin-x64@0.8.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Hn8DROtQkjlA1ACbPgj4a7eP9IuVOI504oiTwpkWPbpaDWD9KdmnVYCqW+1LfenNK/g7O9NhWGpXEdaCNX7lIA=="], - "@yuku-parser/binding-linux-arm-gnu": ["@yuku-parser/binding-linux-arm-gnu@0.8.0", "", { "os": "linux", "cpu": "arm" }, "sha512-+cuJWUK13lwce721XGRjz7izSr2Q1U0RlHkDzdkohWpRWlttTqRdxNnaW13nDc47GBDrNsXMHx+KLcebeOeeGg=="], + "@yuku-parser/binding-freebsd-x64": ["@yuku-parser/binding-freebsd-x64@0.8.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-bAP2OV8wRuzplX/jYxv9+vvqQT8JxyNphI8fLfXGL054Xs+4/J5u33cIm3y4rxY8rdoLmmdiJs2Tq7r7lrDRfA=="], - "@yuku-parser/binding-linux-arm-musl": ["@yuku-parser/binding-linux-arm-musl@0.8.0", "", { "os": "linux", "cpu": "arm" }, "sha512-rOVPRqt9cm1YP/wPV+yyZh0FYr6UR/wm/BXslvLuge0LDewVvDrm/AxcDrwWuPAu61Nzdg2rMVfcNnrplCbC1w=="], + "@yuku-parser/binding-linux-arm-gnu": ["@yuku-parser/binding-linux-arm-gnu@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-kTYwJQQgmZeAWdDIWabiReIZMpmfLueIj1tCmjStUtFGhR1Z0qwxonKVfUC4N7h/VhGGzLZ//7O1kgt1QKqgCg=="], - "@yuku-parser/binding-linux-arm64-gnu": ["@yuku-parser/binding-linux-arm64-gnu@0.8.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-pAoIozKr6E+ptpaQz4CZv1O7cay2f4m7kbd+DSQug5MKdUBTZZ18GdWSPvq0fwQYraH9hZlyLqrMaeP5W/ncrA=="], + "@yuku-parser/binding-linux-arm-musl": ["@yuku-parser/binding-linux-arm-musl@0.8.7", "", { "os": "linux", "cpu": "arm" }, "sha512-uL4jE8HPT2BLlxAXyD10LqgPuXa9eDa0BKpCdSANmzIJghq/2eZo3/gQNtaxPZMupWoxjYzSad9IXrwu7aYPXQ=="], - "@yuku-parser/binding-linux-arm64-musl": ["@yuku-parser/binding-linux-arm64-musl@0.8.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-JCAg50ahXuYlrsIi1jmymX9X/9B0JYBRroAHnYttN44tAvCo1PFqukHrw1up6HvEOoLA0OjlM0Zwh60u3Gc/Zg=="], + "@yuku-parser/binding-linux-arm64-gnu": ["@yuku-parser/binding-linux-arm64-gnu@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-3gVN4pWSKZmXiNX7cU164dR9MPvesCHnlH6nPfpK+yQsCuYphjKKplcb4SnZBrhiqmXbgb2HR0c2TS0IZUPhgA=="], - "@yuku-parser/binding-linux-x64-gnu": ["@yuku-parser/binding-linux-x64-gnu@0.8.0", "", { "os": "linux", "cpu": "x64" }, "sha512-tEeVQ14etp7lpUqXzq+X5AlQzFH+m3TVDiCKIq17zxnxJ117DOVvoveWSkSMt/lj68z48SvZUMHe7xLvqtO1lg=="], + "@yuku-parser/binding-linux-arm64-musl": ["@yuku-parser/binding-linux-arm64-musl@0.8.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0mwfEjoLpxzXeZw802Wa4RaELsQiPtWqG6INcy8j4GtvNFtl4LCX3eGO1XLn9pyLAISLzTRyU3zUCBUPin8lg=="], - "@yuku-parser/binding-linux-x64-musl": ["@yuku-parser/binding-linux-x64-musl@0.8.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+UGRYnF37nnbZNMsMjSGDXKUTIxYUmbbk3Lzib88sLK7kg2y80vjchYYoYsDUK9kAgqPWyA9hKGURHy/Kk9Few=="], + "@yuku-parser/binding-linux-x64-gnu": ["@yuku-parser/binding-linux-x64-gnu@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-lnbWdPmerE5D1uH1G4IEZKnPzCrWCStRGrtgpSIe1RibAo5bZIjDbbbPYXmMHCEh4F+x/JaJpElh26a3r+BPbg=="], - "@yuku-parser/binding-win32-arm64": ["@yuku-parser/binding-win32-arm64@0.8.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-l+7Va9/sX1ccRjzjJj6MR0arKsvHB5b419+pKbwzn+/18A/xfccWqmxXrn0C0NyVLlbEb3AV9Is+AbF72O85yA=="], + "@yuku-parser/binding-linux-x64-musl": ["@yuku-parser/binding-linux-x64-musl@0.8.7", "", { "os": "linux", "cpu": "x64" }, "sha512-769uwndMvMzUvATWbAcEvyLHKA+DzhHSCl/obBUrRdYfRo26yxui6S8y3z7uJ+Naup7UKrDxrpK7OnQkxkl9KQ=="], - "@yuku-parser/binding-win32-x64": ["@yuku-parser/binding-win32-x64@0.8.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dolKDTJv2xrWowDBEkvSaDvCsVFZOA7DCUuD+7DatglS68WbuxS4LudU5bzSeAOL5vgPpyGm82469cMLnL+6vQ=="], + "@yuku-parser/binding-win32-arm64": ["@yuku-parser/binding-win32-arm64@0.8.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-mEB/9PlaAkisJ6KWGz0zvywXoU6+80dTlR2LwS7s/jcXXoU6fm2+sitBZXtqu3+Q4DcDgPxM45uWMCzPs0TSRw=="], - "@yuku-toolchain/types": ["@yuku-toolchain/types@0.8.0", "", {}, "sha512-hL/raFM5V9UT2lVE/lIWDTWvANqSB8TvMVp+PgICehBa96KhTl/UpGm370JXaacukR+QnaHM2Yv6O/UcrzAFGg=="], + "@yuku-parser/binding-win32-x64": ["@yuku-parser/binding-win32-x64@0.8.7", "", { "os": "win32", "cpu": "x64" }, "sha512-8vNB2DP0ou61nGb8tc/qfi41gfyDXz1MHr2zqL3nR+cJ6CEbiuWV/l/a/vv151gCgiZLLAyGkQGENpozdg716w=="], + + "@yuku-toolchain/types": ["@yuku-toolchain/types@0.8.7", "", {}, "sha512-2Z53dNxAJL6UvFoIrDZvYf3zlO8s4VJK4O2hhaB4mXVwwpX/7ajtss3cmfqKvamlNLWyt9FSWs4eoYdlbxpnHA=="], "@zorsh/zorsh": ["@zorsh/zorsh@0.5.0", "", {}, "sha512-aQ3jO0uoFfgPLkg68iP2nVzt9yFpBs/vrSiZ5G2TRmpNuhSW0lL1hMvszbalogf4JSuU7lcVFcPiiu/xC8/5AA=="], + "abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], - "acorn": ["acorn@8.17.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg=="], + "acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="], "acorn-walk": ["acorn-walk@8.3.5", "", { "dependencies": { "acorn": "^8.11.0" } }, "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw=="], - "adm-zip": ["adm-zip@0.5.10", "", {}, "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ=="], + "adm-zip": ["adm-zip@0.6.0", "", {}, "sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg=="], "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], @@ -1492,7 +1512,7 @@ "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], - "axios": ["axios@1.18.1", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g=="], + "axios": ["axios@1.19.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw=="], "axios-retry": ["axios-retry@4.5.0", "", { "dependencies": { "is-retry-allowed": "^2.2.0" }, "peerDependencies": { "axios": "0.x || 1.x" } }, "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ=="], @@ -1502,7 +1522,7 @@ "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.11.5", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-xJo6a6YZnwZfnyGmQKWMbVOcii7XRibjOskRh+WJ9UHQoX16xrQrcIgAMQOzfvs8XiLMx6ih/fsLPF73iY2D1A=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.15", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA=="], "batch": ["batch@0.6.1", "", {}, "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw=="], @@ -1510,7 +1530,7 @@ "better-call": ["better-call@1.3.7", "", { "dependencies": { "@better-auth/utils": "^0.4.0", "@better-fetch/fetch": "^1.1.21", "rou3": "^0.7.12", "set-cookie-parser": "^3.0.1" }, "peerDependencies": { "zod": "^4.0.0" }, "optionalPeers": ["zod"] }, "sha512-Al51/hjp2SSp6CRTa3F2ptcx4yQVS1xWKoY6jcVXqNYOap6mHFP2jUBn5EwIL4iIed1/Sq4hlQ+Umm6EflZG+w=="], - "better-near-auth": ["better-near-auth@1.8.2", "", { "dependencies": { "@scure/base": "^2.2.0", "nanostores": "^1.1.0", "near-kit": "^0.14.0", "zod": "^4.4.3" }, "peerDependencies": { "@better-auth/core": "^1.6.25", "better-auth": "^1.6.25", "typescript": "^5.9.3" } }, "sha512-sZWGaIaFZejENw7Upe77Hg3r6xjG/pN0QOGfvrDUvYoRXbcCLsvdlM0Ra+FLwWYZzidcCGzkarFwJ1ehaoAu8g=="], + "better-near-auth": ["better-near-auth@1.10.0", "", { "dependencies": { "@scure/base": "^2.2.0", "nanostores": "^1.1.0", "near-kit": "^0.14.0", "zod": "^4.4.3" }, "peerDependencies": { "@better-auth/core": "^1.6.25", "better-auth": "^1.6.25", "typescript": "^5.9.3" } }, "sha512-qLMm+pM+3wUHewLvEwpgT0LDFAIXhRwoKr0IgNcKC0Eu+DALaqe6x8KGX7FmIffY9ETevGubNjth2cmG1AaOMg=="], "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], @@ -1520,13 +1540,13 @@ "body-parser": ["body-parser@1.20.6", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g=="], - "bonjour-service": ["bonjour-service@1.4.3", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "sha512-2Kd5UYlFUVgAKMTyuBLl6w49wqfOnbxHqmuH0oCl/n7TfAikR0zoowNOP5BU4dfXmm+Vr9JyEN370auSMx+CNg=="], + "bonjour-service": ["bonjour-service@1.4.4", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "sha512-jCZcVv7eoc4QesRscwEZtSROBen+6LpKAmBIsQYQrsAeVHLyMXWX/t6eIV5KiRZYNUBl8eVqImEEMQ8L5+c/Kw=="], - "brace-expansion": ["brace-expansion@5.0.8", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg=="], + "brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.28.7", "", { "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", "electron-to-chromium": "^1.5.393", "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw=="], + "browserslist": ["browserslist@4.28.8", "", { "dependencies": { "baseline-browser-mapping": "^2.11.12", "caniuse-lite": "^1.0.30001809", "electron-to-chromium": "^1.5.402", "node-releases": "^2.0.53", "update-browserslist-db": "^1.3.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], @@ -1544,7 +1564,7 @@ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - "caniuse-lite": ["caniuse-lite@1.0.30001806", "", {}, "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001809", "", {}, "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -1624,15 +1644,15 @@ "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], - "dayjs": ["dayjs@1.11.21", "", {}, "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA=="], + "dayjs": ["dayjs@1.11.23", "", {}, "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ=="], "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], - "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], + "default-browser": ["default-browser@5.5.1", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw=="], "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], @@ -1684,7 +1704,7 @@ "effect": ["effect@3.21.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg=="], - "electron-to-chromium": ["electron-to-chromium@1.5.396", "", {}, "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ=="], + "electron-to-chromium": ["electron-to-chromium@1.5.408", "", {}, "sha512-SLoprcYpJ/OH2v2ps0+N5biv9H4/KBT3+YmmDew64TwK5y9j2wv7pMOFY7IorVkyMtEyLSCRlXKLsNlakeAlPw=="], "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], @@ -1694,7 +1714,7 @@ "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], - "enhanced-resolve": ["enhanced-resolve@5.24.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ=="], + "enhanced-resolve": ["enhanced-resolve@5.24.5", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A=="], "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], @@ -1706,15 +1726,15 @@ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-module-lexer": ["es-module-lexer@2.3.1", "", {}, "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA=="], + "es-module-lexer": ["es-module-lexer@2.3.2", "", {}, "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw=="], "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "es-toolkit": ["es-toolkit@1.50.0", "", {}, "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w=="], + "es-toolkit": ["es-toolkit@1.51.0", "", {}, "sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw=="], - "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + "esbuild": ["esbuild@0.28.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.2", "@esbuild/android-arm": "0.28.2", "@esbuild/android-arm64": "0.28.2", "@esbuild/android-x64": "0.28.2", "@esbuild/darwin-arm64": "0.28.2", "@esbuild/darwin-x64": "0.28.2", "@esbuild/freebsd-arm64": "0.28.2", "@esbuild/freebsd-x64": "0.28.2", "@esbuild/linux-arm": "0.28.2", "@esbuild/linux-arm64": "0.28.2", "@esbuild/linux-ia32": "0.28.2", "@esbuild/linux-loong64": "0.28.2", "@esbuild/linux-mips64el": "0.28.2", "@esbuild/linux-ppc64": "0.28.2", "@esbuild/linux-riscv64": "0.28.2", "@esbuild/linux-s390x": "0.28.2", "@esbuild/linux-x64": "0.28.2", "@esbuild/netbsd-arm64": "0.28.2", "@esbuild/netbsd-x64": "0.28.2", "@esbuild/openbsd-arm64": "0.28.2", "@esbuild/openbsd-x64": "0.28.2", "@esbuild/openharmony-arm64": "0.28.2", "@esbuild/sunos-x64": "0.28.2", "@esbuild/win32-arm64": "0.28.2", "@esbuild/win32-ia32": "0.28.2", "@esbuild/win32-x64": "0.28.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1736,13 +1756,13 @@ "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], - "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "eventsource": ["eventsource@4.1.0", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-2GuF51iuHX6A9xdTccMTsNb7VO0lHZihApxhvQzJB5A03DvHDd2FQepodbMaztPBmBcE/ox7o2gqaxGhYB9LhQ=="], + "eventsource": ["eventsource@4.1.1", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-D6bTRWh6KahHTK/m4WnjPQyEinNPf9eFLEZSEoj7d6fTibspnAVYfzHvirL7u/aoX5d9YYfIkBVAhmigUELk9w=="], - "eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="], + "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], "every-plugin": ["every-plugin@workspace:packages/every-plugin"], @@ -1756,7 +1776,7 @@ "express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], - "exsolve": ["exsolve@1.1.0", "", {}, "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw=="], + "exsolve": ["exsolve@1.1.1", "", {}, "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], @@ -1768,7 +1788,7 @@ "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - "fast-uri": ["fast-uri@3.1.4", "", {}, "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw=="], + "fast-uri": ["fast-uri@3.1.5", "", {}, "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw=="], "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], @@ -1792,7 +1812,7 @@ "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "flatted": ["flatted@3.4.3", "", {}, "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ=="], + "flatted": ["flatted@3.4.4", "", {}, "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q=="], "follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="], @@ -1802,7 +1822,7 @@ "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "framer-motion": ["framer-motion@12.42.2", "", { "dependencies": { "motion-dom": "^12.42.2", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw=="], + "framer-motion": ["framer-motion@12.43.0", "", { "dependencies": { "motion-dom": "^12.43.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g=="], "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], @@ -1824,13 +1844,13 @@ "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], - "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], + "get-tsconfig": ["get-tsconfig@4.14.3", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA=="], "giget": ["giget@3.3.1", "", { "bin": { "giget": "dist/cli.mjs" } }, "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg=="], - "git-up": ["git-up@7.0.0", "", { "dependencies": { "is-ssh": "^1.4.0", "parse-url": "^8.1.0" } }, "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ=="], + "git-up": ["git-up@8.1.1", "", { "dependencies": { "is-ssh": "^1.4.0", "parse-url": "^9.2.0" } }, "sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g=="], - "git-url-parse": ["git-url-parse@15.0.0", "", { "dependencies": { "git-up": "^7.0.0" } }, "sha512-5reeBufLi+i4QD3ZFftcJs9jC26aULFLBU23FeKM/b1rI0K6ofIeAblmDVO7Ht22zTDE9+CkJ3ZVb0CgJmz3UQ=="], + "git-url-parse": ["git-url-parse@16.1.0", "", { "dependencies": { "git-up": "^8.1.0" } }, "sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw=="], "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], @@ -1878,9 +1898,9 @@ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="], + "highlight.js": ["highlight.js@11.11.2", "", {}, "sha512-oaXMACAU0kzOMXBjWpNcX+vlwSBCIAiZ9BHa7gA15NOTtT2L/l8OSZDuqS2XppOhZBPJ7hm4o8ep2kyuip2uEQ=="], - "hono": ["hono@4.12.32", "", {}, "sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg=="], + "hono": ["hono@4.13.2", "", {}, "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA=="], "hono-rate-limiter": ["hono-rate-limiter@0.5.3", "", { "peerDependencies": { "hono": "^4.10.8", "unstorage": "^1.17.3" }, "optionalPeers": ["unstorage"] }, "sha512-M0DxbVMpPELEzLi0AJg1XyBHLGJXz7GySjsPoK+gc5YeeBsdGDGe+2RvVuCAv8ydINiwlbxqYMNxUEyYfRji/A=="], @@ -1926,7 +1946,7 @@ "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], - "ipaddr.js": ["ipaddr.js@2.4.0", "", {}, "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ=="], + "ipaddr.js": ["ipaddr.js@2.5.0", "", {}, "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w=="], "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], @@ -1980,17 +2000,19 @@ "isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="], + "isows": ["isows@1.0.7", "", { "peerDependencies": { "ws": "*" } }, "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg=="], + "jest-worker": ["jest-worker@27.5.1", "", { "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg=="], "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "jose": ["jose@6.2.4", "", {}, "sha512-N8acGzVsQy6M/fjFcxtysNc4Q379TcM5dM/qKkNtsHFji88yANnXTr7BLeP75iPnFwBfQzM/jg2BZ9+HZrHCZA=="], + "jose": ["jose@6.2.9", "", {}, "sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA=="], - "js-base64": ["js-base64@3.9.2", "", {}, "sha512-6zayE8QlUdiweYI6cETD/XBSqFcoCUlufn/29PJR99r82x1yDnIprRca0YvAYpAW+ez0GuQkVBC6xG5QkD7OjA=="], + "js-base64": ["js-base64@3.9.3", "", {}, "sha512-uwYQp+VJ38FVvtim6qNbit6e9uT6dwWQ4Y1+H9TxhW5hcHjpHwoxlR0nMpqUmIFOmu4VqMxwdJA88gIVuZJQ/g=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "js-yaml": ["js-yaml@4.3.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q=="], + "js-yaml": ["js-yaml@4.3.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], @@ -2006,35 +2028,35 @@ "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], - "kysely": ["kysely@0.28.17", "", {}, "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q=="], + "kysely": ["kysely@0.29.5", "", {}, "sha512-ooa+eSbBNPTo3MycPEuW5jdrxQdQwdtB3LC3h43FiXQbIry5tR0C5lDG7eealK0E4D7XjrnOP5DIUg/LyjRMYQ=="], "launch-editor": ["launch-editor@2.14.1", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.4" } }, "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA=="], "libsql": ["libsql@0.5.29", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.29", "@libsql/darwin-x64": "0.5.29", "@libsql/linux-arm-gnueabihf": "0.5.29", "@libsql/linux-arm-musleabihf": "0.5.29", "@libsql/linux-arm64-gnu": "0.5.29", "@libsql/linux-arm64-musl": "0.5.29", "@libsql/linux-x64-gnu": "0.5.29", "@libsql/linux-x64-musl": "0.5.29", "@libsql/win32-x64-msvc": "0.5.29" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg=="], - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.33.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg=="], - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.33.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ=="], - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.33.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg=="], - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.33.0", "", { "os": "linux", "cpu": "arm" }, "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ=="], - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg=="], - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.33.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ=="], - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg=="], - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.33.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw=="], - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.33.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA=="], - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="], "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], @@ -2046,7 +2068,7 @@ "lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], - "lucide-react": ["lucide-react@1.27.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-rJicGl/3Fly/E0rOH1YmPZ6e49JCnKknh1ox1vpHnkfjujAkKA6sqUZvH3MTAaXXjgexyUwgNwTJzTtYuAFYJw=="], + "lucide-react": ["lucide-react@1.31.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-G8u2eEtoHUnUa9f8lbvqDhCiORMnYLdUEo06EEG9MQvHQrInKcX3Pa2TH39MM5qyzRcWETxB0+aOwAPI1g1kEg=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -2088,7 +2110,7 @@ "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], - "memfs": ["memfs@4.64.0", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.64.0", "@jsonjoy.com/fs-fsa": "4.64.0", "@jsonjoy.com/fs-node": "4.64.0", "@jsonjoy.com/fs-node-builtins": "4.64.0", "@jsonjoy.com/fs-node-to-fsa": "4.64.0", "@jsonjoy.com/fs-node-utils": "4.64.0", "@jsonjoy.com/fs-print": "4.64.0", "@jsonjoy.com/fs-snapshot": "4.64.0", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" } }, "sha512-Kw72fgY7Wn+sD8KmtNWSafl1dz0UvAsE/PHs3YVfLiaZuA3HxNm9sRLqAu0ATiBGJvME1PxZXbBZPv5GycDeAw=="], + "memfs": ["memfs@4.68.1", "", { "dependencies": { "@jsonjoy.com/fs-core": "4.68.1", "@jsonjoy.com/fs-fsa": "4.68.1", "@jsonjoy.com/fs-node": "4.68.1", "@jsonjoy.com/fs-node-builtins": "4.68.1", "@jsonjoy.com/fs-node-to-fsa": "4.68.1", "@jsonjoy.com/fs-node-utils": "4.68.1", "@jsonjoy.com/fs-print": "4.68.1", "@jsonjoy.com/fs-snapshot": "4.68.1", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", "thingies": "^2.5.0", "tree-dump": "^1.0.3", "tslib": "^2.0.0" } }, "sha512-OD+IDRUvIxu3QHL+nFm9gdyugInD27FDJ+sl4B5QgomPHXMlbw+GP918P8VNKu2FkNlVeqBkpzkwROpamVifRw=="], "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], @@ -2166,7 +2188,7 @@ "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], - "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], "minimizer-webpack-plugin": ["minimizer-webpack-plugin@5.6.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "terser": "^5.31.1" }, "peerDependencies": { "@minify-html/node": "*", "@swc/core": "*", "@swc/css": "*", "@swc/html": "*", "clean-css": "*", "cssnano": "*", "csso": "*", "esbuild": "*", "html-minifier-terser": "*", "lightningcss": "*", "postcss": "*", "uglify-js": "*", "webpack": "^5.1.0" }, "optionalPeers": ["@minify-html/node", "@swc/core", "@swc/css", "@swc/html", "clean-css", "cssnano", "csso", "esbuild", "html-minifier-terser", "lightningcss", "postcss", "uglify-js"] }, "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw=="], @@ -2174,7 +2196,9 @@ "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], - "motion-dom": ["motion-dom@12.42.2", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA=="], + "mipd": ["mipd@0.0.7", "", { "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg=="], + + "motion-dom": ["motion-dom@12.43.0", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag=="], "motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="], @@ -2194,7 +2218,7 @@ "nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], - "nanostores": ["nanostores@1.4.1", "", {}, "sha512-PGd3uPojJB9Z07d5NX3Db/SOSBbyy3wLMUGq0GpnEEJfVzY9mq7daPMAZ3jObV5D3Jn+YKND636eI5ULg7F80Q=="], + "nanostores": ["nanostores@1.5.1", "", {}, "sha512-DNIX+HyFpo14fKGe0NsX9/aPzdKGiSZwX5xEMpDwQrDdo2iTiUYunOCCQLYwJrziKVe8ZOtVvcv0dEVI8lMx2g=="], "near-kit": ["near-kit@0.14.0", "", { "dependencies": { "@napi-rs/keyring": "^1.3.0", "@noble/curves": "^2.2.0", "@noble/hashes": "^2.0.1", "@scure/base": "^2.2.0", "@scure/bip32": "^2.2.0", "@scure/bip39": "^2.2.0", "@zorsh/zorsh": "^0.5.0", "tar": "^7.5.13", "zod": "^4.4.3" }, "peerDependencies": { "@hot-labs/near-connect": ">=0.11.0", "@near-wallet-selector/core": ">=8.0.0" }, "optionalPeers": ["@hot-labs/near-connect", "@near-wallet-selector/core"] }, "sha512-RWv/CNhctWJeIxIFDgBXbxym24U93huJYy8zK0u5uEVtrqsb8gwkJ2nRz80tJuoBT16xQbTJi3dzhk6mrWoZuw=="], @@ -2218,7 +2242,7 @@ "node-persist": ["node-persist@4.0.4", "", { "dependencies": { "p-limit": "^3.1.0" } }, "sha512-8sPAz/7tw1mCCc8xBG4f0wi+flHkSSgQeX998iQ75Pu27evA6UUWCjSE7xnrYTg2q33oU5leJ061EKPDv6BocQ=="], - "node-releases": ["node-releases@2.0.51", "", {}, "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ=="], + "node-releases": ["node-releases@2.0.53", "", {}, "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], @@ -2230,7 +2254,7 @@ "obug": ["obug@2.1.4", "", {}, "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA=="], - "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + "ohash": ["ohash@2.0.12", "", {}, "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw=="], "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], @@ -2246,6 +2270,8 @@ "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], + "ox": ["ox@0.14.33", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ=="], + "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], @@ -2266,7 +2292,7 @@ "parse-path": ["parse-path@7.1.0", "", { "dependencies": { "protocols": "^2.0.0" } }, "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw=="], - "parse-url": ["parse-url@8.1.0", "", { "dependencies": { "parse-path": "^7.0.0" } }, "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w=="], + "parse-url": ["parse-url@9.2.0", "", { "dependencies": { "@types/parse-path": "^7.0.0", "parse-path": "^7.0.0" } }, "sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ=="], "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], @@ -2286,7 +2312,7 @@ "perfect-debounce": ["perfect-debounce@2.1.0", "", {}, "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g=="], - "pg": ["pg@8.22.0", "", { "dependencies": { "pg-connection-string": "^2.14.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.15.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA=="], + "pg": ["pg@8.23.0", "", { "dependencies": { "pg-connection-string": "^2.14.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.16.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg=="], "pg-cloudflare": ["pg-cloudflare@1.4.0", "", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="], @@ -2296,7 +2322,7 @@ "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="], - "pg-protocol": ["pg-protocol@1.15.0", "", {}, "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ=="], + "pg-protocol": ["pg-protocol@1.16.0", "", {}, "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg=="], "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], @@ -2310,11 +2336,11 @@ "pkg-types": ["pkg-types@2.3.1", "", { "dependencies": { "confbox": "^0.2.4", "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg=="], - "playwright": ["playwright@1.62.0", "", { "dependencies": { "playwright-core": "1.62.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-Z14dG305dgaLu6foB1TXQagFiW8JfSUIUaUuPaKQ6NtBPKF1P/qXcqfh6c6K/icPqdy37JmjbiBXf6JNg6Sylw=="], + "playwright": ["playwright@1.62.1", "", { "dependencies": { "playwright-core": "1.62.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg=="], - "playwright-core": ["playwright-core@1.62.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-nsNRyq0r2zsG8AcRHWknc9QRA5XCueC7gWMrs+Gx2tlZn9hcl8zudfh00lhJPY1DE7NmZ6bDsT9g2yey8mXljA=="], + "playwright-core": ["playwright-core@1.62.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw=="], - "postcss": ["postcss@8.5.23", "", { "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg=="], + "postcss": ["postcss@8.5.26", "", { "dependencies": { "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ=="], "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], @@ -2348,7 +2374,7 @@ "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], - "pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="], + "pvutils": ["pvutils@1.2.0", "", {}, "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg=="], "qs": ["qs@6.15.3", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A=="], @@ -2392,7 +2418,7 @@ "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - "recast": ["recast@0.23.12", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA=="], + "recast": ["recast@0.23.21", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw=="], "reflect-metadata": ["reflect-metadata@0.2.2", "", {}, "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="], @@ -2424,7 +2450,7 @@ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "rolldown": ["rolldown@1.2.0", "", { "dependencies": { "@oxc-project/types": "=0.140.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.0", "@rolldown/binding-darwin-arm64": "1.2.0", "@rolldown/binding-darwin-x64": "1.2.0", "@rolldown/binding-freebsd-x64": "1.2.0", "@rolldown/binding-linux-arm-gnueabihf": "1.2.0", "@rolldown/binding-linux-arm64-gnu": "1.2.0", "@rolldown/binding-linux-arm64-musl": "1.2.0", "@rolldown/binding-linux-ppc64-gnu": "1.2.0", "@rolldown/binding-linux-s390x-gnu": "1.2.0", "@rolldown/binding-linux-x64-gnu": "1.2.0", "@rolldown/binding-linux-x64-musl": "1.2.0", "@rolldown/binding-openharmony-arm64": "1.2.0", "@rolldown/binding-wasm32-wasi": "1.2.0", "@rolldown/binding-win32-arm64-msvc": "1.2.0", "@rolldown/binding-win32-x64-msvc": "1.2.0" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA=="], + "rolldown": ["rolldown@1.2.4", "", { "dependencies": { "@oxc-project/types": "=0.144.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.2.4", "@rolldown/binding-darwin-arm64": "1.2.4", "@rolldown/binding-darwin-x64": "1.2.4", "@rolldown/binding-freebsd-x64": "1.2.4", "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", "@rolldown/binding-linux-arm64-gnu": "1.2.4", "@rolldown/binding-linux-arm64-musl": "1.2.4", "@rolldown/binding-linux-ppc64-gnu": "1.2.4", "@rolldown/binding-linux-s390x-gnu": "1.2.4", "@rolldown/binding-linux-x64-gnu": "1.2.4", "@rolldown/binding-linux-x64-musl": "1.2.4", "@rolldown/binding-openharmony-arm64": "1.2.4", "@rolldown/binding-win32-arm64-msvc": "1.2.4", "@rolldown/binding-win32-x64-msvc": "1.2.4" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w=="], "rolldown-plugin-dts": ["rolldown-plugin-dts@0.27.14", "", { "dependencies": { "dts-resolver": "^3.0.0", "get-tsconfig": "5.0.0-beta.5", "obug": "^2.1.4", "yuku-ast": "^0.8.0", "yuku-codegen": "^0.8.0", "yuku-parser": "^0.8.0" }, "peerDependencies": { "@typescript/native-preview": "*", "@volar/typescript": "~2.4.0", "rolldown": "^1.0.0", "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", "vue-tsc": "~3.2.0 || ~3.3.0" }, "optionalPeers": ["@typescript/native-preview", "@volar/typescript", "typescript", "vue-tsc"] }, "sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw=="], @@ -2450,9 +2476,9 @@ "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], - "seroval": ["seroval@1.5.6", "", {}, "sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA=="], + "seroval": ["seroval@1.6.2", "", {}, "sha512-mPT+SD2TrlB6wvte1KkYOYUkubaTbd6pZ/6Kk3C9nxzrHmCZyhxOO7XGAeL7f+yLKZglzGtM9odUVvg/EhO+vQ=="], - "seroval-plugins": ["seroval-plugins@1.5.6", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ=="], + "seroval-plugins": ["seroval-plugins@1.6.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-TfxuUjlbBESzUOWdTkTKqvSmav0ABym+itetDXLK6mDz8SmrpdI30aF8RTXE8Bvq+tH/1yIDkvy3W0lfQb1ipQ=="], "serve-index": ["serve-index@1.9.2", "", { "dependencies": { "accepts": "~1.3.8", "batch": "0.6.1", "debug": "2.6.9", "escape-html": "~1.0.3", "http-errors": "~1.8.0", "mime-types": "~2.1.35", "parseurl": "~1.3.3" } }, "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ=="], @@ -2494,7 +2520,7 @@ "solid-js": ["solid-js@1.9.14", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.4", "seroval-plugins": "~1.5.4" } }, "sha512-sAEXC0Kk0S1EDg+8ysEWJDbYhA3RRoEjwuySUGlKIemeo0I5YZfOyumNjNs9Sv3y2nmhD+0rW66ag2HsMuQiGQ=="], - "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + "sonner": ["sonner@2.0.8", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg=="], "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], @@ -2556,7 +2582,7 @@ "terminal-size": ["terminal-size@4.0.1", "", {}, "sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ=="], - "terser": ["terser@5.49.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA=="], + "terser": ["terser@5.50.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-CN9BVxWhgS/hRxtUMjtC2uRWSTcSfQFHMDWma6sKKfIivCD91sM+FOPfvwoaRMqCSrUpe1nv3jDamd9eEQ4y+w=="], "thingies": ["thingies@2.6.1", "", { "peerDependencies": { "tslib": "^2" } }, "sha512-cV/CMGTK3M4MlnJ/0At6ismOw/A0EEniDNScajjz/Br3c1sqE72YD01rGpPTKwd27wAxI5Pr+6+0w8yofzFRYw=="], @@ -2570,13 +2596,13 @@ "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], - "tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], + "tinyexec": ["tinyexec@1.3.0", "", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="], "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], "tinygradient": ["tinygradient@1.1.5", "", { "dependencies": { "@types/tinycolor2": "^1.4.0", "tinycolor2": "^1.0.0" } }, "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw=="], - "tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + "tinyrainbow": ["tinyrainbow@3.1.1", "", {}, "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -2600,7 +2626,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "tsx": ["tsx@4.23.1", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ=="], + "tsx": ["tsx@4.23.12", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q=="], "tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="], @@ -2644,7 +2670,7 @@ "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], - "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "update-browserslist-db": ["update-browserslist-db@1.3.1", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ=="], "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], @@ -2660,13 +2686,15 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "verkit": ["verkit@0.3.1", "", {}, "sha512-w2Eo8LSIIoW7qxNBzT7/17k+bh8plXo7G3dHjEIDqPlnluhzaxr9JX8F28VSYEtDvc1/a3WBDih6xNUZseebXg=="], + "verkit": ["verkit@0.3.2", "", {}, "sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@8.1.5", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.17", "rolldown": "~1.1.5", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.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" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw=="], + "viem": ["viem@2.55.17", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.33", "ws": "8.21.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-XKASpfG9jznxsU/J7Bj2k9aS0UZgU3sBcwVoQ6slD5T1RmNJHh3khmFVJuRKjrOe3uqHb0FJKbb6jwE49WtVDQ=="], + + "vite": ["vite@8.2.1", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.25", "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.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" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw=="], "vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="], @@ -2680,7 +2708,7 @@ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "webpack": ["webpack@5.109.0", "", { "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.24.2", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "graceful-fs": "^4.2.11", "mime-db": "^1.54.0", "minimizer-webpack-plugin": "^5.6.1", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "watchpack": "^2.5.2", "webpack-sources": "^3.5.1" }, "peerDependencies": { "webpack-cli": "*" }, "optionalPeers": ["webpack-cli"], "bin": { "webpack": "bin/webpack.js" } }, "sha512-vomrngskVVXEZF9sMZfYAd4pXZUnfaWdJGlF+BTNF+gJBCKYCQBnOeVPlrh39Ewl7nlCsirDplMy6o5g9xJHBg=="], + "webpack": ["webpack@5.109.2", "", { "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.24.4", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "graceful-fs": "^4.2.11", "mime-db": "^1.54.0", "minimizer-webpack-plugin": "^5.6.1", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", "watchpack": "^2.5.2", "webpack-sources": "^3.5.1" }, "peerDependencies": { "webpack-cli": "*" }, "optionalPeers": ["webpack-cli"], "bin": { "webpack": "bin/webpack.js" } }, "sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw=="], "webpack-bundle-analyzer": ["webpack-bundle-analyzer@4.10.2", "", { "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", "commander": "^7.2.0", "debounce": "^1.2.1", "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { "webpack-bundle-analyzer": "lib/bin/analyzer.js" } }, "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw=="], @@ -2724,25 +2752,25 @@ "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "yuku-ast": ["yuku-ast@0.8.0", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.0" } }, "sha512-trBzFsSa6k32vzNUCH6pFhAoTzWD/NifSYOIQ/6v14vXKh7TRhd2vDNIwRguGdXvcyfBNEEGcsfxFHrnJbS+FQ=="], + "yuku-ast": ["yuku-ast@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7" } }, "sha512-h6+4bDfyootiMB9vckk5uKo5r5j0GHrkr17FQTDNfEsFT3DWlN9uu1HJwQwc64pgmLCI945fWM3lbTIqxjT3GQ=="], - "yuku-codegen": ["yuku-codegen@0.8.0", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.0" }, "optionalDependencies": { "@yuku-codegen/binding-darwin-arm64": "0.8.0", "@yuku-codegen/binding-darwin-x64": "0.8.0", "@yuku-codegen/binding-freebsd-x64": "0.8.0", "@yuku-codegen/binding-linux-arm-gnu": "0.8.0", "@yuku-codegen/binding-linux-arm-musl": "0.8.0", "@yuku-codegen/binding-linux-arm64-gnu": "0.8.0", "@yuku-codegen/binding-linux-arm64-musl": "0.8.0", "@yuku-codegen/binding-linux-x64-gnu": "0.8.0", "@yuku-codegen/binding-linux-x64-musl": "0.8.0", "@yuku-codegen/binding-win32-arm64": "0.8.0", "@yuku-codegen/binding-win32-x64": "0.8.0" } }, "sha512-f82SDo8moLRymtdYN7/cz2yRbWE6Pmbmph+mLj24QkIR8ASG/c2nETdHzBhV/3rR/r8K+qmy7+JqQV3thHAm8g=="], + "yuku-codegen": ["yuku-codegen@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7" }, "optionalDependencies": { "@yuku-codegen/binding-android-arm64": "0.8.7", "@yuku-codegen/binding-darwin-arm64": "0.8.7", "@yuku-codegen/binding-darwin-x64": "0.8.7", "@yuku-codegen/binding-freebsd-x64": "0.8.7", "@yuku-codegen/binding-linux-arm-gnu": "0.8.7", "@yuku-codegen/binding-linux-arm-musl": "0.8.7", "@yuku-codegen/binding-linux-arm64-gnu": "0.8.7", "@yuku-codegen/binding-linux-arm64-musl": "0.8.7", "@yuku-codegen/binding-linux-x64-gnu": "0.8.7", "@yuku-codegen/binding-linux-x64-musl": "0.8.7", "@yuku-codegen/binding-win32-arm64": "0.8.7", "@yuku-codegen/binding-win32-x64": "0.8.7" } }, "sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw=="], - "yuku-parser": ["yuku-parser@0.8.0", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.0", "yuku-ast": "^0.8.0" }, "optionalDependencies": { "@yuku-parser/binding-darwin-arm64": "0.8.0", "@yuku-parser/binding-darwin-x64": "0.8.0", "@yuku-parser/binding-freebsd-x64": "0.8.0", "@yuku-parser/binding-linux-arm-gnu": "0.8.0", "@yuku-parser/binding-linux-arm-musl": "0.8.0", "@yuku-parser/binding-linux-arm64-gnu": "0.8.0", "@yuku-parser/binding-linux-arm64-musl": "0.8.0", "@yuku-parser/binding-linux-x64-gnu": "0.8.0", "@yuku-parser/binding-linux-x64-musl": "0.8.0", "@yuku-parser/binding-win32-arm64": "0.8.0", "@yuku-parser/binding-win32-x64": "0.8.0" } }, "sha512-obrazyE8Cyh79xTQS9wv44khnhvkXG1CDSSy0bg+Pjjla+iXkKXK2b6+LTYXSN3qvVfQxc0MN5qhNKYr9sl3Zw=="], + "yuku-parser": ["yuku-parser@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7", "yuku-ast": "^0.8.7" }, "optionalDependencies": { "@yuku-parser/binding-android-arm64": "0.8.7", "@yuku-parser/binding-darwin-arm64": "0.8.7", "@yuku-parser/binding-darwin-x64": "0.8.7", "@yuku-parser/binding-freebsd-x64": "0.8.7", "@yuku-parser/binding-linux-arm-gnu": "0.8.7", "@yuku-parser/binding-linux-arm-musl": "0.8.7", "@yuku-parser/binding-linux-arm64-gnu": "0.8.7", "@yuku-parser/binding-linux-arm64-musl": "0.8.7", "@yuku-parser/binding-linux-x64-gnu": "0.8.7", "@yuku-parser/binding-linux-x64-musl": "0.8.7", "@yuku-parser/binding-win32-arm64": "0.8.7", "@yuku-parser/binding-win32-x64": "0.8.7" } }, "sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ=="], - "zephyr-agent": ["zephyr-agent@1.1.2", "", { "dependencies": { "@toon-format/toon": "^0.9.0", "axios": "^1.15.0", "axios-retry": "^4.5.0", "debug": "^4.3.4", "eventsource": "^4.0.0", "git-url-parse": "^15.0.0", "https-proxy-agent": "^7.0.6", "is-ci": "^4.1.0", "jose": "^5.10.0", "node-persist": "^4.0.1", "open": "^10.1.0", "proper-lockfile": "^4.1.2", "tslib": "^2.8.1", "zephyr-edge-contract": "1.1.2" } }, "sha512-mJlj9VL2P+wzaLge5sF27LrYBP+wVj+5BiNaQIPTsLSiU9Om3CXHXWTR6ss0w4FIdYazrJyAq2KFw3PgKsBDgA=="], + "zephyr-agent": ["zephyr-agent@1.2.2", "", { "dependencies": { "@toon-format/toon": "^0.9.0", "axios": "^1.18.1", "axios-retry": "^4.5.0", "debug": "^4.4.3", "eventsource": "^4.1.0", "git-url-parse": "^16.1.0", "https-proxy-agent": "^7.0.6", "is-ci": "^4.1.0", "jiti": "^2.7.0", "jose": "^5.10.0", "node-persist": "^4.0.4", "open": "^10.2.0", "proper-lockfile": "^4.1.2", "zephyr-edge-contract": "1.2.2" } }, "sha512-lN/QvWcNj/+5amGqp+FbvpOXGF1yFUptpl8kWSkTMbYiMJGX6qL/5E7QRT9wtD9bgPsqq8HsSG+MkZMPNG3jlw=="], - "zephyr-edge-contract": ["zephyr-edge-contract@1.1.2", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-ZeHYPIrDifKutPzTsjPjHP9JIdPqTpyBESeHHZzG8VSzo6UT58tpwUnDDgJtF/ONwresnD/0ypEYxfBxtcrV6Q=="], + "zephyr-edge-contract": ["zephyr-edge-contract@1.2.2", "", {}, "sha512-2/PdLJ8CBFL7t9L047ejL/adhxv7oVC9kTgvckOuk6EauU9Y1CQ88JsnPB4EkKb/fYjZmurtp43E79MhRU5Rjw=="], - "zephyr-rsbuild-plugin": ["zephyr-rsbuild-plugin@1.1.2", "", { "dependencies": { "zephyr-rspack-plugin": "1.1.2" }, "peerDependencies": { "@rsbuild/core": "^1.0.0 || ^2.0.0-0" } }, "sha512-MhXfQ9oCP76LBDDc8Yl21ridsXFMobvTIjjyYbi+4LpOIof8ap+ZLyVtE14S1w4VyDerZNIzenOEJGbvJ5I/9g=="], + "zephyr-rsbuild-plugin": ["zephyr-rsbuild-plugin@1.2.2", "", { "dependencies": { "zephyr-agent": "1.2.2", "zephyr-rspack-plugin": "1.2.2", "zephyr-xpack-internal": "1.2.2" }, "peerDependencies": { "@rsbuild/core": "^1.0.0 || ^2.0.0-0" } }, "sha512-jeKcM16VHCUutttPgXuj6wPeJ9mT/OVEE/vlHPGOqkmu/vEow8Zf1pVKvurgxP63knoS1lFN8XKzuVZ09Ul9KA=="], - "zephyr-rspack-plugin": ["zephyr-rspack-plugin@1.1.2", "", { "dependencies": { "tslib": "^2.8.1", "zephyr-agent": "1.1.2", "zephyr-xpack-internal": "1.1.2" }, "peerDependencies": { "@rspack/core": "^1.0.0 || ^2.0.0-0" } }, "sha512-OFIghyy2GXaLB/RLJCEWmRoXQf0FNvNB2VQJL3mWofk8oNRu1PxYCxCJ3ThAGN2tRJv0sCjMpzqpuHU+h3Kv/A=="], + "zephyr-rspack-plugin": ["zephyr-rspack-plugin@1.2.2", "", { "dependencies": { "zephyr-agent": "1.2.2", "zephyr-xpack-internal": "1.2.2" }, "peerDependencies": { "@rspack/core": "^1.0.0 || ^2.0.0-0" } }, "sha512-W2Mj9LSLL3HPDdP1WzNnSAp4fnQufSBDzsCPDQy+s9fLZm0kLZMxcS29f06E78t05YyvOg+ZrWqB1y/d7VENYw=="], - "zephyr-xpack-internal": ["zephyr-xpack-internal@1.1.2", "", { "dependencies": { "@module-federation/automatic-vendor-federation": "^1.2.1", "tslib": "^2.8.1", "zephyr-agent": "1.1.2", "zephyr-edge-contract": "1.1.2" } }, "sha512-IYs5aMOxGXH3YGv7Z9uW5pH9/r15UNNEg4CdSuWDDTJ7MskMVmHjSYwM57AYxHvMw/loVXOY1PQDVaUxklOUfQ=="], + "zephyr-xpack-internal": ["zephyr-xpack-internal@1.2.2", "", { "dependencies": { "@module-federation/automatic-vendor-federation": "^1.2.1", "zephyr-agent": "1.2.2", "zephyr-edge-contract": "1.2.2" } }, "sha512-SejAjAhoYX2lSTlFVybeZzbcDeVbk299XIQ458qIx57Vy9u01/CIWeBo14qZam1pEcZT2j/3Xio/6wZfIdk3bg=="], "zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "zustand": ["zustand@5.0.14", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g=="], + "zustand": ["zustand@5.0.15", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], @@ -2752,9 +2780,9 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@effect/platform-node/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "@effect/platform-node/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], - "@effect/platform-node-shared/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "@effect/platform-node-shared/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], @@ -2772,7 +2800,7 @@ "@libsql/hrana-client/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], - "@libsql/isomorphic-ws/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "@libsql/isomorphic-ws/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], @@ -2786,41 +2814,77 @@ "@module-federation/cli/jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], - "@module-federation/dts-plugin/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], - "@module-federation/dts-plugin/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "@module-federation/enhanced/@module-federation/runtime-tools": ["@module-federation/runtime-tools@2.8.0", "", { "dependencies": { "@module-federation/runtime": "2.8.0", "@module-federation/webpack-bundler-runtime": "2.8.0" } }, "sha512-3yOqjdSHXxX4HA3GhlXg3hghGAXW2RJUsnwXCcik2/lTxOHizKI8f3RM+GGCKPxDVqtw43IShe3tA12jNL5A/A=="], + "@module-federation/enhanced/@module-federation/runtime-tools": ["@module-federation/runtime-tools@2.8.2", "", { "dependencies": { "@module-federation/runtime": "2.8.2", "@module-federation/webpack-bundler-runtime": "2.8.2" } }, "sha512-eW/yPvZB2LbpbyPXPTnOeF1ieWl165D9QcPV0y5Bj1QYGDjL2cb+dnzrBp0fmFtJhCYqmAdsVNoYItVa3yuJ3g=="], - "@module-federation/inject-external-runtime-core-plugin/@module-federation/runtime-tools": ["@module-federation/runtime-tools@2.8.0", "", { "dependencies": { "@module-federation/runtime": "2.8.0", "@module-federation/webpack-bundler-runtime": "2.8.0" } }, "sha512-3yOqjdSHXxX4HA3GhlXg3hghGAXW2RJUsnwXCcik2/lTxOHizKI8f3RM+GGCKPxDVqtw43IShe3tA12jNL5A/A=="], + "@module-federation/inject-external-runtime-core-plugin/@module-federation/runtime-tools": ["@module-federation/runtime-tools@2.8.2", "", { "dependencies": { "@module-federation/runtime": "2.8.2", "@module-federation/webpack-bundler-runtime": "2.8.2" } }, "sha512-eW/yPvZB2LbpbyPXPTnOeF1ieWl165D9QcPV0y5Bj1QYGDjL2cb+dnzrBp0fmFtJhCYqmAdsVNoYItVa3yuJ3g=="], - "@module-federation/rspack/@module-federation/runtime-tools": ["@module-federation/runtime-tools@2.8.0", "", { "dependencies": { "@module-federation/runtime": "2.8.0", "@module-federation/webpack-bundler-runtime": "2.8.0" } }, "sha512-3yOqjdSHXxX4HA3GhlXg3hghGAXW2RJUsnwXCcik2/lTxOHizKI8f3RM+GGCKPxDVqtw43IShe3tA12jNL5A/A=="], + "@module-federation/rspack/@module-federation/runtime-tools": ["@module-federation/runtime-tools@2.8.2", "", { "dependencies": { "@module-federation/runtime": "2.8.2", "@module-federation/webpack-bundler-runtime": "2.8.2" } }, "sha512-eW/yPvZB2LbpbyPXPTnOeF1ieWl165D9QcPV0y5Bj1QYGDjL2cb+dnzrBp0fmFtJhCYqmAdsVNoYItVa3yuJ3g=="], "@module-federation/runtime-tools/@module-federation/runtime": ["@module-federation/runtime@0.22.0", "", { "dependencies": { "@module-federation/error-codes": "0.22.0", "@module-federation/runtime-core": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA=="], "@module-federation/runtime-tools/@module-federation/webpack-bundler-runtime": ["@module-federation/webpack-bundler-runtime@0.22.0", "", { "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/sdk": "0.22.0" } }, "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA=="], + "@orpc/client/@orpc/shared": ["@orpc/shared@1.15.0", "", { "dependencies": { "radash": "^12.1.1", "type-fest": "^5.4.4" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-A3/JE7pQYSrrRm6/WYJxV3GBhpMdJRPM3h47slQtWBUZe9Sao5En5WBc4tISGQNP2emcez6qIvDziSgD2T+img=="], + + "@orpc/client/@orpc/standard-server": ["@orpc/standard-server@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0" } }, "sha512-bje/xn6thDqJY/JQ7xoOjmD1KWE4FsyZOPgnPxwqTZLx/r00stRhLuFvk1hDEGj9UlG7NLZEijDVx0wqvcyDzA=="], + + "@orpc/client/@orpc/standard-server-fetch": ["@orpc/standard-server-fetch@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0" } }, "sha512-XYVfgmIt71YrPJSI7RKRiNWjcyktPYHpevkjeohGNBP5aSMAUL95quZArAWr+XOPp4U56XRao54I6wFMZa4How=="], + + "@orpc/client/@orpc/standard-server-peer": ["@orpc/standard-server-peer@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0" } }, "sha512-fsTN+FrdPkseVx99yRenGJYSp0xOrEk9fODk8oh3zzricNYzlZ8GGgCYtDNgt9vFIR/J05dc+Nk76KhcPBQpLw=="], + + "@orpc/contract/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + "@orpc/experimental-publisher/@orpc/client": ["@orpc/client@1.14.2", "", { "dependencies": { "@orpc/shared": "1.14.2", "@orpc/standard-server": "1.14.2", "@orpc/standard-server-fetch": "1.14.2", "@orpc/standard-server-peer": "1.14.2" } }, "sha512-/tFAua/w/mao2kQtJqjoCYEojHrKMisxOCK8qtkMKOUcXVxWMl+QWhP/MykjzFgkFdO9mzKOu1h7vJvpH73EBA=="], "@orpc/experimental-publisher/@orpc/shared": ["@orpc/shared@1.14.2", "", { "dependencies": { "radash": "^12.1.1", "type-fest": "^5.4.4" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-5YtbVz4yGbJgcyj7CmEv5FAy4xagCS/hP/MMAlHpJKBSlMHuD7FrDO4LQLFUmSkcBcbLhzX8Ll1ziUw3vtPasw=="], "@orpc/experimental-publisher/@orpc/standard-server": ["@orpc/standard-server@1.14.2", "", { "dependencies": { "@orpc/shared": "1.14.2" } }, "sha512-XHySJICwDsJf211gcxtJBpzB1ldZrSHDW2mqbBQg+I2AewvTqWiqeGZV+SPvmq87q4IfBzncSuwMrRKplUJhsw=="], - "@quansync/fs/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], + "@orpc/json-schema/@orpc/openapi": ["@orpc/openapi@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/openapi-client": "1.14.3", "@orpc/server": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-0tZN91VoT6MEkOfw+ERKyozsnDXzmDSsBeMgEHN3Hl1WVU97T9l4aZzLlZILSNl3fat3HmduEDy/boNGmAWJkQ=="], + + "@orpc/openapi/@orpc/contract": ["@orpc/contract@1.15.0", "", { "dependencies": { "@orpc/client": "1.15.0", "@orpc/shared": "1.15.0", "@standard-schema/spec": "^1.1.0", "openapi-types": "^12.1.3" } }, "sha512-zo8+x+5iqnIMrFdcj+ibVgn6dJneWzLNcD3kZQxrJFGFNAFoxvZbwryR5wOdcHkaSCeS3N7Ib2NydU6pihSYmg=="], + + "@orpc/openapi/@orpc/interop": ["@orpc/interop@1.15.0", "", {}, "sha512-Ah48o/rc00rN1yP5HcaoVsDCjdUhoh3JP1VibBbvwijhGvdOwRwiw5RWiGu442GSKcrPt9etAGhWXH88q8C3ww=="], - "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA=="], + "@orpc/openapi/@orpc/server": ["@orpc/server@1.15.0", "", { "dependencies": { "@orpc/client": "1.15.0", "@orpc/contract": "1.15.0", "@orpc/interop": "1.15.0", "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0", "@orpc/standard-server-aws-lambda": "1.15.0", "@orpc/standard-server-fastify": "1.15.0", "@orpc/standard-server-fetch": "1.15.0", "@orpc/standard-server-node": "1.15.0", "@orpc/standard-server-peer": "1.15.0", "cookie": "^1.1.1" }, "peerDependencies": { "crossws": ">=0.3.4", "ws": ">=8.18.1" }, "optionalPeers": ["crossws", "ws"] }, "sha512-l9FnL1MjQ8g77mB10/8D61okuKHLJBWhuwmd6OF65POHzBF177kMCcbHvRFEqUuxSbzP2eNiZGf3kXPMHmIKiQ=="], - "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], + "@orpc/openapi/@orpc/shared": ["@orpc/shared@1.15.0", "", { "dependencies": { "radash": "^12.1.1", "type-fest": "^5.4.4" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-A3/JE7pQYSrrRm6/WYJxV3GBhpMdJRPM3h47slQtWBUZe9Sao5En5WBc4tISGQNP2emcez6qIvDziSgD2T+img=="], - "@rspack/dev-server/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "@orpc/openapi/@orpc/standard-server": ["@orpc/standard-server@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0" } }, "sha512-bje/xn6thDqJY/JQ7xoOjmD1KWE4FsyZOPgnPxwqTZLx/r00stRhLuFvk1hDEGj9UlG7NLZEijDVx0wqvcyDzA=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA=="], + "@orpc/openapi-client/@orpc/contract": ["@orpc/contract@1.15.0", "", { "dependencies": { "@orpc/client": "1.15.0", "@orpc/shared": "1.15.0", "@standard-schema/spec": "^1.1.0", "openapi-types": "^12.1.3" } }, "sha512-zo8+x+5iqnIMrFdcj+ibVgn6dJneWzLNcD3kZQxrJFGFNAFoxvZbwryR5wOdcHkaSCeS3N7Ib2NydU6pihSYmg=="], + + "@orpc/openapi-client/@orpc/shared": ["@orpc/shared@1.15.0", "", { "dependencies": { "radash": "^12.1.1", "type-fest": "^5.4.4" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0" }, "optionalPeers": ["@opentelemetry/api"] }, "sha512-A3/JE7pQYSrrRm6/WYJxV3GBhpMdJRPM3h47slQtWBUZe9Sao5En5WBc4tISGQNP2emcez6qIvDziSgD2T+img=="], + + "@orpc/openapi-client/@orpc/standard-server": ["@orpc/standard-server@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0" } }, "sha512-bje/xn6thDqJY/JQ7xoOjmD1KWE4FsyZOPgnPxwqTZLx/r00stRhLuFvk1hDEGj9UlG7NLZEijDVx0wqvcyDzA=="], + + "@orpc/server/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + + "@orpc/tanstack-query/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + + "@orpc/zod/@orpc/openapi": ["@orpc/openapi@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/openapi-client": "1.14.3", "@orpc/server": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-0tZN91VoT6MEkOfw+ERKyozsnDXzmDSsBeMgEHN3Hl1WVU97T9l4aZzLlZILSNl3fat3HmduEDy/boNGmAWJkQ=="], + + "@pingpay/onramp-sdk/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "@pingpay/onramp-sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@pingpay/onramp-types/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "@quansync/fs/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], + + "@rspack/dev-server/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], + + "@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "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" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="], - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" }, "bundled": true }, "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q=="], "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], @@ -2828,11 +2892,11 @@ "@tanstack/devtools-client/@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.5.0", "", { "bin": { "intent": "./bin/intent.js" } }, "sha512-H+OH3zC6Vhu/K0NaVfQKknEKawc/+2PT+D3SB3Ox0V8SiMlTo0abbmH2rH0721R2aNYbjdMXA1oENOd8E2UVoA=="], - "@tanstack/devtools-event-bus/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "@tanstack/devtools-event-bus/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], - "@tanstack/form-core/@tanstack/store": ["@tanstack/store@0.11.0", "", {}, "sha512-WlzzCt3xi0G6pCAJu1U+2jiECwabETDpQDi3hfkFZvJii9AuZqEKbOiVarX1/bWhTNjU486yQtJCCasi/0q+Cw=="], + "@tanstack/form-core/@tanstack/store": ["@tanstack/store@0.11.1", "", {}, "sha512-mzTOBhypOuDJAy/D8n2MfUZ1HFkXnmSETviRyhqEC8LUE7/IZQExOTxMANj3KjTofYTkFNpBY67qaVrT41YccA=="], - "@tanstack/react-form/@tanstack/react-store": ["@tanstack/react-store@0.11.0", "", { "dependencies": { "@tanstack/store": "0.11.0", "use-sync-external-store": "^1.6.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" } }, "sha512-tX4YXh3PDkmpvGQWkWqKpzs/MSqbtuwY9dWdWhtV9Q50PmO+jOkUKIWIX4G85dwt7lxdHLXsiaEKPdKmC8F41w=="], + "@tanstack/react-form/@tanstack/react-store": ["@tanstack/react-store@0.11.1", "", { "dependencies": { "@tanstack/store": "0.11.1", "use-sync-external-store": "^1.6.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" } }, "sha512-HaIGKI3YLmjBYIvy5DFDY23oNaYZIsTZfngey07Uh5iLVJgM3bIGCnZeOFOqzjFld9JHWcaHJnasD/bKoGKwJQ=="], "@tanstack/router-generator/prettier": ["prettier@3.9.6", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g=="], @@ -2844,6 +2908,8 @@ "@vitest/ui/@vitest/utils": ["@vitest/utils@4.1.5", "", { "dependencies": { "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug=="], + "@wagmi/core/zustand": ["zustand@5.0.0", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ=="], + "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -2868,6 +2934,10 @@ "esrecurse/estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + "every-plugin/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + + "every-plugin/@orpc/openapi": ["@orpc/openapi@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/openapi-client": "1.14.3", "@orpc/server": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-0tZN91VoT6MEkOfw+ERKyozsnDXzmDSsBeMgEHN3Hl1WVU97T9l4aZzLlZILSNl3fat3HmduEDy/boNGmAWJkQ=="], + "everything-dev/@types/node": ["@types/node@22.20.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q=="], "everything-dev/vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="], @@ -2878,13 +2948,17 @@ "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "host/@orpc/openapi": ["@orpc/openapi@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/interop": "1.14.3", "@orpc/openapi-client": "1.14.3", "@orpc/server": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "json-schema-typed": "^8.0.2", "rou3": "^0.7.12" } }, "sha512-0tZN91VoT6MEkOfw+ERKyozsnDXzmDSsBeMgEHN3Hl1WVU97T9l4aZzLlZILSNl3fat3HmduEDy/boNGmAWJkQ=="], + "hpack.js/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "http-proxy/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "http-proxy-middleware/is-plain-obj": ["is-plain-obj@3.0.0", "", {}, "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA=="], "ink/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - "ink/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "ink/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], "libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], @@ -2898,13 +2972,23 @@ "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "ox/@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], + + "ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + + "ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "ox/@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="], + + "ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], + "p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], - "postcss/nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + "postcss/nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="], "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], @@ -2916,7 +3000,7 @@ "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], - "read-yaml-file/js-yaml": ["js-yaml@3.15.0", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog=="], + "read-yaml-file/js-yaml": ["js-yaml@3.15.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag=="], "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -2936,6 +3020,10 @@ "sockjs/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "solid-js/seroval": ["seroval@1.5.6", "", {}, "sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA=="], + + "solid-js/seroval-plugins": ["seroval-plugins@1.5.6", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ=="], + "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], @@ -2948,9 +3036,19 @@ "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "ui/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + "unconfig-core/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], - "vite/rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="], + "viem/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], + + "viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "viem/@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="], + + "viem/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="], + + "viem/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "webpack/schema-utils": ["schema-utils@4.3.3", "", { "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", "ajv-formats": "^2.1.1", "ajv-keywords": "^5.1.0" } }, "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA=="], @@ -2962,7 +3060,7 @@ "webpack-dev-middleware/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], - "webpack-dev-server/ws": ["ws@8.21.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw=="], + "webpack-dev-server/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -3036,9 +3134,47 @@ "@orpc/experimental-publisher/@orpc/client/@orpc/standard-server-peer": ["@orpc/standard-server-peer@1.14.2", "", { "dependencies": { "@orpc/shared": "1.14.2", "@orpc/standard-server": "1.14.2" } }, "sha512-uzbgGaxvlZ0IA2lasaLck+yrbR3bKoqJnsZehEdbSm6eWaKln4COvxQJ2PuPow3gA6tklNnGkTfmRmGIHvS/rg=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], + "@orpc/json-schema/@orpc/openapi/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + + "@orpc/json-schema/@orpc/openapi/@orpc/openapi-client": ["@orpc/openapi-client@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3" } }, "sha512-1vp+hi858XDrCwYdhONl15YKNlHtj5F5gI3dq/McRRZ45tZ9/Ma03hxzABOOcayaT1L9nX7gPxhX7l5EuRf2zw=="], + + "@orpc/openapi/@orpc/server/@orpc/standard-server-aws-lambda": ["@orpc/standard-server-aws-lambda@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0", "@orpc/standard-server-fetch": "1.15.0", "@orpc/standard-server-node": "1.15.0" } }, "sha512-laFW7C4t1gEV0NIt4EjCVCBU3kFPnj2FObJLQkTH+DkEOzxVy6zyJ1coIncDSeOPxzbf7rG8cs0B2vQQyuROag=="], + + "@orpc/openapi/@orpc/server/@orpc/standard-server-fastify": ["@orpc/standard-server-fastify@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0", "@orpc/standard-server-node": "1.15.0" }, "peerDependencies": { "fastify": ">=5.6.1" }, "optionalPeers": ["fastify"] }, "sha512-fcOl2004o3RnZxW7rhLuDCrCKSi/H2eWnyljeeoY+xykcygRf0ONCs2K2bSdxPm6cmvQGAXa9m8VBglG9oERVQ=="], + + "@orpc/openapi/@orpc/server/@orpc/standard-server-fetch": ["@orpc/standard-server-fetch@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0" } }, "sha512-XYVfgmIt71YrPJSI7RKRiNWjcyktPYHpevkjeohGNBP5aSMAUL95quZArAWr+XOPp4U56XRao54I6wFMZa4How=="], + + "@orpc/openapi/@orpc/server/@orpc/standard-server-node": ["@orpc/standard-server-node@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0", "@orpc/standard-server-fetch": "1.15.0" } }, "sha512-3ye9SIIhYfJckQwyyGQQRHAEF5vW6QDbax7oi1mOQ9UBHaRDH5kcL28mT0JnETrUvgBoDRx+rfCsQaAOB4n3zw=="], + + "@orpc/openapi/@orpc/server/@orpc/standard-server-peer": ["@orpc/standard-server-peer@1.15.0", "", { "dependencies": { "@orpc/shared": "1.15.0", "@orpc/standard-server": "1.15.0" } }, "sha512-fsTN+FrdPkseVx99yRenGJYSp0xOrEk9fODk8oh3zzricNYzlZ8GGgCYtDNgt9vFIR/J05dc+Nk76KhcPBQpLw=="], + + "@orpc/zod/@orpc/openapi/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + + "@orpc/zod/@orpc/openapi/@orpc/openapi-client": ["@orpc/openapi-client@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3" } }, "sha512-1vp+hi858XDrCwYdhONl15YKNlHtj5F5gI3dq/McRRZ45tZ9/Ma03hxzABOOcayaT1L9nX7gPxhX7l5EuRf2zw=="], + + "@tailwindcss/node/lightningcss/lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], - "@tanstack/react-form/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.11.0", "", {}, "sha512-WlzzCt3xi0G6pCAJu1U+2jiECwabETDpQDi3hfkFZvJii9AuZqEKbOiVarX1/bWhTNjU486yQtJCCasi/0q+Cw=="], + "@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "@tanstack/react-form/@tanstack/react-store/@tanstack/store": ["@tanstack/store@0.11.1", "", {}, "sha512-mzTOBhypOuDJAy/D8n2MfUZ1HFkXnmSETviRyhqEC8LUE7/IZQExOTxMANj3KjTofYTkFNpBY67qaVrT41YccA=="], "@tanstack/zod-form-adapter/@tanstack/form-core/@tanstack/store": ["@tanstack/store@0.7.7", "", {}, "sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ=="], @@ -3048,7 +3184,7 @@ "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "c12/chokidar/readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -3104,18 +3240,28 @@ "drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "every-plugin/@orpc/openapi/@orpc/openapi-client": ["@orpc/openapi-client@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3" } }, "sha512-1vp+hi858XDrCwYdhONl15YKNlHtj5F5gI3dq/McRRZ45tZ9/Ma03hxzABOOcayaT1L9nX7gPxhX7l5EuRf2zw=="], + "everything-dev/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "host/@orpc/openapi/@orpc/client": ["@orpc/client@1.14.3", "", { "dependencies": { "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3", "@orpc/standard-server-fetch": "1.14.3", "@orpc/standard-server-peer": "1.14.3" } }, "sha512-0HzeD/BgPctvFnd6ltjuQvx4/POXo0K01Tee/3whAm3ohXnlGqCfhzR2VMN8zBaGs1SYe6AFzjmGG928Ej3pAg=="], + + "host/@orpc/openapi/@orpc/openapi-client": ["@orpc/openapi-client@1.14.3", "", { "dependencies": { "@orpc/client": "1.14.3", "@orpc/contract": "1.14.3", "@orpc/shared": "1.14.3", "@orpc/standard-server": "1.14.3" } }, "sha512-1vp+hi858XDrCwYdhONl15YKNlHtj5F5gI3dq/McRRZ45tZ9/Ma03hxzABOOcayaT1L9nX7gPxhX7l5EuRf2zw=="], + "hpack.js/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "hpack.js/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], "near-social-js/near-kit/@zorsh/zorsh": ["@zorsh/zorsh@0.4.0", "", {}, "sha512-lDzbhmGFgk5yQOI78v99186nyk+6XW9rGW2BTz/RT1gVM+oiUTtDoTg5/tUYFe+u/+cy9XPmpWWFgRyfYfQcUA=="], + "ox/@scure/bip32/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], + + "ox/@scure/bip39/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -3126,46 +3272,12 @@ "serve-index/http-errors/statuses": ["statuses@1.5.0", "", {}, "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA=="], - "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "vite/rolldown/@oxc-project/types": ["@oxc-project/types@0.139.0", "", {}, "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw=="], - - "vite/rolldown/@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], - - "vite/rolldown/@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], - - "vite/rolldown/@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g=="], - - "vite/rolldown/@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA=="], - - "vite/rolldown/@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.5", "", { "os": "linux", "cpu": "arm" }, "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw=="], - - "vite/rolldown/@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q=="], - - "vite/rolldown/@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA=="], - - "vite/rolldown/@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg=="], - - "vite/rolldown/@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA=="], - - "vite/rolldown/@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ=="], - - "vite/rolldown/@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg=="], - - "vite/rolldown/@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.5", "", { "os": "none", "cpu": "arm64" }, "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw=="], - - "vite/rolldown/@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.5", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA=="], - - "vite/rolldown/@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw=="], - - "vite/rolldown/@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA=="], - - "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "string-width/strip-ansi/ansi-regex": ["ansi-regex@6.3.0", "", {}, "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], + "viem/@scure/bip32/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + "viem/@scure/bip39/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="], - "vite/rolldown/@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.3.0", "", {}, "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ=="], } } diff --git a/docker-compose.yml b/docker-compose.yml index 33ea54b8..7de7c214 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -name: dev.everything.near +name: v1.citynode.near x-pg-common: &pg-common image: postgres:17-alpine @@ -14,14 +14,14 @@ x-pg-common: &pg-common services: postgres-api: <<: *pg-common - container_name: dev.everything.near-postgres-api + container_name: v1.citynode.near-postgres-api environment: <<: *pg-env POSTGRES_DB: api_db ports: - "5432:5432" volumes: - - dev_everything_near_postgres_api_data:/var/lib/postgresql/data + - v1_citynode_near_postgres_api_data:/var/lib/postgresql/data postgres-auth: <<: *pg-common @@ -34,21 +34,8 @@ services: volumes: - auth_everything_near_postgres_auth_data:/var/lib/postgresql/data - postgres-template: - <<: *pg-common - container_name: dev.everything.near-postgres-template - environment: - <<: *pg-env - POSTGRES_DB: template_db - ports: - - "5434:5432" - volumes: - - dev_everything_near_postgres_template_data:/var/lib/postgresql/data - volumes: - dev_everything_near_postgres_api_data: - name: dev_everything_near_postgres_api_data + v1_citynode_near_postgres_api_data: + name: v1_citynode_near_postgres_api_data auth_everything_near_postgres_auth_data: name: auth_everything_near_postgres_auth_data - dev_everything_near_postgres_template_data: - name: dev_everything_near_postgres_template_data diff --git a/host/.env.example b/host/.env.example index 1283ab78..f5d82332 100644 --- a/host/.env.example +++ b/host/.env.example @@ -1,8 +1,5 @@ # app.host CORS_ORIGIN=http://localhost:3000 -TENANT_WHITELIST= -ALLOW_OVERRIDE= -ALLOW_UNTRUSTED_SSR= CSP_STRICT= # app.api diff --git a/host/README.md b/host/README.md index 41c8db50..2f47fe65 100644 --- a/host/README.md +++ b/host/README.md @@ -96,9 +96,6 @@ For the temporary publish registry, use `bos publish` or `bos publish --deploy`. | `API_SOURCE` | `local` or `remote` | Based on NODE_ENV | | `API_PROXY` | Proxy API requests to another host URL | - | | `NETWORK_ID` | Tenant account suffix resolution: `mainnet` or `testnet` | `mainnet` | -| `ALLOW_OVERRIDE` | Comma-separated tenant override targets like `ui`, `plugins.*`, `plugins.apps` | - | -| `TENANT_WHITELIST` | Comma-separated tenant account IDs allowed to SSR | - | -| `ALLOW_UNTRUSTED_SSR` | Allow tenant SSR without whitelist | `false` | | `HOST_DATABASE_URL` | SQLite database URL for auth | `file:./database.db` | | `HOST_DATABASE_AUTH_TOKEN` | Auth token for remote database | - | | `BETTER_AUTH_SECRET` | Secret for session encryption | - | @@ -112,7 +109,7 @@ For the temporary publish registry, use `bos publish` or `bos publish --deploy`. - Current: tenant config must extend the base BOS runtime - Current: tenant accounts derive relative to the active runtime account namespace - Current: supported tenant overrides are `app.ui`, existing `plugins..ui`, and existing `plugins..sidebar` -- Current: tenant SSR is opt-in via `TENANT_WHITELIST` or `ALLOW_UNTRUSTED_SSR=true` +- Current: tenant SSR is gated per-tenant by the `allowSsr` column on the tenant record; the host's BindingResolver reads these permissions from the API's `GET /tenants/bindings` endpoint (cached for 30s) - Not yet implemented: tenant API/auth overrides in fixed-core mode - Not yet implemented: dynamic new plugin IDs per tenant @@ -123,9 +120,6 @@ Example deployment: ```bash BOS_ACCOUNT=linktree.near BOS_GATEWAY=linktree.com -ALLOW_OVERRIDE=ui,plugins.* -TENANT_WHITELIST=alice.linktree.near,bob.linktree.near -ALLOW_UNTRUSTED_SSR=false bos start --no-interactive ``` @@ -139,7 +133,8 @@ Example tenant behavior: Tenant config rules: - must extend the base BOS runtime -- may only override targets allowed by `ALLOW_OVERRIDE` +- may only override the tenant `ui` when `allow_ui_overrides` is true on the tenant record +- plugin UI overrides require `allow_backend_overrides` on the tenant record - in fixed-core mode, only UI-facing overrides are applied - custom UI remotes must provide integrity - custom plugin UI remotes must provide integrity @@ -147,9 +142,8 @@ Tenant config rules: Tenant SSR rules: -- if `ALLOW_UNTRUSTED_SSR=true`, any valid tenant UI with SSR config may SSR -- otherwise the tenant account must appear in `TENANT_WHITELIST` -- non-whitelisted tenants fall back to client rendering +- tenant SSR is allowed only when the tenant record has `allow_ssr` set +- tenants without `allow_ssr` fall back to client rendering ### Proxy Mode diff --git a/host/src/services/binding-resolver.ts b/host/src/services/binding-resolver.ts new file mode 100644 index 00000000..6615fd49 --- /dev/null +++ b/host/src/services/binding-resolver.ts @@ -0,0 +1,141 @@ +import { logger } from "../utils/logger"; +import { resolveDomain } from "../utils/normalize"; +import type { RuntimeConfig } from "./config"; + +export type TenantBindingStatus = "active" | "pending" | "suspended" | "pending_deletion"; + +export interface TenantBinding { + hostname: string; + accountId: string; + allowUiOverrides: boolean; + allowBackendOverrides: boolean; + allowSsr: boolean; + status: TenantBindingStatus; +} + +const BINDINGS_TTL_MS = 30_000; + +interface CachedBindings { + apiUrl: string; + expiresAt: number; + entries: Map; + refetching?: Promise>; +} + +let bindingsCache: CachedBindings | null = null; + +export function clearBindingResolverCache() { + bindingsCache = null; +} + +function isBaseHost(hostname: string, gatewayId: string): boolean { + const normalized = hostname.toLowerCase(); + return normalized === gatewayId || normalized === "localhost" || normalized === "127.0.0.1"; +} + +async function fetchBindingsFromApi(apiUrl: string): Promise { + const endpoint = `${apiUrl.replace(/\/$/, "")}/api/tenants/bindings`; + let response: Response; + try { + response = await fetch(endpoint); + } catch (cause) { + throw new Error(`Failed to reach API at ${endpoint}: ${String(cause)}`, { cause }); + } + + if (!response.ok) { + throw new Error(`GET ${endpoint} failed with HTTP ${response.status}`); + } + + return (await response.json()) as TenantBinding[]; +} + +function resolveGatewayId(config: RuntimeConfig): string { + return resolveDomain(config.domain, config.host.url).toLowerCase(); +} + +function mapBindingsByHostname( + config: RuntimeConfig, + bindings: TenantBinding[], +): Map { + const gatewayId = resolveGatewayId(config); + const entries = new Map(); + for (const binding of bindings) { + entries.set(`${binding.hostname.toLowerCase()}.${gatewayId}`, binding); + } + return entries; +} + +function ensureBindingsLoaded(config: RuntimeConfig): Promise> { + const apiUrl = config.host.url; + if (!apiUrl) { + return Promise.resolve(new Map()); + } + + const now = Date.now(); + if ( + bindingsCache && + bindingsCache.apiUrl === apiUrl && + bindingsCache.expiresAt > now && + bindingsCache.entries.size > 0 + ) { + return Promise.resolve(bindingsCache.entries); + } + + if (bindingsCache?.apiUrl === apiUrl && bindingsCache.refetching) { + return bindingsCache.refetching; + } + + const staleEntries = bindingsCache?.apiUrl === apiUrl ? bindingsCache.entries : null; + + const fetchPromise = fetchBindingsFromApi(apiUrl) + .then((bindings) => { + const entries = mapBindingsByHostname(config, bindings); + bindingsCache = { apiUrl, expiresAt: Date.now() + BINDINGS_TTL_MS, entries }; + return entries; + }) + .catch((cause) => { + if (staleEntries && staleEntries.size > 0) { + logger.error( + `[BindingResolver] Refresh failed, serving ${staleEntries.size} stale binding(s): ${cause instanceof Error ? cause.message : String(cause)}`, + ); + bindingsCache = { + apiUrl, + expiresAt: Date.now() + BINDINGS_TTL_MS, + entries: staleEntries, + }; + return staleEntries; + } + bindingsCache = null; + throw cause; + }); + + bindingsCache = { + apiUrl, + expiresAt: now + BINDINGS_TTL_MS, + entries: staleEntries ?? new Map(), + refetching: fetchPromise, + }; + + return fetchPromise; +} + +export interface BindingResolver { + resolve(hostname: string): Promise; + clear(): void; +} + +export function createBindingResolver(config: RuntimeConfig): BindingResolver { + return { + async resolve(hostname: string): Promise { + const normalized = hostname.toLowerCase(); + const gatewayId = resolveGatewayId(config); + if (isBaseHost(normalized, gatewayId)) { + return null; + } + + const entries = await ensureBindingsLoaded(config); + return entries.get(normalized) ?? null; + }, + clear: clearBindingResolverCache, + }; +} diff --git a/host/src/services/tenant-runtime.ts b/host/src/services/tenant-runtime.ts index 5909f09b..69c1aa2a 100644 --- a/host/src/services/tenant-runtime.ts +++ b/host/src/services/tenant-runtime.ts @@ -1,28 +1,25 @@ -import { - buildRuntimeConfig, - isRuntimeOverrideAllowed, - loadRemoteConfig, - parseRuntimeOverrideTargets, - type RuntimeConfig, -} from "everything-dev/config"; +import { buildRuntimeConfig, loadRemoteConfig, type RuntimeConfig } from "everything-dev/config"; import { verifySriForUrl } from "everything-dev/integrity"; import type { RuntimePlugin } from "../types"; import { logger } from "../utils/logger"; import { resolveDomain } from "../utils/normalize"; +import { + type BindingResolver, + clearBindingResolverCache, + createBindingResolver, +} from "./binding-resolver"; const REMOTE_CONFIG_TTL_MS = 30_000; const VERIFICATION_TTL_MS = 5 * 60_000; const MAX_REMOTE_CONFIG_CACHE_SIZE = 256; const MAX_VERIFICATION_CACHE_SIZE = 512; -const NEAR_ACCOUNT_ID_REGEX = - /^(?=.{2,64}$)([a-z0-9]+(?:[-_][a-z0-9]+)*)(\.([a-z0-9]+(?:[-_][a-z0-9]+)*))*$/; -type RuntimeOverrideTarget = ReturnType[number]; type BosEnv = "development" | "production" | "staging"; type IntegrityVerificationMode = "blocking" | "stale-while-revalidate"; interface ResolveRequestRuntimeOptions { verification?: IntegrityVerificationMode; + bindingResolver?: BindingResolver; } interface CachedRemoteConfig { @@ -55,9 +52,6 @@ export class TenantRuntimeError extends Error { const remoteConfigCache = new Map(); const verifiedUiCache = new Map(); -const unsupportedOverrideWarnings = new Set(); -let tenantWhitelistCache: { raw: string; value: Set } | null = null; -let allowedOverridesCache: { raw: string; value: RuntimeOverrideTarget[] } | null = null; function pruneExpiredCacheEntries( cache: Map, @@ -96,92 +90,7 @@ export function getTenantRuntimeErrorResponse(error: unknown): { status: number; export function clearTenantRuntimeCaches() { remoteConfigCache.clear(); verifiedUiCache.clear(); - unsupportedOverrideWarnings.clear(); - tenantWhitelistCache = null; - allowedOverridesCache = null; -} - -function parseBoolean(value: string | undefined): boolean { - if (!value) return false; - return ["1", "true", "yes", "on"].includes(value.toLowerCase()); -} - -function getTenantWhitelist(): Set { - const raw = process.env.TENANT_WHITELIST ?? ""; - if (tenantWhitelistCache?.raw === raw) { - return tenantWhitelistCache.value; - } - - const value = new Set( - raw - .split(",") - .map((entry) => entry.trim()) - .filter(Boolean), - ); - tenantWhitelistCache = { raw, value }; - return value; -} - -function getAllowedOverrides(): RuntimeOverrideTarget[] { - const raw = process.env.ALLOW_OVERRIDE ?? ""; - if (allowedOverridesCache?.raw === raw) { - return allowedOverridesCache.value; - } - - const value = parseRuntimeOverrideTargets(raw); - allowedOverridesCache = { raw, value }; - return value; -} - -function warnUnsupportedOverrideTargets(targets: ReadonlyArray) { - for (const target of targets) { - if (target === "ui" || target === "plugins" || target.startsWith("plugins.")) { - continue; - } - - if (!unsupportedOverrideWarnings.has(target)) { - unsupportedOverrideWarnings.add(target); - logger.warn( - `[Tenant Runtime] Ignoring unsupported override target "${target}" in fixed-core mode`, - ); - } - } -} - -function resolveTenantAccountId( - hostname: string, - gatewayId: string, - namespaceAccountId: string, -): string | null { - const normalizedHost = hostname.toLowerCase(); - const normalizedGateway = gatewayId.toLowerCase(); - const normalizedNamespaceAccountId = namespaceAccountId.toLowerCase(); - - if ( - normalizedHost === normalizedGateway || - normalizedHost === "localhost" || - normalizedHost === "127.0.0.1" - ) { - return null; - } - - const suffix = `.${normalizedGateway}`; - if (!normalizedHost.endsWith(suffix)) { - return null; - } - - const tenantLabel = normalizedHost.slice(0, -suffix.length); - const tenantSegments = tenantLabel.split(".").filter(Boolean); - if (tenantSegments.length === 0 || tenantSegments.join(".") !== tenantLabel) { - throw new TenantRuntimeError(`Invalid tenant host: ${hostname}`, 404); - } - - const accountId = `${tenantSegments.join(".")}.${normalizedNamespaceAccountId}`; - if (!NEAR_ACCOUNT_ID_REGEX.test(accountId)) { - throw new TenantRuntimeError(`Invalid tenant account: ${accountId}`, 404); - } - - return accountId; + clearBindingResolverCache(); } function getRemoteConfigCached(bosUrl: string, env: BosEnv) { @@ -347,16 +256,6 @@ async function verifyPluginUiIntegrity( ); } -function isPluginOverrideAllowed( - allowedOverrides: ReadonlyArray, - pluginKey: string, -): boolean { - return ( - isRuntimeOverrideAllowed(allowedOverrides, "plugins") || - isRuntimeOverrideAllowed(allowedOverrides, `plugins.${pluginKey}`) - ); -} - function buildEffectivePluginConfig( basePlugin: RuntimePlugin, tenantPlugin: RuntimePlugin, @@ -372,10 +271,9 @@ function buildEffectiveRuntimeConfig( baseConfig: RuntimeConfig, tenantConfig: RuntimeConfig, tenantAccountId: string, - allowedOverrides: ReadonlyArray, + allowUiOverrides: boolean, + allowBackendOverrides: boolean, ): RuntimeConfig { - warnUnsupportedOverrideTargets(allowedOverrides); - const effectiveConfig: RuntimeConfig = { ...baseConfig, account: tenantAccountId, @@ -385,7 +283,7 @@ function buildEffectiveRuntimeConfig( repository: tenantConfig.repository, }; - if (isRuntimeOverrideAllowed(allowedOverrides, "ui")) { + if (allowUiOverrides) { effectiveConfig.ui = tenantConfig.ui; } @@ -399,7 +297,7 @@ function buildEffectiveRuntimeConfig( continue; } - if (!isPluginOverrideAllowed(allowedOverrides, pluginKey)) { + if (!allowBackendOverrides) { continue; } @@ -412,29 +310,6 @@ function buildEffectiveRuntimeConfig( return effectiveConfig; } -function matchesTenantPattern(accountId: string, pattern: string): boolean { - if (pattern === accountId) return true; - if (pattern.startsWith("*.") && accountId.endsWith(pattern.slice(1))) return true; - return false; -} - -function isSsrAllowed(accountId: string): boolean { - if (parseBoolean(process.env.ALLOW_UNTRUSTED_SSR)) { - return true; - } - - const whitelist = getTenantWhitelist(); - for (const entry of whitelist) { - if (matchesTenantPattern(accountId, entry)) return true; - } - return false; -} - -function getTenantStatus(remoteConfig: Awaited>): string { - const raw = remoteConfig.rawConfig as { status?: string } | undefined; - return raw?.status ?? "active"; -} - export async function resolveRequestRuntime( baseConfig: RuntimeConfig, request: Request, @@ -443,8 +318,9 @@ export async function resolveRequestRuntime( const verificationMode = options?.verification ?? "blocking"; const url = new URL(request.url); const gatewayId = resolveDomain(baseConfig.domain, baseConfig.host.url); - const tenantAccountId = resolveTenantAccountId(url.hostname, gatewayId, baseConfig.account); - if (!tenantAccountId) { + const bindingResolver = options?.bindingResolver ?? createBindingResolver(baseConfig); + const binding = await bindingResolver.resolve(url.hostname); + if (!binding) { return { config: baseConfig, tenantAccountId: null, @@ -453,6 +329,14 @@ export async function resolveRequestRuntime( }; } + const tenantAccountId = binding.accountId; + if (binding.status === "suspended") { + throw new TenantRuntimeError("Tenant is suspended", 503); + } + if (binding.status === "pending_deletion") { + throw new TenantRuntimeError("Tenant has been deleted", 410); + } + const bosUrl = `bos://${tenantAccountId}/${gatewayId}`; const remoteConfig = await getRemoteConfigCached(bosUrl, "production"); const baseBosUrl = `bos://${baseConfig.account}/${gatewayId}`; @@ -483,17 +367,10 @@ export async function resolveRequestRuntime( baseConfig, tenantRuntimeConfig, tenantAccountId, - getAllowedOverrides(), + binding.allowUiOverrides, + binding.allowBackendOverrides, ); - const tenantStatus = getTenantStatus(remoteConfig); - if (tenantStatus === "suspended") { - throw new TenantRuntimeError("Tenant is suspended", 503); - } - if (tenantStatus === "pending_deletion") { - throw new TenantRuntimeError("Tenant has been deleted", 410); - } - if (effectiveConfig.ui.url !== baseConfig.ui.url) { await verifyUiIntegrity(effectiveConfig, verificationMode); } @@ -512,7 +389,7 @@ export async function resolveRequestRuntime( const ssrAllowed = Boolean(effectiveConfig.ui.ssrUrl) && Boolean(effectiveConfig.ui.ssrIntegrity) && - isSsrAllowed(tenantAccountId); + binding.allowSsr; return { config: ssrAllowed diff --git a/host/tests/integration/ssr-bundled-runtime.test.ts b/host/tests/integration/ssr-bundled-runtime.test.ts index d26b3177..c880184a 100644 --- a/host/tests/integration/ssr-bundled-runtime.test.ts +++ b/host/tests/integration/ssr-bundled-runtime.test.ts @@ -57,7 +57,7 @@ describe("bundled host SSR runtime", () => { const html = await response.text(); expect(response.status).toBe(200); - expect(html).toContain("connect to everything"); + expect(html).toContain("everything.dev"); expect(html).not.toContain("SSR unavailable, showing client app."); expect(html).not.toContain("

Loading...

"); diff --git a/host/tests/integration/tenant-host-nested.test.ts b/host/tests/integration/tenant-host-nested.test.ts index e2891320..ccde7073 100644 --- a/host/tests/integration/tenant-host-nested.test.ts +++ b/host/tests/integration/tenant-host-nested.test.ts @@ -11,19 +11,6 @@ vi.mock("everything-dev/config", async () => { await vi.importActual("everything-dev/config"); return { ...actual, - parseRuntimeOverrideTargets: (value?: string | null) => - value - ? [ - ...new Set( - value - .split(",") - .map((entry) => entry.trim()) - .filter(Boolean), - ), - ] - : [], - isRuntimeOverrideAllowed: (targets: string[], target: string) => - targets.includes(target) || (target.startsWith("plugins.") && targets.includes("plugins.*")), loadRemoteConfig: loadRemoteConfigMock, buildRuntimeConfig: buildRuntimeConfigMock, }; @@ -39,6 +26,29 @@ vi.mock("everything-dev/integrity", async () => { }; }); +vi.mock("../../src/services/binding-resolver", async () => { + const actual = await vi.importActual( + "../../src/services/binding-resolver", + ); + return { + ...actual, + createBindingResolver: () => ({ + resolve: async (hostname: string) => + hostname === "chicago.alice.linktree.com" + ? { + hostname: "chicago.alice.linktree.com", + accountId: "chicago.alice.linktree.near", + allowUiOverrides: true, + allowBackendOverrides: false, + allowSsr: false, + status: "active", + } + : null, + clear: () => {}, + }), + }; +}); + const { runServer } = await import("../../src/program"); function createBaseConfig() { @@ -117,9 +127,6 @@ describe("tenant host nested integration", () => { const previousNodeEnv = process.env.NODE_ENV; const previousHost = process.env.HOST; const previousPort = process.env.PORT; - const previousAllowOverride = process.env.ALLOW_OVERRIDE; - const previousTenantWhitelist = process.env.TENANT_WHITELIST; - const previousAllowUntrustedSsr = process.env.ALLOW_UNTRUSTED_SSR; beforeAll(async () => { assetServer = await startStaticServer({}); @@ -129,9 +136,6 @@ describe("tenant host nested integration", () => { process.env.NODE_ENV = "production"; process.env.HOST = "127.0.0.1"; process.env.PORT = String(port); - process.env.ALLOW_OVERRIDE = "ui,plugins.*"; - process.env.TENANT_WHITELIST = "chicago.alice.linktree.near"; - process.env.ALLOW_UNTRUSTED_SSR = "false"; process.argv.push("--proxy"); const config = createBaseConfig(); @@ -162,9 +166,6 @@ describe("tenant host nested integration", () => { process.env.NODE_ENV = previousNodeEnv; process.env.HOST = previousHost; process.env.PORT = previousPort; - process.env.ALLOW_OVERRIDE = previousAllowOverride; - process.env.TENANT_WHITELIST = previousTenantWhitelist; - process.env.ALLOW_UNTRUSTED_SSR = previousAllowUntrustedSsr; const proxyIdx = process.argv.indexOf("--proxy"); if (proxyIdx !== -1) { diff --git a/host/tests/integration/tenant-runtime.test.ts b/host/tests/integration/tenant-runtime.test.ts index 9927d32e..1831a817 100644 --- a/host/tests/integration/tenant-runtime.test.ts +++ b/host/tests/integration/tenant-runtime.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import type { RuntimeConfig } from "../../src/services/config"; const loadRemoteConfigMock = vi.fn(); @@ -7,19 +7,6 @@ const verifySriForUrlMock = vi.fn(); vi.mock("everything-dev/config", async () => { return { - parseRuntimeOverrideTargets: (value?: string | null) => - value - ? [ - ...new Set( - value - .split(",") - .map((entry) => entry.trim()) - .filter(Boolean), - ), - ] - : [], - isRuntimeOverrideAllowed: (targets: string[], target: string) => - targets.includes(target) || (target.startsWith("plugins.") && targets.includes("plugins.*")), loadRemoteConfig: loadRemoteConfigMock, buildRuntimeConfig: buildRuntimeConfigMock, }; @@ -33,6 +20,8 @@ const { clearTenantRuntimeCaches, resolveRequestRuntime } = await import( "../../src/services/tenant-runtime" ); +import type { BindingResolver } from "../../src/services/binding-resolver"; + function createDeferred() { let resolve!: (value: T | PromiseLike) => void; const promise = new Promise((res) => { @@ -41,6 +30,34 @@ function createDeferred() { return { promise, resolve }; } +function createMockBindingResolver( + ...hostnames: Array<{ + hostname: string; + allowUiOverrides?: boolean; + allowBackendOverrides?: boolean; + allowSsr?: boolean; + status?: "active" | "pending" | "suspended" | "pending_deletion"; + }> +): BindingResolver { + const map = new Map( + hostnames.map((entry) => [ + entry.hostname, + { + hostname: entry.hostname, + accountId: entry.hostname.replace(/\.com$/, ".near"), + allowUiOverrides: entry.allowUiOverrides ?? true, + allowBackendOverrides: entry.allowBackendOverrides ?? false, + allowSsr: entry.allowSsr ?? false, + status: entry.status ?? "active", + }, + ]), + ); + return { + resolve: async (hostname: string) => map.get(hostname) ?? null, + clear: () => {}, + }; +} + function createBaseRuntimeConfig(): RuntimeConfig { return { env: "production", @@ -96,35 +113,25 @@ function createBaseRuntimeConfig(): RuntimeConfig { } describe("resolveRequestRuntime", () => { - const envSnapshot = { ...process.env }; - beforeEach(() => { vi.clearAllMocks(); clearTenantRuntimeCaches(); verifySriForUrlMock.mockResolvedValue(undefined); - process.env = { - ...envSnapshot, - ALLOW_OVERRIDE: "ui", - TENANT_WHITELIST: "alice.linktree.near", - ALLOW_UNTRUSTED_SSR: "false", - }; - }); - - afterEach(() => { - process.env = { ...envSnapshot }; }); it("returns the base runtime on the bare domain", async () => { const baseConfig = createBaseRuntimeConfig(); - const result = await resolveRequestRuntime(baseConfig, new Request("https://linktree.com/")); + const result = await resolveRequestRuntime(baseConfig, new Request("https://linktree.com/"), { + bindingResolver: createMockBindingResolver(), + }); expect(result.config).toBe(baseConfig); expect(result.tenantAccountId).toBeNull(); expect(loadRemoteConfigMock).not.toHaveBeenCalled(); }); - it("derives tenant accounts relative to the active runtime account", async () => { + it("resolves the tenant account for a hostname via the binding resolver", async () => { loadRemoteConfigMock.mockResolvedValue({ source: "bos://alice.linktree.near/linktree.com", rawConfig: { @@ -155,6 +162,12 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( createBaseRuntimeConfig(), new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + }), + }, ); expect(result.tenantAccountId).toBe("alice.linktree.near"); @@ -164,7 +177,7 @@ describe("resolveRequestRuntime", () => { ); }); - it("supports nested tenant labels within the active runtime namespace", async () => { + it("resolves nested tenant hostnames via the binding resolver", async () => { loadRemoteConfigMock.mockResolvedValue({ source: "bos://chicago.alice.linktree.near/linktree.com", rawConfig: { @@ -198,6 +211,12 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( createBaseRuntimeConfig(), new Request("https://chicago.alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "chicago.alice.linktree.com", + allowUiOverrides: true, + }), + }, ); expect(result.tenantAccountId).toBe("chicago.alice.linktree.near"); @@ -230,61 +249,37 @@ describe("resolveRequestRuntime", () => { buildRuntimeConfigMock.mockResolvedValue(createBaseRuntimeConfig()); await expect( - resolveRequestRuntime(createBaseRuntimeConfig(), new Request("https://alice.linktree.com/")), + resolveRequestRuntime(createBaseRuntimeConfig(), new Request("https://alice.linktree.com/"), { + bindingResolver: createMockBindingResolver({ hostname: "alice.linktree.com" }), + }), ).rejects.toThrow("must extend bos://linktree.near/linktree.com"); }); - it("rejects a suspended tenant with 503 based on published config status", async () => { - loadRemoteConfigMock.mockResolvedValue({ - source: "bos://alice.linktree.near/linktree.com", - rawConfig: { - extends: "bos://linktree.near/linktree.com", - status: "suspended", - }, - config: { - account: "alice.linktree.near", - app: { - host: { development: "local:host", production: "https://host.example.com" }, - ui: { name: "ui", production: "https://cdn.example.com/alice-ui" }, - api: { name: "api", production: "https://api.example.com" }, - }, - }, - extendsChain: ["bos://alice.linktree.near/linktree.com", "bos://linktree.near/linktree.com"], - }); - - buildRuntimeConfigMock.mockResolvedValue(createBaseRuntimeConfig()); - + it("rejects a suspended tenant with 503 based on the binding status", async () => { await expect( - resolveRequestRuntime(createBaseRuntimeConfig(), new Request("https://alice.linktree.com/")), + resolveRequestRuntime(createBaseRuntimeConfig(), new Request("https://alice.linktree.com/"), { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + status: "suspended", + }), + }), ).rejects.toMatchObject({ status: 503, message: "Tenant is suspended" }); + expect(loadRemoteConfigMock).not.toHaveBeenCalled(); }); - it("rejects a pending_deletion tenant with 410 based on published config status", async () => { - loadRemoteConfigMock.mockResolvedValue({ - source: "bos://alice.linktree.near/linktree.com", - rawConfig: { - extends: "bos://linktree.near/linktree.com", - status: "pending_deletion", - }, - config: { - account: "alice.linktree.near", - app: { - host: { development: "local:host", production: "https://host.example.com" }, - ui: { name: "ui", production: "https://cdn.example.com/alice-ui" }, - api: { name: "api", production: "https://api.example.com" }, - }, - }, - extendsChain: ["bos://alice.linktree.near/linktree.com", "bos://linktree.near/linktree.com"], - }); - - buildRuntimeConfigMock.mockResolvedValue(createBaseRuntimeConfig()); - + it("rejects a pending_deletion tenant with 410 based on the binding status", async () => { await expect( - resolveRequestRuntime(createBaseRuntimeConfig(), new Request("https://alice.linktree.com/")), + resolveRequestRuntime(createBaseRuntimeConfig(), new Request("https://alice.linktree.com/"), { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + status: "pending_deletion", + }), + }), ).rejects.toMatchObject({ status: 410, message: "Tenant has been deleted" }); + expect(loadRemoteConfigMock).not.toHaveBeenCalled(); }); - it("applies a tenant UI override and allows SSR for whitelisted tenants", async () => { + it("applies a tenant UI override and allows SSR when the binding enables both", async () => { const baseConfig = createBaseRuntimeConfig(); loadRemoteConfigMock.mockResolvedValue({ @@ -325,6 +320,13 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( baseConfig, new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }, ); expect(result.tenantAccountId).toBe("alice.linktree.near"); @@ -338,7 +340,7 @@ describe("resolveRequestRuntime", () => { ); }); - it("disables SSR for non-whitelisted tenants when untrusted SSR is off", async () => { + it("disables SSR when the binding does not allow it", async () => { const baseConfig = createBaseRuntimeConfig(); loadRemoteConfigMock.mockResolvedValue({ @@ -373,6 +375,13 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( baseConfig, new Request("https://bob.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "bob.linktree.com", + allowUiOverrides: true, + allowSsr: false, + }), + }, ); expect(result.ssrAllowed).toBe(false); @@ -380,7 +389,7 @@ describe("resolveRequestRuntime", () => { expect(result.config.ui.ssrIntegrity).toBeUndefined(); }); - it("disables SSR for whitelisted tenants when ssrIntegrity is missing", async () => { + it("disables SSR for SSR-enabled bindings when ssrIntegrity is missing", async () => { const baseConfig = createBaseRuntimeConfig(); loadRemoteConfigMock.mockResolvedValue({ @@ -419,6 +428,13 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( baseConfig, new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }, ); expect(result.ssrAllowed).toBe(false); @@ -426,7 +442,7 @@ describe("resolveRequestRuntime", () => { expect(result.config.ui.ssrIntegrity).toBeUndefined(); }); - it("disables SSR for whitelisted tenants when ssrUrl is missing", async () => { + it("disables SSR for SSR-enabled bindings when ssrUrl is missing", async () => { const baseConfig = createBaseRuntimeConfig(); loadRemoteConfigMock.mockResolvedValue({ @@ -460,6 +476,13 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( baseConfig, new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }, ); expect(result.ssrAllowed).toBe(false); @@ -467,7 +490,7 @@ describe("resolveRequestRuntime", () => { expect(result.config.ui.ssrIntegrity).toBeUndefined(); }); - it("allows SSR for whitelisted tenants when both ssrUrl and ssrIntegrity are present", async () => { + it("allows SSR when the binding enables SSR and both ssrUrl and ssrIntegrity are present", async () => { const baseConfig = createBaseRuntimeConfig(); loadRemoteConfigMock.mockResolvedValue({ @@ -508,6 +531,13 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( baseConfig, new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }, ); expect(result.ssrAllowed).toBe(true); @@ -515,9 +545,8 @@ describe("resolveRequestRuntime", () => { expect(result.config.ui.ssrIntegrity).toBe("sha384-alice-ssr"); }); - it("applies existing plugin UI overrides when plugins are allowed", async () => { + it("applies existing plugin UI overrides when backend overrides are allowed", async () => { const baseConfig = createBaseRuntimeConfig(); - process.env.ALLOW_OVERRIDE = "ui,plugins.*"; loadRemoteConfigMock.mockResolvedValue({ source: "bos://alice.linktree.near/linktree.com", @@ -591,6 +620,14 @@ describe("resolveRequestRuntime", () => { const result = await resolveRequestRuntime( baseConfig, new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + allowBackendOverrides: true, + }), + }, ); expect(result.config.plugins?.apps.ui?.url).toBe("https://plugins.example.com/alice-apps-ui"); @@ -639,7 +676,13 @@ describe("resolveRequestRuntime", () => { }, }); - await resolveRequestRuntime(baseConfig, new Request("https://alice.linktree.com/")); + await resolveRequestRuntime(baseConfig, new Request("https://alice.linktree.com/"), { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }); const refresh = createDeferred(); verifySriForUrlMock.mockImplementationOnce(() => refresh.promise); @@ -648,6 +691,11 @@ describe("resolveRequestRuntime", () => { await expect( resolveRequestRuntime(baseConfig, new Request("https://alice.linktree.com/asset.js"), { verification: "stale-while-revalidate", + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), }), ).resolves.toMatchObject({ tenantAccountId: "alice.linktree.near" }); expect(verifySriForUrlMock).toHaveBeenCalledTimes(2); @@ -655,6 +703,11 @@ describe("resolveRequestRuntime", () => { await expect( resolveRequestRuntime(baseConfig, new Request("https://alice.linktree.com/asset-2.js"), { verification: "stale-while-revalidate", + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), }), ).resolves.toMatchObject({ tenantAccountId: "alice.linktree.near" }); expect(verifySriForUrlMock).toHaveBeenCalledTimes(2); @@ -704,7 +757,13 @@ describe("resolveRequestRuntime", () => { }, }); - await resolveRequestRuntime(baseConfig, new Request("https://alice.linktree.com/")); + await resolveRequestRuntime(baseConfig, new Request("https://alice.linktree.com/"), { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }); const refresh = createDeferred(); verifySriForUrlMock.mockImplementationOnce(() => refresh.promise); @@ -716,6 +775,11 @@ describe("resolveRequestRuntime", () => { new Request("https://alice.linktree.com/"), { verification: "blocking", + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), }, ).then(() => { settled = true; @@ -732,50 +796,109 @@ describe("resolveRequestRuntime", () => { } }); - it("recomputes the tenant whitelist when the env value changes", async () => { + it("gates SSR per tenant based on the binding allowSsr flag", async () => { const baseConfig = createBaseRuntimeConfig(); loadRemoteConfigMock.mockResolvedValue({ - source: "bos://bob.linktree.near/linktree.com", + source: "bos://alice.linktree.near/linktree.com", rawConfig: { extends: "bos://linktree.near/linktree.com", }, config: { - account: "bob.linktree.near", + account: "alice.linktree.near", app: { host: { development: "local:host", production: "https://host.example.com" }, - ui: { name: "ui", production: "https://cdn.example.com/bob-ui" }, + ui: { name: "ui", production: "https://cdn.example.com/alice-ui" }, api: { name: "api", production: "https://api.example.com" }, }, }, - extendsChain: ["bos://bob.linktree.near/linktree.com", "bos://linktree.near/linktree.com"], + extendsChain: ["bos://alice.linktree.near/linktree.com", "bos://linktree.near/linktree.com"], }); buildRuntimeConfigMock.mockResolvedValue({ ...baseConfig, - account: "bob.linktree.near", + account: "alice.linktree.near", ui: { ...baseConfig.ui, - url: "https://cdn.example.com/bob-ui", - entry: "https://cdn.example.com/bob-ui/mf-manifest.json", - integrity: "sha384-bob", - ssrUrl: "https://cdn.example.com/bob-ui-ssr", - ssrIntegrity: "sha384-bob-ssr", + url: "https://cdn.example.com/alice-ui", + entry: "https://cdn.example.com/alice-ui/mf-manifest.json", + integrity: "sha384-alice", + ssrUrl: "https://cdn.example.com/alice-ui-ssr", + ssrIntegrity: "sha384-alice-ssr", }, }); const blocked = await resolveRequestRuntime( baseConfig, - new Request("https://bob.linktree.com/"), + new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: false, + }), + }, ); expect(blocked.ssrAllowed).toBe(false); - - process.env.TENANT_WHITELIST = "bob.linktree.near"; + expect(blocked.config.ui.ssrUrl).toBeUndefined(); const allowed = await resolveRequestRuntime( baseConfig, - new Request("https://bob.linktree.com/"), + new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: true, + allowSsr: true, + }), + }, ); expect(allowed.ssrAllowed).toBe(true); + expect(allowed.config.ui.ssrUrl).toBe("https://cdn.example.com/alice-ui-ssr"); + }); + + it("does not apply tenant UI overrides when the binding disallows them", async () => { + const baseConfig = createBaseRuntimeConfig(); + + loadRemoteConfigMock.mockResolvedValue({ + source: "bos://alice.linktree.near/linktree.com", + rawConfig: { + extends: "bos://linktree.near/linktree.com", + }, + config: { + account: "alice.linktree.near", + app: { + host: { development: "local:host", production: "https://host.example.com" }, + ui: { name: "ui", production: "https://cdn.example.com/alice-ui" }, + api: { name: "api", production: "https://api.example.com" }, + }, + }, + extendsChain: ["bos://alice.linktree.near/linktree.com", "bos://linktree.near/linktree.com"], + }); + + buildRuntimeConfigMock.mockResolvedValue({ + ...baseConfig, + account: "alice.linktree.near", + ui: { + ...baseConfig.ui, + url: "https://cdn.example.com/alice-ui", + entry: "https://cdn.example.com/alice-ui/mf-manifest.json", + integrity: "sha384-alice", + }, + }); + + const result = await resolveRequestRuntime( + baseConfig, + new Request("https://alice.linktree.com/"), + { + bindingResolver: createMockBindingResolver({ + hostname: "alice.linktree.com", + allowUiOverrides: false, + }), + }, + ); + + expect(result.config.ui.url).toBe(baseConfig.ui.url); + expect(verifySriForUrlMock).not.toHaveBeenCalled(); }); }); diff --git a/package.json b/package.json index 073c58bc..2e44e3b6 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "better-auth": "1.6.25", "@better-auth/api-key": "1.6.25", "@better-auth/passkey": "1.6.25", - "better-near-auth": "1.8.2", + "better-near-auth": "1.10.0", "every-plugin": "^2.10.1", "everything-dev": "^1.53.0", "typescript": "^5.9.3", @@ -79,7 +79,7 @@ "publish": "bun packages/everything-dev/src/cli.ts publish", "bos": "bun packages/everything-dev/src/cli.ts", "types:gen": "bun packages/everything-dev/src/cli.ts types gen", - "typecheck": "bun run types:gen && bun run --cwd packages/everything-dev typecheck & bun run --cwd ui tsc --noEmit & bun run --cwd api tsc --noEmit & bun run --cwd host tsc --noEmit & wait", + "typecheck": "BOS_NO_BANNER=1 bun run types:gen && BOS_NO_BANNER=1 bun run bos typecheck", "lint": "biome check .", "lint:fix": "biome check --write .", "format": "biome format --write .", @@ -98,15 +98,19 @@ "test:api": "cd api && bun run test tests/integration/ tests/unit/", "test:integration": "cd api && bun run test tests/integration/", "test:e2e": "bun run --cwd host test:e2e", - "regression:start:dev": "BOS_NO_PERSIST_PORTS=1 bun packages/everything-dev/src/cli.ts dev --no-interactive --port 4100 --api-port 4101 --auth-port 4102 --ui-port 4103 --plugin-port-start 4110", + "regression:start:dev": "BOS_NO_PERSIST_PORTS=1 bun packages/everything-dev/src/cli.ts dev --no-interactive --ssr --port 4100 --api-port 4101 --auth-port 4102 --ui-port 4103 --plugin-port-start 4110", "regression:start:prod": "BOS_NO_PERSIST_PORTS=1 PORT=4100 bun ./node_modules/everything-dev/dist/cli.mjs start --no-interactive", + "regression:start:backcompat": "BOS_NO_PERSIST_PORTS=1 bun packages/everything-dev/src/cli.ts dev --no-interactive --ssr --host local --ui remote --api remote --auth local --remote-plugins apps,template --port 4100 --api-port 4101 --auth-port 4102 --ui-port 4103 --plugin-port-start 4110", "test:regression:http:dev": "REGRESSION_MODE=dev sh -lc 'cd tests/regression/http && go test ./... -count=1 -v -timeout 10m'", "test:regression:http:prod": "REGRESSION_MODE=prod sh -lc 'cd tests/regression/http && go test ./... -count=1 -v -timeout 10m'", + "test:regression:http:backcompat": "REGRESSION_MODE=backcompat sh -lc 'cd tests/regression/http && go test ./... -count=1 -v -timeout 10m'", "test:regression:browser:install": "bunx playwright install --with-deps chromium", "test:regression:browser:dev": "REGRESSION_MODE=dev bunx playwright test -c tests/regression/browser/playwright.config.mjs --project=dev", "test:regression:browser:prod": "REGRESSION_MODE=prod bunx playwright test -c tests/regression/browser/playwright.config.mjs --project=prod", - "test:regression:dev": "bun run test:regression:http:dev && bun run test:regression:browser:dev", - "test:regression:prod": "bun run test:regression:http:prod && bun run test:regression:browser:prod", + "test:regression:browser:backcompat": "REGRESSION_MODE=backcompat bunx playwright test -c tests/regression/browser/playwright.config.mjs --project=backcompat", + "test:regression:dev": "bun run test:regression:http:dev; bun run test:regression:browser:dev", + "test:regression:prod": "bun run test:regression:http:prod; bun run test:regression:browser:prod", + "test:regression:backcompat": "bun run test:regression:http:backcompat; bun run test:regression:browser:backcompat", "test:regression": "bun run test:regression:dev", "dev:postgres": "docker compose up -d --wait && bun run dev", "dev:postgres:down": "docker compose down", diff --git a/packages/every-plugin/skills/plugin-client/SKILL.md b/packages/every-plugin/skills/plugin-client/SKILL.md index 7c8125b8..5a9f9d30 100644 --- a/packages/every-plugin/skills/plugin-client/SKILL.md +++ b/packages/every-plugin/skills/plugin-client/SKILL.md @@ -437,7 +437,7 @@ This writes `bos.config.json` with `extends`, scaffolds the project, and generat ### What Can Be Overridden -Child configs can override `app.ui` (custom UI CDN) and `plugins.` (custom plugin URLs). The host's `ALLOW_OVERRIDE` env var controls which sections tenants can customize (`ui`, `plugins`, `plugins.`). +Child configs can override `app.ui` (custom UI CDN) and `plugins.` (custom plugin URLs). Which sections a tenant can customize is controlled per-tenant by the `allow_ui_overrides` and `allow_backend_overrides` flags on the tenant record, resolved by the host from the API's `GET /tenants/bindings` endpoint (cached for 30s). See the `extends-config` skill for deep merge semantics, per-environment extends, and canonical field ordering. diff --git a/packages/everything-dev/skills/api-and-auth/SKILL.md b/packages/everything-dev/skills/api-and-auth/SKILL.md index 06416252..e302ff66 100644 --- a/packages/everything-dev/skills/api-and-auth/SKILL.md +++ b/packages/everything-dev/skills/api-and-auth/SKILL.md @@ -356,6 +356,54 @@ export function createPluginsClient(result, context) { } ``` +### Tenant-Scoped Data + +A **tenant** is a deployment record (subdomain + NEAR account + UI/backend/SSR override +permissions) — distinct from an organization (a group of users) and from a user's own data. +If your plugin stores data that belongs to a specific tenant deployment (not just a user or +org), resolve the tenant and scope every query to it. + +The `api` plugin owns the `tenants` table and exposes public lookup routes (no auth required — +tenant lookups are read-only and safe to expose): + +```ts +// From any plugin, via pluginsClient.api (injected through withPlugins()) +const tenant = context.organization?.activeOrganizationId + ? await plugins.api().resolveTenantByOrgId({ orgId: context.organization.activeOrganizationId }) + : await plugins.api().resolveTenant({ accountId: context.near?.primaryAccountId ?? "" }); +``` + +Build a local `requireTenant` middleware in your own plugin (mirrors `requireOrganization`): + +```ts +const requireTenant = builder.middleware(async ({ context, next }) => { + const activeOrgId = context.organization?.activeOrganizationId; + const tenant = activeOrgId + ? await plugins.api().resolveTenantByOrgId({ orgId: activeOrgId }).catch(() => null) + : null; + if (!tenant) { + throw new ORPCError("FORBIDDEN", { message: "No tenant found for this organization" }); + } + return next({ context: { ...context, tenant } }); +}); + +builder.listReports.use(requireTenant).handler(async ({ context }) => { + return await services.reports.listByTenant(context.tenant.id); // always filtered +}); +``` + +**Row-level convention (interim isolation)**: add a `tenantId` column to any table holding +tenant-specific application data, resolved server-side and never trusted from client input — +same discipline the `tenants` table itself uses for `orgId` scoping. See +`plugins/_template/src/db/schema.ts` for a commented example table. + +**Forward path**: the target architecture (see `plans/beta-v2-tenants.md`) is per-tenant-per-plugin +Postgres schema isolation (`tenant__plugin_`, `search_path` injected per request). That +requires request-scoped DB access instead of the current initialize-time singleton pattern — a +larger change, only worth it once there's a real multi-tenant plugin ecosystem to isolate. The +`tenantId` column convention above is forward-compatible: when schema isolation lands, the column +becomes redundant and can be dropped without reworking query logic. + ## Generated Types See `references/generated-types.md` for the full table — files, contents, and regeneration triggers. diff --git a/packages/everything-dev/skills/extends-config/SKILL.md b/packages/everything-dev/skills/extends-config/SKILL.md index 65fe9041..c8fc8d0a 100644 --- a/packages/everything-dev/skills/extends-config/SKILL.md +++ b/packages/everything-dev/skills/extends-config/SKILL.md @@ -73,13 +73,7 @@ Use this mental model: - `domain` is the public ingress for this runtime - a runtime can be a child in lineage and still become a new tenant root on its own domain -With host env like: - -```bash -ALLOW_OVERRIDE=ui,plugins.* -TENANT_WHITELIST=pizza.pingpayio.near -ALLOW_UNTRUSTED_SSR=false -``` +Tenant permissions come from the DB, not env vars. The host fetches the tenant bindings map from the API's `GET /tenants/bindings` endpoint (cached for 30s), and the tenant record's allow flags gate what may be overridden and whether SSR is permitted. Design target for request mapping: - `pizza.com` -> base runtime `bos://pizza.pingpayio.near/pizza.com` @@ -101,9 +95,8 @@ The tenant config must extend the base BOS runtime. Tenant API/auth overrides an ### SSR behavior Tenant SSR is gated separately from inheritance: -- if `ALLOW_UNTRUSTED_SSR=true`, any valid tenant with SSR config may SSR -- otherwise the tenant account must be listed in `TENANT_WHITELIST` -- non-whitelisted tenants fall back to client rendering +- a tenant may SSR only when its record has `allow_ssr` set +- tenants without `allow_ssr` fall back to client rendering ## Resolved Config: `.bos/bos.resolved-config.json` diff --git a/packages/everything-dev/skills/init-upgrade/SKILL.md b/packages/everything-dev/skills/init-upgrade/SKILL.md index df40b85e..dad27afa 100644 --- a/packages/everything-dev/skills/init-upgrade/SKILL.md +++ b/packages/everything-dev/skills/init-upgrade/SKILL.md @@ -81,15 +81,9 @@ Child apps can either run as their own base runtime on their own domain, or as r Shared-host children must extend the base runtime and do not introduce new server-side plugin IDs dynamically. -### 4. Host deployment env for shared-host mode +### 4. Host resolution for shared-host mode -The shared host uses these env vars to resolve descendant requests: - -```bash -ALLOW_OVERRIDE=ui,plugins.* -TENANT_WHITELIST=pizza.pingpayio.near,chicago.pizza.pingpayio.near -ALLOW_UNTRUSTED_SSR=false -``` +The shared host resolves descendant requests via a DB-backed binding map. It fetches tenant permissions from the API's `GET /tenants/bindings` endpoint (cached for 30s) — no env vars are needed: Design target, for example: - `pingpay.io` -> base runtime `bos://pingpayio.near/pingpay.io` diff --git a/packages/everything-dev/skills/super-app/SKILL.md b/packages/everything-dev/skills/super-app/SKILL.md index 4adae2f3..bc32b536 100644 --- a/packages/everything-dev/skills/super-app/SKILL.md +++ b/packages/everything-dev/skills/super-app/SKILL.md @@ -87,20 +87,13 @@ This runtime is still a lineage child because it extends the parent, but it is a You can also override existing plugin UIs and sidebar entries for tenant-specific navigation. -## Host Env For Tenant Mode +## Host Resolution For Tenant Mode -The shared host uses these env vars to resolve tenants: +The shared host resolves tenant permissions from the API, not env vars. It fetches the tenant bindings map from the API's `GET /tenants/bindings` endpoint (cached for 30s) and applies DB-backed per-tenant permissions: -```bash -ALLOW_OVERRIDE=ui,plugins.* -TENANT_WHITELIST=pizza.pingpayio.near,chicago.pizza.pingpayio.near -ALLOW_UNTRUSTED_SSR=false -``` - -Meaning: -- `ALLOW_OVERRIDE` controls which tenant config sections can affect request-scoped composition. Format: comma-separated list (e.g., `ui,plugins.*`) where `plugins.*` is a glob matching all plugin overrides -- `TENANT_WHITELIST` controls which tenants may use SSR -- `ALLOW_UNTRUSTED_SSR=true` allows SSR for any valid tenant with SSR config +- `allow_ui_overrides` controls whether the tenant `ui` override can affect request-scoped composition +- `allow_backend_overrides` controls whether tenant plugin UI overrides (`plugins..ui`, `plugins..sidebar`) are applied +- `allow_ssr` controls whether the tenant may use SSR ## Resolution Rules @@ -141,7 +134,7 @@ If tenant overrides are not applying as expected: 1. **Verify the tenant config exists in FastKV**: The config must be published to `{tenantAccount}/bos/gateways/{gateway}/bos.config.json` 2. **Check extends chain**: The tenant config must extend the base runtime via its `extends` field 3. **Check host logs**: Run `cat .bos/logs/host.log` and look for tenant resolution messages — the host logs which runtime config it resolved for each request -4. **Check env vars**: `ALLOW_OVERRIDE` must include the sections you're overriding (e.g., `ui` or `plugins.*`) +4. **Check tenant permissions**: the tenant record must have the relevant allow flag set in the database (e.g., `allow_ui_overrides` for a `ui` override, `allow_backend_overrides` for plugin UI overrides) 5. **Verify integrity**: If tenant remote URLs have integrity hashes, the host validates them — mismatches cause rejection 6. **Missing tenant config**: If the tenant config is not found in FastKV, the host silently serves the base runtime config without tenant overrides — no error is surfaced to the browser diff --git a/packages/everything-dev/src/api-contract.ts b/packages/everything-dev/src/api-contract.ts index 657d1eb4..4f1993f6 100644 --- a/packages/everything-dev/src/api-contract.ts +++ b/packages/everything-dev/src/api-contract.ts @@ -701,7 +701,7 @@ export async function syncApiContractBridge(opts: { ); pluginResults.forEach((result, index) => { - const [key, plugin] = resolvablePlugins[index]; + const [key, plugin] = resolvablePlugins[index]!; if (result.status === "fulfilled") { sources.push(result.value.source); status.push({ diff --git a/packages/everything-dev/src/cli.ts b/packages/everything-dev/src/cli.ts index c8941bc6..2c3bf892 100644 --- a/packages/everything-dev/src/cli.ts +++ b/packages/everything-dev/src/cli.ts @@ -20,6 +20,7 @@ import type { PsResult, StartOptions, StartResult, + TypecheckWorkspaceResult, } from "./contract"; import type { ProgressEvent, StartSummary } from "./plugin"; import bosPlugin, { consumeDevSession, pluginEvents } from "./plugin"; @@ -913,6 +914,39 @@ async function main() { return; } + if (descriptor.key === "typecheck") { + console.log(); + if (result.status === "error") { + console.error(`[CLI] ${result.error || "Unknown error"}`); + process.exit(1); + } + const failed = result.results.filter((r: TypecheckWorkspaceResult) => !r.passed); + const passed = result.results.filter((r: TypecheckWorkspaceResult) => r.passed); + console.log(colors.cyan(frames.top(52))); + console.log(` ${icons.app} ${gradients.cyber("TYPECHECK")}`); + console.log(colors.cyan(frames.bottom(52))); + console.log(); + for (const r of result.results) { + const icon = r.passed ? colors.green("✓") : colors.error("✗"); + console.log(` ${icon} ${r.workspace}`); + } + if (result.skipped.length > 0) { + console.log(` ${colors.dim(`Skipped: ${result.skipped.join(", ")}`)}`); + } + console.log(); + if (failed.length > 0) { + console.log(` ${colors.error(`${failed.length} failed`)}`); + for (const f of failed) { + if (f.error) console.log(` ${colors.dim(f.error)}`); + } + console.log(); + process.exit(1); + } + console.log(` ${colors.green(`${passed.length} passed`)}`); + console.log(); + return; + } + if (result?.status === "error" && descriptor.key !== "publish" && descriptor.key !== "deploy") { console.error(`[CLI] ${result.error || "Unknown error"}`); process.exit(1); @@ -1111,6 +1145,11 @@ async function main() { process.exit(1); } } + + if (descriptor.key === "infraExport") { + process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); + return; + } } catch (error) { console.error(`[CLI] ${error instanceof Error ? error.message : String(error)}`); process.exit(1); diff --git a/packages/everything-dev/src/cli/infra.ts b/packages/everything-dev/src/cli/infra.ts index 92a0731f..69b80990 100644 --- a/packages/everything-dev/src/cli/infra.ts +++ b/packages/everything-dev/src/cli/infra.ts @@ -118,6 +118,12 @@ export function normalizeRedisSlug(secret: string): string { return secret.replace(/_REDIS_URL$/, "").toLowerCase(); } +function extendsRefAccount(extendsRef: string | undefined): string | null { + if (!extendsRef || typeof extendsRef !== "string") return null; + const match = extendsRef.match(/^bos:\/\/([^/]+)\//); + return match?.[1] ?? null; +} + export function getSecretGroups(runtimeConfig: RuntimeConfig): SecretGroup[] { const groups: SecretGroup[] = []; const seen = new Set(); @@ -172,48 +178,23 @@ export function normalizeDatabaseSlug(secret: string): string { } export function buildOriginMap( - configDir: string, + _configDir: string, runtimeConfig: RuntimeConfig, ): Map { - const configPath = join(configDir, "bos.config.json"); - const originMap = new Map(); const account = runtimeConfig.account; - const resolveOrigin = (extendsRef: unknown): string | null => { - if (typeof extendsRef === "string") { - const match = extendsRef.match(/^bos:\/\/([^/]+)\//); - return match?.[1] ?? null; - } - return null; - }; - - const rawConfig = existsSync(configPath) - ? (JSON.parse(readFileSync(configPath, "utf-8")) as Record) - : null; - const rawPlugins = rawConfig?.plugins as Record | undefined; - for (const secret of runtimeConfig.api.secrets ?? []) { if (!originMap.has(secret)) originMap.set(secret, account); } - const rawApp = rawConfig?.app as Record | undefined; - const authExtends = (rawApp?.auth as Record | undefined)?.extends; - const authOrigin = resolveOrigin(authExtends) ?? account; + const authOrigin = extendsRefAccount(runtimeConfig.auth?.extendsRef) ?? account; for (const secret of runtimeConfig.auth?.secrets ?? []) { if (!originMap.has(secret)) originMap.set(secret, authOrigin); } for (const [pluginKey, pluginEntry] of Object.entries(runtimeConfig.plugins ?? {})) { - const rawPlugin = rawPlugins?.[pluginKey]; - let pluginOrigin: string; - if (typeof rawPlugin === "string") { - pluginOrigin = resolveOrigin(rawPlugin) ?? account; - } else if (rawPlugin && typeof rawPlugin === "object") { - pluginOrigin = resolveOrigin((rawPlugin as Record).extends) ?? account; - } else { - pluginOrigin = account; - } + const pluginOrigin = extendsRefAccount(pluginEntry.extendsRef) ?? account; for (const secret of pluginEntry.secrets ?? []) { if (!originMap.has(secret)) originMap.set(secret, pluginOrigin); } @@ -714,3 +695,146 @@ export function loadProjectEnv(configDir: string): void { loadDotenv({ path: envPath, processEnv: process.env, quiet: true }); envLoadedDir = configDir; } + +export interface CiServiceSpec { + key: string; + slug: string; + image: string; + env: Record; + ports: string[]; + healthcheck?: { test: string[]; interval: string; timeout: string; retries: number }; + volumes: string[]; + database?: { user: string; password: string; name: string }; +} + +export interface CiInfraPlan { + account: string; + gateway: string; + project: string; + env: Record; + services: CiServiceSpec[]; + generatedAt: string; +} + +export function buildCiInfraPlan( + runtimeConfig: RuntimeConfig, + options: { configDir?: string; hostPortOverride?: number } = {}, +): CiInfraPlan { + const configDir = options.configDir; + const originMap = configDir + ? buildOriginMap(configDir, runtimeConfig) + : new Map(); + const portState = loadPortState(configDir); + const allDatabaseSecrets = uniqueSecrets( + collectAllSecrets(runtimeConfig).filter((s) => s.endsWith("_DATABASE_URL")), + ); + const allDatabaseConfigs = buildDatabaseConfigs( + allDatabaseSecrets, + originMap, + portState.postgresPorts, + ); + const allRedisSecrets = uniqueSecrets( + collectAllSecrets(runtimeConfig).filter((s) => s.endsWith("_REDIS_URL")), + ); + const allRedisConfigs = buildRedisConfigs(allRedisSecrets, originMap, portState.redisPorts); + if (configDir) savePortState(configDir, portState); + + const hostPort = + options.hostPortOverride ?? + (Number.isFinite(Number(process.env.BOS_CI_HOST_PORT)) + ? Number(process.env.BOS_CI_HOST_PORT) + : undefined) ?? + resolveDevHostPort(runtimeConfig); + const env: Record = {}; + const envBySecret = new Map(); + for (const db of allDatabaseConfigs) envBySecret.set(db.secret, db.url); + for (const r of allRedisConfigs) envBySecret.set(r.secret, r.url); + + const groups = getSecretGroups(runtimeConfig); + for (const group of groups) { + for (const secret of group.secrets) { + const value = envBySecret.get(secret); + if (value) env[secret] = value; + } + } + + env["CORS_ORIGIN"] = `http://127.0.0.1:${hostPort}`; + if (!env["BETTER_AUTH_SECRET"]) env["BETTER_AUTH_SECRET"] = ""; + + const services: CiServiceSpec[] = []; + + for (const db of allDatabaseConfigs) { + const fromKey = db.fromKey || runtimeConfig.account; + services.push({ + key: db.slug, + slug: db.slug, + image: "postgres:17-alpine", + env: { + POSTGRES_USER: POSTGRES_USER, + POSTGRES_PASSWORD: POSTGRES_PASSWORD, + POSTGRES_DB: db.databaseName, + }, + ports: [`${db.port}:5432`], + healthcheck: { + test: ["CMD-SHELL", `pg_isready -U ${POSTGRES_USER}`], + interval: "3s", + timeout: "3s", + retries: 10, + }, + volumes: [`${fromKey.replace(/\./g, "_")}_postgres_${db.slug}_data:/var/lib/postgresql/data`], + database: { user: POSTGRES_USER, password: POSTGRES_PASSWORD, name: db.databaseName }, + }); + } + + for (const r of allRedisConfigs) { + services.push({ + key: r.slug, + slug: r.slug, + image: "redis:7-alpine", + env: {}, + ports: [`${r.port}:6379`], + healthcheck: { + test: ["CMD", "redis-cli", "ping"], + interval: "3s", + timeout: "3s", + retries: 10, + }, + volumes: [ + `${(r.fromKey || runtimeConfig.account).replace(/\./g, "_")}_redis_${r.slug}_data:/data`, + ], + }); + } + + return { + account: runtimeConfig.account, + gateway: runtimeConfig.domain ?? runtimeConfig.account, + project: runtimeConfig.account, + env, + services, + generatedAt: new Date().toISOString(), + }; +} + +function collectAllSecrets(runtimeConfig: RuntimeConfig): string[] { + const all: string[] = []; + all.push(...(runtimeConfig.host.secrets ?? [])); + all.push(...(runtimeConfig.api.secrets ?? [])); + if (runtimeConfig.auth) all.push(...(runtimeConfig.auth.secrets ?? [])); + if (runtimeConfig.plugins) { + for (const plugin of Object.values(runtimeConfig.plugins)) { + if (plugin.secrets) all.push(...plugin.secrets); + } + } + return all; +} + +function uniqueSlugs(entries: T[]): T[] { + const seen = new Set(); + const out: T[] = []; + for (const entry of entries) { + if (seen.has(entry.slug)) continue; + seen.add(entry.slug); + out.push(entry); + } + return out; +} diff --git a/packages/everything-dev/src/cli/sync.ts b/packages/everything-dev/src/cli/sync.ts index 079fd12c..87bf1350 100644 --- a/packages/everything-dev/src/cli/sync.ts +++ b/packages/everything-dev/src/cli/sync.ts @@ -13,6 +13,7 @@ import { syncResolvedSharedDeps } from "../shared-deps"; import { writeGeneratedInfra } from "./infra"; import { buildChildAgentsMd, + buildChildRootScripts, extractSkillsBlock, personalizeConfig, resolveSourceDir, @@ -153,6 +154,7 @@ export function mergePackageJson( filePath: string, local: PackageJson, template: PackageJson, + childScripts?: Record, ): PackageJson { const merged: PackageJson = { ...local, ...template }; @@ -185,11 +187,14 @@ export function mergePackageJson( if ( (local.scripts && typeof local.scripts === "object") || - (template.scripts && typeof template.scripts === "object") + (template.scripts && typeof template.scripts === "object") || + childScripts ) { const mergedScripts = mergeStringMaps( local.scripts as Record | undefined, - template.scripts as Record | undefined, + filePath === "package.json" && childScripts + ? childScripts + : (template.scripts as Record | undefined), ); if (mergedScripts) { merged.scripts = mergedScripts; @@ -264,6 +269,7 @@ function buildSyncedFileContent( projectDir: string, filePath: string, explicitDestPath?: string, + childScripts?: Record, ): string | Uint8Array { const src = join(sourceDir, filePath); const destPath = @@ -292,7 +298,7 @@ function buildSyncedFileContent( if (localContent) { const local = JSON.parse(localContent) as Record; const template = JSON.parse(templateContent) as Record; - const merged = mergePackageJson(destPath, local, template); + const merged = mergePackageJson(destPath, local, template, childScripts); return `${JSON.stringify(merged, null, 2)}\n`; } } @@ -305,6 +311,7 @@ function writeSyncedFile( projectDir: string, filePath: string, explicitDestPath?: string, + childScripts?: Record, ): void { const destPath = explicitDestPath ?? @@ -313,7 +320,10 @@ function writeSyncedFile( : filePath); const dest = join(projectDir, destPath); mkdirSync(dirname(dest), { recursive: true }); - writeFileSync(dest, buildSyncedFileContent(sourceDir, projectDir, filePath, destPath)); + writeFileSync( + dest, + buildSyncedFileContent(sourceDir, projectDir, filePath, destPath, childScripts), + ); } async function getSelectedChildPlugins( @@ -448,6 +458,13 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr const withHost = existsSync(join(projectDir, "host", "package.json")); const withPlugins = childPlugins.length > 0 || hasPluginsWorkspace(projectDir); + const childScripts = buildChildRootScripts({ + ui: withUi, + api: withApi, + host: withHost, + plugins: withPlugins, + }); + const destToSource = new Map(); for (const destPath of FRAMEWORK_OWNED_SYNC_FILES) { if (destPath.startsWith("ui/") && !withUi) continue; @@ -530,7 +547,9 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr for (const [destPath, filePath] of destToSource.entries()) { const localHash = computeLocalHash(projectDir, destPath); - const sourceHash = computeHash(buildSyncedFileContent(sourceDir, projectDir, filePath)); + const sourceHash = computeHash( + buildSyncedFileContent(sourceDir, projectDir, filePath, undefined, childScripts), + ); if (localHash === null) { added.push(destPath); @@ -604,7 +623,7 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr for (const destPath of filesToWrite) { const sourcePath = destToSource.get(destPath) ?? destPath; - writeSyncedFile(sourceDir, projectDir, sourcePath, destPath); + writeSyncedFile(sourceDir, projectDir, sourcePath, destPath, childScripts); } } diff --git a/packages/everything-dev/src/contract.meta.ts b/packages/everything-dev/src/contract.meta.ts index 4fa8d974..46e5aa65 100644 --- a/packages/everything-dev/src/contract.meta.ts +++ b/packages/everything-dev/src/contract.meta.ts @@ -162,6 +162,17 @@ export const cliCommandMeta = { }, }, }, + typecheck: { + commandPath: ["typecheck"], + summary: "Run TypeScript type checking across all local workspaces", + interactive: false, + fields: { + packages: { + positional: true, + description: "Comma-separated workspace list (default: all)", + }, + }, + }, dbStudio: { commandPath: ["db", "studio"], summary: "Open Drizzle Studio for a plugin's database", @@ -218,4 +229,14 @@ export const cliCommandMeta = { all: { description: "Kill processes across all config directories" }, }, }, + infraExport: { + commandPath: ["infra", "export"], + summary: "Emit the resolved CI infra plan (env + services) for the current runtime", + interactive: false, + fields: { + target: { description: "Export target: ci (default) or local" }, + network: { description: "NEAR network: mainnet or testnet" }, + configDir: { description: "Override config directory" }, + }, + }, } as const satisfies Record; diff --git a/packages/everything-dev/src/contract.ts b/packages/everything-dev/src/contract.ts index a4625810..857040a4 100644 --- a/packages/everything-dev/src/contract.ts +++ b/packages/everything-dev/src/contract.ts @@ -184,7 +184,7 @@ function parseNearAmount(value: string): number { const cleaned = value.replace(/[\s_,]/g, ""); const match = cleaned.match(/^(\d+(?:\.\d+)?)near$/i); if (!match) return NaN; - return Number.parseFloat(match[1]); + return Number.parseFloat(match[1]!); } const MIN_PUBLISH_ALLOWANCE_NEAR = 0.3; @@ -422,6 +422,56 @@ export const KillResultSchema = z.object({ error: z.string().optional(), }); +export const TypecheckOptionsSchema = z.object({ + packages: z.string().default("all"), +}); + +export const TypecheckWorkspaceResultSchema = z.object({ + workspace: z.string(), + passed: z.boolean(), + output: z.string().optional(), + error: z.string().optional(), +}); + +export const TypecheckResultSchema = z.object({ + status: z.enum(["success", "error"]), + checked: z.array(z.string()), + skipped: z.array(z.string()), + results: z.array(TypecheckWorkspaceResultSchema), + error: z.string().optional(), +}); + +export const InfraExportServiceSchema = z.object({ + key: z.string(), + image: z.string(), + env: z.record(z.string(), z.string()).default({}), + ports: z.array(z.string()), + healthcheck: z + .object({ + test: z.array(z.string()), + interval: z.string(), + timeout: z.string(), + retries: z.number(), + }) + .optional(), + volumes: z.array(z.string()).default([]), +}); + +export const InfraExportOptionsSchema = z.object({ + target: z.enum(["ci", "local"]).default("ci"), + network: z.enum(["mainnet", "testnet"]).optional(), + configDir: z.string().optional(), +}); + +export const InfraExportResultSchema = z.object({ + env: z.record(z.string(), z.string()), + services: z.array(InfraExportServiceSchema), + generatedAt: z.string(), + project: z.string(), + account: z.string(), + gateway: z.string(), +}); + export const bosContract = oc.router({ dev: oc.route({ method: "POST", path: "/dev" }).input(DevOptionsSchema).output(DevResultSchema), start: oc @@ -495,6 +545,14 @@ export const bosContract = oc.router({ .route({ method: "POST", path: "/kill" }) .input(KillOptionsSchema) .output(KillResultSchema), + typecheck: oc + .route({ method: "POST", path: "/typecheck" }) + .input(TypecheckOptionsSchema) + .output(TypecheckResultSchema), + infraExport: oc + .route({ method: "POST", path: "/infra/export" }) + .input(InfraExportOptionsSchema) + .output(InfraExportResultSchema), }); export type DevOptions = z.infer; @@ -540,3 +598,9 @@ export type PidEntry = z.infer; export type PsResult = z.infer; export type KillOptions = z.infer; export type KillResult = z.infer; +export type TypecheckOptions = z.infer; +export type TypecheckResult = z.infer; +export type TypecheckWorkspaceResult = z.infer; +export type InfraExportOptions = z.infer; +export type InfraExportResult = z.infer; +export type InfraExportService = z.infer; diff --git a/packages/everything-dev/src/fastkv.ts b/packages/everything-dev/src/fastkv.ts index b8c4c9ce..c3abdf87 100644 --- a/packages/everything-dev/src/fastkv.ts +++ b/packages/everything-dev/src/fastkv.ts @@ -73,7 +73,7 @@ export function parseBosUrl(bosUrl: string): { gatewayId: string; pathSegments: string[]; } { - const strippedUrl = bosUrl.split("#")[0]; + const strippedUrl = bosUrl.split("#")[0]!; const match = strippedUrl.match(/^bos:\/\/([^/]+)\/(.+)$/); if (!match?.[1] || !match[2]) { throw new Error(`Invalid BOS URL: ${bosUrl}`); diff --git a/packages/everything-dev/src/infra/planner.ts b/packages/everything-dev/src/infra/planner.ts index 2efb3939..09bd29ee 100644 --- a/packages/everything-dev/src/infra/planner.ts +++ b/packages/everything-dev/src/infra/planner.ts @@ -453,6 +453,10 @@ export function planInfra(input: InfraInput): Effect.Effect { + try { + const configPath = findConfigPath(); + if (!configPath) { + return { + status: "error" as const, + checked: [], + skipped: [], + results: [], + error: "No bos.config.json found in current directory", + }; + } + + const projectDir = resolve(dirname(configPath)); + const refreshed = await loadResolvedConfig({ cwd: projectDir }); + if (!refreshed) { + return { + status: "error" as const, + checked: [], + skipped: [], + results: [], + error: "Failed to load bos.config.json", + }; + } + + const runtime = refreshed.runtime; + type AppTarget = { source?: string; localPath?: string }; + const workspaceEntries: Array<{ key: string; label: string; dir: string }> = []; + const skipped: Array<{ key: string; label: string }> = []; + + const appTargets: Array<{ key: string; label: string; target: AppTarget | undefined }> = [ + { key: "host", label: "host", target: runtime.host }, + { key: "ui", label: "ui", target: runtime.ui }, + { key: "api", label: "api", target: runtime.api }, + ]; + if (runtime.auth) { + appTargets.push({ key: "auth", label: "auth", target: runtime.auth }); + } + + for (const entry of appTargets) { + if ( + entry.target?.source === "local" && + entry.target.localPath && + existsSync(join(entry.target.localPath, "tsconfig.json")) + ) { + workspaceEntries.push({ + key: entry.key, + label: entry.label, + dir: entry.target.localPath, + }); + } else { + skipped.push({ key: entry.key, label: entry.label }); + } + } + + for (const [key, plugin] of Object.entries(runtime.plugins ?? {})) { + const label = `plugins/${key}`; + if ( + plugin.source === "local" && + plugin.localPath && + existsSync(join(plugin.localPath, "tsconfig.json")) + ) { + workspaceEntries.push({ key, label, dir: plugin.localPath }); + } else { + skipped.push({ key, label }); + } + } + + const selected = selectWorkspaceTargets(input.packages, refreshed.config); + const targets = + input.packages === "all" + ? workspaceEntries + : workspaceEntries.filter((entry) => selected.includes(entry.key)); + const skippedEntries = + input.packages === "all" + ? skipped + : skipped.filter((entry) => selected.includes(entry.key)); + + const checked: string[] = []; + const results: Array<{ workspace: string; passed: boolean; error?: string }> = []; + + for (const entry of targets) { + const packageJsonPath = join(entry.dir, "package.json"); + let args: string[] = ["run", "tsc", "--noEmit"]; + if (existsSync(packageJsonPath)) { + try { + const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as { + scripts?: Record; + }; + if (pkg.scripts?.typecheck) { + args = ["run", "typecheck"]; + } + } catch { + // ignore unreadable package.json + } + } + + console.log(`\n ${colors.dim("Checking")} ${colors.cyan(entry.label)}`); + const child = spawnSync("bun", args, { + cwd: entry.dir, + stdio: "inherit", + }); + const passed = child.status === 0; + checked.push(entry.label); + results.push({ + workspace: entry.label, + passed, + error: passed ? undefined : `typecheck failed (exit code ${child.status ?? "n/a"})`, + }); + } + + return { + status: "success" as const, + checked, + skipped: skippedEntries.map((entry) => entry.label), + results, + }; + } catch (error) { + return { + status: "error" as const, + checked: [], + skipped: [], + results: [], + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }), + dbStudio: builder.dbStudio.handler(async ({ input }) => { try { const configPath = findConfigPath(); @@ -2001,6 +2132,25 @@ export default createPlugin({ }; } }), + + infraExport: builder.infraExport.handler(async ({ input }) => { + const configDir = input.configDir ?? deps.configDir; + const ci = deps.runtimeConfig ? buildCiInfraPlan(deps.runtimeConfig, { configDir }) : null; + if (!ci) { + const refreshed = await loadResolvedConfig({ cwd: configDir }); + if (!refreshed?.runtime) { + throw new Error("No resolved runtime config available for infra export"); + } + deps.runtimeConfig = refreshed.runtime; + return buildCiInfraPlan(refreshed.runtime, { configDir }); + } + const result: CiInfraPlan & { account: string; gateway: string } = { + ...ci, + account: deps.bosConfig?.account ?? ci.account, + gateway: ci.gateway ?? deps.bosConfig?.domain ?? deps.bosConfig?.account ?? ci.account, + }; + return result; + }), }), }); diff --git a/packages/everything-dev/src/publish.ts b/packages/everything-dev/src/publish.ts index 4f721102..a455eb66 100644 --- a/packages/everything-dev/src/publish.ts +++ b/packages/everything-dev/src/publish.ts @@ -10,6 +10,7 @@ import { buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, getRegistryNamespaceForNetwork, + type NetworkId, } from "./fastkv"; import { ensureNearCli, executeTransaction, resolveNearSigningMode } from "./near-cli"; import { getNetworkIdForAccount } from "./network"; @@ -107,7 +108,7 @@ export async function publishToFastKv(input: PublishToFastKvInput): Promise !r.success); - if (failures.length > 0) { - const total = deployResults.length; - console.log(); - console.log( - colors.error( - ` ${icons.err} Deploy failed — ${failures.length} of ${total} workspace${total > 1 ? "s" : ""} failed`, - ), - ); - console.log(); - for (const f of failures) { - const errorLine = (f.error ?? "Failed").split("\n")[0]; - console.log(` ${colors.error(icons.err)} ${padRight(f.key, 28)} ${errorLine}`); - } - console.log(); - if (!input.verbose) { - console.log(colors.dim(" Run with --verbose for full build output.")); + console.log(" Ensuring NEAR CLI..."); + await Effect.runPromise(ensureNearCli); + console.log(" NEAR CLI ready"); + + await generateCodeArtifacts(configDir, bosConfig, { + env: "production", + runtimeConfig: runtimeConfig ?? undefined, + }); + + const result = await buildWorkspaceTargets({ + configDir, + bosConfig, + runtimeConfig, + targets, + deploy: true, + verbose: input.verbose, + }); + built = result.built; + skipped = result.skipped; + deployResults = result.deployResults; + + if (deployResults) { + const failures = deployResults.filter((r) => !r.success); + if (failures.length > 0) { + const total = deployResults.length; + console.log(); + console.log( + colors.error( + ` ${icons.err} Deploy failed — ${failures.length} of ${total} workspace${total > 1 ? "s" : ""} failed`, + ), + ); console.log(); + for (const f of failures) { + const errorLine = (f.error ?? "Failed").split("\n")[0]; + console.log(` ${colors.error(icons.err)} ${padRight(f.key, 28)} ${errorLine}`); + } + console.log(); + if (!input.verbose) { + console.log(colors.dim(" Run with --verbose for full build output.")); + console.log(); + } + return { + status: "error" as const, + registryUrl, + built, + skipped, + deployResults, + error: `${failures.length} of ${total} workspaces failed to deploy`, + }; } + } + + const refreshed = await loadResolvedConfig({ cwd: configDir }); + if (!refreshed?.config) { return { - status: "error" as const, + status: "error", registryUrl, built, skipped, deployResults, - error: `${failures.length} of ${total} workspaces failed to deploy`, + error: "Failed to reload bos.config.json after build", }; } - } - const refreshed = await loadResolvedConfig({ cwd: configDir }); - if (!refreshed?.config) { - return { - status: "error", - registryUrl, - built, - skipped, - deployResults, - error: "Failed to reload bos.config.json after build", - }; + bosConfig = refreshed.config; } - bosConfig = refreshed.config; - } - - const rawConfigPath = join(configDir, "bos.config.json"); - const rawConfig = JSON.parse(readFileSync(rawConfigPath, "utf-8")) as BosConfigInput; - const publishPayload: BosConfigInput = isStaging ? { ...rawConfig, domain: gateway } : rawConfig; + const rawConfigPath = join(configDir, "bos.config.json"); + const rawConfig = JSON.parse(readFileSync(rawConfigPath, "utf-8")) as BosConfigInput; + const publishPayload: BosConfigInput = isStaging + ? { ...rawConfig, domain: gateway } + : rawConfig; - const registryEntries: Record = { - [`apps/${account}/${gateway}/bos.config.json`]: JSON.stringify(publishPayload), - }; + const registryEntries: Record = { + [`apps/${account}/${gateway}/bos.config.json`]: JSON.stringify(publishPayload), + }; - const payload = JSON.stringify(registryEntries); - const argsBase64 = Buffer.from(payload).toString("base64"); + const payload = JSON.stringify(registryEntries); + const argsBase64 = Buffer.from(payload).toString("base64"); - console.log(); - console.log(" Publishing to:"); - console.log(` ${colors.cyan(registryUrl)}`); + console.log(); + console.log(" Publishing to:"); + console.log(` ${colors.cyan(registryUrl)}`); - try { - let txHash: string | undefined; + try { + let txHash: string | undefined; - console.log(` Submitting transaction on ${network}...`); + console.log(` Submitting transaction on ${network}...`); - try { - const tx = await Effect.runPromise( - executeTransaction( - { - account, - contract: getRegistryNamespaceForNetwork(network), - method: "__fastdata_kv", - argsBase64, - network, - privateKey: signingMode._tag === "privateKey" ? signingMode.privateKey : undefined, - gas: "300Tgas", - deposit: "0NEAR", - verbose: input.verbose, - }, - signingMode, - ), - ); - txHash = tx.txHash; - if (txHash && !tx.output?.includes("CodeDoesNotExist")) { - console.log(` Transaction submitted: ${colors.dim(txHash)}`); - } - } catch (error) { - console.log(colors.dim(" Transaction reported an error — verifying publish...")); try { - await waitForPublishedConfig({ - account, - gateway, - publishConfig: publishPayload, - timeoutMs: 30_000, - intervalMs: 2_000, - }); - txHash = extractTransactionHash(error); - } catch { - throw error; + const tx = await Effect.runPromise( + executeTransaction( + { + account, + contract: getRegistryNamespaceForNetwork(network), + method: "__fastdata_kv", + argsBase64, + network, + privateKey: signingMode._tag === "privateKey" ? signingMode.privateKey : undefined, + gas: "300Tgas", + deposit: "0NEAR", + verbose: input.verbose, + }, + signingMode, + ), + ); + txHash = tx.txHash; + if (txHash && !tx.output?.includes("CodeDoesNotExist")) { + console.log(` Transaction submitted: ${colors.dim(txHash)}`); + } + } catch (error) { + console.log(colors.dim(" Transaction reported an error — verifying publish...")); + try { + await waitForPublishedConfig({ + account, + gateway, + publishConfig: publishPayload, + timeoutMs: 30_000, + intervalMs: 2_000, + }); + txHash = extractTransactionHash(error); + } catch { + throw error; + } } - } - console.log(" Waiting for publish confirmation..."); - await waitForPublishedConfig({ - account, - gateway, - publishConfig: publishPayload, - }); + console.log(" Waiting for publish confirmation..."); + await waitForPublishedConfig({ + account, + gateway, + publishConfig: publishPayload, + }); - return { - status: "published", - registryUrl, - txHash, - built, - skipped, - deployResults, - publishConfig: publishPayload, - }; - } catch (error) { - return { - status: "error", - registryUrl, - error: formatNearError(error), - built, - skipped, - deployResults, - }; - } + return { + status: "published", + registryUrl, + txHash, + built, + skipped, + deployResults, + publishConfig: publishPayload, + }; + } catch (error) { + return { + status: "error", + registryUrl, + error: formatNearError(error), + built, + skipped, + deployResults, + }; + } } function formatNearError(error: unknown): string { diff --git a/packages/everything-dev/src/ui/router.ts b/packages/everything-dev/src/ui/router.ts index b999f7b4..31ab29a6 100644 --- a/packages/everything-dev/src/ui/router.ts +++ b/packages/everything-dev/src/ui/router.ts @@ -38,45 +38,26 @@ export async function collectHeadData(router: AnyRouter): Promise { const scriptMap = new Map(); for (const match of router.state.matches) { - const route = - ( - router as AnyRouter & { - routesById?: Record unknown } }>; - } - ).routesById?.[(match as { routeId: string }).routeId] ?? - (match as { route?: { options?: { head?: (...args: unknown[]) => unknown } } }).route; - const headFn = route?.options?.head; - if (!headFn) continue; + const matchData = match as { + meta?: HeadMeta[]; + links?: HeadLink[]; + headScripts?: HeadScript[]; + }; - try { - const headResult = (await headFn({ - loaderData: match.loaderData, - matches: router.state.matches, - match, - params: match.params, - })) as { - meta?: HeadMeta[]; - links?: HeadLink[]; - scripts?: HeadScript[]; - }; - - if (headResult?.meta) { - for (const meta of headResult.meta) { - metaMap.set(getMetaKey(meta), meta); - } + if (matchData.meta) { + for (const meta of matchData.meta) { + metaMap.set(getMetaKey(meta), meta); } - if (headResult?.links) { - for (const link of headResult.links) { - linkMap.set(getLinkKey(link), link); - } + } + if (matchData.links) { + for (const link of matchData.links) { + linkMap.set(getLinkKey(link), link); } - if (headResult?.scripts) { - for (const script of headResult.scripts) { - scriptMap.set(getScriptKey(script), script); - } + } + if (matchData.headScripts) { + for (const script of matchData.headScripts) { + scriptMap.set(getScriptKey(script), script); } - } catch (error) { - console.warn(`[collectHeadData] head() failed for ${match.routeId}:`, error); } } diff --git a/packages/everything-dev/tests/integration/init.structure.test.ts b/packages/everything-dev/tests/integration/init.structure.test.ts index 6ce39a99..0b7d088c 100644 --- a/packages/everything-dev/tests/integration/init.structure.test.ts +++ b/packages/everything-dev/tests/integration/init.structure.test.ts @@ -111,8 +111,8 @@ describe("bos init — structure", () => { expect(existsSync(join(noPluginsDir, "plugins"))).toBe(false); - expect(existsSync(join(noPluginsDir, "ui/src/routes/_layout/apps"))).toBe(false); - expect(existsSync(join(noPluginsDir, "ui/src/routes/_layout/index.tsx"))).toBe(true); + expect(existsSync(join(noPluginsDir, "ui/src/routes/_layout/_public/apps"))).toBe(false); + expect(existsSync(join(noPluginsDir, "ui/src/routes/_layout/_public/index.tsx"))).toBe(true); const config = JSON.parse(readFileSync(join(noPluginsDir, "bos.config.json"), "utf-8")); expect(config.plugins).toEqual({}); diff --git a/packages/everything-dev/tests/integration/personalize-config.test.ts b/packages/everything-dev/tests/integration/personalize-config.test.ts index 5305c3ac..4d00b804 100644 --- a/packages/everything-dev/tests/integration/personalize-config.test.ts +++ b/packages/everything-dev/tests/integration/personalize-config.test.ts @@ -112,9 +112,9 @@ describe("personalizeConfig with real root config", () => { expect(existsSync(join(testDir, "plugins", "apps"))).toBe(true); expect(existsSync(join(testDir, "plugins", "example"))).toBe(false); expect(existsSync(join(testDir, "plugins", "example"))).toBe(false); - expect(existsSync(join(testDir, "ui", "src", "routes", "_layout", "apps", "index.tsx"))).toBe( - true, - ); + expect( + existsSync(join(testDir, "ui", "src", "routes", "_layout", "_public", "apps", "index.tsx")), + ).toBe(true); expect( existsSync(join(testDir, "ui", "src", "routes", "_layout", "_authenticated", "example.tsx")), ).toBe(false); diff --git a/packages/everything-dev/tests/integration/plugin-ui-runtime.test.ts b/packages/everything-dev/tests/integration/plugin-ui-runtime.test.ts index 4bf75cf6..7329358d 100644 --- a/packages/everything-dev/tests/integration/plugin-ui-runtime.test.ts +++ b/packages/everything-dev/tests/integration/plugin-ui-runtime.test.ts @@ -109,7 +109,7 @@ describe("plugin UI runtime config", () => { apps: { name: "isolated-apps", production: "https://isolated-apps.test.dev", - routes: ["ui/src/routes/_layout/apps/**"], + routes: ["ui/src/routes/_layout/_public/apps/**"], }, }, }); @@ -130,7 +130,7 @@ describe("plugin UI runtime config", () => { expect(pluginRuntime?.apps.name).toBe("isolated-apps"); expect(pluginRuntime?.apps.url).toBe("https://isolated-apps.test.dev"); - expect(pluginRuntime?.apps.routes).toEqual(["ui/src/routes/_layout/apps/**"]); + expect(pluginRuntime?.apps.routes).toEqual(["ui/src/routes/_layout/_public/apps/**"]); }); it("does not resolve remote local targets against the consumer root", async () => { diff --git a/packages/everything-dev/tests/integration/sync.structure.test.ts b/packages/everything-dev/tests/integration/sync.structure.test.ts index f2361117..e6aebe85 100644 --- a/packages/everything-dev/tests/integration/sync.structure.test.ts +++ b/packages/everything-dev/tests/integration/sync.structure.test.ts @@ -22,7 +22,7 @@ describe("bos sync — framework-owned files", () => { expect(isFrameworkOwnedSyncFile("Dockerfile")).toBe(false); expect(isFrameworkOwnedSyncFile(".github/renovate.json")).toBe(false); expect(isFrameworkOwnedSyncFile(".opencode/skills/everything-dev/SKILL.md")).toBe(false); - expect(isFrameworkOwnedSyncFile("ui/src/routes/_layout/index.tsx")).toBe(false); + expect(isFrameworkOwnedSyncFile("ui/src/routes/_layout/_public/index.tsx")).toBe(false); expect(isFrameworkOwnedSyncFile("ui/src/components/user-nav.tsx")).toBe(false); expect(isFrameworkOwnedSyncFile("api/src/index.ts")).toBe(false); expect(isFrameworkOwnedSyncFile("plugins/apps/src/index.ts")).toBe(false); @@ -89,6 +89,61 @@ describe("bos sync — package.json merge", () => { expect(merged.workspaces.catalog["everything-dev"]).toBe("^1.2.3"); }); + it("filters root package scripts to the child script set when provided", () => { + const childScripts = { + dev: "bos dev", + build: "bos build", + typecheck: "bun run types:gen && bun run --cwd api typecheck", + }; + const merged = mergePackageJson( + "package.json", + { + name: "my-app", + private: true, + scripts: { + dev: "custom dev", + custom: "custom script", + }, + }, + { + name: "monorepo", + scripts: { + dev: "node_modules/.bin/bos dev", + build: "node_modules/.bin/bos build", + "regression:start:backcompat": "bos dev --ui remote --api remote", + "test:regression:backcompat": "bun run test:regression:http:backcompat", + }, + }, + childScripts, + ) as { + name: string; + scripts: Record; + }; + + expect(merged.name).toBe("my-app"); + expect(merged.scripts.dev).toBe("bos dev"); + expect(merged.scripts.build).toBe("bos build"); + expect(merged.scripts.typecheck).toBe("bun run types:gen && bun run --cwd api typecheck"); + expect(merged.scripts.custom).toBe("custom script"); + expect(merged.scripts["regression:start:backcompat"]).toBeUndefined(); + expect(merged.scripts["test:regression:backcompat"]).toBeUndefined(); + }); + + it("ignores childScripts for non-root package.json", () => { + const childScripts = { dev: "bos dev" }; + const merged = mergePackageJson( + "ui/package.json", + { name: "ui", scripts: { build: "custom build" } }, + { name: "ui", scripts: { build: "bun run build:client" } }, + childScripts, + ) as { + scripts: Record; + }; + + expect(merged.scripts.build).toBe("bun run build:client"); + expect(merged.scripts.dev).toBeUndefined(); + }); + it("preserves custom workspace package data while overwriting scaffold entries", () => { const merged = mergePackageJson( "ui/package.json", diff --git a/packages/everything-dev/tests/unit/infra-export.test.ts b/packages/everything-dev/tests/unit/infra-export.test.ts new file mode 100644 index 00000000..e3c5fffb --- /dev/null +++ b/packages/everything-dev/tests/unit/infra-export.test.ts @@ -0,0 +1,199 @@ +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; +import { + buildCiInfraPlan, + buildOriginMap, + ensureEnvFile, + syncGeneratedInfra, + writeGeneratedInfra, +} from "../../src/cli/infra"; +import type { RuntimeConfig, RuntimePluginConfig } from "../../src/types"; + +function buildRuntimeConfig(overrides?: Partial): RuntimeConfig { + return { + env: "development", + account: "dev.everything.near", + networkId: "mainnet", + domain: "dev.everything.dev", + host: { + name: "host", + url: "http://localhost:4100", + entry: "/mf-manifest.json", + port: 4100, + }, + ui: { name: "ui", url: "http://localhost:3003", entry: "/mf-manifest.json" }, + api: { + name: "api", + url: "http://localhost:3001", + entry: "/mf-manifest.json", + secrets: ["API_DATABASE_URL"], + }, + auth: { + name: "auth", + url: "http://localhost:3002", + entry: "/mf-manifest.json", + secrets: ["AUTH_DATABASE_URL", "BETTER_AUTH_SECRET", "CORS_ORIGIN"], + }, + plugins: { + example: { + name: "example", + url: "http://localhost:3010", + entry: "/mf-manifest.json", + source: "local" as const, + secrets: ["EXAMPLE_DATABASE_URL"], + } as RuntimePluginConfig, + }, + ...overrides, + } as RuntimeConfig; +} + +describe("buildCiInfraPlan", () => { + const tempDirs: string[] = []; + + afterEach(() => { + while (tempDirs.length > 0) { + const dir = tempDirs.pop(); + if (dir) rmSync(dir, { recursive: true, force: true }); + } + }); + + it("emits env vars and services for api+auth+plugin secrets", () => { + const dir = mkdtempSync(join(tmpdir(), "bos-ci-infra-")); + tempDirs.push(dir); + writeGeneratedInfra(dir, buildRuntimeConfig()); + ensureEnvFile(dir); + + const runtime = { + ...buildRuntimeConfig(), + env: "production" as const, + }; + const plan = buildCiInfraPlan(runtime, { configDir: dir }); + + expect(plan.env["API_DATABASE_URL"]).toBe( + "postgres://everythingdev:everythingdev@localhost:5432/api_db", + ); + expect(plan.env["AUTH_DATABASE_URL"]).toBe( + "postgres://everythingdev:everythingdev@localhost:5433/auth_db", + ); + expect(plan.env["EXAMPLE_DATABASE_URL"]).toBe( + "postgres://everythingdev:everythingdev@localhost:5434/example_db", + ); + expect(plan.env["BETTER_AUTH_SECRET"]).toBe(""); + expect(plan.env["CORS_ORIGIN"]).toBe("http://127.0.0.1:4100"); + + expect(plan.services.length).toBeGreaterThanOrEqual(3); + const serviceKeys = plan.services.map((s) => s.key); + expect(serviceKeys).toContain("api"); + expect(serviceKeys).toContain("auth"); + expect(serviceKeys).toContain("example"); + + const apiService = plan.services.find((s) => s.key === "api"); + expect(apiService?.image).toBe("postgres:17-alpine"); + expect(apiService?.ports).toEqual(["5432:5432"]); + expect(apiService?.database).toEqual({ + user: "everythingdev", + password: "everythingdev", + name: "api_db", + }); + }); + + it("honors hostPortOverride and BOS_CI_HOST_PORT env fallback", () => { + const previous = process.env.BOS_CI_HOST_PORT; + try { + process.env.BOS_CI_HOST_PORT = "5173"; + const plan = buildCiInfraPlan(buildRuntimeConfig(), {}); + expect(plan.env["CORS_ORIGIN"]).toBe("http://127.0.0.1:5173"); + + const override = buildCiInfraPlan(buildRuntimeConfig(), { hostPortOverride: 8080 }); + expect(override.env["CORS_ORIGIN"]).toBe("http://127.0.0.1:8080"); + } finally { + if (previous === undefined) delete process.env.BOS_CI_HOST_PORT; + else process.env.BOS_CI_HOST_PORT = previous; + } + }); + + it("tracks stable ports across calls (portMap persistence)", () => { + const dir = mkdtempSync(join(tmpdir(), "bos-ci-infra-ports-")); + tempDirs.push(dir); + const cfg = buildRuntimeConfig(); + syncGeneratedInfra(dir, cfg); + const first = buildCiInfraPlan(cfg, { configDir: dir }); + const second = buildCiInfraPlan(cfg, { configDir: dir }); + + const firstApi = first.services.find((s) => s.key === "api"); + const secondApi = second.services.find((s) => s.key === "api"); + expect(firstApi?.ports).toEqual(secondApi?.ports); + }); +}); + +describe("buildOriginMap from resolved RuntimeConfig", () => { + it("uses plugin extendsRef for plugin origins", () => { + const runtime: RuntimeConfig = { + env: "development", + account: "city.example.near", + networkId: "mainnet", + host: { name: "host", url: "http://localhost:3000", entry: "/mf-manifest.json" }, + ui: { name: "ui", url: "http://localhost:3003", entry: "/mf-manifest.json" }, + api: { + name: "api", + url: "http://localhost:3001", + entry: "/mf-manifest.json", + secrets: ["API_DATABASE_URL"], + }, + auth: { + name: "auth", + url: "http://localhost:3002", + entry: "/mf-manifest.json", + extendsRef: "bos://auth.everything.near/auth.everything.dev#app.auth", + secrets: ["AUTH_DATABASE_URL"], + }, + plugins: { + apps: { + name: "apps", + url: "http://localhost:3010", + entry: "/mf-manifest.json", + source: "local" as const, + extendsRef: "bos://something/near/gateway", + secrets: ["APPS_DATABASE_URL"], + } as RuntimePluginConfig, + }, + } as RuntimeConfig; + + const map = buildOriginMap("", runtime); + + expect(map.get("API_DATABASE_URL")).toBe("city.example.near"); + expect(map.get("AUTH_DATABASE_URL")).toBe("auth.everything.near"); + expect(map.get("APPS_DATABASE_URL")).toBe("something"); + }); + + it("falls back to runtime.account when extendsRef is absent", () => { + const runtime: RuntimeConfig = { + env: "development", + account: "city.example.near", + networkId: "mainnet", + host: { name: "host", url: "http://localhost:3000", entry: "/mf-manifest.json" }, + ui: { name: "ui", url: "http://localhost:3003", entry: "/mf-manifest.json" }, + api: { + name: "api", + url: "http://localhost:3001", + entry: "/mf-manifest.json", + secrets: ["API_DATABASE_URL"], + }, + plugins: { + localonly: { + name: "localonly", + url: "http://localhost:3010", + entry: "/mf-manifest.json", + source: "local" as const, + secrets: ["LOCALONLY_DATABASE_URL"], + } as RuntimePluginConfig, + }, + } as RuntimeConfig; + + const map = buildOriginMap("", runtime); + expect(map.get("API_DATABASE_URL")).toBe("city.example.near"); + expect(map.get("LOCALONLY_DATABASE_URL")).toBe("city.example.near"); + }); +}); diff --git a/packages/everything-dev/tests/unit/upgrade-migration.test.ts b/packages/everything-dev/tests/unit/upgrade-migration.test.ts index 5ae261d7..3889efcc 100644 --- a/packages/everything-dev/tests/unit/upgrade-migration.test.ts +++ b/packages/everything-dev/tests/unit/upgrade-migration.test.ts @@ -229,7 +229,7 @@ describe("upgrade bos config migration", () => { `${JSON.stringify( { domain: "apps.everything.dev", - routes: ["ui/src/routes/_layout/apps/**"], + routes: ["ui/src/routes/_layout/_public/apps/**"], }, null, 2, @@ -249,7 +249,7 @@ describe("upgrade bos config migration", () => { }; }; - expect(rootConfig.plugins.apps.routes).toEqual(["ui/src/routes/_layout/apps/**"]); + expect(rootConfig.plugins.apps.routes).toEqual(["ui/src/routes/_layout/_public/apps/**"]); }); it("deletes plugin bos.config.json files even when no metadata to merge", async () => { diff --git a/plugins/_template/src/db/schema.ts b/plugins/_template/src/db/schema.ts index b355bb35..51574d0f 100644 --- a/plugins/_template/src/db/schema.ts +++ b/plugins/_template/src/db/schema.ts @@ -14,3 +14,21 @@ export const things = pgTable( }, (table) => [index("things_type_idx").on(table.type)], ); + +// Tenant-scoped table convention (interim, row-level isolation): +// +// If your plugin stores data that belongs to a specific tenant deployment +// (not just a user or org), add a `tenantId` column and filter every query +// by it. Resolve the tenant via the API plugin's public lookup routes +// (`pluginsClient.api.resolveTenantByOrgId` / `resolveTenant`) and never +// trust a tenantId passed directly from client input. +// +// export const reports = pgTable("reports", { +// id: uuid("id").defaultRandom().primaryKey(), +// tenantId: text("tenant_id").notNull(), // resolved server-side, always filtered +// title: text("title").notNull(), +// createdAt: timestamp("created_at", { mode: "date", withTimezone: true }).defaultNow().notNull(), +// }, (table) => [index("reports_tenant_id_idx").on(table.tenantId)]); +// +// See the api-and-auth skill's "Tenant-Scoped Data" section for the full +// middleware pattern and the forward path to per-tenant schema isolation. diff --git a/plugins/apps/tsconfig.json b/plugins/apps/tsconfig.json index 94de5854..362a22c2 100644 --- a/plugins/apps/tsconfig.json +++ b/plugins/apps/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { - "lib": ["ESNext"], + "types": ["node"], + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "ESNext", "moduleDetection": "force", diff --git a/tests/regression/browser/helpers/global-setup.ts b/tests/regression/browser/helpers/global-setup.ts new file mode 100644 index 00000000..6b76539e --- /dev/null +++ b/tests/regression/browser/helpers/global-setup.ts @@ -0,0 +1,66 @@ +import { createHmac } from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import pg from "pg"; + +const AUTH_DATABASE_URL = + process.env.AUTH_DATABASE_URL ?? "postgres://everythingdev:everythingdev@127.0.0.1:5433/auth_db"; +const BETTER_AUTH_SECRET = + process.env.BETTER_AUTH_SECRET ?? "regression-test-secret-do-not-use-in-production"; +const ADMIN_USER_ID = "regression-admin-user"; +const ADMIN_SESSION_TOKEN = "regression-admin-session-token"; +const ADMIN_COOKIES_PATH = ".bos/regression/admin-cookies.json"; +const COOKIE_NAME = "better-auth.session_token"; + +function signCookieValue(token: string, secret: string): string { + const signature = createHmac("sha256", secret).update(token).digest("base64"); + return encodeURIComponent(`${token}.${signature}`); +} + +async function seedAdmin() { + const client = new pg.Client({ connectionString: AUTH_DATABASE_URL }); + await client.connect(); + try { + await client.query( + 'INSERT INTO "user" (id, name, email, email_verified, role, created_at, updated_at) VALUES ($1, $2, $3, true, $4, NOW(), NOW()) ON CONFLICT (id) DO NOTHING', + [ADMIN_USER_ID, "admin.near", "admin@everything.near", "admin"], + ); + await client.query( + "INSERT INTO account (id, account_id, provider_id, user_id, created_at, updated_at) VALUES ($1, $2, $3, $4, NOW(), NOW()) ON CONFLICT DO NOTHING", + [`${ADMIN_USER_ID}-account`, "admin.near:mainnet", "siwn", ADMIN_USER_ID], + ); + await client.query( + "INSERT INTO near_account (id, user_id, account_id, network, public_key, is_primary, created_at) VALUES ($1, $2, $3, $4, $5, true, NOW()) ON CONFLICT DO NOTHING", + [`${ADMIN_USER_ID}-near`, ADMIN_USER_ID, "admin.near", "mainnet", "ed25519:test"], + ); + await client.query( + "INSERT INTO session (id, expires_at, token, created_at, updated_at, ip_address, user_agent, user_id) VALUES ($1, NOW() + INTERVAL '1 year', $2, NOW(), NOW(), $3, $4, $5) ON CONFLICT (token) DO NOTHING", + [`${ADMIN_USER_ID}-session`, ADMIN_SESSION_TOKEN, "", "regression", ADMIN_USER_ID], + ); + } finally { + await client.end(); + } +} + +async function writeAdminCookies() { + const value = signCookieValue(ADMIN_SESSION_TOKEN, BETTER_AUTH_SECRET); + const cookies = [ + { + name: COOKIE_NAME, + value, + domain: "localhost", + path: "/", + httpOnly: false, + secure: false, + }, + ]; + const resolved = path.resolve(process.cwd(), ADMIN_COOKIES_PATH); + fs.mkdirSync(path.dirname(resolved), { recursive: true }); + fs.writeFileSync(resolved, JSON.stringify(cookies, null, 2)); + console.log(`[global-setup] wrote ${resolved}`); +} + +export default async function globalSetup() { + await seedAdmin(); + await writeAdminCookies(); +} diff --git a/tests/regression/browser/helpers/page-ready.ts b/tests/regression/browser/helpers/page-ready.ts index f508dec3..dab7f790 100644 --- a/tests/regression/browser/helpers/page-ready.ts +++ b/tests/regression/browser/helpers/page-ready.ts @@ -33,6 +33,24 @@ export async function waitForApp(page: Page): Promise { return typeof window.__RUNTIME_CONFIG__ !== "undefined"; }); expect(hasRuntimeConfig).toBeTruthy(); + + try { + await page.waitForFunction( + () => { + const p = (window as { __EVERYTHING_DEV_HYDRATE_PROMISE__?: Promise }) + .__EVERYTHING_DEV_HYDRATE_PROMISE__; + return p !== undefined; + }, + { timeout: 5000 }, + ); + await page.evaluate(async () => { + const p = (window as { __EVERYTHING_DEV_HYDRATE_PROMISE__?: Promise }) + .__EVERYTHING_DEV_HYDRATE_PROMISE__; + if (p) await p; + }); + } catch { + // Hydration promise never surfaced (e.g. client-shell fallback path) — proceed. + } } export function expectNoHydrationFailure(errors: PageErrors) { diff --git a/tests/regression/browser/helpers/seeded.ts b/tests/regression/browser/helpers/seeded.ts index 26c1efce..4d9bb07c 100644 --- a/tests/regression/browser/helpers/seeded.ts +++ b/tests/regression/browser/helpers/seeded.ts @@ -22,6 +22,7 @@ interface SeedData { } const COOKIES_PATH = ".bos/regression/cookies.json"; +const ADMIN_COOKIES_PATH = ".bos/regression/admin-cookies.json"; const SEED_PATH = ".bos/regression/seed.json"; function readJsonFile(filePath: string) { @@ -37,13 +38,18 @@ export async function injectCookies(page: Page) { await page.context().addCookies(cookies); } +export async function injectAdminCookies(page: Page) { + const cookies: CookieEntry[] = readJsonFile(ADMIN_COOKIES_PATH); + await page.context().addCookies(cookies); +} + export function loadSeedData(): SeedData { return readJsonFile(SEED_PATH) as SeedData; } export async function verifyAuthenticated(page: Page) { - await page.goto("/home", { waitUntil: "domcontentloaded" }); + await page.goto("/dashboard", { waitUntil: "domcontentloaded" }); await page.waitForTimeout(500); await page.waitForLoadState("networkidle"); - await expect(page.locator("button[title='menu']")).toBeVisible({ timeout: 10000 }); + await expect(page.locator("button[title='account menu']")).toBeVisible({ timeout: 10000 }); } diff --git a/tests/regression/browser/playwright.config.mjs b/tests/regression/browser/playwright.config.mjs index e10c8d22..5788da9f 100644 --- a/tests/regression/browser/playwright.config.mjs +++ b/tests/regression/browser/playwright.config.mjs @@ -1,13 +1,19 @@ import { defineConfig } from "@playwright/test"; const mode = process.env.REGRESSION_MODE ?? "dev"; -const command = mode === "prod" ? "bun run regression:start:prod" : "bun run regression:start:dev"; +const command = + mode === "prod" + ? "bun run regression:start:prod" + : mode === "backcompat" + ? "bun run regression:start:backcompat" + : "bun run regression:start:dev"; export default defineConfig({ testDir: "./specs", timeout: 60000, fullyParallel: false, workers: 1, + globalSetup: "./helpers/global-setup.ts", use: { browserName: "chromium", headless: true, @@ -32,5 +38,5 @@ export default defineConfig({ CI: "true", }, }, - projects: [{ name: "dev" }, { name: "prod" }], + projects: [{ name: "dev" }, { name: "prod" }, { name: "backcompat" }], }); diff --git a/tests/regression/browser/specs/admin.spec.ts b/tests/regression/browser/specs/admin.spec.ts new file mode 100644 index 00000000..37c45b8c --- /dev/null +++ b/tests/regression/browser/specs/admin.spec.ts @@ -0,0 +1,83 @@ +import { expect, test } from "@playwright/test"; +import { collectErrors, expectNoHydrationFailure, waitForApp } from "../helpers/page-ready"; +import { injectAdminCookies } from "../helpers/seeded"; + +test.describe("admin", () => { + let pageErrors: string[]; + + test.beforeEach(async ({ page }) => { + pageErrors = collectErrors(page); + await injectAdminCookies(page); + }); + + test("admin page is accessible without redirect", async ({ page }) => { + await page.goto("/admin", { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle"); + await waitForApp(page); + + await expect(page).toHaveURL(/\/admin/, { timeout: 10000 }); + await expect(page.locator("h1")).toContainText("Dashboard", { timeout: 10000 }); + + expectNoHydrationFailure(pageErrors); + }); + + test("admin dashboard loads with account stats", async ({ page }) => { + await page.goto("/admin/", { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle"); + await waitForApp(page); + + await expect(page.locator("h1")).toContainText("Dashboard", { timeout: 10000 }); + await expect(page.getByText("Signed in as", { exact: false })).toBeVisible({ timeout: 5000 }); + await expect(page.getByText("Manage", { exact: true })).toBeVisible({ timeout: 5000 }); + await expect(page.getByText("Organizations", { exact: true }).first()).toBeVisible({ + timeout: 5000, + }); + await expect(page.getByText("Settings", { exact: true }).first()).toBeVisible({ + timeout: 5000, + }); + + expectNoHydrationFailure(pageErrors); + }); + + test("admin system page renders runtime configuration", async ({ page }) => { + await page.goto("/admin/system", { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle"); + await waitForApp(page); + + await expect(page.locator("h1")).toContainText("System", { timeout: 10000 }); + await expect(page.getByText("Runtime", { exact: true })).toBeVisible({ timeout: 5000 }); + await expect(page.getByText("Deployment", { exact: true })).toBeVisible({ timeout: 5000 }); + await expect(page.getByText("Endpoints", { exact: true })).toBeVisible({ timeout: 5000 }); + + expectNoHydrationFailure(pageErrors); + }); + + test("orgs page renders without email verification crash", async ({ page }) => { + await page.goto("/orgs", { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle"); + await waitForApp(page); + + await expect(page.locator("h1")).toContainText("Organizations", { timeout: 10000 }); + await expect(page.getByText("Email verification required", { exact: false })).toHaveCount(0); + + expectNoHydrationFailure(pageErrors); + }); + + test("create organization as admin", async ({ page }) => { + const name = `Admin Org ${Date.now()}`; + const slug = `admin-org-${Date.now().toString().slice(-6)}`; + + await page.goto("/orgs/new", { waitUntil: "domcontentloaded" }); + await page.waitForLoadState("networkidle"); + await waitForApp(page); + + await page.fill("#organization-name", name); + await page.fill("#organization-slug", slug); + await page.getByRole("button", { name: "create" }).click(); + + await expect(page).toHaveURL(new RegExp(`/orgs/${slug}`), { timeout: 15000 }); + await expect(page.locator("h1")).toContainText(name, { timeout: 10000 }); + + expectNoHydrationFailure(pageErrors); + }); +}); diff --git a/tests/regression/browser/specs/auth-client.spec.ts b/tests/regression/browser/specs/auth-client.spec.ts index bda4857d..970989f7 100644 --- a/tests/regression/browser/specs/auth-client.spec.ts +++ b/tests/regression/browser/specs/auth-client.spec.ts @@ -58,9 +58,9 @@ test.describe("authClient", () => { await anonymousBtn.click(); await signInDone; - await page.waitForURL(/\/home$/, { timeout: 15000 }); + await page.waitForURL(/\/dashboard$/, { timeout: 15000 }); await page.reload(); - await page.waitForURL(/\/home$/, { timeout: 15000 }); + await page.waitForURL(/\/dashboard$/, { timeout: 15000 }); await page.waitForLoadState("networkidle"); await waitForApp(page); diff --git a/tests/regression/browser/specs/auth-redirect.spec.ts b/tests/regression/browser/specs/auth-redirect.spec.ts index 94a79c21..afbaf19a 100644 --- a/tests/regression/browser/specs/auth-redirect.spec.ts +++ b/tests/regression/browser/specs/auth-redirect.spec.ts @@ -26,8 +26,8 @@ test.describe("Auth redirect", () => { expectNoHydrationFailure(pageErrors); }); - test("unauthenticated / redirects to /login", async ({ page }) => { - await page.goto("/", { waitUntil: "domcontentloaded" }); + test("unauthenticated /dashboard redirects to /login", async ({ page }) => { + await page.goto("/dashboard", { waitUntil: "domcontentloaded" }); await waitForApp(page); await page.waitForURL(/\/login/, { timeout: 15000 }); diff --git a/tests/regression/browser/specs/logout.spec.ts b/tests/regression/browser/specs/logout.spec.ts index c207bb95..97642bcd 100644 --- a/tests/regression/browser/specs/logout.spec.ts +++ b/tests/regression/browser/specs/logout.spec.ts @@ -8,7 +8,7 @@ test.describe("logout", () => { pageErrors = collectErrors(page); }); - test("sign out redirects to login and session is cleared", async ({ page }) => { + test("sign out lands on public page and session is cleared", async ({ page }) => { await page.goto("/login", { waitUntil: "domcontentloaded" }); await waitForApp(page); @@ -23,23 +23,25 @@ test.describe("logout", () => { await anonymousBtn.click(); await signInDone; - await page.waitForURL(/\/home$/, { timeout: 15000 }); + await page.waitForURL(/\/dashboard$/, { timeout: 15000 }); await page.reload(); - await page.waitForURL(/\/home$/, { timeout: 15000 }); + await page.waitForURL(/\/dashboard$/, { timeout: 15000 }); await page.waitForLoadState("networkidle"); await waitForApp(page); - await page.locator("button[title='menu']").click(); + await page.locator("button[title='account menu']").click(); const signOutItem = page.getByRole("menuitem", { name: "sign out" }); await expect(signOutItem).toBeVisible({ timeout: 5000 }); await signOutItem.click(); - await page.waitForURL(/\/login/, { timeout: 15000 }); - await expect(page.getByText("continue anonymously")).toBeVisible({ timeout: 10000 }); + await page.waitForURL(/\/$/, { timeout: 15000 }); + await expect(page.getByText("Get started")).toBeVisible({ timeout: 10000 }); + await expect(page.locator("button[title='account menu']")).toHaveCount(0); await page.reload({ waitUntil: "domcontentloaded" }); await waitForApp(page); - await expect(page.getByText("continue anonymously")).toBeVisible({ timeout: 10000 }); + await expect(page.getByText("Get started")).toBeVisible({ timeout: 10000 }); + await expect(page.locator("button[title='account menu']")).toHaveCount(0); expectNoHydrationFailure(pageErrors); }); diff --git a/tests/regression/browser/specs/theme.spec.ts b/tests/regression/browser/specs/theme.spec.ts index c8bd569a..0b9a3db8 100644 --- a/tests/regression/browser/specs/theme.spec.ts +++ b/tests/regression/browser/specs/theme.spec.ts @@ -49,19 +49,8 @@ test.describe("theme", () => { await expect(html).toHaveClass(/dark/); - const darkToggle = page.locator("button[aria-label='Switch to light theme']").first(); - await expect(darkToggle).toBeVisible({ timeout: 10000 }); - await darkToggle.click(); - - await expect(html).not.toHaveClass(/dark/); - - const storedAfterLight = await page.evaluate(() => localStorage.getItem("theme")); - expect(storedAfterLight).toBe("light"); - - await page.reload({ waitUntil: "domcontentloaded" }); - await waitForApp(page); - - await expect(html).not.toHaveClass(/dark/); + const storedAfterReload = await page.evaluate(() => localStorage.getItem("theme")); + expect(storedAfterReload).toBe("dark"); expectNoHydrationFailure(pageErrors); }); diff --git a/tests/regression/http/internal/regtest/target.go b/tests/regression/http/internal/regtest/target.go index 98b8cbee..ec4da0ad 100644 --- a/tests/regression/http/internal/regtest/target.go +++ b/tests/regression/http/internal/regtest/target.go @@ -5,14 +5,17 @@ import "os" type TargetMode string const ( - ModeDev TargetMode = "dev" - ModeProd TargetMode = "prod" + ModeDev TargetMode = "dev" + ModeProd TargetMode = "prod" + ModeBackcompat TargetMode = "backcompat" ) func Mode() TargetMode { - m := os.Getenv("REGRESSION_MODE") - if m == "prod" { + switch os.Getenv("REGRESSION_MODE") { + case "prod": return ModeProd + case "backcompat": + return ModeBackcompat } return ModeDev } @@ -22,8 +25,11 @@ func BaseURL() string { } func StartCommand() string { - if Mode() == ModeProd { + switch Mode() { + case ModeProd: return "bun run regression:start:prod" + case ModeBackcompat: + return "bun run regression:start:backcompat" } return "bun run regression:start:dev" } diff --git a/tests/regression/http/metadata_test.go b/tests/regression/http/metadata_test.go index 856cea48..7382a275 100644 --- a/tests/regression/http/metadata_test.go +++ b/tests/regression/http/metadata_test.go @@ -1,6 +1,7 @@ package regression import ( + "strings" "testing" "everything.dev/regression/http/internal/regtest" @@ -33,3 +34,30 @@ func TestRouteMetadata(t *testing.T) { t.Fatal("route HTML missing tag") } } + +func TestProfileMetadata(t *testing.T) { + const accountID = "root.near" + + client := regtest.NewCookieClient() + status, _, body := regtest.GetRaw(t, client, baseURL+"/"+accountID) + regtest.MustStatus(t, status, 200, body) + + if !regtest.HTMLContainsTitle(body) { + t.Fatal("profile HTML missing <title> tag") + } + if !strings.Contains(body, accountID) { + t.Fatalf("profile HTML missing account id %q", accountID) + } + + for _, property := range []string{"og:title", "og:description", "og:type"} { + if !regtest.HTMLContainsMetaProperty(body, property) { + t.Fatalf("profile HTML missing %s meta property", property) + } + } + + for _, name := range []string{"twitter:card", "twitter:title", "twitter:description"} { + if !regtest.HTMLContainsMetaName(body, name) { + t.Fatalf("profile HTML missing %s meta name", name) + } + } +} diff --git a/tests/regression/http/tenant_bindings_test.go b/tests/regression/http/tenant_bindings_test.go new file mode 100644 index 00000000..7ca4c801 --- /dev/null +++ b/tests/regression/http/tenant_bindings_test.go @@ -0,0 +1,184 @@ +package regression + +import ( + "encoding/json" + "fmt" + "os" + "strings" + "testing" + + "everything.dev/regression/http/internal/regtest" +) + +func TestTenantBindingsPublicEndpoint(t *testing.T) { + client := regtest.NewCookieClient() + + t.Run("bindings_return_200_without_auth", func(t *testing.T) { + status, _, body := regtest.GetRaw(t, client, baseURL+"/api/tenants/bindings") + regtest.MustStatus(t, status, 200, body) + }) + + var bindings []struct { + Hostname string `json:"hostname"` + AccountID string `json:"accountId"` + AllowUiOverrides bool `json:"allowUiOverrides"` + AllowBackendOverrides bool `json:"allowBackendOverrides"` + AllowSsr bool `json:"allowSsr"` + Status string `json:"status"` + } + t.Run("bindings_decode_as_array", func(t *testing.T) { + status, _, body := regtest.GetRaw(t, client, baseURL+"/api/tenants/bindings") + regtest.MustStatus(t, status, 200, body) + if err := json.Unmarshal([]byte(body), &bindings); err != nil { + t.Fatalf("decoding bindings response: %v\nBody: %s", err, body) + } + }) + + t.Run("seeded_tenant_binding_present", func(t *testing.T) { + var seeded *struct { + Hostname string `json:"hostname"` + AccountID string `json:"accountId"` + AllowUiOverrides bool `json:"allowUiOverrides"` + AllowBackendOverrides bool `json:"allowBackendOverrides"` + AllowSsr bool `json:"allowSsr"` + Status string `json:"status"` + } + for i := range bindings { + if strings.HasPrefix(bindings[i].Hostname, "regression-tenant-") { + seeded = &bindings[i] + break + } + } + if seeded == nil { + t.Fatal("expected seeded tenant binding to be present") + } + if seeded.Status != "active" { + t.Fatalf("expected seeded tenant status 'active', got %q", seeded.Status) + } + if seeded.AccountID != fmt.Sprintf("%s.testnet", seeded.Hostname) { + t.Fatalf("expected account id derived from hostname, got %q", seeded.AccountID) + } + + // The seeded tenant uses the default permission columns. + if !seeded.AllowUiOverrides { + t.Fatal("expected allowUiOverrides to default to true") + } + if seeded.AllowBackendOverrides { + t.Fatal("expected allowBackendOverrides to default to false") + } + if seeded.AllowSsr { + t.Fatal("expected allowSsr to default to false") + } + }) +} + +func TestTenantBindingsReflectAllowFlags(t *testing.T) { + client := regtest.NewCookieClient() + + t.Run("sign_in", func(t *testing.T) { + status, _, body := regtest.PostEmpty(t, client, baseURL+"/api/auth/sign-in/anonymous") + regtest.MustStatus(t, status, 200, body) + }) + + orgName := fmt.Sprintf("regression-flags-org-%d", os.Getpid()) + t.Run("create_org", func(t *testing.T) { + status, _, body := regtest.PostJSON(t, client, baseURL+"/api/auth/organization/create", map[string]string{ + "name": orgName, + "slug": orgName, + }, map[string]string{ + "Origin": "http://localhost:4100", + }) + regtest.MustStatus(t, status, 200, body) + var result struct { + ID string `json:"id"` + } + if err := json.Unmarshal([]byte(body), &result); err != nil { + t.Fatalf("decoding org response: %v\nBody: %s", err, body) + } + if result.ID == "" { + t.Fatal("expected non-empty org id") + } + + status, _, body = regtest.PostJSON(t, client, baseURL+"/api/auth/organization/set-active", map[string]string{ + "organizationId": result.ID, + }, map[string]string{ + "Origin": "http://localhost:4100", + }) + regtest.MustStatus(t, status, 200, body) + }) + + subdomain := fmt.Sprintf("regression-flags-%d", os.Getpid()) + t.Run("create_tenant_with_explicit_flags", func(t *testing.T) { + status, _, body := regtest.PostJSON(t, client, baseURL+"/api/tenants", map[string]any{ + "subdomain": subdomain, + "name": "Flags Tenant", + "accountId": fmt.Sprintf("%s.testnet", subdomain), + "allowUiOverrides": false, + "allowBackendOverrides": true, + "allowSsr": true, + }, nil) + regtest.MustStatus(t, status, 200, body) + + var result struct { + ID string `json:"id"` + AllowUiOverrides bool `json:"allowUiOverrides"` + AllowBackendOverrides bool `json:"allowBackendOverrides"` + AllowSsr bool `json:"allowSsr"` + } + if err := json.Unmarshal([]byte(body), &result); err != nil { + t.Fatalf("decoding tenant response: %v\nBody: %s", err, body) + } + if result.ID == "" { + t.Fatal("expected non-empty tenant id") + } + if result.AllowUiOverrides { + t.Fatal("expected allowUiOverrides to be false") + } + if !result.AllowBackendOverrides { + t.Fatal("expected allowBackendOverrides to be true") + } + if !result.AllowSsr { + t.Fatal("expected allowSsr to be true") + } + }) + + t.Run("bindings_reflect_flags", func(t *testing.T) { + status, _, body := regtest.GetRaw(t, client, baseURL+"/api/tenants/bindings") + regtest.MustStatus(t, status, 200, body) + + var bindings []struct { + Hostname string `json:"hostname"` + AllowUiOverrides bool `json:"allowUiOverrides"` + AllowBackendOverrides bool `json:"allowBackendOverrides"` + AllowSsr bool `json:"allowSsr"` + } + if err := json.Unmarshal([]byte(body), &bindings); err != nil { + t.Fatalf("decoding bindings response: %v\nBody: %s", err, body) + } + + var found *struct { + Hostname string `json:"hostname"` + AllowUiOverrides bool `json:"allowUiOverrides"` + AllowBackendOverrides bool `json:"allowBackendOverrides"` + AllowSsr bool `json:"allowSsr"` + } + for i := range bindings { + if bindings[i].Hostname == subdomain { + found = &bindings[i] + break + } + } + if found == nil { + t.Fatal("expected created tenant to appear in bindings") + } + if found.AllowUiOverrides { + t.Fatal("expected binding allowUiOverrides to be false") + } + if !found.AllowBackendOverrides { + t.Fatal("expected binding allowBackendOverrides to be true") + } + if !found.AllowSsr { + t.Fatal("expected binding allowSsr to be true") + } + }) +} diff --git a/ui/package.json b/ui/package.json index 014b8499..865ac5f9 100644 --- a/ui/package.json +++ b/ui/package.json @@ -18,10 +18,13 @@ }, "dependencies": { "@better-auth/api-key": "catalog:", + "@better-auth/core": "catalog:", "@better-auth/passkey": "catalog:", "@orpc/client": "catalog:", "@orpc/contract": "catalog:", "@orpc/tanstack-query": "catalog:", + "@pingpay/onramp-sdk": "^0.2.0", + "@pingpay/onramp-types": "^0.0.3", "@radix-ui/react-aspect-ratio": "^1.1.8", "@radix-ui/react-avatar": "^1.1.11", "@radix-ui/react-checkbox": "^1.3.3", @@ -67,8 +70,7 @@ "tailwind-merge": "^3.4.0", "tw-animate-css": "^1.4.0", "zod": "catalog:", - "zustand": "^5.0.9", - "@better-auth/core": "catalog:" + "zustand": "^5.0.9" }, "peerDependencies": { "@tanstack/react-query": "catalog:", diff --git a/ui/rsbuild.config.ts b/ui/rsbuild.config.ts index 8a37905c..b829ffe5 100644 --- a/ui/rsbuild.config.ts +++ b/ui/rsbuild.config.ts @@ -260,7 +260,7 @@ function createServerConfig() { }, }, server: { - port: 3004, + port: Number(process.env.PORT) || 3004, printUrls: ({ urls }) => urls.filter((url) => url.includes("localhost")), headers: { "Access-Control-Allow-Origin": "*", diff --git a/ui/src/assets/brands/pingpay/pingpay-logo-dark.png b/ui/src/assets/brands/pingpay/pingpay-logo-dark.png new file mode 100644 index 00000000..f039f2da Binary files /dev/null and b/ui/src/assets/brands/pingpay/pingpay-logo-dark.png differ diff --git a/ui/src/assets/brands/pingpay/pingpay-logo-light.png b/ui/src/assets/brands/pingpay/pingpay-logo-light.png new file mode 100644 index 00000000..e74e929f Binary files /dev/null and b/ui/src/assets/brands/pingpay/pingpay-logo-light.png differ diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index 476a1331..716fa632 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -9,6 +9,8 @@ export { ConfirmDialog } from "./confirm-dialog"; export { EmptyState } from "./empty-state"; export { PageContainer } from "./layout/page-container"; export { OrgSwitcher } from "./org-switcher"; +export { ThemeToggle } from "./theme-toggle"; +export { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; export { Badge } from "./ui/badge"; export { Button } from "./ui/button"; export { diff --git a/ui/src/components/layout/auth-shell.tsx b/ui/src/components/layout/auth-shell.tsx new file mode 100644 index 00000000..85b3b704 --- /dev/null +++ b/ui/src/components/layout/auth-shell.tsx @@ -0,0 +1,252 @@ +import { Link, Outlet, useRouterState } from "@tanstack/react-router"; +import { Building2, Home, Landmark, Menu, Shield } from "lucide-react"; +import { useState } from "react"; +import type { ClientRuntimeConfig, SessionData } from "@/app"; +import { getAccount, getActiveRuntime, getAppName } from "@/app"; +import { NearBranding } from "@/components/near-branding"; +import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { UserNav } from "@/components/user-nav"; +import { cn } from "@/lib/utils"; + +type SidebarRole = "anon" | "member" | "admin"; + +interface SidebarItem { + icon: React.ComponentType<{ className?: string }>; + label: string; + to: string; + roleRequired: SidebarRole; +} + +function filterSidebarByRole(items: SidebarItem[], userRole: SidebarRole): SidebarItem[] { + return items.filter((item) => { + if (item.roleRequired === "anon") return true; + if (item.roleRequired === "member" && userRole !== "anon") return true; + if (item.roleRequired === "admin" && userRole === "admin") return true; + return false; + }); +} + +function getUserRole(isAuthenticated: boolean, isAdmin: boolean): SidebarRole { + if (isAdmin) return "admin"; + if (isAuthenticated) return "member"; + return "anon"; +} + +interface AuthShellProps { + session: SessionData | null | undefined; + runtimeConfig?: Partial<ClientRuntimeConfig>; + isAdmin?: boolean; +} + +export function AuthShell({ session, runtimeConfig, isAdmin = false }: AuthShellProps) { + const pathname = useRouterState({ select: (s) => s.location.pathname }); + const appName = getAppName(runtimeConfig); + const runtime = getActiveRuntime(runtimeConfig); + const account = getAccount(runtimeConfig); + + const sidebarItems: SidebarItem[] = [ + { icon: Home, label: "dashboard", to: "/dashboard", roleRequired: "anon" }, + { icon: Landmark, label: "stake", to: "/stake", roleRequired: "anon" }, + { icon: Shield, label: "admin", to: "/admin", roleRequired: "admin" }, + ]; + const visibleItems = filterSidebarByRole(sidebarItems, getUserRole(!!session?.user, isAdmin)); + + const isActive = (item: SidebarItem) => { + return pathname === item.to || (item.to !== "/" && pathname.startsWith(`${item.to}/`)); + }; + + return ( + <div className="flex-1 flex min-h-0 min-w-0 overflow-hidden"> + <aside className="hidden sm:flex h-full shrink-0 flex-col items-center border-r border-border bg-card overflow-hidden transition-all duration-300 w-16"> + <div className="flex-1 w-full overflow-y-auto flex flex-col items-center gap-1.5 py-4 min-h-0 min-w-16"> + <Tooltip> + <TooltipTrigger asChild> + <Link + to="/" + preload="intent" + aria-label={`${appName} home`} + className="mb-3 flex items-center justify-center w-10 h-10 border-2 border-outset border-border-strong bg-card shadow-sm transition-shadow duration-200 hover:shadow-md" + > + <svg + viewBox="0 0 24 24" + fill="currentColor" + className="w-5 h-5 text-foreground" + aria-label={`${appName} logo`} + > + <title>{appName} + + + + + {appName} + + + {visibleItems.map((item) => { + const Icon = item.icon; + const active = isActive(item); + const linkClass = cn( + "flex items-center justify-center w-10 h-10 border-2 border-outset border-border-strong shadow-sm transition-all duration-200 ease-out hover:shadow-md", + active ? "bg-foreground text-background" : "bg-card text-foreground hover:bg-muted", + ); + + return ( + + + + + + + {item.label} + + ); + })} + + +
+ +
+ + +
+
+
+
+ + + {appName} + + + + +
+ {runtime?.accountId ?? account} + / + + {pathname === "/" ? "home" : pathname.slice(1).split("/").join(" / ")} + +
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+ + + + ); +} + +function MobileTabBar({ + visibleItems, + isActive, +}: { + visibleItems: SidebarItem[]; + isActive: (item: SidebarItem) => boolean; +}) { + const [drawerOpen, setDrawerOpen] = useState(false); + const pathname = useRouterState({ select: (s) => s.location.pathname }); + const tabActive = (to: string) => (to === "/" ? pathname === "/" : pathname.startsWith(to)); + + return ( + + ); +} + +function TabItem({ + to, + icon: Icon, + label, + active, +}: { + to: string; + icon: React.ComponentType<{ className?: string }>; + label: string; + active: boolean; +}) { + return ( + + + {label} + + ); +} diff --git a/ui/src/components/layout/simple-header.tsx b/ui/src/components/layout/simple-header.tsx new file mode 100644 index 00000000..a9506edc --- /dev/null +++ b/ui/src/components/layout/simple-header.tsx @@ -0,0 +1,36 @@ +import { Link } from "@tanstack/react-router"; +import type { ClientRuntimeConfig } from "@/app"; +import { getAppName } from "@/app"; + +interface SimpleHeaderProps { + runtimeConfig?: Partial; + rightSlot?: React.ReactNode; +} + +export function SimpleHeader({ runtimeConfig, rightSlot }: SimpleHeaderProps) { + const appName = getAppName(runtimeConfig); + + return ( +
+
+ + + {appName} + + + + +
{rightSlot}
+
+
+ ); +} diff --git a/ui/src/components/near-branding.tsx b/ui/src/components/near-branding.tsx new file mode 100644 index 00000000..e3454dcf --- /dev/null +++ b/ui/src/components/near-branding.tsx @@ -0,0 +1,24 @@ +import builtOn from "@/assets/built_on.png"; +import builtOnRev from "@/assets/built_on_rev.png"; + +export function NearBranding() { + return ( + + Built on NEAR + Built on NEAR + + ); +} diff --git a/ui/src/components/org-switcher.tsx b/ui/src/components/org-switcher.tsx index 8d4eada0..7760d57f 100644 --- a/ui/src/components/org-switcher.tsx +++ b/ui/src/components/org-switcher.tsx @@ -64,7 +64,7 @@ export function OrgSwitcher({ organizations, activeOrgId, onSwitch }: OrgSwitche )} - + new organization diff --git a/ui/src/components/theme-toggle.tsx b/ui/src/components/theme-toggle.tsx index 639cf74b..033fd9f0 100644 --- a/ui/src/components/theme-toggle.tsx +++ b/ui/src/components/theme-toggle.tsx @@ -1,5 +1,6 @@ import { Moon, Sun } from "lucide-react"; import { useTheme } from "next-themes"; +import { cn } from "@/lib/utils"; export function ThemeToggle({ className }: { className?: string }) { const { setTheme, resolvedTheme } = useTheme(); @@ -12,7 +13,7 @@ export function ThemeToggle({ className }: { className?: string }) { + ))} ); } diff --git a/ui/src/components/user-nav.tsx b/ui/src/components/user-nav.tsx index a77e1c08..d4a61673 100644 --- a/ui/src/components/user-nav.tsx +++ b/ui/src/components/user-nav.tsx @@ -1,17 +1,18 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Link, useNavigate, useRouter } from "@tanstack/react-router"; +import { Building2, Home, LogOut, Settings, User } from "lucide-react"; import { useMemo } from "react"; import type { Organization } from "@/app"; import { sessionQueryOptions, useAuthClient } from "@/app"; -import { OrgSwitcher } from "@/components"; +import { Avatar, AvatarFallback, AvatarImage, OrgSwitcher, ThemeToggle } from "@/components"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, - DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { getNearInitials, resolveNearImageUrl } from "@/lib/near-profile"; export function UserNav() { const auth = useAuthClient(); @@ -20,6 +21,8 @@ export function UserNav() { const router = useRouter(); const { data: session } = useQuery(sessionQueryOptions(auth)); const user = session?.user; + const nearAccountId = auth.near.getAccountId(); + const { data: organizations } = useQuery({ queryKey: ["organizations"], queryFn: async () => { @@ -35,6 +38,16 @@ export function UserNav() { return organizations?.find((org) => org.id === activeOrgId); }, [organizations, activeOrgId]); + const { data: nearProfile } = useQuery({ + queryKey: ["near-profile", nearAccountId], + queryFn: async () => { + const { data } = await auth.near.getProfile(nearAccountId ?? undefined); + return data ?? null; + }, + enabled: !!nearAccountId, + staleTime: 5 * 60 * 1000, + }); + const signOutMutation = useMutation({ mutationFn: async () => { const { error } = await auth.signOut(); @@ -69,8 +82,31 @@ export function UserNav() { const handleOrgSwitch = async () => { await queryClient.invalidateQueries({ queryKey: ["session"] }); await queryClient.invalidateQueries({ queryKey: ["organizations"] }); + await router.invalidate(); }; + const avatarSrc = resolveNearImageUrl(nearProfile?.image) ?? user.image ?? undefined; + const validEmail = !user.isAnonymous && user.email ? user.email : null; + const displayName = nearProfile?.name || user.name || nearAccountId || validEmail || "guest"; + const handle = nearAccountId || validEmail || "anonymous session"; + const showHandle = handle !== displayName; + const initials = getNearInitials(nearProfile?.name || user.name || nearAccountId); + + const identityContent = ( + <> + + {avatarSrc ? : null} + + {initials || } + + +
+

{displayName}

+ {showHandle &&

{handle}

} +
+ + ); + return (
{organizations && organizations.length > 0 && ( @@ -85,30 +121,57 @@ export function UserNav() { - - -
-

signed in as

-

{user.email || user.id}

-
-
+ + + {nearAccountId ? ( + + {identityContent} + + ) : ( + {identityContent} + )} + - workspace + + + workspace + {activeOrg && ( - + + {activeOrg.name} )} - settings + + + settings + + + + +
+ + + theme + +
+ {signOutMutation.isPending ? "signing out..." : "sign out"}
diff --git a/ui/src/lib/near-profile.ts b/ui/src/lib/near-profile.ts new file mode 100644 index 00000000..deb01fa8 --- /dev/null +++ b/ui/src/lib/near-profile.ts @@ -0,0 +1,17 @@ +export interface NearProfileImage { + url?: string; + ipfs_cid?: string; +} + +export function resolveNearImageUrl(image?: NearProfileImage | null): string | undefined { + if (image?.url) return image.url; + if (image?.ipfs_cid) return `https://ipfs.near.social/ipfs/${image.ipfs_cid}`; + return undefined; +} + +export function getNearInitials(name?: string | null): string { + if (!name) return ""; + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length >= 2) return `${parts[0][0]}${parts[1][0]}`.toUpperCase(); + return name.trim().slice(0, 2).toUpperCase(); +} diff --git a/ui/src/lib/use-relayer-fund.ts b/ui/src/lib/use-relayer-fund.ts new file mode 100644 index 00000000..fa4c98bd --- /dev/null +++ b/ui/src/lib/use-relayer-fund.ts @@ -0,0 +1,111 @@ +import { type UseQueryResult, useQuery } from "@tanstack/react-query"; +import { useCallback, useMemo, useState } from "react"; +import { toast } from "sonner"; +import type { AuthClient } from "./auth"; +import { useAuthClient } from "./auth"; + +export interface RelayerInfoData { + accountId?: string; + mode?: "ephemeral" | "explicit"; + enabled?: boolean; + balance?: string; + available?: string; + network?: "mainnet" | "testnet"; +} + +export interface RelayerInfoResult { + data: RelayerInfoData | null; + refetch: () => Promise; +} + +export const relayerInfoQueryKey = ["relayer-info"] as const; + +export function useRelayerInfoQuery( + auth: AuthClient = useAuthClient(), +): UseQueryResult { + return useQuery({ + queryKey: relayerInfoQueryKey, + queryFn: async () => { + const { data } = await auth.near.getRelayerInfo(); + return (data ?? null) as RelayerInfoData | null; + }, + refetchInterval: 30_000, + }); +} + +export function useRelayerFund( + info: RelayerInfoData | null | undefined, + options?: { onSuccess?: () => void }, +) { + const auth = useAuthClient(); + const [amount, setAmount] = useState("5"); + const [sending, setSending] = useState(false); + + const parsedAmount = useMemo(() => { + const value = Number(amount); + if (!amount || Number.isNaN(value) || value <= 0) return null; + return value; + }, [amount]); + + const sendFund = useCallback(async () => { + const target = info?.accountId; + if (!target) { + toast.error("Relayer not configured on the server."); + return; + } + if (parsedAmount === null) { + toast.error("Enter a valid amount in NEAR."); + return; + } + const connected = await auth.near.ensureConnected(); + if (!connected) { + toast.error("Connect a NEAR wallet first"); + return; + } + const signer = auth.near.getAccountId(); + if (!signer) { + toast.error("Connect a NEAR wallet first"); + return; + } + setSending(true); + try { + const result = await auth.near + .getNearClient() + .transaction(signer) + .transfer(target, `${parsedAmount} NEAR`) + .send({ waitUntil: "FINAL" }); + toast.success("Relayer funded", { + description: result.transaction?.hash + ? `tx: ${result.transaction.hash}` + : `Sent ${parsedAmount} NEAR → ${target}`, + }); + options?.onSuccess?.(); + } catch (err) { + toast.error(err instanceof Error ? err.message : "Funding failed"); + } finally { + setSending(false); + } + }, [auth, info?.accountId, parsedAmount, options]); + + return { + amount, + setAmount, + sending, + parsedAmount, + sendFund, + }; +} + +export function formatYocto(value: string | bigint | number | null | undefined): string | null { + if (value === null || value === undefined) return null; + try { + const v = typeof value === "string" ? BigInt(value) : BigInt(value); + if (v === 0n) return "0 NEAR"; + const whole = v / 10n ** 24n; + const frac = v % 10n ** 24n; + const fracStr = frac.toString().padStart(24, "0").slice(0, 4); + return fracStr === "0000" ? `${whole} NEAR` : `${whole}.${fracStr} NEAR`; + } catch { + return null; + } +} diff --git a/ui/src/routeTree.gen.ts b/ui/src/routeTree.gen.ts index 8ee2490c..c3b89a83 100644 --- a/ui/src/routeTree.gen.ts +++ b/ui/src/routeTree.gen.ts @@ -10,102 +10,127 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as LayoutIndexRouteImport } from './routes/_layout/index' -import { Route as LayoutSkillRouteImport } from './routes/_layout/skill' -import { Route as LayoutLoginRouteImport } from './routes/_layout/login' -import { Route as LayoutAboutRouteImport } from './routes/_layout/about' +import { Route as LayoutPublicRouteImport } from './routes/_layout/_public' import { Route as LayoutAuthenticatedRouteImport } from './routes/_layout/_authenticated' -import { Route as LayoutThingsIndexRouteImport } from './routes/_layout/things/index' -import { Route as LayoutAppsIndexRouteImport } from './routes/_layout/apps/index' -import { Route as LayoutThingsLiveRouteImport } from './routes/_layout/things/live' -import { Route as LayoutThingsThingIdRouteImport } from './routes/_layout/things/$thingId' +import { Route as LayoutAnonRouteImport } from './routes/_layout/_anon' +import { Route as LayoutAdminRouteImport } from './routes/_layout/_admin' +import { Route as LayoutPublicIndexRouteImport } from './routes/_layout/_public/index' +import { Route as LayoutPublicSkillRouteImport } from './routes/_layout/_public/skill' +import { Route as LayoutPublicAboutRouteImport } from './routes/_layout/_public/about' +import { Route as LayoutPublicAccountIdRouteImport } from './routes/_layout/_public/$accountId' +import { Route as LayoutAuthenticatedStakeRouteImport } from './routes/_layout/_authenticated/stake' import { Route as LayoutAuthenticatedSettingsRouteImport } from './routes/_layout/_authenticated/settings' -import { Route as LayoutAuthenticatedHomeRouteImport } from './routes/_layout/_authenticated/home' -import { Route as LayoutAuthenticatedAdminRouteImport } from './routes/_layout/_authenticated/admin' -import { Route as LayoutAppsAccountIdIndexRouteImport } from './routes/_layout/apps/$accountId/index' +import { Route as LayoutAuthenticatedDashboardRouteImport } from './routes/_layout/_authenticated/dashboard' +import { Route as LayoutAnonLoginRouteImport } from './routes/_layout/_anon/login' +import { Route as LayoutAdminAdminRouteImport } from './routes/_layout/_admin/admin' +import { Route as LayoutPublicThingsIndexRouteImport } from './routes/_layout/_public/things/index' +import { Route as LayoutPublicAppsIndexRouteImport } from './routes/_layout/_public/apps/index' +import { Route as LayoutPublicAccountIdIndexRouteImport } from './routes/_layout/_public/$accountId/index' import { Route as LayoutAuthenticatedSettingsIndexRouteImport } from './routes/_layout/_authenticated/settings/index' -import { Route as LayoutAuthenticatedOrganizationsIndexRouteImport } from './routes/_layout/_authenticated/organizations/index' -import { Route as LayoutAppsAccountIdGatewayIdRouteImport } from './routes/_layout/apps/$accountId/$gatewayId' +import { Route as LayoutAuthenticatedOrgsIndexRouteImport } from './routes/_layout/_authenticated/orgs/index' +import { Route as LayoutAdminAdminIndexRouteImport } from './routes/_layout/_admin/admin/index' +import { Route as LayoutPublicThingsLiveRouteImport } from './routes/_layout/_public/things/live' +import { Route as LayoutPublicThingsThingIdRouteImport } from './routes/_layout/_public/things/$thingId' +import { Route as LayoutPublicAccountIdAppsRouteImport } from './routes/_layout/_public/$accountId/apps' import { Route as LayoutAuthenticatedThingsNewRouteImport } from './routes/_layout/_authenticated/things/new' -import { Route as LayoutAuthenticatedTenantNewRouteImport } from './routes/_layout/_authenticated/tenant/new' import { Route as LayoutAuthenticatedTenantTenantIdRouteImport } from './routes/_layout/_authenticated/tenant/$tenantId' import { Route as LayoutAuthenticatedSettingsSecurityRouteImport } from './routes/_layout/_authenticated/settings/security' import { Route as LayoutAuthenticatedSettingsProfileRouteImport } from './routes/_layout/_authenticated/settings/profile' import { Route as LayoutAuthenticatedSettingsAuthMethodsRouteImport } from './routes/_layout/_authenticated/settings/auth-methods' -import { Route as LayoutAuthenticatedOrganizationsNewRouteImport } from './routes/_layout/_authenticated/organizations/new' -import { Route as LayoutAuthenticatedOrganizationsSlugRouteImport } from './routes/_layout/_authenticated/organizations/$slug' -import { Route as LayoutAuthenticatedAcceptInvitationIdRouteImport } from './routes/_layout/_authenticated/accept-invitation.$id' +import { Route as LayoutAuthenticatedOrgsNewRouteImport } from './routes/_layout/_authenticated/orgs/new' +import { Route as LayoutAuthenticatedOrgsSlugRouteImport } from './routes/_layout/_authenticated/orgs/$slug' +import { Route as LayoutAdminAdminTenantsRouteImport } from './routes/_layout/_admin/admin/tenants' +import { Route as LayoutAdminAdminSystemRouteImport } from './routes/_layout/_admin/admin/system' +import { Route as LayoutAdminAdminRelayerRouteImport } from './routes/_layout/_admin/admin/relayer' +import { Route as LayoutPublicAppsAccountIdIndexRouteImport } from './routes/_layout/_public/apps/$accountId/index' +import { Route as LayoutAdminAdminTenantsIndexRouteImport } from './routes/_layout/_admin/admin/tenants/index' +import { Route as LayoutPublicAppsAccountIdGatewayIdRouteImport } from './routes/_layout/_public/apps/$accountId/$gatewayId' +import { Route as LayoutAuthenticatedOrgsInvitesIdRouteImport } from './routes/_layout/_authenticated/orgs/invites.$id' +import { Route as LayoutAdminAdminTenantsNewRouteImport } from './routes/_layout/_admin/admin/tenants/new' const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', getParentRoute: () => rootRouteImport, } as any) -const LayoutIndexRoute = LayoutIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => LayoutRoute, -} as any) -const LayoutSkillRoute = LayoutSkillRouteImport.update({ - id: '/skill', - path: '/skill', - getParentRoute: () => LayoutRoute, -} as any) -const LayoutLoginRoute = LayoutLoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => LayoutRoute, -} as any) -const LayoutAboutRoute = LayoutAboutRouteImport.update({ - id: '/about', - path: '/about', +const LayoutPublicRoute = LayoutPublicRouteImport.update({ + id: '/_public', getParentRoute: () => LayoutRoute, } as any) const LayoutAuthenticatedRoute = LayoutAuthenticatedRouteImport.update({ id: '/_authenticated', getParentRoute: () => LayoutRoute, } as any) -const LayoutThingsIndexRoute = LayoutThingsIndexRouteImport.update({ - id: '/things/', - path: '/things/', +const LayoutAnonRoute = LayoutAnonRouteImport.update({ + id: '/_anon', getParentRoute: () => LayoutRoute, } as any) -const LayoutAppsIndexRoute = LayoutAppsIndexRouteImport.update({ - id: '/apps/', - path: '/apps/', +const LayoutAdminRoute = LayoutAdminRouteImport.update({ + id: '/_admin', getParentRoute: () => LayoutRoute, } as any) -const LayoutThingsLiveRoute = LayoutThingsLiveRouteImport.update({ - id: '/things/live', - path: '/things/live', - getParentRoute: () => LayoutRoute, +const LayoutPublicIndexRoute = LayoutPublicIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LayoutPublicRoute, } as any) -const LayoutThingsThingIdRoute = LayoutThingsThingIdRouteImport.update({ - id: '/things/$thingId', - path: '/things/$thingId', - getParentRoute: () => LayoutRoute, +const LayoutPublicSkillRoute = LayoutPublicSkillRouteImport.update({ + id: '/skill', + path: '/skill', + getParentRoute: () => LayoutPublicRoute, } as any) +const LayoutPublicAboutRoute = LayoutPublicAboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => LayoutPublicRoute, +} as any) +const LayoutPublicAccountIdRoute = LayoutPublicAccountIdRouteImport.update({ + id: '/$accountId', + path: '/$accountId', + getParentRoute: () => LayoutPublicRoute, +} as any) +const LayoutAuthenticatedStakeRoute = + LayoutAuthenticatedStakeRouteImport.update({ + id: '/stake', + path: '/stake', + getParentRoute: () => LayoutAuthenticatedRoute, + } as any) const LayoutAuthenticatedSettingsRoute = LayoutAuthenticatedSettingsRouteImport.update({ id: '/settings', path: '/settings', getParentRoute: () => LayoutAuthenticatedRoute, } as any) -const LayoutAuthenticatedHomeRoute = LayoutAuthenticatedHomeRouteImport.update({ - id: '/home', - path: '/home', - getParentRoute: () => LayoutAuthenticatedRoute, -} as any) -const LayoutAuthenticatedAdminRoute = - LayoutAuthenticatedAdminRouteImport.update({ - id: '/admin', - path: '/admin', +const LayoutAuthenticatedDashboardRoute = + LayoutAuthenticatedDashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', getParentRoute: () => LayoutAuthenticatedRoute, } as any) -const LayoutAppsAccountIdIndexRoute = - LayoutAppsAccountIdIndexRouteImport.update({ - id: '/apps/$accountId/', - path: '/apps/$accountId/', - getParentRoute: () => LayoutRoute, +const LayoutAnonLoginRoute = LayoutAnonLoginRouteImport.update({ + id: '/login', + path: '/login', + getParentRoute: () => LayoutAnonRoute, +} as any) +const LayoutAdminAdminRoute = LayoutAdminAdminRouteImport.update({ + id: '/admin', + path: '/admin', + getParentRoute: () => LayoutAdminRoute, +} as any) +const LayoutPublicThingsIndexRoute = LayoutPublicThingsIndexRouteImport.update({ + id: '/things/', + path: '/things/', + getParentRoute: () => LayoutPublicRoute, +} as any) +const LayoutPublicAppsIndexRoute = LayoutPublicAppsIndexRouteImport.update({ + id: '/apps/', + path: '/apps/', + getParentRoute: () => LayoutPublicRoute, +} as any) +const LayoutPublicAccountIdIndexRoute = + LayoutPublicAccountIdIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LayoutPublicAccountIdRoute, } as any) const LayoutAuthenticatedSettingsIndexRoute = LayoutAuthenticatedSettingsIndexRouteImport.update({ @@ -113,17 +138,33 @@ const LayoutAuthenticatedSettingsIndexRoute = path: '/', getParentRoute: () => LayoutAuthenticatedSettingsRoute, } as any) -const LayoutAuthenticatedOrganizationsIndexRoute = - LayoutAuthenticatedOrganizationsIndexRouteImport.update({ - id: '/organizations/', - path: '/organizations/', +const LayoutAuthenticatedOrgsIndexRoute = + LayoutAuthenticatedOrgsIndexRouteImport.update({ + id: '/orgs/', + path: '/orgs/', getParentRoute: () => LayoutAuthenticatedRoute, } as any) -const LayoutAppsAccountIdGatewayIdRoute = - LayoutAppsAccountIdGatewayIdRouteImport.update({ - id: '/apps/$accountId/$gatewayId', - path: '/apps/$accountId/$gatewayId', - getParentRoute: () => LayoutRoute, +const LayoutAdminAdminIndexRoute = LayoutAdminAdminIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LayoutAdminAdminRoute, +} as any) +const LayoutPublicThingsLiveRoute = LayoutPublicThingsLiveRouteImport.update({ + id: '/things/live', + path: '/things/live', + getParentRoute: () => LayoutPublicRoute, +} as any) +const LayoutPublicThingsThingIdRoute = + LayoutPublicThingsThingIdRouteImport.update({ + id: '/things/$thingId', + path: '/things/$thingId', + getParentRoute: () => LayoutPublicRoute, + } as any) +const LayoutPublicAccountIdAppsRoute = + LayoutPublicAccountIdAppsRouteImport.update({ + id: '/apps', + path: '/apps', + getParentRoute: () => LayoutPublicAccountIdRoute, } as any) const LayoutAuthenticatedThingsNewRoute = LayoutAuthenticatedThingsNewRouteImport.update({ @@ -131,12 +172,6 @@ const LayoutAuthenticatedThingsNewRoute = path: '/things/new', getParentRoute: () => LayoutAuthenticatedRoute, } as any) -const LayoutAuthenticatedTenantNewRoute = - LayoutAuthenticatedTenantNewRouteImport.update({ - id: '/tenant/new', - path: '/tenant/new', - getParentRoute: () => LayoutAuthenticatedRoute, - } as any) const LayoutAuthenticatedTenantTenantIdRoute = LayoutAuthenticatedTenantTenantIdRouteImport.update({ id: '/tenant/$tenantId', @@ -161,185 +196,278 @@ const LayoutAuthenticatedSettingsAuthMethodsRoute = path: '/auth-methods', getParentRoute: () => LayoutAuthenticatedSettingsRoute, } as any) -const LayoutAuthenticatedOrganizationsNewRoute = - LayoutAuthenticatedOrganizationsNewRouteImport.update({ - id: '/organizations/new', - path: '/organizations/new', +const LayoutAuthenticatedOrgsNewRoute = + LayoutAuthenticatedOrgsNewRouteImport.update({ + id: '/orgs/new', + path: '/orgs/new', getParentRoute: () => LayoutAuthenticatedRoute, } as any) -const LayoutAuthenticatedOrganizationsSlugRoute = - LayoutAuthenticatedOrganizationsSlugRouteImport.update({ - id: '/organizations/$slug', - path: '/organizations/$slug', +const LayoutAuthenticatedOrgsSlugRoute = + LayoutAuthenticatedOrgsSlugRouteImport.update({ + id: '/orgs/$slug', + path: '/orgs/$slug', getParentRoute: () => LayoutAuthenticatedRoute, } as any) -const LayoutAuthenticatedAcceptInvitationIdRoute = - LayoutAuthenticatedAcceptInvitationIdRouteImport.update({ - id: '/accept-invitation/$id', - path: '/accept-invitation/$id', +const LayoutAdminAdminTenantsRoute = LayoutAdminAdminTenantsRouteImport.update({ + id: '/tenants', + path: '/tenants', + getParentRoute: () => LayoutAdminAdminRoute, +} as any) +const LayoutAdminAdminSystemRoute = LayoutAdminAdminSystemRouteImport.update({ + id: '/system', + path: '/system', + getParentRoute: () => LayoutAdminAdminRoute, +} as any) +const LayoutAdminAdminRelayerRoute = LayoutAdminAdminRelayerRouteImport.update({ + id: '/relayer', + path: '/relayer', + getParentRoute: () => LayoutAdminAdminRoute, +} as any) +const LayoutPublicAppsAccountIdIndexRoute = + LayoutPublicAppsAccountIdIndexRouteImport.update({ + id: '/apps/$accountId/', + path: '/apps/$accountId/', + getParentRoute: () => LayoutPublicRoute, + } as any) +const LayoutAdminAdminTenantsIndexRoute = + LayoutAdminAdminTenantsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => LayoutAdminAdminTenantsRoute, + } as any) +const LayoutPublicAppsAccountIdGatewayIdRoute = + LayoutPublicAppsAccountIdGatewayIdRouteImport.update({ + id: '/apps/$accountId/$gatewayId', + path: '/apps/$accountId/$gatewayId', + getParentRoute: () => LayoutPublicRoute, + } as any) +const LayoutAuthenticatedOrgsInvitesIdRoute = + LayoutAuthenticatedOrgsInvitesIdRouteImport.update({ + id: '/orgs/invites/$id', + path: '/orgs/invites/$id', getParentRoute: () => LayoutAuthenticatedRoute, } as any) +const LayoutAdminAdminTenantsNewRoute = + LayoutAdminAdminTenantsNewRouteImport.update({ + id: '/new', + path: '/new', + getParentRoute: () => LayoutAdminAdminTenantsRoute, + } as any) export interface FileRoutesByFullPath { - '/': typeof LayoutIndexRoute - '/about': typeof LayoutAboutRoute - '/login': typeof LayoutLoginRoute - '/skill': typeof LayoutSkillRoute - '/admin': typeof LayoutAuthenticatedAdminRoute - '/home': typeof LayoutAuthenticatedHomeRoute + '/': typeof LayoutPublicIndexRoute + '/admin': typeof LayoutAdminAdminRouteWithChildren + '/login': typeof LayoutAnonLoginRoute + '/dashboard': typeof LayoutAuthenticatedDashboardRoute '/settings': typeof LayoutAuthenticatedSettingsRouteWithChildren - '/things/$thingId': typeof LayoutThingsThingIdRoute - '/things/live': typeof LayoutThingsLiveRoute - '/apps/': typeof LayoutAppsIndexRoute - '/things/': typeof LayoutThingsIndexRoute - '/accept-invitation/$id': typeof LayoutAuthenticatedAcceptInvitationIdRoute - '/organizations/$slug': typeof LayoutAuthenticatedOrganizationsSlugRoute - '/organizations/new': typeof LayoutAuthenticatedOrganizationsNewRoute + '/stake': typeof LayoutAuthenticatedStakeRoute + '/$accountId': typeof LayoutPublicAccountIdRouteWithChildren + '/about': typeof LayoutPublicAboutRoute + '/skill': typeof LayoutPublicSkillRoute + '/admin/relayer': typeof LayoutAdminAdminRelayerRoute + '/admin/system': typeof LayoutAdminAdminSystemRoute + '/admin/tenants': typeof LayoutAdminAdminTenantsRouteWithChildren + '/orgs/$slug': typeof LayoutAuthenticatedOrgsSlugRoute + '/orgs/new': typeof LayoutAuthenticatedOrgsNewRoute '/settings/auth-methods': typeof LayoutAuthenticatedSettingsAuthMethodsRoute '/settings/profile': typeof LayoutAuthenticatedSettingsProfileRoute '/settings/security': typeof LayoutAuthenticatedSettingsSecurityRoute '/tenant/$tenantId': typeof LayoutAuthenticatedTenantTenantIdRoute - '/tenant/new': typeof LayoutAuthenticatedTenantNewRoute '/things/new': typeof LayoutAuthenticatedThingsNewRoute - '/apps/$accountId/$gatewayId': typeof LayoutAppsAccountIdGatewayIdRoute - '/organizations/': typeof LayoutAuthenticatedOrganizationsIndexRoute + '/$accountId/apps': typeof LayoutPublicAccountIdAppsRoute + '/things/$thingId': typeof LayoutPublicThingsThingIdRoute + '/things/live': typeof LayoutPublicThingsLiveRoute + '/admin/': typeof LayoutAdminAdminIndexRoute + '/orgs/': typeof LayoutAuthenticatedOrgsIndexRoute '/settings/': typeof LayoutAuthenticatedSettingsIndexRoute - '/apps/$accountId/': typeof LayoutAppsAccountIdIndexRoute + '/$accountId/': typeof LayoutPublicAccountIdIndexRoute + '/apps/': typeof LayoutPublicAppsIndexRoute + '/things/': typeof LayoutPublicThingsIndexRoute + '/admin/tenants/new': typeof LayoutAdminAdminTenantsNewRoute + '/orgs/invites/$id': typeof LayoutAuthenticatedOrgsInvitesIdRoute + '/apps/$accountId/$gatewayId': typeof LayoutPublicAppsAccountIdGatewayIdRoute + '/admin/tenants/': typeof LayoutAdminAdminTenantsIndexRoute + '/apps/$accountId/': typeof LayoutPublicAppsAccountIdIndexRoute } export interface FileRoutesByTo { - '/': typeof LayoutIndexRoute - '/about': typeof LayoutAboutRoute - '/login': typeof LayoutLoginRoute - '/skill': typeof LayoutSkillRoute - '/admin': typeof LayoutAuthenticatedAdminRoute - '/home': typeof LayoutAuthenticatedHomeRoute - '/things/$thingId': typeof LayoutThingsThingIdRoute - '/things/live': typeof LayoutThingsLiveRoute - '/apps': typeof LayoutAppsIndexRoute - '/things': typeof LayoutThingsIndexRoute - '/accept-invitation/$id': typeof LayoutAuthenticatedAcceptInvitationIdRoute - '/organizations/$slug': typeof LayoutAuthenticatedOrganizationsSlugRoute - '/organizations/new': typeof LayoutAuthenticatedOrganizationsNewRoute + '/': typeof LayoutPublicIndexRoute + '/login': typeof LayoutAnonLoginRoute + '/dashboard': typeof LayoutAuthenticatedDashboardRoute + '/stake': typeof LayoutAuthenticatedStakeRoute + '/about': typeof LayoutPublicAboutRoute + '/skill': typeof LayoutPublicSkillRoute + '/admin/relayer': typeof LayoutAdminAdminRelayerRoute + '/admin/system': typeof LayoutAdminAdminSystemRoute + '/orgs/$slug': typeof LayoutAuthenticatedOrgsSlugRoute + '/orgs/new': typeof LayoutAuthenticatedOrgsNewRoute '/settings/auth-methods': typeof LayoutAuthenticatedSettingsAuthMethodsRoute '/settings/profile': typeof LayoutAuthenticatedSettingsProfileRoute '/settings/security': typeof LayoutAuthenticatedSettingsSecurityRoute '/tenant/$tenantId': typeof LayoutAuthenticatedTenantTenantIdRoute - '/tenant/new': typeof LayoutAuthenticatedTenantNewRoute '/things/new': typeof LayoutAuthenticatedThingsNewRoute - '/apps/$accountId/$gatewayId': typeof LayoutAppsAccountIdGatewayIdRoute - '/organizations': typeof LayoutAuthenticatedOrganizationsIndexRoute + '/$accountId/apps': typeof LayoutPublicAccountIdAppsRoute + '/things/$thingId': typeof LayoutPublicThingsThingIdRoute + '/things/live': typeof LayoutPublicThingsLiveRoute + '/admin': typeof LayoutAdminAdminIndexRoute + '/orgs': typeof LayoutAuthenticatedOrgsIndexRoute '/settings': typeof LayoutAuthenticatedSettingsIndexRoute - '/apps/$accountId': typeof LayoutAppsAccountIdIndexRoute + '/$accountId': typeof LayoutPublicAccountIdIndexRoute + '/apps': typeof LayoutPublicAppsIndexRoute + '/things': typeof LayoutPublicThingsIndexRoute + '/admin/tenants/new': typeof LayoutAdminAdminTenantsNewRoute + '/orgs/invites/$id': typeof LayoutAuthenticatedOrgsInvitesIdRoute + '/apps/$accountId/$gatewayId': typeof LayoutPublicAppsAccountIdGatewayIdRoute + '/admin/tenants': typeof LayoutAdminAdminTenantsIndexRoute + '/apps/$accountId': typeof LayoutPublicAppsAccountIdIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/_layout': typeof LayoutRouteWithChildren + '/_layout/_admin': typeof LayoutAdminRouteWithChildren + '/_layout/_anon': typeof LayoutAnonRouteWithChildren '/_layout/_authenticated': typeof LayoutAuthenticatedRouteWithChildren - '/_layout/about': typeof LayoutAboutRoute - '/_layout/login': typeof LayoutLoginRoute - '/_layout/skill': typeof LayoutSkillRoute - '/_layout/': typeof LayoutIndexRoute - '/_layout/_authenticated/admin': typeof LayoutAuthenticatedAdminRoute - '/_layout/_authenticated/home': typeof LayoutAuthenticatedHomeRoute + '/_layout/_public': typeof LayoutPublicRouteWithChildren + '/_layout/_admin/admin': typeof LayoutAdminAdminRouteWithChildren + '/_layout/_anon/login': typeof LayoutAnonLoginRoute + '/_layout/_authenticated/dashboard': typeof LayoutAuthenticatedDashboardRoute '/_layout/_authenticated/settings': typeof LayoutAuthenticatedSettingsRouteWithChildren - '/_layout/things/$thingId': typeof LayoutThingsThingIdRoute - '/_layout/things/live': typeof LayoutThingsLiveRoute - '/_layout/apps/': typeof LayoutAppsIndexRoute - '/_layout/things/': typeof LayoutThingsIndexRoute - '/_layout/_authenticated/accept-invitation/$id': typeof LayoutAuthenticatedAcceptInvitationIdRoute - '/_layout/_authenticated/organizations/$slug': typeof LayoutAuthenticatedOrganizationsSlugRoute - '/_layout/_authenticated/organizations/new': typeof LayoutAuthenticatedOrganizationsNewRoute + '/_layout/_authenticated/stake': typeof LayoutAuthenticatedStakeRoute + '/_layout/_public/$accountId': typeof LayoutPublicAccountIdRouteWithChildren + '/_layout/_public/about': typeof LayoutPublicAboutRoute + '/_layout/_public/skill': typeof LayoutPublicSkillRoute + '/_layout/_public/': typeof LayoutPublicIndexRoute + '/_layout/_admin/admin/relayer': typeof LayoutAdminAdminRelayerRoute + '/_layout/_admin/admin/system': typeof LayoutAdminAdminSystemRoute + '/_layout/_admin/admin/tenants': typeof LayoutAdminAdminTenantsRouteWithChildren + '/_layout/_authenticated/orgs/$slug': typeof LayoutAuthenticatedOrgsSlugRoute + '/_layout/_authenticated/orgs/new': typeof LayoutAuthenticatedOrgsNewRoute '/_layout/_authenticated/settings/auth-methods': typeof LayoutAuthenticatedSettingsAuthMethodsRoute '/_layout/_authenticated/settings/profile': typeof LayoutAuthenticatedSettingsProfileRoute '/_layout/_authenticated/settings/security': typeof LayoutAuthenticatedSettingsSecurityRoute '/_layout/_authenticated/tenant/$tenantId': typeof LayoutAuthenticatedTenantTenantIdRoute - '/_layout/_authenticated/tenant/new': typeof LayoutAuthenticatedTenantNewRoute '/_layout/_authenticated/things/new': typeof LayoutAuthenticatedThingsNewRoute - '/_layout/apps/$accountId/$gatewayId': typeof LayoutAppsAccountIdGatewayIdRoute - '/_layout/_authenticated/organizations/': typeof LayoutAuthenticatedOrganizationsIndexRoute + '/_layout/_public/$accountId/apps': typeof LayoutPublicAccountIdAppsRoute + '/_layout/_public/things/$thingId': typeof LayoutPublicThingsThingIdRoute + '/_layout/_public/things/live': typeof LayoutPublicThingsLiveRoute + '/_layout/_admin/admin/': typeof LayoutAdminAdminIndexRoute + '/_layout/_authenticated/orgs/': typeof LayoutAuthenticatedOrgsIndexRoute '/_layout/_authenticated/settings/': typeof LayoutAuthenticatedSettingsIndexRoute - '/_layout/apps/$accountId/': typeof LayoutAppsAccountIdIndexRoute + '/_layout/_public/$accountId/': typeof LayoutPublicAccountIdIndexRoute + '/_layout/_public/apps/': typeof LayoutPublicAppsIndexRoute + '/_layout/_public/things/': typeof LayoutPublicThingsIndexRoute + '/_layout/_admin/admin/tenants/new': typeof LayoutAdminAdminTenantsNewRoute + '/_layout/_authenticated/orgs/invites/$id': typeof LayoutAuthenticatedOrgsInvitesIdRoute + '/_layout/_public/apps/$accountId/$gatewayId': typeof LayoutPublicAppsAccountIdGatewayIdRoute + '/_layout/_admin/admin/tenants/': typeof LayoutAdminAdminTenantsIndexRoute + '/_layout/_public/apps/$accountId/': typeof LayoutPublicAppsAccountIdIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' - | '/about' - | '/login' - | '/skill' | '/admin' - | '/home' + | '/login' + | '/dashboard' | '/settings' - | '/things/$thingId' - | '/things/live' - | '/apps/' - | '/things/' - | '/accept-invitation/$id' - | '/organizations/$slug' - | '/organizations/new' + | '/stake' + | '/$accountId' + | '/about' + | '/skill' + | '/admin/relayer' + | '/admin/system' + | '/admin/tenants' + | '/orgs/$slug' + | '/orgs/new' | '/settings/auth-methods' | '/settings/profile' | '/settings/security' | '/tenant/$tenantId' - | '/tenant/new' | '/things/new' - | '/apps/$accountId/$gatewayId' - | '/organizations/' + | '/$accountId/apps' + | '/things/$thingId' + | '/things/live' + | '/admin/' + | '/orgs/' | '/settings/' + | '/$accountId/' + | '/apps/' + | '/things/' + | '/admin/tenants/new' + | '/orgs/invites/$id' + | '/apps/$accountId/$gatewayId' + | '/admin/tenants/' | '/apps/$accountId/' fileRoutesByTo: FileRoutesByTo to: | '/' - | '/about' | '/login' + | '/dashboard' + | '/stake' + | '/about' | '/skill' - | '/admin' - | '/home' - | '/things/$thingId' - | '/things/live' - | '/apps' - | '/things' - | '/accept-invitation/$id' - | '/organizations/$slug' - | '/organizations/new' + | '/admin/relayer' + | '/admin/system' + | '/orgs/$slug' + | '/orgs/new' | '/settings/auth-methods' | '/settings/profile' | '/settings/security' | '/tenant/$tenantId' - | '/tenant/new' | '/things/new' - | '/apps/$accountId/$gatewayId' - | '/organizations' + | '/$accountId/apps' + | '/things/$thingId' + | '/things/live' + | '/admin' + | '/orgs' | '/settings' + | '/$accountId' + | '/apps' + | '/things' + | '/admin/tenants/new' + | '/orgs/invites/$id' + | '/apps/$accountId/$gatewayId' + | '/admin/tenants' | '/apps/$accountId' id: | '__root__' | '/_layout' + | '/_layout/_admin' + | '/_layout/_anon' | '/_layout/_authenticated' - | '/_layout/about' - | '/_layout/login' - | '/_layout/skill' - | '/_layout/' - | '/_layout/_authenticated/admin' - | '/_layout/_authenticated/home' + | '/_layout/_public' + | '/_layout/_admin/admin' + | '/_layout/_anon/login' + | '/_layout/_authenticated/dashboard' | '/_layout/_authenticated/settings' - | '/_layout/things/$thingId' - | '/_layout/things/live' - | '/_layout/apps/' - | '/_layout/things/' - | '/_layout/_authenticated/accept-invitation/$id' - | '/_layout/_authenticated/organizations/$slug' - | '/_layout/_authenticated/organizations/new' + | '/_layout/_authenticated/stake' + | '/_layout/_public/$accountId' + | '/_layout/_public/about' + | '/_layout/_public/skill' + | '/_layout/_public/' + | '/_layout/_admin/admin/relayer' + | '/_layout/_admin/admin/system' + | '/_layout/_admin/admin/tenants' + | '/_layout/_authenticated/orgs/$slug' + | '/_layout/_authenticated/orgs/new' | '/_layout/_authenticated/settings/auth-methods' | '/_layout/_authenticated/settings/profile' | '/_layout/_authenticated/settings/security' | '/_layout/_authenticated/tenant/$tenantId' - | '/_layout/_authenticated/tenant/new' | '/_layout/_authenticated/things/new' - | '/_layout/apps/$accountId/$gatewayId' - | '/_layout/_authenticated/organizations/' + | '/_layout/_public/$accountId/apps' + | '/_layout/_public/things/$thingId' + | '/_layout/_public/things/live' + | '/_layout/_admin/admin/' + | '/_layout/_authenticated/orgs/' | '/_layout/_authenticated/settings/' - | '/_layout/apps/$accountId/' + | '/_layout/_public/$accountId/' + | '/_layout/_public/apps/' + | '/_layout/_public/things/' + | '/_layout/_admin/admin/tenants/new' + | '/_layout/_authenticated/orgs/invites/$id' + | '/_layout/_public/apps/$accountId/$gatewayId' + | '/_layout/_admin/admin/tenants/' + | '/_layout/_public/apps/$accountId/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -355,32 +483,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } - '/_layout/': { - id: '/_layout/' - path: '/' + '/_layout/_public': { + id: '/_layout/_public' + path: '' fullPath: '/' - preLoaderRoute: typeof LayoutIndexRouteImport - parentRoute: typeof LayoutRoute - } - '/_layout/skill': { - id: '/_layout/skill' - path: '/skill' - fullPath: '/skill' - preLoaderRoute: typeof LayoutSkillRouteImport - parentRoute: typeof LayoutRoute - } - '/_layout/login': { - id: '/_layout/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LayoutLoginRouteImport - parentRoute: typeof LayoutRoute - } - '/_layout/about': { - id: '/_layout/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof LayoutAboutRouteImport + preLoaderRoute: typeof LayoutPublicRouteImport parentRoute: typeof LayoutRoute } '/_layout/_authenticated': { @@ -390,33 +497,54 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutAuthenticatedRouteImport parentRoute: typeof LayoutRoute } - '/_layout/things/': { - id: '/_layout/things/' - path: '/things' - fullPath: '/things/' - preLoaderRoute: typeof LayoutThingsIndexRouteImport + '/_layout/_anon': { + id: '/_layout/_anon' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutAnonRouteImport parentRoute: typeof LayoutRoute } - '/_layout/apps/': { - id: '/_layout/apps/' - path: '/apps' - fullPath: '/apps/' - preLoaderRoute: typeof LayoutAppsIndexRouteImport + '/_layout/_admin': { + id: '/_layout/_admin' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutAdminRouteImport parentRoute: typeof LayoutRoute } - '/_layout/things/live': { - id: '/_layout/things/live' - path: '/things/live' - fullPath: '/things/live' - preLoaderRoute: typeof LayoutThingsLiveRouteImport - parentRoute: typeof LayoutRoute + '/_layout/_public/': { + id: '/_layout/_public/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof LayoutPublicIndexRouteImport + parentRoute: typeof LayoutPublicRoute } - '/_layout/things/$thingId': { - id: '/_layout/things/$thingId' - path: '/things/$thingId' - fullPath: '/things/$thingId' - preLoaderRoute: typeof LayoutThingsThingIdRouteImport - parentRoute: typeof LayoutRoute + '/_layout/_public/skill': { + id: '/_layout/_public/skill' + path: '/skill' + fullPath: '/skill' + preLoaderRoute: typeof LayoutPublicSkillRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_public/about': { + id: '/_layout/_public/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof LayoutPublicAboutRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_public/$accountId': { + id: '/_layout/_public/$accountId' + path: '/$accountId' + fullPath: '/$accountId' + preLoaderRoute: typeof LayoutPublicAccountIdRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_authenticated/stake': { + id: '/_layout/_authenticated/stake' + path: '/stake' + fullPath: '/stake' + preLoaderRoute: typeof LayoutAuthenticatedStakeRouteImport + parentRoute: typeof LayoutAuthenticatedRoute } '/_layout/_authenticated/settings': { id: '/_layout/_authenticated/settings' @@ -425,26 +553,47 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutAuthenticatedSettingsRouteImport parentRoute: typeof LayoutAuthenticatedRoute } - '/_layout/_authenticated/home': { - id: '/_layout/_authenticated/home' - path: '/home' - fullPath: '/home' - preLoaderRoute: typeof LayoutAuthenticatedHomeRouteImport + '/_layout/_authenticated/dashboard': { + id: '/_layout/_authenticated/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof LayoutAuthenticatedDashboardRouteImport parentRoute: typeof LayoutAuthenticatedRoute } - '/_layout/_authenticated/admin': { - id: '/_layout/_authenticated/admin' + '/_layout/_anon/login': { + id: '/_layout/_anon/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LayoutAnonLoginRouteImport + parentRoute: typeof LayoutAnonRoute + } + '/_layout/_admin/admin': { + id: '/_layout/_admin/admin' path: '/admin' fullPath: '/admin' - preLoaderRoute: typeof LayoutAuthenticatedAdminRouteImport - parentRoute: typeof LayoutAuthenticatedRoute + preLoaderRoute: typeof LayoutAdminAdminRouteImport + parentRoute: typeof LayoutAdminRoute } - '/_layout/apps/$accountId/': { - id: '/_layout/apps/$accountId/' - path: '/apps/$accountId' - fullPath: '/apps/$accountId/' - preLoaderRoute: typeof LayoutAppsAccountIdIndexRouteImport - parentRoute: typeof LayoutRoute + '/_layout/_public/things/': { + id: '/_layout/_public/things/' + path: '/things' + fullPath: '/things/' + preLoaderRoute: typeof LayoutPublicThingsIndexRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_public/apps/': { + id: '/_layout/_public/apps/' + path: '/apps' + fullPath: '/apps/' + preLoaderRoute: typeof LayoutPublicAppsIndexRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_public/$accountId/': { + id: '/_layout/_public/$accountId/' + path: '/' + fullPath: '/$accountId/' + preLoaderRoute: typeof LayoutPublicAccountIdIndexRouteImport + parentRoute: typeof LayoutPublicAccountIdRoute } '/_layout/_authenticated/settings/': { id: '/_layout/_authenticated/settings/' @@ -453,19 +602,40 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutAuthenticatedSettingsIndexRouteImport parentRoute: typeof LayoutAuthenticatedSettingsRoute } - '/_layout/_authenticated/organizations/': { - id: '/_layout/_authenticated/organizations/' - path: '/organizations' - fullPath: '/organizations/' - preLoaderRoute: typeof LayoutAuthenticatedOrganizationsIndexRouteImport + '/_layout/_authenticated/orgs/': { + id: '/_layout/_authenticated/orgs/' + path: '/orgs' + fullPath: '/orgs/' + preLoaderRoute: typeof LayoutAuthenticatedOrgsIndexRouteImport parentRoute: typeof LayoutAuthenticatedRoute } - '/_layout/apps/$accountId/$gatewayId': { - id: '/_layout/apps/$accountId/$gatewayId' - path: '/apps/$accountId/$gatewayId' - fullPath: '/apps/$accountId/$gatewayId' - preLoaderRoute: typeof LayoutAppsAccountIdGatewayIdRouteImport - parentRoute: typeof LayoutRoute + '/_layout/_admin/admin/': { + id: '/_layout/_admin/admin/' + path: '/' + fullPath: '/admin/' + preLoaderRoute: typeof LayoutAdminAdminIndexRouteImport + parentRoute: typeof LayoutAdminAdminRoute + } + '/_layout/_public/things/live': { + id: '/_layout/_public/things/live' + path: '/things/live' + fullPath: '/things/live' + preLoaderRoute: typeof LayoutPublicThingsLiveRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_public/things/$thingId': { + id: '/_layout/_public/things/$thingId' + path: '/things/$thingId' + fullPath: '/things/$thingId' + preLoaderRoute: typeof LayoutPublicThingsThingIdRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_public/$accountId/apps': { + id: '/_layout/_public/$accountId/apps' + path: '/apps' + fullPath: '/$accountId/apps' + preLoaderRoute: typeof LayoutPublicAccountIdAppsRouteImport + parentRoute: typeof LayoutPublicAccountIdRoute } '/_layout/_authenticated/things/new': { id: '/_layout/_authenticated/things/new' @@ -474,13 +644,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutAuthenticatedThingsNewRouteImport parentRoute: typeof LayoutAuthenticatedRoute } - '/_layout/_authenticated/tenant/new': { - id: '/_layout/_authenticated/tenant/new' - path: '/tenant/new' - fullPath: '/tenant/new' - preLoaderRoute: typeof LayoutAuthenticatedTenantNewRouteImport - parentRoute: typeof LayoutAuthenticatedRoute - } '/_layout/_authenticated/tenant/$tenantId': { id: '/_layout/_authenticated/tenant/$tenantId' path: '/tenant/$tenantId' @@ -509,30 +672,136 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutAuthenticatedSettingsAuthMethodsRouteImport parentRoute: typeof LayoutAuthenticatedSettingsRoute } - '/_layout/_authenticated/organizations/new': { - id: '/_layout/_authenticated/organizations/new' - path: '/organizations/new' - fullPath: '/organizations/new' - preLoaderRoute: typeof LayoutAuthenticatedOrganizationsNewRouteImport + '/_layout/_authenticated/orgs/new': { + id: '/_layout/_authenticated/orgs/new' + path: '/orgs/new' + fullPath: '/orgs/new' + preLoaderRoute: typeof LayoutAuthenticatedOrgsNewRouteImport parentRoute: typeof LayoutAuthenticatedRoute } - '/_layout/_authenticated/organizations/$slug': { - id: '/_layout/_authenticated/organizations/$slug' - path: '/organizations/$slug' - fullPath: '/organizations/$slug' - preLoaderRoute: typeof LayoutAuthenticatedOrganizationsSlugRouteImport + '/_layout/_authenticated/orgs/$slug': { + id: '/_layout/_authenticated/orgs/$slug' + path: '/orgs/$slug' + fullPath: '/orgs/$slug' + preLoaderRoute: typeof LayoutAuthenticatedOrgsSlugRouteImport parentRoute: typeof LayoutAuthenticatedRoute } - '/_layout/_authenticated/accept-invitation/$id': { - id: '/_layout/_authenticated/accept-invitation/$id' - path: '/accept-invitation/$id' - fullPath: '/accept-invitation/$id' - preLoaderRoute: typeof LayoutAuthenticatedAcceptInvitationIdRouteImport + '/_layout/_admin/admin/tenants': { + id: '/_layout/_admin/admin/tenants' + path: '/tenants' + fullPath: '/admin/tenants' + preLoaderRoute: typeof LayoutAdminAdminTenantsRouteImport + parentRoute: typeof LayoutAdminAdminRoute + } + '/_layout/_admin/admin/system': { + id: '/_layout/_admin/admin/system' + path: '/system' + fullPath: '/admin/system' + preLoaderRoute: typeof LayoutAdminAdminSystemRouteImport + parentRoute: typeof LayoutAdminAdminRoute + } + '/_layout/_admin/admin/relayer': { + id: '/_layout/_admin/admin/relayer' + path: '/relayer' + fullPath: '/admin/relayer' + preLoaderRoute: typeof LayoutAdminAdminRelayerRouteImport + parentRoute: typeof LayoutAdminAdminRoute + } + '/_layout/_public/apps/$accountId/': { + id: '/_layout/_public/apps/$accountId/' + path: '/apps/$accountId' + fullPath: '/apps/$accountId/' + preLoaderRoute: typeof LayoutPublicAppsAccountIdIndexRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_admin/admin/tenants/': { + id: '/_layout/_admin/admin/tenants/' + path: '/' + fullPath: '/admin/tenants/' + preLoaderRoute: typeof LayoutAdminAdminTenantsIndexRouteImport + parentRoute: typeof LayoutAdminAdminTenantsRoute + } + '/_layout/_public/apps/$accountId/$gatewayId': { + id: '/_layout/_public/apps/$accountId/$gatewayId' + path: '/apps/$accountId/$gatewayId' + fullPath: '/apps/$accountId/$gatewayId' + preLoaderRoute: typeof LayoutPublicAppsAccountIdGatewayIdRouteImport + parentRoute: typeof LayoutPublicRoute + } + '/_layout/_authenticated/orgs/invites/$id': { + id: '/_layout/_authenticated/orgs/invites/$id' + path: '/orgs/invites/$id' + fullPath: '/orgs/invites/$id' + preLoaderRoute: typeof LayoutAuthenticatedOrgsInvitesIdRouteImport parentRoute: typeof LayoutAuthenticatedRoute } + '/_layout/_admin/admin/tenants/new': { + id: '/_layout/_admin/admin/tenants/new' + path: '/new' + fullPath: '/admin/tenants/new' + preLoaderRoute: typeof LayoutAdminAdminTenantsNewRouteImport + parentRoute: typeof LayoutAdminAdminTenantsRoute + } } } +interface LayoutAdminAdminTenantsRouteChildren { + LayoutAdminAdminTenantsNewRoute: typeof LayoutAdminAdminTenantsNewRoute + LayoutAdminAdminTenantsIndexRoute: typeof LayoutAdminAdminTenantsIndexRoute +} + +const LayoutAdminAdminTenantsRouteChildren: LayoutAdminAdminTenantsRouteChildren = + { + LayoutAdminAdminTenantsNewRoute: LayoutAdminAdminTenantsNewRoute, + LayoutAdminAdminTenantsIndexRoute: LayoutAdminAdminTenantsIndexRoute, + } + +const LayoutAdminAdminTenantsRouteWithChildren = + LayoutAdminAdminTenantsRoute._addFileChildren( + LayoutAdminAdminTenantsRouteChildren, + ) + +interface LayoutAdminAdminRouteChildren { + LayoutAdminAdminRelayerRoute: typeof LayoutAdminAdminRelayerRoute + LayoutAdminAdminSystemRoute: typeof LayoutAdminAdminSystemRoute + LayoutAdminAdminTenantsRoute: typeof LayoutAdminAdminTenantsRouteWithChildren + LayoutAdminAdminIndexRoute: typeof LayoutAdminAdminIndexRoute +} + +const LayoutAdminAdminRouteChildren: LayoutAdminAdminRouteChildren = { + LayoutAdminAdminRelayerRoute: LayoutAdminAdminRelayerRoute, + LayoutAdminAdminSystemRoute: LayoutAdminAdminSystemRoute, + LayoutAdminAdminTenantsRoute: LayoutAdminAdminTenantsRouteWithChildren, + LayoutAdminAdminIndexRoute: LayoutAdminAdminIndexRoute, +} + +const LayoutAdminAdminRouteWithChildren = + LayoutAdminAdminRoute._addFileChildren(LayoutAdminAdminRouteChildren) + +interface LayoutAdminRouteChildren { + LayoutAdminAdminRoute: typeof LayoutAdminAdminRouteWithChildren +} + +const LayoutAdminRouteChildren: LayoutAdminRouteChildren = { + LayoutAdminAdminRoute: LayoutAdminAdminRouteWithChildren, +} + +const LayoutAdminRouteWithChildren = LayoutAdminRoute._addFileChildren( + LayoutAdminRouteChildren, +) + +interface LayoutAnonRouteChildren { + LayoutAnonLoginRoute: typeof LayoutAnonLoginRoute +} + +const LayoutAnonRouteChildren: LayoutAnonRouteChildren = { + LayoutAnonLoginRoute: LayoutAnonLoginRoute, +} + +const LayoutAnonRouteWithChildren = LayoutAnonRoute._addFileChildren( + LayoutAnonRouteChildren, +) + interface LayoutAuthenticatedSettingsRouteChildren { LayoutAuthenticatedSettingsAuthMethodsRoute: typeof LayoutAuthenticatedSettingsAuthMethodsRoute LayoutAuthenticatedSettingsProfileRoute: typeof LayoutAuthenticatedSettingsProfileRoute @@ -558,66 +827,92 @@ const LayoutAuthenticatedSettingsRouteWithChildren = ) interface LayoutAuthenticatedRouteChildren { - LayoutAuthenticatedAdminRoute: typeof LayoutAuthenticatedAdminRoute - LayoutAuthenticatedHomeRoute: typeof LayoutAuthenticatedHomeRoute + LayoutAuthenticatedDashboardRoute: typeof LayoutAuthenticatedDashboardRoute LayoutAuthenticatedSettingsRoute: typeof LayoutAuthenticatedSettingsRouteWithChildren - LayoutAuthenticatedAcceptInvitationIdRoute: typeof LayoutAuthenticatedAcceptInvitationIdRoute - LayoutAuthenticatedOrganizationsSlugRoute: typeof LayoutAuthenticatedOrganizationsSlugRoute - LayoutAuthenticatedOrganizationsNewRoute: typeof LayoutAuthenticatedOrganizationsNewRoute + LayoutAuthenticatedStakeRoute: typeof LayoutAuthenticatedStakeRoute + LayoutAuthenticatedOrgsSlugRoute: typeof LayoutAuthenticatedOrgsSlugRoute + LayoutAuthenticatedOrgsNewRoute: typeof LayoutAuthenticatedOrgsNewRoute LayoutAuthenticatedTenantTenantIdRoute: typeof LayoutAuthenticatedTenantTenantIdRoute - LayoutAuthenticatedTenantNewRoute: typeof LayoutAuthenticatedTenantNewRoute LayoutAuthenticatedThingsNewRoute: typeof LayoutAuthenticatedThingsNewRoute - LayoutAuthenticatedOrganizationsIndexRoute: typeof LayoutAuthenticatedOrganizationsIndexRoute + LayoutAuthenticatedOrgsIndexRoute: typeof LayoutAuthenticatedOrgsIndexRoute + LayoutAuthenticatedOrgsInvitesIdRoute: typeof LayoutAuthenticatedOrgsInvitesIdRoute } const LayoutAuthenticatedRouteChildren: LayoutAuthenticatedRouteChildren = { - LayoutAuthenticatedAdminRoute: LayoutAuthenticatedAdminRoute, - LayoutAuthenticatedHomeRoute: LayoutAuthenticatedHomeRoute, + LayoutAuthenticatedDashboardRoute: LayoutAuthenticatedDashboardRoute, LayoutAuthenticatedSettingsRoute: LayoutAuthenticatedSettingsRouteWithChildren, - LayoutAuthenticatedAcceptInvitationIdRoute: - LayoutAuthenticatedAcceptInvitationIdRoute, - LayoutAuthenticatedOrganizationsSlugRoute: - LayoutAuthenticatedOrganizationsSlugRoute, - LayoutAuthenticatedOrganizationsNewRoute: - LayoutAuthenticatedOrganizationsNewRoute, + LayoutAuthenticatedStakeRoute: LayoutAuthenticatedStakeRoute, + LayoutAuthenticatedOrgsSlugRoute: LayoutAuthenticatedOrgsSlugRoute, + LayoutAuthenticatedOrgsNewRoute: LayoutAuthenticatedOrgsNewRoute, LayoutAuthenticatedTenantTenantIdRoute: LayoutAuthenticatedTenantTenantIdRoute, - LayoutAuthenticatedTenantNewRoute: LayoutAuthenticatedTenantNewRoute, LayoutAuthenticatedThingsNewRoute: LayoutAuthenticatedThingsNewRoute, - LayoutAuthenticatedOrganizationsIndexRoute: - LayoutAuthenticatedOrganizationsIndexRoute, + LayoutAuthenticatedOrgsIndexRoute: LayoutAuthenticatedOrgsIndexRoute, + LayoutAuthenticatedOrgsInvitesIdRoute: LayoutAuthenticatedOrgsInvitesIdRoute, } const LayoutAuthenticatedRouteWithChildren = LayoutAuthenticatedRoute._addFileChildren(LayoutAuthenticatedRouteChildren) +interface LayoutPublicAccountIdRouteChildren { + LayoutPublicAccountIdAppsRoute: typeof LayoutPublicAccountIdAppsRoute + LayoutPublicAccountIdIndexRoute: typeof LayoutPublicAccountIdIndexRoute +} + +const LayoutPublicAccountIdRouteChildren: LayoutPublicAccountIdRouteChildren = { + LayoutPublicAccountIdAppsRoute: LayoutPublicAccountIdAppsRoute, + LayoutPublicAccountIdIndexRoute: LayoutPublicAccountIdIndexRoute, +} + +const LayoutPublicAccountIdRouteWithChildren = + LayoutPublicAccountIdRoute._addFileChildren( + LayoutPublicAccountIdRouteChildren, + ) + +interface LayoutPublicRouteChildren { + LayoutPublicAccountIdRoute: typeof LayoutPublicAccountIdRouteWithChildren + LayoutPublicAboutRoute: typeof LayoutPublicAboutRoute + LayoutPublicSkillRoute: typeof LayoutPublicSkillRoute + LayoutPublicIndexRoute: typeof LayoutPublicIndexRoute + LayoutPublicThingsThingIdRoute: typeof LayoutPublicThingsThingIdRoute + LayoutPublicThingsLiveRoute: typeof LayoutPublicThingsLiveRoute + LayoutPublicAppsIndexRoute: typeof LayoutPublicAppsIndexRoute + LayoutPublicThingsIndexRoute: typeof LayoutPublicThingsIndexRoute + LayoutPublicAppsAccountIdGatewayIdRoute: typeof LayoutPublicAppsAccountIdGatewayIdRoute + LayoutPublicAppsAccountIdIndexRoute: typeof LayoutPublicAppsAccountIdIndexRoute +} + +const LayoutPublicRouteChildren: LayoutPublicRouteChildren = { + LayoutPublicAccountIdRoute: LayoutPublicAccountIdRouteWithChildren, + LayoutPublicAboutRoute: LayoutPublicAboutRoute, + LayoutPublicSkillRoute: LayoutPublicSkillRoute, + LayoutPublicIndexRoute: LayoutPublicIndexRoute, + LayoutPublicThingsThingIdRoute: LayoutPublicThingsThingIdRoute, + LayoutPublicThingsLiveRoute: LayoutPublicThingsLiveRoute, + LayoutPublicAppsIndexRoute: LayoutPublicAppsIndexRoute, + LayoutPublicThingsIndexRoute: LayoutPublicThingsIndexRoute, + LayoutPublicAppsAccountIdGatewayIdRoute: + LayoutPublicAppsAccountIdGatewayIdRoute, + LayoutPublicAppsAccountIdIndexRoute: LayoutPublicAppsAccountIdIndexRoute, +} + +const LayoutPublicRouteWithChildren = LayoutPublicRoute._addFileChildren( + LayoutPublicRouteChildren, +) + interface LayoutRouteChildren { + LayoutAdminRoute: typeof LayoutAdminRouteWithChildren + LayoutAnonRoute: typeof LayoutAnonRouteWithChildren LayoutAuthenticatedRoute: typeof LayoutAuthenticatedRouteWithChildren - LayoutAboutRoute: typeof LayoutAboutRoute - LayoutLoginRoute: typeof LayoutLoginRoute - LayoutSkillRoute: typeof LayoutSkillRoute - LayoutIndexRoute: typeof LayoutIndexRoute - LayoutThingsThingIdRoute: typeof LayoutThingsThingIdRoute - LayoutThingsLiveRoute: typeof LayoutThingsLiveRoute - LayoutAppsIndexRoute: typeof LayoutAppsIndexRoute - LayoutThingsIndexRoute: typeof LayoutThingsIndexRoute - LayoutAppsAccountIdGatewayIdRoute: typeof LayoutAppsAccountIdGatewayIdRoute - LayoutAppsAccountIdIndexRoute: typeof LayoutAppsAccountIdIndexRoute + LayoutPublicRoute: typeof LayoutPublicRouteWithChildren } const LayoutRouteChildren: LayoutRouteChildren = { + LayoutAdminRoute: LayoutAdminRouteWithChildren, + LayoutAnonRoute: LayoutAnonRouteWithChildren, LayoutAuthenticatedRoute: LayoutAuthenticatedRouteWithChildren, - LayoutAboutRoute: LayoutAboutRoute, - LayoutLoginRoute: LayoutLoginRoute, - LayoutSkillRoute: LayoutSkillRoute, - LayoutIndexRoute: LayoutIndexRoute, - LayoutThingsThingIdRoute: LayoutThingsThingIdRoute, - LayoutThingsLiveRoute: LayoutThingsLiveRoute, - LayoutAppsIndexRoute: LayoutAppsIndexRoute, - LayoutThingsIndexRoute: LayoutThingsIndexRoute, - LayoutAppsAccountIdGatewayIdRoute: LayoutAppsAccountIdGatewayIdRoute, - LayoutAppsAccountIdIndexRoute: LayoutAppsAccountIdIndexRoute, + LayoutPublicRoute: LayoutPublicRouteWithChildren, } const LayoutRouteWithChildren = diff --git a/ui/src/routes/__root.tsx b/ui/src/routes/__root.tsx index 20446243..ea983de9 100644 --- a/ui/src/routes/__root.tsx +++ b/ui/src/routes/__root.tsx @@ -16,7 +16,7 @@ import { Scripts, } from "@tanstack/react-router"; import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; -import { getRemoteScripts } from "everything-dev/ui/head"; +import { getRemoteScripts, getThemeInitScript } from "everything-dev/ui/head"; import { getSocialImageMeta } from "everything-dev/ui/metadata"; import { ThemeProvider } from "next-themes"; import type { RouterContext } from "@/app"; @@ -123,6 +123,7 @@ export const Route = createRootRouteWithContext()({ ...(typeof window === "undefined" ? [{ children: "window.__EVERYTHING_DEV_SSR__=true" }] : []), + getThemeInitScript(), ...getRemoteScripts({ runtimeConfig: runtimeConfig ?? undefined, containerName: "ui", diff --git a/ui/src/routes/_layout.tsx b/ui/src/routes/_layout.tsx index db4a8ec0..de1371ac 100644 --- a/ui/src/routes/_layout.tsx +++ b/ui/src/routes/_layout.tsx @@ -1,97 +1,24 @@ -import { useQuery } from "@tanstack/react-query"; -import { createFileRoute, Link, Outlet, useRouterState } from "@tanstack/react-router"; -import { ClipboardList, Compass, Globe, Home, Menu, Shield, X } from "lucide-react"; +import { createFileRoute, Outlet, useRouterState } from "@tanstack/react-router"; +import { X } from "lucide-react"; import { useEffect, useState } from "react"; -import { - getAccount, - getActiveRuntime, - getAppName, - sessionQueryOptions, - useAuthClient, -} from "@/app"; -import builtOn from "@/assets/built_on.png"; -import builtOnRev from "@/assets/built_on_rev.png"; -import { ThemeToggle } from "@/components/theme-toggle"; -import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; -import { UserNav } from "@/components/user-nav"; -import { cn } from "@/lib/utils"; - -type SidebarRole = "anon" | "member" | "admin"; - -interface SidebarItem { - icon: React.ComponentType<{ className?: string }>; - label: string; - to: string; - roleRequired: SidebarRole; -} - -function filterSidebarByRole(items: SidebarItem[], userRole: SidebarRole): SidebarItem[] { - return items.filter((item) => { - if (item.roleRequired === "anon") return true; - if (item.roleRequired === "member" && userRole !== "anon") return true; - if (item.roleRequired === "admin" && userRole === "admin") return true; - return false; - }); -} - -function getUserRole(isAuthenticated: boolean, isAdmin: boolean): SidebarRole { - if (isAdmin) return "admin"; - if (isAuthenticated) return "member"; - return "anon"; -} +import { TooltipProvider } from "@/components/ui/tooltip"; export const Route = createFileRoute("/_layout")({ - beforeLoad: async ({ context }) => { - const { queryClient, authClient, apiClient } = context; - const session = await queryClient.ensureQueryData( - sessionQueryOptions(authClient, context.session), - ); - - const accountId = getAccount(context.runtimeConfig); - const tenant = await apiClient.resolveTenant({ accountId }); - - return { - runtimeConfig: context.runtimeConfig, - session, - tenant, - }; - }, component: Layout, }); function Layout() { - const pathname = useRouterState({ select: (s) => s.location.pathname }); const isNavigating = useRouterState({ select: (s) => s.status === "pending" }); - const { runtimeConfig, session, tenant } = Route.useRouteContext(); - const appName = getAppName(runtimeConfig); - const runtime = getActiveRuntime(runtimeConfig); - const account = getAccount(runtimeConfig); - const auth = useAuthClient(); - const isAuthenticated = !!session?.user; - const userRole = getUserRole(isAuthenticated, session?.user?.role === "admin"); - - const { data: liveSession } = useQuery({ - ...sessionQueryOptions(auth, session), - initialData: session, - }); - const activeOrgId = liveSession?.session?.activeOrganizationId ?? null; - const liveIsTenantMember = !!tenant && !!activeOrgId && activeOrgId === tenant.orgId; - - const hideSidebar = pathname === "/login"; + const [showBar, setShowBar] = useState(false); - const sidebarItems: SidebarItem[] = [ - { icon: Home, label: "home", to: "/home", roleRequired: "anon" }, - { icon: Globe, label: "apps", to: "/apps", roleRequired: "anon" }, - ...(liveIsTenantMember - ? ([{ icon: Shield, label: "admin", to: "/admin", roleRequired: "member" }] as SidebarItem[]) - : []), - ]; - const visibleItems = filterSidebarByRole(sidebarItems, userRole); - - const isActive = (item: SidebarItem) => { - return pathname === item.to || (item.to !== "/" && pathname.startsWith(`${item.to}/`)); - }; + useEffect(() => { + if (!isNavigating) { + setShowBar(false); + return; + } + const t = setTimeout(() => setShowBar(true), 150); + return () => clearTimeout(t); + }, [isNavigating]); const [betaBannerDismissed, setBetaBannerDismissed] = useState(() => { if (typeof window === "undefined") return false; @@ -128,303 +55,14 @@ function Layout() {
)} - {isNavigating && ( + {showBar && (
)} -
-
- {isAuthenticated ? ( -
- - - {appName} - - - - -
- {tenant && ( - <> - {tenant.name} - / - - )} - {runtime?.accountId ?? account} - / - - {pathname === "/" ? "home" : pathname.slice(1).split("/").join(" / ")} - -
-
- ) : ( - - - {appName} - - - - )} - -
- -
-
-
- {hideSidebar && } - -
- - -
-
- -
-
-
- -
- -
- - {!isAuthenticated && !hideSidebar && ( -
- -
- )} - -
- -
+
); } - -function MinimalHeader() { - return ( -
- -
- ); -} - -function NearBranding() { - return ( - - Built on NEAR - Built on NEAR - - ); -} - -function MobileTabBar({ - isAuthenticated, - visibleItems, - isActive, -}: { - isAuthenticated: boolean; - visibleItems: SidebarItem[]; - isActive: (item: SidebarItem) => boolean; -}) { - const [drawerOpen, setDrawerOpen] = useState(false); - const pathname = useRouterState({ select: (s) => s.location.pathname }); - const tabActive = (to: string) => (to === "/" ? pathname === "/" : pathname.startsWith(to)); - - return ( - - ); -} - -function TabItem({ - to, - icon: Icon, - label, - active, -}: { - to: string; - icon: React.ComponentType<{ className?: string }>; - label: string; - active: boolean; -}) { - return ( - - - {label} - - ); -} diff --git a/ui/src/routes/_layout/_admin.tsx b/ui/src/routes/_layout/_admin.tsx new file mode 100644 index 00000000..cf6857f1 --- /dev/null +++ b/ui/src/routes/_layout/_admin.tsx @@ -0,0 +1,65 @@ +import { createFileRoute, redirect } from "@tanstack/react-router"; +import type { SessionData } from "@/app"; +import { sessionQueryOptions } from "@/app"; +import { AuthShell } from "@/components/layout/auth-shell"; + +interface AuthContext { + isAuthenticated: boolean; + user: SessionData["user"] | null; + session: SessionData["session"] | null; + activeOrganizationId: string | null; + isAnonymous: boolean; + isAdmin: boolean; + isBanned: boolean; +} + +export const Route = createFileRoute("/_layout/_admin")({ + beforeLoad: async ({ context, location }) => { + const { queryClient, authClient } = context; + + const session = await queryClient.ensureQueryData( + sessionQueryOptions(authClient, context.session), + ); + + if (!session?.user) { + throw redirect({ + to: "/login", + search: { + redirect: location.href, + }, + }); + } + + if (session.user.banned) { + throw redirect({ + to: "/login", + hash: "banned", + }); + } + + if (session.user.role !== "admin") { + throw redirect({ to: "/dashboard" }); + } + + const auth: AuthContext = { + isAuthenticated: true, + user: session.user, + session: session.session, + activeOrganizationId: session.session?.activeOrganizationId || null, + isAnonymous: session.user.isAnonymous || false, + isAdmin: session.user.role === "admin", + isBanned: session.user.banned || false, + }; + return { + auth, + session, + }; + }, + component: AdminGate, +}); + +function AdminGate() { + const { runtimeConfig, session } = Route.useRouteContext(); + const isAdmin = session?.user?.role === "admin"; + return ; +} diff --git a/ui/src/routes/_layout/_admin/admin.tsx b/ui/src/routes/_layout/_admin/admin.tsx new file mode 100644 index 00000000..65b7e6a4 --- /dev/null +++ b/ui/src/routes/_layout/_admin/admin.tsx @@ -0,0 +1,211 @@ +import { useQuery } from "@tanstack/react-query"; +import { createFileRoute, Link, Outlet, useRouterState } from "@tanstack/react-router"; +import { Building2, Fuel, LayoutDashboard, Settings, Shield } from "lucide-react"; +import { getAccount, useAuthClient } from "@/app"; +import { Badge } from "@/components"; +import { EmptyState } from "@/components/empty-state"; +import { PageContainer } from "@/components/layout/page-container"; +import { cn } from "@/lib/utils"; + +export const Route = createFileRoute("/_layout/_admin/admin")({ + head: () => ({ + meta: [{ title: "Admin | app" }], + }), + beforeLoad: async ({ context }) => { + const { apiClient, runtimeConfig } = context; + const accountId = getAccount(runtimeConfig); + let tenant: Awaited> | null = null; + try { + tenant = await apiClient.resolveTenant({ accountId }); + } catch { + tenant = null; + } + return { tenant }; + }, + component: AdminPage, +}); + +function AdminPage() { + const { tenant, session } = Route.useRouteContext(); + const auth = useAuthClient(); + + const activeOrgId = session?.session?.activeOrganizationId ?? null; + const isMember = !!tenant && !!activeOrgId && activeOrgId === tenant.orgId; + const isAdmin = session?.user?.role === "admin"; + const authorized = isMember || isAdmin; + + const { data: relayerInfo } = useQuery({ + queryKey: ["relayer-info"], + queryFn: async () => { + const { data } = await auth.near.getRelayerInfo(); + return data ?? null; + }, + refetchInterval: 60_000, + }); + + const relayerNeedsFunding = + relayerInfo && relayerInfo.enabled === false && !!relayerInfo.accountId; + + if (tenant && !authorized) { + return ( + + You need to be a member of {tenant.subdomain}'s + organization to access tenant admin. + + } + action={ +
+ + home + + + organizations + +
+ } + /> + ); + } + + return ( + +
+ {tenant && ( +
+
+ + Admin +
+
+
+

+ {tenant.name} +

+

+ {tenant.subdomain} · {tenant.accountId} +

+
+
+
+ )} + + {tenant && ( +
+ + + + {tenant.subdomain} + + } + /> + +
+ )} + + {relayerNeedsFunding && ( + +
+
+ +
+

Relayer needs funding

+

+ The ephemeral relayer{" "} + {relayerInfo?.accountId} has + zero balance — gasless relay is disabled. Fund it with NEAR to enable tenant + + app meta publishes. +

+
+
+ action needed +
+ + )} + + + + +
+
+ ); +} + +const NAV_ITEMS = [ + { label: "dashboard", to: "/admin", icon: LayoutDashboard }, + { label: "tenants", to: "/admin/tenants", icon: Building2 }, + { label: "relayer", to: "/admin/relayer", icon: Fuel }, + { label: "system", to: "/admin/system", icon: Settings }, +] as const; + +function AdminNav() { + const pathname = useRouterState({ select: (s) => s.location.pathname }); + const isActive = (to: string) => + to === "/admin" ? pathname === "/admin" || pathname === "/admin/" : pathname.startsWith(to); + + return ( + + ); +} + +function StatCard({ + label, + value, + mono, +}: { + label: string; + value: React.ReactNode; + mono?: boolean; +}) { + return ( +
+
+ {label} +
+
+ {value} +
+
+ ); +} diff --git a/ui/src/routes/_layout/_admin/admin/index.tsx b/ui/src/routes/_layout/_admin/admin/index.tsx new file mode 100644 index 00000000..c2b43a39 --- /dev/null +++ b/ui/src/routes/_layout/_admin/admin/index.tsx @@ -0,0 +1,168 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; +import { Building2, LayoutDashboard, Settings, Users } from "lucide-react"; +import { getAccount } from "@/app"; +import { Button, Card } from "@/components"; +import { InfoRow } from "@/components/ui/info-row"; + +export const Route = createFileRoute("/_layout/_admin/admin/")({ + head: () => ({ + meta: [{ title: "Admin Dashboard | app" }], + }), + component: AdminDashboard, +}); + +function AdminDashboard() { + const { auth, tenant } = Route.useRouteContext(); + const account = getAccount(); + const user = auth?.user ?? null; + + return ( +
+
+
+
+

+ Dashboard +

+

+ Signed in as {account} +

+
+
+
+ +
+ + + + +
+ +
+

Manage

+
+ +
+ +
+

Tenants

+

+ Create and manage tenant deployments for your organization. +

+ +
+ + +
+ +
+

Organizations

+

+ Manage organizations, members, roles, and invitations. +

+ +
+ + +
+ +
+

Settings

+

+ Update your profile, auth methods, and security preferences. +

+ +
+
+
+ + {tenant && ( +
+ + +
+ Configuration +
+
+ + + + + +
+
+
+ )} + + {tenant && ( +
+ + +

+ This tenant is backed by an organization. Manage members, roles, and invitations + there. +

+ + + open organization + +
+
+ )} +
+ ); +} + +function StatCard({ + label, + value, + mono, +}: { + label: string; + value: React.ReactNode; + mono?: boolean; +}) { + return ( +
+
+ {label} +
+
+ {value} +
+
+ ); +} + +function SectionHeader({ title, action }: { title: string; action?: React.ReactNode }) { + return ( +
+

{title}

+ {action} +
+ ); +} diff --git a/ui/src/routes/_layout/_admin/admin/relayer.tsx b/ui/src/routes/_layout/_admin/admin/relayer.tsx new file mode 100644 index 00000000..17923bdc --- /dev/null +++ b/ui/src/routes/_layout/_admin/admin/relayer.tsx @@ -0,0 +1,240 @@ +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { createFileRoute } from "@tanstack/react-router"; +import { Coins, Fuel, Wallet } from "lucide-react"; +import { toast } from "sonner"; +import { useAuthClient } from "@/app"; +import { Badge, Button, Card, CardContent, Field, FieldLabel, Input } from "@/components"; +import { InfoRow } from "@/components/ui/info-row"; +import { + formatYocto, + relayerInfoQueryKey, + useRelayerFund, + useRelayerInfoQuery, +} from "@/lib/use-relayer-fund"; + +export const Route = createFileRoute("/_layout/_admin/admin/relayer")({ + head: () => ({ + meta: [{ title: "Relayer | app" }], + }), + component: AdminRelayerPage, +}); + +const FUND_PRESETS = ["1", "5", "10"] as const; + +function AdminRelayerPage() { + const auth = useAuthClient(); + const queryClient = useQueryClient(); + const nearAccountId = auth.near.getAccountId(); + + const relayerInfoQuery = useRelayerInfoQuery(); + const info = relayerInfoQuery.data; + + const fund = useRelayerFund(info, { + onSuccess: () => { + relayerInfoQuery.refetch(); + queryClient.invalidateQueries({ queryKey: ["relay-history"] }); + }, + }); + + const relayHistoryQuery = useQuery({ + queryKey: ["relay-history"], + queryFn: async () => { + const { data } = await auth.near.relayHistory(); + return data ?? null; + }, + refetchInterval: 30_000, + }); + + const refresh = () => { + queryClient.invalidateQueries({ queryKey: relayerInfoQueryKey }); + queryClient.invalidateQueries({ queryKey: ["relay-history"] }); + }; + + const handleConnect = async () => { + const connected = await auth.near.ensureConnected(); + if (connected) { + toast.success("Wallet connected"); + refresh(); + } else { + toast.error("Wallet connection declined"); + } + }; + + const history = relayHistoryQuery.data; + + const statusLabel = !info + ? "not configured" + : info.enabled + ? "active" + : info.accountId + ? "needs funding" + : "initialising"; + + const statusVariant = + !info || info.enabled ? "default" : info.accountId ? "destructive" : "secondary"; + + return ( +
+
+
+

Relayer

+

+ Gasless NEP-366 delegate action relay for tenant config + app metadata writes. +

+
+
+ +
+ +
+

Status

+
+ {statusLabel} + {info?.mode && ( + + {info.mode} + + )} +
+
+ + {relayerInfoQuery.isLoading ? ( +

Loading relayer info…

+ ) : !info ? ( +

+ No relayer configured. Update bos.config.json with{" "} + app.auth.variables.siwn.relayer and run{" "} + bos publish to enable. +

+ ) : !info.enabled ? ( +

+ {info.accountId ? ( + <> + Relayer keypair generated but the account has zero balance. Fund{" "} + {info.accountId} with NEAR to + activate gasless relay. + + ) : ( + "Restart the auth service to complete ephemeral keypair generation." + )} +

+ ) : ( +
+ + + + +
+ )} + + +
+ + +
+ +

Top up

+
+ + {!nearAccountId ? ( +
+

+ Connect a NEAR wallet to fund the relayer. +

+ +
+ ) : ( +
+

+ Sending from {nearAccountId} +

+ + amount (NEAR) + fund.setAmount(e.target.value)} + disabled={fund.sending} + /> + +
+ {FUND_PRESETS.map((preset) => ( + + ))} +
+ +
+ )} +
+
+ + + +

Recent relays

+ {relayHistoryQuery.isLoading ? ( +

Loading…

+ ) : !history?.transactions?.length ? ( +

+ No relayed transactions yet. Tenant republish + app metadata writes will appear here. +

+ ) : ( +
    + {history.transactions.slice(0, 8).map((tx) => ( +
  • + {tx.txHash.slice(0, 12)}… + {tx.senderId} + + {tx.status} + +
  • + ))} +
+ )} +
+
+
+ ); +} diff --git a/ui/src/routes/_layout/_admin/admin/system.tsx b/ui/src/routes/_layout/_admin/admin/system.tsx new file mode 100644 index 00000000..5dc49657 --- /dev/null +++ b/ui/src/routes/_layout/_admin/admin/system.tsx @@ -0,0 +1,68 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { getAccount, getActiveRuntime, getAppName, getRepository } from "@/app"; +import { Card } from "@/components"; +import { InfoRow } from "@/components/ui/info-row"; + +export const Route = createFileRoute("/_layout/_admin/admin/system")({ + loader: async ({ context }) => ({ + runtimeConfig: context.runtimeConfig, + }), + head: () => ({ + meta: [{ title: "Admin System | app" }], + }), + component: AdminSystem, +}); + +function AdminSystem() { + const { runtimeConfig } = Route.useLoaderData(); + const account = getAccount(runtimeConfig); + const appName = getAppName(runtimeConfig); + const repository = getRepository(runtimeConfig); + const runtime = getActiveRuntime(runtimeConfig); + + const env = runtimeConfig?.env; + const networkId = runtimeConfig?.networkId; + const hostUrl = runtimeConfig?.hostUrl; + const apiBase = runtimeConfig?.apiBase; + const rpcBase = runtimeConfig?.rpcBase; + const assetsUrl = runtimeConfig?.assetsUrl; + const runtimeBasePath = runtime?.runtimeBasePath; + + return ( +
+
+
+

System

+

+ Runtime configuration for this deployment. +

+
+
+ +
+ +

Runtime

+ + + + +
+ + +

Deployment

+ + + + +
+ + +

Endpoints

+ + + +
+
+
+ ); +} diff --git a/ui/src/routes/_layout/_admin/admin/tenants.tsx b/ui/src/routes/_layout/_admin/admin/tenants.tsx new file mode 100644 index 00000000..a4069aac --- /dev/null +++ b/ui/src/routes/_layout/_admin/admin/tenants.tsx @@ -0,0 +1,5 @@ +import { createFileRoute, Outlet } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_layout/_admin/admin/tenants")({ + component: () => , +}); diff --git a/ui/src/routes/_layout/_admin/admin/tenants/index.tsx b/ui/src/routes/_layout/_admin/admin/tenants/index.tsx new file mode 100644 index 00000000..68ce8b55 --- /dev/null +++ b/ui/src/routes/_layout/_admin/admin/tenants/index.tsx @@ -0,0 +1,163 @@ +import { useQuery } from "@tanstack/react-query"; +import { createFileRoute, Link } from "@tanstack/react-router"; +import type { ColumnDef } from "@tanstack/react-table"; +import { Building2, Plus } from "lucide-react"; +import { useMemo } from "react"; +import { getActiveRuntime, useApiClient } from "@/app"; +import { Badge, Button, Card, Skeleton } from "@/components"; +import { EmptyState } from "@/components/empty-state"; +import { DataTable } from "@/components/ui/data-table"; + +type ApiClient = ReturnType; +type Tenant = Awaited>[number]; + +export const Route = createFileRoute("/_layout/_admin/admin/tenants/")({ + head: () => ({ + meta: [{ title: "Tenants | app" }], + }), + component: AdminTenants, +}); + +const STATUS_VARIANT: Record = { + active: "default", + pending: "secondary", + suspended: "destructive", + pending_deletion: "secondary", +}; + +function AdminTenants() { + const apiClient = useApiClient(); + const gatewayId = getActiveRuntime()?.gatewayId ?? "everything.dev"; + + const { + data: tenants = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["tenants"], + queryFn: async () => apiClient.listTenants(), + staleTime: 30 * 1000, + }); + + const columns = useMemo[]>( + () => [ + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => ( + + {row.original.name} + + ), + }, + { + accessorKey: "subdomain", + header: "Subdomain", + cell: ({ row }) => ( + + {row.original.subdomain}.{gatewayId} + + ), + }, + { + accessorKey: "accountId", + header: "Account", + cell: ({ row }) => ( + {row.original.accountId} + ), + }, + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => ( + {row.original.status} + ), + }, + { + accessorKey: "createdAt", + header: "Created", + cell: ({ row }) => + row.original.createdAt ? new Date(row.original.createdAt).toLocaleDateString() : "—", + }, + { + id: "actions", + header: "", + cell: ({ row }) => ( + + ), + }, + ], + [gatewayId], + ); + + return ( +
+
+
+
+
+ + Tenants +
+

+ Tenants +

+

+ Manage tenant deployments for your organization. +

+
+ +
+
+ + {isLoading ? ( + + {[1, 2, 3].map((n) => ( + + ))} + + ) : error ? ( + refetch()}> + retry + + } + /> + ) : tenants.length === 0 ? ( + + + + create tenant + + + } + /> + ) : ( + + )} +
+ ); +} diff --git a/ui/src/routes/_layout/_authenticated/tenant/new.tsx b/ui/src/routes/_layout/_admin/admin/tenants/new.tsx similarity index 99% rename from ui/src/routes/_layout/_authenticated/tenant/new.tsx rename to ui/src/routes/_layout/_admin/admin/tenants/new.tsx index 1ec2c150..b3867da3 100644 --- a/ui/src/routes/_layout/_authenticated/tenant/new.tsx +++ b/ui/src/routes/_layout/_admin/admin/tenants/new.tsx @@ -9,7 +9,7 @@ import { Button, Card, CardContent, Field, FieldLabel, Input } from "@/component import { PageContainer } from "@/components/layout/page-container"; import { StepList, useStepper } from "@/components/ui/stepper"; -export const Route = createFileRoute("/_layout/_authenticated/tenant/new")({ +export const Route = createFileRoute("/_layout/_admin/admin/tenants/new")({ head: () => ({ title: "New Tenant | app", meta: [{ name: "description", content: "Create a new tenant." }], @@ -240,7 +240,7 @@ function NewTenantPage() { return auth.near .getNearClient() - .transaction(accountId) + .transaction(nearAccountId) .functionCall(prepared.data.contractId, prepared.data.methodName, prepared.data.args, { gas: METADATA_GAS, attachedDeposit: 0n, @@ -286,7 +286,7 @@ function NewTenantPage() { return auth.near .getNearClient() - .transaction(accountId) + .transaction(nearAccountId) .functionCall(prepared.data.contractId, prepared.data.methodName, prepared.data.args, { gas: CONFIG_GAS, attachedDeposit: 0n, diff --git a/ui/src/routes/_layout/_anon.tsx b/ui/src/routes/_layout/_anon.tsx new file mode 100644 index 00000000..59dc0fe4 --- /dev/null +++ b/ui/src/routes/_layout/_anon.tsx @@ -0,0 +1,33 @@ +import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"; +import { sessionQueryOptions } from "@/app"; +import { SimpleHeader } from "@/components/layout/simple-header"; + +export const Route = createFileRoute("/_layout/_anon")({ + beforeLoad: async ({ context }) => { + const { queryClient, authClient } = context; + const initialSession = context.session; + const session = + initialSession ?? + queryClient.getQueryData(sessionQueryOptions(authClient, initialSession).queryKey); + if (session?.user) { + throw redirect({ to: "/dashboard", search: {} }); + } + }, + component: AnonLayout, +}); + +function AnonLayout() { + const { runtimeConfig } = Route.useRouteContext(); + + return ( +
+ + +
+
+ +
+
+
+ ); +} diff --git a/ui/src/routes/_layout/login.tsx b/ui/src/routes/_layout/_anon/login.tsx similarity index 85% rename from ui/src/routes/_layout/login.tsx rename to ui/src/routes/_layout/_anon/login.tsx index 96284716..fb37f0ee 100644 --- a/ui/src/routes/_layout/login.tsx +++ b/ui/src/routes/_layout/_anon/login.tsx @@ -3,18 +3,15 @@ import { createFileRoute, Navigate, redirect, useNavigate } from "@tanstack/reac import { useEffect, useState } from "react"; import { toast } from "sonner"; import { getAppName, sessionQueryOptions, useAuthClient } from "@/app"; -import builtOn from "@/assets/built_on.png"; -import builtOnRev from "@/assets/built_on_rev.png"; import { BrandElement } from "@/components/brand-element"; import { Button } from "@/components/ui/button"; -import { NetworkToggle } from "@/components/ui/network-toggle"; import { UnderConstruction } from "@/components/under-construction"; type SearchParams = { redirect?: string; }; -export const Route = createFileRoute("/_layout/login")({ +export const Route = createFileRoute("/_layout/_anon/login")({ ssr: false, validateSearch: (search: Record): SearchParams => ({ redirect: typeof search.redirect === "string" ? search.redirect : undefined, @@ -27,7 +24,7 @@ export const Route = createFileRoute("/_layout/login")({ queryClient.getQueryData(sessionQueryOptions(authClient, initialSession).queryKey); if (session?.user) { - const redirectTo = search.redirect?.startsWith("/") ? search.redirect : "/home"; + const redirectTo = search.redirect?.startsWith("/") ? search.redirect : "/dashboard"; throw redirect({ to: redirectTo, search: {} }); } }, @@ -60,7 +57,7 @@ function LoginPage() { }, [auth.near]); const handleSuccess = async (message: string) => { - const redirectTo = redirect?.startsWith("/") ? redirect : "/home"; + const redirectTo = redirect?.startsWith("/") ? redirect : "/dashboard"; toast.success(message); queryClient.invalidateQueries({ queryKey: ["session"] }); navigate({ to: redirectTo, replace: true, search: {} }); @@ -113,15 +110,14 @@ function LoginPage() { }; if (session?.user) { - const redirectTo = redirect?.startsWith("/") ? redirect : "/home"; + const redirectTo = redirect?.startsWith("/") ? redirect : "/dashboard"; return ; } const isPending = nearPending || anonPending; return ( -
- +
@@ -142,7 +138,25 @@ function LoginPage() {
- -
); } diff --git a/ui/src/routes/_layout/_authenticated.tsx b/ui/src/routes/_layout/_authenticated.tsx index f6c8d6de..40edbe1a 100644 --- a/ui/src/routes/_layout/_authenticated.tsx +++ b/ui/src/routes/_layout/_authenticated.tsx @@ -1,5 +1,7 @@ -import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"; -import { type SessionData, sessionQueryOptions } from "@/app"; +import { createFileRoute, redirect } from "@tanstack/react-router"; +import type { SessionData } from "@/app"; +import { sessionQueryOptions } from "@/app"; +import { AuthShell } from "@/components/layout/auth-shell"; interface AuthContext { isAuthenticated: boolean; @@ -53,9 +55,7 @@ export const Route = createFileRoute("/_layout/_authenticated")({ }); function AuthenticatedLayout() { - return ( -
- -
- ); + const { runtimeConfig, session } = Route.useRouteContext(); + const isAdmin = session?.user?.role === "admin"; + return ; } diff --git a/ui/src/routes/_layout/_authenticated/admin.tsx b/ui/src/routes/_layout/_authenticated/admin.tsx deleted file mode 100644 index a044a4f4..00000000 --- a/ui/src/routes/_layout/_authenticated/admin.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import { createFileRoute, Link, redirect } from "@tanstack/react-router"; -import { Shield, Users } from "lucide-react"; -import { getAccount } from "@/app"; -import { Card } from "@/components"; -import { EmptyState } from "@/components/empty-state"; -import { PageContainer } from "@/components/layout/page-container"; -import { InfoRow } from "@/components/ui/info-row"; - -export const Route = createFileRoute("/_layout/_authenticated/admin")({ - head: () => ({ - meta: [{ title: "Admin | app" }], - }), - beforeLoad: async ({ context }) => { - const { apiClient, runtimeConfig } = context; - const accountId = getAccount(runtimeConfig); - let tenant: Awaited> | null = null; - try { - tenant = await apiClient.resolveTenant({ accountId }); - } catch { - tenant = null; - } - if (!tenant) { - throw redirect({ to: "/" }); - } - return { tenant }; - }, - component: AdminPage, -}); - -function AdminPage() { - const { tenant, session } = Route.useRouteContext(); - - const activeOrgId = session?.session?.activeOrganizationId ?? null; - const isMember = !!tenant && !!activeOrgId && activeOrgId === tenant.orgId; - const isAdmin = session?.user?.role === "admin"; - const authorized = isMember || isAdmin; - - if (!tenant) return null; - - if (!authorized) { - return ( - - You need to be a member of {tenant.subdomain}'s - organization to access tenant admin. - - } - action={ -
- - home - - - organizations - -
- } - /> - ); - } - - return ( - -
-
-
- - Admin -
-
-
-

- {tenant.name} -

-

- {tenant.subdomain} · {tenant.accountId} -

-
-
-
- -
- - - - {tenant.subdomain} - - } - /> - -
- -
- - -
- Configuration -
-
- - - - - -
-
-
- -
- - -

- This tenant is backed by an organization. Manage members, roles, and invitations - there. -

- - - open organization - -
-
-
-
- ); -} - -function StatCard({ - label, - value, - mono, -}: { - label: string; - value: React.ReactNode; - mono?: boolean; -}) { - return ( -
-
- {label} -
-
- {value} -
-
- ); -} - -function SectionHeader({ title, action }: { title: string; action?: React.ReactNode }) { - return ( -
-

{title}

- {action} -
- ); -} diff --git a/ui/src/routes/_layout/_authenticated/home.tsx b/ui/src/routes/_layout/_authenticated/dashboard.tsx similarity index 92% rename from ui/src/routes/_layout/_authenticated/home.tsx rename to ui/src/routes/_layout/_authenticated/dashboard.tsx index e28c6796..86f23a6c 100644 --- a/ui/src/routes/_layout/_authenticated/home.tsx +++ b/ui/src/routes/_layout/_authenticated/dashboard.tsx @@ -2,12 +2,29 @@ import { useQuery } from "@tanstack/react-query"; import { createFileRoute, Link } from "@tanstack/react-router"; import { Home as HomeIcon, Settings } from "lucide-react"; import { useMemo } from "react"; -import { type Passkey, type SessionData, sessionQueryOptions, useAuthClient } from "@/app"; +import { + getAccount, + type Passkey, + type SessionData, + sessionQueryOptions, + useAuthClient, +} from "@/app"; import { Card } from "@/components"; import { PageContainer } from "@/components/layout/page-container"; import { InfoRow } from "@/components/ui/info-row"; -export const Route = createFileRoute("/_layout/_authenticated/home")({ +export const Route = createFileRoute("/_layout/_authenticated/dashboard")({ + beforeLoad: async ({ context }) => { + const { apiClient, runtimeConfig } = context; + const accountId = getAccount(runtimeConfig); + let tenant: Awaited> | null = null; + try { + tenant = await apiClient.resolveTenant({ accountId }); + } catch { + tenant = null; + } + return { tenant }; + }, head: () => ({ meta: [{ title: "Workspace | app" }, { name: "description", content: "Your workspace." }], }), diff --git a/ui/src/routes/_layout/_authenticated/organizations/$slug.tsx b/ui/src/routes/_layout/_authenticated/orgs/$slug.tsx similarity index 98% rename from ui/src/routes/_layout/_authenticated/organizations/$slug.tsx rename to ui/src/routes/_layout/_authenticated/orgs/$slug.tsx index e95cc943..16591f33 100644 --- a/ui/src/routes/_layout/_authenticated/organizations/$slug.tsx +++ b/ui/src/routes/_layout/_authenticated/orgs/$slug.tsx @@ -59,7 +59,7 @@ const orgMembersQueryKey = (orgId: string) => ["org-members", orgId] as const; const orgInvitationsQueryKey = (orgId: string) => ["org-invitations", orgId] as const; const orgApiKeysQueryKey = (orgId: string) => ["org-api-keys", orgId] as const; -export const Route = createFileRoute("/_layout/_authenticated/organizations/$slug")({ +export const Route = createFileRoute("/_layout/_authenticated/orgs/$slug")({ head: () => ({ title: "Organization | auth.everything.dev", meta: [{ name: "description", content: "Manage organization details and members." }], @@ -86,14 +86,7 @@ function OrganizationDetail() { const { slug: orgSlug } = Route.useParams(); const auth = useAuthClient(); - const { data: session } = useQuery({ - queryKey: ["session"], - queryFn: async () => { - const { data } = await auth.getSession(); - return data ?? null; - }, - staleTime: 60 * 1000, - }); + const { data: session } = useQuery(sessionQueryOptions(auth)); const { data: organizations = [], isLoading: isLoadingOrgs } = useQuery({ queryKey: ["organizations"], @@ -328,7 +321,7 @@ function OrganizationDetail() { onSuccess: async () => { toast.success("You have left the organization"); await queryClient.invalidateQueries({ queryKey: ["organizations"] }); - await router.navigate({ to: "/organizations" }); + await router.navigate({ to: "/orgs" }); }, onError: (error: Error) => toast.error(error.message || "Failed to leave organization"), }); @@ -341,7 +334,7 @@ function OrganizationDetail() { onSuccess: async () => { toast.success("Organization deleted"); await queryClient.invalidateQueries({ queryKey: ["organizations"] }); - await router.navigate({ to: "/organizations" }); + await router.navigate({ to: "/orgs" }); }, onError: (error: Error) => toast.error(error.message || "Failed to delete organization"), }); @@ -369,7 +362,7 @@ function OrganizationDetail() { description="This organization does not exist or you do not have access." action={ } /> @@ -383,7 +376,7 @@ function OrganizationDetail() {
- + Organizations / diff --git a/ui/src/routes/_layout/_authenticated/organizations/index.tsx b/ui/src/routes/_layout/_authenticated/orgs/index.tsx similarity index 95% rename from ui/src/routes/_layout/_authenticated/organizations/index.tsx rename to ui/src/routes/_layout/_authenticated/orgs/index.tsx index 96e4c576..a759f5ce 100644 --- a/ui/src/routes/_layout/_authenticated/organizations/index.tsx +++ b/ui/src/routes/_layout/_authenticated/orgs/index.tsx @@ -18,7 +18,7 @@ type UserInvitationsResponse = Awaited< >; type UserInvitationItem = NonNullable[number]; -export const Route = createFileRoute("/_layout/_authenticated/organizations/")({ +export const Route = createFileRoute("/_layout/_authenticated/orgs/")({ head: () => ({ title: "Organizations | auth.everything.dev", meta: [{ name: "description", content: "Manage your organizations and teams." }], @@ -38,9 +38,13 @@ export const Route = createFileRoute("/_layout/_authenticated/organizations/")({ await context.queryClient.ensureQueryData({ queryKey: ["user-invitations"], queryFn: async (): Promise => { - const { data, error } = await context.authClient.organization.listUserInvitations(); - if (error) throw new Error(error.message); - return (data ?? []) as UserInvitationItem[]; + try { + const { data, error } = await context.authClient.organization.listUserInvitations(); + if (error) throw new Error(error.message); + return (data ?? []) as UserInvitationItem[]; + } catch { + return []; + } }, staleTime: 30 * 1000, }); @@ -53,14 +57,7 @@ function OrganizationsList() { const apiClient = useApiClient(); const router = useRouter(); const queryClient = useQueryClient(); - const { data: session } = useQuery({ - queryKey: ["session"], - queryFn: async () => { - const { data } = await auth.getSession(); - return data ?? null; - }, - staleTime: 60 * 1000, - }); + const { data: session } = useQuery(sessionQueryOptions(auth)); const { data: organizations, isLoading } = useQuery({ queryKey: ["organizations"], queryFn: async () => { @@ -73,9 +70,13 @@ function OrganizationsList() { const { data: userInvitations = [] } = useQuery({ queryKey: ["user-invitations"], queryFn: async (): Promise => { - const { data, error } = await auth.organization.listUserInvitations(); - if (error) throw new Error(error.message); - return (data ?? []) as UserInvitationItem[]; + try { + const { data, error } = await auth.organization.listUserInvitations(); + if (error) throw new Error(error.message); + return (data ?? []) as UserInvitationItem[]; + } catch { + return []; + } }, staleTime: 30 * 1000, }); @@ -114,7 +115,7 @@ function OrganizationsList() { await queryClient.refetchQueries({ queryKey: ["organizations"] }); if (invitation.organizationSlug) { await router.navigate({ - to: "/organizations/$slug", + to: "/orgs/$slug", params: { slug: invitation.organizationSlug }, }); } @@ -164,7 +165,7 @@ function OrganizationsList() {
@@ -253,7 +254,7 @@ function OrganizationsList() {

No organizations yet.

create your first org @@ -303,7 +304,7 @@ function OrganizationsList() {
diff --git a/ui/src/routes/_layout/_authenticated/accept-invitation.$id.tsx b/ui/src/routes/_layout/_authenticated/orgs/invites.$id.tsx similarity index 95% rename from ui/src/routes/_layout/_authenticated/accept-invitation.$id.tsx rename to ui/src/routes/_layout/_authenticated/orgs/invites.$id.tsx index e7941f9a..b1c997e1 100644 --- a/ui/src/routes/_layout/_authenticated/accept-invitation.$id.tsx +++ b/ui/src/routes/_layout/_authenticated/orgs/invites.$id.tsx @@ -5,7 +5,7 @@ import { toast } from "sonner"; import { getAppName, useAuthClient } from "@/app"; import { Badge, Button, Card, CardContent } from "@/components"; -export const Route = createFileRoute("/_layout/_authenticated/accept-invitation/$id")({ +export const Route = createFileRoute("/_layout/_authenticated/orgs/invites/$id")({ head: () => ({ meta: [{ title: `Accept Invitation | ${getAppName()}` }], }), @@ -63,7 +63,7 @@ function AcceptInvitation() { ]); await queryClient.refetchQueries({ queryKey: ["organizations"] }); await router.navigate({ - to: "/organizations/$slug", + to: "/orgs/$slug", params: { slug: invitation?.organizationSlug ?? "" }, }); }, @@ -78,7 +78,7 @@ function AcceptInvitation() { onSuccess: async () => { toast.success("Invitation declined"); await queryClient.invalidateQueries({ queryKey: ["user-invitations"] }); - await router.navigate({ to: "/organizations" }); + await router.navigate({ to: "/orgs" }); }, onError: (error: Error) => toast.error(error.message || "Failed to decline invitation"), }); @@ -100,7 +100,7 @@ function AcceptInvitation() { This invitation does not exist, has expired, or is not addressed to your account.

@@ -166,7 +166,7 @@ function AcceptInvitation() {
diff --git a/ui/src/routes/_layout/_authenticated/organizations/new.tsx b/ui/src/routes/_layout/_authenticated/orgs/new.tsx similarity index 96% rename from ui/src/routes/_layout/_authenticated/organizations/new.tsx rename to ui/src/routes/_layout/_authenticated/orgs/new.tsx index 664b81e7..02ed8509 100644 --- a/ui/src/routes/_layout/_authenticated/organizations/new.tsx +++ b/ui/src/routes/_layout/_authenticated/orgs/new.tsx @@ -7,7 +7,7 @@ import { useAuthClient } from "@/app"; import { Button, Card, CardContent, Input } from "@/components"; import { PageContainer } from "@/components/layout/page-container"; -export const Route = createFileRoute("/_layout/_authenticated/organizations/new")({ +export const Route = createFileRoute("/_layout/_authenticated/orgs/new")({ head: () => ({ title: "New Organization | auth.everything.dev", meta: [{ name: "description", content: "Create a new organization." }], @@ -37,7 +37,7 @@ function NewOrganization() { await queryClient.refetchQueries({ queryKey: ["organizations"] }); if (data?.slug) { await router.navigate({ - to: "/organizations/$slug", + to: "/orgs/$slug", params: { slug: data.slug }, }); } @@ -76,7 +76,7 @@ function NewOrganization() {
@@ -122,7 +122,7 @@ function NewOrganization() {
+ +
+ + )} + + ) : ( +
+ + City + + + {selectedCityNode && ( + { + setPoolInput(selectedCityNode.validatorPool); + setEditingPool(true); + }} + onDelete={() => setDeleteOpen(true)} + /> + )} +
+ )} + + {!isTenantSubdomain && isActiveOrgAdmin && ( +
+ + {creating && ( + +

+ Link a validator pool to one of your organization's tenants. +

+ + Tenant + + + + Validator pool + setNewPool(e.target.value)} + placeholder="city-node-3.pool.near" + className="h-9 text-sm font-mono" + /> + + + {availableTenants.length === 0 && ( +

+ No tenants available. Create a tenant first, then link it to a pool. +

+ )} +
+ )} +
+ )} + + +
+ Stake +
+ {!nearAccountId ? ( +
+

+ No NEAR wallet linked. Connect one to stake. +

+ +
+ ) : ( + <> +
+ + {nearAccountId} +
+ + Amount (NEAR) + setAmount(e.target.value)} + className="h-9 text-sm" + /> + + +

+ Staking sends NEAR to the pool contract — your stake stays under your account. +

+ + )} +
+ + onrampMutation.mutate()} + /> + + {deleteOpen && selectedCityNode && ( + +

+ Delete city node {selectedCityNode.hostname}? This + unlinks it from its validator pool. +

+
+ + +
+
+ )} + + + ); +} + +const TENANT_STATUS_VARIANT: Record = { + active: "default", + pending: "secondary", + suspended: "destructive", + pending_deletion: "destructive", +}; + +function CityNodeCard({ + cityNode, + gatewayId, + isAdmin, + memberCount, + totalStaked, + totalStakedLoading, + numDelegators, + numDelegatorsLoading, + onEdit, + onDelete, +}: { + cityNode: { + id: string; + hostname: string; + name: string; + accountId: string; + validatorPool: string; + tenantStatus: string; + }; + gatewayId: string; + isAdmin: boolean; + memberCount: number; + totalStaked: string | undefined; + totalStakedLoading: boolean; + numDelegators: number | undefined; + numDelegatorsLoading: boolean; + onEdit: () => void; + onDelete: () => void; +}) { + return ( + +
+
+
+ +
+
+
+

+ {cityNode.name} +

+ + {cityNode.tenantStatus.replace(/_/g, " ")} + +
+

+ {cityNode.hostname}.{gatewayId} · {cityNode.accountId} +

+
+
+ {isAdmin && ( +
+ + +
+ )} +
+
+ + {cityNode.validatorPool} +
+
+ + + +
+
+ ); +} + +function StatBlock({ label, value }: { label: string; value: string | number }) { + return ( +
+

+ {label} +

+

{value}

+
+ ); +} + +function PingOnrampBanner({ + disabled, + pending, + onBuy, +}: { + disabled: boolean; + pending: boolean; + onBuy: () => void; +}) { + const button = ( + + ); + + return ( +
+
+
+
+ +
+
+

+ Need NEAR to stake? +

+

+ Buy instantly with card, Apple Pay, or bank transfer. +

+
+
+ {disabled ? ( + + {button} + + Connect a NEAR wallet to buy NEAR + + + ) : ( + button + )} +
+
+ ); +} diff --git a/ui/src/routes/_layout/_authenticated/tenant/$tenantId.tsx b/ui/src/routes/_layout/_authenticated/tenant/$tenantId.tsx index 4a49f5c9..996b5699 100644 --- a/ui/src/routes/_layout/_authenticated/tenant/$tenantId.tsx +++ b/ui/src/routes/_layout/_authenticated/tenant/$tenantId.tsx @@ -37,18 +37,42 @@ async function publishTenantConfig( config: tenantConfig, }); - const signed = await auth.near.buildSignedDelegateAction( - prepared.data.contractId, - (builder: TransactionBuilder) => - builder.functionCall(prepared.data.contractId, prepared.data.methodName, prepared.data.args, { - gas: CONFIG_GAS, - attachedDeposit: 0n, - }), - ); + const relayerInfo = await auth.near.getRelayerInfo(); + const hasRelayer = relayerInfo.data?.enabled === true; + + if (hasRelayer) { + const signed = await auth.near.buildSignedDelegateAction( + prepared.data.contractId, + (builder: TransactionBuilder) => + builder.functionCall( + prepared.data.contractId, + prepared.data.methodName, + prepared.data.args, + { + gas: CONFIG_GAS, + attachedDeposit: 0n, + }, + ), + ); + + const relayed = await auth.near.relayTransaction({ payload: signed }); + if (relayed.error) throw new Error(relayed.error.message); + return relayed; + } + + const signerAccountId = auth.near.getAccountId(); + if (!signerAccountId) { + throw new Error("Connect a NEAR wallet first"); + } - const relayed = await auth.near.relayTransaction({ payload: signed }); - if (relayed.error) throw new Error(relayed.error.message); - return relayed; + return auth.near + .getNearClient() + .transaction(signerAccountId) + .functionCall(prepared.data.contractId, prepared.data.methodName, prepared.data.args, { + gas: CONFIG_GAS, + attachedDeposit: 0n, + }) + .send({ waitUntil: "EXECUTED" }); } export const Route = createFileRoute("/_layout/_authenticated/tenant/$tenantId")({ @@ -403,7 +427,7 @@ function TenantDetail() { there.

+ + + +
+
+
+

Live cities

+

+ Stake to a city's validator pool from its subdomain. +

+
+ + {cityNodes.length} {cityNodes.length === 1 ? "city" : "cities"} + +
+ + {cityNodes.length === 0 ? ( + +

+ No city nodes yet. Create a tenant and publish the first one. +

+
+ ) : ( +
+ {cityNodes.map((cityNode) => ( + +
+
+ +
+
+

+ {cityNode.name} +

+

+ {cityNode.hostname}.{runtime?.gatewayId ?? "citynode.app"} +

+
+
+
+ + {cityNode.validatorPool} +
+ +
+ ))} +
+ )} +
+ + + ); +} diff --git a/ui/src/routes/_layout/skill.tsx b/ui/src/routes/_layout/_public/skill.tsx similarity index 98% rename from ui/src/routes/_layout/skill.tsx rename to ui/src/routes/_layout/_public/skill.tsx index db047a71..d2f83c68 100644 --- a/ui/src/routes/_layout/skill.tsx +++ b/ui/src/routes/_layout/_public/skill.tsx @@ -9,7 +9,7 @@ import { Markdown } from "@/components/ui/markdown"; const INTENT_REGISTRY_URL = "https://tanstack.com/intent/registry/everything-dev"; -export const Route = createFileRoute("/_layout/skill")({ +export const Route = createFileRoute("/_layout/_public/skill")({ loader: async ({ context }) => { const runtimeConfig = context.runtimeConfig; diff --git a/ui/src/routes/_layout/things/$thingId.tsx b/ui/src/routes/_layout/_public/things/$thingId.tsx similarity index 98% rename from ui/src/routes/_layout/things/$thingId.tsx rename to ui/src/routes/_layout/_public/things/$thingId.tsx index 0fdb7405..ac3184f4 100644 --- a/ui/src/routes/_layout/things/$thingId.tsx +++ b/ui/src/routes/_layout/_public/things/$thingId.tsx @@ -6,7 +6,7 @@ import { sessionQueryOptions, useApiClient, useAuthClient } from "@/app"; import { Badge, Button } from "@/components"; import { Skeleton } from "@/components/ui/skeleton"; -export const Route = createFileRoute("/_layout/things/$thingId")({ +export const Route = createFileRoute("/_layout/_public/things/$thingId")({ head: ({ params }) => ({ meta: [ { title: `${params.thingId} | Things | everything.dev` }, diff --git a/ui/src/routes/_layout/things/index.tsx b/ui/src/routes/_layout/_public/things/index.tsx similarity index 97% rename from ui/src/routes/_layout/things/index.tsx rename to ui/src/routes/_layout/_public/things/index.tsx index 6cbee7e0..fba26c37 100644 --- a/ui/src/routes/_layout/things/index.tsx +++ b/ui/src/routes/_layout/_public/things/index.tsx @@ -1,7 +1,7 @@ import { createFileRoute, Link, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; -export const Route = createFileRoute("/_layout/things/")({ +export const Route = createFileRoute("/_layout/_public/things/")({ head: () => ({ meta: [ { title: "Things | everything.dev" }, diff --git a/ui/src/routes/_layout/things/live.tsx b/ui/src/routes/_layout/_public/things/live.tsx similarity index 98% rename from ui/src/routes/_layout/things/live.tsx rename to ui/src/routes/_layout/_public/things/live.tsx index 7be8f0c6..674d7813 100644 --- a/ui/src/routes/_layout/things/live.tsx +++ b/ui/src/routes/_layout/_public/things/live.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { useApiClient } from "@/app"; import { Badge } from "@/components"; -export const Route = createFileRoute("/_layout/things/live")({ +export const Route = createFileRoute("/_layout/_public/things/live")({ head: () => ({ meta: [ { title: "Live Stream | Things | everything.dev" }, diff --git a/ui/src/routes/_layout/index.tsx b/ui/src/routes/_layout/index.tsx deleted file mode 100644 index f998f7f7..00000000 --- a/ui/src/routes/_layout/index.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { createFileRoute, Link, redirect } from "@tanstack/react-router"; -import { Building2, Plus, Shield } from "lucide-react"; -import { sessionQueryOptions, useApiClient, useAuthClient } from "@/app"; -import { Button, Card } from "@/components"; -import { PageContainer } from "@/components/layout/page-container"; - -export const Route = createFileRoute("/_layout/")({ - beforeLoad: async ({ context }) => { - const { authClient } = context; - const session = await context.queryClient.ensureQueryData( - sessionQueryOptions(authClient, context.session), - ); - if (!session?.user) { - throw redirect({ to: "/login" }); - } - }, - component: TenantListPage, -}); - -function TenantListPage() { - const apiClient = useApiClient(); - const auth = useAuthClient(); - const { data: session } = useQuery(sessionQueryOptions(auth, undefined)); - - const { data: tenants = [], isLoading } = useQuery({ - queryKey: ["tenants"], - queryFn: () => apiClient.listTenants(), - staleTime: 30_000, - }); - - return ( - -
-
-
- - Tenants -
-
-
-

- {session?.user?.name || session?.user?.email || "Your"} Tenants -

-
- -
-
- - {isLoading ? ( -
- {[1, 2, 3].map((n) => ( - -
-
-
- - ))} -
- ) : tenants.length === 0 ? ( - - -

No tenants yet.

-

- Create a tenant to deploy your own app with custom UI and API. -

- -
- ) : ( -
- {tenants.map((tenant) => ( - -
-
{tenant.name}
-
- {tenant.subdomain} -
-
-
- - -
-
- -
-
- ))} -
- )} -
- - ); -} - -function TenantMeta({ label, value, mono }: { label: string; value: string; mono?: boolean }) { - return ( -
- {label} - - {value} - -
- ); -} diff --git a/ui/src/styles.css b/ui/src/styles.css index 17e0fd36..4da787d1 100644 --- a/ui/src/styles.css +++ b/ui/src/styles.css @@ -202,34 +202,6 @@ code { } } -@keyframes fade-in-up { - from { - opacity: 0; - transform: translateY(8px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.animate-fade-in-up { - animation: fade-in-up 0.4s cubic-bezier(0.4, 0, 0.2, 1) both; -} - -@keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -.animate-fade-in { - animation: fade-in 0.6s cubic-bezier(0.4, 0, 0.2, 1) both; -} - @keyframes subtitle-cycle { 0% { opacity: 0; } 15% { opacity: 1; }