routesync 1.0.36 → 1.0.39

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 (3) hide show
  1. package/README.md +90 -43
  2. package/dist/cli.js +618 -389
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,6 +14,7 @@ RouteSync does all of that. You point it at `routes/api.php` and it generates th
14
14
 
15
15
  ```bash
16
16
  # Step 1 — in your Laravel folder
17
+ npx routesync annotate --input routes/api.php # auto-inject #[Response] to controllers
17
18
  npx routesync scan --input routes/api.php --models
18
19
 
19
20
  # Step 2 — in your frontend folder
@@ -21,6 +22,8 @@ npx routesync generate --manifest routesync.manifest.json --output src/api --nex
21
22
  ```
22
23
 
23
24
  ```
25
+ ✔ Annotated 12 method(s) across 6 controller file(s)
26
+
24
27
  ✔ Found 35 routes, 19 models → routesync.manifest.json
25
28
 
26
29
  ✔ SDK generated → src/api
@@ -65,7 +68,35 @@ npm install routesync zod
65
68
 
66
69
  ## Full Workflow (Laravel + Next.js)
67
70
 
68
- ### 1. Scan routes & models
71
+ ### 1. Auto-annotate controllers (one-time setup)
72
+
73
+ Run this from your **Laravel project root** to auto-inject `#[Response]` attributes into every controller method:
74
+
75
+ ```bash
76
+ npx routesync annotate --input routes/api.php
77
+ ```
78
+
79
+ | Option | Description |
80
+ |---|---|
81
+ | `--input <file>` | Path to routes file (default: `routes/api.php`) |
82
+ | `--dry-run` | Preview what would be injected without writing files |
83
+ | `--force` | Re-annotate methods that already have `#[Response]` |
84
+
85
+ This command:
86
+ - Detects `return new XxxResource(...)` / `XxxResource::collection(...)` / `response()->json(new XxxResource(...))` in each controller method
87
+ - Resolves the model from the Resource's `@mixin` docblock (or strips the `Resource` suffix as fallback)
88
+ - Injects `#[Response(Model::class)]` or `#[Response(Model::class, collection: true)]` above the method
89
+ - Adds `use App\Attributes\Response;` to controller imports automatically
90
+ - Creates `app/Attributes/Response.php` if it doesn't exist yet
91
+
92
+ > **Tip:** Preview first with `--dry-run`:
93
+ > ```bash
94
+ > npx routesync annotate --input routes/api.php --dry-run
95
+ > ```
96
+
97
+ You only need to run this once, or again when you add new endpoints.
98
+
99
+ ### 2. Scan routes & models
69
100
 
70
101
  Run this from your **Laravel project root**:
71
102
 
@@ -92,28 +123,24 @@ npx routesync scan --input routes/api.php --models
92
123
  > cp ../backend/routesync.manifest.json .
93
124
  > ```
94
125
 
95
- ### 2. Generate the SDK
126
+ ### 3. Generate the SDK
96
127
 
97
128
  Run this from your **frontend project root**:
98
129
 
99
130
  ```bash
100
- npx routesync generate \
101
- --manifest routesync.manifest.json \
102
- --output src/api \
103
- --next-actions \
104
- --zod
131
+ npx routesync generate --manifest routesync.manifest.json --output src/api --next-actions --zod
105
132
  ```
106
133
 
107
134
  | Option | Default | Description |
108
135
  |---|---|---|
109
- | `--manifest` | `routesync.manifest.json` | Path to manifest from step 1 |
136
+ | `--manifest` | `routesync.manifest.json` | Path to manifest from step 2 |
110
137
  | `--output` | `src/api` | Output folder |
111
138
  | `--next-actions` | off | Generate `actions.ts` (Next.js Server Actions) |
112
139
  | `--zod` | off | Generate `schemas.ts` (Zod validation) |
113
140
  | `--no-hooks` | off | Skip generating `hooks.ts` |
114
141
  | `--msw` | off | Generate MSW mock handlers |
115
142
 
116
- > **Windows PowerShell note:** Do not use backslash `\` for line continuation — PowerShell treats it differently. Run the command on a single line:
143
+ > **Windows PowerShell note:** Do not use backslash `\` for line continuation. Run the command on a single line:
117
144
  >
118
145
  > ```powershell
119
146
  > npx routesync generate --manifest routesync.manifest.json --output src/api --next-actions --zod
@@ -133,7 +160,7 @@ src/api/
133
160
  └── models.ts ← Raw Eloquent model interfaces (when --models used)
