create-bcp-app 0.2.8 → 0.2.10

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 (3) hide show
  1. package/README.md +125 -210
  2. package/package.json +1 -1
  3. package/template/README.md +133 -148
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.8",
95
+ "frameworkPackage": "npm:@chidchanun/bcp@0.2.10",
96
96
  "createdWith": {
97
97
  "package": "create-bcp-app",
98
- "version": "0.2.8"
98
+ "version": "0.2.10"
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
 
@@ -133,14 +131,7 @@ npm exec -- bcp-framework update --check
133
131
 
134
132
  ## Tailwind CSS
135
133
 
136
- When Tailwind is enabled, the project includes:
137
-
138
- - `tailwindcss`
139
- - `@tailwindcss/cli`
140
- - `concurrently`
141
- - `app/globals.css`
142
- - `/bcp.css` stylesheet setup
143
- - starter utility classes
134
+ When Tailwind is enabled, the project includes `tailwindcss`, `@tailwindcss/cli`, `concurrently`, `app/globals.css`, `/bcp.css` stylesheet setup and starter utility classes.
144
135
 
145
136
  Generated commands include:
146
137
 
@@ -162,235 +153,195 @@ The database choice adds starter configuration and the matching driver:
162
153
  - MongoDB: `mongodb`
163
154
  - None: no database dependency
164
155
 
165
- For **MySQL**, **PostgreSQL** and **SQLite**, generated `lib/database.ts` exposes the framework database primitives through `bcp/database`. Provider connections are managed behind Database Platform v2 instead of being created directly in generated application code.
166
-
167
- Generated MySQL environment variables:
168
-
169
- ```dotenv
170
- DB_HOST=localhost
171
- DB_PORT=3306
172
- DB_USER=
173
- DB_PASSWORD=
174
- DB_NAME=bcp_app
175
- ```
176
-
177
- Generated PostgreSQL environment variable:
178
-
179
- ```dotenv
180
- DATABASE_URL=postgresql://postgres:password@localhost:5432/bcp_app
181
- ```
182
-
183
- Generated SQLite environment variable:
184
-
185
- ```dotenv
186
- DATABASE_URL=./data/bcp.sqlite
187
- ```
188
-
189
- `bcp/database` infers PostgreSQL from `postgres://` or `postgresql://` connection URLs. SQLite is inferred from `:memory:`, `sqlite:` / `file:` locations and common `.sqlite`, `.sqlite3` or `.db` file paths. Applications may also configure `DB_DRIVER` explicitly.
190
-
191
- Placeholder syntax follows the selected provider:
192
-
193
- ```text
194
- MySQL ?
195
- SQLite ?
196
- PostgreSQL $1, $2, ...
197
- ```
156
+ For **MySQL**, **PostgreSQL** and **SQLite**, generated `lib/database.ts` exposes framework database primitives through `bcp/database`.
198
157
 
199
158
  The generator intentionally does not force an ORM.
200
159
 
201
160
  ## Storage providers
202
161
 
203
- Selecting a storage provider creates `lib/storage.ts` and adds provider-specific environment settings.
162
+ Selecting a storage provider creates `lib/storage.ts` and provider-specific environment settings. Supported presets are Local Server, Amazon S3 and Cloudflare R2.
204
163
 
205
- ### Local Server
164
+ Storage credentials are server-only. Do not expose them through `BCP_PUBLIC_*` variables.
206
165
 
207
- ```bash
208
- npx create-bcp-app my-app --storage local
209
- ```
166
+ ## JWT Cookie authentication
210
167
 
211
- Environment:
168
+ Selecting `JWT Cookie` creates `lib/auth.ts` and starter `/api/auth/login`, `/logout` and `/me` routes, and adds:
212
169
 
213
170
  ```dotenv
214
- STORAGE_LOCAL_DIRECTORY=./storage
171
+ BCP_SESSION_SECRET=
215
172
  ```
216
173
 
217
- The generated helper uses `createLocalStorage()`.
218
-
219
- ### Amazon S3
220
-
221
- ```bash
222
- npx create-bcp-app my-app --storage amazon-s3
223
- ```
174
+ Set this to a cryptographically random secret of at least 32 bytes before real authentication use.
224
175
 
225
- Generated environment settings:
176
+ BCP `0.2.5+` can opt into revocable server-side auth state through `AuthSessionStore`.
226
177
 
227
- ```dotenv
228
- AWS_S3_BUCKET=
229
- AWS_REGION=ap-southeast-1
230
- AWS_ACCESS_KEY_ID=
231
- AWS_SECRET_ACCESS_KEY=
232
- AWS_SESSION_TOKEN=
233
- AWS_S3_PREFIX=
234
- ```
178
+ ## Authorization & request security — 0.2.6+
235
179
 
236
- The helper uses `createS3Storage()`. Explicit credentials can remain unset when deployment uses the AWS SDK server-side credential chain, such as an IAM role.
180
+ Generated applications can use permission guards and resource policies from `bcp/auth`, plus same-origin and CSRF protection from `bcp/server`.
237
181
 
238
- ### Cloudflare R2
182
+ Authorization and CSRF checks must remain on the server; hiding UI controls in client code is not an authorization boundary.
239
183
 
240
- ```bash
241
- npx create-bcp-app my-app --storage cloudflare-r2
242
- ```
184
+ ## Observability — 0.2.7+
243
185
 
244
- Generated environment settings:
186
+ Generated projects can opt into process-local metrics and health/readiness without adding another dependency:
245
187
 
246
- ```dotenv
247
- R2_ACCOUNT_ID=
248
- R2_BUCKET=
249
- R2_ACCESS_KEY_ID=
250
- R2_SECRET_ACCESS_KEY=
251
- R2_PREFIX=
188
+ ```ts
189
+ import {
190
+ createHealthRegistry,
191
+ createMetricsRegistry,
192
+ createRequestMetricsMiddleware,
193
+ } from "bcp/observability";
252
194
  ```
253
195
 
254
- Storage credentials are server-only. Do not expose them through `BCP_PUBLIC_*` variables.
255
-
256
- ## JWT Cookie authentication
257
-
258
- Selecting `JWT Cookie` creates:
196
+ `bcp/observability` is server-only. Protect metrics and operational health detail with an appropriate network or authorization boundary when needed.
259
197
 
260
- ```text
261
- lib/
262
- └─ auth.ts
198
+ ## Background jobs — 0.2.8+
263
199
 
264
- app/api/auth/
265
- ├─ login/route.ts
266
- ├─ logout/route.ts
267
- └─ me/route.ts
268
- ```
200
+ Generated projects can create a server-only background job queue:
269
201
 
270
- and adds:
202
+ ```ts
203
+ import {
204
+ createJobQueue,
205
+ } from "bcp/jobs";
271
206
 
272
- ```dotenv
273
- BCP_SESSION_SECRET=
207
+ export const jobs =
208
+ createJobQueue();
274
209
  ```
275
210
 
276
- Set this to a cryptographically random secret of at least 32 bytes before real authentication use.
277
-
278
- The generated `authenticateCredentials(email, password)` returns `null` until the application connects it to its own user store and password-hash verification.
211
+ Workers support concurrency, delayed jobs, retry/backoff and cancellation.
279
212
 
280
- Generated auth stays in backward-compatible stateless JWT-cookie mode by default.
213
+ ## Job scheduling 0.2.9+
281
214
 
282
- BCP `0.2.5+` can opt into revocable server-side auth state without changing the generated auth route structure:
215
+ Recurring schedules use the same `bcp/jobs` entrypoint:
283
216
 
284
217
  ```ts
285
218
  import {
286
- createAuth,
287
- createMemoryAuthSessionStore,
288
- } from "bcp/auth";
289
-
290
- const sessionStore =
291
- createMemoryAuthSessionStore();
219
+ createJobScheduler,
220
+ } from "bcp/jobs";
292
221
 
293
- const frameworkAuth =
294
- createAuth<AuthenticatedUser>({
295
- store: sessionStore,
296
- idleTimeout: 60 * 30,
222
+ export const scheduler =
223
+ createJobScheduler({
224
+ queue: jobs,
297
225
  });
298
226
  ```
299
227
 
300
- The memory store is intended for development/tests. Multi-process production deployments should implement `AuthSessionStore` using shared durable storage.
301
-
302
- ## Authorization & request security — 0.2.6+
303
-
304
- Generated applications can add permissions to their application user shape and use server-side permission guards without changing the auth preset routes:
228
+ Interval schedule:
305
229
 
306
230
  ```ts
307
- import {
308
- createPermissionGuard,
309
- hasPermission,
310
- } from "bcp/auth";
231
+ await scheduler.schedule(
232
+ "cache.cleanup",
233
+ {},
234
+ {
235
+ everyMs: 300_000,
236
+ }
237
+ );
311
238
  ```
312
239
 
313
- For resource-specific decisions, use `defineAuthorizationPolicy()`, `can()` or `authorize()`.
314
-
315
- Cookie-authenticated mutation routes can opt into same-origin and CSRF protection through `bcp/server`:
240
+ UTC cron schedule:
316
241
 
317
242
  ```ts
318
- import {
319
- createCsrfToken,
320
- requireCsrfRequest,
321
- requireSameOriginRequest,
322
- } from "bcp/server";
243
+ await scheduler.schedule(
244
+ "report.weekday",
245
+ {},
246
+ {
247
+ cron: "30 9 * * 1-5",
248
+ }
249
+ );
323
250
  ```
324
251
 
325
- `createCsrfToken()` uses `BCP_CSRF_SECRET` when configured and otherwise falls back to `BCP_SESSION_SECRET`, so the generated JWT auth preset does not require another environment variable to get started. Production applications may define a separate `BCP_CSRF_SECRET` for independent key rotation.
326
-
327
- Authorization and CSRF checks must remain on the server; hiding UI controls in client code is not an authorization boundary.
328
-
329
- ## Observability — 0.2.7+
252
+ ## Durable jobs 0.2.10+
330
253
 
331
- Generated projects can opt into process-local metrics and health/readiness without adding another dependency:
254
+ BCP `0.2.10` adds worker visibility leases, heartbeat renewal, stale-running recovery, DLQ/requeue, retention cleanup and queue statistics.
332
255
 
333
256
  ```ts
334
- import {
335
- createHealthRegistry,
336
- createMetricsRegistry,
337
- createRequestMetricsMiddleware,
338
- } from "bcp/observability";
339
-
340
- export const metrics =
341
- createMetricsRegistry();
257
+ const worker =
258
+ jobs.startWorker({
259
+ workerId: "worker-a",
260
+ concurrency: 4,
261
+ visibilityTimeoutMs: 30_000,
262
+ heartbeatIntervalMs: 10_000,
263
+ });
264
+ ```
342
265
 
343
- export const health =
344
- createHealthRegistry();
266
+ Inspect failed jobs:
345
267
 
346
- export const requestMetrics =
347
- createRequestMetricsMiddleware(
348
- metrics
349
- );
268
+ ```ts
269
+ const deadLetters =
270
+ await jobs.deadLetters();
350
271
  ```
351
272
 
352
- Expose metrics from an application API route with `createMetricsResponse(metrics)`. The response uses Prometheus-compatible text format.
273
+ Requeue:
353
274
 
354
- Health endpoints can return `health.response()`, which uses HTTP `200` when all checks pass and `503` when any dependency check fails or times out.
275
+ ```ts
276
+ await jobs.requeueDeadLetter(
277
+ deadLetters[0].id,
278
+ {
279
+ resetAttempts: true,
280
+ }
281
+ );
282
+ ```
355
283
 
356
- The default HTTP request metrics use `method` and `status` labels only. Raw paths are intentionally excluded to avoid high-cardinality metric series.
284
+ ### Redis-compatible durable adapters
357
285
 
358
- `bcp/observability` is server-only. Protect metrics and operational health detail with an appropriate network or authorization boundary when needed.
286
+ BCP intentionally does not install a Redis client library. Applications own the Redis connection and pass a minimal command client to BCP:
359
287
 
360
- ## Background jobs — 0.2.8+
288
+ ```ts
289
+ interface RedisCommandClient {
290
+ sendCommand(
291
+ command: string[]
292
+ ): Promise<unknown>;
293
+ }
294
+ ```
361
295
 
362
- Generated projects can create a server-only background job queue without adding another package:
296
+ Queue adapter:
363
297
 
364
298
  ```ts
365
299
  import {
366
300
  createJobQueue,
301
+ createRedisJobQueueAdapter,
367
302
  } from "bcp/jobs";
368
303
 
304
+ const adapter =
305
+ createRedisJobQueueAdapter({
306
+ client: redisCommandClient,
307
+ namespace: "my-app:{jobs}",
308
+ });
309
+
369
310
  export const jobs =
370
- createJobQueue();
311
+ createJobQueue({
312
+ adapter,
313
+ });
371
314
  ```
372
315
 
373
- Register handlers and enqueue work:
316
+ Scheduler store:
374
317
 
375
318
  ```ts
376
- jobs.register(
377
- "email.welcome",
378
- async ({ payload }) => {
379
- await sendWelcomeEmail(
380
- payload.userId
381
- );
382
- }
383
- );
319
+ import {
320
+ createJobScheduler,
321
+ createRedisJobScheduleStore,
322
+ } from "bcp/jobs";
384
323
 
385
- await jobs.enqueue(
386
- "email.welcome",
387
- {
388
- userId: 42,
389
- }
390
- );
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
+ });
391
336
  ```
392
337
 
393
- 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 multi-process/container production workloads that must survive restarts.
338
+ A typical application may configure its Redis client with:
339
+
340
+ ```dotenv
341
+ REDIS_URL=redis://localhost:6379
342
+ ```
343
+
344
+ BCP does not read `REDIS_URL` automatically. Redis credentials, TLS/Cluster/Sentinel configuration and connection lifecycle stay application-owned.
394
345
 
395
346
  `bcp/jobs` is server-only and must not be imported into page/client bundles.
396
347
 
@@ -402,32 +353,8 @@ Generated projects include:
402
353
  npm run package
403
354
  ```
404
355
 
405
- This runs the BCP `standalone-node` application packaging flow and writes:
406
-
407
- ```text
408
- .bcp-framework/package/
409
- ├─ client/
410
- ├─ server/
411
- ├─ public/ # when present
412
- ├─ package.json
413
- ├─ package-lock.json # when a safe production lock can be derived
414
- ├─ bcp.package.json
415
- ├─ bcp.deployment.json
416
- ├─ bcp.env.json
417
- ├─ Dockerfile
418
- └─ README.md
419
- ```
420
-
421
356
  The deployment package excludes application `devDependencies` and project `.env` values. Supply real secrets through the deployment environment.
422
357
 
423
- For a package with a generated production lockfile:
424
-
425
- ```bash
426
- cd .bcp-framework/package
427
- npm ci --omit=dev
428
- npm start
429
- ```
430
-
431
358
  ## Project generators after creation
432
359
 
433
360
  ```bash
@@ -455,25 +382,13 @@ bcp generate migration create_users
455
382
  --database <database> none | mysql | postgresql | sqlite | mongodb
456
383
  --auth <preset> none | jwt-cookie
457
384
  --storage <provider> none | local | amazon-s3 | cloudflare-r2
458
- -y, --yes Accept defaults (Tailwind enabled, no database, no auth, no storage)
385
+ -y, --yes Accept defaults
459
386
  --bcp <specifier> Override dependencies.bcp
460
387
  -h, --help Show help
461
388
  ```
462
389
 
463
- Examples:
464
-
465
- ```bash
466
- npx create-bcp-app my-app --tailwind --database mysql --auth jwt-cookie --storage local
467
- npx create-bcp-app my-app --database postgresql
468
- npx create-bcp-app my-app --database sqlite
469
- npx create-bcp-app my-app --storage amazon-s3
470
- npx create-bcp-app my-app --storage cloudflare-r2
471
- npx create-bcp-app my-app --no-tailwind --database mongodb
472
- npx create-bcp-app my-app --yes
473
- ```
474
-
475
390
  The `--bcp` option is mainly for prerelease/local package verification:
476
391
 
477
392
  ```bash
478
- npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.8.tgz
393
+ npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.10.tgz
479
394
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bcp-app",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
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
 
@@ -43,131 +37,187 @@ Commit this file with the project. Do not put passwords, access keys, session se
43
37
 
44
38
  Projects created with the `JWT Cookie` preset remain stateless by default and use the generated `lib/auth.ts` helpers.
45
39
 
46
- BCP Authentication Platform v2 can opt into revocable server-side session state:
40
+ BCP Authentication Platform v2 can opt into revocable server-side session state with `createAuth()` and `AuthSessionStore`.
47
41
 
48
- ```ts
49
- import {
50
- createAuth,
51
- createMemoryAuthSessionStore,
52
- } from "bcp/auth";
42
+ ## Authorization & request security — BCP 0.2.6+
53
43
 
54
- const sessionStore =
55
- createMemoryAuthSessionStore();
44
+ Use permission guards and resource policies from `bcp/auth`, and same-origin/CSRF protection from `bcp/server`.
56
45
 
57
- export const appAuth =
58
- createAuth({
59
- store: sessionStore,
60
- idleTimeout: 60 * 30,
61
- });
46
+ Authorization must always be enforced server-side. Client UI visibility is not a security boundary.
47
+
48
+ ## Observability — BCP 0.2.7+
49
+
50
+ ```ts
51
+ import {
52
+ createHealthRegistry,
53
+ createMetricsRegistry,
54
+ createRequestMetricsMiddleware,
55
+ } from "bcp/observability";
62
56
  ```
63
57
 
64
- The memory store is intended for local development/tests. Use a shared durable `AuthSessionStore` implementation for multi-process or multi-container production deployments.
58
+ Protect operational endpoints when their contents should not be public.
65
59
 
66
- ## Authorization & request security — BCP 0.2.6+
60
+ ## Background jobs — BCP 0.2.8+
67
61
 
68
- Server-side permission guards:
62
+ Create a server-only background queue:
69
63
 
70
64
  ```ts
71
65
  import {
72
- createPermissionGuard,
73
- } from "bcp/auth";
66
+ createJobQueue,
67
+ } from "bcp/jobs";
74
68
 
