create-bcp-app 0.2.11 → 0.2.12
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 +87 -198
- package/package.json +1 -1
- package/template/README.md +75 -200
package/README.md
CHANGED
|
@@ -28,16 +28,6 @@ Generated npm scripts use `bcp` because npm places `node_modules/.bin` on the sc
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
When Tailwind CSS is selected, the generated packaging script preserves the production CSS step:
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
{
|
|
35
|
-
"scripts": {
|
|
36
|
-
"package": "npm run css:build:prod && bcp package"
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
31
|
For direct PowerShell usage, prefer the collision-free local alias:
|
|
42
32
|
|
|
43
33
|
```powershell
|
|
@@ -75,27 +65,21 @@ Select storage provider:
|
|
|
75
65
|
Cloudflare R2
|
|
76
66
|
```
|
|
77
67
|
|
|
78
|
-
Equivalent non-interactive flags are available for CI/scaffolding automation.
|
|
79
|
-
|
|
80
68
|
## Generated project metadata
|
|
81
69
|
|
|
82
|
-
New projects include
|
|
70
|
+
New projects include `bcp.project.json`.
|
|
83
71
|
|
|
84
|
-
|
|
85
|
-
bcp.project.json
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Example:
|
|
72
|
+
Example for the `0.2.12` target:
|
|
89
73
|
|
|
90
74
|
```json
|
|
91
75
|
{
|
|
92
76
|
"schemaVersion": 1,
|
|
93
77
|
"framework": "bcp",
|
|
94
78
|
"projectName": "my-app",
|
|
95
|
-
"frameworkPackage": "npm:@chidchanun/bcp@0.2.
|
|
79
|
+
"frameworkPackage": "npm:@chidchanun/bcp@0.2.12",
|
|
96
80
|
"createdWith": {
|
|
97
81
|
"package": "create-bcp-app",
|
|
98
|
-
"version": "0.2.
|
|
82
|
+
"version": "0.2.12"
|
|
99
83
|
},
|
|
100
84
|
"packageManager": "npm",
|
|
101
85
|
"presets": {
|
|
@@ -109,43 +93,9 @@ Example:
|
|
|
109
93
|
|
|
110
94
|
This manifest records scaffold identity only. It must not contain secrets and should normally be committed to source control.
|
|
111
95
|
|
|
112
|
-
## Generated application defaults
|
|
113
|
-
|
|
114
|
-
Generated applications include the project-root `@/` alias and TypeScript settings compatible with the BCP bundler.
|
|
115
|
-
|
|
116
|
-
Modules that use React client hooks such as `useState` or `useEffect` must declare `"use client"` at the top of the module.
|
|
117
|
-
|
|
118
|
-
The generated BCP dependency is pinned to the exact framework release selected by the generator.
|
|
119
|
-
|
|
120
|
-
Update later with:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
npm run update
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Check without modifying files:
|
|
127
|
-
|
|
128
|
-
```powershell
|
|
129
|
-
npm exec -- bcp-framework update --check
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Tailwind CSS
|
|
133
|
-
|
|
134
|
-
When Tailwind is enabled, the project includes `tailwindcss`, `@tailwindcss/cli`, `concurrently`, `app/globals.css`, `/bcp.css` stylesheet setup and starter utility classes.
|
|
135
|
-
|
|
136
|
-
Generated commands include:
|
|
137
|
-
|
|
138
|
-
```text
|
|
139
|
-
npm run css:build
|
|
140
|
-
npm run css:watch
|
|
141
|
-
npm run css:build:prod
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
`npm run build` and `npm run package` create the production stylesheet before the BCP production build/package flow.
|
|
145
|
-
|
|
146
96
|
## Database presets
|
|
147
97
|
|
|
148
|
-
|
|
98
|
+
Database choices add starter configuration and the matching driver:
|
|
149
99
|
|
|
150
100
|
- MySQL: `mysql2`
|
|
151
101
|
- PostgreSQL: `pg`
|
|
@@ -153,71 +103,31 @@ The database choice adds starter configuration and the matching driver:
|
|
|
153
103
|
- MongoDB: `mongodb`
|
|
154
104
|
- None: no database dependency
|
|
155
105
|
|
|
156
|
-
For
|
|
157
|
-
|
|
158
|
-
The generator intentionally does not force an ORM.
|
|
159
|
-
|
|
160
|
-
## Storage providers
|
|
161
|
-
|
|
162
|
-
Selecting a storage provider creates `lib/storage.ts` and provider-specific environment settings. Supported presets are Local Server, Amazon S3 and Cloudflare R2.
|
|
163
|
-
|
|
164
|
-
Storage credentials are server-only. Do not expose them through `BCP_PUBLIC_*` variables.
|
|
165
|
-
|
|
166
|
-
## JWT Cookie authentication
|
|
167
|
-
|
|
168
|
-
Selecting `JWT Cookie` creates `lib/auth.ts` and starter `/api/auth/login`, `/logout` and `/me` routes, and adds:
|
|
169
|
-
|
|
170
|
-
```dotenv
|
|
171
|
-
BCP_SESSION_SECRET=
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Set this to a cryptographically random secret of at least 32 bytes before real authentication use.
|
|
106
|
+
For MySQL, PostgreSQL and SQLite, generated `lib/database.ts` exposes BCP database primitives through `bcp/database`.
|
|
175
107
|
|
|
176
|
-
|
|
108
|
+
## Authentication and security
|
|
177
109
|
|
|
178
|
-
|
|
110
|
+
The JWT Cookie preset creates `lib/auth.ts` and starter auth routes. BCP `0.2.5+` supports optional revocable server-side auth state, while `0.2.6+` adds permission/policy authorization plus same-origin/CSRF helpers.
|
|
179
111
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
Authorization and CSRF checks must remain on the server; hiding UI controls in client code is not an authorization boundary.
|
|
183
|
-
|
|
184
|
-
## Observability — 0.2.7+
|
|
185
|
-
|
|
186
|
-
Generated projects can opt into process-local metrics and health/readiness without adding another dependency:
|
|
112
|
+
## Observability
|
|
187
113
|
|
|
188
114
|
```ts
|
|
189
115
|
import {
|
|
190
116
|
createHealthRegistry,
|
|
191
117
|
createMetricsRegistry,
|
|
192
|
-
createRequestMetricsMiddleware,
|
|
193
118
|
} from "bcp/observability";
|
|
194
119
|
```
|
|
195
120
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
## Background jobs — 0.2.8+
|
|
199
|
-
|
|
200
|
-
Generated projects can create a server-only background job queue:
|
|
121
|
+
## Background jobs and scheduling
|
|
201
122
|
|
|
202
123
|
```ts
|
|
203
124
|
import {
|
|
204
125
|
createJobQueue,
|
|
126
|
+
createJobScheduler,
|
|
205
127
|
} from "bcp/jobs";
|
|
206
128
|
|
|
207
129
|
export const jobs =
|
|
208
130
|
createJobQueue();
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Workers support concurrency, delayed jobs, retry/backoff and cancellation.
|
|
212
|
-
|
|
213
|
-
## Job scheduling — 0.2.9+
|
|
214
|
-
|
|
215
|
-
Recurring schedules use the same `bcp/jobs` entrypoint:
|
|
216
|
-
|
|
217
|
-
```ts
|
|
218
|
-
import {
|
|
219
|
-
createJobScheduler,
|
|
220
|
-
} from "bcp/jobs";
|
|
221
131
|
|
|
222
132
|
export const scheduler =
|
|
223
133
|
createJobScheduler({
|
|
@@ -225,82 +135,23 @@ export const scheduler =
|
|
|
225
135
|
});
|
|
226
136
|
```
|
|
227
137
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
```ts
|
|
231
|
-
await scheduler.schedule(
|
|
232
|
-
"cache.cleanup",
|
|
233
|
-
{},
|
|
234
|
-
{
|
|
235
|
-
everyMs: 300_000,
|
|
236
|
-
}
|
|
237
|
-
);
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
UTC cron schedule:
|
|
241
|
-
|
|
242
|
-
```ts
|
|
243
|
-
await scheduler.schedule(
|
|
244
|
-
"report.weekday",
|
|
245
|
-
{},
|
|
246
|
-
{
|
|
247
|
-
cron: "30 9 * * 1-5",
|
|
248
|
-
}
|
|
249
|
-
);
|
|
250
|
-
```
|
|
251
|
-
|
|
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.
|
|
255
|
-
|
|
256
|
-
```ts
|
|
257
|
-
const worker =
|
|
258
|
-
jobs.startWorker({
|
|
259
|
-
workerId: "worker-a",
|
|
260
|
-
concurrency: 4,
|
|
261
|
-
visibilityTimeoutMs: 30_000,
|
|
262
|
-
heartbeatIntervalMs: 10_000,
|
|
263
|
-
});
|
|
264
|
-
```
|
|
265
|
-
|
|
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
|
-
|
|
268
|
-
`bcp/jobs` is server-only and must not be imported into page/client bundles.
|
|
138
|
+
`0.2.10+` adds visibility leases, heartbeat renewal, stale-running recovery, DLQ/requeue, retention cleanup and Redis-compatible durable adapters.
|
|
269
139
|
|
|
270
140
|
## Workflow orchestration — 0.2.11+
|
|
271
141
|
|
|
272
|
-
Generated applications can define persistent backend workflows with the new server-only `bcp/workflow` entrypoint:
|
|
273
|
-
|
|
274
142
|
```ts
|
|
275
143
|
import {
|
|
276
144
|
createWorkflow,
|
|
277
145
|
} from "bcp/workflow";
|
|
278
146
|
|
|
279
147
|
export const onboarding =
|
|
280
|
-
createWorkflow
|
|
281
|
-
userId: number;
|
|
282
|
-
}>(
|
|
148
|
+
createWorkflow(
|
|
283
149
|
"user.onboarding",
|
|
284
150
|
workflow => {
|
|
285
151
|
workflow.step(
|
|
286
152
|
"profile",
|
|
287
153
|
createProfile
|
|
288
154
|
);
|
|
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
155
|
workflow.delay(
|
|
305
156
|
"cooldown",
|
|
306
157
|
1_000
|
|
@@ -309,45 +160,92 @@ export const onboarding =
|
|
|
309
160
|
);
|
|
310
161
|
```
|
|
311
162
|
|
|
312
|
-
|
|
163
|
+
`bcp/workflow` supports sequential/parallel steps, retry, persisted delays, compensation and optional execution through `bcp/jobs`.
|
|
164
|
+
|
|
165
|
+
## Transactional Outbox & Events — 0.2.12+
|
|
166
|
+
|
|
167
|
+
Applications using the SQL Database Platform can persist integration events in the same transaction as business data:
|
|
313
168
|
|
|
314
169
|
```ts
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
170
|
+
import {
|
|
171
|
+
db,
|
|
172
|
+
} from "bcp/database";
|
|
173
|
+
|
|
174
|
+
import {
|
|
175
|
+
createSqlOutboxStore,
|
|
176
|
+
createTransactionalOutbox,
|
|
177
|
+
} from "bcp/events";
|
|
178
|
+
|
|
179
|
+
const outboxStore =
|
|
180
|
+
createSqlOutboxStore({
|
|
181
|
+
database: db,
|
|
182
|
+
driver: "mysql",
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const outbox =
|
|
186
|
+
createTransactionalOutbox({
|
|
187
|
+
store: outboxStore,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await db.transaction(
|
|
191
|
+
async tx => {
|
|
192
|
+
await tx.execute(
|
|
193
|
+
"INSERT INTO orders ..."
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
await outbox.publish(
|
|
197
|
+
tx,
|
|
198
|
+
"order.created",
|
|
199
|
+
{
|
|
200
|
+
orderId: 42,
|
|
201
|
+
}
|
|
202
|
+
);
|
|
323
203
|
}
|
|
324
204
|
);
|
|
325
205
|
```
|
|
326
206
|
|
|
327
|
-
|
|
207
|
+
Create migration SQL with:
|
|
328
208
|
|
|
329
209
|
```ts
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
workflowStore,
|
|
338
|
-
}
|
|
210
|
+
import {
|
|
211
|
+
createOutboxMigrationSql,
|
|
212
|
+
} from "bcp/events";
|
|
213
|
+
|
|
214
|
+
const migrationSql =
|
|
215
|
+
createOutboxMigrationSql(
|
|
216
|
+
"mysql"
|
|
339
217
|
);
|
|
340
218
|
```
|
|
341
219
|
|
|
342
|
-
|
|
220
|
+
After commit, dispatch to the durable job queue:
|
|
343
221
|
|
|
344
|
-
|
|
222
|
+
```ts
|
|
223
|
+
import {
|
|
224
|
+
createOutboxDispatcher,
|
|
225
|
+
} from "bcp/events";
|
|
345
226
|
|
|
346
|
-
|
|
227
|
+
const dispatcher =
|
|
228
|
+
createOutboxDispatcher({
|
|
229
|
+
store: outboxStore,
|
|
230
|
+
queue: jobs,
|
|
231
|
+
ownerId: "outbox-a",
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const runner =
|
|
235
|
+
dispatcher.start();
|
|
236
|
+
```
|
|
347
237
|
|
|
348
|
-
|
|
238
|
+
Event type `order.created` is handed off to queue name `event.order.created`.
|
|
349
239
|
|
|
350
|
-
|
|
240
|
+
The outbox dispatcher supports leases, stale recovery, retry/backoff, custom publishers, a local EventBus, cleanup and statistics.
|
|
241
|
+
|
|
242
|
+
`bcp/events` is server-only. The SQL outbox protects the database-commit/external-publish boundary, but downstream delivery remains at-least-once, so non-repeatable side effects should use idempotency protection.
|
|
243
|
+
|
|
244
|
+
## Storage providers
|
|
245
|
+
|
|
246
|
+
Supported presets are Local Server, Amazon S3 and Cloudflare R2. Storage credentials are server-only and must not use `BCP_PUBLIC_*` variables.
|
|
247
|
+
|
|
248
|
+
## Application packaging
|
|
351
249
|
|
|
352
250
|
```bash
|
|
353
251
|
npm run package
|
|
@@ -355,7 +253,7 @@ npm run package
|
|
|
355
253
|
|
|
356
254
|
The deployment package excludes application `devDependencies` and project `.env` values. Supply real secrets through the deployment environment.
|
|
357
255
|
|
|
358
|
-
## Project generators
|
|
256
|
+
## Project generators
|
|
359
257
|
|
|
360
258
|
```bash
|
|
361
259
|
npm run generate -- page dashboard/users
|
|
@@ -364,15 +262,6 @@ npm run generate -- middleware
|
|
|
364
262
|
npm run generate -- migration create_users
|
|
365
263
|
```
|
|
366
264
|
|
|
367
|
-
Equivalent direct CLI:
|
|
368
|
-
|
|
369
|
-
```bash
|
|
370
|
-
bcp generate page dashboard/users
|
|
371
|
-
bcp generate api users
|
|
372
|
-
bcp generate middleware
|
|
373
|
-
bcp generate migration create_users
|
|
374
|
-
```
|
|
375
|
-
|
|
376
265
|
## Options
|
|
377
266
|
|
|
378
267
|
```text
|
|
@@ -387,8 +276,8 @@ bcp generate migration create_users
|
|
|
387
276
|
-h, --help Show help
|
|
388
277
|
```
|
|
389
278
|
|
|
390
|
-
|
|
279
|
+
For prerelease/local package verification:
|
|
391
280
|
|
|
392
281
|
```bash
|
|
393
|
-
npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.
|
|
282
|
+
npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.12.tgz
|
|
394
283
|
```
|
package/package.json
CHANGED
package/template/README.md
CHANGED
|
@@ -12,8 +12,6 @@ Open `http://localhost:3000`.
|
|
|
12
12
|
|
|
13
13
|
## Project commands
|
|
14
14
|
|
|
15
|
-
Generated projects use the project-local BCP Framework CLI through npm scripts:
|
|
16
|
-
|
|
17
15
|
```bash
|
|
18
16
|
npm run dev
|
|
19
17
|
npm run routes
|
|
@@ -25,25 +23,17 @@ npm start
|
|
|
25
23
|
npm run update
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
The generated scripts call commands such as `bcp dev`, `bcp generate`, `bcp build`, `bcp package` and `bcp start`.
|
|
29
|
-
|
|
30
26
|
## Project metadata
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Commit this file with the project. Do not put passwords, access keys, session secrets or tokens in it.
|
|
28
|
+
Generated projects include `bcp.project.json` with non-secret scaffold metadata. Commit it with the project, but never put passwords, tokens or access keys in it.
|
|
35
29
|
|
|
36
30
|
## Authentication — BCP 0.2.5+
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
BCP Authentication Platform v2 can opt into revocable server-side session state with `createAuth()` and `AuthSessionStore`.
|
|
32
|
+
JWT Cookie projects can opt into revocable server-side session state with `createAuth()` and `AuthSessionStore`.
|
|
41
33
|
|
|
42
34
|
## Authorization & request security — BCP 0.2.6+
|
|
43
35
|
|
|
44
|
-
Use permission guards
|
|
45
|
-
|
|
46
|
-
Authorization must always be enforced server-side. Client UI visibility is not a security boundary.
|
|
36
|
+
Use permission guards/resource policies from `bcp/auth`, and same-origin/CSRF protection from `bcp/server`. Authorization must remain server-side.
|
|
47
37
|
|
|
48
38
|
## Observability — BCP 0.2.7+
|
|
49
39
|
|
|
@@ -51,16 +41,11 @@ Authorization must always be enforced server-side. Client UI visibility is not a
|
|
|
51
41
|
import {
|
|
52
42
|
createHealthRegistry,
|
|
53
43
|
createMetricsRegistry,
|
|
54
|
-
createRequestMetricsMiddleware,
|
|
55
44
|
} from "bcp/observability";
|
|
56
45
|
```
|
|
57
46
|
|
|
58
|
-
Protect operational endpoints when their contents should not be public.
|
|
59
|
-
|
|
60
47
|
## Background jobs — BCP 0.2.8+
|
|
61
48
|
|
|
62
|
-
Create a server-only background queue:
|
|
63
|
-
|
|
64
49
|
```ts
|
|
65
50
|
import {
|
|
66
51
|
createJobQueue,
|
|
@@ -70,21 +55,6 @@ export const jobs =
|
|
|
70
55
|
createJobQueue();
|
|
71
56
|
```
|
|
72
57
|
|
|
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
|
-
```
|
|
87
|
-
|
|
88
58
|
## Job scheduling — BCP 0.2.9+
|
|
89
59
|
|
|
90
60
|
```ts
|
|
@@ -98,21 +68,9 @@ export const scheduler =
|
|
|
98
68
|
});
|
|
99
69
|
```
|
|
100
70
|
|
|
101
|
-
Example UTC cron schedule:
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
await scheduler.schedule(
|
|
105
|
-
"report.weekday",
|
|
106
|
-
{},
|
|
107
|
-
{
|
|
108
|
-
cron: "30 9 * * 1-5",
|
|
109
|
-
}
|
|
110
|
-
);
|
|
111
|
-
```
|
|
112
|
-
|
|
113
71
|
## Durable jobs — BCP 0.2.10+
|
|
114
72
|
|
|
115
|
-
Workers can use visibility leases, heartbeat renewal and
|
|
73
|
+
Workers can use visibility leases, heartbeat renewal, stale recovery and DLQ/requeue. Redis-compatible adapters are available without forcing a Redis client dependency.
|
|
116
74
|
|
|
117
75
|
```ts
|
|
118
76
|
const worker =
|
|
@@ -124,132 +82,21 @@ const worker =
|
|
|
124
82
|
});
|
|
125
83
|
```
|
|
126
84
|
|
|
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
|
-
{
|
|
136
|
-
resetAttempts: true,
|
|
137
|
-
}
|
|
138
|
-
);
|
|
139
|
-
```
|
|
140
|
-
|
|
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:
|
|
166
|
-
|
|
167
|
-
```ts
|
|
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}",
|
|
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
|
|
211
|
-
```
|
|
212
|
-
|
|
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.
|
|
216
|
-
|
|
217
|
-
`bcp/jobs` is server-only and cannot be imported into page/client bundles.
|
|
218
|
-
|
|
219
85
|
## Workflow orchestration — BCP 0.2.11+
|
|
220
86
|
|
|
221
|
-
Define persistent server-side workflows through `bcp/workflow`:
|
|
222
|
-
|
|
223
87
|
```ts
|
|
224
88
|
import {
|
|
225
89
|
createWorkflow,
|
|
226
90
|
} from "bcp/workflow";
|
|
227
91
|
|
|
228
92
|
export const onboarding =
|
|
229
|
-
createWorkflow
|
|
230
|
-
userId: number;
|
|
231
|
-
}>(
|
|
93
|
+
createWorkflow(
|
|
232
94
|
"user.onboarding",
|
|
233
95
|
workflow => {
|
|
234
96
|
workflow.step(
|
|
235
97
|
"profile",
|
|
236
98
|
createProfile
|
|
237
99
|
);
|
|
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
100
|
workflow.delay(
|
|
254
101
|
"cooldown",
|
|
255
102
|
1_000
|
|
@@ -258,41 +105,86 @@ export const onboarding =
|
|
|
258
105
|
);
|
|
259
106
|
```
|
|
260
107
|
|
|
261
|
-
|
|
108
|
+
Workflows support sequential/parallel steps, retries, persisted delays, compensation, run leases and optional durable queue execution.
|
|
109
|
+
|
|
110
|
+
## Transactional Outbox & Events — BCP 0.2.12+
|
|
111
|
+
|
|
112
|
+
Use `bcp/events` when application data and an integration event must commit atomically in the same SQL transaction.
|
|
262
113
|
|
|
263
114
|
```ts
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
115
|
+
import {
|
|
116
|
+
db,
|
|
117
|
+
} from "bcp/database";
|
|
118
|
+
|
|
119
|
+
import {
|
|
120
|
+
createSqlOutboxStore,
|
|
121
|
+
createTransactionalOutbox,
|
|
122
|
+
} from "bcp/events";
|
|
123
|
+
|
|
124
|
+
const outboxStore =
|
|
125
|
+
createSqlOutboxStore({
|
|
126
|
+
database: db,
|
|
127
|
+
driver: "mysql",
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const outbox =
|
|
131
|
+
createTransactionalOutbox({
|
|
132
|
+
store: outboxStore,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
await db.transaction(
|
|
136
|
+
async tx => {
|
|
137
|
+
await tx.execute(
|
|
138
|
+
"INSERT INTO orders ..."
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
await outbox.publish(
|
|
142
|
+
tx,
|
|
143
|
+
"order.created",
|
|
144
|
+
{
|
|
145
|
+
orderId: 42,
|
|
146
|
+
}
|
|
147
|
+
);
|
|
272
148
|
}
|
|
273
149
|
);
|
|
274
150
|
```
|
|
275
151
|
|
|
276
|
-
|
|
152
|
+
Generate the required table SQL:
|
|
277
153
|
|
|
278
154
|
```ts
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
workflowStore,
|
|
287
|
-
}
|
|
155
|
+
import {
|
|
156
|
+
createOutboxMigrationSql,
|
|
157
|
+
} from "bcp/events";
|
|
158
|
+
|
|
159
|
+
const sql =
|
|
160
|
+
createOutboxMigrationSql(
|
|
161
|
+
"mysql"
|
|
288
162
|
);
|
|
289
163
|
```
|
|
290
164
|
|
|
291
|
-
|
|
165
|
+
Dispatch committed events to the durable queue:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import {
|
|
169
|
+
createOutboxDispatcher,
|
|
170
|
+
} from "bcp/events";
|
|
171
|
+
|
|
172
|
+
const dispatcher =
|
|
173
|
+
createOutboxDispatcher({
|
|
174
|
+
store: outboxStore,
|
|
175
|
+
queue: jobs,
|
|
176
|
+
ownerId: "outbox-a",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const runner =
|
|
180
|
+
dispatcher.start();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Event `order.created` is queued as `event.order.created`.
|
|
292
184
|
|
|
293
|
-
|
|
185
|
+
The dispatcher supports leases, stale recovery, retry/backoff, custom publishers, local EventBus delivery, retention cleanup and statistics.
|
|
294
186
|
|
|
295
|
-
`bcp/
|
|
187
|
+
`bcp/events` is server-only. Outbox delivery remains at-least-once, so non-repeatable downstream side effects should use idempotency protection.
|
|
296
188
|
|
|
297
189
|
## Generate framework files
|
|
298
190
|
|
|
@@ -303,22 +195,9 @@ npm run generate -- middleware
|
|
|
303
195
|
npm run generate -- migration create_users
|
|
304
196
|
```
|
|
305
197
|
|
|
306
|
-
Equivalent direct commands:
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
bcp generate page dashboard/users
|
|
310
|
-
bcp generate api users
|
|
311
|
-
bcp generate middleware
|
|
312
|
-
bcp generate migration create_users
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
Existing page/API/middleware targets are not replaced unless `--force` is supplied explicitly.
|
|
316
|
-
|
|
317
198
|
## Direct CLI usage
|
|
318
199
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
For PowerShell direct usage:
|
|
200
|
+
For PowerShell:
|
|
322
201
|
|
|
323
202
|
```powershell
|
|
324
203
|
npm exec -- bcp-framework --version
|
|
@@ -330,8 +209,6 @@ npm exec -- bcp-framework build
|
|
|
330
209
|
npm exec -- bcp-framework package
|
|
331
210
|
```
|
|
332
211
|
|
|
333
|
-
Microsoft SQL Server can install another Windows executable named `bcp.exe`, so the `bcp-framework` alias avoids that command-name collision.
|
|
334
|
-
|
|
335
212
|
## Production build
|
|
336
213
|
|
|
337
214
|
```bash
|
|
@@ -339,12 +216,10 @@ npm run build
|
|
|
339
216
|
npm start
|
|
340
217
|
```
|
|
341
218
|
|
|
342
|
-
## Deployment package
|
|
219
|
+
## Deployment package
|
|
343
220
|
|
|
344
221
|
```bash
|
|
345
222
|
npm run package
|
|
346
223
|
```
|
|
347
224
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
BCP intentionally excludes project `.env` files and application `devDependencies` from the deployment package. Provide secrets through your deployment environment.
|
|
225
|
+
BCP excludes project `.env` files and application `devDependencies` from the deployment package. Supply secrets through the deployment environment.
|