134
161
  ```
135
162
 
136
- ### 3. Initialize the client
163
+ ### 4. Initialize the client
137
164
 
138
165
  Call `createClient` once at app startup (e.g. in your layout or provider):
139
166
 
@@ -147,7 +174,7 @@ createClient({
147
174
  })
148
175
  ```
149
176
 
150
- ### 4. Use in components
177
+ ### 5. Use in components
151
178
 
152
179
  ```tsx
153
180
  import { useApiQuery, useApiMutation } from 'routesync/react'
@@ -181,10 +208,9 @@ function AddToCart({ produkItemId }: { produkItemId: string }) {
181
208
  }
182
209
  ```
183
210
 
184
- ### 5. Use Server Actions (Next.js)
211
+ ### 6. Use Server Actions (Next.js)
185
212
 
186
213
  ```ts
187
- // In a Server Component or form action
188
214
  import { produkGetAction, cartPostItemsAction } from '@/api/actions'
189
215
 
190
216
  // GET — no params needed
@@ -216,14 +242,14 @@ Stage 1: PHP 8 #[RouteSyncResponse] attribute on method ← most explicit
216
242
  Stage 2: return new UserResource($user) in method body
217
243
  │ ├─ Stage 2a: #[RouteSyncResponse] on Resource class
218
244
  │ ├─ Stage 2b: @mixin \App\Models\User docblock
219
- │ ├─ Stage 2c: __construct(User $user) type hint ← NEW
220
- │ ├─ Stage 2d: @var User $resource docblock ← NEW
221
- │ ├─ Stage 2e: Strip "Resource" suffix → App\Models\* ← NEW
222
- │ └─ Stage 2f: toArray() keys vs DB column matching ← NEW
245
+ │ ├─ Stage 2c: __construct(User $user) type hint
246
+ │ ├─ Stage 2d: @var User $resource docblock
247
+ │ ├─ Stage 2e: Strip "Resource" suffix → App\Models\*
248
+ │ └─ Stage 2f: toArray() keys vs DB column matching
223
249
 
224
250
 
225
251
  Stage 3: response()->json([...]) inline array
226
- keys matched against DB columns (min score 2) ← NEW
252
+ keys matched against DB columns (min score 2)
227
253
 
228
254
 
229
255
  response: unknown ← annotate manually if all stages fail
@@ -247,9 +273,23 @@ public function index(): JsonResponse
247
273
  }
248
274
  ```
249
275
 
250
- ### Explicit annotation with PHP 8 Attribute
276
+ ### Auto-annotate with CLI (recommended)
251
277
 