75
- export const guard =
76
- createPermissionGuard(
77
- "dashboard.read"
78
- );
69
+ export const jobs =
70
+ createJobQueue();
79
71
  ```
80
72
 
81
- For ownership or resource-specific rules, use `defineAuthorizationPolicy()`, `can()` and `authorize()` from `bcp/auth`.
73
+ Register typed work:
74
+
75
+ ```ts
76
+ jobs.register<{
77
+ userId: number;
78
+ }>(
79
+ "email.welcome",
80
+ async ({ payload }) => {
81
+ await sendWelcomeEmail(
82
+ payload.userId
83
+ );
84
+ }
85
+ );
86
+ ```
82
87
 
83
- Cookie-authenticated mutation routes can validate browser origin and CSRF state:
88
+ ## Job scheduling BCP 0.2.9+
84
89
 
85
90
  ```ts
86
91
  import {
87
- createCsrfToken,
88
- requireCsrfRequest,
89
- requireSameOriginRequest,
90
- } from "bcp/server";
92
+ createJobScheduler,
93
+ } from "bcp/jobs";
94
+
95
+ export const scheduler =
96
+ createJobScheduler({
97
+ queue: jobs,
98
+ });
91
99
  ```
92
100
 
93
- `createCsrfToken()` uses `BCP_CSRF_SECRET` when configured and otherwise falls back to `BCP_SESSION_SECRET`.
101
+ Example UTC cron schedule:
94
102
 
95
- Authorization must always be enforced server-side. Client UI visibility is not a security boundary.
103
+ ```ts
104
+ await scheduler.schedule(
105
+ "report.weekday",
106
+ {},
107
+ {
108
+ cron: "30 9 * * 1-5",
109
+ }
110
+ );
111
+ ```
96
112
 
97
- ## Observability — BCP 0.2.7+
113
+ ## Durable jobs — BCP 0.2.10+
98
114
 
99
- Create process-local metrics and health/readiness registries:
115
+ Workers can use visibility leases, heartbeat renewal and stale-running recovery:
100
116
 
101
117
  ```ts
