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.
Files changed (124) hide show
  1. package/CHANGELOG.md +196 -174
  2. package/LICENSE +21 -21
  3. package/README.md +88 -830
  4. package/dist/cli/index.d.ts +4 -1
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +185 -61
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/core/configuration.d.ts +6 -0
  9. package/dist/core/configuration.d.ts.map +1 -0
  10. package/dist/core/configuration.js +64 -0
  11. package/dist/core/configuration.js.map +1 -0
  12. package/dist/core/index.d.ts +1 -0
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/index.js +1 -0
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/core/output-writer.d.ts +7 -0
  17. package/dist/core/output-writer.d.ts.map +1 -0
  18. package/dist/core/output-writer.js +255 -0
  19. package/dist/core/output-writer.js.map +1 -0
  20. package/dist/core/schemantic.d.ts +8 -2
  21. package/dist/core/schemantic.d.ts.map +1 -1
  22. package/dist/core/schemantic.js +196 -71
  23. package/dist/core/schemantic.js.map +1 -1
  24. package/dist/generators/api-client-generator.d.ts +9 -20
  25. package/dist/generators/api-client-generator.d.ts.map +1 -1
  26. package/dist/generators/api-client-generator.js +364 -424
  27. package/dist/generators/api-client-generator.js.map +1 -1
  28. package/dist/generators/base.d.ts.map +1 -1
  29. package/dist/generators/base.js +7 -25
  30. package/dist/generators/base.js.map +1 -1
  31. package/dist/generators/enum-generator.d.ts.map +1 -1
  32. package/dist/generators/enum-generator.js +30 -3
  33. package/dist/generators/enum-generator.js.map +1 -1
  34. package/dist/generators/hook-generator.d.ts +3 -17
  35. package/dist/generators/hook-generator.d.ts.map +1 -1
  36. package/dist/generators/hook-generator.js +51 -445
  37. package/dist/generators/hook-generator.js.map +1 -1
  38. package/dist/generators/naming.d.ts +10 -0
  39. package/dist/generators/naming.d.ts.map +1 -0
  40. package/dist/generators/naming.js +331 -0
  41. package/dist/generators/naming.js.map +1 -0
  42. package/dist/generators/object-generator.d.ts +0 -4
  43. package/dist/generators/object-generator.d.ts.map +1 -1
  44. package/dist/generators/object-generator.js +13 -101
  45. package/dist/generators/object-generator.js.map +1 -1
  46. package/dist/generators/primitive-generator.d.ts +1 -13
  47. package/dist/generators/primitive-generator.d.ts.map +1 -1
  48. package/dist/generators/primitive-generator.js +11 -76
  49. package/dist/generators/primitive-generator.js.map +1 -1
  50. package/dist/generators/projections.d.ts +10 -0
  51. package/dist/generators/projections.d.ts.map +1 -0
  52. package/dist/generators/projections.js +65 -0
  53. package/dist/generators/projections.js.map +1 -0
  54. package/dist/generators/schema-type.d.ts +23 -0
  55. package/dist/generators/schema-type.d.ts.map +1 -0
  56. package/dist/generators/schema-type.js +233 -0
  57. package/dist/generators/schema-type.js.map +1 -0
  58. package/dist/parsers/base.d.ts +2 -0
  59. package/dist/parsers/base.d.ts.map +1 -1
  60. package/dist/parsers/base.js +12 -2
  61. package/dist/parsers/base.js.map +1 -1
  62. package/dist/parsers/factory.d.ts.map +1 -1
  63. package/dist/parsers/factory.js +2 -1
  64. package/dist/parsers/factory.js.map +1 -1
  65. package/dist/parsers/openapi.d.ts +4 -0
  66. package/dist/parsers/openapi.d.ts.map +1 -1
  67. package/dist/parsers/openapi.js +107 -20
  68. package/dist/parsers/openapi.js.map +1 -1
  69. package/dist/plugins/branded-types.d.ts.map +1 -1
  70. package/dist/plugins/branded-types.js +231 -365
  71. package/dist/plugins/branded-types.js.map +1 -1
  72. package/dist/plugins/builtin.d.ts.map +1 -1
  73. package/dist/plugins/builtin.js +6 -141
  74. package/dist/plugins/builtin.js.map +1 -1
  75. package/dist/plugins/index.d.ts +4 -0
  76. package/dist/plugins/index.d.ts.map +1 -1
  77. package/dist/plugins/index.js +17 -0
  78. package/dist/plugins/index.js.map +1 -1
  79. package/dist/plugins/loader.d.ts +5 -1
  80. package/dist/plugins/loader.d.ts.map +1 -1
  81. package/dist/plugins/loader.js +42 -5
  82. package/dist/plugins/loader.js.map +1 -1
  83. package/dist/plugins/manager.d.ts.map +1 -1
  84. package/dist/plugins/manager.js +7 -7
  85. package/dist/plugins/manager.js.map +1 -1
  86. package/dist/plugins/performance-monitoring.d.ts +31 -1
  87. package/dist/plugins/performance-monitoring.d.ts.map +1 -1
  88. package/dist/plugins/performance-monitoring.js +146 -207
  89. package/dist/plugins/performance-monitoring.js.map +1 -1
  90. package/dist/plugins/request-deduplication.d.ts.map +1 -1
  91. package/dist/plugins/request-deduplication.js +151 -586
  92. package/dist/plugins/request-deduplication.js.map +1 -1
  93. package/dist/plugins/zod-validation.d.ts +23 -15
  94. package/dist/plugins/zod-validation.d.ts.map +1 -1
  95. package/dist/plugins/zod-validation.js +390 -268
  96. package/dist/plugins/zod-validation.js.map +1 -1
  97. package/dist/types/core.d.ts +20 -3
  98. package/dist/types/core.d.ts.map +1 -1
  99. package/dist/types/core.js +2 -0
  100. package/dist/types/core.js.map +1 -1
  101. package/dist/types/openapi.d.ts +14 -1
  102. package/dist/types/openapi.d.ts.map +1 -1
  103. package/docs/ADVANCED_PLUGINS_IMPLEMENTATION.md +24 -355
  104. package/docs/ADVANCED_PLUGIN_DEVELOPMENT.md +33 -309
  105. package/docs/API.md +80 -601
  106. package/docs/AUDIT.md +49 -0
  107. package/docs/CONFIGURATION.md +81 -715
  108. package/docs/FASTAPI_TUTORIAL.md +109 -1497
  109. package/docs/NAMING_MIGRATION.md +59 -0
  110. package/docs/PLUGINS.md +49 -687
  111. package/docs/PLUGIN_DEVELOPMENT.md +53 -720
  112. package/docs/REAUDIT.md +399 -0
  113. package/docs/RELEASING.md +88 -0
  114. package/docs/REMEDIATION.md +58 -0
  115. package/docs/superpowers/plans/2026-10-10-audit-remediation.md +113 -0
  116. package/package.json +114 -95
  117. package/dist/__tests__/setup.d.ts +0 -6
  118. package/dist/__tests__/setup.d.ts.map +0 -1
  119. package/dist/__tests__/setup.js +0 -30
  120. package/dist/__tests__/setup.js.map +0 -1
  121. package/dist/__tests__/test-config.d.ts +0 -36
  122. package/dist/__tests__/test-config.d.ts.map +0 -1
  123. package/dist/__tests__/test-config.js +0 -562
  124. package/dist/__tests__/test-config.js.map +0 -1
