runtypex 0.1.13 → 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.
Files changed (72) hide show
  1. package/README.md +112 -278
  2. package/dist/cjs/core/emitArrayOrTuple.d.ts +6 -0
  3. package/dist/cjs/core/emitArrayOrTuple.js +40 -0
  4. package/dist/cjs/core/emitLiteralOrEnum.d.ts +4 -0
  5. package/dist/cjs/core/emitLiteralOrEnum.js +47 -0
  6. package/dist/cjs/core/emitMapperFromSpec.d.ts +33 -0
  7. package/dist/cjs/core/emitMapperFromSpec.js +199 -0
  8. package/dist/cjs/core/emitObject.d.ts +6 -0
  9. package/dist/cjs/core/emitObject.js +30 -0
  10. package/dist/cjs/core/emitPrimitive.d.ts +6 -0
  11. package/dist/cjs/core/emitPrimitive.js +29 -0
  12. package/dist/cjs/core/emitUnionOrIntersection.d.ts +6 -0
  13. package/dist/cjs/core/emitUnionOrIntersection.js +15 -0
  14. package/dist/cjs/core/index.d.ts +17 -0
  15. package/dist/cjs/core/index.js +41 -0
  16. package/dist/cjs/core/path.d.ts +9 -0
  17. package/dist/cjs/core/path.js +42 -0
  18. package/dist/cjs/generator/generate-jsdoc.d.ts +13 -0
  19. package/dist/cjs/generator/generate-jsdoc.js +103 -0
  20. package/dist/cjs/generator/index.d.ts +1 -0
  21. package/dist/cjs/generator/index.js +17 -0
  22. package/dist/cjs/index.d.ts +4 -0
  23. package/dist/cjs/index.js +20 -0
  24. package/dist/cjs/mapper/index.d.ts +1 -0
  25. package/dist/cjs/mapper/index.js +17 -0
  26. package/dist/cjs/runtime/index.d.ts +2 -0
  27. package/dist/cjs/runtime/index.js +18 -0
  28. package/dist/cjs/runtime/mapper.d.ts +72 -0
  29. package/dist/cjs/runtime/mapper.js +79 -0
  30. package/dist/cjs/runtime/validate.d.ts +11 -0
  31. package/dist/cjs/runtime/validate.js +18 -0
  32. package/dist/cjs/transformer/helper.d.ts +2 -0
  33. package/dist/cjs/transformer/helper.js +63 -0
  34. package/dist/cjs/transformer/index.d.ts +3 -0
  35. package/dist/cjs/transformer/index.js +12 -0
  36. package/dist/cjs/transformer/ts-transformer.d.ts +29 -0
  37. package/dist/cjs/transformer/ts-transformer.js +109 -0
  38. package/dist/cjs/transformer/vite-plugin.d.ts +18 -0
  39. package/dist/cjs/transformer/vite-plugin.js +72 -0
  40. package/dist/esm/core/emitArrayOrTuple.d.ts +1 -1
  41. package/dist/esm/core/emitLiteralOrEnum.d.ts +1 -1
  42. package/dist/esm/core/emitMapperFromSpec.d.ts +33 -0
  43. package/dist/esm/core/emitMapperFromSpec.js +189 -0
  44. package/dist/esm/core/emitObject.d.ts +1 -1
  45. package/dist/esm/core/emitObject.js +4 -2
  46. package/dist/esm/core/emitPrimitive.d.ts +1 -1
  47. package/dist/esm/core/emitUnionOrIntersection.d.ts +1 -1
  48. package/dist/esm/core/path.d.ts +9 -0
  49. package/dist/esm/core/path.js +36 -0
  50. package/dist/esm/generator/generate-jsdoc.d.ts +13 -0
  51. package/dist/esm/generator/generate-jsdoc.js +97 -0
  52. package/dist/esm/generator/index.d.ts +1 -0
  53. package/dist/esm/generator/index.js +1 -0
  54. package/dist/esm/index.d.ts +4 -3
  55. package/dist/esm/index.js +1 -0
  56. package/dist/esm/mapper/index.d.ts +1 -0
  57. package/dist/esm/mapper/index.js +1 -0
  58. package/dist/esm/runtime/index.d.ts +2 -0
  59. package/dist/esm/runtime/index.js +2 -0
  60. package/dist/esm/runtime/mapper.d.ts +72 -0
  61. package/dist/esm/runtime/mapper.js +71 -0
  62. package/dist/esm/transformer/index.d.ts +3 -0
  63. package/dist/esm/transformer/index.js +3 -0
  64. package/dist/esm/transformer/ts-transformer.d.ts +8 -4
  65. package/dist/esm/transformer/ts-transformer.js +51 -10
  66. package/dist/esm/transformer/vite-plugin.js +7 -32
  67. package/docs/build-integrations.md +89 -0
  68. package/docs/jsdoc-generation.md +105 -0
  69. package/docs/mapper.md +104 -0
  70. package/docs/mapping-policy.md +78 -0
  71. package/docs/runtime-validation.md +84 -0
  72. package/package.json +76 -36
@@ -0,0 +1,72 @@
1
+ type Primitive = string | number | boolean | bigint | symbol | null | undefined | Date;
2
+ export type PathOf<T> = T extends Primitive ? never : T extends readonly (infer U)[] ? `${number}` | `${number}.${PathOf<U>}` : {
3
+ [K in Extract<keyof T, string>]: T[K] extends Primitive ? K : T[K] extends readonly (infer U)[] ? K | `${K}.${number}` | `${K}.${number}.${PathOf<U>}` : K | `${K}.${PathOf<T[K]>}`;
4
+ }[Extract<keyof T, string>];
5
+ export type Mapper<TDto, TDomain> = (dto: TDto) => TDomain;
6
+ export type MapperMetadata<TValue = never> = {
7
+ db?: string;
8
+ /** @deprecated Prefer domain property JSDoc for domain field descriptions. */
9
+ description?: string;
10
+ dtoDescription?: string;
11
+ default?: TValue;
12
+ };
13
+ export type MapRule<TDto, TValue> = MapperMetadata<TValue> & {
14
+ from: PathOf<TDto>;
15
+ transform?: (value: unknown, dto: TDto) => TValue;
16
+ };
17
+ export type MapSpec<TDto, TDomain> = {
18
+ [K in keyof TDomain]-?: MapRule<TDto, TDomain[K]>;
19
+ };
20
+ export type MappingPolicy<TDto> = Record<string, MapRule<TDto, unknown>>;
21
+ export type MappingPolicyMode = "warn" | "error";
22
+ export type MapperOptions<TDto> = {
23
+ policy?: MappingPolicy<TDto>;
24
+ policyMode?: MappingPolicyMode;
25
+ };
26
+ declare const DTO_TYPE: unique symbol;
27
+ declare const DOMAIN_TYPE: unique symbol;
28
+ export type DefinedMap<TDto, TDomain> = MapSpec<TDto, TDomain> & {
29
+ readonly [DTO_TYPE]?: TDto;
30
+ readonly [DOMAIN_TYPE]?: TDomain;
31
+ };
32
+ export declare function defineMap<TDto, TDomain>(): <const TSpec extends MapSpec<TDto, TDomain>>(spec: TSpec) => TSpec & DefinedMap<TDto, TDomain>;
33
+ /** Declares canonical DTO path -> Domain field names for consistency checks. */
34
+ export declare function defineMappingPolicy<TDto>(): <const TSpec extends MappingPolicy<TDto>>(spec: TSpec) => TSpec;
35
+ /** Shorthand rule for direct DTO path reads. */
36
+ export declare function source<const TPath extends string, TValue = never>(from: TPath, metadata?: MapperMetadata<TValue>): {
37
+ db?: string;
38
+ description?: string;
39
+ dtoDescription?: string;
40
+ default?: TValue | undefined;
41
+ from: TPath;
42
+ };
43
+ /** Shorthand rule for DTO path reads that require a value conversion. */
44
+ export declare function transform<const TPath extends string, TValue>(from: TPath, transform: (value: unknown, dto: unknown) => TValue, metadata?: MapperMetadata<TValue>): {
45
+ db?: string;
46
+ description?: string;
47
+ dtoDescription?: string;
48
+ default?: TValue | undefined;
49
+ from: TPath;
50
+ transform: (value: unknown, dto: unknown) => TValue;
51
+ };
52
+ /** Typed helpers for callbacks that need access to the source DTO shape. */
53
+ export declare function mapperHelpers<TDto>(): {
54
+ source: <const TPath extends PathOf<TDto>, TValue = never>(from: TPath, metadata?: MapperMetadata<TValue>) => {
55
+ db?: string;
56
+ description?: string;
57
+ dtoDescription?: string;
58
+ default?: TValue | undefined;
59
+ from: TPath;
60
+ };
61
+ transform: <const TPath extends PathOf<TDto>, TValue>(from: TPath, transform: (value: unknown, dto: TDto) => TValue, metadata?: MapperMetadata<TValue>) => {
62
+ db?: string;
63
+ description?: string;
64
+ dtoDescription?: string;
65
+ default?: TValue | undefined;
66
+ from: TPath;
67
+ transform: (value: unknown, dto: TDto) => TValue;
68
+ };
69
+ };
70
+ /** Runtime interpreter used as fallback when the transformer is not configured. */
71
+ export declare function makeMapper<TDto, TDomain>(spec: DefinedMap<TDto, TDomain> | MapSpec<TDto, TDomain>, options?: MapperOptions<TDto>): Mapper<TDto, TDomain>;
72
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineMap = defineMap;
4
+ exports.defineMappingPolicy = defineMappingPolicy;
5
+ exports.source = source;
6
+ exports.transform = transform;
7
+ exports.mapperHelpers = mapperHelpers;
8
+ exports.makeMapper = makeMapper;
9
+ const path_js_1 = require("../core/path.js");
10
+ function defineMap() {
11
+ return (spec) => spec;
12
+ }
13
+ /** Declares canonical DTO path -> Domain field names for consistency checks. */
14
+ function defineMappingPolicy() {
15
+ return (spec) => spec;
16
+ }
17
+ /** Shorthand rule for direct DTO path reads. */
18
+ function source(from, metadata) {
19
+ return { from, ...metadata };
20
+ }
21
+ /** Shorthand rule for DTO path reads that require a value conversion. */
22
+ function transform(from, transform, metadata) {
23
+ return { from, transform, ...metadata };
24
+ }
25
+ /** Typed helpers for callbacks that need access to the source DTO shape. */
26
+ function mapperHelpers() {
27
+ return {
28
+ source: (from, metadata) => source(from, metadata),
29
+ transform: (from, transform, metadata) => ({ from, transform, ...metadata }),
30
+ };
31
+ }
32
+ /** Runtime interpreter used as fallback when the transformer is not configured. */
33
+ function makeMapper(spec, options) {
34
+ _handlePolicyViolations(_findPolicyViolations(spec, options?.policy), options?.policyMode ?? "warn");
35
+ return ((dto) => {
36
+ const output = {};
37
+ for (const key of Object.keys(spec)) {
38
+ const rule = spec[key];
39
+ const raw = (0, path_js_1.getByPath)(dto, String(rule.from));
40
+ const value = raw === undefined && _hasOwn(rule, "default") ? rule.default : raw;
41
+ output[key] = rule.transform ? rule.transform(value, dto) : value;
42
+ }
43
+ return output;
44
+ });
45
+ }
46
+ function _hasOwn(value, key) {
47
+ return Object.prototype.hasOwnProperty.call(value, key);
48
+ }
49
+ function _findPolicyViolations(spec, policy) {
50
+ if (!policy)
51
+ return [];
52
+ const canonicalByPath = new Map();
53
+ const violations = [];
54
+ for (const key of Object.keys(policy)) {
55
+ const from = String(policy[key].from);
56
+ const existing = canonicalByPath.get(from);
57
+ if (existing && existing !== key) {
58
+ violations.push(`DTO path "${from}" is canonically mapped as "${existing}", but this map uses "${key}".`);
59
+ continue;
60
+ }
61
+ canonicalByPath.set(from, key);
62
+ }
63
+ violations.push(...Object.keys(spec).flatMap((key) => {
64
+ const from = String(spec[key].from);
65
+ const expected = canonicalByPath.get(from);
66
+ return expected && expected !== key
67
+ ? [`DTO path "${from}" is canonically mapped as "${expected}", but this map uses "${key}".`]
68
+ : [];
69
+ }));
70
+ return violations;
71
+ }
72
+ function _handlePolicyViolations(violations, mode) {
73
+ if (!violations.length)
74
+ return;
75
+ const message = `[runtypex/mapper] Mapping policy violation:\n${violations.join("\n")}`;
76
+ if (mode === "error")
77
+ throw new Error(message);
78
+ console.warn(message);
79
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * runtypex — runtime APIs
3
+ * 1) makeValidate<T>() : (v) => v is T
4
+ * 2) makeAssert<T>() : throws on invalid
5
+ *
6
+ * NOTE: These factories are replaced at build-time by the transformer.
7
+ */
8
+ export type ValidateFn<T> = (value: unknown) => value is T;
9
+ export type AssertFn<T> = (value: unknown) => asserts value is T;
10
+ export declare function makeValidate<T>(): ValidateFn<T>;
11
+ export declare function makeAssert<T>(): AssertFn<T>;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeValidate = makeValidate;
4
+ exports.makeAssert = makeAssert;
5
+ // Replaced by transformer to generated guard
6
+ function __validate(_value) {
7
+ return true;
8
+ }
9
+ function makeValidate() {
10
+ return (value) => __validate(value);
11
+ }
12
+ function makeAssert() {
13
+ const validate = makeValidate();
14
+ return (value) => {
15
+ if (!validate(value))
16
+ throw new TypeError("[runtypex] Validation failed.");
17
+ };
18
+ }
@@ -0,0 +1,2 @@
1
+ import ts from "typescript";
2
+ export declare function resolveTypeByName(program: ts.Program, sf: ts.SourceFile, checker: ts.TypeChecker, name: string): ts.Type | null;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveTypeByName = resolveTypeByName;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ function resolveTypeByName(program, sf, checker, name) {
9
+ // -1️⃣ Primitive type fallback
10
+ const primitiveNames = ["string", "number", "boolean", "bigint", "symbol", "null", "undefined"];
11
+ if (primitiveNames.includes(name)) {
12
+ const map = {
13
+ string: checker.getStringType(),
14
+ number: checker.getNumberType(),
15
+ boolean: checker.getBooleanType(),
16
+ bigint: checker.getBigIntType(),
17
+ symbol: checker.getESSymbolType(),
18
+ null: checker.getNullType(),
19
+ undefined: checker.getUndefinedType(),
20
+ };
21
+ return map[name];
22
+ }
23
+ // 2️⃣ Scan source files
24
+ for (const file of program.getSourceFiles()) {
25
+ const decl = _findLocalDeclaration(file, name);
26
+ if (!decl)
27
+ continue;
28
+ // ✅ type, interface, enum, class
29
+ if (typescript_1.default.isInterfaceDeclaration(decl) ||
30
+ typescript_1.default.isClassDeclaration(decl) ||
31
+ typescript_1.default.isEnumDeclaration(decl)) {
32
+ if (decl.name) {
33
+ const symbol = checker.getSymbolAtLocation(decl.name);
34
+ if (symbol)
35
+ return checker.getDeclaredTypeOfSymbol(symbol);
36
+ }
37
+ }
38
+ if (typescript_1.default.isTypeAliasDeclaration(decl)) {
39
+ return checker.getTypeFromTypeNode(decl.type);
40
+ }
41
+ }
42
+ // 3️⃣ Scope-based fallback
43
+ const symbol = checker
44
+ .getSymbolsInScope(sf, typescript_1.default.SymbolFlags.Type | typescript_1.default.SymbolFlags.Alias | typescript_1.default.SymbolFlags.Interface)
45
+ .find((s) => s.name === name);
46
+ return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : null;
47
+ }
48
+ function _findLocalDeclaration(sf, name) {
49
+ let found;
50
+ (function walk(node) {
51
+ if ((typescript_1.default.isInterfaceDeclaration(node) ||
52
+ typescript_1.default.isTypeAliasDeclaration(node) ||
53
+ typescript_1.default.isEnumDeclaration(node) ||
54
+ typescript_1.default.isClassDeclaration(node)) &&
55
+ node.name?.text === name) {
56
+ found = node;
57
+ return;
58
+ }
59
+ if (!found)
60
+ node.forEachChild(walk);
61
+ })(sf);
62
+ return found;
63
+ }
@@ -0,0 +1,3 @@
1
+ export { default } from "./ts-transformer.js";
2
+ export { default as tsTransformer } from "./ts-transformer.js";
3
+ export { default as vitePlugin } from "./vite-plugin.js";
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.vitePlugin = exports.tsTransformer = exports.default = void 0;
7
+ var ts_transformer_js_1 = require("./ts-transformer.js");
8
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(ts_transformer_js_1).default; } });
9
+ var ts_transformer_js_2 = require("./ts-transformer.js");
10
+ Object.defineProperty(exports, "tsTransformer", { enumerable: true, get: function () { return __importDefault(ts_transformer_js_2).default; } });
11
+ var vite_plugin_js_1 = require("./vite-plugin.js");
12
+ Object.defineProperty(exports, "vitePlugin", { enumerable: true, get: function () { return __importDefault(vite_plugin_js_1).default; } });
@@ -0,0 +1,29 @@
1
+ import ts from "typescript";
2
+ type TransformerOptions = {
3
+ program: ts.Program;
4
+ removeInProd?: boolean;
5
+ validateDto?: boolean;
6
+ validateDomain?: boolean;
7
+ };
8
+ /**
9
+ * 🧩 tsTransformer
10
+ * TypeScript custom transformer (BEFORE) factory.
11
+ *
12
+ * 📘 Usage (ts-loader / ttypescript):
13
+ * ```ts
14
+ * getCustomTransformers: (program) => ({
15
+ * before: [ tsTransformer({ program, removeInProd: true }) ]
16
+ * })
17
+ * ```
18
+ *
19
+ * 🧠 Purpose:
20
+ * - Replace makeValidate<T>(), makeAssert<T>() calls
21
+ * with *pre-generated runtime validation code* derived from T.
22
+ *
23
+ * 💡 Effect:
24
+ * ✅ No reflection or runtime type parsing
25
+ * ✅ Validation logic embedded at build-time
26
+ * ✅ Optionally removed in production builds
27
+ */
28
+ export default function tsTransformer(options: TransformerOptions): ts.TransformerFactory<ts.SourceFile>;
29
+ export {};
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = tsTransformer;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const index_js_1 = require("../core/index.js");
9
+ const emitMapperFromSpec_js_1 = require("../core/emitMapperFromSpec.js");
10
+ /**
11
+ * 🧩 tsTransformer
12
+ * TypeScript custom transformer (BEFORE) factory.
13
+ *
14
+ * 📘 Usage (ts-loader / ttypescript):
15
+ * ```ts
16
+ * getCustomTransformers: (program) => ({
17
+ * before: [ tsTransformer({ program, removeInProd: true }) ]
18
+ * })
19
+ * ```
20
+ *
21
+ * 🧠 Purpose:
22
+ * - Replace makeValidate<T>(), makeAssert<T>() calls
23
+ * with *pre-generated runtime validation code* derived from T.
24
+ *
25
+ * 💡 Effect:
26
+ * ✅ No reflection or runtime type parsing
27
+ * ✅ Validation logic embedded at build-time
28
+ * ✅ Optionally removed in production builds
29
+ */
30
+ function tsTransformer(options) {
31
+ const checker = options.program.getTypeChecker();
32
+ const removeInProd = !!options.removeInProd;
33
+ const prod = process.env.NODE_ENV === "production";
34
+ return (context) => {
35
+ const visit = (node) => {
36
+ if (typescript_1.default.isCallExpression(node) && typescript_1.default.isIdentifier(node.expression)) {
37
+ const name = node.expression.text;
38
+ if ((name === "makeValidate" || name === "makeAssert") && node.typeArguments?.length) {
39
+ const type = checker.getTypeFromTypeNode(node.typeArguments[0]);
40
+ const isRemovedInProd = removeInProd && prod;
41
+ switch (name) {
42
+ case "makeValidate":
43
+ return _emitMakeValidate(checker, type, isRemovedInProd);
44
+ case "makeAssert":
45
+ return _emitMakeAssert(checker, type, isRemovedInProd);
46
+ }
47
+ }
48
+ // makeMapper<TDto, TDomain>(spec) becomes an inline validating mapper.
49
+ if (name === "makeMapper" && node.typeArguments?.length === 2 && node.arguments[0]) {
50
+ const mapperCallOptions = _readMapperCallOptions(node.arguments[1]);
51
+ const mapper = (0, emitMapperFromSpec_js_1.emitMapperFromSpec)({
52
+ checker,
53
+ dtoType: checker.getTypeFromTypeNode(node.typeArguments[0]),
54
+ domainType: checker.getTypeFromTypeNode(node.typeArguments[1]),
55
+ specNode: node.arguments[0],
56
+ sourceFile: node.getSourceFile(),
57
+ options: {
58
+ validateDto: !(removeInProd && prod) && options.validateDto !== false,
59
+ validateDomain: !(removeInProd && prod) && options.validateDomain !== false,
60
+ mappingPolicy: mapperCallOptions.policy,
61
+ policyMode: mapperCallOptions.policyMode,
62
+ },
63
+ });
64
+ if (mapper)
65
+ return typescript_1.default.factory.createIdentifier(mapper);
66
+ }
67
+ }
68
+ return typescript_1.default.visitEachChild(node, visit, context);
69
+ };
70
+ return (sf) => typescript_1.default.visitNode(sf, visit);
71
+ };
72
+ }
73
+ function _readMapperCallOptions(node) {
74
+ if (!node)
75
+ return {};
76
+ const expr = typescript_1.default.isAsExpression(node) || typescript_1.default.isParenthesizedExpression(node) ? node.expression : node;
77
+ if (!typescript_1.default.isObjectLiteralExpression(expr))
78
+ return {};
79
+ return {
80
+ policy: _readExpressionProperty(expr, "policy"),
81
+ policyMode: _readPolicyMode(expr),
82
+ };
83
+ }
84
+ function _readExpressionProperty(object, name) {
85
+ for (const item of object.properties) {
86
+ if (typescript_1.default.isPropertyAssignment(item) && typescript_1.default.isIdentifier(item.name) && item.name.text === name) {
87
+ return item.initializer;
88
+ }
89
+ if (typescript_1.default.isShorthandPropertyAssignment(item) && item.name.text === name) {
90
+ return item.name;
91
+ }
92
+ }
93
+ return undefined;
94
+ }
95
+ function _readPolicyMode(object) {
96
+ const mode = _readExpressionProperty(object, "policyMode");
97
+ return mode && typescript_1.default.isStringLiteral(mode) && (mode.text === "warn" || mode.text === "error") ? mode.text : undefined;
98
+ }
99
+ function _emitMakeValidate(checker, type, isRemovedInProd) {
100
+ const guard = isRemovedInProd ? "((_)=>true)" : (0, index_js_1.emitGuardFromType)(checker, type);
101
+ return typescript_1.default.factory.createIdentifier(guard);
102
+ }
103
+ function _emitMakeAssert(checker, type, isRemovedInProd) {
104
+ if (isRemovedInProd)
105
+ return typescript_1.default.factory.createIdentifier("((_)=>{})");
106
+ const guard = (0, index_js_1.emitGuardFromType)(checker, type);
107
+ const txt = `(function(){const G=${guard};return(i)=>{if(!G(i))throw new TypeError("[runtypex] Validation failed.");};})()`;
108
+ return typescript_1.default.factory.createIdentifier(txt);
109
+ }
@@ -0,0 +1,18 @@
1
+ import type { Plugin } from "vite";
2
+ /**
3
+ * 🧩 vitePluginRuntypex
4
+ * A Vite plugin that performs build-time type → runtime validation transformation.
5
+ *
6
+ * 📘 Purpose
7
+ * - Replace calls like:
8
+ * makeValidate<T>(), makeAssert<T>()
9
+ * with *inline JavaScript guard functions* derived from TypeScript types.
10
+ *
11
+ * 💡 Features
12
+ * - Works in both dev & build mode
13
+ * - Optional: remove validation code in production (`removeInProd`)
14
+ * - Compatible with Rollup / Webpack (via Vite plugin API)
15
+ */
16
+ export default function vitePluginRuntypex(options?: {
17
+ removeInProd?: boolean;
18
+ }): Plugin;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = vitePluginRuntypex;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const ts_transformer_js_1 = __importDefault(require("./ts-transformer.js"));
10
+ /**
11
+ * 🧩 vitePluginRuntypex
12
+ * A Vite plugin that performs build-time type → runtime validation transformation.
13
+ *
14
+ * 📘 Purpose
15
+ * - Replace calls like:
16
+ * makeValidate<T>(), makeAssert<T>()
17
+ * with *inline JavaScript guard functions* derived from TypeScript types.
18
+ *
19
+ * 💡 Features
20
+ * - Works in both dev & build mode
21
+ * - Optional: remove validation code in production (`removeInProd`)
22
+ * - Compatible with Rollup / Webpack (via Vite plugin API)
23
+ */
24
+ function vitePluginRuntypex(options) {
25
+ const removeInProd = !!options?.removeInProd;
26
+ return {
27
+ name: "vite-plugin-runtypex",
28
+ enforce: "pre",
29
+ transform(code, id) {
30
+ const isTS = id.endsWith(".ts") || id.endsWith(".tsx");
31
+ const isTargetFunction = /make(?:Validate|Assert|Mapper)</.test(code);
32
+ if (!isTS || !isTargetFunction)
33
+ return;
34
+ const { program } = _createProgramFor(id);
35
+ const sf = program.getSourceFile(id);
36
+ if (!sf)
37
+ return;
38
+ const result = typescript_1.default.transform(sf, [(0, ts_transformer_js_1.default)({ program, removeInProd })]);
39
+ const mutated = typescript_1.default.createPrinter().printFile(result.transformed[0]);
40
+ result.dispose();
41
+ return mutated === code ? null : { code: mutated, map: null };
42
+ },
43
+ };
44
+ }
45
+ // ──────────────────────────────────────────────
46
+ // ① createProgram & TypeChecker
47
+ // ──────────────────────────────────────────────
48
+ function _createProgramFor(file) {
49
+ const tsconfig = _findNearestTsconfig(node_path_1.default.dirname(file));
50
+ const cfg = typescript_1.default.readConfigFile(tsconfig, typescript_1.default.sys.readFile);
51
+ if (cfg.error)
52
+ throw new Error(typescript_1.default.flattenDiagnosticMessageText(cfg.error.messageText, "\n"));
53
+ const parsed = typescript_1.default.parseJsonConfigFileContent(cfg.config, typescript_1.default.sys, node_path_1.default.dirname(tsconfig));
54
+ const program = typescript_1.default.createProgram({ rootNames: parsed.fileNames, options: parsed.options });
55
+ return { program };
56
+ }
57
+ function _findNearestTsconfig(start) {
58
+ let dir = start;
59
+ while (true) {
60
+ const candidate = node_path_1.default.join(dir, "tsconfig.json");
61
+ if (typescript_1.default.sys.fileExists(candidate))
62
+ return candidate;
63
+ const parent = node_path_1.default.dirname(dir);
64
+ if (parent === dir)
65
+ break;
66
+ dir = parent;
67
+ }
68
+ const fallback = node_path_1.default.join(process.cwd(), "tsconfig.json");
69
+ if (typescript_1.default.sys.fileExists(fallback))
70
+ return fallback;
71
+ throw new Error("tsconfig.json not found");
72
+ }
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- import type { GenContext } from "./index";
2
+ import type { GenContext } from "./index.js";
3
3
  /**
4
4
  * Handles array (T[]) and tuple ([A, B]) types.
5
5
  */
@@ -1,4 +1,4 @@
1
1
  import ts from "typescript";
2
- import type { GenContext } from "./index";
2
+ import type { GenContext } from "./index.js";
3
3
  /** Handles literal types and enum-like types. */
4
4
  export declare function emitLiteralOrEnum(_: GenContext, expr: string, t: ts.Type): string | null;
@@ -0,0 +1,33 @@
1
+ import ts from "typescript";
2
+ export type MapperEmitOptions = {
3
+ validateDto?: boolean;
4
+ validateDomain?: boolean;
5
+ mappingPolicy?: ts.Expression;
6
+ policyMode?: "warn" | "error";
7
+ };
8
+ export type MapRuleInfo = {
9
+ key: string;
10
+ from: string;
11
+ db?: string;
12
+ /** @deprecated Prefer domain property JSDoc for domain field descriptions. */
13
+ description?: string;
14
+ dtoDescription?: string;
15
+ };
16
+ export declare function emitMapperFromSpec(params: {
17
+ checker: ts.TypeChecker;
18
+ dtoType: ts.Type;
19
+ domainType: ts.Type;
20
+ specNode: ts.Expression;
21
+ sourceFile: ts.SourceFile;
22
+ options?: MapperEmitOptions;
23
+ }): string | null;
24
+ export declare function readMapRules(checker: ts.TypeChecker, specNode: ts.Expression): Map<string, MapRuleInfo>;
25
+ export type MapPolicyViolation = {
26
+ from: string;
27
+ expectedKey: string;
28
+ actualKey: string;
29
+ };
30
+ export declare function findMapPolicyViolations(checker: ts.TypeChecker, specNode: ts.Expression, policyNode: ts.Expression | undefined): MapPolicyViolation[];
31
+ export declare function handleMapPolicyViolations(violations: MapPolicyViolation[], mode: "warn" | "error"): void;
32
+ /** Finds the mapping object behind inline, defineMap-wrapped, or identifier specs. */
33
+ export declare function resolveMapSpecObject(checker: ts.TypeChecker, node: ts.Expression): ts.ObjectLiteralExpression | null;