@gjsify/npm-registry 0.4.43 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @gjsify/npm-registry
2
+
3
+ A small npm registry client used by `gjsify install` to resolve and download packages on GJS. Provides packument fetching with ETag revalidation, tarball download with SRI integrity verification, `.npmrc` parsing with Bearer/Basic auth, and exponential-backoff retry for transient failures. Cross-platform — runs on both GJS and Node using only `globalThis.fetch` and `SubtleCrypto`.
4
+
5
+ Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ gjsify install @gjsify/npm-registry
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import {
17
+ fetchPackument,
18
+ fetchTarball,
19
+ parseNpmrc,
20
+ verifyIntegrity,
21
+ whoami,
22
+ DEFAULT_REGISTRY,
23
+ } from '@gjsify/npm-registry';
24
+
25
+ // Fetch a packument (package metadata)
26
+ const packument = await fetchPackument('lodash');
27
+ const latest = packument['dist-tags'].latest;
28
+ const tarballUrl = packument.versions[latest].dist.tarball;
29
+
30
+ // Download the tarball and verify its integrity
31
+ const bytes = await fetchTarball(tarballUrl, {
32
+ integrity: packument.versions[latest].dist.integrity,
33
+ });
34
+
35
+ // Parse ~/.npmrc for auth
36
+ import { readFileSync } from 'node:fs';
37
+ const npmrc = parseNpmrc(readFileSync('/home/user/.npmrc', 'utf8'));
38
+
39
+ // Verify the current auth token
40
+ const { username } = await whoami(DEFAULT_REGISTRY, npmrc);
41
+ console.log('Logged in as:', username);
42
+ ```
43
+
44
+ ## License
45
+
46
+ MIT
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";const e=`https://registry.npmjs.org/`;function assertPackument(e,t){if(!t||typeof t!=`object`)throw TypeError(`registry: ${e} packument is not an object`);let n=t;if(typeof n.name!=`string`)throw TypeError(`registry: ${e} packument missing string name`);if(!n.versions||typeof n.versions!=`object`)throw TypeError(`registry: ${e} packument missing versions map`)}function registryFor(t,n){if(n&&t.startsWith(`@`)){let e=t.slice(0,t.indexOf(`/`)),r=n.scopes[e];if(r)return ensureTrailingSlash(r)}return n?.registry?ensureTrailingSlash(n.registry):e}function packumentUrl(e,t){let n=ensureTrailingSlash(t);if(e.startsWith(`@`)){let t=e.indexOf(`/`);if(t<0)throw TypeError(`Invalid scoped package name: ${e}`);let r=e.slice(0,t),i=e.slice(t+1);return`${n}${encodeURIComponent(r)}/${encodeURIComponent(i)}`}return`${n}${encodeURIComponent(e)}`}async function fetchPackumentConditional(e,t={}){let n=packumentUrl(e,t.registry??registryFor(e,t.npmrc)),r=buildHeaders(n,{...t,acceptEncoding:`gzip`});r.accept??=`application/vnd.npm.install-v1+json`,t.ifNoneMatch&&(r[`if-none-match`]=t.ifNoneMatch);let i=await fetchWithRetry(n,{headers:r,signal:t.signal,compress:!1},{fetch:t.fetch,retries:t.retries,retryDelayMs:t.retryDelayMs,timeoutMs:t.timeoutMs,onRetry:t.onRetry});if(i.status===304){try{await i.arrayBuffer()}catch{}return{status:`not-modified`,etag:t.ifNoneMatch}}if(!i.ok)throw i.status===404||i.status===406?new PackageNotFoundError(e,n):Error(`registry GET ${n} -> ${i.status} ${i.statusText}`);let a=i.headers.get(`etag`)??void 0,o=await decodeJsonBody(i);return assertPackument(e,o),{status:`fresh`,packument:o,etag:a}}async function decodeJsonBody(e){let t=new Uint8Array(await e.arrayBuffer()),n=t.length>=2&&t[0]===31&&t[1]===139?await gunzip(t):t;return JSON.parse(new TextDecoder().decode(n))}async function gunzip(e){let t=globalThis.DecompressionStream;if(typeof t!=`function`)throw Error(`@gjsify/npm-registry: globalThis.DecompressionStream is unavailable — cannot decode a gzip registry response (on GJS, register '@gjsify/compression-streams')`);let n=new Blob([new Uint8Array(e)]).stream().pipeThrough(new t(`gzip`)).getReader(),r=[],i=0;for(;;){let{value:e,done:t}=await n.read();if(t)break;let a=e instanceof Uint8Array?e:new Uint8Array(e);r.push(a),i+=a.length}let a=new Uint8Array(i),o=0;for(let e of r)a.set(e,o),o+=e.length;return a}async function fetchPackument(e,t={}){let n=await fetchPackumentConditional(e,t);if(!n.packument)throw Error(`registry: ${e} returned 304 Not Modified to an unconditional request`);return n.packument}async function fetchTarball(e,t={}){let n=buildHeaders(e,{...t,acceptEncoding:`identity`});n.accept??=`application/octet-stream`;let r=await fetchWithRetry(e,{headers:n,signal:t.signal},{fetch:t.fetch,retries:t.retries,retryDelayMs:t.retryDelayMs,timeoutMs:t.timeoutMs,onRetry:t.onRetry,retryNotFound:t.retryNotFound??!0});if(!r.ok)throw Error(`tarball GET ${e} -> ${r.status} ${r.statusText}`);let i=new Uint8Array(await r.arrayBuffer());if(t.integrity&&!await verifyIntegrity(i,t.integrity))throw new IntegrityError(e,t.integrity);return i}async function fetchWithRetry(e,t,n){let r=n.fetch??globalThis.fetch;if(!r)throw Error(`@gjsify/npm-registry: globalThis.fetch is missing`);let i=Math.max(0,n.retries??3),a=Math.max(0,n.retryDelayMs??250),o=Math.max(0,n.timeoutMs??3e4),s=n.retryNotFound??!1,c=0,l,u=0;for(;;){if(t.signal?.aborted)throw signalAbortError(t.signal);let d=o>0?new AbortController:null,f=d===null?null:setTimeout(()=>d.abort(Error(`@gjsify/npm-registry: per-request timeout ${o}ms`)),o),p=composeSignals(t.signal,d?.signal);try{let n=await r(e,{...t,signal:p}),a=isRetryableStatus(n.status)||s&&n.status===404;if(n.ok||!a||c>=i)return n;try{await n.arrayBuffer()}catch{}l=Error(`HTTP ${n.status} ${n.statusText}`)}catch(n){let r=d!==null&&d.signal.aborted,a=t.signal?.aborted===!0;if(r&&!a){if(u++,c>=i)throw new RegistryTimeoutError(e,o,u);l=n}else if(a)throw signalAbortError(t.signal);else{if(!isRetryableError(n)||c>=i)throw n;l=n}}finally{f!==null&&clearTimeout(f)}let m=Math.min(a*2**c,8e3);n.onRetry?.({attempt:c+1,error:l,delayMs:m}),await delay(m,t.signal),c++}}function composeSignals(e,t){return e?t?AbortSignal.any([e,t]):e:t}function isRetryableStatus(e){return e===408||e===425||e===429||e===500||e===502||e===503||e===504||e===521||e===522||e===524||e===525}function isRetryableError(e){if(e&&typeof e==`object`&&`name`in e&&e.name===`AbortError`)return!1;if(e instanceof TypeError)return!0;if(e&&typeof e==`object`&&`name`in e){let t=e.name;if(t===`FetchError`)return!0;if(t===`AbortError`)return!1}let t=e?.cause;if(t&&typeof t==`object`&&`code`in t){let e=t.code;if(typeof e==`string`)return e===`ECONNRESET`||e===`ECONNREFUSED`||e===`ENETUNREACH`||e===`ENOTFOUND`||e===`ETIMEDOUT`||e===`EAI_AGAIN`||e===`UND_ERR_SOCKET`||e===`UND_ERR_CONNECT_TIMEOUT`}return!1}function delay(e,t){return e<=0?Promise.resolve():new Promise((n,r)=>{let i=setTimeout(()=>{t?.removeEventListener?.(`abort`,onAbort),n()},e),onAbort=()=>{clearTimeout(i),r(signalAbortError(t))};if(t?.aborted){clearTimeout(i),r(signalAbortError(t));return}t?.addEventListener?.(`abort`,onAbort,{once:!0})})}function signalAbortError(e){let t=e&&`reason`in e?e.reason:void 0;if(t instanceof Error)return t;let n=Error(`Aborted`);return n.name=`AbortError`,n}async function verifyIntegrity(e,t){let n=t.trim().split(/\s+/);for(let t of n){let n=t.indexOf(`-`);if(n<0)continue;let r=t.slice(0,n).toLowerCase(),i=t.slice(n+1),a=globalThis.crypto?.subtle;if(!a)throw Error(`@gjsify/npm-registry: globalThis.crypto.subtle is missing`);let o=subriToWebCryptoAlgo(r);if(!o)continue;let s=await a.digest(o,dataAsArrayBuffer(e));if(bytesToBase64(new Uint8Array(s))===i)return!0}return!1}function subriToWebCryptoAlgo(e){switch(e){case`sha1`:return`SHA-1`;case`sha256`:return`SHA-256`;case`sha384`:return`SHA-384`;case`sha512`:return`SHA-512`;default:return null}}function dataAsArrayBuffer(e){if(e.byteOffset===0&&e.byteLength===e.buffer.byteLength)return e.buffer;let t=new Uint8Array(e.byteLength);return t.set(e),t.buffer}function bytesToBase64(e){let t=``;for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function parseNpmrc(t){let n={registry:e,scopes:{},authTokens:{},basicAuth:{}},r=t.split(/\r?\n/),i={};for(let e of r){let t=e.replace(/^\s+|\s+$/g,``);if(!t||t.startsWith(`#`)||t.startsWith(`;`))continue;let r=t.indexOf(`=`);if(r<0)continue;let a=t.slice(0,r).trim(),o=expandEnv(stripQuotes(t.slice(r+1).trim()));if(a===`registry`){n.registry=ensureTrailingSlash(o);continue}let s=a.match(/^(@[^:]+):registry$/);if(s){n.scopes[s[1]]=ensureTrailingSlash(o);continue}let c=a.match(/^\/\/(.+):_authToken$/);if(c){n.authTokens[normalizeAuthHost(c[1])]=o;continue}let l=a.match(/^\/\/(.+):username$/);if(l){(i[normalizeAuthHost(l[1])]??={}).user=o;continue}let u=a.match(/^\/\/(.+):_password$/);if(u){let e=base64Decode(o);(i[normalizeAuthHost(u[1])]??={}).pass=e;continue}}for(let[e,t]of Object.entries(i))t.user&&t.pass!==void 0&&(n.basicAuth[e]={username:t.user,password:t.pass});return n}function buildHeaders(e,t){let n={"user-agent":`gjsify-install/0.3.7`,"accept-encoding":t.acceptEncoding??`identity`};if(t.npmrc){let r=resolveAuthForUrl(e,t.npmrc);r&&(n.authorization=r)}if(t.headers)for(let[e,r]of Object.entries(t.headers))n[e.toLowerCase()]=r;return n}function resolveAuthForUrl(e,t){let n=pathPrefixes(new URL(e));for(let e of n){let n=t.authTokens[e];if(n)return`Bearer ${n}`;let r=t.basicAuth[e];if(r)return`Basic ${btoa(`${r.username}:${r.password}`)}`}return null}function pathPrefixes(e){let t=e.pathname.split(`/`).filter(Boolean),n=[];for(let r=t.length;r>=0;r--){let i=t.slice(0,r).join(`/`);n.push(i?`//${e.host}/${i}`:`//${e.host}`)}return n}function normalizeAuthHost(e){return`//${e.replace(/\/+$/,``)}`}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:e+`/`}function stripQuotes(e){return e.startsWith(`"`)&&e.endsWith(`"`)||e.startsWith(`'`)&&e.endsWith(`'`)?e.slice(1,-1):e}function expandEnv(e){return e.replace(/\$\{([A-Z0-9_]+)\}/gi,(e,t)=>globalThis.process?.env?.[t]??``)}function base64Decode(e){return atob(e)}async function whoami(e,t){let n=`${ensureTrailingSlash(e)}-/whoami`,r=buildHeaders(n,{npmrc:t});r.accept=`application/json`;let i=globalThis.fetch;if(!i)throw Error(`@gjsify/npm-registry: globalThis.fetch is missing`);let a=await i(n,{method:`GET`,headers:r});if(!a.ok)throw Error(`whoami GET ${n} -> ${a.status} ${a.statusText}`);let o=await a.json();if(o&&typeof o==`object`&&`username`in o){let e=o.username;if(typeof e==`string`&&e.length>0)return{username:e}}return{}}var PackageNotFoundError=class extends Error{name;url;constructor(e,t){super(`Package not found in registry: ${e} (${t})`),this.name=e,this.url=t,this.name=`PackageNotFoundError`}},IntegrityError=class extends Error{url;integrity;constructor(e,t){super(`Tarball integrity mismatch for ${e} (expected ${t})`),this.url=e,this.integrity=t,this.name=`IntegrityError`}},RegistryTimeoutError=class extends Error{url;timeoutMs;attempts;constructor(e,t,n){let r=Math.round(t/100)/10,i=Math.round(t*n/100)/10;super(`@gjsify/npm-registry: GET ${e} timed out after ${r}s × ${n} attempt(s) (total ~${i}s). This usually means the registry CDN is slow or unreachable.`),this.url=e,this.timeoutMs=t,this.attempts=n,this.name=`RegistryTimeoutError`}};export{e as DEFAULT_REGISTRY,IntegrityError,PackageNotFoundError,RegistryTimeoutError,assertPackument,buildHeaders,fetchPackument,fetchPackumentConditional,fetchTarball,fetchWithRetry,packumentUrl,parseNpmrc,registryFor,resolveAuthForUrl,verifyIntegrity,whoami};
1
+ import"./_virtual/_rolldown/runtime.js";const e=`https://registry.npmjs.org/`;function assertPackument(e,t){if(!t||typeof t!=`object`)throw TypeError(`registry: ${e} packument is not an object`);let n=t;if(typeof n.name!=`string`)throw TypeError(`registry: ${e} packument missing string name`);if(!n.versions||typeof n.versions!=`object`)throw TypeError(`registry: ${e} packument missing versions map`)}function registryFor(t,n){if(n&&t.startsWith(`@`)){let e=t.slice(0,t.indexOf(`/`)),r=n.scopes[e];if(r)return ensureTrailingSlash(r)}return n?.registry?ensureTrailingSlash(n.registry):e}function packumentUrl(e,t){let n=ensureTrailingSlash(t);if(e.startsWith(`@`)){let t=e.indexOf(`/`);if(t<0)throw TypeError(`Invalid scoped package name: ${e}`);let r=e.slice(0,t),i=e.slice(t+1);return`${n}${encodeURIComponent(r)}/${encodeURIComponent(i)}`}return`${n}${encodeURIComponent(e)}`}async function fetchPackumentConditional(e,t={}){let n=packumentUrl(e,t.registry??registryFor(e,t.npmrc)),r=buildHeaders(n,{...t,acceptEncoding:`gzip`});r.accept??=`application/vnd.npm.install-v1+json`,t.ifNoneMatch&&(r[`if-none-match`]=t.ifNoneMatch);let i=await fetchWithRetry(n,{headers:r,signal:t.signal,compress:!1},{fetch:t.fetch,retries:t.retries,retryDelayMs:t.retryDelayMs,timeoutMs:t.timeoutMs,onRetry:t.onRetry});if(i.status===304){try{await i.arrayBuffer()}catch{}return{status:`not-modified`,etag:t.ifNoneMatch}}if(!i.ok)throw i.status===404||i.status===406?new PackageNotFoundError(e,n):Error(`registry GET ${n} -> ${i.status} ${i.statusText}`);let a=i.headers.get(`etag`)??void 0,o=await decodeJsonBody(i);return assertPackument(e,o),{status:`fresh`,packument:o,etag:a}}async function decodeJsonBody(e){let t=new Uint8Array(await e.arrayBuffer()),n=t.length>=2&&t[0]===31&&t[1]===139?await gunzip(t):t;return JSON.parse(new TextDecoder().decode(n))}async function gunzip(e){let t=globalThis.DecompressionStream;if(typeof t!=`function`)throw Error(`@gjsify/npm-registry: globalThis.DecompressionStream is unavailable — cannot decode a gzip registry response (on GJS, register '@gjsify/compression-streams')`);let n=new Blob([new Uint8Array(e)]).stream().pipeThrough(new t(`gzip`)).getReader(),r=[],i=0;for(;;){let{value:e,done:t}=await n.read();if(t)break;let a=e instanceof Uint8Array?e:new Uint8Array(e);r.push(a),i+=a.length}let a=new Uint8Array(i),o=0;for(let e of r)a.set(e,o),o+=e.length;return a}async function fetchPackument(e,t={}){let n=await fetchPackumentConditional(e,t);if(!n.packument)throw Error(`registry: ${e} returned 304 Not Modified to an unconditional request`);return n.packument}async function fetchTarball(e,t={}){let n=buildHeaders(e,{...t,acceptEncoding:`identity`});n.accept??=`application/octet-stream`;let r=await fetchWithRetry(e,{headers:n,signal:t.signal},{fetch:t.fetch,retries:t.retries,retryDelayMs:t.retryDelayMs,timeoutMs:t.timeoutMs,onRetry:t.onRetry,retryNotFound:t.retryNotFound??!0});if(!r.ok)throw Error(`tarball GET ${e} -> ${r.status} ${r.statusText}`);let i=new Uint8Array(await r.arrayBuffer());if(t.integrity&&!await verifyIntegrity(i,t.integrity))throw new IntegrityError(e,t.integrity);return i}async function fetchWithRetry(e,t,n){let r=n.fetch??globalThis.fetch;if(!r)throw Error(`@gjsify/npm-registry: globalThis.fetch is missing`);let i=Math.max(0,n.retries??3),a=Math.max(0,n.retryDelayMs??250),o=Math.max(0,n.timeoutMs??3e4),s=n.retryNotFound??!1,c=0,l,u=0;for(;;){if(t.signal?.aborted)throw signalAbortError(t.signal);let d=o>0?new AbortController:null,f=d===null?null:setTimeout(()=>d.abort(Error(`@gjsify/npm-registry: per-request timeout ${o}ms`)),o),p=composeSignals(t.signal,d?.signal);try{let n=await r(e,{...t,signal:p}),a=isRetryableStatus(n.status)||s&&n.status===404;if(n.ok||!a||c>=i)return n;try{await n.arrayBuffer()}catch{}l=Error(`HTTP ${n.status} ${n.statusText}`)}catch(n){let r=d!==null&&d.signal.aborted,a=t.signal?.aborted===!0;if(r&&!a){if(u++,c>=i)throw new RegistryTimeoutError(e,o,u);l=n}else if(a)throw signalAbortError(t.signal);else{if(!isRetryableError(n)||c>=i)throw n;l=n}}finally{f!==null&&clearTimeout(f)}let m=Math.min(a*2**c,8e3);n.onRetry?.({attempt:c+1,error:l,delayMs:m}),await delay(m,t.signal),c++}}function composeSignals(e,t){return e?t?AbortSignal.any([e,t]):e:t}function isRetryableStatus(e){return e===408||e===425||e===429||e===500||e===502||e===503||e===504||e===521||e===522||e===524||e===525}function isRetryableError(e){if(e&&typeof e==`object`&&`name`in e&&e.name===`AbortError`)return!1;if(e instanceof TypeError)return!0;if(e&&typeof e==`object`&&`name`in e){let t=e.name;if(t===`FetchError`)return!0;if(t===`AbortError`)return!1}let t=e?.cause;if(t&&typeof t==`object`&&`code`in t){let e=t.code;if(typeof e==`string`)return e===`ECONNRESET`||e===`ECONNREFUSED`||e===`ENETUNREACH`||e===`ENOTFOUND`||e===`ETIMEDOUT`||e===`EAI_AGAIN`||e===`UND_ERR_SOCKET`||e===`UND_ERR_CONNECT_TIMEOUT`}return!1}function delay(e,t){return e<=0?Promise.resolve():new Promise((n,r)=>{let i=setTimeout(()=>{t?.removeEventListener?.(`abort`,onAbort),n()},e),onAbort=()=>{clearTimeout(i),r(signalAbortError(t))};if(t?.aborted){clearTimeout(i),r(signalAbortError(t));return}t?.addEventListener?.(`abort`,onAbort,{once:!0})})}function signalAbortError(e){let t=e&&`reason`in e?e.reason:void 0;if(t instanceof Error)return t;let n=Error(`Aborted`);return n.name=`AbortError`,n}async function verifyIntegrity(e,t){let n=t.trim().split(/\s+/);for(let t of n){let n=t.indexOf(`-`);if(n<0)continue;let r=t.slice(0,n).toLowerCase(),i=t.slice(n+1),a=globalThis.crypto?.subtle;if(!a)throw Error(`@gjsify/npm-registry: globalThis.crypto.subtle is missing`);let o=subriToWebCryptoAlgo(r);if(!o)continue;let s=await a.digest(o,dataAsArrayBuffer(e));if(bytesToBase64(new Uint8Array(s))===i)return!0}return!1}function subriToWebCryptoAlgo(e){switch(e){case`sha1`:return`SHA-1`;case`sha256`:return`SHA-256`;case`sha384`:return`SHA-384`;case`sha512`:return`SHA-512`;default:return null}}function dataAsArrayBuffer(e){if(e.byteOffset===0&&e.byteLength===e.buffer.byteLength)return e.buffer;let t=new Uint8Array(e.byteLength);return t.set(e),t.buffer}function bytesToBase64(e){let t=``;for(let n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return btoa(t)}function parseNpmrc(t){let n={registry:e,scopes:{},authTokens:{},basicAuth:{}},r=t.split(/\r?\n/),i={};for(let e of r){let t=e.replace(/^\s+|\s+$/g,``);if(!t||t.startsWith(`#`)||t.startsWith(`;`))continue;let r=t.indexOf(`=`);if(r<0)continue;let a=t.slice(0,r).trim(),o=expandEnv(stripQuotes(t.slice(r+1).trim()));if(a===`registry`){n.registry=ensureTrailingSlash(o);continue}let s=a.match(/^(@[^:]+):registry$/);if(s){n.scopes[s[1]]=ensureTrailingSlash(o);continue}let c=a.match(/^\/\/(.+):_authToken$/);if(c){n.authTokens[normalizeAuthHost(c[1])]=o;continue}let l=a.match(/^\/\/(.+):username$/);if(l){(i[normalizeAuthHost(l[1])]??={}).user=o;continue}let u=a.match(/^\/\/(.+):_password$/);if(u){let e=base64Decode(o);(i[normalizeAuthHost(u[1])]??={}).pass=e;continue}}for(let[e,t]of Object.entries(i))t.user&&t.pass!==void 0&&(n.basicAuth[e]={username:t.user,password:t.pass});return n}function buildHeaders(e,t){let n={"user-agent":`gjsify-install/0.3.7`,"accept-encoding":t.acceptEncoding??`identity`};if(t.npmrc){let r=resolveAuthForUrl(e,t.npmrc);r&&(n.authorization=r)}if(t.headers)for(let[e,r]of Object.entries(t.headers))n[e.toLowerCase()]=r;return n}function resolveAuthForUrl(e,t){let n=pathPrefixes(new URL(e));for(let e of n){let n=t.authTokens[e];if(n)return`Bearer ${n}`;let r=t.basicAuth[e];if(r)return`Basic ${btoa(`${r.username}:${r.password}`)}`}return null}function pathPrefixes(e){let t=e.pathname.split(`/`).filter(Boolean),n=[];for(let r=t.length;r>=0;r--){let i=t.slice(0,r).join(`/`);n.push(i?`//${e.host}/${i}`:`//${e.host}`)}return n}function normalizeAuthHost(e){return`//${e.replace(/\/+$/,``)}`}function ensureTrailingSlash(e){return e.endsWith(`/`)?e:e+`/`}function stripQuotes(e){return e.startsWith(`"`)&&e.endsWith(`"`)||e.startsWith(`'`)&&e.endsWith(`'`)?e.slice(1,-1):e}function expandEnv(e){return e.replace(/\$\{([A-Z0-9_]+)\}/gi,(e,t)=>globalThis.process?.env?.[t]??``)}function base64Decode(e){return atob(e)}async function whoami(e,t){let n=`${ensureTrailingSlash(e)}-/whoami`,r=buildHeaders(n,{npmrc:t});r.accept=`application/json`;let i=globalThis.fetch;if(!i)throw Error(`@gjsify/npm-registry: globalThis.fetch is missing`);let a=await i(n,{method:`GET`,headers:r});if(!a.ok)throw Error(`whoami GET ${n} -> ${a.status} ${a.statusText}`);let o=await a.json();if(o&&typeof o==`object`&&`username`in o){let e=o.username;if(typeof e==`string`&&e.length>0)return{username:e}}return{}}var PackageNotFoundError=class extends Error{constructor(e,t){super(`Package not found in registry: ${e} (${t})`),this.name=e,this.url=t,this.name=`PackageNotFoundError`}},IntegrityError=class extends Error{constructor(e,t){super(`Tarball integrity mismatch for ${e} (expected ${t})`),this.url=e,this.integrity=t,this.name=`IntegrityError`}},RegistryTimeoutError=class extends Error{constructor(e,t,n){let r=Math.round(t/100)/10,i=Math.round(t*n/100)/10;super(`@gjsify/npm-registry: GET ${e} timed out after ${r}s × ${n} attempt(s) (total ~${i}s). This usually means the registry CDN is slow or unreachable.`),this.url=e,this.timeoutMs=t,this.attempts=n,this.name=`RegistryTimeoutError`}};export{e as DEFAULT_REGISTRY,IntegrityError,PackageNotFoundError,RegistryTimeoutError,assertPackument,buildHeaders,fetchPackument,fetchPackumentConditional,fetchTarball,fetchWithRetry,packumentUrl,parseNpmrc,registryFor,resolveAuthForUrl,verifyIntegrity,whoami};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/npm-registry",
3
- "version": "0.4.43",
3
+ "version": "0.5.0",
4
4
  "description": "npm registry client for the gjsify install backend — packuments, tarballs, .npmrc auth (Node + GJS)",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -37,8 +37,8 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "devDependencies": {
40
- "@gjsify/cli": "^0.4.43",
41
- "@gjsify/unit": "^0.4.43",
40
+ "@gjsify/cli": "^0.5.0",
41
+ "@gjsify/unit": "^0.5.0",
42
42
  "typescript": "^6.0.3"
43
43
  },
44
44
  "repository": {