diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbc2c50..d7f1694 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,9 @@ on: branches: [main, dev, 'fix/**', 'feat/**'] types: [opened, synchronize, reopened, ready_for_review] +permissions: + contents: read + jobs: ci: if: github.event.pull_request.draft != true @@ -43,7 +46,11 @@ jobs: --health-retries=10 steps: + # Full history: the client provenance check reads the subject of the + # last commit that touched the vendored tarball. - uses: actions/checkout@v6 + with: + fetch-depth: 0 - uses: pnpm/action-setup@v4 @@ -55,6 +62,14 @@ jobs: - name: Install run: pnpm install --frozen-lockfile + # vendor/developmentfinance-client.provenance.json names the sdk + # commit and the spec-kit revision the vendored client was built + # from; the commit that vendored the tarball must name the same sdk + # commit in its subject. See "API client package" in + # docs/development.md. + - name: Check client provenance + run: pnpm check:client-provenance + - name: Lint run: pnpm lint diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a22d468..ced6e2c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,6 +16,9 @@ concurrency: group: deploy-${{ github.ref }} cancel-in-progress: false +permissions: + contents: read + jobs: changes: runs-on: ubuntu-latest diff --git a/db/migrations/0024_drop_share_card_result.sql b/db/migrations/0024_drop_share_card_result.sql new file mode 100644 index 0000000..2b964ab --- /dev/null +++ b/db/migrations/0024_drop_share_card_result.sql @@ -0,0 +1 @@ +DROP TABLE "share_card_result" CASCADE; \ No newline at end of file diff --git a/db/migrations/meta/0024_snapshot.json b/db/migrations/meta/0024_snapshot.json new file mode 100644 index 0000000..0825809 --- /dev/null +++ b/db/migrations/meta/0024_snapshot.json @@ -0,0 +1,1093 @@ +{ + "id": "ea4d3404-8df5-4f62-90bf-93d2f9b32694", + "prevId": "5c9656fc-9b6b-44f8-9c1e-a3ca5c5aaeeb", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.conversation": { + "name": "conversation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_session_id": { + "name": "agent_session_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "turn_count": { + "name": "turn_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "data": { + "name": "data", + "type": "jsonb", + "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": { + "conversation_user_updated_idx": { + "name": "conversation_user_updated_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "conversation_user_id_user_id_fk": { + "name": "conversation_user_id_user_id_fk", + "tableFrom": "conversation", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.event": { + "name": "event", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "event_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visitor_id": { + "name": "visitor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "page": { + "name": "page", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "props": { + "name": "props", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + } + }, + "indexes": { + "event_created_at_idx": { + "name": "event_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "event_name_created_at_idx": { + "name": "event_name_created_at_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.feedback": { + "name": "feedback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "feedback_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "page": { + "name": "page", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "feedback_created_at_idx": { + "name": "feedback_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "feedback_message_length": { + "name": "feedback_message_length", + "value": "char_length(\"feedback\".\"message\") BETWEEN 1 AND 5000" + } + }, + "isRLSEnabled": false + }, + "public.limit_counter": { + "name": "limit_counter", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "window_start": { + "name": "window_start", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "limit_counter_window_start_idx": { + "name": "limit_counter_window_start_idx", + "columns": [ + { + "expression": "window_start", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.roadmap_vote": { + "name": "roadmap_vote", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entry_id": { + "name": "entry_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "roadmap_vote_entry_idx": { + "name": "roadmap_vote_entry_idx", + "columns": [ + { + "expression": "entry_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "roadmap_vote_user_id_user_id_fk": { + "name": "roadmap_vote_user_id_user_id_fk", + "tableFrom": "roadmap_vote", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "roadmap_vote_user_id_entry_id_pk": { + "name": "roadmap_vote_user_id_entry_id_pk", + "columns": [ + "user_id", + "entry_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.share": { + "name": "share", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "card_id": { + "name": "card_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "share_user_idx": { + "name": "share_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "share_user_id_user_id_fk": { + "name": "share_user_id_user_id_fk", + "tableFrom": "share", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.share_cleanup": { + "name": "share_cleanup", + "schema": "", + "columns": { + "share_id": { + "name": "share_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "write_token": { + "name": "write_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.share_ref": { + "name": "share_ref", + "schema": "", + "columns": { + "share_id": { + "name": "share_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "part_index": { + "name": "part_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "spec_id": { + "name": "spec_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "identity": { + "name": "identity", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "entity_groups": { + "name": "entity_groups", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "variable_text": { + "name": "variable_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_version": { + "name": "content_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "artifact_digest": { + "name": "artifact_digest", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "operations": { + "name": "operations", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "assumptions": { + "name": "assumptions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "share_ref_share_id_share_id_fk": { + "name": "share_ref_share_id_share_id_fk", + "tableFrom": "share_ref", + "tableTo": "share", + "columnsFrom": [ + "share_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "share_ref_share_id_part_index_pk": { + "name": "share_ref_share_id_part_index_pk", + "columns": [ + "share_id", + "part_index" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.share_token": { + "name": "share_token", + "schema": "", + "columns": { + "share_id": { + "name": "share_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "write_token": { + "name": "write_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "share_token_user_id_user_id_fk": { + "name": "share_token_user_id_user_id_fk", + "tableFrom": "share_token", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webmail_domain": { + "name": "webmail_domain", + "schema": "", + "columns": { + "domain": { + "name": "domain", + "type": "text", + "primaryKey": true, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleting_at": { + "name": "deleting_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/db/migrations/meta/_journal.json b/db/migrations/meta/_journal.json index 0499520..1cd9871 100644 --- a/db/migrations/meta/_journal.json +++ b/db/migrations/meta/_journal.json @@ -162,6 +162,13 @@ "when": 1793258249005, "tag": "0023_share_cleanup_and_deleting_at", "breakpoints": true + }, + { + "idx": 24, + "version": "7", + "when": 1793871780545, + "tag": "0024_drop_share_card_result", + "breakpoints": true } ] } \ No newline at end of file diff --git a/db/schema.ts b/db/schema.ts index 6b8969b..20c0a1d 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -2,7 +2,6 @@ import type { Assumption, EntityGroup, RequestedOperation } from '@developmentfi import { sql } from 'drizzle-orm'; import { bigint, - boolean, check, index, integer, @@ -115,42 +114,6 @@ export const shareRef = pgTable( export type ShareRefRow = typeof shareRef.$inferSelect; -/** - * One row per data card of a live canvas or card share: the last answer of - * the card's refresh (src/lib/studio/liveShareResults.ts) and the lease that - * lets one request at a time ask the back end for a new one. `status` is - * 'current', 'redrawn', 'pending', or 'error', and null while the first - * refresh is in flight. `card` is the card a 'current' or 'redrawn' answer - * drew, and `frame` the frame a 'redrawn' card was drawn on, which the - * card's download exports. Requests answer from the row until - * `fresh_until`. After it, the request that moves `lease_until` into the - * future calls the back end and the others keep answering from the row. - */ -export const shareCardResult = pgTable( - 'share_card_result', - { - shareId: text('share_id') - .notNull() - .references(() => share.id, { onDelete: 'cascade' }), - cardId: text('card_id').notNull(), - status: text('status'), - card: jsonb('card'), - frame: jsonb('frame'), - // The machine-readable reason of an 'error' answer. - code: text('code'), - // Whether the back end said asking again might succeed, for an 'error' answer. - retryable: boolean('retryable'), - // The wait a 'pending' answer asks of the viewer. - retryAfterS: integer('retry_after_s'), - checkedAt: timestamp('checked_at', { withTimezone: true }), - freshUntil: timestamp('fresh_until', { withTimezone: true }), - leaseUntil: timestamp('lease_until', { withTimezone: true }), - }, - (table) => [primaryKey({ columns: [table.shareId, table.cardId] })], -); - -export type ShareCardResultRow = typeof shareCardResult.$inferSelect; - /** * The write token dfo-apis issued for a share a signed-in user made, which * the website sends on that user's replace and delete requests diff --git a/db/share-export.ts b/db/share-export.ts index 6ae99d7..5ff4a05 100644 --- a/db/share-export.ts +++ b/db/share-export.ts @@ -11,7 +11,6 @@ import type { ShareKind } from '../src/lib/chat/sharePrune.ts'; * defines, which the share migration (db/share-migrate.ts) sends to * dfo-apis' share import route. `placeShare` decides where each stored * share lands, and refuses a row it cannot place with the reason. - * `share_card_result` rows are a cache of live refreshes and are not read. */ /** One data card of a share as dfo-apis holds it, in display order. */ diff --git a/db/test/live-share.test.ts b/db/test/live-share.test.ts deleted file mode 100644 index 2866574..0000000 --- a/db/test/live-share.test.ts +++ /dev/null @@ -1,771 +0,0 @@ -import path from 'node:path'; -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { eq, sql } from 'drizzle-orm'; -import { drizzle } from 'drizzle-orm/node-postgres'; -import type { SavedConversation } from '../../src/lib/chat/history.ts'; -import { deleteOwnedShare, insertStudioShare } from '../../src/lib/chat/shareWrites.ts'; -import { dataResponse, refreshResponse, type LiveDeps } from '../../src/lib/studio/liveShare.ts'; -import { - ShareGone, - leasedResult, - snapshotLiveShare, - storeResult, - storedResult, - takeLease, -} from '../../src/lib/studio/liveShareResults.ts'; -import { - APIS, - DATASET_FRAME, - DERIVED_FRAME, - ENV, - MOVED_FRAME, - PRINCIPAL, - REPLAYED_FRAME, - aiPending, - aiRedrawn, - aiRefusal, - frameAnswer, - json, - pendingAnswer, - refusal, - renderReply, - stalled, - stubBackend, - type StubScript, -} from '../../src/lib/studio/test/fixtures/live-backend-stub.ts'; -import { pairOf } from '../../src/lib/studio/test/fixtures/recorded-wire.ts'; -import { applyMigrations } from '../apply-migrations.ts'; -import { share, shareCardResult, user } from '../schema.ts'; -import { createTestDatabase } from './harness.ts'; - -const MIGRATIONS_FOLDER = path.resolve(import.meta.dirname, '../migrations'); - -const CONCURRENT = 6; - -const recordedHeaders = pairOf('render_data.csv').response.headers; - -// The row is the browser's JSON: its `frame` names a frame no back end -// knows, and its numbers are not the stored revision's. -const ROW_FRAME = { spec_id: 'spec_from_the_browser', content_version: 'cv_row' }; - -const chart = (id: string, componentId?: string) => ({ - kind: 'chart' as const, - id, - turn: 1, - chartType: 'line' as const, - title: id, - categories: ['2024'], - series: [{ label: 'Kenya', values: [999] }], - frame: ROW_FRAME, - ...(componentId ? { ref: { component_id: componentId, revision: 3 } } : {}), -}); - -const saved = { - id: 'conv-1', - sessionId: null, - title: 'Aid to Kenya', - updatedAt: 1, - messages: [], - widgets: [ - chart('data', 'cmp_data'), - chart('derived', 'cmp_derived'), - chart('no-ref'), - chart('removed', 'cmp_data'), - ], - activity: [], - turnCount: 1, - pinnedIds: [], - removedIds: ['removed'], - layout: {}, - textCards: [{ kind: 'text', id: 'note', title: 'Note', body: 'Body' }], -} as SavedConversation; - -const COMPONENTS = { cmp_data: DATASET_FRAME, cmp_derived: DERIVED_FRAME }; - -/** - * `refreshResponse` and `dataResponse` (src/lib/studio/liveShare.ts) on the - * migrated tables as the runtime role, over a stubbed dfo-apis and dfo-ai: - * which shares and cards the routes serve, the answer each back-end verdict - * becomes, the lease that bounds back-end calls per card, the rows a card's - * download streams, and that the credential stays on the server. - */ -test('live share routes', async (t) => { - const database = await createTestDatabase(); - t.after(() => database.teardown()); - - await applyMigrations(database.migratorPool(), { - migrationsFolder: MIGRATIONS_FOLDER, - runtimeDbUser: database.runtimeUser, - migratorDbUser: database.migratorUser, - }); - - // One pool per concurrent request, since each test pool holds one - // connection and the lease must hold across connections. - const handles = Array.from({ length: CONCURRENT }, () => drizzle(database.runtimePool())); - const db = handles[0]!; - - await db.insert(user).values([{ id: 'owner', name: 'Owner', email: 'owner@example.org' }]); - - const liveShare = async (kind: 'canvas' | 'card' = 'canvas', cardId?: string) => - (await insertStudioShare(db, { userId: 'owner', kind, mode: 'live', saved, cardId })).id; - - const over = (script: Partial, handle = db) => { - const stub = stubBackend({ components: COMPONENTS, ...script }); - const deps: LiveDeps = { db: handle, env: ENV, fetch: stub.fetch }; - - return { stub, deps }; - }; - - const rows = (shareId: string) => - db.select().from(shareCardResult).where(eq(shareCardResult.shareId, shareId)); - - // Move the card's stored answer past its window. - const age = (shareId: string) => - db - .update(shareCardResult) - .set({ freshUntil: sql`now() - interval '1 second'` }) - .where(eq(shareCardResult.shareId, shareId)); - - // Every response a test collects, for the credential check at the end. - const seen: Response[] = []; - const refresh = async (deps: LiveDeps, shareId: string, cardId: string) => { - const res = await refreshResponse(deps, shareId, cardId); - - seen.push(res.clone()); - - return { status: res.status, body: await res.json() }; - }; - const download = async (deps: LiveDeps, shareId: string, cardId: string, query = '') => { - const res = await dataResponse(deps, shareId, cardId, new URLSearchParams(query)); - - seen.push(res.clone()); - - return res; - }; - - await t.test('404 unless the share is live and holds the card as a data card', async () => { - const live = await liveShare(); - const snapshot = ( - await insertStudioShare(db, { - userId: 'owner', - kind: 'canvas', - mode: 'snapshot', - saved, - }) - ).id; - const conversation = ( - await insertStudioShare(db, { - userId: 'owner', - kind: 'conversation', - mode: 'snapshot', - saved, - }) - ).id; - - await db.insert(share).values([ - { id: 'nomode00', userId: 'owner', kind: 'canvas', title: 'Old', data: saved }, - { id: 'srch0000', kind: 'search', mode: 'live', title: 'aid', data: { query: 'aid' } }, - ]); - - const { stub, deps } = over({}); - const cases: [string, string, string][] = [ - ['no such share', 'missing0', 'data'], - ['a malformed id', 'not-an-id', 'data'], - ['a snapshot', snapshot, 'data'], - ['a share with no mode', 'nomode00', 'data'], - ['a conversation share', conversation, 'data'], - ['a live search share', 'srch0000', 'data'], - ['no such card', live, 'elsewhere'], - ['a text card', live, 'note'], - ['a data card with no ref', live, 'no-ref'], - ['a removed card', live, 'removed'], - ]; - - for (const [name, shareId, cardId] of cases) { - const answer = await refresh(deps, shareId, cardId); - - assert.equal(answer.status, 404, name); - assert.equal(answer.body.code, 'not_found', name); - assert.equal(answer.body.retryable, false, name); - assert.equal((await download(deps, shareId, cardId)).status, 404, name); - } - - assert.equal(stub.calls.length, 0); - assert.deepEqual(await rows(live), []); - }); - - await t.test('a card share serves its one card', async () => { - const id = await liveShare('card', 'data'); - const { deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - }); - - assert.equal((await refresh(deps, id, 'data')).body.status, 'current'); - assert.equal((await refresh(deps, id, 'derived')).status, 404); - }); - - await t.test('current: the card is the stored revision’s read, not the row', async () => { - const id = await liveShare(); - const { stub, deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - }); - - const answer = await refresh(deps, id, 'data'); - - assert.equal(answer.status, 200); - assert.deepEqual(Object.keys(answer.body).sort(), ['card', 'status']); - assert.equal(answer.body.status, 'current'); - assert.deepEqual(answer.body.card.payload.provenance.inputs, [DATASET_FRAME]); - // The frame dfo-apis checks comes from the stored intent. - assert.deepEqual(stub.to('/frames/refresh')[0]?.body, DATASET_FRAME); - assert.ok(!JSON.stringify(answer.body).includes(ROW_FRAME.spec_id)); - assert.ok(!JSON.stringify(answer.body).includes('999')); - }); - - await t.test('redrawn, pending, and error answers', async () => { - const id = await liveShare(); - - const moved = over({ - refreshFrame: () => frameAnswer('moved', MOVED_FRAME), - render: () => json(renderReply('cmp_data', 3, MOVED_FRAME)), - }); - const redrawn = await refresh(moved.deps, id, 'data'); - - assert.equal(redrawn.status, 200); - assert.equal(redrawn.body.status, 'redrawn'); - assert.deepEqual(redrawn.body.card.payload.provenance.inputs, [MOVED_FRAME]); - assert.equal(redrawn.body.card.payload.provenance.refreshed_from, 3); - assert.deepEqual(Object.keys(redrawn.body.card).sort(), [ - 'component_id', - 'fact_summary', - 'payload', - 'revision', - ]); - - const queued = over({ - refreshDerived: () => aiPending(7), - }); - - assert.deepEqual(await refresh(queued.deps, id, 'derived'), { - status: 200, - body: { status: 'pending', retry_after_s: 7 }, - }); - - await age(id); - - const refused = over({ - refreshDerived: (call) => aiRefusal('recipe_missing', call), - }); - - assert.deepEqual(await refresh(refused.deps, id, 'derived'), { - status: 200, - body: { status: 'error', code: 'recipe_missing', retry_after_s: 30 }, - }); - }); - - await t.test('pending: the recorded fractional wait is served as whole seconds', async () => { - const id = await liveShare(); - const { deps } = over({ refreshFrame: () => pendingAnswer() }); - - // dfo-apis answers 1.5; the stored answer, the route's answer, and - // the Retry-After header of a download all hold 2. - assert.deepEqual(await refresh(deps, id, 'data'), { - status: 200, - body: { status: 'pending', retry_after_s: 2 }, - }); - assert.equal((await rows(id))[0]?.retryAfterS, 2); - - const waiting = await download(deps, id, 'data'); - - assert.equal(waiting.status, 503); - assert.equal(waiting.headers.get('retry-after'), '2'); - assert.equal((await waiting.json()).retry_after_s, 2); - }); - - await t.test('no credential: an error answer and no back-end call', async () => { - const id = await liveShare(); - const { stub, deps } = over({}); - const unset = { ...deps, env: { ...ENV, LIVE_REFRESH_PRINCIPAL: '' } }; - - assert.deepEqual(await refresh(unset, id, 'data'), { - status: 200, - body: { status: 'error', code: 'live_unavailable' }, - }); - - const file = await download(unset, id, 'data'); - - assert.equal(file.status, 502); - assert.equal((await file.json()).code, 'live_unavailable'); - assert.equal(stub.calls.length, 0); - assert.deepEqual(await rows(id), []); - }); - - await t.test('concurrent requests for one card make one back-end call', async () => { - const id = await liveShare(); - let gate = Promise.withResolvers(); - const stub = stubBackend({ - components: COMPONENTS, - refreshFrame: async () => { - await gate.promise; - - return frameAnswer('current', DATASET_FRAME); - }, - }); - const together = () => - handles.map((handle) => - refresh({ db: handle, env: ENV, fetch: stub.fetch }, id, 'data'), - ); - - // First open: one request holds the lease and waits on dfo-apis. The - // others have no stored answer to give and are told to come back. - const leased = async () => { - const answers = together(); - const early = await Promise.race([ - Promise.all(answers), - new Promise((resolve) => setTimeout(() => resolve(null), 500)), - ]); - - assert.equal(early, null, 'the lease holder is still waiting on the back end'); - gate.resolve(); - - return (await Promise.all(answers)) - .map((answer) => answer.body) - .sort((a, b) => a.status.localeCompare(b.status)); - }; - const waiting = Array(CONCURRENT - 1).fill({ status: 'pending', retry_after_s: 2 }); - - assert.deepEqual( - (await leased()).map((body) => body.status), - ['current', ...waiting.map(({ status }) => status)], - ); - assert.equal(stub.to('/frames/refresh').length, 1); - assert.equal(stub.to('/components/cmp_data').length, 1); - - // Inside the window every request answers from the row. - for (const answer of await Promise.all(together())) { - assert.equal(answer.body.status, 'current'); - } - - assert.equal(stub.calls.length, 2); - - // Past the window one request asks again. The others are told to - // come back rather than handed the expired card, which a viewer - // would keep as final. - await age(id); - gate = Promise.withResolvers(); - - const [holder, ...others] = await leased(); - - assert.equal(holder.status, 'current'); - assert.deepEqual(others, waiting); - assert.equal(stub.to('/frames/refresh').length, 2); - assert.equal(stub.to('/components/cmp_data').length, 2); - }); - - await t.test('pending and error answers stand for a short window', async () => { - const id = await liveShare(); - const { stub, deps } = over({ - refreshFrame: () => pendingAnswer(4), - refreshDerived: (call) => aiRefusal('recipe_missing', call), - }); - const freshFor = async (cardId: string) => { - const { rows: found } = await db.execute<{ seconds: number }>( - sql`select extract(epoch from fresh_until - checked_at)::float as seconds - from share_card_result where share_id = ${id} and card_id = ${cardId}`, - ); - - return Math.round(found[0]!.seconds); - }; - - await refresh(deps, id, 'data'); - await refresh(deps, id, 'data'); - await refresh(deps, id, 'derived'); - await refresh(deps, id, 'derived'); - - // A second request inside the window reaches no back end. - assert.equal(stub.to('/frames/refresh').length, 1); - assert.equal(stub.to('/api/studio/refresh').length, 1); - assert.equal(await freshFor('data'), 4); - assert.equal(await freshFor('derived'), 30); - - // The viewer's retry after the wait reaches the back end again. - await age(id); - await refresh(deps, id, 'data'); - assert.equal(stub.to('/frames/refresh').length, 2); - }); - - await t.test( - 'dfo-ai’s transient and credential refusals stand for the error window', - async () => { - for (const code of ['upstream_unavailable', 'unauthorized', 'refresh_not_configured']) { - const id = await liveShare(); - const { stub, deps } = over({ - refreshDerived: (call) => aiRefusal(code, call), - }); - const answer = { status: 200, body: { status: 'error', code, retry_after_s: 30 } }; - - assert.deepEqual(await refresh(deps, id, 'derived'), answer, code); - assert.deepEqual(await refresh(deps, id, 'derived'), answer, code); - assert.equal(stub.to('/api/studio/refresh').length, 1, code); - - const [{ seconds }] = ( - await db.execute<{ seconds: number }>( - sql`select extract(epoch from fresh_until - checked_at)::float as seconds - from share_card_result where share_id = ${id} and card_id = 'derived'`, - ) - ).rows as [{ seconds: number }]; - - assert.equal(Math.round(seconds), 30, code); - - await age(id); - await refresh(deps, id, 'derived'); - assert.equal(stub.to('/api/studio/refresh').length, 2, code); - } - }, - ); - - await t.test('a lease left by a request that died lapses', async () => { - const id = await liveShare(); - const { stub, deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - }); - - await db.insert(shareCardResult).values({ - shareId: id, - cardId: 'data', - leaseUntil: sql`now() + interval '60 seconds'`, - }); - - assert.deepEqual((await refresh(deps, id, 'data')).body, { - status: 'pending', - retry_after_s: 2, - }); - assert.equal(stub.calls.length, 0); - - await db - .update(shareCardResult) - .set({ leaseUntil: sql`now() - interval '1 second'` }) - .where(eq(shareCardResult.shareId, id)); - - assert.equal((await refresh(deps, id, 'data')).body.status, 'current'); - }); - - await t.test('data: a redrawn card streams the export on the refreshed frame', async () => { - const id = await liveShare(); - const { stub, deps } = over({ - refreshFrame: () => frameAnswer('moved', MOVED_FRAME), - render: () => json(renderReply('cmp_data', 3, MOVED_FRAME)), - }); - - // No stored answer yet, so the download refreshes the card first. - const file = await download( - deps, - id, - 'data', - 'format=csv&bundle=false&spec_id=spec_other&content_version=cv_other&extra=1', - ); - - assert.equal(file.status, 200); - assert.ok((await file.text()).startsWith(pairOf('render_data.csv').response.body_starts!)); - assert.equal(file.headers.get('content-type'), recordedHeaders['content-type']); - assert.equal( - file.headers.get('content-disposition'), - recordedHeaders['content-disposition'], - ); - assert.equal(file.headers.get('set-cookie'), null); - - const [call] = stub.to('/render/data'); - const sent = new URL(call!.url); - - assert.equal( - sent.pathname, - '/api/presentation/components/cmp_data/revisions/3/render/data', - ); - // The frame is the one the refresh returned, whatever the caller names. - assert.deepEqual(Object.fromEntries(sent.searchParams), { - format: 'csv', - bundle: 'false', - spec_id: MOVED_FRAME.spec_id, - content_version: MOVED_FRAME.content_version, - }); - assert.equal(call?.headers.get('x-dfo-principal'), PRINCIPAL); - - // A second download serves from the stored answer. - await download(deps, id, 'data', 'format=xlsx'); - assert.equal(stub.to('/frames/refresh').length, 1); - assert.equal(new URL(stub.to('/render/data')[1]!.url).searchParams.get('format'), 'xlsx'); - }); - - await t.test('data: a derived redraw exports the frame its render names', async () => { - const id = await liveShare(); - const { stub, deps } = over({ - refreshDerived: () => aiRedrawn(renderReply('cmp_derived', 3, REPLAYED_FRAME)), - }); - - assert.equal((await download(deps, id, 'derived')).status, 200); - assert.equal( - stub.to('/render/data')[0]?.url, - `${APIS}/api/presentation/components/cmp_derived/revisions/3/render/data?spec_id=${REPLAYED_FRAME.spec_id}&content_version=${REPLAYED_FRAME.content_version}`, - ); - }); - - await t.test('data: a current card streams the stored revision’s public export', async () => { - const id = await liveShare(); - const { stub, deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - }); - - const file = await download(deps, id, 'data', 'format=parquet&bundle=true'); - - assert.equal(file.headers.get('content-disposition'), 'attachment; filename="stored.csv"'); - - const [call] = stub.to('/revisions/3/data'); - - assert.equal( - call?.url, - `${APIS}/api/presentation/components/cmp_data/revisions/3/data?format=parquet&bundle=true`, - ); - assert.equal(call?.headers.get('x-dfo-principal'), null); - assert.equal(stub.to('/render/data').length, 0); - }); - - // Node's fetch decodes a gzipped export and keeps its compressed length. - await t.test( - 'data: a gzipped export streams every row without its compressed length', - async () => { - const id = await liveShare(); - const { deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - storedExport: () => - new Response('year,value\n2024,1\n', { - headers: { - 'content-type': 'text/csv', - 'content-encoding': 'gzip', - 'content-length': '9', - }, - }), - }); - - const file = await download(deps, id, 'data', 'format=csv&bundle=false'); - - assert.equal(file.headers.get('content-length'), null); - assert.equal(await file.text(), 'year,value\n2024,1\n'); - }, - ); - - await t.test('data: no file while pending, on an error, or on a refused export', async () => { - const id = await liveShare(); - const queued = over({ - refreshFrame: () => pendingAnswer(9), - }); - const waiting = await download(queued.deps, id, 'data'); - - assert.equal(waiting.status, 503); - assert.equal(waiting.headers.get('retry-after'), '9'); - const pending = await waiting.json(); - - assert.equal(waiting.headers.get('content-type'), 'application/problem+json'); - assert.equal(pending.code, 'refresh_pending'); - assert.equal(pending.retryable, true); - assert.equal(pending.retry_after_s, 9); - - const failed = over({ refreshDerived: (call) => aiRefusal('recipe_missing', call) }); - const none = await download(failed.deps, id, 'derived'); - - assert.equal(none.status, 502); - const missing = await none.json(); - - assert.equal(none.headers.get('content-type'), 'application/problem+json'); - assert.equal(missing.code, 'recipe_missing'); - assert.equal(missing.retryable, false); - assert.equal(missing.retry_after_s, 30); - - await age(id); - - const refusing = over({ - refreshFrame: () => frameAnswer('moved', MOVED_FRAME), - render: () => json(renderReply('cmp_data', 3, MOVED_FRAME)), - renderedExport: (call) => - call.url.includes('format=xlsx') - ? refusal('request_invalid', 400, call) - : refusal('unauthorized', 401, call), - }); - const badFormat = await download(refusing.deps, id, 'data', 'format=xlsx'); - - assert.equal(badFormat.status, 400); - assert.equal((await badFormat.json()).code, 'request_invalid'); - - // A refusal of the server's own credential is not the caller's error. - const badCredential = await download(refusing.deps, id, 'data', 'format=csv'); - - assert.equal(badCredential.status, 502); - assert.equal((await badCredential.json()).code, 'unauthorized'); - }); - - await t.test('stored answers go with the share and with a switch to snapshot', async () => { - const { deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - }); - const deleted = await liveShare(); - const switched = await liveShare(); - const kept = await liveShare(); - - for (const id of [deleted, switched, kept]) { - await refresh(deps, id, 'data'); - assert.equal((await rows(id)).length, 1); - } - - assert.equal(await deleteOwnedShare(db, deleted, 'owner'), 'done'); - assert.deepEqual(await rows(deleted), []); - - assert.equal(await snapshotLiveShare(db, switched, 'other'), 'not_found'); - assert.equal((await rows(switched)).length, 1); - assert.equal(await snapshotLiveShare(db, switched, 'owner'), 'done'); - assert.deepEqual(await rows(switched), []); - assert.equal((await refresh(deps, switched, 'data')).status, 404); - - assert.equal((await rows(kept)).length, 1); - }); - - await t.test( - 'a stored error answers how long it stands and what the back end said', - async () => { - const id = await liveShare(); - const { deps } = over({ - refreshFrame: () => frameAnswer('moved', MOVED_FRAME), - render: (call) => refusal('release_mismatch', 409, call, true), - }); - const fresh = await refresh(deps, id, 'data'); - - assert.deepEqual(fresh.body, { - status: 'error', - code: 'release_mismatch', - retryable: true, - retry_after_s: 30, - }); - assert.equal((await rows(id))[0]?.retryable, true); - - // Five seconds before the stored answer lapses, a viewer who asks again is - // told so, with the same code and the same word on retrying. - await db - .update(shareCardResult) - .set({ freshUntil: sql`now() + interval '5 seconds'` }) - .where(eq(shareCardResult.shareId, id)); - - const stored = await refresh(deps, id, 'data'); - - assert.deepEqual( - { ...stored.body, retry_after_s: undefined }, - { - status: 'error', - code: 'release_mismatch', - retryable: true, - retry_after_s: undefined, - }, - ); - assert.ok([4, 5].includes(stored.body.retry_after_s), `${stored.body.retry_after_s}`); - - const file = await download(deps, id, 'data'); - - assert.equal(file.status, 502); - assert.equal((await file.json()).retryable, true); - }, - ); - - await t.test('a stalled export ends at its deadline as a visible backend_timeout', async () => { - const id = await liveShare(); - const { deps } = over({ - refreshFrame: () => frameAnswer('current', DATASET_FRAME), - storedExport: stalled, - }); - - await refresh(deps, id, 'data'); - - const res = await Promise.race([ - download({ ...deps, exportDeadlineMs: 30 }, id, 'data'), - new Promise((_, reject) => - setTimeout(() => reject(new Error('the export was never ended')), 3000), - ), - ]); - - assert.equal(res.status, 502); - assert.equal((await res.json()).code, 'backend_timeout'); - }); - - await t.test('a share deleted mid-refresh answers 404, not a foreign-key failure', async () => { - const id = await liveShare(); - const { deps } = over({ refreshFrame: () => frameAnswer('current', DATASET_FRAME) }); - // The share is found live, then deleted before the card's lease is taken. - const gated = (query: T): T => - new Proxy(query, { - get: (target, prop, receiver) => { - if (prop === 'then') { - return (resolve: (v: unknown) => void, reject: (e: unknown) => void) => - deleteOwnedShare(db, id, 'owner').then( - () => - (target as unknown as PromiseLike).then( - resolve, - reject, - ), - reject, - ); - } - - const value = Reflect.get(target, prop, receiver); - - return typeof value === 'function' - ? (...args: unknown[]) => gated(value.apply(target, args)) - : value; - }, - }); - const racing = new Proxy(db, { - get: (target, prop) => - prop === 'execute' - ? (...args: unknown[]) => - gated((target.execute as (...a: unknown[]) => object)(...args)) - : Reflect.get(target, prop, target), - }); - - const answer = await refresh({ ...deps, db: racing }, id, 'data'); - - assert.equal(answer.status, 404); - assert.equal(answer.body.code, 'not_found'); - assert.deepEqual(await rows(id), []); - }); - - await t.test('a share switched to snapshot takes no lease and keeps no result', async () => { - const id = await liveShare(); - const key = { shareId: id, cardId: 'data' }; - - assert.equal(await takeLease(db, key), 'taken'); - assert.equal(await takeLease(db, key), 'held'); - assert.equal(await snapshotLiveShare(db, id, 'owner'), 'done'); - - // A request that found the share live before the switch finishes after - // it: it can neither take a lease nor store its answer. - assert.equal(await takeLease(db, key), 'gone'); - await storeResult(db, key, { status: 'error', code: 'refresh_failed' }, 300); - assert.deepEqual(await rows(id), []); - assert.equal(await storedResult(db, key), null); - await assert.rejects( - leasedResult(db, key, 300, async () => ({ status: 'error', code: 'x' })), - ShareGone, - ); - assert.deepEqual(await rows(id), []); - }); - - await t.test('no response carries the credential', async () => { - assert.ok(seen.length > 40, `${seen.length} responses collected`); - - for (const res of seen) { - const headers = [...res.headers].flat().join('\n'); - - assert.ok(!headers.includes(PRINCIPAL), 'a response header carries the credential'); - assert.ok( - !(await res.text()).includes(PRINCIPAL), - 'a response body carries the credential', - ); - } - }); -}); diff --git a/db/test/share-export.test.ts b/db/test/share-export.test.ts index 1bded48..c42763f 100644 --- a/db/test/share-export.test.ts +++ b/db/test/share-export.test.ts @@ -319,11 +319,6 @@ test('every legacy share case lands in the export', async (t) => { } } - // A live refresh cache row, which the export never reads. - await pool.query( - `insert into share_card_result (share_id, card_id, status) values ('canvLiv1', 'c1', 'current')`, - ); - const lines = (await readShares(pool)).map(placeShare); // The lines are JSON, where a member left undefined is absent. diff --git a/db/test/share-mode.test.ts b/db/test/share-mode.test.ts index 81f16fc..06ca932 100644 --- a/db/test/share-mode.test.ts +++ b/db/test/share-mode.test.ts @@ -4,13 +4,7 @@ import test from 'node:test'; import { eq } from 'drizzle-orm'; import { drizzle } from 'drizzle-orm/node-postgres'; import type { SavedConversation } from '../../src/lib/chat/history.ts'; -import { LIVE_CARD_TITLE } from '../../src/lib/chat/sharePayload.ts'; import { isLiveStudioShare, searchShareMode } from '../../src/lib/chat/shareMode.ts'; -import { - deleteOwnedShare, - insertStudioShare, - snapshotOwnedShare, -} from '../../src/lib/chat/shareWrites.ts'; import { applyMigrations } from '../apply-migrations.ts'; import { share, user } from '../schema.ts'; import { createTestDatabase } from './harness.ts'; @@ -34,12 +28,11 @@ const saved: SavedConversation = { }; /** - * `shareWrites.ts` against the migrated tables as the runtime role: a Studio - * share stores its mode, a row with no mode reads as a snapshot for a Studio - * kind and as live for a search, and only a share's owner switches it to - * snapshot or deletes it. + * The `share.mode` column against the migrated tables as the runtime role: + * a row stored with no mode holds null, which reads as a snapshot for a + * Studio kind and as live for a search (src/lib/chat/shareMode.ts). */ -test('Studio share writes', async (t) => { +test('share mode column', async (t) => { const database = await createTestDatabase(); t.after(() => database.teardown()); @@ -56,67 +49,7 @@ test('Studio share writes', async (t) => { return row; }; - await db.insert(user).values([ - { id: 'owner', name: 'Owner', email: 'owner@example.org' }, - { id: 'other', name: 'Other', email: 'other@example.org' }, - ]); - - await t.test('insertStudioShare stores the mode it is given', async () => { - const cases = [ - ['canvas', 'live'], - ['canvas', 'snapshot'], - ['card', 'live'], - ['card', 'snapshot'], - ['conversation', 'snapshot'], - ] as const; - - for (const [kind, mode] of cases) { - const { id } = await insertStudioShare(db, { - userId: 'owner', - kind, - mode, - saved, - cardId: kind === 'card' ? 'note-1' : undefined, - }); - const row = await stored(id); - - assert.equal(row?.mode, mode, `${kind} ${mode}`); - assert.equal(isLiveStudioShare(row!), mode === 'live', `${kind} ${mode}`); - } - }); - - await t.test('a live card share’s title is the sharer’s own, never the card’s', async () => { - const withCard = { - ...saved, - widgets: [ - { - kind: 'chart' as const, - id: 'chart-1', - turn: 1, - chartType: 'line' as const, - title: 'Stored heading', - categories: ['2024'], - series: [{ label: 'Kenya', values: [1] }], - }, - ], - }; - const titled = async (mode: 'live' | 'snapshot', titles?: Record) => { - const { id } = await insertStudioShare(db, { - userId: 'owner', - kind: 'card', - mode, - saved: { ...withCard, ...(titles ? { titles } : {}) }, - cardId: 'chart-1', - }); - - return (await stored(id))?.title; - }; - - assert.equal(await titled('snapshot'), 'Stored heading'); - assert.equal(await titled('live'), LIVE_CARD_TITLE); - assert.equal(await titled('live', { 'chart-1': 'My own title' }), 'My own title'); - assert.equal(await titled('snapshot', { 'chart-1': 'My own title' }), 'My own title'); - }); + await db.insert(user).values({ id: 'owner', name: 'Owner', email: 'owner@example.org' }); await t.test('a stored row with no mode is a Studio snapshot and a live search', async () => { await db.insert(share).values([ @@ -138,86 +71,4 @@ test('Studio share writes', async (t) => { assert.equal(searchShareMode(search!.mode), 'live'); assert.equal(isLiveStudioShare(search!), false); }); - - await t.test('snapshotOwnedShare switches the owner’s live share only', async () => { - const { id } = await insertStudioShare(db, { - userId: 'owner', - kind: 'canvas', - mode: 'live', - saved, - }); - - assert.equal(await snapshotOwnedShare(db, id, 'other'), 'not_found'); - assert.equal((await stored(id))?.mode, 'live'); - assert.equal(await snapshotOwnedShare(db, 'missing0', 'owner'), 'not_found'); - - assert.equal(await snapshotOwnedShare(db, id, 'owner'), 'done'); - - const row = await stored(id); - - assert.equal(row?.mode, 'snapshot'); - assert.deepEqual(row?.data, { - ...saved, - sessionId: null, - componentRefs: [], - messages: [], - activity: [], - }); - assert.equal(await snapshotOwnedShare(db, id, 'owner'), 'done'); - assert.equal((await stored(id))?.mode, 'snapshot'); - }); - - await t.test('deleteOwnedShare removes the owner’s share only', async () => { - const { id } = await insertStudioShare(db, { - userId: 'owner', - kind: 'card', - mode: 'live', - saved, - cardId: 'note-1', - }); - - assert.equal(await deleteOwnedShare(db, id, 'other'), 'not_found'); - assert.ok(await stored(id)); - assert.equal(await deleteOwnedShare(db, 'missing0', 'owner'), 'not_found'); - - assert.equal(await deleteOwnedShare(db, id, 'owner'), 'done'); - assert.equal(await stored(id), undefined); - assert.equal(await deleteOwnedShare(db, id, 'owner'), 'not_found'); - }); - - await t.test('a guest’s Studio share is unowned and matches no caller', async () => { - const { id } = await insertStudioShare(db, { - userId: null, - kind: 'canvas', - mode: 'live', - saved, - }); - - assert.equal((await stored(id))?.userId, null); - assert.equal(await snapshotOwnedShare(db, id, 'owner'), 'not_found'); - assert.equal(await deleteOwnedShare(db, id, 'owner'), 'not_found'); - assert.equal((await stored(id))?.mode, 'live'); - }); - - await t.test('a search share has no owner writes', async () => { - await db.insert(share).values([ - { - id: 'srch0001', - userId: 'owner', - kind: 'search', - mode: 'live', - title: 'aid', - data: { query: 'aid' }, - }, - ]); - - assert.equal(await snapshotOwnedShare(db, 'srch0001', 'owner'), 'unsupported_kind'); - assert.equal(await deleteOwnedShare(db, 'srch0001', 'owner'), 'unsupported_kind'); - assert.equal(await snapshotOwnedShare(db, 'srch0001', 'other'), 'not_found'); - assert.equal(await deleteOwnedShare(db, 'srch0001', 'other'), 'not_found'); - // An unowned search share matches no caller. - assert.equal(await deleteOwnedShare(db, 'srch0000', 'owner'), 'not_found'); - assert.equal((await stored('srch0001'))?.mode, 'live'); - assert.ok(await stored('srch0000')); - }); }); diff --git a/docs/development.md b/docs/development.md index 9e44e84..1c1f5bb 100644 --- a/docs/development.md +++ b/docs/development.md @@ -209,7 +209,7 @@ The `apis`, `ai` and `dcp` repositories run their own dev services. `apis` and ` ## API client package -The Search, Datasets and Graph types come from `@developmentfinance/client`, vendored as `vendor/developmentfinance-client-0.1.0.tgz`. The tarball is the `typescript/` package of the sdk repository, built and packed. To refresh it, pack that package into `vendor/` and re-lock: +The Search, Datasets and Graph types come from `@developmentfinance/client`, vendored as `vendor/developmentfinance-client-0.1.0.tgz`. The tarball is the `typescript/` package of the sdk repository, built and packed. It records only its package version, so `vendor/developmentfinance-client.provenance.json` beside it names the sdk commit it was packed from and the spec-kit revision that commit's `contract/SOURCE` pins. To refresh the tarball, pack that package into `vendor/`, update the provenance file, and re-lock: ```sh cd /typescript @@ -219,7 +219,9 @@ cd just relock-client ``` -A new package version packs under a new file name. Point the `@developmentfinance/client` entry in `package.json` at it, delete the old tarball, then re-lock. `just relock-client` deletes the client's stale directory under `node_modules/.pnpm` (pnpm keeps an installed `file:` package of the same name and version, so a rebuilt tarball is otherwise ignored), has pnpm re-resolve the client's lockfile entries from the tarball with `pnpm install --lockfile-only --fix-lockfile`, runs `pnpm install --frozen-lockfile`, and imports the client to prove it loads. The re-lock needs the registry. It changes the client's entries and those of any dependency the new tarball adds. pnpm 10.5.2 also fills in deprecation notices and writes `eslint-plugin-import`'s peer suffix in one of two equivalent forms, so a re-lock can touch those few lines too. Commit the tarball and the lockfile together. +Write the sdk checkout's `git rev-parse HEAD` as `sdk.commit` and its `contract/SOURCE` as `spec_kit.commit` in the provenance file, both as full hashes. Name the sdk commit in the subject of the commit that vendors the tarball, as the earlier ones do (`chore(deps): vendor the client at sdk 82701c8`), and keep it in the pull request title, since a squash merge takes its subject from there. CI's `pnpm check:client-provenance` (`scripts/check-client-provenance.mjs`) fails when the provenance file names a tarball other than the one `package.json` installs, when either hash is not a full commit hash, or when the last commit that touched the tarball names no sdk commit or a different one from the provenance file. It reads the git log, so run it after committing. + +A new package version packs under a new file name. Point the `@developmentfinance/client` entry in `package.json` and the provenance file's `package` at it, delete the old tarball, then re-lock. `just relock-client` deletes the client's stale directory under `node_modules/.pnpm` (pnpm keeps an installed `file:` package of the same name and version, so a rebuilt tarball is otherwise ignored), has pnpm re-resolve the client's lockfile entries from the tarball with `pnpm install --lockfile-only --fix-lockfile`, runs `pnpm install --frozen-lockfile`, and imports the client to prove it loads. The re-lock needs the registry. It changes the client's entries and those of any dependency the new tarball adds. pnpm 10.5.2 also fills in deprecation notices and writes `eslint-plugin-import`'s peer suffix in one of two equivalent forms, so a re-lock can touch those few lines too. Commit the tarball and the lockfile together. The tarball carries the statuses of the codes the proxy raises itself and not their descriptions. `src/lib/proxy/proxy-codes.json` holds the full file, copied from the spec-kit repository's `python/client-contract/src/dfo_client_contract/proxy-codes.json` and formatted with Prettier. Copy it again when the client contract changes it. diff --git a/docs/share-export.md b/docs/share-export.md index 0141b8d..36661d7 100644 --- a/docs/share-export.md +++ b/docs/share-export.md @@ -84,7 +84,7 @@ The migration refuses a share, with `placement_refused`, whose: ## Left out -`share_card_result` rows cache live refreshes, and dfo-apis refreshes live slots itself, so the migration leaves them out. It leaves out the `conversation_id` and `card_id` columns too, because no share page reads them. +The migration leaves out the `conversation_id` and `card_id` columns, because no share page reads them. ## Round trip on a local stack diff --git a/justfile b/justfile index f3132b5..ee4d7ef 100644 --- a/justfile +++ b/justfile @@ -182,8 +182,15 @@ test: build: pnpm build +# Checks vendor/developmentfinance-client.provenance.json against the commit +# that vendored the tarball (see "API client package" in docs/development.md). +# +# Check the vendored API client's provenance +check-client-provenance: + pnpm check:client-provenance + # Everything ci.yml runs, in CI's order. Green here means green there. -check: lint typecheck test build +check: check-client-provenance lint typecheck test build # ── Container ────────────────────────────────────────── diff --git a/motion-kit/build.ts b/motion-kit/build.ts index a5b548c..53f2b61 100644 --- a/motion-kit/build.ts +++ b/motion-kit/build.ts @@ -15,7 +15,7 @@ import { htmlLeaks } from './html.ts'; import { type Motion, readMotion } from './motion.ts'; import { previewHtml } from './preview.ts'; import { CELL, DEFAULT_VIEWPORT, ROOT_FONT_SIZE, readSpec, type Viewport } from './spec.ts'; -import { type Source, stateClasses, type StyleModules } from './stateClasses.ts'; +import { missingClasses, type Source, stateClasses, type StyleModules } from './stateClasses.ts'; /** * The generator: bundles render.tsx with esbuild (SCSS modules, SVGR icons @@ -156,16 +156,21 @@ const stubPlugin: Plugin = { // --- the renderer ------------------------------------------------------------- -type Renderer = Pick; +type Renderer = Pick< + typeof import('./render.tsx'), + 'renderStudio' | 'layoutDefaultChart' | 'stepLines' +>; /** render.tsx bundled for Node and loaded, with the CSS its imports pulled * in (every module of every component in the bundle), the class map of - * each SCSS module and the app's TSX sources in the bundle. */ + * each SCSS module, the app's TSX sources in the bundle and the kit's own + * sources in it. */ export const loadRenderer = async (): Promise<{ renderer: Renderer; moduleCss: string; styleModules: StyleModules; sources: Source[]; + kitSources: Source[]; }> => { const out = await mkdtemp(join(tmpdir(), 'motion-kit-')); @@ -204,10 +209,18 @@ export const loadRenderer = async (): Promise<{ const renderer = requireHere(bundlePath) as Renderer; const scope = globalThis as { [STYLE_MODULES]?: StyleModules }; - const sources = await Promise.all( - Object.keys(result.metafile.inputs) - .filter((path) => path.startsWith('src/') && path.endsWith('.tsx')) - .map(async (path) => ({ path, text: await readFile(join(REPO, path), 'utf8') })), + const read = (keep: (path: string) => boolean) => + Promise.all( + Object.keys(result.metafile.inputs) + .filter(keep) + .map(async (path) => ({ + path, + text: await readFile(join(REPO, path), 'utf8'), + })), + ); + const sources = await read((path) => path.startsWith('src/') && path.endsWith('.tsx')); + const kitSources = await read( + (path) => path.startsWith('motion-kit/') && /\.tsx?$/.test(path), ); return { @@ -215,6 +228,7 @@ export const loadRenderer = async (): Promise<{ moduleCss: css?.text ?? '', styleModules: scope[STYLE_MODULES] ?? {}, sources, + kitSources, }; } finally { await rm(out, { recursive: true, force: true }); @@ -456,7 +470,13 @@ export const generate = async ({ // the markup the same on every machine. process.env.TZ = 'UTC'; - const { renderer, moduleCss, styleModules, sources } = await loadRenderer(); + const { renderer, moduleCss, styleModules, sources, kitSources } = await loadRenderer(); + const missing = missingClasses([...kitSources, ...sources], styleModules, REPO); + + if (missing.length) { + throw new Error(`motion-kit: classes no SCSS module defines: ${missing.join(', ')}`); + } + const rendered = renderer.renderStudio(spec, { fontsDir: FONTS }); const html = inertLinks(rendered.html); const leaks = htmlLeaks(html); diff --git a/motion-kit/render.tsx b/motion-kit/render.tsx index 52ad971..a328c46 100644 --- a/motion-kit/render.tsx +++ b/motion-kit/render.tsx @@ -216,7 +216,6 @@ const Slot = ({ placed }: { placed: Placed }) => { } >
- {!isText && ( { + const outermost = (el: Element) => { + for (let up = el.parent; up && up !== turn; up = up.parent) { + if (up.name === 'li') { + return false; + } + } + + return true; + }; + const lines = walk(turn.children).filter((el) => el.name === 'li' && outermost(el)); + const steps = lines.filter((el) => hasClass(el, stepClass)); + + if (steps.length !== lines.length) { + throw new Error( + `render: an activity turn shows ${lines.length} lines, ${steps.length} with the step class`, + ); + } + + return steps; +}; + // The hooks inside the app's own components, found by their classes. const chatHooks = (html: string, spec: DemoSpec): string => { const all = walk(parseMarkup(html)); @@ -555,9 +579,9 @@ const chatHooks = (html: string, spec: DemoSpec): string => { .filter((el) => el.name === 'section' && hasClass(el, activityStyles.turn!)) .forEach((turn, index) => { hook(turn, `activity-${index}`); - within(turn) - .filter((el) => el.name === 'li' && hasClass(el, activityStyles.step!)) - .forEach((line, step) => hook(line, `activity-${index}-${step}`)); + stepLines(turn, activityStyles.step!).forEach((line, step) => + hook(line, `activity-${index}-${step}`), + ); }); // The app's entrance reveals start hidden until an observer marks them; diff --git a/motion-kit/stateClasses.ts b/motion-kit/stateClasses.ts index 8524151..543b8f1 100644 --- a/motion-kit/stateClasses.ts +++ b/motion-kit/stateClasses.ts @@ -25,9 +25,54 @@ const modulePath = (specifier: string, from: string, repo: string): string => repo, specifier.startsWith('@/') ? join(repo, 'src', specifier.slice(2)) - : resolve(dirname(from), specifier), + : resolve(repo, dirname(from), specifier), ); +const parse = ({ path, text }: Source): ts.SourceFile => + ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); + +// The local names of a source's `classnames` import and of each SCSS module +// it imports, with the module's class map, and the modules without a map. +const readImports = ( + source: Source, + styles: StyleModules, + repo: string, +): { + file: ts.SourceFile; + joiners: Set; + modules: Map>; + unmapped: string[]; +} => { + const file = parse(source); + const joiners = new Set(); + const modules = new Map>(); + const unmapped: string[] = []; + + for (const statement of file.statements) { + const name = ts.isImportDeclaration(statement) && statement.importClause?.name?.text; + + if (!name || !ts.isStringLiteral(statement.moduleSpecifier)) { + continue; + } + + const specifier = statement.moduleSpecifier.text; + + if (specifier === 'classnames') { + joiners.add(name); + } else if (specifier.endsWith('.module.scss')) { + const map = styles[modulePath(specifier, source.path, repo)]; + + if (map) { + modules.set(name, map); + } else { + unmapped.push(specifier); + } + } + } + + return { file, joiners, modules, unmapped }; +}; + export const stateClasses = ( sources: Source[], styles: StyleModules, @@ -36,36 +81,7 @@ export const stateClasses = ( const found = new Map>(); for (const { path, text } of sources) { - const file = ts.createSourceFile( - path, - text, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TSX, - ); - // The local names of the `classnames` import and of each SCSS module. - const joiners = new Set(); - const modules = new Map>(); - - for (const statement of file.statements) { - const name = ts.isImportDeclaration(statement) && statement.importClause?.name?.text; - - if (!name || !ts.isStringLiteral(statement.moduleSpecifier)) { - continue; - } - - const specifier = statement.moduleSpecifier.text; - - if (specifier === 'classnames') { - joiners.add(name); - } else if (specifier.endsWith('.module.scss')) { - const map = styles[modulePath(specifier, path, repo)]; - - if (map) { - modules.set(name, map); - } - } - } + const { file, joiners, modules } = readImports({ path, text }, styles, repo); if (!joiners.size || !modules.size) { continue; @@ -156,3 +172,43 @@ export const stateClasses = ( [...found].filter(([, set]) => set.size).map(([base, set]) => [base, [...set].toSorted()]), ); }; + +/** + * Every class a source reads from an SCSS module it imports, as + * `styles.name` or `styles['name']`, that the module's class map lacks, listed + * as `path: styles.name` and sorted. Such a read is `undefined` at run time, + * so the element it names carries no class. An imported module without a + * class map is listed as `path: specifier`, since its reads go unchecked. + */ +export const missingClasses = (sources: Source[], styles: StyleModules, repo: string): string[] => { + const missing = new Set(); + + for (const source of sources) { + const { file, modules, unmapped } = readImports(source, styles, repo); + + unmapped.forEach((specifier) => missing.add(`${source.path}: ${specifier}`)); + + const visit = (node: ts.Node) => { + const read = ts.isPropertyAccessExpression(node) + ? { object: node.expression, key: node.name.text } + : ts.isElementAccessExpression(node) && + ts.isStringLiteralLike(node.argumentExpression) + ? { object: node.expression, key: node.argumentExpression.text } + : null; + + if (read && ts.isIdentifier(read.object)) { + const map = modules.get(read.object.text); + + if (map && !Object.hasOwn(map, read.key)) { + missing.add(`${source.path}: ${read.object.text}.${read.key}`); + } + } + + ts.forEachChild(node, visit); + }; + + visit(file); + } + + return [...missing].toSorted(); +}; diff --git a/motion-kit/test/motion-kit.test.ts b/motion-kit/test/motion-kit.test.ts index af1705d..c90f1a1 100644 --- a/motion-kit/test/motion-kit.test.ts +++ b/motion-kit/test/motion-kit.test.ts @@ -23,12 +23,15 @@ import { externalUrls, filmCss, mediaMatches, scopeSelector, toPx } from '../css import { readCues } from '../cues.ts'; import { outfitMeasure, parseFont } from '../fontMetrics.ts'; import type { Motion } from '../motion.ts'; +import { parseMarkup } from '../html.ts'; import { readSpec } from '../spec.ts'; +import { missingClasses } from '../stateClasses.ts'; import { hasBrowser, startBrowser } from './browser.ts'; import type { ChartWidget } from '../../src/lib/chat/widgets.ts'; import { plotHeight, plotShape } from '../../src/lib/studio/chartFit.ts'; const KENYA = join(import.meta.dirname, '../demos/kenya-debt-health.json'); +const REPO = join(import.meta.dirname, '../..'); const chart = (unit: string, series: (number | null)[][]): ChartWidget => ({ kind: 'chart', @@ -617,3 +620,44 @@ test('the stylesheet keeps its motion, scoped, only when asked', () => { assert.match(kept, /\.dfo-ui \.a\s*\{\s*animation: spin 1s;\s*margin: 16px/); assert.doesNotMatch(frozen, /keyframes|animation/); }); + +test('a class read from an SCSS module must be in its class map', async () => { + const { styleModules, sources, kitSources } = await loadRenderer(); + + // Every class the kit and the app's components in the bundle read exists. + assert.deepEqual(missingClasses([...kitSources, ...sources], styleModules, REPO), []); + + const styles = { 'src/a.module.scss': { known: 'a_known' } }; + const text = [ + "import styles from '@/a.module.scss';", + "import other from './b.module.scss';", + "const ok = [styles.known, styles['known'], styles[name]];", + "const bad = [styles.gone, styles['lost'], other.any];", + ].join('\n'); + + assert.deepEqual(missingClasses([{ path: 'src/x.tsx', text }], styles, '/repo'), [ + 'src/x.tsx: ./b.module.scss', + 'src/x.tsx: styles.gone', + 'src/x.tsx: styles.lost', + ]); +}); + +test('a line an activity turn shows without the step class is refused', async () => { + const { renderer } = await loadRenderer(); + const spec = readSpec(JSON.parse(await readFile(KENYA, 'utf8'))); + const turn = (html: string) => parseMarkup(`
${html}
`)[0]!; + + assert.ok(renderer.renderStudio(spec, { fontsDir: FONTS }).hooks.includes('activity-0-0')); + // A step's tries are list items inside the step's own. + assert.equal( + renderer.stepLines( + turn('
'), + 's', + ).length, + 2, + ); + assert.throws( + () => renderer.stepLines(turn('
'), 's'), + /shows 2 lines, 1 with the step class/, + ); +}); diff --git a/next.config.ts b/next.config.ts index 2ad4108..400ad0d 100644 --- a/next.config.ts +++ b/next.config.ts @@ -26,9 +26,10 @@ const nextConfig: NextConfig = { async headers() { return noindexHeaders(); }, - // ONE's privacy policy covers this site; /privacy is the address the - // MCP directory listings give for it. Temporary, so a later page of our - // own can take the path without browsers holding the old target. + // ONE's privacy policy covers this site, and its Terms and Conditions + // section sets the terms of use; /privacy and /terms are the addresses + // the MCP directory listings give for them. Temporary, so a later page of + // our own can take either path without browsers holding the old target. async redirects() { return [ { @@ -36,6 +37,12 @@ const nextConfig: NextConfig = { destination: 'https://www.one.org/info/privacy-policy/', permanent: false, }, + { + source: '/terms', + destination: + 'https://www.one.org/info/privacy-policy/#h-terms-and-conditions-nbsp', + permanent: false, + }, ]; }, // A docs page's raw markdown sits beside it: /docs/.md serves the diff --git a/package.json b/package.json index 056804f..3e3c0b3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "lint:css:fix": "stylelint \"src/**/*.scss\" --fix", "motion-kit": "node motion-kit/cli.ts", "typecheck": "tsc --noEmit && tsc --noEmit -p motion-kit", + "check:client-provenance": "node scripts/check-client-provenance.mjs", "test": "node --test db/test/*.test.ts src/lib/search/test/*.test.ts src/lib/studio/test/*.test.ts src/lib/chat/test/*.test.ts src/lib/proxy/test/*.test.ts src/lib/auth/test/*.test.ts src/lib/mail/test/*.test.ts src/lib/sources/test/*.test.ts src/lib/test/*.test.ts src/lib/docs/test/*.test.ts motion-kit/test/*.test.ts", "format": "prettier --write \"src/**/*.{ts,tsx,js,json,scss}\"", "optimize:svg": "svgo -f src/assets -r", @@ -31,12 +32,12 @@ "@fontsource/dm-mono": "^5.3.0", "@fontsource/outfit": "^5.3.0", "@google-cloud/bigquery": "^9.4.0", - "@google-cloud/cloud-sql-connector": "^1.12.0", + "@google-cloud/cloud-sql-connector": "^1.12.1", "@radix-ui/react-dialog": "^1.1.23", "@radix-ui/react-dropdown-menu": "^2.1.24", "@radix-ui/react-popover": "^1.1.23", "@sentry/nextjs": "^10.75.2", - "better-auth": "^1.6.29", + "better-auth": "^1.7.7", "chart.js": "^4.5.1", "classnames": "^2.5.1", "disposable-email-domains-js": "^1.26.0", @@ -45,9 +46,9 @@ "html-to-image": "^1.11.13", "isbot": "5.2.2", "lowlight": "^3.3.0", - "mermaid": "^12.1.0", - "next": "16.3.0", - "nodemailer": "^10.0.9", + "mermaid": "^12.3.0", + "next": "16.4.5", + "nodemailer": "^10.1.4", "pg": "^8.23.0", "react": "19.2.8", "react-chartjs-2": "^5.3.1", @@ -65,9 +66,9 @@ "@types/react": "^19", "@types/react-dom": "^19", "drizzle-kit": "^0.31.10", - "esbuild": "^0.25.0", + "esbuild": "^0.28.1", "eslint": "^9", - "eslint-config-next": "16.3.0", + "eslint-config-next": "16.4.5", "playwright-core": "^1.64.1", "postcss": "^8.5.26", "postcss-scss": "^4.0.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 179b5ac..8a8077c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^9.4.0 version: 9.4.0 '@google-cloud/cloud-sql-connector': - specifier: ^1.12.0 - version: 1.12.0 + specifier: ^1.12.1 + version: 1.12.1 '@radix-ui/react-dialog': specifier: ^1.1.23 version: 1.1.23(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -40,10 +40,10 @@ importers: version: 1.1.23(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@sentry/nextjs': specifier: ^10.75.2 - version: 10.75.2(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))(next@16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(react@19.2.8)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)) + version: 10.75.2(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))(next@16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(react@19.2.8)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)) better-auth: - specifier: ^1.6.29 - version: 1.7.4(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0))(next@16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^1.7.7 + version: 1.7.7(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0))(next@16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) chart.js: specifier: ^4.5.1 version: 4.5.1 @@ -55,7 +55,7 @@ importers: version: 1.26.0 drizzle-orm: specifier: ^0.45.2 - version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0) + version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0) free-email-domains: specifier: ^1.15.12 version: 1.15.12 @@ -69,14 +69,14 @@ importers: specifier: ^3.3.0 version: 3.3.0 mermaid: - specifier: ^12.1.0 - version: 12.1.0 + specifier: ^12.3.0 + version: 12.3.0 next: - specifier: 16.3.0 - version: 16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0) + specifier: 16.4.5 + version: 16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0) nodemailer: - specifier: ^10.0.9 - version: 10.0.10 + specifier: ^10.1.4 + version: 10.1.4 pg: specifier: ^8.23.0 version: 8.23.0 @@ -124,14 +124,14 @@ importers: specifier: ^0.31.10 version: 0.31.10 esbuild: - specifier: ^0.25.0 - version: 0.25.12 + specifier: ^0.28.1 + version: 0.28.1 eslint: specifier: ^9 version: 9.39.5 eslint-config-next: - specifier: 16.3.0 - version: 16.3.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) + specifier: 16.4.5 + version: 16.4.5(@typescript-eslint/parser@8.67.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3) playwright-core: specifier: ^1.64.1 version: 1.64.1 @@ -158,7 +158,7 @@ importers: version: 5.0.0(stylelint@17.14.1(typescript@5.9.3)) svgo: specifier: ^4.0.2 - version: 4.0.2 + version: 4.1.0 typescript: specifier: ^5 version: 5.9.3 @@ -797,63 +797,60 @@ packages: resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} - '@better-auth/core@1.7.4': - resolution: {integrity: sha512-g66dKOMB5PLxoujkxkZeYb6dh5wLhfAx/D+w9XtcO669Dslnyen2naBYqnD1r5ZwnxFKcr6aSrUPaH952V89Lg==} + '@better-auth/core@1.7.7': + resolution: {integrity: sha512-srgFzHEjB2WWlokFz1i4BfG41sNhsA105Pukgqu6RrEh5A+LSkzf1mCHydVipkjt9l5qswtBMi7IL6ouHbZL4Q==} peerDependencies: '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - '@cloudflare/workers-types': '>=4' + '@better-fetch/fetch': 1.3.2 '@opentelemetry/api': ^1.9.0 better-call: 1.4.0 jose: ^6.1.0 kysely: ^0.28.5 || ^0.29.0 nanostores: ^1.0.1 peerDependenciesMeta: - '@cloudflare/workers-types': - optional: true '@opentelemetry/api': optional: true - '@better-auth/drizzle-adapter@1.7.4': - resolution: {integrity: sha512-J9JMl0DGYNYwtDj5nq/fzycU9dzPUDEtTNn89KmToUrwPDGfByDgfmiQ9jGK+LN3EnfsG1Pt/dDmRocbnfihlA==} + '@better-auth/drizzle-adapter@1.7.7': + resolution: {integrity: sha512-qon0U94PR5FQysuyGoAlGVQpIIiNg5dtDpwjE63sN/HjoRWZadZzHPcS9mn7JsJyQZK6iTNhd7LD0AaXt2f78w==} peerDependencies: - '@better-auth/core': ^1.7.4 + '@better-auth/core': ^1.7.7 '@better-auth/utils': 0.4.2 drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 peerDependenciesMeta: drizzle-orm: optional: true - '@better-auth/kysely-adapter@1.7.4': - resolution: {integrity: sha512-WHLsTs4ZwTKorifdK6Whol6Zy+0gIzOJEXzWsvzmLtlT1uIZFpd7IPBlDZTrXvIZnxPWJHPIv8/uHqS9QFPZOA==} + '@better-auth/kysely-adapter@1.7.7': + resolution: {integrity: sha512-9FONOCgOcrQI9wJ5v1oDGIg9sT7pa9RjZZQ4fsCTudo8R87SrIGNUvUxDFTJhZvUblvFfGaIBR9Vb8LA+8bqLQ==} peerDependencies: - '@better-auth/core': ^1.7.4 + '@better-auth/core': ^1.7.7 '@better-auth/utils': 0.4.2 kysely: ^0.28.17 || ^0.29.0 peerDependenciesMeta: kysely: optional: true - '@better-auth/memory-adapter@1.7.4': - resolution: {integrity: sha512-xAihOPh4mFjvKLzmu2XNzj7RchDmytA/d/xjrGyps1oHVGCTGDGghlSt/rIT5fagtTn2R9+TBLh5QrZnTvYu9g==} + '@better-auth/memory-adapter@1.7.7': + resolution: {integrity: sha512-FqKFEe+b5tXXV0gRbyirca+qXgMpLOeJ8Wk19yykb/scQKy0WJ4k22WzEV3TTPctQV9DZhwCXPlDtQhnlOv5Gg==} peerDependencies: - '@better-auth/core': ^1.7.4 + '@better-auth/core': ^1.7.7 '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.7.4': - resolution: {integrity: sha512-sIdIc8vONXgQxKPfzL4bbk6gO3gdFEDzC5VG8rD18xSFB6j7blBKcuzOofbswbwwbyuspnshoViGRqSoqp2PnA==} + '@better-auth/mongo-adapter@1.7.7': + resolution: {integrity: sha512-ZnVDuRrXqbf0oHphrEN27oTImNH5NTO26dtWatq5cXyYAsH7goof9QIoTOmxTbBxUQahKBw9T6+9h6n+RtOE+g==} peerDependencies: - '@better-auth/core': ^1.7.4 + '@better-auth/core': ^1.7.7 '@better-auth/utils': 0.4.2 mongodb: ^6.0.0 || ^7.0.0 peerDependenciesMeta: mongodb: optional: true - '@better-auth/prisma-adapter@1.7.4': - resolution: {integrity: sha512-cvsMNaXLcwQiAIxQCx5Gq2VA1reW4RjkC5oLNXNJR4IhgeiqcvYF6qKF6UEC0vV2x3euKz5V/SDY/hblHWwx+w==} + '@better-auth/prisma-adapter@1.7.7': + resolution: {integrity: sha512-4YcnrcVdvWiAZw0sZl63tWUClPXg5r3QsLn7C2sG7kKEBY06zOng0ibecyxRBixKq1zE1ceHuEUqez5Rhi+nQA==} peerDependencies: - '@better-auth/core': ^1.7.4 + '@better-auth/core': ^1.7.7 '@better-auth/utils': 0.4.2 '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 prisma: ^5.0.0 || ^6.0.0 || ^7.0.0 @@ -863,21 +860,21 @@ packages: prisma: optional: true - '@better-auth/telemetry@1.7.4': - resolution: {integrity: sha512-FycnvWXP1gZ8BlSN1lW9L/YYFVqxi7+n6A9i66gJKdoJqREDdmhxBKOaVCbNhNC2UNDAo9PbBm/6J3gv6WmAEA==} + '@better-auth/telemetry@1.7.7': + resolution: {integrity: sha512-PaRA6i+kAioVYmza2gHPcdrxP0hAotph4KV9hft2GeNJq+AZXDiBu3FuLM9HxvcmiZeUT74Vufqd9ypmB8veTA==} peerDependencies: - '@better-auth/core': ^1.7.4 + '@better-auth/core': ^1.7.7 '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 + '@better-fetch/fetch': 1.3.2 '@better-auth/utils@0.4.2': resolution: {integrity: sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==} - '@better-auth/utils@0.5.0': - resolution: {integrity: sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==} + '@better-auth/utils@0.5.1': + resolution: {integrity: sha512-WETqw5vQ52uQ/xwWR4E8rTgT6fDEr/wnS+otDNkBCa/69281FCF32OKDdQUcahMCnjsvqXBNiEvAQHFB0EVRRg==} - '@better-fetch/fetch@1.3.1': - resolution: {integrity: sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g==} + '@better-fetch/fetch@1.3.2': + resolution: {integrity: sha512-Gs7n99b5tqUC6cQAPbV0uED3IraHB6xQbHLQ/C3l7ZFafHScOx9pQ+DYmP5blbLFShVWLqxNUlI9wi4xU/X+ow==} '@braintree/sanitize-url@7.1.2': resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} @@ -986,8 +983,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.28.2': - resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1004,8 +1001,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.28.2': - resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1022,8 +1019,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.28.2': - resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1040,8 +1037,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.28.2': - resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1058,8 +1055,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.28.2': - resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1076,8 +1073,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.28.2': - resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1094,8 +1091,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.28.2': - resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -1112,8 +1109,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.28.2': - resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -1130,8 +1127,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.28.2': - resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -1148,8 +1145,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.28.2': - resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -1166,8 +1163,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.28.2': - resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -1184,8 +1181,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.28.2': - resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -1202,8 +1199,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.28.2': - resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -1220,8 +1217,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.28.2': - resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1238,8 +1235,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.28.2': - resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1256,8 +1253,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.28.2': - resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1274,8 +1271,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.28.2': - resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -1286,8 +1283,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.28.2': - resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1304,8 +1301,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.28.2': - resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1316,8 +1313,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.28.2': - resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1334,8 +1331,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.28.2': - resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1346,8 +1343,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.28.2': - resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1364,8 +1361,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.28.2': - resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1382,8 +1379,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.28.2': - resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1400,8 +1397,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.28.2': - resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1418,8 +1415,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.28.2': - resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1498,8 +1495,8 @@ packages: '@google-cloud/bigquery-storage': optional: true - '@google-cloud/cloud-sql-connector@1.12.0': - resolution: {integrity: sha512-cUrI+6StY8F/dQB5Lujrl9TFYibDZGGWvPp1aHhvLn4btOlbflxdHoZ6NDWeWySCNtnOYhoiZNQfK4qPC5moBw==} + '@google-cloud/cloud-sql-connector@1.12.1': + resolution: {integrity: sha512-Vilc4J0L+rU+4yjxZpBFn8+bvPoibfcnP4cAAFUsON/GiT0p9qwqLKh7J2q4DaG8WnAgesfn/F2Hb+CkEZn3VA==} engines: {node: '>=22'} '@google-cloud/common@8.2.1': @@ -1530,8 +1527,8 @@ packages: resolution: {integrity: sha512-piWbtan+dw/cYzGopUsFH131mVx23osOeBAFSM78fgTzgRJjehgA5QnGGZiGYn+zgSNqdnf+7/ZzdHN1XkUAEg==} engines: {node: '>=12.0.0'} - '@grpc/grpc-js@1.14.4': - resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==} + '@grpc/grpc-js@1.14.7': + resolution: {integrity: sha512-W4W/fcBMGRVtG6dW2c7cw2mnpS3hvbBd+oQXjh2iYFTZDk1JJmcNqkM5KSaPubQpamlUUurrlb7G1s9NJeERmQ==} engines: {node: '>=12.10.0'} '@grpc/proto-loader@0.8.1': @@ -1569,144 +1566,144 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.35.3': - resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.35.3': - resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.3': - resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} engines: {node: '>=20.9.0'} os: [freebsd] - '@img/sharp-libvips-darwin-arm64@1.3.2': - resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.2': - resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.3.2': - resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.3.2': - resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.3.2': - resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-riscv64@1.3.2': - resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} cpu: [riscv64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.3.2': - resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.3.2': - resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': - resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.3.2': - resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.35.3': - resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.35.3': - resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.35.3': - resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] - '@img/sharp-linux-riscv64@0.35.3': - resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] - '@img/sharp-linux-s390x@0.35.3': - resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.35.3': - resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.35.3': - resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.35.3': - resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.35.3': - resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.3': - resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.3': - resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.35.3': - resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.35.3': - resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -1766,56 +1763,56 @@ packages: '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 - '@next/env@16.3.0': - resolution: {integrity: sha512-o9r1S0BNiNreHP9Vs+Qnqd9kviDkJh8xIACY7UFZSmiGbbQRzPBBosvHzAU4TULHOIuOj/18RSsyz2qrREmIFw==} + '@next/env@16.4.5': + resolution: {integrity: sha512-JwSPwpdkZL5VMl2jA/q+JI8MZdweAL3xmw5/tb9y/TLtmS0YXZnmYDzSihwk8xTj1OL+WM53gTqaj96yuLp6Xw==} - '@next/eslint-plugin-next@16.3.0': - resolution: {integrity: sha512-OqgJ8PN0d04KcPhDX/PTY5tJUJZxlbrt7O7FBsm4XE0XW2JDrKnDXsc9uo9WUimJGPoo2j+JRGhyXApC//mvbw==} + '@next/eslint-plugin-next@16.4.5': + resolution: {integrity: sha512-dNmc4y4En/34tWxaBFF2tG/R9GeiRwVPf7v8Az/Pqk8FcT4Uv0ePmrZKvWBL2nFy1QprPgJW+6XjfyV6jbRV3Q==} - '@next/swc-darwin-arm64@16.3.0': - resolution: {integrity: sha512-55hpqq18bEVAlxedlTt3tFqZmKg2nUXT1kn1G/BGEy0R13h3LwtwHPVzzjG6P4LLeOHE32PFDQUVaJEWvBEZBw==} + '@next/swc-darwin-arm64@16.4.5': + resolution: {integrity: sha512-W5TniBzWKASbNNwHZ/S/f1MxQUXU/LdjklVMWA0Ujbfl7SZnUzFls45hO0i/sF7Bx1jJBWp7WWmoQajXtX/O8A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.3.0': - resolution: {integrity: sha512-SOi96kSaF5T+0wW4koiM1bWzSPwjzTesC1p3df+FjdOi5LIQkBK/blxh7HdoKnNuI4PURF1OO7TZqtfnbWDSgw==} + '@next/swc-darwin-x64@16.4.5': + resolution: {integrity: sha512-uWq4qkHapBDS1oFltWS4YxTb9pLHAtLNcpbuK6SmRsebLYZqpBML/j/C0op+50JYGTpZgVL/DWNt/6oWlamrrw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.3.0': - resolution: {integrity: sha512-P0gZAoPMF4dyTRzhmkV4PrqVzSOB6t4mC1oI3c4dqijJ+OVEVx5clIXAKR4/uQpsqw2KKM/0D5tVumcR2r5blg==} + '@next/swc-linux-arm64-gnu@16.4.5': + resolution: {integrity: sha512-FdRcyOx5u5P51mlAqLB9EXdd8jZO5fsdHtk4BAQSkqOywCsbTNN/rmw+jO7pnmjNdAtu2AG46NzbCL2FPDrrgw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.3.0': - resolution: {integrity: sha512-tXXGKJw0m37O0eKJARVTX/TheKPhz0QFVtVVZXmOig+9YKLQOSP6hvf2pxv5DO7CLEJyTHx3Pg043CDQkv1G4Q==} + '@next/swc-linux-arm64-musl@16.4.5': + resolution: {integrity: sha512-cyvmmgtRXR8aGVT86wi9Py53IlfyCx7P2fZxuAXAoTummDnhKtUyjrMeXfapqp4qgD+gT5RqyGD/VfA16jEK7Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.3.0': - resolution: {integrity: sha512-pjGxK5EY7yWml78ALejFkWmgHsU7wbFQrISiugpH6FbUJhgEvw3xFZ/EBAtLl7QtL0WdQKiG9eWJ3mOKGTukHw==} + '@next/swc-linux-x64-gnu@16.4.5': + resolution: {integrity: sha512-hPc+Fl+N22DAvqZHq559NmamXboOX5Lu8I5Wl0DezfrhTk1Zz21NQeYoZDmNI/NjTcrdsVxM72Fzn2lI+Q+vAw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.3.0': - resolution: {integrity: sha512-sjo++Xx+lomlPs3HRsHWhVDyGG6ms1kGW5EtHLERdII8AyG1i+f6aq68xHREO6AEMlhjTNEWBSmfJfqm9orf7g==} + '@next/swc-linux-x64-musl@16.4.5': + resolution: {integrity: sha512-gbACYE8IP/jlprNTpiWXHb8N9CofOzpnEwH2SrIz1dUbiJf1FbDssDupCAPeP1Taxe8mgG20sGZamJI7KFOdZw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.3.0': - resolution: {integrity: sha512-C5JSgiO54wURdaxdEUIXqkz04uMqC9UmPX1gtDrV/5Tf1UowdWYI8uA5hfFbPolTlp0q4KZ60xlHePNibf0VIw==} + '@next/swc-win32-arm64-msvc@16.4.5': + resolution: {integrity: sha512-+8gj++ZMo8H20Xh2ijjNqyFk5LPLw0AC4Fs4Tq/G94rjjQXhOjN4jgSWsWKPFsyY26hfIeemJIwKtMxOsp5GBA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.3.0': - resolution: {integrity: sha512-fDOggsweNb5SSw0ZKVk6U+gxSyGFFlIBY/LBc1r8GUj4u/6t6oArL+Pmkg0MBnsgR+KkdsURilVH4F3GXUGepA==} + '@next/swc-win32-x64-msvc@16.4.5': + resolution: {integrity: sha512-qNZj66pYXnaWLiF9R9GUbfBdrCv7kC6TaZfIwB2KH5WPc/8pEM3nVOj8hpQhWJU8S94rlH5MPDcX15+RrgUhiw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1883,8 +1880,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.43.0': - resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} + '@opentelemetry/semantic-conventions@1.44.0': + resolution: {integrity: sha512-E3JkOlMBi3N8OGGkbl7tFyqk2MAS6/lySRZBK3Mu7AzjF1fT8oIChH9ChE48SUWzDn9Kdwta+G/p5LJ4xW4CLQ==} engines: {node: '>=14'} '@parcel/watcher-android-arm64@2.6.0': @@ -2598,6 +2595,7 @@ packages: '@smithy/core@3.36.0': resolution: {integrity: sha512-3hJiwG1F5t1SoXqp/yOCmYXziyx7ZVqF63S5W6YU201e/qwVaHPex+E/3vvyY9a3ORq/b+BydeNZpP2saQ9X2g==} engines: {node: '>=18.0.0'} + deprecated: Deprecated due to a bug in custom logger data redaction. See https://github.com/aws/aws-sdk-js-v3/issues/8393. '@smithy/credential-provider-imds@4.5.2': resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} @@ -2705,8 +2703,8 @@ packages: resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} engines: {node: '>=14'} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -2810,6 +2808,9 @@ packages: '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.10': + resolution: {integrity: sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -3091,8 +3092,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.18.0: - resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} engines: {node: '>=0.4.0'} hasBin: true @@ -3250,15 +3251,14 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - better-auth@1.7.4: - resolution: {integrity: sha512-rjL9g8b8UvdwK/ZSdawhqYzOm8YegurB6Fuzqtu/zFJamlFrPa+eEhP8Qj2vFWsMzHOk2mb/L4FRYh9pgaU7OA==} + better-auth@1.7.7: + resolution: {integrity: sha512-Zhgxi/c5ylmfB/sX+nwnBbsFf/j6k0L8DvL6CJXZSrXgxc5pPMC/e7j812zNd4L4B4ELmdURCPw3X8nAOUdcjw==} peerDependencies: '@lynx-js/react': '*' '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0 '@sveltejs/kit': ^2.0.0 '@tanstack/react-start': ^1.0.0 '@tanstack/solid-start': ^1.0.0 - better-sqlite3: ^12.0.0 drizzle-kit: '>=0.31.4 || >=1.0.0-beta.1' drizzle-orm: ^0.45.2 || >=1.0.0-rc.1 <2.0.0 mongodb: ^6.0.0 || ^7.0.0 @@ -3283,8 +3283,6 @@ packages: optional: true '@tanstack/solid-start': optional: true - better-sqlite3: - optional: true drizzle-kit: optional: true drizzle-orm: @@ -3332,14 +3330,14 @@ packages: bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - brace-expansion@1.1.18: - resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==} + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} - brace-expansion@2.1.4: - resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} - brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} braces@3.0.3: @@ -3448,6 +3446,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@15.0.0: + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + engines: {node: '>=22.12.0'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -3455,10 +3457,6 @@ packages: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -3507,6 +3505,9 @@ packages: css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + css-select@6.0.0: + resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} + css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -3523,6 +3524,10 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + css-what@7.0.0: + resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} + engines: {node: '>= 6'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -3785,8 +3790,8 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.4.16: - resolution: {integrity: sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ==} + dompurify@3.4.17: + resolution: {integrity: sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==} domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -3914,8 +3919,8 @@ packages: electron-to-chromium@1.5.411: resolution: {integrity: sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg==} - elkjs@0.9.3: - resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} + elkjs@0.12.1: + resolution: {integrity: sha512-IhMYKHE4S2vtx0QrhBP3lYOIKDeIiJXBrdq9MGYkuB8ZHqhrBnHNfSUndjXFewrX1CPK8chwq+2NA0K0hliYuw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3926,8 +3931,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.25.1: - resolution: {integrity: sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w==} + enhanced-resolve@5.26.1: + resolution: {integrity: sha512-PPnnSJq3azKOIewXR/uppBV/6h98dprBJ/WaQ9Qgj10xX1lJLNfg+XShpbsrSqcDSy2yYYyhfXM1wWhftqgyzw==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -3996,8 +4001,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.28.2: - resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true @@ -4013,8 +4018,8 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - eslint-config-next@16.3.0: - resolution: {integrity: sha512-lPrf1kHsMJEZqO0uXkNB400c5MGrhrTk3BNX7P0ol4gt61+iUlQfjy9TyIOEA9eOXrf+5+mYbT/JsY8+zqUByQ==} + eslint-config-next@16.4.5: + resolution: {integrity: sha512-mvh39zYLoNjpT5tVmf8xZu7i5lEOb5jN5cW6qD9OpeCp4GEEOeYta0Z2XyKiD0pYdlvm5/Td6Bi890maPRlIjg==} peerDependencies: eslint: '>=9.0.0' typescript: '>=3.3.1' @@ -4164,8 +4169,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.5: - resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} + fast-uri@3.1.9: + resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} @@ -4254,8 +4259,8 @@ packages: resolution: {integrity: sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==} engines: {node: '>=18'} - gaxios@8.0.0: - resolution: {integrity: sha512-UaktzloW/Hvb/VMNr/wxADpNOsfmnerPpZTthb9R0JzjVTYYcJ0N0o5HOMzKV3XplpcFMSoLj52h9IK/wRs9Gw==} + gaxios@8.3.2: + resolution: {integrity: sha512-7VsaTExxjbDv2ZK1YuC4jbgfN33NI/6/7zdYWUHB6mcg51v4tCBZe+xzjqPzOpVzI+C5k1Kk9OQ80WVrXAHOqw==} engines: {node: '>=22'} gcp-metadata@8.1.4: @@ -4266,6 +4271,10 @@ packages: resolution: {integrity: sha512-2YYnIlHaKBGT2IPg3G2M57hia9Galz15zsEOvw9T3oRf0lSn6KN6VcHQLqby7x8ksYKnjXvp3rp2KJyLCN6zfQ==} engines: {node: '>=22'} + gcp-metadata@9.2.3: + resolution: {integrity: sha512-d2OI/dJJ3IIaeOz3c9jvr5j5G5beI5fQzbgilNiWhDKGwvJN9o10Qub70NkbF6PNJSUoNKZ77JKYGz0Fs4+9Lg==} + engines: {node: '>=22'} + generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -4353,6 +4362,10 @@ packages: resolution: {integrity: sha512-vzpgPutxrghPsnjrjpzLX2bdv8IOL719Rh0oEjGnQu8YCIbnbMuTTQ5zU9LcKvLdOPgCxBwppbvnhgW90Qna5Q==} engines: {node: '>=22'} + google-auth-library@11.3.3: + resolution: {integrity: sha512-VxjQ5+zHeR4RcRi001qnT/6KRKy2CIjQ5I7/wFErvRqr0+setlRqjDOAzvjzWEzyvsWswZE6WMQJtS8OpeTqAQ==} + engines: {node: '>=22'} + google-gax@5.0.8: resolution: {integrity: sha512-M4vpZcXQIC1gqIVGQ7eaU3jXQA6zecStyTXu514TYfThlgSurYJOxHZo9fzU6hAgwPWvuEynAVHWyaIk80VeEA==} engines: {node: '>=18'} @@ -4365,6 +4378,10 @@ packages: resolution: {integrity: sha512-HMhaQghlOTvbcb3c4T5jmmOMtG3JUF1iOQMezaJXL86CDS+Tm2vHd0IeLFRAx3+ewd+bo9E1HFHoy17X5aJa9A==} engines: {node: '>=22'} + google-logging-utils@2.1.1: + resolution: {integrity: sha512-jYZOPMaRGS3Hldyudft6bdibW9ZWUKBkMdB2TeSETkV1c2T9TR4zxEWZETHv9xJ8KHr2hgkNLaSa8kcEcwlpeQ==} + engines: {node: '>=22'} + googleapis-common@9.0.4: resolution: {integrity: sha512-fcI5bm1gN+9MNb+gvANRPFsxaJ07h/ZYvKCh6H3RoDJBUTwp5ySaCpCqnmX8gnGJUD/9bKmkacivRJT1g+xXyg==} engines: {node: '>=22'} @@ -4689,14 +4706,14 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jose@6.2.12: - resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} + jose@6.2.15: + resolution: {integrity: sha512-qdF6ewcZARtef63fJ9AKKZPgxw+t7USH6HLXYcD4CrdmSvDp79rpJSPFEKc90QBeGS63u1KQtN2MZQHC0yDIzA==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.3.1: - resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true jsesc@3.1.0: @@ -4744,8 +4761,8 @@ packages: jws@4.0.1: resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + katex@0.18.10: + resolution: {integrity: sha512-/B6p9eY9DX7aHBfpkHdpirDTZ5QH9xTwL9y837PWuzuv41O5jFdqNQwVQEQsimY0/iVNkwBY4+4hMfhQ7d4Dbw==} hasBin: true keyv@4.5.4: @@ -4764,8 +4781,8 @@ packages: known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} - kysely@0.29.5: - resolution: {integrity: sha512-ooa+eSbBNPTo3MycPEuW5jdrxQdQwdtB3LC3h43FiXQbIry5tR0C5lDG7eealK0E4D7XjrnOP5DIUg/LyjRMYQ==} + kysely@0.29.8: + resolution: {integrity: sha512-ixHU1H4R+LeC5Y0nAhYgiGRFpFRHm2DXKEmJSs/vbes9sKXyssyzxzE/NYiOJX5fY0BL28biCre3GqlCqUwyXQ==} engines: {node: '>=22.0.0'} language-subtag-registry@0.3.23: @@ -4916,8 +4933,8 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - mermaid@12.1.0: - resolution: {integrity: sha512-wlVCp+8eTupfCeeFvoZNNiTuHrvag0P2jz/ILgb/f/6jkVokUefOcujefi8qUe/j2asHiSePncVsz/xzzA80LQ==} + mermaid@12.3.0: + resolution: {integrity: sha512-4113TdXeLAsMruJ79OpFL5O8+LTFXhQqZ260rF3tgUmiNAtRm4Uume04I+65PRN6yZ69J4f8rVYWW8zRk65UYw==} engines: {node: '>=22.12.0'} micromark-core-commonmark@2.0.3: @@ -5026,8 +5043,8 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minimizer-webpack-plugin@5.11.0: - resolution: {integrity: sha512-2JVy2DY+iBtfRyrQB5cRM7lfurnJIi6jFTgLip/7FxDu0WxswrIRzr4fBySQ0X7HqM7BcwE28zm+HfQ5V6y4Cg==} + minimizer-webpack-plugin@5.14.0: + resolution: {integrity: sha512-PnvQXCJxhgH2ZVVO7Lj0sM6q8L1XGxnUvd8wVQIRwi8khMCLq5wg0Ngxe44bcuZL2FSN81RdVE7YHEVzTKsQDQ==} engines: {node: '>= 10.13.0'} peerDependencies: '@minify-html/node': '*' @@ -5096,8 +5113,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanostores@1.5.3: - resolution: {integrity: sha512-rQLB6eV4f2AW/n3L0JmwCROpaisYy9EDEADvEFSd1C/qG8hB6O5TPlh9A791JRbJr4CnMQBzptDcvD9OR1+6WA==} + nanostores@1.5.5: + resolution: {integrity: sha512-FixtE239Gl6Gz0ZGCK0WzvNwObNCIwVT+L7zQcf1QgcA6AzBxblzqe9OT88qiucNkhsjSOqiogSaF5jVbJiqSA==} engines: {node: ^20.0.0 || >=22.0.0} napi-postinstall@0.3.4: @@ -5108,8 +5125,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@16.3.0: - resolution: {integrity: sha512-NEdGOzH+08eTXMUp9UYkA99Nhi5N6Thrhc1jgFOQgfgnGK/dA2hRwBpXep+exdFQrnwlRf/3Wixyp8lLBUpE2A==} + next@16.4.5: + resolution: {integrity: sha512-rmdfsWosjTUO+6R0S3KokoIk6Kj0sWDRTe86tZHdqaWH/oXq/udX3/4xb+YQi7aWbyEFoglDU54kaifLCdXbBw==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -5161,8 +5178,8 @@ packages: resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} engines: {node: '>=18'} - nodemailer@10.0.10: - resolution: {integrity: sha512-He9XskOFms62SyAKkLu8CcGcYHAo+BSHSsORI8s4PJH8qZgZY4L4lDfvyN0twvmbpyG+eGrxpyBlskj9d9rJ8A==} + nodemailer@10.1.4: + resolution: {integrity: sha512-EsMnOR2oRv4LGnWvHgcVYkkQ4O2j8KO4WTw6l+PHR4sOXo8SAgIraxtEnGDmotjbhv0hDsu3IuDIFZm8iXwO4A==} engines: {node: '>=20.0.0'} normalize-path@3.0.0: @@ -5227,8 +5244,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-throttle@8.1.0: - resolution: {integrity: sha512-c1wmXavsHZIC4g1OLhOsafK6jZSAeMo0Ap3yivj59PUcCkpacy5YgWdgIp/dB4vp1JZrfBSsPCR0YuADB+ENLQ==} + p-throttle@8.1.1: + resolution: {integrity: sha512-xxWFBUyvbrQ10XC3VAr0SoXoOCKXqCX/yvbFIkBE98upphHFwjIKR6AId13ZH3jngFfF6yK43QTOtrngYAmFMA==} engines: {node: '>=20'} package-json-from-dist@1.0.1: @@ -5357,8 +5374,8 @@ packages: peerDependencies: postcss: ^8.4.29 - postcss-selector-parser@7.1.5: - resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==} + postcss-selector-parser@7.1.7: + resolution: {integrity: sha512-wWBro/7hJ40ye6TPiXkUQoXKCw23xSsjriUvqd7Uls9Sso3oLiON1flc18cCdnjgCRXw4fBVqUJJeOdDTh3P3g==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -5426,8 +5443,8 @@ packages: resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} engines: {node: '>=20'} - qs@6.16.0: - resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} + qs@6.17.1: + resolution: {integrity: sha512-jd3COkaKVgGUW+ZHyEHCG9bH7RoaaiWuMgzsIBjUMVjLAaWGWpoUAVwljVvBHxRu/2qEHnadLGZrkutYUBPQJA==} engines: {node: '>=0.6'} queue-microtask@1.2.3: @@ -5631,9 +5648,15 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - schema-utils@4.5.0: - resolution: {integrity: sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA==} + schema-utils@4.8.2: + resolution: {integrity: sha512-iKlak3OFHF0dgD1nZRvgWQzG+P1TiEcMnLSBtIVBg937z5Jd6WE1c3SyZkXIyCfPHowL35yMsVvIjotm1c1qiw==} engines: {node: '>= 10.13.0'} + hasBin: true + peerDependencies: + webpack: ^5.112.0 + peerDependenciesMeta: + webpack: + optional: true semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} @@ -5644,8 +5667,8 @@ packages: engines: {node: '>=10'} hasBin: true - set-cookie-parser@3.1.2: - resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} + set-cookie-parser@3.2.2: + resolution: {integrity: sha512-df017qMeYuXo4emL09nlyMpi2i2MtEIKS/6jtXH3vmjFgIRsOi852/5RmhJ4EkvN3ZF8jXadM/EkTPXgc8yPIw==} set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} @@ -5659,8 +5682,8 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - sharp@0.35.3: - resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} engines: {node: '>=20.9.0'} peerDependencies: '@types/node': '*' @@ -5707,8 +5730,8 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} engines: {node: '>=0.10.0'} source-map-support@0.5.21: @@ -5903,13 +5926,13 @@ packages: svg-tags@1.0.0: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} - svgo@3.3.4: - resolution: {integrity: sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==} + svgo@3.3.5: + resolution: {integrity: sha512-8SQMzdrvWaD8deUmrnYB+ASyxBVgWUOilg+A75nE/76WdLpj6LopCwiAVvkzkcqy/9b7t2Mg7faFLjg0ZRcZ3w==} engines: {node: '>=14.0.0'} hasBin: true - svgo@4.0.2: - resolution: {integrity: sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==} + svgo@4.1.0: + resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} engines: {node: '>=16'} hasBin: true @@ -6101,18 +6124,14 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@14.0.2: - resolution: {integrity: sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==} - hasBin: true - vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - watchpack@2.5.2: - resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} + watchpack@2.6.3: + resolution: {integrity: sha512-s3NZy0QpTY9qGqzc0wtz4qI28z3+5qKbbAec4MV4G9N3CfgZ9wgvQRr0cl2jh/tqkWG1NWHkmVMHZpCamev5Wg==} engines: {node: '>=10.13.0'} web-streams-polyfill@3.3.3: @@ -6122,8 +6141,8 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webpack-sources@3.5.1: - resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} + webpack-sources@3.7.0: + resolution: {integrity: sha512-PdUhbCycwEgiS1hB/FBDksWKg/ashAIuJKqwdbPn8/dFVgU5LxQiBvG0js6VRRIzPujMrStYCczywp82P0KWng==} engines: {node: '>=10.13.0'} webpack@5.111.1: @@ -6212,11 +6231,8 @@ packages: peerDependencies: zod: ^3.25.0 || ^4.0.0 - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - - zod@4.6.5: - resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + zod@4.6.7: + resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -7140,64 +7156,64 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3)': + '@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5)': dependencies: '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 - '@opentelemetry/semantic-conventions': 1.43.0 + '@better-fetch/fetch': 1.3.2 + '@opentelemetry/semantic-conventions': 1.44.0 '@standard-schema/spec': 1.1.0 - better-call: 1.4.0(zod@4.6.5) - jose: 6.2.12 - kysely: 0.29.5 - nanostores: 1.5.3 - zod: 4.6.5 + better-call: 1.4.0(zod@4.6.7) + jose: 6.2.15 + kysely: 0.29.8 + nanostores: 1.5.5 + zod: 4.6.7 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@better-auth/drizzle-adapter@1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0))': + '@better-auth/drizzle-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0))': dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) '@better-auth/utils': 0.4.2 optionalDependencies: - drizzle-orm: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0) + drizzle-orm: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0) - '@better-auth/kysely-adapter@1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(kysely@0.29.5)': + '@better-auth/kysely-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(kysely@0.29.8)': dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) '@better-auth/utils': 0.4.2 optionalDependencies: - kysely: 0.29.5 + kysely: 0.29.8 - '@better-auth/memory-adapter@1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + '@better-auth/memory-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) '@better-auth/utils': 0.4.2 - '@better-auth/mongo-adapter@1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + '@better-auth/mongo-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) '@better-auth/utils': 0.4.2 - '@better-auth/prisma-adapter@1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)': + '@better-auth/prisma-adapter@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)': dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) '@better-auth/utils': 0.4.2 - '@better-auth/telemetry@1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)': + '@better-auth/telemetry@1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)': dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 + '@better-fetch/fetch': 1.3.2 '@better-auth/utils@0.4.2': dependencies: '@noble/hashes': 2.4.0 - '@better-auth/utils@0.5.0': + '@better-auth/utils@0.5.1': dependencies: '@noble/hashes': 2.4.0 - '@better-fetch/fetch@1.3.1': {} + '@better-fetch/fetch@1.3.2': {} '@braintree/sanitize-url@7.1.2': {} @@ -7248,13 +7264,13 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/selector-resolve-nested@4.0.1(postcss-selector-parser@7.1.5)': + '@csstools/selector-resolve-nested@4.0.1(postcss-selector-parser@7.1.7)': dependencies: - postcss-selector-parser: 7.1.5 + postcss-selector-parser: 7.1.7 - '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.5)': + '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.7)': dependencies: - postcss-selector-parser: 7.1.5 + postcss-selector-parser: 7.1.7 '@developmentfinance/client@file:vendor/developmentfinance-client-0.1.0.tgz(apache-arrow@21.2.0)': dependencies: @@ -7299,7 +7315,7 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.28.2': + '@esbuild/aix-ppc64@0.28.1': optional: true '@esbuild/android-arm64@0.18.20': @@ -7308,7 +7324,7 @@ snapshots: '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.28.2': + '@esbuild/android-arm64@0.28.1': optional: true '@esbuild/android-arm@0.18.20': @@ -7317,7 +7333,7 @@ snapshots: '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.28.2': + '@esbuild/android-arm@0.28.1': optional: true '@esbuild/android-x64@0.18.20': @@ -7326,7 +7342,7 @@ snapshots: '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.28.2': + '@esbuild/android-x64@0.28.1': optional: true '@esbuild/darwin-arm64@0.18.20': @@ -7335,7 +7351,7 @@ snapshots: '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.28.2': + '@esbuild/darwin-arm64@0.28.1': optional: true '@esbuild/darwin-x64@0.18.20': @@ -7344,7 +7360,7 @@ snapshots: '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.28.2': + '@esbuild/darwin-x64@0.28.1': optional: true '@esbuild/freebsd-arm64@0.18.20': @@ -7353,7 +7369,7 @@ snapshots: '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.28.2': + '@esbuild/freebsd-arm64@0.28.1': optional: true '@esbuild/freebsd-x64@0.18.20': @@ -7362,7 +7378,7 @@ snapshots: '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.28.2': + '@esbuild/freebsd-x64@0.28.1': optional: true '@esbuild/linux-arm64@0.18.20': @@ -7371,7 +7387,7 @@ snapshots: '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.28.2': + '@esbuild/linux-arm64@0.28.1': optional: true '@esbuild/linux-arm@0.18.20': @@ -7380,7 +7396,7 @@ snapshots: '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.28.2': + '@esbuild/linux-arm@0.28.1': optional: true '@esbuild/linux-ia32@0.18.20': @@ -7389,7 +7405,7 @@ snapshots: '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.28.2': + '@esbuild/linux-ia32@0.28.1': optional: true '@esbuild/linux-loong64@0.18.20': @@ -7398,7 +7414,7 @@ snapshots: '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.28.2': + '@esbuild/linux-loong64@0.28.1': optional: true '@esbuild/linux-mips64el@0.18.20': @@ -7407,7 +7423,7 @@ snapshots: '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.28.2': + '@esbuild/linux-mips64el@0.28.1': optional: true '@esbuild/linux-ppc64@0.18.20': @@ -7416,7 +7432,7 @@ snapshots: '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.28.2': + '@esbuild/linux-ppc64@0.28.1': optional: true '@esbuild/linux-riscv64@0.18.20': @@ -7425,7 +7441,7 @@ snapshots: '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.28.2': + '@esbuild/linux-riscv64@0.28.1': optional: true '@esbuild/linux-s390x@0.18.20': @@ -7434,7 +7450,7 @@ snapshots: '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.28.2': + '@esbuild/linux-s390x@0.28.1': optional: true '@esbuild/linux-x64@0.18.20': @@ -7443,13 +7459,13 @@ snapshots: '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.28.2': + '@esbuild/linux-x64@0.28.1': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.28.2': + '@esbuild/netbsd-arm64@0.28.1': optional: true '@esbuild/netbsd-x64@0.18.20': @@ -7458,13 +7474,13 @@ snapshots: '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.28.2': + '@esbuild/netbsd-x64@0.28.1': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.28.2': + '@esbuild/openbsd-arm64@0.28.1': optional: true '@esbuild/openbsd-x64@0.18.20': @@ -7473,13 +7489,13 @@ snapshots: '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.28.2': + '@esbuild/openbsd-x64@0.28.1': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.28.2': + '@esbuild/openharmony-arm64@0.28.1': optional: true '@esbuild/sunos-x64@0.18.20': @@ -7488,7 +7504,7 @@ snapshots: '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.28.2': + '@esbuild/sunos-x64@0.28.1': optional: true '@esbuild/win32-arm64@0.18.20': @@ -7497,7 +7513,7 @@ snapshots: '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.28.2': + '@esbuild/win32-arm64@0.28.1': optional: true '@esbuild/win32-ia32@0.18.20': @@ -7506,7 +7522,7 @@ snapshots: '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.28.2': + '@esbuild/win32-ia32@0.28.1': optional: true '@esbuild/win32-x64@0.18.20': @@ -7515,7 +7531,7 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.28.2': + '@esbuild/win32-x64@0.28.1': optional: true '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': @@ -7554,7 +7570,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.3.1 + js-yaml: 4.3.2 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -7606,14 +7622,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@google-cloud/cloud-sql-connector@1.12.0': + '@google-cloud/cloud-sql-connector@1.12.1': dependencies: '@google-cloud/sql': 0.25.0 '@googleapis/sqladmin': 38.1.1 - '@grpc/grpc-js': 1.14.4 - gaxios: 8.0.0 - google-auth-library: 11.0.2 - p-throttle: 8.1.0 + '@grpc/grpc-js': 1.14.7 + gaxios: 8.3.2 + google-auth-library: 11.3.3 + p-throttle: 8.1.1 transitivePeerDependencies: - supports-color @@ -7653,7 +7669,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@grpc/grpc-js@1.14.4': + '@grpc/grpc-js@1.14.7': dependencies: '@grpc/proto-loader': 0.8.1 '@js-sdsl/ordered-map': 4.4.2 @@ -7692,108 +7708,108 @@ snapshots: '@img/colour@1.1.0': optional: true - '@img/sharp-darwin-arm64@0.35.3': + '@img/sharp-darwin-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-arm64': 1.3.4 optional: true - '@img/sharp-darwin-x64@0.35.3': + '@img/sharp-darwin-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.4 optional: true - '@img/sharp-freebsd-wasm32@0.35.3': + '@img/sharp-freebsd-wasm32@0.35.5': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.5 optional: true - '@img/sharp-libvips-darwin-arm64@1.3.2': + '@img/sharp-libvips-darwin-arm64@1.3.4': optional: true - '@img/sharp-libvips-darwin-x64@1.3.2': + '@img/sharp-libvips-darwin-x64@1.3.4': optional: true - '@img/sharp-libvips-linux-arm64@1.3.2': + '@img/sharp-libvips-linux-arm64@1.3.4': optional: true - '@img/sharp-libvips-linux-arm@1.3.2': + '@img/sharp-libvips-linux-arm@1.3.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.2': + '@img/sharp-libvips-linux-ppc64@1.3.4': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.2': + '@img/sharp-libvips-linux-riscv64@1.3.4': optional: true - '@img/sharp-libvips-linux-s390x@1.3.2': + '@img/sharp-libvips-linux-s390x@1.3.4': optional: true - '@img/sharp-libvips-linux-x64@1.3.2': + '@img/sharp-libvips-linux-x64@1.3.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.2': + '@img/sharp-libvips-linuxmusl-x64@1.3.4': optional: true - '@img/sharp-linux-arm64@0.35.3': + '@img/sharp-linux-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.4 optional: true - '@img/sharp-linux-arm@0.35.3': + '@img/sharp-linux-arm@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.4 optional: true - '@img/sharp-linux-ppc64@0.35.3': + '@img/sharp-linux-ppc64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.4 optional: true - '@img/sharp-linux-riscv64@0.35.3': + '@img/sharp-linux-riscv64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.4 optional: true - '@img/sharp-linux-s390x@0.35.3': + '@img/sharp-linux-s390x@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.4 optional: true - '@img/sharp-linux-x64@0.35.3': + '@img/sharp-linux-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.4 optional: true - '@img/sharp-linuxmusl-arm64@0.35.3': + '@img/sharp-linuxmusl-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 optional: true - '@img/sharp-linuxmusl-x64@0.35.3': + '@img/sharp-linuxmusl-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 optional: true - '@img/sharp-wasm32@0.35.3': + '@img/sharp-wasm32@0.35.5': dependencies: '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.3': + '@img/sharp-webcontainers-wasm32@0.35.5': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.5 optional: true - '@img/sharp-win32-arm64@0.35.3': + '@img/sharp-win32-arm64@0.35.5': optional: true - '@img/sharp-win32-ia32@0.35.3': + '@img/sharp-win32-ia32@0.35.5': optional: true - '@img/sharp-win32-x64@0.35.3': + '@img/sharp-win32-x64@0.35.5': optional: true '@isaacs/cliui@8.0.2': @@ -7855,37 +7871,37 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@next/env@16.3.0': {} + '@next/env@16.4.5': {} - '@next/eslint-plugin-next@16.3.0(eslint@9.39.5)': + '@next/eslint-plugin-next@16.4.5(eslint@9.39.5)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5) fast-glob: 3.3.1 transitivePeerDependencies: - eslint - '@next/swc-darwin-arm64@16.3.0': + '@next/swc-darwin-arm64@16.4.5': optional: true - '@next/swc-darwin-x64@16.3.0': + '@next/swc-darwin-x64@16.4.5': optional: true - '@next/swc-linux-arm64-gnu@16.3.0': + '@next/swc-linux-arm64-gnu@16.4.5': optional: true - '@next/swc-linux-arm64-musl@16.3.0': + '@next/swc-linux-arm64-musl@16.4.5': optional: true - '@next/swc-linux-x64-gnu@16.3.0': + '@next/swc-linux-x64-gnu@16.4.5': optional: true - '@next/swc-linux-x64-musl@16.3.0': + '@next/swc-linux-x64-musl@16.4.5': optional: true - '@next/swc-win32-arm64-msvc@16.3.0': + '@next/swc-win32-arm64-msvc@16.4.5': optional: true - '@next/swc-win32-x64-msvc@16.3.0': + '@next/swc-win32-x64-msvc@16.4.5': optional: true '@noble/ciphers@2.4.0': {} @@ -7915,7 +7931,7 @@ snapshots: '@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/semantic-conventions': 1.43.0 + '@opentelemetry/semantic-conventions': 1.44.0 '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': dependencies: @@ -7930,7 +7946,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.43.0 + '@opentelemetry/semantic-conventions': 1.44.0 '@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)': dependencies: @@ -7938,16 +7954,16 @@ snapshots: '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace': 2.11.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.43.0 + '@opentelemetry/semantic-conventions': 1.44.0 '@opentelemetry/sdk-trace@2.11.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.43.0 + '@opentelemetry/semantic-conventions': 1.44.0 - '@opentelemetry/semantic-conventions@1.43.0': {} + '@opentelemetry/semantic-conventions@1.44.0': {} '@parcel/watcher-android-arm64@2.6.0': optional: true @@ -8442,7 +8458,7 @@ snapshots: - encoding - supports-color - '@sentry/bundler-plugins@10.75.2(rollup@4.63.4)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2))': + '@sentry/bundler-plugins@10.75.2(rollup@4.63.4)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0))': dependencies: '@babel/core': 7.29.7 '@sentry/cli': 2.58.6 @@ -8453,7 +8469,7 @@ snapshots: magic-string: 0.30.21 optionalDependencies: rollup: 4.63.4 - webpack: 5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2) + webpack: 5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0) transitivePeerDependencies: - encoding - supports-color @@ -8512,7 +8528,7 @@ snapshots: dependencies: '@sentry/core': 10.75.2 - '@sentry/nextjs@10.75.2(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))(next@16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(react@19.2.8)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2))': + '@sentry/nextjs@10.75.2(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))(next@16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(react@19.2.8)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0))': dependencies: '@opentelemetry/api': 1.9.1 '@rollup/plugin-commonjs': 28.0.1(rollup@4.63.4) @@ -8525,8 +8541,8 @@ snapshots: '@sentry/react': 10.75.2(react@19.2.8) '@sentry/server-utils': 10.75.2 '@sentry/vercel-edge': 10.75.2 - '@sentry/webpack-plugin': 5.4.0(rollup@4.63.4)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)) - next: 16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0) + '@sentry/webpack-plugin': 5.4.0(rollup@4.63.4)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)) + next: 16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0) rollup: 4.63.4 stacktrace-parser: 0.1.11 transitivePeerDependencies: @@ -8601,10 +8617,10 @@ snapshots: '@opentelemetry/api': 1.9.1 '@sentry/core': 10.75.2 - '@sentry/webpack-plugin@5.4.0(rollup@4.63.4)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2))': + '@sentry/webpack-plugin@5.4.0(rollup@4.63.4)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0))': dependencies: - '@sentry/bundler-plugins': 10.75.2(rollup@4.63.4)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)) - webpack: 5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2) + '@sentry/bundler-plugins': 10.75.2(rollup@4.63.4)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)) + webpack: 5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0) transitivePeerDependencies: - encoding - rollup @@ -8653,7 +8669,7 @@ snapshots: '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) postcss: 8.5.26 - postcss-selector-parser: 7.1.5 + postcss-selector-parser: 7.1.7 postcss-value-parser: 4.2.0 style-search: 0.1.0 stylelint: 17.14.1(typescript@5.9.3) @@ -8733,7 +8749,7 @@ snapshots: '@svgr/core': 8.1.0(typescript@5.9.3) cosmiconfig: 8.3.6(typescript@5.9.3) deepmerge: 4.3.1 - svgo: 3.3.4 + svgo: 3.3.5 transitivePeerDependencies: - typescript @@ -8751,7 +8767,7 @@ snapshots: - supports-color - typescript - '@swc/helpers@0.5.15': + '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -8885,6 +8901,8 @@ snapshots: dependencies: '@types/estree': 1.0.9 + '@types/estree@1.0.10': {} + '@types/estree@1.0.9': {} '@types/geojson@7946.0.16': {} @@ -9184,11 +9202,11 @@ snapshots: '@xtuc/long@4.2.2': {} - acorn-jsx@5.3.2(acorn@8.18.0): + acorn-jsx@5.3.2(acorn@8.19.0): dependencies: - acorn: 8.18.0 + acorn: 8.19.0 - acorn@8.18.0: {} + acorn@8.19.0: {} agent-base@6.0.2: dependencies: @@ -9217,7 +9235,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.5 + fast-uri: 3.1.9 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -9365,44 +9383,43 @@ snapshots: baseline-browser-mapping@2.11.15: {} - better-auth@1.7.4(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0))(next@16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + better-auth@1.7.7(@opentelemetry/api@1.9.1)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0))(next@16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0))(pg@8.23.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@better-auth/core': 1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3) - '@better-auth/drizzle-adapter': 1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0)) - '@better-auth/kysely-adapter': 1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(kysely@0.29.5) - '@better-auth/memory-adapter': 1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2) - '@better-auth/mongo-adapter': 1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2) - '@better-auth/prisma-adapter': 1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2) - '@better-auth/telemetry': 1.7.4(@better-auth/core@1.7.4(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.5))(jose@6.2.12)(kysely@0.29.5)(nanostores@1.5.3))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.1) + '@better-auth/core': 1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5) + '@better-auth/drizzle-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0)) + '@better-auth/kysely-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(kysely@0.29.8) + '@better-auth/memory-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/mongo-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/prisma-adapter': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2) + '@better-auth/telemetry': 1.7.7(@better-auth/core@1.7.7(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.1)(better-call@1.4.0(zod@4.6.7))(jose@6.2.15)(kysely@0.29.8)(nanostores@1.5.5))(@better-auth/utils@0.4.2)(@better-fetch/fetch@1.3.2) '@better-auth/utils': 0.4.2 - '@better-fetch/fetch': 1.3.1 + '@better-fetch/fetch': 1.3.2 '@noble/ciphers': 2.4.0 '@noble/hashes': 2.4.0 - better-call: 1.4.0(zod@4.6.5) + better-call: 1.4.0(zod@4.6.7) defu: 6.1.7 - jose: 6.2.12 - kysely: 0.29.5 - nanostores: 1.5.3 - zod: 4.6.5 + jose: 6.2.15 + kysely: 0.29.8 + nanostores: 1.5.5 + zod: 4.6.7 optionalDependencies: drizzle-kit: 0.31.10 - drizzle-orm: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0) - next: 16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0) + drizzle-orm: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0) + next: 16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0) pg: 8.23.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) transitivePeerDependencies: - - '@cloudflare/workers-types' - '@opentelemetry/api' - better-call@1.4.0(zod@4.6.5): + better-call@1.4.0(zod@4.6.7): dependencies: - '@better-auth/utils': 0.5.0 - '@better-fetch/fetch': 1.3.1 + '@better-auth/utils': 0.5.1 + '@better-fetch/fetch': 1.3.2 rou3: 0.9.2 - set-cookie-parser: 3.1.2 + set-cookie-parser: 3.2.2 optionalDependencies: - zod: 4.6.5 + zod: 4.6.7 big.js@7.0.1: {} @@ -9412,16 +9429,16 @@ snapshots: bowser@2.14.1: {} - brace-expansion@1.1.18: + brace-expansion@1.1.21: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.4: + brace-expansion@2.1.7: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.9: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -9529,12 +9546,12 @@ snapshots: commander@11.1.0: {} + commander@15.0.0: {} + commander@2.20.3: {} commander@7.2.0: {} - commander@8.3.0: {} - commondir@1.0.1: {} concat-map@0.0.1: {} @@ -9556,7 +9573,7 @@ snapshots: cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.1 - js-yaml: 4.3.1 + js-yaml: 4.3.2 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: @@ -9566,7 +9583,7 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.3.1 + js-yaml: 4.3.2 parse-json: 5.2.0 optionalDependencies: typescript: 5.9.3 @@ -9587,23 +9604,33 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-select@6.0.0: + dependencies: + boolbase: 1.0.0 + css-what: 7.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css-tree@2.3.1: dependencies: mdn-data: 2.0.30 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css-tree@3.2.1: dependencies: mdn-data: 2.27.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 css-what@6.2.2: {} + css-what@7.0.0: {} + cssesc@3.0.0: {} csso@5.0.5: @@ -9883,7 +9910,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.4.16: + dompurify@3.4.17: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -9909,11 +9936,11 @@ snapshots: esbuild: 0.25.12 tsx: 4.23.13 - drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.5)(pg@8.23.0): + drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.5)(kysely@0.29.8)(pg@8.23.0): optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/pg': 8.15.5 - kysely: 0.29.5 + kysely: 0.29.8 pg: 8.23.0 dunder-proto@1.0.1: @@ -9937,7 +9964,7 @@ snapshots: electron-to-chromium@1.5.411: {} - elkjs@0.9.3: {} + elkjs@0.12.1: {} emoji-regex@8.0.0: {} @@ -9947,7 +9974,7 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.25.1: + enhanced-resolve@5.26.1: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -10131,34 +10158,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.28.2: + esbuild@0.28.1: 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 + '@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 escalade@3.2.0: {} @@ -10166,9 +10193,9 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@16.3.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3): + eslint-config-next@16.4.5(@typescript-eslint/parser@8.67.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3): dependencies: - '@next/eslint-plugin-next': 16.3.0(eslint@9.39.5) + '@next/eslint-plugin-next': 16.4.5(eslint@9.39.5) eslint: 9.39.5 eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.67.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5))(eslint@9.39.5) @@ -10274,8 +10301,8 @@ snapshots: '@babel/parser': 7.29.8 eslint: 9.39.5 hermes-parser: 0.25.1 - zod: 4.4.3 - zod-validation-error: 4.0.2(zod@4.4.3) + zod: 4.6.7 + zod-validation-error: 4.0.2(zod@4.6.7) transitivePeerDependencies: - supports-color @@ -10325,7 +10352,7 @@ snapshots: '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.9 + '@types/estree': 1.0.10 ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -10353,8 +10380,8 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.18.0 - acorn-jsx: 5.3.2(acorn@8.18.0) + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) eslint-visitor-keys: 4.2.1 esquery@1.7.0: @@ -10399,7 +10426,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.5: {} + fast-uri@3.1.9: {} fastest-levenshtein@1.0.16: {} @@ -10499,7 +10526,7 @@ snapshots: transitivePeerDependencies: - supports-color - gaxios@8.0.0: + gaxios@8.3.2: dependencies: extend: 3.0.2 https-proxy-agent: 7.0.6 @@ -10523,6 +10550,14 @@ snapshots: transitivePeerDependencies: - supports-color + gcp-metadata@9.2.3: + dependencies: + gaxios: 8.3.2 + google-logging-utils: 2.1.1 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + generator-function@2.0.1: {} gensync@1.0.0-beta.2: {} @@ -10638,9 +10673,20 @@ snapshots: transitivePeerDependencies: - supports-color + google-auth-library@11.3.3: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 8.3.2 + gcp-metadata: 9.2.3 + google-logging-utils: 2.1.1 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + google-gax@5.0.8: dependencies: - '@grpc/grpc-js': 1.14.4 + '@grpc/grpc-js': 1.14.7 '@grpc/proto-loader': 0.8.1 duplexify: 4.1.3 google-auth-library: 10.5.0 @@ -10658,13 +10704,15 @@ snapshots: google-logging-utils@2.0.1: {} + google-logging-utils@2.1.1: {} + googleapis-common@9.0.4: dependencies: extend: 3.0.2 gaxios: 7.3.1 - google-auth-library: 11.0.2 + google-auth-library: 11.3.3 google-logging-utils: 2.0.1 - qs: 6.16.0 + qs: 6.17.1 url-template: 2.0.8 transitivePeerDependencies: - supports-color @@ -11001,11 +11049,11 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jose@6.2.12: {} + jose@6.2.15: {} js-tokens@4.0.0: {} - js-yaml@4.3.1: + js-yaml@4.3.2: dependencies: argparse: 2.0.1 @@ -11051,9 +11099,9 @@ snapshots: jwa: 2.0.1 safe-buffer: 5.2.1 - katex@0.16.47: + katex@0.18.10: dependencies: - commander: 8.3.0 + commander: 15.0.0 keyv@4.5.4: dependencies: @@ -11069,7 +11117,7 @@ snapshots: known-css-properties@0.37.0: {} - kysely@0.29.5: {} + kysely@0.29.8: {} language-subtag-registry@0.3.23: {} @@ -11305,7 +11353,7 @@ snapshots: merge2@1.4.1: {} - mermaid@12.1.0: + mermaid@12.3.0: dependencies: '@braintree/sanitize-url': 7.1.2 '@iconify/utils': 3.1.7 @@ -11320,16 +11368,15 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.14 dayjs: 1.11.23 - dompurify: 3.4.16 - elkjs: 0.9.3 + dompurify: 3.4.17 + elkjs: 0.12.1 es-toolkit: 1.52.0 - katex: 0.16.47 + katex: 0.18.10 khroma: 2.1.0 marked: 16.4.2 roughjs: 4.6.6 stylis: 4.4.0 ts-dedent: 2.3.0 - uuid: 14.0.2 micromark-core-commonmark@2.0.3: dependencies: @@ -11531,29 +11578,28 @@ snapshots: minimatch@10.2.6: dependencies: - brace-expansion: 5.0.9 + brace-expansion: 5.0.12 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.18 + brace-expansion: 1.1.21 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.4 + brace-expansion: 2.1.7 minimist@1.2.8: {} - minimizer-webpack-plugin@5.11.0(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)): + minimizer-webpack-plugin@5.14.0(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)): dependencies: - '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 - schema-utils: 4.5.0 + schema-utils: 4.8.2(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)) terser: 5.51.2 - webpack: 5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2) + webpack: 5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0) optionalDependencies: - esbuild: 0.25.12 + esbuild: 0.28.1 postcss: 8.5.26 - svgo: 4.0.2 + svgo: 4.1.0 minipass@7.1.3: {} @@ -11563,16 +11609,16 @@ snapshots: nanoid@3.3.18: {} - nanostores@1.5.3: {} + nanostores@1.5.5: {} napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} - next@16.3.0(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0): + next@16.4.5(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(sass@1.103.0): dependencies: - '@next/env': 16.3.0 - '@swc/helpers': 0.5.15 + '@next/env': 16.4.5 + '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.11.15 caniuse-lite: 1.0.30001809 postcss: 8.5.23 @@ -11580,17 +11626,17 @@ snapshots: react-dom: 19.2.8(react@19.2.8) styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.8) optionalDependencies: - '@next/swc-darwin-arm64': 16.3.0 - '@next/swc-darwin-x64': 16.3.0 - '@next/swc-linux-arm64-gnu': 16.3.0 - '@next/swc-linux-arm64-musl': 16.3.0 - '@next/swc-linux-x64-gnu': 16.3.0 - '@next/swc-linux-x64-musl': 16.3.0 - '@next/swc-win32-arm64-msvc': 16.3.0 - '@next/swc-win32-x64-msvc': 16.3.0 + '@next/swc-darwin-arm64': 16.4.5 + '@next/swc-darwin-x64': 16.4.5 + '@next/swc-linux-arm64-gnu': 16.4.5 + '@next/swc-linux-arm64-musl': 16.4.5 + '@next/swc-linux-x64-gnu': 16.4.5 + '@next/swc-linux-x64-musl': 16.4.5 + '@next/swc-win32-arm64-msvc': 16.4.5 + '@next/swc-win32-x64-msvc': 16.4.5 '@opentelemetry/api': 1.9.1 sass: 1.103.0 - sharp: 0.35.3(@types/node@20.19.43) + sharp: 0.35.5(@types/node@20.19.43) transitivePeerDependencies: - '@babel/core' - '@types/node' @@ -11625,7 +11671,7 @@ snapshots: node-releases@2.0.53: {} - nodemailer@10.0.10: {} + nodemailer@10.1.4: {} normalize-path@3.0.0: {} @@ -11705,7 +11751,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-throttle@8.1.0: {} + p-throttle@8.1.1: {} package-json-from-dist@1.0.1: {} @@ -11822,7 +11868,7 @@ snapshots: dependencies: postcss: 8.5.26 - postcss-selector-parser@7.1.5: + postcss-selector-parser@7.1.7: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -11833,13 +11879,13 @@ snapshots: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 postcss@8.5.26: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 - source-map-js: 1.2.1 + source-map-js: 1.2.2 postgres-array@2.0.0: {} @@ -11891,7 +11937,7 @@ snapshots: dependencies: hookified: 2.2.0 - qs@6.16.0: + qs@6.17.1: dependencies: es-define-property: 1.0.1 side-channel: 1.1.1 @@ -12172,7 +12218,7 @@ snapshots: dependencies: chokidar: 5.0.0 immutable: 5.1.9 - source-map-js: 1.2.1 + source-map-js: 1.2.2 optionalDependencies: '@parcel/watcher': 2.6.0 @@ -12180,18 +12226,20 @@ snapshots: scheduler@0.27.0: {} - schema-utils@4.5.0: + schema-utils@4.8.2(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)): dependencies: '@types/json-schema': 7.0.15 ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) ajv-keywords: 5.1.0(ajv@8.20.0) + optionalDependencies: + webpack: 5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0) semver@6.3.1: {} semver@7.8.5: {} - set-cookie-parser@3.1.2: {} + set-cookie-parser@3.2.2: {} set-function-length@1.2.2: dependencies: @@ -12215,37 +12263,37 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 - sharp@0.35.3(@types/node@20.19.43): + sharp@0.35.5(@types/node@20.19.43): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.3 - '@img/sharp-darwin-x64': 0.35.3 - '@img/sharp-freebsd-wasm32': 0.35.3 - '@img/sharp-libvips-darwin-arm64': 1.3.2 - '@img/sharp-libvips-darwin-x64': 1.3.2 - '@img/sharp-libvips-linux-arm': 1.3.2 - '@img/sharp-libvips-linux-arm64': 1.3.2 - '@img/sharp-libvips-linux-ppc64': 1.3.2 - '@img/sharp-libvips-linux-riscv64': 1.3.2 - '@img/sharp-libvips-linux-s390x': 1.3.2 - '@img/sharp-libvips-linux-x64': 1.3.2 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 - '@img/sharp-linux-arm': 0.35.3 - '@img/sharp-linux-arm64': 0.35.3 - '@img/sharp-linux-ppc64': 0.35.3 - '@img/sharp-linux-riscv64': 0.35.3 - '@img/sharp-linux-s390x': 0.35.3 - '@img/sharp-linux-x64': 0.35.3 - '@img/sharp-linuxmusl-arm64': 0.35.3 - '@img/sharp-linuxmusl-x64': 0.35.3 - '@img/sharp-webcontainers-wasm32': 0.35.3 - '@img/sharp-win32-arm64': 0.35.3 - '@img/sharp-win32-ia32': 0.35.3 - '@img/sharp-win32-x64': 0.35.3 + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 '@types/node': 20.19.43 optional: true @@ -12298,7 +12346,7 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - source-map-js@1.2.1: {} + source-map-js@1.2.2: {} source-map-support@0.5.21: dependencies: @@ -12473,7 +12521,7 @@ snapshots: known-css-properties: 0.37.0 postcss-media-query-parser: 0.2.3 postcss-resolve-nested-selector: 0.1.6 - postcss-selector-parser: 7.1.5 + postcss-selector-parser: 7.1.7 postcss-value-parser: 4.2.0 stylelint: 17.14.1(typescript@5.9.3) @@ -12490,8 +12538,8 @@ snapshots: '@csstools/css-syntax-patches-for-csstree': 1.1.8(css-tree@3.2.1) '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/selector-resolve-nested': 4.0.1(postcss-selector-parser@7.1.5) - '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.5) + '@csstools/selector-resolve-nested': 4.0.1(postcss-selector-parser@7.1.7) + '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.7) colord: 2.9.5 cosmiconfig: 9.0.2(typescript@5.9.3) css-functions-list: 3.3.3 @@ -12513,7 +12561,7 @@ snapshots: picocolors: 1.1.1 postcss: 8.5.26 postcss-safe-parser: 7.0.1(postcss@8.5.26) - postcss-selector-parser: 7.1.5 + postcss-selector-parser: 7.1.7 postcss-value-parser: 4.2.0 string-width: 8.2.2 supports-hyperlinks: 4.5.0 @@ -12547,7 +12595,7 @@ snapshots: svg-tags@1.0.0: {} - svgo@3.3.4: + svgo@3.3.5: dependencies: commander: 7.2.0 css-select: 5.2.2 @@ -12557,12 +12605,12 @@ snapshots: picocolors: 1.1.1 sax: 1.6.1 - svgo@4.0.2: + svgo@4.1.0: dependencies: commander: 11.1.0 - css-select: 5.2.2 + css-select: 6.0.0 css-tree: 3.2.1 - css-what: 6.2.2 + css-what: 7.0.0 csso: 5.0.5 picocolors: 1.1.1 sax: 1.6.1 @@ -12598,7 +12646,7 @@ snapshots: terser@5.51.2: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.18.0 + acorn: 8.19.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -12636,7 +12684,7 @@ snapshots: tsx@4.23.13: dependencies: - esbuild: 0.28.2 + esbuild: 0.28.1 optionalDependencies: fsevents: 2.3.3 @@ -12810,8 +12858,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@14.0.2: {} - vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -12822,7 +12868,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - watchpack@2.5.2: + watchpack@2.6.3: dependencies: graceful-fs: 4.2.11 @@ -12830,27 +12876,27 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-sources@3.5.1: {} + webpack-sources@3.7.0: {} - webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2): + webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0): dependencies: - '@types/estree': 1.0.9 + '@types/estree': 1.0.10 '@types/json-schema': 7.0.15 '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 browserslist: 4.28.8 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.25.1 + enhanced-resolve: 5.26.1 es-module-lexer: 2.3.2 events: 3.3.0 graceful-fs: 4.2.11 mime-db: 1.54.0 - minimizer-webpack-plugin: 5.11.0(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)(webpack@5.111.1(esbuild@0.25.12)(postcss@8.5.26)(svgo@4.0.2)) - schema-utils: 4.5.0 + minimizer-webpack-plugin: 5.14.0(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)) + schema-utils: 4.8.2(webpack@5.111.1(esbuild@0.28.1)(postcss@8.5.26)(svgo@4.1.0)) tapable: 2.3.3 - watchpack: 2.5.2 - webpack-sources: 3.5.1 + watchpack: 2.6.3 + webpack-sources: 3.7.0 transitivePeerDependencies: - '@minify-html/node' - '@napi-rs/image' @@ -12963,12 +13009,10 @@ snapshots: yocto-queue@0.1.0: {} - zod-validation-error@4.0.2(zod@4.4.3): + zod-validation-error@4.0.2(zod@4.6.7): dependencies: - zod: 4.4.3 - - zod@4.4.3: {} + zod: 4.6.7 - zod@4.6.5: {} + zod@4.6.7: {} zwitch@2.0.4: {} diff --git a/scripts/check-client-provenance.mjs b/scripts/check-client-provenance.mjs new file mode 100644 index 0000000..434d5fd --- /dev/null +++ b/scripts/check-client-provenance.mjs @@ -0,0 +1,84 @@ +// Checks that vendor/developmentfinance-client.provenance.json says where the +// vendored @developmentfinance/client tarball came from, and that it agrees +// with the commit that vendored the tarball. The tarball itself records only +// its package version, so the sdk commit and the spec-kit revision it was built +// from live in the provenance file beside it. CI runs this; it needs the full +// git history, because it reads the subject of the last commit that touched the +// tarball, which must name the sdk commit as "sdk ", as the vendoring +// commits do ("chore(deps): vendor the client at sdk 82701c8"). +// +// node scripts/check-client-provenance.mjs +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = fileURLToPath(new URL('../', import.meta.url)); +const PROVENANCE = 'vendor/developmentfinance-client.provenance.json'; +const PACKAGE_NAME = '@developmentfinance/client'; +const SHA = /^[0-9a-f]{40}$/; +const SUBJECT_SHA = /\bsdk ([0-9a-f]{7,40})\b/; + +const failures = []; +const fail = (message) => failures.push(message); + +const provenance = JSON.parse(readFileSync(path.join(ROOT, PROVENANCE), 'utf8')); +const pkg = JSON.parse(readFileSync(path.join(ROOT, 'package.json'), 'utf8')); + +// The provenance names the tarball package.json installs. +const spec = pkg.dependencies?.[PACKAGE_NAME] ?? ''; +const installed = spec.startsWith('file:vendor/') ? spec.slice('file:vendor/'.length) : null; +if (installed === null) { + fail(`package.json does not install ${PACKAGE_NAME} from file:vendor/ (found "${spec}")`); +} else if (provenance.package !== installed) { + fail(`${PROVENANCE} names "${provenance.package}" but package.json installs "${installed}"`); +} +const tarball = `vendor/${provenance.package}`; +if (!existsSync(path.join(ROOT, tarball))) { + fail(`${tarball} does not exist`); +} + +// Both revisions are full commit hashes. +for (const key of ['sdk', 'spec_kit']) { + const commit = provenance[key]?.commit; + if (typeof commit !== 'string' || !SHA.test(commit)) { + fail( + `${PROVENANCE}: ${key}.commit must be a full 40-character commit hash (found ${JSON.stringify(commit)})`, + ); + } +} + +// The last commit that touched the tarball names the same sdk commit. +if (failures.length === 0) { + const log = execFileSync('git', ['log', '-1', '--format=%H%n%s', '--', tarball], { + cwd: ROOT, + encoding: 'utf8', + }).trim(); + if (log === '') { + fail( + `no commit touches ${tarball}; the check needs the full git history (fetch-depth: 0 in CI)`, + ); + } else { + const [hash, subject] = log.split('\n'); + const named = SUBJECT_SHA.exec(subject)?.[1]; + if (named === undefined) { + fail( + `commit ${hash.slice(0, 7)} vendored ${tarball} but its subject does not name the sdk commit ` + + `as "sdk ": "${subject}"`, + ); + } else if (!provenance.sdk.commit.startsWith(named)) { + fail( + `commit ${hash.slice(0, 7)} vendored ${tarball} at sdk ${named}, but ${PROVENANCE} records ` + + `sdk ${provenance.sdk.commit.slice(0, 7)}; update the provenance file with the commit the tarball was built from`, + ); + } + } +} + +if (failures.length > 0) { + for (const message of failures) console.error(`FAIL ${message}`); + process.exit(1); +} +console.log( + `ok ${tarball} from sdk ${provenance.sdk.commit.slice(0, 7)}, spec-kit ${provenance.spec_kit.commit.slice(0, 7)}`, +); diff --git a/src/app/(site)/ai-policy/page.tsx b/src/app/(site)/ai-policy/page.tsx index 61c690b..dc0404f 100644 --- a/src/app/(site)/ai-policy/page.tsx +++ b/src/app/(site)/ai-policy/page.tsx @@ -55,8 +55,9 @@ const AiPolicyPage = () => (
  • How questions are processed. The text of your question is sent - to Google’s Gemini models, which read it to find the data you asked for and, in - the Studio, to write the answer. + to two AI services. Google’s Gemini models read it to find the data you asked + for and, in the Studio, to write the answer. Jev, a model from TypeSafe, reads + it to match what you asked for to the DFO’s datasets.
  • What a search records. When you search on this site, we record @@ -77,10 +78,13 @@ const AiPolicyPage = () => (
  • Connected assistants. When you use the DFO from another - assistant, the DFO receives the question and the data requests that assistant - sends. It needs no account and is not told who you are. Charts and tables the - assistant creates through the DFO are stored so that they can be shown, and - anyone who has a chart’s link can open it. + assistant, the DFO receives the data requests that assistant sends. A request + names the data, places and years the assistant is looking for, and can carry + your question in the assistant’s words. What a request carries goes to Gemini + and Jev, as above. The DFO needs no account and is not told who you are. + Charts and tables the assistant creates through the DFO are stored so that they + can be shown, and anyone who has a chart’s link can open it. Files it exports + are stored behind a public link that does not expire.
  • Internet addresses. Our servers’ request logs record the diff --git a/src/app/s/[id]/download/preparing/PreparingDownload.tsx b/src/app/s/[id]/download/preparing/PreparingDownload.tsx index 4ee4c55..cc613a6 100644 --- a/src/app/s/[id]/download/preparing/PreparingDownload.tsx +++ b/src/app/s/[id]/download/preparing/PreparingDownload.tsx @@ -5,7 +5,7 @@ import { useEffect, useState } from 'react'; import styles from './PreparingDownload.module.scss'; -import { pollOperation, type OperationProgress } from '@/lib/search/operationPoll'; +import { operationPath, pollOperation, type OperationProgress } from '@/lib/search/operationPoll'; import { artifactHref, pinDownloadedVersion } from '@/lib/search/searchClient'; import { renderedFile } from '@/lib/search/shareDownload'; import type { FileRendering } from '@/lib/search/snapshot'; @@ -36,7 +36,9 @@ const PreparingDownload = ({ const bundle = preparing?.rendering.bundle; useEffect(() => { - if (!operationId || !format || bundle === undefined) { + const pollPath = operationPath(operationId); + + if (!operationId || !pollPath || !format || bundle === undefined) { return; } @@ -48,7 +50,7 @@ const PreparingDownload = ({ { operation_id: operationId, status: 'pending', - poll: `/api/datasets/operations/${operationId}`, + poll: pollPath, }, { signal: controller.signal, diff --git a/src/components/atoms/Spinner/Spinner.module.scss b/src/components/atoms/Spinner/Spinner.module.scss index da5f949..d77dcec 100644 --- a/src/components/atoms/Spinner/Spinner.module.scss +++ b/src/components/atoms/Spinner/Spinner.module.scss @@ -17,3 +17,10 @@ rotate: 360deg; } } + +@media (prefers-reduced-motion: reduce) { + // A frozen ring reads as a broken circle, so the spinner stays on, slowed. + .spinner { + animation-duration: 2.4s; + } +} diff --git a/src/components/organisms/CreateCanvas/ShowcaseCards.tsx b/src/components/organisms/CreateCanvas/ShowcaseCards.tsx index 0dd664d..e21ff8b 100644 --- a/src/components/organisms/CreateCanvas/ShowcaseCards.tsx +++ b/src/components/organisms/CreateCanvas/ShowcaseCards.tsx @@ -65,7 +65,9 @@ const extras = (title: string, view: CardExtras['view']): CardExtras => ({ readOnly: true, }); -const theme = deriveTheme(DEFAULT_THEME); +// The section's own ground for the cards is the site's off-white, in place of +// the canvas's white. +const theme = deriveTheme(DEFAULT_THEME, '#f6f6e8'); const widgets = cards as Widget[]; type Tile = Rect & { key: number; card: number; leaving: boolean }; @@ -240,8 +242,11 @@ const ShowcaseCards = ({ grid }: { grid: Grid }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [reducedMotion, phone]); + // Pure decoration: the cards keep their real buttons (the source line, + // the left-out line), so `inert` takes the whole layer out of the tab + // order and hit testing; aria-hidden covers readers on older browsers. return ( -