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
package/dist/index.mjs
CHANGED
|
@@ -768,7 +768,7 @@ var SDK_RELEASE = {
|
|
|
768
768
|
// getters keep their established compatibility behavior.
|
|
769
769
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
770
770
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
771
|
-
version: "0.3.
|
|
771
|
+
version: "0.3.98",
|
|
772
772
|
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.",
|
|
773
773
|
packageCapabilities: {
|
|
774
774
|
updatePreferences: 1
|
|
@@ -2806,9 +2806,9 @@ function nonEmptyString(value) {
|
|
|
2806
2806
|
}
|
|
2807
2807
|
function normalizeDatasetBornFrom(value) {
|
|
2808
2808
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2809
|
-
const
|
|
2810
|
-
const table = nonEmptyString(
|
|
2811
|
-
const rowCountIn = finiteNonNegativeInteger2(
|
|
2809
|
+
const record3 = value;
|
|
2810
|
+
const table = nonEmptyString(record3.table);
|
|
2811
|
+
const rowCountIn = finiteNonNegativeInteger2(record3.rowCountIn);
|
|
2812
2812
|
if (!table || rowCountIn === null) return null;
|
|
2813
2813
|
return { table, rowCountIn };
|
|
2814
2814
|
}
|
|
@@ -3897,21 +3897,21 @@ function normalizePlayRuntimeNamespace(value) {
|
|
|
3897
3897
|
}
|
|
3898
3898
|
function normalizePlayRuntimeSelection(value) {
|
|
3899
3899
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
3900
|
-
const
|
|
3901
|
-
if (Object.keys(
|
|
3900
|
+
const record3 = value;
|
|
3901
|
+
if (Object.keys(record3).some(
|
|
3902
3902
|
(key) => key !== "environment" && key !== "namespace" && key !== "backend"
|
|
3903
|
-
) ||
|
|
3903
|
+
) || record3.environment !== "preview") {
|
|
3904
3904
|
return null;
|
|
3905
3905
|
}
|
|
3906
|
-
const namespace = normalizePlayRuntimeNamespace(
|
|
3906
|
+
const namespace = normalizePlayRuntimeNamespace(record3.namespace);
|
|
3907
3907
|
if (!namespace) return null;
|
|
3908
|
-
if (
|
|
3908
|
+
if (record3.backend === void 0) {
|
|
3909
3909
|
return { environment: "preview", namespace };
|
|
3910
3910
|
}
|
|
3911
|
-
if (
|
|
3911
|
+
if (record3.backend !== PLAY_RUNTIME_BACKENDS.daytona && record3.backend !== PLAY_RUNTIME_BACKENDS.modal) {
|
|
3912
3912
|
return null;
|
|
3913
3913
|
}
|
|
3914
|
-
return { environment: "preview", namespace, backend:
|
|
3914
|
+
return { environment: "preview", namespace, backend: record3.backend };
|
|
3915
3915
|
}
|
|
3916
3916
|
function normalizePlayRuntimeEnvironment(value) {
|
|
3917
3917
|
return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
|
|
@@ -4556,10 +4556,18 @@ var DeeplineClient = class {
|
|
|
4556
4556
|
repair: (options2) => this.repairMonitors(options2),
|
|
4557
4557
|
health: (options2) => this.readMonitorsHealth(options2),
|
|
4558
4558
|
batch: {
|
|
4559
|
+
deploy: (definitions, options2) => this.deployMonitorBatch(definitions, options2),
|
|
4559
4560
|
submit: (options2) => this.submitMonitorBatch(options2),
|
|
4560
4561
|
get: (operationId, options2) => this.getMonitorBatch(operationId, options2)
|
|
4561
4562
|
},
|
|
4563
|
+
jobs: {
|
|
4564
|
+
get: (jobId) => this.getMonitorJob(jobId),
|
|
4565
|
+
logs: (jobId, options2) => this.getMonitorJobLogs(jobId, options2),
|
|
4566
|
+
cancel: (jobId) => this.cancelMonitorJob(jobId),
|
|
4567
|
+
wait: (jobId, options2) => this.waitForMonitorJob(jobId, options2)
|
|
4568
|
+
},
|
|
4562
4569
|
fleets: {
|
|
4570
|
+
deploy: (definitionOrId, options2) => this.syncMonitorFleet(definitionOrId, options2),
|
|
4563
4571
|
sync: (definitionOrId, options2) => this.syncMonitorFleet(definitionOrId, options2),
|
|
4564
4572
|
get: (fleetId, options2) => fleetId === void 0 ? this.listMonitorFleets() : this.getMonitorFleet(fleetId, options2),
|
|
4565
4573
|
list: () => this.listMonitorFleets(),
|
|
@@ -7311,6 +7319,68 @@ var DeeplineClient = class {
|
|
|
7311
7319
|
);
|
|
7312
7320
|
}
|
|
7313
7321
|
// ——————————————————————————————————————————————————————————
|
|
7322
|
+
// Monitor Jobs, Batches, and Fleets
|
|
7323
|
+
// ——————————————————————————————————————————————————————————
|
|
7324
|
+
monitorJobIdempotencyKey(operation) {
|
|
7325
|
+
const uuid = globalThis.crypto?.randomUUID?.();
|
|
7326
|
+
return `monitor-job-${operation}-${uuid ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
|
|
7327
|
+
}
|
|
7328
|
+
monitorJobHeaders(operation, idempotencyKey) {
|
|
7329
|
+
return {
|
|
7330
|
+
"Idempotency-Key": idempotencyKey?.trim() || this.monitorJobIdempotencyKey(operation)
|
|
7331
|
+
};
|
|
7332
|
+
}
|
|
7333
|
+
async deployMonitorBatch(definitions, options) {
|
|
7334
|
+
return this.http.request(
|
|
7335
|
+
"/api/v2/monitors/batch/deploy",
|
|
7336
|
+
{
|
|
7337
|
+
method: "POST",
|
|
7338
|
+
body: { version: 1, kind: "batch", monitors: definitions },
|
|
7339
|
+
headers: this.monitorJobHeaders(
|
|
7340
|
+
"batch-deploy",
|
|
7341
|
+
options?.idempotencyKey
|
|
7342
|
+
),
|
|
7343
|
+
maxRetries: 0,
|
|
7344
|
+
exactUrlOnly: true
|
|
7345
|
+
}
|
|
7346
|
+
);
|
|
7347
|
+
}
|
|
7348
|
+
async getMonitorJob(jobId) {
|
|
7349
|
+
return this.http.request(
|
|
7350
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}`,
|
|
7351
|
+
{ method: "GET" }
|
|
7352
|
+
);
|
|
7353
|
+
}
|
|
7354
|
+
async getMonitorJobLogs(jobId, options) {
|
|
7355
|
+
const query = options?.limit === void 0 ? "" : `?limit=${encodeURIComponent(String(options.limit))}`;
|
|
7356
|
+
return this.http.request(
|
|
7357
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}/logs${query}`,
|
|
7358
|
+
{ method: "GET" }
|
|
7359
|
+
);
|
|
7360
|
+
}
|
|
7361
|
+
async cancelMonitorJob(jobId) {
|
|
7362
|
+
return this.http.request(
|
|
7363
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}/cancel`,
|
|
7364
|
+
{ method: "POST", body: {}, maxRetries: 0, exactUrlOnly: true }
|
|
7365
|
+
);
|
|
7366
|
+
}
|
|
7367
|
+
async waitForMonitorJob(jobId, options) {
|
|
7368
|
+
const timeoutMs = Math.max(1, options?.timeoutMs ?? 10 * 6e4);
|
|
7369
|
+
const pollIntervalMs = Math.max(1, options?.pollIntervalMs ?? 2e3);
|
|
7370
|
+
const terminal = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
|
|
7371
|
+
const startedAt = Date.now();
|
|
7372
|
+
let latest = await this.getMonitorJob(jobId);
|
|
7373
|
+
while (true) {
|
|
7374
|
+
options?.onProgress?.(latest);
|
|
7375
|
+
const job = latest.job;
|
|
7376
|
+
const state = typeof job?.state === "string" ? job.state : void 0;
|
|
7377
|
+
if (state && terminal.has(state)) return latest;
|
|
7378
|
+
if (Date.now() - startedAt >= timeoutMs) return latest;
|
|
7379
|
+
await sleep2(pollIntervalMs);
|
|
7380
|
+
latest = await this.getMonitorJob(jobId);
|
|
7381
|
+
}
|
|
7382
|
+
}
|
|
7383
|
+
// ——————————————————————————————————————————————————————————
|
|
7314
7384
|
// Monitor Fleets
|
|
7315
7385
|
// ——————————————————————————————————————————————————————————
|
|
7316
7386
|
monitorFleetIdempotencyKey(operation) {
|
|
@@ -8469,8 +8539,8 @@ function firstExperienceDate(value) {
|
|
|
8469
8539
|
return null;
|
|
8470
8540
|
}
|
|
8471
8541
|
function normalizeJobChange(value) {
|
|
8472
|
-
const
|
|
8473
|
-
const nested = isRecord8(
|
|
8542
|
+
const record3 = isRecord8(value) ? value : {};
|
|
8543
|
+
const nested = isRecord8(record3.job_change) ? record3.job_change : record3;
|
|
8474
8544
|
const output = isRecord8(nested.output) ? nested.output : nested;
|
|
8475
8545
|
const person = isRecord8(output.person) ? output.person : {};
|
|
8476
8546
|
const status = normalizeJobChangeStatus(
|
|
@@ -11143,25 +11213,388 @@ function defineMonitor(definition) {
|
|
|
11143
11213
|
if (definition.name !== void 0 && typeof definition.name !== "string") {
|
|
11144
11214
|
throw new Error('defineMonitor(definition) "name" must be a string.');
|
|
11145
11215
|
}
|
|
11146
|
-
|
|
11216
|
+
const authored = definition;
|
|
11217
|
+
if ("controls" in authored) {
|
|
11147
11218
|
throw new Error(
|
|
11148
|
-
'defineMonitor(definition) "controls"
|
|
11219
|
+
'defineMonitor(definition) does not accept "controls". Operational controls are managed by Deepline; bind downstream automation in a Play with sqlListeners.'
|
|
11220
|
+
);
|
|
11221
|
+
}
|
|
11222
|
+
if ("play" in authored) {
|
|
11223
|
+
throw new Error(
|
|
11224
|
+
'defineMonitor(definition) does not accept "play". A monitor does not own Play bindings; declare sqlListeners in the Play instead.'
|
|
11149
11225
|
);
|
|
11150
11226
|
}
|
|
11151
11227
|
return definition;
|
|
11152
11228
|
}
|
|
11153
11229
|
|
|
11230
|
+
// ../integration-contracts/monitor-input-contract.ts
|
|
11231
|
+
import Handlebars from "handlebars";
|
|
11232
|
+
function authoredMonitorFieldPath(path, field) {
|
|
11233
|
+
const batch = /^monitors\.(\d+)$/.exec(path);
|
|
11234
|
+
return `${batch ? `monitors[${batch[1]}]` : path}.${field}`;
|
|
11235
|
+
}
|
|
11236
|
+
function record(value) {
|
|
11237
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11238
|
+
}
|
|
11239
|
+
function json(value, path, issues) {
|
|
11240
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
|
|
11241
|
+
return;
|
|
11242
|
+
}
|
|
11243
|
+
if (Array.isArray(value)) {
|
|
11244
|
+
value.forEach((child, index) => json(child, `${path}.${index}`, issues));
|
|
11245
|
+
return;
|
|
11246
|
+
}
|
|
11247
|
+
const object = record(value);
|
|
11248
|
+
if (!object) {
|
|
11249
|
+
issues.push({
|
|
11250
|
+
severity: "error",
|
|
11251
|
+
code: "invalid_monitor_json_value",
|
|
11252
|
+
path,
|
|
11253
|
+
message: "Monitor values must be JSON values."
|
|
11254
|
+
});
|
|
11255
|
+
return;
|
|
11256
|
+
}
|
|
11257
|
+
for (const [key, child] of Object.entries(object)) {
|
|
11258
|
+
json(child, `${path}.${key}`, issues);
|
|
11259
|
+
}
|
|
11260
|
+
}
|
|
11261
|
+
function monitorSpec(value, path, issues) {
|
|
11262
|
+
const input = record(value);
|
|
11263
|
+
if (!input) {
|
|
11264
|
+
issues.push({
|
|
11265
|
+
severity: "error",
|
|
11266
|
+
code: "invalid_monitor_spec",
|
|
11267
|
+
path,
|
|
11268
|
+
message: "A monitor must be a JSON object."
|
|
11269
|
+
});
|
|
11270
|
+
return null;
|
|
11271
|
+
}
|
|
11272
|
+
const allowed = /* @__PURE__ */ new Set(["key", "tool", "name", "payload"]);
|
|
11273
|
+
for (const key2 of Object.keys(input)) {
|
|
11274
|
+
if (!allowed.has(key2)) {
|
|
11275
|
+
if (key2 === "controls") {
|
|
11276
|
+
issues.push({
|
|
11277
|
+
severity: "error",
|
|
11278
|
+
code: "CONTROLS_NOT_SUPPORTED",
|
|
11279
|
+
path: authoredMonitorFieldPath(path, "controls"),
|
|
11280
|
+
message: "Operational controls are managed by Deepline and cannot be authored in Batch or Fleet JSON.",
|
|
11281
|
+
suggestion: "Remove controls. Bind downstream automation in a Play with sqlListeners."
|
|
11282
|
+
});
|
|
11283
|
+
continue;
|
|
11284
|
+
}
|
|
11285
|
+
if (key2 === "play") {
|
|
11286
|
+
issues.push({
|
|
11287
|
+
severity: "error",
|
|
11288
|
+
code: "PLAY_BINDING_NOT_IN_MONITOR_SPEC",
|
|
11289
|
+
path: authoredMonitorFieldPath(path, "play"),
|
|
11290
|
+
message: "A monitor does not own Play bindings.",
|
|
11291
|
+
suggestion: "Declare sqlListeners in the Play, or run: deepline plays bootstrap monitor-triggered --out on-event.play.ts"
|
|
11292
|
+
});
|
|
11293
|
+
continue;
|
|
11294
|
+
}
|
|
11295
|
+
issues.push({
|
|
11296
|
+
severity: "error",
|
|
11297
|
+
code: "unknown_monitor_field",
|
|
11298
|
+
path: `${path}.${key2}`,
|
|
11299
|
+
message: `Monitor definitions do not accept '${key2}'.`
|
|
11300
|
+
});
|
|
11301
|
+
}
|
|
11302
|
+
}
|
|
11303
|
+
const key = typeof input.key === "string" ? input.key.trim() : "";
|
|
11304
|
+
const tool = typeof input.tool === "string" ? input.tool.trim() : "";
|
|
11305
|
+
const name = input.name === void 0 ? void 0 : typeof input.name === "string" && input.name.trim() ? input.name.trim() : null;
|
|
11306
|
+
const payload = record(input.payload);
|
|
11307
|
+
if (!key) {
|
|
11308
|
+
issues.push({
|
|
11309
|
+
severity: "error",
|
|
11310
|
+
code: "invalid_monitor_key",
|
|
11311
|
+
path: `${path}.key`,
|
|
11312
|
+
message: "Monitor key must be a non-empty string."
|
|
11313
|
+
});
|
|
11314
|
+
}
|
|
11315
|
+
if (!tool) {
|
|
11316
|
+
issues.push({
|
|
11317
|
+
severity: "error",
|
|
11318
|
+
code: "invalid_monitor_tool",
|
|
11319
|
+
path: `${path}.tool`,
|
|
11320
|
+
message: "Monitor tool must be a non-empty literal string."
|
|
11321
|
+
});
|
|
11322
|
+
}
|
|
11323
|
+
if (name === null) {
|
|
11324
|
+
issues.push({
|
|
11325
|
+
severity: "error",
|
|
11326
|
+
code: "invalid_monitor_name",
|
|
11327
|
+
path: `${path}.name`,
|
|
11328
|
+
message: "Monitor name must be a non-empty string when supplied."
|
|
11329
|
+
});
|
|
11330
|
+
}
|
|
11331
|
+
if (!payload) {
|
|
11332
|
+
issues.push({
|
|
11333
|
+
severity: "error",
|
|
11334
|
+
code: "invalid_monitor_payload",
|
|
11335
|
+
path: `${path}.payload`,
|
|
11336
|
+
message: "Monitor payload must be an object."
|
|
11337
|
+
});
|
|
11338
|
+
} else {
|
|
11339
|
+
json(payload, `${path}.payload`, issues);
|
|
11340
|
+
}
|
|
11341
|
+
if (!key || !tool || !payload || name === null) {
|
|
11342
|
+
return null;
|
|
11343
|
+
}
|
|
11344
|
+
return {
|
|
11345
|
+
key,
|
|
11346
|
+
tool,
|
|
11347
|
+
...name ? { name } : {},
|
|
11348
|
+
payload
|
|
11349
|
+
};
|
|
11350
|
+
}
|
|
11351
|
+
function issueOrder(left, right) {
|
|
11352
|
+
return left.path.localeCompare(right.path) || left.severity.localeCompare(right.severity) || left.code.localeCompare(right.code);
|
|
11353
|
+
}
|
|
11354
|
+
function lintMonitorBatchInput(value) {
|
|
11355
|
+
const issues = [];
|
|
11356
|
+
const versionlessEnvelope = !Array.isArray(value) && (() => {
|
|
11357
|
+
const candidate = record(value);
|
|
11358
|
+
return candidate !== null && Object.keys(candidate).length === 1 && Object.prototype.hasOwnProperty.call(candidate, "monitors");
|
|
11359
|
+
})();
|
|
11360
|
+
const legacy = Array.isArray(value) || versionlessEnvelope;
|
|
11361
|
+
const input = legacy ? null : record(value);
|
|
11362
|
+
let monitors = Array.isArray(value) ? value : versionlessEnvelope ? (() => {
|
|
11363
|
+
const candidate = record(value)?.monitors;
|
|
11364
|
+
return Array.isArray(candidate) ? candidate : null;
|
|
11365
|
+
})() : null;
|
|
11366
|
+
if (Array.isArray(value)) {
|
|
11367
|
+
issues.push({
|
|
11368
|
+
severity: "warning",
|
|
11369
|
+
code: "deprecated_batch_array",
|
|
11370
|
+
path: "",
|
|
11371
|
+
message: "Bare monitor arrays are supported for compatibility.",
|
|
11372
|
+
suggestion: 'Wrap the list in {"version":1,"kind":"batch","monitors":[...]}.'
|
|
11373
|
+
});
|
|
11374
|
+
} else if (versionlessEnvelope) {
|
|
11375
|
+
issues.push({
|
|
11376
|
+
severity: "warning",
|
|
11377
|
+
code: "deprecated_batch_missing_envelope_fields",
|
|
11378
|
+
path: "",
|
|
11379
|
+
message: "Versionless monitor envelopes are supported for compatibility.",
|
|
11380
|
+
suggestion: 'Add "version": 1 and "kind": "batch".'
|
|
11381
|
+
});
|
|
11382
|
+
if (!monitors) {
|
|
11383
|
+
issues.push({
|
|
11384
|
+
severity: "error",
|
|
11385
|
+
code: "invalid_batch_monitors",
|
|
11386
|
+
path: "monitors",
|
|
11387
|
+
message: "Batch monitors must be an array."
|
|
11388
|
+
});
|
|
11389
|
+
}
|
|
11390
|
+
} else if (!input) {
|
|
11391
|
+
issues.push({
|
|
11392
|
+
severity: "error",
|
|
11393
|
+
code: "invalid_batch",
|
|
11394
|
+
path: "",
|
|
11395
|
+
message: "Batch input must be an object or a legacy array."
|
|
11396
|
+
});
|
|
11397
|
+
} else {
|
|
11398
|
+
for (const key of Object.keys(input)) {
|
|
11399
|
+
if (!["version", "kind", "monitors"].includes(key)) {
|
|
11400
|
+
issues.push({
|
|
11401
|
+
severity: "error",
|
|
11402
|
+
code: key === "source" || key === "monitor" ? "batch_fleet_shape_mixup" : "unknown_batch_field",
|
|
11403
|
+
path: key,
|
|
11404
|
+
message: `Batch input does not accept '${key}'.`
|
|
11405
|
+
});
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
11408
|
+
if (input.version !== 1) {
|
|
11409
|
+
issues.push({
|
|
11410
|
+
severity: "error",
|
|
11411
|
+
code: "unsupported_batch_version",
|
|
11412
|
+
path: "version",
|
|
11413
|
+
message: "Batch input version must be 1."
|
|
11414
|
+
});
|
|
11415
|
+
}
|
|
11416
|
+
if (input.kind !== "batch") {
|
|
11417
|
+
issues.push({
|
|
11418
|
+
severity: "error",
|
|
11419
|
+
code: "invalid_batch_kind",
|
|
11420
|
+
path: "kind",
|
|
11421
|
+
message: 'Batch input kind must be "batch".'
|
|
11422
|
+
});
|
|
11423
|
+
}
|
|
11424
|
+
monitors = Array.isArray(input.monitors) ? input.monitors : null;
|
|
11425
|
+
if (!monitors) {
|
|
11426
|
+
issues.push({
|
|
11427
|
+
severity: "error",
|
|
11428
|
+
code: "invalid_batch_monitors",
|
|
11429
|
+
path: "monitors",
|
|
11430
|
+
message: "Batch monitors must be an array."
|
|
11431
|
+
});
|
|
11432
|
+
}
|
|
11433
|
+
}
|
|
11434
|
+
if (!monitors || monitors.length === 0) {
|
|
11435
|
+
if (monitors?.length === 0) {
|
|
11436
|
+
issues.push({
|
|
11437
|
+
severity: "error",
|
|
11438
|
+
code: "empty_batch",
|
|
11439
|
+
path: "monitors",
|
|
11440
|
+
message: "A Batch needs at least one monitor."
|
|
11441
|
+
});
|
|
11442
|
+
}
|
|
11443
|
+
return { issues: issues.sort(issueOrder) };
|
|
11444
|
+
}
|
|
11445
|
+
const normalized = monitors.map(
|
|
11446
|
+
(item, index) => monitorSpec(item, `monitors.${index}`, issues)
|
|
11447
|
+
);
|
|
11448
|
+
const seen = /* @__PURE__ */ new Map();
|
|
11449
|
+
normalized.forEach((item, index) => {
|
|
11450
|
+
if (!item) return;
|
|
11451
|
+
const first = seen.get(item.key);
|
|
11452
|
+
if (first !== void 0) {
|
|
11453
|
+
for (const position of [first, index]) {
|
|
11454
|
+
issues.push({
|
|
11455
|
+
severity: "error",
|
|
11456
|
+
code: "duplicate_monitor_key",
|
|
11457
|
+
path: `monitors.${position}.key`,
|
|
11458
|
+
message: `Duplicate monitor key '${item.key}'.`
|
|
11459
|
+
});
|
|
11460
|
+
}
|
|
11461
|
+
} else {
|
|
11462
|
+
seen.set(item.key, index);
|
|
11463
|
+
}
|
|
11464
|
+
});
|
|
11465
|
+
const ordered = issues.sort(issueOrder);
|
|
11466
|
+
return ordered.some((issue) => issue.severity === "error") ? { issues: ordered } : {
|
|
11467
|
+
normalized: {
|
|
11468
|
+
version: 1,
|
|
11469
|
+
kind: "batch",
|
|
11470
|
+
monitors: normalized
|
|
11471
|
+
},
|
|
11472
|
+
issues: ordered
|
|
11473
|
+
};
|
|
11474
|
+
}
|
|
11475
|
+
function validateHandlebars(value, path, issues) {
|
|
11476
|
+
if (!value.includes("{{")) return;
|
|
11477
|
+
try {
|
|
11478
|
+
Handlebars.parse(value);
|
|
11479
|
+
} catch (error) {
|
|
11480
|
+
issues.push({
|
|
11481
|
+
severity: "error",
|
|
11482
|
+
code: "invalid_handlebars_template",
|
|
11483
|
+
path,
|
|
11484
|
+
message: error instanceof Error ? error.message : "Invalid Handlebars template."
|
|
11485
|
+
});
|
|
11486
|
+
return;
|
|
11487
|
+
}
|
|
11488
|
+
const expressions = value.match(/{{[\s\S]*?}}/g) ?? [];
|
|
11489
|
+
if (expressions.some(
|
|
11490
|
+
(expression) => !/^{{\s*[A-Za-z_][A-Za-z0-9_]*\s*}}$/.test(expression)
|
|
11491
|
+
)) {
|
|
11492
|
+
issues.push({
|
|
11493
|
+
severity: "error",
|
|
11494
|
+
code: "unsupported_handlebars_expression",
|
|
11495
|
+
path,
|
|
11496
|
+
message: "Fleet templates allow only direct source-row fields such as {{domain}}; helpers, blocks, partials, paths, and prototype access are disabled."
|
|
11497
|
+
});
|
|
11498
|
+
}
|
|
11499
|
+
}
|
|
11500
|
+
function walkTemplates(value, path, issues) {
|
|
11501
|
+
if (typeof value === "string") return validateHandlebars(value, path, issues);
|
|
11502
|
+
if (Array.isArray(value)) {
|
|
11503
|
+
value.forEach(
|
|
11504
|
+
(item, index) => walkTemplates(item, `${path}.${index}`, issues)
|
|
11505
|
+
);
|
|
11506
|
+
return;
|
|
11507
|
+
}
|
|
11508
|
+
const input = record(value);
|
|
11509
|
+
if (!input) return;
|
|
11510
|
+
for (const [key, child] of Object.entries(input)) {
|
|
11511
|
+
walkTemplates(child, `${path}.${key}`, issues);
|
|
11512
|
+
}
|
|
11513
|
+
}
|
|
11514
|
+
function lintMonitorSpecTemplate(value, path = "monitor") {
|
|
11515
|
+
const issues = [];
|
|
11516
|
+
const normalized = monitorSpec(value, path, issues);
|
|
11517
|
+
const input = record(value);
|
|
11518
|
+
if (input && typeof input.tool === "string" && input.tool.includes("{{")) {
|
|
11519
|
+
issues.push({
|
|
11520
|
+
severity: "error",
|
|
11521
|
+
code: "templated_monitor_tool",
|
|
11522
|
+
path: `${path}.tool`,
|
|
11523
|
+
message: "Fleet monitor tool must be literal; only key, name, and payload may be templated."
|
|
11524
|
+
});
|
|
11525
|
+
}
|
|
11526
|
+
if (input) {
|
|
11527
|
+
for (const field of ["key", "name", "payload"]) {
|
|
11528
|
+
if (input[field] !== void 0)
|
|
11529
|
+
walkTemplates(input[field], `${path}.${field}`, issues);
|
|
11530
|
+
}
|
|
11531
|
+
}
|
|
11532
|
+
const ordered = issues.sort(issueOrder);
|
|
11533
|
+
return ordered.some((issue) => issue.severity === "error") ? { issues: ordered } : { normalized, issues: ordered };
|
|
11534
|
+
}
|
|
11535
|
+
function renderValue(value, row) {
|
|
11536
|
+
if (typeof value === "string" && value.includes("{{")) {
|
|
11537
|
+
const context = Object.assign(/* @__PURE__ */ Object.create(null), row);
|
|
11538
|
+
return Handlebars.compile(value, {
|
|
11539
|
+
noEscape: true,
|
|
11540
|
+
strict: true,
|
|
11541
|
+
knownHelpersOnly: true,
|
|
11542
|
+
preventIndent: true
|
|
11543
|
+
})(context, {
|
|
11544
|
+
allowProtoMethodsByDefault: false,
|
|
11545
|
+
allowProtoPropertiesByDefault: false,
|
|
11546
|
+
helpers: /* @__PURE__ */ Object.create(null),
|
|
11547
|
+
partials: /* @__PURE__ */ Object.create(null)
|
|
11548
|
+
});
|
|
11549
|
+
}
|
|
11550
|
+
if (Array.isArray(value)) return value.map((item) => renderValue(item, row));
|
|
11551
|
+
const input = record(value);
|
|
11552
|
+
if (!input) return value;
|
|
11553
|
+
return Object.fromEntries(
|
|
11554
|
+
Object.entries(input).map(([key, item]) => [key, renderValue(item, row)])
|
|
11555
|
+
);
|
|
11556
|
+
}
|
|
11557
|
+
function renderMonitorSpecTemplate(template, row) {
|
|
11558
|
+
const lint = lintMonitorSpecTemplate(template);
|
|
11559
|
+
if (!lint.normalized) return { issues: lint.issues };
|
|
11560
|
+
try {
|
|
11561
|
+
const rendered = renderValue(lint.normalized, row);
|
|
11562
|
+
const issues = [];
|
|
11563
|
+
const spec = monitorSpec(rendered, "monitor", issues);
|
|
11564
|
+
return spec ? { spec, issues: issues.sort(issueOrder) } : { issues: issues.sort(issueOrder) };
|
|
11565
|
+
} catch (error) {
|
|
11566
|
+
return {
|
|
11567
|
+
issues: [
|
|
11568
|
+
{
|
|
11569
|
+
severity: "error",
|
|
11570
|
+
code: "monitor_template_render_failed",
|
|
11571
|
+
path: "monitor",
|
|
11572
|
+
message: error instanceof Error ? error.message : "Fleet template rendering failed."
|
|
11573
|
+
}
|
|
11574
|
+
]
|
|
11575
|
+
};
|
|
11576
|
+
}
|
|
11577
|
+
}
|
|
11578
|
+
|
|
11154
11579
|
// ../integration-contracts/monitor-fleet-contract.ts
|
|
11155
11580
|
var MONITOR_FLEET_MAX_MEMBERS = 1e3;
|
|
11156
11581
|
var MONITOR_FLEET_FRONTIER_MAX_ROWS = 1e4;
|
|
11157
|
-
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION =
|
|
11582
|
+
var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION = 2;
|
|
11158
11583
|
var SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS = [
|
|
11159
|
-
|
|
11584
|
+
1,
|
|
11585
|
+
2
|
|
11160
11586
|
];
|
|
11161
11587
|
var MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG = [
|
|
11162
11588
|
{
|
|
11163
11589
|
edition: 1,
|
|
11164
|
-
changed: "Initial Fleet JSON contract: bounded sticky table membership,
|
|
11590
|
+
changed: "Initial Fleet JSON contract: bounded sticky table membership, source refresh, and a seven-day removal grace.",
|
|
11591
|
+
compatibilityOwner: "Monitors Runtime",
|
|
11592
|
+
newWritesEnd: null,
|
|
11593
|
+
readerRemoval: null
|
|
11594
|
+
},
|
|
11595
|
+
{
|
|
11596
|
+
edition: 2,
|
|
11597
|
+
changed: "Canonical table-backed Fleet envelope with one Handlebars MonitorSpec template.",
|
|
11165
11598
|
compatibilityOwner: "Monitors Runtime",
|
|
11166
11599
|
newWritesEnd: null,
|
|
11167
11600
|
readerRemoval: null
|
|
@@ -11173,11 +11606,11 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
11173
11606
|
authoredFields: {
|
|
11174
11607
|
id: "Stable lowercase fleet id used by sync, get, and deactivate.",
|
|
11175
11608
|
source: "Customer DB table, unique row key, and optional equality filters that select candidate rows.",
|
|
11176
|
-
|
|
11609
|
+
monitor: "One Batch-compatible MonitorSpec. Direct Handlebars source-row fields such as {{domain}} are rendered for each selected row.",
|
|
11177
11610
|
selection: "Deterministic ranking and an active-member limit between 1 and 1,000."
|
|
11178
11611
|
},
|
|
11179
11612
|
fixedPolicy: {
|
|
11180
|
-
cadence: "
|
|
11613
|
+
cadence: "60s fair bounded source refresh",
|
|
11181
11614
|
membership: "sticky",
|
|
11182
11615
|
removalGrace: "7d",
|
|
11183
11616
|
onRemoved: "deactivate",
|
|
@@ -11185,9 +11618,10 @@ var MONITOR_FLEET_DOCUMENTATION = {
|
|
|
11185
11618
|
billing: "No Fleet fee, permit, or renewal. Dry-run reports ordinary monitor lifecycle charges due now."
|
|
11186
11619
|
},
|
|
11187
11620
|
stickyMembership: "Ranking fills vacancies but never replaces a current member merely because another row ranks higher.",
|
|
11188
|
-
|
|
11621
|
+
templating: "Use direct Handlebars fields such as {{domain}} in monitor key, name, or payload. Helpers, blocks, partials, paths, and prototype access are disabled.",
|
|
11622
|
+
legacyCompatibility: 'Edition 1 member/$fleet files remain readable with a lint warning; new files should use version: 2, kind: "fleet", and monitor.'
|
|
11189
11623
|
};
|
|
11190
|
-
function
|
|
11624
|
+
function record2(value) {
|
|
11191
11625
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
11192
11626
|
}
|
|
11193
11627
|
function identifier(value) {
|
|
@@ -11212,7 +11646,7 @@ function jsonValue(value, path, issues) {
|
|
|
11212
11646
|
value.forEach((item, index) => jsonValue(item, `${path}.${index}`, issues));
|
|
11213
11647
|
return;
|
|
11214
11648
|
}
|
|
11215
|
-
const input =
|
|
11649
|
+
const input = record2(value);
|
|
11216
11650
|
if (!input) {
|
|
11217
11651
|
issues.push({
|
|
11218
11652
|
path,
|
|
@@ -11238,7 +11672,7 @@ function defineMonitorFleet(definition) {
|
|
|
11238
11672
|
return definition;
|
|
11239
11673
|
}
|
|
11240
11674
|
function validateExpression(value, path, issues) {
|
|
11241
|
-
const input =
|
|
11675
|
+
const input = record2(value);
|
|
11242
11676
|
if (!input || input.$fleet !== "column" || !identifier(input.name)) {
|
|
11243
11677
|
issues.push({
|
|
11244
11678
|
path,
|
|
@@ -11256,7 +11690,7 @@ function walkPayload(value, path, issues) {
|
|
|
11256
11690
|
);
|
|
11257
11691
|
return;
|
|
11258
11692
|
}
|
|
11259
|
-
const input =
|
|
11693
|
+
const input = record2(value);
|
|
11260
11694
|
if (!input) return;
|
|
11261
11695
|
if ("$fleet" in input) {
|
|
11262
11696
|
validateExpression(input, path, issues);
|
|
@@ -11266,9 +11700,9 @@ function walkPayload(value, path, issues) {
|
|
|
11266
11700
|
walkPayload(item, `${path}.${key}`, issues);
|
|
11267
11701
|
}
|
|
11268
11702
|
}
|
|
11269
|
-
function
|
|
11703
|
+
function validateMonitorFleetDefinitionV1(value) {
|
|
11270
11704
|
const issues = [];
|
|
11271
|
-
const input =
|
|
11705
|
+
const input = record2(value);
|
|
11272
11706
|
if (!input) {
|
|
11273
11707
|
return {
|
|
11274
11708
|
valid: false,
|
|
@@ -11289,7 +11723,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11289
11723
|
message: "Fleet id must be lowercase kebab-case."
|
|
11290
11724
|
});
|
|
11291
11725
|
}
|
|
11292
|
-
const source =
|
|
11726
|
+
const source = record2(input.source);
|
|
11293
11727
|
exactKeys(
|
|
11294
11728
|
source,
|
|
11295
11729
|
["kind", "schema", "table", "key", "where"],
|
|
@@ -11310,7 +11744,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11310
11744
|
message: "Source schema and table must be SQL identifiers."
|
|
11311
11745
|
});
|
|
11312
11746
|
}
|
|
11313
|
-
const sourceKey =
|
|
11747
|
+
const sourceKey = record2(source?.key);
|
|
11314
11748
|
exactKeys(sourceKey, ["column", "type"], "source.key", issues);
|
|
11315
11749
|
if (!sourceKey || !identifier(sourceKey.column) || !["text", "uuid", "int4", "int8"].includes(String(sourceKey.type))) {
|
|
11316
11750
|
issues.push({
|
|
@@ -11320,7 +11754,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11320
11754
|
});
|
|
11321
11755
|
}
|
|
11322
11756
|
if (source?.where !== void 0) {
|
|
11323
|
-
const where =
|
|
11757
|
+
const where = record2(source.where);
|
|
11324
11758
|
if (!where) {
|
|
11325
11759
|
issues.push({
|
|
11326
11760
|
path: "source.where",
|
|
@@ -11346,9 +11780,9 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11346
11780
|
}
|
|
11347
11781
|
}
|
|
11348
11782
|
}
|
|
11349
|
-
const member =
|
|
11783
|
+
const member = record2(input.member);
|
|
11350
11784
|
exactKeys(member, ["tool", "key", "payload"], "member", issues);
|
|
11351
|
-
const key =
|
|
11785
|
+
const key = record2(member?.key);
|
|
11352
11786
|
if (!member || typeof member.tool !== "string" || !member.tool.trim()) {
|
|
11353
11787
|
issues.push({
|
|
11354
11788
|
path: "member.tool",
|
|
@@ -11369,7 +11803,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11369
11803
|
validateExpression(part, `member.key.parts.${index}`, issues);
|
|
11370
11804
|
}
|
|
11371
11805
|
if (sourceKey && !key.parts.some(
|
|
11372
|
-
(part) =>
|
|
11806
|
+
(part) => record2(part)?.$fleet === "column" && record2(part)?.name === sourceKey.column
|
|
11373
11807
|
)) {
|
|
11374
11808
|
issues.push({
|
|
11375
11809
|
path: "member.key.parts",
|
|
@@ -11388,7 +11822,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11388
11822
|
jsonValue(member.payload, "member.payload", issues);
|
|
11389
11823
|
}
|
|
11390
11824
|
walkPayload(member?.payload, "member.payload", issues);
|
|
11391
|
-
const selection =
|
|
11825
|
+
const selection = record2(input.selection);
|
|
11392
11826
|
exactKeys(selection, ["limit", "orderBy", "membership"], "selection", issues);
|
|
11393
11827
|
if (!selection || !Number.isSafeInteger(selection.limit) || Number(selection.limit) < 1 || Number(selection.limit) > MONITOR_FLEET_MAX_MEMBERS) {
|
|
11394
11828
|
issues.push({
|
|
@@ -11414,7 +11848,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11414
11848
|
} else {
|
|
11415
11849
|
const orderColumns = /* @__PURE__ */ new Set();
|
|
11416
11850
|
for (const [index, order] of orderBy.entries()) {
|
|
11417
|
-
const item =
|
|
11851
|
+
const item = record2(order);
|
|
11418
11852
|
exactKeys(
|
|
11419
11853
|
item,
|
|
11420
11854
|
["column", "direction"],
|
|
@@ -11437,7 +11871,7 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11437
11871
|
orderColumns.add(item.column);
|
|
11438
11872
|
}
|
|
11439
11873
|
}
|
|
11440
|
-
if (sourceKey &&
|
|
11874
|
+
if (sourceKey && record2(orderBy.at(-1))?.column !== sourceKey.column) {
|
|
11441
11875
|
issues.push({
|
|
11442
11876
|
path: "selection.orderBy",
|
|
11443
11877
|
code: "fleet_order_missing_key",
|
|
@@ -11456,9 +11890,104 @@ function validateMonitorFleetDefinition(value) {
|
|
|
11456
11890
|
}
|
|
11457
11891
|
return issues.length === 0 ? { valid: true, definition: value, issues } : { valid: false, issues };
|
|
11458
11892
|
}
|
|
11459
|
-
function
|
|
11893
|
+
function v2Issue(issue) {
|
|
11894
|
+
return { path: issue.path, code: issue.code, message: issue.message };
|
|
11895
|
+
}
|
|
11896
|
+
function validateMonitorFleetDefinitionV2(value) {
|
|
11897
|
+
const issues = [];
|
|
11898
|
+
const input = record2(value);
|
|
11899
|
+
if (!input) {
|
|
11900
|
+
return {
|
|
11901
|
+
valid: false,
|
|
11902
|
+
issues: [
|
|
11903
|
+
{
|
|
11904
|
+
path: "",
|
|
11905
|
+
code: "invalid_fleet",
|
|
11906
|
+
message: "Fleet must be an object."
|
|
11907
|
+
}
|
|
11908
|
+
]
|
|
11909
|
+
};
|
|
11910
|
+
}
|
|
11911
|
+
exactKeys(
|
|
11912
|
+
input,
|
|
11913
|
+
["version", "kind", "id", "source", "monitor", "selection"],
|
|
11914
|
+
"",
|
|
11915
|
+
issues
|
|
11916
|
+
);
|
|
11917
|
+
if (input.version !== 2) {
|
|
11918
|
+
issues.push({
|
|
11919
|
+
path: "version",
|
|
11920
|
+
code: "unsupported_fleet_version",
|
|
11921
|
+
message: "Canonical Fleet input version must be 2."
|
|
11922
|
+
});
|
|
11923
|
+
}
|
|
11924
|
+
if (input.kind !== "fleet") {
|
|
11925
|
+
issues.push({
|
|
11926
|
+
path: "kind",
|
|
11927
|
+
code: "invalid_fleet_kind",
|
|
11928
|
+
message: 'Canonical Fleet input kind must be "fleet".'
|
|
11929
|
+
});
|
|
11930
|
+
}
|
|
11931
|
+
const source = record2(input.source);
|
|
11932
|
+
const sourceKey = record2(source?.key);
|
|
11933
|
+
const legacyShape = {
|
|
11934
|
+
id: input.id,
|
|
11935
|
+
source: input.source,
|
|
11936
|
+
member: {
|
|
11937
|
+
tool: record2(input.monitor)?.tool,
|
|
11938
|
+
key: {
|
|
11939
|
+
$fleet: "template",
|
|
11940
|
+
parts: [
|
|
11941
|
+
"fleet-",
|
|
11942
|
+
{
|
|
11943
|
+
$fleet: "column",
|
|
11944
|
+
name: typeof sourceKey?.column === "string" ? sourceKey.column : "invalid_source_key"
|
|
11945
|
+
}
|
|
11946
|
+
]
|
|
11947
|
+
},
|
|
11948
|
+
payload: record2(input.monitor)?.payload
|
|
11949
|
+
},
|
|
11950
|
+
selection: input.selection
|
|
11951
|
+
};
|
|
11952
|
+
const legacyCheck = validateMonitorFleetDefinitionV1(legacyShape);
|
|
11953
|
+
for (const issue of legacyCheck.issues) {
|
|
11954
|
+
issues.push({
|
|
11955
|
+
...issue,
|
|
11956
|
+
path: issue.path.replace(/^member\./, "monitor.")
|
|
11957
|
+
});
|
|
11958
|
+
}
|
|
11959
|
+
const monitorInput = record2(input.monitor);
|
|
11960
|
+
const monitorKey = monitorInput?.key;
|
|
11961
|
+
const monitor = lintMonitorSpecTemplate(input.monitor);
|
|
11962
|
+
issues.push(...monitor.issues.map(v2Issue));
|
|
11963
|
+
if (typeof monitorKey === "string" && typeof sourceKey?.column === "string" && !monitorKey.includes(`{{${sourceKey.column}}}`)) {
|
|
11964
|
+
issues.push({
|
|
11965
|
+
path: "monitor.key",
|
|
11966
|
+
code: "fleet_key_missing_source_key",
|
|
11967
|
+
message: "Monitor key must include the source key as a direct Handlebars expression."
|
|
11968
|
+
});
|
|
11969
|
+
}
|
|
11970
|
+
return issues.length === 0 && monitor.normalized ? {
|
|
11971
|
+
valid: true,
|
|
11972
|
+
definition: {
|
|
11973
|
+
version: 2,
|
|
11974
|
+
kind: "fleet",
|
|
11975
|
+
id: String(input.id),
|
|
11976
|
+
source: input.source,
|
|
11977
|
+
monitor: monitor.normalized,
|
|
11978
|
+
selection: input.selection
|
|
11979
|
+
},
|
|
11980
|
+
issues
|
|
11981
|
+
} : { valid: false, issues };
|
|
11982
|
+
}
|
|
11983
|
+
function validateMonitorFleetDefinition(value) {
|
|
11984
|
+
const input = record2(value);
|
|
11985
|
+
return input?.version === 2 || input?.kind === "fleet" ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
11986
|
+
}
|
|
11987
|
+
function admitMonitorFleetAuthoringContract(value, edition) {
|
|
11988
|
+
const detectedEdition = edition ?? (record2(value)?.version === 2 || record2(value)?.kind === "fleet" ? 2 : 1);
|
|
11460
11989
|
if (!SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS.includes(
|
|
11461
|
-
|
|
11990
|
+
detectedEdition
|
|
11462
11991
|
)) {
|
|
11463
11992
|
return {
|
|
11464
11993
|
valid: false,
|
|
@@ -11466,16 +11995,16 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
11466
11995
|
{
|
|
11467
11996
|
path: "authoring_contract_edition",
|
|
11468
11997
|
code: "unsupported_fleet_authoring_contract_edition",
|
|
11469
|
-
message: `Monitor Fleet authoring contract edition ${
|
|
11998
|
+
message: `Monitor Fleet authoring contract edition ${detectedEdition} is not supported.`
|
|
11470
11999
|
}
|
|
11471
12000
|
]
|
|
11472
12001
|
};
|
|
11473
12002
|
}
|
|
11474
|
-
const result =
|
|
12003
|
+
const result = detectedEdition === 2 ? validateMonitorFleetDefinitionV2(value) : validateMonitorFleetDefinitionV1(value);
|
|
11475
12004
|
return result.valid && result.definition ? {
|
|
11476
12005
|
valid: true,
|
|
11477
12006
|
contract: {
|
|
11478
|
-
edition,
|
|
12007
|
+
edition: detectedEdition,
|
|
11479
12008
|
// JSON cloning prevents later caller mutation from changing the
|
|
11480
12009
|
// admitted snapshot.
|
|
11481
12010
|
definition: JSON.parse(
|
|
@@ -11485,6 +12014,32 @@ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHO
|
|
|
11485
12014
|
issues: []
|
|
11486
12015
|
} : { valid: false, issues: result.issues };
|
|
11487
12016
|
}
|
|
12017
|
+
function lintMonitorFleetInput(value) {
|
|
12018
|
+
const input = record2(value);
|
|
12019
|
+
const legacy = input?.member !== void 0;
|
|
12020
|
+
const admitted = admitMonitorFleetAuthoringContract(value);
|
|
12021
|
+
const issues = admitted.issues.map((issue) => ({
|
|
12022
|
+
...issue,
|
|
12023
|
+
severity: "error"
|
|
12024
|
+
}));
|
|
12025
|
+
if (legacy) {
|
|
12026
|
+
issues.push({
|
|
12027
|
+
severity: "warning",
|
|
12028
|
+
code: "deprecated_fleet_member",
|
|
12029
|
+
path: "member",
|
|
12030
|
+
message: "Legacy Fleet member syntax remains supported."
|
|
12031
|
+
});
|
|
12032
|
+
if (JSON.stringify(input).includes('"$fleet"')) {
|
|
12033
|
+
issues.push({
|
|
12034
|
+
severity: "warning",
|
|
12035
|
+
code: "deprecated_fleet_expression",
|
|
12036
|
+
path: "member",
|
|
12037
|
+
message: "Legacy $fleet expressions remain supported; use direct Handlebars fields in monitor instead."
|
|
12038
|
+
});
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
return admitted.valid && admitted.contract ? { normalized: admitted.contract.definition, issues } : { issues };
|
|
12042
|
+
}
|
|
11488
12043
|
|
|
11489
12044
|
// ../plays/bootstrap-routes.ts
|
|
11490
12045
|
var DEEPLINE_TOOL_CATEGORIES = [
|
|
@@ -11816,6 +12371,10 @@ export {
|
|
|
11816
12371
|
isPlayBootstrapTemplate,
|
|
11817
12372
|
isProviderUnavailable,
|
|
11818
12373
|
isProviderWaterfallUnavailableError,
|
|
12374
|
+
lintMonitorBatchInput,
|
|
12375
|
+
lintMonitorFleetInput,
|
|
12376
|
+
lintMonitorSpecTemplate,
|
|
12377
|
+
renderMonitorSpecTemplate,
|
|
11819
12378
|
resolveConfig,
|
|
11820
12379
|
runIf,
|
|
11821
12380
|
steps,
|