@protoyard/flows 0.0.0-stage → 1.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -2
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2 -0
  5. package/dist/model/conditions.d.ts +75 -0
  6. package/dist/model/conditions.js +297 -0
  7. package/dist/model/data.d.ts +14 -0
  8. package/dist/model/data.js +73 -0
  9. package/dist/model/flow.d.ts +53 -0
  10. package/dist/model/flow.js +93 -0
  11. package/dist/model/index.d.ts +17 -0
  12. package/dist/model/index.js +17 -0
  13. package/dist/model/limits.d.ts +32 -0
  14. package/dist/model/limits.js +32 -0
  15. package/dist/model/ops.d.ts +122 -0
  16. package/dist/model/ops.js +311 -0
  17. package/dist/model/recipes.d.ts +12 -0
  18. package/dist/model/recipes.js +209 -0
  19. package/dist/model/refs.d.ts +30 -0
  20. package/dist/model/refs.js +48 -0
  21. package/dist/model/steps.d.ts +276 -0
  22. package/dist/model/steps.js +206 -0
  23. package/dist/model/text.d.ts +30 -0
  24. package/dist/model/text.js +33 -0
  25. package/dist/model/triggers.d.ts +42 -0
  26. package/dist/model/triggers.js +68 -0
  27. package/dist/model/validate.d.ts +19 -0
  28. package/dist/model/validate.js +207 -0
  29. package/dist/model/walk.d.ts +40 -0
  30. package/dist/model/walk.js +68 -0
  31. package/dist/runtime/clock.d.ts +12 -0
  32. package/dist/runtime/clock.js +40 -0
  33. package/dist/runtime/engine.d.ts +15 -0
  34. package/dist/runtime/engine.js +578 -0
  35. package/dist/runtime/index.d.ts +11 -0
  36. package/dist/runtime/index.js +10 -0
  37. package/dist/runtime/keeps.d.ts +8 -0
  38. package/dist/runtime/keeps.js +33 -0
  39. package/dist/runtime/outbox.d.ts +8 -0
  40. package/dist/runtime/outbox.js +27 -0
  41. package/dist/runtime/seed.d.ts +7 -0
  42. package/dist/runtime/seed.js +45 -0
  43. package/dist/runtime/signals.d.ts +4 -0
  44. package/dist/runtime/signals.js +13 -0
  45. package/dist/runtime/types.d.ts +127 -0
  46. package/dist/runtime/types.js +5 -0
  47. package/package.json +62 -3
