norn-cli 2.3.0 → 2.4.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 (92) hide show
  1. package/.claude/skills/norn-social-campaign/SKILL.md +70 -0
  2. package/CHANGELOG.md +6 -0
  3. package/demos/nornenv-region-refactor/README.md +64 -0
  4. package/dist/cli.js +360 -1
  5. package/out/apiResponseIntellisenseCache.js +394 -0
  6. package/out/assertionRunner.js +567 -0
  7. package/out/cacheDir.js +136 -0
  8. package/out/chatParticipant.js +763 -0
  9. package/out/cli/colors.js +127 -0
  10. package/out/cli/formatters/assertion.js +102 -0
  11. package/out/cli/formatters/index.js +23 -0
  12. package/out/cli/formatters/response.js +106 -0
  13. package/out/cli/formatters/summary.js +246 -0
  14. package/out/cli/redaction.js +237 -0
  15. package/out/cli/reporters/html.js +689 -0
  16. package/out/cli/reporters/index.js +22 -0
  17. package/out/cli/reporters/junit.js +226 -0
  18. package/out/codeLensProvider.js +351 -0
  19. package/out/compareContentProvider.js +85 -0
  20. package/out/completionProvider.js +3739 -0
  21. package/out/contractAssertionSummary.js +225 -0
  22. package/out/contractDecorationProvider.js +243 -0
  23. package/out/coverageCalculator.js +879 -0
  24. package/out/coveragePanel.js +597 -0
  25. package/out/debug/breakpointResolver.js +84 -0
  26. package/out/debug/breakpoints.js +52 -0
  27. package/out/debug/nornDebugAdapter.js +166 -0
  28. package/out/debug/nornDebugSession.js +613 -0
  29. package/out/debug/sequenceLocationIndex.js +77 -0
  30. package/out/debug/types.js +3 -0
  31. package/out/deepClone.js +21 -0
  32. package/out/diagnosticProvider.js +2554 -0
  33. package/out/environmentParser.js +736 -0
  34. package/out/environmentProvider.js +544 -0
  35. package/out/environmentTemplates.js +146 -0
  36. package/out/errors/formatError.js +113 -0
  37. package/out/errors/nornError.js +29 -0
  38. package/out/formUrlEncoded.js +89 -0
  39. package/out/httpClient.js +348 -0
  40. package/out/httpRuntimeOptions.js +16 -0
  41. package/out/importErrors.js +31 -0
  42. package/out/inlayHintResolver.js +70 -0
  43. package/out/jsonFileReader.js +323 -0
  44. package/out/mcpClient.js +193 -0
  45. package/out/mcpConfig.js +184 -0
  46. package/out/mcpToolIntellisenseCache.js +96 -0
  47. package/out/mcpToolSchema.js +50 -0
  48. package/out/nornConfig.js +132 -0
  49. package/out/nornHoverProvider.js +124 -0
  50. package/out/nornInlayHintsProvider.js +191 -0
  51. package/out/nornPrompt.js +755 -0
  52. package/out/nornSqlParser.js +286 -0
  53. package/out/nornapiHoverProvider.js +135 -0
  54. package/out/nornapiInlayHintsProvider.js +94 -0
  55. package/out/nornapiParser.js +324 -0
  56. package/out/nornenvCodeActionProvider.js +101 -0
  57. package/out/nornenvDecorationProvider.js +239 -0
  58. package/out/nornenvFoldingProvider.js +63 -0
  59. package/out/nornenvHoverProvider.js +114 -0
  60. package/out/nornenvInlayHintsProvider.js +99 -0
  61. package/out/nornenvLanguageModel.js +187 -0
  62. package/out/nornenvRegionRefactor.js +267 -0
  63. package/out/nornsqlHoverProvider.js +95 -0
  64. package/out/nornsqlInlayHintsProvider.js +114 -0
  65. package/out/parser.js +839 -0
  66. package/out/pathAccess.js +28 -0
  67. package/out/postmanImportPanel.js +732 -0
  68. package/out/postmanImportPlanner.js +1155 -0
  69. package/out/postmanImportSidebarView.js +532 -0
  70. package/out/quotedString.js +35 -0
  71. package/out/requestPreparation.js +179 -0
  72. package/out/requestValidation.js +146 -0
  73. package/out/responsePanel.js +7754 -0
  74. package/out/schemaGenerator.js +562 -0
  75. package/out/scriptRunner.js +419 -0
  76. package/out/secrets/cliSecrets.js +415 -0
  77. package/out/secrets/crypto.js +105 -0
  78. package/out/secrets/envFileSecrets.js +177 -0
  79. package/out/secrets/keyStore.js +259 -0
  80. package/out/sequenceDeclaration.js +15 -0
  81. package/out/sequenceRunner.js +3590 -0
  82. package/out/sqlAdapterRunner.js +122 -0
  83. package/out/sqlBuiltInAdapters.js +604 -0
  84. package/out/sqlConfig.js +184 -0
  85. package/out/starterCatalog.js +554 -0
  86. package/out/stringUtils.js +25 -0
  87. package/out/swaggerBodyIntellisenseCache.js +114 -0
  88. package/out/swaggerParser.js +464 -0
  89. package/out/testProvider.js +767 -0
  90. package/out/theoryCaseLoader.js +113 -0
  91. package/out/validationCache.js +211 -0
  92. package/package.json +6 -1
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ /**
3
+ * Detects flat `[env:STAGE_REGION]` matrices and suggests a refactor to
4
+ * `[template:STAGE]` + `[template:REGION]` + `[env:STAGE_REGION extends STAGE, REGION]`.
5
+ *
6
+ * The detector is conservative — it only fires when:
7
+ * - At least 2 stages × 2 regions
8
+ * - At least 3 populated cells (so the matrix is big enough to be worth refactoring)
9
+ * - All matrix envs are flat (no existing `extends` clause)
10
+ *
11
+ * For each variable across the matrix, the axis classifier picks one of:
12
+ * - `stage` : value is consistent within each stage, varies between stages
13
+ * - `region` : value is consistent within each region, varies between regions
14
+ * - `leaf` : value varies across both axes — stays in the leaf env
15
+ *
16
+ * Connection-string vars (names ending in `_connectionString`) are intentionally
17
+ * skipped for lifting in v1 because their reverse-format
18
+ * (`connectionString name = ...`) is special. They are preserved in leaf envs.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.detectRegionPattern = detectRegionPattern;
22
+ exports.generateRegionRefactor = generateRegionRefactor;
23
+ exports.applyRegionRefactorToText = applyRegionRefactorToText;
24
+ const nornenvLanguageModel_1 = require("./nornenvLanguageModel");
25
+ const MATRIX_ENV_NAME_REGEX = /^([a-zA-Z][a-zA-Z0-9-]*)_([a-zA-Z][a-zA-Z0-9-]*)$/;
26
+ const MIN_STAGES = 2;
27
+ const MIN_REGIONS = 2;
28
+ const MIN_CELLS = 3;
29
+ function isConnectionStringVar(name) {
30
+ return name.endsWith('_connectionString');
31
+ }
32
+ function inferDeclarationKind(name) {
33
+ return isConnectionStringVar(name) ? 'connectionString' : 'var';
34
+ }
35
+ function inferDisplayName(name) {
36
+ return isConnectionStringVar(name)
37
+ ? name.slice(0, -'_connectionString'.length)
38
+ : name;
39
+ }
40
+ function declarationKind(declaration) {
41
+ return declaration.name.endsWith('_connectionString') && declaration.displayName !== declaration.name
42
+ ? 'connectionString'
43
+ : 'var';
44
+ }
45
+ function buildDeclarationMap(text) {
46
+ const model = (0, nornenvLanguageModel_1.parseNornenvDocumentModel)(text);
47
+ const byEnv = new Map();
48
+ for (const declaration of model.declarations) {
49
+ if (declaration.sectionKind !== 'env' || !declaration.sectionName) {
50
+ continue;
51
+ }
52
+ if (!byEnv.has(declaration.sectionName)) {
53
+ byEnv.set(declaration.sectionName, new Map());
54
+ }
55
+ byEnv.get(declaration.sectionName).set(declaration.name, declaration);
56
+ }
57
+ return byEnv;
58
+ }
59
+ function getCellValue(cell, name, declarationsByEnv, config) {
60
+ const value = cell.env.variables[name];
61
+ if (value === undefined) {
62
+ return undefined;
63
+ }
64
+ const declaration = declarationsByEnv.get(cell.envName)?.get(name);
65
+ return {
66
+ value,
67
+ secret: declaration?.secret ?? config.secretNames.has(name),
68
+ kind: declaration ? declarationKind(declaration) : inferDeclarationKind(name),
69
+ displayName: declaration?.displayName ?? inferDisplayName(name)
70
+ };
71
+ }
72
+ function classifyByAxis(name, cells, axisKeys, getAxisKey, declarationsByEnv, config) {
73
+ const byKey = new Map();
74
+ let presentCells = 0;
75
+ for (const key of axisKeys) {
76
+ const axisCells = cells.filter(cell => getAxisKey(cell) === key);
77
+ const presentValues = axisCells
78
+ .map(cell => getCellValue(cell, name, declarationsByEnv, config))
79
+ .filter((value) => value !== undefined);
80
+ if (presentValues.length === 0) {
81
+ continue;
82
+ }
83
+ presentCells += presentValues.length;
84
+ // Partial presence would add the value to cells that originally did not have it.
85
+ if (presentValues.length !== axisCells.length) {
86
+ return undefined;
87
+ }
88
+ const first = presentValues[0];
89
+ if (presentValues.some(value => value.value !== first.value)) {
90
+ return undefined;
91
+ }
92
+ byKey.set(key, {
93
+ value: first.value,
94
+ secret: presentValues.some(value => value.secret),
95
+ kind: first.kind,
96
+ displayName: first.displayName
97
+ });
98
+ }
99
+ // A single-cell value is not duplication; keep it leaf-specific.
100
+ return byKey.size > 0 && presentCells >= 2 ? byKey : undefined;
101
+ }
102
+ function detectRegionPattern(config, text) {
103
+ const cells = [];
104
+ for (const env of config.environments) {
105
+ const match = env.name.match(MATRIX_ENV_NAME_REGEX);
106
+ if (!match) {
107
+ continue;
108
+ }
109
+ // Flat-only — if any matrix-shaped env already extends, this file is already mixed.
110
+ if (env.parents.length > 0) {
111
+ return undefined;
112
+ }
113
+ cells.push({ stage: match[1], region: match[2], envName: env.name, env });
114
+ }
115
+ const stages = Array.from(new Set(cells.map(c => c.stage)));
116
+ const regions = Array.from(new Set(cells.map(c => c.region)));
117
+ if (stages.length < MIN_STAGES || regions.length < MIN_REGIONS || cells.length < MIN_CELLS) {
118
+ return undefined;
119
+ }
120
+ if (new Set([...stages, ...regions]).size !== stages.length + regions.length) {
121
+ return undefined;
122
+ }
123
+ const existingTemplates = new Set(config.templates.map(template => template.name));
124
+ if ([...stages, ...regions].some(name => existingTemplates.has(name))) {
125
+ return undefined;
126
+ }
127
+ // Collect every variable name that appears in any matrix cell.
128
+ const allVarNames = new Set();
129
+ for (const cell of cells) {
130
+ for (const name of Object.keys(cell.env.variables)) {
131
+ allVarNames.add(name);
132
+ }
133
+ }
134
+ let liftedToStage = 0;
135
+ let liftedToRegion = 0;
136
+ let leafSpecific = 0;
137
+ let skippedConnectionStrings = 0;
138
+ const assignments = [];
139
+ const declarationsByEnv = buildDeclarationMap(text);
140
+ for (const name of allVarNames) {
141
+ if (isConnectionStringVar(name)) {
142
+ skippedConnectionStrings++;
143
+ }
144
+ const stageValues = isConnectionStringVar(name)
145
+ ? undefined
146
+ : classifyByAxis(name, cells, stages, cell => cell.stage, declarationsByEnv, config);
147
+ const regionValues = isConnectionStringVar(name)
148
+ ? undefined
149
+ : classifyByAxis(name, cells, regions, cell => cell.region, declarationsByEnv, config);
150
+ if (stageValues) {
151
+ assignments.push({ name, axis: 'stage', byKey: stageValues });
152
+ liftedToStage++;
153
+ }
154
+ else if (regionValues) {
155
+ assignments.push({ name, axis: 'region', byKey: regionValues });
156
+ liftedToRegion++;
157
+ }
158
+ else {
159
+ const byKey = new Map();
160
+ for (const cell of cells) {
161
+ const value = getCellValue(cell, name, declarationsByEnv, config);
162
+ if (value !== undefined) {
163
+ byKey.set(cell.envName, value);
164
+ }
165
+ }
166
+ if (byKey.size > 0) {
167
+ assignments.push({ name, axis: 'leaf', byKey });
168
+ leafSpecific++;
169
+ }
170
+ }
171
+ }
172
+ // Compute the line range to replace: from the first matrix env header to the line
173
+ // just before the next non-matrix section (or EOF).
174
+ const model = (0, nornenvLanguageModel_1.parseNornenvDocumentModel)(text);
175
+ const matrixNames = new Set(cells.map(c => c.envName));
176
+ const matrixSectionLines = model.sections
177
+ .filter(s => s.kind === 'env' && matrixNames.has(s.name))
178
+ .map(s => s.lineNumber)
179
+ .sort((a, b) => a - b);
180
+ if (matrixSectionLines.length === 0) {
181
+ return undefined;
182
+ }
183
+ const firstLine = matrixSectionLines[0];
184
+ const lastMatrixHeaderLine = matrixSectionLines[matrixSectionLines.length - 1];
185
+ const sectionsInReplacementRange = model.sections.filter(section => section.lineNumber >= firstLine && section.lineNumber <= lastMatrixHeaderLine);
186
+ if (sectionsInReplacementRange.some(section => section.kind !== 'env' || !matrixNames.has(section.name))) {
187
+ return undefined;
188
+ }
189
+ const lineCount = text.split('\n').length;
190
+ const sectionsAfter = model.sections
191
+ .filter(s => s.lineNumber > lastMatrixHeaderLine)
192
+ .map(s => s.lineNumber)
193
+ .sort((a, b) => a - b);
194
+ const lastLine = sectionsAfter.length > 0 ? sectionsAfter[0] - 1 : lineCount - 1;
195
+ return {
196
+ stages,
197
+ regions,
198
+ cells,
199
+ assignments,
200
+ replaceRange: { startLine: firstLine, endLine: lastLine },
201
+ summary: { liftedToStage, liftedToRegion, leafSpecific, skippedConnectionStrings }
202
+ };
203
+ }
204
+ /**
205
+ * Generates the refactored text block: stage templates, then region templates, then
206
+ * leaf envs that compose them. Caller replaces `pattern.replaceRange` with this text.
207
+ */
208
+ function generateRegionRefactor(pattern) {
209
+ const blocks = [];
210
+ const fmtVar = (assignment, value) => {
211
+ if (value.kind === 'connectionString') {
212
+ const secretPrefix = value.secret ? 'secret ' : '';
213
+ return ` ${secretPrefix}connectionString ${value.displayName} = ${value.value}`;
214
+ }
215
+ const keyword = value.secret ? 'secret' : 'var';
216
+ return ` ${keyword} ${assignment.name} = ${value.value}`;
217
+ };
218
+ // Stage templates
219
+ for (const stage of pattern.stages) {
220
+ const stageVars = pattern.assignments.filter(a => a.axis === 'stage' && a.byKey.has(stage));
221
+ if (stageVars.length === 0) {
222
+ continue;
223
+ }
224
+ const lines = [`[template:${stage}]`];
225
+ for (const v of stageVars) {
226
+ lines.push(fmtVar(v, v.byKey.get(stage)));
227
+ }
228
+ blocks.push(lines.join('\n'));
229
+ }
230
+ // Region templates
231
+ for (const region of pattern.regions) {
232
+ const regionVars = pattern.assignments.filter(a => a.axis === 'region' && a.byKey.has(region));
233
+ if (regionVars.length === 0) {
234
+ continue;
235
+ }
236
+ const lines = [`[template:${region}]`];
237
+ for (const v of regionVars) {
238
+ lines.push(fmtVar(v, v.byKey.get(region)));
239
+ }
240
+ blocks.push(lines.join('\n'));
241
+ }
242
+ // Leaf envs
243
+ for (const cell of pattern.cells) {
244
+ const leafVars = pattern.assignments.filter(a => a.axis === 'leaf' && a.byKey.has(cell.envName));
245
+ const header = `[env:${cell.envName} extends ${cell.stage}, ${cell.region}]`;
246
+ if (leafVars.length === 0) {
247
+ blocks.push(header);
248
+ continue;
249
+ }
250
+ const lines = [header];
251
+ for (const v of leafVars) {
252
+ lines.push(fmtVar(v, v.byKey.get(cell.envName)));
253
+ }
254
+ blocks.push(lines.join('\n'));
255
+ }
256
+ return blocks.join('\n\n');
257
+ }
258
+ function applyRegionRefactorToText(text, pattern) {
259
+ const lines = text.split('\n');
260
+ const startLine = pattern.replaceRange.startLine;
261
+ const endLine = Math.min(pattern.replaceRange.endLine, lines.length - 1);
262
+ const replacementLines = generateRegionRefactor(pattern).split('\n');
263
+ lines.splice(startLine, endLine - startLine + 1, ...replacementLines);
264
+ const result = lines.join('\n');
265
+ return text.endsWith('\n') && !result.endsWith('\n') ? `${result}\n` : result;
266
+ }
267
+ //# sourceMappingURL=nornenvRegionRefactor.js.map
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NornsqlHoverProvider = void 0;
37
+ const vscode = __importStar(require("vscode"));
38
+ const environmentProvider_1 = require("./environmentProvider");
39
+ const inlayHintResolver_1 = require("./inlayHintResolver");
40
+ const nornapiHoverProvider_1 = require("./nornapiHoverProvider");
41
+ const CONNECTION_LINE_REGEX = /^(\s*)connection\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*$/;
42
+ /**
43
+ * Hover for `.nornsql`. Two recognised cursors:
44
+ * - On the connection identifier (in a `connection NAME` line) — shows the resolved
45
+ * `NAME_connectionString` value from `.nornenv` and where it's defined.
46
+ * - On any `{{...}}` token — same resolution-chain markdown the `.nornapi` hover
47
+ * builds (env scope).
48
+ */
49
+ class NornsqlHoverProvider {
50
+ provideHover(document, position) {
51
+ try {
52
+ const filePath = document.uri.scheme === 'file' ? document.uri.fsPath : undefined;
53
+ if (!filePath) {
54
+ return undefined;
55
+ }
56
+ const lineText = document.lineAt(position.line).text;
57
+ const config = (0, environmentProvider_1.loadEnvironmentConfig)(filePath);
58
+ if (!config) {
59
+ return undefined;
60
+ }
61
+ const activeEnvironment = (0, environmentProvider_1.getActiveEnvironment)(filePath);
62
+ // Connection identifier hover.
63
+ const connectionMatch = lineText.match(CONNECTION_LINE_REGEX);
64
+ if (connectionMatch) {
65
+ const indent = connectionMatch[1].length;
66
+ const keywordEnd = indent + 'connection'.length;
67
+ // Identifier starts after the whitespace gap between `connection` and the name.
68
+ const identifierStart = lineText.indexOf(connectionMatch[2], keywordEnd);
69
+ if (identifierStart >= 0) {
70
+ const identifierEnd = identifierStart + connectionMatch[2].length;
71
+ if (position.character >= identifierStart && position.character <= identifierEnd) {
72
+ const csName = `${connectionMatch[2]}_connectionString`;
73
+ const markdown = (0, nornapiHoverProvider_1.renderTemplateReferenceHover)(csName, config, activeEnvironment);
74
+ return new vscode.Hover(markdown, new vscode.Range(new vscode.Position(position.line, identifierStart), new vscode.Position(position.line, identifierEnd)));
75
+ }
76
+ }
77
+ }
78
+ // {{...}} token hover.
79
+ const tokenMatch = (0, nornapiHoverProvider_1.findTemplateTokenAt)(lineText, position.character);
80
+ if (tokenMatch) {
81
+ const parsed = (0, inlayHintResolver_1.parseInlayReference)(tokenMatch.referenceRaw);
82
+ if (parsed) {
83
+ const markdown = (0, nornapiHoverProvider_1.renderTemplateReferenceHover)(parsed.name, config, activeEnvironment);
84
+ return new vscode.Hover(markdown, new vscode.Range(new vscode.Position(position.line, tokenMatch.tokenStart), new vscode.Position(position.line, tokenMatch.tokenEnd)));
85
+ }
86
+ }
87
+ }
88
+ catch {
89
+ return undefined;
90
+ }
91
+ return undefined;
92
+ }
93
+ }
94
+ exports.NornsqlHoverProvider = NornsqlHoverProvider;
95
+ //# sourceMappingURL=nornsqlHoverProvider.js.map
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.NornsqlInlayHintsProvider = void 0;
37
+ const vscode = __importStar(require("vscode"));
38
+ const environmentProvider_1 = require("./environmentProvider");
39
+ const inlayHintResolver_1 = require("./inlayHintResolver");
40
+ const TEMPLATE_TOKEN_REGEX = /\{\{([^{}]+)\}\}/g;
41
+ const CONNECTION_LINE_REGEX = /^(\s*)connection\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*$/;
42
+ /**
43
+ * Inlay hints for `.nornsql` files. Two payoffs:
44
+ * - `connection NAME` line: resolves `NAME_connectionString` from `.nornenv` and shows
45
+ * the resolved value as an inlay after the identifier. Secret connection strings
46
+ * are masked.
47
+ * - Any `{{...}}` token in the file (rare in SQL bodies, but handled for consistency).
48
+ */
49
+ class NornsqlInlayHintsProvider {
50
+ emitter = new vscode.EventEmitter();
51
+ environmentSubscription;
52
+ onDidChangeInlayHints = this.emitter.event;
53
+ constructor() {
54
+ this.environmentSubscription = (0, environmentProvider_1.onDidChangeActiveEnvironment)(() => {
55
+ this.emitter.fire();
56
+ });
57
+ }
58
+ provideInlayHints(document, range) {
59
+ const hints = [];
60
+ try {
61
+ const filePath = document.uri.scheme === 'file' ? document.uri.fsPath : undefined;
62
+ if (!filePath) {
63
+ return [];
64
+ }
65
+ const config = (0, environmentProvider_1.loadEnvironmentConfig)(filePath);
66
+ if (!config) {
67
+ return [];
68
+ }
69
+ const variables = (0, environmentProvider_1.getEnvironmentVariables)(filePath);
70
+ for (let lineNumber = range.start.line; lineNumber <= range.end.line; lineNumber++) {
71
+ const line = document.lineAt(lineNumber).text;
72
+ // `connection NAME` — resolve NAME_connectionString from .nornenv.
73
+ const connectionMatch = line.match(CONNECTION_LINE_REGEX);
74
+ if (connectionMatch) {
75
+ const name = connectionMatch[2];
76
+ const csVarName = `${name}_connectionString`;
77
+ const resolved = (0, inlayHintResolver_1.resolveInlayValueLabel)(csVarName, variables, config.secretNames);
78
+ if (resolved) {
79
+ const tokenEnd = line.length;
80
+ const hint = new vscode.InlayHint(new vscode.Position(lineNumber, tokenEnd), resolved.label, vscode.InlayHintKind.Parameter);
81
+ hint.tooltip = resolved.secret
82
+ ? `'${name}_connectionString' resolves from a secret connection string.`
83
+ : `'${name}_connectionString' resolves to '${resolved.value}'.`;
84
+ hints.push(hint);
85
+ }
86
+ }
87
+ // Generic `{{...}}` tokens (rare in SQL bodies but handled for parity).
88
+ TEMPLATE_TOKEN_REGEX.lastIndex = 0;
89
+ for (const match of line.matchAll(TEMPLATE_TOKEN_REGEX)) {
90
+ const resolved = (0, inlayHintResolver_1.resolveInlayValueLabel)(match[1], variables, config.secretNames);
91
+ if (!resolved) {
92
+ continue;
93
+ }
94
+ const tokenEnd = (match.index ?? 0) + match[0].length;
95
+ const hint = new vscode.InlayHint(new vscode.Position(lineNumber, tokenEnd), resolved.label, vscode.InlayHintKind.Parameter);
96
+ hint.tooltip = resolved.secret
97
+ ? `'${match[1].trim()}' resolves from a secret value.`
98
+ : `'${match[1].trim()}' resolves to '${resolved.value}'.`;
99
+ hints.push(hint);
100
+ }
101
+ }
102
+ }
103
+ catch {
104
+ return [];
105
+ }
106
+ return hints;
107
+ }
108
+ dispose() {
109
+ this.environmentSubscription.dispose();
110
+ this.emitter.dispose();
111
+ }
112
+ }
113
+ exports.NornsqlInlayHintsProvider = NornsqlInlayHintsProvider;
114
+ //# sourceMappingURL=nornsqlInlayHintsProvider.js.map