diff --git a/.changeset/perfect-chicken-scream.md b/.changeset/perfect-chicken-scream.md new file mode 100644 index 00000000000..552257a3977 --- /dev/null +++ b/.changeset/perfect-chicken-scream.md @@ -0,0 +1,5 @@ +--- +"@clerk/astro": minor +--- + +Introduce Keyless quickstart for Astro. This allows the Clerk SDK to be used without having to sign up and paste your keys manually. diff --git a/integration/tests/astro/keyless.test.ts b/integration/tests/astro/keyless.test.ts new file mode 100644 index 00000000000..c0808437173 --- /dev/null +++ b/integration/tests/astro/keyless.test.ts @@ -0,0 +1,54 @@ +import { test } from '@playwright/test'; + +import type { Application } from '../../models/application'; +import { appConfigs } from '../../presets'; +import { + testClaimedAppWithMissingKeys, + testKeylessRemovedAfterEnvAndRestart, + testToggleCollapsePopoverAndClaim, +} from '../../testUtils/keylessHelpers'; + +const commonSetup = appConfigs.astro.node.clone(); + +test.describe('Keyless mode @astro', () => { + test.describe.configure({ mode: 'serial' }); + test.setTimeout(90_000); + + test.use({ + extraHTTPHeaders: { + 'x-vercel-protection-bypass': process.env.VERCEL_AUTOMATION_BYPASS_SECRET || '', + }, + }); + + let app: Application; + let dashboardUrl = 'https://dashboard.clerk.com/'; + + test.beforeAll(async () => { + app = await commonSetup.commit(); + await app.setup(); + await app.withEnv(appConfigs.envs.withKeyless); + if (appConfigs.envs.withKeyless.privateVariables.get('CLERK_API_URL')?.includes('clerkstage')) { + dashboardUrl = 'https://dashboard.clerkstage.dev/'; + } + await app.dev(); + }); + + test.afterAll(async () => { + await app?.teardown(); + }); + + test('Toggle collapse popover and claim.', async ({ page, context }) => { + await testToggleCollapsePopoverAndClaim({ page, context, app, dashboardUrl, framework: 'astro' }); + }); + + test('Lands on claimed application with missing explicit keys, expanded by default, click to get keys from dashboard.', async ({ + page, + context, + }) => { + await testClaimedAppWithMissingKeys({ page, context, app, dashboardUrl }); + }); + + test('Keyless popover is removed after adding keys to .env and restarting.', async ({ page, context }) => { + await testKeylessRemovedAfterEnvAndRestart({ page, context, app }); + }); +}); diff --git a/packages/astro/package.json b/packages/astro/package.json index 86a88fd1341..ed5e051a1cf 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -97,7 +97,7 @@ }, "devDependencies": { "@clerk/ui": "workspace:^", - "astro": "^5.15.9" + "astro": "^5.17.1" }, "peerDependencies": { "astro": "^4.15.0 || ^5.0.0" diff --git a/packages/astro/src/env.d.ts b/packages/astro/src/env.d.ts index 933fc2aea3f..205d514c786 100644 --- a/packages/astro/src/env.d.ts +++ b/packages/astro/src/env.d.ts @@ -21,6 +21,7 @@ interface InternalEnv { readonly PUBLIC_CLERK_SIGN_UP_URL?: string; readonly PUBLIC_CLERK_TELEMETRY_DISABLED?: string; readonly PUBLIC_CLERK_TELEMETRY_DEBUG?: string; + readonly PUBLIC_CLERK_KEYLESS_DISABLED?: string; } interface ImportMeta { @@ -30,6 +31,9 @@ interface ImportMeta { declare namespace App { interface Locals { runtime: { env: InternalEnv }; + keylessClaimUrl?: string; + keylessApiKeysUrl?: string; + keylessPublishableKey?: string; } } diff --git a/packages/astro/src/integration/create-integration.ts b/packages/astro/src/integration/create-integration.ts index 1c94bcdfc6e..16644cf54bc 100644 --- a/packages/astro/src/integration/create-integration.ts +++ b/packages/astro/src/integration/create-integration.ts @@ -136,7 +136,7 @@ function createIntegration() function createClerkEnvSchema() { return { - PUBLIC_CLERK_PUBLISHABLE_KEY: envField.string({ context: 'client', access: 'public' }), + PUBLIC_CLERK_PUBLISHABLE_KEY: envField.string({ context: 'client', access: 'public', optional: true }), PUBLIC_CLERK_SIGN_IN_URL: envField.string({ context: 'client', access: 'public', optional: true }), PUBLIC_CLERK_SIGN_UP_URL: envField.string({ context: 'client', access: 'public', optional: true }), PUBLIC_CLERK_IS_SATELLITE: envField.boolean({ context: 'client', access: 'public', optional: true }), @@ -149,7 +149,20 @@ function createClerkEnvSchema() { PUBLIC_CLERK_PREFETCH_UI: envField.string({ context: 'client', access: 'public', optional: true }), PUBLIC_CLERK_TELEMETRY_DISABLED: envField.boolean({ context: 'client', access: 'public', optional: true }), PUBLIC_CLERK_TELEMETRY_DEBUG: envField.boolean({ context: 'client', access: 'public', optional: true }), - CLERK_SECRET_KEY: envField.string({ context: 'server', access: 'secret' }), + PUBLIC_CLERK_KEYLESS_CLAIM_URL: envField.string({ + context: 'client', + access: 'public', + optional: true, + url: true, + }), + PUBLIC_CLERK_KEYLESS_API_KEYS_URL: envField.string({ + context: 'client', + access: 'public', + optional: true, + url: true, + }), + PUBLIC_CLERK_KEYLESS_DISABLED: envField.boolean({ context: 'client', access: 'public', optional: true }), + CLERK_SECRET_KEY: envField.string({ context: 'server', access: 'secret', optional: true }), CLERK_MACHINE_SECRET_KEY: envField.string({ context: 'server', access: 'secret', optional: true }), CLERK_JWT_KEY: envField.string({ context: 'server', access: 'secret', optional: true }), }; diff --git a/packages/astro/src/internal/create-clerk-instance.ts b/packages/astro/src/internal/create-clerk-instance.ts index 1f84cd4a311..1a2455fa79d 100644 --- a/packages/astro/src/internal/create-clerk-instance.ts +++ b/packages/astro/src/internal/create-clerk-instance.ts @@ -9,7 +9,7 @@ import type { Ui } from '@clerk/ui/internal'; import { $clerkStore } from '../stores/external'; import { $clerk, $csrState } from '../stores/internal'; -import type { AstroClerkCreateInstanceParams, AstroClerkUpdateOptions } from '../types'; +import type { AstroClerkCreateInstanceParams, AstroClerkUpdateOptions, InternalRuntimeOptions } from '../types'; import { invokeClerkAstroJSFunctions } from './invoke-clerk-astro-js-functions'; import { mountAllClerkAstroJSComponents } from './mount-clerk-astro-js-components'; import { runOnce } from './run-once'; @@ -54,12 +54,18 @@ async function createClerkInstanceInternal(options?: AstroC $clerk.set(clerkJSInstance); } + const internalOptions = options as AstroClerkCreateInstanceParams & InternalRuntimeOptions; + const keylessClaimUrl = internalOptions.__internal_keylessClaimUrl; + const keylessApiKeysUrl = internalOptions.__internal_keylessApiKeysUrl; + const clerkOptions = { routerPush: createNavigationHandler(window.history.pushState.bind(window.history)), routerReplace: createNavigationHandler(window.history.replaceState.bind(window.history)), ...options, // Pass the clerk-ui constructor promise to clerk.load() ui: { ...options?.ui, ClerkUI }, + ...(keylessClaimUrl && { __internal_keyless_claimKeylessApplicationUrl: keylessClaimUrl }), + ...(keylessApiKeysUrl && { __internal_keyless_copyInstanceKeysUrl: keylessApiKeysUrl }), } as unknown as ClerkOptions; initOptions = clerkOptions; diff --git a/packages/astro/src/internal/merge-env-vars-with-params.ts b/packages/astro/src/internal/merge-env-vars-with-params.ts index bcecc86d790..39dbfd9820b 100644 --- a/packages/astro/src/internal/merge-env-vars-with-params.ts +++ b/packages/astro/src/internal/merge-env-vars-with-params.ts @@ -1,6 +1,6 @@ import { isTruthy } from '@clerk/shared/underscore'; -import type { AstroClerkIntegrationParams } from '../types'; +import type { AstroClerkIntegrationParams, InternalRuntimeOptions } from '../types'; /** * Merges `prefetchUI` param with env vars. @@ -25,7 +25,7 @@ function mergePrefetchUIConfig(paramPrefetchUI: AstroClerkIntegrationParams['pre /** * @internal */ -const mergeEnvVarsWithParams = (params?: AstroClerkIntegrationParams & { publishableKey?: string }) => { +const mergeEnvVarsWithParams = (params?: AstroClerkIntegrationParams & InternalRuntimeOptions) => { const { signInUrl: paramSignIn, signUpUrl: paramSignUp, @@ -42,13 +42,17 @@ const mergeEnvVarsWithParams = (params?: AstroClerkIntegrationParams & { publish ...rest } = params || {}; + const internalOptions = params; + return { signInUrl: paramSignIn || import.meta.env.PUBLIC_CLERK_SIGN_IN_URL, signUpUrl: paramSignUp || import.meta.env.PUBLIC_CLERK_SIGN_UP_URL, isSatellite: paramSatellite || import.meta.env.PUBLIC_CLERK_IS_SATELLITE, proxyUrl: paramProxy || import.meta.env.PUBLIC_CLERK_PROXY_URL, domain: paramDomain || import.meta.env.PUBLIC_CLERK_DOMAIN, - publishableKey: paramPublishableKey || import.meta.env.PUBLIC_CLERK_PUBLISHABLE_KEY || '', + // In keyless mode, use server-injected publishableKey from params + publishableKey: + paramPublishableKey || internalOptions?.publishableKey || import.meta.env.PUBLIC_CLERK_PUBLISHABLE_KEY || '', clerkJSUrl: paramClerkJSUrl || import.meta.env.PUBLIC_CLERK_JS_URL, clerkJSVersion: paramClerkJSVersion || import.meta.env.PUBLIC_CLERK_JS_VERSION, clerkUIUrl: paramClerkUIUrl || import.meta.env.PUBLIC_CLERK_UI_URL, @@ -58,6 +62,10 @@ const mergeEnvVarsWithParams = (params?: AstroClerkIntegrationParams & { publish disabled: isTruthy(import.meta.env.PUBLIC_CLERK_TELEMETRY_DISABLED), debug: isTruthy(import.meta.env.PUBLIC_CLERK_TELEMETRY_DEBUG), }, + // Read from params (server-injected via __CLERK_ASTRO_SAFE_VARS__) + // These are dynamically resolved by middleware, not from env vars + __internal_keylessClaimUrl: internalOptions?.keylessClaimUrl, + __internal_keylessApiKeysUrl: internalOptions?.keylessApiKeysUrl, ...rest, }; }; diff --git a/packages/astro/src/server/clerk-middleware.ts b/packages/astro/src/server/clerk-middleware.ts index fa0500f2197..1583fdb5fc2 100644 --- a/packages/astro/src/server/clerk-middleware.ts +++ b/packages/astro/src/server/clerk-middleware.ts @@ -24,10 +24,12 @@ import type { APIContext } from 'astro'; import { authAsyncStorage } from '#async-local-storage'; +import { canUseKeyless } from '../utils/feature-flags'; import { buildClerkHotloadScript } from './build-clerk-hotload-script'; import { clerkClient } from './clerk-client'; import { createCurrentUser } from './current-user'; import { getClientSafeEnv, getSafeEnv } from './get-safe-env'; +import { resolveKeysWithKeylessFallback } from './keyless/utils'; import { serverRedirectWithAuth } from './server-redirect-with-auth'; import type { AstroMiddleware, @@ -49,7 +51,7 @@ type ClerkAstroMiddlewareHandler = ( next: AstroMiddlewareNextParam, ) => AstroMiddlewareReturn | undefined; -type ClerkAstroMiddlewareOptions = AuthenticateRequestOptions; +export type ClerkAstroMiddlewareOptions = AuthenticateRequestOptions; /** * Middleware for Astro that handles authentication and authorization with Clerk. @@ -79,9 +81,42 @@ export const clerkMiddleware: ClerkMiddleware = (...args: unknown[]): any => { const clerkRequest = createClerkRequest(context.request); + // Resolve keyless URLs per-request in development + let keylessClaimUrl: string | undefined; + let keylessApiKeysUrl: string | undefined; + let keylessOptions = options; + + if (canUseKeyless) { + try { + const env = getSafeEnv(context); + const configuredPublishableKey = options?.publishableKey || env.pk; + const configuredSecretKey = options?.secretKey || env.sk; + + const keylessResult = await resolveKeysWithKeylessFallback( + configuredPublishableKey, + configuredSecretKey, + context, + ); + + keylessClaimUrl = keylessResult.claimUrl; + keylessApiKeysUrl = keylessResult.apiKeysUrl; + + // Override keys with keyless values if returned + if (keylessResult.publishableKey || keylessResult.secretKey) { + keylessOptions = { + ...options, + ...(keylessResult.publishableKey && { publishableKey: keylessResult.publishableKey }), + ...(keylessResult.secretKey && { secretKey: keylessResult.secretKey }), + }; + } + } catch { + // Silently fail - continue without keyless + } + } + const requestState = await clerkClient(context).authenticateRequest( clerkRequest, - createAuthenticateRequestOptions(clerkRequest, options, context), + createAuthenticateRequestOptions(clerkRequest, keylessOptions, context), ); const locationHeader = requestState.headers.get(constants.Headers.Location); @@ -104,6 +139,16 @@ export const clerkMiddleware: ClerkMiddleware = (...args: unknown[]): any => { decorateAstroLocal(clerkRequest, authObjectFn, context, requestState); + // Store keyless data for injection into client + if (keylessClaimUrl || keylessApiKeysUrl) { + context.locals.keylessClaimUrl = keylessClaimUrl; + context.locals.keylessApiKeysUrl = keylessApiKeysUrl; + // Also store the resolved publishable key so client can use it + if (keylessOptions?.publishableKey) { + context.locals.keylessPublishableKey = keylessOptions.publishableKey; + } + } + /** * ALS is crucial for guaranteeing SSR in UI frameworks like React. * This currently powers the `useAuth()` React hook and any other hook or Component that depends on it. diff --git a/packages/astro/src/server/get-safe-env.ts b/packages/astro/src/server/get-safe-env.ts index ebf98b99136..76600aac7c0 100644 --- a/packages/astro/src/server/get-safe-env.ts +++ b/packages/astro/src/server/get-safe-env.ts @@ -21,11 +21,14 @@ function getContextEnvVar(envVarName: keyof InternalEnv, contextOrLocals: Contex * @internal */ function getSafeEnv(context: ContextOrLocals) { + const locals = 'locals' in context ? context.locals : context; + return { domain: getContextEnvVar('PUBLIC_CLERK_DOMAIN', context), isSatellite: getContextEnvVar('PUBLIC_CLERK_IS_SATELLITE', context) === 'true', proxyUrl: getContextEnvVar('PUBLIC_CLERK_PROXY_URL', context), - pk: getContextEnvVar('PUBLIC_CLERK_PUBLISHABLE_KEY', context), + // Use keyless publishable key if available, otherwise read from env + pk: locals.keylessPublishableKey || getContextEnvVar('PUBLIC_CLERK_PUBLISHABLE_KEY', context), sk: getContextEnvVar('CLERK_SECRET_KEY', context), machineSecretKey: getContextEnvVar('CLERK_MACHINE_SECRET_KEY', context), signInUrl: getContextEnvVar('PUBLIC_CLERK_SIGN_IN_URL', context), @@ -39,6 +42,9 @@ function getSafeEnv(context: ContextOrLocals) { apiUrl: getContextEnvVar('CLERK_API_URL', context), telemetryDisabled: isTruthy(getContextEnvVar('PUBLIC_CLERK_TELEMETRY_DISABLED', context)), telemetryDebug: isTruthy(getContextEnvVar('PUBLIC_CLERK_TELEMETRY_DEBUG', context)), + // Read from locals (set by middleware) instead of env vars + keylessClaimUrl: locals.keylessClaimUrl, + keylessApiKeysUrl: locals.keylessApiKeysUrl, }; } @@ -50,12 +56,19 @@ function getSafeEnv(context: ContextOrLocals) { * This is a way to get around it. */ function getClientSafeEnv(context: ContextOrLocals) { + const locals = 'locals' in context ? context.locals : context; + return { domain: getContextEnvVar('PUBLIC_CLERK_DOMAIN', context), isSatellite: getContextEnvVar('PUBLIC_CLERK_IS_SATELLITE', context) === 'true', proxyUrl: getContextEnvVar('PUBLIC_CLERK_PROXY_URL', context), signInUrl: getContextEnvVar('PUBLIC_CLERK_SIGN_IN_URL', context), signUpUrl: getContextEnvVar('PUBLIC_CLERK_SIGN_UP_URL', context), + // In keyless mode, pass the resolved publishable key to client + publishableKey: locals.keylessPublishableKey || getContextEnvVar('PUBLIC_CLERK_PUBLISHABLE_KEY', context), + // Read from locals (set by middleware) instead of env vars + keylessClaimUrl: locals.keylessClaimUrl, + keylessApiKeysUrl: locals.keylessApiKeysUrl, }; } diff --git a/packages/astro/src/server/keyless/file-storage.ts b/packages/astro/src/server/keyless/file-storage.ts new file mode 100644 index 00000000000..f7f93c00b6b --- /dev/null +++ b/packages/astro/src/server/keyless/file-storage.ts @@ -0,0 +1,29 @@ +import { createNodeFileStorage, type KeylessStorage } from '@clerk/shared/keyless'; + +export type { KeylessStorage }; + +export interface FileStorageOptions { + cwd?: () => string; +} + +/** + * Creates a file-based storage adapter for keyless mode. + * Uses dynamic imports to avoid bundler issues with edge runtimes. + */ +export async function createFileStorage(options: FileStorageOptions = {}): Promise { + const { cwd = () => process.cwd() } = options; + + try { + const [fs, path] = await Promise.all([import('node:fs'), import('node:path')]); + + return createNodeFileStorage(fs, path, { + cwd, + frameworkPackageName: '@clerk/astro', + }); + } catch { + throw new Error( + 'Keyless mode requires a Node.js runtime with file system access. ' + + 'Set PUBLIC_CLERK_KEYLESS_DISABLED=1 or CLERK_KEYLESS_DISABLED=1 to disable keyless mode.', + ); + } +} diff --git a/packages/astro/src/server/keyless/index.ts b/packages/astro/src/server/keyless/index.ts new file mode 100644 index 00000000000..641fb961510 --- /dev/null +++ b/packages/astro/src/server/keyless/index.ts @@ -0,0 +1,86 @@ +import { createKeylessService } from '@clerk/shared/keyless'; +import type { APIContext } from 'astro'; + +import { clerkClient } from '../clerk-client'; +import { createFileStorage } from './file-storage.js'; + +let keylessServiceInstance: ReturnType | null = null; +let keylessInitPromise: Promise | null> | null = null; + +function canUseFileSystem(): boolean { + try { + return typeof process !== 'undefined' && typeof process.cwd === 'function'; + } catch { + return false; + } +} + +/** + * Gets or creates the keyless service singleton. + * Returns null for non-Node.js runtimes (e.g., Cloudflare Workers). + */ +export async function keyless(context: APIContext): Promise | null> { + if (!canUseFileSystem()) { + return null; + } + + if (keylessServiceInstance) { + return keylessServiceInstance; + } + + if (keylessInitPromise) { + return keylessInitPromise; + } + + keylessInitPromise = (async () => { + try { + const storage = await createFileStorage(); + + const service = createKeylessService({ + storage, + api: { + async createAccountlessApplication(requestHeaders?: Headers) { + try { + return await clerkClient(context).__experimental_accountlessApplications.createAccountlessApplication({ + requestHeaders, + }); + } catch { + return null; + } + }, + async completeOnboarding(requestHeaders?: Headers) { + try { + return await clerkClient( + context, + ).__experimental_accountlessApplications.completeAccountlessApplicationOnboarding({ + requestHeaders, + }); + } catch { + return null; + } + }, + }, + framework: 'astro', + frameworkVersion: PACKAGE_VERSION, + }); + + keylessServiceInstance = service; + return service; + } catch (error) { + console.warn('[Clerk] Failed to initialize keyless service:', error); + return null; + } finally { + keylessInitPromise = null; + } + })(); + + return keylessInitPromise; +} + +/** + * @internal + */ +export function resetKeylessService(): void { + keylessServiceInstance = null; + keylessInitPromise = null; +} diff --git a/packages/astro/src/server/keyless/utils.ts b/packages/astro/src/server/keyless/utils.ts new file mode 100644 index 00000000000..f0f0d0e0898 --- /dev/null +++ b/packages/astro/src/server/keyless/utils.ts @@ -0,0 +1,23 @@ +import { resolveKeysWithKeylessFallback as sharedResolveKeysWithKeylessFallback } from '@clerk/shared/keyless'; +import type { APIContext } from 'astro'; +export type { KeylessResult } from '@clerk/shared/keyless'; + +import { canUseKeyless } from '../../utils/feature-flags'; +import { keyless } from './index'; + +/** + * Resolves Clerk keys, falling back to keyless mode in development if configured keys are missing. + */ +export async function resolveKeysWithKeylessFallback( + configuredPublishableKey: string | undefined, + configuredSecretKey: string | undefined, + context: APIContext, +) { + const keylessService = await keyless(context); + return sharedResolveKeysWithKeylessFallback( + configuredPublishableKey, + configuredSecretKey, + keylessService, + canUseKeyless, + ); +} diff --git a/packages/astro/src/types.ts b/packages/astro/src/types.ts index 7fd48f44083..8e65d322c27 100644 --- a/packages/astro/src/types.ts +++ b/packages/astro/src/types.ts @@ -51,6 +51,33 @@ type AstroClerkCreateInstanceParams = AstroClerkIntegration publishableKey: string; }; +/** + * @internal + * Internal runtime options injected by the server for keyless mode support. + */ +export type InternalRuntimeOptions = { + /** + * Server-injected publishable key from keyless mode or context.locals + */ + publishableKey?: string; + /** + * Keyless claim URL injected by middleware for the client-side banner + */ + keylessClaimUrl?: string; + /** + * Keyless API keys URL injected by middleware for the client-side banner + */ + keylessApiKeysUrl?: string; + /** + * Internal keyless claim URL passed to Clerk.load() + */ + __internal_keylessClaimUrl?: string; + /** + * Internal keyless API keys URL passed to Clerk.load() + */ + __internal_keylessApiKeysUrl?: string; +}; + // Copied from `@clerk/react` export interface HeadlessBrowserClerk extends Clerk { load: (opts?: Without) => Promise; diff --git a/packages/astro/src/utils/feature-flags.ts b/packages/astro/src/utils/feature-flags.ts new file mode 100644 index 00000000000..94421cb9937 --- /dev/null +++ b/packages/astro/src/utils/feature-flags.ts @@ -0,0 +1,10 @@ +import { getEnvVariable } from '@clerk/shared/getEnvVariable'; +import { isTruthy } from '@clerk/shared/underscore'; +import { isDevelopmentEnvironment } from '@clerk/shared/utils'; + +const KEYLESS_DISABLED = + isTruthy(getEnvVariable('PUBLIC_CLERK_KEYLESS_DISABLED')) || + isTruthy(getEnvVariable('CLERK_KEYLESS_DISABLED')) || + false; + +export const canUseKeyless = isDevelopmentEnvironment() && !KEYLESS_DISABLED; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72c2b119821..35a8bffa8db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -386,7 +386,7 @@ importers: specifier: workspace:^ version: link:../ui astro: - specifier: ^5.15.9 + specifier: ^5.17.1 version: 5.17.1(@types/node@22.19.0)(db0@0.3.4)(idb-keyval@6.2.1)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.1)(terser@5.44.1)(tsx@4.20.6)(typescript@5.8.3)(yaml@2.8.1) packages/backend: @@ -2475,7 +2475,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.22.26': resolution: {integrity: sha512-I689wc8Fn/AX7aUGiwrh3HnssiORMJtR2fpksX+JIe8Cj/EDleblYMSwRPd0025wrwOV9UN1KM/RuEt/QjCS3Q==} @@ -8867,11 +8867,13 @@ packages: glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@11.0.3: resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@13.0.0: @@ -8880,11 +8882,11 @@ packages: glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} @@ -10103,7 +10105,6 @@ packages: keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -13581,12 +13582,12 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me tar@7.5.2: resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}