102
- import {
103
- createHealthRegistry,
104
- createMetricsRegistry,
105
- createRequestMetricsMiddleware,
106
- } from "bcp/observability";
118
+ const worker =
119
+ jobs.startWorker({
120
+ workerId: "worker-a",
121
+ concurrency: 4,
122
+ visibilityTimeoutMs: 30_000,
123
+ heartbeatIntervalMs: 10_000,
124
+ });
125
+ ```
107
126
 
108
- export const metrics =
109
- createMetricsRegistry();
127
+ Retry-exhausted jobs are available through the adapter DLQ contract:
110
128
 
111
- export const health =
112
- createHealthRegistry();
129
+ ```ts
130
+ const deadLetters =
131
+ await jobs.deadLetters();
113
132
 
114
- export const requestMetrics =
115
- createRequestMetricsMiddleware(
116
- metrics
117
- );
133
+ await jobs.requeueDeadLetter(
134
+ deadLetters[0].id,
135
+ {
136
+ resetAttempts: true,
137
+ }
138
+ );
118
139
  ```
119
140
 
120
- Expose Prometheus-compatible metrics with `createMetricsResponse(metrics)` from a server API route.
141
+ Operational helpers:
121
142
 
122
- Use `health.response()` for readiness endpoints. It returns HTTP `200` when all checks pass and `503` when a check fails or times out.
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
+ ```
123
152
 
