deepline 0.3.97 → 0.3.98
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/dist/bundling-sources/sdk/src/client.ts +144 -4
- package/dist/bundling-sources/sdk/src/index.ts +17 -4
- package/dist/bundling-sources/sdk/src/monitor-fleets.ts +1 -0
- package/dist/bundling-sources/sdk/src/monitor-input-contract.ts +1 -0
- package/dist/bundling-sources/sdk/src/monitors.ts +8 -25
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +1 -0
- package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +218 -16
- package/dist/bundling-sources/shared_libs/integration-contracts/monitor-input-contract.ts +455 -0
- package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +6 -1
- package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +2 -1
- package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +12 -0
- package/dist/bundling-sources/shared_libs/observability/scheduled-work-manual.ts +1 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-bootstrap-token.ts +19 -3
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +5 -1
- package/dist/cli/index.js +1004 -289
- package/dist/cli/index.mjs +1004 -289
- package/dist/index.d.mts +43 -24
- package/dist/index.d.ts +43 -24
- package/dist/index.js +604 -41
- package/dist/index.mjs +600 -41
- package/dist/install-integrity.json +6 -0
- package/dist/monitor-fleet-contract.d.mts +44 -8
- package/dist/monitor-fleet-contract.d.ts +44 -8
- package/dist/monitor-fleet-contract.js +354 -27
- package/dist/monitor-fleet-contract.mjs +343 -27
- package/dist/monitor-input-contract.d.mts +33 -0
- package/dist/monitor-input-contract.d.ts +33 -0
- package/dist/monitor-input-contract.js +392 -0
- package/dist/monitor-input-contract.mjs +353 -0
- package/dist/plays/bundle-play-file.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +7 -1
|
@@ -1,14 +1,207 @@
|
|
|
1
|
+
// ../integration-contracts/monitor-input-contract.ts
|
|
2
|
+
import Handlebars from "handlebars";
|
|
3
|
+
function authoredMonitorFieldPath(path, field) {
|
|
4
|
+
const batch = /^monitors\.(\d+)$/.exec(path);
|
|
5
|
+
return `${batch ? `monitors[${batch[1]}]` : path}.${field}`;
|
|
6
|
+
}
|
|
7
|
+
function record(value) {
|
|
8
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
9
|
+
}
|
|
10
|
+
function json(value, path, issues) {
|
|
11
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
value.forEach((child, index) => json(child, `${path}.${index}`, issues));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const object = record(value);
|
|
19
|
+
if (!object) {
|
|
20
|
+
issues.push({
|
|
21
|
+
severity: "error",
|
|
22
|
+
code: "invalid_monitor_json_value",
|
|
23
|
+
path,
|
|
24
|
+
message: "Monitor values must be JSON values."
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const [key, child] of Object.entries(object)) {
|
|
29
|
+
json(child, `${path}.${key}`, issues);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function monitorSpec(value, path, issues) {
|
|
33
|
+
const input = record(value);
|
|
34
|
+
if (!input) {
|
|
35
|
+
issues.push({
|
|
36
|
+
severity: "error",
|
|
37
|
+
code: "invalid_monitor_spec",
|
|
38
|
+
path,
|
|
39
|
+
message: "A monitor must be a JSON object."
|
|
40
|
+
});
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const allowed = /* @__PURE__ */ new Set(["key", "tool", "name", "payload"]);
|
|
44
|
+
for (const key2 of Object.keys(input)) {
|
|
45
|
+
if (!allowed.has(key2)) {
|
|
46
|
+
if (key2 === "controls") {
|
|
47
|
+
issues.push({
|
|
48
|
+
severity: "error",
|
|
49
|
+
code: "CONTROLS_NOT_SUPPORTED",
|
|
50
|
+
path: authoredMonitorFieldPath(path, "controls"),
|
|
51
|
+
message: "Operational controls are managed by Deepline and cannot be authored in Batch or Fleet JSON.",
|
|
52
|
+
suggestion: "Remove controls. Bind downstream automation in a Play with sqlListeners."
|
|
53
|
+
});
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (key2 === "play") {
|
|
57
|
+
issues.push({
|
|
58
|
+
severity: "error",
|
|
59
|
+
code: "PLAY_BINDING_NOT_IN_MONITOR_SPEC",
|
|
60
|
+
path: authoredMonitorFieldPath(path, "play"),
|
|
61
|
+
message: "A monitor does not own Play bindings.",
|
|
62
|
+
suggestion: "Declare sqlListeners in the Play, or run: deepline plays bootstrap monitor-triggered --out on-event.play.ts"
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
issues.push({
|
|
67
|
+
severity: "error",
|
|
68
|
+
code: "unknown_monitor_field",
|
|
69
|
+
path: `${path}.${key2}`,
|
|
70
|
+
message: `Monitor definitions do not accept '${key2}'.`
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const key = typeof input.key === "string" ? input.key.trim() : "";
|
|
75
|
+
const tool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
76
|
+
const name = input.name === void 0 ? void 0 : typeof input.name === "string" && input.name.trim() ? input.name.trim() : null;
|
|
77
|
+
const payload = record(input.payload);
|
|
78
|
+
if (!key) {
|
|
79
|
+
issues.push({
|
|
80
|
+
severity: "error",
|
|
81
|
+
code: "invalid_monitor_key",
|
|
82
|
+
path: `${path}.key`,
|
|
83
|
+
message: "Monitor key must be a non-empty string."
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (!tool) {
|
|
87
|
+
issues.push({
|
|
88
|
+
severity: "error",
|
|
89
|
+
code: "invalid_monitor_tool",
|
|
90
|
+
path: `${path}.tool`,
|
|
91
|
+
message: "Monitor tool must be a non-empty literal string."
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (name === null) {
|
|
95
|
+
issues.push({
|
|
96
|
+
severity: "error",
|
|
97
|
+
code: "invalid_monitor_name",
|
|
98
|
+
path: `${path}.name`,
|
|
99
|
+
message: "Monitor name must be a non-empty string when supplied."
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (!payload) {
|
|
103
|
+
issues.push({
|
|
104
|
+
severity: "error",
|
|
105
|
+
code: "invalid_monitor_payload",
|
|
106
|
+
path: `${path}.payload`,
|
|
107
|
+
message: "Monitor payload must be an object."
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
json(payload, `${path}.payload`, issues);
|
|
111
|
+
}
|
|
112
|
+
if (!key || !tool || !payload || name === null) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
key,
|
|
117
|
+
tool,
|
|
118
|
+
...name ? { name } : {},
|
|
119
|
+
payload
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function issueOrder(left, right) {
|
|
123
|
+
return left.path.localeCompare(right.path) || left.severity.localeCompare(right.severity) || left.code.localeCompare(right.code);
|
|
124
|
+
}
|
|
125
|
+
function validateHandlebars(value, path, issues) {
|
|
126
|
+
if (!value.includes("{{")) return;
|
|
127
|
+
try {
|
|
128
|
+
Handlebars.parse(value);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
issues.push({
|
|
131
|
+
severity: "error",
|
|
132
|
+
code: "invalid_handlebars_template",
|
|
133
|
+
path,
|
|
134
|
+
message: error instanceof Error ? error.message : "Invalid Handlebars template."
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const expressions = value.match(/{{[\s\S]*?}}/g) ?? [];
|
|
139
|
+
if (expressions.some(
|
|
140
|
+
(expression) => !/^{{\s*[A-Za-z_][A-Za-z0-9_]*\s*}}$/.test(expression)
|
|
141
|
+
)) {
|
|
142
|
+
issues.push({
|
|
143
|
+
severity: "error",
|
|
144
|
+
code: "unsupported_handlebars_expression",
|
|
145
|
+
path,
|
|
146
|
+
message: "Fleet templates allow only direct source-row fields such as {{domain}}; helpers, blocks, partials, paths, and prototype access are disabled."
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function walkTemplates(value, path, issues) {
|
|
151
|
+
if (typeof value === "string") return validateHandlebars(value, path, issues);
|
|
152
|
+
if (Array.isArray(value)) {
|
|
153
|
+
value.forEach(
|
|
154
|
+
(item, index) => walkTemplates(item, `${path}.${index}`, issues)
|
|
155
|
+
);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const input = record(value);
|
|
159
|
+
if (!input) return;
|
|
160
|
+
for (const [key, child] of Object.entries(input)) {
|
|
161
|
+
walkTemplates(child, `${path}.${key}`, issues);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function lintMonitorSpecTemplate(value, path = "monitor") {
|
|
165
|
+
const issues = [];
|
|
166
|
+
const normalized = monitorSpec(value, path, issues);
|
|
167
|
+
const input = record(value);
|
|
168
|
+
if (input && typeof input.tool === "string" && input.tool.includes("{{")) {
|
|
169
|
+
issues.push({
|
|
170
|
+
severity: "error",
|
|
171
|
+
code: "templated_monitor_tool",
|
|
172
|
+
path: `${path}.tool`,
|
|
173
|
+
message: "Fleet monitor tool must be literal; only key, name, and payload may be templated."
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (input) {
|
|
177
|
+
for (const field of ["key", "name", "payload"]) {
|
|
178
|
+
if (input[field] !== void 0)
|
|
179
|
+
walkTemplates(input[field], `${path}.${field}`, issues);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const ordered = issues.sort(issueOrder);
|
|
183
|
+
return ordered.some((issue) => issue.severity === "error") ? { issues: ordered } : { normalized, issues: ordered };
|
|
184
|
+
}
|
|
185
|
+
|
|
1
186
|
// ../integration-contracts/monitor-fleet-contract.ts
|
|
2
187
|
var MONITOR_FLEET_MAX_MEMBERS = 1e3;
|
|
3
188
|
var MONITOR_FLEET_FRONTIER_MAX_ROWS = 1e4;
|
|
4
|
-
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION =
|
|
189
|
+
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION = 2;
|
|
5
190
|
var SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS = [
|
|
6
|
-
|
|
191
|
+
1,
|
|
192
|
+
2
|
|
7
193
|
];
|
|
8
194
|
var MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG = [
|
|
9
195
|
{
|
|
10
196
|
edition: 1,
|
|
11
|
-
changed: "Initial Fleet JSON contract: bounded sticky table membership,
|
|
197
|
+
changed: "Initial Fleet JSON contract: bounded sticky table membership, source refresh, and a seven-day removal grace.",
|
|
198
|
+
compatibilityOwner: "Monitors Runtime",
|
|
199
|
+
newWritesEnd: null,
|
|
200
|
+
readerRemoval: null
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
edition: 2,
|
|
204
|
+
changed: "Canonical table-backed Fleet envelope with one Handlebars MonitorSpec template.",
|
|
12
205
|
compatibilityOwner: "Monitors Runtime",
|
|
13
206
|
newWritesEnd: null,
|
|
14
207
|
readerRemoval: null
|
|
@@ -20,11 +213,11 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
20
213
|
authoredFields: {
|
|
21
214
|
id: "Stable lowercase fleet id used by sync, get, and deactivate.",
|
|
22
215
|
source: "Customer DB table, unique row key, and optional equality filters that select candidate rows.",
|
|
23
|
-
|
|
216
|
+
monitor: "One Batch-compatible MonitorSpec. Direct Handlebars source-row fields such as {{domain}} are rendered for each selected row.",
|
|
24
217
|
selection: "Deterministic ranking and an active-member limit between 1 and 1,000."
|
|
25
218
|
},
|
|
26
219
|
fixedPolicy: {
|
|
27
|
-
cadence: "
|
|
220
|
+
cadence: "60s fair bounded source refresh",
|
|
28
221
|
membership: "sticky",
|
|
29
222
|
removalGrace: "7d",
|
|
30
223
|
onRemoved: "deactivate",
|
|
@@ -32,9 +225,10 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
32
225
|
billing: "No Fleet fee, permit, or renewal. Dry-run reports ordinary monitor lifecycle charges due now."
|
|
33
226
|
},
|
|
34
227
|
stickyMembership: "Ranking fills vacancies but never replaces a current member merely because another row ranks higher.",
|
|
35
|
-
|
|
228
|
+
templating: "Use direct Handlebars fields such as {{domain}} in monitor key, name, or payload. Helpers, blocks, partials, paths, and prototype access are disabled.",
|
|
229
|
+
legacyCompatibility: 'Edition 1 member/$fleet files remain readable with a lint warning; new files should use version: 2, kind: "fleet", and monitor.'
|
|
36
230
|
};
|
|
37
|
-
function
|
|
231
|
+
function record2(value) {
|
|
38
232
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
39
233
|
}
|
|
40
234
|
function identifier(value) {
|
|
@@ -59,7 +253,7 @@ function jsonValue(value, path, issues) {
|
|
|
59
253
|
value.forEach((item, index) => jsonValue(item, `${path}.${index}`, issues));
|
|
60
254
|
return;
|
|
61
255
|
}
|
|
62
|
-
const input =
|
|
256
|
+
const input = record2(value);
|
|
63
257
|
if (!input) {
|
|
64
258
|
issues.push({
|
|
65
259
|
path,
|
|
@@ -85,7 +279,7 @@ function defineMonitorFleet(definition) {
|
|
|
85
279
|
return definition;
|
|
86
280
|
}
|
|
87
281
|
function validateExpression(value, path, issues) {
|
|
88
|
-
const input =
|
|
282
|
+
const input = record2(value);
|
|
89
283
|
if (!input || input.$fleet !== "column" || !identifier(input.name)) {
|
|
90
284
|
issues.push({
|
|
91
285
|
path,
|
|
@@ -103,7 +297,7 @@ function walkPayload(value, path, issues) {
|
|
|
103
297
|
);
|
|
104
298
|
return;
|
|
105
299
|
}
|
|
106
|
-
const input =
|
|
300
|
+
const input = record2(value);
|
|
107
301
|
if (!input) return;
|
|
108
302
|
if ("$fleet" in input) {
|
|
109
303
|
validateExpression(input, path, issues);
|
|
@@ -113,9 +307,9 @@ function walkPayload(value, path, issues) {
|
|
|
113
307
|
walkPayload(item, `${path}.${key}`, issues);
|
|
114
308
|
}
|
|
115
309
|
}
|
|
116
|
-
function
|
|
310
|
+
function validateMonitorFleetDefinitionV1(value) {
|
|
117
311
|
const issues = [];
|
|
118
|
-
const input =
|
|
312
|
+
const input = record2(value);
|
|
119
313
|
if (!input) {
|
|
120
314
|
return {
|
|
121
315
|
valid: false,
|
|
@@ -136,7 +330,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
136
330
|
message: "Fleet id must be lowercase kebab-case."
|
|
137
331
|
});
|
|
138
332
|
}
|
|
139
|
-
const source =
|
|
333
|
+
const source = record2(input.source);
|
|
140
334
|
exactKeys(
|
|
141
335
|
source,
|
|
142
336
|
["kind", "schema", "table", "key", "where"],
|
|
@@ -157,7 +351,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
157
351
|
message: "Source schema and table must be SQL identifiers."
|
|
158
352
|
});
|
|
159
353
|
}
|
|
160
|
-
const sourceKey =
|
|
354
|
+
const sourceKey = record2(source?.key);
|
|
161
355
|
exactKeys(sourceKey, ["column", "type"], "source.key", issues);
|
|
162
356
|
if (!sourceKey || !identifier(sourceKey.column) || !["text", "uuid", "int4", "int8"].includes(String(sourceKey.type))) {
|
|
163
357
|
issues.push({
|
|
@@ -167,7 +361,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
167
361
|
});
|
|
168
362
|
}
|
|
169
363
|
if (source?.where !== void 0) {
|
|
170
|
-
const where =
|
|
364
|
+
const where = record2(source.where);
|
|
171
365
|
if (!where) {
|
|
172
366
|
issues.push({
|
|
173
367
|
path: "source.where",
|
|
@@ -193,9 +387,9 @@ function validateMonitorFleetDefinition(value) {
|
|
|
193
387
|
}
|
|
194
388
|
}
|
|
195
389
|
}
|
|
196
|
-
const member =
|
|
390
|
+
const member = record2(input.member);
|
|
197
391
|
exactKeys(member, ["tool", "key", "payload"], "member", issues);
|
|
198
|
-
const key =
|
|
392
|
+
const key = record2(member?.key);
|
|
199
393
|
if (!member || typeof member.tool !== "string" || !member.tool.trim()) {
|
|
200
394
|
issues.push({
|
|
201
395
|
path: "member.tool",
|
|
@@ -216,7 +410,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
216
410
|
validateExpression(part, `member.key.parts.${index}`, issues);
|
|
217
411
|
}
|
|
218
412
|
if (sourceKey && !key.parts.some(
|
|
219
|
-
(part) =>
|
|
413
|
+
(part) => record2(part)?.$fleet === "column" && record2(part)?.name === sourceKey.column
|
|
220
414
|
)) {
|
|
221
415
|
issues.push({
|
|
222
416
|
path: "member.key.parts",
|
|
@@ -235,7 +429,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
235
429
|
jsonValue(member.payload, "member.payload", issues);
|
|
236
430
|
}
|
|
237
431
|
walkPayload(member?.payload, "member.payload", issues);
|
|
238
|
-
const selection =
|
|
432
|
+
const selection = record2(input.selection);
|
|
239
433
|
exactKeys(selection, ["limit", "orderBy", "membership"], "selection", issues);
|
|
240
434
|
if (!selection || !Number.isSafeInteger(selection.limit) || Number(selection.limit) < 1 || Number(selection.limit) > MONITOR_FLEET_MAX_MEMBERS) {
|
|
241
435
|
issues.push({
|
|
@@ -261,7 +455,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
261
455
|
} else {
|
|
262
456
|
const orderColumns = /* @__PURE__ */ new Set();
|
|
263
457
|
for (const [index, order] of orderBy.entries()) {
|
|
264
|
-
const item =
|
|
458
|
+
const item = record2(order);
|
|
265
459
|
exactKeys(
|
|
266
460
|
item,
|
|
267
461
|
["column", "direction"],
|
|
@@ -284,7 +478,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
284
478
|
orderColumns.add(item.column);
|
|
285
479
|
}
|
|
286
480
|
}
|
|
287
|
-
if (sourceKey &&
|
|
481
|
+
if (sourceKey && record2(orderBy.at(-1))?.column !== sourceKey.column) {
|
|
288
482
|
issues.push({
|
|
289
483
|
path: "selection.orderBy",
|
|
290
484
|
code: "fleet_order_missing_key",
|
|
@@ -303,9 +497,104 @@ function validateMonitorFleetDefinition(value) {
|
|
|
303
497
|
}
|
|
304
498
|
return issues.length === 0 ? { valid: true, definition: value, issues } : { valid: false, issues };
|
|
305
499
|
}
|
|
306
|
-
function
|
|
500
|
+
function v2Issue(issue) {
|
|
501
|
+
return { path: issue.path, code: issue.code, message: issue.message };
|
|
502
|
+
}
|
|
503
|
+
function validateMonitorFleetDefinitionV2(value) {
|
|
504
|
+
const issues = [];
|
|
505
|
+
const input = record2(value);
|
|
506
|
+
if (!input) {
|
|
507
|
+
return {
|
|
508
|
+
valid: false,
|
|
509
|
+
issues: [
|
|
510
|
+
{
|
|
511
|
+
path: "",
|
|
512
|
+
code: "invalid_fleet",
|
|
513
|
+
message: "Fleet must be an object."
|
|
514
|
+
}
|
|
515
|
+
]
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
exactKeys(
|
|
519
|
+
input,
|
|
520
|
+
["version", "kind", "id", "source", "monitor", "selection"],
|
|
521
|
+
"",
|
|
522
|
+
issues
|
|
523
|
+
);
|
|
524
|
+
if (input.version !== 2) {
|
|
525
|
+
issues.push({
|
|
526
|
+
path: "version",
|
|
527
|
+
code: "unsupported_fleet_version",
|
|
528
|
+
message: "Canonical Fleet input version must be 2."
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
if (input.kind !== "fleet") {
|
|
532
|
+
issues.push({
|
|
533
|
+
path: "kind",
|
|
534
|
+
code: "invalid_fleet_kind",
|
|
535
|
+
message: 'Canonical Fleet input kind must be "fleet".'
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
const source = record2(input.source);
|
|
539
|
+
const sourceKey = record2(source?.key);
|
|
540
|
+
const legacyShape = {
|
|
541
|
+
id: input.id,
|
|
542
|
+
source: input.source,
|
|
543
|
+
member: {
|
|
544
|
+
tool: record2(input.monitor)?.tool,
|
|
545
|
+
key: {
|
|
546
|
+
$fleet: "template",
|
|
547
|
+
parts: [
|
|
548
|
+
"fleet-",
|
|
549
|
+
{
|
|
550
|
+
$fleet: "column",
|
|
551
|
+
name: typeof sourceKey?.column === "string" ? sourceKey.column : "invalid_source_key"
|
|
552
|
+
}
|
|
553
|
+
]
|
|
554
|
+
},
|
|
555
|
+
payload: record2(input.monitor)?.payload
|
|
556
|
+
},
|
|
557
|
+
selection: input.selection
|
|
558
|
+
};
|
|
559
|
+
const legacyCheck = validateMonitorFleetDefinitionV1(legacyShape);
|
|
560
|
+
for (const issue of legacyCheck.issues) {
|
|
561
|
+
issues.push({
|
|
562
|
+
...issue,
|
|
563
|
+
path: issue.path.replace(/^member\./, "monitor.")
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
const monitorInput = record2(input.monitor);
|
|
567
|
+
const monitorKey = monitorInput?.key;
|
|
568
|
+
const monitor = lintMonitorSpecTemplate(input.monitor);
|
|
569
|
+
issues.push(...monitor.issues.map(v2Issue));
|
|
570
|
+
if (typeof monitorKey === "string" && typeof sourceKey?.column === "string" && !monitorKey.includes(`{{${sourceKey.column}}}`)) {
|
|
571
|
+
issues.push({
|
|
572
|
+
path: "monitor.key",
|
|
573
|
+
code: "fleet_key_missing_source_key",
|
|
574
|
+
message: "Monitor key must include the source key as a direct Handlebars expression."
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
return issues.length === 0 && monitor.normalized ? {
|
|
578
|
+
valid: true,
|
|
579
|
+
definition: {
|
|
580
|
+
version: 2,
|
|
581
|
+
kind: "fleet",
|
|
582
|
+
id: String(input.id),
|
|
583
|
+
source: input.source,
|
|
584
|
+
monitor: monitor.normalized,
|
|
585
|
+
selection: input.selection
|
|
586
|
+
},
|
|
587
|
+
issues
|
|
588
|
+
} : { valid: false, issues };
|
|
589
|
+
}
|
|
590
|
+
function validateMonitorFleetDefinition(value) {
|
|
591
|
+
const input = record2(value);
|
|
592
|
+
return input?.version === 2 || input?.kind === "fleet" ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
593
|
+
}
|
|
594
|
+
function admitMonitorFleetAuthoringContract(value, edition) {
|
|
595
|
+
const detectedEdition = edition ?? (record2(value)?.version === 2 || record2(value)?.kind === "fleet" ? 2 : 1);
|
|
307
596
|
if (!SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS.includes(
|
|
308
|
-
|
|
597
|
+
detectedEdition
|
|
309
598
|
)) {
|
|
310
599
|
return {
|
|
311
600
|
valid: false,
|
|
@@ -313,16 +602,16 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
313
602
|
{
|
|
314
603
|
path: "authoring_contract_edition",
|
|
315
604
|
code: "unsupported_fleet_authoring_contract_edition",
|
|
316
|
-
message: `Monitor Fleet authoring contract edition ${
|
|
605
|
+
message: `Monitor Fleet authoring contract edition ${detectedEdition} is not supported.`
|
|
317
606
|
}
|
|
318
607
|
]
|
|
319
608
|
};
|
|
320
609
|
}
|
|
321
|
-
const result =
|
|
610
|
+
const result = detectedEdition === 2 ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
322
611
|
return result.valid && result.definition ? {
|
|
323
612
|
valid: true,
|
|
324
613
|
contract: {
|
|
325
|
-
edition,
|
|
614
|
+
edition: detectedEdition,
|
|
326
615
|
// JSON cloning prevents later caller mutation from changing the
|
|
327
616
|
// admitted snapshot.
|
|
328
617
|
definition: JSON.parse(
|
|
@@ -332,10 +621,36 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
332
621
|
issues: []
|
|
333
622
|
} : { valid: false, issues: result.issues };
|
|
334
623
|
}
|
|
624
|
+
function lintMonitorFleetInput(value) {
|
|
625
|
+
const input = record2(value);
|
|
626
|
+
const legacy = input?.member !== void 0;
|
|
627
|
+
const admitted = admitMonitorFleetAuthoringContract(value);
|
|
628
|
+
const issues = admitted.issues.map((issue) => ({
|
|
629
|
+
...issue,
|
|
630
|
+
severity: "error"
|
|
631
|
+
}));
|
|
632
|
+
if (legacy) {
|
|
633
|
+
issues.push({
|
|
634
|
+
severity: "warning",
|
|
635
|
+
code: "deprecated_fleet_member",
|
|
636
|
+
path: "member",
|
|
637
|
+
message: "Legacy Fleet member syntax remains supported."
|
|
638
|
+
});
|
|
639
|
+
if (JSON.stringify(input).includes('"$fleet"')) {
|
|
640
|
+
issues.push({
|
|
641
|
+
severity: "warning",
|
|
642
|
+
code: "deprecated_fleet_expression",
|
|
643
|
+
path: "member",
|
|
644
|
+
message: "Legacy $fleet expressions remain supported; use direct Handlebars fields in monitor instead."
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return admitted.valid && admitted.contract ? { normalized: admitted.contract.definition, issues } : { issues };
|
|
649
|
+
}
|
|
335
650
|
function evaluateMonitorFleetValue(value, row) {
|
|
336
651
|
if (Array.isArray(value))
|
|
337
652
|
return value.map((item) => evaluateMonitorFleetValue(item, row));
|
|
338
|
-
const input =
|
|
653
|
+
const input = record2(value);
|
|
339
654
|
if (!input) return value;
|
|
340
655
|
if (input.$fleet === "column") return row[String(input.name)];
|
|
341
656
|
if (input.$fleet === "template" && Array.isArray(input.parts)) {
|
|
@@ -363,5 +678,6 @@ export {
|
|
|
363
678
|
evaluateMonitorFleetValue,
|
|
364
679
|
fleetColumn,
|
|
365
680
|
fleetKey,
|
|
681
|
+
lintMonitorFleetInput,
|
|
366
682
|
validateMonitorFleetDefinition
|
|
367
683
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/// <reference path="./text-imports.d.ts" />
|
|
2
|
+
type MonitorInputIssue = {
|
|
3
|
+
severity: 'error' | 'warning' | 'info';
|
|
4
|
+
code: string;
|
|
5
|
+
path: string;
|
|
6
|
+
message: string;
|
|
7
|
+
suggestion?: string;
|
|
8
|
+
};
|
|
9
|
+
type MonitorSpec = {
|
|
10
|
+
key: string;
|
|
11
|
+
tool: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
payload: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
type BatchMonitorInput = {
|
|
16
|
+
version: 1;
|
|
17
|
+
kind: 'batch';
|
|
18
|
+
monitors: MonitorSpec[];
|
|
19
|
+
};
|
|
20
|
+
declare function lintMonitorBatchInput(value: unknown): {
|
|
21
|
+
normalized?: BatchMonitorInput;
|
|
22
|
+
issues: MonitorInputIssue[];
|
|
23
|
+
};
|
|
24
|
+
declare function lintMonitorSpecTemplate(value: unknown, path?: string): {
|
|
25
|
+
normalized?: MonitorSpec;
|
|
26
|
+
issues: MonitorInputIssue[];
|
|
27
|
+
};
|
|
28
|
+
declare function renderMonitorSpecTemplate(template: MonitorSpec, row: Record<string, unknown>): {
|
|
29
|
+
spec?: MonitorSpec;
|
|
30
|
+
issues: MonitorInputIssue[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { type BatchMonitorInput, type MonitorInputIssue, type MonitorSpec, lintMonitorBatchInput, lintMonitorSpecTemplate, renderMonitorSpecTemplate };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/// <reference path="./text-imports.d.ts" />
|
|
2
|
+
type MonitorInputIssue = {
|
|
3
|
+
severity: 'error' | 'warning' | 'info';
|
|
4
|
+
code: string;
|
|
5
|
+
path: string;
|
|
6
|
+
message: string;
|
|
7
|
+
suggestion?: string;
|
|
8
|
+
};
|
|
9
|
+
type MonitorSpec = {
|
|
10
|
+
key: string;
|
|
11
|
+
tool: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
payload: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
type BatchMonitorInput = {
|
|
16
|
+
version: 1;
|
|
17
|
+
kind: 'batch';
|
|
18
|
+
monitors: MonitorSpec[];
|
|
19
|
+
};
|
|
20
|
+
declare function lintMonitorBatchInput(value: unknown): {
|
|
21
|
+
normalized?: BatchMonitorInput;
|
|
22
|
+
issues: MonitorInputIssue[];
|
|
23
|
+
};
|
|
24
|
+
declare function lintMonitorSpecTemplate(value: unknown, path?: string): {
|
|
25
|
+
normalized?: MonitorSpec;
|
|
26
|
+
issues: MonitorInputIssue[];
|
|
27
|
+
};
|
|
28
|
+
declare function renderMonitorSpecTemplate(template: MonitorSpec, row: Record<string, unknown>): {
|
|
29
|
+
spec?: MonitorSpec;
|
|
30
|
+
issues: MonitorInputIssue[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { type BatchMonitorInput, type MonitorInputIssue, type MonitorSpec, lintMonitorBatchInput, lintMonitorSpecTemplate, renderMonitorSpecTemplate };
|