deepline 0.1.73 → 0.1.74

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/README.md CHANGED
@@ -242,12 +242,12 @@ deepline play run --file rate-limit-repro.play.ts --watch
242
242
 
243
243
  ### 4. Fallback variant (multi-step with rate limits)
244
244
 
245
- Use `steps()` and `runIf()` to express row-level fallback sequences. Skipped
246
- conditional steps yield `null`.
245
+ Use `steps()` with `{ runIf }` options to express row-level fallback sequences.
246
+ Skipped conditional steps yield `null`.
247
247
 
248
248
  ```bash
249
249
  cat > rate-limit-waterfall.play.ts << 'TS'
250
- import { definePlay, runIf, steps } from 'deepline';
250
+ import { definePlay, steps } from 'deepline';
251
251
 
252
252
  export default definePlay('rate-limit-waterfall', async (ctx) => {
253
253
  const leads = Array.from({ length: 24 }, (_, i) => ({
@@ -267,8 +267,8 @@ export default definePlay('rate-limit-waterfall', async (ctx) => {
267
267
  },
268
268
  description: 'Exercise primary rate-limit behavior.',
269
269
  }))
270
- .step("backup_probe", runIf(
271
- (row) => !row.primary_probe,
270
+ .step(
271
+ "backup_probe",
272
272
  (row, ctx) =>
273
273
  ctx.tools.execute({
274
274
  id: 'rate_limit_probe_backup',
@@ -280,7 +280,8 @@ export default definePlay('rate-limit-waterfall', async (ctx) => {
280
280
  },
281
281
  description: 'Exercise fallback rate-limit behavior.',
282
282
  }),
283
- ))
283
+ { runIf: (row) => !row.primary_probe },
284
+ )
284
285
  .return((row) => row.primary_probe ?? row.backup_probe ?? null);
285
286
 
286
287
  // Fan out over leads — each gets a row-level fallback sequence
package/dist/cli/index.js CHANGED
@@ -229,10 +229,10 @@ var import_node_path2 = require("path");
229
229
 
230
230
  // src/release.ts
231
231
  var SDK_RELEASE = {
232
- version: "0.1.73",
232
+ version: "0.1.74",
233
233
  apiContract: "2026-06-dataset-column-syntax-cutover",
234
234
  supportPolicy: {
235
- latest: "0.1.73",
235
+ latest: "0.1.74",
236
236
  minimumSupported: "0.1.53",
237
237
  deprecatedBelow: "0.1.53"
238
238
  }
@@ -6885,9 +6885,6 @@ function playInspectionComments(playRef, indent) {
6885
6885
  function accessorExpression(base, field) {
6886
6886
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(field) ? `${base}.${field}` : `${base}[${jsString(field)}]`;
6887
6887
  }
6888
- function needsRunIfImport(options) {
6889
- return stageProviders(options.email).length > 1 || stageProviders(options.phone).length > 1;
6890
- }
6891
6888
  function sourceCollectionTypeName(entity) {
6892
6889
  return entity === "company" ? "CompanySourceRow" : "ContactSourceRow";
6893
6890
  }
@@ -7238,10 +7235,10 @@ function generateFinderProviderStep(input2) {
7238
7235
  const priorCandidates = input2.stepNames.slice(0, input2.index).map((name) => `row.${name}`).join(", ");
7239
7236
  return `.withColumn(
7240
7237
  ${jsString(stepName)},
7241
- runIf(
7242
- (row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)}),
7243
- ${input2.resolver},
7244
- ),
7238
+ ${input2.resolver},
7239
+ {
7240
+ runIf: (row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)}),
7241
+ },
7245
7242
  )`;
7246
7243
  }
7247
7244
  }
@@ -7272,7 +7269,7 @@ function generateFinderProviderWaterfall(input2) {
7272
7269
  );
7273
7270
  const candidateNames = stepNames.map((name) => `row.${name}`).join(", ");
7274
7271
  return `// ${input2.aggregateStepName} provider waterfall. Each provider leg is active once its TODO throw is removed;
7275
- // delete or comment out legs you do not want before running. Later legs are gated with runIf(...).
7272
+ // delete or comment out legs you do not want before running. Later legs are gated with { runIf }.
7276
7273
  ${providerSteps.join("\n ")}
