@boboddy/sdk 0.1.6-alpha → 0.1.8-alpha

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.
@@ -1,3 +1,50 @@
1
+ /** Aggregation types supported by the runtime for computed signals. */
2
+ export type PipelineStepComputedSignalType = "average" | "weighted_average" | "sum" | "min" | "max" | "count" | "boolean_any" | "boolean_all";
3
+ type Join<T extends readonly string[], D extends string> = T extends readonly [
4
+ infer Head extends string,
5
+ ...infer Rest extends readonly string[]
6
+ ] ? Rest extends readonly [] ? Head : `${Head}${D}${Join<Rest, D>}` : "";
7
+ /**
8
+ * An inline computed-signal token returned by `Computed.X(...)`. Carries the
9
+ * derived `key` (e.g. `"sum_success"`), the aggregation `type`, and the input
10
+ * signal keys it aggregates over. At serialization time, every inline token
11
+ * embedded in a rule's signal position is extracted into the step's
12
+ * `computedSignalDefinitions` and replaced by its bare key string.
13
+ *
14
+ * - `TKey` — the auto-derived key (literal template).
15
+ * - `TInputSignalKeys` — the union of input signal keys; constrained to the
16
+ * step's signals via `Rule.signal` / `Rule.when` typing.
17
+ */
18
+ export type InlineComputedSignal<TKey extends string = string, TInputSignalKeys extends string = string> = {
19
+ readonly _tag: "computed_signal";
20
+ readonly key: TKey;
21
+ readonly type: PipelineStepComputedSignalType;
22
+ readonly inputSignalKeys: ReadonlyArray<TInputSignalKeys>;
23
+ readonly configJson: Record<string, unknown> | null;
24
+ readonly availableWhenResultStatusIn: readonly string[] | null;
25
+ };
26
+ type ComputedOptions = {
27
+ configJson?: Record<string, unknown> | null;
28
+ availableWhenResultStatusIn?: readonly string[] | null;
29
+ };
30
+ /**
31
+ * Factories for inline computed signals. Each call returns a token whose `key`
32
+ * is auto-derived as `${type}_${inputSignalKeys.join("_")}`.
33
+ *
34
+ * @example
35
+ * Rule.when(Computed.sum(["success"]), "greaterThan", 1, "continue")
36
+ * Rule.signal(Computed.average(["score"]), "greaterThanInclusive", 80)
37
+ */
38
+ export declare const Computed: {
39
+ readonly average: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`average_${Join<TInputs, "_">}`, TInputs[number]>;
40
+ readonly weightedAverage: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`weighted_average_${Join<TInputs, "_">}`, TInputs[number]>;
41
+ readonly sum: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`sum_${Join<TInputs, "_">}`, TInputs[number]>;
42
+ readonly min: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`min_${Join<TInputs, "_">}`, TInputs[number]>;
43
+ readonly max: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`max_${Join<TInputs, "_">}`, TInputs[number]>;
44
+ readonly count: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`count_${Join<TInputs, "_">}`, TInputs[number]>;
45
+ readonly booleanAny: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`boolean_any_${Join<TInputs, "_">}`, TInputs[number]>;
46
+ readonly booleanAll: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`boolean_all_${Join<TInputs, "_">}`, TInputs[number]>;
47
+ };
1
48
  /**
2
49
  * Comparison operators supported by json-rules-engine.
3
50
  * These map directly to the operator names the runtime evaluates against signal values.
@@ -26,11 +73,14 @@ export type AdvancementOutcome = AdvancementEventType | {
26
73
  * by `Rule.when()`.
27
74
  *
28
75
  * `TSignalKeys` is constrained to the declaring step's signal keys, so the
29
- * `signal` field is validated at compile time.
76
+ * `signal` field is validated at compile time. `signal` may be either a step
77
+ * signal key (string) or an inline `Computed.X(...)` token — at serialization
78
+ * time, inline tokens are hoisted into `computedSignalDefinitions` and replaced
79
+ * by their bare key string.
30
80
  */
31
81
  export type SignalCondition<TSignalKeys extends string = string> = {
32
82
  readonly _tag: "signal";
33
- signal: TSignalKeys;
83
+ signal: TSignalKeys | InlineComputedSignal<string, TSignalKeys>;
34
84
  operator: ConditionOperator;
35
85
  value: unknown;
36
86
  };
