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 +95 -42
- package/package.json +1 -1
- package/template/README.md +176 -62
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.11",
|
|
96
96
|
"createdWith": {
|
|
97
97
|
"package": "create-bcp-app",
|
|
98
|
-
"version": "0.2.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
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.
|
|
393
|
+
npx create-bcp-app my-app --bcp file:../chidchanun-bcp-0.2.11.tgz
|
|
341
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
|
|
|
@@ -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
|
-
|
|
101
|
+
Example UTC cron schedule:
|
|
118
102
|
|
|
119
103
|
```ts
|
|
120
104
|
await scheduler.schedule(
|
|
121
|
-
"
|
|
105
|
+
"report.weekday",
|
|
122
106
|
{},
|
|
123
107
|
{
|
|
124
|
-
|
|
108
|
+
cron: "30 9 * * 1-5",
|
|
125
109
|
}
|
|
126
110
|
);
|
|
127
111
|
```
|
|
128
112
|
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
136
|
+
resetAttempts: true,
|
|
137
137
|
}
|
|
138
138
|
);
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
|
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.
|
|
319
|
+
BCP Framework is installed as a project-local dependency.
|
|
189
320
|
|
|
190
|
-
PowerShell
|
|
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
|
```
|