@elqnt/workflow 2.1.4 → 2.2.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 (45) hide show
  1. package/README.md +6 -0
  2. package/SKILL.md +442 -0
  3. package/dist/api/index.js +3 -4
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/index.mjs +1 -2
  6. package/dist/chunk-3GG6KTGJ.js +998 -0
  7. package/dist/chunk-3GG6KTGJ.js.map +1 -0
  8. package/dist/{chunk-UE4ZBFLG.mjs → chunk-4AUWFGQK.mjs} +1 -3
  9. package/dist/{chunk-UE4ZBFLG.mjs.map → chunk-4AUWFGQK.mjs.map} +1 -1
  10. package/dist/{chunk-H24IF5AA.js → chunk-6DV2LPSN.js} +2 -4
  11. package/dist/chunk-6DV2LPSN.js.map +1 -0
  12. package/dist/chunk-AXXQSYT7.mjs +998 -0
  13. package/dist/chunk-AXXQSYT7.mjs.map +1 -0
  14. package/dist/{chunk-YCEGKVXP.mjs → chunk-IY5JCMLA.mjs} +1 -3
  15. package/dist/{chunk-YCEGKVXP.mjs.map → chunk-IY5JCMLA.mjs.map} +1 -1
  16. package/dist/{chunk-JES2EBNO.js → chunk-YCO5IFZK.js} +2 -4
  17. package/dist/chunk-YCO5IFZK.js.map +1 -0
  18. package/dist/components/index.d.mts +245 -0
  19. package/dist/components/index.d.ts +245 -0
  20. package/dist/components/index.js +10 -0
  21. package/dist/components/index.js.map +1 -0
  22. package/dist/components/index.mjs +10 -0
  23. package/dist/components/index.mjs.map +1 -0
  24. package/dist/hooks/index.d.mts +72 -27
  25. package/dist/hooks/index.d.ts +72 -27
  26. package/dist/hooks/index.js +397 -5
  27. package/dist/hooks/index.js.map +1 -1
  28. package/dist/hooks/index.mjs +401 -9
  29. package/dist/hooks/index.mjs.map +1 -1
  30. package/dist/index.d.mts +4 -246
  31. package/dist/index.d.ts +4 -246
  32. package/dist/index.js +8 -1012
  33. package/dist/index.js.map +1 -1
  34. package/dist/index.mjs +5 -1009
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/models/index.js +3 -4
  37. package/dist/models/index.js.map +1 -1
  38. package/dist/models/index.mjs +1 -2
  39. package/package.json +12 -6
  40. package/dist/chunk-H24IF5AA.js.map +0 -1
  41. package/dist/chunk-JES2EBNO.js.map +0 -1
  42. package/dist/chunk-KWWX35B4.js +0 -413
  43. package/dist/chunk-KWWX35B4.js.map +0 -1
  44. package/dist/chunk-WDERYFU3.mjs +0 -413
  45. package/dist/chunk-WDERYFU3.mjs.map +0 -1
package/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Workflow definitions and execution for Eloquent platform. Provides models, API functions, and React hooks for building and managing workflows.
4
4
 
5
+ > **📖 Building a custom frontend on the workflow backend?** Read
6
+ > [`SKILL.md`](./SKILL.md) — the authoritative, per-method contract for the
7
+ > `useWorkflows` / `useWorkflowInstances` / `useWorkflowTemplates` hooks, the
8
+ > gateway-token flow, and the recommended app structure. The package's published
9
+ > `.d.ts` is the source of truth; `SKILL.md` is its human-readable mirror.
10
+
5
11
  ## Installation
6
12
 
