@elliots/typical 0.2.0-beta.1 → 0.2.1

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 CHANGED
@@ -286,12 +286,57 @@ Create a `typical.json` file in your project root (optional):
286
286
 
287
287
  ### Options
288
288
 
289
- | Option | Default | Description |
290
- | ------------------- | --------------------------------------------------------- | --------------------------------------------- |
291
- | `include` | `["**/*.ts", "**/*.tsx"]` | Files to transform |
292
- | `exclude` | `["node_modules/**", "**/*.d.ts", "dist/**", "build/**"]` | Files to skip |
293
- | `validateFunctions` | `true` | Validate function parameters and return types |
294
- | `validateCasts` | `false` | Validate type assertions (`as Type`) |
289
+ | Option | Default | Description |
290
+ | ------------------------ | --------------------------------------------------------- | ----------------------------------------------------------------- |
291
+ | `include` | `["**/*.ts", "**/*.tsx"]` | Files to transform |
292
+ | `exclude` | `["node_modules/**", "**/*.d.ts", "dist/**", "build/**"]` | Files to skip |
293
+ | `validateFunctions` | `true` | Validate function parameters and return types |
294
+ | `validateCasts` | `false` | Validate type assertions (`as Type`) |
295
+ | `transformJSONParse` | `true` | Transform `JSON.parse` to validate and filter to typed properties |
296
+ | `transformJSONStringify` | `true` | Transform `JSON.stringify` to only include typed properties |
297
+
298
+ ---
299
+
300
+ ## JSON Transformations
301
+
302
+ Typical automatically transforms `JSON.parse` and `JSON.stringify` calls when type information is available.
303
+
304
+ ### JSON.parse
305
+
306
+ When you cast the result of `JSON.parse`, Typical validates the parsed data and filters it to only include properties defined in your type:
307
+
308
+ ```ts
309
+ interface User {
310
+ name: string;
311
+ age: number;
312
+ }
313
+
314
+ // Input: '{"name":"Alice","age":30,"password":"secret"}'
315
+ const user = JSON.parse(jsonString) as User;
316
+ // Result: { name: "Alice", age: 30 } - password is filtered out!
317
+ // Throws TypeError if name isn't a string or age isn't a number
318
+ ```
319
+
320
+ ### JSON.stringify
321
+
322
+ When you use a type assertion with `JSON.stringify`, only properties defined in your type are included - preventing accidental data leaks:
323
+
324
+ ```ts
325
+ interface PublicUser {
326
+ name: string;
327
+ age: number;
328
+ }
329
+
330
+ const user = { name: "Alice", age: 30, password: "secret", ssn: "123-45-6789" };
331
+ const json = JSON.stringify(user as PublicUser);
332
+ // Result: '{"name":"Alice","age":30}' - sensitive data excluded!
333
+ ```
334
+
335
+ Both patterns detect type information from:
336
+
337
+ - Type assertions: `JSON.parse(str) as User` or `JSON.stringify(obj as User)`
338
+ - Variable declarations: `const user: User = JSON.parse(str)`
339
+ - Function return types: `function getUser(): User { return JSON.parse(str) }`
295
340
 
296
341
  ---
297
342
 
@@ -301,6 +346,16 @@ Typical uses a Go-based compiler that leverages the TypeScript type checker to a
301
346
 
302
347
  Types that can't be validated at runtime (like generic type parameters `T`) are skipped. You can still use `any` and `unknown` to opt out of validation.
303
348
 
