@boboddy/sdk 0.3.1 → 0.4.2
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/client.js +93 -23
- package/dist/definitions/advancement-policies/cohort-advancement-policy.d.ts +117 -0
- package/dist/definitions/advancement-policies/cohort-fluent-rules.d.ts +126 -0
- package/dist/definitions/advancement-policies/index.d.ts +2 -0
- package/dist/definitions/advancement-policies/index.js +242 -0
- package/dist/definitions/pipelines/builder-helpers.d.ts +39 -8
- package/dist/definitions/pipelines/builder.d.ts +50 -44
- package/dist/definitions/pipelines/chain-graph.d.ts +19 -0
- package/dist/definitions/pipelines/define-default-pipeline-assignment.d.ts +7 -7
- package/dist/definitions/pipelines/define-pipeline.d.ts +106 -13
- package/dist/definitions/pipelines/fan-out-builder.d.ts +66 -0
- package/dist/definitions/pipelines/index.js +566 -125
- package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +6 -6
- package/dist/definitions/steps/define-step.d.ts +7 -3
- package/dist/definitions/steps/index.js +92 -23
- package/dist/definitions/validation/index.js +91 -22
- package/dist/generated/index.d.ts +2 -2
- package/dist/generated/sdk.gen.d.ts +40 -24
- package/dist/generated/types.gen.d.ts +1793 -542
- package/dist/index.js +567 -125
- package/dist/push/index.js +603 -153
- package/package.json +1 -1
|
@@ -242,10 +242,252 @@ function makeAdvanceCtx() {
|
|
|
242
242
|
route: (pipelineKey, inputJson) => inputJson !== undefined ? { outcome: "route", pipelineKey, inputJson } : { outcome: "route", pipelineKey }
|
|
243
243
|
};
|
|
244
244
|
}
|
|
245
|
+
// src/definitions/advancement-policies/cohort-advancement-policy.ts
|
|
246
|
+
var cohortAdvancementEventTypeValues = ["continue", "block"];
|
|
247
|
+
function serializeCohortCondition(condition) {
|
|
248
|
+
if (condition._tag === "signal") {
|
|
249
|
+
return {
|
|
250
|
+
fact: typeof condition.signal === "string" ? condition.signal : condition.signal.key,
|
|
251
|
+
operator: condition.operator,
|
|
252
|
+
value: condition.value
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (condition._tag === "all") {
|
|
256
|
+
return { all: condition.conditions.map(serializeCohortCondition) };
|
|
257
|
+
}
|
|
258
|
+
return { any: condition.conditions.map(serializeCohortCondition) };
|
|
259
|
+
}
|
|
260
|
+
function serializeCohortRule(rule) {
|
|
261
|
+
return {
|
|
262
|
+
conditions: { [rule.mode]: rule.conditions.map(serializeCohortCondition) },
|
|
263
|
+
event: {
|
|
264
|
+
type: rule.outcome,
|
|
265
|
+
...rule.outcomeJson ? { params: rule.outcomeJson } : {}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function serializeCohortAdvancementPolicy(policy) {
|
|
270
|
+
if (!policy) {
|
|
271
|
+
return { rules: [], defaultEventType: "continue", defaultEventParamsJson: null };
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
rules: (policy.rules ?? []).map(serializeCohortRule),
|
|
275
|
+
defaultEventType: policy.default,
|
|
276
|
+
defaultEventParamsJson: policy.defaultParamsJson ?? null
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function visitCohortSignalConditions(conditions, visit) {
|
|
280
|
+
for (const c of conditions) {
|
|
281
|
+
if (c._tag === "signal") {
|
|
282
|
+
visit(c);
|
|
283
|
+
} else {
|
|
284
|
+
visitCohortSignalConditions(c.conditions, visit);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function isSameStepSignalsListDefinition(a, b) {
|
|
289
|
+
return JSON.stringify(a.ops) === JSON.stringify(b.ops) && JSON.stringify(a.reducer) === JSON.stringify(b.reducer);
|
|
290
|
+
}
|
|
291
|
+
function extractInlineStepSignalsListDefinitions(policy) {
|
|
292
|
+
if (!policy?.rules)
|
|
293
|
+
return [];
|
|
294
|
+
const byKey = new Map;
|
|
295
|
+
for (const rule of policy.rules) {
|
|
296
|
+
visitCohortSignalConditions(rule.conditions, (cond) => {
|
|
297
|
+
if (typeof cond.signal === "string")
|
|
298
|
+
return;
|
|
299
|
+
const inline = cond.signal;
|
|
300
|
+
const def = {
|
|
301
|
+
key: inline.key,
|
|
302
|
+
ops: inline.ops,
|
|
303
|
+
reducer: inline.reducer
|
|
304
|
+
};
|
|
305
|
+
const existing = byKey.get(def.key);
|
|
306
|
+
if (existing) {
|
|
307
|
+
if (!isSameStepSignalsListDefinition(existing, def)) {
|
|
308
|
+
throw new Error(`Conflicting inline stepSignalsList definitions for key "${def.key}"`);
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
byKey.set(def.key, def);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return [...byKey.values()];
|
|
316
|
+
}
|
|
317
|
+
// src/definitions/advancement-policies/cohort-fluent-rules.ts
|
|
318
|
+
var LEAF_BRAND2 = Symbol("boboddy.cohortRule.leaf");
|
|
319
|
+
var GROUP_BRAND2 = Symbol("boboddy.cohortRule.group");
|
|
320
|
+
function createCohortSignalRef(signal2) {
|
|
321
|
+
const leaf = (operator, value) => {
|
|
322
|
+
const condition = {
|
|
323
|
+
_tag: "signal",
|
|
324
|
+
signal: signal2,
|
|
325
|
+
operator,
|
|
326
|
+
value
|
|
327
|
+
};
|
|
328
|
+
return {
|
|
329
|
+
[LEAF_BRAND2]: condition,
|
|
330
|
+
then(outcome, paramsJson) {
|
|
331
|
+
return {
|
|
332
|
+
_tag: "rule",
|
|
333
|
+
mode: "all",
|
|
334
|
+
conditions: [condition],
|
|
335
|
+
outcome,
|
|
336
|
+
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
return {
|
|
342
|
+
eq: (v) => leaf("equal", v),
|
|
343
|
+
ne: (v) => leaf("notEqual", v),
|
|
344
|
+
gt: (v) => leaf("greaterThan", v),
|
|
345
|
+
gte: (v) => leaf("greaterThanInclusive", v),
|
|
346
|
+
lt: (v) => leaf("lessThan", v),
|
|
347
|
+
lte: (v) => leaf("lessThanInclusive", v),
|
|
348
|
+
in: (vs) => leaf("in", vs),
|
|
349
|
+
notIn: (vs) => leaf("notIn", vs),
|
|
350
|
+
contains: (v) => leaf("contains", v),
|
|
351
|
+
doesNotContain: (v) => leaf("doesNotContain", v)
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function createCohortLeafFromCondition(condition) {
|
|
355
|
+
return {
|
|
356
|
+
[LEAF_BRAND2]: condition,
|
|
357
|
+
then(outcome, paramsJson) {
|
|
358
|
+
return {
|
|
359
|
+
_tag: "rule",
|
|
360
|
+
mode: "all",
|
|
361
|
+
conditions: [condition],
|
|
362
|
+
outcome,
|
|
363
|
+
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function extractCohortCondition(ref) {
|
|
369
|
+
if (LEAF_BRAND2 in ref)
|
|
370
|
+
return ref[LEAF_BRAND2];
|
|
371
|
+
const group = ref[GROUP_BRAND2];
|
|
372
|
+
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
373
|
+
}
|
|
374
|
+
function createCohortGroup(mode, refs) {
|
|
375
|
+
const conditions = refs.map(extractCohortCondition);
|
|
376
|
+
return {
|
|
377
|
+
[GROUP_BRAND2]: { mode, conditions },
|
|
378
|
+
then(outcome, paramsJson) {
|
|
379
|
+
return {
|
|
380
|
+
_tag: "rule",
|
|
381
|
+
mode,
|
|
382
|
+
conditions,
|
|
383
|
+
outcome,
|
|
384
|
+
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
function makeKeyedCohortSignalRef(key) {
|
|
390
|
+
return createCohortSignalRef(key);
|
|
391
|
+
}
|
|
392
|
+
function makeAdvanceEachCtx() {
|
|
393
|
+
return {
|
|
394
|
+
signal: (key) => makeKeyedCohortSignalRef(key),
|
|
395
|
+
stepSignals: new Proxy({}, {
|
|
396
|
+
get(_, key) {
|
|
397
|
+
if (typeof key === "string")
|
|
398
|
+
return makeKeyedCohortSignalRef(key);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
}),
|
|
402
|
+
all: (...refs) => createCohortGroup("all", refs),
|
|
403
|
+
any: (...refs) => createCohortGroup("any", refs)
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
var branchOutcomeValues = [
|
|
407
|
+
"continue",
|
|
408
|
+
"block",
|
|
409
|
+
"error",
|
|
410
|
+
"abandoned"
|
|
411
|
+
];
|
|
412
|
+
function summarizeTransformOp(op) {
|
|
413
|
+
if (op.op === "filter") {
|
|
414
|
+
return `filter_${op.operator}_${JSON.stringify(op.value)}`;
|
|
415
|
+
}
|
|
416
|
+
if (op.op === "sortBy") {
|
|
417
|
+
return `sortBy_${op.direction}`;
|
|
418
|
+
}
|
|
419
|
+
return "unique";
|
|
420
|
+
}
|
|
421
|
+
function deriveStepSignalsListKey(ops, reducer) {
|
|
422
|
+
const pluck = ops.find((op) => op.op === "pluck");
|
|
423
|
+
const base = `${reducer.op}_${pluck?.signalKey ?? "value"}`;
|
|
424
|
+
const extras = ops.filter((op) => op.op !== "pluck").map(summarizeTransformOp);
|
|
425
|
+
const reducerExtra = reducer.op === "join" ? `sep_${reducer.separator}` : null;
|
|
426
|
+
const suffixParts = [...extras, ...reducerExtra ? [reducerExtra] : []];
|
|
427
|
+
return suffixParts.length > 0 ? `${base}_${suffixParts.join("_")}` : base;
|
|
428
|
+
}
|
|
429
|
+
function createStepSignalsListBuilder(ops) {
|
|
430
|
+
const withOp = (op) => createStepSignalsListBuilder([...ops, op]);
|
|
431
|
+
const reduce = (reducer) => {
|
|
432
|
+
const token = {
|
|
433
|
+
_tag: "step_signals_list",
|
|
434
|
+
key: deriveStepSignalsListKey(ops, reducer),
|
|
435
|
+
ops: [...ops],
|
|
436
|
+
reducer
|
|
437
|
+
};
|
|
438
|
+
return createCohortSignalRef(token);
|
|
439
|
+
};
|
|
440
|
+
return {
|
|
441
|
+
filter: (operator, value) => withOp({ op: "filter", operator, value }),
|
|
442
|
+
sortBy: (direction = "asc") => withOp({ op: "sortBy", direction }),
|
|
443
|
+
unique: () => withOp({ op: "unique" }),
|
|
444
|
+
count: () => reduce({ op: "count" }),
|
|
445
|
+
sum: () => reduce({ op: "sum" }),
|
|
446
|
+
avg: () => reduce({ op: "avg" }),
|
|
447
|
+
min: () => reduce({ op: "min" }),
|
|
448
|
+
max: () => reduce({ op: "max" }),
|
|
449
|
+
booleanAll: () => reduce({ op: "booleanAll" }),
|
|
450
|
+
booleanAny: () => reduce({ op: "booleanAny" }),
|
|
451
|
+
join: (separator = ",") => reduce({ op: "join", separator }),
|
|
452
|
+
first: () => reduce({ op: "first" }),
|
|
453
|
+
last: () => reduce({ op: "last" })
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
function makeAdvanceAllCtx() {
|
|
457
|
+
return {
|
|
458
|
+
branchOutcomes: {
|
|
459
|
+
total: () => createCohortSignalRef("branchCount"),
|
|
460
|
+
count: (outcome) => createCohortSignalRef(`${outcome}Count`),
|
|
461
|
+
every: (outcome) => createCohortLeafFromCondition({
|
|
462
|
+
_tag: "signal",
|
|
463
|
+
signal: `${outcome}Count`,
|
|
464
|
+
operator: "equal",
|
|
465
|
+
value: { fact: "branchCount" }
|
|
466
|
+
}),
|
|
467
|
+
some: (outcome) => createCohortLeafFromCondition({
|
|
468
|
+
_tag: "signal",
|
|
469
|
+
signal: `${outcome}Count`,
|
|
470
|
+
operator: "greaterThan",
|
|
471
|
+
value: 0
|
|
472
|
+
})
|
|
473
|
+
},
|
|
474
|
+
stepSignalsList: {
|
|
475
|
+
pluck: (signalKey) => createStepSignalsListBuilder([{ op: "pluck", signalKey }])
|
|
476
|
+
},
|
|
477
|
+
all: (...refs) => createCohortGroup("all", refs),
|
|
478
|
+
any: (...refs) => createCohortGroup("any", refs)
|
|
479
|
+
};
|
|
480
|
+
}
|
|
245
481
|
export {
|
|
482
|
+
serializeCohortAdvancementPolicy,
|
|
246
483
|
serializeAdvancementPolicy,
|
|
484
|
+
makeAdvanceEachCtx,
|
|
247
485
|
makeAdvanceCtx,
|
|
486
|
+
makeAdvanceAllCtx,
|
|
487
|
+
extractInlineStepSignalsListDefinitions,
|
|
248
488
|
extractInlineComputedSignals,
|
|
489
|
+
cohortAdvancementEventTypeValues,
|
|
490
|
+
branchOutcomeValues,
|
|
249
491
|
Rule,
|
|
250
492
|
Computed
|
|
251
493
|
};
|
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
import { z, type ZodType } from "zod";
|
|
2
2
|
import { type AdditionalStepInputBinding, type TypedStepDefinitionSpec } from "../steps/define-step";
|
|
3
|
-
import { type AnyBinding, type LiteralBinding, type StepOutputBinding, type StepSignalBinding, type WorkItemBinding } from "./define-pipeline";
|
|
3
|
+
import { type AnyBinding, type FanOutItemBinding, type LiteralBinding, type SignalsListBinding, type StepOutputBinding, type StepSignalBinding, type WorkItemBinding } from "./define-pipeline";
|
|
4
4
|
import { type InputAccessor } from "./input-accessor";
|
|
5
5
|
export type AnyTypedStep = TypedStepDefinitionSpec<any, any, any, any>;
|
|
6
|
-
export type StepConfig = {
|
|
7
|
-
timeout?: number | null;
|
|
8
|
-
};
|
|
9
6
|
type ElementOf<T extends ReadonlyArray<unknown>> = T extends ReadonlyArray<infer U> ? U : never;
|
|
7
|
+
export type LastStep<T extends ReadonlyArray<AnyTypedStep>> = T extends readonly [...AnyTypedStep[], infer L] ? L extends AnyTypedStep ? L : never : never;
|
|
8
|
+
export type LastSignalKeys<T extends ReadonlyArray<AnyTypedStep>> = LastStep<T> extends AnyTypedStep ? LastStep<T>["__signalKeys"] : never;
|
|
9
|
+
/**
|
|
10
|
+
* The per-branch `item` type a fan-out's `over` key resolves to (issue
|
|
11
|
+
* #167): `never` unless `K` names a signal on the most recent ordinary step
|
|
12
|
+
* (`LastStep<TSteps>`) whose resolved TS type is itself an array — in which
|
|
13
|
+
* case this is that array's element type. A number-typed signal (count-only
|
|
14
|
+
* mode) resolves to `never`, which is how `FanOutInputCtx` below decides
|
|
15
|
+
* whether `item` exists on the ctx type at all.
|
|
16
|
+
*/
|
|
17
|
+
export type FanOutItemType<TSteps extends ReadonlyArray<AnyTypedStep>, K extends string> = K extends keyof LastStep<TSteps>["__signalTypeMap"] ? LastStep<TSteps>["__signalTypeMap"][K] extends ReadonlyArray<infer Item> ? Item : never : never;
|
|
18
|
+
/**
|
|
19
|
+
* A fan-out's own `input` mapper ctx: everything `StepInputCtx` already
|
|
20
|
+
* offers, plus `item` when `FanOutItemType` resolves to something other
|
|
21
|
+
* than `never`. `item`'s exposed type is intersected with `FanOutItemBinding`
|
|
22
|
+
* (the same "phantom binding" trick `WithWorkItemFields`/`InputAccessor`
|
|
23
|
+
* use elsewhere in this file) so it reads as the real per-item TS type
|
|
24
|
+
* (e.g. `string`) to callers while still structurally satisfying `AnyBinding`
|
|
25
|
+
* when assigned straight into a `FanOutInputMapping` field — at runtime it
|
|
26
|
+
* is always the single `{ source: "fan_out_item" }` binding object,
|
|
27
|
+
* regardless of `Item`'s shape.
|
|
28
|
+
*/
|
|
29
|
+
export type FanOutInputCtx<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>, TFanOuts extends ReadonlyArray<AnyTypedStep>, K extends string> = StepInputCtx<TInput, TSteps, TFanOuts> & (FanOutItemType<TSteps, K> extends never ? unknown : {
|
|
30
|
+
item: FanOutItemBinding & FanOutItemType<TSteps, K>;
|
|
31
|
+
});
|
|
32
|
+
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
10
33
|
export type WorkItemAccessor = {
|
|
11
34
|
readonly title: WorkItemBinding;
|
|
12
35
|
readonly description: WorkItemBinding;
|
|
@@ -21,20 +44,28 @@ export type WithWorkItemFields<T> = {
|
|
|
21
44
|
workItemDescription: string | null;
|
|
22
45
|
workItemComments: PinnedWorkItemComment[];
|
|
23
46
|
} & T;
|
|
24
|
-
type RequiredInputKeys<T extends object> = {
|
|
47
|
+
export type RequiredInputKeys<T extends object> = {
|
|
25
48
|
[K in keyof T & string]-?: undefined extends T[K] ? never : K;
|
|
26
49
|
}[keyof T & string];
|
|
27
|
-
type OptionalInputKeys<T extends object> = {
|
|
50
|
+
export type OptionalInputKeys<T extends object> = {
|
|
28
51
|
[K in keyof T & string]-?: undefined extends T[K] ? K : never;
|
|
29
52
|
}[keyof T & string];
|
|
30
53
|
type Prettify<T> = {
|
|
31
54
|
[K in keyof T]: T[K];
|
|
32
55
|
} & {};
|
|
33
|
-
export type StepInputCtx<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep
|
|
56
|
+
export type StepInputCtx<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>, TFanOuts extends ReadonlyArray<AnyTypedStep> = []> = {
|
|
34
57
|
input: InputAccessor<Prettify<WithWorkItemFields<TInput["_output"]>>>;
|
|
35
58
|
signal: <S extends ElementOf<TSteps>>(step: S, key: S["__signalKeys"]) => StepSignalBinding;
|
|
36
59
|
output: (step: ElementOf<TSteps>) => StepOutputBinding;
|
|
37
60
|
literal: (value: unknown) => LiteralBinding;
|
|
61
|
+
/**
|
|
62
|
+
* Reaches a fan-out's whole cohort — every terminal branch's own signals
|
|
63
|
+
* + output, resolved server-side — from a later, non-adjacent step's
|
|
64
|
+
* input mapper (issue #167). `fanOutStep` is constrained to a fan-out
|
|
65
|
+
* step already seen earlier in this pipeline (`.fanOutStep(fanOutStep, ...)`),
|
|
66
|
+
* the same way `signal`/`output` are constrained to `TSteps`.
|
|
67
|
+
*/
|
|
68
|
+
signalsList: (fanOutStep: ElementOf<TFanOuts>) => SignalsListBinding;
|
|
38
69
|
};
|
|
39
70
|
type ReservedPipelineInputKeys = "workItemTitle" | "workItemDescription" | "workItemComments";
|
|
40
71
|
export type NoReservedKeys<T extends ZodType> = T extends {
|
|
@@ -66,7 +97,7 @@ export type PipelineMeta<TInput extends ZodType = z.ZodUnknown> = {
|
|
|
66
97
|
};
|
|
67
98
|
};
|
|
68
99
|
export declare const WORK_ITEM_ACCESSOR: WorkItemAccessor;
|
|
69
|
-
export declare function makeStepInputCtx<TInput extends ZodType>(inputSchema: TInput): StepInputCtx<TInput, ReadonlyArray<AnyTypedStep>>;
|
|
100
|
+
export declare function makeStepInputCtx<TInput extends ZodType>(inputSchema: TInput): StepInputCtx<TInput, ReadonlyArray<AnyTypedStep>, ReadonlyArray<AnyTypedStep>>;
|
|
70
101
|
export declare function literal(value: unknown): LiteralBinding;
|
|
71
102
|
export declare function normalizeInputMapping(mapping: Record<string, AnyBinding | undefined> | undefined): Record<string, AnyBinding> | undefined;
|
|
72
103
|
export declare function resolveAdditionalStepInputBindings(label: "additionalStepInput", definition: PipelineMeta["additionalStepInput"] | undefined): Record<string, AnyBinding>;
|
|
@@ -1,74 +1,80 @@
|
|
|
1
1
|
import { z, type ZodType } from "zod";
|
|
2
2
|
import { type AdvanceCtx, type AdvanceResult } from "../advancement-policies/fluent-rules";
|
|
3
|
-
import { type AnyBinding, type PipelineDefinitionSpec, type
|
|
4
|
-
import { type AnyTypedStep, type PipelineMeta, type
|
|
5
|
-
|
|
6
|
-
type
|
|
7
|
-
|
|
8
|
-
infer L
|
|
9
|
-
] ? L extends AnyTypedStep ? L : never : never;
|
|
10
|
-
type LastSignalKeys<T extends ReadonlyArray<AnyTypedStep>> = LastStep<T> extends AnyTypedStep ? LastStep<T>["__signalKeys"] : never;
|
|
11
|
-
type LastSignalTypeMap<T extends ReadonlyArray<AnyTypedStep>> = LastStep<T> extends AnyTypedStep ? LastStep<T>["__signalTypeMap"] : Record<string, unknown>;
|
|
12
|
-
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
13
|
-
type RequiredInputKeys<T extends object> = {
|
|
14
|
-
[K in keyof T & string]-?: undefined extends T[K] ? never : K;
|
|
15
|
-
}[keyof T & string];
|
|
16
|
-
type OptionalInputKeys<T extends object> = {
|
|
17
|
-
[K in keyof T & string]-?: undefined extends T[K] ? K : never;
|
|
18
|
-
}[keyof T & string];
|
|
3
|
+
import { type AnyBinding, type PipelineDefinitionSpec, type PipelineNodeConfig } from "./define-pipeline";
|
|
4
|
+
import { type AnyTypedStep, type IsAny, type LastSignalKeys, type OptionalInputKeys, type PipelineMeta, type RequiredInputKeys, type StepInputCtx } from "./builder-helpers";
|
|
5
|
+
import { type FanOutStepConfig } from "./fan-out-builder";
|
|
6
|
+
export type { AnyTypedStep, PipelineMeta, StepInputCtx, WorkItemAccessor, } from "./builder-helpers";
|
|
7
|
+
export { type FanOutStepConfig, type FanOutInputMapping, } from "./fan-out-builder";
|
|
19
8
|
type StepInputMapping<S extends AnyTypedStep> = IsAny<S["__inputType"]> extends true ? Partial<Record<string, AnyBinding>> : S["__inputType"] extends object ? {
|
|
20
9
|
[K in RequiredInputKeys<S["__inputType"]>]: AnyBinding;
|
|
21
10
|
} & {
|
|
22
11
|
[K in OptionalInputKeys<S["__inputType"]>]?: AnyBinding;
|
|
23
12
|
} : Partial<Record<string, AnyBinding>>;
|
|
24
13
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
14
|
+
* `.step()`'s single options argument. Deliberately a single generic type
|
|
15
|
+
* (not a set of overload signatures split on `S["__hasAdditionalInput"]`):
|
|
16
|
+
* with overloads, a mistake inside `options.input`'s return value fails
|
|
17
|
+
* every overload, and TS reports "no overload matches" against the whole
|
|
18
|
+
* call rather than pointing at the specific missing/wrong property inside
|
|
19
|
+
* `input`'s return type. A single signature lets TS check `options`
|
|
20
|
+
* structurally in one pass and localize the error correctly.
|
|
27
21
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
22
|
+
type StepOptions<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>, TFanOuts extends ReadonlyArray<AnyTypedStep>, S extends AnyTypedStep> = (S extends {
|
|
23
|
+
__hasAdditionalInput: false;
|
|
24
|
+
} ? {
|
|
25
|
+
input?: (ctx: StepInputCtx<TInput, TSteps, TFanOuts>) => Partial<Record<string, AnyBinding>>;
|
|
26
|
+
} : {
|
|
27
|
+
input: (ctx: StepInputCtx<TInput, TSteps, TFanOuts>) => StepInputMapping<S>;
|
|
28
|
+
}) & {
|
|
29
|
+
advance: (ctx: AdvanceCtx<S["__signalKeys"], S["__signalTypeMap"]>) => AdvanceResult<S["__signalKeys"]>;
|
|
30
|
+
timeout?: number | null;
|
|
31
|
+
};
|
|
38
32
|
/**
|
|
39
|
-
* Returned by `.
|
|
40
|
-
* `.
|
|
33
|
+
* Returned by `.step()`/`pipeline()`. Provides `.step()` to chain the next
|
|
34
|
+
* step, `.fanOutStep()` to begin a fan-out+cohort-gate pair (issue #167),
|
|
35
|
+
* and `.build()` to finalize. `.step()` requires an `advance` callback in
|
|
36
|
+
* its options — deciding how the pipeline continues past this step — as
|
|
37
|
+
* part of the same call that declares the step's input, rather than as a
|
|
38
|
+
* separate chained method on an intermediate builder class.
|
|
41
39
|
*/
|
|
42
|
-
export declare class PipelineStepBuilder<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep
|
|
40
|
+
export declare class PipelineStepBuilder<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>, TFanOuts extends ReadonlyArray<AnyTypedStep> = []> {
|
|
43
41
|
protected readonly inputSchema: TInput;
|
|
44
42
|
protected readonly meta: Omit<PipelineMeta<TInput>, "additionalPipelineInput" | "additionalStepInput">;
|
|
45
|
-
protected readonly
|
|
43
|
+
protected readonly nodes: PipelineNodeConfig[];
|
|
46
44
|
protected readonly pipelineInputBindings: Record<string, AnyBinding>;
|
|
47
45
|
protected readonly pipelineStepInputBindings: Record<string, AnyBinding>;
|
|
48
46
|
readonly __steps: TSteps;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
readonly __fanOuts: TFanOuts;
|
|
48
|
+
constructor(inputSchema: TInput, meta: Omit<PipelineMeta<TInput>, "additionalPipelineInput" | "additionalStepInput">, nodes: PipelineNodeConfig[], pipelineInputBindings?: Record<string, AnyBinding>, pipelineStepInputBindings?: Record<string, AnyBinding>);
|
|
49
|
+
step<S extends AnyTypedStep>(step: S, options: StepOptions<TInput, TSteps, TFanOuts, S>): PipelineStepBuilder<TInput, [...TSteps, S], TFanOuts>;
|
|
50
|
+
/**
|
|
51
|
+
* Begins a fan-out+cohort-gate pair (issue #167): `step` is the template
|
|
52
|
+
* every branch executes, with its branch count (and, when `over` names
|
|
53
|
+
* an array-typed signal, each branch's own typed `item`) resolved at
|
|
54
|
+
* runtime from `config.over` (a signal on the step immediately
|
|
55
|
+
* preceding this fan-out). `config` requires both `advance` (each
|
|
56
|
+
* branch's own continue/block decision) and `advanceAll` (the
|
|
57
|
+
* whole-cohort decision — a pure gate, not a step; nothing besides the
|
|
58
|
+
* fan-out+gate pair itself is appended to the pipeline's node sequence)
|
|
59
|
+
* up front, mirroring how `.step()` requires `advance` in its own
|
|
60
|
+
* options rather than as a separate chained call.
|
|
61
|
+
*/
|
|
62
|
+
fanOutStep<S extends AnyTypedStep, K extends LastSignalKeys<TSteps>>(step: S, config: FanOutStepConfig<TInput, TSteps, TFanOuts, S, K>): PipelineStepBuilder<TInput, TSteps, [...TFanOuts, S]>;
|
|
54
63
|
build(): PipelineDefinitionSpec;
|
|
55
64
|
}
|
|
56
65
|
/**
|
|
57
66
|
* Entry-point builder returned by `pipeline()`. Only exposes `.step()` —
|
|
58
|
-
* call that to receive a `
|
|
59
|
-
* `.
|
|
67
|
+
* call that to receive a `PipelineStepBuilder`, which chains further
|
|
68
|
+
* `.step()`/`.fanOutStep()` calls or finalizes with `.build()`.
|
|
60
69
|
*/
|
|
61
70
|
export declare class PipelineBuilder<TInput extends ZodType> {
|
|
62
71
|
private readonly inputSchema;
|
|
63
72
|
private readonly meta;
|
|
64
|
-
private readonly
|
|
73
|
+
private readonly nodes;
|
|
65
74
|
private readonly pipelineInputBindings;
|
|
66
75
|
private readonly pipelineStepInputBindings;
|
|
67
76
|
constructor(meta: PipelineMeta<TInput>);
|
|
68
|
-
step<S extends AnyTypedStep
|
|
69
|
-
__hasAdditionalInput: false;
|
|
70
|
-
}>(step: S, mapper?: (ctx: StepInputCtx<TInput, []>) => Partial<Record<string, AnyBinding>>, configFn?: (config: StepConfig) => void): PipelineStepAdvancementBuilder<TInput, [S]>;
|
|
71
|
-
step<S extends AnyTypedStep>(step: S, mapper: (ctx: StepInputCtx<TInput, []>) => StepInputMapping<S>, configFn?: (config: StepConfig) => void): PipelineStepAdvancementBuilder<TInput, [S]>;
|
|
77
|
+
step<S extends AnyTypedStep>(step: S, options: StepOptions<TInput, [], [], S>): PipelineStepBuilder<TInput, [S]>;
|
|
72
78
|
}
|
|
73
79
|
export { literal } from "./builder-helpers";
|
|
74
80
|
export declare function pipeline<TInput extends ZodType = z.ZodUnknown>(meta: PipelineMeta<TInput>): PipelineBuilder<TInput>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DependencyEdgeSpec, NodeDefinitionSpec } from "./define-pipeline";
|
|
2
|
+
/**
|
|
3
|
+
* Orders `nodeDefinitions` by walking `dependencyEdges` from the single root
|
|
4
|
+
* to the single leaf. Returns `null` (does not throw) on any structural
|
|
5
|
+
* problem: multiple roots, a node with more than one outgoing edge, a node
|
|
6
|
+
* with more than one incoming edge, a cycle, or a disconnected node.
|
|
7
|
+
*/
|
|
8
|
+
export declare function tryOrderChainNodeDefinitions(nodeDefinitions: readonly NodeDefinitionSpec[], dependencyEdges: readonly DependencyEdgeSpec[]): NodeDefinitionSpec[] | null;
|
|
9
|
+
/**
|
|
10
|
+
* Builds one dependency edge between each consecutive pair of `orderedNodes`,
|
|
11
|
+
* in the order given. Used to synthesize a chain's edges from an already-known
|
|
12
|
+
* author order (e.g. declaration order in `.step()` calls).
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildChainDependencyEdges(orderedNodes: readonly Pick<NodeDefinitionSpec, "nodeKey">[]): DependencyEdgeSpec[];
|
|
15
|
+
/**
|
|
16
|
+
* `tryOrderChainNodeDefinitions`, but throws a descriptive error instead of
|
|
17
|
+
* returning `null` when the graph isn't a single valid chain.
|
|
18
|
+
*/
|
|
19
|
+
export declare function orderChainNodeDefinitions(nodeDefinitions: readonly NodeDefinitionSpec[], dependencyEdges: readonly DependencyEdgeSpec[]): NodeDefinitionSpec[];
|
|
@@ -89,7 +89,7 @@ export type DefaultPipelineAssignmentCtx = {
|
|
|
89
89
|
* @example
|
|
90
90
|
* workItem.field("issueType").eq("bug").then(assign(bugTriage))
|
|
91
91
|
*/
|
|
92
|
-
assign(pipeline: PipelineDefinitionSpec)
|
|
92
|
+
assign: (pipeline: PipelineDefinitionSpec) => AssignOutcome;
|
|
93
93
|
/**
|
|
94
94
|
* Outcome: do not assign any pipeline to this work item.
|
|
95
95
|
* Used in both `rules` (via `.then(skip())`) and `default`.
|
|
@@ -97,7 +97,7 @@ export type DefaultPipelineAssignmentCtx = {
|
|
|
97
97
|
* @example
|
|
98
98
|
* workItem.field("status").eq("resolved").then(skip())
|
|
99
99
|
*/
|
|
100
|
-
skip()
|
|
100
|
+
skip: () => SkipOutcome;
|
|
101
101
|
/**
|
|
102
102
|
* All nested conditions must match.
|
|
103
103
|
*
|
|
@@ -107,7 +107,7 @@ export type DefaultPipelineAssignmentCtx = {
|
|
|
107
107
|
* workItem.field("priority").eq("high"),
|
|
108
108
|
* ).then(assign(bugTriage))
|
|
109
109
|
*/
|
|
110
|
-
all(...refs: AssignmentNestable[])
|
|
110
|
+
all: (...refs: AssignmentNestable[]) => AssignmentGroup;
|
|
111
111
|
/**
|
|
112
112
|
* Any nested condition must match.
|
|
113
113
|
*
|
|
@@ -117,7 +117,7 @@ export type DefaultPipelineAssignmentCtx = {
|
|
|
117
117
|
* workItem.field("status").eq("closed"),
|
|
118
118
|
* ).then(skip())
|
|
119
119
|
*/
|
|
120
|
-
any(...refs: AssignmentNestable[])
|
|
120
|
+
any: (...refs: AssignmentNestable[]) => AssignmentGroup;
|
|
121
121
|
};
|
|
122
122
|
export type DefaultPipelineAssignmentInput = {
|
|
123
123
|
/**
|
|
@@ -185,10 +185,10 @@ type SerializedAssignmentRule = {
|
|
|
185
185
|
export type SerializedDefaultPipelineAssignment = {
|
|
186
186
|
/**
|
|
187
187
|
* Pipeline key for the primary assign pipeline; resolved to a
|
|
188
|
-
* `
|
|
188
|
+
* `pipelineDefinitionId` by the push layer. Null when `default`
|
|
189
189
|
* is `skip()` and no rule assigns a pipeline (push will reject this).
|
|
190
190
|
*/
|
|
191
|
-
|
|
191
|
+
pipelineDefinitionKey: string;
|
|
192
192
|
rulesJson: {
|
|
193
193
|
rules: SerializedAssignmentRule[];
|
|
194
194
|
};
|
|
@@ -199,7 +199,7 @@ export type SerializedDefaultPipelineAssignment = {
|
|
|
199
199
|
/**
|
|
200
200
|
* Serialize a `DefaultPipelineAssignmentSpec` to the wire format.
|
|
201
201
|
*
|
|
202
|
-
* `
|
|
202
|
+
* `pipelineDefinitionKey` is the key of the primary assign pipeline —
|
|
203
203
|
* taken from `default` if it's `assign(...)`, otherwise from the first rule
|
|
204
204
|
* that assigns a pipeline. The push layer rejects specs with no assign outcome.
|
|
205
205
|
*/
|