@chidchanun/bcp 0.2.3 → 0.2.5

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.
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  export type CliCommand =
5
5
  | "dev"
6
6
  | "build"
7
+ | "package"
7
8
  | "start"
8
9
  | "routes"
9
10
  | "update"
@@ -417,6 +418,7 @@ export function parseCliArgs(
417
418
  if (
418
419
  argument === "dev" ||
419
420
  argument === "build" ||
421
+ argument === "package" ||
420
422
  argument === "start" ||
421
423
  argument === "routes" ||
422
424
  argument === "update" ||
@@ -520,6 +520,7 @@ function resolveEnvironmentMode(
520
520
 
521
521
  if (
522
522
  command === "build" ||
523
+ command === "package" ||
523
524
  command === "start"
524
525
  ) {
525
526
  return "production";
@@ -51,6 +51,11 @@ switch (cliOptions.command) {
51
51
  break;
52
52
  }
53
53
 
54
+ case "package": {
55
+ await runPackage();
56
+ break;
57
+ }
58
+
54
59
  case "start": {
55
60
  await runStart();
56
61
  break;
@@ -356,6 +361,57 @@ async function runBuild() {
356
361
  console.log("");
357
362
  }
358
363
 
364
+ async function runPackage(): Promise<void> {
365
+ process.env.NODE_ENV =
366
+ "production";
367
+
368
+ await runBuild();
369
+
370
+ const rootDirectory =
371
+ resolveProjectRoot(
372
+ cliOptions.rootDirectory
373
+ );
374
+ const {
375
+ packageApplication,
376
+ } =
377
+ await import(
378
+ "./application-packaging.js"
379
+ );
380
+ const result =
381
+ packageApplication({
382
+ rootDirectory,
383
+ frameworkVersion:
384
+ FRAMEWORK_VERSION,
385
+ });
386
+
387
+ console.log(
388
+ "[BCP Package] Application package created."
389
+ );
390
+ console.log(
391
+ `[BCP Package] Output: ${result.outputDirectory}`
392
+ );
393
+ console.log(
394
+ `[BCP Package] Target: ${result.packageManifest.target}`
395
+ );
396
+ console.log(
397
+ `[BCP Package] Files: ${result.packageManifest.files.length}`
398
+ );
399
+ console.log(
400
+ `[BCP Package] Install: ${result.packageManifest.install.command}`
401
+ );
402
+ console.log(
403
+ `[BCP Package] Start: ${result.packageManifest.runtime.startCommand}`
404
+ );
405
+
406
+ for (const warning of result.warnings) {
407
+ console.warn(
408
+ `[BCP Package] Warning: ${warning}`
409
+ );
410
+ }
411
+
412
+ console.log("");
413
+ }
414
+
359
415
  async function runStart() {
360
416
  process.env.NODE_ENV =
361
417
  "production";
@@ -866,6 +922,7 @@ Usage:
866
922
  Commands:
867
923
  dev Start the development server
868
924
  build Create optimized client assets and standalone server.mjs
925
+ package Build and create a production deployment package
869
926
  start Start the standalone production build
870
927
  routes Print discovered page and API routes
871
928
  update [target] Update BCP Framework (default target: latest)
@@ -890,12 +947,13 @@ Options:
890
947
  -v, --version Show version
891
948
 
892
949
  Config precedence:
893
- dev/build: CLI > BCP_* environment > bcp.config.ts > defaults
894
- start: CLI > runtime BCP_* environment > frozen build config
950
+ dev/build/package: CLI > BCP_* environment > bcp.config.ts > defaults
951
+ start: CLI > runtime BCP_* environment > frozen build config
895
952
 
896
953
  Examples:
897
954
  bcp dev
898
955
  bcp build
956
+ bcp package
899
957
  bcp start
900
958
  bcp routes
901
959
  bcp generate page dashboard/users
@@ -4,6 +4,9 @@ export {
4
4
  getSession,
5
5
  login,
6
6
  logout,
7
+ logoutAll,
8
+ revokeSession,
9
+ revokeUserSessions,
7
10
  rotateSession,
8
11
 
9
12
  type AuthApi,
@@ -13,17 +16,30 @@ export {
13
16
  type AuthUser,
14
17
  } from "../../server/src/auth.js";
15
18
 
19
+ export {
20
+ createMemoryAuthSessionStore,
21
+
22
+ type AuthSessionStore,
23
+ type AuthSessionStoreRecord,
24
+ type MemoryAuthSessionStore,
25
+ } from "../../server/src/auth-session-store.js";
26
+
16
27
  export {
17
28
  createAuthGuard,
29
+ createGuestGuard,
18
30
  createRoleGuard,
19
31
  getGuardAuth,
20
32
  requireAuth,
33
+ requireGuest,
21
34
  requireRole,
22
35
 
23
36
  type AuthGuardData,
24
37
  type AuthGuardFunction,
25
38
  type AuthGuardResult,
39
+ type GuestGuardFunction,
40
+ type GuestGuardResult,
26
41
  type RequireAuthOptions,
42
+ type RequireGuestOptions,
27
43
  type RequireRoleOptions,
28
44
  type RequiredRole,
29
45
  } from "../../server/src/auth-guard.js";
@@ -18,10 +18,13 @@ export interface AuthGuardData<
18
18
 
19
19
  export interface RequireAuthOptions
20
20
  extends AuthOptions {
21
- redirectTo?:
22
- string |
23
- URL |
24
- null;
21
+ redirectTo?: string | URL | null;
22
+ redirectStatus?: RedirectStatus;
23
+ }
24
+
25
+ export interface RequireGuestOptions
26
+ extends AuthOptions {
27
+ redirectTo?: string | URL | null;
25
28
  redirectStatus?: RedirectStatus;
26
29
  }
27
30
 
@@ -29,10 +32,7 @@ export interface RequireRoleOptions
29
32
  extends RequireAuthOptions {
30
33
  roleField?: string;
31
34
  match?: "any" | "all";
32
- forbiddenRedirectTo?:
33
- string |
34
- URL |
35
- null;
35
+ forbiddenRedirectTo?: string | URL | null;
36
36
  forbiddenRedirectStatus?: RedirectStatus;
37
37
  }
38
38
 
@@ -47,16 +47,20 @@ export type AuthGuardResult<
47
47
  | AuthGuardData<TUser, TData>
48
48
  | Response;
49
49
 
50
+ export type GuestGuardResult =
51
+ | Record<string, never>
52
+ | Response;
53
+
50
54
  export type AuthGuardFunction<
51
55
  TUser extends AuthUser = AuthUser,
52
56
  TData extends object = Record<string, never>
53
57
  > = () => Promise<
54
- AuthGuardResult<
55
- TUser,
56
- TData
57
- >
58
+ AuthGuardResult<TUser, TData>
58
59
  >;
59
60
 
61
+ export type GuestGuardFunction =
62
+ () => Promise<GuestGuardResult>;
63
+
60
64
  export async function requireAuth<
61
65
  TUser extends AuthUser = AuthUser,
62
66
  TData extends object = Record<string, never>
@@ -69,23 +73,18 @@ export async function requireAuth<
69
73
  ...authOptions
70
74
  } = options;
71
75
  const session =
72
- await auth<
73
- TUser,
74
- TData
75
- >(
76
+ await auth<TUser, TData>(
76
77
  authOptions
77
78
  );
78
79
 
79
80
  if (!session) {
80
81
  if (
81
- redirectTo ===
82
- null
82
+ redirectTo === null
83
83
  ) {
84
84
  return new Response(
85
85
  "Unauthorized",
86
86
  {
87
- status:
88
- 401,
87
+ status: 401,
89
88
  }
90
89
  );
91
90
  }
@@ -97,11 +96,47 @@ export async function requireAuth<
97
96
  }
98
97
 
99
98
  return {
100
- auth:
101
- session,
99
+ auth: session,
102
100
  };
103
101
  }
104
102
 
103
+ export async function requireGuest<
104
+ TUser extends AuthUser = AuthUser,
105
+ TData extends object = Record<string, never>
106
+ >(
107
+ options: RequireGuestOptions = {}
108
+ ): Promise<GuestGuardResult> {
109
+ const {
110
+ redirectTo = "/",
111
+ redirectStatus = 303,
112
+ ...authOptions
113
+ } = options;
114
+ const session =
115
+ await auth<TUser, TData>(
116
+ authOptions
117
+ );
118
+
119
+ if (!session) {
120
+ return {};
121
+ }
122
+
123
+ if (
124
+ redirectTo === null
125
+ ) {
126
+ return new Response(
127
+ "Already authenticated",
128
+ {
129
+ status: 409,
130
+ }
131
+ );
132
+ }
133
+
134
+ return redirect(
135
+ redirectTo,
136
+ redirectStatus
137
+ );
138
+ }
139
+
105
140
  export async function requireRole<
106
141
  TUser extends AuthUser = AuthUser,
107
142
  TData extends object = Record<string, never>
@@ -121,16 +156,12 @@ export async function requireRole<
121
156
  ...authOptions
122
157
  } = options;
123
158
  const authenticated =
124
- await requireAuth<
125
- TUser,
126
- TData
127
- >(
159
+ await requireAuth<TUser, TData>(
128
160
  authOptions
129
161
  );
130
162
 
131
163
  if (
132
- authenticated instanceof
133
- Response
164
+ authenticated instanceof Response
134
165
  ) {
135
166
  return authenticated;
136
167
  }
@@ -157,8 +188,7 @@ export async function requireRole<
157
188
 
158
189
  if (!allowed) {
159
190
  if (
160
- forbiddenRedirectTo !==
161
- null
191
+ forbiddenRedirectTo !== null
162
192
  ) {
163
193
  return redirect(
164
194
  forbiddenRedirectTo,
@@ -169,8 +199,7 @@ export async function requireRole<
169
199
  return new Response(
170
200
  "Forbidden",
171
201
  {
172
- status:
173
- 403,
202
+ status: 403,
174
203
  }
175
204
  );
176
205
  }
@@ -185,10 +214,19 @@ export function createAuthGuard<
185
214
  options: RequireAuthOptions = {}
186
215
  ): AuthGuardFunction<TUser, TData> {
187
216
  return () =>
188
- requireAuth<
189
- TUser,
190
- TData
191
- >(
217
+ requireAuth<TUser, TData>(
218
+ options
219
+ );
220
+ }
221
+
222
+ export function createGuestGuard<
223
+ TUser extends AuthUser = AuthUser,
224
+ TData extends object = Record<string, never>
225
+ >(
226
+ options: RequireGuestOptions = {}
227
+ ): GuestGuardFunction {
228
+ return () =>
229
+ requireGuest<TUser, TData>(
192
230
  options
193
231
  );
194
232
  }
@@ -201,10 +239,7 @@ export function createRoleGuard<
201
239
  options: RequireRoleOptions = {}
202
240
  ): AuthGuardFunction<TUser, TData> {
203
241
  return () =>
204
- requireRole<
205
- TUser,
206
- TData
207
- >(
242
+ requireRole<TUser, TData>(
208
243
  requiredRole,
209
244
  options
210
245
  );
@@ -214,18 +249,16 @@ export function getGuardAuth<
214
249
  TUser extends AuthUser = AuthUser,
215
250
  TData extends object = Record<string, never>
216
251
  >(
217
- guardData:
218
- Readonly<{
219
- auth?: unknown;
220
- }>
252
+ guardData: Readonly<{
253
+ auth?: unknown;
254
+ }>
221
255
  ): AuthSession<TUser, TData> | null {
222
256
  const value =
223
257
  guardData.auth;
224
258
 
225
259
  if (
226
260
  !value ||
227
- typeof value !==
228
- "object" ||
261
+ typeof value !== "object" ||
229
262
  Array.isArray(
230
263
  value
231
264
  )
@@ -235,24 +268,17 @@ export function getGuardAuth<
235
268
 
236
269
  const session =
237
270
  value as Partial<
238
- AuthSession<
239
- TUser,
240
- TData
241
- >
271
+ AuthSession<TUser, TData>
242
272
  >;
243
273
 
244
274
  if (
245
- typeof session.sid !==
246
- "string" ||
247
- session.sid.trim().length ===
248
- 0 ||
249
- typeof session.iat !==
250
- "number" ||
275
+ typeof session.sid !== "string" ||
276
+ session.sid.trim().length === 0 ||
277
+ typeof session.iat !== "number" ||
251
278
  !Number.isFinite(
252
279
  session.iat
253
280
  ) ||
254
- typeof session.exp !==
255
- "number" ||
281
+ typeof session.exp !== "number" ||
256
282
  !Number.isFinite(
257
283
  session.exp
258
284
  ) ||
@@ -264,18 +290,14 @@ export function getGuardAuth<
264
290
  }
265
291
 
266
292
  return value as
267
- AuthSession<
268
- TUser,
269
- TData
270
- >;
293
+ AuthSession<TUser, TData>;
271
294
  }
272
295
 
273
296
  function normalizeRequiredRoles(
274
297
  value: RequiredRole
275
298
  ): string[] {
276
299
  const roles =
277
- typeof value ===
278
- "string"
300
+ typeof value === "string"
279
301
  ? [
280
302
  value,
281
303
  ]
@@ -289,12 +311,10 @@ function normalizeRequiredRoles(
289
311
  );
290
312
 
291
313
  if (
292
- normalized.length ===
293
- 0 ||
314
+ normalized.length === 0 ||
294
315
  normalized.some(
295
316
  (role) =>
296
- role.length ===
297
- 0
317
+ role.length === 0
298
318
  )
299
319
  ) {
300
320
  throw new TypeError(
@@ -324,14 +344,12 @@ function readAssignedRoles(
324
344
 
325
345
  const value =
326
346
  (
327
- user as
328
- unknown as
347
+ user as unknown as
329
348
  Record<string, unknown>
330
349
  )[field];
331
350
 
332
351
  if (
333
- typeof value ===
334
- "string"
352
+ typeof value === "string"
335
353
  ) {
336
354
  const role =
337
355
  value.trim();
@@ -353,8 +371,7 @@ function readAssignedRoles(
353
371
  (
354
372
  role
355
373
  ): role is string =>
356
- typeof role ===
357
- "string"
374
+ typeof role === "string"
358
375
  )
359
376
  .map(
360
377
  (role) =>
@@ -373,8 +390,7 @@ function isGuardAuthUser(
373
390
  ): value is AuthUser {
374
391
  if (
375
392
  !value ||
376
- typeof value !==
377
- "object" ||
393
+ typeof value !== "object" ||
378
394
  Array.isArray(
379
395
  value
380
396
  )
@@ -389,13 +405,10 @@ function isGuardAuthUser(
389
405
  ).id;
390
406
 
391
407
  return (
392
- typeof id ===
393
- "string" &&
394
- id.trim().length >
395
- 0
408
+ typeof id === "string" &&
409
+ id.trim().length > 0
396
410
  ) || (
397
- typeof id ===
398
- "number" &&
411
+ typeof id === "number" &&
399
412
  Number.isFinite(
400
413
  id
401
414
  )