create-bcp-app 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 CHANGED
@@ -92,10 +92,10 @@ Example:
92
92
  "schemaVersion": 1,
93
93
  "framework": "bcp",
94
94
  "projectName": "my-app",
95
- "frameworkPackage": "npm:@chidchanun/bcp@0.2.10",
95
+ "frameworkPackage": "npm:@chidchanun/bcp@0.2.11",
96
96
  "createdWith": {
97
97
  "package": "create-bcp-app",
98
- "version": "0.2.10"
98
+ "version": "0.2.11"
99
99
  },
100
100
  "packageManager": "npm",
101
101
  "presets": {
@@ -263,87 +263,87 @@ const worker =
263
263
  });
264
264
  ```
265
265
 
266
- Inspect failed jobs:
266
+ BCP intentionally does not install a Redis client library. Applications own the Redis connection and can pass a minimal `RedisCommandClient` to `createRedisJobQueueAdapter()` and `createRedisJobScheduleStore()`.
267
267
 
268
- ```ts
269
- const deadLetters =
270
- await jobs.deadLetters();
271
- ```
272
-
273
- Requeue:
274
-
275
- ```ts
276
- await jobs.requeueDeadLetter(
277
- deadLetters[0].id,
278
- {
279
- resetAttempts: true,
280
- }
281
- );
282
- ```
268
+ `bcp/jobs` is server-only and must not be imported into page/client bundles.
283
269
 
284
- ### Redis-compatible durable adapters
270
+ ## Workflow orchestration — 0.2.11+
285
271
 
286
- BCP intentionally does not install a Redis client library. Applications own the Redis connection and pass a minimal command client to BCP:
272
+ Generated applications can define persistent backend workflows with the new server-only `bcp/workflow` entrypoint:
287
273
 
288
274
  ```ts
289
- interface RedisCommandClient {
290
- sendCommand(
291
- command: string[]
292
- ): Promise<unknown>;
293
- }
275
+ import {
276
+ createWorkflow,
277
+ } from "bcp/workflow";
278
+
279
+ export const onboarding =
280
+ createWorkflow<{
281
+ userId: number;
282
+ }>(
283
+ "user.onboarding",
284
+ workflow => {
285
+ workflow.step(
286
+ "profile",
287
+ createProfile
288
+ );
289
+
290
+ workflow.parallel(
291
+ "initialize",
292
+ parallel => {
293
+ parallel.step(
294
+ "preferences",
295
+ createPreferences
296
+ );
297
+ parallel.step(
298
+ "workspace",
299
+ createWorkspace
300
+ );
301
+ }
302
+ );
303
+
304
+ workflow.delay(
305
+ "cooldown",
306
+ 1_000
307
+ );
308
+ }
309
+ );
294
310
  ```
295
311
 
296
- Queue adapter:
312
+ Step-level retry and compensation are supported:
297
313
 
298
314
  ```ts
299
- import {
300
- createJobQueue,
301
- createRedisJobQueueAdapter,
302
- } from "bcp/jobs";
303
-
304
- const adapter =
305
- createRedisJobQueueAdapter({
306
- client: redisCommandClient,
307
- namespace: "my-app:{jobs}",
308
- });
309
-
310
- export const jobs =
311
- createJobQueue({
312
- adapter,
313
- });
315
+ workflow.step(
316
+ "reserve-stock",
317
+ reserveStock,
318
+ {
319
+ maxAttempts: 3,
320
+ retryDelayMs: 1_000,
321
+ compensate:
322
+ releaseStock,
323
+ }
324
+ );
314
325
  ```
315
326
 
316
- Scheduler store:
327
+ For durable queue-backed execution, pass an existing `BackgroundJobQueue` and a durable shared `WorkflowStore`:
317
328
 
318
329
  ```ts
