@wildo-ai/wildo-module-client 1.1.5 → 1.1.6

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.
Files changed (64) hide show
  1. package/dist/esm/services/_http-client.d.ts +32 -3
  2. package/dist/esm/services/_http-client.d.ts.map +1 -1
  3. package/dist/esm/services/_http-client.js +100 -6
  4. package/dist/esm/services/_http-client.js.map +1 -1
  5. package/dist/esm/services/module-adopter.service.d.ts +1 -1
  6. package/dist/esm/services/module-adopter.service.d.ts.map +1 -1
  7. package/dist/esm/services/module-adopter.service.js +7 -8
  8. package/dist/esm/services/module-adopter.service.js.map +1 -1
  9. package/dist/esm/services/module-packager.service.d.ts.map +1 -1
  10. package/dist/esm/services/module-packager.service.js +16 -19
  11. package/dist/esm/services/module-packager.service.js.map +1 -1
  12. package/dist/esm/services/provider-sync-runner.d.ts +2 -3
  13. package/dist/esm/services/provider-sync-runner.d.ts.map +1 -1
  14. package/dist/esm/services/provider-sync-runner.js +14 -2
  15. package/dist/esm/services/provider-sync-runner.js.map +1 -1
  16. package/dist/esm/services/registry-companion-client.d.ts +10 -47
  17. package/dist/esm/services/registry-companion-client.d.ts.map +1 -1
  18. package/dist/esm/services/registry-companion-client.js +9 -97
  19. package/dist/esm/services/registry-companion-client.js.map +1 -1
  20. package/dist/esm/services/registry-lockfile.service.d.ts +7 -3
  21. package/dist/esm/services/registry-lockfile.service.d.ts.map +1 -1
  22. package/dist/esm/services/registry-lockfile.service.js +8 -3
  23. package/dist/esm/services/registry-lockfile.service.js.map +1 -1
  24. package/dist/esm/services/registry-server-client.d.ts +35 -9
  25. package/dist/esm/services/registry-server-client.d.ts.map +1 -1
  26. package/dist/esm/services/registry-server-client.js +115 -58
  27. package/dist/esm/services/registry-server-client.js.map +1 -1
  28. package/dist/tsconfig.build.tsbuildinfo +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +2 -2
  31. package/src/__tests__/module-adopter.service.test.ts +6 -4
  32. package/src/__tests__/module-packager.service.test.ts +36 -34
  33. package/src/__tests__/provider-sync-runner.test.ts +12 -0
  34. package/src/__tests__/registry-lockfile.service.test.ts +3 -3
  35. package/src/__tests__/registry-server-client.test.ts +129 -0
  36. package/src/__tests__/registry-transfer-timeouts.test.ts +94 -0
  37. package/src/services/_http-client.ts +100 -6
  38. package/src/services/module-adopter.service.ts +6 -9
  39. package/src/services/module-packager.service.ts +19 -16
  40. package/src/services/provider-sync-runner.ts +16 -5
  41. package/src/services/registry-companion-client.ts +10 -166
  42. package/src/services/registry-lockfile.service.ts +9 -3
  43. package/src/services/registry-server-client.ts +147 -57
  44. package/dist/esm/__tests__/lockfile-repair.service.test.d.ts +0 -0
  45. package/dist/esm/__tests__/lockfile-repair.service.test.d.ts.map +0 -0
  46. package/dist/esm/__tests__/lockfile-repair.service.test.js.map +0 -0
  47. package/dist/esm/__tests__/module-adopter.service.test.d.ts +0 -0
  48. package/dist/esm/__tests__/module-adopter.service.test.d.ts.map +0 -0
  49. package/dist/esm/__tests__/module-adopter.service.test.js.map +0 -0
  50. package/dist/esm/__tests__/module-packager.service.test.d.ts +0 -0
  51. package/dist/esm/__tests__/module-packager.service.test.d.ts.map +0 -0
  52. package/dist/esm/__tests__/module-packager.service.test.js.map +0 -0
  53. package/dist/esm/__tests__/provider-materialization-hook.test.d.ts +0 -0
  54. package/dist/esm/__tests__/provider-materialization-hook.test.d.ts.map +0 -0
  55. package/dist/esm/__tests__/provider-materialization-hook.test.js.map +0 -0
  56. package/dist/esm/__tests__/provider-sync-runner.test.d.ts +0 -0
  57. package/dist/esm/__tests__/provider-sync-runner.test.d.ts.map +0 -0
  58. package/dist/esm/__tests__/provider-sync-runner.test.js.map +0 -0
  59. package/dist/esm/__tests__/registry-lockfile.service.test.d.ts +0 -0
  60. package/dist/esm/__tests__/registry-lockfile.service.test.d.ts.map +0 -0
  61. package/dist/esm/__tests__/registry-lockfile.service.test.js.map +0 -0
  62. package/dist/esm/__tests__/registry-server-client.test.d.ts +0 -0
  63. package/dist/esm/__tests__/registry-server-client.test.d.ts.map +0 -0
  64. package/dist/esm/__tests__/registry-server-client.test.js.map +0 -0
@@ -12,12 +12,13 @@
12
12
  * Auth (KD-11): if `authToken` is supplied, every request adds
13
13
  * `Authorization: Bearer <token>`. Public read paths work without
14
14
  * a token; restricted paths return 401/403, surfaced as
15
- * `RegistryAuthRequiredError` / `RegistryAuthForbiddenError`.
15
+ * `RegistryAuthRequiredError` (no key sent), `RegistryCredentialRejectedError` (a key sent and refused)
16
+ * or `RegistryAuthForbiddenError`; a 429 is `RegistryRateLimitedError`.
16
17
  */
17
18
  import { z } from 'zod';
18
19
  import { formatZodIssuesAsSingleLine } from './_zod-issue-formatting.js';
