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.
- package/.claude/skills/norn-social-campaign/SKILL.md +70 -0
- package/CHANGELOG.md +6 -0
- package/demos/nornenv-region-refactor/README.md +64 -0
- package/dist/cli.js +360 -1
- 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 +6 -1
|
@@ -0,0 +1,554 @@
|
|
|
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.STARTER_CATALOG_ITEMS = void 0;
|
|
37
|
+
exports.getStarterCatalogItem = getStarterCatalogItem;
|
|
38
|
+
exports.createStarterCatalogItem = createStarterCatalogItem;
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const vscode = __importStar(require("vscode"));
|
|
41
|
+
const simpleRequestsNorn = `# Simple named requests and a sequence that uses them.
|
|
42
|
+
# Run from VS Code or with the Norn CLI from this folder.
|
|
43
|
+
|
|
44
|
+
[GetTodo]
|
|
45
|
+
GET https://jsonplaceholder.typicode.com/todos/1
|
|
46
|
+
|
|
47
|
+
[CreatePost]
|
|
48
|
+
POST https://jsonplaceholder.typicode.com/posts
|
|
49
|
+
Content-Type: application/json
|
|
50
|
+
Accept: application/json
|
|
51
|
+
{
|
|
52
|
+
"title": "Norn starter post",
|
|
53
|
+
"body": "Created from a named request",
|
|
54
|
+
"userId": 1
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
test sequence SimpleRequestFlow
|
|
58
|
+
var todo = run GetTodo
|
|
59
|
+
assert todo.status == 200
|
|
60
|
+
assert todo.body.id == 1
|
|
61
|
+
assert todo.body.title isType string
|
|
62
|
+
|
|
63
|
+
var created = run CreatePost
|
|
64
|
+
assert created.status == 201
|
|
65
|
+
assert created.body.title == "Norn starter post"
|
|
66
|
+
assert created.body.userId == 1
|
|
67
|
+
|
|
68
|
+
print "Created post" | "id={{created.body.id}}, title={{created.body.title}}"
|
|
69
|
+
end sequence
|
|
70
|
+
`;
|
|
71
|
+
const scriptsJsonNorn = `# Script execution and JSON runtime editing.
|
|
72
|
+
# Run from VS Code or with the Norn CLI from this folder.
|
|
73
|
+
|
|
74
|
+
test sequence ScriptsAndJsonWorkflow
|
|
75
|
+
run bash ./scripts/echo.sh first second
|
|
76
|
+
|
|
77
|
+
var signature = run js ./scripts/sign.js "starter-payload"
|
|
78
|
+
assert signature isType string
|
|
79
|
+
|
|
80
|
+
var payload = run readJson ./data/payload.json
|
|
81
|
+
assert payload.title == "Original starter payload"
|
|
82
|
+
|
|
83
|
+
payload.title = "Updated by Norn"
|
|
84
|
+
payload.signature = signature
|
|
85
|
+
payload.userId = 42
|
|
86
|
+
|
|
87
|
+
POST https://httpbin.org/post
|
|
88
|
+
Content-Type: application/json
|
|
89
|
+
Accept: application/json
|
|
90
|
+
|
|
91
|
+
payload
|
|
92
|
+
|
|
93
|
+
assert $1.status == 200
|
|
94
|
+
assert $1.body.json.title == "Updated by Norn"
|
|
95
|
+
assert $1.body.json.signature == "{{signature}}"
|
|
96
|
+
assert $1.body.json.userId == 42
|
|
97
|
+
|
|
98
|
+
print "Payload" | "title={{$1.body.json.title}}, signature={{signature}}"
|
|
99
|
+
end sequence
|
|
100
|
+
|
|
101
|
+
# Run this sequence when PowerShell or pwsh is available on your machine.
|
|
102
|
+
sequence PowerShellDataExample
|
|
103
|
+
var users = run powershell ./scripts/user-data.ps1
|
|
104
|
+
assert users[0].id == 101
|
|
105
|
+
assert users[0].role == "admin"
|
|
106
|
+
return users
|
|
107
|
+
end sequence
|
|
108
|
+
`;
|
|
109
|
+
const echoScript = `#!/bin/bash
|
|
110
|
+
echo "Hello from bash"
|
|
111
|
+
echo "Arguments: $@"
|
|
112
|
+
echo "First: $1"
|
|
113
|
+
echo "Second: $2"
|
|
114
|
+
`;
|
|
115
|
+
const signScript = `const input = process.argv[2] || 'starter-payload';
|
|
116
|
+
const signature = Buffer.from(input + ':signed').toString('base64');
|
|
117
|
+
console.log(signature);
|
|
118
|
+
`;
|
|
119
|
+
const userDataPowerShell = `$users = @(
|
|
120
|
+
[PSCustomObject]@{ id = 101; name = "Ada"; role = "admin" }
|
|
121
|
+
[PSCustomObject]@{ id = 102; name = "Lin"; role = "tester" }
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
$users | ConvertTo-Json -Compress
|
|
125
|
+
`;
|
|
126
|
+
const payloadJson = `{
|
|
127
|
+
"title": "Original starter payload",
|
|
128
|
+
"body": "This JSON file is loaded, edited at runtime, and sent as a request body.",
|
|
129
|
+
"userId": 1,
|
|
130
|
+
"tags": ["starter", "json"]
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
const sqlDemoNorn = `import "./queries.nornsql"
|
|
134
|
+
|
|
135
|
+
# Run from VS Code or with the Norn CLI from this folder.
|
|
136
|
+
|
|
137
|
+
test sequence SqlDemoFlow
|
|
138
|
+
var users = run sql ListDemoUsers
|
|
139
|
+
assert users[0].Email == "qa@example.com"
|
|
140
|
+
assert users[1].Id == 2
|
|
141
|
+
|
|
142
|
+
var user = run sql GetDemoUserByEmail("qa@example.com")
|
|
143
|
+
assert user[0].FirstName == "QA"
|
|
144
|
+
|
|
145
|
+
var createResult = run sql CreateDemoUser("new@example.com", "New", "User")
|
|
146
|
+
assert createResult.affectedRows == 1
|
|
147
|
+
|
|
148
|
+
print "SQL demo" | "first={{users[0].Name}}, created={{createResult.affectedRows}}"
|
|
149
|
+
end sequence
|
|
150
|
+
`;
|
|
151
|
+
const sqlDemoOperations = `connection demoDb
|
|
152
|
+
|
|
153
|
+
query ListDemoUsers
|
|
154
|
+
select Id, Email, Name
|
|
155
|
+
from DemoUsers
|
|
156
|
+
end query
|
|
157
|
+
|
|
158
|
+
query GetDemoUserByEmail(email)
|
|
159
|
+
select Id, Email, FirstName, LastName
|
|
160
|
+
from DemoUsers
|
|
161
|
+
where Email = :email
|
|
162
|
+
end query
|
|
163
|
+
|
|
164
|
+
command CreateDemoUser(email, firstName, lastName)
|
|
165
|
+
insert into DemoUsers (Email, FirstName, LastName)
|
|
166
|
+
values (:email, :firstName, :lastName)
|
|
167
|
+
end command
|
|
168
|
+
`;
|
|
169
|
+
const sqlDemoEnv = `var demoDb_server = starter-demo
|
|
170
|
+
var demoDb_database = StarterDemoDb
|
|
171
|
+
var demoDb_user = norn_demo
|
|
172
|
+
var demoDb_password = demo_password
|
|
173
|
+
|
|
174
|
+
[env:demo]
|
|
175
|
+
var demoDb_database = StarterDemoDb
|
|
176
|
+
`;
|
|
177
|
+
const sqlDemoConfig = `${JSON.stringify({
|
|
178
|
+
version: 1,
|
|
179
|
+
sql: {
|
|
180
|
+
connections: {
|
|
181
|
+
demoDb: {
|
|
182
|
+
adapter: 'starter-fake-sql-adapter',
|
|
183
|
+
profile: 'demoDb'
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
adapters: {
|
|
187
|
+
'starter-fake-sql-adapter': {
|
|
188
|
+
command: ['node', './adapters/fake-sql-adapter.js']
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}, null, '\t')}\n`;
|
|
193
|
+
const fakeSqlAdapter = `const chunks = [];
|
|
194
|
+
|
|
195
|
+
process.stdin.on('data', chunk => {
|
|
196
|
+
chunks.push(chunk);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
process.stdin.on('end', () => {
|
|
200
|
+
try {
|
|
201
|
+
const request = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
202
|
+
const values = request.connection && request.connection.values ? request.connection.values : {};
|
|
203
|
+
|
|
204
|
+
if (!values.server || !values.database || !values.user || !values.password) {
|
|
205
|
+
process.stdout.write(JSON.stringify({
|
|
206
|
+
success: false,
|
|
207
|
+
error: 'Missing expected demoDb connection values'
|
|
208
|
+
}));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
switch (request.operation.name) {
|
|
213
|
+
case 'ListDemoUsers':
|
|
214
|
+
process.stdout.write(JSON.stringify({
|
|
215
|
+
success: true,
|
|
216
|
+
result: {
|
|
217
|
+
kind: 'rows',
|
|
218
|
+
rowCount: 2,
|
|
219
|
+
rows: [
|
|
220
|
+
{ Id: 1, Email: 'qa@example.com', Name: 'QA User' },
|
|
221
|
+
{ Id: 2, Email: 'second@example.com', Name: 'Second User' }
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
}));
|
|
225
|
+
return;
|
|
226
|
+
|
|
227
|
+
case 'GetDemoUserByEmail':
|
|
228
|
+
process.stdout.write(JSON.stringify({
|
|
229
|
+
success: true,
|
|
230
|
+
result: {
|
|
231
|
+
kind: 'rows',
|
|
232
|
+
rowCount: 1,
|
|
233
|
+
rows: [
|
|
234
|
+
{ Id: 1, Email: request.params.email, FirstName: 'QA', LastName: 'User' }
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
}));
|
|
238
|
+
return;
|
|
239
|
+
|
|
240
|
+
case 'CreateDemoUser':
|
|
241
|
+
process.stdout.write(JSON.stringify({
|
|
242
|
+
success: true,
|
|
243
|
+
result: {
|
|
244
|
+
kind: 'exec',
|
|
245
|
+
affectedRows: 1
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
248
|
+
return;
|
|
249
|
+
|
|
250
|
+
default:
|
|
251
|
+
process.stdout.write(JSON.stringify({
|
|
252
|
+
success: false,
|
|
253
|
+
error: 'Unknown fake SQL operation: ' + request.operation.name
|
|
254
|
+
}));
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
process.stdout.write(JSON.stringify({
|
|
258
|
+
success: false,
|
|
259
|
+
error: error instanceof Error ? error.message : String(error)
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
`;
|
|
264
|
+
const realSqlNorn = `import "./sample.nornsql"
|
|
265
|
+
|
|
266
|
+
# Fill in .nornenv before running this file against your database.
|
|
267
|
+
# Example CLI run from this folder: norn real-db-starter.norn -e dev
|
|
268
|
+
|
|
269
|
+
test sequence VerifyRealDatabaseConnection
|
|
270
|
+
var activeUsers = run sql ListUsersByStatus("active")
|
|
271
|
+
assert activeUsers isType array
|
|
272
|
+
print "Active users" | activeUsers
|
|
273
|
+
|
|
274
|
+
var updateResult = run sql UpdateUserStatus(1, "active")
|
|
275
|
+
assert updateResult.affectedRows >= 0
|
|
276
|
+
end sequence
|
|
277
|
+
`;
|
|
278
|
+
const realSqlOperations = `# Starter SQL operations for Norn.
|
|
279
|
+
# Keep the connection name aligned with norn.config.json.
|
|
280
|
+
|
|
281
|
+
connection appDb
|
|
282
|
+
|
|
283
|
+
query ListUsersByStatus(status)
|
|
284
|
+
select Id, Email, Status
|
|
285
|
+
from Users
|
|
286
|
+
where Status = :status
|
|
287
|
+
end query
|
|
288
|
+
|
|
289
|
+
command UpdateUserStatus(id, status)
|
|
290
|
+
update Users
|
|
291
|
+
set Status = :status
|
|
292
|
+
where Id = :id
|
|
293
|
+
end command
|
|
294
|
+
`;
|
|
295
|
+
const realSqlEnv = `# Choose the connection string that matches your adapter and environment.
|
|
296
|
+
# Built-in adapters supported by Norn: postgres, sqlserver, sqlserver-windows.
|
|
297
|
+
|
|
298
|
+
connectionString appDb = postgresql://user:password@localhost:5432/app_db
|
|
299
|
+
|
|
300
|
+
[env:dev]
|
|
301
|
+
connectionString appDb = postgresql://dev_user:dev_password@localhost:5432/app_dev
|
|
302
|
+
|
|
303
|
+
# For production, prefer encrypted secrets once you have a real value.
|
|
304
|
+
# [env:prod]
|
|
305
|
+
# secret connectionString appDb = ENC[paste-encrypted-value-here]
|
|
306
|
+
`;
|
|
307
|
+
const realSqlConfig = `${JSON.stringify({
|
|
308
|
+
_comment: [
|
|
309
|
+
'Change sql.connections.appDb.adapter to postgres, sqlserver, or sqlserver-windows.',
|
|
310
|
+
'The profile name appDb maps to connectionString appDb = ... in .nornenv.',
|
|
311
|
+
'sqlserver-windows is for Windows / Trusted authentication.'
|
|
312
|
+
],
|
|
313
|
+
version: 1,
|
|
314
|
+
sql: {
|
|
315
|
+
connections: {
|
|
316
|
+
appDb: {
|
|
317
|
+
adapter: 'postgres',
|
|
318
|
+
profile: 'appDb'
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}, null, '\t')}\n`;
|
|
323
|
+
const realSqlReadme = `# Real SQL Connection Starter
|
|
324
|
+
|
|
325
|
+
This starter is for wiring Norn to a real database.
|
|
326
|
+
|
|
327
|
+
Files:
|
|
328
|
+
- norn.config.json maps the appDb connection to a built-in SQL adapter.
|
|
329
|
+
- .nornenv stores connectionString appDb values for each environment.
|
|
330
|
+
- sample.nornsql contains reusable query and command definitions.
|
|
331
|
+
- real-db-starter.norn shows how to call those operations from a sequence.
|
|
332
|
+
|
|
333
|
+
Before running:
|
|
334
|
+
1. Pick the adapter in norn.config.json: postgres, sqlserver, or sqlserver-windows.
|
|
335
|
+
2. Replace the connectionString appDb values in .nornenv.
|
|
336
|
+
3. Update sample.nornsql so table and column names match your database.
|
|
337
|
+
4. Run the sample with the local CLI or from VS Code.
|
|
338
|
+
`;
|
|
339
|
+
const advancedEnv = `var primaryUserId = 1
|
|
340
|
+
|
|
341
|
+
[env:demo]
|
|
342
|
+
var baseUrl = https://jsonplaceholder.typicode.com
|
|
343
|
+
var postTitle = Norn advanced flow
|
|
344
|
+
`;
|
|
345
|
+
const advancedApi = `headers Json
|
|
346
|
+
Content-Type: application/json
|
|
347
|
+
Accept: application/json
|
|
348
|
+
end headers
|
|
349
|
+
|
|
350
|
+
endpoints
|
|
351
|
+
GetUser: GET {{baseUrl}}/users/{id}
|
|
352
|
+
GetUserPosts: GET {{baseUrl}}/users/{userId}/posts
|
|
353
|
+
CreatePost: POST {{baseUrl}}/posts
|
|
354
|
+
end endpoints
|
|
355
|
+
`;
|
|
356
|
+
const advancedHelpers = `import "./api.nornapi"
|
|
357
|
+
|
|
358
|
+
sequence LoadUser(userId = 1)
|
|
359
|
+
var userResponse = GET GetUser({{userId}}) Json
|
|
360
|
+
assert userResponse.status == 200
|
|
361
|
+
assert userResponse.body matchesSchema "./schemas/user.schema.json"
|
|
362
|
+
return userResponse.body
|
|
363
|
+
end sequence
|
|
364
|
+
|
|
365
|
+
sequence LoadPostsForUser(userId = 1)
|
|
366
|
+
var postsResponse = GET GetUserPosts({{userId}}) Json
|
|
367
|
+
assert postsResponse.status == 200
|
|
368
|
+
assert postsResponse.body isType array
|
|
369
|
+
assert postsResponse.body[0] matchesSchema "./schemas/post.schema.json"
|
|
370
|
+
return postsResponse.body
|
|
371
|
+
end sequence
|
|
372
|
+
|
|
373
|
+
sequence CreateWorkflowPost(userId = 1)
|
|
374
|
+
var createResponse = POST CreatePost Json
|
|
375
|
+
{
|
|
376
|
+
"title": "{{postTitle}}",
|
|
377
|
+
"body": "Created by an advanced Norn flow",
|
|
378
|
+
"userId": {{userId}}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
assert createResponse.status == 201
|
|
382
|
+
assert createResponse.body matchesSchema "./schemas/post.schema.json"
|
|
383
|
+
return createResponse.body
|
|
384
|
+
end sequence
|
|
385
|
+
`;
|
|
386
|
+
const advancedFlow = `import "./helpers.norn"
|
|
387
|
+
|
|
388
|
+
# Run from VS Code or with the Norn CLI from this folder using the demo environment.
|
|
389
|
+
|
|
390
|
+
test sequence AdvancedEndToEndFlow
|
|
391
|
+
var user = run LoadUser({{primaryUserId}})
|
|
392
|
+
assert user.id == {{primaryUserId}}
|
|
393
|
+
assert user.email isType string
|
|
394
|
+
|
|
395
|
+
var posts = run LoadPostsForUser(user.id)
|
|
396
|
+
assert posts[0].userId == user.id
|
|
397
|
+
assert posts[0] matchesSchema "./schemas/post.schema.json"
|
|
398
|
+
|
|
399
|
+
var createdPost = run CreateWorkflowPost(user.id)
|
|
400
|
+
assert createdPost.userId == user.id
|
|
401
|
+
assert createdPost.title == "{{postTitle}}"
|
|
402
|
+
assert createdPost matchesSchema "./schemas/post.schema.json"
|
|
403
|
+
|
|
404
|
+
print "Advanced flow" | "user={{user.name}}, existingPosts={{posts[0].id}}, created={{createdPost.id}}"
|
|
405
|
+
end sequence
|
|
406
|
+
`;
|
|
407
|
+
const userSchema = `{
|
|
408
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
409
|
+
"title": "JSONPlaceholder user response",
|
|
410
|
+
"type": "object",
|
|
411
|
+
"properties": {
|
|
412
|
+
"id": { "type": "integer" },
|
|
413
|
+
"name": { "type": "string" },
|
|
414
|
+
"username": { "type": "string" },
|
|
415
|
+
"email": { "type": "string" },
|
|
416
|
+
"address": { "type": "object" },
|
|
417
|
+
"phone": { "type": "string" },
|
|
418
|
+
"website": { "type": "string" },
|
|
419
|
+
"company": { "type": "object" }
|
|
420
|
+
},
|
|
421
|
+
"required": ["id", "name", "username", "email"],
|
|
422
|
+
"additionalProperties": true
|
|
423
|
+
}
|
|
424
|
+
`;
|
|
425
|
+
const postSchema = `{
|
|
426
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
427
|
+
"title": "JSONPlaceholder post response",
|
|
428
|
+
"type": "object",
|
|
429
|
+
"properties": {
|
|
430
|
+
"userId": { "type": "integer" },
|
|
431
|
+
"id": { "type": "integer" },
|
|
432
|
+
"title": { "type": "string" },
|
|
433
|
+
"body": { "type": "string" }
|
|
434
|
+
},
|
|
435
|
+
"required": ["userId", "id", "title"],
|
|
436
|
+
"additionalProperties": true
|
|
437
|
+
}
|
|
438
|
+
`;
|
|
439
|
+
exports.STARTER_CATALOG_ITEMS = [
|
|
440
|
+
{
|
|
441
|
+
id: 'simple-requests',
|
|
442
|
+
label: 'Simple Requests and Sequences',
|
|
443
|
+
description: 'Named GET and POST requests called from one sequence.',
|
|
444
|
+
detail: 'No env file, just direct public URLs and assertions.',
|
|
445
|
+
folderName: 'norn-simple-requests',
|
|
446
|
+
openFile: 'requests.norn',
|
|
447
|
+
files: [
|
|
448
|
+
{ path: 'requests.norn', content: simpleRequestsNorn }
|
|
449
|
+
]
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
id: 'scripts-json',
|
|
453
|
+
label: 'Scripts and JSON Workflow',
|
|
454
|
+
description: 'Bash, JavaScript, PowerShell, readJson, and JSON body mutation.',
|
|
455
|
+
detail: 'Loads JSON, edits it at runtime, and sends it to httpbin.',
|
|
456
|
+
folderName: 'norn-scripts-json',
|
|
457
|
+
openFile: 'scripts-json.norn',
|
|
458
|
+
files: [
|
|
459
|
+
{ path: 'scripts-json.norn', content: scriptsJsonNorn },
|
|
460
|
+
{ path: 'scripts/echo.sh', content: echoScript },
|
|
461
|
+
{ path: 'scripts/sign.js', content: signScript },
|
|
462
|
+
{ path: 'scripts/user-data.ps1', content: userDataPowerShell },
|
|
463
|
+
{ path: 'data/payload.json', content: payloadJson }
|
|
464
|
+
]
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
id: 'sql-demo',
|
|
468
|
+
label: 'SQL Demo',
|
|
469
|
+
description: 'Runnable SQL-shaped flow with a local fake adapter.',
|
|
470
|
+
detail: 'Shows query and command results without needing a real database.',
|
|
471
|
+
folderName: 'norn-sql-demo',
|
|
472
|
+
openFile: 'sql-demo.norn',
|
|
473
|
+
files: [
|
|
474
|
+
{ path: 'sql-demo.norn', content: sqlDemoNorn },
|
|
475
|
+
{ path: 'queries.nornsql', content: sqlDemoOperations },
|
|
476
|
+
{ path: '.nornenv', content: sqlDemoEnv },
|
|
477
|
+
{ path: 'norn.config.json', content: sqlDemoConfig },
|
|
478
|
+
{ path: 'adapters/fake-sql-adapter.js', content: fakeSqlAdapter }
|
|
479
|
+
]
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
id: 'sql-connection',
|
|
483
|
+
label: 'Real SQL Connection Starter',
|
|
484
|
+
description: 'Config, env, and commented SQL files for a real database.',
|
|
485
|
+
detail: 'Fill in connection details for postgres, sqlserver, or sqlserver-windows.',
|
|
486
|
+
folderName: 'norn-sql-connection-starter',
|
|
487
|
+
openFile: 'real-db-starter.norn',
|
|
488
|
+
files: [
|
|
489
|
+
{ path: 'real-db-starter.norn', content: realSqlNorn },
|
|
490
|
+
{ path: 'sample.nornsql', content: realSqlOperations },
|
|
491
|
+
{ path: '.nornenv', content: realSqlEnv },
|
|
492
|
+
{ path: 'norn.config.json', content: realSqlConfig },
|
|
493
|
+
{ path: 'README.md', content: realSqlReadme }
|
|
494
|
+
]
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
id: 'advanced-flow',
|
|
498
|
+
label: 'Advanced End-to-End Flow',
|
|
499
|
+
description: 'Env, API definitions, helper sequences, tests, and contracts.',
|
|
500
|
+
detail: 'A complete flow with sequence composition and schema validation.',
|
|
501
|
+
folderName: 'norn-advanced-flow',
|
|
502
|
+
openFile: 'advanced-flow.norn',
|
|
503
|
+
files: [
|
|
504
|
+
{ path: 'advanced-flow.norn', content: advancedFlow },
|
|
505
|
+
{ path: 'helpers.norn', content: advancedHelpers },
|
|
506
|
+
{ path: 'api.nornapi', content: advancedApi },
|
|
507
|
+
{ path: '.nornenv', content: advancedEnv },
|
|
508
|
+
{ path: 'schemas/user.schema.json', content: userSchema },
|
|
509
|
+
{ path: 'schemas/post.schema.json', content: postSchema }
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
function getStarterCatalogItem(id) {
|
|
514
|
+
return exports.STARTER_CATALOG_ITEMS.find((starterItem) => starterItem.id === id);
|
|
515
|
+
}
|
|
516
|
+
async function createStarterCatalogItem(parentFolderUri, starterItem) {
|
|
517
|
+
const folderUri = await resolveUniqueFolderUri(parentFolderUri, starterItem.folderName);
|
|
518
|
+
await vscode.workspace.fs.createDirectory(folderUri);
|
|
519
|
+
const createdFiles = [];
|
|
520
|
+
for (const starterFile of starterItem.files) {
|
|
521
|
+
const targetUri = vscode.Uri.joinPath(folderUri, ...starterFile.path.split('/'));
|
|
522
|
+
const directoryName = path.posix.dirname(starterFile.path);
|
|
523
|
+
if (directoryName !== '.') {
|
|
524
|
+
await vscode.workspace.fs.createDirectory(vscode.Uri.joinPath(folderUri, ...directoryName.split('/')));
|
|
525
|
+
}
|
|
526
|
+
await vscode.workspace.fs.writeFile(targetUri, Buffer.from(starterFile.content, 'utf8'));
|
|
527
|
+
createdFiles.push(starterFile.path);
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
item: starterItem,
|
|
531
|
+
folderUri,
|
|
532
|
+
openFileUri: vscode.Uri.joinPath(folderUri, ...starterItem.openFile.split('/')),
|
|
533
|
+
createdFiles
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
async function resolveUniqueFolderUri(parentFolderUri, baseFolderName) {
|
|
537
|
+
let candidateName = baseFolderName;
|
|
538
|
+
let suffix = 2;
|
|
539
|
+
while (await uriExists(vscode.Uri.joinPath(parentFolderUri, candidateName))) {
|
|
540
|
+
candidateName = `${baseFolderName}-${suffix}`;
|
|
541
|
+
suffix += 1;
|
|
542
|
+
}
|
|
543
|
+
return vscode.Uri.joinPath(parentFolderUri, candidateName);
|
|
544
|
+
}
|
|
545
|
+
async function uriExists(uri) {
|
|
546
|
+
try {
|
|
547
|
+
await vscode.workspace.fs.stat(uri);
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
catch {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
//# sourceMappingURL=starterCatalog.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stripInlineComment = stripInlineComment;
|
|
4
|
+
function stripInlineComment(line) {
|
|
5
|
+
let inSingleQuote = false;
|
|
6
|
+
let inDoubleQuote = false;
|
|
7
|
+
for (let index = 0; index < line.length; index++) {
|
|
8
|
+
const char = line[index];
|
|
9
|
+
const previousChar = index > 0 ? line[index - 1] : '';
|
|
10
|
+
if (previousChar === '\\') {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (char === '"' && !inSingleQuote) {
|
|
14
|
+
inDoubleQuote = !inDoubleQuote;
|
|
15
|
+
}
|
|
16
|
+
else if (char === "'" && !inDoubleQuote) {
|
|
17
|
+
inSingleQuote = !inSingleQuote;
|
|
18
|
+
}
|
|
19
|
+
else if (char === '#' && !inSingleQuote && !inDoubleQuote) {
|
|
20
|
+
return line.substring(0, index).trimEnd();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return line;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=stringUtils.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Swagger Body IntelliSense Cache
|
|
4
|
+
*
|
|
5
|
+
* Persists request-body schemas extracted from Swagger/OpenAPI specs.
|
|
6
|
+
* The cache is used by completionProvider to provide endpoint request-body
|
|
7
|
+
* templates and inline key suggestions without fetching remote specs.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.loadSwaggerBodyCache = loadSwaggerBodyCache;
|
|
44
|
+
exports.getCachedRequestBodySchemasForUrl = getCachedRequestBodySchemasForUrl;
|
|
45
|
+
exports.saveRequestBodySchemasForUrl = saveRequestBodySchemasForUrl;
|
|
46
|
+
exports.invalidateSwaggerBodyCache = invalidateSwaggerBodyCache;
|
|
47
|
+
const vscode = __importStar(require("vscode"));
|
|
48
|
+
const cacheDir_1 = require("./cacheDir");
|
|
49
|
+
const CACHE_VERSION = 1;
|
|
50
|
+
const CACHE_FILE = 'swagger-body-intellisense.json';
|
|
51
|
+
function getWorkspaceRoot() {
|
|
52
|
+
const workspaceFolders = vscode.workspace.workspaceFolders;
|
|
53
|
+
if (workspaceFolders && workspaceFolders.length > 0) {
|
|
54
|
+
return workspaceFolders[0].uri.fsPath;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
function getCachePath() {
|
|
59
|
+
const root = getWorkspaceRoot();
|
|
60
|
+
if (!root) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return (0, cacheDir_1.getNornCacheFilePath)(root, CACHE_FILE);
|
|
64
|
+
}
|
|
65
|
+
function ensureCacheDir() {
|
|
66
|
+
const root = getWorkspaceRoot();
|
|
67
|
+
if (!root) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return !!(0, cacheDir_1.ensureNornCacheDir)(root);
|
|
71
|
+
}
|
|
72
|
+
function loadSwaggerBodyCache() {
|
|
73
|
+
return (0, cacheDir_1.loadVersionedJsonCache)({
|
|
74
|
+
cachePath: getCachePath(),
|
|
75
|
+
version: CACHE_VERSION,
|
|
76
|
+
createDefault: () => ({ version: CACHE_VERSION, urls: {} }),
|
|
77
|
+
isValid: cache => typeof cache.urls === 'object' && cache.urls !== null
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function saveSwaggerBodyCache(cache) {
|
|
81
|
+
return (0, cacheDir_1.saveVersionedJsonCache)(getCachePath(), cache, ensureCacheDir);
|
|
82
|
+
}
|
|
83
|
+
function getCachedRequestBodySchemasForUrl(url) {
|
|
84
|
+
const cache = loadSwaggerBodyCache();
|
|
85
|
+
return cache.urls[url];
|
|
86
|
+
}
|
|
87
|
+
function saveRequestBodySchemasForUrl(url, baseUrl, schemas) {
|
|
88
|
+
const cache = loadSwaggerBodyCache();
|
|
89
|
+
cache.urls[url] = {
|
|
90
|
+
baseUrl,
|
|
91
|
+
fetchedAt: new Date().toISOString(),
|
|
92
|
+
schemas: schemas.map(schema => ({
|
|
93
|
+
operationId: schema.operationId,
|
|
94
|
+
method: schema.method.toUpperCase(),
|
|
95
|
+
path: schema.path,
|
|
96
|
+
required: Array.isArray(schema.required) ? schema.required : [],
|
|
97
|
+
schema: schema.schema
|
|
98
|
+
}))
|
|
99
|
+
};
|
|
100
|
+
saveSwaggerBodyCache(cache);
|
|
101
|
+
}
|
|
102
|
+
function invalidateSwaggerBodyCache(url) {
|
|
103
|
+
if (url) {
|
|
104
|
+
const cache = loadSwaggerBodyCache();
|
|
105
|
+
delete cache.urls[url];
|
|
106
|
+
saveSwaggerBodyCache(cache);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
saveSwaggerBodyCache({
|
|
110
|
+
version: CACHE_VERSION,
|
|
111
|
+
urls: {}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=swaggerBodyIntellisenseCache.js.map
|