349
+ ## Compiler Optimizations
350
+
351
+ The generated validation code is optimized for runtime performance:
352
+
353
+ - **Skip redundant validation** - When returning a validated parameter directly, the return validation is skipped (e.g., `return x` where `x: string` was already validated)
354
+ - **Subtype-aware skipping** - If a validated type is assignable to the return type, validation is skipped (e.g., `string` parameter returned as `string | null`)
355
+ - **Property chain tracking** - Accessing properties of validated objects skips re-validation (e.g., `return user.name` when `user: User` was validated)
356
+ - **Type-aware dirty tracking** - Primitives stay validated after being passed to functions (they're copied), but objects are re-validated (they could be mutated)
357
+ - **Union early bail-out** - Union type checks use if-else chains so the first matching type succeeds immediately
358
+
304
359
  ## Debugging
305
360
 
306
361
  Set `DEBUG=1` for verbose logging:
@@ -31,55 +31,39 @@ export interface TypicalConfig {
31
31
  * Example: ["React.*", "Express.Request", "*.Event"]
32
32
  */
33
33
  ignoreTypes?: string[];
34
- /**
35
- * Skip validation for DOM types (Document, Element, Node, etc.) and their subclasses.
36
- * These types have complex Window intersections that typia cannot process.
37
- * Default: true
38
- */
39
- ignoreDOMTypes?: boolean;
40
34
  /**
41
35
  * Validate function parameters and return types at runtime.
42
36
  * When enabled, typed function parameters get runtime validation calls injected.
43
37
  * Default: true
44
38
  */
45
39
  validateFunctions?: boolean;
40
+ /**
41
+ * Transform JSON.parse<T>() calls to validate and filter the parsed result
42
+ * to only include properties defined in type T.
43
+ * Default: true
44
+ */
45
+ transformJSONParse?: boolean;
46
+ /**
47
+ * Transform JSON.stringify<T>() calls to only stringify properties defined
48
+ * in type T, preventing accidental data leaks.
49
+ * Default: true
50
+ */
51
+ transformJSONStringify?: boolean;
46
52
  /**
47
53
  * Source map generation settings.
48
54
  * Controls whether and how source maps are generated for transformed code.
49
55
  */
50
56
  sourceMap?: TypicalSourceMapConfig;
51
- }
52
- /**
53
- * Pre-compiled regex patterns for ignore type matching.
54
- * This is populated during config loading for performance.
55
- */
56
- export interface CompiledIgnorePatterns {
57
- /** Compiled patterns from user ignoreTypes config */
58
- userPatterns: RegExp[];
59
- /** Compiled patterns from DOM_TYPES_TO_IGNORE (when ignoreDOMTypes is true) */
60
- domPatterns: RegExp[];
61
- /** All patterns combined for quick checking */
62
- allPatterns: RegExp[];
57
+ /**
58
+ * Maximum number of helper functions (_io0, _io1, etc.) that can be generated
59
+ * for a single type before erroring. Complex DOM types or library types can
60
+ * generate hundreds of functions which indicates a type that should be excluded.
61
+ * Set to 0 to disable the limit.
62
+ * Default: 50
63
+ */
64
+ maxGeneratedFunctions?: number;
63
65
  }
64
66
  export declare const defaultConfig: TypicalConfig;
65
- /**
66
- * DOM types that typia cannot process due to Window global intersections.
67
- * These are the base DOM types - classes extending them are checked separately.
68
- */
69
- export declare const DOM_TYPES_TO_IGNORE: string[];
70
- /**
71
- * Convert a glob pattern to a RegExp for type matching.
72
- * Supports wildcards: "React.*" -> /^React\..*$/
73
- */
74
- export declare function compileIgnorePattern(pattern: string): RegExp | null;
75
- /**
76
- * Pre-compile all ignore patterns for efficient matching.
77
- */
78
- export declare function compileIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns;
79
- /**
80
- * Get compiled ignore patterns, using cache if config hasn't changed.
81
- */
82
- export declare function getCompiledIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns;
83
67
  export declare function loadConfig(configPath?: string): TypicalConfig;
84
68
  /**
85
69
  * Validate and adjust config for consistency.
@@ -4,8 +4,9 @@ export const defaultConfig = {
4
4
  reusableValidators: false, // Off by default for accurate source maps (set to true for production)
5
5
  validateCasts: false,
6
6
  validateFunctions: true,
7
+ transformJSONParse: true,
8
+ transformJSONStringify: true,
7
9
  hoistRegex: true,
8
- ignoreDOMTypes: true,
9
10
  debug: {
10
11
  writeIntermediateFiles: false,
11
12
  },
@@ -15,110 +16,8 @@ export const defaultConfig = {
15
16
  inline: false,
16
17
  },
17
18
  };
18
- // FIXME: find a better way to work out which types to ignore
19
- /**
20
- * DOM types that typia cannot process due to Window global intersections.
21
- * These are the base DOM types - classes extending them are checked separately.
22
- */
23
- export const DOM_TYPES_TO_IGNORE = [
24
- // Core DOM types
25
- 'Document',
26
- 'DocumentFragment',
27
- 'Element',
28
- 'Node',
29
- 'ShadowRoot',
30
- 'Window',
31
- 'EventTarget',
32
- // HTML Elements
33
- 'HTML*Element',
34
- 'HTMLElement',
35
- 'HTMLCollection',
36
- // SVG Elements
37
- 'SVG*Element',
38
- 'SVGElement',
39
- // Events
40
- '*Event',
41
- // Other common DOM types
42
- 'NodeList',
43
- 'DOMTokenList',
44
- 'NamedNodeMap',
45
- 'CSSStyleDeclaration',
46
- 'Selection',
47
- 'Range',
48
- 'Text',
49
- 'Comment',
50
- 'CDATASection',
51
- 'ProcessingInstruction',
52
- 'DocumentType',
53
- 'Attr',
54
- 'Table',
55
- 'TableRow',
56
- 'TableCell',
57
- 'StyleSheet',
58
- ];
59
19
  import fs from 'fs';
60
20
  import path from 'path';
61
- /**
62
- * Convert a glob pattern to a RegExp for type matching.
63
- * Supports wildcards: "React.*" -> /^React\..*$/
64
- */
65
- export function compileIgnorePattern(pattern) {
66
- try {
67
- const regexStr = '^' +
68
- pattern
69
- .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
70
- .replace(/\*/g, '.*') +
71
- '$';
72
- return new RegExp(regexStr);
73
- }
74
- catch (error) {
75
- console.warn(`TYPICAL: Invalid ignoreTypes pattern "${pattern}": ${error.message}`);
76
- return null;
77
- }
78
- }
79
- /**
80
- * Pre-compile all ignore patterns for efficient matching.
81
- */
82
- export function compileIgnorePatterns(config) {
83
- const userPatterns = [];
84
- const domPatterns = [];
85
- // Compile user patterns
86
- for (const pattern of config.ignoreTypes ?? []) {
87
- const compiled = compileIgnorePattern(pattern);
88
- if (compiled) {
89
- userPatterns.push(compiled);
90
- }
91
- }
92
- // Compile DOM patterns if enabled (default: true)
93
- if (config.ignoreDOMTypes !== false) {
94
- for (const pattern of DOM_TYPES_TO_IGNORE) {
95
- const compiled = compileIgnorePattern(pattern);
96
- if (compiled) {
97
- domPatterns.push(compiled);
98
- }
99
- }
100
- }
101
- return {
102
- userPatterns,
103
- domPatterns,
104
- allPatterns: [...userPatterns, ...domPatterns],
105
- };
106
- }
107
- // Cache for compiled patterns, keyed by config identity
108
- let cachedPatterns = null;
109
- let cachedConfig = null;
110
- /**
111
- * Get compiled ignore patterns, using cache if config hasn't changed.
112
- */
113
- export function getCompiledIgnorePatterns(config) {
114
- // Simple identity check - if same config object, use cache
115
- if (cachedConfig === config && cachedPatterns) {
116
- return cachedPatterns;
117
- }
118
- cachedConfig = config;
119
- cachedPatterns = compileIgnorePatterns(config);
120
- return cachedPatterns;
121
- }
122
21
  export function loadConfig(configPath) {
123
22
  const configFile = configPath || path.join(process.cwd(), 'typical.json');
124
23
  if (fs.existsSync(configFile)) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAoEA,MAAM,CAAC,MAAM,aAAa,GAAkB;IAC1C,OAAO,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC;IAChC,OAAO,EAAE,CAAC,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC;IAChE,kBAAkB,EAAE,KAAK,EAAE,uEAAuE;IAClG,aAAa,EAAE,KAAK;IACpB,iBAAiB,EAAE,IAAI;IACvB,kBAAkB,EAAE,IAAI;IACxB,sBAAsB,EAAE,IAAI;IAC5B,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE;QACL,sBAAsB,EAAE,KAAK;KAC9B;IACD,SAAS,EAAE;QACT,OAAO,EAAE,IAAI,EAAE,4DAA4D;QAC3E,cAAc,EAAE,IAAI;QACpB,MAAM,EAAE,KAAK;KACd;CACF,CAAA;AAED,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,MAAM,UAAU,UAAU,CAAC,UAAmB;IAC5C,MAAM,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,CAAA;IAEzE,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YACzD,MAAM,UAAU,GAA2B,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;YAEpE,OAAO;gBACL,GAAG,aAAa;gBAChB,GAAG,UAAU;aACd,CAAA;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,+BAA+B,UAAU,GAAG,EAAE,KAAK,CAAC,CAAA;YACjE,OAAO,aAAa,CAAA;QACtB,CAAC;IACH,CAAC;IAED,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,IAAI,qBAAqB,GAAG,KAAK,CAAA;AAEjC;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,MAAqB;IAClD,IAAI,MAAM,GAAG,MAAM,CAAA;IAEnB,+EAA+E;IAC/E,gFAAgF;IAChF,mFAAmF;IACnF,sDAAsD;IACtD,MAAM,gBAAgB,GAAG,MAAM,CAAC,SAAS,EAAE,OAAO,KAAK,KAAK,CAAA;IAC5D,MAAM,yBAAyB,GAAG,MAAM,CAAC,kBAAkB,KAAK,IAAI,CAAA;IAEpE,IAAI,gBAAgB,IAAI,yBAAyB,EAAE,CAAC;QAClD,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC3B,qBAAqB,GAAG,IAAI,CAAA;YAC5B,OAAO,CAAC,IAAI,CACV,8DAA8D,GAAG,4DAA4D,GAAG,gFAAgF,CACjN,CAAA;QACH,CAAC;QACD,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAA;IACnD,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -45,7 +45,8 @@ export class TypicalTransformer {
45
45
  }
46
46
  await this.ensureInitialized();
47
47
  const resolvedPath = resolve(fileName);
48
- const result = await this.compiler.transformFile(this.projectHandle, resolvedPath);
48
+ // Pass ignoreTypes and maxGeneratedFunctions from config to the Go compiler
49
+ const result = await this.compiler.transformFile(this.projectHandle, resolvedPath, this.config.ignoreTypes, this.config.maxGeneratedFunctions);
49
50
  return {
50
51
  code: result.code,
51
52
  map: result.sourceMap ?? null,
@@ -1 +1 @@
1
- {"version":3,"file":"transformer.js","sourceRoot":"","sources":["../../src/transformer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAC9B,OAAO,EAAE,eAAe,EAAyC,MAAM,2BAA2B,CAAA;AAElG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAOxC,MAAM,OAAO,kBAAkB;IACtB,MAAM,CAAe;IACpB,QAAQ,CAAiB;IACzB,aAAa,GAAyB,IAAI,CAAA;IAC1C,WAAW,GAAyB,IAAI,CAAA;IACxC,UAAU,CAAQ;IAE1B,YAAY,MAAsB,EAAE,aAAqB,eAAe;QACtE,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,UAAU,EAAE,CAAA;QACpC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,iBAAiB;QAC7B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;gBAC7B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;gBAC3B,IAAI,CAAC,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YACvE,CAAC,CAAC,EAAE,CAAA;QACN,CAAC;QACD,MAAM,IAAI,CAAC,WAAW,CAAA;IACxB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,QAAgB,EAAE,OAAoB,IAAI;QACxD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAA;QACpF,CAAC;QAED,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAE9B,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;QACtC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,aAAc,EAAE,YAAY,CAAC,CAAA;QAEnF,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI;SAC9B,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;QACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACvB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;IAC7B,CAAC;CACF"}
1
+ {"version":3,"file":"transformer.js","sourceRoot":"","sources":["../../src/transformer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAC9B,OAAO,EAAE,eAAe,EAAyC,MAAM,2BAA2B,CAAA;AAElG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAOxC,MAAM,OAAO,kBAAkB;IACtB,MAAM,CAAe;IACpB,QAAQ,CAAiB;IACzB,aAAa,GAAyB,IAAI,CAAA;IAC1C,WAAW,GAAyB,IAAI,CAAA;IACxC,UAAU,CAAQ;IAE1B,YAAY,MAAsB,EAAE,aAAqB,eAAe;QACtE,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,UAAU,EAAE,CAAA;QACpC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC7D,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,iBAAiB;QAC7B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;gBAC7B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;gBAC3B,IAAI,CAAC,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YACvE,CAAC,CAAC,EAAE,CAAA;QACN,CAAC;QACD,MAAM,IAAI,CAAC,WAAW,CAAA;IACxB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,CAAC,QAAgB,EAAE,OAAoB,IAAI;QACxD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAA;QACpF,CAAC;QAED,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAE9B,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;QACtC,4EAA4E;QAC5E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,aAAc,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAA;QAE/I,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,MAAM,CAAC,SAAS,IAAI,IAAI;SAC9B,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,aAAa,GAAG,IAAI,CAAA;QACzB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACvB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;IAC7B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliots/typical",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.1",
4
4
  "description": "Runtime safe TypeScript transformer using typia",
5
5
  "keywords": [
6
6
  "runtime",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "commander": "14.0.2",
43
- "@elliots/typical-compiler": "0.2.0-beta.1"
43
+ "@elliots/typical-compiler": "0.2.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "22",
package/src/config.ts CHANGED
@@ -33,36 +33,37 @@ export interface TypicalConfig {
33
33
  * Example: ["React.*", "Express.Request", "*.Event"]
34
34
  */
35
35
  ignoreTypes?: string[]
36
- /**
37
- * Skip validation for DOM types (Document, Element, Node, etc.) and their subclasses.
38
- * These types have complex Window intersections that typia cannot process.
39
- * Default: true
40
- */
41
- ignoreDOMTypes?: boolean
42
36
  /**
43
37
  * Validate function parameters and return types at runtime.
44
38
  * When enabled, typed function parameters get runtime validation calls injected.
45
39
  * Default: true
46
40
  */
47
41
  validateFunctions?: boolean
42
+ /**
43
+ * Transform JSON.parse<T>() calls to validate and filter the parsed result
44
+ * to only include properties defined in type T.
45
+ * Default: true
46
+ */
47
+ transformJSONParse?: boolean
48
+ /**
49
+ * Transform JSON.stringify<T>() calls to only stringify properties defined
50
+ * in type T, preventing accidental data leaks.
51
+ * Default: true
52
+ */
53
+ transformJSONStringify?: boolean
48
54
  /**
49
55
  * Source map generation settings.
50
56
  * Controls whether and how source maps are generated for transformed code.
51
57
  */
52
58
  sourceMap?: TypicalSourceMapConfig
53
- }
54
-
55
- /**
56
- * Pre-compiled regex patterns for ignore type matching.
57
- * This is populated during config loading for performance.
58
- */
59
- export interface CompiledIgnorePatterns {
60
- /** Compiled patterns from user ignoreTypes config */
61
- userPatterns: RegExp[]
62
- /** Compiled patterns from DOM_TYPES_TO_IGNORE (when ignoreDOMTypes is true) */
63
- domPatterns: RegExp[]
64
- /** All patterns combined for quick checking */
65
- allPatterns: RegExp[]
59
+ /**
60
+ * Maximum number of helper functions (_io0, _io1, etc.) that can be generated
61
+ * for a single type before erroring. Complex DOM types or library types can
62
+ * generate hundreds of functions which indicates a type that should be excluded.
63
+ * Set to 0 to disable the limit.
64
+ * Default: 50
65
+ */
66
+ maxGeneratedFunctions?: number
66
67
  }
67
68
 
68
69
  export const defaultConfig: TypicalConfig = {
@@ -71,8 +72,9 @@ export const defaultConfig: TypicalConfig = {
71
72
  reusableValidators: false, // Off by default for accurate source maps (set to true for production)
72
73
  validateCasts: false,
73
74
  validateFunctions: true,
75
+ transformJSONParse: true,
76
+ transformJSONStringify: true,
74
77
  hoistRegex: true,
75
- ignoreDOMTypes: true,
76
78
  debug: {
77
79
  writeIntermediateFiles: false,
78
80
  },
@@ -83,120 +85,9 @@ export const defaultConfig: TypicalConfig = {
83
85
  },
84
86
  }
85
87
 
86
- // FIXME: find a better way to work out which types to ignore
87
- /**
88
- * DOM types that typia cannot process due to Window global intersections.
89
- * These are the base DOM types - classes extending them are checked separately.
90
- */
91
- export const DOM_TYPES_TO_IGNORE = [
92
- // Core DOM types
93
- 'Document',
94
- 'DocumentFragment',
95
- 'Element',
96
- 'Node',
97
- 'ShadowRoot',
98
- 'Window',
99
- 'EventTarget',
100
- // HTML Elements
101
- 'HTML*Element',
102
- 'HTMLElement',
103
- 'HTMLCollection',
104
- // SVG Elements
105
- 'SVG*Element',
106
- 'SVGElement',
107
- // Events
108
- '*Event',
109
- // Other common DOM types
110
- 'NodeList',
111
- 'DOMTokenList',
112
- 'NamedNodeMap',
113
- 'CSSStyleDeclaration',
114
- 'Selection',
115
- 'Range',
116
- 'Text',
117
- 'Comment',
118
- 'CDATASection',
119
- 'ProcessingInstruction',
120
- 'DocumentType',
121
- 'Attr',
122
- 'Table',
123
- 'TableRow',
124
- 'TableCell',
125
- 'StyleSheet',
126
- ]
127
-
128
88
  import fs from 'fs'
129
89
  import path from 'path'
130
90
 
131
- /**
132
- * Convert a glob pattern to a RegExp for type matching.
133
- * Supports wildcards: "React.*" -> /^React\..*$/
134
- */
135
- export function compileIgnorePattern(pattern: string): RegExp | null {
136
- try {
137
- const regexStr =
138
- '^' +
139
- pattern
140
- .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *
141
- .replace(/\*/g, '.*') +
142
- '$'
143
- return new RegExp(regexStr)
144
- } catch (error) {
145
- console.warn(`TYPICAL: Invalid ignoreTypes pattern "${pattern}": ${(error as Error).message}`)
146
- return null
147
- }
148
- }
149
-
150
- /**
151
- * Pre-compile all ignore patterns for efficient matching.
152
- */
153
- export function compileIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns {
154
- const userPatterns: RegExp[] = []
155
- const domPatterns: RegExp[] = []
156
-
157
- // Compile user patterns
158
- for (const pattern of config.ignoreTypes ?? []) {
159
- const compiled = compileIgnorePattern(pattern)
160
- if (compiled) {
161
- userPatterns.push(compiled)
162
- }
163
- }
164
-
165
- // Compile DOM patterns if enabled (default: true)
166
- if (config.ignoreDOMTypes !== false) {
167
- for (const pattern of DOM_TYPES_TO_IGNORE) {
168
- const compiled = compileIgnorePattern(pattern)
169
- if (compiled) {
170
- domPatterns.push(compiled)
171
- }
172
- }
173
- }
174
-
175
- return {
176
- userPatterns,
177
- domPatterns,
178
- allPatterns: [...userPatterns, ...domPatterns],
179
- }
180
- }
181
-
182
- // Cache for compiled patterns, keyed by config identity
183
- let cachedPatterns: CompiledIgnorePatterns | null = null
184
- let cachedConfig: TypicalConfig | null = null
185
-
186
- /**
187
- * Get compiled ignore patterns, using cache if config hasn't changed.
188
- */
189
- export function getCompiledIgnorePatterns(config: TypicalConfig): CompiledIgnorePatterns {
190
- // Simple identity check - if same config object, use cache
191
- if (cachedConfig === config && cachedPatterns) {
192
- return cachedPatterns
193
- }
194
-
195
- cachedConfig = config
196
- cachedPatterns = compileIgnorePatterns(config)
197
- return cachedPatterns
198
- }
199
-
200
91
  export function loadConfig(configPath?: string): TypicalConfig {
201
92
  const configFile = configPath || path.join(process.cwd(), 'typical.json')
202
93
 
@@ -58,7 +58,8 @@ export class TypicalTransformer {
58
58
  await this.ensureInitialized()
59
59
 
60
60
  const resolvedPath = resolve(fileName)
61
- const result = await this.compiler.transformFile(this.projectHandle!, resolvedPath)
61
+ // Pass ignoreTypes and maxGeneratedFunctions from config to the Go compiler
62
+ const result = await this.compiler.transformFile(this.projectHandle!, resolvedPath, this.config.ignoreTypes, this.config.maxGeneratedFunctions)
62
63
 
63
64
  return {
64
65
  code: result.code,