From 52245ad694c855e1b6f20b35b9f5f4d123e5304e Mon Sep 17 00:00:00 2001 From: jdalton Date: Sat, 28 Jun 2025 16:40:05 -0400 Subject: [PATCH] Add support for custom registries to updateNotifier --- package-lock.json | 115 ++++++++++++++++++++++++ package.json | 2 + patches/tiny-updater#3.5.3.patch | 150 +++++++++++++++++++++++++++++++ src/cli.mts | 8 +- 4 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 patches/tiny-updater#3.5.3.patch diff --git a/package-lock.json b/package-lock.json index 8bad93460..1d5467bd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,8 @@ "open": "10.1.2", "oxlint": "1.3.0", "pony-cause": "2.1.11", + "registry-auth-token": "5.1.0", + "registry-url": "7.2.0", "rollup": "4.44.1", "semver": "7.7.2", "synp": "1.9.14", @@ -3269,6 +3271,16 @@ "node": ">=14" } }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, "node_modules/@pnpm/constants": { "version": "1001.1.0", "resolved": "https://registry.npmjs.org/@pnpm/constants/-/constants-1001.1.0.tgz", @@ -3521,6 +3533,41 @@ "url": "https://opencollective.com/pnpm" } }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@pnpm/object.key-sorting": { "version": "1000.0.1", "resolved": "https://registry.npmjs.org/@pnpm/object.key-sorting/-/object.key-sorting-1000.0.1.tgz", @@ -7021,6 +7068,24 @@ "dev": true, "license": "MIT" }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -10023,6 +10088,19 @@ "node": ">= 0.8" } }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -13907,6 +13985,13 @@ "url": "https://github.com/steveukx/properties?sponsor=1" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -14254,6 +14339,36 @@ "regexp-tree": "bin/regexp-tree" } }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-7.2.0.tgz", + "integrity": "sha512-I5UEBQ+09LWKInA1fPswOMZps0cs2Z+IQXb5Z5EkTJiUmIN52Vm/FD3ji5X82c5jIXL3nWEWOrYK0RkON6Oqyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.1", + "ini": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/regjsparser": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", diff --git a/package.json b/package.json index b55ac60f2..e9c8759d3 100644 --- a/package.json +++ b/package.json @@ -159,6 +159,8 @@ "open": "10.1.2", "oxlint": "1.3.0", "pony-cause": "2.1.11", + "registry-auth-token": "5.1.0", + "registry-url": "7.2.0", "rollup": "4.44.1", "semver": "7.7.2", "synp": "1.9.14", diff --git a/patches/tiny-updater#3.5.3.patch b/patches/tiny-updater#3.5.3.patch new file mode 100644 index 000000000..eab59dd1a --- /dev/null +++ b/patches/tiny-updater#3.5.3.patch @@ -0,0 +1,150 @@ +Index: /tiny-updater/dist/index.d.ts +=================================================================== +--- /tiny-updater/dist/index.d.ts ++++ /tiny-updater/dist/index.d.ts +@@ -1,4 +1,10 @@ + import type { Options } from './types.js'; +-declare const updater: ({ name, version, ttl }: Options) => Promise; ++declare const updater: ({ ++ authInfo, ++ name, ++ registryUrl, ++ version, ++ ttl ++}: Options) => Promise; + export default updater; + export type { Options }; +Index: /tiny-updater/dist/index.js +=================================================================== +--- /tiny-updater/dist/index.js ++++ /tiny-updater/dist/index.js +@@ -2,13 +2,22 @@ + import Store from './store.js'; + import Utils from './utils.js'; + /* MAIN */ + //TODO: Account for non-latest releases +-const updater = async ({ name, version, ttl = 0 }) => { ++const updater = async (options) => { ++ const { ++ authInfo, ++ name, ++ registryUrl, ++ version, ++ ttl = 0, ++ } = { __proto__: null, ...options }; + const record = Store.get(name); + const timestamp = Date.now(); + const isFresh = !record || (timestamp - record.timestampFetch) >= ttl; +- const latest = isFresh ? await Utils.getLatestVersion(name).catch(Utils.noop) : record?.version; ++ const latest = isFresh ++ ? await Utils.getLatestVersion(name, { authInfo, registryUrl }).catch(Utils.noop) ++ : record?.version; + if (!latest) + return false; + if (isFresh) { + const record = { timestampFetch: timestamp, timestampNotification: timestamp, version: latest }; +Index: /tiny-updater/dist/types.d.ts +=================================================================== +--- /tiny-updater/dist/types.d.ts ++++ /tiny-updater/dist/types.d.ts +@@ -1,11 +1,31 @@ ++ ++type AuthInfo = { ++ type: string; ++ token: string; ++}; + type Options = { ++ authInfo?: AuthInfo | undefined; + name: string; ++ registryUrl?: string | undefined; + version: string; +- ttl?: number; ++ ttl?: number | undefined; + }; + type StoreRecord = { + timestampFetch: number; + timestampNotification: number; + version: string; + }; +-export type { Options, StoreRecord }; ++type UtilsFetchOptions = { ++ authInfo?: AuthInfo | undefined; ++}; ++type UtilsGetLatestVersionOptions = { ++ authInfo?: AuthInfo | undefined; ++ registryUrl?: string | undefined; ++}; ++export type { ++ AuthInfo, ++ Options, ++ StoreRecord, ++ UtilsFetchOptions, ++ UtilsGetLatestVersionOptions ++}; +Index: /tiny-updater/dist/utils.d.ts +=================================================================== +--- /tiny-updater/dist/utils.d.ts ++++ /tiny-updater/dist/utils.d.ts +@@ -1,10 +1,14 @@ ++import { UtilsFetchOptions, UtilsGetLatestVersionOptions } from './types'; + declare const Utils: { +- fetch: (url: string) => Promise<{ ++ fetch: (url: string, options?: UtilsFetchOptions | undefined) => Promise<{ + version?: string; + }>; + getExitSignal: () => AbortSignal; +- getLatestVersion: (name: string) => Promise; ++ getLatestVersion: ( ++ name: string, ++ options?: UtilsGetLatestVersionOptions | undefined ++ ) => Promise; + isNumber: (value: unknown) => value is number; + isString: (value: unknown) => value is string; + isUpdateAvailable: (current: string, latest: string) => boolean; + noop: () => undefined; +Index: /tiny-updater/dist/utils.js +=================================================================== +--- /tiny-updater/dist/utils.js ++++ /tiny-updater/dist/utils.js +@@ -4,23 +4,35 @@ + import compare from './compare.js'; + /* MAIN */ + const Utils = { + /* API */ +- fetch: async (url) => { ++ fetch: async (url, options = {}) => { ++ const { authInfo } = { __proto__: null, ...options }; ++ const headers = new Headers({ ++ 'Accept': 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*' ++ }); ++ if (authInfo) { ++ headers.set('Authorization', `${authInfo.type} ${authInfo.token}`); ++ } + const signal = Utils.getExitSignal(); +- const request = await fetch(url, { signal }); ++ const request = await fetch(url, { headers, signal }); + const json = await request.json(); + return json; + }, + getExitSignal: () => { + const aborter = new AbortController(); + whenExit(() => aborter.abort()); + return aborter.signal; + }, +- getLatestVersion: async (name) => { +- const latestUrl = `https://registry.npmjs.org/${name}/latest`; +- const latest = await Utils.fetch(latestUrl); +- return latest.version; ++ getLatestVersion: async (name, options = {}) => { ++ const { ++ authInfo, ++ registryUrl = 'https://registry.npmjs.org/', ++ } = { __proto__: null, ...options }; ++ const maybeSlash = registryUrl.endsWith('/') ? '' : '/'; ++ const latestUrl = `${registryUrl}${maybeSlash}${name}/latest`; ++ const json = await Utils.fetch(latestUrl, { authInfo }); ++ return json.version; + }, + isNumber: (value) => { + return typeof value === 'number'; + }, diff --git a/src/cli.mts b/src/cli.mts index 2b0aeccae..850f83b4b 100755 --- a/src/cli.mts +++ b/src/cli.mts @@ -4,6 +4,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url' import meow from 'meow' import { messageWithCauses, stackWithCauses } from 'pony-cause' +import lookupRegistryAuthToken from 'registry-auth-token' +import lookupRegistryUrl from 'registry-url' import updateNotifier from 'tiny-updater' import { debugFn, debugLog } from '@socketsecurity/registry/lib/debug' @@ -46,13 +48,15 @@ const __filename = fileURLToPath(import.meta.url) const { SOCKET_CLI_BIN_NAME } = constants -// TODO: Add autocompletion using https://socket.dev/npm/package/omelette void (async () => { + const registryUrl = lookupRegistryUrl() await updateNotifier({ + authInfo: lookupRegistryAuthToken(registryUrl, { recursive: true }), name: SOCKET_CLI_BIN_NAME, + registryUrl, + ttl: 86_400_000 /* 24 hours in milliseconds */, // Lazily access constants.ENV.INLINED_SOCKET_CLI_VERSION. version: constants.ENV.INLINED_SOCKET_CLI_VERSION, - ttl: 86_400_000 /* 24 hours in milliseconds */, }) try {