@boboddy/sdk 0.1.13-alpha → 0.1.15-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.
- package/dist/boboddy-config-parser.js +4 -325
- package/dist/client.js +7 -6
- package/dist/defaults/auth-file.d.ts +14 -0
- package/dist/defaults/base-url.d.ts +1 -0
- package/dist/defaults/index.d.ts +7 -0
- package/dist/defaults/index.js +312 -0
- package/dist/defaults/load-push-defaults.d.ts +21 -0
- package/dist/defaults/project-config.d.ts +5 -0
- package/dist/definitions/advancement-policies/define-advancement-policy.d.ts +4 -0
- package/dist/definitions/advancement-policies/fluent-rules.d.ts +82 -0
- package/dist/definitions/advancement-policies/index.d.ts +1 -0
- package/dist/definitions/advancement-policies/index.js +87 -0
- package/dist/definitions/pipelines/builder.d.ts +85 -0
- package/dist/definitions/pipelines/define-pipeline.d.ts +16 -52
- package/dist/definitions/pipelines/index.d.ts +2 -0
- package/dist/definitions/pipelines/index.js +16069 -202
- package/dist/definitions/pipelines/input-accessor.d.ts +25 -0
- package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +231 -44
- package/dist/definitions/steps/define-step.d.ts +17 -4
- package/dist/definitions/steps/index.d.ts +1 -0
- package/dist/definitions/steps/index.js +1616 -1586
- package/dist/definitions/steps/prompt-template.d.ts +16 -0
- package/dist/definitions/steps/step-definitions-client.d.ts +9 -3
- package/dist/definitions/steps/step-features.d.ts +1 -1
- package/dist/generated/index.d.ts +1 -1
- package/dist/generated/sdk.gen.d.ts +2 -2
- package/dist/generated/types.gen.d.ts +154 -174
- package/dist/index.js +1979 -1989
- package/dist/jsonc.js +1 -0
- package/dist/opencode-mcp.js +1573 -1572
- package/dist/push/index.d.ts +2 -0
- package/dist/push/index.js +16423 -0
- package/dist/push/push-from-directory.d.ts +21 -0
- package/package.json +9 -1
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/jsonc.ts
|
|
18
|
+
var stripJsoncComments = (content) => {
|
|
19
|
+
let result = "";
|
|
20
|
+
let inString = false;
|
|
21
|
+
let escapeNextCharacter = false;
|
|
22
|
+
let lineComment = false;
|
|
23
|
+
let blockComment = false;
|
|
24
|
+
for (let index = 0;index < content.length; index += 1) {
|
|
25
|
+
const character = content.charAt(index);
|
|
26
|
+
const nextCharacter = content.charAt(index + 1);
|
|
27
|
+
if (lineComment) {
|
|
28
|
+
if (character === `
|
|
29
|
+
`) {
|
|
30
|
+
lineComment = false;
|
|
31
|
+
result += character;
|
|
32
|
+
}
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (blockComment) {
|
|
36
|
+
if (character === "*" && nextCharacter === "/") {
|
|
37
|
+
blockComment = false;
|
|
38
|
+
index += 1;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (inString) {
|
|
43
|
+
result += character;
|
|
44
|
+
if (escapeNextCharacter) {
|
|
45
|
+
escapeNextCharacter = false;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (character === "\\") {
|
|
49
|
+
escapeNextCharacter = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (character === '"') {
|
|
53
|
+
inString = false;
|
|
54
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (character === '"') {
|
|
58
|
+
inString = true;
|
|
59
|
+
result += character;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (character === "/" && nextCharacter === "/") {
|
|
63
|
+
lineComment = true;
|
|
64
|
+
index += 1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (character === "/" && nextCharacter === "*") {
|
|
68
|
+
blockComment = true;
|
|
69
|
+
index += 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
result += character;
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
76
|
+
var stripTrailingCommas = (content) => {
|
|
77
|
+
let result = "";
|
|
78
|
+
let inString = false;
|
|
79
|
+
let escapeNextCharacter = false;
|
|
80
|
+
for (let index = 0;index < content.length; index += 1) {
|
|
81
|
+
const character = content.charAt(index);
|
|
82
|
+
if (inString) {
|
|
83
|
+
result += character;
|
|
84
|
+
if (escapeNextCharacter) {
|
|
85
|
+
escapeNextCharacter = false;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (character === "\\") {
|
|
89
|
+
escapeNextCharacter = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (character === '"') {
|
|
93
|
+
inString = false;
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (character === '"') {
|
|
98
|
+
inString = true;
|
|
99
|
+
result += character;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (character === ",") {
|
|
103
|
+
let lookaheadIndex = index + 1;
|
|
104
|
+
while (lookaheadIndex < content.length) {
|
|
105
|
+
const lookaheadCharacter2 = content.charAt(lookaheadIndex);
|
|
106
|
+
if (/\s/u.test(lookaheadCharacter2)) {
|
|
107
|
+
lookaheadIndex += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (lookaheadCharacter2 === "}" || lookaheadCharacter2 === "]") {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
result += character;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if (lookaheadIndex >= content.length) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const lookaheadCharacter = content.charAt(lookaheadIndex);
|
|
120
|
+
if (lookaheadCharacter === "}" || lookaheadCharacter === "]") {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
result += character;
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
};
|
|
129
|
+
var parseJsonc = (content) => JSON.parse(stripTrailingCommas(stripJsoncComments(content)));
|
|
130
|
+
|
|
131
|
+
// src/defaults/auth-file.ts
|
|
132
|
+
import {
|
|
133
|
+
chmodSync,
|
|
134
|
+
existsSync,
|
|
135
|
+
lstatSync,
|
|
136
|
+
mkdirSync,
|
|
137
|
+
readFileSync,
|
|
138
|
+
renameSync,
|
|
139
|
+
rmSync,
|
|
140
|
+
writeFileSync
|
|
141
|
+
} from "fs";
|
|
142
|
+
import { dirname, join } from "path";
|
|
143
|
+
import { homedir } from "os";
|
|
144
|
+
var LEGACY_AUTH_FILE_PATH = join(homedir(), ".boboddy");
|
|
145
|
+
var AUTH_FILE_PATH = join(homedir(), ".boboddy.json");
|
|
146
|
+
var EMPTY_AUTH_FILE = {
|
|
147
|
+
profiles: {}
|
|
148
|
+
};
|
|
149
|
+
function isAuthProfile(value) {
|
|
150
|
+
if (typeof value !== "object" || value === null)
|
|
151
|
+
return false;
|
|
152
|
+
const obj = value;
|
|
153
|
+
if (typeof obj["accessToken"] !== "string")
|
|
154
|
+
return false;
|
|
155
|
+
for (const key of ["userId", "email", "name"]) {
|
|
156
|
+
const v = obj[key];
|
|
157
|
+
if (v !== undefined && typeof v !== "string")
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
function isAuthFile(value) {
|
|
163
|
+
if (typeof value !== "object" || value === null)
|
|
164
|
+
return false;
|
|
165
|
+
const profiles = value["profiles"];
|
|
166
|
+
if (typeof profiles !== "object" || profiles === null)
|
|
167
|
+
return false;
|
|
168
|
+
for (const profile of Object.values(profiles)) {
|
|
169
|
+
if (!isAuthProfile(profile))
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
var ensureFilePermissions = (filePath) => {
|
|
175
|
+
try {
|
|
176
|
+
chmodSync(filePath, 384);
|
|
177
|
+
} catch {}
|
|
178
|
+
};
|
|
179
|
+
var getAuthFilePath = () => AUTH_FILE_PATH;
|
|
180
|
+
var loadAuthFileFromPath = (filePath) => {
|
|
181
|
+
if (!existsSync(filePath))
|
|
182
|
+
return EMPTY_AUTH_FILE;
|
|
183
|
+
if (!lstatSync(filePath).isFile())
|
|
184
|
+
return EMPTY_AUTH_FILE;
|
|
185
|
+
const content = readFileSync(filePath, "utf8");
|
|
186
|
+
if (content.trim().length === 0)
|
|
187
|
+
return EMPTY_AUTH_FILE;
|
|
188
|
+
let parsed;
|
|
189
|
+
try {
|
|
190
|
+
parsed = JSON.parse(content);
|
|
191
|
+
} catch {
|
|
192
|
+
return EMPTY_AUTH_FILE;
|
|
193
|
+
}
|
|
194
|
+
if (!isAuthFile(parsed))
|
|
195
|
+
return EMPTY_AUTH_FILE;
|
|
196
|
+
return parsed;
|
|
197
|
+
};
|
|
198
|
+
var loadAuthFile = () => {
|
|
199
|
+
if (existsSync(AUTH_FILE_PATH)) {
|
|
200
|
+
return loadAuthFileFromPath(AUTH_FILE_PATH);
|
|
201
|
+
}
|
|
202
|
+
return loadAuthFileFromPath(LEGACY_AUTH_FILE_PATH);
|
|
203
|
+
};
|
|
204
|
+
var writeAuthFile = (data) => {
|
|
205
|
+
const parentDirectory = dirname(AUTH_FILE_PATH);
|
|
206
|
+
if (!existsSync(parentDirectory)) {
|
|
207
|
+
mkdirSync(parentDirectory, { recursive: true });
|
|
208
|
+
}
|
|
209
|
+
const temporaryPath = `${AUTH_FILE_PATH}.${String(process.pid)}.${String(Date.now())}.tmp`;
|
|
210
|
+
writeFileSync(temporaryPath, `${JSON.stringify(data, null, 2)}
|
|
211
|
+
`, { encoding: "utf8", mode: 384 });
|
|
212
|
+
ensureFilePermissions(temporaryPath);
|
|
213
|
+
renameSync(temporaryPath, AUTH_FILE_PATH);
|
|
214
|
+
ensureFilePermissions(AUTH_FILE_PATH);
|
|
215
|
+
};
|
|
216
|
+
var loadAuthProfile = (baseUrl) => {
|
|
217
|
+
const authFile = loadAuthFile();
|
|
218
|
+
return authFile.profiles[baseUrl] ?? null;
|
|
219
|
+
};
|
|
220
|
+
var saveAuthProfile = (baseUrl, profile) => {
|
|
221
|
+
const authFile = loadAuthFile();
|
|
222
|
+
authFile.profiles[baseUrl] = profile;
|
|
223
|
+
writeAuthFile(authFile);
|
|
224
|
+
};
|
|
225
|
+
var deleteAuthProfile = (baseUrl) => {
|
|
226
|
+
const authFile = loadAuthFile();
|
|
227
|
+
if (!(baseUrl in authFile.profiles))
|
|
228
|
+
return;
|
|
229
|
+
const remainingProfiles = Object.fromEntries(Object.entries(authFile.profiles).filter(([profileBaseUrl]) => profileBaseUrl !== baseUrl));
|
|
230
|
+
if (Object.keys(remainingProfiles).length === 0) {
|
|
231
|
+
rmSync(AUTH_FILE_PATH, { force: true });
|
|
232
|
+
if (existsSync(LEGACY_AUTH_FILE_PATH) && lstatSync(LEGACY_AUTH_FILE_PATH).isFile()) {
|
|
233
|
+
rmSync(LEGACY_AUTH_FILE_PATH, { force: true });
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
writeAuthFile({ profiles: remainingProfiles });
|
|
238
|
+
};
|
|
239
|
+
// src/defaults/base-url.ts
|
|
240
|
+
var DEFAULT_BASE_URL = "https://app.boboddy.dev";
|
|
241
|
+
var trimTrailingSlashes = (value) => {
|
|
242
|
+
let endIndex = value.length;
|
|
243
|
+
while (endIndex > 0 && value.charAt(endIndex - 1) === "/") {
|
|
244
|
+
endIndex -= 1;
|
|
245
|
+
}
|
|
246
|
+
return value.slice(0, endIndex);
|
|
247
|
+
};
|
|
248
|
+
var resolveBoboddyBaseUrl = (value) => {
|
|
249
|
+
const trimmedValue = value?.trim();
|
|
250
|
+
if (trimmedValue)
|
|
251
|
+
return trimTrailingSlashes(trimmedValue);
|
|
252
|
+
const envValue = process.env["BOBODDY_BASE_URL"]?.trim();
|
|
253
|
+
if (envValue)
|
|
254
|
+
return trimTrailingSlashes(envValue);
|
|
255
|
+
return DEFAULT_BASE_URL;
|
|
256
|
+
};
|
|
257
|
+
// src/defaults/project-config.ts
|
|
258
|
+
import { readFile } from "fs/promises";
|
|
259
|
+
import path from "path";
|
|
260
|
+
var BOBODDY_DIR = ".boboddy";
|
|
261
|
+
var CONFIG_FILENAME = "boboddy.jsonc";
|
|
262
|
+
var PROJECT_CONFIG_RELATIVE_PATH = path.join(BOBODDY_DIR, CONFIG_FILENAME);
|
|
263
|
+
function getConfigPath(rootDir) {
|
|
264
|
+
return path.join(rootDir, PROJECT_CONFIG_RELATIVE_PATH);
|
|
265
|
+
}
|
|
266
|
+
function isProjectConfig(value) {
|
|
267
|
+
if (typeof value !== "object" || value === null)
|
|
268
|
+
return false;
|
|
269
|
+
return typeof value["projectId"] === "string";
|
|
270
|
+
}
|
|
271
|
+
async function loadProjectConfig(rootDir = process.cwd()) {
|
|
272
|
+
try {
|
|
273
|
+
const content = await readFile(getConfigPath(rootDir), "utf8");
|
|
274
|
+
const parsed = parseJsonc(content);
|
|
275
|
+
return isProjectConfig(parsed) ? parsed : null;
|
|
276
|
+
} catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// src/defaults/load-push-defaults.ts
|
|
281
|
+
import path2 from "path";
|
|
282
|
+
async function findProjectConfigUpwards(startDir) {
|
|
283
|
+
let current = path2.resolve(startDir);
|
|
284
|
+
while (true) {
|
|
285
|
+
const found = await loadProjectConfig(current);
|
|
286
|
+
if (found)
|
|
287
|
+
return found;
|
|
288
|
+
const parent = path2.dirname(current);
|
|
289
|
+
if (parent === current)
|
|
290
|
+
return null;
|
|
291
|
+
current = parent;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async function loadPushDefaults(opts) {
|
|
295
|
+
const baseUrl = resolveBoboddyBaseUrl();
|
|
296
|
+
const envProjectId = process.env["BOBODDY_PROJECT_ID"]?.trim();
|
|
297
|
+
const projectId = envProjectId && envProjectId.length > 0 ? envProjectId : (await findProjectConfigUpwards(opts.dir))?.projectId;
|
|
298
|
+
const envAccessToken = process.env["BOBODDY_ACCESS_TOKEN"]?.trim();
|
|
299
|
+
const accessToken = envAccessToken && envAccessToken.length > 0 ? envAccessToken : loadAuthProfile(baseUrl)?.accessToken;
|
|
300
|
+
return { baseUrl, projectId, accessToken };
|
|
301
|
+
}
|
|
302
|
+
export {
|
|
303
|
+
saveAuthProfile,
|
|
304
|
+
resolveBoboddyBaseUrl,
|
|
305
|
+
loadPushDefaults,
|
|
306
|
+
loadProjectConfig,
|
|
307
|
+
loadAuthProfile,
|
|
308
|
+
loadAuthFile,
|
|
309
|
+
getAuthFilePath,
|
|
310
|
+
deleteAuthProfile,
|
|
311
|
+
PROJECT_CONFIG_RELATIVE_PATH
|
|
312
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface PushDefaults {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
projectId: string | undefined;
|
|
4
|
+
accessToken: string | undefined;
|
|
5
|
+
}
|
|
6
|
+
export interface LoadPushDefaultsOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Directory the script is running from. The project config (`.boboddy/boboddy.jsonc`)
|
|
9
|
+
* is searched for in this directory and each ancestor directory.
|
|
10
|
+
*/
|
|
11
|
+
dir: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Reads the conventional defaults for a `boboddy pipelines push` run:
|
|
15
|
+
* - `baseUrl`: from `BOBODDY_BASE_URL` env var or the built-in default.
|
|
16
|
+
* - `projectId`: from `BOBODDY_PROJECT_ID` env var or the nearest ancestor
|
|
17
|
+
* `.boboddy/boboddy.jsonc` of `opts.dir`.
|
|
18
|
+
* - `accessToken`: from `BOBODDY_ACCESS_TOKEN` env var or the saved auth
|
|
19
|
+
* profile for the resolved `baseUrl`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadPushDefaults(opts: LoadPushDefaultsOptions): Promise<PushDefaults>;
|
|
@@ -34,6 +34,10 @@ type ComputedOptions = {
|
|
|
34
34
|
* @example
|
|
35
35
|
* Rule.when(Computed.sum(["success"]), "greaterThan", 1, "continue")
|
|
36
36
|
* Rule.signal(Computed.average(["score"]), "greaterThanInclusive", 80)
|
|
37
|
+
*
|
|
38
|
+
* For new code, prefer the equivalent factories on the `.advance()` callback
|
|
39
|
+
* context of the `pipeline()` builder (`avg`, `sum`, `min`, `max`, …) — they
|
|
40
|
+
* return chainable `SignalRef`s and infer signal keys from the current step.
|
|
37
41
|
*/
|
|
38
42
|
export declare const Computed: {
|
|
39
43
|
readonly average: <const TInputs extends readonly [string, string, ...string[]]>(inputSignalKeys: TInputs, options?: ComputedOptions) => InlineComputedSignal<`average_${Join<TInputs, "_">}`, TInputs[number]>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type AdvancementOutcome, type Rule, type RuleCondition, type RouteOutcome, type SignalCondition } from "./define-advancement-policy";
|
|
2
|
+
declare const LEAF_BRAND: unique symbol;
|
|
3
|
+
declare const GROUP_BRAND: unique symbol;
|
|
4
|
+
declare const SIGNAL_KEY: unique symbol;
|
|
5
|
+
/**
|
|
6
|
+
* A leaf condition + outcome attacher. Built by `signal(key).<op>(value)` or
|
|
7
|
+
* any computed factory chained with a comparator. Nestable inside `all()` /
|
|
8
|
+
* `any()`, or terminable with `.then(outcome)`.
|
|
9
|
+
*/
|
|
10
|
+
export interface RuleLeaf<TSignalKeys extends string = string> {
|
|
11
|
+
readonly [LEAF_BRAND]: SignalCondition<TSignalKeys>;
|
|
12
|
+
then(outcome: AdvancementOutcome): Rule<TSignalKeys>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A grouped condition + outcome attacher. Built by `all(...)` or `any(...)`.
|
|
16
|
+
* Nestable inside another group, or terminable with `.then(outcome)`.
|
|
17
|
+
*/
|
|
18
|
+
export interface RuleGroup<TSignalKeys extends string = string> {
|
|
19
|
+
readonly [GROUP_BRAND]: {
|
|
20
|
+
mode: "all" | "any";
|
|
21
|
+
conditions: RuleCondition<TSignalKeys>[];
|
|
22
|
+
};
|
|
23
|
+
then(outcome: AdvancementOutcome): Rule<TSignalKeys>;
|
|
24
|
+
}
|
|
25
|
+
type Nestable<K extends string> = RuleLeaf<K> | RuleGroup<K>;
|
|
26
|
+
/**
|
|
27
|
+
* A comparator-bound signal reference. Returned by `signal(key)` and by every
|
|
28
|
+
* computed factory (`avg`, `sum`, etc.). Calling any comparator (`gte`, `eq`,
|
|
29
|
+
* …) produces a `RuleLeaf`.
|
|
30
|
+
*
|
|
31
|
+
* `TValue` is the TypeScript type of the signal's value, enabling type-safe
|
|
32
|
+
* comparisons (e.g. `eq(true)` errors when the signal is a string).
|
|
33
|
+
*/
|
|
34
|
+
export interface SignalRef<TSignalKeys extends string = string, TValue = unknown> {
|
|
35
|
+
eq(value: TValue): RuleLeaf<TSignalKeys>;
|
|
36
|
+
ne(value: TValue): RuleLeaf<TSignalKeys>;
|
|
37
|
+
gt(value: number): RuleLeaf<TSignalKeys>;
|
|
38
|
+
gte(value: number): RuleLeaf<TSignalKeys>;
|
|
39
|
+
lt(value: number): RuleLeaf<TSignalKeys>;
|
|
40
|
+
lte(value: number): RuleLeaf<TSignalKeys>;
|
|
41
|
+
in(values: ReadonlyArray<TValue>): RuleLeaf<TSignalKeys>;
|
|
42
|
+
notIn(values: ReadonlyArray<TValue>): RuleLeaf<TSignalKeys>;
|
|
43
|
+
contains(value: unknown): RuleLeaf<TSignalKeys>;
|
|
44
|
+
doesNotContain(value: unknown): RuleLeaf<TSignalKeys>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A `SignalRef` backed by a plain signal key (not a computed token). The
|
|
48
|
+
* embedded `SIGNAL_KEY` brand lets computed factories extract the key at
|
|
49
|
+
* runtime so callers can pass `stepSignals.foo` instead of the string `"foo"`.
|
|
50
|
+
*/
|
|
51
|
+
export interface KeyedSignalRef<TSignalKeys extends string = string, TValue = unknown> extends SignalRef<TSignalKeys, TValue> {
|
|
52
|
+
readonly [SIGNAL_KEY]: TSignalKeys;
|
|
53
|
+
}
|
|
54
|
+
type ComputedTuple<K extends string, TValue = unknown> = [
|
|
55
|
+
KeyedSignalRef<K, TValue>,
|
|
56
|
+
KeyedSignalRef<K, TValue>,
|
|
57
|
+
...KeyedSignalRef<K, TValue>[]
|
|
58
|
+
];
|
|
59
|
+
type SignalValue<TSignalKeys extends string, TSignalTypeMap extends Partial<Record<string, unknown>>, K extends TSignalKeys> = K extends keyof TSignalTypeMap ? TSignalTypeMap[K] : unknown;
|
|
60
|
+
export interface AdvanceCtx<TSignalKeys extends string = string, TSignalTypeMap extends Partial<Record<string, unknown>> = Record<string, unknown>> {
|
|
61
|
+
signal<K extends TSignalKeys>(key: K): KeyedSignalRef<TSignalKeys, SignalValue<TSignalKeys, TSignalTypeMap, K>>;
|
|
62
|
+
stepSignals: {
|
|
63
|
+
[K in TSignalKeys]: KeyedSignalRef<TSignalKeys, SignalValue<TSignalKeys, TSignalTypeMap, K>>;
|
|
64
|
+
};
|
|
65
|
+
avg(...args: ComputedTuple<TSignalKeys, number>): SignalRef<TSignalKeys>;
|
|
66
|
+
weightedAvg(...args: ComputedTuple<TSignalKeys, number>): SignalRef<TSignalKeys>;
|
|
67
|
+
sum(...args: ComputedTuple<TSignalKeys, number>): SignalRef<TSignalKeys>;
|
|
68
|
+
min(...args: ComputedTuple<TSignalKeys, number>): SignalRef<TSignalKeys>;
|
|
69
|
+
max(...args: ComputedTuple<TSignalKeys, number>): SignalRef<TSignalKeys>;
|
|
70
|
+
count(...args: ComputedTuple<TSignalKeys>): SignalRef<TSignalKeys>;
|
|
71
|
+
booleanAny(...args: ComputedTuple<TSignalKeys, boolean>): SignalRef<TSignalKeys>;
|
|
72
|
+
booleanAll(...args: ComputedTuple<TSignalKeys, boolean>): SignalRef<TSignalKeys>;
|
|
73
|
+
all(...refs: Nestable<TSignalKeys>[]): RuleGroup<TSignalKeys>;
|
|
74
|
+
any(...refs: Nestable<TSignalKeys>[]): RuleGroup<TSignalKeys>;
|
|
75
|
+
route(pipelineKey: string, inputJson?: Record<string, unknown>): RouteOutcome;
|
|
76
|
+
}
|
|
77
|
+
export interface AdvanceResult<TSignalKeys extends string = string> {
|
|
78
|
+
default: AdvancementOutcome;
|
|
79
|
+
rules?: Rule<TSignalKeys>[];
|
|
80
|
+
}
|
|
81
|
+
export declare function makeAdvanceCtx<TSignalKeys extends string, TSignalTypeMap extends Partial<Record<string, unknown>> = Record<string, unknown>>(): AdvanceCtx<TSignalKeys, TSignalTypeMap>;
|
|
82
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @bun
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __returnValue = (v) => v;
|
|
3
4
|
function __exportSetter(name, newValue) {
|
|
@@ -156,8 +157,94 @@ function extractInlineComputedSignals(policy) {
|
|
|
156
157
|
}
|
|
157
158
|
return [...byKey.values()];
|
|
158
159
|
}
|
|
160
|
+
// src/definitions/advancement-policies/fluent-rules.ts
|
|
161
|
+
var LEAF_BRAND = Symbol.for("boboddy.fluentRule.leaf");
|
|
162
|
+
var GROUP_BRAND = Symbol.for("boboddy.fluentRule.group");
|
|
163
|
+
var SIGNAL_KEY = Symbol.for("boboddy.fluentRule.signalKey");
|
|
164
|
+
function createSignalRef(signal2) {
|
|
165
|
+
const leaf = (operator, value) => {
|
|
166
|
+
const condition = {
|
|
167
|
+
_tag: "signal",
|
|
168
|
+
signal: signal2,
|
|
169
|
+
operator,
|
|
170
|
+
value
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
[LEAF_BRAND]: condition,
|
|
174
|
+
then(outcome) {
|
|
175
|
+
return {
|
|
176
|
+
_tag: "rule",
|
|
177
|
+
mode: "all",
|
|
178
|
+
conditions: [condition],
|
|
179
|
+
outcome
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
eq: (v) => leaf("equal", v),
|
|
186
|
+
ne: (v) => leaf("notEqual", v),
|
|
187
|
+
gt: (v) => leaf("greaterThan", v),
|
|
188
|
+
gte: (v) => leaf("greaterThanInclusive", v),
|
|
189
|
+
lt: (v) => leaf("lessThan", v),
|
|
190
|
+
lte: (v) => leaf("lessThanInclusive", v),
|
|
191
|
+
in: (vs) => leaf("in", vs),
|
|
192
|
+
notIn: (vs) => leaf("notIn", vs),
|
|
193
|
+
contains: (v) => leaf("contains", v),
|
|
194
|
+
doesNotContain: (v) => leaf("doesNotContain", v)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function extractCondition(ref) {
|
|
198
|
+
if (LEAF_BRAND in ref)
|
|
199
|
+
return ref[LEAF_BRAND];
|
|
200
|
+
const group = ref[GROUP_BRAND];
|
|
201
|
+
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
202
|
+
}
|
|
203
|
+
function createGroup(mode, refs) {
|
|
204
|
+
const conditions = refs.map(extractCondition);
|
|
205
|
+
return {
|
|
206
|
+
[GROUP_BRAND]: { mode, conditions },
|
|
207
|
+
then(outcome) {
|
|
208
|
+
return { _tag: "rule", mode, conditions, outcome };
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function resolveComputedArg(arg) {
|
|
213
|
+
return arg[SIGNAL_KEY];
|
|
214
|
+
}
|
|
215
|
+
function makeKeyedSignalRef(key) {
|
|
216
|
+
const ref = createSignalRef(key);
|
|
217
|
+
return Object.assign(ref, {
|
|
218
|
+
[SIGNAL_KEY]: key
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function makeAdvanceCtx() {
|
|
222
|
+
const wrapComputed = (token) => createSignalRef(token);
|
|
223
|
+
return {
|
|
224
|
+
signal: (key) => makeKeyedSignalRef(key),
|
|
225
|
+
stepSignals: new Proxy({}, {
|
|
226
|
+
get(_, key) {
|
|
227
|
+
if (typeof key === "string")
|
|
228
|
+
return makeKeyedSignalRef(key);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}),
|
|
232
|
+
avg: (...args) => wrapComputed(Computed.average(args.map(resolveComputedArg))),
|
|
233
|
+
weightedAvg: (...args) => wrapComputed(Computed.weightedAverage(args.map(resolveComputedArg))),
|
|
234
|
+
sum: (...args) => wrapComputed(Computed.sum(args.map(resolveComputedArg))),
|
|
235
|
+
min: (...args) => wrapComputed(Computed.min(args.map(resolveComputedArg))),
|
|
236
|
+
max: (...args) => wrapComputed(Computed.max(args.map(resolveComputedArg))),
|
|
237
|
+
count: (...args) => wrapComputed(Computed.count(args.map(resolveComputedArg))),
|
|
238
|
+
booleanAny: (...args) => wrapComputed(Computed.booleanAny(args.map(resolveComputedArg))),
|
|
239
|
+
booleanAll: (...args) => wrapComputed(Computed.booleanAll(args.map(resolveComputedArg))),
|
|
240
|
+
all: (...refs) => createGroup("all", refs),
|
|
241
|
+
any: (...refs) => createGroup("any", refs),
|
|
242
|
+
route: (pipelineKey, inputJson) => inputJson !== undefined ? { outcome: "route", pipelineKey, inputJson } : { outcome: "route", pipelineKey }
|
|
243
|
+
};
|
|
244
|
+
}
|
|
159
245
|
export {
|
|
160
246
|
serializeAdvancementPolicy,
|
|
247
|
+
makeAdvanceCtx,
|
|
161
248
|
extractInlineComputedSignals,
|
|
162
249
|
Rule,
|
|
163
250
|
Computed
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z, type ZodType } from "zod";
|
|
2
|
+
import type { TypedStepDefinitionSpec } from "../steps/define-step";
|
|
3
|
+
import { type AdvanceCtx, type AdvanceResult } from "../advancement-policies/fluent-rules";
|
|
4
|
+
import { type AnyBinding, type PipelineDefinitionSpec, type PipelineStepConfig, type StepOutputBinding, type StepSignalBinding, type WorkItemBinding } from "./define-pipeline";
|
|
5
|
+
import { type InputAccessor } from "./input-accessor";
|
|
6
|
+
type AnyTypedStep = TypedStepDefinitionSpec<any, any, any, any>;
|
|
7
|
+
export type StepConfig = {
|
|
8
|
+
timeout?: number | null;
|
|
9
|
+
};
|
|
10
|
+
type ElementOf<T extends ReadonlyArray<unknown>> = T extends ReadonlyArray<infer U> ? U : never;
|
|
11
|
+
type LastStep<T extends ReadonlyArray<AnyTypedStep>> = T extends readonly [
|
|
12
|
+
...AnyTypedStep[],
|
|
13
|
+
infer L
|
|
14
|
+
] ? L extends AnyTypedStep ? L : never : never;
|
|
15
|
+
type LastSignalKeys<T extends ReadonlyArray<AnyTypedStep>> = LastStep<T> extends AnyTypedStep ? LastStep<T>["__signalKeys"] : never;
|
|
16
|
+
type LastSignalTypeMap<T extends ReadonlyArray<AnyTypedStep>> = LastStep<T> extends AnyTypedStep ? LastStep<T>["__signalTypeMap"] : Record<string, unknown>;
|
|
17
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
18
|
+
type RequiredInputKeys<T extends object> = {
|
|
19
|
+
[K in keyof T & string]-?: undefined extends T[K] ? never : K;
|
|
20
|
+
}[keyof T & string];
|
|
21
|
+
type OptionalInputKeys<T extends object> = {
|
|
22
|
+
[K in keyof T & string]-?: undefined extends T[K] ? K : never;
|
|
23
|
+
}[keyof T & string];
|
|
24
|
+
type StepInputMapping<S extends AnyTypedStep> = IsAny<S["__inputType"]> extends true ? Partial<Record<string, AnyBinding>> : S["__inputType"] extends object ? {
|
|
25
|
+
[K in RequiredInputKeys<S["__inputType"]>]: AnyBinding;
|
|
26
|
+
} & {
|
|
27
|
+
[K in OptionalInputKeys<S["__inputType"]>]?: AnyBinding;
|
|
28
|
+
} : Partial<Record<string, AnyBinding>>;
|
|
29
|
+
export type WorkItemAccessor = {
|
|
30
|
+
readonly title: WorkItemBinding;
|
|
31
|
+
readonly description: WorkItemBinding;
|
|
32
|
+
};
|
|
33
|
+
export type StepInputCtx<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>> = {
|
|
34
|
+
input: InputAccessor<TInput["_output"]>;
|
|
35
|
+
workItem: WorkItemAccessor;
|
|
36
|
+
signal: <S extends ElementOf<TSteps>>(step: S, key: S["__signalKeys"]) => StepSignalBinding;
|
|
37
|
+
output: <S extends ElementOf<TSteps>>(step: S) => StepOutputBinding;
|
|
38
|
+
};
|
|
39
|
+
export type PipelineMeta<TInput extends ZodType = z.ZodUnknown> = {
|
|
40
|
+
key: string;
|
|
41
|
+
name: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
version?: number;
|
|
44
|
+
status?: "draft" | "active";
|
|
45
|
+
input?: TInput;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Returned by `.step()`. Requires `.advance()` before the pipeline can
|
|
49
|
+
* continue. Also accepts `.timeout()` before `.advance()`.
|
|
50
|
+
*/
|
|
51
|
+
export declare class PipelineStepAdvancementBuilder<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>> {
|
|
52
|
+
protected readonly inputSchema: TInput;
|
|
53
|
+
protected readonly meta: Omit<PipelineMeta<TInput>, "input">;
|
|
54
|
+
protected readonly steps: PipelineStepConfig[];
|
|
55
|
+
readonly __steps: TSteps;
|
|
56
|
+
constructor(inputSchema: TInput, meta: Omit<PipelineMeta<TInput>, "input">, steps: PipelineStepConfig[]);
|
|
57
|
+
advance(callback: (ctx: AdvanceCtx<LastSignalKeys<TSteps>, LastSignalTypeMap<TSteps>>) => AdvanceResult<LastSignalKeys<TSteps>>): PipelineStepBuilder<TInput, TSteps>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returned by `.advance()`. Provides `.step()` to chain additional steps,
|
|
61
|
+
* `.timeout()` to set timeout after advancing, and `.build()` to finalize.
|
|
62
|
+
*/
|
|
63
|
+
export declare class PipelineStepBuilder<TInput extends ZodType, TSteps extends ReadonlyArray<AnyTypedStep>> {
|
|
64
|
+
protected readonly inputSchema: TInput;
|
|
65
|
+
protected readonly meta: Omit<PipelineMeta<TInput>, "input">;
|
|
66
|
+
protected readonly steps: PipelineStepConfig[];
|
|
67
|
+
readonly __steps: TSteps;
|
|
68
|
+
constructor(inputSchema: TInput, meta: Omit<PipelineMeta<TInput>, "input">, steps: PipelineStepConfig[]);
|
|
69
|
+
step<S extends AnyTypedStep>(step: S, mapper: (ctx: StepInputCtx<TInput, TSteps>) => StepInputMapping<S>, configFn?: (config: StepConfig) => void): PipelineStepAdvancementBuilder<TInput, [...TSteps, S]>;
|
|
70
|
+
build(): PipelineDefinitionSpec;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Entry-point builder returned by `pipeline()`. Only exposes `.step()` —
|
|
74
|
+
* call that to receive a `PipelineStepAdvancementBuilder` which requires
|
|
75
|
+
* `.advance()` before the pipeline can proceed.
|
|
76
|
+
*/
|
|
77
|
+
export declare class PipelineBuilder<TInput extends ZodType> {
|
|
78
|
+
private readonly inputSchema;
|
|
79
|
+
private readonly meta;
|
|
80
|
+
private readonly steps;
|
|
81
|
+
constructor(meta: PipelineMeta<TInput>);
|
|
82
|
+
step<S extends AnyTypedStep>(step: S, mapper: (ctx: StepInputCtx<TInput, []>) => StepInputMapping<S>, configFn?: (config: StepConfig) => void): PipelineStepAdvancementBuilder<TInput, [S]>;
|
|
83
|
+
}
|
|
84
|
+
export declare function pipeline<TInput extends ZodType = z.ZodUnknown>(meta: PipelineMeta<TInput>): PipelineBuilder<TInput>;
|
|
85
|
+
export {};
|