jcc-express-mvc 1.9.4 → 1.9.6

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 (117) hide show
  1. package/final-documentation/Digging Deeper/Helpers.md +8 -7
  2. package/final-documentation/Digging Deeper/Monitor.md +30 -7
  3. package/final-documentation/Getting-Started/Frontend.md +12 -3
  4. package/final-documentation/Getting-Started/Installation.md +11 -3
  5. package/final-documentation/JCC-Eloquent/Defining-Model.md +1 -1
  6. package/final-documentation/JCC-Eloquent/JCC-Eloquent-Introduction.md +1 -1
  7. package/final-documentation/JCC-Eloquent/Pagination.md +13 -10
  8. package/final-documentation/JCC-Eloquent/Query-Builder.md +2 -2
  9. package/final-documentation/Security/Authentication.md +39 -5
  10. package/final-documentation/The Basics/Artisan-Node.md +19 -2
  11. package/final-documentation/The Basics/Asset-bundling.md +5 -4
  12. package/lib/Application/ApplicationBuilder.d.ts.map +1 -1
  13. package/lib/Application/ApplicationBuilder.js +2 -0
  14. package/lib/Auth/index.d.ts +2 -0
  15. package/lib/Auth/index.d.ts.map +1 -1
  16. package/lib/Auth/index.js +15 -16
  17. package/lib/Auth/type.d.ts +11 -0
  18. package/lib/Auth/type.d.ts.map +1 -0
  19. package/lib/Auth/type.js +2 -0
  20. package/lib/Command-Line/MakeCommand.d.ts +1 -0
  21. package/lib/Command-Line/MakeCommand.d.ts.map +1 -1
  22. package/lib/Command-Line/MakeCommand.js +62 -0
  23. package/lib/Command-Line/NodeArtisanCommand.d.ts +2 -0
  24. package/lib/Command-Line/NodeArtisanCommand.d.ts.map +1 -1
  25. package/lib/Command-Line/NodeArtisanCommand.js +19 -0
  26. package/lib/Command-Line/PrismaCommand.d.ts +9 -0
  27. package/lib/Command-Line/PrismaCommand.d.ts.map +1 -0
  28. package/lib/Command-Line/PrismaCommand.js +46 -0
  29. package/lib/Command-Line/files/Resource.d.ts +8 -0
  30. package/lib/Command-Line/files/Resource.d.ts.map +1 -0
  31. package/lib/Command-Line/files/Resource.js +25 -0
  32. package/lib/Database/Database.d.ts +1 -6
  33. package/lib/Database/Database.d.ts.map +1 -1
  34. package/lib/Database/Database.js +22 -9
  35. package/lib/Database/DatabaseServiceProvider.d.ts.map +1 -1
  36. package/lib/Database/DatabaseServiceProvider.js +4 -0
  37. package/lib/Database/Drivers/Prisma/adapter.d.ts +14 -0
  38. package/lib/Database/Drivers/Prisma/adapter.d.ts.map +1 -0
  39. package/lib/Database/Drivers/Prisma/adapter.js +50 -0
  40. package/lib/Database/Drivers/Prisma/adapters/mariadb.d.ts +6 -0
  41. package/lib/Database/Drivers/Prisma/adapters/mariadb.d.ts.map +1 -0
  42. package/lib/Database/Drivers/Prisma/adapters/mariadb.js +28 -0
  43. package/lib/Database/Drivers/Prisma/adapters/postgres.d.ts +4 -0
  44. package/lib/Database/Drivers/Prisma/adapters/postgres.d.ts.map +1 -0
  45. package/lib/Database/Drivers/Prisma/adapters/postgres.js +30 -0
  46. package/lib/Database/Drivers/Prisma/adapters/sqlite.d.ts +6 -0
  47. package/lib/Database/Drivers/Prisma/adapters/sqlite.d.ts.map +1 -0
  48. package/lib/Database/Drivers/Prisma/adapters/sqlite.js +60 -0
  49. package/lib/Database/Drivers/Prisma/connection.d.ts +5 -0
  50. package/lib/Database/Drivers/Prisma/connection.d.ts.map +1 -0
  51. package/lib/Database/Drivers/Prisma/connection.js +14 -0
  52. package/lib/Database/Drivers/Prisma/readConnectionEnv.d.ts +3 -0
  53. package/lib/Database/Drivers/Prisma/readConnectionEnv.d.ts.map +1 -0
  54. package/lib/Database/Drivers/Prisma/readConnectionEnv.js +15 -0
  55. package/lib/Database/Drivers/Prisma/register.d.ts +7 -0
  56. package/lib/Database/Drivers/Prisma/register.d.ts.map +1 -0
  57. package/lib/Database/Drivers/Prisma/register.js +43 -0
  58. package/lib/Database/Drivers/Prisma/types.d.ts +18 -0
  59. package/lib/Database/Drivers/Prisma/types.d.ts.map +1 -0
  60. package/lib/Database/Drivers/Prisma/types.js +2 -0
  61. package/lib/Database/Drivers/PrismaDriver.d.ts +24 -0
  62. package/lib/Database/Drivers/PrismaDriver.d.ts.map +1 -0
  63. package/lib/Database/Drivers/PrismaDriver.js +41 -0
  64. package/lib/Database/Prisma/connection.d.ts +6 -0
  65. package/lib/Database/Prisma/connection.d.ts.map +1 -0
  66. package/lib/Database/Prisma/connection.js +19 -0
  67. package/lib/Database/PrismaServiceProvider.d.ts +12 -0
  68. package/lib/Database/PrismaServiceProvider.d.ts.map +1 -0
  69. package/lib/Database/PrismaServiceProvider.js +45 -0
  70. package/lib/Database/index.d.ts +4 -0
  71. package/lib/Database/index.d.ts.map +1 -1
  72. package/lib/Database/index.js +13 -1
  73. package/lib/Database/interface.d.ts +1 -1
  74. package/lib/Database/isPrismaOrm.d.ts +3 -0
  75. package/lib/Database/isPrismaOrm.d.ts.map +1 -0
  76. package/lib/Database/isPrismaOrm.js +7 -0
  77. package/lib/Database/type.d.ts +1 -1
  78. package/lib/Database/type.d.ts.map +1 -1
  79. package/lib/Error/public/419.html +1 -1
  80. package/lib/Global/helpers.d.ts.map +1 -1
  81. package/lib/Global/helpers.js +18 -5
  82. package/lib/Jcc-eloquent/lib/Builder.d.ts +3 -4
  83. package/lib/Jcc-eloquent/lib/Builder.d.ts.map +1 -1
  84. package/lib/Jcc-eloquent/lib/Builder.js +29 -23
  85. package/lib/Jcc-eloquent/lib/Model.d.ts +3 -4
  86. package/lib/Jcc-eloquent/lib/Model.d.ts.map +1 -1
  87. package/lib/Jcc-eloquent/lib/Model.js +32 -31
  88. package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts +1 -2
  89. package/lib/Jcc-eloquent/lib/QueryBuilder.d.ts.map +1 -1
  90. package/lib/Jcc-eloquent/lib/QueryBuilder.js +2 -2
  91. package/lib/Middleware/index.d.ts.map +1 -1
  92. package/lib/Middleware/index.js +0 -1
  93. package/lib/Resources/JsonResources.d.ts +27 -0
  94. package/lib/Resources/JsonResources.d.ts.map +1 -0
  95. package/lib/Resources/JsonResources.js +137 -0
  96. package/lib/Resources/ResourceCollection.d.ts +15 -0
  97. package/lib/Resources/ResourceCollection.d.ts.map +1 -0
  98. package/lib/Resources/ResourceCollection.js +32 -0
  99. package/lib/Resources/helpers.d.ts +8 -0
  100. package/lib/Resources/helpers.d.ts.map +1 -0
  101. package/lib/Resources/helpers.js +38 -0
  102. package/lib/Resources/index.d.ts +5 -0
  103. package/lib/Resources/index.d.ts.map +1 -0
  104. package/lib/Resources/index.js +9 -0
  105. package/lib/Resources/types.d.ts +10 -0
  106. package/lib/Resources/types.d.ts.map +1 -0
  107. package/lib/Resources/types.js +5 -0
  108. package/lib/util/index.d.ts +8 -0
  109. package/lib/util/index.d.ts.map +1 -1
  110. package/lib/util/index.js +25 -1
  111. package/package.json +1 -1
  112. package/lib/Auth/Auth.d.ts +0 -8
  113. package/lib/Auth/Auth.d.ts.map +0 -1
  114. package/lib/Auth/Auth.js +0 -22
  115. package/lib/Inertia/old-index.d.ts +0 -7
  116. package/lib/Inertia/old-index.d.ts.map +0 -1
  117. package/lib/Inertia/old-index.js +0 -55