252
- Use `#[RouteSyncResponse]` when auto-inference fails — for example, when the Resource name doesn't match the model, or the response is a DTO/custom shape.
278
+ Instead of adding `#[Response]` by hand, let the CLI do it:
279
+
280
+ ```bash
281
+ # Preview first
282
+ npx routesync annotate --input routes/api.php --dry-run
283
+
284
+ # Apply
285
+ npx routesync annotate --input routes/api.php
286
+ ```
287
+
288
+ This scans every controller method, detects which Resource it returns, resolves the model, and injects `#[Response(Model::class)]` automatically. See [Auto-annotate controllers](#1-auto-annotate-controllers-one-time-setup) for details.
289
+
290
+ ### Manual annotation with PHP 8 Attribute
291
+
292
+ Use `#[RouteSyncResponse]` when auto-inference fails — for example when the Resource name doesn't match the model, or the response is a DTO/custom shape.
253
293
 
254
294
  **Step 1 — Create the attribute class** (`app/Attributes/RouteSyncResponse.php`):
255
295
 
@@ -278,17 +318,14 @@ use App\Models\User;
278
318
 
279
319
  class AuthController extends Controller
280
320
  {
281
- // Single object response
282
321
  #[RouteSyncResponse(model: User::class)]
283
322
  public function register(RegisterRequest $request): JsonResponse
284
323
  {
285
324
  $user = User::create($request->validated());
286
325
  $token = $user->createToken('auth')->plainTextToken;
287
-
288
326
  return response()->json(['token' => $token, 'user' => new UserResource($user)]);
289
327
  }
290
328
 
291
- // Collection response
292
329
  #[RouteSyncResponse(model: User::class, collection: true)]
293
330
  public function index(): JsonResponse
294
331
  {
@@ -308,11 +345,7 @@ class UserResource extends JsonResource
308
345
  {
309
346
  public function toArray($request): array
310
347
  {
311
- return [
312
- 'id' => $this->id,
313
- 'name' => $this->name,
314
- 'email' => $this->email,
315
- ];
348
+ return ['id' => $this->id, 'name' => $this->name, 'email' => $this->email];
316
349
  }
317
350
  }
318
351
  ```
@@ -356,13 +389,10 @@ import { defineApi, endpoint, resource } from 'routesync'
356
389
  createClient({ baseURL: 'https://api.myapp.com/api' })
357
390
 
358
391
  export const api = defineApi({
359
- // Basic endpoint
360
392
  auth: {
361
393
  login: endpoint<{ token: string }>({ method: 'POST', path: '/login' }),
362
394
  logout: endpoint({ method: 'POST', path: '/logout', auth: true }),
363
395
  },
364
-
365
- // With path params
366
396
  produk: {
367
397
  list: endpoint<ProdukItem[]>({ method: 'GET', path: '/produk' }),
368
398
  detail: endpoint<ProdukItem, { id: string }>({ method: 'GET', path: '/produk/:id' }),
@@ -370,8 +400,6 @@ export const api = defineApi({
370
400
  method: 'POST', path: '/produk', auth: true
371
401
  }),
372
402
  },
373
-
374
- // resource() — shared auth/headers for a group
375
403
  cart: resource({
376
404
  auth: true,
377
405
  endpoints: {
@@ -420,7 +448,7 @@ import { api } from '@/api/api'
420
448
  // GET
421
449
  const { data, isLoading } = useApiQuery(api.orders.get)
422
450
 
423
- // GET with params + query
451
+ // GET with params
424
452
  const { data } = useApiQuery(api.orders.getId, { params: { id: '1' } })
425
453
 
426
454
  // Mutation
@@ -433,8 +461,7 @@ mutation.mutate({ body: { produk_item_id: '5', qty: 1 } })
433
461
  ```ts
434
462
  import { generateHooks } from 'routesync'
435
463
 
436
- const hooks = generateHooks(api)
437
- const { useOrdersGet, useCartPostItems } = hooks
464
+ const { useOrdersGet, useCartPostItems } = generateHooks(api)
438
465
  // GET/DELETE → useQuery, everything else → useMutation
439
466
  ```
440
467
 
@@ -443,8 +470,7 @@ const { useOrdersGet, useCartPostItems } = hooks
443
470
  ```ts
444
471
  import { createHooks } from 'routesync/react'
445
472
 
446
- const cartHooks = createHooks(api.cart)
447
- const { usePostItems, usePatchItemsProdukItemId } = cartHooks
473
+ const { usePostItems, usePatchItemsProdukItemId } = createHooks(api.cart)
448
474
  ```
449
475
 
450
476
  ---
@@ -470,6 +496,15 @@ When using `--zod` with `routesync generate`, `schemas.ts` is generated from you
470
496
  ## CLI Reference
471
497
 
472
498
  ```bash
499
+ # Auto-inject #[Response] attributes into controller methods
500
+ npx routesync annotate --input routes/api.php
501
+
502
+ # Preview without writing files
503
+ npx routesync annotate --input routes/api.php --dry-run
504
+
505
+ # Re-annotate already-annotated methods
506
+ npx routesync annotate --input routes/api.php --force
507
+
473
508
  # Scan Laravel routes only
474
509
  npx routesync scan --input routes/api.php
475
510
 
@@ -492,16 +527,28 @@ npx routesync watch --input routes/api.php --output src/api
492
527
 
493
528
  ```
494
529
  routes/api.php + app/Models/
495
- ↓ routesync scan --models
530
+
531
+
532
+ npx routesync annotate ← inject #[Response] into controllers
533
+
534
+
535
+ npx routesync scan --models ← read routes + DB columns → manifest
536
+
537
+
496
538
  routesync.manifest.json
497
- ↓ routesync generate
539
+
540
+
541
+ npx routesync generate ← generate SDK from manifest
542
+
543
+
498
544
  src/api/
499
- ├── api.ts ← defineApi + endpoints
545
+ ├── api.ts ← defineApi + endpoints + Contract types
500
546
  ├── types.ts ← interfaces from DB columns
501
547
  ├── hooks.ts ← TanStack Query hooks
502
548
  ├── actions.ts ← Next.js Server Actions
503
549
  └── schemas.ts ← Zod schemas
504
-
550
+
551
+
505
552
  React / Vue / Next.js
506
553
  ```
507
554
 
@@ -512,7 +559,7 @@ The CLI parses your route file via PHP reflection (using Laravel's own bootstrap
512
559
  ## Requirements
513
560
 
514
561
  - Node.js >= 20
515
- - PHP available in PATH (for `scan --models`)
562
+ - PHP available in PATH (for `scan --models` and `annotate`)
516
563
  - Laravel project with database accessible (for `scan --models`)
517
564
 
518
565
  ## License