124
- The default request metrics use bounded `method` and `status` labels and do not include raw paths.
153
+ ### Redis-compatible adapters
125
154
 
126
- Protect operational endpoints when their contents should not be public.
155
+ BCP does not install a Redis library. Supply an application-owned command client:
127
156
 
128
- ## Background jobs — BCP 0.2.8+
157
+ ```ts
158
+ interface RedisCommandClient {
159
+ sendCommand(
160
+ command: string[]
161
+ ): Promise<unknown>;
162
+ }
163
+ ```
129
164
 
130
- Create a server-only background queue:
165
+ Create a durable queue:
131
166
 
132
167
  ```ts
133
168
  import {
134
169
  createJobQueue,
170
+ createRedisJobQueueAdapter,
135
171
  } from "bcp/jobs";
136
172
 
173
+ const adapter =
174
+ createRedisJobQueueAdapter({
175
+ client: redisCommandClient,
176
+ namespace: "my-app:{jobs}",
177
+ });
178
+
137
179
  export const jobs =
138
- createJobQueue();
180
+ createJobQueue({
181
+ adapter,
182
+ });
139
183
  ```
140
184
 
141
- Register and enqueue work:
185
+ Create a shared schedule store:
142
186
 
143
187
  ```ts
144
- jobs.register(
145
- "email.welcome",
146
- async ({ payload }) => {
147
- await sendWelcomeEmail(
148
- payload.userId
149
- );
150
- }
151
- );
188
+ import {
189
+ createJobScheduler,
190
+ createRedisJobScheduleStore,
191
+ } from "bcp/jobs";
152
192
 
153
- await jobs.enqueue(
154
- "email.welcome",
155
- {
156
- userId: 42,
157
- }
158
- );
193
+ const store =
194
+ createRedisJobScheduleStore({
195
+ client: redisCommandClient,
196
+ namespace: "my-app:{jobs}",
197
+ });
198
+
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
159
211
  ```
