@telorun/analyzer 0.50.0 → 0.52.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.
@@ -11,6 +11,13 @@
11
11
  * only algorithm accepted today; the prefix leaves room to migrate. */
12
12
  const INTEGRITY_FRAGMENT = /#(sha256-[A-Za-z0-9_+/=-]+)$/;
13
13
 
14
+ /** The canonical written form: SHA-256 as unpadded base64url, exactly what
15
+ * {@link sha256Base64Url} emits and what the `layers[].integrity` schema
16
+ * accepts. Stricter than {@link INTEGRITY_FRAGMENT}, which also tolerates the
17
+ * padded / standard-base64 spellings {@link verifyIntegrity} normalizes on the
18
+ * way in — reading is forgiving, writing is not. */
19
+ const CANONICAL_INTEGRITY = /^sha256-[A-Za-z0-9_-]{43}$/;
20
+
14
21
  /** A failed integrity/tamper check — always terminal, never best-effort. A
15
22
  * distinct type so a caller doing best-effort network handling (e.g. the
16
23
  * bundle extractor warning-and-skipping on a fetch blip) can still let a
@@ -41,6 +48,19 @@ export function foldIntegrity(source: string, integrity: unknown): string {
41
48
  : source;
42
49
  }
43
50
 
51
+ /** True for a value safe to WRITE into a manifest as an integrity pin.
52
+ *
53
+ * A pin arriving from outside the file — the hub's version index, a registry
54
+ * response — is untrusted text before it is untrusted *content*: a value
55
+ * carrying a quote, a `#`, or a newline corrupts the YAML it is spliced into,
56
+ * which no later install-time verification can catch because the manifest no
57
+ * longer parses. A wrong-but-well-formed hash is the case install *does*
58
+ * catch; this is the case it cannot. Callers writing a pin they did not
59
+ * compute check here first and fall back to writing none. */
60
+ export function isCanonicalIntegrity(value: unknown): value is string {
61
+ return typeof value === "string" && CANONICAL_INTEGRITY.test(value);
62
+ }
63
+
44
64
  function toBase64Url(bytes: Uint8Array): string {
45
65
  let binary = "";
46
66
  for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
@@ -325,11 +325,16 @@ export function resolveContextAnnotations(
325
325
  : [fromRefKindRaw];
326
326
  if (fromRoot || fromRefKinds.length > 0) {
327
327
  if (fromRoot) {
328
- const resolved = navigatePath(manifestRoot, fromRoot.split("/")) as
328
+ const navigated = navigatePath(manifestRoot, fromRoot.split("/")) as
329
329
  | Record<string, any>
330
330
  | undefined;
331
- if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
332
- return resolved;
331
+ if (navigated && typeof navigated === "object" && !Array.isArray(navigated)) {
332
+ // A `telo#Type` slot resolves to the schema it names — the inline
333
+ // `{ kind, schema }` wrapper, a `!ref` to a named type, or a bare name —
334
+ // so the variable is typed by the CONTRACT rather than by the wrapper
335
+ // around it. A raw JSON Schema resolves to itself, and a plain property
336
+ // map (a transport scope) resolves to nothing and is used verbatim.
337
+ return resolveTypeFieldToSchema(navigated, allManifests ?? []) ?? navigated;
333
338
  }
334
339
  }
335
340
  if (defs) {