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.
- package/README.md +125 -210
- package/package.json +1 -1
- 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.
|
|
95
|
+
"frameworkPackage": "npm:@chidchanun/bcp@0.2.10",
|
|
96
96
|
"createdWith": {
|
|
97
97
|
"package": "create-bcp-app",
|
|
98
|
-
"version": "0.2.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
164
|
+
Storage credentials are server-only. Do not expose them through `BCP_PUBLIC_*` variables.
|
|
206
165
|
|
|
207
|
-
|
|
208
|
-
npx create-bcp-app my-app --storage local
|
|
209
|
-
```
|
|
166
|
+
## JWT Cookie authentication
|
|
210
167
|
|
|
211
|
-
|
|
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
|
-
|
|
171
|
+
BCP_SESSION_SECRET=
|
|
215
172
|
```
|
|
216
173
|
|
|
217
|
-
|
|
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
|
-
|
|
176
|
+
BCP `0.2.5+` can opt into revocable server-side auth state through `AuthSessionStore`.
|
|
226
177
|
|
|
227
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
+
Authorization and CSRF checks must remain on the server; hiding UI controls in client code is not an authorization boundary.
|
|
239
183
|
|
|
240
|
-
|
|
241
|
-
npx create-bcp-app my-app --storage cloudflare-r2
|
|
242
|
-
```
|
|
184
|
+
## Observability — 0.2.7+
|
|
243
185
|
|
|
244
|
-
Generated
|
|
186
|
+
Generated projects can opt into process-local metrics and health/readiness without adding another dependency:
|
|
245
187
|
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
188
|
+
```ts
|
|
189
|
+
import {
|
|
190
|
+
createHealthRegistry,
|
|
191
|
+
createMetricsRegistry,
|
|
192
|
+
createRequestMetricsMiddleware,
|
|
193
|
+
} from "bcp/observability";
|
|
252
194
|
```
|
|
253
195
|
|
|
254
|
-
|
|
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
|
-
|
|
261
|
-
lib/
|
|
262
|
-
└─ auth.ts
|
|
198
|
+
## Background jobs — 0.2.8+
|
|
263
199
|
|
|
264
|
-
|
|
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
|
-
|
|
202
|
+
```ts
|
|
203
|
+
import {
|
|
204
|
+
createJobQueue,
|
|
205
|
+
} from "bcp/jobs";
|
|
271
206
|
|
|
272
|
-
|
|
273
|
-
|
|
207
|
+
export const jobs =
|
|
208
|
+
createJobQueue();
|
|
274
209
|
```
|
|
275
210
|
|
|
276
|
-
|
|
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
|
-
|
|
213
|
+
## Job scheduling — 0.2.9+
|
|
281
214
|
|
|
282
|
-
|
|
215
|
+
Recurring schedules use the same `bcp/jobs` entrypoint:
|
|
283
216
|
|
|
284
217
|
```ts
|
|
285
218
|
import {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
} from "bcp/auth";
|
|
289
|
-
|
|
290
|
-
const sessionStore =
|
|
291
|
-
createMemoryAuthSessionStore();
|
|
219
|
+
createJobScheduler,
|
|
220
|
+
} from "bcp/jobs";
|
|
292
221
|
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
idleTimeout: 60 * 30,
|
|
222
|
+
export const scheduler =
|
|
223
|
+
createJobScheduler({
|
|
224
|
+
queue: jobs,
|
|
297
225
|
});
|
|
298
226
|
```
|
|
299
227
|
|
|
300
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
231
|
+
await scheduler.schedule(
|
|
232
|
+
"cache.cleanup",
|
|
233
|
+
{},
|
|
234
|
+
{
|
|
235
|
+
everyMs: 300_000,
|
|
236
|
+
}
|
|
237
|
+
);
|
|
311
238
|
```
|
|
312
239
|
|
|
313
|
-
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
243
|
+
await scheduler.schedule(
|
|
244
|
+
"report.weekday",
|
|
245
|
+
{},
|
|
246
|
+
{
|
|
247
|
+
cron: "30 9 * * 1-5",
|
|
248
|
+
}
|
|
249
|
+
);
|
|
323
250
|
```
|
|
324
251
|
|
|
325
|
-
|
|
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
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
344
|
-
createHealthRegistry();
|
|
266
|
+
Inspect failed jobs:
|
|
345
267
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
);
|
|
268
|
+
```ts
|
|
269
|
+
const deadLetters =
|
|
270
|
+
await jobs.deadLetters();
|
|
350
271
|
```
|
|
351
272
|
|
|
352
|
-
|
|
273
|
+
Requeue:
|
|
353
274
|
|
|
354
|
-
|
|
275
|
+
```ts
|
|
276
|
+
await jobs.requeueDeadLetter(
|
|
277
|
+
deadLetters[0].id,
|
|
278
|
+
{
|
|
279
|
+
resetAttempts: true,
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
```
|
|
355
283
|
|
|
356
|
-
|
|
284
|
+
### Redis-compatible durable adapters
|
|
357
285
|
|
|
358
|
-
|
|
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
|
-
|
|
288
|
+
```ts
|
|
289
|
+
interface RedisCommandClient {
|
|
290
|
+
sendCommand(
|
|
291
|
+
command: string[]
|
|
292
|
+
): Promise<unknown>;
|
|
293
|
+
}
|
|
294
|
+
```
|
|
361
295
|
|
|
362
|
-
|
|
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
|
-
|
|
316
|
+
Scheduler store:
|
|
374
317
|
|
|
375
318
|
```ts
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
payload.userId
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
);
|
|
319
|
+
import {
|
|
320
|
+
createJobScheduler,
|
|
321
|
+
createRedisJobScheduleStore,
|
|
322
|
+
} from "bcp/jobs";
|
|
384
323
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
393
|
+
npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.10.tgz
|
|
479
394
|
```
|
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -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`.
|
|
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
|
-
|
|
49
|
-
import {
|
|
50
|
-
createAuth,
|
|
51
|
-
createMemoryAuthSessionStore,
|
|
52
|
-
} from "bcp/auth";
|
|
42
|
+
## Authorization & request security — BCP 0.2.6+
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
createMemoryAuthSessionStore();
|
|
44
|
+
Use permission guards and resource policies from `bcp/auth`, and same-origin/CSRF protection from `bcp/server`.
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
58
|
+
Protect operational endpoints when their contents should not be public.
|
|
65
59
|
|
|
66
|
-
##
|
|
60
|
+
## Background jobs — BCP 0.2.8+
|
|
67
61
|
|
|
68
|
-
|
|
62
|
+
Create a server-only background queue:
|
|
69
63
|
|
|
70
64
|
```ts
|
|
71
65
|
import {
|
|
72
|
-
|
|
73
|
-
} from "bcp/
|
|
66
|
+
createJobQueue,
|
|
67
|
+
} from "bcp/jobs";
|
|
74
68
|
|
|
75
|
-
export const
|
|
76
|
-
|
|
77
|
-
"dashboard.read"
|
|
78
|
-
);
|
|
69
|
+
export const jobs =
|
|
70
|
+
createJobQueue();
|
|
79
71
|
```
|
|
80
72
|
|
|
81
|
-
|
|
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
|
-
|
|
88
|
+
## Job scheduling — BCP 0.2.9+
|
|
84
89
|
|
|
85
90
|
```ts
|
|
86
91
|
import {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
createJobScheduler,
|
|
93
|
+
} from "bcp/jobs";
|
|
94
|
+
|
|
95
|
+
export const scheduler =
|
|
96
|
+
createJobScheduler({
|
|
97
|
+
queue: jobs,
|
|
98
|
+
});
|
|
91
99
|
```
|
|
92
100
|
|
|
93
|
-
|
|
101
|
+
Example UTC cron schedule:
|
|
94
102
|
|
|
95
|
-
|
|
103
|
+
```ts
|
|
104
|
+
await scheduler.schedule(
|
|
105
|
+
"report.weekday",
|
|
106
|
+
{},
|
|
107
|
+
{
|
|
108
|
+
cron: "30 9 * * 1-5",
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
```
|
|
96
112
|
|
|
97
|
-
##
|
|
113
|
+
## Durable jobs — BCP 0.2.10+
|
|
98
114
|
|
|
99
|
-
|
|
115
|
+
Workers can use visibility leases, heartbeat renewal and stale-running recovery:
|
|
100
116
|
|
|
101
117
|
```ts
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
createMetricsRegistry();
|
|
127
|
+
Retry-exhausted jobs are available through the adapter DLQ contract:
|
|
110
128
|
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
```ts
|
|
130
|
+
const deadLetters =
|
|
131
|
+
await jobs.deadLetters();
|
|
113
132
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
133
|
+
await jobs.requeueDeadLetter(
|
|
134
|
+
deadLetters[0].id,
|
|
135
|
+
{
|
|
136
|
+
resetAttempts: true,
|
|
137
|
+
}
|
|
138
|
+
);
|
|
118
139
|
```
|
|
119
140
|
|
|
120
|
-
|
|
141
|
+
Operational helpers:
|
|
121
142
|
|
|
122
|
-
|
|
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
|
-
|
|
153
|
+
### Redis-compatible adapters
|
|
125
154
|
|
|
126
|
-
|
|
155
|
+
BCP does not install a Redis library. Supply an application-owned command client:
|
|
127
156
|
|
|
128
|
-
|
|
157
|
+
```ts
|
|
158
|
+
interface RedisCommandClient {
|
|
159
|
+
sendCommand(
|
|
160
|
+
command: string[]
|
|
161
|
+
): Promise<unknown>;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
129
164
|
|
|
130
|
-
Create a
|
|
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
|
-
|
|
185
|
+
Create a shared schedule store:
|
|
142
186
|
|
|
143
187
|
```ts
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
payload.userId
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
);
|
|
188
|
+
import {
|
|
189
|
+
createJobScheduler,
|
|
190
|
+
createRedisJobScheduleStore,
|
|
191
|
+
} from "bcp/jobs";
|
|
152
192
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
213
|
+
BCP does not read this variable automatically. The application owns Redis connection creation, credentials, TLS/Cluster configuration and shutdown.
|
|
162
214
|
|
|
163
|
-
The
|
|
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
|
|
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.
|
|
241
|
+
BCP Framework is installed as a project-local dependency.
|
|
198
242
|
|
|
199
|
-
PowerShell
|
|
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.
|