@telorun/ide-support 0.14.1 → 0.16.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 (85) hide show
  1. package/dist/cel/cursor-chain.d.ts +30 -0
  2. package/dist/cel/cursor-chain.d.ts.map +1 -0
  3. package/dist/cel/cursor-chain.js +30 -0
  4. package/dist/cel/symbols.d.ts +82 -0
  5. package/dist/cel/symbols.d.ts.map +1 -0
  6. package/dist/cel/symbols.js +147 -0
  7. package/dist/cel/tokens.d.ts +32 -0
  8. package/dist/cel/tokens.d.ts.map +1 -0
  9. package/dist/cel/tokens.js +162 -0
  10. package/dist/completions/build.d.ts +6 -2
  11. package/dist/completions/build.d.ts.map +1 -1
  12. package/dist/completions/build.js +52 -24
  13. package/dist/completions/call-inputs.d.ts +25 -0
  14. package/dist/completions/call-inputs.d.ts.map +1 -0
  15. package/dist/completions/call-inputs.js +78 -0
  16. package/dist/completions/cel-completions.d.ts +26 -0
  17. package/dist/completions/cel-completions.d.ts.map +1 -0
  18. package/dist/completions/cel-completions.js +78 -0
  19. package/dist/completions/detect-context.d.ts +47 -8
  20. package/dist/completions/detect-context.d.ts.map +1 -1
  21. package/dist/completions/detect-context.js +51 -15
  22. package/dist/completions/prop-keys.d.ts +5 -1
  23. package/dist/completions/prop-keys.d.ts.map +1 -1
  24. package/dist/completions/prop-keys.js +51 -3
  25. package/dist/completions/resolve-node.d.ts +9 -2
  26. package/dist/completions/resolve-node.d.ts.map +1 -1
  27. package/dist/completions/resolve-node.js +63 -21
  28. package/dist/definition/build-definition.d.ts +6 -2
  29. package/dist/definition/build-definition.d.ts.map +1 -1
  30. package/dist/definition/build-definition.js +16 -3
  31. package/dist/definition/locate-context-binding.d.ts +15 -0
  32. package/dist/definition/locate-context-binding.d.ts.map +1 -0
  33. package/dist/definition/locate-context-binding.js +35 -0
  34. package/dist/definition/locate-step.d.ts +13 -0
  35. package/dist/definition/locate-step.d.ts.map +1 -0
  36. package/dist/definition/locate-step.js +33 -0
  37. package/dist/definition/resolve-cel-target.d.ts +11 -1
  38. package/dist/definition/resolve-cel-target.d.ts.map +1 -1
  39. package/dist/definition/resolve-cel-target.js +14 -14
  40. package/dist/doc-identity.d.ts +17 -0
  41. package/dist/doc-identity.d.ts.map +1 -0
  42. package/dist/doc-identity.js +19 -0
  43. package/dist/hover/build-hover.d.ts +6 -2
  44. package/dist/hover/build-hover.d.ts.map +1 -1
  45. package/dist/hover/build-hover.js +64 -3
  46. package/dist/import-upgrades/build-import-upgrades.d.ts +57 -3
  47. package/dist/import-upgrades/build-import-upgrades.d.ts.map +1 -1
  48. package/dist/import-upgrades/build-import-upgrades.js +67 -10
  49. package/dist/import-upgrades/index.d.ts +5 -2
  50. package/dist/import-upgrades/index.d.ts.map +1 -1
  51. package/dist/import-upgrades/index.js +3 -1
  52. package/dist/import-upgrades/manifest-cache-url.d.ts +17 -0
  53. package/dist/import-upgrades/manifest-cache-url.d.ts.map +1 -0
  54. package/dist/import-upgrades/manifest-cache-url.js +30 -0
  55. package/dist/import-upgrades/version-compatibility.d.ts +92 -0
  56. package/dist/import-upgrades/version-compatibility.d.ts.map +1 -0
  57. package/dist/import-upgrades/version-compatibility.js +121 -0
  58. package/dist/semantic-tokens/build-semantic-tokens.d.ts +13 -8
  59. package/dist/semantic-tokens/build-semantic-tokens.d.ts.map +1 -1
  60. package/dist/semantic-tokens/build-semantic-tokens.js +81 -37
  61. package/dist/types.d.ts +25 -5
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js +16 -2
  64. package/package.json +2 -2
  65. package/src/cel/cursor-chain.ts +58 -0
  66. package/src/cel/symbols.ts +189 -0
  67. package/src/cel/tokens.ts +169 -0
  68. package/src/completions/build.ts +85 -22
  69. package/src/completions/call-inputs.ts +92 -0
  70. package/src/completions/cel-completions.ts +108 -0
  71. package/src/completions/detect-context.ts +107 -13
  72. package/src/completions/prop-keys.ts +59 -2
  73. package/src/completions/resolve-node.ts +82 -17
  74. package/src/definition/build-definition.ts +30 -2
  75. package/src/definition/locate-context-binding.ts +53 -0
  76. package/src/definition/locate-step.ts +50 -0
  77. package/src/definition/resolve-cel-target.ts +25 -0
  78. package/src/doc-identity.ts +31 -0
  79. package/src/hover/build-hover.ts +67 -1
  80. package/src/import-upgrades/build-import-upgrades.ts +115 -11
  81. package/src/import-upgrades/index.ts +22 -1
  82. package/src/import-upgrades/manifest-cache-url.ts +40 -0
  83. package/src/import-upgrades/version-compatibility.ts +196 -0
  84. package/src/semantic-tokens/build-semantic-tokens.ts +84 -30
  85. package/src/types.ts +47 -6