@@ -23,6 +23,7 @@
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.brandedTypesPlugin = void 0;
25
25
  const schema_1 = require("../types/schema");
26
+ const zod_validation_1 = require("./zod-validation");
26
27
  /**
27
28
  * Advanced Branded Types Plugin Implementation
28
29
  */
@@ -62,25 +63,29 @@ exports.brandedTypesPlugin = {
62
63
  */
63
64
  afterTypeGeneration: async (typeName, generatedType, context) => {
64
65
  const options = getPluginOptions(context);
65
- if (!options.enabled || !generatedType.isInterface) {
66
+ if (!options.enabled) {
66
67
  return;
67
68
  }
68
- try {
69
- const brandedCode = generateBrandedTypeEnhancements(typeName, generatedType, options);
70
- if (brandedCode) {
71
- generatedType.content += brandedCode;
72
- // Update exports
73
- generatedType.exports.push(`Branded${typeName}`, `create${typeName}`, `is${typeName}`, `assert${typeName}`);
74
- if (options.enablePhantomTypes) {
75
- generatedType.exports.push(`${typeName}WithAuth`, `authorize${typeName}`, `unauthorize${typeName}`);
76
- }
77
- if (options.enableDiscriminatedUnions) {
78
- generatedType.exports.push(`${typeName}Union`, `match${typeName}`, `is${typeName}Variant`);
69
+ const brandedCode = generateBrandedTypeEnhancements(typeName, generatedType, options, context);
70
+ if (brandedCode) {
71
+ generatedType.content += brandedCode;
72
+ generatedType.dependencies.push("zod");
73
+ generatedType.exports.push(`Branded${typeName}`, `create${typeName}`);
74
+ if (options.enableRuntimeGuards)
75
+ generatedType.exports.push(`is${typeName}`);
76
+ if (options.enableAssertionFunctions)
77
+ generatedType.exports.push(`assert${typeName}`);
78
+ if (options.enableOpaqueTypes)
79
+ generatedType.exports.push(`Opaque${typeName}`, `createOpaque${typeName}`, `unwrapOpaque${typeName}`);
80
+ if (options.enablePhantomTypes && generatedType.isInterface) {
81
+ generatedType.exports.push(`${typeName}WithAuth`, `authorize${typeName}`, `unauthorize${typeName}`, `isAuthenticated${typeName}`);
82
+ if (!Array.from(context.generatedTypes.values()).some(type => type !== generatedType && type.exports.includes("AuthState"))) {
83
+ generatedType.content += "\nexport type AuthState = 'authenticated' | 'unauthenticated';\n";
84
+ generatedType.exports.push("AuthState");
79
85
  }
80
86
  }
81
- }
82
- catch (error) {
83
- console.warn(`Branded types plugin afterTypeGeneration failed for ${typeName}:`, error);
87
+ if (options.enableDiscriminatedUnions)
88
+ generatedType.exports.push(`${typeName}Union`, `match${typeName}`, `is${typeName}Variant`);
84
89
  }
85
90
  },
86
91
  /**
@@ -91,20 +96,15 @@ exports.brandedTypesPlugin = {
91
96
  if (!options.enabled) {
92
97
  return;
93
98
  }
94
- try {
95
- const clientEnhancements = generateClientBrandedTypeIntegration(generatedClient, options);
96
- if (clientEnhancements) {
97
- generatedClient.content += clientEnhancements;
98
- // Update exports
99
- generatedClient.exports.push("BrandedApiClient", "TypeSafeApiClient", "withBrandValidation");
100
- if (options.enablePhantomTypes) {
101
- generatedClient.exports.push("AuthenticatedApiClient", "UnauthenticatedApiClient");
102
- }
99
+ const clientEnhancements = generateClientBrandedTypeIntegration(generatedClient, options);
100
+ if (clientEnhancements) {
101
+ generatedClient.content += clientEnhancements;
102
+ // Update exports
103
+ generatedClient.exports.push("BrandedApiClient", "withBrandValidation");
104
+ if (options.enablePhantomTypes) {
105
+ generatedClient.exports.push("TypeSafeApiClient", "AuthenticatedApiClient", "UnauthenticatedApiClient");
103
106
  }
104
107
  }
105
- catch (error) {
106
- console.warn("Branded types plugin afterClientGeneration failed:", error);
107
- }
108
108
  },
109
109
  };
110
110
  /**
@@ -188,379 +188,245 @@ function extractValidationConstraints(schema) {
188
188
  /**
189
189
  * Generate branded type enhancements for a given type
190
190
  */
191
- function generateBrandedTypeEnhancements(typeName, generatedType, options) {
192
- if (!(0, schema_1.isOpenAPISchemaObject)(generatedType.sourceSchema)) {
193
- return "";
194
- }
195
- const schema = generatedType.sourceSchema;
196
- const brandMetadata = schema._brandMetadata || {};
197
- if (!brandMetadata.shouldBrand) {
198
- return generateBasicBrandedType(typeName, options);
199
- }
200
- return generateAdvancedBrandedType(typeName, schema, options, brandMetadata);
191
+ function generateBrandedTypeEnhancements(typeName, generatedType, options, context) {
192
+ const marker = options.brandIdentifier ?? "Brand";
193
+ const brandName = options.brandNamingConvention === "suffix" ? `${typeName}${marker}`
194
+ : options.brandNamingConvention === "namespace" ? `${marker}.${typeName}` : `${marker}${typeName}`;
195
+ const validator = (0, zod_validation_1.buildZodSchema)(generatedType.sourceSchema, { strictMode: false }, 0, context).replace(/z\.lazy\(\(\) => (\w+)Schema\)/g, "z.lazy(() => __$1BrandSchema)");
196
+ const constraints = options.enableTypeLevelValidations && (0, schema_1.isOpenAPISchemaObject)(generatedType.sourceSchema)
197
+ ? `; readonly __constraints: ${JSON.stringify(extractValidationConstraints(generatedType.sourceSchema))}` : "";
198
+ return `
199
+ import { z } from 'zod';
200
+ const __${typeName}BrandSchema: z.ZodType = ${validator};
201
+ export type Branded${typeName} = ${typeName} & { readonly __brand: ${JSON.stringify(brandName)}${constraints} };
202
+ /** Validates the source schema before applying a compile-time brand. */
203
+ export function create${typeName}(data: ${typeName}): Branded${typeName} {
204
+ __${typeName}BrandSchema.parse(data); return data as Branded${typeName};
201
205
  }
202
- /**
203
- * Generate basic branded type without specific validation
204
- */
205
- function generateBasicBrandedType(typeName, options) {
206
- const brandName = `${typeName}${options.brandIdentifier || "Brand"}`;
207
- return `
208
-
209
- /**
210
- * Branded type for ${typeName} with compile-time safety guarantees
211
- */
212
- export type Branded${typeName} = ${typeName} & { readonly __brand: '${brandName}' };
213
-
214
- /**
215
- * Create a branded ${typeName} instance
216
- */
217
- export function create${typeName}(data: ${typeName}): Branded${typeName} {
218
- // Runtime validation would go here
219
- return data as Branded${typeName};
220
- }
221
-
222
- /**
223
- * Type guard for Branded${typeName}
224
- */
225
- export function is${typeName}(value: unknown): value is Branded${typeName} {
226
- // Basic type checking
227
- return typeof value === 'object' && value !== null;
228
- }
229
-
230
- /**
231
- * Assertion function for ${typeName}
232
- */
233
- export function assert${typeName}(value: unknown): asserts value is Branded${typeName} {
234
- if (!is${typeName}(value)) {
235
- throw new Error(\`Expected ${typeName}, got \${typeof value}\`);
236
- }
237
- }${options.enablePhantomTypes ? generatePhantomTypes(typeName) : ""}${options.enableDiscriminatedUnions
238
- ? generateDiscriminatedUnions(typeName)
239
- : ""}`;
206
+ ${options.enableRuntimeGuards ? `export function is${typeName}(value: unknown): ${options.enableTypePredicates ? `value is Branded${typeName}` : 'boolean'} { return __${typeName}BrandSchema.safeParse(value).success; }` : ''}
207
+ ${options.enableAssertionFunctions ? `export function assert${typeName}(value: unknown): asserts value is Branded${typeName} { __${typeName}BrandSchema.parse(value); }` : ''}
208
+ ${options.enableOpaqueTypes ? generateOpaqueTypeHelpers(typeName) : ''}
209
+ ${options.enablePhantomTypes && generatedType.isInterface ? generatePhantomTypes(typeName) : ''}
210
+ ${options.enableDiscriminatedUnions ? generateDiscriminatedUnions(typeName) : ''}
211
+ `;
240
212
  }
241
213
  /**
242
- * Generate advanced branded type with validation constraints
214
+ * Generate opaque type helpers
243
215
  */
244
- function generateAdvancedBrandedType(typeName, schema, options, brandMetadata) {
245
- const brandName = `${typeName}${options.brandIdentifier || "Brand"}`;
246
- const validationCode = generateValidationCode(schema, brandMetadata);
247
- return `
248
-
249
- /**
250
- * Advanced branded type for ${typeName} with validation constraints
251
- * Constraints: ${Array.isArray(brandMetadata.validationConstraints)
252
- ? brandMetadata.validationConstraints.join(", ")
253
- : "none"}
254
- */
255
- export type Branded${typeName} = ${typeName} & {
256
- readonly __brand: '${brandName}';
257
- readonly __constraints: {${generateConstraintTypes(schema)}};
258
- };
259
-
260
- /**
261
- * Create a validated branded ${typeName} instance
262
- */
263
- export function create${typeName}(data: ${typeName}): Branded${typeName} {
264
- ${validationCode}
265
- return data as Branded${typeName};
266
- }
267
-
268
- /**
269
- * Advanced type guard with constraint validation
270
- */
271
- export function is${typeName}(value: unknown): value is Branded${typeName} {
272
- if (typeof value !== 'object' || value === null) {
273
- return false;
274
- }
275
-
276
- try {
277
- create${typeName}(value as ${typeName});
278
- return true;
279
- } catch {
280
- return false;
281
- }
282
- }
283
-
284
- /**
285
- * Assertion function with detailed error messages
286
- */
287
- export function assert${typeName}(value: unknown): asserts value is Branded${typeName} {
288
- if (!is${typeName}(value)) {
289
- throw new TypeError(\`Value does not satisfy ${typeName} constraints\`);
290
- }
291
- }
292
-
293
- ${options.enableOpaqueTypes ? generateOpaqueTypeHelpers(typeName) : ""}${options.enablePhantomTypes ? generatePhantomTypes(typeName) : ""}${options.enableDiscriminatedUnions
294
- ? generateDiscriminatedUnions(typeName)
295
- : ""}`;
296
- }
216
+ function generateOpaqueTypeHelpers(typeName) {
217
+ return `
218
+
297
219
  /**
298
- * Generate validation code based on schema constraints
220
+ * Opaque type for ${typeName} - completely hides the underlying type
299
221
  */
300
- function generateValidationCode(schema, _brandMetadata) {
301
- const validations = [];
302
- if (schema.type === "string") {
303
- if (schema.minLength !== undefined) {
304
- validations.push(`if (typeof data !== 'string' || data.length < ${schema.minLength}) throw new Error('String too short');`);
305
- }
306
- if (schema.maxLength !== undefined) {
307
- validations.push(`if (typeof data !== 'string' || data.length > ${schema.maxLength}) throw new Error('String too long');`);
308
- }
309
- if (schema.pattern) {
310
- validations.push(`if (typeof data !== 'string' || !/${schema.pattern}/.test(data)) throw new Error('Pattern mismatch');`);
311
- }
312
- }
313
- if (schema.type === "number" || schema.type === "integer") {
314
- if (schema.minimum !== undefined) {
315
- validations.push(`if (typeof data !== 'number' || data < ${schema.minimum}) throw new Error('Number too small');`);
316
- }
317
- if (schema.maximum !== undefined) {
318
- validations.push(`if (typeof data !== 'number' || data > ${schema.maximum}) throw new Error('Number too large');`);
319
- }
320
- }
321
- return validations.join("\n ");
322
- }
222
+ declare const __${typeName}Opaque: unique symbol;
223
+ export type Opaque${typeName} = ${typeName} & { readonly [__${typeName}Opaque]: true };
224
+
323
225
  /**
324
- * Generate constraint types for the brand
226
+ * Create opaque ${typeName}
325
227
  */
326
- function generateConstraintTypes(schema) {
327
- const constraints = [];
328
- if (schema.minLength !== undefined) {
329
- constraints.push(`minLength: ${schema.minLength}`);
330
- }
331
- if (schema.maxLength !== undefined) {
332
- constraints.push(`maxLength: ${schema.maxLength}`);
333
- }
334
- if (schema.minimum !== undefined) {
335
- constraints.push(`minimum: ${schema.minimum}`);
336
- }
337
- if (schema.maximum !== undefined) {
338
- constraints.push(`maximum: ${schema.maximum}`);
339
- }
340
- if (schema.pattern) {
341
- constraints.push(`pattern: '${schema.pattern}'`);
342
- }
343
- return constraints.join("; ");
228
+ export function createOpaque${typeName}(data: ${typeName}): Opaque${typeName} {
229
+ __${typeName}BrandSchema.parse(data); return data as Opaque${typeName};
344
230
  }
231
+
345
232
  /**
346
- * Generate opaque type helpers
233
+ * Unwrap opaque ${typeName}
347
234
  */
348
- function generateOpaqueTypeHelpers(typeName) {
349
- return `
350
-
351
- /**
352
- * Opaque type for ${typeName} - completely hides the underlying type
353
- */
354
- declare const __${typeName}Opaque: unique symbol;
355
- export type Opaque${typeName} = ${typeName} & { readonly [__${typeName}Opaque]: true };
356
-
357
- /**
358
- * Create opaque ${typeName}
359
- */
360
- export function createOpaque${typeName}(data: ${typeName}): Opaque${typeName} {
361
- return data as Opaque${typeName};
362
- }
363
-
364
- /**
365
- * Unwrap opaque ${typeName}
366
- */
367
- export function unwrapOpaque${typeName}(data: Opaque${typeName}): ${typeName} {
368
- return data as ${typeName};
235
+ export function unwrapOpaque${typeName}(data: Opaque${typeName}): ${typeName} {
236
+ return data as ${typeName};
369
237
  }`;
370
238
  }
371
239
  /**
372
240
  * Generate phantom types for authentication state management
373
241
  */
374
242
  function generatePhantomTypes(typeName) {
375
- return `
376
-
377
- /**
378
- * Phantom types for authentication state management
379
- */
380
- export type AuthState = 'authenticated' | 'unauthenticated';
381
-
382
- export type ${typeName}WithAuth<TAuth extends AuthState = 'unauthenticated'> =
383
- Branded${typeName} & { readonly __auth: TAuth };
384
-
385
- /**
386
- * Authorize a ${typeName} instance
387
- */
388
- export function authorize${typeName}(
389
- data: ${typeName}WithAuth<'unauthenticated'>
390
- ): ${typeName}WithAuth<'authenticated'> {
391
- return data as ${typeName}WithAuth<'authenticated'>;
392
- }
393
-
394
- /**
395
- * Remove authorization from ${typeName} instance
396
- */
397
- export function unauthorize${typeName}(
398
- data: ${typeName}WithAuth<'authenticated'>
399
- ): ${typeName}WithAuth<'unauthenticated'> {
400
- return data as ${typeName}WithAuth<'unauthenticated'>;
401
- }
402
-
403
- /**
404
- * Type guard for authenticated ${typeName}
405
- */
406
- export function isAuthenticated${typeName}(
407
- value: ${typeName}WithAuth<AuthState>
408
- ): value is ${typeName}WithAuth<'authenticated'> {
409
- // In practice, this would check actual auth state
410
- return true; // Placeholder implementation
243
+ return `
244
+
245
+ /**
246
+ * Phantom types for authentication state management
247
+ */
248
+
249
+ export type ${typeName}WithAuth<TAuth extends AuthState = 'unauthenticated'> =
250
+ Branded${typeName} & { readonly __auth: TAuth };
251
+
252
+ /**
253
+ * Authorize a ${typeName} instance
254
+ */
255
+ export function authorize${typeName}(
256
+ data: ${typeName}WithAuth<'unauthenticated'>
257
+ ): ${typeName}WithAuth<'authenticated'> {
258
+ return { ...data, __auth: 'authenticated' };
259
+ }
260
+
261
+ /**
262
+ * Remove authorization from ${typeName} instance
263
+ */
264
+ export function unauthorize${typeName}(
265
+ data: ${typeName}WithAuth<'authenticated'>
266
+ ): ${typeName}WithAuth<'unauthenticated'> {
267
+ return { ...data, __auth: 'unauthenticated' };
268
+ }
269
+
270
+ /**
271
+ * Type guard for authenticated ${typeName}
272
+ */
273
+ export function isAuthenticated${typeName}(
274
+ value: ${typeName}WithAuth<AuthState>
275
+ ): value is ${typeName}WithAuth<'authenticated'> {
276
+ return value.__auth === 'authenticated';
411
277
  }`;
412
278
  }
413
279
  /**
414
280
  * Generate discriminated unions
415
281
  */
416
282
  function generateDiscriminatedUnions(typeName) {
417
- return `
418
-
419
- /**
420
- * Discriminated union variants for ${typeName}
421
- */
422
- export type ${typeName}Union =
423
- | { type: 'valid'; data: Branded${typeName} }
424
- | { type: 'invalid'; error: string }
425
- | { type: 'pending'; progress?: number };
426
-
427
- /**
428
- * Pattern matching for ${typeName}Union
429
- */
430
- export function match${typeName}<T>(
431
- union: ${typeName}Union,
432
- handlers: {
433
- valid: (data: Branded${typeName}) => T;
434
- invalid: (error: string) => T;
435
- pending: (progress?: number) => T;
436
- }
437
- ): T {
438
- switch (union.type) {
439
- case 'valid':
440
- return handlers.valid(union.data);
441
- case 'invalid':
442
- return handlers.invalid(union.error);
443
- case 'pending':
444
- return handlers.pending(union.progress);
445
- default:
446
- // Exhaustiveness check
447
- const _exhaustive: never = union;
448
- throw new Error(\`Unhandled union type: \${JSON.stringify(_exhaustive)}\`);
449
- }
450
- }
451
-
452
- /**
453
- * Type guard for ${typeName}Union variants
454
- */
455
- export function is${typeName}Variant<T extends ${typeName}Union['type']>(
456
- union: ${typeName}Union,
457
- type: T
458
- ): union is Extract<${typeName}Union, { type: T }> {
459
- return union.type === type;
283
+ return `
284
+
285
+ /**
286
+ * Discriminated union variants for ${typeName}
287
+ */
288
+ export type ${typeName}Union =
289
+ | { type: 'valid'; data: Branded${typeName} }
290
+ | { type: 'invalid'; error: string }
291
+ | { type: 'pending'; progress?: number };
292
+
293
+ /**
294
+ * Pattern matching for ${typeName}Union
295
+ */
296
+ export function match${typeName}<T>(
297
+ union: ${typeName}Union,
298
+ handlers: {
299
+ valid: (data: Branded${typeName}) => T;
300
+ invalid: (error: string) => T;
301
+ pending: (progress?: number) => T;
302
+ }
303
+ ): T {
304
+ switch (union.type) {
305
+ case 'valid':
306
+ return handlers.valid(union.data);
307
+ case 'invalid':
308
+ return handlers.invalid(union.error);
309
+ case 'pending':
310
+ return handlers.pending(union.progress);
311
+ default:
312
+ // Exhaustiveness check
313
+ const _exhaustive: never = union;
314
+ throw new Error(\`Unhandled union type: \${JSON.stringify(_exhaustive)}\`);
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Type guard for ${typeName}Union variants
320
+ */
321
+ export function is${typeName}Variant<T extends ${typeName}Union['type']>(
322
+ union: ${typeName}Union,
323
+ type: T
324
+ ): union is Extract<${typeName}Union, { type: T }> {
325
+ return union.type === type;
460
326
  }`;
461
327
  }
462
328
  /**
463
329
  * Generate client integration with branded types
464
330
  */
465
331
  function generateClientBrandedTypeIntegration(_generatedClient, options) {
466
- return `
467
-
468
- /**
469
- * Branded type integration for API client
470
- */
471
-
472
- /**
473
- * Type-safe API client with branded type validation
474
- */
475
- export class BrandedApiClient {
476
- /**
477
- * Validate and brand response data
478
- */
479
- static validateResponse<T, B extends T & { __brand: any }>(
480
- data: T,
481
- validator: (data: T) => B
482
- ): B {
483
- try {
484
- return validator(data);
485
- } catch (error) {
486
- throw new Error(\`Response validation failed: \${error instanceof Error ? error.message : 'Unknown error'}\`);
487
- }
488
- }
489
-
490
- /**
491
- * Wrap request with branded type validation
492
- */
493
- static withBrandValidation<TRequest, TResponse, TBrandedResponse extends TResponse & { __brand: any }>(
494
- requestFn: (data: TRequest) => Promise<TResponse>,
495
- responseValidator: (data: TResponse) => TBrandedResponse
496
- ) {
497
- return async (data: TRequest): Promise<TBrandedResponse> => {
498
- const response = await requestFn(data);
499
- return this.validateResponse(response, responseValidator);
500
- };
501
- }
502
- }
503
-
504
- /**
505
- * Type-safe wrapper function
506
- */
507
- export function withBrandValidation<T extends (...args: any[]) => Promise<any>, R>(
508
- fn: T,
509
- validator: (result: Awaited<ReturnType<T>>) => R
510
- ): (...args: Parameters<T>) => Promise<R> {
511
- return async (...args: Parameters<T>): Promise<R> => {
512
- const result = await fn(...args);
513
- return validator(result);
514
- };
515
- }
516
-
332
+ return `
333
+
334
+ /**
335
+ * Branded type integration for API client
336
+ */
337
+
338
+ /**
339
+ * Type-safe API client with branded type validation
340
+ */
341
+ export class BrandedApiClient {
342
+ /**
343
+ * Validate and brand response data
344
+ */
345
+ static validateResponse<T, B extends T & { __brand: any }>(
346
+ data: T,
347
+ validator: (data: T) => B
348
+ ): B {
349
+ try {
350
+ return validator(data);
351
+ } catch (error) {
352
+ throw new Error(\`Response validation failed: \${error instanceof Error ? error.message : 'Unknown error'}\`);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Wrap request with branded type validation
358
+ */
359
+ static withBrandValidation<TRequest, TResponse, TBrandedResponse extends TResponse & { __brand: any }>(
360
+ requestFn: (data: TRequest) => Promise<TResponse>,
361
+ responseValidator: (data: TResponse) => TBrandedResponse
362
+ ) {
363
+ return async (data: TRequest): Promise<TBrandedResponse> => {
364
+ const response = await requestFn(data);
365
+ return this.validateResponse<TResponse, TBrandedResponse>(response, responseValidator);
366
+ };
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Type-safe wrapper function
372
+ */
373
+ export function withBrandValidation<T extends (...args: any[]) => Promise<any>, R>(
374
+ fn: T,
375
+ validator: (result: Awaited<ReturnType<T>>) => R
376
+ ): (...args: Parameters<T>) => Promise<R> {
377
+ return async (...args: Parameters<T>): Promise<R> => {
378
+ const result = await fn(...args);
379
+ return validator(result);
380
+ };
381
+ }
382
+
517
383
  ${options.enablePhantomTypes ? generatePhantomApiClient() : ""}`;
518
384
  }
519
385
  /**
520
386
  * Generate phantom type API client integration
521
387
  */
522
388
  function generatePhantomApiClient() {
523
- return `
524
-
525
- /**
526
- * API client with phantom authentication state
527
- */
528
- export class AuthenticatedApiClient {
529
- private readonly isAuthenticated = true;
530
-
531
- /**
532
- * Execute authenticated request
533
- */
534
- async executeAuthenticatedRequest<T>(
535
- request: () => Promise<T>
536
- ): Promise<T> {
537
- if (!this.isAuthenticated) {
538
- throw new Error('Client is not authenticated');
539
- }
540
- return request();
541
- }
542
- }
543
-
544
- export class UnauthenticatedApiClient {
545
- private readonly isAuthenticated = false;
546
-
547
- /**
548
- * Execute public request
549
- */
550
- async executePublicRequest<T>(
551
- request: () => Promise<T>
552
- ): Promise<T> {
553
- return request();
554
- }
555
-
556
- /**
557
- * Authenticate and return authenticated client
558
- */
559
- async authenticate(credentials: { token: string }): Promise<AuthenticatedApiClient> {
560
- // Authentication logic would go here
561
- console.log('Authenticating with token:', credentials.token);
562
- return new AuthenticatedApiClient();
563
- }
389
+ return `
390
+
391
+ /**
392
+ * API client with phantom authentication state
393
+ */
394
+ export { BrandedApiClient as TypeSafeApiClient };
395
+
396
+ export class AuthenticatedApiClient {
397
+ private readonly isAuthenticated = true;
398
+
399
+ /**
400
+ * Execute authenticated request
401
+ */
402
+ async executeAuthenticatedRequest<T>(
403
+ request: () => Promise<T>
404
+ ): Promise<T> {
405
+ if (!this.isAuthenticated) {
406
+ throw new Error('Client is not authenticated');
407
+ }
408
+ return request();
409
+ }
410
+ }
411
+
412
+ export class UnauthenticatedApiClient {
413
+
414
+ /**
415
+ * Execute public request
416
+ */
417
+ async executePublicRequest<T>(
418
+ request: () => Promise<T>
419
+ ): Promise<T> {
420
+ return request();
421
+ }
422
+
423
+ /**
424
+ * Authenticate and return authenticated client
425
+ */
426
+ async authenticate(credentials: { token: string }): Promise<AuthenticatedApiClient> {
427
+ if (!credentials.token) throw new Error('A token is required for client authentication state metadata');
428
+ return new AuthenticatedApiClient();
429
+ }
564
430
  }`;
565
431
  }
566
432
  // Export plugin for registration