deepline 0.1.93 → 0.1.95

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.
@@ -73,6 +73,10 @@
73
73
  import { DeeplineClient } from './client.js';
74
74
  import { DeeplineError } from './errors.js';
75
75
  import { createToolExecuteResult } from '../../shared_libs/play-runtime/tool-result.js';
76
+ export {
77
+ readValue,
78
+ readList,
79
+ } from '../../shared_libs/play-runtime/tool-result.js';
76
80
  import type {
77
81
  PlayDataset,
78
82
  PlayDatasetInput,
@@ -81,6 +85,7 @@ import type {
81
85
  ToolExecuteResult,
82
86
  ToolResultMetadataInput,
83
87
  } from '../../shared_libs/play-runtime/tool-result-types.js';
88
+ import type { EmailStatusExtractorConfig } from '../../shared_libs/play-runtime/email-status.js';
84
89
  import type { PreviousCell } from '../../shared_libs/play-runtime/cell-staleness.js';
85
90
  import type {
86
91
  DeeplineClientOptions,
@@ -1549,6 +1554,50 @@ function stringArray(value: unknown): string[] {
1549
1554
  return Array.isArray(value) ? value.map(String) : [];
1550
1555
  }
1551
1556
 
1557
+ function emailStatusExtractorConfig(
1558
+ value: unknown,
1559
+ ): EmailStatusExtractorConfig | undefined {
1560
+ if (!isRecord(value)) return undefined;
1561
+ const readPaths = (key: string): string[] | undefined => {
1562
+ const paths = stringArray(value[key])
1563
+ .map((path) => path.trim())
1564
+ .filter(Boolean);
1565
+ return paths.length > 0 ? paths : undefined;
1566
+ };
1567
+ const provider =
1568
+ typeof value.provider === 'string' && value.provider.trim()
1569
+ ? value.provider.trim()
1570
+ : null;
1571
+ if (!provider) return undefined;
1572
+ const config: EmailStatusExtractorConfig = { provider };
1573
+ for (const key of [
1574
+ 'rawStatus',
1575
+ 'rawScore',
1576
+ 'valid',
1577
+ 'deliverability',
1578
+ 'catchAll',
1579
+ 'mxProvider',
1580
+ 'mxRecord',
1581
+ 'fraudScore',
1582
+ 'disposable',
1583
+ 'roleBased',
1584
+ 'freeEmail',
1585
+ 'abuse',
1586
+ 'spamtrap',
1587
+ 'suspect',
1588
+ ] as const) {
1589
+ const paths = readPaths(key);
1590
+ if (paths) config[key] = paths;
1591
+ }
1592
+ if (isRecord(value.statusMap)) {
1593
+ config.statusMap = value.statusMap as EmailStatusExtractorConfig['statusMap'];
1594
+ }
1595
+ if (Array.isArray(value.rules)) {
1596
+ config.rules = value.rules as EmailStatusExtractorConfig['rules'];
1597
+ }
1598
+ return config;
1599
+ }
1600
+
1552
1601
  function extractorDescriptorRecord(
1553
1602
  value: unknown,
1554
1603
  ): ToolResultMetadataInput['extractors'] {
@@ -1566,6 +1615,7 @@ function extractorDescriptorRecord(
1566
1615
  const enumValues = stringArray(descriptor.enum)
1567
1616
  .map((entry) => entry.trim())
1568
1617
  .filter(Boolean);
1618
+ const emailStatus = emailStatusExtractorConfig(descriptor.emailStatus);
1569
1619
  return [
1570
1620
  [
1571
1621
  key,
@@ -1573,6 +1623,7 @@ function extractorDescriptorRecord(
1573
1623
  paths,
1574
1624
  ...(transforms.length > 0 ? { transforms } : {}),
1575
1625
  ...(enumValues.length > 0 ? { enum: enumValues } : {}),
1626
+ ...(emailStatus ? { emailStatus } : {}),
1576
1627
  },
1577
1628
  ],
1578
1629
  ];
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.93',
53
+ version: '0.1.95',
54
54
  apiContract: '2026-06-dataset-column-cell-stale-hard-cutover',
55
55
  supportPolicy: {
56
- latest: '0.1.93',
56
+ latest: '0.1.95',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
@@ -89,41 +89,15 @@ export type EmailStatusBuildInput = {
89
89
  values: Record<string, unknown>;
90
90
  };
91
91
 
92
- const DEFAULT_STATUS_MAP: Record<string, EmailStatusMapEntry> = {
93
- verified: { status: 'valid', verdict: 'send', verified: true },
94
- valid: { status: 'valid', verdict: 'send', verified: true },
95
- deliverable: { status: 'valid', verdict: 'send', verified: true },
96
- true: { status: 'valid', verdict: 'send', verified: true },
97
- invalid: { status: 'invalid', verdict: 'drop', verified: false },
98
- undeliverable: { status: 'invalid', verdict: 'drop', verified: false },
99
- false: { status: 'invalid', verdict: 'drop', verified: false },
100
- 'catch-all': {
101
- status: 'catch_all',
102
- verdict: 'verify_next',
103
- verified: false,
104
- },
105
- catch_all: {
106
- status: 'catch_all',
107
- verdict: 'verify_next',
108
- verified: false,
109
- },
110
- valid_catch_all: {
111
- status: 'valid_catch_all',
112
- verdict: 'send_with_caution',
113
- verified: true,
114
- },
115
- accept_all: {
116
- status: 'catch_all',
117
- verdict: 'verify_next',
118
- verified: false,
119
- },
120
- unknown: { status: 'unknown', verdict: 'hold', verified: false },
121
- unavailable: { status: 'unknown', verdict: 'hold', verified: false },
122
- do_not_mail: { status: 'do_not_mail', verdict: 'drop', verified: false },
123
- spamtrap: { status: 'spamtrap', verdict: 'drop', verified: false },
124
- abuse: { status: 'abuse', verdict: 'drop', verified: false },
125
- disposable: { status: 'disposable', verdict: 'drop', verified: false },
126
- };
92
+ // There is intentionally NO shared default status map. Each provider must
93
+ // declare its own `statusMap` / `rules` for the raw status strings it emits
94
+ // (see the provider registries under src/lib/integrations/**). A coarse global
95
+ // map silently coerced provider-specific strings ("deliverable", "accept_all",
96
+ // etc.) into canonical verdicts and let validators depend on guesses instead of
97
+ // documented behavior. With it gone, an unmapped raw status falls through to
98
+ // the typed signal inference below (and ultimately to `unknown`/`hold`) rather
99
+ // than being normalized by a global lookup — loud and provider-declared over
100
+ // silent and implicit.
127
101
 
128
102
  function normalizeKey(value: unknown): string | null {
129
103
  if (value == null) return null;
@@ -186,7 +160,7 @@ function entryForStatus(
186
160
  map: Record<string, EmailStatusMapEntry> | undefined,
187
161
  ): EmailStatusMapEntry | null {
188
162
  if (!key) return null;
189
- return map?.[key] ?? DEFAULT_STATUS_MAP[key] ?? null;
163
+ return map?.[key] ?? null;
190
164
  }
191
165
 
192
166
  function read(values: Record<string, unknown>, name: string): unknown {
@@ -1,4 +1,4 @@
1
- import type { EmailStatus, EmailStatusValue } from './email-status';
1
+ import type { EmailStatus } from './email-status';
2
2
 
3
3
  export const JOB_CHANGE_STATUS_VALUES = [
4
4
  'moved',
@@ -65,7 +65,7 @@ export const DEEPLINE_EXTRACTOR_TARGETS = Object.keys(
65
65
  DEEPLINE_EXTRACTOR_TARGET_DEFINITIONS,
66
66
  ) as DeeplineExtractorTarget[];
67
67
 
68
- export type DeeplineEmailStatusGetterValue = EmailStatus | EmailStatusValue;
68
+ export type DeeplineEmailStatusGetterValue = EmailStatus;
69
69
 
70
70
  export type DeeplineGetterValueMap = {
71
71
  id: string;
@@ -91,7 +91,7 @@ export type DeeplineGetterValueMap = {
91
91
  status: string;
92
92
  job_change: JobChangeGetterValue;
93
93
  job_change_status: JobChangeStatus;
94
- email_status: DeeplineEmailStatusGetterValue;
94
+ email_status: EmailStatus;
95
95
  phone_status: PhoneStatus;
96
96
  };
97
97
 
@@ -1029,6 +1029,50 @@ export function isToolExecuteResult(
1029
1029
  );
1030
1030
  }
1031
1031
 
1032
+ function resultRootOf(result: ToolExecuteResult): unknown {
1033
+ return { toolResponse: result.toolOutput };
1034
+ }
1035
+
1036
+ /**
1037
+ * Read a single value out of a tool result — by declared getter name, then by
1038
+ * selector paths, then by key. Used by SDK play helpers (`extractValue`).
1039
+ */
1040
+ export function readValue(
1041
+ result: ToolExecuteResult,
1042
+ selector: readonly string[] | string,
1043
+ ): unknown {
1044
+ if (typeof selector === 'string') {
1045
+ const declared = result.extractedValues[selector]?.get();
1046
+ if (declared != null) return declared;
1047
+ }
1048
+ const root = resultRootOf(result);
1049
+ const paths = Array.isArray(selector) ? selector : [selector];
1050
+ const byPath = findFirstTargetByPath(root, paths);
1051
+ if (byPath) return byPath.value;
1052
+ if (typeof selector === 'string') {
1053
+ const byKey = findFirstTargetByKey(root, selector);
1054
+ if (byKey) return byKey.value;
1055
+ }
1056
+ return null;
1057
+ }
1058
+
1059
+ /**
1060
+ * Read array rows out of a tool result — by selector paths, else the first
1061
+ * declared list accessor. Companion to {@link readValue} for `extractList`.
1062
+ */
1063
+ export function readList(
1064
+ result: ToolExecuteResult,
1065
+ selector?: readonly string[] | string,
1066
+ ): Record<string, unknown>[] {
1067
+ if (selector) {
1068
+ const paths = Array.isArray(selector) ? selector : [selector];
1069
+ const found = findFirstTargetByPath(resultRootOf(result), paths)?.value;
1070
+ const rows = normalizeRows(found);
1071
+ if (rows) return rows;
1072
+ }
1073
+ return Object.values(result.extractedLists)[0]?.get() ?? [];
1074
+ }
1075
+
1032
1076
  function metadataInputFromToolExecuteResult(
1033
1077
  value: ToolExecuteResult,
1034
1078
  ): ToolResultMetadataInput {
@@ -5,8 +5,7 @@ const PRIVATE_KEY_PATTERN =
5
5
  const BEARER_LITERAL_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/i;
6
6
  const ASSIGNMENT_SECRET_LITERAL_PATTERN =
7
7
  /\b(?:api[_-]?key|token|secret|password)\b\s*[:=]\s*['"][^'"]{12,}['"]/i;
8
- const HIGH_ENTROPY_LITERAL_PATTERN =
9
- /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
8
+ const HIGH_ENTROPY_LITERAL_PATTERN = /['"]([A-Za-z0-9+/=_-]{32,})['"]/g;
10
9
 
11
10
  function shannonEntropy(value: string): number {
12
11
  const counts = new Map<string, number>();
@@ -17,26 +16,38 @@ function shannonEntropy(value: string): number {
17
16
  }, 0);
18
17
  }
19
18
 
20
- export function validatePlaySourceHasNoInlineSecrets(input: {
21
- sourceCode: string;
22
- filePath: string;
23
- }): void {
19
+ /**
20
+ * Returns the inline-secret findings in a string (empty if none). The throwing
21
+ * validator below and the workflows→plays migration validator both call this so
22
+ * the heuristics stay a single source of truth (no drift between "publish
23
+ * rejects it" and "transform skips it loudly").
24
+ */
25
+ export function collectInlineSecretFindings(sourceCode: string): string[] {
24
26
  const findings: string[] = [];
25
- for (const match of input.sourceCode.matchAll(SECRET_ENV_PATTERN)) {
27
+ for (const match of sourceCode.matchAll(SECRET_ENV_PATTERN)) {
26
28
  findings.push(`process.env.${match[1]}`);
27
29
  }
28
- if (PRIVATE_KEY_PATTERN.test(input.sourceCode)) findings.push('private key block');
29
- if (BEARER_LITERAL_PATTERN.test(input.sourceCode)) findings.push('bearer token literal');
30
- if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(input.sourceCode)) {
30
+ if (PRIVATE_KEY_PATTERN.test(sourceCode)) findings.push('private key block');
31
+ if (BEARER_LITERAL_PATTERN.test(sourceCode))
32
+ findings.push('bearer token literal');
33
+ if (ASSIGNMENT_SECRET_LITERAL_PATTERN.test(sourceCode)) {
31
34
  findings.push('secret-looking assignment literal');
32
35
  }
33
- for (const match of input.sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
36
+ for (const match of sourceCode.matchAll(HIGH_ENTROPY_LITERAL_PATTERN)) {
34
37
  const literal = match[1] ?? '';
35
38
  if (literal.length >= 40 && shannonEntropy(literal) >= 4.2) {
36
39
  findings.push('high-entropy string literal');
37
40
  break;
38
41
  }
39
42
  }
43
+ return [...new Set(findings)];
44
+ }
45
+
46
+ export function validatePlaySourceHasNoInlineSecrets(input: {
47
+ sourceCode: string;
48
+ filePath: string;
49
+ }): void {
50
+ const findings = collectInlineSecretFindings(input.sourceCode);
40
51
  if (!findings.length) return;
41
52
  throw new Error(
42
53
  [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.93",
3
+ "version": "0.1.95",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {