create-bcp-app 0.2.9 → 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.9",
95
+ "frameworkPackage": "npm:@chidchanun/bcp@0.2.11",
96
96
  "createdWith": {
97
97
  "package": "create-bcp-app",
98
- "version": "0.2.9"
98
+ "version": "0.2.11"
99
99
  },
100
100
  "packageManager": "npm",
101
101
  "presets": {
@@ -107,9 +107,7 @@ Example:
107
107
  }
108
108
  ```
109
109
 
110
- This manifest records scaffold identity only. It is intended for framework diagnostics, update/migration tooling and project-aware documentation. It must not contain secrets.
111
-
112
- The file should normally be committed to source control.
110
+ This manifest records scaffold identity only. It must not contain secrets and should normally be committed to source control.
113
111
 
114
112
  ## Generated application defaults
115
113
 
@@ -157,24 +155,6 @@ The database choice adds starter configuration and the matching driver:
157
155
 
158
156
  For **MySQL**, **PostgreSQL** and **SQLite**, generated `lib/database.ts` exposes framework database primitives through `bcp/database`.
159
157
 
160
- Generated examples:
161
-
162
- ```dotenv
163
- DB_HOST=localhost
164
- DB_PORT=3306
165
- DB_USER=
166
- DB_PASSWORD=
167
- DB_NAME=bcp_app
168
- ```
169
-
170
- ```dotenv
171
- DATABASE_URL=postgresql://postgres:password@localhost:5432/bcp_app
172
- ```
173
-
174
- ```dotenv
175
- DATABASE_URL=./data/bcp.sqlite
176
- ```
177
-
178
158
  The generator intentionally does not force an ORM.
179
159
 
180
160
  ## Storage providers
@@ -193,16 +173,7 @@ BCP_SESSION_SECRET=
193
173
 
194
174
  Set this to a cryptographically random secret of at least 32 bytes before real authentication use.
195
175
 
196
- BCP `0.2.5+` can opt into revocable server-side auth state:
197
-
198
- ```ts
199
- import {
200
- createAuth,
201
- createMemoryAuthSessionStore,
202
- } from "bcp/auth";
203
- ```
204
-
205
- The memory store is intended for development/tests. Multi-process production deployments should implement `AuthSessionStore` using shared durable storage.
176
+ BCP `0.2.5+` can opt into revocable server-side auth state through `AuthSessionStore`.
206
177
 
207
178
  ## Authorization & request security — 0.2.6+
208
179
 
@@ -226,7 +197,7 @@ import {
226
197
 
227
198
  ## Background jobs — 0.2.8+
228
199
 
229
- Generated projects can create a server-only background job queue without adding another package:
200
+ Generated projects can create a server-only background job queue:
230
201
 
231
202
  ```ts
232
203
  import {
@@ -237,7 +208,7 @@ export const jobs =
237
208
  createJobQueue();
238
209
  ```
239
210
 
240
- Workers support concurrency, delayed jobs, retry/backoff and cancellation. The default memory adapter is process-local and should be replaced with a durable `JobQueueAdapter` for production workloads that must survive restarts or coordinate across instances.
211
+ Workers support concurrency, delayed jobs, retry/backoff and cancellation.
241
212
 
242
213
  ## Job scheduling — 0.2.9+
243
214
 
@@ -278,20 +249,102 @@ await scheduler.schedule(
278
249
  );
279
250
  ```
280
251
 
281
- Start the scheduler loop:
252
+ ## Durable jobs — 0.2.10+
253
+
254
+ BCP `0.2.10` adds worker visibility leases, heartbeat renewal, stale-running recovery, DLQ/requeue, retention cleanup and queue statistics.
282
255
 
283
256
  ```ts
284
- const runner =
285
- scheduler.start({
286
- pollIntervalMs: 1_000,
287
- leaseMs: 30_000,
257
+ const worker =
258
+ jobs.startWorker({
259
+ workerId: "worker-a",
260
+ concurrency: 4,
261
+ visibilityTimeoutMs: 30_000,
262
+ heartbeatIntervalMs: 10_000,
288
263
  });
289
264
  ```
290
265
 
291
- The default schedule store is process-local. Multi-instance production deployments should implement a shared durable `JobScheduleStore`; its `acquireDue()` operation must atomically lease due schedules. A durable scheduler deployment normally also uses a shared durable `JobQueueAdapter`.
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()`.
292
267
 
293
268
  `bcp/jobs` is server-only and must not be imported into page/client bundles.
294
269
 
270
+ ## Workflow orchestration — 0.2.11+
271
+
272
+ Generated applications can define persistent backend workflows with the new server-only `bcp/workflow` entrypoint:
273
+
274
+ ```ts
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
+ );
310
+ ```
311
+
312
+ Step-level retry and compensation are supported:
313
+
314
+ ```ts
315
+ workflow.step(
316
+ "reserve-stock",
317
+ reserveStock,
318
+ {
319
+ maxAttempts: 3,
320
+ retryDelayMs: 1_000,
321
+ compensate:
322
+ releaseStock,
323
+ }
324
+ );
325
+ ```
326
+
327
+ For durable queue-backed execution, pass an existing `BackgroundJobQueue` and a durable shared `WorkflowStore`:
328
+
329
+ ```ts
330
+ const fulfillment =
331
+ createWorkflow(
332
+ "order.fulfillment",
333
+ defineWorkflow,
334
+ {
335
+ queue: jobs,
336
+ store:
337
+ workflowStore,
338
+ }
339
+ );
340
+ ```
341
+
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.
343
+
344
+ External side effects should remain idempotent because durable queue execution is at-least-once.
345
+
346
+ `bcp/workflow` is server-only and cannot be imported into page/client bundles.
347
+
295
348
  ## Application Packaging — 0.2.4+
296
349
 
297
350
  Generated projects include:
@@ -337,5 +390,5 @@ bcp generate migration create_users
337
390
  The `--bcp` option is mainly for prerelease/local package verification:
338
391
 
339
392
  ```bash
340
- npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.9.tgz
393
+ npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.11.tgz
341
394
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bcp-app",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Create a new BCP Framework application.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,17 +25,11 @@ npm start
25
25
  npm run update
26
26
  ```
27
27
 
28
- The generated scripts call commands such as `bcp dev`, `bcp generate`, `bcp build`, `bcp package` and `bcp start`. npm automatically adds the project's `node_modules/.bin` directory to `PATH` while an npm script is running.
28
+ The generated scripts call commands such as `bcp dev`, `bcp generate`, `bcp build`, `bcp package` and `bcp start`.
29
29
 
30
30
  ## Project metadata
31
31
 
32
- Projects created with BCP `0.1.29+` include:
33
-
34
- ```text
35
- bcp.project.json
36
- ```
37
-
38
- It records non-secret scaffold choices such as Tailwind, database, authentication and storage presets so BCP diagnostics/tooling can understand the project without guessing configuration from source files.
32
+ Projects created with BCP `0.1.29+` include `bcp.project.json` with non-secret scaffold metadata.
39
33
 
40
34
  Commit this file with the project. Do not put passwords, access keys, session secrets or tokens in it.
41
35
 
@@ -45,8 +39,6 @@ Projects created with the `JWT Cookie` preset remain stateless by default and us
45
39
 
46
40
  BCP Authentication Platform v2 can opt into revocable server-side session state with `createAuth()` and `AuthSessionStore`.
47
41
 
48
- The memory store is intended for local development/tests. Use a shared durable `AuthSessionStore` implementation for multi-process or multi-container production deployments.
49
-
50
42
  ## Authorization & request security — BCP 0.2.6+
51
43
 
52
44
  Use permission guards and resource policies from `bcp/auth`, and same-origin/CSRF protection from `bcp/server`.
@@ -55,8 +47,6 @@ Authorization must always be enforced server-side. Client UI visibility is not a
55
47
 
56
48
  ## Observability — BCP 0.2.7+
57
49
 
58
- Create process-local metrics and health/readiness registries:
59
-
60
50
  ```ts
61
51
  import {
62
52
  createHealthRegistry,
@@ -95,14 +85,8 @@ jobs.register<{
95
85
  );
96
86
  ```
97
87
 
98
- Workers support delayed jobs, retry/backoff, cancellation and configurable concurrency.
99
-
100
- The default memory adapter is process-local and not durable. For production jobs that must survive restarts or run across multiple processes/containers, implement `JobQueueAdapter` with shared durable infrastructure.
101
-
102
88
  ## Job scheduling — BCP 0.2.9+
103
89
 
104
- Add recurring schedules on top of the same queue:
105
-
106
90
  ```ts
107
91
  import {
108
92
  createJobScheduler,
@@ -114,50 +98,203 @@ export const scheduler =
114
98
  });
115
99
  ```
116
100
 
117
- Interval schedule:
101
+ Example UTC cron schedule:
118
102
 
119
103
  ```ts
120
104
  await scheduler.schedule(
121
- "cache.cleanup",
105
+ "report.weekday",
122
106
  {},
123
107
  {
124
- everyMs: 300_000,
108
+ cron: "30 9 * * 1-5",
125
109
  }
126
110
  );
127
111
  ```
128
112
 
129
- UTC cron schedule:
113
+ ## Durable jobs — BCP 0.2.10+
114
+
115
+ Workers can use visibility leases, heartbeat renewal and stale-running recovery:
130
116
 
131
117
  ```ts
