svelte-reflector 1.3.10 → 2.0.0
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 +455 -395
- package/dist/core/module/ModuleClassBuilder.js +0 -17
- package/dist/core/module/ModuleImports.d.ts +0 -1
- package/dist/core/module/ModuleImports.js +2 -9
- package/dist/props/array.property.d.ts +1 -1
- package/dist/props/array.property.js +20 -12
- package/dist/props/enum.property.d.ts +0 -1
- package/dist/props/enum.property.js +1 -4
- package/dist/props/primitive.property.d.ts +1 -2
- package/dist/props/primitive.property.js +12 -5
- package/dist/runtime/reflector.svelte.ts +24 -15
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,395 +1,455 @@
|
|
|
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`
|
|
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
|
-
|
|
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` / `EnumQueryBuilder` instances
|
|
194
|
+
that read directly from `page.url.searchParams`. There is no local cached
|
|
195
|
+
state — every read goes through the URL, so multiple instances with the same
|
|
196
|
+
key are always coherent.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// Single value query parameter
|
|
200
|
+
const querys = module.querys;
|
|
201
|
+
|
|
202
|
+
querys.status.value; // string | null — read-only getter, always
|
|
203
|
+
// reflects the current URL
|
|
204
|
+
querys.status.update("active"); // pushes ?status=active via goto()
|
|
205
|
+
|
|
206
|
+
// Array enum query parameter
|
|
207
|
+
const enumQuery = module.querys.roles; // EnumQueryBuilder<RoleType>
|
|
208
|
+
enumQuery.selected = "admin";
|
|
209
|
+
enumQuery.add(); // appends to URL searchParams
|
|
210
|
+
enumQuery.remove(0); // removes from URL searchParams
|
|
211
|
+
enumQuery.values; // $derived — always in sync with URL
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### Defaults
|
|
215
|
+
|
|
216
|
+
`default` declared in the OpenAPI schema is propagated to the builder
|
|
217
|
+
constructor automatically:
|
|
218
|
+
|
|
219
|
+
```yaml
|
|
220
|
+
parameters:
|
|
221
|
+
- name: limit
|
|
222
|
+
in: query
|
|
223
|
+
schema: { type: integer, default: 10 }
|
|
224
|
+
- name: tags
|
|
225
|
+
in: query
|
|
226
|
+
schema:
|
|
227
|
+
type: array
|
|
228
|
+
items: { type: string, enum: [hot, new, sale] }
|
|
229
|
+
default: [hot]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
generates:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
class Querys {
|
|
236
|
+
readonly limit = new QueryBuilder({ key: 'limit', defaultValue: 10 });
|
|
237
|
+
readonly tags = new EnumQueryBuilder<'hot' | 'new' | 'sale'>({
|
|
238
|
+
key: 'tags',
|
|
239
|
+
defaultValues: ['hot'],
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
When the URL has the param, the URL value wins. When the URL has no param,
|
|
245
|
+
the default is returned. The URL stays clean until the user interacts.
|
|
246
|
+
|
|
247
|
+
#### Migrating from 1.x
|
|
248
|
+
|
|
249
|
+
`value` is no longer a setter. Two replacements:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// 1.x
|
|
253
|
+
querys.page.value = "1";
|
|
254
|
+
querys.page.value ??= "1"; // seed default
|
|
255
|
+
|
|
256
|
+
// 2.x — declarative default (preferred, set at codegen via OpenAPI)
|
|
257
|
+
new QueryBuilder({ key: "page", defaultValue: "1" });
|
|
258
|
+
|
|
259
|
+
// 2.x — imperative update (push to URL)
|
|
260
|
+
querys.page.update("1");
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The auto-injected `setQueryGroup([...])` constructor on the generated
|
|
264
|
+
`Querys` class was removed — defaults now live on the builder. Import
|
|
265
|
+
`setQueryGroup` manually from `$reflector/reflector.svelte` if you still
|
|
266
|
+
need batch URL writes.
|
|
267
|
+
|
|
268
|
+
## Configuration
|
|
269
|
+
|
|
270
|
+
### Environment Variables
|
|
271
|
+
|
|
272
|
+
| Variable | Required | Description |
|
|
273
|
+
|----------|----------|-------------|
|
|
274
|
+
| `BACKEND_URL` | Yes* | Backend API URL |
|
|
275
|
+
| `PUBLIC_BACKEND` | Yes* | Alternative to BACKEND_URL |
|
|
276
|
+
| `ENVIRONMENT` | No | DEV/PROD (defaults to PROD) |
|
|
277
|
+
| `VITE_ENVIRONMENT` | No | Vite-specific env var |
|
|
278
|
+
| `NODE_ENV` | No | Node environment |
|
|
279
|
+
|
|
280
|
+
\* At least one of `BACKEND_URL` or `PUBLIC_BACKEND` is required.
|
|
281
|
+
|
|
282
|
+
### Behavior Pattern
|
|
283
|
+
|
|
284
|
+
All API methods accept a `Behavior` object for callbacks:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
class Behavior<TSuccess, TError> {
|
|
288
|
+
onSuccess?: (value: TSuccess) => Promise<void> | void;
|
|
289
|
+
onError?: (error: TError) => Promise<void> | void;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Usage
|
|
293
|
+
await userService.createUser({
|
|
294
|
+
behavior: {
|
|
295
|
+
onSuccess: (user) => console.log("Created:", user),
|
|
296
|
+
onError: (err) => console.error("Failed:", err),
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Form Validation
|
|
302
|
+
|
|
303
|
+
Forms use `BuildedInput` class with validation:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
class BuildedInput<T> {
|
|
307
|
+
value: T; // Current value ($state)
|
|
308
|
+
display: T; // Display value ($state)
|
|
309
|
+
required: boolean; // Is field required
|
|
310
|
+
placeholder: T; // Placeholder/example value
|
|
311
|
+
readonly kind: 'builded';
|
|
312
|
+
validator?: (v: T) => string | null; // Validation function
|
|
313
|
+
validate(): string | null; // Run validation
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Check if all form fields are valid
|
|
317
|
+
import { isFormValid } from "$reflector/reflector.svelte";
|
|
318
|
+
|
|
319
|
+
if (isFormValid(userService.forms.createUser)) {
|
|
320
|
+
await userService.createUser();
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## TypeScript Configuration
|
|
325
|
+
|
|
326
|
+
Add path aliases to your `tsconfig.json`:
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"compilerOptions": {
|
|
331
|
+
"paths": {
|
|
332
|
+
"$reflector/*": ["./src/reflector/*"],
|
|
333
|
+
"$lib/*": ["./src/lib/*"]
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
For Vite projects, also update `vite.config.ts`:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
export default defineConfig({
|
|
343
|
+
resolve: {
|
|
344
|
+
alias: {
|
|
345
|
+
$reflector: path.resolve("./src/reflector"),
|
|
346
|
+
$lib: path.resolve("./src/lib"),
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Workflow
|
|
353
|
+
|
|
354
|
+
### Development Mode
|
|
355
|
+
|
|
356
|
+
In `ENVIRONMENT=DEV`:
|
|
357
|
+
- Schemas are **NOT** auto-regenerated on build
|
|
358
|
+
- Use `npx reflect` to manually regenerate
|
|
359
|
+
- Faster builds, manual control
|
|
360
|
+
|
|
361
|
+
### Production Mode
|
|
362
|
+
|
|
363
|
+
In `ENVIRONMENT=PROD`:
|
|
364
|
+
- Schemas are auto-regenerated on each build
|
|
365
|
+
- Fresh types from latest OpenAPI spec
|
|
366
|
+
- Fallback to `backup.json` if backend is unavailable
|
|
367
|
+
|
|
368
|
+
## Advanced Usage
|
|
369
|
+
|
|
370
|
+
### Extending Abstract Modules
|
|
371
|
+
|
|
372
|
+
Since modules are abstract, you can add custom logic:
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { UserModule } from "$reflector/controllers/user/user.module.svelte";
|
|
376
|
+
|
|
377
|
+
class UserService extends UserModule {
|
|
378
|
+
// Add custom computed state
|
|
379
|
+
get activeUsers() {
|
|
380
|
+
return this.list.filter(u => u.active);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Override protected methods for custom behavior
|
|
384
|
+
protected override clearForms() {
|
|
385
|
+
super.clearForms();
|
|
386
|
+
// custom cleanup logic
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Add custom methods
|
|
390
|
+
async fetchAndFilter(status: string) {
|
|
391
|
+
this.querys.status.update(status);
|
|
392
|
+
await this.listAll();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Manual Schema Access
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
import { User } from "$reflector/controllers/user/user.schema.svelte";
|
|
401
|
+
|
|
402
|
+
// Create instance
|
|
403
|
+
const user = new User({ name: "John", email: "john@example.com" });
|
|
404
|
+
|
|
405
|
+
// Get data bundle
|
|
406
|
+
const data = user.bundle(); // { name: "John", email: "john@example.com" }
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Batch Query Updates
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
import { setQueryGroup } from "$reflector/reflector.svelte";
|
|
413
|
+
|
|
414
|
+
// Update multiple query params at once
|
|
415
|
+
setQueryGroup([
|
|
416
|
+
{ key: "page", value: 1 },
|
|
417
|
+
{ key: "status", value: "active" },
|
|
418
|
+
{ key: "roles", value: ["admin", "editor"] }, // Array params supported
|
|
419
|
+
]);
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Troubleshooting
|
|
423
|
+
|
|
424
|
+
### "BACKEND_URL vazio" Error
|
|
425
|
+
|
|
426
|
+
Ensure you have set `BACKEND_URL` or `PUBLIC_BACKEND` in your `.env` file.
|
|
427
|
+
|
|
428
|
+
### Schemas Not Updating
|
|
429
|
+
|
|
430
|
+
In DEV mode, run `npx reflect` manually. Check that your backend's OpenAPI spec is accessible at `{BACKEND_URL}openapi.json`.
|
|
431
|
+
|
|
432
|
+
### Type Errors After Generation
|
|
433
|
+
|
|
434
|
+
1. Restart your TypeScript language server
|
|
435
|
+
2. Check path aliases in `tsconfig.json`
|
|
436
|
+
3. Ensure `$reflector/*` alias is configured
|
|
437
|
+
|
|
438
|
+
## License
|
|
439
|
+
|
|
440
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
441
|
+
|
|
442
|
+
## Contributing
|
|
443
|
+
|
|
444
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
445
|
+
|
|
446
|
+
## Links
|
|
447
|
+
|
|
448
|
+
- [npm](https://www.npmjs.com/package/svelte-reflector)
|
|
449
|
+
- [GitHub](https://github.com/aleleppy/reflector)
|
|
450
|
+
- [Svelte](https://svelte.dev/)
|
|
451
|
+
- [OpenAPI Specification](https://swagger.io/specification/)
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
Built with by the Pinaculo Digital team.
|
|
@@ -26,8 +26,6 @@ export class ModuleClassBuilder {
|
|
|
26
26
|
`;
|
|
27
27
|
}
|
|
28
28
|
else if (name === "Querys") {
|
|
29
|
-
this.imports.addSetQueryGroupImport();
|
|
30
|
-
const queryGroupValues = [];
|
|
31
29
|
props.forEach((prop) => {
|
|
32
30
|
if ("rawType" in prop) {
|
|
33
31
|
attributes.push(prop.queryBuild());
|
|
@@ -35,13 +33,9 @@ export class ModuleClassBuilder {
|
|
|
35
33
|
this.imports.addReflectorImport("EnumQueryBuilder");
|
|
36
34
|
this.imports.addEnumImport(String(prop.type));
|
|
37
35
|
bundle.push(`${prop.name}: this.${prop.name}?.values`);
|
|
38
|
-
// Array de enum usa valor padrão []
|
|
39
|
-
queryGroupValues.push(`{ key: '${prop.name}', value: ${prop.queryDefaultValue()} }`);
|
|
40
36
|
}
|
|
41
37
|
else {
|
|
42
38
|
bundle.push(prop.bundleBuild());
|
|
43
|
-
// PrimitiveProp usa seu valor padrão
|
|
44
|
-
queryGroupValues.push(`{ key: '${prop.name}', value: ${prop.queryDefaultValue()} }`);
|
|
45
39
|
}
|
|
46
40
|
}
|
|
47
41
|
else if ("enumName" in prop) {
|
|
@@ -49,7 +43,6 @@ export class ModuleClassBuilder {
|
|
|
49
43
|
this.imports.addEnumImport(prop.enumName);
|
|
50
44
|
attributes.push(prop.queryBuild());
|
|
51
45
|
bundle.push(prop.bundleBuild());
|
|
52
|
-
queryGroupValues.push(`{ key: '${prop.name}', value: ${prop.queryDefaultValue()} }`);
|
|
53
46
|
}
|
|
54
47
|
else {
|
|
55
48
|
// ArrayProp (não-enum)
|
|
@@ -57,16 +50,8 @@ export class ModuleClassBuilder {
|
|
|
57
50
|
this.imports.addEnumImport(prop.type);
|
|
58
51
|
bundle.push(prop.queryBundleBuild());
|
|
59
52
|
this.imports.addReflectorImport("EnumQueryBuilder");
|
|
60
|
-
queryGroupValues.push(`{ key: '${prop.name}', value: ${prop.queryDefaultValue()} }`);
|
|
61
53
|
}
|
|
62
54
|
});
|
|
63
|
-
const constructorBuild = `
|
|
64
|
-
constructor() {
|
|
65
|
-
setQueryGroup([
|
|
66
|
-
${queryGroupValues.join(",\n ")}
|
|
67
|
-
]);
|
|
68
|
-
}
|
|
69
|
-
`;
|
|
70
55
|
if (bundle.length > 0) {
|
|
71
56
|
this.imports.addReflectorImport("bundleStrict");
|
|
72
57
|
}
|
|
@@ -74,8 +59,6 @@ export class ModuleClassBuilder {
|
|
|
74
59
|
class ${outputName} {
|
|
75
60
|
${attributes.join(";")}
|
|
76
61
|
|
|
77
|
-
${constructorBuild}
|
|
78
|
-
|
|
79
62
|
${bundle.length > 0 ? `
|
|
80
63
|
bundle() {
|
|
81
64
|
return bundleStrict({
|
|
@@ -9,7 +9,6 @@ export declare class ModuleImports {
|
|
|
9
9
|
addImport(importStr: string): void;
|
|
10
10
|
addMockedImport(importStr: string): void;
|
|
11
11
|
addReflectorImport(importStr: string): void;
|
|
12
|
-
addSetQueryGroupImport(): void;
|
|
13
12
|
addEnumImport(enumName: string): void;
|
|
14
13
|
addPageStateImport(): void;
|
|
15
14
|
getImportsArray(): string[];
|
|
@@ -25,9 +25,6 @@ export class ModuleImports {
|
|
|
25
25
|
addReflectorImport(importStr) {
|
|
26
26
|
this.reflectorImports.add(importStr);
|
|
27
27
|
}
|
|
28
|
-
addSetQueryGroupImport() {
|
|
29
|
-
this.reflectorImports.add("setQueryGroup");
|
|
30
|
-
}
|
|
31
28
|
addEnumImport(enumName) {
|
|
32
29
|
this.enumImports.add(enumName);
|
|
33
30
|
}
|
|
@@ -55,16 +52,12 @@ export class ModuleImports {
|
|
|
55
52
|
typeOnlyImports = new Set(["ApiCallParams"]);
|
|
56
53
|
buildReflectorImportsLine() {
|
|
57
54
|
const imports = this.getReflectorImportsArray();
|
|
58
|
-
const regularImports = imports.filter(i =>
|
|
59
|
-
const typeImports = imports.filter(i => this.typeOnlyImports.has(i));
|
|
60
|
-
const hasSetQueryGroup = imports.includes("setQueryGroup");
|
|
55
|
+
const regularImports = imports.filter((i) => !this.typeOnlyImports.has(i));
|
|
56
|
+
const typeImports = imports.filter((i) => this.typeOnlyImports.has(i));
|
|
61
57
|
let result = `import { ${regularImports.join(", ")}, type ApiErrorResponse`;
|
|
62
58
|
for (const t of typeImports) {
|
|
63
59
|
result += `, type ${t}`;
|
|
64
60
|
}
|
|
65
|
-
if (hasSetQueryGroup) {
|
|
66
|
-
result += `, setQueryGroup`;
|
|
67
|
-
}
|
|
68
61
|
result += ` } from "${this.config.reflectorAlias}/reflector.svelte";`;
|
|
69
62
|
return result;
|
|
70
63
|
}
|
|
@@ -8,6 +8,7 @@ export declare class ArrayProp {
|
|
|
8
8
|
private _isPrimitiveType;
|
|
9
9
|
private readonly isNullable;
|
|
10
10
|
private readonly context;
|
|
11
|
+
private readonly defaultValues;
|
|
11
12
|
get isSchemaRef(): boolean;
|
|
12
13
|
readonly isEnum: boolean;
|
|
13
14
|
constructor(params: {
|
|
@@ -28,5 +29,4 @@ export declare class ArrayProp {
|
|
|
28
29
|
queryBundleBuild(): string;
|
|
29
30
|
queryBuild(): string;
|
|
30
31
|
staticBuild(): string;
|
|
31
|
-
queryDefaultValue(): string;
|
|
32
32
|
}
|
|
@@ -9,6 +9,7 @@ export class ArrayProp {
|
|
|
9
9
|
_isPrimitiveType = false;
|
|
10
10
|
isNullable;
|
|
11
11
|
context;
|
|
12
|
+
defaultValues;
|
|
12
13
|
get isSchemaRef() {
|
|
13
14
|
return !this._isPrimitiveType && !this.isEnum;
|
|
14
15
|
}
|
|
@@ -22,6 +23,7 @@ export class ArrayProp {
|
|
|
22
23
|
this.type = this.getType({ schemaObject, schemaName });
|
|
23
24
|
this.isRequired = required;
|
|
24
25
|
this.isParam = !!isParam;
|
|
26
|
+
this.defaultValues = Array.isArray(schemaObject.default) ? schemaObject.default : [];
|
|
25
27
|
}
|
|
26
28
|
getType(params) {
|
|
27
29
|
const { schemaObject, schemaName } = params;
|
|
@@ -66,22 +68,31 @@ export class ArrayProp {
|
|
|
66
68
|
return `${this.name}${required}: ${sanitizedType}[]${nullable}`;
|
|
67
69
|
}
|
|
68
70
|
bundleBuild() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
if (this._isPrimitiveType || this.isEnum) {
|
|
72
|
+
return `${this.name}: this.${this.name}`;
|
|
73
|
+
}
|
|
74
|
+
if (this.isNullable) {
|
|
75
|
+
return `${this.name}: this.${this.name} == null ? this.${this.name} : this.${this.name}.map((obj) => obj.bundle())`;
|
|
76
|
+
}
|
|
77
|
+
if (!this.isRequired) {
|
|
78
|
+
return `${this.name}: this.${this.name}?.map((obj) => obj.bundle())`;
|
|
79
|
+
}
|
|
80
|
+
return `${this.name}: this.${this.name}.map((obj) => obj.bundle())`;
|
|
75
81
|
}
|
|
76
82
|
queryBundleBuild() {
|
|
77
83
|
return `${this.name}: this.${this.name}?.values`;
|
|
78
84
|
}
|
|
79
85
|
queryBuild() {
|
|
80
86
|
if (this.isEnum) {
|
|
81
|
-
|
|
87
|
+
if (this.defaultValues.length === 0) {
|
|
88
|
+
return `readonly ${this.name} = new EnumQueryBuilder<${this.type}>({ key: '${this.name}' })`;
|
|
89
|
+
}
|
|
90
|
+
const literal = `[${this.defaultValues
|
|
91
|
+
.map((v) => (typeof v === "string" ? `'${v}'` : String(v)))
|
|
92
|
+
.join(", ")}]`;
|
|
93
|
+
return `readonly ${this.name} = new EnumQueryBuilder<${this.type}>({ key: '${this.name}', defaultValues: ${literal} })`;
|
|
82
94
|
}
|
|
83
|
-
|
|
84
|
-
return `readonly ${this.name} = $derived(new QueryBuilder({ key: '${this.name}' }))`;
|
|
95
|
+
return `readonly ${this.name} = new QueryBuilder({ key: '${this.name}' })`;
|
|
85
96
|
}
|
|
86
97
|
staticBuild() {
|
|
87
98
|
const result = this._isPrimitiveType ? "obj" : `new ${this.type}({ data: obj })`;
|
|
@@ -92,7 +103,4 @@ export class ArrayProp {
|
|
|
92
103
|
}
|
|
93
104
|
`;
|
|
94
105
|
}
|
|
95
|
-
queryDefaultValue() {
|
|
96
|
-
return "[]";
|
|
97
|
-
}
|
|
98
106
|
}
|
|
@@ -34,12 +34,9 @@ export class EnumProp {
|
|
|
34
34
|
return `${this.name}${req}: ${this.enumName}`;
|
|
35
35
|
}
|
|
36
36
|
queryBuild() {
|
|
37
|
-
return `readonly ${this.name} =
|
|
37
|
+
return `readonly ${this.name} = new QueryBuilder({ key: '${this.name}' })`;
|
|
38
38
|
}
|
|
39
39
|
bundleBuild() {
|
|
40
40
|
return `${this.name}: this.${this.name}?.value`;
|
|
41
41
|
}
|
|
42
|
-
queryDefaultValue() {
|
|
43
|
-
return `'${this.example}'`;
|
|
44
|
-
}
|
|
45
42
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { SchemaObject } from "../types/open-api-spec.interface.js";
|
|
2
2
|
import type { ReflectorParamType } from "../types/types.js";
|
|
3
3
|
type AbstractType = string | boolean | number | undefined;
|
|
4
|
-
type Example = string | boolean | number;
|
|
5
4
|
export declare class PrimitiveProp {
|
|
6
5
|
name: string;
|
|
7
6
|
type: AbstractType;
|
|
@@ -14,6 +13,7 @@ export declare class PrimitiveProp {
|
|
|
14
13
|
private readonly buildedConst;
|
|
15
14
|
private readonly example;
|
|
16
15
|
private readonly fallbackExample;
|
|
16
|
+
private readonly defaultValue;
|
|
17
17
|
private get effectiveType();
|
|
18
18
|
/** Non-required fields become nullable (| null) instead of optional (?) */
|
|
19
19
|
private get isEffectivelyNullable();
|
|
@@ -37,6 +37,5 @@ export declare class PrimitiveProp {
|
|
|
37
37
|
queryBuild(): string;
|
|
38
38
|
updateQueryBuild(): string;
|
|
39
39
|
bundleBuild(): string;
|
|
40
|
-
queryDefaultValue(): Example;
|
|
41
40
|
}
|
|
42
41
|
export {};
|
|
@@ -12,6 +12,7 @@ export class PrimitiveProp {
|
|
|
12
12
|
buildedConst;
|
|
13
13
|
example;
|
|
14
14
|
fallbackExample;
|
|
15
|
+
defaultValue;
|
|
15
16
|
get effectiveType() {
|
|
16
17
|
return this.customType ?? this.rawType;
|
|
17
18
|
}
|
|
@@ -28,6 +29,7 @@ export class PrimitiveProp {
|
|
|
28
29
|
const { emptyExample, example } = this.getExampleAndFallback({ schemaObject, type, name });
|
|
29
30
|
this.example = example;
|
|
30
31
|
this.fallbackExample = emptyExample;
|
|
32
|
+
this.defaultValue = schemaObject.default;
|
|
31
33
|
const buildedType = customType ?? type;
|
|
32
34
|
const treated = treatPropertyName(name);
|
|
33
35
|
this.name = treated.name;
|
|
@@ -95,7 +97,9 @@ export class PrimitiveProp {
|
|
|
95
97
|
};
|
|
96
98
|
const buildedExample = this.customType
|
|
97
99
|
? `${this.example}`
|
|
98
|
-
:
|
|
100
|
+
: this.rawType === "boolean" && this.fallbackExample === false && this.example === true
|
|
101
|
+
? `!(params?.empty || isEmpty)`
|
|
102
|
+
: `params?.empty || isEmpty ? ${this.fallbackExample} : ${this.example}`;
|
|
99
103
|
const effectivelyNullable = this.isEffectivelyNullable;
|
|
100
104
|
const keyExpr = effectivelyNullable ? `params?.data?.${name} ?? null` : `params?.data?.${name}`;
|
|
101
105
|
let typeParam = "";
|
|
@@ -130,7 +134,13 @@ export class PrimitiveProp {
|
|
|
130
134
|
return `readonly ${this.name} = $derived.by(() => '${this.name}' in page.params ? page.params.${this.name} : mockedParams.${this.name}) as string | null;`;
|
|
131
135
|
}
|
|
132
136
|
queryBuild() {
|
|
133
|
-
|
|
137
|
+
if (this.defaultValue === undefined || this.defaultValue === null) {
|
|
138
|
+
return `readonly ${this.name} = new QueryBuilder({ key: '${this.name}' })`;
|
|
139
|
+
}
|
|
140
|
+
const literal = typeof this.defaultValue === "string"
|
|
141
|
+
? `'${this.defaultValue}'`
|
|
142
|
+
: String(this.defaultValue);
|
|
143
|
+
return `readonly ${this.name} = new QueryBuilder({ key: '${this.name}', defaultValue: ${literal} })`;
|
|
134
144
|
}
|
|
135
145
|
updateQueryBuild() {
|
|
136
146
|
return `${this.name}: (event: SvelteEvent) => changeParam({ key: '${this.name}', event })`;
|
|
@@ -138,7 +148,4 @@ export class PrimitiveProp {
|
|
|
138
148
|
bundleBuild() {
|
|
139
149
|
return `${this.name}: ${this.thisDot()}${this.name}?.value`;
|
|
140
150
|
}
|
|
141
|
-
queryDefaultValue() {
|
|
142
|
-
return this.fallbackExample;
|
|
143
|
-
}
|
|
144
151
|
}
|
|
@@ -79,13 +79,18 @@ export class BuildedInput<T> {
|
|
|
79
79
|
|
|
80
80
|
export class EnumQueryBuilder<T> {
|
|
81
81
|
readonly key: string = "";
|
|
82
|
-
|
|
83
|
-
selected = $state<T | null>(null);
|
|
82
|
+
private readonly defaultValues: T[] = [];
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
values = $derived(
|
|
85
|
+
page.url.searchParams.has(this.key)
|
|
86
|
+
? (page.url.searchParams.getAll(this.key) as T[])
|
|
87
|
+
: this.defaultValues,
|
|
88
|
+
);
|
|
89
|
+
selected = $state<T | null>(null);
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
constructor(params: { key: string; defaultValues?: T[] }) {
|
|
92
|
+
this.key = params.key;
|
|
93
|
+
this.defaultValues = params.defaultValues ?? [];
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
add = () => {
|
|
@@ -167,22 +172,26 @@ type QueryWithArrayType = {
|
|
|
167
172
|
};
|
|
168
173
|
|
|
169
174
|
export class QueryBuilder {
|
|
170
|
-
readonly key: string
|
|
171
|
-
value = $state<string | null>(null);
|
|
175
|
+
readonly key: string;
|
|
172
176
|
readonly kind = "query";
|
|
177
|
+
private readonly defaultValue: string | null;
|
|
178
|
+
|
|
179
|
+
constructor(params: { key: string; defaultValue?: string | number | null }) {
|
|
180
|
+
this.key = params.key;
|
|
181
|
+
this.defaultValue =
|
|
182
|
+
params.defaultValue === undefined || params.defaultValue === null
|
|
183
|
+
? null
|
|
184
|
+
: String(params.defaultValue);
|
|
185
|
+
}
|
|
173
186
|
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
this.
|
|
177
|
-
|
|
178
|
-
const urlValue = page.url.searchParams.get(key);
|
|
179
|
-
this.value = urlValue !== null ? urlValue : null;
|
|
187
|
+
get value(): string | null {
|
|
188
|
+
const fromUrl = page.url.searchParams.get(this.key);
|
|
189
|
+
return fromUrl !== null ? fromUrl : this.defaultValue;
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
update(event: string | number | null) {
|
|
183
193
|
if (event === null || event === undefined) return;
|
|
184
|
-
this.
|
|
185
|
-
return changeParam({ key: this.key, event: this.value });
|
|
194
|
+
return changeParam({ key: this.key, event: String(event) });
|
|
186
195
|
}
|
|
187
196
|
}
|
|
188
197
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-reflector",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Reflects zod types from openAPI schemas",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"build": "tsc && tsc -p tsconfig.runtime.json && node scripts/copy-runtime.mjs",
|
|
22
22
|
"typecheck:runtime": "tsc -p tsconfig.runtime.json",
|
|
23
23
|
"test": "vitest run",
|
|
24
|
-
"test:watch": "vitest"
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"patch": "npm run build && npm version patch && npm publish"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"axios": "^1.12.2",
|