@@ -21,21 +21,22 @@ These helpers are available during request handling and in `tinker` (with contai
21
21
 
22
22
  ## HTTP context helpers
23
23
 
24
- - `request()` -> current `AppRequest`
25
- - `response()` -> current `AppResponse`
26
- - `next()` -> current `next` function
24
+ - `request()` -> current `AppRequest` (returns `{}` outside an HTTP request)
25
+ - `response()` -> current `AppResponse` (returns `{}` outside an HTTP request)
27
26
  - `view(view, options?, callback?)`
28
27
  - `inertia(component, props?, option?)`
29
28
  - `redirect(url, status?)`
30
29
  - `back(url?)`
31
30
 
32
- These depend on per-request container bindings, so they are request-lifecycle helpers.
31
+ These depend on per-request container bindings, so they are request-lifecycle helpers. `request()` and `response()` are safe to call from code that may run outside middleware (for example pagination link builders) — they return an empty object when no request is bound.
32
+
33
+ `next()` is **not** exposed as a global helper. Use the `next` argument in middleware and route handlers directly.
33
34
 
34
35
  ---
35
36
 
36
37
  ## Auth and authorization helpers
37
38
 
38
- - `auth()` -> current request user
39
+ - `auth()` -> `Authentication` class (`Auth.attempt`, `Auth.apiAttempt`, `Auth.logout`, etc.)
39
40
  - `Gate` -> `GateFacade`
40
41
  - `can(user, ability, model?, ...args)`
41
42
  - `authorize(user, ability, ...args)`
@@ -98,8 +99,8 @@ Global helper types are declared in `jcc-express-mvc/global.d.ts`, so TypeScript
98
99
 
99
100
  ## Notes
100
101
 
101
- - `auth` is assigned twice inside `globalHelpers`; the later assignment returns `request().user`.
102
- - Helpers that rely on request bindings (`request`, `response`, `next`, `validate`, etc.) are not meaningful before HTTP middleware bootstraps those bindings.
102
+ - Helpers that rely on request bindings (`request`, `response`, `validate`, `session`, etc.) are not meaningful before HTTP middleware bootstraps those bindings.
103
+ - `request()` and `response()` catch missing bindings and return `{}` instead of throwing.
103
104
 
104
105
  ---
105
106
 
@@ -157,8 +157,14 @@ export const monitor: Partial<MonitorConfig> = {
157
157
  },
158
158
  system: { enabled: true, intervalMs: 15_000 },
159
159
  errors: { enabled: true },
160
- database: { enabled: false, slowQueryMs: 200 },
161
- queue: { enabled: false },
160
+ database: { enabled: true, slowQueryMs: 200 },
161
+ queue: { enabled: true },
162
+ commands: { enabled: true },
163
+ schedule: { enabled: true },
164
+ mail: { enabled: true },
165
+ cache: { enabled: true },
166
+ outgoing: { enabled: true },
167
+ logs: { enabled: true },
162
168
  },
