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