160
212
 
161
- Workers support delayed jobs, retry/backoff, cancellation and configurable concurrency.
213
+ BCP does not read this variable automatically. The application owns Redis connection creation, credentials, TLS/Cluster configuration and shutdown.
162
214
 
163
- 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.
215
+ The processing model is at-least-once, so side-effecting job handlers should be idempotent when duplicate execution is unsafe.
164
216
 
165
217
  `bcp/jobs` is server-only and cannot be imported into page/client bundles.
166
218
 
167
219
  ## Generate framework files
168
220
 
169
- BCP can generate common project files:
170
-
171
221
  ```bash
172
222
  npm run generate -- page dashboard/users
173
223
  npm run generate -- api users
@@ -175,7 +225,7 @@ npm run generate -- middleware
175
225
  npm run generate -- migration create_users
176
226
  ```
177
227
 
178
- Equivalent direct BCP CLI commands are:
228
+ Equivalent direct commands:
179
229
 
180
230
  ```bash
181
231
  bcp generate page dashboard/users
@@ -186,102 +236,37 @@ bcp generate migration create_users
186
236
 
187
237
  Existing page/API/middleware targets are not replaced unless `--force` is supplied explicitly.
188
238
 
189
- After generating routes, inspect them with:
190
-
191
- ```bash
192
- npm run routes
193
- ```
194
-
195
239
  ## Direct CLI usage
196
240
 
197
- BCP Framework is installed as a project-local dependency. It is not installed globally by `create-bcp-app`.
241
+ BCP Framework is installed as a project-local dependency.
198
242
 
199
- PowerShell does not automatically add `node_modules/.bin` to its normal command search path, so use `npm exec` for direct commands:
243
+ For PowerShell direct usage:
200
244
 
201
245
  ```powershell
