deepline 0.2.48 → 0.2.50

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.
@@ -0,0 +1,61 @@
1
+ // Compile-time safety gate for user-authored play code (enrich `extract_js`,
2
+ // `run_if_js`, and `run_javascript` step `code`).
3
+ //
4
+ // Plays replay deterministically under Worker Loader, and the step code runs
5
+ // with provider data in scope. So user code may NOT be non-deterministic
6
+ // (Math.random, Date.now, ...) — replay would diverge — and may NOT reach out
7
+ // of the sandbox (fetch, require, process, ...). We reject those at compile
8
+ // time with a clear, named error (fail loud, per the repo non-negotiables).
9
+ //
10
+ // This is a fast static scan, not a security boundary on its own: the play
11
+ // runtime is the real boundary (it executes step code in an isolate that does
12
+ // not expose these globals). The scan exists to fail authoring early with a
13
+ // readable message instead of producing a play that silently misbehaves at run
14
+ // time. The `(?<!\.)` guards keep it from flagging harmless property access on
15
+ // user objects (e.g. `row.process_date`, `data.fetch`).
16
+
17
+ type ForbiddenRule = { readonly pattern: RegExp; readonly reason: string };
18
+
19
+ const FORBIDDEN: readonly ForbiddenRule[] = [
20
+ // Non-deterministic — breaks replay.
21
+ { pattern: /\bMath\s*\.\s*random\b/, reason: 'Math.random()' },
22
+ { pattern: /\bDate\s*\.\s*now\b/, reason: 'Date.now()' },
23
+ { pattern: /\bnew\s+Date\s*\(\s*\)/, reason: 'new Date() with no argument' },
24
+ { pattern: /\bperformance\s*\.\s*now\b/, reason: 'performance.now()' },
25
+ {
26
+ pattern: /\bcrypto\s*\.\s*(?:randomUUID|getRandomValues)\b/,
27
+ reason: 'crypto random',
28
+ },
29
+ // Sandbox escape / I/O.
30
+ { pattern: /(?<!\.)\bfetch\s*\(/, reason: 'fetch()' },
31
+ { pattern: /(?<!\.)\bimport\s*\(/, reason: 'dynamic import()' },
32
+ { pattern: /(?<!\.)\brequire\s*\(/, reason: 'require()' },
33
+ { pattern: /(?<!\.)\beval\s*\(/, reason: 'eval()' },
34
+ {
35
+ pattern: /(?<!\.)\bnew\s+Function\b|(?<!\.)\bFunction\s*\(/,
36
+ reason: 'the Function constructor',
37
+ },
38
+ { pattern: /(?<!\.)\bprocess\b/, reason: 'process' },
39
+ { pattern: /(?<!\.)\bglobalThis\b/, reason: 'globalThis' },
40
+ { pattern: /(?<!\.)\b(?:window|self)\b/, reason: 'window/self' },
41
+ { pattern: /(?<!\.)\bXMLHttpRequest\b/, reason: 'XMLHttpRequest' },
42
+ { pattern: /(?<!\.)\bWebAssembly\b/, reason: 'WebAssembly' },
43
+ ];
44
+
45
+ /**
46
+ * Throw if `code` references a forbidden (non-deterministic or sandbox-escaping)
47
+ * construct. `label` describes where the code came from for the error message,
48
+ * e.g. `extract_js for "email"` or `run_javascript step "enrich"`.
49
+ */
50
+ export function assertUserCodeIsSafe(code: string, label: string): void {
51
+ if (typeof code !== 'string' || !code.trim()) return;
52
+ for (const { pattern, reason } of FORBIDDEN) {
53
+ if (pattern.test(code)) {
54
+ throw new Error(
55
+ `${label} uses ${reason}, which is not allowed in play code: it ` +
56
+ `breaks deterministic replay or escapes the sandbox. Remove it and ` +
57
+ `compute the value from the row/result instead.`,
58
+ );
59
+ }
60
+ }
61
+ }
package/dist/cli/index.js CHANGED
@@ -1044,7 +1044,7 @@ var SDK_RELEASE = {
1044
1044
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1045
1045
  // exposed storage-dependent synchronous access. This deliberate minor
1046
1046
  // release keeps lazy paging semantics independent of row residency.
1047
- version: "0.2.48",
1047
+ version: "0.2.50",
1048
1048
  contracts: {
1049
1049
  api: {
1050
1050
  name: "sdk-http-api",
@@ -3879,7 +3879,7 @@ var DeeplineClient = class {
3879
3879
  deploy: (definition, options2) => this.deployMonitor(definition, options2),
3880
3880
  list: (options2) => this.listMonitors(options2),
3881
3881
  get: (key) => this.getMonitor(key),
3882
- test: (key, payload) => this.testMonitorWebhook(key, payload),
3882
+ test: (key, payload, options2) => this.testMonitorWebhook(key, payload, options2),
3883
3883
  validate: (key) => this.validateMonitor(key),
3884
3884
  dependents: (key) => this.getMonitorDependents(key),
3885
3885
  update: (key, patch) => this.updateMonitor(key, patch),
@@ -6048,10 +6048,16 @@ var DeeplineClient = class {
6048
6048
  { method: "GET" }
6049
6049
  );
6050
6050
  }
6051
- async testMonitorWebhook(key, payload) {
6051
+ async testMonitorWebhook(key, payload, options) {
6052
6052
  return this.http.request(
6053
6053
  `/api/v2/monitors/deployed/${encodeURIComponent(key)}/test`,
6054
- { method: "POST", body: { payload } }
6054
+ {
6055
+ method: "POST",
6056
+ body: {
6057
+ payload,
6058
+ ...options?.validationOnly ? { mode: "validation_only" } : {}
6059
+ }
6060
+ }
6055
6061
  );
6056
6062
  }
6057
6063
  async setupMonitor(tool, payload) {
@@ -15021,6 +15027,76 @@ var PLAY_AUTHORING_FIELD_REGISTRY = {
15021
15027
  description: "HTTP header containing the webhook signature.",
15022
15028
  errorMessage: "bindings.webhook.hmac.header must be a non-empty static string."
15023
15029
  },
15030
+ "bindings.webhook.auth.type": {
15031
+ schema: Type.Literal("standard-webhooks"),
15032
+ fixtures: {
15033
+ valid: "standard-webhooks",
15034
+ invalid: "svix",
15035
+ absent: void 0,
15036
+ unresolved: { expression: "type" },
15037
+ edition1: void 0
15038
+ },
15039
+ referenceType: "'standard-webhooks'",
15040
+ // auth itself is optional; once present, the AST adapter requires this
15041
+ // field together with headerFamily and signingSecrets.
15042
+ required: false,
15043
+ resolution: "static-required",
15044
+ issueCode: "play_authoring_standard_webhooks_invalid",
15045
+ description: "Uses the Standard Webhooks v1 symmetric signing scheme.",
15046
+ errorMessage: 'bindings.webhook.auth.type must be the static literal "standard-webhooks".'
15047
+ },
15048
+ "bindings.webhook.auth.headerFamily": {
15049
+ schema: Type.Union([Type.Literal("standard"), Type.Literal("svix")]),
15050
+ fixtures: {
15051
+ valid: "svix",
15052
+ invalid: "webhook",
15053
+ absent: void 0,
15054
+ unresolved: { expression: "headerFamily" },
15055
+ edition1: void 0
15056
+ },
15057
+ referenceType: "'standard' | 'svix'",
15058
+ // auth itself is optional; once present, the AST adapter requires this
15059
+ // field together with type and signingSecrets.
15060
+ required: false,
15061
+ resolution: "static-required",
15062
+ issueCode: "play_authoring_standard_webhooks_invalid",
15063
+ description: "Header namespace expected from the webhook provider.",
15064
+ errorMessage: 'bindings.webhook.auth.headerFamily must be the static literal "standard" or "svix".'
15065
+ },
15066
+ "bindings.webhook.auth.signingSecrets[]": {
15067
+ schema: SecretEnvironmentNameSchema,
15068
+ fixtures: {
15069
+ valid: "VECTOR_WEBHOOK_SECRET",
15070
+ invalid: "vector_webhook_secret",
15071
+ absent: void 0,
15072
+ unresolved: { expression: "secret" },
15073
+ edition1: void 0
15074
+ },
15075
+ referenceType: "string",
15076
+ // auth itself is optional; once present, the AST adapter requires this
15077
+ // field together with type and headerFamily.
15078
+ required: false,
15079
+ resolution: "static-required",
15080
+ issueCode: "play_authoring_standard_webhooks_invalid",
15081
+ description: "Deepline Secret name used to verify Standard Webhooks.",
15082
+ errorMessage: "bindings.webhook.auth.signingSecrets entries must be uppercase Deepline Secret names beginning with a letter."
15083
+ },
15084
+ "bindings.webhook.auth.toleranceSeconds": {
15085
+ schema: Type.Integer({ minimum: 1, maximum: 3600 }),
15086
+ fixtures: {
15087
+ valid: 300,
15088
+ invalid: 0,
15089
+ absent: void 0,
15090
+ unresolved: { expression: "toleranceSeconds" },
15091
+ edition1: void 0
15092
+ },
15093
+ referenceType: "number",
15094
+ required: false,
15095
+ resolution: "static-required",
15096
+ issueCode: "play_authoring_standard_webhooks_invalid",
15097
+ description: "Accepted delivery timestamp skew in seconds, from 1 through 3600.",
15098
+ errorMessage: "bindings.webhook.auth.toleranceSeconds must be a static whole number from 1 through 3600."
15099
+ },
15024
15100
  "bindings.cron.schedule": {
15025
15101
  schema: Type.String({ minLength: 1 }),
15026
15102
  fixtures: {
@@ -16045,7 +16121,7 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
16045
16121
  ` inline?: ${cloudReferenceType("inline")};`,
16046
16122
  ` billing?: { maxCreditsPerRun?: ${cloudReferenceType("billing.maxCreditsPerRun")} };`,
16047
16123
  ` runtime?: { timeout?: ${cloudReferenceType("runtime.timeout")}; size?: ${cloudReferenceType("runtime.size")} };`,
16048
- ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} } };`,
16124
+ ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} }; auth?: { type: ${cloudReferenceType("bindings.webhook.auth.type")}; headerFamily: ${cloudReferenceType("bindings.webhook.auth.headerFamily")}; signingSecrets: readonly ${cloudReferenceType("bindings.webhook.auth.signingSecrets[]")}[]; toleranceSeconds?: ${cloudReferenceType("bindings.webhook.auth.toleranceSeconds")} } };`,
16049
16125
  ` cron?: { schedule: ${cloudReferenceType("bindings.cron.schedule")}; timezone?: ${cloudReferenceType("bindings.cron.timezone")} };`,
16050
16126
  " sqlListeners?: readonly SqlListenerDeclaration[];",
16051
16127
  ` secrets?: readonly ${cloudReferenceType("bindings.secrets[]")}[];`,
@@ -24587,7 +24663,7 @@ function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
24587
24663
  return null;
24588
24664
  }
24589
24665
 
24590
- // src/cli/enrich-compat-adapter.ts
24666
+ // ../shared_libs/plays/enrich-compat-adapter.ts
24591
24667
  var ENRICH_COMPAT_DEFAULT_PLAY_NAME = "deepline-enrich-v1-compat";
24592
24668
  var ENRICH_COMPAT_DEFAULT_MAP_NAME = "deepline_enrich_rows";
24593
24669
  function buildEnrichCompatibilityPlan(options = {}) {
@@ -24597,7 +24673,7 @@ function buildEnrichCompatibilityPlan(options = {}) {
24597
24673
  };
24598
24674
  }
24599
24675
 
24600
- // src/cli/user-code-safety.ts
24676
+ // ../shared_libs/plays/user-code-safety.ts
24601
24677
  var FORBIDDEN = [
24602
24678
  // Non-deterministic — breaks replay.
24603
24679
  { pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
@@ -24634,7 +24710,7 @@ function assertUserCodeIsSafe(code, label) {
24634
24710
  }
24635
24711
  }
24636
24712
 
24637
- // src/cli/enrich-play-compiler.ts
24713
+ // ../shared_libs/plays/enrich-play-compiler.ts
24638
24714
  function isWaterfall(command) {
24639
24715
  return "with_waterfall" in command;
24640
24716
  }
@@ -25065,7 +25141,9 @@ function compileEnrichConfigToPlaySource(config, options = {}) {
25065
25141
  const generatedAliases = collectGeneratedAliases(config.commands);
25066
25142
  const runOptionsSource = options.failFast ? `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart), onRowError: 'fail' as const }` : `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart) }`;
25067
25143
  const playOptionsSource = [
25068
- `description: ${stringLiteral("Read a CSV file, run the configured Deepline enrich commands, and return enriched rows.")}`,
25144
+ `description: ${stringLiteral(
25145
+ "Read a CSV file, run the configured Deepline enrich commands, and return enriched rows."
25146
+ )}`,
25069
25147
  ...options.maxCreditsPerRun === void 0 ? [] : [`billing: { maxCreditsPerRun: ${String(options.maxCreditsPerRun)} }`]
25070
25148
  ].join(", ");
25071
25149
  const body = [
@@ -30757,6 +30835,31 @@ function readDeployOutputContract(payload) {
30757
30835
  });
30758
30836
  return { tool: asString(contract.tool), streams };
30759
30837
  }
30838
+ function renderDeployReplacementWarning(input2) {
30839
+ const summary = asRecord2(input2.payload.change_summary);
30840
+ const upstream = summary ? asRecord2(summary.upstream) : void 0;
30841
+ if (!summary || !upstream || upstream.resource_replaced !== true) return [];
30842
+ const definition = asRecord2(summary.definition);
30843
+ const changed = definition && Array.isArray(definition.changed) ? definition.changed : [];
30844
+ const lines = [
30845
+ input2.completed ? "WARNING: this deploy replaced the existing upstream monitor." : "WARNING: this deploy replaces the existing upstream monitor.",
30846
+ "Existing Customer DB rows are preserved. Provider backfill is not implied."
30847
+ ];
30848
+ for (const raw of changed) {
30849
+ const item = asRecord2(raw);
30850
+ const path = item ? asString(item.path) : void 0;
30851
+ if (!item || !path) continue;
30852
+ lines.push(
30853
+ ` ${path}: ${JSON.stringify(item.before ?? null)} \u2192 ${JSON.stringify(
30854
+ item.after ?? null
30855
+ )}`
30856
+ );
30857
+ }
30858
+ lines.push(
30859
+ input2.monitorKey ? `Use \`deepline monitors update ${input2.monitorKey} <patch>\` for a patch-style change.` : "Use `deepline monitors update <key> <patch>` for a patch-style change."
30860
+ );
30861
+ return lines;
30862
+ }
30760
30863
  function renderMonitorDeployCompletion(payload) {
30761
30864
  const monitor = asRecord2(payload.monitor);
30762
30865
  const key = monitor ? asString(monitor.key) : void 0;
@@ -30784,6 +30887,12 @@ function renderMonitorDeployCompletion(payload) {
30784
30887
  if (pricingLine) {
30785
30888
  lines.push("", `Pricing: ${pricingLine}`);
30786
30889
  }
30890
+ const replacementWarning = renderDeployReplacementWarning({
30891
+ payload,
30892
+ completed: true,
30893
+ monitorKey: key
30894
+ });
30895
+ if (replacementWarning.length) lines.push("", ...replacementWarning);
30787
30896
  const guidance = asRecord2(payload.setup_guidance);
30788
30897
  if (guidance) {
30789
30898
  const callbackUrl = asString(guidance.callback_url);
@@ -30835,6 +30944,11 @@ function renderMonitorDeployPlan(payload) {
30835
30944
  if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
30836
30945
  }
30837
30946
  }
30947
+ const replacementWarning = renderDeployReplacementWarning({
30948
+ payload,
30949
+ completed: false
30950
+ });
30951
+ if (replacementWarning.length) lines.push("", ...replacementWarning);
30838
30952
  const estimate = asRecord2(payload.deploy_cost_estimate);
30839
30953
  const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
30840
30954
  if (credits !== void 0) {
@@ -31201,10 +31315,17 @@ async function handleMonitorsGet(key, options) {
31201
31315
  }
31202
31316
  async function handleMonitorsTest(key, payload, options) {
31203
31317
  const explicitPayload = parseJsonObjectArg(payload, "<payload>");
31204
- const result = await new DeeplineClient().monitors.test(key, explicitPayload);
31205
- const text = `Webhook test for ${key}: ${result.accepted === true ? "accepted" : "rejected"}
31206
- persisted rows: ${asFiniteNumber(result.persisted_rows) ?? 0}
31207
- bound Plays dispatched: ${asFiniteNumber(result.dispatched_bound_plays) ?? 0}
31318
+ const result = await new DeeplineClient().monitors.test(
31319
+ key,
31320
+ explicitPayload,
31321
+ {
31322
+ validationOnly: options.dispatch !== true
31323
+ }
31324
+ );
31325
+ const dispatch = options.dispatch === true;
31326
+ const text = `Monitor diagnostic for ${key}: ${result.accepted === true ? "accepted" : "rejected"}
31327
+ ` + (dispatch ? " mode: dispatch (writes rows and may dispatch bound Plays)\n" : " mode: validation_only (no rows written, credits spent, or Plays dispatched)\n") + `${dispatch ? "persisted" : "would persist"} rows: ${asFiniteNumber(result.persisted_rows) ?? 0}
31328
+ ${dispatch ? "dispatched" : "would dispatch"} bound Plays: ${asFiniteNumber(result.dispatched_bound_plays) ?? 0}
31208
31329
  `;
31209
31330
  printCommandEnvelope(result, { json: options.json, text });
31210
31331
  }
@@ -31416,12 +31537,18 @@ Examples:
31416
31537
  withJsonOption(
31417
31538
  monitors.command("test <key> <payload>").description(
31418
31539
  "Send an explicit payload through a monitor\u2019s webhook ingestion path."
31540
+ ).option(
31541
+ "--dispatch",
31542
+ "Inject the test event through normal ingestion (writes rows and may dispatch bound Plays)"
31419
31543
  ).addHelpText(
31420
31544
  "after",
31421
31545
  `
31422
31546
  Notes:
31423
31547
  <payload> must be an explicit JSON object. The command uses the deployed
31424
- monitor\u2019s real validation, persistence, and inline Play dispatch path; it does
31548
+ monitor\u2019s real binding and payload validation, but is a side-effect-free
31549
+ diagnostic: it does not persist rows, spend credits, dispatch Plays, or alter
31550
+ monitor state. Pass --dispatch only when you deliberately need the historic
31551
+ full-ingestion test event; it can write rows and trigger bound Plays. It does
31425
31552
  not synthesize a provider event or accept an omitted payload.
31426
31553
 
31427
31554
  Examples:
@@ -31442,7 +31569,9 @@ Notes:
31442
31569
  via --file <path>, or
31443
31570
  from stdin with --file -. Does not deploy or spend credits.
31444
31571
  For Deepline Native Company Radar monitors, check validates persona-filter enums and the
31445
- job_titles Boolean-expression grammar locally. Inspect the exact schema with
31572
+ job_titles Boolean-expression grammar locally (parentheses are unsupported;
31573
+ NOT > AND > OR).
31574
+ Inspect the exact schema with
31446
31575
  \`deepline monitors available deepline_native.company_radar --json\`.
31447
31576
 
31448
31577
  Examples:
@@ -31466,6 +31595,9 @@ Notes:
31466
31595
  --dry-run validates the definition and shows the plan (deploy cost in Deepline
31467
31596
  credits when the server reports it, plus any existing monitors that may
31468
31597
  already cover this scope) WITHOUT deploying. Exits 0 when valid, 7 when not.
31598
+ Deploy is a full desired definition for its key: omitting a previously stored
31599
+ field removes it and can replace the upstream resource. Use \`monitors update\`
31600
+ for a patch-style change.
31469
31601
 
31470
31602
  Examples:
31471
31603
  deepline monitors deploy '{"key":"job-openings","tool":"deepline_native.company_radar","payload":{"domain":"stripe.com","radar_type":"company_job_openings"}}'
@@ -1030,7 +1030,7 @@ var SDK_RELEASE = {
1030
1030
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1031
1031
  // exposed storage-dependent synchronous access. This deliberate minor
1032
1032
  // release keeps lazy paging semantics independent of row residency.
1033
- version: "0.2.48",
1033
+ version: "0.2.50",
1034
1034
  contracts: {
1035
1035
  api: {
1036
1036
  name: "sdk-http-api",
@@ -3865,7 +3865,7 @@ var DeeplineClient = class {
3865
3865
  deploy: (definition, options2) => this.deployMonitor(definition, options2),
3866
3866
  list: (options2) => this.listMonitors(options2),
3867
3867
  get: (key) => this.getMonitor(key),
3868
- test: (key, payload) => this.testMonitorWebhook(key, payload),
3868
+ test: (key, payload, options2) => this.testMonitorWebhook(key, payload, options2),
3869
3869
  validate: (key) => this.validateMonitor(key),
3870
3870
  dependents: (key) => this.getMonitorDependents(key),
3871
3871
  update: (key, patch) => this.updateMonitor(key, patch),
@@ -6034,10 +6034,16 @@ var DeeplineClient = class {
6034
6034
  { method: "GET" }
6035
6035
  );
6036
6036
  }
6037
- async testMonitorWebhook(key, payload) {
6037
+ async testMonitorWebhook(key, payload, options) {
6038
6038
  return this.http.request(
6039
6039
  `/api/v2/monitors/deployed/${encodeURIComponent(key)}/test`,
6040
- { method: "POST", body: { payload } }
6040
+ {
6041
+ method: "POST",
6042
+ body: {
6043
+ payload,
6044
+ ...options?.validationOnly ? { mode: "validation_only" } : {}
6045
+ }
6046
+ }
6041
6047
  );
6042
6048
  }
6043
6049
  async setupMonitor(tool, payload) {
@@ -12307,7 +12313,9 @@ import {
12307
12313
  extname,
12308
12314
  isAbsolute as isAbsolute3,
12309
12315
  join as join7,
12310
- resolve as resolve8
12316
+ relative as relative2,
12317
+ resolve as resolve8,
12318
+ sep
12311
12319
  } from "path";
12312
12320
  import { builtinModules } from "module";
12313
12321
  import { Parser } from "acorn";
@@ -15058,6 +15066,76 @@ var PLAY_AUTHORING_FIELD_REGISTRY = {
15058
15066
  description: "HTTP header containing the webhook signature.",
15059
15067
  errorMessage: "bindings.webhook.hmac.header must be a non-empty static string."
15060
15068
  },
15069
+ "bindings.webhook.auth.type": {
15070
+ schema: Type.Literal("standard-webhooks"),
15071
+ fixtures: {
15072
+ valid: "standard-webhooks",
15073
+ invalid: "svix",
15074
+ absent: void 0,
15075
+ unresolved: { expression: "type" },
15076
+ edition1: void 0
15077
+ },
15078
+ referenceType: "'standard-webhooks'",
15079
+ // auth itself is optional; once present, the AST adapter requires this
15080
+ // field together with headerFamily and signingSecrets.
15081
+ required: false,
15082
+ resolution: "static-required",
15083
+ issueCode: "play_authoring_standard_webhooks_invalid",
15084
+ description: "Uses the Standard Webhooks v1 symmetric signing scheme.",
15085
+ errorMessage: 'bindings.webhook.auth.type must be the static literal "standard-webhooks".'
15086
+ },
15087
+ "bindings.webhook.auth.headerFamily": {
15088
+ schema: Type.Union([Type.Literal("standard"), Type.Literal("svix")]),
15089
+ fixtures: {
15090
+ valid: "svix",
15091
+ invalid: "webhook",
15092
+ absent: void 0,
15093
+ unresolved: { expression: "headerFamily" },
15094
+ edition1: void 0
15095
+ },
15096
+ referenceType: "'standard' | 'svix'",
15097
+ // auth itself is optional; once present, the AST adapter requires this
15098
+ // field together with type and signingSecrets.
15099
+ required: false,
15100
+ resolution: "static-required",
15101
+ issueCode: "play_authoring_standard_webhooks_invalid",
15102
+ description: "Header namespace expected from the webhook provider.",
15103
+ errorMessage: 'bindings.webhook.auth.headerFamily must be the static literal "standard" or "svix".'
15104
+ },
15105
+ "bindings.webhook.auth.signingSecrets[]": {
15106
+ schema: SecretEnvironmentNameSchema,
15107
+ fixtures: {
15108
+ valid: "VECTOR_WEBHOOK_SECRET",
15109
+ invalid: "vector_webhook_secret",
15110
+ absent: void 0,
15111
+ unresolved: { expression: "secret" },
15112
+ edition1: void 0
15113
+ },
15114
+ referenceType: "string",
15115
+ // auth itself is optional; once present, the AST adapter requires this
15116
+ // field together with type and headerFamily.
15117
+ required: false,
15118
+ resolution: "static-required",
15119
+ issueCode: "play_authoring_standard_webhooks_invalid",
15120
+ description: "Deepline Secret name used to verify Standard Webhooks.",
15121
+ errorMessage: "bindings.webhook.auth.signingSecrets entries must be uppercase Deepline Secret names beginning with a letter."
15122
+ },
15123
+ "bindings.webhook.auth.toleranceSeconds": {
15124
+ schema: Type.Integer({ minimum: 1, maximum: 3600 }),
15125
+ fixtures: {
15126
+ valid: 300,
15127
+ invalid: 0,
15128
+ absent: void 0,
15129
+ unresolved: { expression: "toleranceSeconds" },
15130
+ edition1: void 0
15131
+ },
15132
+ referenceType: "number",
15133
+ required: false,
15134
+ resolution: "static-required",
15135
+ issueCode: "play_authoring_standard_webhooks_invalid",
15136
+ description: "Accepted delivery timestamp skew in seconds, from 1 through 3600.",
15137
+ errorMessage: "bindings.webhook.auth.toleranceSeconds must be a static whole number from 1 through 3600."
15138
+ },
15061
15139
  "bindings.cron.schedule": {
15062
15140
  schema: Type.String({ minLength: 1 }),
15063
15141
  fixtures: {
@@ -16082,7 +16160,7 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
16082
16160
  ` inline?: ${cloudReferenceType("inline")};`,
16083
16161
  ` billing?: { maxCreditsPerRun?: ${cloudReferenceType("billing.maxCreditsPerRun")} };`,
16084
16162
  ` runtime?: { timeout?: ${cloudReferenceType("runtime.timeout")}; size?: ${cloudReferenceType("runtime.size")} };`,
16085
- ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} } };`,
16163
+ ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} }; auth?: { type: ${cloudReferenceType("bindings.webhook.auth.type")}; headerFamily: ${cloudReferenceType("bindings.webhook.auth.headerFamily")}; signingSecrets: readonly ${cloudReferenceType("bindings.webhook.auth.signingSecrets[]")}[]; toleranceSeconds?: ${cloudReferenceType("bindings.webhook.auth.toleranceSeconds")} } };`,
16086
16164
  ` cron?: { schedule: ${cloudReferenceType("bindings.cron.schedule")}; timezone?: ${cloudReferenceType("bindings.cron.timezone")} };`,
16087
16165
  " sqlListeners?: readonly SqlListenerDeclaration[];",
16088
16166
  ` secrets?: readonly ${cloudReferenceType("bindings.secrets[]")}[];`,
@@ -24631,7 +24709,7 @@ function getterFromLegacyExtractJs(extractJs, fallbackAlias) {
24631
24709
  return null;
24632
24710
  }
24633
24711
 
24634
- // src/cli/enrich-compat-adapter.ts
24712
+ // ../shared_libs/plays/enrich-compat-adapter.ts
24635
24713
  var ENRICH_COMPAT_DEFAULT_PLAY_NAME = "deepline-enrich-v1-compat";
24636
24714
  var ENRICH_COMPAT_DEFAULT_MAP_NAME = "deepline_enrich_rows";
24637
24715
  function buildEnrichCompatibilityPlan(options = {}) {
@@ -24641,7 +24719,7 @@ function buildEnrichCompatibilityPlan(options = {}) {
24641
24719
  };
24642
24720
  }
24643
24721
 
24644
- // src/cli/user-code-safety.ts
24722
+ // ../shared_libs/plays/user-code-safety.ts
24645
24723
  var FORBIDDEN = [
24646
24724
  // Non-deterministic — breaks replay.
24647
24725
  { pattern: /\bMath\s*\.\s*random\b/, reason: "Math.random()" },
@@ -24678,7 +24756,7 @@ function assertUserCodeIsSafe(code, label) {
24678
24756
  }
24679
24757
  }
24680
24758
 
24681
- // src/cli/enrich-play-compiler.ts
24759
+ // ../shared_libs/plays/enrich-play-compiler.ts
24682
24760
  function isWaterfall(command) {
24683
24761
  return "with_waterfall" in command;
24684
24762
  }
@@ -25109,7 +25187,9 @@ function compileEnrichConfigToPlaySource(config, options = {}) {
25109
25187
  const generatedAliases = collectGeneratedAliases(config.commands);
25110
25188
  const runOptionsSource = options.failFast ? `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart), onRowError: 'fail' as const }` : `{ key: (row, index) => __dlEnrichRowKey(row, index + rowStart) }`;
25111
25189
  const playOptionsSource = [
25112
- `description: ${stringLiteral("Read a CSV file, run the configured Deepline enrich commands, and return enriched rows.")}`,
25190
+ `description: ${stringLiteral(
25191
+ "Read a CSV file, run the configured Deepline enrich commands, and return enriched rows."
25192
+ )}`,
25113
25193
  ...options.maxCreditsPerRun === void 0 ? [] : [`billing: { maxCreditsPerRun: ${String(options.maxCreditsPerRun)} }`]
25114
25194
  ].join(", ");
25115
25195
  const body = [
@@ -30808,6 +30888,31 @@ function readDeployOutputContract(payload) {
30808
30888
  });
30809
30889
  return { tool: asString(contract.tool), streams };
30810
30890
  }
30891
+ function renderDeployReplacementWarning(input2) {
30892
+ const summary = asRecord2(input2.payload.change_summary);
30893
+ const upstream = summary ? asRecord2(summary.upstream) : void 0;
30894
+ if (!summary || !upstream || upstream.resource_replaced !== true) return [];
30895
+ const definition = asRecord2(summary.definition);
30896
+ const changed = definition && Array.isArray(definition.changed) ? definition.changed : [];
30897
+ const lines = [
30898
+ input2.completed ? "WARNING: this deploy replaced the existing upstream monitor." : "WARNING: this deploy replaces the existing upstream monitor.",
30899
+ "Existing Customer DB rows are preserved. Provider backfill is not implied."
30900
+ ];
30901
+ for (const raw of changed) {
30902
+ const item = asRecord2(raw);
30903
+ const path = item ? asString(item.path) : void 0;
30904
+ if (!item || !path) continue;
30905
+ lines.push(
30906
+ ` ${path}: ${JSON.stringify(item.before ?? null)} \u2192 ${JSON.stringify(
30907
+ item.after ?? null
30908
+ )}`
30909
+ );
30910
+ }
30911
+ lines.push(
30912
+ input2.monitorKey ? `Use \`deepline monitors update ${input2.monitorKey} <patch>\` for a patch-style change.` : "Use `deepline monitors update <key> <patch>` for a patch-style change."
30913
+ );
30914
+ return lines;
30915
+ }
30811
30916
  function renderMonitorDeployCompletion(payload) {
30812
30917
  const monitor = asRecord2(payload.monitor);
30813
30918
  const key = monitor ? asString(monitor.key) : void 0;
@@ -30835,6 +30940,12 @@ function renderMonitorDeployCompletion(payload) {
30835
30940
  if (pricingLine) {
30836
30941
  lines.push("", `Pricing: ${pricingLine}`);
30837
30942
  }
30943
+ const replacementWarning = renderDeployReplacementWarning({
30944
+ payload,
30945
+ completed: true,
30946
+ monitorKey: key
30947
+ });
30948
+ if (replacementWarning.length) lines.push("", ...replacementWarning);
30838
30949
  const guidance = asRecord2(payload.setup_guidance);
30839
30950
  if (guidance) {
30840
30951
  const callbackUrl = asString(guidance.callback_url);
@@ -30886,6 +30997,11 @@ function renderMonitorDeployPlan(payload) {
30886
30997
  if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
30887
30998
  }
30888
30999
  }
31000
+ const replacementWarning = renderDeployReplacementWarning({
31001
+ payload,
31002
+ completed: false
31003
+ });
31004
+ if (replacementWarning.length) lines.push("", ...replacementWarning);
30889
31005
  const estimate = asRecord2(payload.deploy_cost_estimate);
30890
31006
  const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
30891
31007
  if (credits !== void 0) {
@@ -31252,10 +31368,17 @@ async function handleMonitorsGet(key, options) {
31252
31368
  }
31253
31369
  async function handleMonitorsTest(key, payload, options) {
31254
31370
  const explicitPayload = parseJsonObjectArg(payload, "<payload>");
31255
- const result = await new DeeplineClient().monitors.test(key, explicitPayload);
31256
- const text = `Webhook test for ${key}: ${result.accepted === true ? "accepted" : "rejected"}
31257
- persisted rows: ${asFiniteNumber(result.persisted_rows) ?? 0}
31258
- bound Plays dispatched: ${asFiniteNumber(result.dispatched_bound_plays) ?? 0}
31371
+ const result = await new DeeplineClient().monitors.test(
31372
+ key,
31373
+ explicitPayload,
31374
+ {
31375
+ validationOnly: options.dispatch !== true
31376
+ }
31377
+ );
31378
+ const dispatch = options.dispatch === true;
31379
+ const text = `Monitor diagnostic for ${key}: ${result.accepted === true ? "accepted" : "rejected"}
31380
+ ` + (dispatch ? " mode: dispatch (writes rows and may dispatch bound Plays)\n" : " mode: validation_only (no rows written, credits spent, or Plays dispatched)\n") + `${dispatch ? "persisted" : "would persist"} rows: ${asFiniteNumber(result.persisted_rows) ?? 0}
31381
+ ${dispatch ? "dispatched" : "would dispatch"} bound Plays: ${asFiniteNumber(result.dispatched_bound_plays) ?? 0}
31259
31382
  `;
31260
31383
  printCommandEnvelope(result, { json: options.json, text });
31261
31384
  }
@@ -31467,12 +31590,18 @@ Examples:
31467
31590
  withJsonOption(
31468
31591
  monitors.command("test <key> <payload>").description(
31469
31592
  "Send an explicit payload through a monitor\u2019s webhook ingestion path."
31593
+ ).option(
31594
+ "--dispatch",
31595
+ "Inject the test event through normal ingestion (writes rows and may dispatch bound Plays)"
31470
31596
  ).addHelpText(
31471
31597
  "after",
31472
31598
  `
31473
31599
  Notes:
31474
31600
  <payload> must be an explicit JSON object. The command uses the deployed
31475
- monitor\u2019s real validation, persistence, and inline Play dispatch path; it does
31601
+ monitor\u2019s real binding and payload validation, but is a side-effect-free
31602
+ diagnostic: it does not persist rows, spend credits, dispatch Plays, or alter
31603
+ monitor state. Pass --dispatch only when you deliberately need the historic
31604
+ full-ingestion test event; it can write rows and trigger bound Plays. It does
31476
31605
  not synthesize a provider event or accept an omitted payload.
31477
31606
 
31478
31607
  Examples:
@@ -31493,7 +31622,9 @@ Notes:
31493
31622
  via --file <path>, or
31494
31623
  from stdin with --file -. Does not deploy or spend credits.
31495
31624
  For Deepline Native Company Radar monitors, check validates persona-filter enums and the
31496
- job_titles Boolean-expression grammar locally. Inspect the exact schema with
31625
+ job_titles Boolean-expression grammar locally (parentheses are unsupported;
31626
+ NOT > AND > OR).
31627
+ Inspect the exact schema with
31497
31628
  \`deepline monitors available deepline_native.company_radar --json\`.
31498
31629
 
31499
31630
  Examples:
@@ -31517,6 +31648,9 @@ Notes:
31517
31648
  --dry-run validates the definition and shows the plan (deploy cost in Deepline
31518
31649
  credits when the server reports it, plus any existing monitors that may
31519
31650
  already cover this scope) WITHOUT deploying. Exits 0 when valid, 7 when not.
31651
+ Deploy is a full desired definition for its key: omitting a previously stored
31652
+ field removes it and can replace the upstream resource. Use \`monitors update\`
31653
+ for a patch-style change.
31520
31654
 
31521
31655
  Examples:
31522
31656
  deepline monitors deploy '{"key":"job-openings","tool":"deepline_native.company_radar","payload":{"domain":"stripe.com","radar_type":"company_job_openings"}}'