svelte-reflector 1.2.0 → 1.2.2
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 +395 -518
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,518 +1,395 @@
|
|
|
1
|
-
# Svelte Reflector
|
|
2
|
-
|
|
3
|
-
**Turn your OpenAPI into a first-class Svelte 5 DX.**
|
|
4
|
-
|
|
5
|
-
Svelte Reflector is a **developer-experience-first code generator** that converts OpenAPI specs into fully typed, reactive Svelte 5 modules — ready for production, forms included.
|
|
6
|
-
|
|
7
|
-
[](https://www.npmjs.com/package/svelte-reflector)
|
|
8
|
-
[](https://www.npmjs.com/package/svelte-reflector)
|
|
9
|
-
[](https://www.npmjs.com/package/svelte-reflector)
|
|
10
|
-
[](https://www.typescriptlang.org/)
|
|
11
|
-
[](https://svelte.dev/)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **Form Handling** - Auto-generates form schemas with validation support
|
|
20
|
-
- **Type-Safe API Calls** - Full TypeScript support for all API operations
|
|
21
|
-
- **Query Parameter Sync** - `QueryBuilder` and `EnumQueryBuilder` keep state synced with URL searchParams
|
|
22
|
-
- **Enum Support** - Auto-generates enum types and array enum query builders
|
|
23
|
-
- **OpenAPI/Swagger Compatible** - Works with any backend that exposes OpenAPI specs
|
|
24
|
-
- **Development Mode** - Smart regeneration based on environment
|
|
25
|
-
- **Validation Ready** - Built-in support for custom field validators
|
|
26
|
-
- **Vite Plugin** - Can be used as a Vite plugin for automatic generation on build
|
|
27
|
-
|
|
28
|
-
## Installation
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm install svelte-reflector
|
|
32
|
-
# or
|
|
33
|
-
yarn add svelte-reflector
|
|
34
|
-
# or
|
|
35
|
-
pnpm add svelte-reflector
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
### 3.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (!/[0-9]/.test(value)) return "Password must contain a number";
|
|
397
|
-
if (!/[!@#$%^&*]/.test(value)) return "Password must contain a special character";
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Date validation
|
|
402
|
-
export function validateDate(value: string): string | null {
|
|
403
|
-
if (!value) return null;
|
|
404
|
-
const date = new Date(value);
|
|
405
|
-
if (isNaN(date.getTime())) return "Invalid date";
|
|
406
|
-
if (date > new Date()) return "Date cannot be in the future";
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Brazilian ZIP code (CEP) validation
|
|
411
|
-
export function validateZipcode(value: string): string | null {
|
|
412
|
-
if (!value) return null;
|
|
413
|
-
const cepRegex = /^\d{5}-?\d{3}$/;
|
|
414
|
-
return cepRegex.test(value) ? null : "Invalid ZIP code format";
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// URL validation
|
|
418
|
-
export function validateUrl(value: string): string | null {
|
|
419
|
-
if (!value) return null;
|
|
420
|
-
try {
|
|
421
|
-
new URL(value);
|
|
422
|
-
return null;
|
|
423
|
-
} catch {
|
|
424
|
-
return "Invalid URL format";
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Min/max length validator factory
|
|
429
|
-
export function minLength(min: number) {
|
|
430
|
-
return (value: string): string | null => {
|
|
431
|
-
if (!value) return null;
|
|
432
|
-
return value.length >= min ? null : `Must be at least ${min} characters`;
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
export function maxLength(max: number) {
|
|
437
|
-
return (value: string): string | null => {
|
|
438
|
-
if (!value) return null;
|
|
439
|
-
return value.length <= max ? null : `Must be at most ${max} characters`;
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Number range validator factory
|
|
444
|
-
export function numberRange(min: number, max: number) {
|
|
445
|
-
return (value: number): string | null => {
|
|
446
|
-
if (value === null || value === undefined) return null;
|
|
447
|
-
return value >= min && value <= max ? null : `Must be between ${min} and ${max}`;
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// Required field validator
|
|
452
|
-
export function required(value: string | number | boolean | null): string | null {
|
|
453
|
-
if (value === null || value === undefined || value === '') {
|
|
454
|
-
return "This field is required";
|
|
455
|
-
}
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### Manual Schema Access
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
import { User } from "$reflector/schemas.svelte";
|
|
464
|
-
|
|
465
|
-
// Create instance
|
|
466
|
-
const user = new User({ name: "John", email: "john@example.com" });
|
|
467
|
-
|
|
468
|
-
// Get data bundle
|
|
469
|
-
const data = user.bundle(); // { name: "John", email: "john@example.com" }
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
### Batch Query Updates
|
|
473
|
-
|
|
474
|
-
```typescript
|
|
475
|
-
import { setQueryGroup } from "$reflector/reflector.svelte";
|
|
476
|
-
|
|
477
|
-
// Update multiple query params at once
|
|
478
|
-
setQueryGroup([
|
|
479
|
-
{ key: "page", value: 1 },
|
|
480
|
-
{ key: "status", value: "active" },
|
|
481
|
-
{ key: "roles", value: ["admin", "editor"] }, // Array params supported
|
|
482
|
-
]);
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
## Troubleshooting
|
|
486
|
-
|
|
487
|
-
### "BACKEND_URL vazio" Error
|
|
488
|
-
|
|
489
|
-
Ensure you have set `BACKEND_URL` or `PUBLIC_BACKEND` in your `.env` file.
|
|
490
|
-
|
|
491
|
-
### Schemas Not Updating
|
|
492
|
-
|
|
493
|
-
In DEV mode, run `npx reflect` manually. Check that your backend's OpenAPI spec is accessible at `{BACKEND_URL}openapi.json`.
|
|
494
|
-
|
|
495
|
-
### Type Errors After Generation
|
|
496
|
-
|
|
497
|
-
1. Restart your TypeScript language server
|
|
498
|
-
2. Check path aliases in `tsconfig.json`
|
|
499
|
-
3. Ensure `$reflector/*` alias is configured
|
|
500
|
-
|
|
501
|
-
## License
|
|
502
|
-
|
|
503
|
-
MIT License - see [LICENSE](LICENSE) for details.
|
|
504
|
-
|
|
505
|
-
## Contributing
|
|
506
|
-
|
|
507
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
508
|
-
|
|
509
|
-
## Links
|
|
510
|
-
|
|
511
|
-
- [npm](https://www.npmjs.com/package/svelte-reflector)
|
|
512
|
-
- [GitHub](https://github.com/aleleppy/reflector)
|
|
513
|
-
- [Svelte](https://svelte.dev/)
|
|
514
|
-
- [OpenAPI Specification](https://swagger.io/specification/)
|
|
515
|
-
|
|
516
|
-
---
|
|
517
|
-
|
|
518
|
-
Built with by the Pináculo Digital team.
|
|
1
|
+
# Svelte Reflector
|
|
2
|
+
|
|
3
|
+
**Turn your OpenAPI into a first-class Svelte 5 DX.**
|
|
4
|
+
|
|
5
|
+
Svelte Reflector is a **developer-experience-first code generator** that converts OpenAPI specs into fully typed, reactive Svelte 5 modules — ready for production, forms included.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/svelte-reflector)
|
|
8
|
+
[](https://www.npmjs.com/package/svelte-reflector)
|
|
9
|
+
[](https://www.npmjs.com/package/svelte-reflector)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](https://svelte.dev/)
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Automatic Type Generation** - Generates TypeScript interfaces and classes from OpenAPI schemas
|
|
16
|
+
- **Svelte 5 Runes Integration** - Uses `$state` and `$derived` for reactive state management
|
|
17
|
+
- **Abstract Modules** - Generated modules are abstract classes, ready to be extended with custom logic
|
|
18
|
+
- **Per-Module Schemas** - Each module gets its own schema file with only the types it needs (tree-shaking friendly)
|
|
19
|
+
- **Form Handling** - Auto-generates form schemas with `BuildedInput<T>` wrappers and validation support
|
|
20
|
+
- **Type-Safe API Calls** - Full TypeScript support for all API operations
|
|
21
|
+
- **Query Parameter Sync** - `QueryBuilder` and `EnumQueryBuilder` keep state synced with URL searchParams
|
|
22
|
+
- **Enum Support** - Auto-generates enum types and array enum query builders
|
|
23
|
+
- **OpenAPI/Swagger Compatible** - Works with any backend that exposes OpenAPI specs
|
|
24
|
+
- **Development Mode** - Smart regeneration based on environment
|
|
25
|
+
- **Validation Ready** - Built-in support for custom field validators
|
|
26
|
+
- **Vite Plugin** - Can be used as a Vite plugin for automatic generation on build
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install svelte-reflector
|
|
32
|
+
# or
|
|
33
|
+
yarn add svelte-reflector
|
|
34
|
+
# or
|
|
35
|
+
pnpm add svelte-reflector
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> **Note:** `prettier` >= 3.0.0 is a required peer dependency. Make sure it's installed in your project.
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
### 1. Configure Environment Variables
|
|
43
|
+
|
|
44
|
+
Create a `.env` file in your project root:
|
|
45
|
+
|
|
46
|
+
```env
|
|
47
|
+
# Required - Your backend URL
|
|
48
|
+
BACKEND_URL=https://api.example.com/
|
|
49
|
+
# or
|
|
50
|
+
PUBLIC_BACKEND=https://api.example.com/
|
|
51
|
+
|
|
52
|
+
# Optional - Environment (defaults to PROD)
|
|
53
|
+
ENVIRONMENT=DEV
|
|
54
|
+
# or
|
|
55
|
+
VITE_ENVIRONMENT=DEV
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Create Reflector Config (Optional)
|
|
59
|
+
|
|
60
|
+
Create a `src/reflector.config.ts` to define custom validators:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
export const validators = [
|
|
64
|
+
{
|
|
65
|
+
fields: ["email", "userEmail"],
|
|
66
|
+
validator: "validateEmail",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
fields: ["phone", "mobile"],
|
|
70
|
+
validator: "validatePhone",
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Validators are resolved from `$lib/sanitizers/validateFormats` — you need to implement and export them in your project.
|
|
76
|
+
|
|
77
|
+
### 3. Configure API Import Path (Optional)
|
|
78
|
+
|
|
79
|
+
Create a `reflector.json` in your project root to customize the API import path:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"api": "$lib/api"
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Defaults to `$lib/api` if not specified. This is the module that generated modules will import for making HTTP requests.
|
|
88
|
+
|
|
89
|
+
### 4. Run the Generator
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Manual generation
|
|
93
|
+
npx reflect
|
|
94
|
+
|
|
95
|
+
# Or programmatically as a Vite plugin
|
|
96
|
+
import { reflector } from "svelte-reflector";
|
|
97
|
+
await reflector(true); // true = force generation
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 5. Use Generated Modules
|
|
101
|
+
|
|
102
|
+
Generated modules are **abstract classes**. Extend them to add custom logic or simply to instantiate:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { UserModule } from "$reflector/controllers/user/user.module.svelte";
|
|
106
|
+
import type { User } from "$reflector/controllers/user/user.schema.svelte";
|
|
107
|
+
|
|
108
|
+
// Extend the abstract module
|
|
109
|
+
class UserService extends UserModule {}
|
|
110
|
+
|
|
111
|
+
const userService = new UserService();
|
|
112
|
+
|
|
113
|
+
// Access reactive state
|
|
114
|
+
console.log(userService.loading); // $state<boolean>
|
|
115
|
+
console.log(userService.list); // $state<User[]>
|
|
116
|
+
|
|
117
|
+
// Call API methods
|
|
118
|
+
await userService.listAll({
|
|
119
|
+
behavior: {
|
|
120
|
+
onSuccess: (response) => console.log(response),
|
|
121
|
+
onError: (error) => console.error(error),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Work with forms
|
|
126
|
+
const userForm = userService.forms.createUser;
|
|
127
|
+
userForm.name.value = "John Doe";
|
|
128
|
+
userForm.email.value = "john@example.com";
|
|
129
|
+
|
|
130
|
+
// Submit form
|
|
131
|
+
await userService.createUser();
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Generated Structure
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
src/reflector/
|
|
138
|
+
├── controllers/
|
|
139
|
+
│ └── user/
|
|
140
|
+
│ ├── user.module.svelte.ts # Abstract API module with methods
|
|
141
|
+
│ └── user.schema.svelte.ts # Schemas & types used by this module
|
|
142
|
+
├── reflector.svelte.ts # Core utilities (build, isFormValid, QueryBuilder, etc.)
|
|
143
|
+
├── fields.ts # Field name constants
|
|
144
|
+
├── enums.ts # Enum type definitions
|
|
145
|
+
├── mocked-params.svelte.ts # Mocked path parameters ($state)
|
|
146
|
+
└── backup.json # Cached OpenAPI spec
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Each module gets its own schema file (`*.schema.svelte.ts`) containing only the schemas it uses, with transitive dependencies automatically resolved.
|
|
150
|
+
|
|
151
|
+
## Generated Module API
|
|
152
|
+
|
|
153
|
+
Each generated module is an **abstract class** that provides:
|
|
154
|
+
|
|
155
|
+
### State Properties
|
|
156
|
+
|
|
157
|
+
| Property | Type | Description |
|
|
158
|
+
|----------|------|-------------|
|
|
159
|
+
| `loading` | `$state<boolean>` | Request loading state |
|
|
160
|
+
| `list` | `$state<T[]>` | List results (for list endpoints) |
|
|
161
|
+
| `forms` | `$state<Record<string, T>>` | Form instances |
|
|
162
|
+
| `querys` | `Querys` | Query parameter state (QueryBuilder instances) |
|
|
163
|
+
| `headers` | `Headers` | Header state |
|
|
164
|
+
| `paths` | `Paths` | Path parameter state |
|
|
165
|
+
|
|
166
|
+
### Methods
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// List all items (GET with page parameter)
|
|
170
|
+
async listAll(params?: { behavior?: Behavior }): Promise<T[]>
|
|
171
|
+
|
|
172
|
+
// Get single entity (GET without page parameter)
|
|
173
|
+
async get(params?: { behavior?: Behavior }): Promise<T>
|
|
174
|
+
|
|
175
|
+
// Create/Update (POST/PUT/PATCH)
|
|
176
|
+
async create(params?: { behavior?: Behavior }): Promise<T>
|
|
177
|
+
async update(params?: { behavior?: Behavior }): Promise<T>
|
|
178
|
+
|
|
179
|
+
// Delete (DELETE)
|
|
180
|
+
async delete(params?: { behavior?: Behavior }): Promise<void>
|
|
181
|
+
|
|
182
|
+
// Reset all state (protected)
|
|
183
|
+
protected reset(): void
|
|
184
|
+
|
|
185
|
+
// Clear forms (protected)
|
|
186
|
+
protected clearForms(): void
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
> `reset()` and `clearForms()` are `protected` — override them in your subclass if you need custom reset behavior.
|
|
190
|
+
|
|
191
|
+
### QueryBuilder
|
|
192
|
+
|
|
193
|
+
Query parameters are wrapped in `QueryBuilder` instances that sync with URL searchParams:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Single value query parameter
|
|
197
|
+
const querys = module.querys;
|
|
198
|
+
querys.status.update("active"); // Updates URL searchParam and internal state
|
|
199
|
+
|
|
200
|
+
// Array enum query parameter
|
|
201
|
+
const enumQuery = module.querys.roles; // EnumQueryBuilder<RoleType>
|
|
202
|
+
enumQuery.selected = "admin";
|
|
203
|
+
enumQuery.add(); // Adds to URL searchParams
|
|
204
|
+
enumQuery.remove(0); // Removes from URL searchParams
|
|
205
|
+
enumQuery.values; // $derived from URL - always in sync
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Configuration
|
|
209
|
+
|
|
210
|
+
### Environment Variables
|
|
211
|
+
|
|
212
|
+
| Variable | Required | Description |
|
|
213
|
+
|----------|----------|-------------|
|
|
214
|
+
| `BACKEND_URL` | Yes* | Backend API URL |
|
|
215
|
+
| `PUBLIC_BACKEND` | Yes* | Alternative to BACKEND_URL |
|
|
216
|
+
| `ENVIRONMENT` | No | DEV/PROD (defaults to PROD) |
|
|
217
|
+
| `VITE_ENVIRONMENT` | No | Vite-specific env var |
|
|
218
|
+
| `NODE_ENV` | No | Node environment |
|
|
219
|
+
|
|
220
|
+
\* At least one of `BACKEND_URL` or `PUBLIC_BACKEND` is required.
|
|
221
|
+
|
|
222
|
+
### Behavior Pattern
|
|
223
|
+
|
|
224
|
+
All API methods accept a `Behavior` object for callbacks:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
class Behavior<TSuccess, TError> {
|
|
228
|
+
onSuccess?: (value: TSuccess) => Promise<void> | void;
|
|
229
|
+
onError?: (error: TError) => Promise<void> | void;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Usage
|
|
233
|
+
await userService.createUser({
|
|
234
|
+
behavior: {
|
|
235
|
+
onSuccess: (user) => console.log("Created:", user),
|
|
236
|
+
onError: (err) => console.error("Failed:", err),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Form Validation
|
|
242
|
+
|
|
243
|
+
Forms use `BuildedInput` class with validation:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
class BuildedInput<T> {
|
|
247
|
+
value: T; // Current value ($state)
|
|
248
|
+
display: T; // Display value ($state)
|
|
249
|
+
required: boolean; // Is field required
|
|
250
|
+
placeholder: T; // Placeholder/example value
|
|
251
|
+
readonly kind: 'builded';
|
|
252
|
+
validator?: (v: T) => string | null; // Validation function
|
|
253
|
+
validate(): string | null; // Run validation
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Check if all form fields are valid
|
|
257
|
+
import { isFormValid } from "$reflector/reflector.svelte";
|
|
258
|
+
|
|
259
|
+
if (isFormValid(userService.forms.createUser)) {
|
|
260
|
+
await userService.createUser();
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## TypeScript Configuration
|
|
265
|
+
|
|
266
|
+
Add path aliases to your `tsconfig.json`:
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"compilerOptions": {
|
|
271
|
+
"paths": {
|
|
272
|
+
"$reflector/*": ["./src/reflector/*"],
|
|
273
|
+
"$lib/*": ["./src/lib/*"]
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
For Vite projects, also update `vite.config.ts`:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
export default defineConfig({
|
|
283
|
+
resolve: {
|
|
284
|
+
alias: {
|
|
285
|
+
$reflector: path.resolve("./src/reflector"),
|
|
286
|
+
$lib: path.resolve("./src/lib"),
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Workflow
|
|
293
|
+
|
|
294
|
+
### Development Mode
|
|
295
|
+
|
|
296
|
+
In `ENVIRONMENT=DEV`:
|
|
297
|
+
- Schemas are **NOT** auto-regenerated on build
|
|
298
|
+
- Use `npx reflect` to manually regenerate
|
|
299
|
+
- Faster builds, manual control
|
|
300
|
+
|
|
301
|
+
### Production Mode
|
|
302
|
+
|
|
303
|
+
In `ENVIRONMENT=PROD`:
|
|
304
|
+
- Schemas are auto-regenerated on each build
|
|
305
|
+
- Fresh types from latest OpenAPI spec
|
|
306
|
+
- Fallback to `backup.json` if backend is unavailable
|
|
307
|
+
|
|
308
|
+
## Advanced Usage
|
|
309
|
+
|
|
310
|
+
### Extending Abstract Modules
|
|
311
|
+
|
|
312
|
+
Since modules are abstract, you can add custom logic:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { UserModule } from "$reflector/controllers/user/user.module.svelte";
|
|
316
|
+
|
|
317
|
+
class UserService extends UserModule {
|
|
318
|
+
// Add custom computed state
|
|
319
|
+
get activeUsers() {
|
|
320
|
+
return this.list.filter(u => u.active);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Override protected methods for custom behavior
|
|
324
|
+
protected override clearForms() {
|
|
325
|
+
super.clearForms();
|
|
326
|
+
// custom cleanup logic
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Add custom methods
|
|
330
|
+
async fetchAndFilter(status: string) {
|
|
331
|
+
this.querys.status.update(status);
|
|
332
|
+
await this.listAll();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Manual Schema Access
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import { User } from "$reflector/controllers/user/user.schema.svelte";
|
|
341
|
+
|
|
342
|
+
// Create instance
|
|
343
|
+
const user = new User({ name: "John", email: "john@example.com" });
|
|
344
|
+
|
|
345
|
+
// Get data bundle
|
|
346
|
+
const data = user.bundle(); // { name: "John", email: "john@example.com" }
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Batch Query Updates
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { setQueryGroup } from "$reflector/reflector.svelte";
|
|
353
|
+
|
|
354
|
+
// Update multiple query params at once
|
|
355
|
+
setQueryGroup([
|
|
356
|
+
{ key: "page", value: 1 },
|
|
357
|
+
{ key: "status", value: "active" },
|
|
358
|
+
{ key: "roles", value: ["admin", "editor"] }, // Array params supported
|
|
359
|
+
]);
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Troubleshooting
|
|
363
|
+
|
|
364
|
+
### "BACKEND_URL vazio" Error
|
|
365
|
+
|
|
366
|
+
Ensure you have set `BACKEND_URL` or `PUBLIC_BACKEND` in your `.env` file.
|
|
367
|
+
|
|
368
|
+
### Schemas Not Updating
|
|
369
|
+
|
|
370
|
+
In DEV mode, run `npx reflect` manually. Check that your backend's OpenAPI spec is accessible at `{BACKEND_URL}openapi.json`.
|
|
371
|
+
|
|
372
|
+
### Type Errors After Generation
|
|
373
|
+
|
|
374
|
+
1. Restart your TypeScript language server
|
|
375
|
+
2. Check path aliases in `tsconfig.json`
|
|
376
|
+
3. Ensure `$reflector/*` alias is configured
|
|
377
|
+
|
|
378
|
+
## License
|
|
379
|
+
|
|
380
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
381
|
+
|
|
382
|
+
## Contributing
|
|
383
|
+
|
|
384
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
385
|
+
|
|
386
|
+
## Links
|
|
387
|
+
|
|
388
|
+
- [npm](https://www.npmjs.com/package/svelte-reflector)
|
|
389
|
+
- [GitHub](https://github.com/aleleppy/reflector)
|
|
390
|
+
- [Svelte](https://svelte.dev/)
|
|
391
|
+
- [OpenAPI Specification](https://swagger.io/specification/)
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
Built with by the Pinaculo Digital team.
|