deepline 0.3.96 → 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 +1005 -290
- package/dist/cli/index.mjs +1005 -290
- 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
package/dist/index.js
CHANGED
|
@@ -84,6 +84,10 @@ __export(src_exports, {
|
|
|
84
84
|
isPlayBootstrapTemplate: () => isPlayBootstrapTemplate,
|
|
85
85
|
isProviderUnavailable: () => isProviderUnavailable,
|
|
86
86
|
isProviderWaterfallUnavailableError: () => isProviderWaterfallUnavailableError,
|
|
87
|
+
lintMonitorBatchInput: () => lintMonitorBatchInput,
|
|
88
|
+
lintMonitorFleetInput: () => lintMonitorFleetInput,
|
|
89
|
+
lintMonitorSpecTemplate: () => lintMonitorSpecTemplate,
|
|
90
|
+
renderMonitorSpecTemplate: () => renderMonitorSpecTemplate,
|
|
87
91
|
resolveConfig: () => resolveConfig,
|
|
88
92
|
runIf: () => runIf,
|
|
89
93
|
steps: () => steps,
|
|
@@ -856,7 +860,7 @@ var SDK_RELEASE = {
|
|
|
856
860
|
// getters keep their established compatibility behavior.
|
|
857
861
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
858
862
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
859
|
-
version: "0.3.
|
|
863
|
+
version: "0.3.98",
|
|
860
864
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
861
865
|
packageCapabilities: {
|
|
862
866
|
updatePreferences: 1
|
|
@@ -2894,9 +2898,9 @@ function nonEmptyString(value) {
|
|
|
2894
2898
|
}
|
|
2895
2899
|
function normalizeDatasetBornFrom(value) {
|
|
2896
2900
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2897
|
-
const
|
|
2898
|
-
const table = nonEmptyString(
|
|
2899
|
-
const rowCountIn = finiteNonNegativeInteger2(
|
|
2901
|
+
const record3 = value;
|
|
2902
|
+
const table = nonEmptyString(record3.table);
|
|
2903
|
+
const rowCountIn = finiteNonNegativeInteger2(record3.rowCountIn);
|
|
2900
2904
|
if (!table || rowCountIn === null) return null;
|
|
2901
2905
|
return { table, rowCountIn };
|
|
2902
2906
|
}
|
|
@@ -3985,21 +3989,21 @@ function normalizePlayRuntimeNamespace(value) {
|
|
|
3985
3989
|
}
|
|
3986
3990
|
function normalizePlayRuntimeSelection(value) {
|
|
3987
3991
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
3988
|
-
const
|
|
3989
|
-
if (Object.keys(
|
|
3992
|
+
const record3 = value;
|
|
3993
|
+
if (Object.keys(record3).some(
|
|
3990
3994
|
(key) => key !== "environment" && key !== "namespace" && key !== "backend"
|
|
3991
|
-
) ||
|
|
3995
|
+
) || record3.environment !== "preview") {
|
|
3992
3996
|
return null;
|
|
3993
3997
|
}
|
|
3994
|
-
const namespace = normalizePlayRuntimeNamespace(
|
|
3998
|
+
const namespace = normalizePlayRuntimeNamespace(record3.namespace);
|
|
3995
3999
|
if (!namespace) return null;
|
|
3996
|
-
if (
|
|
4000
|
+
if (record3.backend === void 0) {
|
|
3997
4001
|
return { environment: "preview", namespace };
|
|
3998
4002
|
}
|
|
3999
|
-
if (
|
|
4003
|
+
if (record3.backend !== PLAY_RUNTIME_BACKENDS.daytona && record3.backend !== PLAY_RUNTIME_BACKENDS.modal) {
|
|
4000
4004
|
return null;
|
|
4001
4005
|
}
|
|
4002
|
-
return { environment: "preview", namespace, backend:
|
|
4006
|
+
return { environment: "preview", namespace, backend: record3.backend };
|
|
4003
4007
|
}
|
|
4004
4008
|
function normalizePlayRuntimeEnvironment(value) {
|
|
4005
4009
|
return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
|
|
@@ -4644,10 +4648,18 @@ var DeeplineClient = class {
|
|
|
4644
4648
|
repair: (options2) => this.repairMonitors(options2),
|
|
4645
4649
|
health: (options2) => this.readMonitorsHealth(options2),
|
|
4646
4650
|
batch: {
|
|
4651
|
+
deploy: (definitions, options2) => this.deployMonitorBatch(definitions, options2),
|
|
4647
4652
|
submit: (options2) => this.submitMonitorBatch(options2),
|
|
4648
4653
|
get: (operationId, options2) => this.getMonitorBatch(operationId, options2)
|
|
4649
4654
|
},
|
|
4655
|
+
jobs: {
|
|
4656
|
+
get: (jobId) => this.getMonitorJob(jobId),
|
|
4657
|
+
logs: (jobId, options2) => this.getMonitorJobLogs(jobId, options2),
|
|
4658
|
+
cancel: (jobId) => this.cancelMonitorJob(jobId),
|
|
4659
|
+
wait: (jobId, options2) => this.waitForMonitorJob(jobId, options2)
|
|
4660
|
+
},
|
|
4650
4661
|
fleets: {
|
|
4662
|
+
deploy: (definitionOrId, options2) => this.syncMonitorFleet(definitionOrId, options2),
|
|
4651
4663
|
sync: (definitionOrId, options2) => this.syncMonitorFleet(definitionOrId, options2),
|
|
4652
4664
|
get: (fleetId, options2) => fleetId === void 0 ? this.listMonitorFleets() : this.getMonitorFleet(fleetId, options2),
|
|
4653
4665
|
list: () => this.listMonitorFleets(),
|
|
@@ -7399,6 +7411,68 @@ var DeeplineClient = class {
|
|
|
7399
7411
|
);
|
|
7400
7412
|
}
|
|
7401
7413
|
// ——————————————————————————————————————————————————————————
|
|
7414
|
+
// Monitor Jobs, Batches, and Fleets
|
|
7415
|
+
// ——————————————————————————————————————————————————————————
|
|
7416
|
+
monitorJobIdempotencyKey(operation) {
|
|
7417
|
+
const uuid = globalThis.crypto?.randomUUID?.();
|
|
7418
|
+
return `monitor-job-${operation}-${uuid ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
|
|
7419
|
+
}
|
|
7420
|
+
monitorJobHeaders(operation, idempotencyKey) {
|
|
7421
|
+
return {
|
|
7422
|
+
"Idempotency-Key": idempotencyKey?.trim() || this.monitorJobIdempotencyKey(operation)
|
|
7423
|
+
};
|
|
7424
|
+
}
|
|
7425
|
+
async deployMonitorBatch(definitions, options) {
|
|
7426
|
+
return this.http.request(
|
|
7427
|
+
"/api/v2/monitors/batch/deploy",
|
|
7428
|
+
{
|
|
7429
|
+
method: "POST",
|
|
7430
|
+
body: { version: 1, kind: "batch", monitors: definitions },
|
|
7431
|
+
headers: this.monitorJobHeaders(
|
|
7432
|
+
"batch-deploy",
|
|
7433
|
+
options?.idempotencyKey
|
|
7434
|
+
),
|
|
7435
|
+
maxRetries: 0,
|
|
7436
|
+
exactUrlOnly: true
|
|
7437
|
+
}
|
|
7438
|
+
);
|
|
7439
|
+
}
|
|
7440
|
+
async getMonitorJob(jobId) {
|
|
7441
|
+
return this.http.request(
|
|
7442
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}`,
|
|
7443
|
+
{ method: "GET" }
|
|
7444
|
+
);
|
|
7445
|
+
}
|
|
7446
|
+
async getMonitorJobLogs(jobId, options) {
|
|
7447
|
+
const query = options?.limit === void 0 ? "" : `?limit=${encodeURIComponent(String(options.limit))}`;
|
|
7448
|
+
return this.http.request(
|
|
7449
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}/logs${query}`,
|
|
7450
|
+
{ method: "GET" }
|
|
7451
|
+
);
|
|
7452
|
+
}
|
|
7453
|
+
async cancelMonitorJob(jobId) {
|
|
7454
|
+
return this.http.request(
|
|
7455
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}/cancel`,
|
|
7456
|
+
{ method: "POST", body: {}, maxRetries: 0, exactUrlOnly: true }
|
|
7457
|
+
);
|
|
7458
|
+
}
|
|
7459
|
+
async waitForMonitorJob(jobId, options) {
|
|
7460
|
+
const timeoutMs = Math.max(1, options?.timeoutMs ?? 10 * 6e4);
|
|
7461
|
+
const pollIntervalMs = Math.max(1, options?.pollIntervalMs ?? 2e3);
|
|
7462
|
+
const terminal = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
|
|
7463
|
+
const startedAt = Date.now();
|
|
7464
|
+
let latest = await this.getMonitorJob(jobId);
|
|
7465
|
+
while (true) {
|
|
7466
|
+
options?.onProgress?.(latest);
|
|
7467
|
+
const job = latest.job;
|
|
7468
|
+
const state = typeof job?.state === "string" ? job.state : void 0;
|
|
7469
|
+
if (state && terminal.has(state)) return latest;
|
|
7470
|
+
if (Date.now() - startedAt >= timeoutMs) return latest;
|
|
7471
|
+
await sleep2(pollIntervalMs);
|
|
7472
|
+
latest = await this.getMonitorJob(jobId);
|
|
7473
|
+
}
|
|
7474
|
+
}
|
|
7475
|
+
// ——————————————————————————————————————————————————————————
|
|
7402
7476
|
// Monitor Fleets
|
|
7403
7477
|
// ——————————————————————————————————————————————————————————
|
|
7404
7478
|
monitorFleetIdempotencyKey(operation) {
|
|
@@ -8557,8 +8631,8 @@ function firstExperienceDate(value) {
|
|
|
8557
8631
|
return null;
|
|
8558
8632
|
}
|
|
8559
8633
|
function normalizeJobChange(value) {
|
|
8560
|
-
const
|
|
8561
|
-
const nested = isRecord8(
|
|
8634
|
+
const record3 = isRecord8(value) ? value : {};
|
|
8635
|
+
const nested = isRecord8(record3.job_change) ? record3.job_change : record3;
|
|
8562
8636
|
const output = isRecord8(nested.output) ? nested.output : nested;
|
|
8563
8637
|
const person = isRecord8(output.person) ? output.person : {};
|
|
8564
8638
|
const status = normalizeJobChangeStatus(
|
|
@@ -11231,25 +11305,388 @@ function defineMonitor(definition) {
|
|
|
11231
11305
|
if (definition.name !== void 0 && typeof definition.name !== "string") {
|
|
11232
11306
|
throw new Error('defineMonitor(definition) "name" must be a string.');
|
|
11233
11307
|
}
|
|
11234
|
-
|
|
11308
|
+
const authored = definition;
|
|
11309
|
+
if ("controls" in authored) {
|
|
11235
11310
|
throw new Error(
|
|
11236
|
-
'defineMonitor(definition) "controls"
|
|
11311
|
+
'defineMonitor(definition) does not accept "controls". Operational controls are managed by Deepline; bind downstream automation in a Play with sqlListeners.'
|
|
11312
|
+
);
|
|
11313
|
+
}
|
|
11314
|
+
if ("play" in authored) {
|
|
11315
|
+
throw new Error(
|
|
11316
|
+
'defineMonitor(definition) does not accept "play". A monitor does not own Play bindings; declare sqlListeners in the Play instead.'
|
|
11237
11317
|
);
|
|
11238
11318
|
}
|
|
11239
11319
|
return definition;
|
|
11240
11320
|
}
|
|
11241
11321
|
|
|
11322
|
+
// ../integration-contracts/monitor-input-contract.ts
|
|
11323
|
+
var import_handlebars = __toESM(require("handlebars"), 1);
|
|
11324
|
+
function authoredMonitorFieldPath(path, field) {
|
|
11325
|
+
const batch = /^monitors\.(\d+)$/.exec(path);
|
|
11326
|
+
return `${batch ? `monitors[${batch[1]}]` : path}.${field}`;
|
|
11327
|
+
}
|
|
11328
|
+
function record(value) {
|
|
11329
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11330
|
+
}
|
|
11331
|
+
function json(value, path, issues) {
|
|
11332
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
|
|
11333
|
+
return;
|
|
11334
|
+
}
|
|
11335
|
+
if (Array.isArray(value)) {
|
|
11336
|
+
value.forEach((child, index) => json(child, `${path}.${index}`, issues));
|
|
11337
|
+
return;
|
|
11338
|
+
}
|
|
11339
|
+
const object = record(value);
|
|
11340
|
+
if (!object) {
|
|
11341
|
+
issues.push({
|
|
11342
|
+
severity: "error",
|
|
11343
|
+
code: "invalid_monitor_json_value",
|
|
11344
|
+
path,
|
|
11345
|
+
message: "Monitor values must be JSON values."
|
|
11346
|
+
});
|
|
11347
|
+
return;
|
|
11348
|
+
}
|
|
11349
|
+
for (const [key, child] of Object.entries(object)) {
|
|
11350
|
+
json(child, `${path}.${key}`, issues);
|
|
11351
|
+
}
|
|
11352
|
+
}
|
|
11353
|
+
function monitorSpec(value, path, issues) {
|
|
11354
|
+
const input = record(value);
|
|
11355
|
+
if (!input) {
|
|
11356
|
+
issues.push({
|
|
11357
|
+
severity: "error",
|
|
11358
|
+
code: "invalid_monitor_spec",
|
|
11359
|
+
path,
|
|
11360
|
+
message: "A monitor must be a JSON object."
|
|
11361
|
+
});
|
|
11362
|
+
return null;
|
|
11363
|
+
}
|
|
11364
|
+
const allowed = /* @__PURE__ */ new Set(["key", "tool", "name", "payload"]);
|
|
11365
|
+
for (const key2 of Object.keys(input)) {
|
|
11366
|
+
if (!allowed.has(key2)) {
|
|
11367
|
+
if (key2 === "controls") {
|
|
11368
|
+
issues.push({
|
|
11369
|
+
severity: "error",
|
|
11370
|
+
code: "CONTROLS_NOT_SUPPORTED",
|
|
11371
|
+
path: authoredMonitorFieldPath(path, "controls"),
|
|
11372
|
+
message: "Operational controls are managed by Deepline and cannot be authored in Batch or Fleet JSON.",
|
|
11373
|
+
suggestion: "Remove controls. Bind downstream automation in a Play with sqlListeners."
|
|
11374
|
+
});
|
|
11375
|
+
continue;
|
|
11376
|
+
}
|
|
11377
|
+
if (key2 === "play") {
|
|
11378
|
+
issues.push({
|
|
11379
|
+
severity: "error",
|
|
11380
|
+
code: "PLAY_BINDING_NOT_IN_MONITOR_SPEC",
|
|
11381
|
+
path: authoredMonitorFieldPath(path, "play"),
|
|
11382
|
+
message: "A monitor does not own Play bindings.",
|
|
11383
|
+
suggestion: "Declare sqlListeners in the Play, or run: deepline plays bootstrap monitor-triggered --out on-event.play.ts"
|
|
11384
|
+
});
|
|
11385
|
+
continue;
|
|
11386
|
+
}
|
|
11387
|
+
issues.push({
|
|
11388
|
+
severity: "error",
|
|
11389
|
+
code: "unknown_monitor_field",
|
|
11390
|
+
path: `${path}.${key2}`,
|
|
11391
|
+
message: `Monitor definitions do not accept '${key2}'.`
|
|
11392
|
+
});
|
|
11393
|
+
}
|
|
11394
|
+
}
|
|
11395
|
+
const key = typeof input.key === "string" ? input.key.trim() : "";
|
|
11396
|
+
const tool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
11397
|
+
const name = input.name === void 0 ? void 0 : typeof input.name === "string" && input.name.trim() ? input.name.trim() : null;
|
|
11398
|
+
const payload = record(input.payload);
|
|
11399
|
+
if (!key) {
|
|
11400
|
+
issues.push({
|
|
11401
|
+
severity: "error",
|
|
11402
|
+
code: "invalid_monitor_key",
|
|
11403
|
+
path: `${path}.key`,
|
|
11404
|
+
message: "Monitor key must be a non-empty string."
|
|
11405
|
+
});
|
|
11406
|
+
}
|
|
11407
|
+
if (!tool) {
|
|
11408
|
+
issues.push({
|
|
11409
|
+
severity: "error",
|
|
11410
|
+
code: "invalid_monitor_tool",
|
|
11411
|
+
path: `${path}.tool`,
|
|
11412
|
+
message: "Monitor tool must be a non-empty literal string."
|
|
11413
|
+
});
|
|
11414
|
+
}
|
|
11415
|
+
if (name === null) {
|
|
11416
|
+
issues.push({
|
|
11417
|
+
severity: "error",
|
|
11418
|
+
code: "invalid_monitor_name",
|
|
11419
|
+
path: `${path}.name`,
|
|
11420
|
+
message: "Monitor name must be a non-empty string when supplied."
|
|
11421
|
+
});
|
|
11422
|
+
}
|
|
11423
|
+
if (!payload) {
|
|
11424
|
+
issues.push({
|
|
11425
|
+
severity: "error",
|
|
11426
|
+
code: "invalid_monitor_payload",
|
|
11427
|
+
path: `${path}.payload`,
|
|
11428
|
+
message: "Monitor payload must be an object."
|
|
11429
|
+
});
|
|
11430
|
+
} else {
|
|
11431
|
+
json(payload, `${path}.payload`, issues);
|
|
11432
|
+
}
|
|
11433
|
+
if (!key || !tool || !payload || name === null) {
|
|
11434
|
+
return null;
|
|
11435
|
+
}
|
|
11436
|
+
return {
|
|
11437
|
+
key,
|
|
11438
|
+
tool,
|
|
11439
|
+
...name ? { name } : {},
|
|
11440
|
+
payload
|
|
11441
|
+
};
|
|
11442
|
+
}
|
|
11443
|
+
function issueOrder(left, right) {
|
|
11444
|
+
return left.path.localeCompare(right.path) || left.severity.localeCompare(right.severity) || left.code.localeCompare(right.code);
|
|
11445
|
+
}
|
|
11446
|
+
function lintMonitorBatchInput(value) {
|
|
11447
|
+
const issues = [];
|
|
11448
|
+
const versionlessEnvelope = !Array.isArray(value) && (() => {
|
|
11449
|
+
const candidate = record(value);
|
|
11450
|
+
return candidate !== null && Object.keys(candidate).length === 1 && Object.prototype.hasOwnProperty.call(candidate, "monitors");
|
|
11451
|
+
})();
|
|
11452
|
+
const legacy = Array.isArray(value) || versionlessEnvelope;
|
|
11453
|
+
const input = legacy ? null : record(value);
|
|
11454
|
+
let monitors = Array.isArray(value) ? value : versionlessEnvelope ? (() => {
|
|
11455
|
+
const candidate = record(value)?.monitors;
|
|
11456
|
+
return Array.isArray(candidate) ? candidate : null;
|
|
11457
|
+
})() : null;
|
|
11458
|
+
if (Array.isArray(value)) {
|
|
11459
|
+
issues.push({
|
|
11460
|
+
severity: "warning",
|
|
11461
|
+
code: "deprecated_batch_array",
|
|
11462
|
+
path: "",
|
|
11463
|
+
message: "Bare monitor arrays are supported for compatibility.",
|
|
11464
|
+
suggestion: 'Wrap the list in {"version":1,"kind":"batch","monitors":[...]}.'
|
|
11465
|
+
});
|
|
11466
|
+
} else if (versionlessEnvelope) {
|
|
11467
|
+
issues.push({
|
|
11468
|
+
severity: "warning",
|
|
11469
|
+
code: "deprecated_batch_missing_envelope_fields",
|
|
11470
|
+
path: "",
|
|
11471
|
+
message: "Versionless monitor envelopes are supported for compatibility.",
|
|
11472
|
+
suggestion: 'Add "version": 1 and "kind": "batch".'
|
|
11473
|
+
});
|
|
11474
|
+
if (!monitors) {
|
|
11475
|
+
issues.push({
|
|
11476
|
+
severity: "error",
|
|
11477
|
+
code: "invalid_batch_monitors",
|
|
11478
|
+
path: "monitors",
|
|
11479
|
+
message: "Batch monitors must be an array."
|
|
11480
|
+
});
|
|
11481
|
+
}
|
|
11482
|
+
} else if (!input) {
|
|
11483
|
+
issues.push({
|
|
11484
|
+
severity: "error",
|
|
11485
|
+
code: "invalid_batch",
|
|
11486
|
+
path: "",
|
|
11487
|
+
message: "Batch input must be an object or a legacy array."
|
|
11488
|
+
});
|
|
11489
|
+
} else {
|
|
11490
|
+
for (const key of Object.keys(input)) {
|
|
11491
|
+
if (!["version", "kind", "monitors"].includes(key)) {
|
|
11492
|
+
issues.push({
|
|
11493
|
+
severity: "error",
|
|
11494
|
+
code: key === "source" || key === "monitor" ? "batch_fleet_shape_mixup" : "unknown_batch_field",
|
|
11495
|
+
path: key,
|
|
11496
|
+
message: `Batch input does not accept '${key}'.`
|
|
11497
|
+
});
|
|
11498
|
+
}
|
|
11499
|
+
}
|
|
11500
|
+
if (input.version !== 1) {
|
|
11501
|
+
issues.push({
|
|
11502
|
+
severity: "error",
|
|
11503
|
+
code: "unsupported_batch_version",
|
|
11504
|
+
path: "version",
|
|
11505
|
+
message: "Batch input version must be 1."
|
|
11506
|
+
});
|
|
11507
|
+
}
|
|
11508
|
+
if (input.kind !== "batch") {
|
|
11509
|
+
issues.push({
|
|
11510
|
+
severity: "error",
|
|
11511
|
+
code: "invalid_batch_kind",
|
|
11512
|
+
path: "kind",
|
|
11513
|
+
message: 'Batch input kind must be "batch".'
|
|
11514
|
+
});
|
|
11515
|
+
}
|
|
11516
|
+
monitors = Array.isArray(input.monitors) ? input.monitors : null;
|
|
11517
|
+
if (!monitors) {
|
|
11518
|
+
issues.push({
|
|
11519
|
+
severity: "error",
|
|
11520
|
+
code: "invalid_batch_monitors",
|
|
11521
|
+
path: "monitors",
|
|
11522
|
+
message: "Batch monitors must be an array."
|
|
11523
|
+
});
|
|
11524
|
+
}
|
|
11525
|
+
}
|
|
11526
|
+
if (!monitors || monitors.length === 0) {
|
|
11527
|
+
if (monitors?.length === 0) {
|
|
11528
|
+
issues.push({
|
|
11529
|
+
severity: "error",
|
|
11530
|
+
code: "empty_batch",
|
|
11531
|
+
path: "monitors",
|
|
11532
|
+
message: "A Batch needs at least one monitor."
|
|
11533
|
+
});
|
|
11534
|
+
}
|
|
11535
|
+
return { issues: issues.sort(issueOrder) };
|
|
11536
|
+
}
|
|
11537
|
+
const normalized = monitors.map(
|
|
11538
|
+
(item, index) => monitorSpec(item, `monitors.${index}`, issues)
|
|
11539
|
+
);
|
|
11540
|
+
const seen = /* @__PURE__ */ new Map();
|
|
11541
|
+
normalized.forEach((item, index) => {
|
|
11542
|
+
if (!item) return;
|
|
11543
|
+
const first = seen.get(item.key);
|
|
11544
|
+
if (first !== void 0) {
|
|
11545
|
+
for (const position of [first, index]) {
|
|
11546
|
+
issues.push({
|
|
11547
|
+
severity: "error",
|
|
11548
|
+
code: "duplicate_monitor_key",
|
|
11549
|
+
path: `monitors.${position}.key`,
|
|
11550
|
+
message: `Duplicate monitor key '${item.key}'.`
|
|
11551
|
+
});
|
|
11552
|
+
}
|
|
11553
|
+
} else {
|
|
11554
|
+
seen.set(item.key, index);
|
|
11555
|
+
}
|
|
11556
|
+
});
|
|
11557
|
+
const ordered = issues.sort(issueOrder);
|
|
11558
|
+
return ordered.some((issue) => issue.severity === "error") ? { issues: ordered } : {
|
|
11559
|
+
normalized: {
|
|
11560
|
+
version: 1,
|
|
11561
|
+
kind: "batch",
|
|
11562
|
+
monitors: normalized
|
|
11563
|
+
},
|
|
11564
|
+
issues: ordered
|
|
11565
|
+
};
|
|
11566
|
+
}
|
|
11567
|
+
function validateHandlebars(value, path, issues) {
|
|
11568
|
+
if (!value.includes("{{")) return;
|
|
11569
|
+
try {
|
|
11570
|
+
import_handlebars.default.parse(value);
|
|
11571
|
+
} catch (error) {
|
|
11572
|
+
issues.push({
|
|
11573
|
+
severity: "error",
|
|
11574
|
+
code: "invalid_handlebars_template",
|
|
11575
|
+
path,
|
|
11576
|
+
message: error instanceof Error ? error.message : "Invalid Handlebars template."
|
|
11577
|
+
});
|
|
11578
|
+
return;
|
|
11579
|
+
}
|
|
11580
|
+
const expressions = value.match(/{{[\s\S]*?}}/g) ?? [];
|
|
11581
|
+
if (expressions.some(
|
|
11582
|
+
(expression) => !/^{{\s*[A-Za-z_][A-Za-z0-9_]*\s*}}$/.test(expression)
|
|
11583
|
+
)) {
|
|
11584
|
+
issues.push({
|
|
11585
|
+
severity: "error",
|
|
11586
|
+
code: "unsupported_handlebars_expression",
|
|
11587
|
+
path,
|
|
11588
|
+
message: "Fleet templates allow only direct source-row fields such as {{domain}}; helpers, blocks, partials, paths, and prototype access are disabled."
|
|
11589
|
+
});
|
|
11590
|
+
}
|
|
11591
|
+
}
|
|
11592
|
+
function walkTemplates(value, path, issues) {
|
|
11593
|
+
if (typeof value === "string") return validateHandlebars(value, path, issues);
|
|
11594
|
+
if (Array.isArray(value)) {
|
|
11595
|
+
value.forEach(
|
|
11596
|
+
(item, index) => walkTemplates(item, `${path}.${index}`, issues)
|
|
11597
|
+
);
|
|
11598
|
+
return;
|
|
11599
|
+
}
|
|
11600
|
+
const input = record(value);
|
|
11601
|
+
if (!input) return;
|
|
11602
|
+
for (const [key, child] of Object.entries(input)) {
|
|
11603
|
+
walkTemplates(child, `${path}.${key}`, issues);
|
|
11604
|
+
}
|
|
11605
|
+
}
|
|
11606
|
+
function lintMonitorSpecTemplate(value, path = "monitor") {
|
|
11607
|
+
const issues = [];
|
|
11608
|
+
const normalized = monitorSpec(value, path, issues);
|
|
11609
|
+
const input = record(value);
|
|
11610
|
+
if (input && typeof input.tool === "string" && input.tool.includes("{{")) {
|
|
11611
|
+
issues.push({
|
|
11612
|
+
severity: "error",
|
|
11613
|
+
code: "templated_monitor_tool",
|
|
11614
|
+
path: `${path}.tool`,
|
|
11615
|
+
message: "Fleet monitor tool must be literal; only key, name, and payload may be templated."
|
|
11616
|
+
});
|
|
11617
|
+
}
|
|
11618
|
+
if (input) {
|
|
11619
|
+
for (const field of ["key", "name", "payload"]) {
|
|
11620
|
+
if (input[field] !== void 0)
|
|
11621
|
+
walkTemplates(input[field], `${path}.${field}`, issues);
|
|
11622
|
+
}
|
|
11623
|
+
}
|
|
11624
|
+
const ordered = issues.sort(issueOrder);
|
|
11625
|
+
return ordered.some((issue) => issue.severity === "error") ? { issues: ordered } : { normalized, issues: ordered };
|
|
11626
|
+
}
|
|
11627
|
+
function renderValue(value, row) {
|
|
11628
|
+
if (typeof value === "string" && value.includes("{{")) {
|
|
11629
|
+
const context = Object.assign(/* @__PURE__ */ Object.create(null), row);
|
|
11630
|
+
return import_handlebars.default.compile(value, {
|
|
11631
|
+
noEscape: true,
|
|
11632
|
+
strict: true,
|
|
11633
|
+
knownHelpersOnly: true,
|
|
11634
|
+
preventIndent: true
|
|
11635
|
+
})(context, {
|
|
11636
|
+
allowProtoMethodsByDefault: false,
|
|
11637
|
+
allowProtoPropertiesByDefault: false,
|
|
11638
|
+
helpers: /* @__PURE__ */ Object.create(null),
|
|
11639
|
+
partials: /* @__PURE__ */ Object.create(null)
|
|
11640
|
+
});
|
|
11641
|
+
}
|
|
11642
|
+
if (Array.isArray(value)) return value.map((item) => renderValue(item, row));
|
|
11643
|
+
const input = record(value);
|
|
11644
|
+
if (!input) return value;
|
|
11645
|
+
return Object.fromEntries(
|
|
11646
|
+
Object.entries(input).map(([key, item]) => [key, renderValue(item, row)])
|
|
11647
|
+
);
|
|
11648
|
+
}
|
|
11649
|
+
function renderMonitorSpecTemplate(template, row) {
|
|
11650
|
+
const lint = lintMonitorSpecTemplate(template);
|
|
11651
|
+
if (!lint.normalized) return { issues: lint.issues };
|
|
11652
|
+
try {
|
|
11653
|
+
const rendered = renderValue(lint.normalized, row);
|
|
11654
|
+
const issues = [];
|
|
11655
|
+
const spec = monitorSpec(rendered, "monitor", issues);
|
|
11656
|
+
return spec ? { spec, issues: issues.sort(issueOrder) } : { issues: issues.sort(issueOrder) };
|
|
11657
|
+
} catch (error) {
|
|
11658
|
+
return {
|
|
11659
|
+
issues: [
|
|
11660
|
+
{
|
|
11661
|
+
severity: "error",
|
|
11662
|
+
code: "monitor_template_render_failed",
|
|
11663
|
+
path: "monitor",
|
|
11664
|
+
message: error instanceof Error ? error.message : "Fleet template rendering failed."
|
|
11665
|
+
}
|
|
11666
|
+
]
|
|
11667
|
+
};
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
|
|
11242
11671
|
// ../integration-contracts/monitor-fleet-contract.ts
|
|
11243
11672
|
var MONITOR_FLEET_MAX_MEMBERS = 1e3;
|
|
11244
11673
|
var MONITOR_FLEET_FRONTIER_MAX_ROWS = 1e4;
|
|
11245
|
-
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION =
|
|
11674
|
+
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION = 2;
|
|
11246
11675
|
var SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS = [
|
|
11247
|
-
|
|
11676
|
+
1,
|
|
11677
|
+
2
|
|
11248
11678
|
];
|
|
11249
11679
|
var MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG = [
|
|
11250
11680
|
{
|
|
11251
11681
|
edition: 1,
|
|
11252
|
-
changed: "Initial Fleet JSON contract: bounded sticky table membership,
|
|
11682
|
+
changed: "Initial Fleet JSON contract: bounded sticky table membership, source refresh, and a seven-day removal grace.",
|
|
11683
|
+
compatibilityOwner: "Monitors Runtime",
|
|
11684
|
+
newWritesEnd: null,
|
|
11685
|
+
readerRemoval: null
|
|
11686
|
+
},
|
|
11687
|
+
{
|
|
11688
|
+
edition: 2,
|
|
11689
|
+
changed: "Canonical table-backed Fleet envelope with one Handlebars MonitorSpec template.",
|
|
11253
11690
|
compatibilityOwner: "Monitors Runtime",
|
|
11254
11691
|
newWritesEnd: null,
|
|
11255
11692
|
readerRemoval: null
|
|
@@ -11261,11 +11698,11 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
11261
11698
|
authoredFields: {
|
|
11262
11699
|
id: "Stable lowercase fleet id used by sync, get, and deactivate.",
|
|
11263
11700
|
source: "Customer DB table, unique row key, and optional equality filters that select candidate rows.",
|
|
11264
|
-
|
|
11701
|
+
monitor: "One Batch-compatible MonitorSpec. Direct Handlebars source-row fields such as {{domain}} are rendered for each selected row.",
|
|
11265
11702
|
selection: "Deterministic ranking and an active-member limit between 1 and 1,000."
|
|
11266
11703
|
},
|
|
11267
11704
|
fixedPolicy: {
|
|
11268
|
-
cadence: "
|
|
11705
|
+
cadence: "60s fair bounded source refresh",
|
|
11269
11706
|
membership: "sticky",
|
|
11270
11707
|
removalGrace: "7d",
|
|
11271
11708
|
onRemoved: "deactivate",
|
|
@@ -11273,9 +11710,10 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
11273
11710
|
billing: "No Fleet fee, permit, or renewal. Dry-run reports ordinary monitor lifecycle charges due now."
|
|
11274
11711
|
},
|
|
11275
11712
|
stickyMembership: "Ranking fills vacancies but never replaces a current member merely because another row ranks higher.",
|
|
11276
|
-
|
|
11713
|
+
templating: "Use direct Handlebars fields such as {{domain}} in monitor key, name, or payload. Helpers, blocks, partials, paths, and prototype access are disabled.",
|
|
11714
|
+
legacyCompatibility: 'Edition 1 member/$fleet files remain readable with a lint warning; new files should use version: 2, kind: "fleet", and monitor.'
|
|
11277
11715
|
};
|
|
11278
|
-
function
|
|
11716
|
+
function record2(value) {
|
|
11279
11717
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11280
11718
|
}
|
|
11281
11719
|
function identifier(value) {
|
|
@@ -11300,7 +11738,7 @@ function jsonValue(value, path, issues) {
|
|
|
11300
11738
|
value.forEach((item, index) => jsonValue(item, `${path}.${index}`, issues));
|
|
11301
11739
|
return;
|
|
11302
11740
|
}
|
|
11303
|
-
const input =
|
|
11741
|
+
const input = record2(value);
|
|
11304
11742
|
if (!input) {
|
|
11305
11743
|
issues.push({
|
|
11306
11744
|
path,
|
|
@@ -11326,7 +11764,7 @@ function defineMonitorFleet(definition) {
|
|
|
11326
11764
|
return definition;
|
|
11327
11765
|
}
|
|
11328
11766
|
function validateExpression(value, path, issues) {
|
|
11329
|
-
const input =
|
|
11767
|
+
const input = record2(value);
|
|
11330
11768
|
if (!input || input.$fleet !== "column" || !identifier(input.name)) {
|
|
11331
11769
|
issues.push({
|
|
11332
11770
|
path,
|
|
@@ -11344,7 +11782,7 @@ function walkPayload(value, path, issues) {
|
|
|
11344
11782
|
);
|
|
11345
11783
|
return;
|
|
11346
11784
|
}
|
|
11347
|
-
const input =
|
|
11785
|
+
const input = record2(value);
|
|
11348
11786
|
if (!input) return;
|
|
11349
11787
|
if ("$fleet" in input) {
|
|
11350
11788
|
validateExpression(input, path, issues);
|
|
@@ -11354,9 +11792,9 @@ function walkPayload(value, path, issues) {
|
|
|
11354
11792
|
walkPayload(item, `${path}.${key}`, issues);
|
|
11355
11793
|
}
|
|
11356
11794
|
}
|
|
11357
|
-
function
|
|
11795
|
+
function validateMonitorFleetDefinitionV1(value) {
|
|
11358
11796
|
const issues = [];
|
|
11359
|
-
const input =
|
|
11797
|
+
const input = record2(value);
|
|
11360
11798
|
if (!input) {
|
|
11361
11799
|
return {
|
|
11362
11800
|
valid: false,
|
|
@@ -11377,7 +11815,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11377
11815
|
message: "Fleet id must be lowercase kebab-case."
|
|
11378
11816
|
});
|
|
11379
11817
|
}
|
|
11380
|
-
const source =
|
|
11818
|
+
const source = record2(input.source);
|
|
11381
11819
|
exactKeys(
|
|
11382
11820
|
source,
|
|
11383
11821
|
["kind", "schema", "table", "key", "where"],
|
|
@@ -11398,7 +11836,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11398
11836
|
message: "Source schema and table must be SQL identifiers."
|
|
11399
11837
|
});
|
|
11400
11838
|
}
|
|
11401
|
-
const sourceKey =
|
|
11839
|
+
const sourceKey = record2(source?.key);
|
|
11402
11840
|
exactKeys(sourceKey, ["column", "type"], "source.key", issues);
|
|
11403
11841
|
if (!sourceKey || !identifier(sourceKey.column) || !["text", "uuid", "int4", "int8"].includes(String(sourceKey.type))) {
|
|
11404
11842
|
issues.push({
|
|
@@ -11408,7 +11846,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11408
11846
|
});
|
|
11409
11847
|
}
|
|
11410
11848
|
if (source?.where !== void 0) {
|
|
11411
|
-
const where =
|
|
11849
|
+
const where = record2(source.where);
|
|
11412
11850
|
if (!where) {
|
|
11413
11851
|
issues.push({
|
|
11414
11852
|
path: "source.where",
|
|
@@ -11434,9 +11872,9 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11434
11872
|
}
|
|
11435
11873
|
}
|
|
11436
11874
|
}
|
|
11437
|
-
const member =
|
|
11875
|
+
const member = record2(input.member);
|
|
11438
11876
|
exactKeys(member, ["tool", "key", "payload"], "member", issues);
|
|
11439
|
-
const key =
|
|
11877
|
+
const key = record2(member?.key);
|
|
11440
11878
|
if (!member || typeof member.tool !== "string" || !member.tool.trim()) {
|
|
11441
11879
|
issues.push({
|
|
11442
11880
|
path: "member.tool",
|
|
@@ -11457,7 +11895,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11457
11895
|
validateExpression(part, `member.key.parts.${index}`, issues);
|
|
11458
11896
|
}
|
|
11459
11897
|
if (sourceKey && !key.parts.some(
|
|
11460
|
-
(part) =>
|
|
11898
|
+
(part) => record2(part)?.$fleet === "column" && record2(part)?.name === sourceKey.column
|
|
11461
11899
|
)) {
|
|
11462
11900
|
issues.push({
|
|
11463
11901
|
path: "member.key.parts",
|
|
@@ -11476,7 +11914,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11476
11914
|
jsonValue(member.payload, "member.payload", issues);
|
|
11477
11915
|
}
|
|
11478
11916
|
walkPayload(member?.payload, "member.payload", issues);
|
|
11479
|
-
const selection =
|
|
11917
|
+
const selection = record2(input.selection);
|
|
11480
11918
|
exactKeys(selection, ["limit", "orderBy", "membership"], "selection", issues);
|
|
11481
11919
|
if (!selection || !Number.isSafeInteger(selection.limit) || Number(selection.limit) < 1 || Number(selection.limit) > MONITOR_FLEET_MAX_MEMBERS) {
|
|
11482
11920
|
issues.push({
|
|
@@ -11502,7 +11940,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11502
11940
|
} else {
|
|
11503
11941
|
const orderColumns = /* @__PURE__ */ new Set();
|
|
11504
11942
|
for (const [index, order] of orderBy.entries()) {
|
|
11505
|
-
const item =
|
|
11943
|
+
const item = record2(order);
|
|
11506
11944
|
exactKeys(
|
|
11507
11945
|
item,
|
|
11508
11946
|
["column", "direction"],
|
|
@@ -11525,7 +11963,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11525
11963
|
orderColumns.add(item.column);
|
|
11526
11964
|
}
|
|
11527
11965
|
}
|
|
11528
|
-
if (sourceKey &&
|
|
11966
|
+
if (sourceKey && record2(orderBy.at(-1))?.column !== sourceKey.column) {
|
|
11529
11967
|
issues.push({
|
|
11530
11968
|
path: "selection.orderBy",
|
|
11531
11969
|
code: "fleet_order_missing_key",
|
|
@@ -11544,9 +11982,104 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11544
11982
|
}
|
|
11545
11983
|
return issues.length === 0 ? { valid: true, definition: value, issues } : { valid: false, issues };
|
|
11546
11984
|
}
|
|
11547
|
-
function
|
|
11985
|
+
function v2Issue(issue) {
|
|
11986
|
+
return { path: issue.path, code: issue.code, message: issue.message };
|
|
11987
|
+
}
|
|
11988
|
+
function validateMonitorFleetDefinitionV2(value) {
|
|
11989
|
+
const issues = [];
|
|
11990
|
+
const input = record2(value);
|
|
11991
|
+
if (!input) {
|
|
11992
|
+
return {
|
|
11993
|
+
valid: false,
|
|
11994
|
+
issues: [
|
|
11995
|
+
{
|
|
11996
|
+
path: "",
|
|
11997
|
+
code: "invalid_fleet",
|
|
11998
|
+
message: "Fleet must be an object."
|
|
11999
|
+
}
|
|
12000
|
+
]
|
|
12001
|
+
};
|
|
12002
|
+
}
|
|
12003
|
+
exactKeys(
|
|
12004
|
+
input,
|
|
12005
|
+
["version", "kind", "id", "source", "monitor", "selection"],
|
|
12006
|
+
"",
|
|
12007
|
+
issues
|
|
12008
|
+
);
|
|
12009
|
+
if (input.version !== 2) {
|
|
12010
|
+
issues.push({
|
|
12011
|
+
path: "version",
|
|
12012
|
+
code: "unsupported_fleet_version",
|
|
12013
|
+
message: "Canonical Fleet input version must be 2."
|
|
12014
|
+
});
|
|
12015
|
+
}
|
|
12016
|
+
if (input.kind !== "fleet") {
|
|
12017
|
+
issues.push({
|
|
12018
|
+
path: "kind",
|
|
12019
|
+
code: "invalid_fleet_kind",
|
|
12020
|
+
message: 'Canonical Fleet input kind must be "fleet".'
|
|
12021
|
+
});
|
|
12022
|
+
}
|
|
12023
|
+
const source = record2(input.source);
|
|
12024
|
+
const sourceKey = record2(source?.key);
|
|
12025
|
+
const legacyShape = {
|
|
12026
|
+
id: input.id,
|
|
12027
|
+
source: input.source,
|
|
12028
|
+
member: {
|
|
12029
|
+
tool: record2(input.monitor)?.tool,
|
|
12030
|
+
key: {
|
|
12031
|
+
$fleet: "template",
|
|
12032
|
+
parts: [
|
|
12033
|
+
"fleet-",
|
|
12034
|
+
{
|
|
12035
|
+
$fleet: "column",
|
|
12036
|
+
name: typeof sourceKey?.column === "string" ? sourceKey.column : "invalid_source_key"
|
|
12037
|
+
}
|
|
12038
|
+
]
|
|
12039
|
+
},
|
|
12040
|
+
payload: record2(input.monitor)?.payload
|
|
12041
|
+
},
|
|
12042
|
+
selection: input.selection
|
|
12043
|
+
};
|
|
12044
|
+
const legacyCheck = validateMonitorFleetDefinitionV1(legacyShape);
|
|
12045
|
+
for (const issue of legacyCheck.issues) {
|
|
12046
|
+
issues.push({
|
|
12047
|
+
...issue,
|
|
12048
|
+
path: issue.path.replace(/^member\./, "monitor.")
|
|
12049
|
+
});
|
|
12050
|
+
}
|
|
12051
|
+
const monitorInput = record2(input.monitor);
|
|
12052
|
+
const monitorKey = monitorInput?.key;
|
|
12053
|
+
const monitor = lintMonitorSpecTemplate(input.monitor);
|
|
12054
|
+
issues.push(...monitor.issues.map(v2Issue));
|
|
12055
|
+
if (typeof monitorKey === "string" && typeof sourceKey?.column === "string" && !monitorKey.includes(`{{${sourceKey.column}}}`)) {
|
|
12056
|
+
issues.push({
|
|
12057
|
+
path: "monitor.key",
|
|
12058
|
+
code: "fleet_key_missing_source_key",
|
|
12059
|
+
message: "Monitor key must include the source key as a direct Handlebars expression."
|
|
12060
|
+
});
|
|
12061
|
+
}
|
|
12062
|
+
return issues.length === 0 && monitor.normalized ? {
|
|
12063
|
+
valid: true,
|
|
12064
|
+
definition: {
|
|
12065
|
+
version: 2,
|
|
12066
|
+
kind: "fleet",
|
|
12067
|
+
id: String(input.id),
|
|
12068
|
+
source: input.source,
|
|
12069
|
+
monitor: monitor.normalized,
|
|
12070
|
+
selection: input.selection
|
|
12071
|
+
},
|
|
12072
|
+
issues
|
|
12073
|
+
} : { valid: false, issues };
|
|
12074
|
+
}
|
|
12075
|
+
function validateMonitorFleetDefinition(value) {
|
|
12076
|
+
const input = record2(value);
|
|
12077
|
+
return input?.version === 2 || input?.kind === "fleet" ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
12078
|
+
}
|
|
12079
|
+
function admitMonitorFleetAuthoringContract(value, edition) {
|
|
12080
|
+
const detectedEdition = edition ?? (record2(value)?.version === 2 || record2(value)?.kind === "fleet" ? 2 : 1);
|
|
11548
12081
|
if (!SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS.includes(
|
|
11549
|
-
|
|
12082
|
+
detectedEdition
|
|
11550
12083
|
)) {
|
|
11551
12084
|
return {
|
|
11552
12085
|
valid: false,
|
|
@@ -11554,16 +12087,16 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
11554
12087
|
{
|
|
11555
12088
|
path: "authoring_contract_edition",
|
|
11556
12089
|
code: "unsupported_fleet_authoring_contract_edition",
|
|
11557
|
-
message: `Monitor Fleet authoring contract edition ${
|
|
12090
|
+
message: `Monitor Fleet authoring contract edition ${detectedEdition} is not supported.`
|
|
11558
12091
|
}
|
|
11559
12092
|
]
|
|
11560
12093
|
};
|
|
11561
12094
|
}
|
|
11562
|
-
const result =
|
|
12095
|
+
const result = detectedEdition === 2 ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
11563
12096
|
return result.valid && result.definition ? {
|
|
11564
12097
|
valid: true,
|
|
11565
12098
|
contract: {
|
|
11566
|
-
edition,
|
|
12099
|
+
edition: detectedEdition,
|
|
11567
12100
|
// JSON cloning prevents later caller mutation from changing the
|
|
11568
12101
|
// admitted snapshot.
|
|
11569
12102
|
definition: JSON.parse(
|
|
@@ -11573,6 +12106,32 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
11573
12106
|
issues: []
|
|
11574
12107
|
} : { valid: false, issues: result.issues };
|
|
11575
12108
|
}
|
|
12109
|
+
function lintMonitorFleetInput(value) {
|
|
12110
|
+
const input = record2(value);
|
|
12111
|
+
const legacy = input?.member !== void 0;
|
|
12112
|
+
const admitted = admitMonitorFleetAuthoringContract(value);
|
|
12113
|
+
const issues = admitted.issues.map((issue) => ({
|
|
12114
|
+
...issue,
|
|
12115
|
+
severity: "error"
|
|
12116
|
+
}));
|
|
12117
|
+
if (legacy) {
|
|
12118
|
+
issues.push({
|
|
12119
|
+
severity: "warning",
|
|
12120
|
+
code: "deprecated_fleet_member",
|
|
12121
|
+
path: "member",
|
|
12122
|
+
message: "Legacy Fleet member syntax remains supported."
|
|
12123
|
+
});
|
|
12124
|
+
if (JSON.stringify(input).includes('"$fleet"')) {
|
|
12125
|
+
issues.push({
|
|
12126
|
+
severity: "warning",
|
|
12127
|
+
code: "deprecated_fleet_expression",
|
|
12128
|
+
path: "member",
|
|
12129
|
+
message: "Legacy $fleet expressions remain supported; use direct Handlebars fields in monitor instead."
|
|
12130
|
+
});
|
|
12131
|
+
}
|
|
12132
|
+
}
|
|
12133
|
+
return admitted.valid && admitted.contract ? { normalized: admitted.contract.definition, issues } : { issues };
|
|
12134
|
+
}
|
|
11576
12135
|
|
|
11577
12136
|
// ../plays/bootstrap-routes.ts
|
|
11578
12137
|
var DEEPLINE_TOOL_CATEGORIES = [
|
|
@@ -11899,6 +12458,10 @@ function extractSummaryFields(payload) {
|
|
|
11899
12458
|
isPlayBootstrapTemplate,
|
|
11900
12459
|
isProviderUnavailable,
|
|
11901
12460
|
isProviderWaterfallUnavailableError,
|
|
12461
|
+
lintMonitorBatchInput,
|
|
12462
|
+
lintMonitorFleetInput,
|
|
12463
|
+
lintMonitorSpecTemplate,
|
|
12464
|
+
renderMonitorSpecTemplate,
|
|
11902
12465
|
resolveConfig,
|
|
11903
12466
|
runIf,
|
|
11904
12467
|
steps,
|