19
- import { ModuleManifestSchema, ModuleVersionRecordSchema, RegistryArtifactIntegrityMismatchError, RegistryAuthForbiddenError, RegistryAuthRequiredError, RegistryMalformedPayloadError, RegistryModuleNotFoundError, RegistryUnexpectedStatusError, RegistryVersionNotFoundError, Sha256DigestSchema, } from '@wildo-ai/platform-config-lib';
20
- import { fetchJsonOk, fetchWithTimeout, joinUrl } from './_http-client.js';
20
+ import { ModuleManifestSchema, ModuleVersionRecordSchema, RegistryArtifactIntegrityMismatchError, RegistryAuthForbiddenError, RegistryAuthRequiredError, RegistryCredentialRejectedError, RegistryRateLimitedError, isRegistryError, isRegistryReleaseStateConflictBody, RegistryMalformedPayloadError, RegistryModuleNotFoundError, RegistryUnexpectedStatusError, RegistryVersionNotFoundError, RegistryYankNoticeSchema, RegistryYankStateConflictError, RegistryYankVerb, REGISTRY_YANK_REASON_HEADER, REGISTRY_YANKED_AT_HEADER, Sha256DigestSchema, ModuleSearchHitSchema, moduleSearchFilterQueryPairs, moduleSearchHitMatchesFilter, } from '@wildo-ai/platform-config-lib';
21
+ import { fetchJsonOk, fetchTransfer, fetchWithTimeout, joinUrl, readJsonBody } from './_http-client.js';
21
22
  /**
22
23
  * Zod schema for the `info()` response payload — mirrors
23
24
  * `RegistryInfoResult` and is enforced at the client boundary so a
@@ -31,22 +32,39 @@ const RegistryInfoResponseSchema = z.object({
31
32
  tags: z.record(z.string(), z.string()),
32
33
  });
33
34
  const RegistrySearchResponseSchema = z.object({
34
- hits: z.array(z.object({
35
- moduleId: z.string().min(1),
36
- description: z.string().optional(),
37
- latestVersion: z.string(),
38
- })),
35
+ hits: z.array(ModuleSearchHitSchema),
39
36
  });
40
37
  const DIAGNOSTIC_LABEL = 'Registry';
41
38
  function authHeaders(options) {
42
39
  return options.authToken ? { Authorization: `Bearer ${options.authToken}` } : {};
43
40
  }
44
- async function classifyAuthError(res, registryUrl, action) {
45
- if (res.status === 401)
46
- return new RegistryAuthRequiredError(registryUrl);
47
- if (res.status === 403)
48
- return new RegistryAuthForbiddenError(action);
49
- return null;
41
+ /**
42
+ * Throw the typed error for a non-2xx response, after releasing its body (#1763 review: an unread body
43
+ * holds the connection until garbage collection). A 401 is told apart by whether a key was SENT — "log
44
+ * in" and "your key no longer works" are different remedies (#1759 review).
45
+ */
46
+ async function refuseUnlessOk(res, options, action, notFound) {
47
+ if (res.ok)
48
+ return;
49
+ let error;
50
+ if (res.status === 404 && notFound)
51
+ error = notFound();
52
+ else if (res.status === 401)
53
+ error = options.authToken ? new RegistryCredentialRejectedError(options.baseUrl) : new RegistryAuthRequiredError(options.baseUrl);
54
+ else if (res.status === 403)
55
+ error = new RegistryAuthForbiddenError(action);
56
+ else if (res.status === 429)
57
+ error = new RegistryRateLimitedError(options.baseUrl, action, parseRetryAfterSeconds(res.headers.get('retry-after')));
58
+ else
59
+ error = new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, action);
60
+ await res.body?.cancel().catch(() => undefined);
61
+ throw error;
62
+ }
63
+ /** `Retry-After` in its delta-seconds form; the HTTP-date form is not produced by a Wildo backend. */
64
+ function parseRetryAfterSeconds(value) {
65
+ if (!value || !/^\d+$/.test(value.trim()))
66
+ return undefined;
67
+ return Number(value.trim());
50
68
  }
51
69
  /**
52
70
  * 6th-pass M3: typed JSON-parse helper. A misconfigured proxy that
@@ -56,11 +74,14 @@ async function classifyAuthError(res, registryUrl, action) {
56
74
  * failure into a `RegistryMalformedPayloadError` so the typed-error
57
75
  * contract is universal across the client surface.
58
76
  */
