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,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/monitor-fleet-contract.ts
|
|
@@ -32,21 +42,215 @@ __export(monitor_fleet_contract_exports, {
|
|
|
32
42
|
evaluateMonitorFleetValue: () => evaluateMonitorFleetValue,
|
|
33
43
|
fleetColumn: () => fleetColumn,
|
|
34
44
|
fleetKey: () => fleetKey,
|
|
45
|
+
lintMonitorFleetInput: () => lintMonitorFleetInput,
|
|
35
46
|
validateMonitorFleetDefinition: () => validateMonitorFleetDefinition
|
|
36
47
|
});
|
|
37
48
|
module.exports = __toCommonJS(monitor_fleet_contract_exports);
|
|
38
49
|
|
|
50
|
+
// ../integration-contracts/monitor-input-contract.ts
|
|
51
|
+
var import_handlebars = __toESM(require("handlebars"), 1);
|
|
52
|
+
function authoredMonitorFieldPath(path, field) {
|
|
53
|
+
const batch = /^monitors\.(\d+)$/.exec(path);
|
|
54
|
+
return `${batch ? `monitors[${batch[1]}]` : path}.${field}`;
|
|
55
|
+
}
|
|
56
|
+
function record(value) {
|
|
57
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
function json(value, path, issues) {
|
|
60
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
value.forEach((child, index) => json(child, `${path}.${index}`, issues));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const object = record(value);
|
|
68
|
+
if (!object) {
|
|
69
|
+
issues.push({
|
|
70
|
+
severity: "error",
|
|
71
|
+
code: "invalid_monitor_json_value",
|
|
72
|
+
path,
|
|
73
|
+
message: "Monitor values must be JSON values."
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
for (const [key, child] of Object.entries(object)) {
|
|
78
|
+
json(child, `${path}.${key}`, issues);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function monitorSpec(value, path, issues) {
|
|
82
|
+
const input = record(value);
|
|
83
|
+
if (!input) {
|
|
84
|
+
issues.push({
|
|
85
|
+
severity: "error",
|
|
86
|
+
code: "invalid_monitor_spec",
|
|
87
|
+
path,
|
|
88
|
+
message: "A monitor must be a JSON object."
|
|
89
|
+
});
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const allowed = /* @__PURE__ */ new Set(["key", "tool", "name", "payload"]);
|
|
93
|
+
for (const key2 of Object.keys(input)) {
|
|
94
|
+
if (!allowed.has(key2)) {
|
|
95
|
+
if (key2 === "controls") {
|
|
96
|
+
issues.push({
|
|
97
|
+
severity: "error",
|
|
98
|
+
code: "CONTROLS_NOT_SUPPORTED",
|
|
99
|
+
path: authoredMonitorFieldPath(path, "controls"),
|
|
100
|
+
message: "Operational controls are managed by Deepline and cannot be authored in Batch or Fleet JSON.",
|
|
101
|
+
suggestion: "Remove controls. Bind downstream automation in a Play with sqlListeners."
|
|
102
|
+
});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (key2 === "play") {
|
|
106
|
+
issues.push({
|
|
107
|
+
severity: "error",
|
|
108
|
+
code: "PLAY_BINDING_NOT_IN_MONITOR_SPEC",
|
|
109
|
+
path: authoredMonitorFieldPath(path, "play"),
|
|
110
|
+
message: "A monitor does not own Play bindings.",
|
|
111
|
+
suggestion: "Declare sqlListeners in the Play, or run: deepline plays bootstrap monitor-triggered --out on-event.play.ts"
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
issues.push({
|
|
116
|
+
severity: "error",
|
|
117
|
+
code: "unknown_monitor_field",
|
|
118
|
+
path: `${path}.${key2}`,
|
|
119
|
+
message: `Monitor definitions do not accept '${key2}'.`
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const key = typeof input.key === "string" ? input.key.trim() : "";
|
|
124
|
+
const tool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
125
|
+
const name = input.name === void 0 ? void 0 : typeof input.name === "string" && input.name.trim() ? input.name.trim() : null;
|
|
126
|
+
const payload = record(input.payload);
|
|
127
|
+
if (!key) {
|
|
128
|
+
issues.push({
|
|
129
|
+
severity: "error",
|
|
130
|
+
code: "invalid_monitor_key",
|
|
131
|
+
path: `${path}.key`,
|
|
132
|
+
message: "Monitor key must be a non-empty string."
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (!tool) {
|
|
136
|
+
issues.push({
|
|
137
|
+
severity: "error",
|
|
138
|
+
code: "invalid_monitor_tool",
|
|
139
|
+
path: `${path}.tool`,
|
|
140
|
+
message: "Monitor tool must be a non-empty literal string."
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (name === null) {
|
|
144
|
+
issues.push({
|
|
145
|
+
severity: "error",
|
|
146
|
+
code: "invalid_monitor_name",
|
|
147
|
+
path: `${path}.name`,
|
|
148
|
+
message: "Monitor name must be a non-empty string when supplied."
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (!payload) {
|
|
152
|
+
issues.push({
|
|
153
|
+
severity: "error",
|
|
154
|
+
code: "invalid_monitor_payload",
|
|
155
|
+
path: `${path}.payload`,
|
|
156
|
+
message: "Monitor payload must be an object."
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
json(payload, `${path}.payload`, issues);
|
|
160
|
+
}
|
|
161
|
+
if (!key || !tool || !payload || name === null) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
key,
|
|
166
|
+
tool,
|
|
167
|
+
...name ? { name } : {},
|
|
168
|
+
payload
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function issueOrder(left, right) {
|
|
172
|
+
return left.path.localeCompare(right.path) || left.severity.localeCompare(right.severity) || left.code.localeCompare(right.code);
|
|
173
|
+
}
|
|
174
|
+
function validateHandlebars(value, path, issues) {
|
|
175
|
+
if (!value.includes("{{")) return;
|
|
176
|
+
try {
|
|
177
|
+
import_handlebars.default.parse(value);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
issues.push({
|
|
180
|
+
severity: "error",
|
|
181
|
+
code: "invalid_handlebars_template",
|
|
182
|
+
path,
|
|
183
|
+
message: error instanceof Error ? error.message : "Invalid Handlebars template."
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const expressions = value.match(/{{[\s\S]*?}}/g) ?? [];
|
|
188
|
+
if (expressions.some(
|
|
189
|
+
(expression) => !/^{{\s*[A-Za-z_][A-Za-z0-9_]*\s*}}$/.test(expression)
|
|
190
|
+
)) {
|
|
191
|
+
issues.push({
|
|
192
|
+
severity: "error",
|
|
193
|
+
code: "unsupported_handlebars_expression",
|
|
194
|
+
path,
|
|
195
|
+
message: "Fleet templates allow only direct source-row fields such as {{domain}}; helpers, blocks, partials, paths, and prototype access are disabled."
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function walkTemplates(value, path, issues) {
|
|
200
|
+
if (typeof value === "string") return validateHandlebars(value, path, issues);
|
|
201
|
+
if (Array.isArray(value)) {
|
|
202
|
+
value.forEach(
|
|
203
|
+
(item, index) => walkTemplates(item, `${path}.${index}`, issues)
|
|
204
|
+
);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const input = record(value);
|
|
208
|
+
if (!input) return;
|
|
209
|
+
for (const [key, child] of Object.entries(input)) {
|
|
210
|
+
walkTemplates(child, `${path}.${key}`, issues);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function lintMonitorSpecTemplate(value, path = "monitor") {
|
|
214
|
+
const issues = [];
|
|
215
|
+
const normalized = monitorSpec(value, path, issues);
|
|
216
|
+
const input = record(value);
|
|
217
|
+
if (input && typeof input.tool === "string" && input.tool.includes("{{")) {
|
|
218
|
+
issues.push({
|
|
219
|
+
severity: "error",
|
|
220
|
+
code: "templated_monitor_tool",
|
|
221
|
+
path: `${path}.tool`,
|
|
222
|
+
message: "Fleet monitor tool must be literal; only key, name, and payload may be templated."
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (input) {
|
|
226
|
+
for (const field of ["key", "name", "payload"]) {
|
|
227
|
+
if (input[field] !== void 0)
|
|
228
|
+
walkTemplates(input[field], `${path}.${field}`, issues);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const ordered = issues.sort(issueOrder);
|
|
232
|
+
return ordered.some((issue) => issue.severity === "error") ? { issues: ordered } : { normalized, issues: ordered };
|
|
233
|
+
}
|
|
234
|
+
|
|
39
235
|
// ../integration-contracts/monitor-fleet-contract.ts
|
|
40
236
|
var MONITOR_FLEET_MAX_MEMBERS = 1e3;
|
|
41
237
|
var MONITOR_FLEET_FRONTIER_MAX_ROWS = 1e4;
|
|
42
|
-
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION =
|
|
238
|
+
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION = 2;
|
|
43
239
|
var SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS = [
|
|
44
|
-
|
|
240
|
+
1,
|
|
241
|
+
2
|
|
45
242
|
];
|
|
46
243
|
var MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG = [
|
|
47
244
|
{
|
|
48
245
|
edition: 1,
|
|
49
|
-
changed: "Initial Fleet JSON contract: bounded sticky table membership,
|
|
246
|
+
changed: "Initial Fleet JSON contract: bounded sticky table membership, source refresh, and a seven-day removal grace.",
|
|
247
|
+
compatibilityOwner: "Monitors Runtime",
|
|
248
|
+
newWritesEnd: null,
|
|
249
|
+
readerRemoval: null
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
edition: 2,
|
|
253
|
+
changed: "Canonical table-backed Fleet envelope with one Handlebars MonitorSpec template.",
|
|
50
254
|
compatibilityOwner: "Monitors Runtime",
|
|
51
255
|
newWritesEnd: null,
|
|
52
256
|
readerRemoval: null
|
|
@@ -58,11 +262,11 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
58
262
|
authoredFields: {
|
|
59
263
|
id: "Stable lowercase fleet id used by sync, get, and deactivate.",
|
|
60
264
|
source: "Customer DB table, unique row key, and optional equality filters that select candidate rows.",
|
|
61
|
-
|
|
265
|
+
monitor: "One Batch-compatible MonitorSpec. Direct Handlebars source-row fields such as {{domain}} are rendered for each selected row.",
|
|
62
266
|
selection: "Deterministic ranking and an active-member limit between 1 and 1,000."
|
|
63
267
|
},
|
|
64
268
|
fixedPolicy: {
|
|
65
|
-
cadence: "
|
|
269
|
+
cadence: "60s fair bounded source refresh",
|
|
66
270
|
membership: "sticky",
|
|
67
271
|
removalGrace: "7d",
|
|
68
272
|
onRemoved: "deactivate",
|
|
@@ -70,9 +274,10 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
70
274
|
billing: "No Fleet fee, permit, or renewal. Dry-run reports ordinary monitor lifecycle charges due now."
|
|
71
275
|
},
|
|
72
276
|
stickyMembership: "Ranking fills vacancies but never replaces a current member merely because another row ranks higher.",
|
|
73
|
-
|
|
277
|
+
templating: "Use direct Handlebars fields such as {{domain}} in monitor key, name, or payload. Helpers, blocks, partials, paths, and prototype access are disabled.",
|
|
278
|
+
legacyCompatibility: 'Edition 1 member/$fleet files remain readable with a lint warning; new files should use version: 2, kind: "fleet", and monitor.'
|
|
74
279
|
};
|
|
75
|
-
function
|
|
280
|
+
function record2(value) {
|
|
76
281
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
77
282
|
}
|
|
78
283
|
function identifier(value) {
|
|
@@ -97,7 +302,7 @@ function jsonValue(value, path, issues) {
|
|
|
97
302
|
value.forEach((item, index) => jsonValue(item, `${path}.${index}`, issues));
|
|
98
303
|
return;
|
|
99
304
|
}
|
|
100
|
-
const input =
|
|
305
|
+
const input = record2(value);
|
|
101
306
|
if (!input) {
|
|
102
307
|
issues.push({
|
|
103
308
|
path,
|
|
@@ -123,7 +328,7 @@ function defineMonitorFleet(definition) {
|
|
|
123
328
|
return definition;
|
|
124
329
|
}
|
|
125
330
|
function validateExpression(value, path, issues) {
|
|
126
|
-
const input =
|
|
331
|
+
const input = record2(value);
|
|
127
332
|
if (!input || input.$fleet !== "column" || !identifier(input.name)) {
|
|
128
333
|
issues.push({
|
|
129
334
|
path,
|
|
@@ -141,7 +346,7 @@ function walkPayload(value, path, issues) {
|
|
|
141
346
|
);
|
|
142
347
|
return;
|
|
143
348
|
}
|
|
144
|
-
const input =
|
|
349
|
+
const input = record2(value);
|
|
145
350
|
if (!input) return;
|
|
146
351
|
if ("$fleet" in input) {
|
|
147
352
|
validateExpression(input, path, issues);
|
|
@@ -151,9 +356,9 @@ function walkPayload(value, path, issues) {
|
|
|
151
356
|
walkPayload(item, `${path}.${key}`, issues);
|
|
152
357
|
}
|
|
153
358
|
}
|
|
154
|
-
function
|
|
359
|
+
function validateMonitorFleetDefinitionV1(value) {
|
|
155
360
|
const issues = [];
|
|
156
|
-
const input =
|
|
361
|
+
const input = record2(value);
|
|
157
362
|
if (!input) {
|
|
158
363
|
return {
|
|
159
364
|
valid: false,
|
|
@@ -174,7 +379,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
174
379
|
message: "Fleet id must be lowercase kebab-case."
|
|
175
380
|
});
|
|
176
381
|
}
|
|
177
|
-
const source =
|
|
382
|
+
const source = record2(input.source);
|
|
178
383
|
exactKeys(
|
|
179
384
|
source,
|
|
180
385
|
["kind", "schema", "table", "key", "where"],
|
|
@@ -195,7 +400,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
195
400
|
message: "Source schema and table must be SQL identifiers."
|
|
196
401
|
});
|
|
197
402
|
}
|
|
198
|
-
const sourceKey =
|
|
403
|
+
const sourceKey = record2(source?.key);
|
|
199
404
|
exactKeys(sourceKey, ["column", "type"], "source.key", issues);
|
|
200
405
|
if (!sourceKey || !identifier(sourceKey.column) || !["text", "uuid", "int4", "int8"].includes(String(sourceKey.type))) {
|
|
201
406
|
issues.push({
|
|
@@ -205,7 +410,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
205
410
|
});
|
|
206
411
|
}
|
|
207
412
|
if (source?.where !== void 0) {
|
|
208
|
-
const where =
|
|
413
|
+
const where = record2(source.where);
|
|
209
414
|
if (!where) {
|
|
210
415
|
issues.push({
|
|
211
416
|
path: "source.where",
|
|
@@ -231,9 +436,9 @@ function validateMonitorFleetDefinition(value) {
|
|
|
231
436
|
}
|
|
232
437
|
}
|
|
233
438
|
}
|
|
234
|
-
const member =
|
|
439
|
+
const member = record2(input.member);
|
|
235
440
|
exactKeys(member, ["tool", "key", "payload"], "member", issues);
|
|
236
|
-
const key =
|
|
441
|
+
const key = record2(member?.key);
|
|
237
442
|
if (!member || typeof member.tool !== "string" || !member.tool.trim()) {
|
|
238
443
|
issues.push({
|
|
239
444
|
path: "member.tool",
|
|
@@ -254,7 +459,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
254
459
|
validateExpression(part, `member.key.parts.${index}`, issues);
|
|
255
460
|
}
|
|
256
461
|
if (sourceKey && !key.parts.some(
|
|
257
|
-
(part) =>
|
|
462
|
+
(part) => record2(part)?.$fleet === "column" && record2(part)?.name === sourceKey.column
|
|
258
463
|
)) {
|
|
259
464
|
issues.push({
|
|
260
465
|
path: "member.key.parts",
|
|
@@ -273,7 +478,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
273
478
|
jsonValue(member.payload, "member.payload", issues);
|
|
274
479
|
}
|
|
275
480
|
walkPayload(member?.payload, "member.payload", issues);
|
|
276
|
-
const selection =
|
|
481
|
+
const selection = record2(input.selection);
|
|
277
482
|
exactKeys(selection, ["limit", "orderBy", "membership"], "selection", issues);
|
|
278
483
|
if (!selection || !Number.isSafeInteger(selection.limit) || Number(selection.limit) < 1 || Number(selection.limit) > MONITOR_FLEET_MAX_MEMBERS) {
|
|
279
484
|
issues.push({
|
|
@@ -299,7 +504,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
299
504
|
} else {
|
|
300
505
|
const orderColumns = /* @__PURE__ */ new Set();
|
|
301
506
|
for (const [index, order] of orderBy.entries()) {
|
|
302
|
-
const item =
|
|
507
|
+
const item = record2(order);
|
|
303
508
|
exactKeys(
|
|
304
509
|
item,
|
|
305
510
|
["column", "direction"],
|
|
@@ -322,7 +527,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
322
527
|
orderColumns.add(item.column);
|
|
323
528
|
}
|
|
324
529
|
}
|
|
325
|
-
if (sourceKey &&
|
|
530
|
+
if (sourceKey && record2(orderBy.at(-1))?.column !== sourceKey.column) {
|
|
326
531
|
issues.push({
|
|
327
532
|
path: "selection.orderBy",
|
|
328
533
|
code: "fleet_order_missing_key",
|
|
@@ -341,9 +546,104 @@ function validateMonitorFleetDefinition(value) {
|
|
|
341
546
|
}
|
|
342
547
|
return issues.length === 0 ? { valid: true, definition: value, issues } : { valid: false, issues };
|
|
343
548
|
}
|
|
344
|
-
function
|
|
549
|
+
function v2Issue(issue) {
|
|
550
|
+
return { path: issue.path, code: issue.code, message: issue.message };
|
|
551
|
+
}
|
|
552
|
+
function validateMonitorFleetDefinitionV2(value) {
|
|
553
|
+
const issues = [];
|
|
554
|
+
const input = record2(value);
|
|
555
|
+
if (!input) {
|
|
556
|
+
return {
|
|
557
|
+
valid: false,
|
|
558
|
+
issues: [
|
|
559
|
+
{
|
|
560
|
+
path: "",
|
|
561
|
+
code: "invalid_fleet",
|
|
562
|
+
message: "Fleet must be an object."
|
|
563
|
+
}
|
|
564
|
+
]
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
exactKeys(
|
|
568
|
+
input,
|
|
569
|
+
["version", "kind", "id", "source", "monitor", "selection"],
|
|
570
|
+
"",
|
|
571
|
+
issues
|
|
572
|
+
);
|
|
573
|
+
if (input.version !== 2) {
|
|
574
|
+
issues.push({
|
|
575
|
+
path: "version",
|
|
576
|
+
code: "unsupported_fleet_version",
|
|
577
|
+
message: "Canonical Fleet input version must be 2."
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
if (input.kind !== "fleet") {
|
|
581
|
+
issues.push({
|
|
582
|
+
path: "kind",
|
|
583
|
+
code: "invalid_fleet_kind",
|
|
584
|
+
message: 'Canonical Fleet input kind must be "fleet".'
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const source = record2(input.source);
|
|
588
|
+
const sourceKey = record2(source?.key);
|
|
589
|
+
const legacyShape = {
|
|
590
|
+
id: input.id,
|
|
591
|
+
source: input.source,
|
|
592
|
+
member: {
|
|
593
|
+
tool: record2(input.monitor)?.tool,
|
|
594
|
+
key: {
|
|
595
|
+
$fleet: "template",
|
|
596
|
+
parts: [
|
|
597
|
+
"fleet-",
|
|
598
|
+
{
|
|
599
|
+
$fleet: "column",
|
|
600
|
+
name: typeof sourceKey?.column === "string" ? sourceKey.column : "invalid_source_key"
|
|
601
|
+
}
|
|
602
|
+
]
|
|
603
|
+
},
|
|
604
|
+
payload: record2(input.monitor)?.payload
|
|
605
|
+
},
|
|
606
|
+
selection: input.selection
|
|
607
|
+
};
|
|
608
|
+
const legacyCheck = validateMonitorFleetDefinitionV1(legacyShape);
|
|
609
|
+
for (const issue of legacyCheck.issues) {
|
|
610
|
+
issues.push({
|
|
611
|
+
...issue,
|
|
612
|
+
path: issue.path.replace(/^member\./, "monitor.")
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
const monitorInput = record2(input.monitor);
|
|
616
|
+
const monitorKey = monitorInput?.key;
|
|
617
|
+
const monitor = lintMonitorSpecTemplate(input.monitor);
|
|
618
|
+
issues.push(...monitor.issues.map(v2Issue));
|
|
619
|
+
if (typeof monitorKey === "string" && typeof sourceKey?.column === "string" && !monitorKey.includes(`{{${sourceKey.column}}}`)) {
|
|
620
|
+
issues.push({
|
|
621
|
+
path: "monitor.key",
|
|
622
|
+
code: "fleet_key_missing_source_key",
|
|
623
|
+
message: "Monitor key must include the source key as a direct Handlebars expression."
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
return issues.length === 0 && monitor.normalized ? {
|
|
627
|
+
valid: true,
|
|
628
|
+
definition: {
|
|
629
|
+
version: 2,
|
|
630
|
+
kind: "fleet",
|
|
631
|
+
id: String(input.id),
|
|
632
|
+
source: input.source,
|
|
633
|
+
monitor: monitor.normalized,
|
|
634
|
+
selection: input.selection
|
|
635
|
+
},
|
|
636
|
+
issues
|
|
637
|
+
} : { valid: false, issues };
|
|
638
|
+
}
|
|
639
|
+
function validateMonitorFleetDefinition(value) {
|
|
640
|
+
const input = record2(value);
|
|
641
|
+
return input?.version === 2 || input?.kind === "fleet" ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
642
|
+
}
|
|
643
|
+
function admitMonitorFleetAuthoringContract(value, edition) {
|
|
644
|
+
const detectedEdition = edition ?? (record2(value)?.version === 2 || record2(value)?.kind === "fleet" ? 2 : 1);
|
|
345
645
|
if (!SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS.includes(
|
|
346
|
-
|
|
646
|
+
detectedEdition
|
|
347
647
|
)) {
|
|
348
648
|
return {
|
|
349
649
|
valid: false,
|
|
@@ -351,16 +651,16 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
351
651
|
{
|
|
352
652
|
path: "authoring_contract_edition",
|
|
353
653
|
code: "unsupported_fleet_authoring_contract_edition",
|
|
354
|
-
message: `Monitor Fleet authoring contract edition ${
|
|
654
|
+
message: `Monitor Fleet authoring contract edition ${detectedEdition} is not supported.`
|
|
355
655
|
}
|
|
356
656
|
]
|
|
357
657
|
};
|
|
358
658
|
}
|
|
359
|
-
const result =
|
|
659
|
+
const result = detectedEdition === 2 ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
360
660
|
return result.valid && result.definition ? {
|
|
361
661
|
valid: true,
|
|
362
662
|
contract: {
|
|
363
|
-
edition,
|
|
663
|
+
edition: detectedEdition,
|
|
364
664
|
// JSON cloning prevents later caller mutation from changing the
|
|
365
665
|
// admitted snapshot.
|
|
366
666
|
definition: JSON.parse(
|
|
@@ -370,10 +670,36 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
370
670
|
issues: []
|
|
371
671
|
} : { valid: false, issues: result.issues };
|
|
372
672
|
}
|
|
673
|
+
function lintMonitorFleetInput(value) {
|
|
674
|
+
const input = record2(value);
|
|
675
|
+
const legacy = input?.member !== void 0;
|
|
676
|
+
const admitted = admitMonitorFleetAuthoringContract(value);
|
|
677
|
+
const issues = admitted.issues.map((issue) => ({
|
|
678
|
+
...issue,
|
|
679
|
+
severity: "error"
|
|
680
|
+
}));
|
|
681
|
+
if (legacy) {
|
|
682
|
+
issues.push({
|
|
683
|
+
severity: "warning",
|
|
684
|
+
code: "deprecated_fleet_member",
|
|
685
|
+
path: "member",
|
|
686
|
+
message: "Legacy Fleet member syntax remains supported."
|
|
687
|
+
});
|
|
688
|
+
if (JSON.stringify(input).includes('"$fleet"')) {
|
|
689
|
+
issues.push({
|
|
690
|
+
severity: "warning",
|
|
691
|
+
code: "deprecated_fleet_expression",
|
|
692
|
+
path: "member",
|
|
693
|
+
message: "Legacy $fleet expressions remain supported; use direct Handlebars fields in monitor instead."
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return admitted.valid && admitted.contract ? { normalized: admitted.contract.definition, issues } : { issues };
|
|
698
|
+
}
|
|
373
699
|
function evaluateMonitorFleetValue(value, row) {
|
|
374
700
|
if (Array.isArray(value))
|
|
375
701
|
return value.map((item) => evaluateMonitorFleetValue(item, row));
|
|
376
|
-
const input =
|
|
702
|
+
const input = record2(value);
|
|
377
703
|
if (!input) return value;
|
|
378
704
|
if (input.$fleet === "column") return row[String(input.name)];
|
|
379
705
|
if (input.$fleet === "template" && Array.isArray(input.parts)) {
|
|
@@ -402,5 +728,6 @@ function evaluateMonitorFleetValue(value, row) {
|
|
|
402
728
|
evaluateMonitorFleetValue,
|
|
403
729
|
fleetColumn,
|
|
404
730
|
fleetKey,
|
|
731
|
+
lintMonitorFleetInput,
|
|
405
732
|
validateMonitorFleetDefinition
|
|
406
733
|
});
|