132
- await scheduler.schedule(
133
- "report.weekday",
134
- {},
118
+ const worker =
119
+ jobs.startWorker({
120
+ workerId: "worker-a",
121
+ concurrency: 4,
122
+ visibilityTimeoutMs: 30_000,
123
+ heartbeatIntervalMs: 10_000,
124
+ });
125
+ ```
126
+
127
+ Retry-exhausted jobs are available through the adapter DLQ contract:
128
+
129
+ ```ts
130
+ const deadLetters =
131
+ await jobs.deadLetters();
132
+
133
+ await jobs.requeueDeadLetter(
134
+ deadLetters[0].id,
135
135
  {
136
- cron: "30 9 * * 1-5",
136
+ resetAttempts: true,
137
137
  }
138
138
  );
139
139
  ```
140
140
 
141
- Start and stop the scheduler lifecycle:
141
+ Operational helpers:
142
+
143
+ ```ts
144
+ await jobs.recoverStale();
145
+ const stats = await jobs.stats();
146
+ await jobs.cleanup({
147
+ before:
148
+ Date.now() -
149
+ 7 * 24 * 60 * 60 * 1000,
150
+ });
151
+ ```
152
+
153
+ ### Redis-compatible adapters
154
+
155
+ BCP does not install a Redis library. Supply an application-owned command client:
156
+
157
+ ```ts
158
+ interface RedisCommandClient {
159
+ sendCommand(
160
+ command: string[]
161
+ ): Promise<unknown>;
162
+ }
163
+ ```
164
+
165
+ Create a durable queue:
142
166
 
143
167
  ```ts
144
- const schedulerRunner =
145
- scheduler.start({
146
- pollIntervalMs: 1_000,
147
- leaseMs: 30_000,
168
+ import {
169
+ createJobQueue,
170
+ createRedisJobQueueAdapter,
171
+ } from "bcp/jobs";
172
+
173
+ const adapter =
174
+ createRedisJobQueueAdapter({
175
+ client: redisCommandClient,
176
+ namespace: "my-app:{jobs}",
177
+ });
178
+
179
+ export const jobs =
180
+ createJobQueue({
181
+ adapter,
182
+ });
183
+ ```
184
+
185
+ Create a shared schedule store:
186
+
187
+ ```ts
188
+ import {
189
+ createJobScheduler,
190
+ createRedisJobScheduleStore,
191
+ } from "bcp/jobs";
192
+
193
+ const store =
194
+ createRedisJobScheduleStore({
195
+ client: redisCommandClient,
196
+ namespace: "my-app:{jobs}",
148
197
  });
149
198
 
150
- await schedulerRunner.stop();
151
- await scheduler.close();
199
+ export const scheduler =
200
+ createJobScheduler({
201
+ queue: jobs,
202
+ store,
203
+ ownerId: "scheduler-a",
204
+ });
205
+ ```
206
+
207
+ A typical deployment may use:
208
+
209
+ ```dotenv
210
+ REDIS_URL=redis://localhost:6379
152
211
  ```
153
212
 
154
- The default `createMemoryJobScheduleStore()` is process-local. Multi-instance production deployments should implement a shared durable `JobScheduleStore` whose `acquireDue()` atomically leases due schedules. Durable deployments normally also use a shared `JobQueueAdapter`.
213
+ BCP does not read this variable automatically. The application owns Redis connection creation, credentials, TLS/Cluster configuration and shutdown.
214
+
215
+ The processing model is at-least-once, so side-effecting job handlers should be idempotent when duplicate execution is unsafe.
155
216
 
156
217
  `bcp/jobs` is server-only and cannot be imported into page/client bundles.
157
218
 
158
- ## Generate framework files
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.
159
292
 
160
- BCP can generate common project files:
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
+
297
+ ## Generate framework files
161
298
 
162
299
  ```bash
163
300
  npm run generate -- page dashboard/users
@@ -166,7 +303,7 @@ npm run generate -- middleware
166
303
  npm run generate -- migration create_users
167
304
  ```
168
305
 
169
- Equivalent direct BCP CLI commands are:
306
+ Equivalent direct commands:
170
307
 
171
308
  ```bash
172
309
  bcp generate page dashboard/users
@@ -177,23 +314,16 @@ bcp generate migration create_users
177
314
 
178
315
  Existing page/API/middleware targets are not replaced unless `--force` is supplied explicitly.
179
316
 
180
- After generating routes, inspect them with:
181
-
182
- ```bash
183
- npm run routes
184
- ```
185
-
186
317
  ## Direct CLI usage
187
318
 
188
- BCP Framework is installed as a project-local dependency. It is not installed globally by `create-bcp-app`.
319
+ BCP Framework is installed as a project-local dependency.
189
320
 
190
- PowerShell does not automatically add `node_modules/.bin` to its normal command search path, so use `npm exec` for direct commands:
321
+ For PowerShell direct usage:
191
322
 
192
323
  ```powershell
193
324
  npm exec -- bcp-framework --version
194
325
  npm exec -- bcp-framework doctor
195
326
  npm exec -- bcp-framework inspect
196
- npm exec -- bcp-framework generate page dashboard/users
197
327
  npm exec -- bcp-framework routes
198
328
  npm exec -- bcp-framework dev
199
329
  npm exec -- bcp-framework build
@@ -202,31 +332,15 @@ npm exec -- bcp-framework package
202
332
 
203
333
  Microsoft SQL Server can install another Windows executable named `bcp.exe`, so the `bcp-framework` alias avoids that command-name collision.
204
334
 
205
- ## Project diagnostics
206
-
207
- ```powershell
208
- npm exec -- bcp-framework doctor
209
- npm exec -- bcp-framework inspect
210
- ```
211
-
212
335
  ## Production build
213
336
 
214
- Create the raw standalone production build:
215
-
216
337
  ```bash
217
338
  npm run build
218
- ```
219
-
220
- Start it with:
221
-
222
- ```bash
223
339
  npm start
224
340
  ```
225
341
 
226
342
  ## Deployment package — BCP 0.2.4+
227
343
 
228
- Create a fresh production build and deployment-oriented package:
229
-
230
344
  ```bash
231
345
  npm run package
232
346
  ```