@@ -80,7 +130,7 @@ export type Rule<TSignalKeys extends string = string> = {
80
130
  * Rule.signal("flagged", "equal", false),
81
131
  * ], "continue")
82
132
  */
83
- declare function signal<TSignalKeys extends string>(signal: TSignalKeys, operator: ConditionOperator, value: unknown): SignalCondition<TSignalKeys>;
133
+ declare function signal<TSignalKeys extends string>(signal: TSignalKeys | InlineComputedSignal<string, TSignalKeys>, operator: ConditionOperator, value: unknown): SignalCondition<TSignalKeys>;
84
134
  /**
85
135
  * When called with only `conditions`: returns a nested `AllCondition` group
86
136
  * for use inside another `Rule.all()` or `Rule.any()`.
@@ -136,7 +186,7 @@ declare function any<TSignalKeys extends string>(conditions: RuleCondition<TSign
136
186
  * Rule.when("passed", "equal", true, "continue")
137
187
  * Rule.when("score", "greaterThanInclusive", 80, { outcome: "continue", outcomeJson: { via: "score" } })
138
188
  */
139
- declare function when<TSignalKeys extends string>(signal: TSignalKeys, operator: ConditionOperator, value: unknown, outcome: AdvancementOutcome): Rule<TSignalKeys>;
189
+ declare function when<TSignalKeys extends string>(signal: TSignalKeys | InlineComputedSignal<string, TSignalKeys>, operator: ConditionOperator, value: unknown, outcome: AdvancementOutcome): Rule<TSignalKeys>;
140
190
  /**
141
191
  * All factories for building advancement rules.
142
192
  *
@@ -215,4 +265,19 @@ export type SerializedAdvancementPolicy = {
215
265
  allowedEventTypes: AdvancementEventType[];
216
266
  };
217
267
  export declare function serializeAdvancementPolicy(policy: AdvancementPolicy | undefined): SerializedAdvancementPolicy;
268
+ /** Wire-format computed signal definition emitted on the pipeline step. */
269
+ export type SerializedComputedSignalDefinition = {
270
+ key: string;
271
+ type: PipelineStepComputedSignalType;
272
+ inputSignalKeys: string[];
273
+ configJson: Record<string, unknown> | null;
274
+ availableWhenResultStatusIn: string[] | null;
275
+ };
276
+ /**
277
+ * Walks the rules tree, extracts every inline `Computed.X(...)` token embedded
278
+ * in a `SignalCondition.signal` position, dedupes by key, and returns the
279
+ * resulting computed-signal definitions. Two tokens with the same key but
280
+ * differing definitions are a programming error and throw.
281
+ */
282
+ export declare function extractInlineComputedSignals(policy: AdvancementPolicy | undefined): SerializedComputedSignalDefinition[];
218
283
  export {};
@@ -14,6 +14,27 @@ var __export = (target, all) => {
14
14
  };
15
15
 
16
16
  // src/definitions/advancement-policies/define-advancement-policy.ts
17
+ function makeComputed(type, inputSignalKeys, options) {
18
+ const key = [type, ...inputSignalKeys].join("_");
19
+ return {
20
+ _tag: "computed_signal",
21
+ key,
22
+ type,
23
+ inputSignalKeys,
24
+ configJson: options?.configJson ?? null,
25
+ availableWhenResultStatusIn: options?.availableWhenResultStatusIn ?? null
26
+ };
27
+ }
28
+ var Computed = {
29
+ average: (inputSignalKeys, options) => makeComputed("average", inputSignalKeys, options),
30
+ weightedAverage: (inputSignalKeys, options) => makeComputed("weighted_average", inputSignalKeys, options),
31
+ sum: (inputSignalKeys, options) => makeComputed("sum", inputSignalKeys, options),
32
+ min: (inputSignalKeys, options) => makeComputed("min", inputSignalKeys, options),
33
+ max: (inputSignalKeys, options) => makeComputed("max", inputSignalKeys, options),
34
+ count: (inputSignalKeys, options) => makeComputed("count", inputSignalKeys, options),
35
+ booleanAny: (inputSignalKeys, options) => makeComputed("boolean_any", inputSignalKeys, options),
36
+ booleanAll: (inputSignalKeys, options) => makeComputed("boolean_all", inputSignalKeys, options)
37
+ };
17
38
  function signal(signal2, operator, value) {
18
39
  return { _tag: "signal", signal: signal2, operator, value };
19
40
  }
