From 9dde99783019ee763318df2c1a6b04077aa2d6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 11 Aug 2026 16:32:34 +0200 Subject: [PATCH 1/3] Fix compatibility with external CIMD URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Appwrite's apps.get()/apps.list() no longer resolve CIMD URLs — appId is always a plain ID now. Detect URL-shaped OAuth2 client IDs in the console and fetch the Client ID Metadata Document directly from the browser, mapping its RFC 7591 fields onto Models.App for consent/device/applications rendering. Fetch or validation failures fall back to hostname-only branding so the flow never blocks. Co-Authored-By: Claude Fable 5 --- src/lib/helpers/oauth2-cimd.test.ts | 76 ++++++++++ src/lib/helpers/oauth2-cimd.ts | 141 ++++++++++++++++++ .../(console)/account/applications/+page.ts | 4 +- .../(public)/oauth2/consent/+page.svelte | 7 +- .../(public)/oauth2/device/+page.svelte | 5 +- 5 files changed, 224 insertions(+), 9 deletions(-) create mode 100644 src/lib/helpers/oauth2-cimd.test.ts create mode 100644 src/lib/helpers/oauth2-cimd.ts diff --git a/src/lib/helpers/oauth2-cimd.test.ts b/src/lib/helpers/oauth2-cimd.test.ts new file mode 100644 index 0000000000..fce9d1b2a2 --- /dev/null +++ b/src/lib/helpers/oauth2-cimd.test.ts @@ -0,0 +1,76 @@ +import { cimdDocumentToApp, isCimdClientId } from '$lib/helpers/oauth2-cimd'; +import { describe, expect, it } from 'vitest'; + +describe('isCimdClientId', () => { + it('accepts https URLs', () => { + expect(isCimdClientId('https://example.com/oauth/client-metadata.json')).toBe(true); + }); + + it('accepts http only for loopback', () => { + expect(isCimdClientId('http://localhost:3000/client.json')).toBe(true); + expect(isCimdClientId('http://127.0.0.1/client.json')).toBe(true); + expect(isCimdClientId('http://example.com/client.json')).toBe(false); + }); + + it('rejects plain app IDs and non-http schemes', () => { + expect(isCimdClientId('my-app_1.0')).toBe(false); + expect(isCimdClientId('64f1e2a9b3c4d5e6f7a8')).toBe(false); + expect(isCimdClientId('javascript:alert(1)')).toBe(false); + }); +}); + +describe('cimdDocumentToApp', () => { + const clientId = 'https://example.com/oauth/client-metadata.json'; + + it('maps RFC 7591 metadata onto the App model', () => { + const app = cimdDocumentToApp(clientId, { + client_id: clientId, + client_name: 'Example App', + client_uri: 'https://example.com', + logo_uri: 'https://example.com/logo.png', + policy_uri: 'https://example.com/privacy', + tos_uri: 'https://example.com/terms', + contacts: ['support@example.com'], + redirect_uris: ['https://example.com/callback'], + token_endpoint_auth_method: 'none', + grant_types: ['authorization_code', 'urn:ietf:params:oauth:grant-type:device_code'] + }); + + expect(app.$id).toBe(clientId); + expect(app.name).toBe('Example App'); + expect(app.clientUri).toBe('https://example.com'); + expect(app.logoUri).toBe('https://example.com/logo.png'); + expect(app.privacyPolicyUrl).toBe('https://example.com/privacy'); + expect(app.termsUrl).toBe('https://example.com/terms'); + expect(app.contacts).toEqual(['support@example.com']); + expect(app.redirectUris).toEqual(['https://example.com/callback']); + expect(app.type).toBe('public'); + expect(app.deviceFlow).toBe(true); + expect(app.enabled).toBe(true); + }); + + it('falls back to the hostname when client_name is missing', () => { + const app = cimdDocumentToApp(clientId, { client_id: clientId }); + expect(app.name).toBe('example.com'); + expect(app.deviceFlow).toBe(false); + }); + + it('rejects a document whose client_id does not match its URL', () => { + expect(() => + cimdDocumentToApp(clientId, { client_id: 'https://evil.example/other.json' }) + ).toThrow(); + expect(() => cimdDocumentToApp(clientId, 'not an object')).toThrow(); + }); + + it('drops unrenderable URI values', () => { + const app = cimdDocumentToApp(clientId, { + client_id: clientId, + logo_uri: 'javascript:alert(1)', + client_uri: 'not a url', + contacts: ['ok', 42] + }); + expect(app.logoUri).toBe(''); + expect(app.clientUri).toBe(''); + expect(app.contacts).toEqual(['ok']); + }); +}); diff --git a/src/lib/helpers/oauth2-cimd.ts b/src/lib/helpers/oauth2-cimd.ts new file mode 100644 index 0000000000..bf0d9881cb --- /dev/null +++ b/src/lib/helpers/oauth2-cimd.ts @@ -0,0 +1,141 @@ +import { sdk } from '$lib/stores/sdk'; +import type { Models } from '@appwrite.io/console'; + +/** + * CIMD (Client ID Metadata Document) support: a `client_id` may be an HTTPS + * URL pointing to a JSON document of RFC 7591 client metadata. The Appwrite + * API no longer resolves these — `apps.get()` accepts plain app IDs only — so + * the console fetches the document itself for consent-screen branding. The + * server still validates the client during authorization, so a failed fetch + * only degrades branding, never security. + */ + +const FETCH_TIMEOUT = 10_000; + +const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code'; + +/** The RFC 7591 metadata fields the console renders. */ +type CimdDocument = { + client_id?: unknown; + client_name?: unknown; + client_uri?: unknown; + logo_uri?: unknown; + policy_uri?: unknown; + tos_uri?: unknown; + contacts?: unknown; + redirect_uris?: unknown; + post_logout_redirect_uris?: unknown; + token_endpoint_auth_method?: unknown; + grant_types?: unknown; +}; + +function isLoopback(hostname: string): boolean { + return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]'; +} + +/** + * Plain app IDs are at most 36 chars of `[a-zA-Z0-9._-]` and never parse as + * absolute URLs, so anything URL-shaped is a CIMD client_id. HTTP is only + * accepted for loopback (local development). + */ +export function isCimdClientId(clientId: string): boolean { + let url: URL; + try { + url = new URL(clientId); + } catch { + return false; + } + return url.protocol === 'https:' || (url.protocol === 'http:' && isLoopback(url.hostname)); +} + +/** Documents are untrusted input — only pass through values safe to render in href/src. */ +function httpUrl(value: unknown): string { + if (typeof value !== 'string') return ''; + try { + const url = new URL(value); + return url.protocol === 'https:' || url.protocol === 'http:' ? value : ''; + } catch { + return ''; + } +} + +function stringArray(value: unknown): string[] { + return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : []; +} + +/** A minimal app so consent can still render when the document is unavailable. */ +function fallbackApp(clientId: string): Models.App { + return { + $id: clientId, + $createdAt: '', + $updatedAt: '', + name: new URL(clientId).hostname, + description: '', + clientUri: '', + logoUri: '', + privacyPolicyUrl: '', + termsUrl: '', + contacts: [], + tagline: '', + tags: [], + images: [], + supportUrl: '', + dataDeletionUrl: '', + redirectUris: [], + postLogoutRedirectUris: [], + enabled: true, + type: 'public', + deviceFlow: false, + teamId: '', + userId: '', + secrets: [] + }; +} + +export function cimdDocumentToApp(clientId: string, document: unknown): Models.App { + if (typeof document !== 'object' || document === null) { + throw new Error('CIMD document is not a JSON object'); + } + const doc = document as CimdDocument; + // The document's client_id MUST equal the URL it was fetched from — a + // mismatch means the document describes a different client. + if (doc.client_id !== clientId) { + throw new Error('CIMD document client_id does not match its URL'); + } + const name = typeof doc.client_name === 'string' ? doc.client_name.trim() : ''; + return { + ...fallbackApp(clientId), + name: name || new URL(clientId).hostname, + clientUri: httpUrl(doc.client_uri), + logoUri: httpUrl(doc.logo_uri), + privacyPolicyUrl: httpUrl(doc.policy_uri), + termsUrl: httpUrl(doc.tos_uri), + contacts: stringArray(doc.contacts), + redirectUris: stringArray(doc.redirect_uris), + postLogoutRedirectUris: stringArray(doc.post_logout_redirect_uris), + type: doc.token_endpoint_auth_method === 'none' ? 'public' : 'confidential', + deviceFlow: stringArray(doc.grant_types).includes(DEVICE_GRANT_TYPE) + }; +} + +/** + * Resolve an app for display: plain IDs via the API, CIMD URLs by fetching + * the document directly. CIMD fetch/validation failures fall back to + * hostname-only branding rather than blocking the flow. + */ +export async function getOAuth2App(appId: string): Promise { + if (!isCimdClientId(appId)) { + return sdk.forConsole.apps.get({ appId }); + } + try { + const response = await fetch(appId, { + headers: { accept: 'application/json' }, + credentials: 'omit', + signal: AbortSignal.timeout(FETCH_TIMEOUT) + }); + if (!response.ok) throw new Error(`CIMD document request failed: ${response.status}`); + return cimdDocumentToApp(appId, await response.json()); + } catch { + return fallbackApp(appId); + } +} diff --git a/src/routes/(console)/account/applications/+page.ts b/src/routes/(console)/account/applications/+page.ts index 14645b4465..c453ee958c 100644 --- a/src/routes/(console)/account/applications/+page.ts +++ b/src/routes/(console)/account/applications/+page.ts @@ -1,5 +1,5 @@ import { Dependencies } from '$lib/constants'; -import { sdk } from '$lib/stores/sdk'; +import { getOAuth2App } from '$lib/helpers/oauth2-cimd'; import type { Models } from '@appwrite.io/console'; import type { PageLoad } from './$types'; @@ -19,7 +19,7 @@ export const load: PageLoad = async ({ depends, parent }) => { const connectedApps = await Promise.all( grants.map(async (identity) => { const appId = identity.provider.slice(OAUTH2_PREFIX.length); - const app = await sdk.forConsole.apps.get({ appId }).catch(() => null); + const app = await getOAuth2App(appId).catch(() => null); return { identity, appId, app }; }) ); diff --git a/src/routes/(public)/oauth2/consent/+page.svelte b/src/routes/(public)/oauth2/consent/+page.svelte index 81553b1727..0599bd277e 100644 --- a/src/routes/(public)/oauth2/consent/+page.svelte +++ b/src/routes/(public)/oauth2/consent/+page.svelte @@ -9,6 +9,7 @@ import { sdk } from '$lib/stores/sdk'; import { logout } from '$lib/helpers/logout'; import { isWebRedirect } from '$lib/helpers/oauth2-redirect'; + import { getOAuth2App } from '$lib/helpers/oauth2-cimd'; import OAuth2ConsentCard, { type OAuth2Outcome } from '../consent-card.svelte'; import OAuth2OutcomeCard from '../outcome-card.svelte'; import { OAuth2ErrorMessage, OAuth2ErrorType } from '../errors'; @@ -85,7 +86,7 @@ ): Promise { const loadedGrant = await sdk.forConsole.oauth2.getGrant({ grantId }); const [loadedApp, loadedAccount] = await Promise.all([ - sdk.forConsole.apps.get({ appId: loadedGrant.appId }), + getOAuth2App(loadedGrant.appId), knownAccount !== undefined ? Promise.resolve(knownAccount) : getAccount() ]); if (cancelled()) return; @@ -122,9 +123,7 @@ if (!isWebRedirect(result.redirectUrl)) { completedRedirectUrl = result.redirectUrl; account = loggedInAccount; - app = clientId - ? await sdk.forConsole.apps.get({ appId: clientId }).catch(() => null) - : null; + app = clientId ? await getOAuth2App(clientId).catch(() => null) : null; if (cancelled()) return; phase = 'approved'; } diff --git a/src/routes/(public)/oauth2/device/+page.svelte b/src/routes/(public)/oauth2/device/+page.svelte index 15f37b0d4c..15e0c43c03 100644 --- a/src/routes/(public)/oauth2/device/+page.svelte +++ b/src/routes/(public)/oauth2/device/+page.svelte @@ -10,6 +10,7 @@ import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; + import { getOAuth2App } from '$lib/helpers/oauth2-cimd'; import OAuth2ConsentCard, { type OAuth2Flow, type OAuth2Outcome } from '../consent-card.svelte'; import OAuth2OutcomeCard from '../outcome-card.svelte'; @@ -95,9 +96,7 @@ const loadedGrant = await sdk.forConsole.oauth2.createGrant({ userCode: normalized }); - const loadedApp = await sdk.forConsole.apps.get({ - appId: loadedGrant.appId - }); + const loadedApp = await getOAuth2App(loadedGrant.appId); // A fresh `user_code` may have arrived while we awaited. Ignore this // now-stale result so we never show consent for a superseded request. if (normalizeUserCode(code) !== normalized) return; From 8ed7be09ae76994ac8ba99b25e0d338ddafcb0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 11 Aug 2026 16:36:53 +0200 Subject: [PATCH 2/3] refactor(oauth2): inline the one-off CIMD helpers Co-Authored-By: Claude Fable 5 --- src/lib/helpers/oauth2-cimd.ts | 127 ++++++++++++--------------------- 1 file changed, 47 insertions(+), 80 deletions(-) diff --git a/src/lib/helpers/oauth2-cimd.ts b/src/lib/helpers/oauth2-cimd.ts index bf0d9881cb..7d631f678e 100644 --- a/src/lib/helpers/oauth2-cimd.ts +++ b/src/lib/helpers/oauth2-cimd.ts @@ -1,20 +1,14 @@ import { sdk } from '$lib/stores/sdk'; import type { Models } from '@appwrite.io/console'; -/** - * CIMD (Client ID Metadata Document) support: a `client_id` may be an HTTPS - * URL pointing to a JSON document of RFC 7591 client metadata. The Appwrite - * API no longer resolves these — `apps.get()` accepts plain app IDs only — so - * the console fetches the document itself for consent-screen branding. The - * server still validates the client during authorization, so a failed fetch - * only degrades branding, never security. - */ +// CIMD (Client ID Metadata Document): a client_id may be an HTTPS URL pointing +// to a JSON document of RFC 7591 client metadata. The Appwrite API no longer +// resolves these, so the console fetches the document itself for branding. const FETCH_TIMEOUT = 10_000; - const DEVICE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code'; +const HTTP_URL = /^https?:\/\//i; -/** The RFC 7591 metadata fields the console renders. */ type CimdDocument = { client_id?: unknown; client_name?: unknown; @@ -29,100 +23,73 @@ type CimdDocument = { grant_types?: unknown; }; -function isLoopback(hostname: string): boolean { - return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]'; -} - -/** - * Plain app IDs are at most 36 chars of `[a-zA-Z0-9._-]` and never parse as - * absolute URLs, so anything URL-shaped is a CIMD client_id. HTTP is only - * accepted for loopback (local development). - */ +// Plain app IDs never parse as URLs; http is allowed for local development only. export function isCimdClientId(clientId: string): boolean { - let url: URL; try { - url = new URL(clientId); + const url = new URL(clientId); + return ( + url.protocol === 'https:' || + (url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) + ); } catch { return false; } - return url.protocol === 'https:' || (url.protocol === 'http:' && isLoopback(url.hostname)); } -/** Documents are untrusted input — only pass through values safe to render in href/src. */ -function httpUrl(value: unknown): string { - if (typeof value !== 'string') return ''; - try { - const url = new URL(value); - return url.protocol === 'https:' || url.protocol === 'http:' ? value : ''; - } catch { - return ''; +export function cimdDocumentToApp(clientId: string, document: unknown): Models.App { + if (typeof document !== 'object' || document === null) { + throw new Error('CIMD document is not a JSON object'); } -} - -function stringArray(value: unknown): string[] { - return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : []; -} - -/** A minimal app so consent can still render when the document is unavailable. */ -function fallbackApp(clientId: string): Models.App { + const doc = document as CimdDocument; + // The document's client_id must equal the URL it was fetched from. + if (doc.client_id !== clientId) { + throw new Error('CIMD document client_id does not match its URL'); + } + const name = typeof doc.client_name === 'string' ? doc.client_name.trim() : ''; return { $id: clientId, $createdAt: '', $updatedAt: '', - name: new URL(clientId).hostname, + name: name || new URL(clientId).hostname, description: '', - clientUri: '', - logoUri: '', - privacyPolicyUrl: '', - termsUrl: '', - contacts: [], + // Untrusted values rendered in href/src must be http(s) URLs. + clientUri: + typeof doc.client_uri === 'string' && HTTP_URL.test(doc.client_uri) + ? doc.client_uri + : '', + logoUri: + typeof doc.logo_uri === 'string' && HTTP_URL.test(doc.logo_uri) ? doc.logo_uri : '', + privacyPolicyUrl: + typeof doc.policy_uri === 'string' && HTTP_URL.test(doc.policy_uri) + ? doc.policy_uri + : '', + termsUrl: typeof doc.tos_uri === 'string' && HTTP_URL.test(doc.tos_uri) ? doc.tos_uri : '', + contacts: Array.isArray(doc.contacts) + ? doc.contacts.filter((contact) => typeof contact === 'string') + : [], tagline: '', tags: [], images: [], supportUrl: '', dataDeletionUrl: '', - redirectUris: [], - postLogoutRedirectUris: [], + redirectUris: Array.isArray(doc.redirect_uris) + ? doc.redirect_uris.filter((uri) => typeof uri === 'string') + : [], + postLogoutRedirectUris: Array.isArray(doc.post_logout_redirect_uris) + ? doc.post_logout_redirect_uris.filter((uri) => typeof uri === 'string') + : [], enabled: true, - type: 'public', - deviceFlow: false, + type: doc.token_endpoint_auth_method === 'none' ? 'public' : 'confidential', + deviceFlow: Array.isArray(doc.grant_types) && doc.grant_types.includes(DEVICE_GRANT_TYPE), teamId: '', userId: '', secrets: [] }; } -export function cimdDocumentToApp(clientId: string, document: unknown): Models.App { - if (typeof document !== 'object' || document === null) { - throw new Error('CIMD document is not a JSON object'); - } - const doc = document as CimdDocument; - // The document's client_id MUST equal the URL it was fetched from — a - // mismatch means the document describes a different client. - if (doc.client_id !== clientId) { - throw new Error('CIMD document client_id does not match its URL'); - } - const name = typeof doc.client_name === 'string' ? doc.client_name.trim() : ''; - return { - ...fallbackApp(clientId), - name: name || new URL(clientId).hostname, - clientUri: httpUrl(doc.client_uri), - logoUri: httpUrl(doc.logo_uri), - privacyPolicyUrl: httpUrl(doc.policy_uri), - termsUrl: httpUrl(doc.tos_uri), - contacts: stringArray(doc.contacts), - redirectUris: stringArray(doc.redirect_uris), - postLogoutRedirectUris: stringArray(doc.post_logout_redirect_uris), - type: doc.token_endpoint_auth_method === 'none' ? 'public' : 'confidential', - deviceFlow: stringArray(doc.grant_types).includes(DEVICE_GRANT_TYPE) - }; -} - -/** - * Resolve an app for display: plain IDs via the API, CIMD URLs by fetching - * the document directly. CIMD fetch/validation failures fall back to - * hostname-only branding rather than blocking the flow. - */ +// Plain IDs resolve via the API; CIMD URLs are fetched directly. Fetch or +// validation failures fall back to hostname-only branding rather than blocking +// the flow — the server still validates the client during authorization. export async function getOAuth2App(appId: string): Promise { if (!isCimdClientId(appId)) { return sdk.forConsole.apps.get({ appId }); @@ -136,6 +103,6 @@ export async function getOAuth2App(appId: string): Promise { if (!response.ok) throw new Error(`CIMD document request failed: ${response.status}`); return cimdDocumentToApp(appId, await response.json()); } catch { - return fallbackApp(appId); + return cimdDocumentToApp(appId, { client_id: appId }); } } From 6b7c35ae15314a569fc9203b731b3d6039663b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 11 Aug 2026 16:44:21 +0200 Subject: [PATCH 3/3] chore(deps): raise the nanoid floor past GHSA-28wg-ghj8-5hjv and GHSA-2v37-7h3g-55p8 bun audit fails the CI build on three high nanoid advisories. All fixes are within the existing semver ranges: the direct dependency and @melt-ui/svelte's nested copy move to 5.1.16, and the 3.x copies under @ai-sdk/provider-utils and postcss move to 3.3.17. Co-Authored-By: Claude Fable 5 --- bun.lock | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bun.lock b/bun.lock index 263bb6ac76..eed9e9aa41 100644 --- a/bun.lock +++ b/bun.lock @@ -37,7 +37,7 @@ "flatted": "^3.4.2", "ignore": "^6.0.2", "json5": "^2.2.3", - "nanoid": "^5.1.11", + "nanoid": "^5.1.16", "nanotar": "^0.3.0", "pretty-bytes": "^6.1.1", "remarkable": "^2.0.1", @@ -1128,7 +1128,7 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="], + "nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], "nanotar": ["nanotar@0.3.0", "", {}, "sha512-Kv2JYYiCzt16Kt5QwAc9BFG89xfPNBx+oQL4GQXD9nLqPkZBiNaqaCWtwnbk/q7UVsTYevvM1b0UF8zmEI4pCg=="], @@ -1500,7 +1500,7 @@ "@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.0.11", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-CPyImHGiT3svyfmvPvAFTianZzWFtm0qK82XjwlQIA1C3IQ2iku/PMQXi7aFyrX0TyMh3VTkJPB03tjU2VXVrw=="], - "@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.17", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g=="], "@ai-sdk/ui-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.0.11", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-CPyImHGiT3svyfmvPvAFTianZzWFtm0qK82XjwlQIA1C3IQ2iku/PMQXi7aFyrX0TyMh3VTkJPB03tjU2VXVrw=="], @@ -1524,7 +1524,7 @@ "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "@melt-ui/svelte/nanoid": ["nanoid@5.1.7", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-ua3NDgISf6jdwezAheMOk4mbE1LXjm1DfMUDMuJf4AqxLFK3ccGpgWizwa5YV7Yz9EpXwEaWoRXSb/BnV0t5dQ=="], + "@melt-ui/svelte/nanoid": ["nanoid@5.1.16", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ=="], "@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], @@ -1574,7 +1574,7 @@ "popmotion/tslib": ["tslib@2.4.0", "", {}, "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="], - "postcss/nanoid": ["nanoid@3.3.16", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q=="], + "postcss/nanoid": ["nanoid@3.3.17", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g=="], "style-value-types/tslib": ["tslib@2.4.0", "", {}, "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="], diff --git a/package.json b/package.json index d8a7e51e64..86727369cc 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "flatted": "^3.4.2", "ignore": "^6.0.2", "json5": "^2.2.3", - "nanoid": "^5.1.11", + "nanoid": "^5.1.16", "nanotar": "^0.3.0", "pretty-bytes": "^6.1.1", "remarkable": "^2.0.1",