163
169
 
164
170
  exporters: {
@@ -187,6 +193,22 @@ export const monitor: Partial<MonitorConfig> = {
187
193
  recentErrors: 100,
188
194
  recentJobs: 100,
189
195
  recentQueries: 100,
196
+ recentCommands: 100,
197
+ recentScheduledTasks: 100,
198
+ recentNotifications: 100,
199
+ recentMail: 100,
200
+ recentCacheOps: 100,
201
+ recentOutgoingRequests: 100,
202
+ recentLogs: 100,
203
+ },
204
+
205
+ storage: {
206
+ driver: config.get("MONITOR_STORAGE", "memory") as
207
+ | "memory"
208
+ | "file"
209
+ | "redis"
210
+ | "database",
211
+ file: { path: config.get("MONITOR_STORAGE_PATH", "storage/monitor") },
190
212
  },
191
213
 
192
214
  alerts: {
@@ -258,13 +280,14 @@ The `/_monitor` path is excluded from request metric collection so the dashboard
258
280
  | --------------- | ------------------------------------------------------------------- |
259
281
  | Requests | Auto — `MonitorMiddleware` records every request |
260
282
  | Exceptions | Auto — `ErrorCollector` + `MonitorErrorMiddleware` |
261
- | Queries | Manual call `Monitor.recordQuery({ ... })` from your ORM hook |
262
- | Jobs | Manual call `Monitor.recordJob({ ... })` or hook `Queue.events` |
283
+ | Queries | Auto when `collectors.database.enabled` ORM query hooks |
284
+ | Jobs | Auto when `collectors.queue.enabled` `Queue` records job events |
285
+ | Commands | Auto when `collectors.commands.enabled` — console kernel records runs |
286
+ | Scheduled Tasks | Auto when `collectors.schedule.enabled` — scheduler records runs |
263
287
  | Mail | Auto when `collectors.mail.enabled` — wraps the `Mail` facade |
264
288
  | Cache | Auto when `collectors.cache.enabled` — wraps `Cache.get/put/forget` |
265
- | Outgoing | Auto when `collectors.outgoing.enabled` — patches global `fetch` |
266
- | Commands | Manual `Monitor.recordCommand({ ... })` from your console kernel |
267
- | Scheduled Tasks | Manual — `Monitor.recordScheduledTask({ ... })` from the runner |
289
+ | Outgoing | Auto when `collectors.outgoing.enabled` — axios + `fetch` patch |
290
+ | Logs | Auto when `collectors.logs.enabled` framework logger integration |
268
291
  | Notifications | Manual — `Monitor.recordNotification({ ... })` after dispatch |
269
292
 
270
293
  ---
@@ -156,11 +156,20 @@ Vite is the default bundler: fast builds and Hot Module Replacement (HMR) during
156
156
 
157
157
  For Blade tags, `public/hot`, manifest resolution, and troubleshooting, see [Asset-bundling.md](../The Basics/Asset-bundling.md).
158
158
 
159
- Local workflow — start the asset server before the HTTP server:
159
+ Local workflow — use two terminals:
160
160
 
161
161
  ```bash
162
- bun run watch # or: npm run watch → runs Vite
163
- bun run dev # or: npm run dev → runs the Express app
162
+ # Terminal 1 backend (recommended: Laravel-style watcher)
163
+ bun artisanNode serve
164
+
165
+ # Terminal 2 — Vite assets (writes public/hot)
166
+ bun run watch # or: npm run watch
167
+ ```
168
+
169
+ Alternative backend command:
170
+
171
+ ```bash
172
+ bun run dev # or: npm run dev → bun --watch server.ts (simpler, no serve watcher)
164
173
  ```
165
174
 
166
175
  Production-style build:
@@ -224,6 +224,14 @@ This starts Vite in watch mode (React/Vue, Tailwind, hot reload, etc.). Let it k
224
224
 
225
225
  In a second terminal, from the same project root:
226
226
 
227
+ ```bash
228
+ bun artisanNode serve
229
+ ```
230
+
231
+ This is the recommended dev server: it watches backend files, reloads silently, keeps the same port, and reacts to `public/hot` when Vite starts.
232
+
233
+ Alternative (simpler, without the Laravel-style watcher):
234
+
227
235
  ```bash
228
236
  bun run dev
229
237
  ```
@@ -234,7 +242,7 @@ or:
234
242
  npm run dev
235
243
  ```
236
244
 
237
- This usually runs `bun --watch server.ts` (when using Bun) or the `dev` script from `package.json`, which boots Express and your bootstrap pipeline.
245
+ This runs `bun --watch server.ts`, which boots Express and your bootstrap pipeline with Bun's built-in file watcher.
238
246
 
239
247
  ### 7.3 Open the app
240
248
 
@@ -267,7 +275,7 @@ Use `bun artisanNode list` or `bun artisanNode help [command]` to see commands (
267
275
  - [ ] `.env` exists and is filled in for your machine (starter projects already have the file; cloned repos may need `cp .env.example .env`)
268
276
  - [ ] `bun artisanNode key:generate` has set `JWT_SECRET` (required for JWT auth)
269
277
  - [ ] `bun artisanNode migrate` completed (or you intentionally skip DB for now)
270
- - [ ] `watch` then `dev` both run without crashing (`bun run` / `npm run` as you prefer)
278
+ - [ ] `watch` then `serve` (or `dev`) both run without crashing (`bun run` / `npm run` as you prefer)
271
279
  - [ ] Browser loads `http://localhost:<PORT>`
272
280
 
273
281
  ---
@@ -287,7 +295,7 @@ Use `bun artisanNode list` or `bun artisanNode help [command]` to see commands (
287
295
 
288
296
  - [Configuration.md](./Configuration.md) — `.env`, `app/Config`, and how values are loaded at runtime
289
297
  - [Directory-structure.md](./Directory-structure.md) — Project folders and what each is for
290
- - [Frontend.md](./Frontend.md) — Why `watch` runs before `dev` (Vite + Inertia)
298
+ - [Frontend.md](./Frontend.md) — Why `watch` runs alongside `serve` (Vite + Inertia)
291
299
  - [Deployment.md](./Deployment.md) — Production `.env`, builds, migrations, and process management
292
300
  - `docs/docs-for-dev.md` — broad framework guide (routing, Eloquent, validation, queues, etc.)
293
301
  - `docs/getting-started.md` — shorter orientation
@@ -279,7 +279,7 @@ Route.middleware(["apiAuth"]).get("/me", async (req) => {
279
279
  If `hasToken` is not enabled on the model:
280
280
 
281
281
  ```text
282
- ModelTokenError: User Missing required token. Please set 'protected hasToken = true' .
282
+ ModelTokenError: User Model Missing required token. Please set 'protected hasToken = true' .
283
283
  ```
284
284
 
285
285
  The global error handler returns `500` with the error message for `ModelTokenError`.
@@ -111,7 +111,7 @@ export class User extends Model {
111
111
  Model-level pagination helper exists:
112
112
 
113
113
  ```typescript
114
- const page = await User.paginate(req, 15);
114
+ const page = await User.paginate(15);
115
115
  ```
116
116
 
117
117
  This returns `data` plus pagination metadata/links.
@@ -4,16 +4,18 @@
4
4
 
5
5
  JCC-Eloquent provides pagination at both model and builder level.
6
6
 
7
- Both methods read `request.query.page` and return `data` + pagination metadata.
7
+ Pagination reads the current HTTP request via the global `request()` helper — you do **not** pass `req` as the first argument. Page number comes from `request().query.page` (default `1`). Link URLs use `request().protocol`, host, and path.
8
+
9
+ Both methods return `data` + pagination metadata.
8
10
 
9
11
  ---
10
12
 
11
13
  ## Model pagination
12
14
 
13
- `Model.paginate(request, perPage?)`:
15
+ `Model.paginate(perPage?, page?)`:
14
16
 
15
17
  ```typescript
16
- const paginatedUsers = await User.paginate(req, 15);
18
+ const paginatedUsers = await User.paginate(15);
17
19
  ```
18
20
 
19
21
  This returns:
@@ -31,7 +33,7 @@ Builder pagination works on filtered queries:
31
33
  ```typescript
32
34
  const page = await User.where("active", true)
33
35
  .orderBy("created_at", "desc")
34
- .paginate(req, 20);
36
+ .paginate(20);
35
37
  ```
36
38
 
37
39
  Use this when pagination must respect custom constraints.
@@ -43,7 +45,7 @@ Use this when pagination must respect custom constraints.
43
45
  `simplePaginate` uses offset pagination without running a `COUNT(*)` query — faster for large tables when you don't need a total count:
44
46
 
45
47
  ```typescript
46
- const page = await User.where("active", true).simplePaginate(req, 15);
48
+ const page = await User.where("active", true).simplePaginate(15);
47
49
  ```
48
50
 
49
51
  Response shape:
@@ -70,7 +72,7 @@ No `total` or `total_pages` — just whether there is a next page.
70
72
  `cursorPaginate` implements keyset (cursor-based) pagination — stable and O(1) for any page depth, regardless of table size:
71
73
 
72
74
  ```typescript
73
- const page = await User.orderBy("id").cursorPaginate(req, 20);
75
+ const page = await User.orderBy("id").cursorPaginate(20);
74
76
  ```
75
77
 
76
78
  Response shape:
@@ -112,7 +114,7 @@ Pass the next page by forwarding `?cursor=<token>` from `meta.next_cursor`.
112
114
  For full manual control:
113
115
 
114
116
  ```typescript
115
- const currentPage = Number(req.query.page || 1);
117
+ const currentPage = Number(request().query?.page || 1);
116
118
  const perPage = 20;
117
119
  const offset = (currentPage - 1) * perPage;
118
120
 
@@ -131,7 +133,8 @@ const rows = await User.orderBy("id", "desc").offset(offset).limit(perPage).get(
131
133
 
132
134
  ## Summary
133
135
 
134
- - Use `paginate(req, perPage)` for standard pagination with total counts and page links.
135
- - Use `simplePaginate(req, perPage)` for large tables where you don't need a total count.
136
- - Use `cursorPaginate(req, perPage)` for high-performance, stable pagination on large or frequently-updated tables.
136
+ - Use `paginate(perPage)` for standard pagination with total counts and page links.
137
+ - Use `simplePaginate(perPage)` for large tables where you don't need a total count.
138
+ - Use `cursorPaginate(perPage)` for high-performance, stable pagination on large or frequently-updated tables.
139
+ - Pagination uses the global `request()` helper for page number and link generation — call it inside a route handler or middleware context.
137
140
  - Use `offset` + `limit` only for fully custom paging flows.
@@ -258,7 +258,7 @@ await User.where("id", 1).updateJson("preferences.notifications.email", false);
258
258
  For large datasets where `COUNT(*)` is too slow, use cursor-based (keyset) pagination:
259
259
 
260
260
  ```typescript
261
- const page = await User.orderBy("id").cursorPaginate(req, 20);
261
+ const page = await User.orderBy("id").cursorPaginate(20);
262
262
  ```
263
263
 
264
264
  Response shape:
@@ -291,7 +291,7 @@ Pass the next page by forwarding `?cursor=<token>` from `meta.next_cursor`.
291
291
  Offset pagination without a `COUNT(*)` query — faster for large tables:
292
292
 
293
293
  ```typescript
294
- const page = await User.where("active", true).simplePaginate(req, 15);
294
+ const page = await User.where("active", true).simplePaginate(15);
295
295
  ```
296
296
 
297
297
  Response:
@@ -101,9 +101,43 @@ Route.middleware(["loginThrottle"]).post("/auth/login", async (req, res, next) =
101
101
  Route.middleware(["auth"]).get("/home", homeHandler);
102
102
  ```
103
103
 
104
- ### API token login (model `createToken`)
104
+ ### API token login (`apiAttempt`)
105
105
 
106
- For stateless API clients, issue a bearer token from a model instance instead of cookie login:
106
+ For stateless API clients, use `auth().apiAttempt()` to validate credentials and issue a bearer token in one step:
107
+
108
+ ```typescript
109
+ Route.post("/api/login", async () => {
110
+ const result = await auth().apiAttempt({ field: "email", table: "User" });
111
+
112
+ if (!result.success) {
113
+ return response().status(401).json({ message: result.message });
114
+ }
115
+
116
+ return {
117
+ token: result.token,
118
+ user: result.user,
119
+ };
120
+ });
121
+
122
+ Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
123
+ return { user: req.user };
124
+ });
125
+ ```
126
+
127
+ `apiAttempt` options:
128
+
129
+ | Field | Default | Description |
130
+ |---|---|---|
131
+ | `field` | `"email"` | Request input key to match (`email`, `phone`, `username`, etc.) |
132
+ | `table` | `"User"` | Model name resolved via `getModel()` |
133
+
134
+ The request body must include the credential field and `password`. On success, returns `{ success: true, token, user, message }`. On failure, returns `{ success: false, token: null, user: null, message: "Invalid credentials" }`.
135
+
136
+ The model must have `protected hasToken = true`. User lookup and password verification are shared with session login via `findUserForAuth()`.
137
+
138
+ ### Manual token login (`createToken`)
139
+
140
+ You can also issue a token manually after your own credential check:
107
141
 
108
142
  ```typescript
109
143
  import { User } from "@/Model/User";
@@ -120,13 +154,13 @@ Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
120
154
  });
121
155
  ```
122
156
 
123
- The model must have `protected hasToken = true`. Full details: [Defining-Model.md](../JCC-Eloquent/Defining-Model.md#api-tokens-createtoken).
157
+ Full `createToken` details: [Defining-Model.md](../JCC-Eloquent/Defining-Model.md#api-tokens-createtoken).
124
158
 
125
159
  ---
126
160
 
127
161
  ## Summary
128
162
 
129
163
  - Authentication uses JWT cookies with rotating refresh tokens.
130
- - Use `Auth.attempt(next)` for login, `auth`/`apiAuth` middleware for protection.
131
- - For API bearer tokens, enable `hasToken` on the model and call `createToken()` — `apiAuth` reloads the user from JWT claims.
164
+ - Use `Auth.attempt(next)` for cookie login, `auth().apiAttempt()` for API email/password + bearer token, and `auth`/`apiAuth` middleware for protection.
165
+ - For manual API tokens, enable `hasToken` on the model and call `createToken()` — `apiAuth` reloads the user from JWT claims.
132
166
  - `Auth.logout()` clears auth state and redirects to login.
@@ -60,12 +60,29 @@ bun artisanNode db:wipe
60
60
  ## Dev server
61
61
 
62
62
  ```bash
63
- bun artisanNode serve # start the dev server with a file watcher (entry: server.ts)
63
+ bun artisanNode serve # Laravel-style dev server with file watcher (entry: server.ts)
64
64
  bun artisanNode serve app.ts # use a different entry file
65
65
  bun artisanNode watch # alias of `serve`
66
66
  ```
67
67
 
68
- `serve` (alias `watch`) boots the app under a file watcher and restarts on change — the same thing `npm run dev` runs. It takes an optional entry file argument (defaults to `server.ts`).
68
+ `serve` (alias `watch`) is the recommended backend dev command. It:
69
+
70
+ - watches backend paths (`app/`, `route/`, `jcc-express-mvc/`, `resources/views/`, etc.)
71
+ - reloads **silently** on code changes (no terminal clear, no restart banner)
72
+ - keeps the same `PORT` from `.env` across restarts
73
+ - watches `public/hot` so the backend picks up Vite when the dev asset server starts
74
+
75
+ Run Vite separately for frontend assets:
76
+
77
+ ```bash
78
+ # Terminal 1 — backend (file watcher + Express)
79
+ bun artisanNode serve
80
+
81
+ # Terminal 2 — frontend (Vite HMR, writes public/hot)
82
+ npm run watch
83
+ ```
84
+
85
+ `npm run dev` (`bun --watch server.ts`) is a simpler alternative without the Laravel-style watcher. Prefer `bun artisanNode serve` when you want silent reloads, stable ports, and `public/hot` integration.
69
86
 
70
87
  ---
71
88
 
@@ -15,11 +15,12 @@ For Inertia, SSR, and the HTTP kernel’s `inertia()` middleware, see `Getting-S
15
15
  Typical `package.json` scripts:
16
16
 
17
17
  - `watch` — Runs `vite` (dev server, HMR, writes `public/hot`).
18
- - `dev` — Runs the Node/Bun HTTP app (`server.ts`); use alongside `watch` during local UI work.
18
+ - `dev` — Runs `bun --watch server.ts` (simple backend reload; no Laravel-style watcher).
19
+ - `serve` — Run via `bun artisanNode serve` (recommended backend dev server; see `Artisan-Node.md`).
19
20
  - `vite-build` — `vite build` plus `vite build --ssr` when you ship SSR (adjust to your app).
20
21
  - `build` — TypeScript compile (`tsc`) for the server codebase; not a substitute for `vite-build`.
21
22
 
22
- Start Vite before hitting pages that use `@vite`, or the template layer will not resolve the dev server URL.
23
+ Start Vite before hitting pages that use `@vite`, or refresh after `public/hot` appears. The template layer reads `public/hot` on each request in development.
23
24
 
24
25
  ---
25
26
 
@@ -41,13 +42,13 @@ Add or change `input` when you create new entrypoints. Tailwind and React (or Vu
41
42
 
42
43
  ## `public/hot` and the dev server
43
44
 
44
- In non-production, `EngineHelpers` reads `public/hot` (first line is the Vite origin, e.g. `http://127.0.0.1:5173`). That value becomes `viteHost` for:
45
+ In non-production, `EngineHelpers` reads `public/hot` on **each render** (first line is the Vite origin, e.g. `http://127.0.0.1:5173`). That value becomes `viteHost` for:
45
46
 
46
47
  - Module `<script>` tags pointing at your entries.
47
48
  - Optional link to the CSS entry when you pass more than one resource in `@vite` (first entry is treated as CSS in dev in that case).
48
49
  - Injection of `/@vite/client` for HMR.
49
50
 
50
- If `hot` is missing, tags may point at an empty base—run `npm run watch` / `bun run watch`.
51
+ If `hot` is missing, tags may point at an empty base run `npm run watch` / `bun run watch`. After Vite creates `public/hot`, refresh the page (or use `bun artisanNode serve`, which watches `public/hot` and reloads the backend when Vite starts).
51
52
 
52
53
  ---
53
54
 
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationBuilder.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Application/ApplicationBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAU3C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,qBAAa,kBAAkB;IACtB,GAAG,EAAE,WAAW,CAAC;gBAEZ,GAAG,EAAE,WAAW;IAIrB,UAAU,CAAC,MAAM,EAAE,GAAG;IAKtB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAMtC,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE;IAOtC,aAAa,CAClB,SAAS,EAAE,KAAK,CAAC,KAAK,GAAG,EAAE,WAAW,KAAK,eAAe,CAAC;IA2B7D,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,gBAAgB;IAIjB,WAAW;IAKX,cAAc,CAAC,oBAAoB,GAAE,cAAc,EAAO;IAY1D,MAAM;CAKd"}
1
+ {"version":3,"file":"ApplicationBuilder.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Application/ApplicationBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAI/D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAU3C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,qBAAa,kBAAkB;IACtB,GAAG,EAAE,WAAW,CAAC;gBAEZ,GAAG,EAAE,WAAW;IAIrB,UAAU,CAAC,MAAM,EAAE,GAAG;IAKtB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAMtC,WAAW,CAAC,WAAW,EAAE,WAAW,EAAE;IAOtC,aAAa,CAClB,SAAS,EAAE,KAAK,CAAC,KAAK,GAAG,EAAE,WAAW,KAAK,eAAe,CAAC;IA4B7D,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,gBAAgB;IAIjB,WAAW;IAKX,cAAc,CAAC,oBAAoB,GAAE,cAAc,EAAO;IAY1D,MAAM;CAKd"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApplicationBuilder = void 0;
4
4
  const DatabaseServiceProvider_1 = require("../Database/DatabaseServiceProvider");
5
+ const PrismaServiceProvider_1 = require("../Database/PrismaServiceProvider");
5
6
  const Middleware_1 = require("../Middleware");
6
7
  const NodeArtisanCommand_1 = require("../Command-Line/NodeArtisanCommand");
7
8
  const helpers_1 = require("../Global/helpers");
@@ -40,6 +41,7 @@ class ApplicationBuilder {
40
41
  const chain = [
41
42
  LogServiceProvider_1.LogServiceProvider,
42
43
  DatabaseServiceProvider_1.DatabaseServiceProvider,
44
+ PrismaServiceProvider_1.PrismaServiceProvider,
43
45
  SessionServiceProvider_1.SessionServiceProvider,
44
46
  CacheServiceProvider_1.CacheServiceProvider,
45
47
  ...ordered,
@@ -1,5 +1,6 @@
1
1
  import { AppRequest, AppResponse, AppNext } from "../Interface";
2
2
  import { type IRefreshTokenStore } from "./refreshTokenStore";
3
+ import { ApiAuthResponse, ApiAuthType } from "./type";
3
4
  export declare class Authentication {
4
5
  private static refreshStore;
5
6
  /** Use a shared store (e.g. Redis) when running multiple app instances. */
@@ -12,6 +13,7 @@ export declare class Authentication {
12
13
  private static setTokens;
13
14
  /** Handle user login attempt */
14
15
  static attempt(next: AppNext, redirect?: string): Promise<void>;
16
+ static apiAttempt(data: ApiAuthType): Promise<ApiAuthResponse>;
15
17
  /**
16
18
  * After the user is resolved (e.g. OAuth via Socialite), issue JWT cookies
17
19
  * and redirect or JSON response like {@link Authentication.attempt}.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAgBhE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAsB7B,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAgD;IAE3E,2EAA2E;IAC3E,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAI5D,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,cAAc;IAc7B,0DAA0D;mBACrC,OAAO;IAqB5B,wFAAwF;IACxF,OAAO,CAAC,MAAM,CAAC,SAAS;IA0BxB,gCAAgC;WACnB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAgB;IA2B9D;;;OAGG;WACU,aAAa,CACxB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,GAAE,MAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IA4BhB,+BAA+B;WAClB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO;IAsC1E,qBAAqB;IACrB,MAAM,CAAC,MAAM;IAsBb,4FAA4F;IAC5F,MAAM,CAAC,KAAK,IAAI,OAAO;IAWvB,MAAM,CAAC,IAAI;IAIX,MAAM,CAAC,EAAE;WAII,WAAW,CACtB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,GAAE,MAAgB;CAI7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAkBhE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAsBtD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,YAAY,CAAgD;IAE3E,2EAA2E;IAC3E,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAI5D,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,cAAc;IAc7B,0DAA0D;mBACrC,OAAO;IAS5B,wFAAwF;IACxF,OAAO,CAAC,MAAM,CAAC,SAAS;IA0BxB,gCAAgC;WACnB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAgB;WA2BjD,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IAwBpE;;;OAGG;WACU,aAAa,CACxB,GAAG,EAAE,UAAU,EACf,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,GAAE,MAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IA4BhB,+BAA+B;WAClB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO;IAsC1E,qBAAqB;IACrB,MAAM,CAAC,MAAM;IAsBb,4FAA4F;IAC5F,MAAM,CAAC,KAAK,IAAI,OAAO;IAWvB,MAAM,CAAC,IAAI;IAIX,MAAM,CAAC,EAAE;WAII,WAAW,CACtB,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,QAAQ,GAAE,MAAgB;CAI7B"}
package/lib/Auth/index.js CHANGED
@@ -2,9 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Authentication = void 0;
4
4
  const util_1 = require("../util");
5
- const Config_1 = require("../Config/Config");
6
5
  const ValidationException_v2_1 = require("../Error/ValidationException-v2");
7
- const Jcc_eloquent_1 = require("../Jcc-eloquent");
8
6
  const refreshTokenStore_1 = require("./refreshTokenStore");
9
7
  const REFRESH_TTL_MS = 7 * 24 * 60 * 60 * 1000;
10
8
  const ACCESS_MAX_AGE_MS = 60 * 60 * 1000;
@@ -47,20 +45,7 @@ class Authentication {
47
45
  const field = this.getCredentials(data);
48
46
  if (!Object.keys(field).length)
49
47
  return { user: null, field: "email" };
50
- let user = null;
51
- const orm = Config_1.config.get("DB_ORM");
52
- if (orm === "mongodb" || orm === "mongoose") {
53
- user = await User.findOne(field).select("+password");
54
- }
55
- else if (orm === "sequelize") {
56
- user = await User.findOne({ where: field });
57
- }
58
- else {
59
- // JCC ORM
60
- user = await Jcc_eloquent_1.DB.table("users")
61
- .where(Object.keys(field)[0], Object.values(field)[0])
62
- .first();
63
- }
48
+ let user = await (0, util_1.findUserForAuth)(User, field);
64
49
  return { user, field: Object.keys(field)[0] || "email" };
65
50
  }
66
51
  /** Generate and attach tokens to cookies (refresh is rotated server-side via `jti`). */
@@ -100,6 +85,20 @@ class Authentication {
100
85
  next(error);
101
86
  }
102
87
  }
88
+ static async apiAttempt(data) {
89
+ data = data || { field: "email", model: "User" };
90
+ const input = request().input(data.field || "");
91
+ const password = request().input("password");
92
+ const { [data.model]: Model } = (0, util_1.getModel)(data.model);
93
+ let user = await (0, util_1.findUserForAuth)(Model, { [data.field]: input });
94
+ if (!user) {
95
+ return (0, util_1.authResponseMessages)(false, "Invalid credentials", null, null);
96
+ }
97
+ if (!(await (0, util_1.verifyHash)(password, user?.password || ""))) {
98
+ return (0, util_1.authResponseMessages)(false, "Invalid credentials", null, null);
99
+ }
100
+ return (0, util_1.authResponseMessages)(true, "Login successful", user?.toJSON(), user.createToken());
101
+ }
103
102
  /**
104
103
  * After the user is resolved (e.g. OAuth via Socialite), issue JWT cookies
105
104
  * and redirect or JSON response like {@link Authentication.attempt}.
@@ -0,0 +1,11 @@
1
+ export type ApiAuthType = {
2
+ field?: string;
3
+ model?: string;
4
+ };
5
+ export type ApiAuthResponse = {
6
+ success: boolean;
7
+ user: Record<string, any> | null;
8
+ token: string | null;
9
+ message: string;
10
+ };
11
+ //# sourceMappingURL=type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Auth/type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -3,6 +3,7 @@ export declare class MakeCommand {
3
3
  createController(controllerName: string, modelName?: boolean): any;
4
4
  createApiController(controllerName: string, modelName?: boolean): any;
5
5
  createModel(modelName: string): any;
6
+ createResource(name: string): void;
6
7
  createRequest(requestName: string): any;
7
8
  createMigration(migrationName: string, optionalTable: string): any;
8
9
  createSeeder(name: string): any;
@@ -1 +1 @@
1
- {"version":3,"file":"MakeCommand.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Command-Line/MakeCommand.ts"],"names":[],"mappings":"AAmCA,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAA0C;IAE/D,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,GAAG;IAsChE,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,GAAG;IAkDnE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG;IAuBnC,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG;IA4BvC,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,GAAG;IA4BlE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;IA2B/B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG;IA8B/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IA8B3B,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC1D,gBAAgB;IA+BhB,kBAAkB;IAyBlB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;IAiCjD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC/C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC1D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IA+BjC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IA+BpD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAiC7B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IA8BhC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;CAkCrC"}
1
+ {"version":3,"file":"MakeCommand.d.ts","sourceRoot":"","sources":["../../../jcc-express-mvc/lib/Command-Line/MakeCommand.ts"],"names":[],"mappings":"AAoCA,qBAAa,WAAW;IACtB,OAAO,CAAC,aAAa,CAA0C;IAE/D,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,GAAG;IAsChE,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,GAAG;IAkDnE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG;IAuBnC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAuClC,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG;IA4BvC,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,GAAG;IA4BlE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;IA2B/B,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG;IA8B/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IA8B3B,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC1D,gBAAgB;IA+BhB,kBAAkB;IAyBlB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;IAiCjD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC/C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,GAAG;IAmC1D,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IA+BjC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IA+BpD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAiC7B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IA8BhC,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG;CAkCrC"}
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -28,6 +61,7 @@ const Service_1 = __importDefault(require("./files/Service"));
28
61
  const Repository_1 = __importDefault(require("./files/Repository"));
29
62
  const Dto_1 = __importDefault(require("./files/Dto"));
30
63
  const Action_1 = __importDefault(require("./files/Action"));
64
+ const Resource_1 = __importStar(require("./files/Resource"));
31
65
  const Config_1 = require("../Config/Config");
32
66
  const rootPath = app_root_path_1.default.path;
33
67
  /** Convert PascalCase to kebab-case (e.g. SendEmails -> send:emails) */
@@ -115,6 +149,34 @@ class MakeCommand {
115
149
  return console.log(colors_1.default.red(`${modelName} model not added`)); // Log error if model addition fails
116
150
  }
117
151
  }
152
+ createResource(name) {
153
+ try {
154
+ if (!name) {
155
+ console.log(colors_1.default.red("Please give a name for your resource"));
156
+ return;
157
+ }
158
+ const { className, filePath } = (0, Resource_1.parseResourceName)(name);
159
+ const resourcePath = path_1.default.resolve(`${rootPath}/app/Http/Resources`);
160
+ if (!fs_1.default.existsSync(resourcePath)) {
161
+ fs_1.default.mkdirSync(resourcePath, { recursive: true });
162
+ }
163
+ const targetDir = path_1.default.dirname(path_1.default.resolve(resourcePath, filePath));
164
+ if (!fs_1.default.existsSync(targetDir)) {
165
+ fs_1.default.mkdirSync(targetDir, { recursive: true });
166
+ }
167
+ const resourceFile = path_1.default.resolve(resourcePath, `${filePath}.${this.fileExtension}`);
168
+ if (fs_1.default.existsSync(resourceFile)) {
169
+ console.log(colors_1.default.yellow(`${className} already exists`));
170
+ return;
171
+ }
172
+ fs_1.default.writeFileSync(resourceFile, (0, Resource_1.default)(className));
173
+ console.log(colors_1.default.green(`${className} created successfully [${resourceFile}]`));
174
+ }
175
+ catch (err) {
176
+ console.log(colors_1.default.red(`${name} resource not created`));
177
+ console.log(err.message);
178
+ }
179
+ }
118
180
  createRequest(requestName) {
119
181
  try {
120
182
  if (!requestName) {
@@ -12,6 +12,7 @@ export declare class ConsoleKernel {
12
12
  private _watch;
13
13
  private _app;
14
14
  private _cache;
15
+ private _prisma;
15
16
  private get migrate();
16
17
  private get db();
17
18
  private get make();
@@ -19,6 +20,7 @@ export declare class ConsoleKernel {
19
20
  private get schedule();
20
21
  private get watch();
21
22
  private get cache();
23
+ private get prisma();
22
24
  private app;
23
25
  constructor();
24
26
  /** Load user commands from app/Console/Command (guarded, non-blocking for missing dir) */