@telorun/analyzer 0.59.0 → 0.60.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 (80) hide show
  1. package/dist/analyzer.d.ts +9 -2
  2. package/dist/analyzer.d.ts.map +1 -1
  3. package/dist/analyzer.js +38 -10
  4. package/dist/builtins.d.ts.map +1 -1
  5. package/dist/builtins.js +16 -42
  6. package/dist/cel-environment.d.ts.map +1 -1
  7. package/dist/cel-environment.js +35 -0
  8. package/dist/flatten-for-analyzer.d.ts +4 -0
  9. package/dist/flatten-for-analyzer.d.ts.map +1 -1
  10. package/dist/flatten-for-analyzer.js +8 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +6 -0
  14. package/dist/kernel-globals.d.ts +1 -1
  15. package/dist/kernel-globals.d.ts.map +1 -1
  16. package/dist/kernel-globals.js +15 -1
  17. package/dist/manifest-schemas.d.ts +407 -0
  18. package/dist/manifest-schemas.d.ts.map +1 -0
  19. package/dist/manifest-schemas.js +396 -0
  20. package/dist/module-file-claims.d.ts +6 -0
  21. package/dist/module-file-claims.d.ts.map +1 -1
  22. package/dist/module-file-claims.js +4 -0
  23. package/dist/module-metadata-scope.d.ts +44 -0
  24. package/dist/module-metadata-scope.d.ts.map +1 -0
  25. package/dist/module-metadata-scope.js +82 -0
  26. package/dist/parse-loaded-file.d.ts.map +1 -1
  27. package/dist/parse-loaded-file.js +16 -0
  28. package/dist/release/bump-level.d.ts +46 -0
  29. package/dist/release/bump-level.d.ts.map +1 -0
  30. package/dist/release/bump-level.js +80 -0
  31. package/dist/release/changelog.d.ts +36 -0
  32. package/dist/release/changelog.d.ts.map +1 -0
  33. package/dist/release/changelog.js +53 -0
  34. package/dist/release/fragment.d.ts +48 -0
  35. package/dist/release/fragment.d.ts.map +1 -0
  36. package/dist/release/fragment.js +84 -0
  37. package/dist/release/index.d.ts +26 -0
  38. package/dist/release/index.d.ts.map +1 -0
  39. package/dist/release/index.js +18 -0
  40. package/dist/release/ledger.d.ts +53 -0
  41. package/dist/release/ledger.d.ts.map +1 -0
  42. package/dist/release/ledger.js +109 -0
  43. package/dist/release/payload-digest.d.ts +63 -0
  44. package/dist/release/payload-digest.d.ts.map +1 -0
  45. package/dist/release/payload-digest.js +65 -0
  46. package/dist/release/release-plan.d.ts +128 -0
  47. package/dist/release/release-plan.d.ts.map +1 -0
  48. package/dist/release/release-plan.js +277 -0
  49. package/dist/release/version-stamp.d.ts +50 -0
  50. package/dist/release/version-stamp.d.ts.map +1 -0
  51. package/dist/release/version-stamp.js +119 -0
  52. package/dist/release/workspace-config.d.ts +41 -0
  53. package/dist/release/workspace-config.d.ts.map +1 -0
  54. package/dist/release/workspace-config.js +60 -0
  55. package/dist/schema-compat.js +2 -1
  56. package/dist/validate-step-inputs.d.ts +1 -1
  57. package/dist/validate-step-inputs.d.ts.map +1 -1
  58. package/dist/validate-step-inputs.js +99 -8
  59. package/package.json +3 -3
  60. package/src/analyzer.ts +41 -10
  61. package/src/builtins.ts +16 -42
  62. package/src/cel-environment.ts +37 -0
  63. package/src/flatten-for-analyzer.ts +12 -0
  64. package/src/index.ts +9 -1
  65. package/src/kernel-globals.ts +23 -1
  66. package/src/manifest-schemas.ts +408 -0
  67. package/src/module-file-claims.ts +10 -0
  68. package/src/module-metadata-scope.ts +88 -0
  69. package/src/parse-loaded-file.ts +16 -0
  70. package/src/release/bump-level.ts +95 -0
  71. package/src/release/changelog.ts +62 -0
  72. package/src/release/fragment.ts +130 -0
  73. package/src/release/index.ts +66 -0
  74. package/src/release/ledger.ts +142 -0
  75. package/src/release/payload-digest.ts +83 -0
  76. package/src/release/release-plan.ts +392 -0
  77. package/src/release/version-stamp.ts +142 -0
  78. package/src/release/workspace-config.ts +81 -0
  79. package/src/schema-compat.ts +1 -1
  80. package/src/validate-step-inputs.ts +109 -9
