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
|
@@ -996,6 +996,35 @@ export type MonitorValidateResult = Record<string, unknown>;
|
|
|
996
996
|
*/
|
|
997
997
|
export type MonitorFleetResult = Record<string, unknown>;
|
|
998
998
|
|
|
999
|
+
/** Shared response envelope for immutable batch and desired-set Monitor Jobs. */
|
|
1000
|
+
export type MonitorJobResult = Record<string, unknown>;
|
|
1001
|
+
|
|
1002
|
+
export type MonitorJobDeployOptions = {
|
|
1003
|
+
/** Stable retry key; the server binds it to the submitted immutable set. */
|
|
1004
|
+
idempotencyKey?: string;
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
export type MonitorJobLogsOptions = { limit?: number };
|
|
1008
|
+
|
|
1009
|
+
export type MonitorJobWaitOptions = {
|
|
1010
|
+
timeoutMs?: number;
|
|
1011
|
+
pollIntervalMs?: number;
|
|
1012
|
+
onProgress?: (payload: MonitorJobResult) => void;
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
export type MonitorJobsNamespace = {
|
|
1016
|
+
get: (jobId: string) => Promise<MonitorJobResult>;
|
|
1017
|
+
logs: (
|
|
1018
|
+
jobId: string,
|
|
1019
|
+
options?: MonitorJobLogsOptions,
|
|
1020
|
+
) => Promise<MonitorJobResult>;
|
|
1021
|
+
cancel: (jobId: string) => Promise<MonitorJobResult>;
|
|
1022
|
+
wait: (
|
|
1023
|
+
jobId: string,
|
|
1024
|
+
options?: MonitorJobWaitOptions,
|
|
1025
|
+
) => Promise<MonitorJobResult>;
|
|
1026
|
+
};
|
|
1027
|
+
|
|
999
1028
|
/** Server-computed fleet verdict. Never derived client-side. */
|
|
1000
1029
|
export type MonitorFleetStatus =
|
|
1001
1030
|
| 'converging'
|
|
@@ -1093,7 +1122,7 @@ export type MonitorBatchOperationResult = {
|
|
|
1093
1122
|
| 'reactivate'
|
|
1094
1123
|
| 'deploy'
|
|
1095
1124
|
| 'repair';
|
|
1096
|
-
state: 'running' | 'retry_scheduled' | 'completed' | 'failed';
|
|
1125
|
+
state: 'running' | 'retry_scheduled' | 'completed' | 'failed' | 'cancelled';
|
|
1097
1126
|
dryRun: boolean;
|
|
1098
1127
|
scope: unknown;
|
|
1099
1128
|
submittedAt: number;
|
|
@@ -1113,7 +1142,15 @@ export type MonitorBatchOperationResult = {
|
|
|
1113
1142
|
|
|
1114
1143
|
export type MonitorFleetsNamespace = {
|
|
1115
1144
|
/**
|
|
1116
|
-
*
|
|
1145
|
+
* Primary table-backed Fleet command. It has the same idempotent PUT
|
|
1146
|
+
* contract as `sync`; `sync` remains for installed-client compatibility.
|
|
1147
|
+
*/
|
|
1148
|
+
deploy: (
|
|
1149
|
+
definitionOrId: string | MonitorFleetDefinition,
|
|
1150
|
+
options?: MonitorFleetSyncOptions,
|
|
1151
|
+
) => Promise<MonitorFleetResult>;
|
|
1152
|
+
/**
|
|
1153
|
+
* Compatibility alias for `deploy`. Pass a definition to admit new source
|
|
1117
1154
|
* of truth; pass a fleet id to re-plan from the stored definition.
|
|
1118
1155
|
*/
|
|
1119
1156
|
sync: (
|
|
@@ -1145,6 +1182,11 @@ export type MonitorFleetsNamespace = {
|
|
|
1145
1182
|
};
|
|
1146
1183
|
|
|
1147
1184
|
export type MonitorBatchNamespace = {
|
|
1185
|
+
/** Deploy an immutable one-off list through the common Monitor Job engine. */
|
|
1186
|
+
deploy: (
|
|
1187
|
+
definitions: MonitorDefinition[],
|
|
1188
|
+
options?: MonitorJobDeployOptions,
|
|
1189
|
+
) => Promise<MonitorJobResult>;
|
|
1148
1190
|
submit: (
|
|
1149
1191
|
options: MonitorBatchSubmitOptions,
|
|
1150
1192
|
) => Promise<MonitorBatchOperationResult>;
|
|
@@ -1370,10 +1412,12 @@ export type MonitorsNamespace = {
|
|
|
1370
1412
|
}) => Promise<MonitorsRepairResult>;
|
|
1371
1413
|
/** Delivery and convergence health for the workspace or one fleet. */
|
|
1372
1414
|
health: (options?: { fleetId?: string }) => Promise<MonitorsHealth>;
|
|
1373
|
-
/** Durable bulk audit/deactivation/reactivation/repair with retained progress. */
|
|
1374
|
-
batch: MonitorBatchNamespace;
|
|
1375
1415
|
/** Define, reconcile, and control table-backed monitor fleets. */
|
|
1376
1416
|
fleets: MonitorFleetsNamespace;
|
|
1417
|
+
/** Deploy an immutable one-off list through the common Monitor Job engine. */
|
|
1418
|
+
batch: MonitorBatchNamespace;
|
|
1419
|
+
/** Read, wait for, inspect, or cancel Monitor Jobs. */
|
|
1420
|
+
jobs: MonitorJobsNamespace;
|
|
1377
1421
|
};
|
|
1378
1422
|
|
|
1379
1423
|
/** One credit grant pool reported by the billing subscription status endpoint. */
|
|
@@ -2333,11 +2377,21 @@ export class DeeplineClient {
|
|
|
2333
2377
|
repair: (options) => this.repairMonitors(options),
|
|
2334
2378
|
health: (options) => this.readMonitorsHealth(options),
|
|
2335
2379
|
batch: {
|
|
2380
|
+
deploy: (definitions, options) =>
|
|
2381
|
+
this.deployMonitorBatch(definitions, options),
|
|
2336
2382
|
submit: (options) => this.submitMonitorBatch(options),
|
|
2337
2383
|
get: (operationId, options) =>
|
|
2338
2384
|
this.getMonitorBatch(operationId, options),
|
|
2339
2385
|
},
|
|
2386
|
+
jobs: {
|
|
2387
|
+
get: (jobId) => this.getMonitorJob(jobId),
|
|
2388
|
+
logs: (jobId, options) => this.getMonitorJobLogs(jobId, options),
|
|
2389
|
+
cancel: (jobId) => this.cancelMonitorJob(jobId),
|
|
2390
|
+
wait: (jobId, options) => this.waitForMonitorJob(jobId, options),
|
|
2391
|
+
},
|
|
2340
2392
|
fleets: {
|
|
2393
|
+
deploy: (definitionOrId, options) =>
|
|
2394
|
+
this.syncMonitorFleet(definitionOrId, options),
|
|
2341
2395
|
sync: (definitionOrId, options) =>
|
|
2342
2396
|
this.syncMonitorFleet(definitionOrId, options),
|
|
2343
2397
|
get: (fleetId, options) =>
|
|
@@ -6017,6 +6071,92 @@ export class DeeplineClient {
|
|
|
6017
6071
|
);
|
|
6018
6072
|
}
|
|
6019
6073
|
|
|
6074
|
+
// ——————————————————————————————————————————————————————————
|
|
6075
|
+
// Monitor Jobs, Batches, and Fleets
|
|
6076
|
+
// ——————————————————————————————————————————————————————————
|
|
6077
|
+
|
|
6078
|
+
private monitorJobIdempotencyKey(operation: string): string {
|
|
6079
|
+
const uuid = globalThis.crypto?.randomUUID?.();
|
|
6080
|
+
return `monitor-job-${operation}-${uuid ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
|
|
6081
|
+
}
|
|
6082
|
+
|
|
6083
|
+
private monitorJobHeaders(
|
|
6084
|
+
operation: string,
|
|
6085
|
+
idempotencyKey?: string,
|
|
6086
|
+
): Record<string, string> {
|
|
6087
|
+
return {
|
|
6088
|
+
'Idempotency-Key':
|
|
6089
|
+
idempotencyKey?.trim() || this.monitorJobIdempotencyKey(operation),
|
|
6090
|
+
};
|
|
6091
|
+
}
|
|
6092
|
+
|
|
6093
|
+
async deployMonitorBatch(
|
|
6094
|
+
definitions: MonitorDefinition[],
|
|
6095
|
+
options?: MonitorJobDeployOptions,
|
|
6096
|
+
): Promise<MonitorJobResult> {
|
|
6097
|
+
return this.http.request<MonitorJobResult>(
|
|
6098
|
+
'/api/v2/monitors/batch/deploy',
|
|
6099
|
+
{
|
|
6100
|
+
method: 'POST',
|
|
6101
|
+
body: { version: 1, kind: 'batch', monitors: definitions },
|
|
6102
|
+
headers: this.monitorJobHeaders(
|
|
6103
|
+
'batch-deploy',
|
|
6104
|
+
options?.idempotencyKey,
|
|
6105
|
+
),
|
|
6106
|
+
maxRetries: 0,
|
|
6107
|
+
exactUrlOnly: true,
|
|
6108
|
+
},
|
|
6109
|
+
);
|
|
6110
|
+
}
|
|
6111
|
+
|
|
6112
|
+
async getMonitorJob(jobId: string): Promise<MonitorJobResult> {
|
|
6113
|
+
return this.http.request<MonitorJobResult>(
|
|
6114
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}`,
|
|
6115
|
+
{ method: 'GET' },
|
|
6116
|
+
);
|
|
6117
|
+
}
|
|
6118
|
+
|
|
6119
|
+
async getMonitorJobLogs(
|
|
6120
|
+
jobId: string,
|
|
6121
|
+
options?: MonitorJobLogsOptions,
|
|
6122
|
+
): Promise<MonitorJobResult> {
|
|
6123
|
+
const query =
|
|
6124
|
+
options?.limit === undefined
|
|
6125
|
+
? ''
|
|
6126
|
+
: `?limit=${encodeURIComponent(String(options.limit))}`;
|
|
6127
|
+
return this.http.request<MonitorJobResult>(
|
|
6128
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}/logs${query}`,
|
|
6129
|
+
{ method: 'GET' },
|
|
6130
|
+
);
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
async cancelMonitorJob(jobId: string): Promise<MonitorJobResult> {
|
|
6134
|
+
return this.http.request<MonitorJobResult>(
|
|
6135
|
+
`/api/v2/monitors/jobs/${encodeURIComponent(jobId)}/cancel`,
|
|
6136
|
+
{ method: 'POST', body: {}, maxRetries: 0, exactUrlOnly: true },
|
|
6137
|
+
);
|
|
6138
|
+
}
|
|
6139
|
+
|
|
6140
|
+
async waitForMonitorJob(
|
|
6141
|
+
jobId: string,
|
|
6142
|
+
options?: MonitorJobWaitOptions,
|
|
6143
|
+
): Promise<MonitorJobResult> {
|
|
6144
|
+
const timeoutMs = Math.max(1, options?.timeoutMs ?? 10 * 60_000);
|
|
6145
|
+
const pollIntervalMs = Math.max(1, options?.pollIntervalMs ?? 2_000);
|
|
6146
|
+
const terminal = new Set(['succeeded', 'failed', 'cancelled']);
|
|
6147
|
+
const startedAt = Date.now();
|
|
6148
|
+
let latest = await this.getMonitorJob(jobId);
|
|
6149
|
+
while (true) {
|
|
6150
|
+
options?.onProgress?.(latest);
|
|
6151
|
+
const job = latest.job as Record<string, unknown> | undefined;
|
|
6152
|
+
const state = typeof job?.state === 'string' ? job.state : undefined;
|
|
6153
|
+
if (state && terminal.has(state)) return latest;
|
|
6154
|
+
if (Date.now() - startedAt >= timeoutMs) return latest;
|
|
6155
|
+
await sleep(pollIntervalMs);
|
|
6156
|
+
latest = await this.getMonitorJob(jobId);
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
|
|
6020
6160
|
// ——————————————————————————————————————————————————————————
|
|
6021
6161
|
// Monitor Fleets
|
|
6022
6162
|
// ——————————————————————————————————————————————————————————
|
|
@@ -86,6 +86,12 @@ export type {
|
|
|
86
86
|
MonitorUpdateResult,
|
|
87
87
|
MonitorDeleteResult,
|
|
88
88
|
MonitorReactivateResult,
|
|
89
|
+
MonitorJobResult,
|
|
90
|
+
MonitorJobDeployOptions,
|
|
91
|
+
MonitorJobLogsOptions,
|
|
92
|
+
MonitorJobWaitOptions,
|
|
93
|
+
MonitorJobsNamespace,
|
|
94
|
+
MonitorBatchNamespace,
|
|
89
95
|
MonitorFleetDeactivateOptions,
|
|
90
96
|
MonitorFleetReactivateOptions,
|
|
91
97
|
MonitorFleetGetOptions,
|
|
@@ -113,11 +119,17 @@ export type {
|
|
|
113
119
|
|
|
114
120
|
// ——— Monitors framework ———
|
|
115
121
|
export { defineMonitor } from './monitors.js';
|
|
122
|
+
export type { MonitorDefinition, MonitorPayload } from './monitors.js';
|
|
123
|
+
export {
|
|
124
|
+
lintMonitorBatchInput,
|
|
125
|
+
lintMonitorSpecTemplate,
|
|
126
|
+
renderMonitorSpecTemplate,
|
|
127
|
+
} from './monitor-input-contract.js';
|
|
116
128
|
export type {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
} from './
|
|
129
|
+
BatchMonitorInput,
|
|
130
|
+
MonitorInputIssue,
|
|
131
|
+
MonitorSpec as MonitorInputSpec,
|
|
132
|
+
} from './monitor-input-contract.js';
|
|
121
133
|
export {
|
|
122
134
|
admitMonitorFleetAuthoringContract,
|
|
123
135
|
MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG,
|
|
@@ -128,6 +140,7 @@ export {
|
|
|
128
140
|
defineMonitorFleet,
|
|
129
141
|
fleetColumn,
|
|
130
142
|
fleetKey,
|
|
143
|
+
lintMonitorFleetInput,
|
|
131
144
|
validateMonitorFleetDefinition,
|
|
132
145
|
} from './monitor-fleets.js';
|
|
133
146
|
export type {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../../shared_libs/integration-contracts/monitor-input-contract';
|
|
@@ -48,30 +48,11 @@ export type MonitorDefinition<
|
|
|
48
48
|
name?: string;
|
|
49
49
|
/** Provider-specific monitor payload (e.g. domain + radar_type). */
|
|
50
50
|
payload: TPayload;
|
|
51
|
-
/** Optional Deepline lifecycle metadata (deploy/reuse controls). */
|
|
52
|
-
controls?: MonitorControls;
|
|
53
51
|
};
|
|
54
52
|
|
|
55
53
|
/** Provider-specific monitor payload. Keys and value types depend on the tool. */
|
|
56
54
|
export type MonitorPayload = Record<string, unknown>;
|
|
57
55
|
|
|
58
|
-
/**
|
|
59
|
-
* Deepline lifecycle metadata attached to a monitor definition. These are
|
|
60
|
-
* Deepline-side deploy/reuse controls, not provider payload fields. The set is
|
|
61
|
-
* intentionally open (server-owned) so newer controls do not require an SDK
|
|
62
|
-
* bump; known controls are typed for discoverability.
|
|
63
|
-
*/
|
|
64
|
-
export type MonitorControls = {
|
|
65
|
-
/**
|
|
66
|
-
* Request Deepline Native priority execution for a bounded urgent preview or
|
|
67
|
-
* calibration radar. Deepline sends the upstream custom-field marker and
|
|
68
|
-
* enforces a maximum of ten active or in-flight priority radars per org.
|
|
69
|
-
* Omit this for regular and bulk-scale monitoring.
|
|
70
|
-
*/
|
|
71
|
-
execution_type?: 'priority';
|
|
72
|
-
[key: string]: unknown;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
56
|
/**
|
|
76
57
|
* Define a typed monitor definition.
|
|
77
58
|
*
|
|
@@ -125,13 +106,15 @@ export function defineMonitor<TPayload extends MonitorPayload = MonitorPayload>(
|
|
|
125
106
|
if (definition.name !== undefined && typeof definition.name !== 'string') {
|
|
126
107
|
throw new Error('defineMonitor(definition) "name" must be a string.');
|
|
127
108
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
109
|
+
const authored = definition as Record<string, unknown>;
|
|
110
|
+
if ('controls' in authored) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
'defineMonitor(definition) does not accept "controls". Operational controls are managed by Deepline; bind downstream automation in a Play with sqlListeners.',
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if ('play' in authored) {
|
|
133
116
|
throw new Error(
|
|
134
|
-
'defineMonitor(definition) "
|
|
117
|
+
'defineMonitor(definition) does not accept "play". A monitor does not own Play bindings; declare sqlListeners in the Play instead.',
|
|
135
118
|
);
|
|
136
119
|
}
|
|
137
120
|
return definition;
|
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.98',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'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.',
|
|
206
206
|
packageCapabilities: {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lintMonitorSpecTemplate,
|
|
3
|
+
type MonitorInputIssue,
|
|
4
|
+
} from './monitor-input-contract';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* The canonical Fleet authoring contract, shared by the SDK and server.
|
|
3
8
|
* Monitor transaction admission reuses this ceiling; it must never drift
|
|
@@ -12,9 +17,9 @@ export const MONITOR_FLEET_FRONTIER_MAX_ROWS = 10_000;
|
|
|
12
17
|
* edition: a future syntax change can add a reader without reinterpreting a
|
|
13
18
|
* stored Fleet under new rules.
|
|
14
19
|
*/
|
|
15
|
-
export const MONITOR_FLEET_AUTHORING_CONTRACT_EDITION =
|
|
20
|
+
export const MONITOR_FLEET_AUTHORING_CONTRACT_EDITION = 2 as const;
|
|
16
21
|
export const SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS = [
|
|
17
|
-
|
|
22
|
+
1, 2,
|
|
18
23
|
] as const;
|
|
19
24
|
|
|
20
25
|
export type MonitorFleetAuthoringContractEdition =
|
|
@@ -24,7 +29,15 @@ export const MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG = [
|
|
|
24
29
|
{
|
|
25
30
|
edition: 1,
|
|
26
31
|
changed:
|
|
27
|
-
'Initial Fleet JSON contract: bounded sticky table membership,
|
|
32
|
+
'Initial Fleet JSON contract: bounded sticky table membership, source refresh, and a seven-day removal grace.',
|
|
33
|
+
compatibilityOwner: 'Monitors Runtime',
|
|
34
|
+
newWritesEnd: null,
|
|
35
|
+
readerRemoval: null,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
edition: 2,
|
|
39
|
+
changed:
|
|
40
|
+
'Canonical table-backed Fleet envelope with one Handlebars MonitorSpec template.',
|
|
28
41
|
compatibilityOwner: 'Monitors Runtime',
|
|
29
42
|
newWritesEnd: null,
|
|
30
43
|
readerRemoval: null,
|
|
@@ -50,13 +63,13 @@ export const MONITOR_FLEET_DOCUMENTATION = {
|
|
|
50
63
|
id: 'Stable lowercase fleet id used by sync, get, and deactivate.',
|
|
51
64
|
source:
|
|
52
65
|
'Customer DB table, unique row key, and optional equality filters that select candidate rows.',
|
|
53
|
-
|
|
54
|
-
'
|
|
66
|
+
monitor:
|
|
67
|
+
'One Batch-compatible MonitorSpec. Direct Handlebars source-row fields such as {{domain}} are rendered for each selected row.',
|
|
55
68
|
selection:
|
|
56
69
|
'Deterministic ranking and an active-member limit between 1 and 1,000.',
|
|
57
70
|
},
|
|
58
71
|
fixedPolicy: {
|
|
59
|
-
cadence: '
|
|
72
|
+
cadence: '60s fair bounded source refresh',
|
|
60
73
|
membership: 'sticky',
|
|
61
74
|
removalGrace: '7d',
|
|
62
75
|
onRemoved: 'deactivate',
|
|
@@ -67,8 +80,10 @@ export const MONITOR_FLEET_DOCUMENTATION = {
|
|
|
67
80
|
},
|
|
68
81
|
stickyMembership:
|
|
69
82
|
'Ranking fills vacancies but never replaces a current member merely because another row ranks higher.',
|
|
70
|
-
|
|
71
|
-
'Use
|
|
83
|
+
templating:
|
|
84
|
+
'Use direct Handlebars fields such as {{domain}} in monitor key, name, or payload. Helpers, blocks, partials, paths, and prototype access are disabled.',
|
|
85
|
+
legacyCompatibility:
|
|
86
|
+
'Edition 1 member/$fleet files remain readable with a lint warning; new files should use version: 2, kind: "fleet", and monitor.',
|
|
72
87
|
} as const;
|
|
73
88
|
|
|
74
89
|
export type MonitorFleetColumn = {
|
|
@@ -83,7 +98,7 @@ export type MonitorFleetTemplate = {
|
|
|
83
98
|
|
|
84
99
|
export type MonitorFleetExpression = MonitorFleetColumn | MonitorFleetTemplate;
|
|
85
100
|
|
|
86
|
-
export type
|
|
101
|
+
export type MonitorFleetDefinitionV1 = {
|
|
87
102
|
/** Stable lowercase fleet id, used by CLI and API lifecycle commands. */
|
|
88
103
|
id: string;
|
|
89
104
|
/** Customer DB rows that may become members. */
|
|
@@ -115,6 +130,28 @@ export type MonitorFleetDefinition = {
|
|
|
115
130
|
};
|
|
116
131
|
};
|
|
117
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Edition 2 keeps the public file self-describing and aligns the rendered
|
|
135
|
+
* member with Batch exactly. Only source-row values may be interpolated.
|
|
136
|
+
*/
|
|
137
|
+
export type MonitorFleetDefinitionV2 = {
|
|
138
|
+
version: 2;
|
|
139
|
+
kind: 'fleet';
|
|
140
|
+
id: string;
|
|
141
|
+
source: MonitorFleetDefinitionV1['source'];
|
|
142
|
+
monitor: {
|
|
143
|
+
key: string;
|
|
144
|
+
tool: string;
|
|
145
|
+
name?: string;
|
|
146
|
+
payload: Record<string, unknown>;
|
|
147
|
+
};
|
|
148
|
+
selection: MonitorFleetDefinitionV1['selection'];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export type MonitorFleetDefinition =
|
|
152
|
+
| MonitorFleetDefinitionV1
|
|
153
|
+
| MonitorFleetDefinitionV2;
|
|
154
|
+
|
|
118
155
|
export type MonitorFleetContractIssue = {
|
|
119
156
|
path: string;
|
|
120
157
|
code: string;
|
|
@@ -249,7 +286,7 @@ function walkPayload(
|
|
|
249
286
|
}
|
|
250
287
|
}
|
|
251
288
|
|
|
252
|
-
|
|
289
|
+
function validateMonitorFleetDefinitionV1(value: unknown): {
|
|
253
290
|
valid: boolean;
|
|
254
291
|
definition?: MonitorFleetDefinition;
|
|
255
292
|
issues: MonitorFleetContractIssue[];
|
|
@@ -475,10 +512,130 @@ export function validateMonitorFleetDefinition(value: unknown): {
|
|
|
475
512
|
}
|
|
476
513
|
}
|
|
477
514
|
return issues.length === 0
|
|
478
|
-
? { valid: true, definition: value as
|
|
515
|
+
? { valid: true, definition: value as MonitorFleetDefinitionV1, issues }
|
|
479
516
|
: { valid: false, issues };
|
|
480
517
|
}
|
|
481
518
|
|
|
519
|
+
function v2Issue(issue: MonitorInputIssue): MonitorFleetContractIssue {
|
|
520
|
+
return { path: issue.path, code: issue.code, message: issue.message };
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function validateMonitorFleetDefinitionV2(value: unknown): {
|
|
524
|
+
valid: boolean;
|
|
525
|
+
definition?: MonitorFleetDefinitionV2;
|
|
526
|
+
issues: MonitorFleetContractIssue[];
|
|
527
|
+
} {
|
|
528
|
+
const issues: MonitorFleetContractIssue[] = [];
|
|
529
|
+
const input = record(value);
|
|
530
|
+
if (!input) {
|
|
531
|
+
return {
|
|
532
|
+
valid: false,
|
|
533
|
+
issues: [
|
|
534
|
+
{
|
|
535
|
+
path: '',
|
|
536
|
+
code: 'invalid_fleet',
|
|
537
|
+
message: 'Fleet must be an object.',
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
exactKeys(
|
|
543
|
+
input,
|
|
544
|
+
['version', 'kind', 'id', 'source', 'monitor', 'selection'],
|
|
545
|
+
'',
|
|
546
|
+
issues,
|
|
547
|
+
);
|
|
548
|
+
if (input.version !== 2) {
|
|
549
|
+
issues.push({
|
|
550
|
+
path: 'version',
|
|
551
|
+
code: 'unsupported_fleet_version',
|
|
552
|
+
message: 'Canonical Fleet input version must be 2.',
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
if (input.kind !== 'fleet') {
|
|
556
|
+
issues.push({
|
|
557
|
+
path: 'kind',
|
|
558
|
+
code: 'invalid_fleet_kind',
|
|
559
|
+
message: 'Canonical Fleet input kind must be "fleet".',
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
// Reuse the battle-tested source and selection validator without making
|
|
563
|
+
// edition 2 accept its legacy member grammar.
|
|
564
|
+
const source = record(input.source);
|
|
565
|
+
const sourceKey = record(source?.key);
|
|
566
|
+
const legacyShape = {
|
|
567
|
+
id: input.id,
|
|
568
|
+
source: input.source,
|
|
569
|
+
member: {
|
|
570
|
+
tool: record(input.monitor)?.tool,
|
|
571
|
+
key: {
|
|
572
|
+
$fleet: 'template',
|
|
573
|
+
parts: [
|
|
574
|
+
'fleet-',
|
|
575
|
+
{
|
|
576
|
+
$fleet: 'column',
|
|
577
|
+
name:
|
|
578
|
+
typeof sourceKey?.column === 'string'
|
|
579
|
+
? sourceKey.column
|
|
580
|
+
: 'invalid_source_key',
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
},
|
|
584
|
+
payload: record(input.monitor)?.payload,
|
|
585
|
+
},
|
|
586
|
+
selection: input.selection,
|
|
587
|
+
};
|
|
588
|
+
const legacyCheck = validateMonitorFleetDefinitionV1(legacyShape);
|
|
589
|
+
for (const issue of legacyCheck.issues) {
|
|
590
|
+
issues.push({
|
|
591
|
+
...issue,
|
|
592
|
+
path: issue.path.replace(/^member\./, 'monitor.'),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
const monitorInput = record(input.monitor);
|
|
596
|
+
const monitorKey = monitorInput?.key;
|
|
597
|
+
const monitor = lintMonitorSpecTemplate(input.monitor);
|
|
598
|
+
issues.push(...monitor.issues.map(v2Issue));
|
|
599
|
+
if (
|
|
600
|
+
typeof monitorKey === 'string' &&
|
|
601
|
+
typeof sourceKey?.column === 'string' &&
|
|
602
|
+
!monitorKey.includes(`{{${sourceKey.column}}}`)
|
|
603
|
+
) {
|
|
604
|
+
issues.push({
|
|
605
|
+
path: 'monitor.key',
|
|
606
|
+
code: 'fleet_key_missing_source_key',
|
|
607
|
+
message:
|
|
608
|
+
'Monitor key must include the source key as a direct Handlebars expression.',
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
return issues.length === 0 && monitor.normalized
|
|
612
|
+
? {
|
|
613
|
+
valid: true,
|
|
614
|
+
definition: {
|
|
615
|
+
version: 2,
|
|
616
|
+
kind: 'fleet',
|
|
617
|
+
id: String(input.id),
|
|
618
|
+
source: input.source as MonitorFleetDefinitionV2['source'],
|
|
619
|
+
monitor: monitor.normalized,
|
|
620
|
+
selection: input.selection as MonitorFleetDefinitionV2['selection'],
|
|
621
|
+
},
|
|
622
|
+
issues,
|
|
623
|
+
}
|
|
624
|
+
: { valid: false, issues };
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** Validate the declared edition, or detect canonical v2 versus legacy v1. */
|
|
628
|
+
export function validateMonitorFleetDefinition(value: unknown): {
|
|
629
|
+
valid: boolean;
|
|
630
|
+
definition?: MonitorFleetDefinition;
|
|
631
|
+
issues: MonitorFleetContractIssue[];
|
|
632
|
+
} {
|
|
633
|
+
const input = record(value);
|
|
634
|
+
return input?.version === 2 || input?.kind === 'fleet'
|
|
635
|
+
? validateMonitorFleetDefinitionV2(value)
|
|
636
|
+
: validateMonitorFleetDefinitionV1(value);
|
|
637
|
+
}
|
|
638
|
+
|
|
482
639
|
/**
|
|
483
640
|
* Compile JSON into the exact authoring snapshot persisted by a Fleet. This
|
|
484
641
|
* does not inspect the Customer DB or provider; use
|
|
@@ -487,11 +644,14 @@ export function validateMonitorFleetDefinition(value: unknown): {
|
|
|
487
644
|
*/
|
|
488
645
|
export function admitMonitorFleetAuthoringContract(
|
|
489
646
|
value: unknown,
|
|
490
|
-
edition
|
|
647
|
+
edition?: number,
|
|
491
648
|
): MonitorFleetAuthoringContractResult {
|
|
649
|
+
const detectedEdition =
|
|
650
|
+
edition ??
|
|
651
|
+
(record(value)?.version === 2 || record(value)?.kind === 'fleet' ? 2 : 1);
|
|
492
652
|
if (
|
|
493
653
|
!SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS.includes(
|
|
494
|
-
|
|
654
|
+
detectedEdition as MonitorFleetAuthoringContractEdition,
|
|
495
655
|
)
|
|
496
656
|
) {
|
|
497
657
|
return {
|
|
@@ -500,17 +660,20 @@ export function admitMonitorFleetAuthoringContract(
|
|
|
500
660
|
{
|
|
501
661
|
path: 'authoring_contract_edition',
|
|
502
662
|
code: 'unsupported_fleet_authoring_contract_edition',
|
|
503
|
-
message: `Monitor Fleet authoring contract edition ${
|
|
663
|
+
message: `Monitor Fleet authoring contract edition ${detectedEdition} is not supported.`,
|
|
504
664
|
},
|
|
505
665
|
],
|
|
506
666
|
};
|
|
507
667
|
}
|
|
508
|
-
const result =
|
|
668
|
+
const result =
|
|
669
|
+
detectedEdition === 2
|
|
670
|
+
? validateMonitorFleetDefinitionV2(value)
|
|
671
|
+
: validateMonitorFleetDefinitionV1(value);
|
|
509
672
|
return result.valid && result.definition
|
|
510
673
|
? {
|
|
511
674
|
valid: true,
|
|
512
675
|
contract: {
|
|
513
|
-
edition:
|
|
676
|
+
edition: detectedEdition as MonitorFleetAuthoringContractEdition,
|
|
514
677
|
// JSON cloning prevents later caller mutation from changing the
|
|
515
678
|
// admitted snapshot.
|
|
516
679
|
definition: JSON.parse(
|
|
@@ -522,6 +685,45 @@ export function admitMonitorFleetAuthoringContract(
|
|
|
522
685
|
: { valid: false, issues: result.issues };
|
|
523
686
|
}
|
|
524
687
|
|
|
688
|
+
/**
|
|
689
|
+
* Fast provider-free admission shared by the Fleet CLI, SDK, and API. Legacy
|
|
690
|
+
* documents remain executable but receive an actionable migration warning.
|
|
691
|
+
*/
|
|
692
|
+
export function lintMonitorFleetInput(value: unknown): {
|
|
693
|
+
normalized?: MonitorFleetDefinition;
|
|
694
|
+
issues: Array<MonitorFleetContractIssue & { severity: 'error' | 'warning' }>;
|
|
695
|
+
} {
|
|
696
|
+
const input = record(value);
|
|
697
|
+
const legacy = input?.member !== undefined;
|
|
698
|
+
const admitted = admitMonitorFleetAuthoringContract(value);
|
|
699
|
+
const issues: Array<
|
|
700
|
+
MonitorFleetContractIssue & { severity: 'error' | 'warning' }
|
|
701
|
+
> = admitted.issues.map((issue) => ({
|
|
702
|
+
...issue,
|
|
703
|
+
severity: 'error' as const,
|
|
704
|
+
}));
|
|
705
|
+
if (legacy) {
|
|
706
|
+
issues.push({
|
|
707
|
+
severity: 'warning',
|
|
708
|
+
code: 'deprecated_fleet_member',
|
|
709
|
+
path: 'member',
|
|
710
|
+
message: 'Legacy Fleet member syntax remains supported.',
|
|
711
|
+
});
|
|
712
|
+
if (JSON.stringify(input).includes('"$fleet"')) {
|
|
713
|
+
issues.push({
|
|
714
|
+
severity: 'warning',
|
|
715
|
+
code: 'deprecated_fleet_expression',
|
|
716
|
+
path: 'member',
|
|
717
|
+
message:
|
|
718
|
+
'Legacy $fleet expressions remain supported; use direct Handlebars fields in monitor instead.',
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return admitted.valid && admitted.contract
|
|
723
|
+
? { normalized: admitted.contract.definition, issues }
|
|
724
|
+
: { issues };
|
|
725
|
+
}
|
|
726
|
+
|
|
525
727
|
export function evaluateMonitorFleetValue(
|
|
526
728
|
value: unknown,
|
|
527
729
|
row: Record<string, unknown>,
|