@wildo-ai/wildo-module-client 1.1.1
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/LICENSE +34 -0
- package/dist/esm/.builder.pid +9 -0
- package/dist/esm/__tests__/lockfile-repair.service.test.d.ts +0 -0
- package/dist/esm/__tests__/lockfile-repair.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/lockfile-repair.service.test.js.map +0 -0
- package/dist/esm/__tests__/module-adopter.service.test.d.ts +0 -0
- package/dist/esm/__tests__/module-adopter.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/module-adopter.service.test.js.map +0 -0
- package/dist/esm/__tests__/module-packager.service.test.d.ts +0 -0
- package/dist/esm/__tests__/module-packager.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/module-packager.service.test.js.map +0 -0
- package/dist/esm/__tests__/provider-materialization-hook.test.d.ts +0 -0
- package/dist/esm/__tests__/provider-materialization-hook.test.d.ts.map +0 -0
- package/dist/esm/__tests__/provider-materialization-hook.test.js.map +0 -0
- package/dist/esm/__tests__/provider-sync-runner.test.d.ts +0 -0
- package/dist/esm/__tests__/provider-sync-runner.test.d.ts.map +0 -0
- package/dist/esm/__tests__/provider-sync-runner.test.js.map +0 -0
- package/dist/esm/__tests__/registry-lockfile.service.test.d.ts +0 -0
- package/dist/esm/__tests__/registry-lockfile.service.test.d.ts.map +0 -0
- package/dist/esm/__tests__/registry-lockfile.service.test.js.map +0 -0
- package/dist/esm/__tests__/registry-server-client.test.d.ts +0 -0
- package/dist/esm/__tests__/registry-server-client.test.d.ts.map +0 -0
- package/dist/esm/__tests__/registry-server-client.test.js.map +0 -0
- package/dist/esm/index.d.ts +18 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/services/_http-client.d.ts +44 -0
- package/dist/esm/services/_http-client.d.ts.map +1 -0
- package/dist/esm/services/_http-client.js +75 -0
- package/dist/esm/services/_http-client.js.map +1 -0
- package/dist/esm/services/_zod-issue-formatting.d.ts +12 -0
- package/dist/esm/services/_zod-issue-formatting.d.ts.map +1 -0
- package/dist/esm/services/_zod-issue-formatting.js +47 -0
- package/dist/esm/services/_zod-issue-formatting.js.map +1 -0
- package/dist/esm/services/index.d.ts +10 -0
- package/dist/esm/services/index.d.ts.map +1 -0
- package/dist/esm/services/index.js +10 -0
- package/dist/esm/services/index.js.map +1 -0
- package/dist/esm/services/lockfile-repair.service.d.ts +68 -0
- package/dist/esm/services/lockfile-repair.service.d.ts.map +1 -0
- package/dist/esm/services/lockfile-repair.service.js +160 -0
- package/dist/esm/services/lockfile-repair.service.js.map +1 -0
- package/dist/esm/services/module-adopter.service.d.ts +59 -0
- package/dist/esm/services/module-adopter.service.d.ts.map +1 -0
- package/dist/esm/services/module-adopter.service.js +126 -0
- package/dist/esm/services/module-adopter.service.js.map +1 -0
- package/dist/esm/services/module-packager.service.d.ts +105 -0
- package/dist/esm/services/module-packager.service.d.ts.map +1 -0
- package/dist/esm/services/module-packager.service.js +212 -0
- package/dist/esm/services/module-packager.service.js.map +1 -0
- package/dist/esm/services/provider-materialization-hook.d.ts +32 -0
- package/dist/esm/services/provider-materialization-hook.d.ts.map +1 -0
- package/dist/esm/services/provider-materialization-hook.js +33 -0
- package/dist/esm/services/provider-materialization-hook.js.map +1 -0
- package/dist/esm/services/provider-sync-runner.d.ts +87 -0
- package/dist/esm/services/provider-sync-runner.d.ts.map +1 -0
- package/dist/esm/services/provider-sync-runner.js +207 -0
- package/dist/esm/services/provider-sync-runner.js.map +1 -0
- package/dist/esm/services/registry-companion-client.d.ts +39 -0
- package/dist/esm/services/registry-companion-client.d.ts.map +1 -0
- package/dist/esm/services/registry-companion-client.js +109 -0
- package/dist/esm/services/registry-companion-client.js.map +1 -0
- package/dist/esm/services/registry-lockfile.service.d.ts +56 -0
- package/dist/esm/services/registry-lockfile.service.d.ts.map +1 -0
- package/dist/esm/services/registry-lockfile.service.js +99 -0
- package/dist/esm/services/registry-lockfile.service.js.map +1 -0
- package/dist/esm/services/registry-server-client.d.ts +94 -0
- package/dist/esm/services/registry-server-client.d.ts.map +1 -0
- package/dist/esm/services/registry-server-client.js +225 -0
- package/dist/esm/services/registry-server-client.js.map +1 -0
- package/dist/esm/services/types.d.ts +22 -0
- package/dist/esm/services/types.d.ts.map +1 -0
- package/dist/esm/services/types.js +16 -0
- package/dist/esm/services/types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +51 -0
- package/src/__tests__/lockfile-repair.service.test.ts +106 -0
- package/src/__tests__/module-adopter.service.test.ts +96 -0
- package/src/__tests__/module-packager.service.test.ts +258 -0
- package/src/__tests__/provider-materialization-hook.test.ts +47 -0
- package/src/__tests__/provider-sync-runner.test.ts +113 -0
- package/src/__tests__/registry-lockfile.service.test.ts +134 -0
- package/src/__tests__/registry-server-client.test.ts +312 -0
- package/src/index.ts +18 -0
- package/src/services/_http-client.ts +91 -0
- package/src/services/_zod-issue-formatting.ts +50 -0
- package/src/services/index.ts +9 -0
- package/src/services/lockfile-repair.service.ts +215 -0
- package/src/services/module-adopter.service.ts +155 -0
- package/src/services/module-packager.service.ts +251 -0
- package/src/services/provider-materialization-hook.ts +54 -0
- package/src/services/provider-sync-runner.ts +298 -0
- package/src/services/registry-companion-client.ts +163 -0
- package/src/services/registry-lockfile.service.ts +122 -0
- package/src/services/registry-server-client.ts +334 -0
- package/src/services/types.ts +22 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RegistryLockfileService` — section-scoped reader/writer for the
|
|
3
|
+
* `sections.modules` slice of `wildo-saas.lock.json` (KD-4).
|
|
4
|
+
*
|
|
5
|
+
* Phase 1 graduates the locking + atomic-write primitives into
|
|
6
|
+
* `@wildo-ai/platform-config-lib` (validated by the Phase 0.B
|
|
7
|
+
* contention spike). This service is a thin wrapper around
|
|
8
|
+
* `withLockfileSectionMutate` that pre-binds the `modules` section
|
|
9
|
+
* key + schema, so module-registry callers cannot accidentally
|
|
10
|
+
* touch sibling sections.
|
|
11
|
+
*
|
|
12
|
+
* Hard contracts (KD-4 / KD-15 / KD-18):
|
|
13
|
+
* - Mutates ONLY `sections.modules`. The shared library preserves
|
|
14
|
+
* `sections.providers`, `sections.i18n`, and every unknown
|
|
15
|
+
* `sections.*` payload verbatim — no defensive logic needed
|
|
16
|
+
* here.
|
|
17
|
+
* - Never writes `.wildo-saas/generated/providers/*.runtime-providers.generated.json`
|
|
18
|
+
* or `src/generated/wildo-provider-runtime.generated.ts` —
|
|
19
|
+
* those are produced by the provider materialization workflow.
|
|
20
|
+
* - Never mutates `sections.providers.runtimeScopes`.
|
|
21
|
+
*/
|
|
22
|
+
import { RegistryModulesLockSectionSchema, withLockfileSectionMutate, readWildoSaasLock, } from '@wildo-ai/platform-config-lib';
|
|
23
|
+
// L6 (third-pass review): the package's barrel-discipline rule says
|
|
24
|
+
// "Never re-export (except in the root `index.ts` of each package)."
|
|
25
|
+
// The previous `export { WILDO_SAAS_LOCK_RELATIVE_PATH }` here AND in
|
|
26
|
+
// `lockfile-repair.service.ts` surfaced the symbol twice from
|
|
27
|
+
// `wildo-module-client`'s root. Consumers that need it can import
|
|
28
|
+
// directly from `@wildo-ai/platform-config-lib`, OR rely on the root
|
|
29
|
+
// `index.ts` to re-export it once.
|
|
30
|
+
const SECTION_KEY = 'modules';
|
|
31
|
+
function emptySection() {
|
|
32
|
+
return RegistryModulesLockSectionSchema.parse({});
|
|
33
|
+
}
|
|
34
|
+
export const RegistryLockfileService = {
|
|
35
|
+
/**
|
|
36
|
+
* Read the current `sections.modules` payload (or an empty section
|
|
37
|
+
* when missing). Unsynchronized — callers needing read-after-write
|
|
38
|
+
* coherence should read INSIDE `mutate`.
|
|
39
|
+
*/
|
|
40
|
+
async read(saasRoot) {
|
|
41
|
+
const envelope = await readWildoSaasLock({ saasRoot });
|
|
42
|
+
const raw = envelope.sections.modules;
|
|
43
|
+
if (raw === undefined)
|
|
44
|
+
return emptySection();
|
|
45
|
+
return RegistryModulesLockSectionSchema.parse(raw);
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Run `mutator` against the current `sections.modules` payload
|
|
49
|
+
* under an OS-level lock + atomic-write commit. The mutator can
|
|
50
|
+
* either mutate-in-place or return a new value; either way the
|
|
51
|
+
* result is re-validated against `RegistryModulesLockSectionSchema`
|
|
52
|
+
* before commit.
|
|
53
|
+
*/
|
|
54
|
+
async mutate(saasRoot, mutator) {
|
|
55
|
+
return withLockfileSectionMutate({
|
|
56
|
+
saasRoot,
|
|
57
|
+
sectionKey: SECTION_KEY,
|
|
58
|
+
sectionSchema: RegistryModulesLockSectionSchema,
|
|
59
|
+
initialSection: emptySection,
|
|
60
|
+
}, mutator);
|
|
61
|
+
},
|
|
62
|
+
/** Convenience helper used by Phase 5+ install flow. */
|
|
63
|
+
async upsertEntry(saasRoot, entry) {
|
|
64
|
+
await this.mutate(saasRoot, (current) => ({
|
|
65
|
+
...current,
|
|
66
|
+
installed: { ...current.installed, [entry.moduleId]: entry },
|
|
67
|
+
}));
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Find every other lockfile entry whose `files[]` claims any of the
|
|
71
|
+
* given paths. Used by `update`/`remove` to skip deletes that would
|
|
72
|
+
* silently pull files out from under modules that own them too
|
|
73
|
+
* (cross-module file overlap).
|
|
74
|
+
*
|
|
75
|
+
* Returns a `Map<relPath, owners[]>` where `owners` excludes
|
|
76
|
+
* `excludeModuleId`. Empty array → uniquely owned by the excluded
|
|
77
|
+
* module, safe to delete.
|
|
78
|
+
*/
|
|
79
|
+
async findFileOwners(args) {
|
|
80
|
+
const section = await this.read(args.saasRoot);
|
|
81
|
+
const claimedBy = new Map();
|
|
82
|
+
for (const p of args.paths)
|
|
83
|
+
claimedBy.set(p, []);
|
|
84
|
+
for (const otherModuleId of Object.keys(section.installed)) {
|
|
85
|
+
if (otherModuleId === args.excludeModuleId)
|
|
86
|
+
continue;
|
|
87
|
+
const otherEntry = section.installed[otherModuleId];
|
|
88
|
+
if (!otherEntry)
|
|
89
|
+
continue;
|
|
90
|
+
for (const otherFile of otherEntry.files) {
|
|
91
|
+
if (claimedBy.has(otherFile)) {
|
|
92
|
+
claimedBy.get(otherFile).push(otherModuleId);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return claimedBy;
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=registry-lockfile.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-lockfile.service.js","sourceRoot":"","sources":["../../../../src/services/registry-lockfile.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,gCAAgC,EAChC,yBAAyB,EACzB,iBAAiB,GAGlB,MAAM,+BAA+B,CAAC;AACvC,oEAAoE;AACpE,qEAAqE;AACrE,sEAAsE;AACtE,8DAA8D;AAC9D,kEAAkE;AAClE,qEAAqE;AACrE,mCAAmC;AAEnC,MAAM,WAAW,GAAG,SAAkB,CAAC;AAEvC,SAAS,YAAY;IACnB,OAAO,gCAAgC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,QAAgB;QACzB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvD,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtC,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,YAAY,EAAE,CAAC;QAC7C,OAAO,gCAAgC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CACV,QAAgB,EAChB,OAK8C;QAE9C,OAAO,yBAAyB,CAC9B;YACE,QAAQ;YACR,UAAU,EAAE,WAAW;YACvB,aAAa,EAAE,gCAAgC;YAC/C,cAAc,EAAE,YAAY;SAC7B,EACD,OAAO,CACR,CAAC;IACJ,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,WAAW,CAAC,QAAgB,EAAE,KAA+B;QACjE,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACxC,GAAG,OAAO;YACV,SAAS,EAAE,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE;SAC7D,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc,CAAC,IAIpB;QACC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;YAAE,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjD,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,IAAI,aAAa,KAAK,IAAI,CAAC,eAAe;gBAAE,SAAS;YACrD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YACpD,IAAI,CAAC,UAAU;gBAAE,SAAS;YAC1B,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;gBACzC,IAAI,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC7B,SAAS,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACO,CAAC","sourcesContent":["/**\n * `RegistryLockfileService` — section-scoped reader/writer for the\n * `sections.modules` slice of `wildo-saas.lock.json` (KD-4).\n *\n * Phase 1 graduates the locking + atomic-write primitives into\n * `@wildo-ai/platform-config-lib` (validated by the Phase 0.B\n * contention spike). This service is a thin wrapper around\n * `withLockfileSectionMutate` that pre-binds the `modules` section\n * key + schema, so module-registry callers cannot accidentally\n * touch sibling sections.\n *\n * Hard contracts (KD-4 / KD-15 / KD-18):\n * - Mutates ONLY `sections.modules`. The shared library preserves\n * `sections.providers`, `sections.i18n`, and every unknown\n * `sections.*` payload verbatim — no defensive logic needed\n * here.\n * - Never writes `.wildo-saas/generated/providers/*.runtime-providers.generated.json`\n * or `src/generated/wildo-provider-runtime.generated.ts` —\n * those are produced by the provider materialization workflow.\n * - Never mutates `sections.providers.runtimeScopes`.\n */\n\nimport {\n RegistryModulesLockSectionSchema,\n withLockfileSectionMutate,\n readWildoSaasLock,\n type RegistryModulesLockSection,\n type RegistryModulesLockEntry,\n} from '@wildo-ai/platform-config-lib';\n// L6 (third-pass review): the package's barrel-discipline rule says\n// \"Never re-export (except in the root `index.ts` of each package).\"\n// The previous `export { WILDO_SAAS_LOCK_RELATIVE_PATH }` here AND in\n// `lockfile-repair.service.ts` surfaced the symbol twice from\n// `wildo-module-client`'s root. Consumers that need it can import\n// directly from `@wildo-ai/platform-config-lib`, OR rely on the root\n// `index.ts` to re-export it once.\n\nconst SECTION_KEY = 'modules' as const;\n\nfunction emptySection(): RegistryModulesLockSection {\n return RegistryModulesLockSectionSchema.parse({});\n}\n\nexport const RegistryLockfileService = {\n /**\n * Read the current `sections.modules` payload (or an empty section\n * when missing). Unsynchronized — callers needing read-after-write\n * coherence should read INSIDE `mutate`.\n */\n async read(saasRoot: string): Promise<RegistryModulesLockSection> {\n const envelope = await readWildoSaasLock({ saasRoot });\n const raw = envelope.sections.modules;\n if (raw === undefined) return emptySection();\n return RegistryModulesLockSectionSchema.parse(raw);\n },\n\n /**\n * Run `mutator` against the current `sections.modules` payload\n * under an OS-level lock + atomic-write commit. The mutator can\n * either mutate-in-place or return a new value; either way the\n * result is re-validated against `RegistryModulesLockSectionSchema`\n * before commit.\n */\n async mutate(\n saasRoot: string,\n mutator: (\n current: RegistryModulesLockSection,\n ) =>\n | RegistryModulesLockSection\n | void\n | Promise<RegistryModulesLockSection | void>,\n ): Promise<RegistryModulesLockSection> {\n return withLockfileSectionMutate(\n {\n saasRoot,\n sectionKey: SECTION_KEY,\n sectionSchema: RegistryModulesLockSectionSchema,\n initialSection: emptySection,\n },\n mutator,\n );\n },\n\n /** Convenience helper used by Phase 5+ install flow. */\n async upsertEntry(saasRoot: string, entry: RegistryModulesLockEntry): Promise<void> {\n await this.mutate(saasRoot, (current) => ({\n ...current,\n installed: { ...current.installed, [entry.moduleId]: entry },\n }));\n },\n\n /**\n * Find every other lockfile entry whose `files[]` claims any of the\n * given paths. Used by `update`/`remove` to skip deletes that would\n * silently pull files out from under modules that own them too\n * (cross-module file overlap).\n *\n * Returns a `Map<relPath, owners[]>` where `owners` excludes\n * `excludeModuleId`. Empty array → uniquely owned by the excluded\n * module, safe to delete.\n */\n async findFileOwners(args: {\n saasRoot: string;\n paths: readonly string[];\n excludeModuleId: string;\n }): Promise<Map<string, string[]>> {\n const section = await this.read(args.saasRoot);\n const claimedBy = new Map<string, string[]>();\n for (const p of args.paths) claimedBy.set(p, []);\n for (const otherModuleId of Object.keys(section.installed)) {\n if (otherModuleId === args.excludeModuleId) continue;\n const otherEntry = section.installed[otherModuleId];\n if (!otherEntry) continue;\n for (const otherFile of otherEntry.files) {\n if (claimedBy.has(otherFile)) {\n claimedBy.get(otherFile)!.push(otherModuleId);\n }\n }\n }\n return claimedBy;\n },\n} as const;\n"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RegistryServerClient` — HTTP client for the central registry
|
|
3
|
+
* server (`platform-module-registry`, KD-7).
|
|
4
|
+
*
|
|
5
|
+
* Phase 2 fills out the read + download surface (`resolve`,
|
|
6
|
+
* `info`, `search`, `download`). The wire shapes mirror the
|
|
7
|
+
* gateway endpoints documented in the analysis plan's "Registry
|
|
8
|
+
* HTTP API" section. The actual server (Phase 3) ships these
|
|
9
|
+
* routes; until it does, the client throws typed errors when it
|
|
10
|
+
* can't reach the server.
|
|
11
|
+
*
|
|
12
|
+
* Auth (KD-11): if `authToken` is supplied, every request adds
|
|
13
|
+
* `Authorization: Bearer <token>`. Public read paths work without
|
|
14
|
+
* a token; restricted paths return 401/403, surfaced as
|
|
15
|
+
* `RegistryAuthRequiredError` / `RegistryAuthForbiddenError`.
|
|
16
|
+
*/
|
|
17
|
+
import { type ModuleManifest, type ModuleVersionRecord } from '@wildo-ai/platform-config-lib';
|
|
18
|
+
export interface RegistryServerClientOptions {
|
|
19
|
+
/** Registry origin (e.g. `https://registry.wildo.ai`). */
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
/** Optional bearer / API-key. CLI credentials are per-machine (KD-11). */
|
|
22
|
+
authToken?: string;
|
|
23
|
+
/** Request timeout in ms. */
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface RegistryResolveResult {
|
|
27
|
+
/** Resolved exact version (matches `Sha256DigestSchema` for digest). */
|
|
28
|
+
version: string;
|
|
29
|
+
artifactDigest: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RegistryInfoResult {
|
|
32
|
+
moduleId: string;
|
|
33
|
+
manifest: ModuleManifest;
|
|
34
|
+
versions: readonly ModuleVersionRecord[];
|
|
35
|
+
/** dist-tag → version map (e.g. `latest → 1.2.3`). */
|
|
36
|
+
tags: Readonly<Record<string, string>>;
|
|
37
|
+
}
|
|
38
|
+
export interface RegistrySearchHit {
|
|
39
|
+
moduleId: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
latestVersion: string;
|
|
42
|
+
}
|
|
43
|
+
export declare const RegistryServerClient: {
|
|
44
|
+
/**
|
|
45
|
+
* Liveness probe against `/health`. Returns boolean — used by
|
|
46
|
+
* `wildo registry ping` smoke. The registry server now relies on
|
|
47
|
+
* the framework's auto-mounted `HealthController` at the engine
|
|
48
|
+
* root (`GET /health` always 200; `GET /health/ready` enforces
|
|
49
|
+
* dependency readiness), so the path has NO `/v1` prefix.
|
|
50
|
+
*/
|
|
51
|
+
readonly ping: (options: RegistryServerClientOptions) => Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve a semver range / tag against a registry domain. The
|
|
54
|
+
* server returns the concrete version + artifact digest so the
|
|
55
|
+
* caller can verify the download against `artifactDigest`.
|
|
56
|
+
*/
|
|
57
|
+
readonly resolve: (options: RegistryServerClientOptions, args: {
|
|
58
|
+
domain: string;
|
|
59
|
+
moduleId: string;
|
|
60
|
+
range: string;
|
|
61
|
+
}) => Promise<RegistryResolveResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Fetch full info for a module: manifest, every published
|
|
64
|
+
* version, dist-tags. Used by `wildo registry info` (Phase 4).
|
|
65
|
+
*/
|
|
66
|
+
readonly info: (options: RegistryServerClientOptions, args: {
|
|
67
|
+
domain: string;
|
|
68
|
+
moduleId: string;
|
|
69
|
+
}) => Promise<RegistryInfoResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Search the registry's metadata index. The shape is
|
|
72
|
+
* intentionally narrow until Phase 4 designs the search UX.
|
|
73
|
+
*/
|
|
74
|
+
readonly search: (options: RegistryServerClientOptions, args: {
|
|
75
|
+
domain: string;
|
|
76
|
+
query: string;
|
|
77
|
+
}) => Promise<readonly RegistrySearchHit[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Stream-download a published artifact's bytes and verify the
|
|
80
|
+
* sha256 integrity matches `expectedDigest`. Throws
|
|
81
|
+
* `RegistryArtifactIntegrityMismatchError` on mismatch — the
|
|
82
|
+
* digest check is non-negotiable per KD-9 / Security model.
|
|
83
|
+
*/
|
|
84
|
+
readonly download: (options: RegistryServerClientOptions, args: {
|
|
85
|
+
domain: string;
|
|
86
|
+
moduleId: string;
|
|
87
|
+
version: string;
|
|
88
|
+
expectedDigest: string;
|
|
89
|
+
}) => Promise<{
|
|
90
|
+
bytes: Uint8Array;
|
|
91
|
+
integrity: string;
|
|
92
|
+
}>;
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=registry-server-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-server-client.d.ts","sourceRoot":"","sources":["../../../../src/services/registry-server-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAYL,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAC;AA4BvC,MAAM,WAAW,2BAA2B;IAC1C,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoCD,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,sDAAsD;IACtD,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,oBAAoB;IAC/B;;;;;;OAMG;6BACiB,2BAA2B,KAAG,OAAO,CAAC,OAAO,CAAC;IAalE;;;;OAIG;gCAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KACxD,OAAO,CAAC,qBAAqB,CAAC;IAgDjC;;;OAGG;6BAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KACzC,OAAO,CAAC,kBAAkB,CAAC;IA8B9B;;;OAGG;+BAEQ,2BAA2B,QAC9B;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KACtC,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC;IA6BxC;;;;;OAKG;iCAEQ,2BAA2B,QAC9B;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,MAAM,CAAC;KACxB,KACA,OAAO,CAAC;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CA6C5C,CAAC"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RegistryServerClient` — HTTP client for the central registry
|
|
3
|
+
* server (`platform-module-registry`, KD-7).
|
|
4
|
+
*
|
|
5
|
+
* Phase 2 fills out the read + download surface (`resolve`,
|
|
6
|
+
* `info`, `search`, `download`). The wire shapes mirror the
|
|
7
|
+
* gateway endpoints documented in the analysis plan's "Registry
|
|
8
|
+
* HTTP API" section. The actual server (Phase 3) ships these
|
|
9
|
+
* routes; until it does, the client throws typed errors when it
|
|
10
|
+
* can't reach the server.
|
|
11
|
+
*
|
|
12
|
+
* Auth (KD-11): if `authToken` is supplied, every request adds
|
|
13
|
+
* `Authorization: Bearer <token>`. Public read paths work without
|
|
14
|
+
* a token; restricted paths return 401/403, surfaced as
|
|
15
|
+
* `RegistryAuthRequiredError` / `RegistryAuthForbiddenError`.
|
|
16
|
+
*/
|
|
17
|
+
import { z } from 'zod';
|
|
18
|
+
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';
|
|
21
|
+
/**
|
|
22
|
+
* Zod schema for the `info()` response payload — mirrors
|
|
23
|
+
* `RegistryInfoResult` and is enforced at the client boundary so a
|
|
24
|
+
* server schema drift fails fast instead of returning malformed data
|
|
25
|
+
* to downstream consumers.
|
|
26
|
+
*/
|
|
27
|
+
const RegistryInfoResponseSchema = z.object({
|
|
28
|
+
moduleId: z.string().min(1),
|
|
29
|
+
manifest: ModuleManifestSchema,
|
|
30
|
+
versions: z.array(ModuleVersionRecordSchema),
|
|
31
|
+
tags: z.record(z.string(), z.string()),
|
|
32
|
+
});
|
|
33
|
+
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
|
+
})),
|
|
39
|
+
});
|
|
40
|
+
const DIAGNOSTIC_LABEL = 'Registry';
|
|
41
|
+
function authHeaders(options) {
|
|
42
|
+
return options.authToken ? { Authorization: `Bearer ${options.authToken}` } : {};
|
|
43
|
+
}
|
|
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;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 6th-pass M3: typed JSON-parse helper. A misconfigured proxy that
|
|
53
|
+
* returns HTML at 2xx (or a truncated stream) would otherwise throw a
|
|
54
|
+
* bare `SyntaxError` from `await res.json()`, bypassing every
|
|
55
|
+
* `isRegistryError(err)` handler. This helper translates the parse
|
|
56
|
+
* failure into a `RegistryMalformedPayloadError` so the typed-error
|
|
57
|
+
* contract is universal across the client surface.
|
|
58
|
+
*/
|
|
59
|
+
async function parseJsonOrThrow(res, registryUrl, action) {
|
|
60
|
+
try {
|
|
61
|
+
return await res.json();
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
throw new RegistryMalformedPayloadError(registryUrl, action, `response is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export const RegistryServerClient = {
|
|
68
|
+
/**
|
|
69
|
+
* Liveness probe against `/health`. Returns boolean — used by
|
|
70
|
+
* `wildo registry ping` smoke. The registry server now relies on
|
|
71
|
+
* the framework's auto-mounted `HealthController` at the engine
|
|
72
|
+
* root (`GET /health` always 200; `GET /health/ready` enforces
|
|
73
|
+
* dependency readiness), so the path has NO `/v1` prefix.
|
|
74
|
+
*/
|
|
75
|
+
async ping(options) {
|
|
76
|
+
try {
|
|
77
|
+
await fetchJsonOk(joinUrl(options.baseUrl, '/health'), {
|
|
78
|
+
method: 'GET',
|
|
79
|
+
timeoutMs: options.timeoutMs,
|
|
80
|
+
diagnosticLabel: DIAGNOSTIC_LABEL,
|
|
81
|
+
});
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a semver range / tag against a registry domain. The
|
|
90
|
+
* server returns the concrete version + artifact digest so the
|
|
91
|
+
* caller can verify the download against `artifactDigest`.
|
|
92
|
+
*/
|
|
93
|
+
async resolve(options, args) {
|
|
94
|
+
const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/resolve?range=${encodeURIComponent(args.range)}`);
|
|
95
|
+
const res = await fetchWithTimeout(url, {
|
|
96
|
+
method: 'GET',
|
|
97
|
+
headers: authHeaders(options),
|
|
98
|
+
timeoutMs: options.timeoutMs,
|
|
99
|
+
diagnosticLabel: DIAGNOSTIC_LABEL,
|
|
100
|
+
});
|
|
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
|
+
}
|
|
110
|
+
// 6th-pass M3: typed JSON-parse (was bare `SyntaxError` for
|
|
111
|
+
// malformed proxy responses).
|
|
112
|
+
const json = (await parseJsonOrThrow(res, options.baseUrl, `resolve ${args.moduleId}`));
|
|
113
|
+
const digest = String(json.artifactDigest ?? '');
|
|
114
|
+
// 6th-pass M4: typed digest validation (was bare `ZodError`).
|
|
115
|
+
const digestParse = Sha256DigestSchema.safeParse(digest);
|
|
116
|
+
if (!digestParse.success) {
|
|
117
|
+
throw new RegistryMalformedPayloadError(options.baseUrl, `resolve ${args.moduleId}`, `artifactDigest is not a valid sha256:<64-hex> string: ${formatZodIssuesAsSingleLine(digestParse.error)}`);
|
|
118
|
+
}
|
|
119
|
+
if (typeof json.version !== 'string' || json.version.length === 0) {
|
|
120
|
+
// 3rd-pass M6: typed `RegistryMalformedPayloadError` instead of
|
|
121
|
+
// bare `Error` so callers' `isRegistryError(err)` handlers fire
|
|
122
|
+
// correctly.
|
|
123
|
+
throw new RegistryMalformedPayloadError(options.baseUrl, `resolve ${args.moduleId}`, 'response missing required `version` string');
|
|
124
|
+
}
|
|
125
|
+
return { version: json.version, artifactDigest: digest };
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* Fetch full info for a module: manifest, every published
|
|
129
|
+
* version, dist-tags. Used by `wildo registry info` (Phase 4).
|
|
130
|
+
*/
|
|
131
|
+
async info(options, args) {
|
|
132
|
+
const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}`);
|
|
133
|
+
const res = await fetchWithTimeout(url, {
|
|
134
|
+
method: 'GET',
|
|
135
|
+
headers: authHeaders(options),
|
|
136
|
+
timeoutMs: options.timeoutMs,
|
|
137
|
+
diagnosticLabel: DIAGNOSTIC_LABEL,
|
|
138
|
+
});
|
|
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
|
+
}
|
|
147
|
+
// 6th-pass M3: typed JSON-parse.
|
|
148
|
+
const json = await parseJsonOrThrow(res, options.baseUrl, `info ${args.moduleId}`);
|
|
149
|
+
const parsed = RegistryInfoResponseSchema.safeParse(json);
|
|
150
|
+
if (!parsed.success) {
|
|
151
|
+
throw new RegistryMalformedPayloadError(options.baseUrl, `info ${args.moduleId}`, formatZodIssuesAsSingleLine(parsed.error));
|
|
152
|
+
}
|
|
153
|
+
return parsed.data;
|
|
154
|
+
},
|
|
155
|
+
/**
|
|
156
|
+
* Search the registry's metadata index. The shape is
|
|
157
|
+
* intentionally narrow until Phase 4 designs the search UX.
|
|
158
|
+
*/
|
|
159
|
+
async search(options, args) {
|
|
160
|
+
const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules?q=${encodeURIComponent(args.query)}`);
|
|
161
|
+
const res = await fetchWithTimeout(url, {
|
|
162
|
+
method: 'GET',
|
|
163
|
+
headers: authHeaders(options),
|
|
164
|
+
timeoutMs: options.timeoutMs,
|
|
165
|
+
diagnosticLabel: DIAGNOSTIC_LABEL,
|
|
166
|
+
});
|
|
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
|
+
}
|
|
173
|
+
// 6th-pass M3: typed JSON-parse.
|
|
174
|
+
const json = await parseJsonOrThrow(res, options.baseUrl, 'search');
|
|
175
|
+
const parsed = RegistrySearchResponseSchema.safeParse(json);
|
|
176
|
+
if (!parsed.success) {
|
|
177
|
+
throw new RegistryMalformedPayloadError(options.baseUrl, 'search', formatZodIssuesAsSingleLine(parsed.error));
|
|
178
|
+
}
|
|
179
|
+
return parsed.data.hits;
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* Stream-download a published artifact's bytes and verify the
|
|
183
|
+
* sha256 integrity matches `expectedDigest`. Throws
|
|
184
|
+
* `RegistryArtifactIntegrityMismatchError` on mismatch — the
|
|
185
|
+
* digest check is non-negotiable per KD-9 / Security model.
|
|
186
|
+
*/
|
|
187
|
+
async download(options, args) {
|
|
188
|
+
// 7th-pass C-M1: caller-supplied digest is a developer-contract
|
|
189
|
+
// violation if malformed, not a registry-runtime condition.
|
|
190
|
+
// Throw `TypeError` (matching `C-L3` precedent for
|
|
191
|
+
// `getModuleStagingPaths` argument-shape violations) so the
|
|
192
|
+
// typed `RegistryError` vocabulary stays reserved for
|
|
193
|
+
// registry-runtime errors. Prior `Sha256DigestSchema.parse(...)`
|
|
194
|
+
// leaked a bare `ZodError` that bypassed
|
|
195
|
+
// `isRegistryError(err)` handlers but ALSO confused
|
|
196
|
+
// typed-handler maintainers about the contract.
|
|
197
|
+
const digestParse = Sha256DigestSchema.safeParse(args.expectedDigest);
|
|
198
|
+
if (!digestParse.success) {
|
|
199
|
+
throw new TypeError(`download() expectedDigest must be a valid sha256:<64-hex> string (got: ${JSON.stringify(args.expectedDigest)}). ${formatZodIssuesAsSingleLine(digestParse.error)}`);
|
|
200
|
+
}
|
|
201
|
+
const url = joinUrl(options.baseUrl, `/v1/registry/${encodeURIComponent(args.domain)}/modules/${encodeURIComponent(args.moduleId)}/${encodeURIComponent(args.version)}/artifact`);
|
|
202
|
+
const res = await fetchWithTimeout(url, {
|
|
203
|
+
method: 'GET',
|
|
204
|
+
headers: authHeaders(options),
|
|
205
|
+
timeoutMs: options.timeoutMs,
|
|
206
|
+
diagnosticLabel: DIAGNOSTIC_LABEL,
|
|
207
|
+
});
|
|
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());
|
|
217
|
+
const { createHash } = await import('node:crypto');
|
|
218
|
+
const observed = `sha256:${createHash('sha256').update(buf).digest('hex')}`;
|
|
219
|
+
if (observed !== args.expectedDigest) {
|
|
220
|
+
throw new RegistryArtifactIntegrityMismatchError(args.moduleId, args.version, args.expectedDigest, observed);
|
|
221
|
+
}
|
|
222
|
+
return { bytes: buf, integrity: observed };
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=registry-server-client.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared structural types consumed across `wildo-module-client`
|
|
3
|
+
* services.
|
|
4
|
+
*
|
|
5
|
+
* The package intentionally avoids importing the CLI's full
|
|
6
|
+
* `CommandContext` (defined in `wildo-cli/src/kernel/types.ts`)
|
|
7
|
+
* to keep the dependency arrow CLI → wildo-module-client (KD-3
|
|
8
|
+
* forbids a reverse edge). Phase 2 services accept a structural
|
|
9
|
+
* subset of whatever the host (CLI / companion) needs to pass
|
|
10
|
+
* down — typically just `saasRoot` + a logger sink.
|
|
11
|
+
*
|
|
12
|
+
* The single shared type today is `RegistryClientLogger`. Adding
|
|
13
|
+
* more shared structural types here is fine; importing
|
|
14
|
+
* `wildo-cli` types is not.
|
|
15
|
+
*/
|
|
16
|
+
export interface RegistryClientLogger {
|
|
17
|
+
info: (msg: string) => void;
|
|
18
|
+
success?: (msg: string) => void;
|
|
19
|
+
warn: (msg: string) => void;
|
|
20
|
+
error: (msg: string) => void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/services/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared structural types consumed across `wildo-module-client`
|
|
3
|
+
* services.
|
|
4
|
+
*
|
|
5
|
+
* The package intentionally avoids importing the CLI's full
|
|
6
|
+
* `CommandContext` (defined in `wildo-cli/src/kernel/types.ts`)
|
|
7
|
+
* to keep the dependency arrow CLI → wildo-module-client (KD-3
|
|
8
|
+
* forbids a reverse edge). Phase 2 services accept a structural
|
|
9
|
+
* subset of whatever the host (CLI / companion) needs to pass
|
|
10
|
+
* down — typically just `saasRoot` + a logger sink.
|
|
11
|
+
*
|
|
12
|
+
* The single shared type today is `RegistryClientLogger`. Adding
|
|
13
|
+
* more shared structural types here is fine; importing
|
|
14
|
+
* `wildo-cli` types is not.
|
|
15
|
+
*/
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG","sourcesContent":["/**\n * Shared structural types consumed across `wildo-module-client`\n * services.\n *\n * The package intentionally avoids importing the CLI's full\n * `CommandContext` (defined in `wildo-cli/src/kernel/types.ts`)\n * to keep the dependency arrow CLI → wildo-module-client (KD-3\n * forbids a reverse edge). Phase 2 services accept a structural\n * subset of whatever the host (CLI / companion) needs to pass\n * down — typically just `saasRoot` + a logger sink.\n *\n * The single shared type today is `RegistryClientLogger`. Adding\n * more shared structural types here is fine; importing\n * `wildo-cli` types is not.\n */\n\nexport interface RegistryClientLogger {\n info: (msg: string) => void;\n success?: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n"]}
|