7
13
  ```bash
package/SKILL.md ADDED
@@ -0,0 +1,442 @@
1
+ ---
2
+ name: workflow
3
+ description: Build a custom frontend on the Eloquent workflow backend using the @elqnt/workflow hooks. Covers the one true path (custom app → @elqnt/workflow hooks → API Gateway → workflow Go service), the gateway-token/secret flow, and the EXACT input/output spec of every hook method (useWorkflows, useWorkflowInstances, useWorkflowTemplates — definition CRUD, instance lifecycle, node execute/resume/retry, templates) plus all types.
4
+ ---
5
+
6
+ # Workflow — Building a Custom Frontend
7
+
8
+ Eloquent's **workflow** service runs custom, multi-step processes: a
9
+ **definition** is a graph of typed nodes (trigger, human action, agent, action,
10
+ logic, loop, …); an **instance** is one live run of a definition; a **template**
11
+ is a parameterized definition you instantiate with variables. This skill is
12
+ **only** about one thing: building a custom app that drives it through the
13
+ `@elqnt/workflow` package.
14
+
15
+ > The package is `@elqnt/workflow`. It exports three hooks —
16
+ > `useWorkflows` (definition CRUD), `useWorkflowInstances` (instance lifecycle +
17
+ > node execution), and `useWorkflowTemplates` (template list/get/instantiate).
18
+ > It also ships the Redux store slices, the JSON schemas, the typed models, the
19
+ > browser API fns, and the `schema-builder` / `dynamic-schema-form` React
20
+ > components.
21
+
22
+ ---
23
+
24
+ ## ⛔ The contract — read this before writing any code
25
+
26
+ This skill ships **inside the package**, so the contract is not a separate file
27
+ to keep in sync — it **is** the package's own published type declarations
28
+ (`@elqnt/workflow` → `dist/**/*.d.ts`). Because your app *imports the real
29
+ hooks*, the TypeScript compiler enforces the contract for you: a wrong param or a
30
+ misused return value won't compile.
31
+
32
+ The single source of truth, in order:
33
+
34
+ 1. `import { useWorkflows, useWorkflowInstances, useWorkflowTemplates } from "@elqnt/workflow/hooks"`
35
+ — the only three hooks.
36
+ 2. `import type { UseWorkflowsReturn, UseWorkflowInstancesReturn, UseWorkflowTemplatesReturn } from "@elqnt/workflow/hooks"`
37
+ — the **named, exact** method surface of each hook (every method, its params,
38
+ its return type). The tables below are a human-readable mirror of these.
39
+ 3. `import type { … } from "@elqnt/workflow/models"` — `WorkflowDefinition`,
40
+ `WorkflowInstance`, node/edge types, etc. (the DTOs).
41
+ 4. `import type { WorkflowTemplate } from "@elqnt/workflow/api"` — the template
42
+ DTO (it lives in the api ring, not models).
43
+
44
+ **Rules (do not drift):**
45
+
46
+ 1. **Use only the three exported hooks** and only the methods they expose. Do
47
+ **not** invent hooks, methods, params, or return shapes — if it's not on
48
+ `UseWorkflowsReturn` / `UseWorkflowInstancesReturn` / `UseWorkflowTemplatesReturn`,
49
+ it doesn't exist.
50
+ 2. **Let the compiler check you.** Build with `tsc`; never `as any` /
51
+ `@ts-ignore` your way around a hook's types to force a call that isn't there.
52
+ 3. **Never bypass the hooks.** No `fetch`/`axios` to `/api/v1/workflows/...`, no
53
+ NATS, no direct service calls. (Server-side only: `@elqnt/workflow/api` fns +
54
+ `createServerClient`, same signatures.)
55
+ 4. **Wrap, don't scatter.** The hook is imported in exactly one app file
56
+ (`hooks/use-<domain>.ts`); see ["The domain layer"](#the-domain-layer).
57
+ 5. If a requirement seems to need a call the package doesn't define, **stop and
58
+ flag it** — do not improvise an endpoint.
59
+
60
+ The prose/tables below explain each method; the package's shipped `.d.ts` is what
61
+ your code type-checks against.
62
+
63
+ ---
64
+
65
+ ## Architecture — the one and only path
66
+
67
+ A component **never** calls a package hook directly. It only sees your **domain
68
+ type**, served by a **context**, backed by your **app hook**, which is the *only*
69
+ place that wraps the `@elqnt/workflow` hook. Everything below the app hook is
70
+ plumbing the component never imports.
71
+
72
+ ```
73
+ ┌──────────────────────────────────────────────────────────────────────────────┐
74
+ │ YOUR CUSTOM APP (Next.js / React) │
75
+ │ │
76
+ │ SSR layout: read API_GATEWAY_URL_PUBLIC (request-time) + ORG_ID │
77
+ │ └─► AppConfigProvider { apiGatewayUrl, orgId } │
78
+ │ │
79
+ │ components/workflows/* ── speak only your domain type │
80
+ │ │ useWorkflowsContext() │
81
+ │ ▼ │
82
+ │ contexts/workflows-context.tsx ── one shared app-hook instance │
83
+ │ │ │
84
+ │ ▼ │
85
+ │ hooks/use-app-workflows.ts ── app hook: CRUD + state, the only @elqnt import│
86
+ │ │ │
87
+ │ ▼ │
88
+ │ useWorkflows / useWorkflowInstances / useWorkflowTemplates (@elqnt/workflow/hooks)│
89
+ │ │ │
90
+ │ ▼ │
91
+ │ @elqnt/workflow/api → browserApiRequest │
92
+ │ │ getGatewayToken() ⇒ GET /api/gateway-token (mint HS256 JWT, JWT_SECRET)│
93
+ │ │ attaches: Authorization: Bearer <jwt>, X-Org-ID, X-User-ID, X-Product│
94
+ └─────────┼──────────────────────────────────────────────────────────────────────┘
95
+
96
+ ┌─────────────────┐ verify JWT, stamp X-Org-ID/X-Product, route match
97
+ │ API GATEWAY │ /api/v1/workflows/** → workflow svc
98
+ └───────┬─────────┘
99
+
100
+ ┌─────────────────┐ workflow definitions / instances; node execution engine
101
+ │ workflow (Go) │ per-org tenancy; emits SSE/NATS events for live nodes
102
+ └─────────────────┘
103
+ ```
104
+
105
+ Rules: the frontend **never** calls the workflow service directly and **never**
106
+ touches NATS. Every call is HTTP through the gateway carrying an org id + gateway
107
+ token. And **components never import `@elqnt/workflow`** — only the app hook does.
108
+
109
+ ---
110
+
111
+ ## The gateway token (the secret) — how the custom app gets it
112
+
113
+ Every request to the gateway needs `Authorization: Bearer <token>`. The token is
114
+ a short-lived **HS256 JWT** signed with the shared **gateway secret**. You never
115
+ hardcode it; there are two flows depending on where the code runs.
116
+
117
+ ### Browser flow (what the hooks use)
118
+
119
+ The hooks → API fns → `browserApiRequest` → `getGatewayToken()` internally. You
120
+ do **not** pass a token to the hook. `getGatewayToken()` (from
121
+ `@elqnt/api-client/browser`) by default does:
122
+
123
+ ```
124
+ fetch("/api/gateway-token") ⇒ { token, expiresIn }
125
+ ```
126
+
127
+ So your custom app must expose a **`/api/gateway-token` route** that mints the JWT
128
+ server-side (the secret stays on the server, never reaches the browser). The
129
+ client caches the token and refreshes ~5 min before expiry.
130
+
131
+ ```ts
132
+ // app/api/gateway-token/route.ts (Next.js route handler — server only)
133
+ import { NextResponse } from "next/server";
134
+ import * as jose from "jose";
135
+
136
+ export async function GET() {
137
+ const secret = new TextEncoder().encode(process.env.JWT_SECRET!); // SAME secret the gateway validates with
138
+ const token = await new jose.SignJWT({
139
+ org_id: process.env.ORG_ID!,
140
+ user_id: "system",
141
+ email: "system@my-app.com",
142
+ role: "system",
143
+ scopes: ["read:workflows", "write:workflows"], // OR-matched against the route's required scopes
144
+ product: "my-product", // gateway resolves product from THIS claim first
145
+ })
146
+ .setProtectedHeader({ alg: "HS256" })
147
+ .setIssuedAt()
148
+ .setIssuer("eloquent-gateway") // must match gateway JWT_ISSUER
149
+ .setAudience("eloquent-api") // must match gateway JWT_AUDIENCE
150
+ .setExpirationTime("1h")
151
+ .sign(secret);
152
+
153
+ return NextResponse.json({ token, expiresIn: 3600 });
154
+ }
155
+ ```
156
+
157
+ > The gateway re-stamps `X-Org-ID` / `X-User-ID` / `X-Product` from the **signed
158
+ > JWT claims** before proxying, so a forged header can't override the token — the
159
+ > JWT is authoritative. Routes declare required `scopes` (read routes need none;
160
+ > create/update/delete need `write:workflows`); the check is an **OR** match, and
161
+ > `admin`/`super_admin` role or a `*` scope bypasses it.
162
+
163
+ Override the token source (mobile/native, or a non-default URL) once at startup:
164
+
165
+ ```ts
166
+ import { configureAuth } from "@elqnt/api-client/browser";
167
+ configureAuth(async () => myTokenProvider()); // URL string or async () => token|null
168
+ // clearGatewayTokenCache() — call after switching orgs
169
+ ```
170
+
171
+ ### Server flow (server actions / SSR)
172
+
173
+ `@elqnt/api-client/server` mints the JWT itself with `jose` — no
174
+ `/api/gateway-token` hop. This is where the secret is injected directly:
175
+
176
+ ```ts
177
+ import { createServerClient } from "@elqnt/api-client/server";
178
+
179
+ const client = createServerClient({
180
+ gatewayUrl: process.env.API_GATEWAY_URL_INTERNAL!, // in-cluster gateway URL (server-side)
181
+ jwtSecret: process.env.JWT_SECRET!, // the shared gateway secret
182
+ defaultProduct: "my-product", // REQUIRED: gateway reads product from the JWT claim first
183
+ defaultScopes: ["read", "write", "admin"],
184
+ });
185
+
186
+ await client.get("/api/v1/workflows", { orgId, userId });
187
+ ```
188
+
189
+ > The workflow **hooks are browser-only** (`"use client"`). For SSR/server
190
+ > actions call the API fns or `createServerClient` directly — not the hooks.
191
+
192
+ ### Env vars
193
+
194
+ | Var | Used by | Purpose |
195
+ |---|---|---|
196
+ | `JWT_SECRET` | `/api/gateway-token` route + `createServerClient` | sign the gateway JWT (same value the gateway validates with) |
197
+ | `API_GATEWAY_URL_INTERNAL` | `createServerClient` `gatewayUrl` | in-cluster gateway URL (server) |
198
+ | `API_GATEWAY_URL_PUBLIC` | SSR layout → `AppConfigProvider` → browser `baseUrl` | public gateway URL, read **at request time** (not `NEXT_PUBLIC_*`) |
199
+ | `ORG_ID` | token route / app config | the org all requests are scoped to |
200
+
201
+ ### Headers the API layer sets per request
202
+
203
+ | From hook option | Header |
204
+ |---|---|
205
+ | auto token | `Authorization: Bearer <token>` |
206
+ | `orgId` | `X-Org-ID` |
207
+ | `userId` | `X-User-ID` |
208
+ | `userEmail` | `X-User-Email` |
209
+ | `product` (default `"eloquent"`) | `X-Product` |
210
+
211
+ ---
212
+
213
+ ## Hook options (all three hooks)
214
+
215
+ There is **no provider/context**. You pass options into each hook call. All three
216
+ take `ApiClientOptions`:
217
+
218
+ ```ts
219
+ interface ApiClientOptions {
220
+ baseUrl: string; // API Gateway base URL — required
221
+ orgId: string; // required → X-Org-ID
222
+ userId?: string; // → X-User-ID
223
+ userEmail?: string; // → X-User-Email
224
+ product?: string; // → X-Product, defaults to "eloquent"
225
+ headers?: Record<string, string>;
226
+ }
227
+
228
+ type UseWorkflowsOptions = ApiClientOptions;
229
+ type UseWorkflowInstancesOptions = ApiClientOptions;
230
+ type UseWorkflowTemplatesOptions = ApiClientOptions;
231
+ ```
232
+
233
+ > **Imperative, not auto-fetching.** Every method is an `async` function you call
234
+ > on demand; there is **no** `useEffect`-on-mount inside the hook. Each hook
235
+ > exposes aggregate `loading` and `error` flags. On failure a method returns its
236
+ > default (`[]`, `null`, `false`) and sets `error` — it does **not** throw.
237
+
238
+ ---
239
+
240
+ ## Hook: `useWorkflows` (definition CRUD)
241
+
242
+ ```ts
243
+ import { useWorkflows } from "@elqnt/workflow/hooks";
244
+
245
+ const wf = useWorkflows({ baseUrl, orgId, product: "my-product" });
246
+ ```
247
+
248
+ Returns `{ loading, error, ...methods }`:
249
+
250
+ | Method | Signature | Resolves to | Endpoint |
251
+ |---|---|---|---|
252
+ | `listWorkflows` | `() => Promise<WorkflowDefinition[]>` | `[]` on error | `GET /api/v1/workflows` |
253
+ | `getWorkflow` | `(workflowId: string) => Promise<WorkflowDefinition \| null>` | `null` | `GET /api/v1/workflows/{id}` |
254
+ | `createWorkflow` | `(workflow: Partial<WorkflowDefinition>) => Promise<WorkflowDefinition \| null>` | created definition | `POST /api/v1/workflows` |
255
+ | `updateWorkflow` | `(workflowId: string, workflow: Partial<WorkflowDefinition>) => Promise<WorkflowDefinition \| null>` | updated definition | `PUT /api/v1/workflows/{id}` |
256
+ | `deleteWorkflow` | `(workflowId: string) => Promise<boolean>` | `true`/`false` | `DELETE /api/v1/workflows/{id}` |
257
+
258
+ ---
259
+
260
+ ## Hook: `useWorkflowInstances` (instance lifecycle + node execution)
261
+
262
+ ```ts
263
+ import { useWorkflowInstances } from "@elqnt/workflow/hooks";
264
+
265
+ const inst = useWorkflowInstances({ baseUrl, orgId, product: "my-product" });
266
+ ```
267
+
268
+ Returns `{ loading, error, ...methods }`:
269
+
270
+ | Method | Signature | Resolves to | Endpoint |
271
+ |---|---|---|---|
272
+ | `listInstances` | `(definitionId: string, filters?: { userId?: string; status?: string }) => Promise<WorkflowInstance[]>` | `[]` | `GET /api/v1/workflows/{definitionId}/instances` (`?userId=&status=`) |
273
+ | `getInstance` | `(instanceId: string) => Promise<WorkflowInstance \| null>` | `null` | `GET /api/v1/workflows/instances/{id}` |
274
+ | `createInstance` | `(definitionId: string, data?: { variables?: Record<string, unknown>; autoExecute?: boolean }) => Promise<WorkflowInstance \| null>` | new instance | `POST /api/v1/workflows/{definitionId}/instances` |
275
+ | `updateStatus` | `(instanceId: string, status: string) => Promise<WorkflowInstance \| null>` | updated instance | `PUT /api/v1/workflows/instances/{id}/status` body `{ status }` |
276
+ | `executeNode` | `(instanceId: string, nodeId: string, input: Record<string, unknown>) => Promise<Record<string, unknown> \| null>` | node output map | `POST .../instances/{id}/nodes/{nodeId}/execute` body `{ input }` |
277
+ | `resumeNode` | `(instanceId: string, nodeId: string, result: Record<string, unknown>) => Promise<WorkflowInstance \| null>` | updated instance | `POST .../instances/{id}/nodes/{nodeId}/resume` body `{ result }` |
278
+ | `retryNode` | `(instanceId: string, nodeId: string) => Promise<WorkflowInstance \| null>` | updated instance | `POST .../instances/{id}/nodes/{nodeId}/retry` |
279
+
280
+ > `resumeNode` is the **human-in-the-loop** completion call: a paused
281
+ > `humanAction` node (review / approval / data-entry / assignment) resumes when
282
+ > you POST the human's `result`. `retryNode` re-runs a failed node.
283
+
284
+ ---
285
+
286
+ ## Hook: `useWorkflowTemplates` (template list / get / instantiate)
287
+
288
+ ```ts
289
+ import { useWorkflowTemplates } from "@elqnt/workflow/hooks";
290
+
291
+ const tpl = useWorkflowTemplates({ baseUrl, orgId, product: "my-product" });
292
+ ```
293
+
294
+ Returns `{ loading, error, ...methods }`:
295
+
296
+ | Method | Signature | Resolves to | Endpoint |
297
+ |---|---|---|---|
298
+ | `listTemplates` | `(category?: string) => Promise<WorkflowTemplate[]>` | `[]` | `GET /api/v1/workflows/templates` (`?category=`) |
299
+ | `getTemplate` | `(templateId: string) => Promise<WorkflowTemplate \| null>` | `null` | `GET /api/v1/workflows/templates/{id}` |
300
+ | `instantiateTemplate` | `(templateId: string, params: { variables: Record<string, unknown>; title?: string }) => Promise<WorkflowDefinition \| null>` | new definition | `POST /api/v1/workflows/templates/{id}/instantiate` |
301
+
302
+ ```ts
303
+ interface WorkflowTemplate {
304
+ id: string;
305
+ name: string;
306
+ title: string;
307
+ description: string;
308
+ category: string;
309
+ variables: Array<{ name: string; type: string; required: boolean; description?: string }>;
310
+ preview?: string;
311
+ createdAt: string;
312
+ updatedAt: string;
313
+ }
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Core models (`@elqnt/workflow/models`)
319
+
320
+ The definition/instance DTOs are tygo-generated from the Go service. The shapes
321
+ below are the load-bearing fields; the package's `.d.ts` is the full, authoritative
322
+ list.
323
+
324
+ ```ts
325
+ interface WorkflowDefinition {
326
+ id: string;
327
+ title: string;
328
+ description: string;
329
+ nodes: WorkflowNode[]; // the graph
330
+ edges: WorkflowEdge[]; // connections between nodes
331
+ variables?: Record<string, unknown>;
332
+ status: string; // draft | active | …
333
+ version: number;
334
+ createdAt: string;
335
+ updatedAt: string;
336
+ // …plus trigger/metadata fields — see models/workflow.ts
337
+ }
338
+
339
+ interface WorkflowInstance {
340
+ id: string;
341
+ definitionId: string;
342
+ status: string; // running | paused | completed | failed | …
343
+ currentNodeIds: string[]; // nodes currently active / awaiting input
344
+ variables: Record<string, unknown>;
345
+ nodeStates: Record<string, unknown>; // per-node execution state
346
+ createdAt: string;
347
+ updatedAt: string;
348
+ }
349
+ ```
350
+
351
+ **Node types** (`NodeType` consts): `trigger`, `humanAction`, `agent`, `action`,
352
+ `logic`, `loop`, `parallel`, `delay`, `data`, `integration`, `timer`, `subflow`,
353
+ `custom`, `accounting`, `documentExtraction`. Each has a `NodeSubType` family
354
+ (e.g. `humanActionApproval`, `agentChat`, `actionSendEmail`,
355
+ `logicIf`/`logicSwitch`/`logicFor`). The full enumerations live in
356
+ `models/workflow.ts` — reference them by the exported consts, never string
357
+ literals.
358
+
359
+ ---
360
+
361
+ ## Store, schema & components (other rings)
362
+
363
+ - **Redux store** (`@elqnt/workflow`, root): `workflowDefinitionReducer` and
364
+ `workflowInstanceReducer` (+ their `*State` / `*Selector` types). Mount them in
365
+ your `configureStore`. These are how the editor UI holds in-progress
366
+ definition/instance state client-side.
367
+ - **JSON schemas** (`@elqnt/workflow`, root, via `./schema`): generated JSON
368
+ Schemas for node config validation.
369
+ - **Components** (`@elqnt/workflow/components`, also re-exported from root):
370
+ `SchemaBuilder` and `DynamicSchemaForm` — `"use client"` React building blocks
371
+ for editing node config schemas and rendering schema-driven forms.
372
+
373
+ ---
374
+
375
+ ## The domain layer
376
+
377
+ A real custom app stacks **models → app hook → context → components** on top of
378
+ the package hooks, so components never see `WorkflowDefinition`/`WorkflowInstance`
379
+ or the gateway directly.
380
+
381
+ | Layer | Where (your app) | Role |
382
+ |---|---|---|
383
+ | Components | `components/<domain>/*` | render; consume the context; see only the domain type |
384
+ | Context | `contexts/<domain>-context.tsx` | one shared app-hook instance for the subtree |
385
+ | App hook | `hooks/use-app-workflows.ts` | CRUD + state; **the only file importing `@elqnt/workflow`** |
386
+ | Domain type | `models/<domain>.ts` | the camelCase business type |
387
+ | Config | `contexts/app-config-context.tsx` | `{ apiGatewayUrl, orgId }` from SSR |
388
+ | — package — | `@elqnt/workflow/{hooks,api,models}` | the gateway client the app hook wraps |
389
+
390
+ ```ts
391
+ // hooks/use-app-workflows.ts — the ONLY file that imports @elqnt/workflow
392
+ "use client";
393
+ import { useMemo } from "react";
394
+ import { useWorkflows, useWorkflowInstances } from "@elqnt/workflow/hooks";
395
+ import { useAppConfig } from "@/contexts/app-config-context";
396
+
397
+ export function useAppWorkflows() {
398
+ const { apiGatewayUrl, orgId } = useAppConfig();
399
+ const defs = useWorkflows({ baseUrl: apiGatewayUrl, orgId, product: "my-product" });
400
+ const inst = useWorkflowInstances({ baseUrl: apiGatewayUrl, orgId, product: "my-product" });
401
+
402
+ return useMemo(() => ({
403
+ loading: defs.loading || inst.loading,
404
+ error: defs.error ?? inst.error,
405
+ listWorkflows: defs.listWorkflows,
406
+ startRun: (definitionId: string) => inst.createInstance(definitionId, { autoExecute: true }),
407
+ approve: (instanceId: string, nodeId: string, result: Record<string, unknown>) =>
408
+ inst.resumeNode(instanceId, nodeId, result),
409
+ }), [defs, inst]);
410
+ }
411
+
412
+ export type UseAppWorkflowsReturn = ReturnType<typeof useAppWorkflows>;
413
+ ```
414
+
415
+ Read the gateway URL **server-side at request time from
416
+ `API_GATEWAY_URL_PUBLIC`** and forward it through `AppConfigProvider` — do **not**
417
+ use a `NEXT_PUBLIC_*` var (those bake the build-env URL into the bundle forever).
418
+
419
+ ---
420
+
421
+ ## Gotchas
422
+
423
+ - **Three hooks, reached only via `@elqnt/workflow/hooks`.** The root barrel
424
+ re-exports models / api / store / schema / components — **never** the hooks
425
+ (rule 1, the root-barrel rule). Import hooks from the `/hooks` subpath so
426
+ `"use client"` is never dragged into a server-component import.
427
+ - **Methods don't throw.** They return defaults (`[]` / `null` / `false`) and set
428
+ `error`. Check `error` / null results; don't wrap in try/catch expecting throws.
429
+ - **The package has no provider.** Options go into every hook call — which is why
430
+ your app builds `AppConfigProvider` (inject `baseUrl`/`orgId` once via SSR) and
431
+ the domain context, so components and the app hook never re-thread config.
432
+ - **Components never import `@elqnt/workflow`.** It's wrapped in one app hook;
433
+ components speak the domain type via the context.
434
+ - **`product` matters.** The gateway resolves product from the **JWT claim**
435
+ first (server) and `X-Product` (browser). Mismatched product = wrong tenancy.
436
+ - **`WorkflowTemplate` lives in `@elqnt/workflow/api`, not `/models`.** The
437
+ template DTO + its responses are defined alongside the API fns.
438
+ - **Human-in-the-loop = `resumeNode`.** A paused `humanAction` node resumes only
439
+ when you POST the human's `result` via `resumeNode`; `retryNode` re-runs a
440
+ failed node. Use the node's `id` from the instance's `currentNodeIds`.
441
+ - **Hooks are browser-only.** For SSR / server actions call the `@elqnt/workflow/api`
442
+ fns (or `createServerClient`) directly — same signatures, no React.
package/dist/api/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
3
 
4
4
 
@@ -14,8 +14,8 @@
14
14
 
15
15
 
16
16
 
17
+ var _chunkYCO5IFZKjs = require('../chunk-YCO5IFZK.js');
17
18
 
18
- var _chunkJES2EBNOjs = require('../chunk-JES2EBNO.js');
19
19
 
20
20
 
21
21
 
@@ -31,6 +31,5 @@ var _chunkJES2EBNOjs = require('../chunk-JES2EBNO.js');
31
31
 
32
32
 
33
33
 
34
-
35
- exports.createWorkflowApi = _chunkJES2EBNOjs.createWorkflowApi; exports.createWorkflowInstanceApi = _chunkJES2EBNOjs.createWorkflowInstanceApi; exports.deleteWorkflowApi = _chunkJES2EBNOjs.deleteWorkflowApi; exports.executeWorkflowNodeApi = _chunkJES2EBNOjs.executeWorkflowNodeApi; exports.getWorkflowApi = _chunkJES2EBNOjs.getWorkflowApi; exports.getWorkflowInstanceApi = _chunkJES2EBNOjs.getWorkflowInstanceApi; exports.getWorkflowTemplateApi = _chunkJES2EBNOjs.getWorkflowTemplateApi; exports.instantiateWorkflowTemplateApi = _chunkJES2EBNOjs.instantiateWorkflowTemplateApi; exports.listWorkflowInstancesApi = _chunkJES2EBNOjs.listWorkflowInstancesApi; exports.listWorkflowTemplatesApi = _chunkJES2EBNOjs.listWorkflowTemplatesApi; exports.listWorkflowsApi = _chunkJES2EBNOjs.listWorkflowsApi; exports.resumeWorkflowNodeApi = _chunkJES2EBNOjs.resumeWorkflowNodeApi; exports.retryWorkflowNodeApi = _chunkJES2EBNOjs.retryWorkflowNodeApi; exports.updateWorkflowApi = _chunkJES2EBNOjs.updateWorkflowApi; exports.updateWorkflowInstanceStatusApi = _chunkJES2EBNOjs.updateWorkflowInstanceStatusApi;
34
+ exports.createWorkflowApi = _chunkYCO5IFZKjs.createWorkflowApi; exports.createWorkflowInstanceApi = _chunkYCO5IFZKjs.createWorkflowInstanceApi; exports.deleteWorkflowApi = _chunkYCO5IFZKjs.deleteWorkflowApi; exports.executeWorkflowNodeApi = _chunkYCO5IFZKjs.executeWorkflowNodeApi; exports.getWorkflowApi = _chunkYCO5IFZKjs.getWorkflowApi; exports.getWorkflowInstanceApi = _chunkYCO5IFZKjs.getWorkflowInstanceApi; exports.getWorkflowTemplateApi = _chunkYCO5IFZKjs.getWorkflowTemplateApi; exports.instantiateWorkflowTemplateApi = _chunkYCO5IFZKjs.instantiateWorkflowTemplateApi; exports.listWorkflowInstancesApi = _chunkYCO5IFZKjs.listWorkflowInstancesApi; exports.listWorkflowTemplatesApi = _chunkYCO5IFZKjs.listWorkflowTemplatesApi; exports.listWorkflowsApi = _chunkYCO5IFZKjs.listWorkflowsApi; exports.resumeWorkflowNodeApi = _chunkYCO5IFZKjs.resumeWorkflowNodeApi; exports.retryWorkflowNodeApi = _chunkYCO5IFZKjs.retryWorkflowNodeApi; exports.updateWorkflowApi = _chunkYCO5IFZKjs.updateWorkflowApi; exports.updateWorkflowInstanceStatusApi = _chunkYCO5IFZKjs.updateWorkflowInstanceStatusApi;
36
35
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/eloquent/eloquent/packages/@elqnt/workflow/dist/api/index.js"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,uDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,qkCAAC","file":"/home/runner/work/eloquent/eloquent/packages/@elqnt/workflow/dist/api/index.js"}
1
+ {"version":3,"sources":["/home/runner/work/eloquent/eloquent/packages/@elqnt/workflow/dist/api/index.js"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,uDAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF,qkCAAC","file":"/home/runner/work/eloquent/eloquent/packages/@elqnt/workflow/dist/api/index.js"}
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  import {
3
2
  createWorkflowApi,
4
3
  createWorkflowInstanceApi,
@@ -15,7 +14,7 @@ import {
15
14
  retryWorkflowNodeApi,
16
15
  updateWorkflowApi,
17
16
  updateWorkflowInstanceStatusApi
18
- } from "../chunk-UE4ZBFLG.mjs";
17
+ } from "../chunk-4AUWFGQK.mjs";
19
18
  export {
20
19
  createWorkflowApi,
21
20
  createWorkflowInstanceApi,