@@ -9,7 +9,8 @@ import {
9
9
  validateAgainstSchema,
10
10
  } from "./schema-compat.js";
11
11
  import { plainChainOf } from "@telorun/templating";
12
- import { valueTypeOf } from "@telorun/sdk";
12
+ import { isLiveSlot, valueTypeOf } from "@telorun/sdk";
13
+ import { manifestFragmentOf } from "./manifest-schemas.js";
13
14
  import {
14
15
  analyzerContractScope,
15
16
  containerOf,
@@ -25,7 +26,7 @@ export interface StepInputIssue {
25
26
  message: string;
26
27
  /** Set when the issue is a type-argument disagreement rather than a contract
27
28
  * shape violation — the two read differently and deserve their own code. */
28
- code?: "CEL_TYPE_ARGUMENT_MISMATCH";
29
+ code?: "CEL_TYPE_ARGUMENT_MISMATCH" | "LIVE_VALUE_RETRIED";
29
30
  }
30
31
 
31
32
 
@@ -118,15 +119,30 @@ export function collectStepInputIssues(
118
119
  // that silence is exactly where a stream of the wrong element used to
119
120
  // flow. The comparison is covariant and gradual: an omitted argument is
120
121
  // *any* in both directions, so only a definite conflict is reported.
121
- if (stepContext) {
122
+ // The roots a plain chain may name here, each paired with the schema it is
123
+ // navigated against. `steps.` is the step map (analyzer state, supplied by
124
+ // the caller). `inputs.` is the ENCLOSING kind's own declared inputType,
125
+ // which is how a value produced OUTSIDE this resource reaches a step at
126
+ // all: an HTTP route maps `request.body` into its handler's inputs, and the
127
+ // handler forwards `inputs.body` onward — the shape a live value most often
128
+ // arrives in, and the one covering only `steps.` missed entirely. A root
129
+ // this cannot resolve contributes nothing rather than guessing at a schema.
130
+ const roots: Array<[string, Record<string, any>]> = [];
131
+ if (stepContext) roots.push(["steps.", stepContext]);
132
+ const ownContract = resolveContract(
133
+ "inputType",
134
+ manifest,
135
+ contractScope.resolveIn(manifest.kind as string, readingModule),
136
+ contractScope,
137
+ );
138
+ if (ownContract) roots.push(["inputs.", ownContract.schema]);
139
+
140
+ if (roots.length > 0) {
122
141
  for (const [inputName, inputValue] of Object.entries(values)) {
123
142
  const chain = plainChainOf(inputValue);
124
- // The step context is rooted at the STEP MAP, so a `steps.` prefix is
125
- // the namespace name and not a property of it. Only that namespace is
126
- // navigated: `inputs.` and a named binding resolve elsewhere, and
127
- // guessing at a root this does not hold would compare the wrong schema.
128
- if (!chain?.startsWith("steps.")) continue;
129
- const produced = navigateSchemaToExprPath(stepContext, chain.slice("steps.".length));
143
+ const root = chain ? roots.find(([prefix]) => chain.startsWith(prefix)) : undefined;
144
+ if (!chain || !root) continue;
145
+ const produced = navigateSchemaToExprPath(root[1], chain.slice(root[0].length));
130
146
  const slotSchema = (contract.schema.properties as Record<string, any> | undefined)?.[
131
147
  inputName
132
148
  ];
@@ -138,6 +154,28 @@ export function collectStepInputIssues(
138
154
  // broad new Error-severity check hidden behind an argument-specific
139
155
  // name. Both sides must declare a value type for the question to be
140
156
  // about arguments at all.
157
+ // A LIVE value is consumed by reading, so it exists exactly once —
158
+ // that is what `live` says in the vocabulary, and re-attempting a
159
+ // dispatch that already read it re-sends nothing. Reported here rather
160
+ // than through a slot-specific annotation because both facts are
161
+ // already declared: the value's liveness by its value type, and the
162
+ // re-attempt by the retry policy. No kind is named.
163
+ if (isLiveSlot(produced)) {
164
+ const retry = declaredRetry(step, stepItemSchema, invokedManifest, invokedDef);
165
+ if (retry !== undefined) {
166
+ out.push({
167
+ path: `${stepPath}.${inputsField}.${inputName}`,
168
+ targetLabel: invokedName ?? invokedKind ?? "the invoked resource",
169
+ message:
170
+ `'${inputName}' is a live value, which is consumed by reading and so exists ` +
171
+ `once — but ${retry} re-attempts the dispatch, and a re-attempt would pass ` +
172
+ `nothing. Collect it to a value first, or chunk the work so each attempt ` +
173
+ `carries its own replayable piece.`,
174
+ code: "LIVE_VALUE_RETRIED",
175
+ });
176
+ continue;
177
+ }
178
+ }
141
179
  if (!valueTypeOf(produced) || !valueTypeOf(slotSchema)) continue;
142
180
  const { compatible, issues } = checkSchemaCompatibility(produced, slotSchema, (ref) =>
143
181
  defs.schemaForId(ref),
@@ -170,3 +208,65 @@ export function collectStepInputIssues(
170
208
  return out;
171
209
  }
172
210
 
211
+ /**
212
+ * Where a re-attempt is declared for this dispatch, described for a diagnostic,
213
+ * or undefined when none is.
214
+ *
215
+ * A field declares one when its schema was expanded from a shared retry fragment
216
+ * — the shape the author pointed at, rather than a marker they had to remember to
217
+ * write beside it. Which fragment also says WHERE the budget is, so the two
218
+ * spellings a kind may carry (a policy object, or the deprecated bare count) need
219
+ * no guessing between them and no rule about which one wins.
220
+ *
221
+ * Two sites are consulted because there are two real ones: the STEP's own policy
222
+ * — `retry` on the kernel-owned dispatch site — and the TARGET's, a field on an
223
+ * arbitrary kind, because `Http.Request` re-attempts inside its own `invoke()`
224
+ * where only it can tell a 429 from a 500. A live value is equally doomed by
225
+ * either. EVERY retry-bearing field at a site is checked, not the first, since
226
+ * `Http.Request` carries both spellings and property order must not decide which
227
+ * is seen.
228
+ *
229
+ * Only a STATICALLY KNOWN non-zero budget counts. An `attempts` written as CEL
230
+ * says nothing here, and guessing would report a conflict against a manifest that
231
+ * may never retry — the same posture the `use` case-map selector takes.
232
+ */
233
+ function declaredRetry(
234
+ step: Record<string, any>,
235
+ stepItemSchema: Record<string, any> | undefined,
236
+ invokedManifest: Record<string, any> | undefined,
237
+ invokedDef: Record<string, any> | undefined,
238
+ ): string | undefined {
239
+ for (const [field, budget] of retryFields(stepItemSchema)) {
240
+ if (budget(step?.[field]) > 0) return `the step's \`${field}\``;
241
+ }
242
+ for (const [field, budget] of retryFields(invokedDef?.schema as Record<string, any>)) {
243
+ if (budget(invokedManifest?.[field]) > 0) return `the target's \`${field}\``;
244
+ }
245
+ return undefined;
246
+ }
247
+
248
+ /** How each shared retry fragment carries its budget. Keyed on fragment name —
249
+ * the analyzer's own built-ins, never a module's kind — so a kind that adopts a
250
+ * shape is covered without naming it here. */
251
+ const RETRY_BUDGET: Record<string, (value: unknown) => number> = {
252
+ RetryPolicy: (value) => {
253
+ if (!value || typeof value !== "object") return 0;
254
+ const attempts = (value as Record<string, unknown>).attempts;
255
+ return typeof attempts === "number" ? attempts : 0;
256
+ },
257
+ RetryAttempts: (value) => (typeof value === "number" ? value : 0),
258
+ };
259
+
260
+ /** Every property of `schema` whose shape came from a retry fragment, paired with
261
+ * the reader for that fragment's budget. */
262
+ function retryFields(
263
+ schema: Record<string, any> | undefined,
264
+ ): Array<[string, (value: unknown) => number]> {
265
+ if (!schema) return [];
266
+ const out: Array<[string, (value: unknown) => number]> = [];
267
+ for (const [key, sub] of gatherPropertySchemas(schema)) {
268
+ const budget = RETRY_BUDGET[manifestFragmentOf(sub) ?? ""];
269
+ if (budget) out.push([key, budget]);
270
+ }
271
+ return out;
272
+ }