@telorun/analyzer 0.71.0 → 0.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/analyzer.d.ts.map +1 -1
  2. package/dist/analyzer.js +92 -15
  3. package/dist/builtins.d.ts.map +1 -1
  4. package/dist/builtins.js +71 -97
  5. package/dist/catch-scope.d.ts +72 -0
  6. package/dist/catch-scope.d.ts.map +1 -0
  7. package/dist/catch-scope.js +102 -0
  8. package/dist/cel-scope-query.d.ts.map +1 -1
  9. package/dist/cel-scope-query.js +32 -11
  10. package/dist/cel-scope.d.ts.map +1 -1
  11. package/dist/cel-scope.js +1 -0
  12. package/dist/deprecation.d.ts +21 -0
  13. package/dist/deprecation.d.ts.map +1 -0
  14. package/dist/deprecation.js +26 -0
  15. package/dist/eval-paths.d.ts +36 -5
  16. package/dist/eval-paths.d.ts.map +1 -1
  17. package/dist/eval-paths.js +47 -6
  18. package/dist/extends-resolution.d.ts +12 -0
  19. package/dist/extends-resolution.d.ts.map +1 -1
  20. package/dist/extends-resolution.js +23 -0
  21. package/dist/index.d.ts +4 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -2
  24. package/dist/manifest-visitor.d.ts +17 -1
  25. package/dist/manifest-visitor.d.ts.map +1 -1
  26. package/dist/manifest-visitor.js +5 -1
  27. package/dist/migrations/report.d.ts +1 -1
  28. package/dist/migrations/report.d.ts.map +1 -1
  29. package/dist/migrations/report.js +5 -0
  30. package/dist/nearest-name.d.ts +22 -0
  31. package/dist/nearest-name.d.ts.map +1 -0
  32. package/dist/nearest-name.js +43 -0
  33. package/dist/ref-slot.d.ts +15 -0
  34. package/dist/ref-slot.d.ts.map +1 -1
  35. package/dist/ref-slot.js +7 -0
  36. package/dist/release/destinations.d.ts +59 -0
  37. package/dist/release/destinations.d.ts.map +1 -0
  38. package/dist/release/destinations.js +75 -0
  39. package/dist/release/index.d.ts +6 -2
  40. package/dist/release/index.d.ts.map +1 -1
  41. package/dist/release/index.js +3 -1
  42. package/dist/release/ledger.d.ts +20 -11
  43. package/dist/release/ledger.d.ts.map +1 -1
  44. package/dist/release/ledger.js +43 -14
  45. package/dist/release/release-plan.d.ts +3 -2
  46. package/dist/release/release-plan.d.ts.map +1 -1
  47. package/dist/release/release-plan.js +12 -19
  48. package/dist/release/workspace-config.d.ts +114 -34
  49. package/dist/release/workspace-config.d.ts.map +1 -1
  50. package/dist/release/workspace-config.js +322 -38
  51. package/dist/release/workspace-schema.d.ts +41 -0
  52. package/dist/release/workspace-schema.d.ts.map +1 -0
  53. package/dist/release/workspace-schema.js +77 -0
  54. package/dist/resolve-throws-union.d.ts +29 -1
  55. package/dist/resolve-throws-union.d.ts.map +1 -1
  56. package/dist/resolve-throws-union.js +111 -16
  57. package/dist/schema-compat.d.ts.map +1 -1
  58. package/dist/schema-compat.js +13 -1
  59. package/dist/schema-error-report.d.ts +7 -0
  60. package/dist/schema-error-report.d.ts.map +1 -1
  61. package/dist/schema-error-report.js +57 -8
  62. package/dist/schema-keywords.d.ts.map +1 -1
  63. package/dist/schema-keywords.js +3 -1
  64. package/dist/schema-walk.d.ts +27 -0
  65. package/dist/schema-walk.d.ts.map +1 -1
  66. package/dist/schema-walk.js +44 -0
  67. package/dist/telo-version.d.ts +1 -1
  68. package/dist/telo-version.js +1 -1
  69. package/dist/template-body.d.ts +10 -0
  70. package/dist/template-body.d.ts.map +1 -1
  71. package/dist/template-body.js +25 -0
  72. package/dist/types.d.ts +17 -0
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js +11 -0
  75. package/dist/validate-cel-context.d.ts +23 -8
  76. package/dist/validate-cel-context.d.ts.map +1 -1
  77. package/dist/validate-cel-context.js +48 -16
  78. package/dist/validate-exports.d.ts +28 -0
  79. package/dist/validate-exports.d.ts.map +1 -0
  80. package/dist/validate-exports.js +146 -0
  81. package/dist/validate-extends.d.ts.map +1 -1
  82. package/dist/validate-extends.js +37 -1
  83. package/dist/validate-identifier-names.d.ts +2 -2
  84. package/dist/validate-identifier-names.d.ts.map +1 -1
  85. package/dist/validate-identifier-names.js +22 -7
  86. package/dist/validate-provider-coherence.d.ts +4 -8
  87. package/dist/validate-provider-coherence.d.ts.map +1 -1
  88. package/dist/validate-provider-coherence.js +12 -123
  89. package/dist/validate-ref-slots.d.ts +1 -1
  90. package/dist/validate-ref-slots.d.ts.map +1 -1
  91. package/dist/validate-ref-slots.js +34 -0
  92. package/dist/validate-references.d.ts.map +1 -1
  93. package/dist/validate-references.js +168 -63
  94. package/dist/validate-template-body.d.ts +36 -0
  95. package/dist/validate-template-body.d.ts.map +1 -0
  96. package/dist/validate-template-body.js +246 -0
  97. package/dist/validate-throws-coverage.d.ts.map +1 -1
  98. package/dist/validate-throws-coverage.js +236 -85
  99. package/package.json +3 -2
  100. package/src/analyzer.ts +118 -12
  101. package/src/builtins.ts +85 -97
  102. package/src/catch-scope.ts +157 -0
  103. package/src/cel-scope-query.ts +28 -9
  104. package/src/cel-scope.ts +1 -0
  105. package/src/deprecation.ts +36 -0
  106. package/src/eval-paths.ts +53 -6
  107. package/src/extends-resolution.ts +24 -0
  108. package/src/index.ts +9 -1
  109. package/src/manifest-visitor.ts +19 -2
  110. package/src/migrations/report.ts +5 -1
  111. package/src/nearest-name.ts +47 -0
  112. package/src/ref-slot.ts +19 -0
  113. package/src/release/destinations.ts +105 -0
  114. package/src/release/index.ts +33 -2
  115. package/src/release/ledger.ts +51 -21
  116. package/src/release/release-plan.ts +16 -26
  117. package/src/release/workspace-config.ts +483 -47
  118. package/src/release/workspace-schema.ts +99 -0
  119. package/src/resolve-throws-union.ts +139 -21
  120. package/src/schema-compat.ts +13 -0
  121. package/src/schema-error-report.ts +66 -10
  122. package/src/schema-keywords.ts +4 -1
  123. package/src/schema-walk.ts +56 -0
  124. package/src/telo-version.ts +1 -1
  125. package/src/template-body.ts +25 -0
  126. package/src/types.ts +18 -0
  127. package/src/validate-cel-context.ts +57 -16
  128. package/src/validate-exports.ts +185 -0
  129. package/src/validate-extends.ts +43 -0
  130. package/src/validate-identifier-names.ts +28 -9
  131. package/src/validate-provider-coherence.ts +17 -127
  132. package/src/validate-ref-slots.ts +41 -1
  133. package/src/validate-references.ts +174 -63
  134. package/src/validate-template-body.ts +274 -0
  135. package/src/validate-throws-coverage.ts +333 -92
  136. package/dist/validate-template-dispatch.d.ts +0 -27
  137. package/dist/validate-template-dispatch.d.ts.map +0 -1
  138. package/dist/validate-template-dispatch.js +0 -95
  139. package/src/validate-template-dispatch.ts +0 -99