202
246
  npm exec -- bcp-framework --version
203
247
  npm exec -- bcp-framework doctor
204
248
  npm exec -- bcp-framework inspect
205
- npm exec -- bcp-framework generate page dashboard/users
206
249
  npm exec -- bcp-framework routes
207
250
  npm exec -- bcp-framework dev
208
251
  npm exec -- bcp-framework build
209
252
  npm exec -- bcp-framework package
210
253
  ```
211
254
 
212
- You can also execute the Windows command shim explicitly:
213
-
214
- ```powershell
215
- .\node_modules\.bin\bcp-framework.cmd --version
216
- ```
217
-
218
255
  Microsoft SQL Server can install another Windows executable named `bcp.exe`, so the `bcp-framework` alias avoids that command-name collision.
219
256
 
220
- Use this convention:
221
-
222
- ```text
223
- Inside npm scripts -> bcp ...
224
- Direct PowerShell usage -> npm exec -- bcp-framework ...
225
- ```
226
-
227
- ## Project diagnostics
228
-
229
- Run:
230
-
231
- ```powershell
232
- npm exec -- bcp-framework doctor
233
- npm exec -- bcp-framework inspect
234
- ```
235
-
236
- Machine-readable reports:
237
-
238
- ```powershell
239
- npm exec -- bcp-framework doctor --json
240
- npm exec -- bcp-framework inspect --json
241
- ```
242
-
243
- Doctor checks common project/runtime problems while Inspect shows the resolved configuration, routes, dependencies and project metadata BCP sees.
244
-
245
257
  ## Production build
246
258
 
247
- Create the raw standalone production build:
248
-
249
259
  ```bash