59
- async function parseJsonOrThrow(res, registryUrl, action) {
77
+ async function parseJsonOrThrow(res, registryUrl, action, timeoutMs) {
60
78
  try {
61
- return await res.json();
79
+ return await readJsonBody(res, registryUrl, action, timeoutMs);
62
80
  }
63
81
  catch (err) {
82
+ // A body cut short by the request budget is a timeout (#1763), not a malformed payload.
83
+ if (isRegistryError(err))
84
+ throw err;
64
85
  throw new RegistryMalformedPayloadError(registryUrl, action, `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
65
86
  }
66
87
  }
@@ -98,18 +119,10 @@ export const RegistryServerClient = {
98
119
  timeoutMs: options.timeoutMs,
99
120
  diagnosticLabel: DIAGNOSTIC_LABEL,
100
121
  });
101
- if (res.status === 404) {
102
- throw new RegistryVersionNotFoundError(args.moduleId, args.range);
103
- }
104
- const authErr = await classifyAuthError(res, options.baseUrl, `resolve ${args.moduleId}`);
105
- if (authErr)
106
- throw authErr;
107
- if (!res.ok) {
108
- throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `resolve ${args.moduleId}`);
109
- }
122
+ await refuseUnlessOk(res, options, `resolve ${args.moduleId}`, () => new RegistryVersionNotFoundError(args.moduleId, args.range));
110
123
  // 6th-pass M3: typed JSON-parse (was bare `SyntaxError` for
111
124
  // malformed proxy responses).
112
- const json = (await parseJsonOrThrow(res, options.baseUrl, `resolve ${args.moduleId}`));
125
+ const json = (await parseJsonOrThrow(res, options.baseUrl, `resolve ${args.moduleId}`, options.timeoutMs));
113
126
  const digest = String(json.artifactDigest ?? '');
114
127
  // 6th-pass M4: typed digest validation (was bare `ZodError`).
115
128
  const digestParse = Sha256DigestSchema.safeParse(digest);
@@ -122,7 +135,15 @@ export const RegistryServerClient = {
122
135
  // correctly.
123
136
  throw new RegistryMalformedPayloadError(options.baseUrl, `resolve ${args.moduleId}`, 'response missing required `version` string');
124
137
  }
125
- return { version: json.version, artifactDigest: digest };
138
+ let yanked;
139
+ if (json.yanked !== undefined) {
140
+ const yankParse = RegistryYankNoticeSchema.safeParse(json.yanked);
141
+ if (!yankParse.success) {
142
+ throw new RegistryMalformedPayloadError(options.baseUrl, `resolve ${args.moduleId}`, `yanked: ${formatZodIssuesAsSingleLine(yankParse.error)}`);
143
+ }
144
+ yanked = yankParse.data;
145
+ }
146
+ return { version: json.version, artifactDigest: digest, ...(yanked ? { yanked } : {}) };
126
147
  },
127
148
  /**
128
149
  * Fetch full info for a module: manifest, every published
@@ -136,16 +157,9 @@ export const RegistryServerClient = {
136
157
  timeoutMs: options.timeoutMs,
137
158
  diagnosticLabel: DIAGNOSTIC_LABEL,
138
159
  });
139
- if (res.status === 404)
140
- throw new RegistryModuleNotFoundError(args.moduleId);
141
- const authErr = await classifyAuthError(res, options.baseUrl, `info ${args.moduleId}`);
142
- if (authErr)
143
- throw authErr;
144
- if (!res.ok) {
145
- throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `info ${args.moduleId}`);
146
- }
160
+ await refuseUnlessOk(res, options, `info ${args.moduleId}`, () => new RegistryModuleNotFoundError(args.moduleId));
147
161
  // 6th-pass M3: typed JSON-parse.
148
- const json = await parseJsonOrThrow(res, options.baseUrl, `info ${args.moduleId}`);
162
+ const json = await parseJsonOrThrow(res, options.baseUrl, `info ${args.moduleId}`, options.timeoutMs);
149
163
  const parsed = RegistryInfoResponseSchema.safeParse(json);
150
164
  if (!parsed.success) {
151
165
  throw new RegistryMalformedPayloadError(options.baseUrl, `info ${args.moduleId}`, formatZodIssuesAsSingleLine(parsed.error));
@@ -153,30 +167,27 @@ export const RegistryServerClient = {
153
167
  return parsed.data;
154
168
  },
155
169
  /**
156
- * Search the registry's metadata index. The shape is
157
- * intentionally narrow until Phase 4 designs the search UX.
170
+ * Search the registry's metadata index: a module-id substring, and (#2014) the manifest filters —
171
+ * kind, engine capability and provider maturity. The server refuses an unknown filter value.
158
172
  */
159
173
  async search(options, args) {
160
- const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules?q=${encodeURIComponent(args.query)}`);
174
+ const params = new URLSearchParams([['q', args.query], ...moduleSearchFilterQueryPairs(args.filter ?? {})]);
175
+ const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules?${params.toString()}`);
161
176
  const res = await fetchWithTimeout(url, {
162
177
  method: 'GET',
163
178
  headers: authHeaders(options),
164
179
  timeoutMs: options.timeoutMs,
165
180
  diagnosticLabel: DIAGNOSTIC_LABEL,
166
181
  });
167
- const authErr = await classifyAuthError(res, options.baseUrl, `search`);
168
- if (authErr)
169
- throw authErr;
170
- if (!res.ok) {
171
- throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, 'search');
172
- }
182
+ await refuseUnlessOk(res, options, 'search');
173
183
  // 6th-pass M3: typed JSON-parse.
174
- const json = await parseJsonOrThrow(res, options.baseUrl, 'search');
184
+ const json = await parseJsonOrThrow(res, options.baseUrl, 'search', options.timeoutMs);
175
185
  const parsed = RegistrySearchResponseSchema.safeParse(json);
176
186
  if (!parsed.success) {
177
187
  throw new RegistryMalformedPayloadError(options.baseUrl, 'search', formatZodIssuesAsSingleLine(parsed.error));
178
188
  }
179
- return parsed.data.hits;
189
+ // Re-applied here: a server that predates the filters ignores them and answers every module (#2014 review F9).
190
+ return parsed.data.hits.filter((hit) => moduleSearchHitMatchesFilter(hit, args.filter ?? {}));
180
191
  },
181
192
  /**
182
193
  * Stream-download a published artifact's bytes and verify the
@@ -199,27 +210,73 @@ export const RegistryServerClient = {
199
210
  throw new TypeError(`download() expectedDigest must be a valid sha256:<64-hex> string (got: ${JSON.stringify(args.expectedDigest)}). ${formatZodIssuesAsSingleLine(digestParse.error)}`);
200
211
  }
201
212
  const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/${encodeURIComponent(args.version)}/artifact`);
202
- const res = await fetchWithTimeout(url, {
213
+ // A transfer, not a metadata call: the response is bounded by `timeoutMs`, the body by an IDLE budget
214
+ // reset on every chunk, so a large artifact over a slow link installs and a stalled one fails (#1763).
215
+ const { response: res, readAll } = await fetchTransfer(url, {
203
216
  method: 'GET',
204
217
  headers: authHeaders(options),
205
218
  timeoutMs: options.timeoutMs,
206
219
  diagnosticLabel: DIAGNOSTIC_LABEL,
207
220
  });
208
- if (res.status === 404)
209
- throw new RegistryVersionNotFoundError(args.moduleId, args.version);
210
- const authErr = await classifyAuthError(res, options.baseUrl, `download ${args.moduleId}@${args.version}`);
211
- if (authErr)
212
- throw authErr;
213
- if (!res.ok) {
214
- throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `download ${args.moduleId}@${args.version}`);
215
- }
216
- const buf = new Uint8Array(await res.arrayBuffer());
221
+ await refuseUnlessOk(res, options, `download ${args.moduleId}@${args.version}`, () => new RegistryVersionNotFoundError(args.moduleId, args.version));
222
+ const buf = await readAll();
217
223
  const { createHash } = await import('node:crypto');
218
224
  const observed = `sha256:${createHash('sha256').update(buf).digest('hex')}`;
219
225
  if (observed !== args.expectedDigest) {
220
226
  throw new RegistryArtifactIntegrityMismatchError(args.moduleId, args.version, args.expectedDigest, observed);
221
227
  }
222
- return { bytes: buf, integrity: observed };
228
+ // A yanked release is served by exact version (#1758) and says so in headers.
229
+ const yankedAt = res.headers.get(REGISTRY_YANKED_AT_HEADER);
230
+ const encodedReason = res.headers.get(REGISTRY_YANK_REASON_HEADER);
231
+ let reason;
232
+ if (encodedReason) {
233
+ try {
234
+ reason = decodeURIComponent(encodedReason);
235
+ }
236
+ catch {
237
+ reason = encodedReason;
238
+ }
239
+ }
240
+ return { bytes: buf, integrity: observed, ...(yankedAt ? { yanked: { yankedAt, ...(reason ? { reason } : {}) } } : {}) };
241
+ },
242
+ /**
243
+ * Yank a release (#1758): ranges and `latest` stop selecting it, and `latest` moves to the highest
244
+ * live release; an exact pin still installs it, with a warning. Needs a key holding the version UPDATE
245
+ * role in the domain's owning organization. Returns where `latest` points afterwards.
246
+ */
247
+ async yank(options, args) {
248
+ return changeYankState(options, args, RegistryYankVerb.YANK);
249
+ },
250
+ /** Restore a yanked release (#1758); `latest` returns to it when it outranks the current target. */
251
+ async unyank(options, args) {
252
+ return changeYankState(options, args, RegistryYankVerb.UNYANK);
223
253
  },
224
254
  };
255
+ const RegistryYankResponseSchema = z.object({ ok: z.literal(true), latest: z.string().nullable().optional() });
256
+ async function changeYankState(options, args, verb) {
257
+ const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/${verb}`);
258
+ const action = `${verb} ${args.moduleId}@${args.version}`;
259
+ const res = await fetchWithTimeout(url, {
260
+ method: 'POST',
261
+ headers: { ...authHeaders(options), 'content-type': 'application/json' },
262
+ body: JSON.stringify({ version: args.version, ...(args.reason !== undefined ? { reason: args.reason } : {}) }),
263
+ timeoutMs: options.timeoutMs,
264
+ diagnosticLabel: DIAGNOSTIC_LABEL,
265
+ });
266
+ if (res.status === 409) {
267
+ // Only the release-state refusal is the no-op; a 409 for anything else (the registry giving up after
268
+ // lost optimistic checks) is a FAILED yank and must not be reported as done (#1758 re-review R3).
269
+ const body = await res.json().catch(() => undefined);
270
+ if (isRegistryReleaseStateConflictBody(body))
271
+ throw new RegistryYankStateConflictError(args.moduleId, args.version, verb);
272
+ throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, action);
273
+ }
274
+ // A 404 names the module or the version; the sanitized envelope does not say which.
275
+ await refuseUnlessOk(res, options, action, () => new RegistryVersionNotFoundError(args.moduleId, args.version));
276
+ const parsed = RegistryYankResponseSchema.safeParse(await parseJsonOrThrow(res, options.baseUrl, action, options.timeoutMs));
277
+ if (!parsed.success) {
278
+ throw new RegistryMalformedPayloadError(options.baseUrl, action, formatZodIssuesAsSingleLine(parsed.error));
279
+ }
280
+ return { latest: parsed.data.latest ?? null };
281
+ }
225
282
  //# sourceMappingURL=registry-server-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry-server-client.js","sourceRoot":"","sources":["../../../../src/services/registry-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `RegistryServerClient` — HTTP client for the central registry\n * server (`platform-module-registry`, KD-7).\n *\n * Phase 2 fills out the read + download surface (`resolve`,\n * `info`, `search`, `download`). The wire shapes mirror the\n * gateway endpoints documented in the analysis plan's \"Registry\n * HTTP API\" section. The actual server (Phase 3) ships these\n * routes; until it does, the client throws typed errors when it\n * can't reach the server.\n *\n * Auth (KD-11): if `authToken` is supplied, every request adds\n * `Authorization: Bearer <token>`. Public read paths work without\n * a token; restricted paths return 401/403, surfaced as\n * `RegistryAuthRequiredError` / `RegistryAuthForbiddenError`.\n */\n\nimport { z } from 'zod';\nimport { formatZodIssuesAsSingleLine } from './_zod-issue-formatting';\nimport {\n ModuleManifestSchema,\n ModuleVersionRecordSchema,\n RegistryArtifactIntegrityMismatchError,\n RegistryAuthForbiddenError,\n RegistryAuthRequiredError,\n RegistryError,\n RegistryMalformedPayloadError,\n RegistryModuleNotFoundError,\n RegistryUnexpectedStatusError,\n RegistryVersionNotFoundError,\n Sha256DigestSchema,\n type ModuleManifest,\n type ModuleVersionRecord,\n} from '@wildo-ai/platform-config-lib';\nimport { fetchJsonOk, fetchWithTimeout, joinUrl } from './_http-client';\n\n/**\n * Zod schema for the `info()` response payload — mirrors\n * `RegistryInfoResult` and is enforced at the client boundary so a\n * server schema drift fails fast instead of returning malformed data\n * to downstream consumers.\n */\nconst RegistryInfoResponseSchema = z.object({\n moduleId: z.string().min(1),\n manifest: ModuleManifestSchema,\n versions: z.array(ModuleVersionRecordSchema),\n tags: z.record(z.string(), z.string()),\n});\n\nconst RegistrySearchResponseSchema = z.object({\n hits: z.array(\n z.object({\n moduleId: z.string().min(1),\n description: z.string().optional(),\n latestVersion: z.string(),\n }),\n ),\n});\n\nconst DIAGNOSTIC_LABEL = 'Registry';\n\nexport interface RegistryServerClientOptions {\n /** Registry origin (e.g. `https://registry.wildo.ai`). */\n baseUrl: string;\n /** Optional bearer / API-key. CLI credentials are per-machine (KD-11). */\n authToken?: string;\n /** Request timeout in ms. */\n timeoutMs?: number;\n}\n\nfunction authHeaders(options: RegistryServerClientOptions): Record<string, string> {\n return options.authToken ? { Authorization: `Bearer ${options.authToken}` } : {};\n}\n\nasync function classifyAuthError(res: Response, registryUrl: string, action: string): Promise<RegistryError | null> {\n if (res.status === 401) return new RegistryAuthRequiredError(registryUrl);\n if (res.status === 403) return new RegistryAuthForbiddenError(action);\n return null;\n}\n\n/**\n * 6th-pass M3: typed JSON-parse helper. A misconfigured proxy that\n * returns HTML at 2xx (or a truncated stream) would otherwise throw a\n * bare `SyntaxError` from `await res.json()`, bypassing every\n * `isRegistryError(err)` handler. This helper translates the parse\n * failure into a `RegistryMalformedPayloadError` so the typed-error\n * contract is universal across the client surface.\n */\nasync function parseJsonOrThrow(\n res: Response,\n registryUrl: string,\n action: string,\n): Promise<unknown> {\n try {\n return await res.json();\n } catch (err) {\n throw new RegistryMalformedPayloadError(\n registryUrl,\n action,\n `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nexport interface RegistryResolveResult {\n /** Resolved exact version (matches `Sha256DigestSchema` for digest). */\n version: string;\n artifactDigest: string;\n}\n\nexport interface RegistryInfoResult {\n moduleId: string;\n manifest: ModuleManifest;\n versions: readonly ModuleVersionRecord[];\n /** dist-tag → version map (e.g. `latest → 1.2.3`). */\n tags: Readonly<Record<string, string>>;\n}\n\nexport interface RegistrySearchHit {\n moduleId: string;\n description?: string;\n latestVersion: string;\n}\n\nexport const RegistryServerClient = {\n /**\n * Liveness probe against `/health`. Returns boolean — used by\n * `wildo registry ping` smoke. The registry server now relies on\n * the framework's auto-mounted `HealthController` at the engine\n * root (`GET /health` always 200; `GET /health/ready` enforces\n * dependency readiness), so the path has NO `/v1` prefix.\n */\n async ping(options: RegistryServerClientOptions): Promise<boolean> {\n try {\n await fetchJsonOk(joinUrl(options.baseUrl, '/health'), {\n method: 'GET',\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n return true;\n } catch {\n return false;\n }\n },\n\n /**\n * Resolve a semver range / tag against a registry domain. The\n * server returns the concrete version + artifact digest so the\n * caller can verify the download against `artifactDigest`.\n */\n async resolve(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string; range: string },\n ): Promise<RegistryResolveResult> {\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/resolve?range=${encodeURIComponent(args.range)}`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n if (res.status === 404) {\n throw new RegistryVersionNotFoundError(args.moduleId, args.range);\n }\n const authErr = await classifyAuthError(res, options.baseUrl, `resolve ${args.moduleId}`);\n if (authErr) throw authErr;\n if (!res.ok) {\n throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `resolve ${args.moduleId}`);\n }\n // 6th-pass M3: typed JSON-parse (was bare `SyntaxError` for\n // malformed proxy responses).\n const json = (await parseJsonOrThrow(res, options.baseUrl, `resolve ${args.moduleId}`)) as {\n version?: unknown;\n artifactDigest?: unknown;\n };\n const digest = String(json.artifactDigest ?? '');\n // 6th-pass M4: typed digest validation (was bare `ZodError`).\n const digestParse = Sha256DigestSchema.safeParse(digest);\n if (!digestParse.success) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `resolve ${args.moduleId}`,\n `artifactDigest is not a valid sha256:<64-hex> string: ${formatZodIssuesAsSingleLine(digestParse.error)}`,\n );\n }\n if (typeof json.version !== 'string' || json.version.length === 0) {\n // 3rd-pass M6: typed `RegistryMalformedPayloadError` instead of\n // bare `Error` so callers' `isRegistryError(err)` handlers fire\n // correctly.\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `resolve ${args.moduleId}`,\n 'response missing required `version` string',\n );\n }\n return { version: json.version, artifactDigest: digest };\n },\n\n /**\n * Fetch full info for a module: manifest, every published\n * version, dist-tags. Used by `wildo registry info` (Phase 4).\n */\n async info(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string },\n ): Promise<RegistryInfoResult> {\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n if (res.status === 404) throw new RegistryModuleNotFoundError(args.moduleId);\n const authErr = await classifyAuthError(res, options.baseUrl, `info ${args.moduleId}`);\n if (authErr) throw authErr;\n if (!res.ok) {\n throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `info ${args.moduleId}`);\n }\n // 6th-pass M3: typed JSON-parse.\n const json = await parseJsonOrThrow(res, options.baseUrl, `info ${args.moduleId}`);\n const parsed = RegistryInfoResponseSchema.safeParse(json);\n if (!parsed.success) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `info ${args.moduleId}`,\n formatZodIssuesAsSingleLine(parsed.error),\n );\n }\n return parsed.data;\n },\n\n /**\n * Search the registry's metadata index. The shape is\n * intentionally narrow until Phase 4 designs the search UX.\n */\n async search(\n options: RegistryServerClientOptions,\n args: { domain: string; query: string },\n ): Promise<readonly RegistrySearchHit[]> {\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules?q=${encodeURIComponent(args.query)}`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n const authErr = await classifyAuthError(res, options.baseUrl, `search`);\n if (authErr) throw authErr;\n if (!res.ok) {\n throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, 'search');\n }\n // 6th-pass M3: typed JSON-parse.\n const json = await parseJsonOrThrow(res, options.baseUrl, 'search');\n const parsed = RegistrySearchResponseSchema.safeParse(json);\n if (!parsed.success) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n 'search',\n formatZodIssuesAsSingleLine(parsed.error),\n );\n }\n return parsed.data.hits;\n },\n\n /**\n * Stream-download a published artifact's bytes and verify the\n * sha256 integrity matches `expectedDigest`. Throws\n * `RegistryArtifactIntegrityMismatchError` on mismatch — the\n * digest check is non-negotiable per KD-9 / Security model.\n */\n async download(\n options: RegistryServerClientOptions,\n args: {\n domain: string;\n moduleId: string;\n version: string;\n expectedDigest: string;\n },\n ): Promise<{ bytes: Uint8Array; integrity: string }> {\n // 7th-pass C-M1: caller-supplied digest is a developer-contract\n // violation if malformed, not a registry-runtime condition.\n // Throw `TypeError` (matching `C-L3` precedent for\n // `getModuleStagingPaths` argument-shape violations) so the\n // typed `RegistryError` vocabulary stays reserved for\n // registry-runtime errors. Prior `Sha256DigestSchema.parse(...)`\n // leaked a bare `ZodError` that bypassed\n // `isRegistryError(err)` handlers but ALSO confused\n // typed-handler maintainers about the contract.\n const digestParse = Sha256DigestSchema.safeParse(args.expectedDigest);\n if (!digestParse.success) {\n throw new TypeError(\n `download() expectedDigest must be a valid sha256:<64-hex> string (got: ${JSON.stringify(args.expectedDigest)}). ${formatZodIssuesAsSingleLine(digestParse.error)}`,\n );\n }\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/${encodeURIComponent(args.version)}/artifact`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n if (res.status === 404) throw new RegistryVersionNotFoundError(args.moduleId, args.version);\n const authErr = await classifyAuthError(res, options.baseUrl, `download ${args.moduleId}@${args.version}`);\n if (authErr) throw authErr;\n if (!res.ok) {\n throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, `download ${args.moduleId}@${args.version}`);\n }\n const buf = new Uint8Array(await res.arrayBuffer());\n const { createHash } = await import('node:crypto');\n const observed = `sha256:${createHash('sha256').update(buf).digest('hex')}`;\n if (observed !== args.expectedDigest) {\n throw new RegistryArtifactIntegrityMismatchError(\n args.moduleId,\n args.version,\n args.expectedDigest,\n observed,\n );\n }\n return { bytes: buf, integrity: observed };\n },\n} as const;\n"]}
1
+ {"version":3,"file":"registry-server-client.js","sourceRoot":"","sources":["../../../../src/services/registry-server-client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `RegistryServerClient` — HTTP client for the central registry\n * server (`platform-module-registry`, KD-7).\n *\n * Phase 2 fills out the read + download surface (`resolve`,\n * `info`, `search`, `download`). The wire shapes mirror the\n * gateway endpoints documented in the analysis plan's \"Registry\n * HTTP API\" section. The actual server (Phase 3) ships these\n * routes; until it does, the client throws typed errors when it\n * can't reach the server.\n *\n * Auth (KD-11): if `authToken` is supplied, every request adds\n * `Authorization: Bearer <token>`. Public read paths work without\n * a token; restricted paths return 401/403, surfaced as\n * `RegistryAuthRequiredError` (no key sent), `RegistryCredentialRejectedError` (a key sent and refused)\n * or `RegistryAuthForbiddenError`; a 429 is `RegistryRateLimitedError`.\n */\n\nimport { z } from 'zod';\nimport { formatZodIssuesAsSingleLine } from './_zod-issue-formatting';\nimport {\n ModuleManifestSchema,\n ModuleVersionRecordSchema,\n RegistryArtifactIntegrityMismatchError,\n RegistryAuthForbiddenError,\n RegistryAuthRequiredError,\n RegistryCredentialRejectedError,\n RegistryError,\n RegistryRateLimitedError,\n isRegistryError,\n isRegistryReleaseStateConflictBody,\n RegistryMalformedPayloadError,\n RegistryModuleNotFoundError,\n RegistryUnexpectedStatusError,\n RegistryVersionNotFoundError,\n RegistryYankNoticeSchema,\n RegistryYankStateConflictError,\n RegistryYankVerb,\n REGISTRY_YANK_REASON_HEADER,\n REGISTRY_YANKED_AT_HEADER,\n Sha256DigestSchema,\n ModuleSearchHitSchema,\n moduleSearchFilterQueryPairs,\n moduleSearchHitMatchesFilter,\n type ModuleManifest,\n type ModuleSearchFilter,\n type ModuleSearchHit,\n type RegistryYankNotice,\n type ModuleVersionRecord,\n} from '@wildo-ai/platform-config-lib';\nimport { fetchJsonOk, fetchTransfer, fetchWithTimeout, joinUrl, readJsonBody } from './_http-client';\n\n/**\n * Zod schema for the `info()` response payload — mirrors\n * `RegistryInfoResult` and is enforced at the client boundary so a\n * server schema drift fails fast instead of returning malformed data\n * to downstream consumers.\n */\nconst RegistryInfoResponseSchema = z.object({\n moduleId: z.string().min(1),\n manifest: ModuleManifestSchema,\n versions: z.array(ModuleVersionRecordSchema),\n tags: z.record(z.string(), z.string()),\n});\n\nconst RegistrySearchResponseSchema = z.object({\n hits: z.array(ModuleSearchHitSchema),\n});\n\nconst DIAGNOSTIC_LABEL = 'Registry';\n\nexport interface RegistryServerClientOptions {\n /** Registry origin (e.g. `https://registry.wildo.ai`). */\n baseUrl: string;\n /** Optional bearer / API-key. CLI credentials are per-machine (KD-11). */\n authToken?: string;\n /** Request timeout in ms. */\n timeoutMs?: number;\n}\n\nfunction authHeaders(options: RegistryServerClientOptions): Record<string, string> {\n return options.authToken ? { Authorization: `Bearer ${options.authToken}` } : {};\n}\n\n/**\n * Throw the typed error for a non-2xx response, after releasing its body (#1763 review: an unread body\n * holds the connection until garbage collection). A 401 is told apart by whether a key was SENT — \"log\n * in\" and \"your key no longer works\" are different remedies (#1759 review).\n */\nasync function refuseUnlessOk(\n res: Response,\n options: RegistryServerClientOptions,\n action: string,\n notFound?: () => RegistryError,\n): Promise<void> {\n if (res.ok) return;\n let error: RegistryError;\n if (res.status === 404 && notFound) error = notFound();\n else if (res.status === 401) error = options.authToken ? new RegistryCredentialRejectedError(options.baseUrl) : new RegistryAuthRequiredError(options.baseUrl);\n else if (res.status === 403) error = new RegistryAuthForbiddenError(action);\n else if (res.status === 429) error = new RegistryRateLimitedError(options.baseUrl, action, parseRetryAfterSeconds(res.headers.get('retry-after')));\n else error = new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, action);\n await res.body?.cancel().catch(() => undefined);\n throw error;\n}\n\n/** `Retry-After` in its delta-seconds form; the HTTP-date form is not produced by a Wildo backend. */\nfunction parseRetryAfterSeconds(value: string | null): number | undefined {\n if (!value || !/^\\d+$/.test(value.trim())) return undefined;\n return Number(value.trim());\n}\n\n/**\n * 6th-pass M3: typed JSON-parse helper. A misconfigured proxy that\n * returns HTML at 2xx (or a truncated stream) would otherwise throw a\n * bare `SyntaxError` from `await res.json()`, bypassing every\n * `isRegistryError(err)` handler. This helper translates the parse\n * failure into a `RegistryMalformedPayloadError` so the typed-error\n * contract is universal across the client surface.\n */\nasync function parseJsonOrThrow(\n res: Response,\n registryUrl: string,\n action: string,\n timeoutMs: number | undefined,\n): Promise<unknown> {\n try {\n return await readJsonBody(res, registryUrl, action, timeoutMs);\n } catch (err) {\n // A body cut short by the request budget is a timeout (#1763), not a malformed payload.\n if (isRegistryError(err)) throw err;\n throw new RegistryMalformedPayloadError(\n registryUrl,\n action,\n `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nexport interface RegistryResolveResult {\n /** Resolved exact version (matches `Sha256DigestSchema` for digest). */\n version: string;\n artifactDigest: string;\n /**\n * Present when the resolved release is yanked. Only an EXACT-version request can resolve to one\n * (#1758) — ranges and tags never do — so the caller is pinned to it and should warn, not refuse.\n */\n yanked?: RegistryYankNotice;\n}\n\nexport interface RegistryInfoResult {\n moduleId: string;\n manifest: ModuleManifest;\n versions: readonly ModuleVersionRecord[];\n /** dist-tag → version map (e.g. `latest → 1.2.3`). */\n tags: Readonly<Record<string, string>>;\n}\n\n/** One search hit: the module, its latest version, and (#2014) its kind and provider facts. */\nexport type RegistrySearchHit = ModuleSearchHit;\n\nexport const RegistryServerClient = {\n /**\n * Liveness probe against `/health`. Returns boolean — used by\n * `wildo registry ping` smoke. The registry server now relies on\n * the framework's auto-mounted `HealthController` at the engine\n * root (`GET /health` always 200; `GET /health/ready` enforces\n * dependency readiness), so the path has NO `/v1` prefix.\n */\n async ping(options: RegistryServerClientOptions): Promise<boolean> {\n try {\n await fetchJsonOk(joinUrl(options.baseUrl, '/health'), {\n method: 'GET',\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n return true;\n } catch {\n return false;\n }\n },\n\n /**\n * Resolve a semver range / tag against a registry domain. The\n * server returns the concrete version + artifact digest so the\n * caller can verify the download against `artifactDigest`.\n */\n async resolve(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string; range: string },\n ): Promise<RegistryResolveResult> {\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/resolve?range=${encodeURIComponent(args.range)}`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n await refuseUnlessOk(res, options, `resolve ${args.moduleId}`, () => new RegistryVersionNotFoundError(args.moduleId, args.range));\n // 6th-pass M3: typed JSON-parse (was bare `SyntaxError` for\n // malformed proxy responses).\n const json = (await parseJsonOrThrow(res, options.baseUrl, `resolve ${args.moduleId}`, options.timeoutMs)) as {\n version?: unknown;\n artifactDigest?: unknown;\n yanked?: unknown;\n };\n const digest = String(json.artifactDigest ?? '');\n // 6th-pass M4: typed digest validation (was bare `ZodError`).\n const digestParse = Sha256DigestSchema.safeParse(digest);\n if (!digestParse.success) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `resolve ${args.moduleId}`,\n `artifactDigest is not a valid sha256:<64-hex> string: ${formatZodIssuesAsSingleLine(digestParse.error)}`,\n );\n }\n if (typeof json.version !== 'string' || json.version.length === 0) {\n // 3rd-pass M6: typed `RegistryMalformedPayloadError` instead of\n // bare `Error` so callers' `isRegistryError(err)` handlers fire\n // correctly.\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `resolve ${args.moduleId}`,\n 'response missing required `version` string',\n );\n }\n let yanked: RegistryYankNotice | undefined;\n if (json.yanked !== undefined) {\n const yankParse = RegistryYankNoticeSchema.safeParse(json.yanked);\n if (!yankParse.success) {\n throw new RegistryMalformedPayloadError(options.baseUrl, `resolve ${args.moduleId}`, `yanked: ${formatZodIssuesAsSingleLine(yankParse.error)}`);\n }\n yanked = yankParse.data;\n }\n return { version: json.version, artifactDigest: digest, ...(yanked ? { yanked } : {}) };\n },\n\n /**\n * Fetch full info for a module: manifest, every published\n * version, dist-tags. Used by `wildo registry info` (Phase 4).\n */\n async info(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string },\n ): Promise<RegistryInfoResult> {\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n await refuseUnlessOk(res, options, `info ${args.moduleId}`, () => new RegistryModuleNotFoundError(args.moduleId));\n // 6th-pass M3: typed JSON-parse.\n const json = await parseJsonOrThrow(res, options.baseUrl, `info ${args.moduleId}`, options.timeoutMs);\n const parsed = RegistryInfoResponseSchema.safeParse(json);\n if (!parsed.success) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n `info ${args.moduleId}`,\n formatZodIssuesAsSingleLine(parsed.error),\n );\n }\n return parsed.data;\n },\n\n /**\n * Search the registry's metadata index: a module-id substring, and (#2014) the manifest filters —\n * kind, engine capability and provider maturity. The server refuses an unknown filter value.\n */\n async search(\n options: RegistryServerClientOptions,\n args: { domain: string; query: string; filter?: ModuleSearchFilter },\n ): Promise<readonly RegistrySearchHit[]> {\n const params = new URLSearchParams([['q', args.query], ...moduleSearchFilterQueryPairs(args.filter ?? {})]);\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules?${params.toString()}`,\n );\n const res = await fetchWithTimeout(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n await refuseUnlessOk(res, options, 'search');\n // 6th-pass M3: typed JSON-parse.\n const json = await parseJsonOrThrow(res, options.baseUrl, 'search', options.timeoutMs);\n const parsed = RegistrySearchResponseSchema.safeParse(json);\n if (!parsed.success) {\n throw new RegistryMalformedPayloadError(\n options.baseUrl,\n 'search',\n formatZodIssuesAsSingleLine(parsed.error),\n );\n }\n // Re-applied here: a server that predates the filters ignores them and answers every module (#2014 review F9).\n return parsed.data.hits.filter((hit) => moduleSearchHitMatchesFilter(hit, args.filter ?? {}));\n },\n\n /**\n * Stream-download a published artifact's bytes and verify the\n * sha256 integrity matches `expectedDigest`. Throws\n * `RegistryArtifactIntegrityMismatchError` on mismatch — the\n * digest check is non-negotiable per KD-9 / Security model.\n */\n async download(\n options: RegistryServerClientOptions,\n args: {\n domain: string;\n moduleId: string;\n version: string;\n expectedDigest: string;\n },\n ): Promise<{ bytes: Uint8Array; integrity: string; yanked?: RegistryYankNotice }> {\n // 7th-pass C-M1: caller-supplied digest is a developer-contract\n // violation if malformed, not a registry-runtime condition.\n // Throw `TypeError` (matching `C-L3` precedent for\n // `getModuleStagingPaths` argument-shape violations) so the\n // typed `RegistryError` vocabulary stays reserved for\n // registry-runtime errors. Prior `Sha256DigestSchema.parse(...)`\n // leaked a bare `ZodError` that bypassed\n // `isRegistryError(err)` handlers but ALSO confused\n // typed-handler maintainers about the contract.\n const digestParse = Sha256DigestSchema.safeParse(args.expectedDigest);\n if (!digestParse.success) {\n throw new TypeError(\n `download() expectedDigest must be a valid sha256:<64-hex> string (got: ${JSON.stringify(args.expectedDigest)}). ${formatZodIssuesAsSingleLine(digestParse.error)}`,\n );\n }\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/${encodeURIComponent(args.version)}/artifact`,\n );\n // A transfer, not a metadata call: the response is bounded by `timeoutMs`, the body by an IDLE budget\n // reset on every chunk, so a large artifact over a slow link installs and a stalled one fails (#1763).\n const { response: res, readAll } = await fetchTransfer(url, {\n method: 'GET',\n headers: authHeaders(options),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n await refuseUnlessOk(res, options, `download ${args.moduleId}@${args.version}`, () => new RegistryVersionNotFoundError(args.moduleId, args.version));\n const buf = await readAll();\n const { createHash } = await import('node:crypto');\n const observed = `sha256:${createHash('sha256').update(buf).digest('hex')}`;\n if (observed !== args.expectedDigest) {\n throw new RegistryArtifactIntegrityMismatchError(\n args.moduleId,\n args.version,\n args.expectedDigest,\n observed,\n );\n }\n // A yanked release is served by exact version (#1758) and says so in headers.\n const yankedAt = res.headers.get(REGISTRY_YANKED_AT_HEADER);\n const encodedReason = res.headers.get(REGISTRY_YANK_REASON_HEADER);\n let reason: string | undefined;\n if (encodedReason) {\n try { reason = decodeURIComponent(encodedReason); } catch { reason = encodedReason; }\n }\n return { bytes: buf, integrity: observed, ...(yankedAt ? { yanked: { yankedAt, ...(reason ? { reason } : {}) } } : {}) };\n },\n /**\n * Yank a release (#1758): ranges and `latest` stop selecting it, and `latest` moves to the highest\n * live release; an exact pin still installs it, with a warning. Needs a key holding the version UPDATE\n * role in the domain's owning organization. Returns where `latest` points afterwards.\n */\n async yank(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string; version: string; reason?: string },\n ): Promise<{ latest: string | null }> {\n return changeYankState(options, args, RegistryYankVerb.YANK);\n },\n\n /** Restore a yanked release (#1758); `latest` returns to it when it outranks the current target. */\n async unyank(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string; version: string },\n ): Promise<{ latest: string | null }> {\n return changeYankState(options, args, RegistryYankVerb.UNYANK);\n },\n} as const;\n\nconst RegistryYankResponseSchema = z.object({ ok: z.literal(true), latest: z.string().nullable().optional() });\n\nasync function changeYankState(\n options: RegistryServerClientOptions,\n args: { domain: string; moduleId: string; version: string; reason?: string },\n verb: RegistryYankVerb,\n): Promise<{ latest: string | null }> {\n const url = joinUrl(\n options.baseUrl,\n `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/${verb}`,\n );\n const action = `${verb} ${args.moduleId}@${args.version}`;\n const res = await fetchWithTimeout(url, {\n method: 'POST',\n headers: { ...authHeaders(options), 'content-type': 'application/json' },\n body: JSON.stringify({ version: args.version, ...(args.reason !== undefined ? { reason: args.reason } : {}) }),\n timeoutMs: options.timeoutMs,\n diagnosticLabel: DIAGNOSTIC_LABEL,\n });\n if (res.status === 409) {\n // Only the release-state refusal is the no-op; a 409 for anything else (the registry giving up after\n // lost optimistic checks) is a FAILED yank and must not be reported as done (#1758 re-review R3).\n const body = await res.json().catch(() => undefined);\n if (isRegistryReleaseStateConflictBody(body)) throw new RegistryYankStateConflictError(args.moduleId, args.version, verb);\n throw new RegistryUnexpectedStatusError(options.baseUrl, res.status, res.statusText, action);\n }\n // A 404 names the module or the version; the sanitized envelope does not say which.\n await refuseUnlessOk(res, options, action, () => new RegistryVersionNotFoundError(args.moduleId, args.version));\n const parsed = RegistryYankResponseSchema.safeParse(await parseJsonOrThrow(res, options.baseUrl, action, options.timeoutMs));\n if (!parsed.success) {\n throw new RegistryMalformedPayloadError(options.baseUrl, action, formatZodIssuesAsSingleLine(parsed.error));\n }\n return { latest: parsed.data.latest ?? null };\n}\n"]}