@chidchanun/bcp 0.2.10 → 0.2.11
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/README.md +118 -45
- package/docs/README.md +47 -45
- package/docs/api-manifest.json +13 -1
- package/docs/api-reference.md +59 -15
- package/docs/docs-web-manifest.json +5 -3
- package/docs/platform-manifest.json +15 -4
- package/docs/releases/0.2.11.md +180 -0
- package/docs/workflow-orchestration.md +374 -0
- package/package.json +6 -1
- package/packages/bundler/src/client-boundary.ts +1 -0
- package/packages/client/src/workflow.mjs +601 -0
- package/packages/client/src/workflow.ts +23 -0
- package/packages/server/src/workflow.ts +887 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# BCP Framework 0.2.11 — Workflow Orchestration
|
|
2
|
+
|
|
3
|
+
> Release state: unreleased development target until RC validation, tagging and npm publication complete.
|
|
4
|
+
|
|
5
|
+
`0.2.11` adds server-side workflow orchestration on top of the durable jobs foundation introduced in `0.2.8`–`0.2.10`.
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- new `bcp/workflow` server-only public entrypoint,
|
|
10
|
+
- sequential workflow steps,
|
|
11
|
+
- parallel step groups,
|
|
12
|
+
- per-step retry policies,
|
|
13
|
+
- persisted workflow delays,
|
|
14
|
+
- manual resume/retry/cancel controls,
|
|
15
|
+
- saga-style compensation in reverse completion order,
|
|
16
|
+
- `WorkflowStore` persistence contract,
|
|
17
|
+
- workflow run leases via atomic `claim()` / `release()`,
|
|
18
|
+
- optional `bcp/jobs` queue-backed execution,
|
|
19
|
+
- delayed workflow continuation through the job queue,
|
|
20
|
+
- compiled `workflow.mjs` runtime in the npm package,
|
|
21
|
+
- browser/client bundle boundary enforcement,
|
|
22
|
+
- unit and prepared-package smoke coverage.
|
|
23
|
+
|
|
24
|
+
## Basic API
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {
|
|
28
|
+
createWorkflow,
|
|
29
|
+
} from "bcp/workflow";
|
|
30
|
+
|
|
31
|
+
const onboarding =
|
|
32
|
+
createWorkflow<{
|
|
33
|
+
userId: number;
|
|
34
|
+
}>(
|
|
35
|
+
"user.onboarding",
|
|
36
|
+
workflow => {
|
|
37
|
+
workflow.step(
|
|
38
|
+
"profile",
|
|
39
|
+
createProfile
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
workflow.parallel(
|
|
43
|
+
"initialize",
|
|
44
|
+
parallel => {
|
|
45
|
+
parallel.step(
|
|
46
|
+
"preferences",
|
|
47
|
+
createPreferences
|
|
48
|
+
);
|
|
49
|
+
parallel.step(
|
|
50
|
+
"workspace",
|
|
51
|
+
createWorkspace
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
workflow.delay(
|
|
57
|
+
"cooldown",
|
|
58
|
+
1_000
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Queue-backed workflows
|
|
65
|
+
|
|
66
|
+
Applications may provide an existing `BackgroundJobQueue`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const workflow =
|
|
70
|
+
createWorkflow(
|
|
71
|
+
"order.fulfillment",
|
|
72
|
+
defineWorkflow,
|
|
73
|
+
{
|
|
74
|
+
queue: jobs,
|
|
75
|
+
store:
|
|
76
|
+
workflowStore,
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`start()`, `retry()` and `resume()` then enqueue workflow execution rather than executing the run inline. Delay steps enqueue delayed continuation jobs.
|
|
82
|
+
|
|
83
|
+
The queue retains responsibility for delivery, worker concurrency, visibility timeout, heartbeat and stale-job recovery.
|
|
84
|
+
|
|
85
|
+
## Persistence
|
|
86
|
+
|
|
87
|
+
`createMemoryWorkflowStore()` is included for development/tests.
|
|
88
|
+
|
|
89
|
+
Production multi-instance deployments should implement `WorkflowStore` with shared durable storage. `claim()` must atomically lease a workflow run so multiple workers cannot execute the same run concurrently.
|
|
90
|
+
|
|
91
|
+
## Retry behavior
|
|
92
|
+
|
|
93
|
+
A step can define:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
{
|
|
97
|
+
maxAttempts: 3,
|
|
98
|
+
retryDelayMs:
|
|
99
|
+
attempt =>
|
|
100
|
+
attempt * 1_000,
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
A run that remains failed after its step retry policy is exhausted can later be retried with:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
await workflow.retry(
|
|
108
|
+
runId
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Already-successful preceding steps remain complete.
|
|
113
|
+
|
|
114
|
+
## Compensation
|
|
115
|
+
|
|
116
|
+
Steps can define compensation handlers:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
workflow.step(
|
|
120
|
+
"reserve-stock",
|
|
121
|
+
reserveStock,
|
|
122
|
+
{
|
|
123
|
+
compensate:
|
|
124
|
+
releaseStock,
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Then:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
await workflow.compensate(
|
|
133
|
+
runId
|
|
134
|
+
);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Successful compensatable steps execute in reverse completion order.
|
|
138
|
+
|
|
139
|
+
## Delays
|
|
140
|
+
|
|
141
|
+
Delay steps persist `waiting` state and `waitUntil` rather than holding a long-running process timer.
|
|
142
|
+
|
|
143
|
+
Without a queue, application code calls `resume()` after the due time. With a queue, BCP schedules a delayed workflow continuation job.
|
|
144
|
+
|
|
145
|
+
## Compatibility
|
|
146
|
+
|
|
147
|
+
`0.2.11` is additive relative to `0.2.10`.
|
|
148
|
+
|
|
149
|
+
Existing `bcp/jobs`, scheduler, Redis durable adapters and all prior public entrypoints remain supported.
|
|
150
|
+
|
|
151
|
+
No intentional breaking changes are introduced from the `0.2.10` baseline.
|
|
152
|
+
|
|
153
|
+
## Validation
|
|
154
|
+
|
|
155
|
+
Before release:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npm run typecheck
|
|
159
|
+
npm run test:unit
|
|
160
|
+
npm run test:integration
|
|
161
|
+
npm run test:e2e
|
|
162
|
+
npm run test:package
|
|
163
|
+
npm run rc:check
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Workflow validation covers:
|
|
167
|
+
|
|
168
|
+
- sequential step execution,
|
|
169
|
+
- transient step retry,
|
|
170
|
+
- parallel groups,
|
|
171
|
+
- persisted delay/resume semantics,
|
|
172
|
+
- manual failed-run retry,
|
|
173
|
+
- reverse compensation order,
|
|
174
|
+
- queue-backed execution,
|
|
175
|
+
- workflow store lease behavior,
|
|
176
|
+
- browser bundle rejection,
|
|
177
|
+
- compiled `workflow.mjs` package execution,
|
|
178
|
+
- API/platform/docs manifest parity.
|
|
179
|
+
|
|
180
|
+
The final release tag must point to the exact commit that passed the complete RC sequence.
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# Workflow Orchestration
|
|
2
|
+
|
|
3
|
+
BCP `0.2.11` adds a server-only workflow orchestration layer through `bcp/workflow`.
|
|
4
|
+
|
|
5
|
+
The workflow runtime is designed for application processes that need to coordinate multiple backend operations as one observable run instead of manually chaining unrelated jobs.
|
|
6
|
+
|
|
7
|
+
## Public entrypoint
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
createMemoryWorkflowStore,
|
|
12
|
+
createWorkflow,
|
|
13
|
+
} from "bcp/workflow";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`bcp/workflow` is server-only and must not be imported into page/client bundles.
|
|
17
|
+
|
|
18
|
+
## Basic workflow
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
createWorkflow,
|
|
23
|
+
} from "bcp/workflow";
|
|
24
|
+
|
|
25
|
+
export const onboarding =
|
|
26
|
+
createWorkflow<{
|
|
27
|
+
userId: number;
|
|
28
|
+
}>(
|
|
29
|
+
"user.onboarding",
|
|
30
|
+
workflow => {
|
|
31
|
+
workflow.step(
|
|
32
|
+
"create-profile",
|
|
33
|
+
async ({ input }) => {
|
|
34
|
+
await createProfile(
|
|
35
|
+
input.userId
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
workflow.step(
|
|
41
|
+
"send-email",
|
|
42
|
+
async ({ input }) => {
|
|
43
|
+
await sendWelcomeEmail(
|
|
44
|
+
input.userId
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Start a run:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const run =
|
|
56
|
+
await onboarding.start({
|
|
57
|
+
userId: 42,
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Without a background queue, `start()` executes immediately until the workflow either succeeds, fails, is cancelled or reaches a delay.
|
|
62
|
+
|
|
63
|
+
## Step retries
|
|
64
|
+
|
|
65
|
+
Each step can define its own retry policy:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
workflow.step(
|
|
69
|
+
"charge-card",
|
|
70
|
+
chargeCard,
|
|
71
|
+
{
|
|
72
|
+
maxAttempts: 3,
|
|
73
|
+
retryDelayMs:
|
|
74
|
+
attempt =>
|
|
75
|
+
attempt * 1_000,
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The runtime persists the step attempt count and terminal error in the workflow run record.
|
|
81
|
+
|
|
82
|
+
Step retries happen inside the workflow execution attempt. Long-running production workflows should prefer idempotent handlers so a process restart or at-least-once queue delivery can safely repeat work.
|
|
83
|
+
|
|
84
|
+
## Parallel steps
|
|
85
|
+
|
|
86
|
+
Use `parallel()` when independent operations can run together:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
workflow.parallel(
|
|
90
|
+
"initialize",
|
|
91
|
+
parallel => {
|
|
92
|
+
parallel.step(
|
|
93
|
+
"preferences",
|
|
94
|
+
createPreferences
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
parallel.step(
|
|
98
|
+
"workspace",
|
|
99
|
+
createDefaultWorkspace
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
All child step records are persisted under the parent parallel step.
|
|
106
|
+
|
|
107
|
+
The parallel group succeeds only when every child succeeds.
|
|
108
|
+
|
|
109
|
+
## Delays
|
|
110
|
+
|
|
111
|
+
A workflow delay is persisted instead of blocking a process with a long timer:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
workflow.delay(
|
|
115
|
+
"cooldown",
|
|
116
|
+
5 * 60 * 1000
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The run transitions to:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
waiting
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
with a persisted `waitUntil` timestamp.
|
|
127
|
+
|
|
128
|
+
For an in-process workflow, resume after the timestamp:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
await workflow.resume(
|
|
132
|
+
run.id
|
|
133
|
+
);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Calling `resume()` before `waitUntil` returns the current waiting run unchanged.
|
|
137
|
+
|
|
138
|
+
For administrative tooling, a forced resume is available:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
await workflow.resume(
|
|
142
|
+
run.id,
|
|
143
|
+
{
|
|
144
|
+
force: true,
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Queue-backed execution
|
|
150
|
+
|
|
151
|
+
A workflow can use the BCP background job queue as its execution layer:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import {
|
|
155
|
+
createJobQueue,
|
|
156
|
+
} from "bcp/jobs";
|
|
157
|
+
import {
|
|
158
|
+
createWorkflow,
|
|
159
|
+
} from "bcp/workflow";
|
|
160
|
+
|
|
161
|
+
const jobs =
|
|
162
|
+
createJobQueue({
|
|
163
|
+
adapter:
|
|
164
|
+
redisQueueAdapter,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export const workflow =
|
|
168
|
+
createWorkflow(
|
|
169
|
+
"order.fulfillment",
|
|
170
|
+
defineOrderWorkflow,
|
|
171
|
+
{
|
|
172
|
+
queue: jobs,
|
|
173
|
+
store:
|
|
174
|
+
workflowStore,
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
When a queue is configured:
|
|
180
|
+
|
|
181
|
+
- `start()` persists the run and enqueues execution,
|
|
182
|
+
- `retry()` enqueues another execution attempt,
|
|
183
|
+
- `resume()` enqueues continuation,
|
|
184
|
+
- workflow delays enqueue a delayed continuation job,
|
|
185
|
+
- worker concurrency, visibility leases, heartbeat and stale-job recovery remain owned by `bcp/jobs`.
|
|
186
|
+
|
|
187
|
+
This keeps job delivery and workflow state as separate contracts.
|
|
188
|
+
|
|
189
|
+
## Workflow store
|
|
190
|
+
|
|
191
|
+
`WorkflowStore` is the persistence boundary:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
interface WorkflowStore {
|
|
195
|
+
create(run): Promise<void>;
|
|
196
|
+
update(run): Promise<void>;
|
|
197
|
+
get(id): Promise<WorkflowRunRecord | null>;
|
|
198
|
+
list(workflowName?): Promise<WorkflowRunRecord[]>;
|
|
199
|
+
|
|
200
|
+
claim(
|
|
201
|
+
id,
|
|
202
|
+
{
|
|
203
|
+
ownerId,
|
|
204
|
+
now,
|
|
205
|
+
leaseMs,
|
|
206
|
+
}
|
|
207
|
+
): Promise<boolean>;
|
|
208
|
+
|
|
209
|
+
release(
|
|
210
|
+
id,
|
|
211
|
+
ownerId
|
|
212
|
+
): Promise<void>;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The built-in:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
createMemoryWorkflowStore()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
is intended for local development and deterministic tests.
|
|
223
|
+
|
|
224
|
+
Production multi-instance deployments should use a shared durable store whose `claim()` operation is atomic.
|
|
225
|
+
|
|
226
|
+
The claim is the workflow concurrency boundary. Only one owner should execute a run while its lease is active.
|
|
227
|
+
|
|
228
|
+
## Run states
|
|
229
|
+
|
|
230
|
+
Workflow runs use:
|
|
231
|
+
|
|
232
|
+
```text
|
|
233
|
+
pending
|
|
234
|
+
running
|
|
235
|
+
waiting
|
|
236
|
+
succeeded
|
|
237
|
+
failed
|
|
238
|
+
cancelled
|
|
239
|
+
compensating
|
|
240
|
+
compensated
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Step records use the corresponding lifecycle states where applicable.
|
|
244
|
+
|
|
245
|
+
## Inspect runs
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const run =
|
|
249
|
+
await workflow.get(
|
|
250
|
+
runId
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const runs =
|
|
254
|
+
await workflow.list();
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
A run includes:
|
|
258
|
+
|
|
259
|
+
- workflow name,
|
|
260
|
+
- input payload,
|
|
261
|
+
- current state,
|
|
262
|
+
- timestamps,
|
|
263
|
+
- current step,
|
|
264
|
+
- step attempts/errors,
|
|
265
|
+
- parallel child state,
|
|
266
|
+
- `waitUntil`,
|
|
267
|
+
- completion order,
|
|
268
|
+
- current workflow lease metadata.
|
|
269
|
+
|
|
270
|
+
## Manual retry
|
|
271
|
+
|
|
272
|
+
A failed run can retry its failed step without rerunning already successful preceding steps:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
await workflow.retry(
|
|
276
|
+
runId
|
|
277
|
+
);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The failed step attempt counter is reset for the new manual retry cycle.
|
|
281
|
+
|
|
282
|
+
## Compensation
|
|
283
|
+
|
|
284
|
+
A successful step can provide a compensation handler:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
workflow.step(
|
|
288
|
+
"reserve-stock",
|
|
289
|
+
reserveStock,
|
|
290
|
+
{
|
|
291
|
+
compensate:
|
|
292
|
+
releaseStock,
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
If a later step fails, application code can request compensation:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
await workflow.compensate(
|
|
301
|
+
runId
|
|
302
|
+
);
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Compensation runs successful compensatable steps in reverse completion order.
|
|
306
|
+
|
|
307
|
+
This is a saga-style compensation mechanism. It does not provide database transaction rollback across external services.
|
|
308
|
+
|
|
309
|
+
Cancellation can also request compensation:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
await workflow.cancel(
|
|
313
|
+
runId,
|
|
314
|
+
{
|
|
315
|
+
compensate: true,
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Idempotency
|
|
321
|
+
|
|
322
|
+
Workflow state and background queues provide orchestration and delivery coordination, but external side effects should still be idempotent.
|
|
323
|
+
|
|
324
|
+
For example, payment and email handlers should use application-level idempotency keys where duplicate execution would be harmful.
|
|
325
|
+
|
|
326
|
+
A durable queue provides at-least-once delivery semantics, not exactly-once external side effects.
|
|
327
|
+
|
|
328
|
+
## Recommended production topology
|
|
329
|
+
|
|
330
|
+
```text
|
|
331
|
+
API / application
|
|
332
|
+
|
|
|
333
|
+
v
|
|
334
|
+
WorkflowStore (shared durable state)
|
|
335
|
+
|
|
|
336
|
+
v
|
|
337
|
+
BCP Workflow
|
|
338
|
+
|
|
|
339
|
+
v
|
|
340
|
+
Durable JobQueueAdapter
|
|
341
|
+
|
|
|
342
|
+
+---+---+
|
|
343
|
+
| |
|
|
344
|
+
Worker A Worker B
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The workflow store and queue do not have to use the same backend.
|
|
348
|
+
|
|
349
|
+
## Lifecycle
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
await workflow.close();
|
|
353
|
+
await jobs.close();
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`workflow.close()` unregisters its internal queue handler and closes the configured workflow store when the store exposes `close()`.
|
|
357
|
+
|
|
358
|
+
The application remains responsible for lifecycle of external database/Redis clients used by custom stores/adapters.
|
|
359
|
+
|
|
360
|
+
## Current scope
|
|
361
|
+
|
|
362
|
+
`0.2.11` intentionally focuses on the orchestration core:
|
|
363
|
+
|
|
364
|
+
- sequential steps,
|
|
365
|
+
- parallel steps,
|
|
366
|
+
- step retries,
|
|
367
|
+
- persisted delays,
|
|
368
|
+
- manual retry/resume/cancel,
|
|
369
|
+
- compensation,
|
|
370
|
+
- workflow state persistence,
|
|
371
|
+
- run leases,
|
|
372
|
+
- optional `bcp/jobs` execution.
|
|
373
|
+
|
|
374
|
+
Transactional outbox/event delivery remains a separate future milestone so workflow orchestration does not pretend to make a database transaction and external queue publish atomic.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chidchanun/bcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -67,6 +67,11 @@
|
|
|
67
67
|
"browser": "./packages/client/src/server-only.browser.mjs",
|
|
68
68
|
"default": "./packages/client/src/jobs.mjs"
|
|
69
69
|
},
|
|
70
|
+
"./workflow": {
|
|
71
|
+
"types": "./packages/client/src/workflow.ts",
|
|
72
|
+
"browser": "./packages/client/src/server-only.browser.mjs",
|
|
73
|
+
"default": "./packages/client/src/workflow.mjs"
|
|
74
|
+
},
|
|
70
75
|
"./observability": {
|
|
71
76
|
"types": "./packages/client/src/observability.ts",
|
|
72
77
|
"browser": "./packages/client/src/server-only.browser.mjs",
|