@@ -0,0 +1,45 @@
1
+ /** Small deterministic helpers (no Math.random, no Date.now). */
2
+ export function hashSeed(seed) {
3
+ if (typeof seed === 'number' && Number.isFinite(seed)) {
4
+ return seed >>> 0;
5
+ }
6
+ const text = seed === undefined ? 'flows' : String(seed);
7
+ let h = 2166136261;
8
+ for (let i = 0; i < text.length; i += 1) {
9
+ h ^= text.charCodeAt(i);
10
+ h = Math.imul(h, 16777619);
11
+ }
12
+ return h >>> 0;
13
+ }
14
+ /** Mulberry32 — same stream for the same seed. */
15
+ export function createRng(seed) {
16
+ let state = hashSeed(seed) || 1;
17
+ return () => {
18
+ state = (state + 0x6d2b79f5) >>> 0;
19
+ let t = state;
20
+ t = Math.imul(t ^ (t >>> 15), t | 1);
21
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
22
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
23
+ };
24
+ }
25
+ export function createIdFactory(seed) {
26
+ const prefixSeed = hashSeed(seed).toString(36);
27
+ let n = 0;
28
+ return (prefix) => {
29
+ n += 1;
30
+ return `${prefix}_${prefixSeed}_${String(n).padStart(4, '0')}`;
31
+ };
32
+ }
33
+ /** Fisher–Yates with an injected rng. */
34
+ export function shuffleInPlace(items, rng) {
35
+ for (let i = items.length - 1; i > 0; i -= 1) {
36
+ const j = Math.floor(rng() * (i + 1));
37
+ const left = items[i];
38
+ const right = items[j];
39
+ if (left === undefined || right === undefined)
40
+ continue;
41
+ items[i] = right;
42
+ items[j] = left;
43
+ }
44
+ return items;
45
+ }
@@ -0,0 +1,4 @@
1
+ import type { Signal } from './types.ts';
2
+ export declare function openSignals(signals: readonly Signal[], flowId?: string): Signal[];
3
+ /** Open signals for attention UI: failures first, then approvals, blockers, waits. */
4
+ export declare function attentionOf(signals: readonly Signal[], flowId?: string): Signal[];
@@ -0,0 +1,13 @@
1
+ export function openSignals(signals, flowId) {
2
+ return signals.filter((signal) => !signal.resolved && (!flowId || signal.flowId === flowId));
3
+ }
4
+ /** Open signals for attention UI: failures first, then approvals, blockers, waits. */
5
+ export function attentionOf(signals, flowId) {
6
+ const order = {
7
+ failed: 0,
8
+ approval: 1,
9
+ blocker: 2,
10
+ waiting: 3,
11
+ };
12
+ return openSignals(signals, flowId).sort((a, b) => order[a.kind] - order[b.kind] || b.at.localeCompare(a.at) || b.id.localeCompare(a.id));
13
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Mock flow runtime types. Everything a run produces stays in-memory as
3
+ * proposed effects, keeps, signals and a masked outbox — never a network call.
4
+ */
5
+ export type FlowRunStatus = 'running' | 'waiting' | 'done' | 'failed' | 'cancelled';
6
+ export type OutboxKind = 'dm' | 'channel' | 'thread' | 'email' | 'inbox' | 'notification' | 'reply' | 'tool' | 'http';
7
+ export type SignalKind = 'blocker' | 'waiting' | 'approval' | 'failed';
8
+ export interface FlowPerson {
9
+ id: string;
10
+ name: string;
11
+ role?: string;
12
+ team?: string;
13
+ }
14
+ export interface FlowKeepRecord {
15
+ id: string;
16
+ collection: string;
17
+ title: string;
18
+ state: string | null;
19
+ fields: Record<string, string>;
20
+ runId: string;
21
+ createdAt: string;
22
+ history: {
23
+ to: string;
24
+ at: string;
25
+ by: string | null;
26
+ }[];
27
+ }
28
+ export interface FlowListState {
29
+ items: string[];
30
+ used: string[];
31
+ history: string[][];
32
+ }
33
+ export interface OutboxItem {
34
+ id: string;
35
+ at: string;
36
+ kind: OutboxKind;
37
+ text: string;
38
+ flowId: string;
39
+ runId: string;
40
+ stepId: string;
41
+ to?: string[];
42
+ channel?: string;
43
+ subject?: string;
44
+ /** Masked when kind is http. */
45
+ url?: string;
46
+ method?: string;
47
+ tool?: string;
48
+ action?: string;
49
+ }
50
+ export interface ProposedEffect {
51
+ type: string;
52
+ stepId: string;
53
+ payload: Readonly<Record<string, unknown>>;
54
+ }
55
+ export interface Signal {
56
+ id: string;
57
+ kind: SignalKind;
58
+ at: string;
59
+ resolved: boolean;
60
+ flowId: string;
61
+ runId: string;
62
+ stepId?: string;
63
+ title: string;
64
+ detail: string;
65
+ }
66
+ export interface FlowRun {
67
+ id: string;
68
+ flowId: string;
69
+ status: FlowRunStatus;
70
+ startedAt: string;
71
+ finishedAt: string | null;
72
+ trigger: Readonly<Record<string, unknown>>;
73
+ error: string | null;
74
+ /** Step ids visited in order (including skipped branches' chosen arm). */
75
+ path: string[];
76
+ }
77
+ export interface FlowRunResult {
78
+ run: FlowRun;
79
+ /** Step outputs keyed by step id. */
80
+ outputs: Readonly<Record<string, unknown>>;
81
+ /** Proposed side effects for a service adapter (messages, tools, records…). */
82
+ effects: readonly ProposedEffect[];
83
+ /** Masked delivery log — nothing left the browser. */
84
+ outbox: readonly OutboxItem[];
85
+ /** Records and list mutations the run kept. */
86
+ keeps: {
87
+ records: readonly FlowKeepRecord[];
88
+ lists: Readonly<Record<string, FlowListState>>;
89
+ };
90
+ signals: readonly Signal[];
91
+ /** Simulated clock after the run (ISO). */
92
+ now: string;
93
+ }
94
+ export interface FlowRuntimeOptions {
95
+ /** Simulated instant (ISO string, Date, or epoch ms). Default: fixed epoch. */
96
+ now?: string | number | Date;
97
+ /** Deterministic seed for picks, groups and assistant labels. */
98
+ seed?: number | string;
99
+ /** Payload that started the run (manual form, event, webhook sample). */
100
+ trigger?: Readonly<Record<string, unknown>>;
101
+ /** Record the run is about (event triggers). */
102
+ record?: unknown;
103
+ /** Directory for audience resolution (fictional people only). */
104
+ people?: readonly FlowPerson[];
105
+ /** Existing keeps the run may read. */
106
+ records?: Readonly<Record<string, readonly FlowKeepRecord[]>>;
107
+ lists?: Readonly<Record<string, FlowListState>>;
108
+ /**
109
+ * Answers for `ask` steps: step id → field map, or a single value applied to
110
+ * the first question.
111
+ */
112
+ answers?: Readonly<Record<string, unknown>>;
113
+ /** Decisions for `approval` steps: step id → approved | declined. */
114
+ decisions?: Readonly<Record<string, 'approved' | 'declined'>>;
115
+ /** Tool handlers by tool id. Must be pure and local — never fetch. */
116
+ tools?: Readonly<Record<string, (input: Readonly<Record<string, unknown>>) => unknown>>;
117
+ /** Optional judge for `judge` conditions (`undecided` leaves the run waiting). */
118
+ judge?: (request: {
119
+ phrase: string;
120
+ judge: string;
121
+ }) => boolean | 'undecided';
122
+ /**
123
+ * When true (default), `ask` / `approval` without answers leave the run
124
+ * `waiting`. When false, missing answers fail the run.
125
+ */
126
+ waitForPeople?: boolean;
127
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Mock flow runtime types. Everything a run produces stays in-memory as
3
+ * proposed effects, keeps, signals and a masked outbox — never a network call.
4
+ */
5
+ export {};
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "@protoyard/flows",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.1.0",
4
+ "description": "Framework-agnostic workflow engine for Protoyard: a serializable flow model with triggers, steps, data-only conditions, edit operations, recipes and validation (no Svelte, no executable expressions).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./model": {
15
+ "types": "./dist/model/index.d.ts",
16
+ "import": "./dist/model/index.js",
17
+ "default": "./dist/model/index.js"
18
+ },
19
+ "./runtime": {
20
+ "types": "./dist/runtime/index.d.ts",
21
+ "import": "./dist/runtime/index.js",
22
+ "default": "./dist/runtime/index.js"
23
+ }
24
+ },
25
+ "dependencies": {
26
+ "zod": "^4.6.7"
27
+ },
28
+ "protoyard": {
29
+ "build": {
30
+ "group": "engine",
31
+ "assets": []
32
+ }
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "provenance": true
42
+ },
43
+ "engines": {
44
+ "node": ">=24.20.0"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/DailybotHQ/protoyard",
49
+ "directory": "packages/flows"
50
+ },
51
+ "homepage": "https://protoyard.app",
52
+ "bugs": {
53
+ "url": "https://github.com/DailybotHQ/protoyard/issues"
54
+ },
55
+ "keywords": [
56
+ "protoyard",
57
+ "workflow",
58
+ "automation",
59
+ "typescript"
60
+ ],
61
+ "scripts": {
62
+ "typecheck": "tsc -p tsconfig.json"
63
+ },
64
+ "types": "./dist/index.d.ts"
6
65
  }