7277
7274
  .withColumn(${jsString(input2.aggregateStepName)}, (row) => {
7278
7275
  const candidates = [${candidateNames}];
@@ -7329,8 +7326,7 @@ function generateBootstrapPlaySource(input2) {
7329
7326
  rowTypeDefinitions,
7330
7327
  finderPlayResultTypes
7331
7328
  ].filter((definition) => definition.trim().length > 0).join("\n\n");
7332
- const importNames = needsRunIfImport(input2.options) ? "definePlay, runIf" : "definePlay";
7333
- return `import { ${importNames} } from 'deepline';
7329
+ return `import { definePlay } from 'deepline';
7334
7330
 
7335
7331
  ${typeDefinitions}
7336
7332
 
@@ -206,10 +206,10 @@ import { join as join2 } from "path";
206
206
 
207
207
  // src/release.ts
208
208
  var SDK_RELEASE = {
209
- version: "0.1.73",
209
+ version: "0.1.74",
210
210
  apiContract: "2026-06-dataset-column-syntax-cutover",
211
211
  supportPolicy: {
212
- latest: "0.1.73",
212
+ latest: "0.1.74",
213
213
  minimumSupported: "0.1.53",
214
214
  deprecatedBelow: "0.1.53"
215
215
  }
@@ -6888,9 +6888,6 @@ function playInspectionComments(playRef, indent) {
6888
6888
  function accessorExpression(base, field) {
6889
6889
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(field) ? `${base}.${field}` : `${base}[${jsString(field)}]`;
6890
6890
  }
6891
- function needsRunIfImport(options) {
6892
- return stageProviders(options.email).length > 1 || stageProviders(options.phone).length > 1;
6893
- }
6894
6891
  function sourceCollectionTypeName(entity) {
6895
6892
  return entity === "company" ? "CompanySourceRow" : "ContactSourceRow";
6896
6893
  }
@@ -7241,10 +7238,10 @@ function generateFinderProviderStep(input2) {
7241
7238
  const priorCandidates = input2.stepNames.slice(0, input2.index).map((name) => `row.${name}`).join(", ");
7242
7239
  return `.withColumn(
7243
7240
  ${jsString(stepName)},
7244
- runIf(
7245
- (row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)}),
7246
- ${input2.resolver},
7247
- ),
7241
+ ${input2.resolver},
7242
+ {
7243
+ runIf: (row) => ![${priorCandidates}].some((candidate) => ${optionalFinderValueExpression("candidate", input2.outputField)}),
7244
+ },
7248
7245
  )`;
7249
7246
  }
7250
7247
  }
@@ -7275,7 +7272,7 @@ function generateFinderProviderWaterfall(input2) {
7275
7272
  );
7276
7273
  const candidateNames = stepNames.map((name) => `row.${name}`).join(", ");
7277
7274
  return `// ${input2.aggregateStepName} provider waterfall. Each provider leg is active once its TODO throw is removed;
7278
- // delete or comment out legs you do not want before running. Later legs are gated with runIf(...).
7275
+ // delete or comment out legs you do not want before running. Later legs are gated with { runIf }.
7279
7276
  ${providerSteps.join("\n ")}
7280
7277
  .withColumn(${jsString(input2.aggregateStepName)}, (row) => {
7281
7278
  const candidates = [${candidateNames}];
@@ -7332,8 +7329,7 @@ function generateBootstrapPlaySource(input2) {
7332
7329
  rowTypeDefinitions,
7333
7330
  finderPlayResultTypes
7334
7331
  ].filter((definition) => definition.trim().length > 0).join("\n\n");
7335
- const importNames = needsRunIfImport(input2.options) ? "definePlay, runIf" : "definePlay";
7336
- return `import { ${importNames} } from 'deepline';
7332
+ return `import { definePlay } from 'deepline';
7337
7333
 
7338
7334
  ${typeDefinitions}
7339
7335
 
package/dist/index.d.mts CHANGED
@@ -2143,12 +2143,16 @@ type ConditionalStepResolver<Row, Value, Else = null> = {
2143
2143
  readonly elseValue: Else;
2144
2144
  else<ValueElse>(value: ValueElse): ConditionalStepResolver<Row, Value, ValueElse>;
2145
2145
  };
2146
+ type StepOptions<Row> = {
2147
+ readonly runIf?: (row: Row, index: number) => boolean | Promise<boolean>;
2148
+ };
2146
2149
  type StepProgram<Input, Output, Return = Output> = {
2147
2150
  readonly kind: 'steps';
2148
2151
  readonly steps: readonly PlayStepProgramStep[];
2149
2152
  readonly returnResolver?: StepResolver<Output, Return>;
2150
2153
  readonly __inputType?: (input: Input) => void;
2151
2154
  step<Name extends string, Value>(name: Name, resolver: StepResolver<Output, Value> | ConditionalStepResolver<Output, Value> | StepProgramResolver<Output, Value>): StepProgram<Input, Output & Record<Name, Value>, Return>;
2155
+ step<Name extends string, Value>(name: Name, resolver: StepResolver<Output, Value> | StepProgramResolver<Output, Value>, options: StepOptions<Output>): StepProgram<Input, Output & Record<Name, Value | null>, Return>;
2152
2156
  return<Value>(resolver: StepResolver<Output, Value>): StepProgram<Input, Output, Value>;
2153
2157
  };
2154
2158
  type StepProgramResolver<Input, Return> = {
@@ -2162,6 +2166,7 @@ type PlayStepProgramStep = {
2162
2166
  readonly resolver: StepResolver<Record<string, unknown>, unknown> | ConditionalStepResolver<Record<string, unknown>, unknown> | StepProgramResolver<Record<string, unknown>, unknown>;
2163
2167
  };
2164
2168
  type ColumnResolver<Row, Value> = StepResolver<Row, Value> | ConditionalStepResolver<Row, Value> | StepProgramResolver<Row, Value>;
2169
+ type StepProgramOutput<TProgram> = TProgram extends StepProgram<any, infer Output, any> ? Output : never;
2165
2170
  type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
2166
2171
  /**
2167
2172
  * Define one output column for every row in this dataset.
@@ -2177,6 +2182,8 @@ type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
2177
2182
  * @returns The same dataset builder with the new column type.
2178
2183
  */
2179
2184
  withColumn<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;
2185
+ withColumn<Name extends string, Value>(name: Name, resolver: StepResolver<OutputRow, Value> | StepProgramResolver<OutputRow, Value>, options: StepOptions<OutputRow>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;
2186
+ withColumns<Program extends StepProgram<OutputRow, object, unknown>>(program: Program): DatasetBuilder<InputRow, StepProgramOutput<Program>>;
2180
2187
  /** @deprecated Dataset `.step(...)` was replaced by `.withColumn(...)`. */
2181
2188
  step<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): never;
2182
2189
  /**
package/dist/index.d.ts CHANGED
@@ -2143,12 +2143,16 @@ type ConditionalStepResolver<Row, Value, Else = null> = {
2143
2143
  readonly elseValue: Else;
2144
2144
  else<ValueElse>(value: ValueElse): ConditionalStepResolver<Row, Value, ValueElse>;
2145
2145
  };
2146
+ type StepOptions<Row> = {
2147
+ readonly runIf?: (row: Row, index: number) => boolean | Promise<boolean>;
2148
+ };
2146
2149
  type StepProgram<Input, Output, Return = Output> = {
2147
2150
  readonly kind: 'steps';
2148
2151
  readonly steps: readonly PlayStepProgramStep[];
2149
2152
  readonly returnResolver?: StepResolver<Output, Return>;
2150
2153
  readonly __inputType?: (input: Input) => void;
2151
2154
  step<Name extends string, Value>(name: Name, resolver: StepResolver<Output, Value> | ConditionalStepResolver<Output, Value> | StepProgramResolver<Output, Value>): StepProgram<Input, Output & Record<Name, Value>, Return>;
2155
+ step<Name extends string, Value>(name: Name, resolver: StepResolver<Output, Value> | StepProgramResolver<Output, Value>, options: StepOptions<Output>): StepProgram<Input, Output & Record<Name, Value | null>, Return>;
2152
2156
  return<Value>(resolver: StepResolver<Output, Value>): StepProgram<Input, Output, Value>;
2153
2157
  };
2154
2158
  type StepProgramResolver<Input, Return> = {
@@ -2162,6 +2166,7 @@ type PlayStepProgramStep = {
2162
2166
  readonly resolver: StepResolver<Record<string, unknown>, unknown> | ConditionalStepResolver<Record<string, unknown>, unknown> | StepProgramResolver<Record<string, unknown>, unknown>;
2163
2167
  };
2164
2168
  type ColumnResolver<Row, Value> = StepResolver<Row, Value> | ConditionalStepResolver<Row, Value> | StepProgramResolver<Row, Value>;
2169
+ type StepProgramOutput<TProgram> = TProgram extends StepProgram<any, infer Output, any> ? Output : never;
2165
2170
  type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
2166
2171
  /**
2167
2172
  * Define one output column for every row in this dataset.
@@ -2177,6 +2182,8 @@ type DatasetBuilder<InputRow extends object, OutputRow extends object> = {
2177
2182
  * @returns The same dataset builder with the new column type.
2178
2183
  */
2179
2184
  withColumn<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;
2185
+ withColumn<Name extends string, Value>(name: Name, resolver: StepResolver<OutputRow, Value> | StepProgramResolver<OutputRow, Value>, options: StepOptions<OutputRow>): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;
2186
+ withColumns<Program extends StepProgram<OutputRow, object, unknown>>(program: Program): DatasetBuilder<InputRow, StepProgramOutput<Program>>;
2180
2187
  /** @deprecated Dataset `.step(...)` was replaced by `.withColumn(...)`. */
2181
2188
  step<Name extends string, Value>(name: Name, resolver: ColumnResolver<OutputRow, Value>): never;
2182
2189
  /**
package/dist/index.js CHANGED
@@ -241,10 +241,10 @@ var import_node_path2 = require("path");
241
241
 
242
242
  // src/release.ts
243
243
  var SDK_RELEASE = {
244
- version: "0.1.73",
244
+ version: "0.1.74",
245
245
  apiContract: "2026-06-dataset-column-syntax-cutover",
246
246
  supportPolicy: {
247
- latest: "0.1.73",
247
+ latest: "0.1.74",
248
248
  minimumSupported: "0.1.53",
249
249
  deprecatedBelow: "0.1.53"
250
250
  }
@@ -2410,18 +2410,23 @@ var DeeplineStepProgram = class _DeeplineStepProgram {
2410
2410
  steps;
2411
2411
  returnResolver;
2412
2412
  kind = "steps";
2413
- step(name, resolver) {
2413
+ step(name, resolver, options) {
2414
2414
  if (!name.trim()) {
2415
2415
  throw new Error(
2416
2416
  "steps().step(name, ...) requires a non-empty step name."
2417
2417
  );
2418
2418
  }
2419
+ const stepResolver = options?.runIf && !isConditionalStepResolver(resolver) ? new DeeplineConditionalStepResolver(
2420
+ options.runIf,
2421
+ resolver,
2422
+ null
2423
+ ) : resolver;
2419
2424
  return new _DeeplineStepProgram(
2420
2425
  [
2421
2426
  ...this.steps,
2422
2427
  {
2423
2428
  name,
2424
- resolver
2429
+ resolver: stepResolver
2425
2430
  }
2426
2431
  ],
2427
2432
  this.returnResolver
@@ -2431,6 +2436,9 @@ var DeeplineStepProgram = class _DeeplineStepProgram {
2431
2436
  return new _DeeplineStepProgram(this.steps, resolver);
2432
2437
  }
2433
2438
  };
2439
+ function isConditionalStepResolver(value) {
2440
+ return value !== null && typeof value === "object" && value.kind === "conditional";
2441
+ }
2434
2442
  function steps() {
2435
2443
  return new DeeplineStepProgram([]);
2436
2444
  }
package/dist/index.mjs CHANGED
@@ -179,10 +179,10 @@ import { join as join2 } from "path";
179
179
 
180
180
  // src/release.ts
181
181
  var SDK_RELEASE = {
182
- version: "0.1.73",
182
+ version: "0.1.74",
183
183
  apiContract: "2026-06-dataset-column-syntax-cutover",
184
184
  supportPolicy: {
185
- latest: "0.1.73",
185
+ latest: "0.1.74",
186
186
  minimumSupported: "0.1.53",
187
187
  deprecatedBelow: "0.1.53"
188
188
  }
@@ -2348,18 +2348,23 @@ var DeeplineStepProgram = class _DeeplineStepProgram {
2348
2348
  steps;
2349
2349
  returnResolver;
2350
2350
  kind = "steps";
2351
- step(name, resolver) {
2351
+ step(name, resolver, options) {
2352
2352
  if (!name.trim()) {
2353
2353
  throw new Error(
2354
2354
  "steps().step(name, ...) requires a non-empty step name."
2355
2355
  );
2356
2356
  }
2357
+ const stepResolver = options?.runIf && !isConditionalStepResolver(resolver) ? new DeeplineConditionalStepResolver(
2358
+ options.runIf,
2359
+ resolver,
2360
+ null
2361
+ ) : resolver;
2357
2362
  return new _DeeplineStepProgram(
2358
2363
  [
2359
2364
  ...this.steps,
2360
2365
  {
2361
2366
  name,
2362
- resolver
2367
+ resolver: stepResolver
2363
2368
  }
2364
2369
  ],
2365
2370
  this.returnResolver
@@ -2369,6 +2374,9 @@ var DeeplineStepProgram = class _DeeplineStepProgram {
2369
2374
  return new _DeeplineStepProgram(this.steps, resolver);
2370
2375
  }
2371
2376
  };
2377
+ function isConditionalStepResolver(value) {
2378
+ return value !== null && typeof value === "object" && value.kind === "conditional";
2379
+ }
2372
2380
  function steps() {
2373
2381
  return new DeeplineStepProgram([]);
2374
2382
  }
@@ -151,6 +151,10 @@ import {
151
151
  isHardBillingToolHttpError,
152
152
  normalizeToolHttpErrorMessage,
153
153
  } from './runtime/tool-http-errors';
154
+ import {
155
+ StepProgramDatasetBuilder,
156
+ type StepProgramDatasetOptions,
157
+ } from '../../../shared_libs/play-runtime/step-program-dataset-builder';
154
158
 
155
159
  // The play's default export. The bundler injects this — see bundle-play-file.ts.
156
160
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -4017,37 +4021,55 @@ function createMinimalWorkerCtx(
4017
4021
  };
4018
4022
 
4019
4023
  class WorkerDatasetBuilder<T extends Record<string, unknown>> {
4020
- private readonly program: WorkerStepProgram = {
4021
- kind: 'steps',
4022
- steps: [],
4023
- };
4024
+ private readonly builder: StepProgramDatasetBuilder<
4025
+ WorkerStepProgramStep,
4026
+ WorkerStepProgramStep['resolver'],
4027
+ WorkerMapOptions,
4028
+ Promise<unknown>
4029
+ >;
4024
4030
 
4025
4031
  constructor(
4026
4032
  private readonly name: string,
4027
4033
  private readonly rows: WorkerDatasetInput<T>,
4028
- ) {}
4034
+ ) {
4035
+ this.builder = new StepProgramDatasetBuilder(
4036
+ (program, opts) => {
4037
+ const fields = Object.fromEntries(
4038
+ program.steps.map((step) => [step.name, step.resolver]),
4039
+ );
4040
+ return runMap(this.name, this.rows, fields, opts);
4041
+ },
4042
+ {
4043
+ emptyColumnName:
4044
+ 'ctx.dataset(...).withColumn(name, ...) requires a name.',
4045
+ invalidColumnsProgram:
4046
+ 'ctx.dataset(...).withColumns(...) requires a steps() program.',
4047
+ legacyStep:
4048
+ 'ctx.dataset(...).step(...) has been replaced by ctx.dataset(...).withColumn(...).',
4049
+ },
4050
+ );
4051
+ }
4029
4052
 
4030
- withColumn(name: string, resolver: WorkerStepProgramStep['resolver']): this {
4031
- if (!name.trim()) {
4032
- throw new Error(
4033
- 'ctx.dataset(...).withColumn(name, ...) requires a name.',
4034
- );
4035
- }
4036
- this.program.steps = [...this.program.steps, { name, resolver }];
4053
+ withColumn(
4054
+ name: string,
4055
+ resolver: WorkerStepProgramStep['resolver'],
4056
+ options?: StepProgramDatasetOptions,
4057
+ ): this {
4058
+ this.builder.withColumn(name, resolver, options);
4059
+ return this;
4060
+ }
4061
+
4062
+ withColumns(program: WorkerStepProgram): this {
4063
+ this.builder.withColumns(program);
4037
4064
  return this;
4038
4065
  }
4039
4066
 
4040
4067
  step(): never {
4041
- throw new Error(
4042
- 'ctx.dataset(...).step(...) has been replaced by ctx.dataset(...).withColumn(...).',
4043
- );
4068
+ return this.builder.step();
4044
4069
  }
4045
4070
 
4046
4071
  run(opts?: WorkerMapOptions): Promise<unknown> {
4047
- const fields = Object.fromEntries(
4048
- this.program.steps.map((step) => [step.name, step.resolver]),
4049
- );
4050
- return runMap(this.name, this.rows, fields, opts);
4072
+ return this.builder.run(opts);
4051
4073
  }
4052
4074
  }
4053
4075
 
@@ -222,6 +222,10 @@ export type ConditionalStepResolver<Row, Value, Else = null> = {
222
222
  ): ConditionalStepResolver<Row, Value, ValueElse>;
223
223
  };
224
224
 
225
+ export type StepOptions<Row> = {
226
+ readonly runIf?: (row: Row, index: number) => boolean | Promise<boolean>;
227
+ };
228
+
225
229
  export type StepProgram<Input, Output, Return = Output> = {
226
230
  readonly kind: 'steps';
227
231
  readonly steps: readonly PlayStepProgramStep[];
@@ -234,6 +238,11 @@ export type StepProgram<Input, Output, Return = Output> = {
234
238
  | ConditionalStepResolver<Output, Value>
235
239
  | StepProgramResolver<Output, Value>,
236
240
  ): StepProgram<Input, Output & Record<Name, Value>, Return>;
241
+ step<Name extends string, Value>(
242
+ name: Name,
243
+ resolver: StepResolver<Output, Value> | StepProgramResolver<Output, Value>,
244
+ options: StepOptions<Output>,
245
+ ): StepProgram<Input, Output & Record<Name, Value | null>, Return>;
237
246
  return<Value>(
238
247
  resolver: StepResolver<Output, Value>,
239
248
  ): StepProgram<Input, Output, Value>;
@@ -259,6 +268,9 @@ export type ColumnResolver<Row, Value> =
259
268
  | ConditionalStepResolver<Row, Value>
260
269
  | StepProgramResolver<Row, Value>;
261
270
 
271
+ export type StepProgramOutput<TProgram> =
272
+ TProgram extends StepProgram<any, infer Output, any> ? Output : never;
273
+
262
274
  export type DatasetBuilder<
263
275
  InputRow extends object,
264
276
  OutputRow extends object,
@@ -280,6 +292,16 @@ export type DatasetBuilder<
280
292
  name: Name,
281
293
  resolver: ColumnResolver<OutputRow, Value>,
282
294
  ): DatasetBuilder<InputRow, OutputRow & Record<Name, Value>>;
295
+ withColumn<Name extends string, Value>(
296
+ name: Name,
297
+ resolver:
298
+ | StepResolver<OutputRow, Value>
299
+ | StepProgramResolver<OutputRow, Value>,
300
+ options: StepOptions<OutputRow>,
301
+ ): DatasetBuilder<InputRow, OutputRow & Record<Name, Value | null>>;
302
+ withColumns<Program extends StepProgram<OutputRow, object, unknown>>(
303
+ program: Program,
304
+ ): DatasetBuilder<InputRow, StepProgramOutput<Program>>;
283
305
  /** @deprecated Dataset `.step(...)` was replaced by `.withColumn(...)`. */
284
306
  step<Name extends string, Value>(
285
307
  name: Name,
@@ -307,7 +329,6 @@ export type DatasetBuilder<
307
329
  }): Promise<PlayDataset<OutputRow>>;
308
330
  };
309
331
 
310
-
311
332
  export type CsvRenameMap = Record<string, string | readonly string[]>;
312
333
 
313
334
  /**
@@ -478,10 +499,7 @@ export interface DeeplinePlayRuntimeContext {
478
499
  /**
479
500
  * @deprecated `ctx.map(...)` was replaced by `ctx.dataset(...)`.
480
501
  */
481
- map<TItem extends object>(
482
- key: string,
483
- items: PlayDatasetInput<TItem>,
484
- ): never;
502
+ map<TItem extends object>(key: string, items: PlayDatasetInput<TItem>): never;
485
503
 
486
504
  /** Tool execution namespace. */
487
505
  tools: {
@@ -863,22 +881,43 @@ class DeeplineStepProgram<Input, Output, ReturnValue> implements StepProgram<
863
881
  | StepResolver<Output, Value>
864
882
  | ConditionalStepResolver<Output, Value>
865
883
  | StepProgramResolver<Output, Value>,
866
- ): StepProgram<Input, Output & Record<Name, Value>, ReturnValue> {
884
+ ): StepProgram<Input, Output & Record<Name, Value>, ReturnValue>;
885
+ step<Name extends string, Value>(
886
+ name: Name,
887
+ resolver: StepResolver<Output, Value> | StepProgramResolver<Output, Value>,
888
+ options: StepOptions<Output>,
889
+ ): StepProgram<Input, Output & Record<Name, Value | null>, ReturnValue>;
890
+ step<Name extends string, Value>(
891
+ name: Name,
892
+ resolver:
893
+ | StepResolver<Output, Value>
894
+ | ConditionalStepResolver<Output, Value>
895
+ | StepProgramResolver<Output, Value>,
896
+ options?: StepOptions<Output>,
897
+ ): StepProgram<Input, Output & Record<Name, Value | null>, ReturnValue> {
867
898
  if (!name.trim()) {
868
899
  throw new Error(
869
900
  'steps().step(name, ...) requires a non-empty step name.',
870
901
  );
871
902
  }
903
+ const stepResolver =
904
+ options?.runIf && !isConditionalStepResolver(resolver)
905
+ ? new DeeplineConditionalStepResolver(
906
+ options.runIf,
907
+ resolver as StepResolver<Output, Value>,
908
+ null,
909
+ )
910
+ : resolver;
872
911
  return new DeeplineStepProgram(
873
912
  [
874
913
  ...this.steps,
875
914
  {
876
915
  name,
877
- resolver: resolver as PlayStepProgramStep['resolver'],
916
+ resolver: stepResolver as PlayStepProgramStep['resolver'],
878
917
  },
879
918
  ],
880
919
  this.returnResolver as StepResolver<
881
- Output & Record<Name, Value>,
920
+ Output & Record<Name, Value | null>,
882
921
  ReturnValue
883
922
  >,
884
923
  );
@@ -891,6 +930,16 @@ class DeeplineStepProgram<Input, Output, ReturnValue> implements StepProgram<
891
930
  }
892
931
  }
893
932
 
933
+ function isConditionalStepResolver(
934
+ value: unknown,
935
+ ): value is ConditionalStepResolver<unknown, unknown> {
936
+ return (
937
+ value !== null &&
938
+ typeof value === 'object' &&
939
+ (value as { kind?: unknown }).kind === 'conditional'
940
+ );
941
+ }
942
+
894
943
  export function steps<TInput>(): StepProgram<TInput, TInput, TInput> {
895
944
  return new DeeplineStepProgram<TInput, TInput, TInput>([]);
896
945
  }
@@ -50,10 +50,10 @@ export type SdkRelease = {
50
50
  };
51
51
 
52
52
  export const SDK_RELEASE = {
53
- version: '0.1.73',
53
+ version: '0.1.74',
54
54
  apiContract: '2026-06-dataset-column-syntax-cutover',
55
55
  supportPolicy: {
56
- latest: '0.1.73',
56
+ latest: '0.1.74',
57
57
  minimumSupported: '0.1.53',
58
58
  deprecatedBelow: '0.1.53',
59
59
  },
@@ -19,6 +19,7 @@ import type {
19
19
  PlayStepProgramStep,
20
20
  StepProgram,
21
21
  StepProgramResolver,
22
+ StepOptions,
22
23
  StepResolver,
23
24
  } from './play.js';
24
25
 
@@ -43,6 +44,7 @@ export type {
43
44
  PrebuiltPlayRef,
44
45
  StepProgram,
45
46
  StepProgramResolver,
47
+ StepOptions,
46
48
  StepResolver,
47
49
  ToolExecuteResult,
48
50
  } from './play.js';
@@ -95,20 +97,41 @@ class WorkerStepProgram<Input, Output, ReturnValue> implements StepProgram<
95
97
  | StepResolver<Output, Value>
96
98
  | ConditionalStepResolver<Output, Value>
97
99
  | StepProgramResolver<Output, Value>,
98
- ): StepProgram<Input, Output & Record<Name, Value>, ReturnValue> {
100
+ ): StepProgram<Input, Output & Record<Name, Value>, ReturnValue>;
101
+ step<Name extends string, Value>(
102
+ name: Name,
103
+ resolver: StepResolver<Output, Value> | StepProgramResolver<Output, Value>,
104
+ options: StepOptions<Output>,
105
+ ): StepProgram<Input, Output & Record<Name, Value | null>, ReturnValue>;
106
+ step<Name extends string, Value>(
107
+ name: Name,
108
+ resolver:
109
+ | StepResolver<Output, Value>
110
+ | ConditionalStepResolver<Output, Value>
111
+ | StepProgramResolver<Output, Value>,
112
+ options?: StepOptions<Output>,
113
+ ): StepProgram<Input, Output & Record<Name, Value | null>, ReturnValue> {
99
114
  if (!name.trim()) {
100
115
  throw new Error('Step name required.');
101
116
  }
117
+ const stepResolver =
118
+ options?.runIf && !isConditionalStepResolver(resolver)
119
+ ? new WorkerConditionalStepResolver(
120
+ options.runIf,
121
+ resolver as StepResolver<Output, Value>,
122
+ null,
123
+ )
124
+ : resolver;
102
125
  return new WorkerStepProgram(
103
126
  [
104
127
  ...this.steps,
105
128
  {
106
129
  name,
107
- resolver: resolver as PlayStepProgramStep['resolver'],
130
+ resolver: stepResolver as PlayStepProgramStep['resolver'],
108
131
  },
109
132
  ],
110
133
  this.returnResolver as StepResolver<
111
- Output & Record<Name, Value>,
134
+ Output & Record<Name, Value | null>,
112
135
  ReturnValue
113
136
  >,
114
137
  );
@@ -121,6 +144,16 @@ class WorkerStepProgram<Input, Output, ReturnValue> implements StepProgram<
121
144
  }
122
145
  }
123
146
 
147
+ function isConditionalStepResolver(
148
+ value: unknown,
149
+ ): value is ConditionalStepResolver<unknown, unknown> {
150
+ return (
151
+ value !== null &&
152
+ typeof value === 'object' &&
153
+ (value as { kind?: unknown }).kind === 'conditional'
154
+ );
155
+ }
156
+
124
157
  export function steps<TInput>(): StepProgram<TInput, TInput, TInput> {
125
158
  return new WorkerStepProgram<TInput, TInput, TInput>([]);
126
159
  }
@@ -0,0 +1,125 @@
1
+ export type StepProgramDatasetOptions = {
2
+ runIf?: (
3
+ row: Record<string, unknown>,
4
+ index: number,
5
+ ) => boolean | Promise<boolean>;
6
+ };
7
+
8
+ export type StepProgramDatasetStep<TResolver> = {
9
+ name: string;
10
+ resolver: TResolver;
11
+ };
12
+
13
+ export type StepProgramDatasetProgram<TStep> = {
14
+ kind: 'steps';
15
+ steps: readonly TStep[];
16
+ };
17
+
18
+ export type StepProgramDatasetConditionalResolver<TResolver> = {
19
+ kind: 'conditional';
20
+ when: (
21
+ row: Record<string, unknown>,
22
+ index: number,
23
+ ) => boolean | Promise<boolean>;
24
+ run: TResolver;
25
+ elseValue?: unknown;
26
+ };
27
+
28
+ export function isStepProgramDatasetProgram<TStep>(
29
+ value: unknown,
30
+ ): value is StepProgramDatasetProgram<TStep> {
31
+ return (
32
+ value !== null &&
33
+ typeof value === 'object' &&
34
+ (value as { kind?: unknown }).kind === 'steps' &&
35
+ Array.isArray((value as { steps?: unknown }).steps)
36
+ );
37
+ }
38
+
39
+ export function isStepProgramDatasetConditionalResolver<TResolver>(
40
+ value: unknown,
41
+ ): value is StepProgramDatasetConditionalResolver<TResolver> {
42
+ return (
43
+ value !== null &&
44
+ typeof value === 'object' &&
45
+ (value as { kind?: unknown }).kind === 'conditional' &&
46
+ typeof (value as { when?: unknown }).when === 'function' &&
47
+ typeof (value as { run?: unknown }).run === 'function'
48
+ );
49
+ }
50
+
51
+ export class StepProgramDatasetBuilder<
52
+ TStep extends StepProgramDatasetStep<TResolver>,
53
+ TResolver,
54
+ TRunOptions,
55
+ TResult,
56
+ > {
57
+ private readonly program: StepProgramDatasetProgram<TStep> = {
58
+ kind: 'steps',
59
+ steps: [],
60
+ };
61
+
62
+ constructor(
63
+ private readonly runProgram: (
64
+ program: StepProgramDatasetProgram<TStep>,
65
+ options?: TRunOptions,
66
+ ) => TResult,
67
+ private readonly messages: {
68
+ emptyColumnName: string;
69
+ invalidColumnsProgram: string;
70
+ legacyStep: string;
71
+ },
72
+ ) {}
73
+
74
+ withColumn(
75
+ name: string,
76
+ resolver: TResolver,
77
+ options?: StepProgramDatasetOptions,
78
+ ): this {
79
+ if (!name.trim()) {
80
+ throw new Error(this.messages.emptyColumnName);
81
+ }
82
+ this.program.steps = [
83
+ ...this.program.steps,
84
+ {
85
+ name,
86
+ resolver: this.applyDerivationOptions(resolver, options),
87
+ } as TStep,
88
+ ];
89
+ return this;
90
+ }
91
+
92
+ withColumns(program: StepProgramDatasetProgram<TStep>): this {
93
+ if (!isStepProgramDatasetProgram<TStep>(program)) {
94
+ throw new Error(this.messages.invalidColumnsProgram);
95
+ }
96
+ this.program.steps = [...this.program.steps, ...program.steps];
97
+ return this;
98
+ }
99
+
100
+ step(): never {
101
+ throw new Error(this.messages.legacyStep);
102
+ }
103
+
104
+ run(options?: TRunOptions): TResult {
105
+ return this.runProgram(this.program, options);
106
+ }
107
+
108
+ private applyDerivationOptions(
109
+ resolver: TResolver,
110
+ options?: StepProgramDatasetOptions,
111
+ ): TResolver {
112
+ if (
113
+ !options?.runIf ||
114
+ isStepProgramDatasetConditionalResolver<TResolver>(resolver)
115
+ ) {
116
+ return resolver;
117
+ }
118
+ return {
119
+ kind: 'conditional',
120
+ when: options.runIf,
121
+ run: resolver,
122
+ elseValue: null,
123
+ } as TResolver;
124
+ }
125
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.73",
3
+ "version": "0.1.74",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {