@telorun/analyzer 0.44.0 → 0.46.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/alias-resolver.d.ts +45 -0
- package/dist/alias-resolver.d.ts.map +1 -1
- package/dist/alias-resolver.js +33 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +104 -3
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +3 -0
- package/dist/extends-resolution.d.ts +19 -2
- package/dist/extends-resolution.d.ts.map +1 -1
- package/dist/extends-resolution.js +25 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/kernel-globals.d.ts +9 -1
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +24 -1
- package/dist/module-version-order.d.ts +36 -0
- package/dist/module-version-order.d.ts.map +1 -0
- package/dist/module-version-order.js +91 -0
- package/dist/reconcile-module-versions.d.ts.map +1 -1
- package/dist/reconcile-module-versions.js +3 -65
- package/dist/sources/versioned-ref.d.ts +34 -0
- package/dist/sources/versioned-ref.d.ts.map +1 -0
- package/dist/sources/versioned-ref.js +60 -0
- package/dist/validate-observed-state.d.ts +98 -0
- package/dist/validate-observed-state.d.ts.map +1 -0
- package/dist/validate-observed-state.js +304 -0
- package/package.json +2 -2
- package/src/alias-resolver.ts +58 -0
- package/src/analyzer.ts +118 -2
- package/src/builtins.ts +3 -0
- package/src/extends-resolution.ts +37 -3
- package/src/index.ts +23 -0
- package/src/kernel-globals.ts +20 -0
- package/src/module-version-order.ts +91 -0
- package/src/reconcile-module-versions.ts +11 -61
- package/src/sources/versioned-ref.ts +77 -0
- package/src/validate-observed-state.ts +354 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** SemVer precedence for module versions — the single ordering rule shared by
|
|
2
|
+
* version reconciliation and by any host deciding whether an import is behind.
|
|
3
|
+
*
|
|
4
|
+
* Pure and dependency-free (no `semver` package), so the analyzer stays
|
|
5
|
+
* browser-safe and the editor can reach the same rule the kernel-side analysis
|
|
6
|
+
* uses. A caller that cannot parse a version must not guess: an OCI digest, a
|
|
7
|
+
* moving tag like `latest`, and a malformed pin all come back `null` rather
|
|
8
|
+
* than being ordered by some weaker fallback. */
|
|
9
|
+
/** Parse `X.Y.Z`, `vX.Y.Z`, or `X.Y.Z-pre.1`. Returns `null` for anything that
|
|
10
|
+
* isn't a plain three-part numeric core — an unparseable version is never
|
|
11
|
+
* silently ordered. */
|
|
12
|
+
export function parseModuleVersion(raw) {
|
|
13
|
+
if (typeof raw !== "string")
|
|
14
|
+
return null;
|
|
15
|
+
const v = raw.startsWith("v") ? raw.slice(1) : raw;
|
|
16
|
+
const [core, ...preParts] = v.split("-");
|
|
17
|
+
const pre = preParts.length > 0 ? preParts.join("-") : null;
|
|
18
|
+
const segments = core.split(".");
|
|
19
|
+
if (segments.length !== 3)
|
|
20
|
+
return null;
|
|
21
|
+
const [major, minor, patch] = segments.map((s) => {
|
|
22
|
+
if (!/^\d+$/.test(s))
|
|
23
|
+
return NaN;
|
|
24
|
+
return Number(s);
|
|
25
|
+
});
|
|
26
|
+
if ([major, minor, patch].some((n) => Number.isNaN(n)))
|
|
27
|
+
return null;
|
|
28
|
+
return { major, minor, patch, pre: pre === null ? null : pre.split(".") };
|
|
29
|
+
}
|
|
30
|
+
/** SemVer precedence: numeric core, then a release outranks a prerelease, then
|
|
31
|
+
* prerelease identifiers compared field-by-field (numeric < non-numeric per
|
|
32
|
+
* spec, shorter set loses when all shared fields are equal). */
|
|
33
|
+
export function compareParsedModuleVersions(a, b) {
|
|
34
|
+
if (a.major !== b.major)
|
|
35
|
+
return a.major - b.major;
|
|
36
|
+
if (a.minor !== b.minor)
|
|
37
|
+
return a.minor - b.minor;
|
|
38
|
+
if (a.patch !== b.patch)
|
|
39
|
+
return a.patch - b.patch;
|
|
40
|
+
if (a.pre === null && b.pre === null)
|
|
41
|
+
return 0;
|
|
42
|
+
if (a.pre === null)
|
|
43
|
+
return 1;
|
|
44
|
+
if (b.pre === null)
|
|
45
|
+
return -1;
|
|
46
|
+
const len = Math.max(a.pre.length, b.pre.length);
|
|
47
|
+
for (let i = 0; i < len; i++) {
|
|
48
|
+
const ai = a.pre[i];
|
|
49
|
+
const bi = b.pre[i];
|
|
50
|
+
if (ai === undefined)
|
|
51
|
+
return -1;
|
|
52
|
+
if (bi === undefined)
|
|
53
|
+
return 1;
|
|
54
|
+
const an = /^\d+$/.test(ai);
|
|
55
|
+
const bn = /^\d+$/.test(bi);
|
|
56
|
+
if (an && bn) {
|
|
57
|
+
const d = Number(ai) - Number(bi);
|
|
58
|
+
if (d !== 0)
|
|
59
|
+
return d;
|
|
60
|
+
}
|
|
61
|
+
else if (an !== bn) {
|
|
62
|
+
return an ? -1 : 1;
|
|
63
|
+
}
|
|
64
|
+
else if (ai !== bi) {
|
|
65
|
+
return ai < bi ? -1 : 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
/** Negative / zero / positive when both versions parse, `null` when either does
|
|
71
|
+
* not. The string-in convenience over {@link compareParsedModuleVersions}. */
|
|
72
|
+
export function compareModuleVersions(a, b) {
|
|
73
|
+
const left = parseModuleVersion(a);
|
|
74
|
+
const right = parseModuleVersion(b);
|
|
75
|
+
if (!left || !right)
|
|
76
|
+
return null;
|
|
77
|
+
return compareParsedModuleVersions(left, right);
|
|
78
|
+
}
|
|
79
|
+
/** True when `candidate` is strictly newer than `current` — the test for
|
|
80
|
+
* whether an import is behind. False when they are equal, when `current` is
|
|
81
|
+
* ahead (a version index can lag the module's own origin, and "upgrading" to
|
|
82
|
+
* what it knows would be a downgrade), or when either side is unparseable. */
|
|
83
|
+
export function isNewerModuleVersion(candidate, current) {
|
|
84
|
+
return (compareModuleVersions(candidate, current) ?? 0) > 0;
|
|
85
|
+
}
|
|
86
|
+
/** True when two tags name the same version, tolerating a `v` prefix on either
|
|
87
|
+
* side. Falls back to exact equality for unparseable tags, so a digest still
|
|
88
|
+
* matches itself. */
|
|
89
|
+
export function isSameModuleVersion(a, b) {
|
|
90
|
+
return a === b || compareModuleVersions(a, b) === 0;
|
|
91
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOlE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAgJD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAChD,qBAAqB,CAsEvB"}
|
|
@@ -1,69 +1,7 @@
|
|
|
1
1
|
import { isModuleKind } from "./module-kinds.js";
|
|
2
|
+
import { compareParsedModuleVersions, parseModuleVersion, } from "./module-version-order.js";
|
|
2
3
|
import { DiagnosticSeverity } from "./types.js";
|
|
3
4
|
const SOURCE = "telo-analyzer";
|
|
4
|
-
/** Parse `X.Y.Z`, `vX.Y.Z`, or `X.Y.Z-pre.1`. Returns `null` for anything that
|
|
5
|
-
* isn't a plain three-part numeric core — an unparseable version forces the
|
|
6
|
-
* group onto the conflict path (we never silently hoist across a version we
|
|
7
|
-
* can't reason about). Pure: no dependency on the `semver` package, so the
|
|
8
|
-
* analyzer stays browser-safe and dependency-free. */
|
|
9
|
-
function parseVersion(raw) {
|
|
10
|
-
if (typeof raw !== "string")
|
|
11
|
-
return null;
|
|
12
|
-
const v = raw.startsWith("v") ? raw.slice(1) : raw;
|
|
13
|
-
const [core, ...preParts] = v.split("-");
|
|
14
|
-
const pre = preParts.length > 0 ? preParts.join("-") : null;
|
|
15
|
-
const segments = core.split(".");
|
|
16
|
-
if (segments.length !== 3)
|
|
17
|
-
return null;
|
|
18
|
-
const [major, minor, patch] = segments.map((s) => {
|
|
19
|
-
if (!/^\d+$/.test(s))
|
|
20
|
-
return NaN;
|
|
21
|
-
return Number(s);
|
|
22
|
-
});
|
|
23
|
-
if ([major, minor, patch].some((n) => Number.isNaN(n)))
|
|
24
|
-
return null;
|
|
25
|
-
return { major, minor, patch, pre: pre === null ? null : pre.split(".") };
|
|
26
|
-
}
|
|
27
|
-
/** SemVer precedence: numeric core, then a release outranks a prerelease, then
|
|
28
|
-
* prerelease identifiers compared field-by-field (numeric < non-numeric per
|
|
29
|
-
* spec, shorter set loses when all shared fields are equal). */
|
|
30
|
-
function compareVersions(a, b) {
|
|
31
|
-
if (a.major !== b.major)
|
|
32
|
-
return a.major - b.major;
|
|
33
|
-
if (a.minor !== b.minor)
|
|
34
|
-
return a.minor - b.minor;
|
|
35
|
-
if (a.patch !== b.patch)
|
|
36
|
-
return a.patch - b.patch;
|
|
37
|
-
if (a.pre === null && b.pre === null)
|
|
38
|
-
return 0;
|
|
39
|
-
if (a.pre === null)
|
|
40
|
-
return 1;
|
|
41
|
-
if (b.pre === null)
|
|
42
|
-
return -1;
|
|
43
|
-
const len = Math.max(a.pre.length, b.pre.length);
|
|
44
|
-
for (let i = 0; i < len; i++) {
|
|
45
|
-
const ai = a.pre[i];
|
|
46
|
-
const bi = b.pre[i];
|
|
47
|
-
if (ai === undefined)
|
|
48
|
-
return -1;
|
|
49
|
-
if (bi === undefined)
|
|
50
|
-
return 1;
|
|
51
|
-
const an = /^\d+$/.test(ai);
|
|
52
|
-
const bn = /^\d+$/.test(bi);
|
|
53
|
-
if (an && bn) {
|
|
54
|
-
const d = Number(ai) - Number(bi);
|
|
55
|
-
if (d !== 0)
|
|
56
|
-
return d;
|
|
57
|
-
}
|
|
58
|
-
else if (an !== bn) {
|
|
59
|
-
return an ? -1 : 1;
|
|
60
|
-
}
|
|
61
|
-
else if (ai !== bi) {
|
|
62
|
-
return ai < bi ? -1 : 1;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return 0;
|
|
66
|
-
}
|
|
67
5
|
/** The location identity of an import ref: the ref with its version stripped.
|
|
68
6
|
* Two refs share an identity when they address the same module at different
|
|
69
7
|
* versions, whatever transport owns them:
|
|
@@ -115,7 +53,7 @@ function moduleIdentityOf(mod, identity) {
|
|
|
115
53
|
source: mod.owner.source,
|
|
116
54
|
identity,
|
|
117
55
|
version,
|
|
118
|
-
parsed:
|
|
56
|
+
parsed: parseModuleVersion(version),
|
|
119
57
|
text: mod.owner.text,
|
|
120
58
|
};
|
|
121
59
|
}
|
|
@@ -135,7 +73,7 @@ function resolveGroup(members) {
|
|
|
135
73
|
return best;
|
|
136
74
|
if (!best.parsed)
|
|
137
75
|
return cur;
|
|
138
|
-
const cmp =
|
|
76
|
+
const cmp = compareParsedModuleVersions(cur.parsed, best.parsed);
|
|
139
77
|
if (cmp > 0)
|
|
140
78
|
return cur;
|
|
141
79
|
if (cmp === 0 && cur.source < best.source)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** A module ref split into the parts an upgrade needs: the version-independent
|
|
2
|
+
* ref, the version segment it currently names, and any inline pin. */
|
|
3
|
+
export interface ParsedVersionedRef {
|
|
4
|
+
/** The ref with its `@version` segment and integrity fragment removed —
|
|
5
|
+
* `std/run`, `oci://ghcr.io/telorun/timer`. This is the identity a version
|
|
6
|
+
* list is keyed by (the hub registers modules under exactly this form). */
|
|
7
|
+
baseRef: string;
|
|
8
|
+
/** The version segment, raw — a registry `@version`, an OCI tag, or an OCI
|
|
9
|
+
* digest reference (`sha256:…`). The caller applies its own SemVer check. */
|
|
10
|
+
version: string;
|
|
11
|
+
/** Telo's inline `sha256-<base64url>` pin, when the ref carried one. */
|
|
12
|
+
integrity?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Split a versioned module ref. Returns `null` when the ref names no
|
|
15
|
+
* upgradeable version — a local path, a bare `https://` URL, or an OCI ref
|
|
16
|
+
* with no explicit reference (an implicit `latest` is not a pin).
|
|
17
|
+
*
|
|
18
|
+
* Browser-safe and transport-neutral: this is the *grammar* half of an
|
|
19
|
+
* upgrade, shared by the kernel transports (whose `refVersion` / `withVersion`
|
|
20
|
+
* delegate here) and the editor, which cannot use a transport at all — the
|
|
21
|
+
* *network* half (enumerating versions) is scheme-specific and stays behind
|
|
22
|
+
* `Transport.listVersions` on Node and the hub's `/module/versions` in the
|
|
23
|
+
* browser. */
|
|
24
|
+
export declare function parseVersionedRef(ref: string): ParsedVersionedRef | null;
|
|
25
|
+
/** `ref` rewritten to name `version`, dropping any integrity fragment (the
|
|
26
|
+
* caller re-pins the result). Appends the version when the ref carries none —
|
|
27
|
+
* an untagged `oci://host/repo` is still a versionable address.
|
|
28
|
+
*
|
|
29
|
+
* Throws when the ref's grammar has no version segment at all — a relative
|
|
30
|
+
* path, a bare `https://` URL. Producing `../lib@0.4.0` for those would write
|
|
31
|
+
* a ref nothing can resolve, so this fails where the transport-specific
|
|
32
|
+
* parsers it replaced (`parseOciRef` / `parseModuleRef`) also failed. */
|
|
33
|
+
export declare function withRefVersion(ref: string, version: string): string;
|
|
34
|
+
//# sourceMappingURL=versioned-ref.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versioned-ref.d.ts","sourceRoot":"","sources":["../../src/sources/versioned-ref.ts"],"names":[],"mappings":"AAIA;uEACuE;AACvE,MAAM,WAAW,kBAAkB;IACjC;;gFAE4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB;kFAC8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;eASe;AACf,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAOxE;AAED;;;;;;;0EAO0E;AAC1E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAUnE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { splitIntegrity } from "./integrity.js";
|
|
2
|
+
import { isRegistryRef } from "./module-ref.js";
|
|
3
|
+
import { OCI_SCHEME } from "./oci-ref.js";
|
|
4
|
+
/** Split a versioned module ref. Returns `null` when the ref names no
|
|
5
|
+
* upgradeable version — a local path, a bare `https://` URL, or an OCI ref
|
|
6
|
+
* with no explicit reference (an implicit `latest` is not a pin).
|
|
7
|
+
*
|
|
8
|
+
* Browser-safe and transport-neutral: this is the *grammar* half of an
|
|
9
|
+
* upgrade, shared by the kernel transports (whose `refVersion` / `withVersion`
|
|
10
|
+
* delegate here) and the editor, which cannot use a transport at all — the
|
|
11
|
+
* *network* half (enumerating versions) is scheme-specific and stays behind
|
|
12
|
+
* `Transport.listVersions` on Node and the hub's `/module/versions` in the
|
|
13
|
+
* browser. */
|
|
14
|
+
export function parseVersionedRef(ref) {
|
|
15
|
+
const { base, integrity } = splitIntegrity(ref);
|
|
16
|
+
const at = versionSeparator(base);
|
|
17
|
+
if (at === null)
|
|
18
|
+
return null;
|
|
19
|
+
const version = base.slice(at + 1);
|
|
20
|
+
if (!version)
|
|
21
|
+
return null;
|
|
22
|
+
return { baseRef: base.slice(0, at), version, integrity };
|
|
23
|
+
}
|
|
24
|
+
/** `ref` rewritten to name `version`, dropping any integrity fragment (the
|
|
25
|
+
* caller re-pins the result). Appends the version when the ref carries none —
|
|
26
|
+
* an untagged `oci://host/repo` is still a versionable address.
|
|
27
|
+
*
|
|
28
|
+
* Throws when the ref's grammar has no version segment at all — a relative
|
|
29
|
+
* path, a bare `https://` URL. Producing `../lib@0.4.0` for those would write
|
|
30
|
+
* a ref nothing can resolve, so this fails where the transport-specific
|
|
31
|
+
* parsers it replaced (`parseOciRef` / `parseModuleRef`) also failed. */
|
|
32
|
+
export function withRefVersion(ref, version) {
|
|
33
|
+
const { base } = splitIntegrity(ref);
|
|
34
|
+
if (refGrammar(base) === null) {
|
|
35
|
+
throw new Error(`Cannot set a version on '${ref}' — only registry (namespace/name@version) ` +
|
|
36
|
+
`and oci:// refs carry a version segment.`);
|
|
37
|
+
}
|
|
38
|
+
const at = versionSeparator(base);
|
|
39
|
+
return `${at === null ? base : base.slice(0, at)}@${version}`;
|
|
40
|
+
}
|
|
41
|
+
/** Which versionable ref grammar `base` is written in, or `null` when it is
|
|
42
|
+
* neither — a relative/absolute path, a `file:`/`https://` URL. */
|
|
43
|
+
function refGrammar(base) {
|
|
44
|
+
if (base.startsWith(OCI_SCHEME)) {
|
|
45
|
+
// A host alone is not addressable; the repo path is what carries a version.
|
|
46
|
+
return base.indexOf("/", OCI_SCHEME.length) > OCI_SCHEME.length ? "oci" : null;
|
|
47
|
+
}
|
|
48
|
+
// `isRegistryRef` requires the `@`, so a version-less `std/console` is not a
|
|
49
|
+
// registry ref by this test — matching `parseModuleRef`, which throws on it.
|
|
50
|
+
return isRegistryRef(base) ? "registry" : null;
|
|
51
|
+
}
|
|
52
|
+
/** Index of the `@` that separates the version, or `null` when the ref names
|
|
53
|
+
* none. Split on the LAST `@` so a digest reference (`repo@sha256:…`) keeps
|
|
54
|
+
* everything before it as the ref. */
|
|
55
|
+
function versionSeparator(base) {
|
|
56
|
+
if (refGrammar(base) === null)
|
|
57
|
+
return null;
|
|
58
|
+
const at = base.lastIndexOf("@");
|
|
59
|
+
return at > (base.startsWith(OCI_SCHEME) ? OCI_SCHEME.length : 0) ? at : null;
|
|
60
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import { type ModuleScopes } from "./alias-resolver.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `status:` block's own schema — a plain JSON Schema, structurally. The one
|
|
5
|
+
* normative restriction (`required:` is rejected) is enforced by
|
|
6
|
+
* {@link validateObservedStateDeclarations} rather than here, so the author gets
|
|
7
|
+
* a message naming the rule and the fix instead of AJV's "must NOT be valid".
|
|
8
|
+
*
|
|
9
|
+
* Exported from the analyzer and re-used by the kernel's manifest schemas, so
|
|
10
|
+
* the rule has one definition rather than two kept in sync by hand.
|
|
11
|
+
*/
|
|
12
|
+
export declare const OBSERVED_STATE_SCHEMA: {
|
|
13
|
+
type: string;
|
|
14
|
+
additionalProperties: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* `required:` inside a `status:` block. Every declared field is mandatory once
|
|
18
|
+
* the resource has run, so the list would be either redundant or a lie; a
|
|
19
|
+
* genuinely sometimes-absent value is declared with a nullable type, which
|
|
20
|
+
* `CEL_NULLABLE_ACCESS` already guards.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateObservedStateDeclarations(manifests: readonly ResourceManifest[]): Array<{
|
|
23
|
+
kind: string;
|
|
24
|
+
name: string;
|
|
25
|
+
filePath?: string;
|
|
26
|
+
message: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** A CEL access into a resource's observed-state segment. */
|
|
29
|
+
export interface ObservedStateRead {
|
|
30
|
+
/** Import alias, when the read crosses a module boundary
|
|
31
|
+
* (`resources.<Alias>.<name>.status`). */
|
|
32
|
+
alias?: string;
|
|
33
|
+
/** Resource name. */
|
|
34
|
+
name: string;
|
|
35
|
+
/** The field read under `.status`, when the chain names one. */
|
|
36
|
+
field?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Recognise an observed-state read in a member-access chain. Purely syntactic —
|
|
40
|
+
* it inspects the chain, not the topology — so the availability rule it feeds
|
|
41
|
+
* applies to every kind, declared or not.
|
|
42
|
+
*
|
|
43
|
+
* `resources.<name>.status.<field>` and the two-level cross-module form
|
|
44
|
+
* `resources.<Alias>.<name>.status.<field>` are both observed-state reads.
|
|
45
|
+
*/
|
|
46
|
+
export declare function observedStateRead(chain: readonly string[]): ObservedStateRead | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* The names of every resource some slot can start: referenced from a ref slot
|
|
49
|
+
* that accepts a `Telo.Runnable` / `Telo.Service`, or named as a step's
|
|
50
|
+
* `invoke:` target. A resource in none of them can never `run()`, so it can
|
|
51
|
+
* never report observed state.
|
|
52
|
+
*
|
|
53
|
+
* Deliberately an over-approximation — a name reachable through any of these
|
|
54
|
+
* routes counts as runnable — because the cost of a false "can never run" is a
|
|
55
|
+
* valid manifest rejected, while the cost of a miss is only that the reader
|
|
56
|
+
* finds out at runtime instead, with a message that names the same fix.
|
|
57
|
+
*/
|
|
58
|
+
export declare function collectRunReachableNames(manifests: readonly ResourceManifest[], defs: {
|
|
59
|
+
resolve(kind: string): ResourceDefinition | undefined;
|
|
60
|
+
}, aliases?: {
|
|
61
|
+
resolveKind(kind: string): string | undefined;
|
|
62
|
+
}): Set<string>;
|
|
63
|
+
/** What a resource name resolves to for CEL purposes. `status` is present only
|
|
64
|
+
* when the kind declares one; `scoped` marks a resource declared inside an
|
|
65
|
+
* `x-telo-scope` slot, which resolves only within that scope's regions. */
|
|
66
|
+
export interface AnalyzedResource {
|
|
67
|
+
kind: string;
|
|
68
|
+
status?: Record<string, any>;
|
|
69
|
+
scoped?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Index every resource a CEL `resources.…` read can name: the module's own
|
|
73
|
+
* top-level resources, the ones declared inside `x-telo-scope` slots (a
|
|
74
|
+
* `Run.Sequence`'s `with:`), and each import's exported instances — keyed
|
|
75
|
+
* `<Alias>.<name>`, the two-level shape those publish under.
|
|
76
|
+
*
|
|
77
|
+
* Scope slots are found through the declaring kind's schema annotation, not by
|
|
78
|
+
* field name, so any composer with a scope participates.
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildObservedStateIndex(manifests: readonly ResourceManifest[], defs: {
|
|
81
|
+
resolve(kind: string): ResourceDefinition | undefined;
|
|
82
|
+
}, aliases?: {
|
|
83
|
+
resolveKind(kind: string): string | undefined;
|
|
84
|
+
moduleForAlias?(alias: string): string | undefined;
|
|
85
|
+
}, scopes?: ModuleScopes): Map<string, AnalyzedResource>;
|
|
86
|
+
/** The `resources` node of a CEL context schema: one entry per resource, each
|
|
87
|
+
* open except for a typed, closed `status` node on kinds that declare one.
|
|
88
|
+
* `open` keeps the map itself permissive, so unknown resource names and every
|
|
89
|
+
* flat field pass exactly as they do today. */
|
|
90
|
+
export declare function buildObservedStateResourcesSchema(index: ReadonlyMap<string, AnalyzedResource>, open: boolean): Record<string, any>;
|
|
91
|
+
/**
|
|
92
|
+
* Write the typed `status` node for one index key into a `resources` property
|
|
93
|
+
* map. A dotted key (`Alias.name`) is an import's exported instance, which
|
|
94
|
+
* publishes two levels deep — the alias node stays open so every other name
|
|
95
|
+
* under it keeps resolving as it does today.
|
|
96
|
+
*/
|
|
97
|
+
export declare function applyObservedStateNode(properties: Record<string, any>, key: string, status: Record<string, any>): void;
|
|
98
|
+
//# sourceMappingURL=validate-observed-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-observed-state.d.ts","sourceRoot":"","sources":["../src/validate-observed-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAyBjF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB;;;CAGjC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,SAAS,gBAAgB,EAAE,GACrC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAmB3E;AAED,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC;+CAC2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,iBAAiB,GAAG,SAAS,CAOzF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,gBAAgB,EAAE,EACtC,IAAI,EAAE;IAAE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAA;CAAE,EAC/D,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,GAC1D,GAAG,CAAC,MAAM,CAAC,CAyBb;AAgDD;;4EAE4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS,gBAAgB,EAAE,EACtC,IAAI,EAAE;IAAE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAAA;CAAE,EAC/D,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAAC,cAAc,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,EAC/G,MAAM,CAAC,EAAE,YAAY,GACpB,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAsC/B;AAyDD;;;gDAGgD;AAChD,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAC5C,IAAI,EAAE,OAAO,GACZ,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CASrB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC1B,IAAI,CAmBN"}
|