@@ -1,4 +1,5 @@
1
1
  import { type AstDocument, type Range } from "@telorun/analyzer";
2
+ import { type IncompatibilityReason, type VersionCompatibilityCheck } from "./version-compatibility.js";
2
3
  /** One version of a module, as the hub reports it.
3
4
  *
4
5
  * `integrity` is the import pin for exactly this version (`sha256-<base64url>`
@@ -23,6 +24,22 @@ export interface ModuleVersion {
23
24
  * `GET /module/versions` and passes the body through
24
25
  * {@link parseModuleVersions}. */
25
26
  export type ModuleVersionLookup = (baseRef: string) => Promise<ModuleVersion[]>;
27
+ /** Everything an upgrade needs from its host: what versions exist, and whether
28
+ * this runtime can host one.
29
+ *
30
+ * `isCompatible` is required rather than optional so no host can quietly skip
31
+ * the check and walk an author onto a version their telo cannot load — the gap
32
+ * this package existed with. A host that genuinely cannot fetch candidate
33
+ * manifests passes {@link uncheckedVersionCompatibility}, which says so
34
+ * instead of pretending. */
35
+ export interface ImportUpgradeEnvironment {
36
+ listVersions: ModuleVersionLookup;
37
+ isCompatible: VersionCompatibilityCheck;
38
+ /** Offer prereleases as automatic upgrade targets. Off by default, matching
39
+ * `telo upgrade`: an upgrade nobody asked for must not walk a caller onto an
40
+ * `-rc` build. */
41
+ includePrerelease?: boolean;
42
+ }
26
43
  /** A source edit a host applies verbatim to upgrade an import. Ranges never
27
44
  * overlap, within an upgrade or across a batch, so a host may apply the whole
28
45
  * set in one pass without ordering them. */
