@telorun/analyzer 0.65.0 → 0.67.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.
- package/dist/analyzer.d.ts +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +194 -65
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +67 -16
- package/dist/call-graph.d.ts.map +1 -1
- package/dist/call-graph.js +7 -1
- package/dist/cel-environment.d.ts +12 -0
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +35 -19
- package/dist/cel-scope.d.ts +8 -0
- package/dist/cel-scope.d.ts.map +1 -1
- package/dist/cel-scope.js +89 -11
- package/dist/definition-registry.d.ts +41 -0
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +76 -0
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +65 -0
- package/dist/eval-paths.d.ts +68 -0
- package/dist/eval-paths.d.ts.map +1 -1
- package/dist/eval-paths.js +138 -0
- package/dist/flatten-for-analyzer.d.ts +37 -1
- package/dist/flatten-for-analyzer.d.ts.map +1 -1
- package/dist/flatten-for-analyzer.js +104 -5
- package/dist/import-resolution-diagnostics.d.ts +1 -1
- package/dist/import-resolution-diagnostics.js +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/inline-imports.d.ts.map +1 -1
- package/dist/inline-imports.js +1 -0
- package/dist/manifest-visitor.d.ts +4 -0
- package/dist/manifest-visitor.d.ts.map +1 -1
- package/dist/manifest-visitor.js +36 -1
- package/dist/migrations/entries/index.d.ts.map +1 -1
- package/dist/migrations/entries/index.js +2 -0
- package/dist/migrations/entries/schema-prepare-bucket.json +27 -0
- package/dist/peer-binding.d.ts +232 -0
- package/dist/peer-binding.d.ts.map +1 -0
- package/dist/peer-binding.js +418 -0
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +8 -0
- package/dist/ref-slot.d.ts +13 -0
- package/dist/ref-slot.d.ts.map +1 -1
- package/dist/ref-slot.js +26 -0
- package/dist/reference-field-map.d.ts +28 -0
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +25 -0
- package/dist/referrer-rule.d.ts +17 -0
- package/dist/referrer-rule.d.ts.map +1 -1
- package/dist/referrer-rule.js +4 -1
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.js +1 -1
- package/dist/resolve-ref-sentinels.d.ts.map +1 -1
- package/dist/resolve-ref-sentinels.js +14 -1
- package/dist/resource-input.d.ts +75 -0
- package/dist/resource-input.d.ts.map +1 -0
- package/dist/resource-input.js +90 -0
- package/dist/resource-rule.d.ts +40 -27
- package/dist/resource-rule.d.ts.map +1 -1
- package/dist/resource-rule.js +61 -7
- package/dist/rule-condition.d.ts +11 -0
- package/dist/rule-condition.d.ts.map +1 -1
- package/dist/rule-condition.js +13 -0
- package/dist/schema-projection.d.ts +119 -4
- package/dist/schema-projection.d.ts.map +1 -1
- package/dist/schema-projection.js +213 -18
- package/dist/system-kinds.d.ts +7 -2
- package/dist/system-kinds.d.ts.map +1 -1
- package/dist/system-kinds.js +7 -2
- package/dist/telo-version.d.ts +1 -1
- package/dist/telo-version.js +1 -1
- package/dist/template-body.d.ts +50 -0
- package/dist/template-body.d.ts.map +1 -0
- package/dist/template-body.js +58 -0
- package/dist/validate-cel-context.d.ts +1 -17
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +73 -78
- package/dist/validate-identifier-names.d.ts.map +1 -1
- package/dist/validate-identifier-names.js +17 -2
- package/dist/validate-reference-forms.d.ts +10 -0
- package/dist/validate-reference-forms.d.ts.map +1 -1
- package/dist/validate-reference-forms.js +23 -0
- package/dist/validate-references.d.ts +17 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +77 -18
- package/dist/validate-referrer-rules.d.ts +38 -4
- package/dist/validate-referrer-rules.d.ts.map +1 -1
- package/dist/validate-referrer-rules.js +139 -31
- package/dist/validate-resource-inputs.d.ts +35 -0
- package/dist/validate-resource-inputs.d.ts.map +1 -0
- package/dist/validate-resource-inputs.js +319 -0
- package/dist/validate-resource-rules.d.ts +11 -2
- package/dist/validate-resource-rules.d.ts.map +1 -1
- package/dist/validate-resource-rules.js +18 -12
- package/dist/validate-schema-projection.d.ts.map +1 -1
- package/dist/validate-schema-projection.js +33 -2
- package/dist/validate-template-dispatch.d.ts +27 -0
- package/dist/validate-template-dispatch.d.ts.map +1 -0
- package/dist/validate-template-dispatch.js +95 -0
- package/dist/with-synthetic-positions.d.ts +1 -1
- package/dist/with-synthetic-positions.js +1 -1
- package/package.json +3 -3
- package/src/analyzer.ts +236 -70
- package/src/builtins.ts +69 -16
- package/src/call-graph.ts +7 -1
- package/src/cel-environment.ts +48 -19
- package/src/cel-scope.ts +113 -19
- package/src/definition-registry.ts +78 -0
- package/src/dependency-graph.ts +66 -0
- package/src/eval-paths.ts +152 -0
- package/src/flatten-for-analyzer.ts +117 -4
- package/src/import-resolution-diagnostics.ts +1 -1
- package/src/index.ts +57 -4
- package/src/inline-imports.ts +1 -0
- package/src/manifest-visitor.ts +41 -1
- package/src/migrations/entries/index.ts +2 -0
- package/src/migrations/entries/schema-prepare-bucket.json +27 -0
- package/src/peer-binding.ts +641 -0
- package/src/precompile.ts +8 -0
- package/src/ref-slot.ts +36 -0
- package/src/reference-field-map.ts +42 -0
- package/src/referrer-rule.ts +20 -1
- package/src/release/index.ts +1 -1
- package/src/resolve-ref-sentinels.ts +12 -1
- package/src/resource-input.ts +132 -0
- package/src/resource-rule.ts +69 -7
- package/src/rule-condition.ts +15 -0
- package/src/schema-projection.ts +302 -20
- package/src/system-kinds.ts +7 -2
- package/src/telo-version.ts +1 -1
- package/src/template-body.ts +104 -0
- package/src/validate-cel-context.ts +73 -78
- package/src/validate-identifier-names.ts +18 -3
- package/src/validate-reference-forms.ts +22 -0
- package/src/validate-references.ts +85 -16
- package/src/validate-referrer-rules.ts +215 -28
- package/src/validate-resource-inputs.ts +367 -0
- package/src/validate-resource-rules.ts +26 -12
- package/src/validate-schema-projection.ts +50 -1
- package/src/validate-template-dispatch.ts +99 -0
- package/src/with-synthetic-positions.ts +1 -1
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
export { extractAccessChains, validateChainAgainstSchema } from "@telorun/templating";
|
|
2
2
|
import { elementSchemaOf, isLiveSlot, mergeTypeSchemas, parseCanonicalTypeSchemaId, } from "@telorun/sdk";
|
|
3
3
|
import { KERNEL_BUILTINS } from "./builtins.js";
|
|
4
|
-
import { isStepSlot } from "./step-slot.js";
|
|
5
4
|
import { withRefSlotsAsReadings } from "./ref-slot-reading.js";
|
|
5
|
+
// Where CEL is evaluated is one reader (`eval-paths.ts`), and the region half of
|
|
6
|
+
// it moved there so the scope walk and the `x-telo-eval` walk answer the same
|
|
7
|
+
// question in one place. Re-exported: this module is where every existing
|
|
8
|
+
// consumer imports them from.
|
|
9
|
+
export { extractCelRegionScopes, pathMatchesScope } from "./eval-paths.js";
|
|
10
|
+
/** True when a node's value is decided at load or dispatch rather than written
|
|
11
|
+
* — a tagged sentinel (`!cel`, `!sql`, an embed) or an already-compiled value.
|
|
12
|
+
* A schema position holding one declares a shape nothing static can read. */
|
|
13
|
+
function isDynamicNode(value) {
|
|
14
|
+
if (!value || typeof value !== "object")
|
|
15
|
+
return false;
|
|
16
|
+
const node = value;
|
|
17
|
+
return node.__tagged === true || node.__compiled !== undefined;
|
|
18
|
+
}
|
|
19
|
+
/** Replace every dynamically-valued property with an OPEN schema, so the name is
|
|
20
|
+
* in scope with its members unconstrained. */
|
|
21
|
+
function openDynamicProperties(props) {
|
|
22
|
+
if (!props || typeof props !== "object")
|
|
23
|
+
return props;
|
|
24
|
+
let out;
|
|
25
|
+
for (const [key, value] of Object.entries(props)) {
|
|
26
|
+
if (!isDynamicNode(value))
|
|
27
|
+
continue;
|
|
28
|
+
out ??= { ...props };
|
|
29
|
+
out[key] = {};
|
|
30
|
+
}
|
|
31
|
+
return out ?? props;
|
|
32
|
+
}
|
|
6
33
|
/**
|
|
7
34
|
* Resolve a type field value (string name, inline type, or raw schema) to a JSON Schema.
|
|
8
35
|
* - String: look up the named type in allManifests (Type.JsonSchema resources)
|
|
@@ -229,36 +256,6 @@ function collectionBindingWithheld(schema, manifestRoot, allManifests) {
|
|
|
229
256
|
return false;
|
|
230
257
|
return isLiveSlot(resolveCollectionSchema(manifestRoot, from, allManifests));
|
|
231
258
|
}
|
|
232
|
-
/**
|
|
233
|
-
* Returns true when a CEL expression path (from walkCelExpressions, e.g. "routes[0].inputs.q")
|
|
234
|
-
* falls within the scope of a context (e.g. "$.routes[*].inputs").
|
|
235
|
-
*
|
|
236
|
-
* The scope is matched directly (no sibling sharing): a context at "$.routes[*].inputs" only
|
|
237
|
-
* applies to expressions whose path starts with "routes[N].inputs", not to other sibling fields.
|
|
238
|
-
*/
|
|
239
|
-
export function pathMatchesScope(exprPath, scope) {
|
|
240
|
-
const stripped = scope.startsWith("$.") ? scope.slice(2) : scope;
|
|
241
|
-
if (!stripped)
|
|
242
|
-
return false;
|
|
243
|
-
// Split on wildcard array segments; each [*] must match a concrete [N] in exprPath
|
|
244
|
-
const parts = stripped.split("[*]");
|
|
245
|
-
let remaining = exprPath;
|
|
246
|
-
for (let i = 0; i < parts.length; i++) {
|
|
247
|
-
const part = parts[i];
|
|
248
|
-
if (!remaining.startsWith(part))
|
|
249
|
-
return false;
|
|
250
|
-
remaining = remaining.slice(part.length);
|
|
251
|
-
if (i < parts.length - 1) {
|
|
252
|
-
// Expect a concrete array index like [0], [12], ...
|
|
253
|
-
const m = remaining.match(/^\[\d+\]/);
|
|
254
|
-
if (!m)
|
|
255
|
-
return false;
|
|
256
|
-
remaining = remaining.slice(m[0].length);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
// Expression must end here or continue into a child path
|
|
260
|
-
return remaining === "" || remaining[0] === "." || remaining[0] === "[";
|
|
261
|
-
}
|
|
262
259
|
/**
|
|
263
260
|
* Resolves `x-telo-context-*` annotations in a context schema using the concrete
|
|
264
261
|
* manifest item (per-scope) and the manifest root.
|
|
@@ -324,8 +321,19 @@ export function resolveContextAnnotations(schema, manifestItem, opts) {
|
|
|
324
321
|
// to be resolved first: the standard library writes it as the inline
|
|
325
322
|
// `{ kind: Type.JsonSchema, schema: … }` wrapper, so merging it verbatim
|
|
326
323
|
// would type the variable as `{ kind, schema }` instead of its properties.
|
|
324
|
+
// The navigated node — or one of the properties in it — may itself be an
|
|
325
|
+
// expression: a route whose `request.schema.body` is `!cel "self.model.schema"`
|
|
326
|
+
// declares a body whose SHAPE is only known once the template is
|
|
327
|
+
// instantiated. Such a name types as OPEN rather than as nothing, and a
|
|
328
|
+
// wholly dynamic map leaves the whole node open. Resolving it to nothing
|
|
329
|
+
// reports `request.body` as undefined, which blames the reader for the
|
|
330
|
+
// writer's dynamism — the same posture a rule takes when a value it reads
|
|
331
|
+
// is dynamic: skip the judgement, never invert it.
|
|
332
|
+
if (isDynamicNode(navigated)) {
|
|
333
|
+
return { ...schema, properties: { ...(schema.properties ?? {}) }, additionalProperties: true };
|
|
334
|
+
}
|
|
327
335
|
const asType = resolveTypeFieldToSchema(navigated, allManifests ?? []);
|
|
328
|
-
const resolved = asType?.properties ?? navigated;
|
|
336
|
+
const resolved = openDynamicProperties(asType?.properties ?? navigated);
|
|
329
337
|
const required = Array.isArray(asType?.required) ? asType.required : undefined;
|
|
330
338
|
return {
|
|
331
339
|
...schema,
|
|
@@ -464,14 +472,40 @@ export function resolveContextAnnotations(schema, manifestItem, opts) {
|
|
|
464
472
|
*/
|
|
465
473
|
export function getManifestItem(exprPath, scope, manifest) {
|
|
466
474
|
const stripped = scope.startsWith("$.") ? scope.slice(2) : scope;
|
|
467
|
-
const
|
|
468
|
-
if (
|
|
469
|
-
return manifest;
|
|
470
|
-
const arrayProp = stripped.slice(0, wildcardIdx); // e.g. "routes"
|
|
471
|
-
const m = exprPath.match(new RegExp(`^${arrayProp}\\[(\\d+)\\]`));
|
|
472
|
-
if (!m)
|
|
475
|
+
const parts = stripped.split("[*]");
|
|
476
|
+
if (parts.length === 1)
|
|
473
477
|
return manifest;
|
|
474
|
-
|
|
478
|
+
// Resolve each `[*]` against the concrete index the expression path carries at
|
|
479
|
+
// the same position, accumulating up to the LAST wildcard: that node is the
|
|
480
|
+
// per-scope item (`routes[2]`), which is what `x-telo-context-from` navigates
|
|
481
|
+
// from. Built by walking the pattern rather than by a regex over the scope —
|
|
482
|
+
// a scope is a path, not a pattern, and its own `[4]` segments are regex
|
|
483
|
+
// character classes, which is what silently made every nested scope resolve
|
|
484
|
+
// to the whole document instead of the item.
|
|
485
|
+
let remaining = exprPath;
|
|
486
|
+
let concrete = "";
|
|
487
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
488
|
+
const part = parts[i];
|
|
489
|
+
if (!remaining.startsWith(part))
|
|
490
|
+
return manifest;
|
|
491
|
+
remaining = remaining.slice(part.length);
|
|
492
|
+
const index = remaining.match(/^\[(\d+)\]/);
|
|
493
|
+
if (!index)
|
|
494
|
+
return manifest;
|
|
495
|
+
remaining = remaining.slice(index[0].length);
|
|
496
|
+
concrete += `${part}${index[0]}`;
|
|
497
|
+
}
|
|
498
|
+
return navigateConcretePath(manifest, concrete) ?? manifest;
|
|
499
|
+
}
|
|
500
|
+
/** Walk a concrete dotted path with `[N]` indices (`resources[4].routes[2]`). */
|
|
501
|
+
function navigateConcretePath(root, path) {
|
|
502
|
+
let cur = root;
|
|
503
|
+
for (const segment of path.match(/[^.[\]]+/g) ?? []) {
|
|
504
|
+
if (cur === null || typeof cur !== "object")
|
|
505
|
+
return undefined;
|
|
506
|
+
cur = cur[segment];
|
|
507
|
+
}
|
|
508
|
+
return cur;
|
|
475
509
|
}
|
|
476
510
|
function navigatePath(obj, segments) {
|
|
477
511
|
let cur = obj;
|
|
@@ -496,45 +530,6 @@ export function extractContextsFromSchema(schema, path = "$") {
|
|
|
496
530
|
const all = collectContexts(schema, path);
|
|
497
531
|
return all.sort((a, b) => b.scope.length - a.scope.length);
|
|
498
532
|
}
|
|
499
|
-
/** Schema keys that declare a CEL-bearing region: a field carrying any of these
|
|
500
|
-
* is evaluated at runtime, so a `!cel` inside it (or a descendant) is live. A
|
|
501
|
-
* STEP BODY is one too, and says so through the grammar its items point at
|
|
502
|
-
* rather than through a key — {@link isStepSlot} reads either spelling. */
|
|
503
|
-
const CEL_REGION_KEYS = [
|
|
504
|
-
"x-telo-context",
|
|
505
|
-
"x-telo-step-context",
|
|
506
|
-
"x-telo-error-context",
|
|
507
|
-
];
|
|
508
|
-
/**
|
|
509
|
-
* Walk a JSON Schema tree and collect the JSONPath scopes of every field that
|
|
510
|
-
* declares a CEL-bearing region (`x-telo-context` / `x-telo-step-context` /
|
|
511
|
-
* `x-telo-error-context`). Used — alongside `x-telo-eval` paths — to decide
|
|
512
|
-
* whether a `!cel` expression sits in a slot the runtime actually evaluates.
|
|
513
|
-
* Scopes use the same `$.a.b[*]` form as `extractContextsFromSchema`, matched
|
|
514
|
-
* against expression paths with `pathMatchesScope`.
|
|
515
|
-
*/
|
|
516
|
-
export function extractCelRegionScopes(schema, path = "$") {
|
|
517
|
-
if (!schema || typeof schema !== "object")
|
|
518
|
-
return [];
|
|
519
|
-
const out = [];
|
|
520
|
-
if (CEL_REGION_KEYS.some((k) => schema[k]) || isStepSlot(schema))
|
|
521
|
-
out.push(path);
|
|
522
|
-
if (schema.properties) {
|
|
523
|
-
for (const [key, value] of Object.entries(schema.properties)) {
|
|
524
|
-
out.push(...extractCelRegionScopes(value, `${path}.${key}`));
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
if (schema.items && typeof schema.items === "object") {
|
|
528
|
-
out.push(...extractCelRegionScopes(schema.items, `${path}[*]`));
|
|
529
|
-
}
|
|
530
|
-
for (const key of ["oneOf", "anyOf", "allOf"]) {
|
|
531
|
-
if (Array.isArray(schema[key])) {
|
|
532
|
-
for (const subschema of schema[key])
|
|
533
|
-
out.push(...extractCelRegionScopes(subschema, path));
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
return out;
|
|
537
|
-
}
|
|
538
533
|
function collectContexts(schema, path) {
|
|
539
534
|
if (!schema || typeof schema !== "object")
|
|
540
535
|
return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-identifier-names.d.ts","sourceRoot":"","sources":["../src/validate-identifier-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-identifier-names.d.ts","sourceRoot":"","sources":["../src/validate-identifier-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAQnE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,KAAK,EAAE,SAAS,GACf,kBAAkB,EAAE,CAwEtB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TYPE_LEVEL_DOC_KINDS, checkName, } from "./identifier-name.js";
|
|
2
|
+
import { isInjectedDeclaration } from "./resource-input.js";
|
|
2
3
|
const SOURCE = "telo-analyzer";
|
|
3
4
|
/**
|
|
4
5
|
* Every author-written name in one pass — resource instances, kinds, modules,
|
|
@@ -36,6 +37,11 @@ export function validateIdentifierNames(manifests, registry, aliases, rootModule
|
|
|
36
37
|
// would blame them for a spelling this pass's own pipeline chose.
|
|
37
38
|
if (metadata?.xTeloOrigin)
|
|
38
39
|
continue;
|
|
40
|
+
// A kind-only stand-in for a `resources:` entry carries the name the author
|
|
41
|
+
// wrote in that block, and the block's own keys are checked below — on the
|
|
42
|
+
// module doc, where the name is written and a squiggle can land.
|
|
43
|
+
if (isInjectedDeclaration(manifest))
|
|
44
|
+
continue;
|
|
39
45
|
const ownModule = metadata?.module;
|
|
40
46
|
if (ownModule && !rootModules.has(ownModule))
|
|
41
47
|
continue;
|
|
@@ -53,7 +59,10 @@ export function validateIdentifierNames(manifests, registry, aliases, rootModule
|
|
|
53
59
|
// keys are the imported library's declarations, so a violation there is
|
|
54
60
|
// the library author's, reported when their module is analyzed as a root.
|
|
55
61
|
if (manifest.kind === "Telo.Application" || manifest.kind === "Telo.Library") {
|
|
56
|
-
|
|
62
|
+
// `resources:` joins them: a library's resource inputs are named in the
|
|
63
|
+
// same identifier space (`!ref connection`, `resources.connection`) and
|
|
64
|
+
// break the same way.
|
|
65
|
+
for (const field of ["variables", "secrets", "ports", "resources"]) {
|
|
57
66
|
const block = manifest[field];
|
|
58
67
|
if (!block || typeof block !== "object" || Array.isArray(block))
|
|
59
68
|
continue;
|
|
@@ -125,7 +134,13 @@ function surfaceFor(kind) {
|
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
function singular(field) {
|
|
128
|
-
return field === "variables"
|
|
137
|
+
return field === "variables"
|
|
138
|
+
? "variable"
|
|
139
|
+
: field === "secrets"
|
|
140
|
+
? "secret"
|
|
141
|
+
: field === "ports"
|
|
142
|
+
? "port"
|
|
143
|
+
: "resource input";
|
|
129
144
|
}
|
|
130
145
|
function push(out, violation, at) {
|
|
131
146
|
if (!violation)
|
|
@@ -23,6 +23,16 @@ import { type AnalysisDiagnostic } from "./types.js";
|
|
|
23
23
|
* Rejected, each with an actionable diagnostic pointing at `!ref`:
|
|
24
24
|
* - the object form `{ kind, name }` (the old reference object), and
|
|
25
25
|
* - a bare string (the old name / dotted-FQN reference).
|
|
26
|
+
*
|
|
27
|
+
* **A value-or-reference union slot is the one exception**, and it is decided by
|
|
28
|
+
* the value rather than by a new spelling: where the reference constraint is a
|
|
29
|
+
* *branch* of a union (`type:` on a column, holding either a storage class or a
|
|
30
|
+
* `!ref` to a declared enum), a SCALAR is a value and is left to that branch,
|
|
31
|
+
* while an object is still checked for the removed `{kind, name}` form unless a
|
|
32
|
+
* branch describes it. The rule guards a REMOVED spelling — a bare string is
|
|
33
|
+
* simply not a reference in Telo — so admitting one takes nothing away: what it
|
|
34
|
+
* costs is the answer when an author meant a reference and wrote a bare name,
|
|
35
|
+
* which against a closed branch AJV reports as an unknown value.
|
|
26
36
|
*/
|
|
27
37
|
export declare function validateReferenceForms(resources: ResourceManifest[], registry: DefinitionRegistry, aliases?: AliasResolver, aliasesByModule?: Map<string, AliasResolver>): AnalysisDiagnostic[];
|
|
28
38
|
//# sourceMappingURL=validate-reference-forms.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-reference-forms.d.ts","sourceRoot":"","sources":["../src/validate-reference-forms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-reference-forms.d.ts","sourceRoot":"","sources":["../src/validate-reference-forms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,kBAAkB,EAAE,CA0EtB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isTaggedSentinel } from "@telorun/templating";
|
|
2
2
|
import { visitManifest } from "./manifest-visitor.js";
|
|
3
|
+
import { satisfiesValueBranch } from "./reference-field-map.js";
|
|
3
4
|
import { REF_VALIDATION_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
|
|
4
5
|
import { DiagnosticSeverity } from "./types.js";
|
|
5
6
|
const SOURCE = "telo-analyzer";
|
|
@@ -24,6 +25,16 @@ const SOURCE = "telo-analyzer";
|
|
|
24
25
|
* Rejected, each with an actionable diagnostic pointing at `!ref`:
|
|
25
26
|
* - the object form `{ kind, name }` (the old reference object), and
|
|
26
27
|
* - a bare string (the old name / dotted-FQN reference).
|
|
28
|
+
*
|
|
29
|
+
* **A value-or-reference union slot is the one exception**, and it is decided by
|
|
30
|
+
* the value rather than by a new spelling: where the reference constraint is a
|
|
31
|
+
* *branch* of a union (`type:` on a column, holding either a storage class or a
|
|
32
|
+
* `!ref` to a declared enum), a SCALAR is a value and is left to that branch,
|
|
33
|
+
* while an object is still checked for the removed `{kind, name}` form unless a
|
|
34
|
+
* branch describes it. The rule guards a REMOVED spelling — a bare string is
|
|
35
|
+
* simply not a reference in Telo — so admitting one takes nothing away: what it
|
|
36
|
+
* costs is the answer when an author meant a reference and wrote a bare name,
|
|
37
|
+
* which against a closed branch AJV reports as an unknown value.
|
|
27
38
|
*/
|
|
28
39
|
export function validateReferenceForms(resources, registry, aliases, aliasesByModule) {
|
|
29
40
|
if (!aliases)
|
|
@@ -37,6 +48,18 @@ export function validateReferenceForms(resources, registry, aliases, aliasesByMo
|
|
|
37
48
|
// `!ref` and `!cel`/`${{ }}` sentinels are the supported shapes.
|
|
38
49
|
if (isTaggedSentinel(value))
|
|
39
50
|
return;
|
|
51
|
+
// A SCALAR at a slot whose union has a value branch is a value, and the
|
|
52
|
+
// value branch is what judges it. Reporting a mistyped storage class as
|
|
53
|
+
// a malformed reference — "write it as '!ref txt'" — instructs the author
|
|
54
|
+
// to convert a typo into a reference, and AJV has already said what the
|
|
55
|
+
// value must be one of. The object form below is not ambiguous the same
|
|
56
|
+
// way: `{kind, name}` is the removed reference object, so it is still
|
|
57
|
+
// reported unless a branch genuinely describes that shape.
|
|
58
|
+
const hasValueBranch = (e.entry.valueBranches?.length ?? 0) > 0;
|
|
59
|
+
if (hasValueBranch && typeof value !== "object")
|
|
60
|
+
return;
|
|
61
|
+
if (satisfiesValueBranch(value, e.entry.valueBranches, registry))
|
|
62
|
+
return;
|
|
40
63
|
const r = e.source;
|
|
41
64
|
const resourceLabel = `${r.kind}/${r.metadata.name}`;
|
|
42
65
|
const resourceData = { kind: r.kind, name: r.metadata.name };
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
2
|
import { type AnalysisDiagnostic, type AnalysisContext } from "./types.js";
|
|
3
|
+
import type { DefinitionRegistry } from "./definition-registry.js";
|
|
4
|
+
/**
|
|
5
|
+
* Liskov substitutability at a kind constraint: is `resolved` (a canonical
|
|
6
|
+
* `<module>.<Kind>`) accepted where `targetKind` is required?
|
|
7
|
+
*
|
|
8
|
+
* THE one implementation — `checkKind` below builds a MESSAGE from it and adds
|
|
9
|
+
* nothing to the rule, so a resource input's injection site and an ordinary ref
|
|
10
|
+
* slot cannot come to disagree about what satisfies a constraint.
|
|
11
|
+
*
|
|
12
|
+
* A value satisfies the slot when it transitively extends the target kind, or —
|
|
13
|
+
* for a CONCRETE target — IS that kind; `getByExtends` is the same transitive
|
|
14
|
+
* subtype index for both, and an abstract is satisfied only by an implementer,
|
|
15
|
+
* never by the abstract itself (which is non-instantiable). Accepts a constraint
|
|
16
|
+
* that resolves to nothing, and an abstract with no loaded implementations:
|
|
17
|
+
* partial context, where a rejection would be a guess.
|
|
18
|
+
*/
|
|
19
|
+
export declare function kindSatisfies(resolved: string, targetKind: string, registry: DefinitionRegistry): boolean;
|
|
3
20
|
/**
|
|
4
21
|
* Phase 3 — Reference validation.
|
|
5
22
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAarD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAwBT;AA8DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CA2gBtB"}
|
|
@@ -1,19 +1,73 @@
|
|
|
1
1
|
import { isRefSentinel } from "@telorun/templating";
|
|
2
2
|
import { visitManifest } from "./manifest-visitor.js";
|
|
3
|
-
import { isInlineResource, resolveFieldEntries, resolveFieldValues } from "./reference-field-map.js";
|
|
3
|
+
import { isInlineResource, resolveFieldEntries, resolveFieldValues, satisfiesValueBranch, } from "./reference-field-map.js";
|
|
4
4
|
import { navigateJsonPointer, substituteCelFields } from "./schema-compat.js";
|
|
5
5
|
import { REF_VALIDATION_SKIP_KINDS as SYSTEM_KINDS } from "./system-kinds.js";
|
|
6
6
|
import { resolveTypeFieldToSchema } from "./validate-cel-context.js";
|
|
7
7
|
import { DiagnosticSeverity } from "./types.js";
|
|
8
8
|
const SOURCE = "telo-analyzer";
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Liskov substitutability at a kind constraint: is `resolved` (a canonical
|
|
11
|
+
* `<module>.<Kind>`) accepted where `targetKind` is required?
|
|
12
|
+
*
|
|
13
|
+
* THE one implementation — `checkKind` below builds a MESSAGE from it and adds
|
|
14
|
+
* nothing to the rule, so a resource input's injection site and an ordinary ref
|
|
15
|
+
* slot cannot come to disagree about what satisfies a constraint.
|
|
16
|
+
*
|
|
17
|
+
* A value satisfies the slot when it transitively extends the target kind, or —
|
|
18
|
+
* for a CONCRETE target — IS that kind; `getByExtends` is the same transitive
|
|
19
|
+
* subtype index for both, and an abstract is satisfied only by an implementer,
|
|
20
|
+
* never by the abstract itself (which is non-instantiable). Accepts a constraint
|
|
21
|
+
* that resolves to nothing, and an abstract with no loaded implementations:
|
|
22
|
+
* partial context, where a rejection would be a guess.
|
|
14
23
|
*/
|
|
24
|
+
export function kindSatisfies(resolved, targetKind, registry) {
|
|
25
|
+
const canonical = registry.resolveRef(targetKind) ?? targetKind;
|
|
26
|
+
const targetDef = registry.resolve(canonical);
|
|
27
|
+
if (!targetDef)
|
|
28
|
+
return true;
|
|
29
|
+
if (targetDef.kind !== "Telo.Abstract" && resolved === canonical)
|
|
30
|
+
return true;
|
|
31
|
+
const subtypes = registry.getByExtends(canonical);
|
|
32
|
+
if (subtypes.some((d) => `${d.metadata.module}.${d.metadata.name}` === resolved))
|
|
33
|
+
return true;
|
|
34
|
+
// Leniency is about the CANDIDATE, not about the population, and it is the
|
|
35
|
+
// same question for an abstract target and a concrete one. Accepting whenever
|
|
36
|
+
// no subtype happened to be loaded silenced the check exactly where it was
|
|
37
|
+
// needed: an app whose imports declare no `Telo.Runnable` is an app whose boot
|
|
38
|
+
// targets are all wrong, and every one of them passed.
|
|
39
|
+
//
|
|
40
|
+
// An UNREGISTERED candidate is not partial context, and the distinction is
|
|
41
|
+
// load-bearing: a kind nobody declared is an unknown kind or an unimported
|
|
42
|
+
// alias prefix (`NotAnAlias.Script`), which is precisely what this check
|
|
43
|
+
// exists to report. Only a kind that IS declared, whose ancestry reaches
|
|
44
|
+
// something this analysis never saw, is undecidable — there the missing hop is
|
|
45
|
+
// where the target it appears not to reach could have been declared.
|
|
46
|
+
// A candidate the registry never saw cannot be judged on what it implements —
|
|
47
|
+
// the analysis simply does not hold it. Whether its NAME is resolvable is a
|
|
48
|
+
// different question, asked of the alias scope by the caller.
|
|
49
|
+
if (!registry.resolve(resolved))
|
|
50
|
+
return true;
|
|
51
|
+
return !registry.ancestryResolved(resolved);
|
|
52
|
+
}
|
|
53
|
+
/** {@link kindSatisfies} at every kind a slot accepts, rendered as messages.
|
|
54
|
+
* The acceptance rule is not restated here — only what to say when it fails,
|
|
55
|
+
* which needs the slot's alternatives and the target's flavour. */
|
|
15
56
|
function checkKind(kind, entry, registry, aliases) {
|
|
16
57
|
const resolved = aliases.resolveKind(kind) ?? kind;
|
|
58
|
+
// A qualified kind whose prefix names no import in this scope is a bad NAME
|
|
59
|
+
// rather than a bad type — `{kind: NotAnAlias.Script}` where only `Lib` is
|
|
60
|
+
// imported — and this check is the only place it surfaces, so it is never
|
|
61
|
+
// waved through as partial context. Asked of the resolver, which reports
|
|
62
|
+
// `unknown` for exactly that case: a kind reached through a DECLARED alias
|
|
63
|
+
// resolves (`ok`) even when the target's definitions are not loaded, and a
|
|
64
|
+
// gated one says so separately.
|
|
65
|
+
const unknownAlias = kind.includes(".") &&
|
|
66
|
+
aliases.resolveKindResult(kind).status === "unknown" &&
|
|
67
|
+
// …and the registry does not hold it under the name as written either. A
|
|
68
|
+
// manifest may carry a canonical `<module>.<Kind>`, which no alias resolves
|
|
69
|
+
// and which is nonetheless perfectly identified.
|
|
70
|
+
registry.resolve(resolved) === undefined;
|
|
17
71
|
const errors = [];
|
|
18
72
|
for (const refStr of entry.refs) {
|
|
19
73
|
const targetKind = registry.resolveRef(refStr);
|
|
@@ -22,20 +76,11 @@ function checkKind(kind, entry, registry, aliases) {
|
|
|
22
76
|
const targetDef = registry.resolve(targetKind);
|
|
23
77
|
if (!targetDef)
|
|
24
78
|
return [];
|
|
25
|
-
|
|
26
|
-
// extends the target kind, or — for a CONCRETE target — IS that kind.
|
|
27
|
-
// `getByExtends` is the same transitive subtype index for abstract and
|
|
28
|
-
// concrete targets alike; an abstract is satisfied only by an implementer,
|
|
29
|
-
// never by the abstract kind itself (which is non-instantiable).
|
|
30
|
-
if (targetDef.kind !== "Telo.Abstract" && resolved === targetKind)
|
|
79
|
+
if (!unknownAlias && kindSatisfies(resolved, targetKind, registry))
|
|
31
80
|
return [];
|
|
32
81
|
const subtypes = registry.getByExtends(targetKind);
|
|
33
82
|
const subtypeKinds = new Set(subtypes.map((d) => `${d.metadata.module}.${d.metadata.name}`));
|
|
34
|
-
if (subtypeKinds.has(resolved))
|
|
35
|
-
return [];
|
|
36
83
|
if (targetDef.kind === "Telo.Abstract") {
|
|
37
|
-
if (subtypes.length === 0)
|
|
38
|
-
return []; // partial context — no implementations loaded yet
|
|
39
84
|
// Suggest only what an author can actually wire: with abstract-extends-
|
|
40
85
|
// abstract real (Telo.Executable over Invocable/Runnable), the transitive
|
|
41
86
|
// subtype list contains abstracts, which are non-instantiable and would
|
|
@@ -43,8 +88,15 @@ function checkKind(kind, entry, registry, aliases) {
|
|
|
43
88
|
const concrete = subtypes
|
|
44
89
|
.filter((d) => d.kind !== "Telo.Abstract")
|
|
45
90
|
.map((d) => `${d.metadata.module}.${d.metadata.name}`);
|
|
46
|
-
const options =
|
|
47
|
-
|
|
91
|
+
const options = concrete.length > 0 ? concrete : [...subtypeKinds];
|
|
92
|
+
// With nothing loaded that implements the target there is no list to
|
|
93
|
+
// offer, so the message says what this kind IS instead — which is the
|
|
94
|
+
// half the author can act on ("it declares 'Telo.Invocable'" points
|
|
95
|
+
// straight at a boot target that should have been an invoke step).
|
|
96
|
+
const declared = registry.resolve(resolved)?.capability;
|
|
97
|
+
errors.push(options.length > 0
|
|
98
|
+
? `'${kind}' does not implement '${targetKind}' (known implementations: ${options.join(", ")})`
|
|
99
|
+
: `'${kind}' does not implement '${targetKind}'${declared ? ` — it declares '${declared}'` : ""}`);
|
|
48
100
|
}
|
|
49
101
|
else {
|
|
50
102
|
const options = subtypeKinds.size > 0 ? ` or a subtype (${[...subtypeKinds].join(", ")})` : "";
|
|
@@ -91,7 +143,7 @@ export function validateReferences(resources, context) {
|
|
|
91
143
|
// Group manifests by name to detect collisions. Two subtleties:
|
|
92
144
|
//
|
|
93
145
|
// 1. Some analyzer hosts emit the SAME physical document twice through
|
|
94
|
-
// their pipeline — e.g.
|
|
146
|
+
// their pipeline — e.g. telo studio's `toAnalysisManifests` walks
|
|
95
147
|
// each workspace module's documents independently, and a file
|
|
96
148
|
// reachable from two angles (entry module + `include:` partial)
|
|
97
149
|
// shows up twice. The fingerprint includes `sourceLine` so identical
|
|
@@ -278,6 +330,13 @@ export function validateReferences(resources, context) {
|
|
|
278
330
|
if (typeof val !== "object")
|
|
279
331
|
return;
|
|
280
332
|
const refVal = val;
|
|
333
|
+
// A value the slot's own union describes is a value, not a reference —
|
|
334
|
+
// the same narrowing `validateReferenceForms` applies, through the same
|
|
335
|
+
// function, because it has to be applied here too: an OBJECT-shaped value
|
|
336
|
+
// branch would otherwise reach the structural check below and be reported
|
|
337
|
+
// as a reference missing 'kind' and 'name'.
|
|
338
|
+
if (satisfiesValueBranch(val, entry.valueBranches, registry))
|
|
339
|
+
return;
|
|
281
340
|
// Skip inline resources — Phase 2 normalization hasn't run yet.
|
|
282
341
|
if (isInlineResource(refVal))
|
|
283
342
|
return;
|
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { ResourceManifest } from "@telorun/sdk";
|
|
26
26
|
import { type ReferrerRule } from "./referrer-rule.js";
|
|
27
|
+
import type { PeerBinder, PeerBindingFailure, PeersTarget } from "./peer-binding.js";
|
|
28
|
+
import { type DynamicLeaf } from "./resource-rule.js";
|
|
27
29
|
export interface ReferrerRuleIssue {
|
|
28
30
|
code: "REFERRER_RULE_INVALID";
|
|
29
31
|
manifest: ResourceManifest;
|
|
@@ -48,7 +50,12 @@ export type ReferrerRuleFinding = {
|
|
|
48
50
|
kind: "skipped";
|
|
49
51
|
rule: ReferrerRule;
|
|
50
52
|
referrer: Referrer;
|
|
51
|
-
|
|
53
|
+
dynamic: DynamicLeaf;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "unbound";
|
|
56
|
+
rule: ReferrerRule;
|
|
57
|
+
referrer: Referrer;
|
|
58
|
+
failure: PeerBindingFailure;
|
|
52
59
|
} | {
|
|
53
60
|
kind: "failed";
|
|
54
61
|
rule: ReferrerRule;
|
|
@@ -60,6 +67,28 @@ export type ReferrerRuleFinding = {
|
|
|
60
67
|
referrer: Referrer;
|
|
61
68
|
elapsedMs: number;
|
|
62
69
|
};
|
|
70
|
+
/** What a peer rule needs beyond the referrers themselves: the binder that
|
|
71
|
+
* resolves a referrer's collection into declarations. Absent for a host that
|
|
72
|
+
* cannot resolve one — a rule declaring `peers:` then reports as unbound rather
|
|
73
|
+
* than running with the binding missing, which would evaluate a condition
|
|
74
|
+
* against names that are simply not there.
|
|
75
|
+
*
|
|
76
|
+
* ONE binder per analysis, because it caches each referrer's resolved
|
|
77
|
+
* collection and both the evaluation and the exercised check ask for it. */
|
|
78
|
+
export interface ReferrerRuleContext {
|
|
79
|
+
readonly peerBinder?: PeerBinder;
|
|
80
|
+
}
|
|
81
|
+
/** What a `peers:` pointer names in the referrer kind it is filtered to. Defined
|
|
82
|
+
* with the binding vocabulary; re-exported here because this is the surface
|
|
83
|
+
* that consumes it. The caller resolves it — only it holds the definition
|
|
84
|
+
* registry, and only after every kind is registered, since a rule may name a
|
|
85
|
+
* kind declared later in the same file. */
|
|
86
|
+
export type { PeersTarget };
|
|
87
|
+
export interface ReferrerRuleDeclarationContext {
|
|
88
|
+
/** Resolves a rule's `peers:` against its `referrer:` kind. Omit to skip the
|
|
89
|
+
* check — the safe direction for a host with no registry. */
|
|
90
|
+
readonly peersTarget?: (referrerKind: string, pointer: string) => PeersTarget;
|
|
91
|
+
}
|
|
63
92
|
/**
|
|
64
93
|
* Report every way a kind's referrer-rule declarations are malformed. Runs on
|
|
65
94
|
* the `Telo.Definition` / `Telo.Abstract` doc, so a defect lands on the line the
|
|
@@ -69,7 +98,7 @@ export type ReferrerRuleFinding = {
|
|
|
69
98
|
* declaring module's scope by `resolveSchemaRefKinds`, the only pass holding
|
|
70
99
|
* that scope, and a name resolving to nothing is reported from there.
|
|
71
100
|
*/
|
|
72
|
-
export declare function validateReferrerRuleDeclarations(manifest: ResourceManifest): ReferrerRuleIssue[];
|
|
101
|
+
export declare function validateReferrerRuleDeclarations(manifest: ResourceManifest, context?: ReferrerRuleDeclarationContext): ReferrerRuleIssue[];
|
|
73
102
|
/**
|
|
74
103
|
* Run a kind's referrer rules against every resource that references one of its
|
|
75
104
|
* resources.
|
|
@@ -79,16 +108,21 @@ export declare function validateReferrerRuleDeclarations(manifest: ResourceManif
|
|
|
79
108
|
* manifest identity, since a name alone is module-scoped — because the condition
|
|
80
109
|
* reads the two manifests and nothing about the site, so a second site could only
|
|
81
110
|
* produce the identical verdict at a different path.
|
|
111
|
+
*
|
|
112
|
+
* **A rule declaring `peers:` is the exception, and evaluates once per ENTRY.**
|
|
113
|
+
* That is a deliberate departure from judge-once rather than an inconsistency: a
|
|
114
|
+
* rule that binds `entry` is about the entry, so a resource listed twice has two
|
|
115
|
+
* entries to answer for, and the two sites can genuinely disagree.
|
|
82
116
|
*/
|
|
83
117
|
export declare function evaluateReferrerRules(manifest: ResourceManifest, definitionSchema: unknown, referrers: readonly Referrer[],
|
|
84
118
|
/** Whether a referrer of `kind` satisfies a rule's `referrer:` filter —
|
|
85
119
|
* Liskov-substitutable, so a child of the named kind matches. Supplied by the
|
|
86
120
|
* caller, which holds the definition registry. */
|
|
87
|
-
kindMatches: (filter: string, kind: string) => boolean): ReferrerRuleFinding[];
|
|
121
|
+
kindMatches: (filter: string, kind: string) => boolean, context?: ReferrerRuleContext): ReferrerRuleFinding[];
|
|
88
122
|
/** Whether a rule found any referrer to judge — the input to the never-exercised
|
|
89
123
|
* report, which is how a mistyped `referrer:` filter would otherwise disable a
|
|
90
124
|
* check in silence. */
|
|
91
|
-
export declare function referrerRuleExercised(rule: ReferrerRule, referrers: readonly Referrer[], kindMatches: (filter: string, kind: string) => boolean): boolean;
|
|
125
|
+
export declare function referrerRuleExercised(rule: ReferrerRule, referrers: readonly Referrer[], kindMatches: (filter: string, kind: string) => boolean, context?: ReferrerRuleContext): boolean;
|
|
92
126
|
/** Where a referrer-rule finding is reported, and how loudly. Plain data, so the
|
|
93
127
|
* caller pushes it exactly as it does for the resource-rule reports. */
|
|
94
128
|
export interface ReferrerRuleDiagnostic {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-referrer-rules.d.ts","sourceRoot":"","sources":["../src/validate-referrer-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-referrer-rules.d.ts","sourceRoot":"","sources":["../src/validate-referrer-rules.ts"],"names":[],"mappings":"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"}
|