@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.
Files changed (98) hide show
  1. package/LICENSE +34 -0
  2. package/dist/esm/.builder.pid +9 -0
  3. package/dist/esm/__tests__/lockfile-repair.service.test.d.ts +0 -0
  4. package/dist/esm/__tests__/lockfile-repair.service.test.d.ts.map +0 -0
  5. package/dist/esm/__tests__/lockfile-repair.service.test.js.map +0 -0
  6. package/dist/esm/__tests__/module-adopter.service.test.d.ts +0 -0
  7. package/dist/esm/__tests__/module-adopter.service.test.d.ts.map +0 -0
  8. package/dist/esm/__tests__/module-adopter.service.test.js.map +0 -0
  9. package/dist/esm/__tests__/module-packager.service.test.d.ts +0 -0
  10. package/dist/esm/__tests__/module-packager.service.test.d.ts.map +0 -0
  11. package/dist/esm/__tests__/module-packager.service.test.js.map +0 -0
  12. package/dist/esm/__tests__/provider-materialization-hook.test.d.ts +0 -0
  13. package/dist/esm/__tests__/provider-materialization-hook.test.d.ts.map +0 -0
  14. package/dist/esm/__tests__/provider-materialization-hook.test.js.map +0 -0
  15. package/dist/esm/__tests__/provider-sync-runner.test.d.ts +0 -0
  16. package/dist/esm/__tests__/provider-sync-runner.test.d.ts.map +0 -0
  17. package/dist/esm/__tests__/provider-sync-runner.test.js.map +0 -0
  18. package/dist/esm/__tests__/registry-lockfile.service.test.d.ts +0 -0
  19. package/dist/esm/__tests__/registry-lockfile.service.test.d.ts.map +0 -0
  20. package/dist/esm/__tests__/registry-lockfile.service.test.js.map +0 -0
  21. package/dist/esm/__tests__/registry-server-client.test.d.ts +0 -0
  22. package/dist/esm/__tests__/registry-server-client.test.d.ts.map +0 -0
  23. package/dist/esm/__tests__/registry-server-client.test.js.map +0 -0
  24. package/dist/esm/index.d.ts +18 -0
  25. package/dist/esm/index.d.ts.map +1 -0
  26. package/dist/esm/index.js +18 -0
  27. package/dist/esm/index.js.map +1 -0
  28. package/dist/esm/services/_http-client.d.ts +44 -0
  29. package/dist/esm/services/_http-client.d.ts.map +1 -0
  30. package/dist/esm/services/_http-client.js +75 -0
  31. package/dist/esm/services/_http-client.js.map +1 -0
  32. package/dist/esm/services/_zod-issue-formatting.d.ts +12 -0
  33. package/dist/esm/services/_zod-issue-formatting.d.ts.map +1 -0
  34. package/dist/esm/services/_zod-issue-formatting.js +47 -0
  35. package/dist/esm/services/_zod-issue-formatting.js.map +1 -0
  36. package/dist/esm/services/index.d.ts +10 -0
  37. package/dist/esm/services/index.d.ts.map +1 -0
  38. package/dist/esm/services/index.js +10 -0
  39. package/dist/esm/services/index.js.map +1 -0
  40. package/dist/esm/services/lockfile-repair.service.d.ts +68 -0
  41. package/dist/esm/services/lockfile-repair.service.d.ts.map +1 -0
  42. package/dist/esm/services/lockfile-repair.service.js +160 -0
  43. package/dist/esm/services/lockfile-repair.service.js.map +1 -0
  44. package/dist/esm/services/module-adopter.service.d.ts +59 -0
  45. package/dist/esm/services/module-adopter.service.d.ts.map +1 -0
  46. package/dist/esm/services/module-adopter.service.js +126 -0
  47. package/dist/esm/services/module-adopter.service.js.map +1 -0
  48. package/dist/esm/services/module-packager.service.d.ts +105 -0
  49. package/dist/esm/services/module-packager.service.d.ts.map +1 -0
  50. package/dist/esm/services/module-packager.service.js +212 -0
  51. package/dist/esm/services/module-packager.service.js.map +1 -0
  52. package/dist/esm/services/provider-materialization-hook.d.ts +32 -0
  53. package/dist/esm/services/provider-materialization-hook.d.ts.map +1 -0
  54. package/dist/esm/services/provider-materialization-hook.js +33 -0
  55. package/dist/esm/services/provider-materialization-hook.js.map +1 -0
  56. package/dist/esm/services/provider-sync-runner.d.ts +87 -0
  57. package/dist/esm/services/provider-sync-runner.d.ts.map +1 -0
  58. package/dist/esm/services/provider-sync-runner.js +207 -0
  59. package/dist/esm/services/provider-sync-runner.js.map +1 -0
  60. package/dist/esm/services/registry-companion-client.d.ts +39 -0
  61. package/dist/esm/services/registry-companion-client.d.ts.map +1 -0
  62. package/dist/esm/services/registry-companion-client.js +109 -0
  63. package/dist/esm/services/registry-companion-client.js.map +1 -0
  64. package/dist/esm/services/registry-lockfile.service.d.ts +56 -0
  65. package/dist/esm/services/registry-lockfile.service.d.ts.map +1 -0
  66. package/dist/esm/services/registry-lockfile.service.js +99 -0
  67. package/dist/esm/services/registry-lockfile.service.js.map +1 -0
  68. package/dist/esm/services/registry-server-client.d.ts +94 -0
  69. package/dist/esm/services/registry-server-client.d.ts.map +1 -0
  70. package/dist/esm/services/registry-server-client.js +225 -0
  71. package/dist/esm/services/registry-server-client.js.map +1 -0
  72. package/dist/esm/services/types.d.ts +22 -0
  73. package/dist/esm/services/types.d.ts.map +1 -0
  74. package/dist/esm/services/types.js +16 -0
  75. package/dist/esm/services/types.js.map +1 -0
  76. package/dist/tsconfig.build.tsbuildinfo +1 -0
  77. package/dist/tsconfig.tsbuildinfo +1 -0
  78. package/package.json +51 -0
  79. package/src/__tests__/lockfile-repair.service.test.ts +106 -0
  80. package/src/__tests__/module-adopter.service.test.ts +96 -0
  81. package/src/__tests__/module-packager.service.test.ts +258 -0
  82. package/src/__tests__/provider-materialization-hook.test.ts +47 -0
  83. package/src/__tests__/provider-sync-runner.test.ts +113 -0
  84. package/src/__tests__/registry-lockfile.service.test.ts +134 -0
  85. package/src/__tests__/registry-server-client.test.ts +312 -0
  86. package/src/index.ts +18 -0
  87. package/src/services/_http-client.ts +91 -0
  88. package/src/services/_zod-issue-formatting.ts +50 -0
  89. package/src/services/index.ts +9 -0
  90. package/src/services/lockfile-repair.service.ts +215 -0
  91. package/src/services/module-adopter.service.ts +155 -0
  92. package/src/services/module-packager.service.ts +251 -0
  93. package/src/services/provider-materialization-hook.ts +54 -0
  94. package/src/services/provider-sync-runner.ts +298 -0
  95. package/src/services/registry-companion-client.ts +163 -0
  96. package/src/services/registry-lockfile.service.ts +122 -0
  97. package/src/services/registry-server-client.ts +334 -0
  98. package/src/services/types.ts +22 -0