@@ -50,6 +67,14 @@ export interface ImportUpgrade {
50
67
  * means no pin was available for the target version — a host should say so
51
68
  * when `wasPinned`, since the rewrite silently drops a hash the author had. */
52
69
  repinned: boolean;
70
+ /** A newer version that exists but this runtime cannot host, when
71
+ * `latestVersion` is not the newest published. Surfacing it is not optional:
72
+ * an upgrade that quietly stops short of the newest version, with no reason
73
+ * given, reads as a bug in the tooling. */
74
+ heldBack?: {
75
+ version: string;
76
+ reason: IncompatibilityReason;
77
+ };
53
78
  /** Span of the alias key — where a per-entry affordance anchors. */
54
79
  keyRange: Range;
55
80
  /** Apply all of these to upgrade this one import. */
@@ -79,8 +104,18 @@ export interface ImportUpgradeSkip {
79
104
  latestVersion: string;
80
105
  /** Span of the alias key — where a per-entry affordance anchors. */
81
106
  keyRange: Range;
82
- /** Author-facing sentence: what was not done, and what to run instead. */
83
- reason: string;
107
+ /** Which decision this was, for a host that styles or filters them.
108
+ * `incompatible` — every newer version declares a telo this runtime is not;
109
+ * `stale-inline-pin` — the rewrite could not carry or drop the author's pin. */
110
+ code: "incompatible" | "stale-inline-pin";
111
+ /** Which rejection produced an `incompatible` skip, so a host can phrase its
112
+ * own affordance without re-deriving one. Carried BESIDE `code` because the
113
+ * code says what was not done while this says why, and the remedies differ:
114
+ * updating telo cannot fix a requirement the module failed to state. Absent
115
+ * for `stale-inline-pin`, which is not a compatibility decision at all. */
116
+ reason?: IncompatibilityReason;
117
+ /** Author-facing sentence: what was not done, and what to do instead. */
118
+ message: string;
84
119
  }
85
120
  export interface ImportUpgradeSet {
86
121
  /** Span of the `imports:` key — where a summary affordance anchors. */
@@ -102,6 +137,13 @@ export interface ImportUpgradeSet {
102
137
  * re-point it — plus every entry already at the newest version that carries no
103
138
  * integrity pin, and the edits that pin it.
104
139
  *
140
+ * The target is the newest version this runtime can HOST, which is not always
141
+ * the newest one published: each candidate's declared `requires.telo` is checked
142
+ * newest-first, and a newer version that was held back is reported on the
143
+ * upgrade rather than dropped. An import whose every newer version needs a newer
144
+ * telo produces a `skipped` entry instead, since the alternative — offering it
145
+ * anyway — is a manifest that fails at the load gate.
146
+ *
105
147
  * Skips what carries no upgradeable version: local path imports, bare URLs,
106
148
  * untagged refs, and pins that are not SemVer (an OCI digest, a moving tag like
107
149
  * `latest`) — `parseVersionedRef` and `isNewerModuleVersion` both decline to
@@ -111,5 +153,17 @@ export interface ImportUpgradeSet {
111
153
  * API. Returns `undefined` when the file declares no module document or the
112
154
  * module declares no `imports:`.
113
155
  */
114
- export declare function buildImportUpgrades(text: string, listVersions: ModuleVersionLookup, docs?: AstDocument[]): Promise<ImportUpgradeSet | undefined>;
156
+ export declare function buildImportUpgrades(text: string, env: ImportUpgradeEnvironment, docs?: AstDocument[]): Promise<ImportUpgradeSet | undefined>;
157
+ /** How a held-back version reads in a sentence, with the version itself as the
158
+ * implied subject. Names the cause the check actually established.
159
+ *
160
+ * The parameter is REQUIRED, so a caller cannot reach a sentence for a cause
161
+ * nothing established — defaulting an absent reason to "requires a newer telo"
162
+ * is precisely the invented verdict the four-way split exists to prevent. */
163
+ export declare function describeReason(reason: IncompatibilityReason): string;
164
+ /** What the author can do about it. Split from {@link describeReason} because
165
+ * the two rejections have DIFFERENT remedies: an unreadable requirement is not
166
+ * a version skew, so telling the author to update telo would send them after a
167
+ * fix that cannot work — only the module's own author can repair it. */
168
+ export declare function describeRemedy(reason: IncompatibilityReason): string;
115
169
  //# sourceMappingURL=build-import-upgrades.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-import-upgrades.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/build-import-upgrades.ts"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,WAAW,EAChB,KAAK,KAAK,EACX,MAAM,mBAAmB,CAAC;AAG3B;;;;;oEAKoE;AACpE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;mCAYmC;AACnC,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AAEhF;;6CAE6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;6EAKyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB;;oFAEgF;IAChF,QAAQ,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,QAAQ,EAAE,KAAK,CAAC;IAChB,qDAAqD;IACrD,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED;;;kFAGkF;AAClF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,QAAQ,EAAE,KAAK,CAAC;IAChB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED;;;yDAGyD;AACzD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,EAAE,KAAK,CAAC;IAChB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,eAAe,EAAE,KAAK,CAAC;IACvB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B;;wDAEoD;IACpD,QAAQ,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,mBAAmB,EACjC,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAmEvC"}
1
+ {"version":3,"file":"build-import-upgrades.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/build-import-upgrades.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,19 @@
1
- import { buildLineOffsets, foldIntegrity, isCanonicalIntegrity, isLocalPathSource, isNewerModuleVersion, isSameModuleVersion, newestModuleVersion, parseModuleVersion, parseToAst, parseVersionedRef, withRefVersion, } from "@telorun/analyzer";
1
+ import { TELO_SURFACE_VERSION, buildLineOffsets, foldIntegrity, isCanonicalIntegrity, isLocalPathSource, isNewerModuleVersion, isSameModuleVersion, newestModuleVersion, parseModuleVersion, parseToAst, parseVersionedRef, withRefVersion, } from "@telorun/analyzer";
2
2
  import { findImportEntries } from "./find-import-entries.js";
3
+ import { selectCompatibleVersion, } from "./version-compatibility.js";
3
4
  /**
4
5
  * Find every `imports:` entry of a module document that names a version older
5
6
  * than the newest one `listVersions` reports, and produce the source edits that
6
7
  * re-point it — plus every entry already at the newest version that carries no
7
8
  * integrity pin, and the edits that pin it.
8
9
  *
10
+ * The target is the newest version this runtime can HOST, which is not always
11
+ * the newest one published: each candidate's declared `requires.telo` is checked
12
+ * newest-first, and a newer version that was held back is reported on the
13
+ * upgrade rather than dropped. An import whose every newer version needs a newer
14
+ * telo produces a `skipped` entry instead, since the alternative — offering it
15
+ * anyway — is a manifest that fails at the load gate.
16
+ *
9
17
  * Skips what carries no upgradeable version: local path imports, bare URLs,
10
18
  * untagged refs, and pins that are not SemVer (an OCI digest, a moving tag like
11
19
  * `latest`) — `parseVersionedRef` and `isNewerModuleVersion` both decline to
@@ -15,7 +23,7 @@ import { findImportEntries } from "./find-import-entries.js";
15
23
  * API. Returns `undefined` when the file declares no module document or the
16
24
  * module declares no `imports:`.
17
25
  */
18
- export async function buildImportUpgrades(text, listVersions, docs) {
26
+ export async function buildImportUpgrades(text, env, docs) {
19
27
  const lineOffsets = buildLineOffsets(text);
20
28
  const block = findImportEntries(text, docs ?? parseToAst(text), lineOffsets);
21
29
  if (!block)
@@ -27,7 +35,7 @@ export async function buildImportUpgrades(text, listVersions, docs) {
27
35
  return ref ? [{ entry, ref }] : [];
28
36
  });
29
37
  const failures = [];
30
- const known = await resolveVersions([...new Set(candidates.map((c) => c.ref.baseRef))], listVersions, failures);
38
+ const known = await resolveVersions([...new Set(candidates.map((c) => c.ref.baseRef))], env.listVersions, failures);
31
39
  const upgrades = [];
32
40
  const pins = [];
33
41
  const skipped = [];
@@ -35,7 +43,9 @@ export async function buildImportUpgrades(text, listVersions, docs) {
35
43
  const versions = known.get(ref.baseRef);
36
44
  if (!versions)
37
45
  continue;
38
- const newest = newestModuleVersion(versions.map((v) => v.version));
46
+ const newest = newestModuleVersion(versions.map((v) => v.version), {
47
+ includePrerelease: env.includePrerelease,
48
+ });
39
49
  if (!newest)
40
50
  continue;
41
51
  if (!isNewerModuleVersion(newest, ref.version)) {
@@ -44,7 +54,32 @@ export async function buildImportUpgrades(text, listVersions, docs) {
44
54
  pins.push(pin);
45
55
  continue;
46
56
  }
47
- const integrity = integrityFor(versions, newest);
57
+ // The target is the newest version this runtime can actually host, not
58
+ // simply the newest one published. An upgrade the author's telo cannot load
59
+ // is not an upgrade — it is a load failure they were walked into.
60
+ const { best, heldBack } = await selectCompatibleVersion(ref.baseRef, versions, ref.version, env.isCompatible, { includePrerelease: env.includePrerelease });
61
+ // `heldBack` is non-null whenever a candidate existed, which `newest` being
62
+ // newer than the current version already established. Narrowed rather than
63
+ // defaulted: a reason nothing established is exactly what the four-verdict
64
+ // split exists to keep out of a message.
65
+ if (!best) {
66
+ if (!heldBack)
67
+ continue;
68
+ skipped.push({
69
+ alias: entry.alias,
70
+ currentVersion: ref.version,
71
+ latestVersion: newest,
72
+ keyRange: entry.keyRange,
73
+ code: "incompatible",
74
+ reason: heldBack.reason,
75
+ message: `'${entry.alias}' has newer versions (up to ${newest}), but none runs on telo ` +
76
+ `${TELO_SURFACE_VERSION} — ${describeReason(heldBack.reason)}. ` +
77
+ describeRemedy(heldBack.reason),
78
+ });
79
+ continue;
80
+ }
81
+ const target = best.version;
82
+ const integrity = integrityFor(versions, target);
48
83
  // A stale pin that can be neither replaced nor removed is the one case left
49
84
  // that has to be declined: re-pointing the source while leaving a hash for
50
85
  // the version it replaced turns the next install into a tamper error.
@@ -52,29 +87,51 @@ export async function buildImportUpgrades(text, listVersions, docs) {
52
87
  skipped.push({
53
88
  alias: entry.alias,
54
89
  currentVersion: ref.version,
55
- latestVersion: newest,
90
+ latestVersion: target,
56
91
  keyRange: entry.keyRange,
57
- reason: `'${entry.alias}' carries an inline 'integrity:' that shares a line with other ` +
58
- `fields, and no pin is published for ${newest}, so the stale one cannot be ` +
92
+ code: "stale-inline-pin",
93
+ message: `'${entry.alias}' carries an inline 'integrity:' that shares a line with other ` +
94
+ `fields, and no pin is published for ${target}, so the stale one cannot be ` +
59
95
  `removed by a line edit. Run \`telo upgrade\`.`,
60
96
  });
61
97
  continue;
62
98
  }
63
- const newSource = withRefVersion(entry.source, newest);
99
+ const newSource = withRefVersion(entry.source, target);
64
100
  upgrades.push({
65
101
  alias: entry.alias,
66
102
  source: entry.source,
67
103
  currentVersion: ref.version,
68
- latestVersion: newest,
104
+ latestVersion: target,
69
105
  newSource: foldIntegrity(newSource, integrity),
70
106
  wasPinned: ref.integrity != null,
71
107
  repinned: integrity != null,
72
108
  keyRange: entry.keyRange,
109
+ ...(heldBack ? { heldBack } : {}),
73
110
  edits: buildEdits(entry, newSource, integrity),
74
111
  });
75
112
  }
76
113
  return { importsKeyRange: block.keyRange, upgrades, pins, skipped, failures };
77
114
  }
115
+ /** How a held-back version reads in a sentence, with the version itself as the
116
+ * implied subject. Names the cause the check actually established.
117
+ *
118
+ * The parameter is REQUIRED, so a caller cannot reach a sentence for a cause
119
+ * nothing established — defaulting an absent reason to "requires a newer telo"
120
+ * is precisely the invented verdict the four-way split exists to prevent. */
121
+ export function describeReason(reason) {
122
+ return reason === "unreadable"
123
+ ? "its declared requirement cannot be read"
124
+ : "it requires a newer telo";
125
+ }
126
+ /** What the author can do about it. Split from {@link describeReason} because
127
+ * the two rejections have DIFFERENT remedies: an unreadable requirement is not
128
+ * a version skew, so telling the author to update telo would send them after a
129
+ * fix that cannot work — only the module's own author can repair it. */
130
+ export function describeRemedy(reason) {
131
+ return reason === "unreadable"
132
+ ? "Only the module's author can fix that."
133
+ : "Update telo to upgrade it.";
134
+ }
78
135
  /** The edits that re-point an entry at `newSource` and settle its pin.
79
136
  *
80
137
  * Where the pin is written follows the shape the author chose: an entry with an
@@ -1,6 +1,9 @@
1
- export { buildImportUpgrades } from "./build-import-upgrades.js";
2
- export type { ImportPin, ImportUpgrade, ImportUpgradeEdit, ImportUpgradeSet, ImportUpgradeSkip, ModuleVersion, ModuleVersionLookup, } from "./build-import-upgrades.js";
1
+ export { buildImportUpgrades, describeReason, describeRemedy, } from "./build-import-upgrades.js";
2
+ export type { ImportPin, ImportUpgrade, ImportUpgradeEdit, ImportUpgradeEnvironment, ImportUpgradeSet, ImportUpgradeSkip, ModuleVersion, ModuleVersionLookup, } from "./build-import-upgrades.js";
3
+ export { moduleManifestCacheUrl } from "./manifest-cache-url.js";
3
4
  export { parseModuleVersions } from "./parse-module-versions.js";
4
5
  export { findImportEntries } from "./find-import-entries.js";
5
6
  export type { ImportEntry, ImportEntryIntegrity, ImportsBlock, } from "./find-import-entries.js";
7
+ export { createVersionCompatibility, markVersionCompatibility, noneRunnableReason, selectCompatibleVersion, uncheckedVersionCompatibility, } from "./version-compatibility.js";
8
+ export type { IncompatibilityReason, MarkedVersion, ModuleCompatibility, ModuleManifestReader, VersionCompatibilityCheck, VersionSelection, } from "./version-compatibility.js";
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EACV,WAAW,EACX,oBAAoB,EACpB,YAAY,GACb,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,cAAc,GACf,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EACV,WAAW,EACX,oBAAoB,EACpB,YAAY,GACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC"}
@@ -1,3 +1,5 @@
1
- export { buildImportUpgrades } from "./build-import-upgrades.js";
1
+ export { buildImportUpgrades, describeReason, describeRemedy, } from "./build-import-upgrades.js";
2
+ export { moduleManifestCacheUrl } from "./manifest-cache-url.js";
2
3
  export { parseModuleVersions } from "./parse-module-versions.js";
3
4
  export { findImportEntries } from "./find-import-entries.js";
5
+ export { createVersionCompatibility, markVersionCompatibility, noneRunnableReason, selectCompatibleVersion, uncheckedVersionCompatibility, } from "./version-compatibility.js";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Where one version of a module's `telo.yaml` can be read from the hub's static
3
+ * manifest cache, or `null` when this ref is not addressable there.
4
+ *
5
+ * This is the read path an upgrade check uses to ask a candidate version what
6
+ * runtime it requires: a plain CORS GET of a single small file, no transport
7
+ * protocol and no artifact payload, so it works identically from a browser and
8
+ * from an extension host. The `fetch` itself stays with the host — everything
9
+ * in this package leaves the network to its caller.
10
+ *
11
+ * A ref the cache cannot address (a registry ref, whose origin this side does
12
+ * not know; a URL with no version segment) returns `null`, which the caller
13
+ * reads as "not known" and never as "incompatible". Losing the check for those
14
+ * refs is the honest outcome: the manifest is still gated at load time.
15
+ */
16
+ export declare function moduleManifestCacheUrl(baseRef: string, version: string, baseUrl?: string): string | null;
17
+ //# sourceMappingURL=manifest-cache-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-cache-url.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/manifest-cache-url.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAgC,GACxC,MAAM,GAAG,IAAI,CAYf"}
@@ -0,0 +1,30 @@
1
+ import { MANIFEST_CACHE_BASE_URL, isOciRef, manifestCacheUrl, urlManifestCacheCoords, withRefVersion, } from "@telorun/analyzer";
2
+ /**
3
+ * Where one version of a module's `telo.yaml` can be read from the hub's static
4
+ * manifest cache, or `null` when this ref is not addressable there.
5
+ *
6
+ * This is the read path an upgrade check uses to ask a candidate version what
7
+ * runtime it requires: a plain CORS GET of a single small file, no transport
8
+ * protocol and no artifact payload, so it works identically from a browser and
9
+ * from an extension host. The `fetch` itself stays with the host — everything
10
+ * in this package leaves the network to its caller.
11
+ *
12
+ * A ref the cache cannot address (a registry ref, whose origin this side does
13
+ * not know; a URL with no version segment) returns `null`, which the caller
14
+ * reads as "not known" and never as "incompatible". Losing the check for those
15
+ * refs is the honest outcome: the manifest is still gated at load time.
16
+ */
17
+ export function moduleManifestCacheUrl(baseRef, version, baseUrl = MANIFEST_CACHE_BASE_URL) {
18
+ if (isOciRef(baseRef)) {
19
+ let versioned;
20
+ try {
21
+ versioned = withRefVersion(baseRef, version);
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ return manifestCacheUrl(versioned, baseUrl);
27
+ }
28
+ const coords = urlManifestCacheCoords(baseRef, version);
29
+ return coords ? manifestCacheUrl(coords, baseUrl) : null;
30
+ }
@@ -0,0 +1,92 @@
1
+ import { type ModuleCompatibility } from "@telorun/analyzer";
2
+ import type { ModuleVersion } from "./build-import-upgrades.js";
3
+ export type { ModuleCompatibility };
4
+ /**
5
+ * Host capability: the `telo.yaml` text of one version of a module, or `null`
6
+ * when this host cannot address it (a transport it does not speak, a ref with
7
+ * no version segment). Rejecting is equally fine — both read as "not known",
8
+ * never as "incompatible".
9
+ *
10
+ * Narrow on purpose, and separate from {@link ModuleVersionLookup}: enumerating
11
+ * versions and reading one manifest are different routes with different costs,
12
+ * and a host caches or throttles them differently.
13
+ */
14
+ export type ModuleManifestReader = (baseRef: string, version: string) => Promise<string | null | undefined>;
15
+ /** The question an upgrade asks of a candidate version. */
16
+ export type VersionCompatibilityCheck = (baseRef: string, version: string) => Promise<ModuleCompatibility>;
17
+ /**
18
+ * A compatibility check backed by `read`, memoized per module and version for
19
+ * the lifetime of the returned function.
20
+ *
21
+ * The cache is not an optimization detail — an IDE re-derives its upgrade
22
+ * affordances on every keystroke, and a published version's declared
23
+ * requirement is immutable, so refetching it would be pure waste against an
24
+ * answer that cannot have changed. In-flight promises are shared, so the
25
+ * concurrent lookups one file's imports produce collapse to one read each.
26
+ *
27
+ * The verdict is always the analyzer's {@link manifestCompatibility}: an IDE
28
+ * reports NO host versions, because it is not the machine that will run the
29
+ * manifest. Only the telo surface is checked here; a host requirement still
30
+ * surfaces at the load gate when the manifest is actually run.
31
+ */
32
+ export declare function createVersionCompatibility(read: ModuleManifestReader, teloVersion?: string): VersionCompatibilityCheck;
33
+ /** A check that answers `unknown` for everything — the honest reading for a
34
+ * host with no way to fetch a candidate manifest. Never blocks an upgrade. */
35
+ export declare const uncheckedVersionCompatibility: VersionCompatibilityCheck;
36
+ /** Why a candidate was not offered. `unknown` never appears: a version that
37
+ * could not be read is offered, since an unreachable source must not silently
38
+ * freeze an author's imports. */
39
+ export type IncompatibilityReason = Exclude<ModuleCompatibility, "yes" | "unknown">;
40
+ export interface VersionSelection {
41
+ /** The newest version this runtime can host that is also newer than the
42
+ * current one, or `null` when there is none. */
43
+ best: ModuleVersion | null;
44
+ /** The newest candidate overall, when it is NOT `best` — what was held back.
45
+ * Reported rather than swallowed: without it an upgrade affordance says
46
+ * "up to date" while newer versions exist, which is a silent ceiling and a
47
+ * worse report than the load failure this whole check replaces. */
48
+ heldBack: {
49
+ version: string;
50
+ reason: IncompatibilityReason;
51
+ } | null;
52
+ }
53
+ /**
54
+ * The newest version whose declared requirement accepts this runtime, among
55
+ * those newer than `currentVersion`.
56
+ *
57
+ * Walks newest-first and stops at the first satisfied candidate, so the common
58
+ * case — the newest version is compatible — costs a single manifest read.
59
+ * Candidates older than the current version are never considered: an upgrade
60
+ * that walks backwards is a downgrade nobody asked for. Pass `null` for an
61
+ * import that does not exist yet, where every published version is a candidate
62
+ * and there is nothing to walk backwards from.
63
+ */
64
+ export declare function selectCompatibleVersion(baseRef: string, versions: readonly ModuleVersion[], currentVersion: string | null, isCompatible: VersionCompatibilityCheck, options?: {
65
+ includePrerelease?: boolean;
66
+ }): Promise<VersionSelection>;
67
+ /** One version as a picker renders it: what it is, and what this runtime makes
68
+ * of it. */
69
+ export interface MarkedVersion extends ModuleVersion {
70
+ compatibility: ModuleCompatibility;
71
+ }
72
+ /**
73
+ * Every version, each carrying its own verdict — what a deliberate pick needs.
74
+ *
75
+ * Unlike {@link selectCompatibleVersion} this cannot short-circuit: a picker
76
+ * marks entries it is not going to choose, so every entry has to be asked. That
77
+ * is why the two are separate operations rather than one with a flag — the
78
+ * automatic path must stay one read, and the picker is an explicit action on a
79
+ * single import whose reads are cached from then on.
80
+ */
81
+ export declare function markVersionCompatibility(baseRef: string, versions: readonly ModuleVersion[], isCompatible: VersionCompatibilityCheck): Promise<MarkedVersion[]>;
82
+ /**
83
+ * Why nothing in a marked list can be offered, or `null` when something can.
84
+ *
85
+ * A picker has to state this outright: a list where every row is marked
86
+ * explains nothing on its own, and the reader is left thinking the tool is
87
+ * broken. A reason rather than a boolean because the two rejections have
88
+ * different remedies — see {@link describeRemedy}. `unknown` counts as
89
+ * offerable: a version this host could not check is not one it may refuse.
90
+ */
91
+ export declare function noneRunnableReason(versions: readonly MarkedVersion[]): IncompatibilityReason | null;
92
+ //# sourceMappingURL=version-compatibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-compatibility.d.ts","sourceRoot":"","sources":["../../src/import-upgrades/version-compatibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEhE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExC,2DAA2D;AAC3D,MAAM,MAAM,yBAAyB,GAAG,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,oBAAoB,EAC1B,WAAW,GAAE,MAA6B,GACzC,yBAAyB,CAqB3B;AAED;+EAC+E;AAC/E,eAAO,MAAM,6BAA6B,EAAE,yBAAiD,CAAC;AAE9F;;kCAEkC;AAClC,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;AAEpF,MAAM,WAAW,gBAAgB;IAC/B;qDACiD;IACjD,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IAC3B;;;wEAGoE;IACpE,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,qBAAqB,CAAA;KAAE,GAAG,IAAI,CAAC;CACrE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,YAAY,EAAE,yBAAyB,EACvC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC5C,OAAO,CAAC,gBAAgB,CAAC,CAgB3B;AAED;aACa;AACb,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,aAAa,EAAE,mBAAmB,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,YAAY,EAAE,yBAAyB,GACtC,OAAO,CAAC,aAAa,EAAE,CAAC,CAO1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,aAAa,EAAE,GACjC,qBAAqB,GAAG,IAAI,CAQ9B"}
@@ -0,0 +1,121 @@
1
+ import { TELO_SURFACE_VERSION, compareModuleVersions, isNewerModuleVersion, manifestCompatibility, parseModuleVersion, } from "@telorun/analyzer";
2
+ /**
3
+ * A compatibility check backed by `read`, memoized per module and version for
4
+ * the lifetime of the returned function.
5
+ *
6
+ * The cache is not an optimization detail — an IDE re-derives its upgrade
7
+ * affordances on every keystroke, and a published version's declared
8
+ * requirement is immutable, so refetching it would be pure waste against an
9
+ * answer that cannot have changed. In-flight promises are shared, so the
10
+ * concurrent lookups one file's imports produce collapse to one read each.
11
+ *
12
+ * The verdict is always the analyzer's {@link manifestCompatibility}: an IDE
13
+ * reports NO host versions, because it is not the machine that will run the
14
+ * manifest. Only the telo surface is checked here; a host requirement still
15
+ * surfaces at the load gate when the manifest is actually run.
16
+ */
17
+ export function createVersionCompatibility(read, teloVersion = TELO_SURFACE_VERSION) {
18
+ const cache = new Map();
19
+ return (baseRef, version) => {
20
+ const key = `${baseRef}@${version}`;
21
+ const cached = cache.get(key);
22
+ if (cached)
23
+ return cached;
24
+ const pending = read(baseRef, version)
25
+ .then((text) => (text ? manifestCompatibility(text, teloVersion) : "unknown"))
26
+ .catch(() => "unknown");
27
+ cache.set(key, pending);
28
+ // Only a DECIDED verdict is kept. What is immutable is a published version's
29
+ // declared requirement, not the network: caching `unknown` would let one
30
+ // offline moment disable the check for the rest of the session, leaving the
31
+ // UI reading as though every version had been checked and cleared. Dropped
32
+ // after it settles rather than before, so concurrent asks still share the
33
+ // one in-flight read.
34
+ void pending.then((verdict) => {
35
+ if (verdict === "unknown" && cache.get(key) === pending)
36
+ cache.delete(key);
37
+ });
38
+ return pending;
39
+ };
40
+ }
41
+ /** A check that answers `unknown` for everything — the honest reading for a
42
+ * host with no way to fetch a candidate manifest. Never blocks an upgrade. */
43
+ export const uncheckedVersionCompatibility = async () => "unknown";
44
+ /**
45
+ * The newest version whose declared requirement accepts this runtime, among
46
+ * those newer than `currentVersion`.
47
+ *
48
+ * Walks newest-first and stops at the first satisfied candidate, so the common
49
+ * case — the newest version is compatible — costs a single manifest read.
50
+ * Candidates older than the current version are never considered: an upgrade
51
+ * that walks backwards is a downgrade nobody asked for. Pass `null` for an
52
+ * import that does not exist yet, where every published version is a candidate
53
+ * and there is nothing to walk backwards from.
54
+ */
55
+ export async function selectCompatibleVersion(baseRef, versions, currentVersion, isCompatible, options = {}) {
56
+ const candidates = upgradeCandidates(versions, currentVersion, options);
57
+ if (candidates.length === 0)
58
+ return { best: null, heldBack: null };
59
+ const newest = candidates[0];
60
+ let firstReason = null;
61
+ for (const candidate of candidates) {
62
+ const verdict = await isCompatible(baseRef, candidate.version);
63
+ if (verdict === "too-new" || verdict === "unreadable") {
64
+ firstReason ??= verdict;
65
+ continue;
66
+ }
67
+ const held = candidate === newest ? null : { version: newest.version, reason: firstReason };
68
+ return { best: candidate, heldBack: held };
69
+ }
70
+ return { best: null, heldBack: { version: newest.version, reason: firstReason } };
71
+ }
72
+ /**
73
+ * Every version, each carrying its own verdict — what a deliberate pick needs.
74
+ *
75
+ * Unlike {@link selectCompatibleVersion} this cannot short-circuit: a picker
76
+ * marks entries it is not going to choose, so every entry has to be asked. That
77
+ * is why the two are separate operations rather than one with a flag — the
78
+ * automatic path must stay one read, and the picker is an explicit action on a
79
+ * single import whose reads are cached from then on.
80
+ */
81
+ export async function markVersionCompatibility(baseRef, versions, isCompatible) {
82
+ return Promise.all(versions.map(async (version) => ({
83
+ ...version,
84
+ compatibility: await isCompatible(baseRef, version.version),
85
+ })));
86
+ }
87
+ /**
88
+ * Why nothing in a marked list can be offered, or `null` when something can.
89
+ *
90
+ * A picker has to state this outright: a list where every row is marked
91
+ * explains nothing on its own, and the reader is left thinking the tool is
92
+ * broken. A reason rather than a boolean because the two rejections have
93
+ * different remedies — see {@link describeRemedy}. `unknown` counts as
94
+ * offerable: a version this host could not check is not one it may refuse.
95
+ */
96
+ export function noneRunnableReason(versions) {
97
+ if (versions.length === 0)
98
+ return null;
99
+ if (versions.some((v) => v.compatibility === "yes" || v.compatibility === "unknown")) {
100
+ return null;
101
+ }
102
+ // The newest blocked version's reason, in the list's own (newest-first)
103
+ // order: it is the one the reader is asking about.
104
+ return versions[0].compatibility;
105
+ }
106
+ /** Candidates strictly newer than `currentVersion`, newest first.
107
+ *
108
+ * Ordering and prerelease exclusion come from the analyzer's shared rule, so
109
+ * "is this import behind" and "what would it move to" cannot disagree. */
110
+ function upgradeCandidates(versions, currentVersion, options) {
111
+ return versions
112
+ .filter((v) => {
113
+ const parsed = parseModuleVersion(v.version);
114
+ if (parsed === null)
115
+ return false;
116
+ if (parsed.pre !== null && !options.includePrerelease)
117
+ return false;
118
+ return currentVersion === null || isNewerModuleVersion(v.version, currentVersion);
119
+ })
120
+ .sort((a, b) => compareModuleVersions(b.version, a.version) ?? 0);
121
+ }
@@ -1,12 +1,17 @@
1
- import { type AnalysisRegistry, type AstDocument } from "@telorun/analyzer";
1
+ import { type AnalysisRegistry, type AstDocument, type ManifestAnalysis } from "@telorun/analyzer";
2
2
  import type { SemanticToken } from "../types.js";
3
3
  /** Registry-aware semantic tokens: a `kind:` value that resolves to a known
4
4
  * definition is a `type`; a `capability:` value is an `interface`; a `!ref`
5
- * target is a `variable`. Everything else (structure, CEL, tags) is left to the
6
- * TextMate grammar. Ref targets are colored here rather than in the grammar
7
- * because a `!ref` after a `key:` is tokenized by the bundled YAML grammar
8
- * before a Telo pattern can claim it the AST sees it unambiguously. An
9
- * unresolved kind gets no token, so a typo stays uncolored — a quiet signal
10
- * that pairs with the analyzer's `UNDEFINED_KIND` diagnostic. */
11
- export declare function buildSemanticTokens(text: string, registry: AnalysisRegistry | undefined, docs?: AstDocument[]): SemanticToken[];
5
+ * target is a `variable`. Ref targets are colored here rather than in the
6
+ * grammar because a `!ref` after a `key:` is tokenized by the bundled YAML
7
+ * grammar before a Telo pattern can claim it the AST sees it unambiguously.
8
+ * An unresolved kind gets no token, so a typo stays uncolored a quiet signal
9
+ * that pairs with the analyzer's `UNDEFINED_KIND` diagnostic.
10
+ *
11
+ * The inside of a `!cel` / `${{ }}` body is colored here too, and for the same
12
+ * reason one level down: a grammar can only know the roots someone hardcoded
13
+ * into it, while `scopeQuery` knows what is in scope at this exact site. With
14
+ * no query the names are colored syntactically instead — a CEL body must never
15
+ * read as a plain string, which is what the stock YAML grammar makes of it. */
16
+ export declare function buildSemanticTokens(text: string, registry: AnalysisRegistry | undefined, docs?: AstDocument[], analysis?: ManifestAnalysis): SemanticToken[];
12
17
  //# sourceMappingURL=build-semantic-tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-semantic-tokens.d.ts","sourceRoot":"","sources":["../../src/semantic-tokens/build-semantic-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD;;;;;;;kEAOkE;AAClE,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,aAAa,EAAE,CAqCjB"}
1
+ {"version":3,"file":"build-semantic-tokens.d.ts","sourceRoot":"","sources":["../../src/semantic-tokens/build-semantic-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAIhB,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAajD;;;;;;;;;;;;gFAYgF;AAChF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,IAAI,CAAC,EAAE,WAAW,EAAE,EACpB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,aAAa,EAAE,CA2EjB"}