package/dist/ref-slot.js CHANGED
@@ -166,6 +166,7 @@ export function readRefSlot(node) {
166
166
  const uses = new Set();
167
167
  let useCases;
168
168
  let inputs;
169
+ let throwsThrough = false;
169
170
  for (const carrier of nodes) {
170
171
  const annotation = carrier["x-telo-ref"];
171
172
  for (const kind of readKinds(annotation)) {
@@ -180,6 +181,8 @@ export function readRefSlot(node) {
180
181
  useCases ??= readUseCases(obj.use);
181
182
  if (typeof obj.inputs === "string")
182
183
  inputs ??= obj.inputs;
184
+ if (obj.throwsThrough === true)
185
+ throwsThrough = true;
183
186
  }
184
187
  const slot = {
185
188
  kinds,
@@ -191,6 +194,8 @@ export function readRefSlot(node) {
191
194
  slot.useCases = useCases;
192
195
  if (inputs !== undefined)
193
196
  slot.inputs = inputs;
197
+ if (throwsThrough)
198
+ slot.throwsThrough = true;
194
199
  return slot;
195
200
  }
196
201
  /** True when the node declares a reference slot in any accepted shape. The
@@ -218,6 +223,8 @@ export function refSlotAnnotation(slot) {
218
223
  annotation.use = slot.uses;
219
224
  if (slot.inputs !== undefined)
220
225
  annotation.inputs = slot.inputs;
226
+ if (slot.throwsThrough)
227
+ annotation.throwsThrough = true;
221
228
  return annotation;
222
229
  }
223
230
  /**
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Where each module publishes, and the two ways a set of destinations can be
3
+ * inconsistent.
4
+ *
5
+ * A destination is the module's resolved registry base plus its own directory
6
+ * name. That rule held silently for as long as every module sat at one depth
7
+ * under one base with a unique directory name; per-subtree bases remove that
8
+ * coincidence, so both failures it was hiding become reachable and are checked
9
+ * here — **at plan time, before any payload is built**, because the payload
10
+ * builder's own refusal speaks about a manifest published to two places rather
11
+ * than about the workspace file that said so.
12
+ *
13
+ * Pure data in, diagnostics out: the destinations are already computed for
14
+ * `telo release order`, so one derivation feeds that payload and both checks.
15
+ */
16
+ import type { ModuleKey } from "./fragment.js";
17
+ import type { ReleaseDiagnostic } from "./release-plan.js";
18
+ export interface ModuleDestination {
19
+ readonly key: ModuleKey;
20
+ /** `<registry>/<the module's own directory name>`. */
21
+ readonly destination: string;
22
+ }
23
+ /**
24
+ * A relative import between two workspace modules, with both answers about
25
+ * where the target publishes.
26
+ *
27
+ * `derived` is what the importer's own destination yields when the import path
28
+ * is applied to it — the transport's rule, and what ref canonicalization writes
29
+ * into the published manifest. `assigned` is what discovery independently gives
30
+ * that module.
31
+ */
32
+ export interface ImportDestination {
33
+ readonly from: ModuleKey;
34
+ readonly to: ModuleKey;
35
+ readonly derived: string;
36
+ readonly assigned: string;
37
+ }
38
+ /**
39
+ * Two modules must not resolve to one ref.
40
+ *
41
+ * Nothing else catches it: the payload builder is keyed by manifest, so it sees
42
+ * one module claimed by two destinations and never two modules claiming one,
43
+ * while the ledger keys by module — both entries would record digests for a
44
+ * single published artifact and reconciliation could never settle.
45
+ */
46
+ export declare function checkDestinationCollisions(destinations: readonly ModuleDestination[]): ReleaseDiagnostic[];
47
+ /**
48
+ * A relative import is valid only where the importer's derived ref for its
49
+ * target equals the destination that module is independently assigned.
50
+ *
51
+ * Equal registries are necessary and not sufficient — two modules under one base
52
+ * at different directory depths derive differently too. Making the builder take
53
+ * the assigned destination instead is not the fix: it would put a ref in the
54
+ * artifact that the transport's resolution rule does not produce, so the
55
+ * manifest would say one thing and every consumer resolving relatively would
56
+ * compute another.
57
+ */
58
+ export declare function checkImportDestinations(edges: readonly ImportDestination[]): ReleaseDiagnostic[];
59
+ //# sourceMappingURL=destinations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"destinations.d.ts","sourceRoot":"","sources":["../../src/release/destinations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,iBAAiB,EAAE,CAsBrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,iBAAiB,EAAE,GAClC,iBAAiB,EAAE,CAgBrB"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Where each module publishes, and the two ways a set of destinations can be
3
+ * inconsistent.
4
+ *
5
+ * A destination is the module's resolved registry base plus its own directory
6
+ * name. That rule held silently for as long as every module sat at one depth
7
+ * under one base with a unique directory name; per-subtree bases remove that
8
+ * coincidence, so both failures it was hiding become reachable and are checked
9
+ * here — **at plan time, before any payload is built**, because the payload
10
+ * builder's own refusal speaks about a manifest published to two places rather
11
+ * than about the workspace file that said so.
12
+ *
13
+ * Pure data in, diagnostics out: the destinations are already computed for
14
+ * `telo release order`, so one derivation feeds that payload and both checks.
15
+ */
16
+ /**
17
+ * Two modules must not resolve to one ref.
18
+ *
19
+ * Nothing else catches it: the payload builder is keyed by manifest, so it sees
20
+ * one module claimed by two destinations and never two modules claiming one,
21
+ * while the ledger keys by module — both entries would record digests for a
22
+ * single published artifact and reconciliation could never settle.
23
+ */
24
+ export function checkDestinationCollisions(destinations) {
25
+ const byDestination = new Map();
26
+ for (const { key, destination } of destinations) {
27
+ const sharing = byDestination.get(destination);
28
+ if (sharing)
29
+ sharing.push(key);
30
+ else
31
+ byDestination.set(destination, [key]);
32
+ }
33
+ const diagnostics = [];
34
+ for (const [destination, keys] of byDestination) {
35
+ if (keys.length < 2)
36
+ continue;
37
+ diagnostics.push({
38
+ severity: "error",
39
+ code: "DESTINATION_COLLISION",
40
+ message: `${keys.sort().join(" and ")} both publish to '${destination}'. A module's ref is its ` +
41
+ `registry base plus its own directory name, so they would overwrite one artifact and ` +
42
+ `their ledger entries could never reconcile. Rename one directory, or give one of the ` +
43
+ `subtrees its own 'registry:' in telo-workspace.yaml.`,
44
+ });
45
+ }
46
+ return diagnostics;
47
+ }
48
+ /**
49
+ * A relative import is valid only where the importer's derived ref for its
50
+ * target equals the destination that module is independently assigned.
51
+ *
52
+ * Equal registries are necessary and not sufficient — two modules under one base
53
+ * at different directory depths derive differently too. Making the builder take
54
+ * the assigned destination instead is not the fix: it would put a ref in the
55
+ * artifact that the transport's resolution rule does not produce, so the
56
+ * manifest would say one thing and every consumer resolving relatively would
57
+ * compute another.
58
+ */
59
+ export function checkImportDestinations(edges) {
60
+ const diagnostics = [];
61
+ for (const edge of edges) {
62
+ if (edge.derived === edge.assigned)
63
+ continue;
64
+ diagnostics.push({
65
+ severity: "error",
66
+ code: "IMPORT_DESTINATION_CONFLICT",
67
+ message: `${edge.from} imports ${edge.to} by relative path, which canonicalizes to ` +
68
+ `'${edge.derived}' — but ${edge.to} publishes to '${edge.assigned}'. Publishing rewrites ` +
69
+ `the import to the ref its own path yields, so the artifact would name a module nobody ` +
70
+ `pushes. Give the two the same registry base and the same directory depth, or make it a ` +
71
+ `pinned remote import, which is what a dependency across a publish boundary is.`,
72
+ });
73
+ }
74
+ return diagnostics;
75
+ }
@@ -21,6 +21,10 @@ export type { LayerChange, LayerDigests } from "./payload-digest.js";
21
21
  export { orderByImports, planRelease } from "./release-plan.js";
22
22
  export type { ArtifactKind, BumpReason, ChangelogEntry, ModuleEvidence, PlannedModule, ReleaseDiagnostic, ReleaseEvidence, ReleasePlan, } from "./release-plan.js";
23
23
  export { VersionStampError, readManifestVersion, stampCrateVersion, stampManifestVersion, stampSelfNpmPins, stampPackageVersion, } from "./version-stamp.js";
24
- export { WORKSPACE_FILENAME, WorkspaceConfigError, parseWorkspaceConfig } from "./workspace-config.js";
25
- export type { WorkspaceConfig } from "./workspace-config.js";
24
+ export { checkDestinationCollisions, checkImportDestinations } from "./destinations.js";
25
+ export type { ImportDestination, ModuleDestination } from "./destinations.js";
26
+ export { DEFAULT_ENV_FILES, DEFAULT_RELEASE_IGNORE, WORKSPACE_FILENAME, WorkspaceConfigError, diagnosticsFor, hasError, matchesPatterns, readWorkspaceConfig, requireReleaseSettings, settingsForModule, } from "./workspace-config.js";
27
+ export type { EnvSettings, ModuleEntry, ModuleSettings, PatternMatch, ReleaseSettings, WorkspaceConfig, WorkspaceDiagnostic, WorkspaceDiagnosticCode, WorkspaceRead, } from "./workspace-config.js";
28
+ export { MODULE_ENTRY_KEYS, WORKSPACE_BLOCKS, WORKSPACE_SCHEMA, } from "./workspace-schema.js";
29
+ export type { WorkspaceBlockSchema, WorkspaceKeySchema } from "./workspace-schema.js";
26
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,QAAQ,GACT,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpG,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnG,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EACV,YAAY,EACZ,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACvG,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/release/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,QAAQ,GACT,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpG,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnG,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EACV,YAAY,EACZ,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACxF,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE9E,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACZ,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -15,4 +15,6 @@ export { EMPTY_LEDGER, LEDGER_PATH, LedgerError, parseLedger, serializeLedger }
15
15
  export { LOCALLY_DERIVED_LAYERS, MANIFEST_LAYER, diffLayerDigests, layerDigestKey, } from "./payload-digest.js";
16
16
  export { orderByImports, planRelease } from "./release-plan.js";
17
17
  export { VersionStampError, readManifestVersion, stampCrateVersion, stampManifestVersion, stampSelfNpmPins, stampPackageVersion, } from "./version-stamp.js";
18
- export { WORKSPACE_FILENAME, WorkspaceConfigError, parseWorkspaceConfig } from "./workspace-config.js";
18
+ export { checkDestinationCollisions, checkImportDestinations } from "./destinations.js";
19
+ export { DEFAULT_ENV_FILES, DEFAULT_RELEASE_IGNORE, WORKSPACE_FILENAME, WorkspaceConfigError, diagnosticsFor, hasError, matchesPatterns, readWorkspaceConfig, requireReleaseSettings, settingsForModule, } from "./workspace-config.js";
20
+ export { MODULE_ENTRY_KEYS, WORKSPACE_BLOCKS, WORKSPACE_SCHEMA, } from "./workspace-schema.js";
@@ -14,10 +14,22 @@
14
14
  * drift: it means nothing is published, which is the correct reading for a
15
15
  * module that has never shipped.
16
16
  *
17
- * **It records the registry base**, because canonicalizing a relative `imports:`
18
- * source writes the destination into the manifest layer — so the digests below
19
- * are digests *against that base*, and comparing them to digests taken against
20
- * another one would be comparing two different artifacts.
17
+ * **Every entry records its own registry base**, because canonicalizing a
18
+ * relative `imports:` source writes the destination into the manifest layer — so
19
+ * each digest is a digest *against that base*, and comparing it to one taken
20
+ * against another would be comparing two different artifacts.
21
+ *
22
+ * Per entry rather than one top-level base with per-entry deltas: a workspace
23
+ * whose `release.modules` entries each author a destination has no meaningful
24
+ * top-level base, an absent one already means *nothing has been published yet*,
25
+ * and the agreement check returns early on that — so the multi-destination
26
+ * workspace would be the one whose bases are never compared. The file is
27
+ * generated and never hand-maintained, so the redundancy costs nothing and it
28
+ * removes the "differs from what?" question and the absent-versus-unknown
29
+ * conflation together. A **top-level `registry:` is a legacy form the reader
30
+ * accepts and never writes**, applied to every entry: the credential-free PR
31
+ * gate reads whatever ledger is committed on the branch, and that stays in the
32
+ * old shape until a release regenerates it.
21
33
  */
22
34
  import { type ModuleKey } from "./fragment.js";
23
35
  import type { LayerDigests } from "./payload-digest.js";
@@ -26,16 +38,13 @@ export interface LedgerEntry {
26
38
  /** The version these digests were taken at — the tag the artifact published
27
39
  * under. */
28
40
  readonly version: string;
41
+ /** The publish destination base these digests were taken against
42
+ * (`oci://ghcr.io/telorun`). Absent only in a ledger written before the base
43
+ * was recorded at all. */
44
+ readonly registry?: string;
29
45
  readonly layers: LayerDigests;
30
46
  }
31
47
  export interface Ledger {
32
- /**
33
- * The publish destination base the digests were taken against
34
- * (`oci://ghcr.io/telorun`). Absent in a workspace that has published nothing
35
- * yet, which is why it is optional rather than required — but a `check` that
36
- * has entries and no base cannot reproduce them, and says so.
37
- */
38
- readonly registry?: string;
39
48
  readonly modules: ReadonlyMap<ModuleKey, LedgerEntry>;
40
49
  }
41
50
  export declare class LedgerError extends Error {
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/release/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,eAAO,MAAM,WAAW,yBAAyB,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B;iBACa;IACb,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B;AAED,MAAM,WAAW,MAAM;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;CACvD;AAED,qBAAa,WAAY,SAAQ,KAAK;CAAG;AAEzC,eAAO,MAAM,YAAY,EAAE,MAA+B,CAAC;AAE3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CA+D/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAkBtD"}
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/release/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,eAAO,MAAM,WAAW,yBAAyB,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B;iBACa;IACb,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;+BAE2B;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;CACvD;AAED,qBAAa,WAAY,SAAQ,KAAK;CAAG;AAEzC,eAAO,MAAM,YAAY,EAAE,MAA+B,CAAC;AAE3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAmF/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBtD"}
@@ -14,10 +14,22 @@
14
14
  * drift: it means nothing is published, which is the correct reading for a
15
15
  * module that has never shipped.
16
16
  *
17
- * **It records the registry base**, because canonicalizing a relative `imports:`
18
- * source writes the destination into the manifest layer — so the digests below
19
- * are digests *against that base*, and comparing them to digests taken against
20
- * another one would be comparing two different artifacts.
17
+ * **Every entry records its own registry base**, because canonicalizing a
18
+ * relative `imports:` source writes the destination into the manifest layer — so
19
+ * each digest is a digest *against that base*, and comparing it to one taken
20
+ * against another would be comparing two different artifacts.
21
+ *
22
+ * Per entry rather than one top-level base with per-entry deltas: a workspace
23
+ * whose `release.modules` entries each author a destination has no meaningful
24
+ * top-level base, an absent one already means *nothing has been published yet*,
25
+ * and the agreement check returns early on that — so the multi-destination
26
+ * workspace would be the one whose bases are never compared. The file is
27
+ * generated and never hand-maintained, so the redundancy costs nothing and it
28
+ * removes the "differs from what?" question and the absent-versus-unknown
29
+ * conflation together. A **top-level `registry:` is a legacy form the reader
30
+ * accepts and never writes**, applied to every entry: the credential-free PR
31
+ * gate reads whatever ledger is committed on the branch, and that stays in the
32
+ * old shape until a release regenerates it.
21
33
  */
22
34
  import { Document, parseDocument } from "yaml";
23
35
  import { isReleaseVersion } from "./bump-level.js";
@@ -45,13 +57,14 @@ export function parseLedger(text, where) {
45
57
  throw new LedgerError(`${where}: unknown field '${key}'. The ledger carries 'registry:' and 'modules:'.`);
46
58
  }
47
59
  }
48
- const registry = record.registry;
49
- if (registry !== undefined && typeof registry !== "string") {
60
+ // The legacy top-level base: read and applied to every entry, never written.
61
+ const inherited = record.registry;
62
+ if (inherited !== undefined && typeof inherited !== "string") {
50
63
  throw new LedgerError(`${where}: 'registry' must be the publish destination base, as a string.`);
51
64
  }
52
65
  const rawModules = record.modules;
53
66
  if (rawModules === undefined || rawModules === null)
54
- return { ...(registry ? { registry } : {}), modules: new Map() };
67
+ return { modules: new Map() };
55
68
  if (typeof rawModules !== "object" || Array.isArray(rawModules)) {
56
69
  throw new LedgerError(`${where}: 'modules' must be a mapping of module path to entry.`);
57
70
  }
@@ -61,6 +74,16 @@ export function parseLedger(text, where) {
61
74
  throw new LedgerError(`${where}: entry '${key}' must be a mapping.`);
62
75
  }
63
76
  const entry = raw;
77
+ for (const field of Object.keys(entry)) {
78
+ if (field !== "version" && field !== "registry" && field !== "layers") {
79
+ throw new LedgerError(`${where}: entry '${key}' has unknown field '${field}'. An entry carries 'version:', ` +
80
+ `'registry:' and 'layers:'.`);
81
+ }
82
+ }
83
+ if (entry.registry !== undefined && typeof entry.registry !== "string") {
84
+ throw new LedgerError(`${where}: entry '${key}' has a non-string 'registry'. It is the publish destination base ` +
85
+ `the digests beside it were taken against.`);
86
+ }
64
87
  if (!isReleaseVersion(entry.version)) {
65
88
  throw new LedgerError(`${where}: entry '${key}' has no major.minor.patch 'version'. The ledger records the ` +
66
89
  `version each digest was taken at; without it the digest says nothing.`);
@@ -78,9 +101,14 @@ export function parseLedger(text, where) {
78
101
  layers[layer] = digest;
79
102
  }
80
103
  }
81
- modules.set(normalizeModuleKey(key), { version: entry.version, layers });
104
+ const registry = entry.registry ?? inherited;
105
+ modules.set(normalizeModuleKey(key), {
106
+ version: entry.version,
107
+ ...(registry ? { registry } : {}),
108
+ layers,
109
+ });
82
110
  }
83
- return { ...(registry ? { registry } : {}), modules };
111
+ return { modules };
84
112
  }
85
113
  /**
86
114
  * Render the ledger.
@@ -96,12 +124,13 @@ export function serializeLedger(ledger) {
96
124
  const layers = {};
97
125
  for (const layer of Object.keys(entry.layers).sort())
98
126
  layers[layer] = entry.layers[layer];
99
- modules[key] = { version: entry.version, layers };
127
+ modules[key] = {
128
+ version: entry.version,
129
+ ...(entry.registry ? { registry: entry.registry } : {}),
130
+ layers,
131
+ };
100
132
  }
101
- const doc = new Document({
102
- ...(ledger.registry ? { registry: ledger.registry } : {}),
103
- modules,
104
- });
133
+ const doc = new Document({ modules });
105
134
  return ("# Generated by `telo release apply` — what each module looks like as published.\n" +
106
135
  "# A cache of the registry's answer, so a PR gate needs no credentials.\n" +
107
136
  "# `telo release verify` reconciles it against the registry.\n" +
@@ -58,13 +58,14 @@ export interface ModuleEvidence {
58
58
  /** Whether a file under this module's own directory that reaches the artifact
59
59
  * changed. Decides only whether a changelog line is requested. */
60
60
  readonly ownFilesChanged: boolean;
61
+ /** The publish destination base THIS module's payload was built against —
62
+ * per module, because a workspace may declare one per subtree. */
63
+ readonly registry: string;
61
64
  }
62
65
  export interface ReleaseEvidence {
63
66
  readonly modules: readonly ModuleEvidence[];
64
67
  readonly ledger: Ledger;
65
68
  readonly fragments: readonly ReleaseFragment[];
66
- /** The publish destination base the digests above were built against. */
67
- readonly registry?: string;
68
69
  }
69
70
  /** Why a module is in the plan. A module usually carries several. */
70
71
  export type BumpReason = {
@@ -1 +1 @@
1
- {"version":3,"file":"release-plan.d.ts","sourceRoot":"","sources":["../../src/release/release-plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"release-plan.d.ts","sourceRoot":"","sources":["../../src/release/release-plan.ts"],"names":[],"mappings":"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"}
@@ -28,7 +28,6 @@ import { diffLayerDigests } from "./payload-digest.js";
28
28
  export function planRelease(evidence) {
29
29
  const diagnostics = [];
30
30
  const byKey = new Map(evidence.modules.map((module) => [module.key, module]));
31
- checkRegistryAgreement(evidence, diagnostics);
32
31
  // What the digest says, per module. A module with no ledger entry has never
33
32
  // been published, which is not drift — there is nothing to differ from.
34
33
  const drift = new Map();
@@ -36,6 +35,18 @@ export function planRelease(evidence) {
36
35
  const recorded = evidence.ledger.modules.get(module.key);
37
36
  if (!recorded)
38
37
  continue;
38
+ if (recorded.registry && recorded.registry !== module.registry) {
39
+ diagnostics.push({
40
+ severity: "error",
41
+ code: "LEDGER_REGISTRY_MISMATCH",
42
+ message: `${module.key}: its ledger digests were taken against '${recorded.registry}', but this ` +
43
+ `run built against '${module.registry}'. Publishing rewrites each relative import to ` +
44
+ `'<base>/<sibling>@<version>', so the manifest layers of the two are different bytes ` +
45
+ `and comparing them would report the module as changed. Publish to the recorded base, ` +
46
+ `or re-record with \`telo release verify --write\`.`,
47
+ });
48
+ continue;
49
+ }
39
50
  if (recorded.version !== module.version) {
40
51
  diagnostics.push({
41
52
  severity: "error",
@@ -228,24 +239,6 @@ function requestMissingChangelogEntries(modules, entries, diagnostics) {
228
239
  `One \`telo release add\` can name them all.`,
229
240
  });
230
241
  }
231
- /**
232
- * The base the digests were built against has to be the base they were recorded
233
- * against, or the manifest layers are not comparable: canonicalization writes
234
- * the destination into them.
235
- */
236
- function checkRegistryAgreement(evidence, diagnostics) {
237
- const recorded = evidence.ledger.registry;
238
- if (!recorded || !evidence.registry || recorded === evidence.registry)
239
- return;
240
- diagnostics.push({
241
- severity: "error",
242
- code: "LEDGER_REGISTRY_MISMATCH",
243
- message: `The ledger's digests were taken against '${recorded}', but this run built against ` +
244
- `'${evidence.registry}'. Publishing rewrites each relative import to ` +
245
- `'<base>/<sibling>@<version>', so the manifest layers of the two are different bytes ` +
246
- `and comparing them would report every module as changed.`,
247
- });
248
- }
249
242
  /**
250
243
  * Dependency order over in-repo imports — a dependency before its dependents.
251
244
  *
@@ -1,45 +1,125 @@
1
1
  /**
2
- * `telo-workspace.yaml` — the release anchor.
3
- *
4
- * Every path the release system names a module key, a ledger entry, a
5
- * fragment's `modules:` is relative to this file's directory. That is the
6
- * whole of its job: **its location is the anchor**, and its one field names the
7
- * subtrees that may hold modules, which is not derivable (a whole-tree scan
8
- * would read every example and every cached `.telo/manifests` copy as a released
9
- * module).
10
- *
11
- * The file is **optional**, and everything reading it is harmless without one.
12
- * `telo release` requires one. `telo run` uses its LOCATION never `modules:`,
13
- * which is release scope — as the bound on the walk-up that collects `.env` /
14
- * `.env.local`, and with no marker reads the manifest's own directory alone, as
15
- * it did before this file existed; a bound is what keeps that walk out of a
16
- * user's home directory. Nothing else — not `check`, `publish`, `install`,
17
- * `upgrade`, `migrate`, `module`, and not the kernel looks for it, so a
18
- * single-manifest repo, a bare `examples/` directory and a third-party module
19
- * checkout keep working with nothing added. Any field added later must be true
20
- * of the whole tree, not derivable from it, and harmless by its absence.
21
- *
22
- * Parsing lives here, in the browser-safe half, because the editor answers
23
- * "what does changing this library bump?" from the same model. Finding the file
24
- * on disk is the CLI's half this side takes text.
2
+ * `telo-workspace.yaml` — the anchor.
3
+ *
4
+ * **Its location is the anchor, and every field lives in a block scoped to what
5
+ * it governs.** Every path the release system names a module key, a ledger
6
+ * entry, a fragment's `modules:` is relative to this file's directory, and so
7
+ * is the `.telo` cache. `release:` is what `telo release` reads; `env:` is what
8
+ * `telo run` reads when it resolves a manifest's environment.
9
+ *
10
+ * That rule is why `modules:` sits under `release:` rather than at the top
11
+ * level. It is a release inventory, not an inventory of manifests: it exists so
12
+ * a whole-tree scan does not read every example and every cached
13
+ * `.telo/manifests` copy as something to version and publish, and nothing else
14
+ * consults it the env walk refuses to, which is why a manifest under
15
+ * `examples/`, in no release subtree, still gets the full walk.
16
+ *
17
+ * The file is **optional and every block in it is optional**. A marker whose
18
+ * whole content is comments is valid: it anchors the cache and bounds the env
19
+ * walk, which is exactly what a runner seeds one for.
20
+ *
21
+ * **The reader is lenient; the diagnostics are the strict half.** It returns
22
+ * everything it could read plus everything it found wrong, each anchored at a
23
+ * key path, because an editor wants all of them and a command wants to exit on
24
+ * the first. Which of them are fatal is the CALLER's policy, not the file's:
25
+ * `telo release` refuses on any error, while `telo run` consumes `env:` alone
26
+ * and prints the rest — aborting every app in a workspace over a release typo is
27
+ * not a trade to make silently.
28
+ *
29
+ * Parsing lives here, in the browser-safe half, because the editor answers "what
30
+ * does changing this library bump?" from the same model. Finding the file on
31
+ * disk is the CLI's half — this side takes text. Gitignore matching is INJECTED
32
+ * (`PatternMatch`) for the same reason: the grammar belongs to the glob package,
33
+ * which this half does not depend on.
25
34
  */
35
+ import { DEFAULT_ENV_FILES, DEFAULT_RELEASE_IGNORE } from "./workspace-schema.js";
26
36
  export declare const WORKSPACE_FILENAME = "telo-workspace.yaml";
37
+ export { DEFAULT_ENV_FILES, DEFAULT_RELEASE_IGNORE };
38
+ /** One `release.modules` entry. A bare string in the file normalizes to this
39
+ * with no overrides. */
40
+ export interface ModuleEntry {
41
+ readonly path: string;
42
+ readonly registry?: string;
43
+ readonly ignore?: readonly string[];
44
+ }
45
+ export interface ReleaseSettings {
46
+ readonly registry?: string;
47
+ readonly ignore?: readonly string[];
48
+ readonly modules: readonly ModuleEntry[];
49
+ }
50
+ export interface EnvSettings {
51
+ readonly roots?: readonly string[];
52
+ readonly files?: readonly string[];
53
+ }
27
54
  export interface WorkspaceConfig {
28
- /**
29
- * Gitignore-style patterns, workspace-relative, naming the subtrees that may
30
- * hold modules (`modules/*`, `apps/*`). A pattern names a place to look, never
31
- * a module: what makes a directory a module is its `telo.yaml`.
32
- */
33
- readonly modules: readonly string[];
55
+ readonly release?: ReleaseSettings;
56
+ readonly env?: EnvSettings;
57
+ }
58
+ export type WorkspaceDiagnosticCode = "WORKSPACE_UNKNOWN_KEY" | "WORKSPACE_MODULES_MOVED" | "WORKSPACE_INVALID_VALUE" | "WORKSPACE_ENTRY_MATCHES_NOTHING" | "WORKSPACE_ENTRY_SHADOWED" | "WORKSPACE_MARKER_SHADOWED";
59
+ export interface WorkspaceDiagnostic {
60
+ readonly code: WorkspaceDiagnosticCode;
61
+ readonly severity: "error" | "warning";
62
+ readonly message: string;
63
+ /** Key path from the document root (`["release", "modules", 2, "registry"]`),
64
+ * so a host can anchor a squiggle without re-deriving where it came from. */
65
+ readonly path: readonly (string | number)[];
66
+ }
67
+ export interface WorkspaceRead {
68
+ readonly config: WorkspaceConfig;
69
+ readonly diagnostics: readonly WorkspaceDiagnostic[];
34
70
  }
35
71
  export declare class WorkspaceConfigError extends Error {
36
72
  }
37
73
  /**
38
- * Parse the marker file's text.
74
+ * Read the marker's text.
75
+ *
76
+ * Never throws: unreadable YAML is itself a diagnostic, because the one caller
77
+ * that must not abort on it is the run path.
78
+ */
79
+ export declare function readWorkspaceConfig(text: string, where: string): WorkspaceRead;
80
+ /** The diagnostics anchored inside one top-level block, plus the ones anchored
81
+ * at the document itself (unreadable YAML, a non-mapping root) — those are
82
+ * every block's problem. */
83
+ export declare function diagnosticsFor(diagnostics: readonly WorkspaceDiagnostic[], block: string): readonly WorkspaceDiagnostic[];
84
+ export declare function hasError(diagnostics: readonly WorkspaceDiagnostic[]): boolean;
85
+ /**
86
+ * The release settings, or a throw naming what is missing.
87
+ *
88
+ * `telo release` is the one caller for which any error is fatal, and for which
89
+ * an absent block is itself one: a marker with no `release:` anchors a cache and
90
+ * declares no release scope, which is exactly the state a runner's seeded marker
91
+ * is in.
92
+ */
93
+ export declare function requireReleaseSettings(read: WorkspaceRead, where: string): ReleaseSettings;
94
+ /**
95
+ * Gitignore-style matching over one authored list.
96
+ *
97
+ * Injected rather than imported: the grammar is the glob package's, and this
98
+ * half is browser-safe. Returns the index of the LAST pattern matching `path`,
99
+ * or `-1` — which is what makes a decision attributable to the entry that made
100
+ * it (the `buildImportUpgrades` environment precedent).
101
+ */
102
+ export type PatternMatch = (path: string, patterns: readonly string[]) => number;
103
+ export interface ModuleSettings {
104
+ /** Index into `release.modules` of the entry that decided this module. */
105
+ readonly entry: number;
106
+ readonly registry?: string;
107
+ readonly ignore: readonly string[];
108
+ }
109
+ /**
110
+ * Which entry claims `key`, and what it therefore settles.
111
+ *
112
+ * **The last matching entry supplies them** — the rule the list already has,
113
+ * since it is one gitignore-style list evaluated last-match-wins. A negation
114
+ * entry is an exclusion and therefore supplies nothing: a module the last match
115
+ * excludes is not a module of this workspace at all, which is why this returns
116
+ * `undefined` rather than a settings object for it.
39
117
  *
40
- * Strict about its one field, because there is nothing here to be lenient with:
41
- * an empty or absent `modules:` names no subtree, so discovery would find no
42
- * module and every gate would silently pass over a whole repo.
118
+ * The cascade is key-wise and each key's value replaces whole, so an entry
119
+ * naming only `registry:` keeps the block's `ignore:`.
43
120
  */
44
- export declare function parseWorkspaceConfig(text: string, where: string): WorkspaceConfig;
121
+ export declare function settingsForModule(release: ReleaseSettings, key: string, match: PatternMatch): ModuleSettings | undefined;
122
+ /** Whether `path` is matched by an authored pattern list, negations honoured.
123
+ * `env.roots` and a resolved `ignore` list are both read through this. */
124
+ export declare function matchesPatterns(path: string, patterns: readonly string[], match: PatternMatch): boolean;
45
125
  //# sourceMappingURL=workspace-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-config.d.ts","sourceRoot":"","sources":["../../src/release/workspace-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAElD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAoCjF"}
1
+ {"version":3,"file":"workspace-config.d.ts","sourceRoot":"","sources":["../../src/release/workspace-config.ts"],"names":[],"mappings":"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"}