norn-cli 2.2.2 → 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.
- package/.claude/settings.local.json +18 -0
- package/.claude/skills/norn-social-campaign/SKILL.md +70 -0
- package/CHANGELOG.md +22 -1
- package/LICENSE +20 -29
- package/README.md +32 -1
- package/demos/nornenv-region-refactor/README.md +64 -0
- package/demos/nornenv-showcase/README.md +62 -0
- package/demos/nornenv-showcase/norn.config.json +16 -0
- package/demos/nornenv-showcase/showcase.norn +70 -0
- package/demos/nornenv-showcase/showcase.nornapi +26 -0
- package/demos/nornenv-showcase/showcase.nornsql +20 -0
- package/dist/cli.js +564 -54
- package/out/apiResponseIntellisenseCache.js +394 -0
- package/out/assertionRunner.js +567 -0
- package/out/cacheDir.js +136 -0
- package/out/chatParticipant.js +763 -0
- package/out/cli/colors.js +127 -0
- package/out/cli/formatters/assertion.js +102 -0
- package/out/cli/formatters/index.js +23 -0
- package/out/cli/formatters/response.js +106 -0
- package/out/cli/formatters/summary.js +246 -0
- package/out/cli/redaction.js +237 -0
- package/out/cli/reporters/html.js +689 -0
- package/out/cli/reporters/index.js +22 -0
- package/out/cli/reporters/junit.js +226 -0
- package/out/codeLensProvider.js +351 -0
- package/out/compareContentProvider.js +85 -0
- package/out/completionProvider.js +3739 -0
- package/out/contractAssertionSummary.js +225 -0
- package/out/contractDecorationProvider.js +243 -0
- package/out/coverageCalculator.js +879 -0
- package/out/coveragePanel.js +597 -0
- package/out/debug/breakpointResolver.js +84 -0
- package/out/debug/breakpoints.js +52 -0
- package/out/debug/nornDebugAdapter.js +166 -0
- package/out/debug/nornDebugSession.js +613 -0
- package/out/debug/sequenceLocationIndex.js +77 -0
- package/out/debug/types.js +3 -0
- package/out/deepClone.js +21 -0
- package/out/diagnosticProvider.js +2554 -0
- package/out/environmentParser.js +736 -0
- package/out/environmentProvider.js +544 -0
- package/out/environmentTemplates.js +146 -0
- package/out/errors/formatError.js +113 -0
- package/out/errors/nornError.js +29 -0
- package/out/formUrlEncoded.js +89 -0
- package/out/httpClient.js +348 -0
- package/out/httpRuntimeOptions.js +16 -0
- package/out/importErrors.js +31 -0
- package/out/inlayHintResolver.js +70 -0
- package/out/jsonFileReader.js +323 -0
- package/out/mcpClient.js +193 -0
- package/out/mcpConfig.js +184 -0
- package/out/mcpToolIntellisenseCache.js +96 -0
- package/out/mcpToolSchema.js +50 -0
- package/out/nornConfig.js +132 -0
- package/out/nornHoverProvider.js +124 -0
- package/out/nornInlayHintsProvider.js +191 -0
- package/out/nornPrompt.js +755 -0
- package/out/nornSqlParser.js +286 -0
- package/out/nornapiHoverProvider.js +135 -0
- package/out/nornapiInlayHintsProvider.js +94 -0
- package/out/nornapiParser.js +324 -0
- package/out/nornenvCodeActionProvider.js +101 -0
- package/out/nornenvDecorationProvider.js +239 -0
- package/out/nornenvFoldingProvider.js +63 -0
- package/out/nornenvHoverProvider.js +114 -0
- package/out/nornenvInlayHintsProvider.js +99 -0
- package/out/nornenvLanguageModel.js +187 -0
- package/out/nornenvRegionRefactor.js +267 -0
- package/out/nornsqlHoverProvider.js +95 -0
- package/out/nornsqlInlayHintsProvider.js +114 -0
- package/out/parser.js +839 -0
- package/out/pathAccess.js +28 -0
- package/out/postmanImportPanel.js +732 -0
- package/out/postmanImportPlanner.js +1155 -0
- package/out/postmanImportSidebarView.js +532 -0
- package/out/quotedString.js +35 -0
- package/out/requestPreparation.js +179 -0
- package/out/requestValidation.js +146 -0
- package/out/responsePanel.js +7754 -0
- package/out/schemaGenerator.js +562 -0
- package/out/scriptRunner.js +419 -0
- package/out/secrets/cliSecrets.js +415 -0
- package/out/secrets/crypto.js +105 -0
- package/out/secrets/envFileSecrets.js +177 -0
- package/out/secrets/keyStore.js +259 -0
- package/out/sequenceDeclaration.js +15 -0
- package/out/sequenceRunner.js +3590 -0
- package/out/sqlAdapterRunner.js +122 -0
- package/out/sqlBuiltInAdapters.js +604 -0
- package/out/sqlConfig.js +184 -0
- package/out/starterCatalog.js +554 -0
- package/out/stringUtils.js +25 -0
- package/out/swaggerBodyIntellisenseCache.js +114 -0
- package/out/swaggerParser.js +464 -0
- package/out/testProvider.js +767 -0
- package/out/theoryCaseLoader.js +113 -0
- package/out/validationCache.js +211 -0
- package/package.json +38 -11
- package/.kanbn/index.md +0 -31
- package/.kanbn/tasks/book-first-mentor-session.md +0 -13
- package/.kanbn/tasks/decide-what-success-in-a-pilot-looks-like.md +0 -9
- package/.kanbn/tasks/do-5-customer-conversations.md +0 -9
- package/.kanbn/tasks/finalise-the-one-line-pitch.md +0 -11
- package/.kanbn/tasks/interview-script.md +0 -49
- package/.kanbn/tasks/make-a-list-of-10-people-to-speak-to.md +0 -11
- package/.kanbn/tasks/prepare-your-customer-interview-questions.md +0 -11
- package/.kanbn/tasks/recruit-2/342/200/2233-pilot-users.md +0 -9
- package/.kanbn/tasks/refine-your-pitch.md +0 -9
- package/.kanbn/tasks/use-the-shiplight-website-as-a-template-to-improve-norn-website.md +0 -9
- package/.kanbn/tasks/write-down-repeated-wording.md +0 -9
- package/.kanbn/tasks/write-the-one-pager.md +0 -27
|
@@ -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
|