319
- import {
320
- createJobScheduler,
321
- createRedisJobScheduleStore,
322
- } from "bcp/jobs";
323
-
324
- const store =
325
- createRedisJobScheduleStore({
326
- client: redisCommandClient,
327
- namespace: "my-app:{jobs}",
328
- });
329
-
330
- export const scheduler =
331
- createJobScheduler({
332
- queue: jobs,
333
- store,
334
- ownerId: "scheduler-a",
335
- });
330
+ const fulfillment =
331
+ createWorkflow(
332
+ "order.fulfillment",
333
+ defineWorkflow,
334
+ {
335
+ queue: jobs,
336
+ store:
337
+ workflowStore,
338
+ }
339
+ );
336
340
  ```
337
341
 
338
- A typical application may configure its Redis client with:
342
+ The built-in `createMemoryWorkflowStore()` is intended for development/tests. Multi-instance production stores should make `claim()` atomic so only one executor owns a workflow run lease at a time.
339
343
 
340
- ```dotenv
341
- REDIS_URL=redis://localhost:6379
342
- ```
344
+ External side effects should remain idempotent because durable queue execution is at-least-once.
343
345
 
344
- BCP does not read `REDIS_URL` automatically. Redis credentials, TLS/Cluster/Sentinel configuration and connection lifecycle stay application-owned.
345
-
346
- `bcp/jobs` is server-only and must not be imported into page/client bundles.
346
+ `bcp/workflow` is server-only and cannot be imported into page/client bundles.
347
347
 
348
348
  ## Application Packaging — 0.2.4+
349
349
 
@@ -390,5 +390,5 @@ bcp generate migration create_users
390
390
  The `--bcp` option is mainly for prerelease/local package verification:
391
391
 
392
392
  ```bash
393
- npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.10.tgz
393
+ npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.11.tgz
394
394
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bcp-app",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Create a new BCP Framework application.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -216,6 +216,84 @@ The processing model is at-least-once, so side-effecting job handlers should be
216
216
 
217
217
  `bcp/jobs` is server-only and cannot be imported into page/client bundles.
218
218
 
219
+ ## Workflow orchestration — BCP 0.2.11+
220
+
221
+ Define persistent server-side workflows through `bcp/workflow`:
222
+
223
+ ```ts
224
+ import {
225
+ createWorkflow,
226
+ } from "bcp/workflow";
227
+
228
+ export const onboarding =
229
+ createWorkflow<{
230
+ userId: number;
231
+ }>(
232
+ "user.onboarding",
233
+ workflow => {
234
+ workflow.step(
235
+ "profile",
236
+ createProfile
237
+ );
238
+
239
+ workflow.parallel(
240
+ "initialize",
241
+ parallel => {
242
+ parallel.step(
243
+ "preferences",
244
+ createPreferences
245
+ );
246
+ parallel.step(
247
+ "workspace",
248
+ createWorkspace
249
+ );
250
+ }
251
+ );
252
+
253
+ workflow.delay(
254
+ "cooldown",
255
+ 1_000
256
+ );
257
+ }
258
+ );
259
+ ```
260
+
261
+ Step retry and compensation:
262
+
263
+ ```ts
264
+ workflow.step(
265
+ "reserve-stock",
266
+ reserveStock,
267
+ {
268
+ maxAttempts: 3,
269
+ retryDelayMs: 1_000,
270
+ compensate:
271
+ releaseStock,
272
+ }
273
+ );
274
+ ```
275
+
276
+ For queue-backed execution:
277
+
278
+ ```ts
279
+ const fulfillment =
280
+ createWorkflow(
281
+ "order.fulfillment",
282
+ defineWorkflow,
283
+ {
284
+ queue: jobs,
285
+ store:
286
+ workflowStore,
287
+ }
288
+ );
289
+ ```
290
+
291
+ The default `createMemoryWorkflowStore()` is for local development/tests. Multi-instance production deployments should implement a shared durable `WorkflowStore` with atomic `claim()` behavior.
292
+
293
+ Workflow handlers that perform external side effects should be idempotent because durable job execution is at-least-once.
294
+
295
+ `bcp/workflow` is server-only and cannot be imported into page/client bundles.
296
+
219
297
  ## Generate framework files
220
298
 
221
299
  ```bash