package/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ Copyright (c) 2026 P2C Consulting, s.à r.l.-S
2
+ RCS Luxembourg B307591
3
+ 6C, Porte de France, L-4360 Esch-sur-Alzette, Luxembourg
4
+
5
+ All rights reserved.
6
+
7
+ This software and its accompanying materials (the "Software") are the
8
+ confidential and proprietary property of P2C Consulting, s.à r.l.-S (the
9
+ "Licensor"). The Software is made available for viewing only. No licence,
10
+ right, or permission of any kind is granted by its publication or by its
11
+ availability on any package registry or source repository.
12
+
13
+ Without the Licensor's prior written permission, you may not use, copy,
14
+ reproduce, modify, adapt, translate, merge, publish, distribute, sublicense,
15
+ sell, host, or create derivative works of the Software, in whole or in part,
16
+ nor use it to provide a service to any third party.
17
+
18
+ Publication of the Software in any public registry is for distribution to
19
+ authorised recipients and for technical evaluation of its packaging only. It
20
+ does not place the Software in the public domain, does not constitute an open
21
+ source release, and does not waive any right of the Licensor.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
+
30
+ Third-party components distributed with the Software remain governed by their
31
+ own licences.
32
+
33
+ Contact: P2C Consulting, s.à r.l.-S, 6C, Porte de France, L-4360
34
+ Esch-sur-Alzette, Luxembourg.
@@ -0,0 +1,9 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "writerPid": 95348,
4
+ "orchestratorPid": 94847,
5
+ "orchestratorSessionId": null,
6
+ "orchestratorStartedAt": "Mon Aug 31 09:18:19 2026",
7
+ "distDirectory": "/Users/paulcayon/Projects/Wildo.ai/dev/wildo-ai/platform/factory/wildo-module-client/dist/esm",
8
+ "acquiredAt": "2026-08-31T07:18:20.844Z"
9
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@wildo-ai/wildo-module-client` — runtime services for the Wildo
3
+ * module-registry workstream (KD-3).
4
+ *
5
+ * Phase 0.5 surface:
6
+ * - `RegistryServerClient` — HTTP client for the registry server.
7
+ * - `RegistryCompanionClient` — HTTP client for the dev companion.
8
+ * - `RegistryLockfileService` — section-scoped `sections.modules`
9
+ * reader/writer.
10
+ * - `runProviderMaterializationHook` — post-mutation no-op hook
11
+ * that delegates to `wildo config sync --domain config`.
12
+ *
13
+ * Phase 1+ adds `ModulePackagerService`, `ModuleAdopterService`,
14
+ * `LockfileRepairService`, and the real (locked + atomic) lockfile
15
+ * mutation surface.
16
+ */
17
+ export * from './services';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `@wildo-ai/wildo-module-client` — runtime services for the Wildo
3
+ * module-registry workstream (KD-3).
4
+ *
5
+ * Phase 0.5 surface:
6
+ * - `RegistryServerClient` — HTTP client for the registry server.
7
+ * - `RegistryCompanionClient` — HTTP client for the dev companion.
8
+ * - `RegistryLockfileService` — section-scoped `sections.modules`
9
+ * reader/writer.
10
+ * - `runProviderMaterializationHook` — post-mutation no-op hook
11
+ * that delegates to `wildo config sync --domain config`.
12
+ *
13
+ * Phase 1+ adds `ModulePackagerService`, `ModuleAdopterService`,
14
+ * `LockfileRepairService`, and the real (locked + atomic) lockfile
15
+ * mutation surface.
16
+ */
17
+ export * from './services/index.js';
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,YAAY,CAAC","sourcesContent":["/**\n * `@wildo-ai/wildo-module-client` — runtime services for the Wildo\n * module-registry workstream (KD-3).\n *\n * Phase 0.5 surface:\n * - `RegistryServerClient` — HTTP client for the registry server.\n * - `RegistryCompanionClient` — HTTP client for the dev companion.\n * - `RegistryLockfileService` — section-scoped `sections.modules`\n * reader/writer.\n * - `runProviderMaterializationHook` — post-mutation no-op hook\n * that delegates to `wildo config sync --domain config`.\n *\n * Phase 1+ adds `ModulePackagerService`, `ModuleAdopterService`,\n * `LockfileRepairService`, and the real (locked + atomic) lockfile\n * mutation surface.\n */\n\nexport * from './services';\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Internal HTTP client utilities shared by `RegistryServerClient`
3
+ * and `RegistryCompanionClient`.
4
+ *
5
+ * Both clients want the same primitives:
6
+ * - `joinUrl(base, path)` — single-source-of-truth path joiner
7
+ * that tolerates leading/trailing slashes;
8
+ * - `fetchWithTimeout(url, init, timeoutMs)` — abort-on-timeout
9
+ * wrapper that converts `AbortError`/`TimeoutError` into a
10
+ * uniform diagnostic message;
11
+ * - `fetchJsonOk(...)` — convenience for liveness probes that
12
+ * want a boolean answer.
13
+ *
14
+ * The leading underscore signals "package-internal" — these are
15
+ * intentionally NOT re-exported from `services/index.ts`. Phase 2
16
+ * services that need fancier HTTP (multipart upload, streaming
17
+ * download) extend this file rather than copy the primitives.
18
+ */
19
+ export declare function joinUrl(base: string, path: string): string;
20
+ export interface FetchWithTimeoutInit extends RequestInit {
21
+ timeoutMs?: number;
22
+ /** Used in the diagnostic timeout message (e.g. "Registry", "Companion registry"). */
23
+ diagnosticLabel?: string;
24
+ }
25
+ export declare function fetchWithTimeout(url: string, init?: FetchWithTimeoutInit): Promise<Response>;
26
+ /**
27
+ * Throws on non-2xx; returns the Response otherwise.
28
+ *
29
+ * C-L2 (4th-pass review) waiver: this helper deliberately throws
30
+ * bare `Error` and is NOT part of the typed `RegistryError`
31
+ * vocabulary. It exists for liveness probes (`ping()`) that wrap
32
+ * the call in their own try/catch and convert any thrown exception
33
+ * to `false`. Future callers MUST do the same — if a caller wants
34
+ * to surface a typed error to the user, they should not use
35
+ * `fetchJsonOk` and should call `fetchWithTimeout` directly +
36
+ * inspect `res.ok` themselves to throw the right
37
+ * `RegistryUnexpectedStatusError` / `RegistryAuthRequiredError` /
38
+ * etc. variant.
39
+ *
40
+ * @internal — internal lib helper, not re-exported from the
41
+ * package root.
42
+ */
43
+ export declare function fetchJsonOk(url: string, init?: FetchWithTimeoutInit): Promise<Response>;
44
+ //# sourceMappingURL=_http-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_http-client.d.ts","sourceRoot":"","sources":["../../../../src/services/_http-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sFAAsF;IACtF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAyBnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAQnB"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Internal HTTP client utilities shared by `RegistryServerClient`
3
+ * and `RegistryCompanionClient`.
4
+ *
5
+ * Both clients want the same primitives:
6
+ * - `joinUrl(base, path)` — single-source-of-truth path joiner
7
+ * that tolerates leading/trailing slashes;
8
+ * - `fetchWithTimeout(url, init, timeoutMs)` — abort-on-timeout
9
+ * wrapper that converts `AbortError`/`TimeoutError` into a
10
+ * uniform diagnostic message;
11
+ * - `fetchJsonOk(...)` — convenience for liveness probes that
12
+ * want a boolean answer.
13
+ *
14
+ * The leading underscore signals "package-internal" — these are
15
+ * intentionally NOT re-exported from `services/index.ts`. Phase 2
16
+ * services that need fancier HTTP (multipart upload, streaming
17
+ * download) extend this file rather than copy the primitives.
18
+ */
19
+ const DEFAULT_TIMEOUT_MS = 10_000;
20
+ export function joinUrl(base, path) {
21
+ const normalized = path.startsWith('/') ? path : `/${path}`;
22
+ return `${base.replace(/\/$/, '')}${normalized}`;
23
+ }
24
+ export async function fetchWithTimeout(url, init = {}) {
25
+ const timeoutMs = init.timeoutMs ?? DEFAULT_TIMEOUT_MS;
26
+ const label = init.diagnosticLabel ?? 'HTTP';
27
+ const signal = AbortSignal.timeout(timeoutMs);
28
+ // Strip the wrapper-only fields before forwarding to `fetch`. The
29
+ // `_` names are conventional unused-binding markers.
30
+ //
31
+ // 6th-pass M1 — tsconfig premise (corrected): the wider repo's
32
+ // `engine/tsconfig.common.json` currently sets both `noUnusedLocals`
33
+ // and `noUnusedParameters` to `false`, so any unused binding (with
34
+ // or without `_` prefix) compiles clean today. If either flag is
35
+ // ever flipped to `true`, TypeScript's underscore-prefix convention
36
+ // applies to parameters reliably and to destructured `let`/`const`
37
+ // bindings depending on TS version + setting — so the `_` prefix
38
+ // here is a defensive signal of "intentional discard" should that
39
+ // happen. Today it's purely cosmetic.
40
+ const { timeoutMs: _timeoutMs, diagnosticLabel: _diagnosticLabel, ...rest } = init;
41
+ try {
42
+ return await fetch(url, { ...rest, signal });
43
+ }
44
+ catch (err) {
45
+ if (err instanceof Error && (err.name === 'AbortError' || err.name === 'TimeoutError')) {
46
+ throw new Error(`${label} request timed out after ${timeoutMs}ms (${url})`);
47
+ }
48
+ throw err;
49
+ }
50
+ }
51
+ /**
52
+ * Throws on non-2xx; returns the Response otherwise.
53
+ *
54
+ * C-L2 (4th-pass review) waiver: this helper deliberately throws
55
+ * bare `Error` and is NOT part of the typed `RegistryError`
56
+ * vocabulary. It exists for liveness probes (`ping()`) that wrap
57
+ * the call in their own try/catch and convert any thrown exception
58
+ * to `false`. Future callers MUST do the same — if a caller wants
59
+ * to surface a typed error to the user, they should not use
60
+ * `fetchJsonOk` and should call `fetchWithTimeout` directly +
61
+ * inspect `res.ok` themselves to throw the right
62
+ * `RegistryUnexpectedStatusError` / `RegistryAuthRequiredError` /
63
+ * etc. variant.
64
+ *
65
+ * @internal — internal lib helper, not re-exported from the
66
+ * package root.
67
+ */
68
+ export async function fetchJsonOk(url, init = {}) {
69
+ const res = await fetchWithTimeout(url, init);
70
+ if (!res.ok) {
71
+ throw new Error(`${init.diagnosticLabel ?? 'HTTP'} request rejected (${res.status} ${res.statusText}) at ${url}`);
72
+ }
73
+ return res;
74
+ }
75
+ //# sourceMappingURL=_http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_http-client.js","sourceRoot":"","sources":["../../../../src/services/_http-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAY;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAC5D,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC;AACnD,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,OAA6B,EAAE;IAE/B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC;IAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9C,kEAAkE;IAClE,qDAAqD;IACrD,EAAE;IACF,+DAA+D;IAC/D,qEAAqE;IACrE,mEAAmE;IACnE,iEAAiE;IACjE,oEAAoE;IACpE,mEAAmE;IACnE,iEAAiE;IACjE,kEAAkE;IAClE,sCAAsC;IACtC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACnF,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,YAAY,IAAI,GAAG,CAAC,IAAI,KAAK,cAAc,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,4BAA4B,SAAS,OAAO,GAAG,GAAG,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAW,EACX,OAA6B,EAAE;IAE/B,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC9C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,eAAe,IAAI,MAAM,sBAAsB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,QAAQ,GAAG,EAAE,CACjG,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * Internal HTTP client utilities shared by `RegistryServerClient`\n * and `RegistryCompanionClient`.\n *\n * Both clients want the same primitives:\n * - `joinUrl(base, path)` — single-source-of-truth path joiner\n * that tolerates leading/trailing slashes;\n * - `fetchWithTimeout(url, init, timeoutMs)` — abort-on-timeout\n * wrapper that converts `AbortError`/`TimeoutError` into a\n * uniform diagnostic message;\n * - `fetchJsonOk(...)` — convenience for liveness probes that\n * want a boolean answer.\n *\n * The leading underscore signals \"package-internal\" — these are\n * intentionally NOT re-exported from `services/index.ts`. Phase 2\n * services that need fancier HTTP (multipart upload, streaming\n * download) extend this file rather than copy the primitives.\n */\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport function joinUrl(base: string, path: string): string {\n const normalized = path.startsWith('/') ? path : `/${path}`;\n return `${base.replace(/\\/$/, '')}${normalized}`;\n}\n\nexport interface FetchWithTimeoutInit extends RequestInit {\n timeoutMs?: number;\n /** Used in the diagnostic timeout message (e.g. \"Registry\", \"Companion registry\"). */\n diagnosticLabel?: string;\n}\n\nexport async function fetchWithTimeout(\n url: string,\n init: FetchWithTimeoutInit = {},\n): Promise<Response> {\n const timeoutMs = init.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const label = init.diagnosticLabel ?? 'HTTP';\n const signal = AbortSignal.timeout(timeoutMs);\n // Strip the wrapper-only fields before forwarding to `fetch`. The\n // `_` names are conventional unused-binding markers.\n //\n // 6th-pass M1 — tsconfig premise (corrected): the wider repo's\n // `engine/tsconfig.common.json` currently sets both `noUnusedLocals`\n // and `noUnusedParameters` to `false`, so any unused binding (with\n // or without `_` prefix) compiles clean today. If either flag is\n // ever flipped to `true`, TypeScript's underscore-prefix convention\n // applies to parameters reliably and to destructured `let`/`const`\n // bindings depending on TS version + setting — so the `_` prefix\n // here is a defensive signal of \"intentional discard\" should that\n // happen. Today it's purely cosmetic.\n const { timeoutMs: _timeoutMs, diagnosticLabel: _diagnosticLabel, ...rest } = init;\n try {\n return await fetch(url, { ...rest, signal });\n } catch (err) {\n if (err instanceof Error && (err.name === 'AbortError' || err.name === 'TimeoutError')) {\n throw new Error(`${label} request timed out after ${timeoutMs}ms (${url})`);\n }\n throw err;\n }\n}\n\n/**\n * Throws on non-2xx; returns the Response otherwise.\n *\n * C-L2 (4th-pass review) waiver: this helper deliberately throws\n * bare `Error` and is NOT part of the typed `RegistryError`\n * vocabulary. It exists for liveness probes (`ping()`) that wrap\n * the call in their own try/catch and convert any thrown exception\n * to `false`. Future callers MUST do the same — if a caller wants\n * to surface a typed error to the user, they should not use\n * `fetchJsonOk` and should call `fetchWithTimeout` directly +\n * inspect `res.ok` themselves to throw the right\n * `RegistryUnexpectedStatusError` / `RegistryAuthRequiredError` /\n * etc. variant.\n *\n * @internal — internal lib helper, not re-exported from the\n * package root.\n */\nexport async function fetchJsonOk(\n url: string,\n init: FetchWithTimeoutInit = {},\n): Promise<Response> {\n const res = await fetchWithTimeout(url, init);\n if (!res.ok) {\n throw new Error(\n `${init.diagnosticLabel ?? 'HTTP'} request rejected (${res.status} ${res.statusText}) at ${url}`,\n );\n }\n return res;\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { ZodError } from 'zod';
2
+ /**
3
+ * Render a `ZodError` as one operator-readable line —
4
+ * `path: message; path: message; …` — bounded at {@link ISSUE_RENDER_LIMIT}
5
+ * with an explicit `(+N more)` so truncation is never silent.
6
+ *
7
+ * Used as the `detail` on `RegistryMalformedPayloadError` and on lockfile
8
+ * repair findings. The raw `error.message` in Zod 4 is the JSON-stringified
9
+ * issues array — verbose, multi-line, and unreadable in CLI / SIEM output.
10
+ */
11
+ export declare function formatZodIssuesAsSingleLine(error: ZodError): string;
12
+ //# sourceMappingURL=_zod-issue-formatting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_zod-issue-formatting.d.ts","sourceRoot":"","sources":["../../../../src/services/_zod-issue-formatting.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAiCpC;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAOnE"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Package-internal mirror of `summarizeZodIssues` from `@wildo-ai/saas-models`,
3
+ * which is the canonical authority every other Wildo surface delegates to.
4
+ *
5
+ * zod-issue-formatter-mirror: engine/saas-models/src/errors/zod-issue-formatting.shared.utils.ts
6
+ *
7
+ * Why a mirror rather than an import: this package declares exactly ONE
8
+ * workspace dependency (`@wildo-ai/platform-config-lib`) plus a `zod` peer.
9
+ * Importing `saas-models` would pull its decorator-bound schema tree into a
10
+ * lean registry client to save a handful of lines — a bad trade. The boundary
11
+ * is the reason for the duplication; keep the RENDERING identical, and mirror
12
+ * any change to the canonical helper's path / root-label / overflow semantics
13
+ * here.
14
+ *
15
+ * The marker above is not decoration — `pnpm check:zod-issue-formatter-mirror`
16
+ * discovers this file by it and fails if the constants below drift from the
17
+ * canonical source it names. If the dependency constraint that justifies this
18
+ * mirror ever stops holding, delete the file and import the canonical formatter
19
+ * rather than keeping a mirror nobody needs.
20
+ *
21
+ * Within this package this is the single source: `registry-server-client.ts`
22
+ * and `lockfile-repair.service.ts` both call it instead of re-inlining the
23
+ * `.slice(0, 5).map(...).join('; ')` chain.
24
+ */
25
+ /** Label for an issue whose `path` is empty — the value itself failed. */
26
+ const ROOT_ISSUE_PATH_LABEL = '<root>';
27
+ /** Issue cap; a malformed payload can fail on hundreds of paths. */
28
+ const ISSUE_RENDER_LIMIT = 5;
29
+ /**
30
+ * Render a `ZodError` as one operator-readable line —
31
+ * `path: message; path: message; …` — bounded at {@link ISSUE_RENDER_LIMIT}
32
+ * with an explicit `(+N more)` so truncation is never silent.
33
+ *
34
+ * Used as the `detail` on `RegistryMalformedPayloadError` and on lockfile
35
+ * repair findings. The raw `error.message` in Zod 4 is the JSON-stringified
36
+ * issues array — verbose, multi-line, and unreadable in CLI / SIEM output.
37
+ */
38
+ export function formatZodIssuesAsSingleLine(error) {
39
+ const rendered = error.issues
40
+ .slice(0, ISSUE_RENDER_LIMIT)
41
+ .map((issue) => `${issue.path.join('.') || ROOT_ISSUE_PATH_LABEL}: ${issue.message}`);
42
+ const omitted = error.issues.length - ISSUE_RENDER_LIMIT;
43
+ if (omitted > 0)
44
+ rendered.push(`(+${omitted} more)`);
45
+ return rendered.join('; ');
46
+ }
47
+ //# sourceMappingURL=_zod-issue-formatting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_zod-issue-formatting.js","sourceRoot":"","sources":["../../../../src/services/_zod-issue-formatting.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,0EAA0E;AAC1E,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAEvC,oEAAoE;AACpE,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B;;;;;;;;GAQG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAAe;IACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM;SAC1B,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC;SAC5B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,qBAAqB,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACxF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,kBAAkB,CAAC;IACzD,IAAI,OAAO,GAAG,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC","sourcesContent":["import type { ZodError } from 'zod';\n\n/**\n * Package-internal mirror of `summarizeZodIssues` from `@wildo-ai/saas-models`,\n * which is the canonical authority every other Wildo surface delegates to.\n *\n * zod-issue-formatter-mirror: engine/saas-models/src/errors/zod-issue-formatting.shared.utils.ts\n *\n * Why a mirror rather than an import: this package declares exactly ONE\n * workspace dependency (`@wildo-ai/platform-config-lib`) plus a `zod` peer.\n * Importing `saas-models` would pull its decorator-bound schema tree into a\n * lean registry client to save a handful of lines — a bad trade. The boundary\n * is the reason for the duplication; keep the RENDERING identical, and mirror\n * any change to the canonical helper's path / root-label / overflow semantics\n * here.\n *\n * The marker above is not decoration — `pnpm check:zod-issue-formatter-mirror`\n * discovers this file by it and fails if the constants below drift from the\n * canonical source it names. If the dependency constraint that justifies this\n * mirror ever stops holding, delete the file and import the canonical formatter\n * rather than keeping a mirror nobody needs.\n *\n * Within this package this is the single source: `registry-server-client.ts`\n * and `lockfile-repair.service.ts` both call it instead of re-inlining the\n * `.slice(0, 5).map(...).join('; ')` chain.\n */\n\n/** Label for an issue whose `path` is empty — the value itself failed. */\nconst ROOT_ISSUE_PATH_LABEL = '<root>';\n\n/** Issue cap; a malformed payload can fail on hundreds of paths. */\nconst ISSUE_RENDER_LIMIT = 5;\n\n/**\n * Render a `ZodError` as one operator-readable line —\n * `path: message; path: message; …` — bounded at {@link ISSUE_RENDER_LIMIT}\n * with an explicit `(+N more)` so truncation is never silent.\n *\n * Used as the `detail` on `RegistryMalformedPayloadError` and on lockfile\n * repair findings. The raw `error.message` in Zod 4 is the JSON-stringified\n * issues array — verbose, multi-line, and unreadable in CLI / SIEM output.\n */\nexport function formatZodIssuesAsSingleLine(error: ZodError): string {\n const rendered = error.issues\n .slice(0, ISSUE_RENDER_LIMIT)\n .map((issue) => `${issue.path.join('.') || ROOT_ISSUE_PATH_LABEL}: ${issue.message}`);\n const omitted = error.issues.length - ISSUE_RENDER_LIMIT;\n if (omitted > 0) rendered.push(`(+${omitted} more)`);\n return rendered.join('; ');\n}\n"]}
@@ -0,0 +1,10 @@
1
+ export * from './types';
2
+ export * from './registry-server-client';
3
+ export * from './registry-companion-client';
4
+ export * from './registry-lockfile.service';
5
+ export * from './provider-sync-runner';
6
+ export * from './provider-materialization-hook';
7
+ export * from './module-packager.service';
8
+ export * from './module-adopter.service';
9
+ export * from './lockfile-repair.service';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,10 @@
1
+ export * from './types.js';
2
+ export * from './registry-server-client.js';
3
+ export * from './registry-companion-client.js';
4
+ export * from './registry-lockfile.service.js';
5
+ export * from './provider-sync-runner.js';
6
+ export * from './provider-materialization-hook.js';
7
+ export * from './module-packager.service.js';
8
+ export * from './module-adopter.service.js';
9
+ export * from './lockfile-repair.service.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC","sourcesContent":["export * from './types';\nexport * from './registry-server-client';\nexport * from './registry-companion-client';\nexport * from './registry-lockfile.service';\nexport * from './provider-sync-runner';\nexport * from './provider-materialization-hook';\nexport * from './module-packager.service';\nexport * from './module-adopter.service';\nexport * from './lockfile-repair.service';\n"]}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `LockfileRepairService` — Phase 2 read-only lockfile diagnostics.
3
+ *
4
+ * Backs `wildo registry repair-lockfile` (CLI command lands in a
5
+ * later step). The service does NOT mutate the lockfile by
6
+ * default — its job is to surface a structured diagnostic the
7
+ * operator can act on:
8
+ * - validate the canonical envelope shape;
9
+ * - validate `sections.modules` against
10
+ * `RegistryModulesLockSectionSchema`;
11
+ * - report which `installed[*].files` are missing on disk
12
+ * (drift between the lockfile and the source tree);
13
+ * - report which `installed[*].artifactDigest` strings are not
14
+ * `Sha256DigestSchema`-shaped (residue from pre-tightening
15
+ * publishes).
16
+ *
17
+ * Mutating repair is a Phase 7 deliverable — surfacing the gaps is
18
+ * what unblocks the operator now.
19
+ *
20
+ * `i18n` validation is intentionally NOT performed here (KD-15 +
21
+ * Review-pass-2 / MEDIUM-5): repair is the wrong place to
22
+ * second-guess companion-owned section semantics. The shared
23
+ * lockfile library STILL validates i18n on the companion-owned
24
+ * read path; this service only owns module diagnostics.
25
+ */
26
+ import { type RegistryModulesLockEntry } from '@wildo-ai/platform-config-lib';
27
+ export type LockfileRepairFinding = {
28
+ severity: 'error';
29
+ kind: 'envelope-malformed';
30
+ message: string;
31
+ detail?: string;
32
+ } | {
33
+ severity: 'error';
34
+ kind: 'modules-section-malformed';
35
+ message: string;
36
+ detail?: string;
37
+ } | {
38
+ severity: 'warn';
39
+ kind: 'module-file-missing';
40
+ moduleId: string;
41
+ filePath: string;
42
+ } | {
43
+ severity: 'warn';
44
+ kind: 'module-digest-not-sha256';
45
+ moduleId: string;
46
+ observedDigest: string;
47
+ };
48
+ export interface LockfileRepairReport {
49
+ /** Absolute path the report covers. */
50
+ lockfilePath: string;
51
+ /** True when no errors were produced (warnings allowed). */
52
+ ok: boolean;
53
+ findings: readonly LockfileRepairFinding[];
54
+ }
55
+ export declare const LockfileRepairService: {
56
+ /** Compute the lockfile path for a saas root. */
57
+ readonly pathFor: (saasRoot: string) => string;
58
+ /**
59
+ * Read the envelope + run every diagnostic. Read-only.
60
+ */
61
+ readonly inspect: (saasRoot: string) => Promise<LockfileRepairReport>;
62
+ /**
63
+ * Per-entry diagnostic helper. Exposed so Phase 7 mutating-
64
+ * repair flows can reuse the same predicates.
65
+ */
66
+ readonly collectEntryFindings: (saasRoot: string, moduleId: string, entry: RegistryModulesLockEntry, findings: LockfileRepairFinding[]) => Promise<void>;
67
+ };
68
+ //# sourceMappingURL=lockfile-repair.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile-repair.service.d.ts","sourceRoot":"","sources":["../../../../src/services/lockfile-repair.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,EAIL,KAAK,wBAAwB,EAE9B,MAAM,+BAA+B,CAAC;AAcvC,MAAM,MAAM,qBAAqB,GAC7B;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,0BAA0B,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEN,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC5C;AAWD,eAAO,MAAM,qBAAqB;IAChC,iDAAiD;iCAC/B,MAAM,KAAG,MAAM;IAIjC;;OAEG;iCACqB,MAAM,KAAG,OAAO,CAAC,oBAAoB,CAAC;IA8E9D;;;OAGG;8CAES,MAAM,YACN,MAAM,SACT,wBAAwB,YACrB,qBAAqB,EAAE,KAChC,OAAO,CAAC,IAAI,CAAC;CAqBR,CAAC"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * `LockfileRepairService` — Phase 2 read-only lockfile diagnostics.
3
+ *
4
+ * Backs `wildo registry repair-lockfile` (CLI command lands in a
5
+ * later step). The service does NOT mutate the lockfile by
6
+ * default — its job is to surface a structured diagnostic the
7
+ * operator can act on:
8
+ * - validate the canonical envelope shape;
9
+ * - validate `sections.modules` against
10
+ * `RegistryModulesLockSectionSchema`;
11
+ * - report which `installed[*].files` are missing on disk
12
+ * (drift between the lockfile and the source tree);
13
+ * - report which `installed[*].artifactDigest` strings are not
14
+ * `Sha256DigestSchema`-shaped (residue from pre-tightening
15
+ * publishes).
16
+ *
17
+ * Mutating repair is a Phase 7 deliverable — surfacing the gaps is
18
+ * what unblocks the operator now.
19
+ *
20
+ * `i18n` validation is intentionally NOT performed here (KD-15 +
21
+ * Review-pass-2 / MEDIUM-5): repair is the wrong place to
22
+ * second-guess companion-owned section semantics. The shared
23
+ * lockfile library STILL validates i18n on the companion-owned
24
+ * read path; this service only owns module diagnostics.
25
+ */
26
+ import { promises as fs } from 'node:fs';
27
+ import * as path from 'node:path';
28
+ import { z } from 'zod';
29
+ import { formatZodIssuesAsSingleLine } from './_zod-issue-formatting.js';
30
+ import { RegistryModulesLockSectionSchema, Sha256DigestSchema, getWildoSaasLockPath, } from '@wildo-ai/platform-config-lib';
31
+ /**
32
+ * Permissive envelope schema used by repair only — it accepts any
33
+ * `sections.*` payload without delegating to per-section schemas
34
+ * so the repair flow can isolate "envelope is malformed" from
35
+ * "envelope is fine but modules section is malformed".
36
+ */
37
+ const RelaxedEnvelopeSchema = z.strictObject({
38
+ schemaVersion: z.literal(2),
39
+ generatedAt: z.iso.datetime({ offset: true }),
40
+ sections: z.record(z.string(), z.unknown()),
41
+ });
42
+ async function fileExists(absPath) {
43
+ try {
44
+ await fs.access(absPath);
45
+ return true;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
51
+ export const LockfileRepairService = {
52
+ /** Compute the lockfile path for a saas root. */
53
+ pathFor(saasRoot) {
54
+ return getWildoSaasLockPath(saasRoot);
55
+ },
56
+ /**
57
+ * Read the envelope + run every diagnostic. Read-only.
58
+ */
59
+ async inspect(saasRoot) {
60
+ const lockfilePath = getWildoSaasLockPath(saasRoot);
61
+ const findings = [];
62
+ // Read raw bytes ourselves so we can produce per-section
63
+ // diagnostics: a malformed `modules` section under an
64
+ // otherwise-valid envelope should report `modules-section-malformed`,
65
+ // NOT `envelope-malformed` (the latter would tell the operator
66
+ // to re-author the whole file when only one section is bad).
67
+ let raw;
68
+ try {
69
+ raw = await fs.readFile(lockfilePath, 'utf8');
70
+ }
71
+ catch (err) {
72
+ const errno = err?.code;
73
+ if (errno === 'ENOENT') {
74
+ // Missing lockfile is "ok" from a repair POV.
75
+ return { lockfilePath, ok: true, findings: [] };
76
+ }
77
+ findings.push({
78
+ severity: 'error',
79
+ kind: 'envelope-malformed',
80
+ message: 'Failed to read lockfile bytes.',
81
+ detail: err instanceof Error ? err.message : String(err),
82
+ });
83
+ return { lockfilePath, ok: false, findings };
84
+ }
85
+ let parsedJson;
86
+ try {
87
+ parsedJson = JSON.parse(raw);
88
+ }
89
+ catch (err) {
90
+ findings.push({
91
+ severity: 'error',
92
+ kind: 'envelope-malformed',
93
+ message: 'Lockfile is not valid JSON.',
94
+ detail: err instanceof Error ? err.message : String(err),
95
+ });
96
+ return { lockfilePath, ok: false, findings };
97
+ }
98
+ const envelopeParsed = RelaxedEnvelopeSchema.safeParse(parsedJson);
99
+ if (!envelopeParsed.success) {
100
+ findings.push({
101
+ severity: 'error',
102
+ kind: 'envelope-malformed',
103
+ message: 'Top-level envelope failed to parse.',
104
+ detail: formatZodIssuesAsSingleLine(envelopeParsed.error),
105
+ });
106
+ return { lockfilePath, ok: false, findings };
107
+ }
108
+ const envelope = envelopeParsed.data;
109
+ const modulesPayload = envelope.sections.modules;
110
+ if (modulesPayload === undefined) {
111
+ // No modules section present — nothing to inspect.
112
+ return { lockfilePath, ok: true, findings };
113
+ }
114
+ const parsed = RegistryModulesLockSectionSchema.safeParse(modulesPayload);
115
+ if (!parsed.success) {
116
+ findings.push({
117
+ severity: 'error',
118
+ kind: 'modules-section-malformed',
119
+ message: '`sections.modules` failed schema validation.',
120
+ detail: formatZodIssuesAsSingleLine(parsed.error),
121
+ });
122
+ return { lockfilePath, ok: false, findings };
123
+ }
124
+ const modules = parsed.data;
125
+ for (const [moduleId, entry] of Object.entries(modules.installed)) {
126
+ await this.collectEntryFindings(saasRoot, moduleId, entry, findings);
127
+ }
128
+ const ok = findings.every((f) => f.severity !== 'error');
129
+ return { lockfilePath, ok, findings };
130
+ },
131
+ /**
132
+ * Per-entry diagnostic helper. Exposed so Phase 7 mutating-
133
+ * repair flows can reuse the same predicates.
134
+ */
135
+ async collectEntryFindings(saasRoot, moduleId, entry, findings) {
136
+ if (!Sha256DigestSchema.safeParse(entry.artifactDigest).success) {
137
+ findings.push({
138
+ severity: 'warn',
139
+ kind: 'module-digest-not-sha256',
140
+ moduleId,
141
+ observedDigest: entry.artifactDigest,
142
+ });
143
+ }
144
+ for (const filePath of entry.files) {
145
+ const abs = path.resolve(saasRoot, filePath);
146
+ if (!(await fileExists(abs))) {
147
+ findings.push({
148
+ severity: 'warn',
149
+ kind: 'module-file-missing',
150
+ moduleId,
151
+ filePath,
152
+ });
153
+ }
154
+ }
155
+ },
156
+ };
157
+ // L6 (third-pass review): re-export removed — barrel-discipline rule
158
+ // says "Never re-export except in the root `index.ts`." Consumers
159
+ // import the symbol directly from `@wildo-ai/platform-config-lib`.
160
+ //# sourceMappingURL=lockfile-repair.service.js.map