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