diff --git a/apps/journal/app/lib/federation.server.test.ts b/apps/journal/app/lib/federation.server.test.ts new file mode 100644 index 0000000..880247e --- /dev/null +++ b/apps/journal/app/lib/federation.server.test.ts @@ -0,0 +1,129 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +// Spike validation (social-federation task 1.1): exercise Fedify's URL +// dispatcher the way a remote AP client would — WebFinger lookup, then +// actor fetch with an ActivityPub Accept header — without a running +// server or database. + +const dbUsers: Array> = []; + +vi.mock("./db.ts", () => ({ + getDb: () => ({ + select: () => ({ + from: () => ({ + where: () => ({ + limit: async () => dbUsers, + }), + }), + }), + }), +})); + +const { handleFederationRequest, wantsActivityJson } = await import( + "./federation.server.ts" +); + +const PUBLIC_USER = { + id: "u1", + username: "bruno", + displayName: "Bruno", + bio: "Riding bikes", + domain: "localhost", + profileVisibility: "public", +}; + +function webfingerRequest(handle: string): Request { + return new Request( + `http://localhost:3000/.well-known/webfinger?resource=${encodeURIComponent(handle)}`, + { headers: { accept: "application/jrd+json" } }, + ); +} + +function actorRequest(username: string): Request { + return new Request(`http://localhost:3000/users/${username}`, { + headers: { accept: "application/activity+json" }, + }); +} + +beforeEach(() => { + process.env.FEDERATION_ENABLED = "true"; + dbUsers.length = 0; +}); + +describe("federation flag", () => { + it("404s every federation request when FEDERATION_ENABLED is off", async () => { + process.env.FEDERATION_ENABLED = "false"; + dbUsers.push(PUBLIC_USER); + const res = await handleFederationRequest(webfingerRequest("acct:bruno@localhost:3000")); + expect(res.status).toBe(404); + }); +}); + +describe("WebFinger", () => { + it("resolves a public user's handle to their actor IRI", async () => { + dbUsers.push(PUBLIC_USER); + const res = await handleFederationRequest(webfingerRequest("acct:bruno@localhost:3000")); + expect(res.status).toBe(200); + const jrd = await res.json(); + expect(jrd.subject).toBe("acct:bruno@localhost:3000"); + const self = jrd.links.find((l: { rel: string }) => l.rel === "self"); + expect(self.href).toBe("http://localhost:3000/users/bruno"); + expect(self.type).toContain("application/activity+json"); + }); + + it("404s for a private user without leaking existence", async () => { + dbUsers.push({ ...PUBLIC_USER, profileVisibility: "private" }); + const res = await handleFederationRequest(webfingerRequest("acct:bruno@localhost:3000")); + expect(res.status).toBe(404); + }); + + it("404s for an unknown user", async () => { + const res = await handleFederationRequest(webfingerRequest("acct:ghost@localhost:3000")); + expect(res.status).toBe(404); + }); +}); + +describe("actor object", () => { + it("serves a Person actor for a public user", async () => { + dbUsers.push(PUBLIC_USER); + const res = await handleFederationRequest(actorRequest("bruno")); + expect(res.status).toBe(200); + expect(res.headers.get("content-type")).toContain("application/activity+json"); + const actor = await res.json(); + expect(actor.type).toBe("Person"); + expect(actor.id).toBe("http://localhost:3000/users/bruno"); + expect(actor.preferredUsername).toBe("bruno"); + expect(actor.name).toBe("Bruno"); + expect(actor.summary).toBe("Riding bikes"); + }); + + it("404s the actor for a private user", async () => { + dbUsers.push({ ...PUBLIC_USER, profileVisibility: "private" }); + const res = await handleFederationRequest(actorRequest("bruno")); + expect(res.status).toBe(404); + }); +}); + +describe("wantsActivityJson", () => { + it("matches AP client Accept headers, not browsers", () => { + expect(wantsActivityJson(actorRequest("bruno"))).toBe(true); + expect( + wantsActivityJson( + new Request("http://localhost:3000/users/bruno", { + // Mastodon's exact Accept header + headers: { + accept: + 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + }, + }), + ), + ).toBe(true); + expect( + wantsActivityJson( + new Request("http://localhost:3000/users/bruno", { + headers: { accept: "text/html,application/xhtml+xml" }, + }), + ), + ).toBe(false); + }); +}); diff --git a/apps/journal/app/lib/federation.server.ts b/apps/journal/app/lib/federation.server.ts new file mode 100644 index 0000000..1bda9bd --- /dev/null +++ b/apps/journal/app/lib/federation.server.ts @@ -0,0 +1,99 @@ +// ActivityPub federation via Fedify. See openspec/changes/social-federation. +// +// Spike scope (task 1.1): a minimal Federation instance serving WebFinger +// discovery + Person actor objects for public users. No keypairs, no inbox +// processing, no delivery yet — those land in later tasks. +// +// Version pinning (task 1.2): @fedify/fedify is pinned to exactly 2.1.16. +// The whole 2.2.x line is uninstallable from npm — each release depends on +// @fedify/webfinger@2.2.x, which was never published (latest is 2.1.16). +// Revisit the pin once `pnpm view @fedify/fedify dependencies` resolves. +// +// Mounting model: Fedify owns URL dispatch for its endpoints via +// `federation.fetch(request)`. We delegate to it from React Router routes: +// - /.well-known/webfinger → resource route (no component, raw Response) +// - /users/:username → route middleware short-circuits to Fedify +// when the request asks for an ActivityPub representation; browsers +// fall through to the HTML profile loader (content negotiation per +// the design decision "actor IRI is the profile URL"). +import { createFederation, MemoryKvStore, type Federation } from "@fedify/fedify"; +import { Person } from "@fedify/fedify/vocab"; +import { eq } from "drizzle-orm"; +import { users } from "@trails-cool/db/schema/journal"; +import { getDb } from "./db.ts"; +import { getOrigin } from "./config.server.ts"; + +/** + * Feature flag (task 1.3). All federation surfaces — WebFinger, actor + * objects, inbox, outbox — are disabled (404) unless FEDERATION_ENABLED + * is exactly "true". Default off so schema/code can deploy before any + * federation traffic is possible. + */ +export function federationEnabled(): boolean { + return process.env.FEDERATION_ENABLED === "true"; +} + +/** Does the request ask for an ActivityPub representation of a resource? */ +export function wantsActivityJson(request: Request): boolean { + const accept = request.headers.get("accept") ?? ""; + return ( + accept.includes("application/activity+json") || + accept.includes("application/ld+json") + ); +} + +let _federation: Federation | null = null; + +function buildFederation(): Federation { + const federation = createFederation({ + // MemoryKvStore is fine for the spike (it only caches remote documents + // and nonces). Replace with a Postgres-backed store before real + // federation traffic (revisit in task 4.x). + kv: new MemoryKvStore(), + // Canonical origin so generated IRIs are correct behind the Caddy + // proxy (the Node server itself only sees plain HTTP). + origin: getOrigin(), + }); + + federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => { + const db = getDb(); + const [user] = await db + .select() + .from(users) + .where(eq(users.username, identifier)) + .limit(1); + // Private profiles do not federate at all: returning null makes Fedify + // respond 404, and WebFinger lookups for this user 404 with it — no + // leak of user existence (spec: "Private user is invisible to + // federation"). + if (!user || user.profileVisibility !== "public") return null; + return new Person({ + id: ctx.getActorUri(identifier), + preferredUsername: identifier, + name: user.displayName ?? user.username, + summary: user.bio || undefined, + // Human-facing profile URL — same as the actor IRI by design, but + // declared explicitly so AP clients link browsers to the HTML view. + url: new URL(`/users/${identifier}`, getOrigin()), + }); + }); + + return federation; +} + +export function getFederation(): Federation { + _federation ??= buildFederation(); + return _federation; +} + +/** + * Delegate a request to Fedify's URL dispatcher. 404s when the feature + * flag is off so disabled instances are indistinguishable from instances + * without federation. + */ +export function handleFederationRequest(request: Request): Promise { + if (!federationEnabled()) { + return Promise.resolve(new Response("Not Found", { status: 404 })); + } + return getFederation().fetch(request, { contextData: undefined }); +} diff --git a/apps/journal/app/routes.ts b/apps/journal/app/routes.ts index f28fbda..4fabf60 100644 --- a/apps/journal/app/routes.ts +++ b/apps/journal/app/routes.ts @@ -3,6 +3,7 @@ import { type RouteConfig, index, route } from "@react-router/dev/routes"; export default [ index("routes/home.tsx"), route(".well-known/trails-cool", "routes/api.well-known.trails-cool.ts"), + route(".well-known/webfinger", "routes/api.well-known.webfinger.ts"), route("oauth/authorize", "routes/oauth.authorize.tsx"), route("oauth/token", "routes/oauth.token.ts"), route("auth/register", "routes/auth.register.tsx"), diff --git a/apps/journal/app/routes/api.well-known.webfinger.ts b/apps/journal/app/routes/api.well-known.webfinger.ts new file mode 100644 index 0000000..851429a --- /dev/null +++ b/apps/journal/app/routes/api.well-known.webfinger.ts @@ -0,0 +1,13 @@ +import type { Route } from "./+types/api.well-known.webfinger"; +import { handleFederationRequest } from "~/lib/federation.server"; + +/** + * GET /.well-known/webfinger?resource=acct:user@domain + * + * ActivityPub discovery: resolves an acct: handle to the user's actor IRI. + * Fully handled by Fedify; 404s while FEDERATION_ENABLED is off, and for + * users whose profile_visibility is not 'public'. + */ +export function loader({ request }: Route.LoaderArgs) { + return handleFederationRequest(request); +} diff --git a/apps/journal/app/routes/users.$username.tsx b/apps/journal/app/routes/users.$username.tsx index 3b22319..7fb950d 100644 --- a/apps/journal/app/routes/users.$username.tsx +++ b/apps/journal/app/routes/users.$username.tsx @@ -4,6 +4,24 @@ import { useTranslation } from "react-i18next"; import { ClientDate } from "~/components/ClientDate"; import { FollowButton } from "~/components/FollowButton"; import { loadUserProfile } from "./users.$username.server"; +import { + federationEnabled, + wantsActivityJson, + handleFederationRequest, +} from "~/lib/federation.server"; + +// Content negotiation: this URL is both the human profile (HTML) and the +// ActivityPub actor IRI (application/activity+json). AP clients are +// short-circuited to Fedify before the HTML loader runs; browsers fall +// through untouched. +export const middleware: Route.MiddlewareFunction[] = [ + async ({ request }, next) => { + if (federationEnabled() && wantsActivityJson(request)) { + return handleFederationRequest(request); + } + return next(); + }, +]; export async function loader({ params, request }: Route.LoaderArgs) { return data(await loadUserProfile(request, params.username)); diff --git a/apps/journal/package.json b/apps/journal/package.json index 242eead..79f4c76 100644 --- a/apps/journal/package.json +++ b/apps/journal/package.json @@ -12,18 +12,19 @@ "test": "vitest run" }, "dependencies": { + "@fedify/fedify": "2.1.16", "@react-router/node": "catalog:", "@react-router/serve": "catalog:", "@sentry/node": "catalog:", "@sentry/react": "catalog:", "@simplewebauthn/browser": "^13.3.0", - "@simplewebauthn/server": "^13.3.1", + "@simplewebauthn/server": "^13.3.0", "@trails-cool/api": "workspace:*", "@trails-cool/db": "workspace:*", - "@trails-cool/jobs": "workspace:*", "@trails-cool/fit": "workspace:*", "@trails-cool/gpx": "workspace:*", "@trails-cool/i18n": "workspace:*", + "@trails-cool/jobs": "workspace:*", "@trails-cool/map": "workspace:*", "@trails-cool/sentry-config": "workspace:*", "@trails-cool/types": "workspace:*", @@ -31,7 +32,7 @@ "drizzle-orm": "catalog:", "isbot": "^5.1.40", "jose": "^6.2.3", - "nodemailer": "^8.0.10", + "nodemailer": "^8.0.8", "pino": "^10.3.1", "prom-client": "^15.1.3", "react": "catalog:", diff --git a/apps/journal/react-router.config.ts b/apps/journal/react-router.config.ts index e45e273..5142398 100644 --- a/apps/journal/react-router.config.ts +++ b/apps/journal/react-router.config.ts @@ -2,4 +2,11 @@ import type { Config } from "@react-router/dev/config"; export default { ssr: true, + future: { + // Route middleware — used for ActivityPub content negotiation on + // /users/:username (see app/lib/federation.server.ts). No loader or + // action reads the `context` arg, so the flag's type change to + // RouterContextProvider is a no-op for existing code. + v8_middleware: true, + }, } satisfies Config; diff --git a/openspec/changes/social-federation/tasks.md b/openspec/changes/social-federation/tasks.md index 7787e9c..dcbea6f 100644 --- a/openspec/changes/social-federation/tasks.md +++ b/openspec/changes/social-federation/tasks.md @@ -1,8 +1,8 @@ ## 1. Library + dependencies -- [ ] 1.1 Spike Fedify on a branch: import, mount minimal actor object endpoint for one user, validate WebFinger lookup from a remote AP client. Confirm fit. If unfit, document why and pivot to alternative -- [ ] 1.2 Add Fedify (or chosen lib) to `apps/journal/package.json`; document version pinning rationale -- [ ] 1.3 Add `FEDERATION_ENABLED` env var (default `false`) and a thin runtime feature flag the rest of the change reads from +- [x] 1.1 Spike Fedify on a branch: import, mount minimal actor object endpoint for one user, validate WebFinger lookup from a remote AP client. Confirm fit. If unfit, document why and pivot to alternative +- [x] 1.2 Add Fedify (or chosen lib) to `apps/journal/package.json`; document version pinning rationale +- [x] 1.3 Add `FEDERATION_ENABLED` env var (default `false`) and a thin runtime feature flag the rest of the change reads from ## 2. Schema diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b6ebf18..cd23e25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,6 +203,9 @@ importers: apps/journal: dependencies: + '@fedify/fedify': + specifier: 2.1.16 + version: 2.1.16 '@react-router/node': specifier: 'catalog:' version: 7.16.0(react-router@7.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@6.0.3) @@ -219,7 +222,7 @@ importers: specifier: ^13.3.0 version: 13.3.0 '@simplewebauthn/server': - specifier: ^13.3.1 + specifier: ^13.3.0 version: 13.3.1 '@trails-cool/api': specifier: workspace:* @@ -253,7 +256,7 @@ importers: version: link:../../packages/ui drizzle-orm: specifier: 'catalog:' - version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9) + version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9) isbot: specifier: ^5.1.40 version: 5.1.40 @@ -261,7 +264,7 @@ importers: specifier: ^6.2.3 version: 6.2.3 nodemailer: - specifier: ^8.0.10 + specifier: ^8.0.8 version: 8.0.10 pino: specifier: ^10.3.1 @@ -513,7 +516,7 @@ importers: version: 6.0.2 drizzle-orm: specifier: 'catalog:' - version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9) + version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9) isbot: specifier: ^5.1.40 version: 5.1.40 @@ -601,7 +604,7 @@ importers: dependencies: drizzle-orm: specifier: 'catalog:' - version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9) + version: 0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9) postgres: specifier: 'catalog:' version: 3.4.9 @@ -1223,6 +1226,9 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true + '@cfworker/json-schema@4.1.1': + resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + '@codemirror/autocomplete@6.20.1': resolution: {integrity: sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==} @@ -1280,6 +1286,10 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@digitalbazaar/http-client@4.3.0': + resolution: {integrity: sha512-6lMpxpt9BOmqHKGs9Xm6DP4LlZTBFer/ZjHvP3FcW3IaUWYIWC7dw5RFZnvw4fP57kAVcm1dp3IF+Y50qhBvAw==} + engines: {node: '>=18.0'} + '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} @@ -2113,6 +2123,26 @@ packages: resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} hasBin: true + '@fedify/fedify@2.1.16': + resolution: {integrity: sha512-zBuNLjx7jqg/Dig19IUkUI91UL1JC9M3GWYJOG1cq0RFUkfh/soqiY0UBNhFJGEXyEqI+QIGS976NDZzb9t/ZA==} + engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} + + '@fedify/vocab-runtime@2.1.16': + resolution: {integrity: sha512-1I+x1I+AZmQo956SQXPlGWiMjQNdOGgN0o8bBMCQO4pOgudKCiX9JQjVvfSDyr+3MwAJnC3ay6yuY74gRhhGxw==} + engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} + + '@fedify/vocab-tools@2.1.16': + resolution: {integrity: sha512-MZSJHPOr6hzTtYJUm1r1PBjFBqhLEuNLoJYrC7yj7JiZQwmWYcjbwOYCxeljnQ2SNt52WOqVXV7AFCbofuRNXA==} + engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} + + '@fedify/vocab@2.1.16': + resolution: {integrity: sha512-bVe07C6AFtyMqiqJzB9TO7IR9L6l2gA2ZeOqw5f0/d28jJSahsr0ZnmWi+ckf1/YpPq1Yl6uMx37m/udW8wwUA==} + engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} + + '@fedify/webfinger@2.1.16': + resolution: {integrity: sha512-QVq8sFd0xVg4r2IySeoX7H2HnUwdiGkHyTrOT8G+5qu8/5nqVEP/ufcoM2HSU8zNElCjR+M4yjlOMUiW7hTkWg==} + engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} + '@fission-ai/openspec@1.3.1': resolution: {integrity: sha512-QnbJfq/lUNCRY+TTXo87fuIpGCCaOYt280tmbuI112B/1vF0feIneK0/qhoTZNslRDhwwg1YcYDX0suxq2h6tw==} engines: {node: '>=20.19.0'} @@ -2418,6 +2448,10 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@js-temporal/polyfill@0.5.1': + resolution: {integrity: sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==} + engines: {node: '>=12'} + '@levischuck/tiny-cbor@0.2.11': resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} @@ -2430,6 +2464,9 @@ packages: '@lezer/lr@1.4.8': resolution: {integrity: sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==} + '@logtape/logtape@2.1.1': + resolution: {integrity: sha512-aULbCqUQGerfOsZ3CMvcKtueKzmdchluXYUd3bIHKmOIS93fx1ko0+hyRQ4flloGZ8EiyRPydZXiy8n1J/eAQA==} + '@mapbox/jsonlint-lines-primitives@2.0.2': resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==} engines: {node: '>= 0.6'} @@ -2493,12 +2530,19 @@ packages: cpu: [x64] os: [win32] + '@multiformats/base-x@4.0.1': + resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} + '@napi-rs/wasm-runtime@1.1.4': resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -4466,10 +4510,17 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + byte-encodings@1.0.11: + resolution: {integrity: sha512-+/xR2+ySc2yKGtud3DGkGSH1DNwHfRVK0KTnMhoeH36/KwG+tHQ4d9B3jxJFq7dW27YcfudkywaYJRPA2dmxzg==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -4497,6 +4548,10 @@ packages: caniuse-lite@1.0.30001793: resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + canonicalize@2.1.0: + resolution: {integrity: sha512-F705O3xrsUtgt98j7leetNhTWPe+5S72rlL5O4jA1pKqBVQ/dT1O1D6PFxmSXvc0SUOinWS57DKx0I3CHrXJHQ==} + hasBin: true + canvas@3.2.3: resolution: {integrity: sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==} engines: {node: ^18.12.0 || >= 20.9.0} @@ -5045,6 +5100,9 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + es-toolkit@1.43.0: + resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} + esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} @@ -6162,6 +6220,9 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsbi@4.3.2: + resolution: {integrity: sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew==} + jsc-safe-url@0.2.4: resolution: {integrity: sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==} @@ -6196,6 +6257,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-canon@1.0.1: + resolution: {integrity: sha512-PQcj4PFOTAQxE8PgoQ4KrM0DcKWZd7S3ELOON8rmysl9I8JuFMgxu1H9v+oZsTPjjkpeS3IHPwLjr7d+gKygnw==} + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -6213,6 +6277,10 @@ packages: engines: {node: '>=6'} hasBin: true + jsonld@9.0.0: + resolution: {integrity: sha512-pjMIdkXfC1T2wrX9B9i2uXhGdyCmgec3qgMht+TDj+S0qX3bjWMQUfL7NeqEhuRTi8G5ESzmL9uGlST7nzSEWg==} + engines: {node: '>=18'} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -6220,6 +6288,10 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + ky@1.14.3: + resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==} + engines: {node: '>=18'} + lan-network@0.2.1: resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==} hasBin: true @@ -6373,6 +6445,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -6935,6 +7011,10 @@ packages: pkg-types@2.3.1: resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + playwright-core@1.60.0: resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} engines: {node: '>=18'} @@ -7113,6 +7193,10 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + rdf-canonize@5.0.0: + resolution: {integrity: sha512-g8OUrgMXAR9ys/ZuJVfBr05sPPoMA7nHIVs8VEvg9QwM5W4GR2qSFEEHjsyHF1eWlBaf8Ev40WNjQFQ+nJTO3w==} + engines: {node: '>=18'} + react-devtools-core@6.1.5: resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==} @@ -7475,6 +7559,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -7698,6 +7785,9 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} + structured-field-values@2.0.4: + resolution: {integrity: sha512-5zpJXYLPwW3WYUD/D58tQjIBs10l3Yx64jZfcKGs/RH79E2t9Xm/b9+ydwdMNVSksnsIY+HR/2IlQmgo0AcTAg==} + structured-headers@0.4.1: resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} @@ -7957,9 +8047,19 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-template-router@1.0.0: + resolution: {integrity: sha512-WKcL9ZSIEhHE3f5P4Z47Tf0nWbcgV1ISb/OBuF8YKEYi0SQOyTLCzM6B/gAKFWZhRhqA+C/Ks8UXe2qU5W0FVg==} + url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + url-template@3.1.1: + resolution: {integrity: sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + urlpattern-polyfill@10.1.0: + resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} + use-callback-ref@1.3.3: resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} engines: {node: '>=10'} @@ -8343,6 +8443,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -9005,6 +9108,8 @@ snapshots: dependencies: css-tree: 3.2.1 + '@cfworker/json-schema@4.1.1': {} + '@codemirror/autocomplete@6.20.1': dependencies: '@codemirror/language': 6.12.3 @@ -9075,6 +9180,11 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} + '@digitalbazaar/http-client@4.3.0': + dependencies: + ky: 1.14.3 + undici: 6.25.0 + '@drizzle-team/brocli@0.10.2': {} '@egjs/hammerjs@2.0.17': @@ -10064,7 +10174,7 @@ snapshots: - '@types/react-dom' optional: true - '@expo/vector-icons@15.1.1(expo-font@55.0.8(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)': + '@expo/vector-icons@15.1.1(expo-font@55.0.8)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)': dependencies: expo-font: 55.0.8(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6) react: 19.2.6 @@ -10078,6 +10188,65 @@ snapshots: chalk: 4.1.2 js-yaml: 4.1.1 + '@fedify/fedify@2.1.16': + dependencies: + '@fedify/vocab': 2.1.16 + '@fedify/vocab-runtime': 2.1.16 + '@fedify/webfinger': 2.1.16 + '@js-temporal/polyfill': 0.5.1 + '@logtape/logtape': 2.1.1 + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.40.0 + byte-encodings: 1.0.11 + es-toolkit: 1.43.0 + json-canon: 1.0.1 + jsonld: 9.0.0 + structured-field-values: 2.0.4 + uri-template-router: 1.0.0 + url-template: 3.1.1 + urlpattern-polyfill: 10.1.0 + + '@fedify/vocab-runtime@2.1.16': + dependencies: + '@js-temporal/polyfill': 0.5.1 + '@logtape/logtape': 2.1.1 + '@multiformats/base-x': 4.0.1 + '@opentelemetry/api': 1.9.1 + asn1js: 3.0.10 + byte-encodings: 1.0.11 + jsonld: 9.0.0 + pkijs: 3.4.0 + + '@fedify/vocab-tools@2.1.16': + dependencies: + '@cfworker/json-schema': 4.1.1 + byte-encodings: 1.0.11 + es-toolkit: 1.43.0 + yaml: 2.9.0 + + '@fedify/vocab@2.1.16': + dependencies: + '@fedify/vocab-runtime': 2.1.16 + '@fedify/vocab-tools': 2.1.16 + '@fedify/webfinger': 2.1.16 + '@js-temporal/polyfill': 0.5.1 + '@logtape/logtape': 2.1.1 + '@multiformats/base-x': 4.0.1 + '@opentelemetry/api': 1.9.1 + asn1js: 3.0.10 + es-toolkit: 1.43.0 + jsonld: 9.0.0 + pkijs: 3.4.0 + + '@fedify/webfinger@2.1.16': + dependencies: + '@fedify/vocab-runtime': 2.1.16 + '@logtape/logtape': 2.1.1 + '@opentelemetry/api': 1.9.1 + es-toolkit: 1.43.0 + '@fission-ai/openspec@1.3.1(@types/node@25.9.1)': dependencies: '@inquirer/core': 10.3.2(@types/node@25.9.1) @@ -10475,6 +10644,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@js-temporal/polyfill@0.5.1': + dependencies: + jsbi: 4.3.2 + '@levischuck/tiny-cbor@0.2.11': {} '@lezer/common@1.5.2': {} @@ -10487,6 +10660,8 @@ snapshots: dependencies: '@lezer/common': 1.5.2 + '@logtape/logtape@2.1.1': {} + '@mapbox/jsonlint-lines-primitives@2.0.2': {} '@mapbox/unitbezier@0.0.1': {} @@ -10536,6 +10711,8 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true + '@multiformats/base-x@4.0.1': {} + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 @@ -10543,6 +10720,8 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@noble/hashes@1.4.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -12811,8 +12990,12 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + byte-encodings@1.0.11: {} + bytes@3.1.2: {} + bytestreamjs@2.0.1: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -12833,6 +13016,8 @@ snapshots: caniuse-lite@1.0.30001793: {} + canonicalize@2.1.0: {} + canvas@3.2.3: dependencies: node-addon-api: 7.1.1 @@ -13197,6 +13382,14 @@ snapshots: pg: 8.20.0 postgres: 3.4.9 + drizzle-orm@0.45.2(@opentelemetry/api@1.9.1)(@types/pg@8.15.6)(expo-sqlite@56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(pg@8.20.0)(postgres@3.4.9): + optionalDependencies: + '@opentelemetry/api': 1.9.1 + '@types/pg': 8.15.6 + expo-sqlite: 56.0.4(expo@56.0.4)(react-native@0.85.3(@babel/core@7.29.7)(@react-native/jest-preset@0.85.3(@babel/core@7.29.7)(react@19.2.6))(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6) + pg: 8.20.0 + postgres: 3.4.9 + drizzle-postgis@1.1.1: dependencies: wkx: 0.5.0 @@ -13265,6 +13458,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.4 + es-toolkit@1.43.0: {} + esbuild@0.18.20: optionalDependencies: '@esbuild/android-arm': 0.18.20 @@ -13932,7 +14127,7 @@ snapshots: '@expo/log-box': 55.0.12(@expo/dom-webview@55.0.5)(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6) '@expo/metro': 55.1.1 '@expo/metro-config': 55.0.23(expo@55.0.26)(typescript@6.0.3) - '@expo/vector-icons': 15.1.1(expo-font@55.0.8(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6))(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6) + '@expo/vector-icons': 15.1.1(expo-font@55.0.8)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6) '@ungap/structured-clone': 1.3.1 babel-preset-expo: 55.0.22(@babel/core@7.29.7)(@babel/runtime@7.29.7)(expo@55.0.26)(react-refresh@0.14.2) expo-asset: 55.0.17(expo@55.0.26)(react-native@0.83.4(@babel/core@7.29.7)(@react-native/metro-config@0.85.3(@babel/core@7.29.7))(@types/react@19.2.15)(react@19.2.6))(react@19.2.6)(typescript@6.0.3) @@ -14885,6 +15080,8 @@ snapshots: dependencies: argparse: 2.0.1 + jsbi@4.3.2: {} + jsc-safe-url@0.2.4: {} jsdom@20.0.3(canvas@3.2.3): @@ -14956,6 +15153,8 @@ snapshots: json-buffer@3.0.1: {} + json-canon@1.0.1: {} + json-parse-even-better-errors@2.3.1: {} json-schema-traverse@0.4.1: {} @@ -14966,12 +15165,21 @@ snapshots: json5@2.2.3: {} + jsonld@9.0.0: + dependencies: + '@digitalbazaar/http-client': 4.3.0 + canonicalize: 2.1.0 + lru-cache: 6.0.0 + rdf-canonize: 5.0.0 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 kleur@3.0.3: {} + ky@1.14.3: {} + lan-network@0.2.1: {} leaflet.markercluster@1.5.3(leaflet@1.9.4): @@ -15094,6 +15302,10 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + luxon@3.7.2: {} lz-string@1.5.0: {} @@ -15862,6 +16074,15 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + pkijs@3.4.0: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + playwright-core@1.60.0: {} playwright@1.60.0: @@ -16041,6 +16262,10 @@ snapshots: strip-json-comments: 2.0.1 optional: true + rdf-canonize@5.0.0: + dependencies: + setimmediate: 1.0.5 + react-devtools-core@6.1.5: dependencies: shell-quote: 1.8.3 @@ -16574,6 +16799,8 @@ snapshots: set-cookie-parser@2.7.2: {} + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} sf-symbols-typescript@2.2.0: {} @@ -16781,6 +17008,8 @@ snapshots: strip-json-comments@5.0.3: {} + structured-field-values@2.0.4: {} + structured-headers@0.4.1: {} style-mod@4.1.3: {} @@ -17044,11 +17273,17 @@ snapshots: dependencies: punycode: 2.3.1 + uri-template-router@1.0.0: {} + url-parse@1.5.10: dependencies: querystringify: 2.2.0 requires-port: 1.0.0 + url-template@3.1.1: {} + + urlpattern-polyfill@10.1.0: {} + use-callback-ref@1.3.3(@types/react@19.2.15)(react@19.2.6): dependencies: react: 19.2.6 @@ -17332,6 +17567,8 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + yaml@2.9.0: {} yargs-parser@21.1.1: {}