schemantic 0.3.0 → 0.4.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/CHANGELOG.md +196 -174
- package/LICENSE +21 -21
- package/README.md +88 -830
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +185 -61
- package/dist/cli/index.js.map +1 -1
- package/dist/core/configuration.d.ts +6 -0
- package/dist/core/configuration.d.ts.map +1 -0
- package/dist/core/configuration.js +64 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/output-writer.d.ts +7 -0
- package/dist/core/output-writer.d.ts.map +1 -0
- package/dist/core/output-writer.js +255 -0
- package/dist/core/output-writer.js.map +1 -0
- package/dist/core/schemantic.d.ts +8 -2
- package/dist/core/schemantic.d.ts.map +1 -1
- package/dist/core/schemantic.js +196 -71
- package/dist/core/schemantic.js.map +1 -1
- package/dist/generators/api-client-generator.d.ts +9 -20
- package/dist/generators/api-client-generator.d.ts.map +1 -1
- package/dist/generators/api-client-generator.js +364 -424
- package/dist/generators/api-client-generator.js.map +1 -1
- package/dist/generators/base.d.ts.map +1 -1
- package/dist/generators/base.js +7 -25
- package/dist/generators/base.js.map +1 -1
- package/dist/generators/enum-generator.d.ts.map +1 -1
- package/dist/generators/enum-generator.js +30 -3
- package/dist/generators/enum-generator.js.map +1 -1
- package/dist/generators/hook-generator.d.ts +3 -17
- package/dist/generators/hook-generator.d.ts.map +1 -1
- package/dist/generators/hook-generator.js +51 -445
- package/dist/generators/hook-generator.js.map +1 -1
- package/dist/generators/naming.d.ts +10 -0
- package/dist/generators/naming.d.ts.map +1 -0
- package/dist/generators/naming.js +331 -0
- package/dist/generators/naming.js.map +1 -0
- package/dist/generators/object-generator.d.ts +0 -4
- package/dist/generators/object-generator.d.ts.map +1 -1
- package/dist/generators/object-generator.js +13 -101
- package/dist/generators/object-generator.js.map +1 -1
- package/dist/generators/primitive-generator.d.ts +1 -13
- package/dist/generators/primitive-generator.d.ts.map +1 -1
- package/dist/generators/primitive-generator.js +11 -76
- package/dist/generators/primitive-generator.js.map +1 -1
- package/dist/generators/projections.d.ts +10 -0
- package/dist/generators/projections.d.ts.map +1 -0
- package/dist/generators/projections.js +65 -0
- package/dist/generators/projections.js.map +1 -0
- package/dist/generators/schema-type.d.ts +23 -0
- package/dist/generators/schema-type.d.ts.map +1 -0
- package/dist/generators/schema-type.js +233 -0
- package/dist/generators/schema-type.js.map +1 -0
- package/dist/parsers/base.d.ts +2 -0
- package/dist/parsers/base.d.ts.map +1 -1
- package/dist/parsers/base.js +12 -2
- package/dist/parsers/base.js.map +1 -1
- package/dist/parsers/factory.d.ts.map +1 -1
- package/dist/parsers/factory.js +2 -1
- package/dist/parsers/factory.js.map +1 -1
- package/dist/parsers/openapi.d.ts +4 -0
- package/dist/parsers/openapi.d.ts.map +1 -1
- package/dist/parsers/openapi.js +107 -20
- package/dist/parsers/openapi.js.map +1 -1
- package/dist/plugins/branded-types.d.ts.map +1 -1
- package/dist/plugins/branded-types.js +231 -365
- package/dist/plugins/branded-types.js.map +1 -1
- package/dist/plugins/builtin.d.ts.map +1 -1
- package/dist/plugins/builtin.js +6 -141
- package/dist/plugins/builtin.js.map +1 -1
- package/dist/plugins/index.d.ts +4 -0
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +17 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/loader.d.ts +5 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +42 -5
- package/dist/plugins/loader.js.map +1 -1
- package/dist/plugins/manager.d.ts.map +1 -1
- package/dist/plugins/manager.js +7 -7
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/performance-monitoring.d.ts +31 -1
- package/dist/plugins/performance-monitoring.d.ts.map +1 -1
- package/dist/plugins/performance-monitoring.js +146 -207
- package/dist/plugins/performance-monitoring.js.map +1 -1
- package/dist/plugins/request-deduplication.d.ts.map +1 -1
- package/dist/plugins/request-deduplication.js +151 -586
- package/dist/plugins/request-deduplication.js.map +1 -1
- package/dist/plugins/zod-validation.d.ts +23 -15
- package/dist/plugins/zod-validation.d.ts.map +1 -1
- package/dist/plugins/zod-validation.js +390 -268
- package/dist/plugins/zod-validation.js.map +1 -1
- package/dist/types/core.d.ts +20 -3
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +2 -0
- package/dist/types/core.js.map +1 -1
- package/dist/types/openapi.d.ts +14 -1
- package/dist/types/openapi.d.ts.map +1 -1
- package/docs/ADVANCED_PLUGINS_IMPLEMENTATION.md +24 -355
- package/docs/ADVANCED_PLUGIN_DEVELOPMENT.md +33 -309
- package/docs/API.md +80 -601
- package/docs/AUDIT.md +49 -0
- package/docs/CONFIGURATION.md +81 -715
- package/docs/FASTAPI_TUTORIAL.md +109 -1497
- package/docs/NAMING_MIGRATION.md +59 -0
- package/docs/PLUGINS.md +49 -687
- package/docs/PLUGIN_DEVELOPMENT.md +53 -720
- package/docs/REAUDIT.md +399 -0
- package/docs/RELEASING.md +88 -0
- package/docs/REMEDIATION.md +58 -0
- package/docs/superpowers/plans/2026-10-10-audit-remediation.md +113 -0
- package/package.json +114 -95
- package/dist/__tests__/setup.d.ts +0 -6
- package/dist/__tests__/setup.d.ts.map +0 -1
- package/dist/__tests__/setup.js +0 -30
- package/dist/__tests__/setup.js.map +0 -1
- package/dist/__tests__/test-config.d.ts +0 -36
- package/dist/__tests__/test-config.d.ts.map +0 -1
- package/dist/__tests__/test-config.js +0 -562
- package/dist/__tests__/test-config.js.map +0 -1
package/docs/PLUGINS.md
CHANGED
|
@@ -1,687 +1,49 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Schema transformation hooks
|
|
52
|
-
transformSchema?(
|
|
53
|
-
schema: ResolvedSchema,
|
|
54
|
-
context: GenerationContext
|
|
55
|
-
): ResolvedSchema;
|
|
56
|
-
|
|
57
|
-
// Custom generators
|
|
58
|
-
customTypeGenerators?: Record<
|
|
59
|
-
string,
|
|
60
|
-
(schema: ResolvedSchema, context: GenerationContext) => GeneratedType
|
|
61
|
-
>;
|
|
62
|
-
customClientGenerators?: Record<
|
|
63
|
-
string,
|
|
64
|
-
(context: GenerationContext) => GeneratedApiClient
|
|
65
|
-
>;
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Basic Plugin Structure
|
|
70
|
-
|
|
71
|
-
Here's a minimal plugin:
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import { SchemanticPlugin, GenerationContext, GeneratedType } from "schemantic";
|
|
75
|
-
|
|
76
|
-
const myPlugin: SchemanticPlugin = {
|
|
77
|
-
name: "my-plugin",
|
|
78
|
-
version: "1.0.0",
|
|
79
|
-
description: "My custom plugin",
|
|
80
|
-
|
|
81
|
-
beforeGeneration: async (context: GenerationContext) => {
|
|
82
|
-
console.log("Starting generation...");
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
afterGeneration: async (context: GenerationContext, result: any) => {
|
|
86
|
-
console.log("Generation completed!");
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Plugin Hooks
|
|
92
|
-
|
|
93
|
-
### Lifecycle Hooks
|
|
94
|
-
|
|
95
|
-
#### `beforeGeneration`
|
|
96
|
-
|
|
97
|
-
Called before the generation process starts. Use this to:
|
|
98
|
-
|
|
99
|
-
- Validate configuration
|
|
100
|
-
- Set up plugin-specific state
|
|
101
|
-
- Modify the generation context
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
beforeGeneration: async (context: GenerationContext) => {
|
|
105
|
-
// Validate configuration
|
|
106
|
-
if (!context.config.customOption) {
|
|
107
|
-
throw new Error('Custom option is required');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Set up plugin state
|
|
111
|
-
context.pluginState = { myData: 'value' };
|
|
112
|
-
},
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
#### `afterGeneration`
|
|
116
|
-
|
|
117
|
-
Called after the generation process completes. Use this to:
|
|
118
|
-
|
|
119
|
-
- Clean up resources
|
|
120
|
-
- Generate additional files
|
|
121
|
-
- Post-process results
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
afterGeneration: async (context: GenerationContext, result: any) => {
|
|
125
|
-
// Generate additional files
|
|
126
|
-
if (result.success) {
|
|
127
|
-
await generateAdditionalFiles(context);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Clean up
|
|
131
|
-
delete context.pluginState;
|
|
132
|
-
},
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
Note: The `react-hooks` built-in plugin is temporarily disabled in the default plugin lists due to known generation issues that can corrupt output for some schemas. To re-enable it for a specific project, add `{ name: 'react-hooks', enabled: true }` to your `plugins` array in `schemantic.config.*`, and run the generator on a small sample schema first to validate output.
|
|
138
|
-
|
|
139
|
-
### Type Generation Hooks
|
|
140
|
-
|
|
141
|
-
#### `beforeTypeGeneration`
|
|
142
|
-
|
|
143
|
-
Called before generating a specific type. Use this to:
|
|
144
|
-
|
|
145
|
-
- Transform the schema
|
|
146
|
-
- Add metadata
|
|
147
|
-
- Skip generation for certain types
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
beforeTypeGeneration: async (typeName: string, schema: ResolvedSchema, context: GenerationContext) => {
|
|
151
|
-
// Add custom metadata
|
|
152
|
-
schema._customMetadata = { processed: true };
|
|
153
|
-
|
|
154
|
-
// Skip certain types
|
|
155
|
-
if (typeName.includes('Internal')) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
#### `afterTypeGeneration`
|
|
162
|
-
|
|
163
|
-
Called after generating a specific type. Use this to:
|
|
164
|
-
|
|
165
|
-
- Modify the generated type content
|
|
166
|
-
- Add imports
|
|
167
|
-
- Post-process the type
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
afterTypeGeneration: async (typeName: string, generatedType: GeneratedType, context: GenerationContext) => {
|
|
171
|
-
// Add custom imports
|
|
172
|
-
generatedType.content = `import { CustomType } from './custom';\n\n${generatedType.content}`;
|
|
173
|
-
|
|
174
|
-
// Add custom decorators
|
|
175
|
-
if (generatedType.isInterface) {
|
|
176
|
-
generatedType.content = generatedType.content.replace(
|
|
177
|
-
/export interface (\w+)/g,
|
|
178
|
-
'@CustomDecorator\nexport interface $1'
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Client Generation Hooks
|
|
185
|
-
|
|
186
|
-
#### `beforeClientGeneration`
|
|
187
|
-
|
|
188
|
-
Called before generating API clients. Use this to:
|
|
189
|
-
|
|
190
|
-
- Set up client-specific configuration
|
|
191
|
-
- Modify the generation context
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
beforeClientGeneration: async (context: GenerationContext) => {
|
|
195
|
-
// Add custom configuration
|
|
196
|
-
context.clientConfig = {
|
|
197
|
-
baseUrl: 'https://api.example.com',
|
|
198
|
-
timeout: 30000,
|
|
199
|
-
};
|
|
200
|
-
},
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
#### `afterClientGeneration`
|
|
204
|
-
|
|
205
|
-
Called after generating API clients. Use this to:
|
|
206
|
-
|
|
207
|
-
- Modify the generated client
|
|
208
|
-
- Add custom methods
|
|
209
|
-
- Post-process the client
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
afterClientGeneration: async (generatedClient: GeneratedApiClient, context: GenerationContext) => {
|
|
213
|
-
// Add custom methods
|
|
214
|
-
const customMethods = `
|
|
215
|
-
async customMethod(): Promise<string> {
|
|
216
|
-
return 'custom result';
|
|
217
|
-
}
|
|
218
|
-
`;
|
|
219
|
-
|
|
220
|
-
generatedClient.content = generatedClient.content.replace(
|
|
221
|
-
/}\s*$/,
|
|
222
|
-
`${customMethods}\n}`
|
|
223
|
-
);
|
|
224
|
-
},
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Schema Transformation
|
|
228
|
-
|
|
229
|
-
#### `transformSchema`
|
|
230
|
-
|
|
231
|
-
Transform schemas before type generation. Use this to:
|
|
232
|
-
|
|
233
|
-
- Add custom properties
|
|
234
|
-
- Modify type information
|
|
235
|
-
- Standardize schema format
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
transformSchema: (schema: ResolvedSchema, context: GenerationContext): ResolvedSchema => {
|
|
239
|
-
// Add custom properties
|
|
240
|
-
if (schema.properties) {
|
|
241
|
-
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
242
|
-
if (typeof propSchema === 'object' && propSchema !== null) {
|
|
243
|
-
// Add custom metadata
|
|
244
|
-
propSchema._customProperty = true;
|
|
245
|
-
|
|
246
|
-
// Transform specific types
|
|
247
|
-
if (propSchema.format === 'uuid') {
|
|
248
|
-
propSchema._customType = 'CustomUuid';
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return schema;
|
|
255
|
-
},
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Custom Generators
|
|
259
|
-
|
|
260
|
-
### Custom Type Generators
|
|
261
|
-
|
|
262
|
-
Add custom type generators for specific schema types:
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
customTypeGenerators: {
|
|
266
|
-
'custom-type': (schema: ResolvedSchema, context: GenerationContext): GeneratedType => {
|
|
267
|
-
return {
|
|
268
|
-
name: 'CustomType',
|
|
269
|
-
content: `export type CustomType = string;`,
|
|
270
|
-
dependencies: [],
|
|
271
|
-
exports: ['CustomType'],
|
|
272
|
-
isInterface: false,
|
|
273
|
-
isEnum: false,
|
|
274
|
-
isUnion: false,
|
|
275
|
-
sourceSchema: schema,
|
|
276
|
-
};
|
|
277
|
-
},
|
|
278
|
-
|
|
279
|
-
'date-time': (schema: ResolvedSchema, context: GenerationContext): GeneratedType => {
|
|
280
|
-
return {
|
|
281
|
-
name: 'DateTime',
|
|
282
|
-
content: `export type DateTime = string; // ISO 8601 format`,
|
|
283
|
-
dependencies: [],
|
|
284
|
-
exports: ['DateTime'],
|
|
285
|
-
isInterface: false,
|
|
286
|
-
isEnum: false,
|
|
287
|
-
isUnion: false,
|
|
288
|
-
sourceSchema: schema,
|
|
289
|
-
};
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Custom Client Generators
|
|
295
|
-
|
|
296
|
-
Add custom client generators:
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
customClientGenerators: {
|
|
300
|
-
'react-query': (context: GenerationContext): GeneratedApiClient => {
|
|
301
|
-
const hooks = generateReactQueryHooks(context);
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
name: 'ReactQueryHooks',
|
|
305
|
-
content: hooks,
|
|
306
|
-
dependencies: ['react-query'],
|
|
307
|
-
exports: ['useApiHooks'],
|
|
308
|
-
endpoints: [],
|
|
309
|
-
};
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
'axios': (context: GenerationContext): GeneratedApiClient => {
|
|
313
|
-
const client = generateAxiosClient(context);
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
name: 'AxiosClient',
|
|
317
|
-
content: client,
|
|
318
|
-
dependencies: ['axios'],
|
|
319
|
-
exports: ['AxiosApiClient'],
|
|
320
|
-
endpoints: [],
|
|
321
|
-
};
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## Plugin Examples
|
|
327
|
-
|
|
328
|
-
### JSDoc Plugin
|
|
329
|
-
|
|
330
|
-
Adds JSDoc comments to generated types:
|
|
331
|
-
|
|
332
|
-
```typescript
|
|
333
|
-
const jsdocPlugin: SchemanticPlugin = {
|
|
334
|
-
name: "jsdoc",
|
|
335
|
-
version: "1.0.0",
|
|
336
|
-
description: "Adds JSDoc comments to generated types",
|
|
337
|
-
|
|
338
|
-
afterTypeGeneration: async (
|
|
339
|
-
typeName: string,
|
|
340
|
-
generatedType: GeneratedType,
|
|
341
|
-
context: GenerationContext
|
|
342
|
-
) => {
|
|
343
|
-
if (!generatedType.content.includes("/**")) {
|
|
344
|
-
const comment = `/**\n * ${
|
|
345
|
-
generatedType.sourceSchema.description || `Generated type: ${typeName}`
|
|
346
|
-
}\n */\n`;
|
|
347
|
-
generatedType.content = comment + generatedType.content;
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
};
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Validation Plugin
|
|
354
|
-
|
|
355
|
-
Adds validation decorators for class-validator:
|
|
356
|
-
|
|
357
|
-
```typescript
|
|
358
|
-
const validationPlugin: SchemanticPlugin = {
|
|
359
|
-
name: "validation",
|
|
360
|
-
version: "1.0.0",
|
|
361
|
-
description: "Adds validation decorators to generated types",
|
|
362
|
-
|
|
363
|
-
afterTypeGeneration: async (
|
|
364
|
-
typeName: string,
|
|
365
|
-
generatedType: GeneratedType,
|
|
366
|
-
context: GenerationContext
|
|
367
|
-
) => {
|
|
368
|
-
if (generatedType.isInterface) {
|
|
369
|
-
// Add validation imports
|
|
370
|
-
const imports = `import { IsString, IsNumber, IsOptional, IsNotEmpty } from 'class-validator';\n\n`;
|
|
371
|
-
|
|
372
|
-
// Add validation decorators to properties
|
|
373
|
-
let content = generatedType.content;
|
|
374
|
-
content = content.replace(
|
|
375
|
-
/(\w+)(\?)?: (\w+);/g,
|
|
376
|
-
(match, propName, optional, type) => {
|
|
377
|
-
const decorators = [];
|
|
378
|
-
|
|
379
|
-
if (type === "string") decorators.push("@IsString()");
|
|
380
|
-
if (type === "number") decorators.push("@IsNumber()");
|
|
381
|
-
if (optional) decorators.push("@IsOptional()");
|
|
382
|
-
else decorators.push("@IsNotEmpty()");
|
|
383
|
-
|
|
384
|
-
return `${decorators.join("\n ")}\n ${match}`;
|
|
385
|
-
}
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
generatedType.content = imports + content;
|
|
389
|
-
}
|
|
390
|
-
},
|
|
391
|
-
};
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### React Hooks Plugin
|
|
395
|
-
|
|
396
|
-
Generates React hooks for API endpoints:
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
const reactHooksPlugin: SchemanticPlugin = {
|
|
400
|
-
name: "react-hooks",
|
|
401
|
-
version: "1.0.0",
|
|
402
|
-
description: "Generates React hooks for API endpoints",
|
|
403
|
-
|
|
404
|
-
customClientGenerators: {
|
|
405
|
-
"react-hooks": (context: GenerationContext): GeneratedApiClient => {
|
|
406
|
-
const hooks = generateReactHooks(context);
|
|
407
|
-
|
|
408
|
-
return {
|
|
409
|
-
name: "ApiHooks",
|
|
410
|
-
content: hooks,
|
|
411
|
-
dependencies: ["react-query"],
|
|
412
|
-
exports: ["useApiHooks"],
|
|
413
|
-
endpoints: [],
|
|
414
|
-
};
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
function generateReactHooks(context: GenerationContext): string {
|
|
420
|
-
let hooks = `import { useQuery, useMutation, useQueryClient } from 'react-query';\n`;
|
|
421
|
-
hooks += `import { ApiClient } from './api-client';\n\n`;
|
|
422
|
-
|
|
423
|
-
hooks += `export const useApiHooks = (client: ApiClient) => {\n`;
|
|
424
|
-
hooks += ` const queryClient = useQueryClient();\n\n`;
|
|
425
|
-
|
|
426
|
-
// Generate hooks for each endpoint
|
|
427
|
-
for (const [path, pathItem] of Object.entries(context.schema.paths)) {
|
|
428
|
-
if (typeof pathItem === "object" && pathItem !== null) {
|
|
429
|
-
const operations = ["get", "post", "put", "delete", "patch"] as const;
|
|
430
|
-
|
|
431
|
-
for (const method of operations) {
|
|
432
|
-
const operation = pathItem[method];
|
|
433
|
-
if (operation) {
|
|
434
|
-
const hookName = generateHookName(
|
|
435
|
-
operation.operationId || `${method}${path}`
|
|
436
|
-
);
|
|
437
|
-
const hook = generateHook(operation, method, path);
|
|
438
|
-
hooks += ` ${hookName} = ${hook};\n`;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
hooks += `};\n`;
|
|
445
|
-
return hooks;
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
## Plugin Loading
|
|
450
|
-
|
|
451
|
-
### From File
|
|
452
|
-
|
|
453
|
-
```typescript
|
|
454
|
-
import { PluginLoader } from "schemantic";
|
|
455
|
-
|
|
456
|
-
const loader = new PluginLoader();
|
|
457
|
-
const plugin = await loader.loadPluginFromFile("./my-plugin.js");
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
### From Package
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
const plugin = await loader.loadPluginFromPackage("my-Schemantic-plugin");
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
### From Directory
|
|
467
|
-
|
|
468
|
-
```typescript
|
|
469
|
-
const plugins = await loader.loadPluginsFromDirectory("./plugins");
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
## Plugin Registration
|
|
473
|
-
|
|
474
|
-
### In Code
|
|
475
|
-
|
|
476
|
-
```typescript
|
|
477
|
-
import { Schemantic } from "schemantic";
|
|
478
|
-
|
|
479
|
-
const Schemantic = new Schemantic(config);
|
|
480
|
-
const pluginManager = Schemantic.getPluginManager();
|
|
481
|
-
|
|
482
|
-
pluginManager.registerPlugin(myPlugin);
|
|
483
|
-
pluginManager.enablePlugin("my-plugin");
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
### Via Configuration
|
|
487
|
-
|
|
488
|
-
```typescript
|
|
489
|
-
const config: SchemanticConfig = {
|
|
490
|
-
// ... other config
|
|
491
|
-
plugins: [
|
|
492
|
-
{ name: "my-plugin", enabled: true },
|
|
493
|
-
{ name: "jsdoc", enabled: true },
|
|
494
|
-
],
|
|
495
|
-
};
|
|
496
|
-
```
|
|
497
|
-
|
|
498
|
-
### Via CLI
|
|
499
|
-
|
|
500
|
-
```bash
|
|
501
|
-
npx schemantic generate --plugins my-plugin,jsdoc
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
## Best Practices
|
|
505
|
-
|
|
506
|
-
### 1. Error Handling
|
|
507
|
-
|
|
508
|
-
Always handle errors gracefully:
|
|
509
|
-
|
|
510
|
-
```typescript
|
|
511
|
-
beforeTypeGeneration: async (typeName: string, schema: ResolvedSchema, context: GenerationContext) => {
|
|
512
|
-
try {
|
|
513
|
-
// Plugin logic
|
|
514
|
-
} catch (error) {
|
|
515
|
-
console.warn(`Plugin ${this.name} failed for type ${typeName}:`, error);
|
|
516
|
-
// Don't throw - let generation continue
|
|
517
|
-
}
|
|
518
|
-
},
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
### 2. Performance
|
|
522
|
-
|
|
523
|
-
Consider performance implications:
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
// Cache expensive operations
|
|
527
|
-
const cache = new Map();
|
|
528
|
-
|
|
529
|
-
transformSchema: (schema: ResolvedSchema, context: GenerationContext): ResolvedSchema => {
|
|
530
|
-
const cacheKey = JSON.stringify(schema);
|
|
531
|
-
|
|
532
|
-
if (cache.has(cacheKey)) {
|
|
533
|
-
return cache.get(cacheKey);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const result = expensiveTransformation(schema);
|
|
537
|
-
cache.set(cacheKey, result);
|
|
538
|
-
return result;
|
|
539
|
-
},
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
### 3. Documentation
|
|
543
|
-
|
|
544
|
-
Document your plugin thoroughly:
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
const myPlugin: SchemanticPlugin = {
|
|
548
|
-
name: "my-plugin",
|
|
549
|
-
version: "1.0.0",
|
|
550
|
-
description: `
|
|
551
|
-
My custom plugin that adds special functionality.
|
|
552
|
-
|
|
553
|
-
Features:
|
|
554
|
-
- Adds custom decorators
|
|
555
|
-
- Transforms specific types
|
|
556
|
-
- Generates additional files
|
|
557
|
-
|
|
558
|
-
Usage:
|
|
559
|
-
Enable via configuration or CLI: --plugins my-plugin
|
|
560
|
-
`,
|
|
561
|
-
|
|
562
|
-
// ... implementation
|
|
563
|
-
};
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
### 4. Testing
|
|
567
|
-
|
|
568
|
-
Test your plugins:
|
|
569
|
-
|
|
570
|
-
```typescript
|
|
571
|
-
import { Schemantic, SchemanticConfig } from "schemantic";
|
|
572
|
-
|
|
573
|
-
describe("My Plugin", () => {
|
|
574
|
-
it("should transform schemas correctly", async () => {
|
|
575
|
-
const config: SchemanticConfig = {
|
|
576
|
-
schemaData: testSchema,
|
|
577
|
-
outputDir: "./test-output",
|
|
578
|
-
plugins: [{ name: "my-plugin", enabled: true }],
|
|
579
|
-
};
|
|
580
|
-
|
|
581
|
-
const Schemantic = new Schemantic(config);
|
|
582
|
-
const result = await Schemantic.generate();
|
|
583
|
-
|
|
584
|
-
expect(result.success).toBe(true);
|
|
585
|
-
expect(result.generatedFiles).toHaveLength(1);
|
|
586
|
-
});
|
|
587
|
-
});
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
### 5. Versioning
|
|
591
|
-
|
|
592
|
-
Use semantic versioning for your plugins:
|
|
593
|
-
|
|
594
|
-
```typescript
|
|
595
|
-
const myPlugin: SchemanticPlugin = {
|
|
596
|
-
name: "my-plugin",
|
|
597
|
-
version: "1.2.3", // Major.Minor.Patch
|
|
598
|
-
description: "My plugin description",
|
|
599
|
-
|
|
600
|
-
// ... implementation
|
|
601
|
-
};
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
## Publishing Plugins
|
|
605
|
-
|
|
606
|
-
### As NPM Package
|
|
607
|
-
|
|
608
|
-
1. Create a package.json:
|
|
609
|
-
|
|
610
|
-
```json
|
|
611
|
-
{
|
|
612
|
-
"name": "Schemantic-my-plugin",
|
|
613
|
-
"version": "1.0.0",
|
|
614
|
-
"description": "My custom Schemantic plugin",
|
|
615
|
-
"main": "dist/index.js",
|
|
616
|
-
"types": "dist/index.d.ts",
|
|
617
|
-
"keywords": ["Schemantic", "plugin", "typescript", "openapi"],
|
|
618
|
-
"peerDependencies": {
|
|
619
|
-
"schemantic": "^1.0.0"
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
2. Export your plugin:
|
|
625
|
-
|
|
626
|
-
```typescript
|
|
627
|
-
// src/index.ts
|
|
628
|
-
export { myPlugin } from "./plugin";
|
|
629
|
-
export * from "./types";
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
3. Build and publish:
|
|
633
|
-
|
|
634
|
-
```bash
|
|
635
|
-
npm run build
|
|
636
|
-
npm publish
|
|
637
|
-
```
|
|
638
|
-
|
|
639
|
-
### Usage
|
|
640
|
-
|
|
641
|
-
Users can install and use your plugin:
|
|
642
|
-
|
|
643
|
-
```bash
|
|
644
|
-
npm install Schemantic-my-plugin
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
```typescript
|
|
648
|
-
import { myPlugin } from "Schemantic-my-plugin";
|
|
649
|
-
import { Schemantic } from "schemantic";
|
|
650
|
-
|
|
651
|
-
const Schemantic = new Schemantic(config);
|
|
652
|
-
Schemantic.getPluginManager().registerPlugin(myPlugin);
|
|
653
|
-
```
|
|
654
|
-
|
|
655
|
-
## Troubleshooting
|
|
656
|
-
|
|
657
|
-
### Common Issues
|
|
658
|
-
|
|
659
|
-
1. **Plugin not loading**: Check that the plugin exports the correct interface
|
|
660
|
-
2. **Type errors**: Ensure your plugin implements all required interface properties
|
|
661
|
-
3. **Performance issues**: Use caching and avoid expensive operations in hooks
|
|
662
|
-
4. **Conflicts**: Check for naming conflicts with other plugins
|
|
663
|
-
|
|
664
|
-
### Debug Mode
|
|
665
|
-
|
|
666
|
-
Enable debug mode to see plugin execution:
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
const config: SchemanticConfig = {
|
|
670
|
-
// ... other config
|
|
671
|
-
plugins: [{ name: "my-plugin", enabled: true, options: { debug: true } }],
|
|
672
|
-
};
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
### Logging
|
|
676
|
-
|
|
677
|
-
Add logging to your plugin:
|
|
678
|
-
|
|
679
|
-
```typescript
|
|
680
|
-
beforeTypeGeneration: async (typeName: string, schema: ResolvedSchema, context: GenerationContext) => {
|
|
681
|
-
console.log(`[My Plugin] Processing type: ${typeName}`);
|
|
682
|
-
|
|
683
|
-
// Plugin logic
|
|
684
|
-
|
|
685
|
-
console.log(`[My Plugin] Completed processing: ${typeName}`);
|
|
686
|
-
},
|
|
687
|
-
```
|
|
1
|
+
# Plugins
|
|
2
|
+
|
|
3
|
+
Plugins are generation extensions or explicit runtime helper emitters. Enabling validation, caching, branding, or timing does not automatically wrap client endpoints. Import helpers from generated output and integrate them deliberately.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx schemantic plugin list
|
|
7
|
+
npx schemantic generate ./openapi.json --plugins jsdoc,zod-validation --output ./generated
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Install `zod@^4` in the consuming project for generated validation and schema-backed branded helpers. React hooks use `--hooks`/`generateHooks`, not a `react-hooks` plugin.
|
|
11
|
+
|
|
12
|
+
| Name | Observable behavior and boundary |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `jsdoc` | Adds documentation to generated declarations |
|
|
15
|
+
| `validation` | Compatibility alias for the Zod validation plugin |
|
|
16
|
+
| `zod-validation` | Generates Zod schemas/helpers; consumer must call them |
|
|
17
|
+
| `strict-mode` | Retired; active use fails explicitly. Use core strict output defaults |
|
|
18
|
+
| `branded-types` | Nominal metadata and schema-backed guards/assertions/create helpers; Zod dependency |
|
|
19
|
+
| `request-deduplication` | Emits manual caching/coalescing helpers; endpoints are not automatically cached |
|
|
20
|
+
| `performance-monitoring` | Emits optional timing helpers; source-size analysis is an estimate, not a bundled artifact measurement |
|
|
21
|
+
|
|
22
|
+
Local and installed package plugins are resolved during the generation invocation:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"schemaFile": "./openapi.json",
|
|
27
|
+
"outputDir": "./generated",
|
|
28
|
+
"plugins": [
|
|
29
|
+
{ "name": "./plugins/example.cjs", "enabled": true },
|
|
30
|
+
{ "name": "installed-plugin-package", "enabled": true }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
File paths resolve relative to the config. Package names resolve from the consumer/config directory. `--plugins ./plugin.cjs` is also supported. Plugins must export nonempty `name`, `version`, `description`, and compatible hook functions. Compile TypeScript plugins to JavaScript before loading. The inspection command `plugin load ./plugin.cjs` does not register that plugin in a future process.
|
|
36
|
+
|
|
37
|
+
Options belong in the plugin's config `options` object; enabled/disabled status is explicit. Programmatic factories are useful for function-valued settings that JSON cannot represent. See [development](PLUGIN_DEVELOPMENT.md).
|
|
38
|
+
|
|
39
|
+
## Runtime caching scope
|
|
40
|
+
|
|
41
|
+
The deduplication helper includes request method/URL/body/parameters/headers in identity. Use an execution `cacheScope` (or an instance per authenticated session) when ambient cookies or credential context changes the response. A shared instance cannot infer browser cookie changes. Mutation methods bypass caching. Custom key generators run on the runtime `RequestDeduplicator` constructor; do not place functions in JSON configuration. Avoid logging credentials.
|
|
42
|
+
|
|
43
|
+
Regex and string invalidation operate on tracked request URLs. Cache statistics report actual counters. Endpoint stale/TTL policies govern helper calls; they do not create automatic endpoint instrumentation.
|
|
44
|
+
|
|
45
|
+
## Capability limits
|
|
46
|
+
|
|
47
|
+
Auth-state branding records client metadata; server authorization still requires server checks. Zod's public constraints and unsupported schema diagnostics determine validation coverage. `optimizeBundle`, custom validation error wording, and automatic middleware claims should not be treated as implemented runtime features. Timing helpers observe explicit calls. `generateReports` writes `performance-report.json`, `.html`, or `.md`; report utilities can also return strings. These observe generator activity or explicitly instrumented calls; they are not historical regression monitoring or continuous browser memory monitoring. No request/error reduction percentages are promised.
|
|
48
|
+
|
|
49
|
+
See [advanced capabilities](ADVANCED_PLUGINS_IMPLEMENTATION.md) for experimental options and [API](API.md) for the actual hook/client contract.
|