@kici-dev/sdk 0.0.0 → 0.1.1
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/LICENSE +202 -0
- package/README.md +1 -6
- package/dist/api-types.d.ts +47 -0
- package/dist/api-types.js +15 -0
- package/dist/chunk-gOLHoazu.js +4 -0
- package/dist/context.d.ts +185 -0
- package/dist/context.js +2 -0
- package/dist/dynamic-group.d.ts +29 -0
- package/dist/dynamic-group.js +34 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +17 -0
- package/dist/events/define-event.d.ts +32 -0
- package/dist/events/define-event.js +29 -0
- package/dist/events/event-payloads.d.ts +396 -0
- package/dist/events/event-payloads.js +21 -0
- package/dist/events/index.d.ts +6 -0
- package/dist/events/index.js +4 -0
- package/dist/events/types.d.ts +13 -0
- package/dist/events/types.js +2 -0
- package/dist/fixture.d.ts +50 -0
- package/dist/fixture.js +35 -0
- package/dist/hooks/index.d.ts +33 -0
- package/dist/hooks/index.js +94 -0
- package/dist/hooks/types.d.ts +35 -0
- package/dist/hooks/types.js +2 -0
- package/dist/idempotent.d.ts +71 -0
- package/dist/idempotent.js +77 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +50 -0
- package/dist/job.d.ts +31 -0
- package/dist/job.js +46 -0
- package/dist/matrix/expand.d.ts +32 -0
- package/dist/matrix/expand.js +77 -0
- package/dist/matrix/index.d.ts +4 -0
- package/dist/matrix/index.js +4 -0
- package/dist/matrix/types.d.ts +60 -0
- package/dist/matrix/types.js +18 -0
- package/dist/outputs.d.ts +86 -0
- package/dist/outputs.js +180 -0
- package/dist/rules/evaluator.d.ts +24 -0
- package/dist/rules/evaluator.js +52 -0
- package/dist/rules/index.d.ts +6 -0
- package/dist/rules/index.js +5 -0
- package/dist/rules/rule.d.ts +34 -0
- package/dist/rules/rule.js +37 -0
- package/dist/rules/types.d.ts +42 -0
- package/dist/rules/types.js +2 -0
- package/dist/secrets.d.ts +192 -0
- package/dist/secrets.js +132 -0
- package/dist/step.d.ts +50 -0
- package/dist/step.js +78 -0
- package/dist/triggers/comment.d.ts +20 -0
- package/dist/triggers/comment.js +47 -0
- package/dist/triggers/create.d.ts +20 -0
- package/dist/triggers/create.js +32 -0
- package/dist/triggers/delete.d.ts +20 -0
- package/dist/triggers/delete.js +29 -0
- package/dist/triggers/dispatch.d.ts +17 -0
- package/dist/triggers/dispatch.js +27 -0
- package/dist/triggers/fork.d.ts +17 -0
- package/dist/triggers/fork.js +26 -0
- package/dist/triggers/generic-webhook.d.ts +32 -0
- package/dist/triggers/generic-webhook.js +45 -0
- package/dist/triggers/index.d.ts +28 -0
- package/dist/triggers/index.js +25 -0
- package/dist/triggers/job-complete.d.ts +23 -0
- package/dist/triggers/job-complete.js +33 -0
- package/dist/triggers/kici-event.d.ts +23 -0
- package/dist/triggers/kici-event.js +34 -0
- package/dist/triggers/lifecycle.d.ts +20 -0
- package/dist/triggers/lifecycle.js +29 -0
- package/dist/triggers/pr.d.ts +27 -0
- package/dist/triggers/pr.js +48 -0
- package/dist/triggers/push.d.ts +29 -0
- package/dist/triggers/push.js +48 -0
- package/dist/triggers/release.d.ts +17 -0
- package/dist/triggers/release.js +27 -0
- package/dist/triggers/review-comment.d.ts +17 -0
- package/dist/triggers/review-comment.js +27 -0
- package/dist/triggers/review.d.ts +17 -0
- package/dist/triggers/review.js +28 -0
- package/dist/triggers/schedule.d.ts +20 -0
- package/dist/triggers/schedule.js +29 -0
- package/dist/triggers/star.d.ts +17 -0
- package/dist/triggers/star.js +27 -0
- package/dist/triggers/status.d.ts +17 -0
- package/dist/triggers/status.js +28 -0
- package/dist/triggers/tag.d.ts +20 -0
- package/dist/triggers/tag.js +31 -0
- package/dist/triggers/types.d.ts +529 -0
- package/dist/triggers/types.js +36 -0
- package/dist/triggers/watch.d.ts +17 -0
- package/dist/triggers/watch.js +27 -0
- package/dist/triggers/webhook.d.ts +19 -0
- package/dist/triggers/webhook.js +29 -0
- package/dist/triggers/workflow-complete.d.ts +23 -0
- package/dist/triggers/workflow-complete.js +32 -0
- package/dist/triggers/workflow-run.d.ts +17 -0
- package/dist/triggers/workflow-run.js +29 -0
- package/dist/types.d.ts +483 -0
- package/dist/types.js +35 -0
- package/dist/validation/dag.d.ts +42 -0
- package/dist/validation/dag.js +70 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.js +3 -0
- package/dist/wait-for.d.ts +109 -0
- package/dist/wait-for.js +144 -0
- package/dist/workflow.d.ts +3 -0
- package/dist/workflow.js +83 -0
- package/package.json +42 -5
- package/sbom.spdx.json +9150 -0
- package/index.js +0 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/validation/dag.ts
|
|
3
|
+
/**
|
|
4
|
+
* Validate a DAG for common issues: self-references, missing dependencies, and cycles.
|
|
5
|
+
*
|
|
6
|
+
* Validation order:
|
|
7
|
+
* 1. Check for self-references (node depends on itself)
|
|
8
|
+
* 2. Check for missing dependencies (depends on non-existent node)
|
|
9
|
+
* 3. Check for cycles using Kahn's algorithm
|
|
10
|
+
*
|
|
11
|
+
* @param nodes - Array of DAG nodes to validate
|
|
12
|
+
* @returns Validation result with error details or sorted order
|
|
13
|
+
*/
|
|
14
|
+
function validateDag(nodes) {
|
|
15
|
+
if (nodes.length === 0) return {
|
|
16
|
+
valid: true,
|
|
17
|
+
sortedOrder: []
|
|
18
|
+
};
|
|
19
|
+
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
20
|
+
for (const node of nodes) if (node.needs.includes(node.id)) return {
|
|
21
|
+
valid: false,
|
|
22
|
+
error: "self-reference",
|
|
23
|
+
nodeId: node.id
|
|
24
|
+
};
|
|
25
|
+
for (const node of nodes) for (const dep of node.needs) if (!nodeIds.has(dep)) return {
|
|
26
|
+
valid: false,
|
|
27
|
+
error: "missing-dependency",
|
|
28
|
+
nodeId: node.id,
|
|
29
|
+
missingDep: dep
|
|
30
|
+
};
|
|
31
|
+
const adjacencyList = /* @__PURE__ */ new Map();
|
|
32
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const node of nodes) {
|
|
34
|
+
adjacencyList.set(node.id, []);
|
|
35
|
+
inDegree.set(node.id, 0);
|
|
36
|
+
}
|
|
37
|
+
for (const node of nodes) for (const dep of node.needs) {
|
|
38
|
+
adjacencyList.get(dep).push(node.id);
|
|
39
|
+
inDegree.set(node.id, (inDegree.get(node.id) ?? 0) + 1);
|
|
40
|
+
}
|
|
41
|
+
const queue = [];
|
|
42
|
+
for (const [id, degree] of inDegree) if (degree === 0) queue.push(id);
|
|
43
|
+
const sortedOrder = [];
|
|
44
|
+
while (queue.length > 0) {
|
|
45
|
+
const current = queue.shift();
|
|
46
|
+
sortedOrder.push(current);
|
|
47
|
+
for (const neighbor of adjacencyList.get(current) ?? []) {
|
|
48
|
+
const newDegree = (inDegree.get(neighbor) ?? 0) - 1;
|
|
49
|
+
inDegree.set(neighbor, newDegree);
|
|
50
|
+
if (newDegree === 0) queue.push(neighbor);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (sortedOrder.length !== nodes.length) {
|
|
54
|
+
const cycleNodes = [];
|
|
55
|
+
for (const [id, degree] of inDegree) if (degree > 0) cycleNodes.push(id);
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
error: "cycle",
|
|
59
|
+
nodesInCycle: cycleNodes
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
valid: true,
|
|
64
|
+
sortedOrder
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
export { validateDag };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=dag.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK wait-for helpers for workflow authors.
|
|
3
|
+
*
|
|
4
|
+
* Workflows often need to pause until an external condition becomes
|
|
5
|
+
* true — a resource finishes provisioning, a file appears on disk, an
|
|
6
|
+
* HTTP endpoint returns a healthy response, an external job reaches a
|
|
7
|
+
* terminal state. The helpers in this file express that pattern with
|
|
8
|
+
* the same shape as `idempotent` / `idempotentStep`:
|
|
9
|
+
*
|
|
10
|
+
* - `check()` returns the resolved value when the condition is met, or
|
|
11
|
+
* `null` to keep polling.
|
|
12
|
+
* - Optional `onSuccess(value)` runs once after the wait succeeds; its
|
|
13
|
+
* return value becomes the step result.
|
|
14
|
+
* - Optional `onTimeout({ elapsedMs, attempts })` runs if the deadline
|
|
15
|
+
* is exceeded; its return value becomes the step result. If absent,
|
|
16
|
+
* the helper throws a typed `WaitForTimeoutError` so the step fails.
|
|
17
|
+
* - Errors thrown by `check()` are swallowed by default (matching the
|
|
18
|
+
* "poll-until-healthy" pattern). Opt out via `swallowErrors: false`.
|
|
19
|
+
*
|
|
20
|
+
* Cancellation: the helper has no `AbortSignal` plumbing. A `check()`
|
|
21
|
+
* that takes longer than `intervalMs` is not aborted mid-flight; the
|
|
22
|
+
* loop only inspects the deadline at the top of each iteration. The
|
|
23
|
+
* step's own `timeout` field is the hard kill.
|
|
24
|
+
*
|
|
25
|
+
* Two entry points:
|
|
26
|
+
*
|
|
27
|
+
* - `waitFor(options)` — generic helper callable from any step, hook,
|
|
28
|
+
* or bare async function. Resolves to the discriminated result.
|
|
29
|
+
* - `waitForStep(name, options)` — factory returning an SDK `Step`
|
|
30
|
+
* whose run function executes `waitFor(...)` and propagates status
|
|
31
|
+
* lines into `ctx.log.info`.
|
|
32
|
+
*/
|
|
33
|
+
import type { Step } from './types.js';
|
|
34
|
+
export interface WaitForOptions<TValue, TSuccess = void, TTimeout = void> {
|
|
35
|
+
/** Optional name surfaced in status lines and the timeout error. */
|
|
36
|
+
name?: string;
|
|
37
|
+
/** Polled inspection. Return the resolved value when the condition
|
|
38
|
+
* is met, or `null` to keep polling. */
|
|
39
|
+
check: () => Promise<TValue | null>;
|
|
40
|
+
/** Time between successive `check()` invocations, in milliseconds.
|
|
41
|
+
* Defaults to 2000. */
|
|
42
|
+
intervalMs?: number;
|
|
43
|
+
/** Total time budget for the wait, in milliseconds. Defaults to
|
|
44
|
+
* 60000. The deadline is inspected at the top of each iteration. */
|
|
45
|
+
timeoutMs?: number;
|
|
46
|
+
/** Time to wait before the first `check()` invocation, in
|
|
47
|
+
* milliseconds. Defaults to 0. */
|
|
48
|
+
initialDelayMs?: number;
|
|
49
|
+
/** Optional success action. Runs once after `check()` returns a
|
|
50
|
+
* non-null value. Its return value is surfaced as `result` on the
|
|
51
|
+
* 'succeeded' outcome. */
|
|
52
|
+
onSuccess?: (value: TValue) => Promise<TSuccess>;
|
|
53
|
+
/** Optional timeout action. Runs when the deadline is exceeded. Its
|
|
54
|
+
* return value is surfaced as `result` on the 'timed-out' outcome.
|
|
55
|
+
* When omitted, the helper throws a `WaitForTimeoutError` instead. */
|
|
56
|
+
onTimeout?: (info: {
|
|
57
|
+
elapsedMs: number;
|
|
58
|
+
attempts: number;
|
|
59
|
+
}) => Promise<TTimeout>;
|
|
60
|
+
/** When `true` (default), errors thrown by `check()` are logged and
|
|
61
|
+
* swallowed so polling continues. When `false`, the first error
|
|
62
|
+
* propagates out of the helper. */
|
|
63
|
+
swallowErrors?: boolean;
|
|
64
|
+
/** Sink for status lines. Defaults to console.log. */
|
|
65
|
+
log?: (line: string) => void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The discriminated result of a `waitFor` invocation. Narrow on
|
|
69
|
+
* `outcome` to read the correct branch.
|
|
70
|
+
*/
|
|
71
|
+
export type WaitForResult<TValue, TSuccess = void, TTimeout = void> = {
|
|
72
|
+
outcome: 'succeeded';
|
|
73
|
+
value: TValue;
|
|
74
|
+
elapsedMs: number;
|
|
75
|
+
attempts: number;
|
|
76
|
+
result: TSuccess;
|
|
77
|
+
} | {
|
|
78
|
+
outcome: 'timed-out';
|
|
79
|
+
elapsedMs: number;
|
|
80
|
+
attempts: number;
|
|
81
|
+
result: TTimeout;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Thrown by `waitFor` when the deadline is exceeded and no
|
|
85
|
+
* `onTimeout` callback was supplied. The instance fields expose the
|
|
86
|
+
* wait stats so callers can branch on them in a catch block.
|
|
87
|
+
*/
|
|
88
|
+
export declare class WaitForTimeoutError extends Error {
|
|
89
|
+
/** The step name supplied via `WaitForOptions.name` (or 'waitFor'). */
|
|
90
|
+
readonly stepName: string;
|
|
91
|
+
/** Wall-clock duration of the wait, in milliseconds. */
|
|
92
|
+
readonly elapsedMs: number;
|
|
93
|
+
/** Number of `check()` invocations that ran before the timeout. */
|
|
94
|
+
readonly attempts: number;
|
|
95
|
+
constructor(stepName: string, elapsedMs: number, attempts: number);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generic wait-for helper for use inside any step, hook, or bare
|
|
99
|
+
* async function. Polls `check()` on a fixed interval until it returns
|
|
100
|
+
* a non-null value or the deadline is exceeded.
|
|
101
|
+
*/
|
|
102
|
+
export declare function waitFor<TValue, TSuccess = void, TTimeout = void>(opts: WaitForOptions<TValue, TSuccess, TTimeout>): Promise<WaitForResult<TValue, TSuccess, TTimeout>>;
|
|
103
|
+
/**
|
|
104
|
+
* Factory returning an SDK Step whose run function executes
|
|
105
|
+
* `waitFor(...)`. Status lines are routed through `ctx.log.info`.
|
|
106
|
+
* The step's typed return value is the WaitForResult union.
|
|
107
|
+
*/
|
|
108
|
+
export declare function waitForStep<TValue, TSuccess = void, TTimeout = void>(name: string, opts: Omit<WaitForOptions<TValue, TSuccess, TTimeout>, 'name' | 'log'>): Step<WaitForResult<TValue, TSuccess, TTimeout>>;
|
|
109
|
+
//# sourceMappingURL=wait-for.d.ts.map
|
package/dist/wait-for.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import "./chunk-gOLHoazu.js";
|
|
2
|
+
import { step } from "./step.js";
|
|
3
|
+
//#region src/wait-for.ts
|
|
4
|
+
/**
|
|
5
|
+
* SDK wait-for helpers for workflow authors.
|
|
6
|
+
*
|
|
7
|
+
* Workflows often need to pause until an external condition becomes
|
|
8
|
+
* true — a resource finishes provisioning, a file appears on disk, an
|
|
9
|
+
* HTTP endpoint returns a healthy response, an external job reaches a
|
|
10
|
+
* terminal state. The helpers in this file express that pattern with
|
|
11
|
+
* the same shape as `idempotent` / `idempotentStep`:
|
|
12
|
+
*
|
|
13
|
+
* - `check()` returns the resolved value when the condition is met, or
|
|
14
|
+
* `null` to keep polling.
|
|
15
|
+
* - Optional `onSuccess(value)` runs once after the wait succeeds; its
|
|
16
|
+
* return value becomes the step result.
|
|
17
|
+
* - Optional `onTimeout({ elapsedMs, attempts })` runs if the deadline
|
|
18
|
+
* is exceeded; its return value becomes the step result. If absent,
|
|
19
|
+
* the helper throws a typed `WaitForTimeoutError` so the step fails.
|
|
20
|
+
* - Errors thrown by `check()` are swallowed by default (matching the
|
|
21
|
+
* "poll-until-healthy" pattern). Opt out via `swallowErrors: false`.
|
|
22
|
+
*
|
|
23
|
+
* Cancellation: the helper has no `AbortSignal` plumbing. A `check()`
|
|
24
|
+
* that takes longer than `intervalMs` is not aborted mid-flight; the
|
|
25
|
+
* loop only inspects the deadline at the top of each iteration. The
|
|
26
|
+
* step's own `timeout` field is the hard kill.
|
|
27
|
+
*
|
|
28
|
+
* Two entry points:
|
|
29
|
+
*
|
|
30
|
+
* - `waitFor(options)` — generic helper callable from any step, hook,
|
|
31
|
+
* or bare async function. Resolves to the discriminated result.
|
|
32
|
+
* - `waitForStep(name, options)` — factory returning an SDK `Step`
|
|
33
|
+
* whose run function executes `waitFor(...)` and propagates status
|
|
34
|
+
* lines into `ctx.log.info`.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Thrown by `waitFor` when the deadline is exceeded and no
|
|
38
|
+
* `onTimeout` callback was supplied. The instance fields expose the
|
|
39
|
+
* wait stats so callers can branch on them in a catch block.
|
|
40
|
+
*/
|
|
41
|
+
var WaitForTimeoutError = class extends Error {
|
|
42
|
+
/** The step name supplied via `WaitForOptions.name` (or 'waitFor'). */
|
|
43
|
+
stepName;
|
|
44
|
+
/** Wall-clock duration of the wait, in milliseconds. */
|
|
45
|
+
elapsedMs;
|
|
46
|
+
/** Number of `check()` invocations that ran before the timeout. */
|
|
47
|
+
attempts;
|
|
48
|
+
constructor(stepName, elapsedMs, attempts) {
|
|
49
|
+
super(`${stepName}: timed out after ${elapsedMs}ms (attempts=${attempts})`);
|
|
50
|
+
this.name = "WaitForTimeoutError";
|
|
51
|
+
this.stepName = stepName;
|
|
52
|
+
this.elapsedMs = elapsedMs;
|
|
53
|
+
this.attempts = attempts;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const DEFAULT_NAME = "waitFor";
|
|
57
|
+
const DEFAULT_INTERVAL_MS = 2e3;
|
|
58
|
+
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
59
|
+
function sleep(ms) {
|
|
60
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
61
|
+
}
|
|
62
|
+
async function runCheckOnce(check, swallowErrors, log, name, attempt) {
|
|
63
|
+
try {
|
|
64
|
+
return { value: await check() };
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (!swallowErrors) throw err;
|
|
67
|
+
log(`! ${name} — attempt ${attempt} error swallowed: ${err instanceof Error ? err.message : String(err)}`);
|
|
68
|
+
return { value: null };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generic wait-for helper for use inside any step, hook, or bare
|
|
73
|
+
* async function. Polls `check()` on a fixed interval until it returns
|
|
74
|
+
* a non-null value or the deadline is exceeded.
|
|
75
|
+
*/
|
|
76
|
+
async function waitFor(opts) {
|
|
77
|
+
const name = opts.name ?? DEFAULT_NAME;
|
|
78
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
79
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
80
|
+
const initialDelayMs = opts.initialDelayMs ?? 0;
|
|
81
|
+
const swallowErrors = opts.swallowErrors ?? true;
|
|
82
|
+
const log = opts.log ?? ((line) => console.log(line));
|
|
83
|
+
log(`→ ${name} — waiting (timeout=${timeoutMs}ms, interval=${intervalMs}ms)`);
|
|
84
|
+
const startedAt = Date.now();
|
|
85
|
+
const deadline = startedAt + timeoutMs;
|
|
86
|
+
if (initialDelayMs > 0) await sleep(initialDelayMs);
|
|
87
|
+
let attempts = 0;
|
|
88
|
+
while (Date.now() < deadline) {
|
|
89
|
+
attempts += 1;
|
|
90
|
+
const { value } = await runCheckOnce(opts.check, swallowErrors, log, name, attempts);
|
|
91
|
+
if (value !== null) {
|
|
92
|
+
const elapsedMs = Date.now() - startedAt;
|
|
93
|
+
log(`✓ ${name} — succeeded after ${elapsedMs}ms (attempts=${attempts})`);
|
|
94
|
+
const result = opts.onSuccess ? await opts.onSuccess(value) : void 0;
|
|
95
|
+
return {
|
|
96
|
+
outcome: "succeeded",
|
|
97
|
+
value,
|
|
98
|
+
elapsedMs,
|
|
99
|
+
attempts,
|
|
100
|
+
result
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const remaining = deadline - Date.now();
|
|
104
|
+
if (remaining <= 0) break;
|
|
105
|
+
await sleep(Math.min(intervalMs, remaining));
|
|
106
|
+
}
|
|
107
|
+
const elapsedMs = Date.now() - startedAt;
|
|
108
|
+
log(`✗ ${name} — timed out after ${elapsedMs}ms (attempts=${attempts})`);
|
|
109
|
+
if (opts.onTimeout) {
|
|
110
|
+
const result = await opts.onTimeout({
|
|
111
|
+
elapsedMs,
|
|
112
|
+
attempts
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
outcome: "timed-out",
|
|
116
|
+
elapsedMs,
|
|
117
|
+
attempts,
|
|
118
|
+
result
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw new WaitForTimeoutError(name, elapsedMs, attempts);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Factory returning an SDK Step whose run function executes
|
|
125
|
+
* `waitFor(...)`. Status lines are routed through `ctx.log.info`.
|
|
126
|
+
* The step's typed return value is the WaitForResult union.
|
|
127
|
+
*/
|
|
128
|
+
function waitForStep(name, opts) {
|
|
129
|
+
return step(name, { run: async (ctx) => waitFor({
|
|
130
|
+
name,
|
|
131
|
+
check: opts.check,
|
|
132
|
+
intervalMs: opts.intervalMs,
|
|
133
|
+
timeoutMs: opts.timeoutMs,
|
|
134
|
+
initialDelayMs: opts.initialDelayMs,
|
|
135
|
+
onSuccess: opts.onSuccess,
|
|
136
|
+
onTimeout: opts.onTimeout,
|
|
137
|
+
swallowErrors: opts.swallowErrors,
|
|
138
|
+
log: (line) => ctx.log.info(line)
|
|
139
|
+
}) });
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { WaitForTimeoutError, waitFor, waitForStep };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=wait-for.js.map
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import "./chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/workflow.ts
|
|
3
|
+
/**
|
|
4
|
+
* Normalize triggers to array.
|
|
5
|
+
* Handles single trigger config or array of trigger configs.
|
|
6
|
+
*/
|
|
7
|
+
function normalizeTriggers(on) {
|
|
8
|
+
if (!on) return void 0;
|
|
9
|
+
return Array.isArray(on) ? on : [on];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a workflow containing jobs.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Simple workflow
|
|
16
|
+
* export default workflow('ci', {
|
|
17
|
+
* jobs: [build, test, deploy],
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Workflow with triggers and rules
|
|
22
|
+
* export default workflow('ci', {
|
|
23
|
+
* on: [pr({ target: 'main' }), push({ branches: 'main' })],
|
|
24
|
+
* jobs: [build, test, deploy],
|
|
25
|
+
* rules: [rule('has src changes')],
|
|
26
|
+
* description: 'Main CI pipeline',
|
|
27
|
+
* });
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Validates a `<environment>:<secret-name>` qualified secret reference.
|
|
31
|
+
* Both halves must be non-empty and free of additional colons.
|
|
32
|
+
*/
|
|
33
|
+
function isQualifiedSecretRef(value) {
|
|
34
|
+
const idx = value.indexOf(":");
|
|
35
|
+
if (idx <= 0 || idx >= value.length - 1) return false;
|
|
36
|
+
const env = value.slice(0, idx);
|
|
37
|
+
const name = value.slice(idx + 1);
|
|
38
|
+
return env.length > 0 && name.length > 0 && !name.includes(":");
|
|
39
|
+
}
|
|
40
|
+
function workflow(name, options) {
|
|
41
|
+
if (options.registries) {
|
|
42
|
+
const seenScopes = /* @__PURE__ */ new Set();
|
|
43
|
+
let defaultCount = 0;
|
|
44
|
+
for (const [i, reg] of options.registries.entries()) {
|
|
45
|
+
try {
|
|
46
|
+
new URL(reg.url);
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error(`workflow('${name}'): registries[${i}].url is not a valid URL: ${reg.url}`);
|
|
49
|
+
}
|
|
50
|
+
if (!isQualifiedSecretRef(reg.tokenSecret)) throw new Error(`workflow('${name}'): registries[${i}].tokenSecret must use qualified <environment>:<secret-name> syntax (got: ${reg.tokenSecret})`);
|
|
51
|
+
if (reg.scope === void 0) defaultCount += 1;
|
|
52
|
+
else {
|
|
53
|
+
if (!/^@[a-z0-9][a-z0-9-]*$/i.test(reg.scope)) throw new Error(`workflow('${name}'): registries[${i}].scope must match @<package-scope> (got: ${reg.scope})`);
|
|
54
|
+
if (seenScopes.has(reg.scope)) throw new Error(`workflow('${name}'): registries declares scope ${reg.scope} more than once`);
|
|
55
|
+
seenScopes.add(reg.scope);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (defaultCount > 1) throw new Error(`workflow('${name}'): at most one registries entry may omit \`scope\` (default registry); got ${defaultCount}`);
|
|
59
|
+
}
|
|
60
|
+
if (options.installEnv) {
|
|
61
|
+
for (const [i, ref] of options.installEnv.entries()) if (!isQualifiedSecretRef(ref)) throw new Error(`workflow('${name}'): installEnv[${i}] must use qualified <environment>:<secret-name> syntax (got: ${ref})`);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
_tag: "Workflow",
|
|
65
|
+
name,
|
|
66
|
+
jobs: options.jobs,
|
|
67
|
+
on: normalizeTriggers(options.on),
|
|
68
|
+
rules: options.rules,
|
|
69
|
+
description: options.description,
|
|
70
|
+
hashFiles: options.hashFiles,
|
|
71
|
+
registries: options.registries,
|
|
72
|
+
installEnv: options.installEnv,
|
|
73
|
+
onCancel: options.onCancel,
|
|
74
|
+
cleanup: options.cleanup,
|
|
75
|
+
onSuccess: options.onSuccess,
|
|
76
|
+
onFailure: options.onFailure,
|
|
77
|
+
concurrency: options.concurrency
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { workflow };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=workflow.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "TypeScript SDK for defining KiCI workflows. Import into `.kici/workflows/*.ts` to declare workflows, jobs, steps, triggers, rules, and matrix configurations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kici",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"ci-cd",
|
|
10
10
|
"typescript",
|
|
11
11
|
"workflows",
|
|
12
|
-
"devops"
|
|
12
|
+
"devops",
|
|
13
|
+
"sdk",
|
|
14
|
+
"dsl"
|
|
13
15
|
],
|
|
14
16
|
"homepage": "https://kici.dev",
|
|
15
17
|
"author": {
|
|
@@ -17,9 +19,44 @@
|
|
|
17
19
|
"email": "hello@kici.dev"
|
|
18
20
|
},
|
|
19
21
|
"license": "Apache-2.0",
|
|
20
|
-
"
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=24"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"!dist/**/*.map",
|
|
29
|
+
"sbom.spdx.json"
|
|
30
|
+
],
|
|
21
31
|
"publishConfig": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
32
|
+
"registry": "https://registry.npmjs.org/",
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"main": "dist/index.js",
|
|
36
|
+
"types": "dist/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/index.js",
|
|
41
|
+
"default": "./dist/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "node ../../scripts/build-ts.mjs && tsc --emitDeclarationOnly",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"prepublishOnly": "npx tsx ../../hack/generate-sbom.ts --package packages/sdk --name @kici-dev/sdk"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@kici-dev/engine": "workspace:*",
|
|
53
|
+
"@kici-dev/shared": "workspace:*",
|
|
54
|
+
"fast-cartesian": "^9.0.1",
|
|
55
|
+
"micromatch": "^4.0.8",
|
|
56
|
+
"zod": "^4.3.6",
|
|
57
|
+
"zx": "^8.8.5"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/micromatch": "^4.0.10"
|
|
24
61
|
}
|
|
25
62
|
}
|