flowneer 0.1.0

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.
@@ -0,0 +1,56 @@
1
+ // plugins/observability/withTiming.ts
2
+ var withTiming = {
3
+ withTiming() {
4
+ const starts = /* @__PURE__ */ new Map();
5
+ this._setHooks({
6
+ beforeStep: (meta) => {
7
+ starts.set(meta.index, Date.now());
8
+ },
9
+ afterStep: (meta, shared) => {
10
+ const start = starts.get(meta.index) ?? Date.now();
11
+ if (!shared.__timings) shared.__timings = {};
12
+ shared.__timings[meta.index] = Date.now() - start;
13
+ starts.delete(meta.index);
14
+ }
15
+ });
16
+ return this;
17
+ }
18
+ };
19
+
20
+ // plugins/observability/withHistory.ts
21
+ var withHistory = {
22
+ withHistory() {
23
+ this._setHooks({
24
+ afterStep: (meta, shared) => {
25
+ if (!Array.isArray(shared.__history)) shared.__history = [];
26
+ const { __history: _h, ...rest } = shared;
27
+ shared.__history.push({
28
+ index: meta.index,
29
+ type: meta.type,
30
+ snapshot: { ...rest }
31
+ });
32
+ }
33
+ });
34
+ return this;
35
+ }
36
+ };
37
+
38
+ // plugins/observability/withVerbose.ts
39
+ var withVerbose = {
40
+ withVerbose() {
41
+ this._setHooks({
42
+ afterStep: (meta, shared) => {
43
+ console.log(
44
+ `[flowneer] step ${meta.index} (${meta.type}):`,
45
+ JSON.stringify(shared, null, 2)
46
+ );
47
+ }
48
+ });
49
+ return this;
50
+ }
51
+ };
52
+ export {
53
+ withHistory,
54
+ withTiming,
55
+ withVerbose
56
+ };
@@ -0,0 +1,51 @@
1
+ import { FlowneerPlugin } from '../../index.js';
2
+
3
+ interface CheckpointStore<S = any> {
4
+ /** Called after each successful step with the step index and current shared state. */
5
+ save: (stepIndex: number, shared: S) => void | Promise<void>;
6
+ }
7
+ declare module "../../Flowneer" {
8
+ interface FlowBuilder<S, P> {
9
+ /** Saves `shared` to `store` after each successful step. */
10
+ withCheckpoint(store: CheckpointStore<S>): this;
11
+ }
12
+ }
13
+ declare const withCheckpoint: FlowneerPlugin;
14
+
15
+ interface AuditEntry<S = any> {
16
+ stepIndex: number;
17
+ type: string;
18
+ timestamp: number;
19
+ /** Deep clone of `shared` at the time of the event. */
20
+ shared: S;
21
+ /** Error message, present only on failed steps. */
22
+ error?: string;
23
+ }
24
+ interface AuditLogStore<S = any> {
25
+ append: (entry: AuditEntry<S>) => void | Promise<void>;
26
+ }
27
+ declare module "../../Flowneer" {
28
+ interface FlowBuilder<S, P> {
29
+ /**
30
+ * Writes an immutable audit entry to `store` after each step (success and error).
31
+ * The `shared` snapshot in each entry is a deep clone via `JSON.parse/stringify`.
32
+ */
33
+ withAuditLog(store: AuditLogStore<S>): this;
34
+ }
35
+ }
36
+ declare const withAuditLog: FlowneerPlugin;
37
+
38
+ declare module "../../Flowneer" {
39
+ interface FlowBuilder<S, P> {
40
+ /**
41
+ * Skips execution of all steps before `fromStep`.
42
+ * Combine with `.withCheckpoint()`: restore `shared` from the checkpoint
43
+ * store before calling `.run()`, then call `.withReplay(lastSavedIndex + 1)`
44
+ * so only future steps execute.
45
+ */
46
+ withReplay(fromStep: number): this;
47
+ }
48
+ }
49
+ declare const withReplay: FlowneerPlugin;
50
+
51
+ export { type AuditEntry, type AuditLogStore, type CheckpointStore, withAuditLog, withCheckpoint, withReplay };
@@ -0,0 +1,56 @@
1
+ // plugins/persistence/withCheckpoint.ts
2
+ var withCheckpoint = {
3
+ withCheckpoint(store) {
4
+ this._setHooks({
5
+ afterStep: async (meta, shared) => {
6
+ await store.save(meta.index, shared);
7
+ }
8
+ });
9
+ return this;
10
+ }
11
+ };
12
+
13
+ // plugins/persistence/withAuditLog.ts
14
+ var withAuditLog = {
15
+ withAuditLog(store) {
16
+ const clone = (v) => JSON.parse(JSON.stringify(v));
17
+ this._setHooks({
18
+ afterStep: async (meta, shared) => {
19
+ await store.append({
20
+ stepIndex: meta.index,
21
+ type: meta.type,
22
+ timestamp: Date.now(),
23
+ shared: clone(shared)
24
+ });
25
+ },
26
+ onError: (meta, err, shared) => {
27
+ store.append({
28
+ stepIndex: meta.index,
29
+ type: meta.type,
30
+ timestamp: Date.now(),
31
+ shared: clone(shared),
32
+ error: err instanceof Error ? err.message : String(err)
33
+ });
34
+ }
35
+ });
36
+ return this;
37
+ }
38
+ };
39
+
40
+ // plugins/persistence/withReplay.ts
41
+ var withReplay = {
42
+ withReplay(fromStep) {
43
+ this._setHooks({
44
+ wrapStep: async (meta, next) => {
45
+ if (meta.index < fromStep) return;
46
+ await next();
47
+ }
48
+ });
49
+ return this;
50
+ }
51
+ };
52
+ export {
53
+ withAuditLog,
54
+ withCheckpoint,
55
+ withReplay
56
+ };
@@ -0,0 +1,46 @@
1
+ import { FlowneerPlugin } from '../../index.js';
2
+
3
+ declare module "../../Flowneer" {
4
+ interface FlowBuilder<S, P> {
5
+ /**
6
+ * Catches any step error and calls `fn` instead of propagating.
7
+ * The flow continues normally after the fallback runs.
8
+ */
9
+ withFallback(fn: NodeFn<S, P>): this;
10
+ }
11
+ }
12
+ declare const withFallback: FlowneerPlugin;
13
+
14
+ interface CircuitBreakerOptions {
15
+ /** Number of consecutive failures that open the circuit. Default: 3 */
16
+ maxFailures?: number;
17
+ /**
18
+ * Milliseconds after which the circuit resets and allows one probe attempt.
19
+ * Default: 30 000
20
+ */
21
+ resetMs?: number;
22
+ }
23
+ declare module "../../Flowneer" {
24
+ interface FlowBuilder<S, P> {
25
+ /**
26
+ * Trips an open-circuit after `maxFailures` consecutive failures.
27
+ * While open, every step throws immediately without executing.
28
+ * Resets after `resetMs` milliseconds.
29
+ */
30
+ withCircuitBreaker(opts?: CircuitBreakerOptions): this;
31
+ }
32
+ }
33
+ declare const withCircuitBreaker: FlowneerPlugin;
34
+
35
+ declare module "../../Flowneer" {
36
+ interface FlowBuilder<S, P> {
37
+ /**
38
+ * Applies a per-step wall-clock timeout to every step.
39
+ * Throws `"step N timed out after Xms"` if any step exceeds the limit.
40
+ */
41
+ withTimeout(ms: number): this;
42
+ }
43
+ }
44
+ declare const withTimeout: FlowneerPlugin;
45
+
46
+ export { type CircuitBreakerOptions, withCircuitBreaker, withFallback, withTimeout };
@@ -0,0 +1,71 @@
1
+ // plugins/resilience/withFallback.ts
2
+ var withFallback = {
3
+ withFallback(fn) {
4
+ this._setHooks({
5
+ wrapStep: async (_meta, next, shared, params) => {
6
+ try {
7
+ await next();
8
+ } catch {
9
+ await fn(shared, params);
10
+ }
11
+ }
12
+ });
13
+ return this;
14
+ }
15
+ };
16
+
17
+ // plugins/resilience/withCircuitBreaker.ts
18
+ var withCircuitBreaker = {
19
+ withCircuitBreaker(opts = {}) {
20
+ const { maxFailures = 3, resetMs = 3e4 } = opts;
21
+ let consecutiveFailures = 0;
22
+ let openedAt = null;
23
+ this._setHooks({
24
+ beforeStep: () => {
25
+ if (openedAt !== null) {
26
+ if (Date.now() - openedAt >= resetMs) {
27
+ consecutiveFailures = 0;
28
+ openedAt = null;
29
+ } else {
30
+ throw new Error(
31
+ `circuit open after ${consecutiveFailures} consecutive failures`
32
+ );
33
+ }
34
+ }
35
+ },
36
+ afterStep: () => {
37
+ consecutiveFailures = 0;
38
+ },
39
+ onError: () => {
40
+ consecutiveFailures += 1;
41
+ if (consecutiveFailures >= maxFailures) {
42
+ openedAt = Date.now();
43
+ }
44
+ }
45
+ });
46
+ return this;
47
+ }
48
+ };
49
+
50
+ // plugins/resilience/withTimeout.ts
51
+ var withTimeout = {
52
+ withTimeout(ms) {
53
+ this._setHooks({
54
+ wrapStep: (meta, next) => Promise.race([
55
+ next(),
56
+ new Promise(
57
+ (_, reject) => setTimeout(
58
+ () => reject(new Error(`step ${meta.index} timed out after ${ms}ms`)),
59
+ ms
60
+ )
61
+ )
62
+ ])
63
+ });
64
+ return this;
65
+ }
66
+ };
67
+ export {
68
+ withCircuitBreaker,
69
+ withFallback,
70
+ withTimeout
71
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "flowneer",
3
+ "version": "0.1.0",
4
+ "description": "Zero-dependency fluent flow builder for AI agents",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": "./dist/index.js",
11
+ "./plugins": "./dist/plugins/index.js",
12
+ "./plugins/observability": "./dist/plugins/observability/index.js",
13
+ "./plugins/resilience": "./dist/plugins/resilience/index.js",
14
+ "./plugins/persistence": "./dist/plugins/persistence/index.js",
15
+ "./plugins/llm": "./dist/plugins/llm/index.js",
16
+ "./plugins/dev": "./dist/plugins/dev/index.js"
17
+ },
18
+ "sideEffects": false,
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "keywords": [
23
+ "flow",
24
+ "agent",
25
+ "llm",
26
+ "pipeline",
27
+ "fluent",
28
+ "builder"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "prepublishOnly": "bun test && bun run build"
33
+ },
34
+ "devDependencies": {
35
+ "@types/bun": "latest",
36
+ "dotenv": "17.3.1",
37
+ "openai": "6.22.0",
38
+ "tsup": "^8.5.1"
39
+ },
40
+ "peerDependencies": {
41
+ "typescript": "^5"
42
+ }
43
+ }