250
260
  npm run build
251
- ```
252
-
253
- Start it with:
254
-
255
- ```bash
256
261
  npm start
257
262
  ```
258
263
 
259
- The production build is written to `.bcp-framework/build` and runs as a standalone Node.js server.
260
-
261
264
  ## Deployment package — BCP 0.2.4+
262
265
 
263
- Create a fresh production build and deployment-oriented package:
264
-
265
266
  ```bash
266
267
  npm run package
267
268
  ```
268
269
 
269
- The output is written to:
270
-
271
- ```text
272
- .bcp-framework/package/
273
- ```
274
-
275
- It contains the standalone client/server output, a production-only dependency manifest, deployment/environment metadata, file integrity hashes and a starter Dockerfile.
270
+ The output is written to `.bcp-framework/package/` and contains standalone output, production dependency metadata, deployment/environment manifests, integrity hashes and a starter Dockerfile.
276
271
 
277
272
  BCP intentionally excludes project `.env` files and application `devDependencies` from the deployment package. Provide secrets through your deployment environment.
278
-
279
- For a package with a generated production lockfile:
280
-
281
- ```bash
282
- cd .bcp-framework/package
283
- npm ci --omit=dev
284
- npm start
285
- ```
286
-
287
- If `bcp.package.json` reports that no lockfile was included, use the install command recorded in that manifest instead.