@@ -47,7 +68,7 @@ function resolveOutcome(outcome) {
47
68
  function serializeCondition(condition) {
48
69
  if (condition._tag === "signal") {
49
70
  return {
50
- fact: condition.signal,
71
+ fact: typeof condition.signal === "string" ? condition.signal : condition.signal.key,
51
72
  operator: condition.operator,
52
73
  value: condition.value
53
74
  };
@@ -89,7 +110,49 @@ function serializeAdvancementPolicy(policy) {
89
110
  allowedEventTypes: [...outcomeSet]
90
111
  };
91
112
  }
113
+ function visitSignalConditions(conditions, visit) {
114
+ for (const c of conditions) {
115
+ if (c._tag === "signal") {
116
+ visit(c);
117
+ } else {
118
+ visitSignalConditions(c.conditions, visit);
119
+ }
120
+ }
121
+ }
122
+ function isSameComputedDefinition(a, b) {
123
+ return a.type === b.type && a.inputSignalKeys.length === b.inputSignalKeys.length && a.inputSignalKeys.every((k, i) => k === b.inputSignalKeys[i]) && JSON.stringify(a.configJson) === JSON.stringify(b.configJson) && JSON.stringify(a.availableWhenResultStatusIn) === JSON.stringify(b.availableWhenResultStatusIn);
124
+ }
125
+ function extractInlineComputedSignals(policy) {
126
+ if (!policy?.rules)
127
+ return [];
128
+ const byKey = new Map;
129
+ for (const rule of policy.rules) {
130
+ visitSignalConditions(rule.conditions, (cond) => {
131
+ if (typeof cond.signal === "string")
132
+ return;
133
+ const inline = cond.signal;
134
+ const def = {
135
+ key: inline.key,
136
+ type: inline.type,
137
+ inputSignalKeys: [...inline.inputSignalKeys],
138
+ configJson: inline.configJson,
139
+ availableWhenResultStatusIn: inline.availableWhenResultStatusIn ? [...inline.availableWhenResultStatusIn] : null
140
+ };
141
+ const existing = byKey.get(def.key);
142
+ if (existing) {
143
+ if (!isSameComputedDefinition(existing, def)) {
144
+ throw new Error(`Conflicting inline computed signal definitions for key "${def.key}"`);
145
+ }
146
+ return;
147
+ }
148
+ byKey.set(def.key, def);
149
+ });
150
+ }
151
+ return [...byKey.values()];
152
+ }
92
153
  export {
93
154
  serializeAdvancementPolicy,
94
- Rule
155
+ extractInlineComputedSignals,
156
+ Rule,
157
+ Computed
95
158
  };
@@ -1,8 +1,8 @@
1
1
  import type { ZodType } from "zod";
2
- import type { DotPaths, TypedStepDefinitionSpec } from "../steps/define-step";
3
- import { type AdvancementPolicy, type SerializedAdvancementPolicy } from "../advancement-policies/define-advancement-policy";
4
- export type { AdvancementPolicy } from "../advancement-policies/define-advancement-policy";
5
- export { Rule } from "../advancement-policies/define-advancement-policy";
2
+ import type { DotPaths, StepDefinitionSpec, TypedStepDefinitionSpec } from "../steps/define-step";
3
+ import { type AdvancementPolicy, type SerializedAdvancementPolicy, type SerializedComputedSignalDefinition } from "../advancement-policies/define-advancement-policy";
4
+ export type { AdvancementPolicy, PipelineStepComputedSignalType, } from "../advancement-policies/define-advancement-policy";
5
+ export { Computed, Rule, } from "../advancement-policies/define-advancement-policy";
6
6
  type AnyTypedStep = TypedStepDefinitionSpec<any, any, any>;
7
7
  export type PipelineInputBinding = {
8
8
  source: "pipeline_input";
@@ -34,14 +34,6 @@ export declare function fromSignal<TStep extends AnyTypedStep>(step: TStep, sign
34
34
  * For a stable contract, prefer fromSignal() instead.
35
35
  */
36
36
  export declare function stepOutput(step: AnyTypedStep): StepOutputBinding;
37
- export type PipelineStepComputedSignalType = "average" | "weighted_average" | "sum" | "min" | "max" | "count" | "boolean_any" | "boolean_all";
38
- export type PipelineStepComputedSignalSpec<TSignalKey extends string = string> = {
39
- key: string;
40
- type: PipelineStepComputedSignalType;
41
- inputSignalKeys: ReadonlyArray<TSignalKey>;
42
- configJson?: Record<string, unknown> | null;
43
- availableWhenResultStatusIn?: string[] | null;
44
- };
45
37
  export type PipelineStepConfig<TStep extends AnyTypedStep = AnyTypedStep> = {
46
38
  step: TStep;
47
39
  /** Maps each step input field to an input source. Extra keys are ignored at runtime. */
@@ -51,16 +43,13 @@ export type PipelineStepConfig<TStep extends AnyTypedStep = AnyTypedStep> = {
51
43
  timeout?: number | null;
52
44
  /**
53
45
  * Controls when and how this step advances in the pipeline.
54
- * Signal keys in `whenSignal()` rules are type-checked against this step's declared signals.
46
+ * Signal keys in `Rule.signal()` / `Rule.when()` calls are type-checked
47
+ * against the step's declared signals. Inline `Computed.X(...)` tokens can
48
+ * also appear in the signal position; they're hoisted into the step's
49
+ * `computedSignalDefinitions` at serialization time.
55
50
  * Defaults to `{ defaultOutcome: "continue" }` when omitted.
56
51
  */
57
52
  advancement?: AdvancementPolicy<TStep["__signalKeys"]>;
58
- /**
59
- * Aggregations derived from this step's emitted signals (extractor keys plus any
60
- * earlier computed-signal keys in this list). Each entry's `inputSignalKeys` is
61
- * type-checked against the step's declared signal keys.
62
- */
63
- computedSignals?: ReadonlyArray<PipelineStepComputedSignalSpec<TStep["__signalKeys"]>>;
64
53
  };
65
54
  type SerializedBinding = {
66
55
  source: "pipeline_input";
@@ -73,13 +62,6 @@ type SerializedBinding = {
73
62
  source: "step_output";
74
63
  stepKey: string;
75
64
  };
76
- type SerializedComputedSignalDefinition = {
77
- key: string;
78
- type: PipelineStepComputedSignalType;
79
- inputSignalKeys: string[];
80
- configJson: Record<string, unknown> | null;
81
- availableWhenResultStatusIn: string[] | null;
82
- };
83
65
  export type PipelineDefinitionSpec = {
84
66
  key: string;
85
67
  name: string;
@@ -96,6 +78,8 @@ export type PipelineDefinitionSpec = {
96
78
  advancementPolicyDefinition: SerializedAdvancementPolicy;
97
79
  computedSignalDefinitions: SerializedComputedSignalDefinition[];
98
80
  }>;
81
+ /** Step specs referenced by this pipeline. Populated by definePipeline so the push command can auto-push steps that aren't explicitly exported. */
82
+ _stepDefinitions?: StepDefinitionSpec[];
99
83
  };
100
84
  /**
101
85
  * Untyped input shape used for documentation and as the internal implementation
@@ -114,7 +98,10 @@ export type DefinePipelineInput = {
114
98
  * Defines a pipeline from an ordered list of step configs.
115
99
  *
116
100
  * Each step's `advancement` policy is typed against that step's declared signal
117
- * keys — passing an unknown signal key to `whenSignal()` is a compile-time error.
101
+ * keys — passing an unknown signal key to `Rule.signal()` / `Rule.when()` is a
102
+ * compile-time error. Inline `Computed.X(...)` tokens can also be used in the
103
+ * signal position; they're walked out of the rules tree at serialization time
104
+ * and emitted as the step's `computedSignalDefinitions`.
118
105
  *
119
106
  * TypeScript achieves per-element signal key checking by constraining `TSteps`
120
107
  * to a tuple of step instances (`AnyTypedStep[]`), not step configs. Each element
@@ -14,6 +14,27 @@ var __export = (target, all) => {
14
14
  };
15
15
 
16
16
  // src/definitions/advancement-policies/define-advancement-policy.ts
17
+ function makeComputed(type, inputSignalKeys, options) {
18
+ const key = [type, ...inputSignalKeys].join("_");
19
+ return {
20
+ _tag: "computed_signal",
21
+ key,
22
+ type,
23
+ inputSignalKeys,
24
+ configJson: options?.configJson ?? null,
25
+ availableWhenResultStatusIn: options?.availableWhenResultStatusIn ?? null
26
+ };
27
+ }
28
+ var Computed = {
29
+ average: (inputSignalKeys, options) => makeComputed("average", inputSignalKeys, options),
30
+ weightedAverage: (inputSignalKeys, options) => makeComputed("weighted_average", inputSignalKeys, options),
31
+ sum: (inputSignalKeys, options) => makeComputed("sum", inputSignalKeys, options),
32
+ min: (inputSignalKeys, options) => makeComputed("min", inputSignalKeys, options),
33
+ max: (inputSignalKeys, options) => makeComputed("max", inputSignalKeys, options),
34
+ count: (inputSignalKeys, options) => makeComputed("count", inputSignalKeys, options),
35
+ booleanAny: (inputSignalKeys, options) => makeComputed("boolean_any", inputSignalKeys, options),
36
+ booleanAll: (inputSignalKeys, options) => makeComputed("boolean_all", inputSignalKeys, options)
37
+ };
17
38
  function signal(signal2, operator, value) {
18
39
  return { _tag: "signal", signal: signal2, operator, value };
19
40
  }
@@ -47,7 +68,7 @@ function resolveOutcome(outcome) {
47
68
  function serializeCondition(condition) {
48
69
  if (condition._tag === "signal") {
49
70
  return {
50
- fact: condition.signal,
71
+ fact: typeof condition.signal === "string" ? condition.signal : condition.signal.key,
51
72
  operator: condition.operator,
52
73
  value: condition.value
53
74
  };
@@ -89,6 +110,46 @@ function serializeAdvancementPolicy(policy) {
89
110
  allowedEventTypes: [...outcomeSet]
90
111
  };
91
112
  }
113
+ function visitSignalConditions(conditions, visit) {
114
+ for (const c of conditions) {
115
+ if (c._tag === "signal") {
116
+ visit(c);
117
+ } else {
118
+ visitSignalConditions(c.conditions, visit);
119
+ }
120
+ }
121
+ }
122
+ function isSameComputedDefinition(a, b) {
123
+ return a.type === b.type && a.inputSignalKeys.length === b.inputSignalKeys.length && a.inputSignalKeys.every((k, i) => k === b.inputSignalKeys[i]) && JSON.stringify(a.configJson) === JSON.stringify(b.configJson) && JSON.stringify(a.availableWhenResultStatusIn) === JSON.stringify(b.availableWhenResultStatusIn);
124
+ }
125
+ function extractInlineComputedSignals(policy) {
126
+ if (!policy?.rules)
127
+ return [];
128
+ const byKey = new Map;
129
+ for (const rule of policy.rules) {
130
+ visitSignalConditions(rule.conditions, (cond) => {
131
+ if (typeof cond.signal === "string")
132
+ return;
133
+ const inline = cond.signal;
134
+ const def = {
135
+ key: inline.key,
136
+ type: inline.type,
137
+ inputSignalKeys: [...inline.inputSignalKeys],
138
+ configJson: inline.configJson,
139
+ availableWhenResultStatusIn: inline.availableWhenResultStatusIn ? [...inline.availableWhenResultStatusIn] : null
140
+ };
141
+ const existing = byKey.get(def.key);
142
+ if (existing) {
143
+ if (!isSameComputedDefinition(existing, def)) {
144
+ throw new Error(`Conflicting inline computed signal definitions for key "${def.key}"`);
145
+ }
146
+ return;
147
+ }
148
+ byKey.set(def.key, def);
149
+ });
150
+ }
151
+ return [...byKey.values()];
152
+ }
92
153
 
93
154
  // src/definitions/pipelines/define-pipeline.ts
94
155
  function fromPipelineInput(_schema, path) {
@@ -115,12 +176,20 @@ function serializeBinding(binding) {
115
176
  }
116
177
  function definePipeline(config) {
117
178
  const steps = config.steps;
179
+ const stepDefMap = new Map;
180
+ for (const stepConfig of steps) {
181
+ const mapKey = `${stepConfig.step.key}@v${String(stepConfig.step.version)}`;
182
+ if (!stepDefMap.has(mapKey)) {
183
+ stepDefMap.set(mapKey, stepConfig.step);
184
+ }
185
+ }
118
186
  return {
119
187
  key: config.key,
120
188
  name: config.name,
121
189
  description: config.description ?? null,
122
190
  version: config.version ?? 1,
123
191
  status: config.status ?? "active",
192
+ _stepDefinitions: [...stepDefMap.values()],
124
193
  steps: steps.map((stepConfig, index) => ({
125
194
  stepKey: stepConfig.step.key,
126
195
  stepName: stepConfig.step.name,
@@ -129,13 +198,7 @@ function definePipeline(config) {
129
198
  inputBindingsJson: Object.fromEntries(Object.entries(stepConfig.input ?? {}).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)])),
130
199
  timeoutSeconds: stepConfig.timeout ?? null,
131
200
  advancementPolicyDefinition: serializeAdvancementPolicy(stepConfig.advancement),
132
- computedSignalDefinitions: (stepConfig.computedSignals ?? []).map((cs) => ({
133
- key: cs.key,
134
- type: cs.type,
135
- inputSignalKeys: [...cs.inputSignalKeys],
136
- configJson: cs.configJson ?? null,
137
- availableWhenResultStatusIn: cs.availableWhenResultStatusIn ?? null
138
- }))
201
+ computedSignalDefinitions: extractInlineComputedSignals(stepConfig.advancement)
139
202
  }))
140
203
  };
141
204
  }
@@ -186,5 +249,6 @@ export {
186
249
  fromPipelineInput,
187
250
  definePipeline,
188
251
  createPipelineDefinitionsClient,
189
- Rule
252
+ Rule,
253
+ Computed
190
254
  };
package/dist/index.js CHANGED
@@ -15797,6 +15797,27 @@ var Features = {
15797
15797
  })
15798
15798
  };
15799
15799
  // src/definitions/advancement-policies/define-advancement-policy.ts
15800
+ function makeComputed(type, inputSignalKeys, options) {
15801
+ const key = [type, ...inputSignalKeys].join("_");
15802
+ return {
15803
+ _tag: "computed_signal",
15804
+ key,
15805
+ type,
15806
+ inputSignalKeys,
15807
+ configJson: options?.configJson ?? null,
15808
+ availableWhenResultStatusIn: options?.availableWhenResultStatusIn ?? null
15809
+ };
15810
+ }
15811
+ var Computed = {
15812
+ average: (inputSignalKeys, options) => makeComputed("average", inputSignalKeys, options),
15813
+ weightedAverage: (inputSignalKeys, options) => makeComputed("weighted_average", inputSignalKeys, options),
15814
+ sum: (inputSignalKeys, options) => makeComputed("sum", inputSignalKeys, options),
15815
+ min: (inputSignalKeys, options) => makeComputed("min", inputSignalKeys, options),
15816
+ max: (inputSignalKeys, options) => makeComputed("max", inputSignalKeys, options),
15817
+ count: (inputSignalKeys, options) => makeComputed("count", inputSignalKeys, options),
15818
+ booleanAny: (inputSignalKeys, options) => makeComputed("boolean_any", inputSignalKeys, options),
15819
+ booleanAll: (inputSignalKeys, options) => makeComputed("boolean_all", inputSignalKeys, options)
15820
+ };
15800
15821
  function signal(signal2, operator, value) {
15801
15822
  return { _tag: "signal", signal: signal2, operator, value };
15802
15823
  }
@@ -15830,7 +15851,7 @@ function resolveOutcome(outcome) {
15830
15851
  function serializeCondition(condition) {
15831
15852
  if (condition._tag === "signal") {
15832
15853
  return {
15833
- fact: condition.signal,
15854
+ fact: typeof condition.signal === "string" ? condition.signal : condition.signal.key,
15834
15855
  operator: condition.operator,
15835
15856
  value: condition.value
15836
15857
  };
@@ -15872,6 +15893,46 @@ function serializeAdvancementPolicy(policy) {
15872
15893
  allowedEventTypes: [...outcomeSet]
15873
15894
  };
15874
15895
  }
15896
+ function visitSignalConditions(conditions, visit) {
15897
+ for (const c of conditions) {
15898
+ if (c._tag === "signal") {
15899
+ visit(c);
15900
+ } else {
15901
+ visitSignalConditions(c.conditions, visit);
15902
+ }
15903
+ }
15904
+ }
15905
+ function isSameComputedDefinition(a, b) {
15906
+ return a.type === b.type && a.inputSignalKeys.length === b.inputSignalKeys.length && a.inputSignalKeys.every((k, i) => k === b.inputSignalKeys[i]) && JSON.stringify(a.configJson) === JSON.stringify(b.configJson) && JSON.stringify(a.availableWhenResultStatusIn) === JSON.stringify(b.availableWhenResultStatusIn);
15907
+ }
15908
+ function extractInlineComputedSignals(policy) {
15909
+ if (!policy?.rules)
15910
+ return [];
15911
+ const byKey = new Map;
15912
+ for (const rule of policy.rules) {
15913
+ visitSignalConditions(rule.conditions, (cond) => {
15914
+ if (typeof cond.signal === "string")
15915
+ return;
15916
+ const inline = cond.signal;
15917
+ const def = {
15918
+ key: inline.key,
15919
+ type: inline.type,
15920
+ inputSignalKeys: [...inline.inputSignalKeys],
15921
+ configJson: inline.configJson,
15922
+ availableWhenResultStatusIn: inline.availableWhenResultStatusIn ? [...inline.availableWhenResultStatusIn] : null
15923
+ };
15924
+ const existing = byKey.get(def.key);
15925
+ if (existing) {
15926
+ if (!isSameComputedDefinition(existing, def)) {
15927
+ throw new Error(`Conflicting inline computed signal definitions for key "${def.key}"`);
15928
+ }
15929
+ return;
15930
+ }
15931
+ byKey.set(def.key, def);
15932
+ });
15933
+ }
15934
+ return [...byKey.values()];
15935
+ }
15875
15936
 
15876
15937
  // src/definitions/pipelines/define-pipeline.ts
15877
15938
  function fromPipelineInput(_schema, path) {
@@ -15898,12 +15959,20 @@ function serializeBinding(binding) {
15898
15959
  }
15899
15960
  function definePipeline(config2) {
15900
15961
  const steps = config2.steps;
15962
+ const stepDefMap = new Map;
15963
+ for (const stepConfig of steps) {
15964
+ const mapKey = `${stepConfig.step.key}@v${String(stepConfig.step.version)}`;
15965
+ if (!stepDefMap.has(mapKey)) {
15966
+ stepDefMap.set(mapKey, stepConfig.step);
15967
+ }
15968
+ }
15901
15969
  return {
15902
15970
  key: config2.key,
15903
15971
  name: config2.name,
15904
15972
  description: config2.description ?? null,
15905
15973
  version: config2.version ?? 1,
15906
15974
  status: config2.status ?? "active",
15975
+ _stepDefinitions: [...stepDefMap.values()],
15907
15976
  steps: steps.map((stepConfig, index) => ({
15908
15977
  stepKey: stepConfig.step.key,
15909
15978
  stepName: stepConfig.step.name,
@@ -15912,13 +15981,7 @@ function definePipeline(config2) {
15912
15981
  inputBindingsJson: Object.fromEntries(Object.entries(stepConfig.input ?? {}).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)])),
15913
15982
  timeoutSeconds: stepConfig.timeout ?? null,
15914
15983
  advancementPolicyDefinition: serializeAdvancementPolicy(stepConfig.advancement),
15915
- computedSignalDefinitions: (stepConfig.computedSignals ?? []).map((cs) => ({
15916
- key: cs.key,
15917
- type: cs.type,
15918
- inputSignalKeys: [...cs.inputSignalKeys],
15919
- configJson: cs.configJson ?? null,
15920
- availableWhenResultStatusIn: cs.availableWhenResultStatusIn ?? null
15921
- }))
15984
+ computedSignalDefinitions: extractInlineComputedSignals(stepConfig.advancement)
15922
15985
  }))
15923
15986
  };
15924
15987
  }
@@ -16633,6 +16696,7 @@ export {
16633
16696
  PipelineDefinitions,
16634
16697
  FeedbackRequests,
16635
16698
  Features,
16699
+ Computed,
16636
16700
  BoboddyClient,
16637
16701
  BOBODDY_CONFIG_RELATIVE_PATH,
16638
16702
  Api
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@boboddy/sdk",
4
- "version": "0.1.6-alpha",
4
+ "version": "0.1.8-alpha",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {