@ts-runtypes/devtools 0.9.3 → 0.10.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 (52) hide show
  1. package/dist/diagnosticCatalog.d.ts +2 -2
  2. package/dist/diagnosticCatalog.d.ts.map +1 -1
  3. package/dist/diagnosticCatalog.js +2 -2
  4. package/dist/diagnosticCatalog.js.map +1 -1
  5. package/dist/eslint/diagnosticRouting.d.ts +8 -1
  6. package/dist/eslint/diagnosticRouting.d.ts.map +1 -1
  7. package/dist/eslint/diagnosticRouting.js +210 -35
  8. package/dist/eslint/diagnosticRouting.js.map +1 -1
  9. package/dist/eslint/index.d.ts +2 -0
  10. package/dist/eslint/index.d.ts.map +1 -1
  11. package/dist/eslint/index.js +7 -26
  12. package/dist/eslint/index.js.map +1 -1
  13. package/dist/eslint/lint-worker.js +8 -19
  14. package/dist/eslint/lint-worker.js.map +1 -1
  15. package/dist/eslint/prefilter.d.ts.map +1 -1
  16. package/dist/eslint/prefilter.js +2 -2
  17. package/dist/eslint/prefilter.js.map +1 -1
  18. package/dist/eslint/session-protocol.d.ts +0 -4
  19. package/dist/eslint/session-protocol.d.ts.map +1 -1
  20. package/dist/eslint/session.js +1 -1
  21. package/dist/eslint/session.js.map +1 -1
  22. package/dist/{diagnosticCatalog.generated.d.ts → go-generated/diagnosticCatalog.generated.d.ts} +1 -0
  23. package/dist/go-generated/diagnosticCatalog.generated.d.ts.map +1 -0
  24. package/dist/{diagnosticCatalog.generated.js → go-generated/diagnosticCatalog.generated.js} +208 -40
  25. package/dist/go-generated/diagnosticCatalog.generated.js.map +1 -0
  26. package/dist/go-generated/reflectionKind.generated.d.ts +59 -0
  27. package/dist/go-generated/reflectionKind.generated.d.ts.map +1 -0
  28. package/dist/go-generated/reflectionKind.generated.js +59 -0
  29. package/dist/go-generated/reflectionKind.generated.js.map +1 -0
  30. package/dist/{runtypes-constants.generated.d.ts → go-generated/runtypes-constants.generated.d.ts} +9 -27
  31. package/dist/go-generated/runtypes-constants.generated.d.ts.map +1 -0
  32. package/dist/{runtypes-constants.generated.js → go-generated/runtypes-constants.generated.js} +5 -18
  33. package/dist/go-generated/runtypes-constants.generated.js.map +1 -0
  34. package/dist/packages/ts-runtypes-devtools/tsconfig.test.tsbuildinfo +1 -1
  35. package/dist/protocol.d.ts +19 -42
  36. package/dist/protocol.d.ts.map +1 -1
  37. package/dist/protocol.js +3 -41
  38. package/dist/protocol.js.map +1 -1
  39. package/dist/resolver-client.d.ts +6 -3
  40. package/dist/resolver-client.d.ts.map +1 -1
  41. package/dist/resolver-client.js +7 -2
  42. package/dist/resolver-client.js.map +1 -1
  43. package/dist/unplugin.d.ts +13 -8
  44. package/dist/unplugin.d.ts.map +1 -1
  45. package/dist/unplugin.js +31 -33
  46. package/dist/unplugin.js.map +1 -1
  47. package/oxlint-recommended.json +31 -0
  48. package/package.json +4 -3
  49. package/dist/diagnosticCatalog.generated.d.ts.map +0 -1
  50. package/dist/diagnosticCatalog.generated.js.map +0 -1
  51. package/dist/runtypes-constants.generated.d.ts.map +0 -1
  52. package/dist/runtypes-constants.generated.js.map +0 -1
@@ -1,571 +1,739 @@
1
1
  export const DIAGNOSTIC_CATALOG = {
2
+ CES001: {
3
+ headline: '`cloneExactShape` does not support unions with object members — the emitter cannot know which declared shape to rebuild at runtime.',
4
+ severity: 'error',
5
+ detail: 'A clone built from the declared shape needs to know WHICH union arm the\nruntime value matches; v1 has no arm discrimination, and silently keeping\nunknown keys would defeat the strip guarantee, so the build fails instead.\n\nWorkarounds: narrow the value to one arm before cloning (one\n`createCloneExactShape<Arm>()` per arm), or restructure the union into a\nsingle object with optional properties.',
6
+ },
7
+ CES003: {
8
+ headline: '`cloneExactShape` cannot clone a function-typed value.',
9
+ severity: 'error',
10
+ detail: "Functions aren't data — there is no declared shape to rebuild. Function-typed\nPROPERTIES are dropped from the clone (CES010/CES011); a function at the root\nor a propagating position fails the build.",
11
+ },
12
+ CES010: {
13
+ headline: 'Property `{0}` is a function — `cloneExactShape` cannot rebuild it, so it is kept on the clone, SHARED BY REFERENCE.',
14
+ severity: 'warning',
15
+ detail: "Declared members are never dropped (only UNDECLARED keys are — that is the\nstrip guarantee). Functions cannot be rebuilt from a declared shape, so the\nclone's property points at the SAME function as the input's. Class METHODS\ndiffer: they ride the shared prototype and are not copied as own props\n(CES011).",
16
+ },
17
+ CES011: {
18
+ headline: "Method `{0}` is not copied onto the clone's own properties — methods ride the prototype.",
19
+ severity: 'warning',
20
+ detail: 'For a plain class instance the clone preserves the PROTOTYPE\n(`Object.create(Object.getPrototypeOf(v))`), so methods keep working via the\nprototype chain; they are simply not copied as own properties. For object\nliterals a method-typed member is omitted like any function value.',
21
+ },
22
+ CES012: {
23
+ headline: 'Static member `{0}` is not part of instance data — `cloneExactShape` skips it.',
24
+ severity: 'warning',
25
+ detail: 'Statics live on the class, not the instance; the clone rebuilds instance\ndata only.',
26
+ },
27
+ CES015: {
28
+ headline: 'Property `{0}` has a value type `cloneExactShape` cannot rebuild (symbol, Promise, or a non-serialisable built-in) — it is kept on the clone, SHARED BY REFERENCE.',
29
+ severity: 'warning',
30
+ detail: "Declared members are never dropped (only UNDECLARED keys are — that is the\nstrip guarantee). A value the emitter cannot rebuild passes through by\nreference instead: the clone's property points at the SAME handle as the\ninput's, so mutations through it are visible on both sides. Register\n`overrideCloneExactShape<T>()` if this type needs custom copying.",
31
+ },
2
32
  CLS001: {
3
33
  headline: 'class `{0}` is serialized structurally; register it via `registerClassSerializer({0}, { deserialize })` to round-trip a real instance.',
34
+ severity: 'warning',
4
35
  detail: "By default a user class is serialized by its declared properties and\ndecoded back to a prototype-less plain object — `instanceof {0}` is\nfalse on the decoded value, and any class methods / getters are gone.\nThis is fine when you only care about the data.\n\nTo round-trip a real `{0}` instance, register it once, passing the class\nitself (not a name string):\n import {registerClassSerializer} from '@ts-runtypes/core';\n\n // zero-arg constructor: nothing else needed\n registerClassSerializer({0});\n\n // non-empty constructor: only `deserialize` is required\n registerClassSerializer({0}, {\n deserialize: (data) => new {0}(/* rebuild from data */),\n });\n\n`serialize` is optional (default: structural, same as any interface);\n`deserialize` is optional for a zero-arg class (default:\n`Object.assign(new {0}(), data)`). The same registration is used by the\nJSON and binary families. `validate` / `getValidationErrors` are\nunaffected — they always validate structurally.",
5
36
  },
6
37
  CTA001: {
7
38
  headline: '`CompTimeArgs<T>` argument must be a literal at the call site, or a `const` whose initializer is itself entirely literal (a same-module or imported `const` both work).',
39
+ severity: 'error',
8
40
  detail: "The build resolves the argument before running, so it needs to read its\nvalue from the source. Function-call results, property accesses, ternary\nexpressions, and `let`/`var` bindings can't be evaluated at build time.\nAccepted: an inline literal, or a `const` whose initializer is itself\nfully literal — including a `const` imported from another module. (An\nobject `const` must be `as const` so its members stay literal; see CTA004.)\n\nFix — inline at the call site:\n- const opts = getOpts();\n- const isUser = createValidate<User>(undefined, opts);\n+ const isUser = createValidate<User>(undefined, {mode: 'unsafe'});\n\nFix — use a const of literals (here or in another module):\n const opts = {mode: 'unsafe'} as const; // literal initializer ✓\n const isUser = createValidate<User>(undefined, opts);",
9
41
  },
10
42
  CTA002: {
11
43
  headline: '`CompTimeArgs<T>` literal nesting exceeds the depth cap (16) — refactor to flatten.',
44
+ severity: 'error',
12
45
  detail: 'Deeply nested literal walks are capped at 16 levels to keep the build\npredictable. If you hit this, the value is almost certainly not what\nyou want at compile time — split it across multiple smaller\n`CompTimeArgs<T>` arguments, or flatten the nesting.',
13
46
  },
14
47
  CTA003: {
15
48
  headline: '`CompTimeArgs<T>` literal contains a forbidden construct ({0}). Only literals and nested literals are allowed.',
49
+ severity: 'error',
16
50
  detail: "The Go scanner cannot statically evaluate computed property names,\nfunction calls, ternary expressions, or template-string substitutions.\nInside a `CompTimeArgs<T>` literal every node must be a direct literal\n(string / number / bigint / boolean / null / undefined / regex / arrow /\nobject literal / array literal) or a const-traced identifier that\nresolves to one.\n\nSpread IS allowed when its operand resolves to a literal container of the\nmatching kind — a `const`-bound (or imported) object literal for an\nobject spread, an array literal for an array spread:\n const base = {strict: true};\n const a = {...base, mode: 'unsafe'}; // ok — merges a const fragment\n\nA spread is still rejected when the operand can't be statically merged —\na dynamic value, or a shape mismatch:\n - const a = {...getDefaults(), mode: 'unsafe'}; // dynamic operand\n - const a = {...[1, 2], mode: 'unsafe'}; // object spread of an array",
17
51
  },
18
52
  CTA004: {
19
53
  headline: '`CompTimeArgs<T>` value comes from a `const` with a widened (non-literal) member ({0}) — declare the const `as const`.',
54
+ severity: 'error',
20
55
  detail: "A `const` used as a CompTimeArgs / CompTimeFnArgs argument (a whole option\nbag, or a builder child) must carry LITERAL value types, so the value the\nbuild reads matches the type TypeScript resolves the call against. Without\n`as const`, an object literal's members widen — `{strategy: 'mutate'}`\nbecomes `{strategy: string}` — which can let the type system select one\nfunction variant while the build injects another.\n\nWhole imported consts now resolve cross-module (like a spread fragment), so\nthis rule keeps that path sound.\n\nFix — add `as const`:\n- const preset = {strategy: 'mutate'};\n+ const preset = {strategy: 'mutate'} as const;\n createJsonEncoder(undefined, preset);",
21
56
  },
22
57
  FB001: {
23
- headline: 'Cannot deserialise `{0}` from binary.',
58
+ headline: 'Type `{0}` can never be deserialised from binary — the generated function will always fail.',
59
+ severity: 'error',
24
60
  detail: "`never` is the empty type — no value can ever inhabit it. A field\ntyped `never` cannot carry a runtime value, so there is nothing to\nencode/decode/validate.\n\nFix — use `unknown` if you really want to accept any value:\n interface User {\n- tag: never;\n+ tag: unknown; // narrow before use\n }\n\nFix — pick a concrete type matching your real data:\n interface User {\n- tag: never;\n+ tag: 'pending' | 'active' | 'done';\n }",
25
61
  },
26
62
  FB002: {
27
- headline: 'Cannot deserialise `{0}` from binary.',
63
+ headline: 'Type `{0}` can never be deserialised from binary — the generated function will always fail.',
64
+ severity: 'error',
28
65
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
29
66
  },
30
67
  FB003: {
31
- headline: 'Cannot deserialise `{0}` from binary.',
68
+ headline: 'Type `{0}` can never be deserialised from binary — the generated function will always fail.',
69
+ severity: 'error',
32
70
  detail: "Functions have no value form to serialise — their closure, prototype,\nand bound state aren't representable in JSON or binary.\n\nFix — drop the function from your type, or replace it with the data the\nfunction would produce:\n interface User {\n- getName: () => string;\n+ name: string;\n }",
33
71
  },
34
72
  FB004: {
35
- headline: 'Cannot deserialise `{0}` from binary.',
73
+ headline: 'Type `{0}` can never be deserialised from binary — the generated function will always fail.',
74
+ severity: 'error',
36
75
  detail: "Arrays of un-serialisable elements (`symbol[]`, `(() => void)[]`,\n`Map<K, V>[]`, etc.) can't be encoded — every element would need to be\nrepresentable, and these aren't. Dropping individual elements would\nchange the array length, so the encoder refuses rather than silently\nshipping a different shape.\n\nFix — change the element type to something serialisable:\n - type Items = (() => void)[];\n+ type Items = string[];",
37
76
  },
38
77
  FB005: {
39
- headline: 'Cannot deserialise `{0}` from binary.',
78
+ headline: 'Type `{0}` can never be deserialised from binary — the generated function will always fail.',
79
+ severity: 'error',
40
80
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
41
81
  },
42
82
  FB006: {
43
- headline: 'Cannot deserialise `{0}` from binary.',
83
+ headline: 'Type `{0}` can never be deserialised from binary — the generated function will always fail.',
84
+ severity: 'error',
44
85
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
45
86
  },
46
87
  FB010: {
47
88
  headline: 'Property `{0}` is a function — `fromBinary` does not handle function values, so this property is silently not deserialised.',
89
+ severity: 'warning',
48
90
  detail: '`fromBinary` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
49
91
  },
50
92
  FB011: {
51
93
  headline: "Method `{0}` is silently not deserialised by `fromBinary` — methods aren't data.",
94
+ severity: 'warning',
52
95
  detail: "Class and object methods aren't part of the serialisable shape, so\n`fromBinary` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
53
96
  },
54
97
  FB012: {
55
98
  headline: "Static member `{0}` is silently not deserialised by `fromBinary` — statics aren't part of instance data.",
99
+ severity: 'warning',
56
100
  detail: 'Class static members live on the class, not on individual instances.\n`fromBinary` operates on instance shape, so statics are excluded.',
57
101
  },
58
102
  FB013: {
59
103
  headline: "Symbol-keyed property `{0}` is silently not deserialised by `fromBinary` — symbol keys aren't JSON-representable.",
104
+ severity: 'warning',
60
105
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `fromBinary` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
61
106
  },
62
107
  FB014: {
63
108
  headline: "Union member(s) of type `{0}` can't be represented as data — `fromBinary` drops them, so the union is deserialised as its remaining members.",
109
+ severity: 'warning',
64
110
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `fromBinary` deserialised only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `fromBinary` throws at build time instead.',
65
111
  },
66
112
  FB015: {
67
113
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `fromBinary` drops it, so this property is silently not deserialised.',
114
+ severity: 'warning',
68
115
  detail: '`fromBinary` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `fromBinary` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
69
116
  },
70
117
  FMT001: {
71
118
  headline: 'TypeFormat mockSample "{0}" does not match its pattern /{1}/ — fix the sample or the pattern.',
119
+ severity: 'error',
72
120
  },
73
121
  FMT002: {
74
122
  headline: 'Invalid type-format params — {0}',
123
+ severity: 'error',
75
124
  },
76
125
  FMT003: {
77
126
  headline: 'TypeFormat mockSample violates a sibling constraint — {0}',
127
+ severity: 'error',
78
128
  detail: "A mockSample is meant to be a canonical VALID value for the format, so it\nmust satisfy the format's own statically checkable siblings (length /\nminLength / maxLength, and the plain-string allowedChars / disallowedChars /\ndisallowedValues ops). A sample that its siblings reject means\n`createMockData` would either produce an invalid value or filter every\nsample out and throw at mock time.\n\nLengths are counted in UTF-16 code units, exactly as the emitted validator's\n`.length` check counts them.\n\nFix — adjust the offending sample(s), or relax the constraint:\n - String<{minLength: 5; pattern: {source: '^b+$'; mockSamples: ['b', 'bb']}}>\n+ String<{minLength: 1; pattern: {source: '^b+$'; mockSamples: ['b', 'bb']}}>",
79
129
  },
80
130
  FMT004: {
81
131
  headline: "TypeFormat pattern /{0}/ carries mockSamples but uses JS-only regex features RE2 can't compile ({1}) — samples can't be verified at build time.",
132
+ severity: 'error',
82
133
  detail: "The build-time sample check compiles the pattern with Go's RE2 engine, which\ndoesn't support JS-only features (lookarounds, backreferences). The build\nfails closed rather than ship samples it can't verify.\n\nFix — set `allowUncheckedPatterns` (plugin option / tsconfig plugin key)\nto assert that the JS linter owns the check, then wire the ts-runtypes lint\nplugin into your editor + CI: it evaluates the real `RegExp.test(sample)` and\nreports any mismatch (as FMT001) at the definition site.\n\nFix — or rewrite the pattern using RE2-compatible syntax so the fast\nbuild-time check can run (no lookarounds / backreferences).",
83
134
  },
84
135
  FT002: {
85
136
  headline: 'Unknown field `{0}` — the type does not declare it, so this FriendlyText entry is dead.',
137
+ severity: 'error',
86
138
  detail: "The FriendlyText map names a field the source type does not have\n(removed, renamed, or a typo). Its labels and messages can never be\nused.\n\nExample — `nick` no longer exists on the type:\n interface User { name: string }\n export const friendlyUser: FriendlyText<User> = {\n name: {rt$label: 'Name'},\n- nick: {rt$label: 'Nickname'},\n };\n\nFix — remove the entry, or re-run the reconcile so the mirror follows\nthe type (a renamed field carries its authored values along):\n ts-runtypes gen <source.ts> <Type> --update",
87
139
  },
88
140
  FT003: {
89
141
  headline: 'Error key `{0}` is not a declared constraint of this field — the message can never fire.',
142
+ severity: 'warning',
90
143
  detail: "`rt$errors` keys must name a failure the field can actually produce:\n`type`, `rt$default`, or one of the field's declared format constraints\n(`minLength`, `pattern`, `min`, …). An undeclared key is dead\nconfiguration.\n\nExample — the field has no `maxLength` constraint:\n interface User { name: string & FormatString<{minLength: 2}> }\n export const friendlyUser: FriendlyText<User> = {\n name: {\n rt$errors: {\n minLength: 'Name needs at least 2 characters',\n- maxLength: 'Name is too long',\n },\n },\n };\n\nFix — remove the key, or declare the matching constraint on the field's\nTypeFormat so the message has a failure to describe.",
91
144
  },
92
145
  FT005: {
93
146
  headline: 'Unknown placeholder `$[{0}]` — expected one of `$[label]`, `$[val]`, `$[path]`, `$[index]`.',
147
+ severity: 'warning',
94
148
  detail: "Error-message templates substitute a fixed placeholder set; an unknown\nname renders literally instead of substituting.\n\nExample:\n- rt$errors: {minLength: '$[name] is too short'}\n+ rt$errors: {minLength: '$[label] is too short'}\n\nFix — use one of the recognised placeholders, or write the literal text\nwithout the `$[…]` wrapper.",
95
149
  },
96
150
  FT006: {
97
151
  headline: 'Plural error template is missing the mandatory `other` arm — the render has no backstop.',
152
+ severity: 'error',
98
153
  detail: "Plural templates render the CLDR arm matching the count, and `other` is\nthe arm every locale falls back to. Without it some counts have no\nmessage at all.\n\nExample:\n rt$errors: {\n minLength: {\n one: 'Needs one more character',\n+ other: 'Needs $[val] more characters',\n },\n }\n\nFix — add the `other` arm to the plural object.",
99
154
  },
100
155
  FT007: {
101
156
  headline: 'Unknown plural arm `{0}` — CLDR categories are `zero`, `one`, `two`, `few`, `many`, `other`.',
157
+ severity: 'warning',
102
158
  detail: "Plural template keys must be CLDR plural categories; anything else can\nnever be selected by any locale's plural rules.\n\nExample:\n rt$errors: {\n minLength: {\n- single: 'Needs one more character',\n+ one: 'Needs one more character',\n other: 'Needs $[val] more characters',\n },\n }\n\nFix — rename the arm to one of the six categories, or remove it.",
103
159
  },
104
160
  FT008: {
105
161
  headline: 'Constraint `{0}` carries no count — a plural template here has dead arms; use a plain string.',
162
+ severity: 'warning',
106
163
  detail: "Only count-bearing constraints (`minLength`, `maxLength`, `min`, `max`,\n…) can select a plural arm. On a non-count constraint only `other` ever\nrenders, so the remaining arms are dead configuration.\n\nExample — `pattern` has no count:\n rt$errors: {\n- pattern: {one: 'One bad character', other: 'Invalid characters'},\n+ pattern: 'Only letters and numbers are allowed',\n }\n\nFix — replace the plural object with a plain string message.",
107
164
  },
108
165
  FT009: {
109
166
  headline: '`rt$default` is mutually exclusive with per-constraint messages — use one mode or the other.',
167
+ severity: 'error',
110
168
  detail: "An `rt$errors` record is either ONE `rt$default` catch-all or a set of\nper-constraint keys, mirroring the TypeScript union. Mixing them makes\nthe intent ambiguous (which message wins?).\n\nExample:\n rt$errors: {\n- rt$default: 'Invalid name',\n minLength: 'Name is too short',\n }\n\nFix — keep `{rt$default: '…'}` alone, or keep the per-constraint keys\nand drop `rt$default`.",
111
169
  },
112
170
  FT011: {
113
171
  headline: 'Property `{0}` collides with the reserved `rt$` enrichment prefix — the type cannot be enriched.',
172
+ severity: 'error',
114
173
  detail: '`rt$`-prefixed keys are reserved for enrichment meta (`rt$label`,\n`rt$errors`, `rt$items`, …); a source property with that prefix is\nindistinguishable from node meta, so gen refuses the type and the\nFriendlyType checker reports it here.\n\nFix — rename the property (a plain `$` prefix is fine; only `rt$` is\nreserved):\n interface Config {\n- rt$mode: string;\n+ $mode: string;\n }',
115
174
  },
116
175
  FT020: {
117
176
  headline: 'Unfilled `@todo` placeholder — fill in the real labels/messages, then delete the `@todo` line.',
177
+ severity: 'error',
118
178
  detail: "The generator stamps a `@todo` line on every freshly-scaffolded const in\na FriendlyText mirror file. It means \"this skeleton still carries\ngenerated blanks\". A clean, committed mirror has none.\n\nExample — a fresh scaffold:\n /** @rtType User#a1b2c3 @rtIds {name: d4e5f6} */\n- // @todo: generated skeleton — fill in real data, then delete this line\n export const friendlyUser: FriendlyText<User> = {\n- name: {rt$label: ''},\n+ name: {rt$label: 'Name'},\n };\n\nFix — author the real labels and error messages for the const, then\ndelete the whole `@todo` line (the compiler never removes it for you).",
119
179
  },
120
180
  FT021: {
121
181
  headline: 'Stale `@rtOrphan` carcass — run `ts-runtypes gen --prune` to remove it (or restore the type).',
182
+ severity: 'error',
122
183
  detail: 'The reconcile commented this FriendlyText const out because its source\ntype was deleted or renamed. The carcass preserves your authored labels\nand messages so a reappearing type can restore them — but a clean,\ncommitted mirror has none.\n\nFix — if the type is really gone, prune the carcass:\n ts-runtypes gen --prune\n\nFix — if the type was renamed, re-run the reconcile; a matching carcass\nis restored with your values intact:\n ts-runtypes gen <source.ts> <NewName> --update',
123
184
  },
124
185
  FT022: {
125
186
  headline: 'Stale `@rtOrphanChild` field carcass — run `ts-runtypes gen --prune` to remove it (or restore the field).',
187
+ severity: 'error',
126
188
  detail: "The reconcile commented this field out because the source type no longer\ndeclares it. The carcass preserves your authored value inline — but a\nclean, committed mirror has none.\n\nExample:\n export const friendlyUser: FriendlyText<User> = {\n- /* @rtOrphanChild nick: {rt$label: 'Nickname'}, */\n name: {rt$label: 'Name'},\n };\n\nFix — if the field is really gone: `ts-runtypes gen --prune`.\nFix — if the field was renamed, re-run `--update`; the authored value\nmoves to the renamed field when the ids match.",
127
189
  },
128
190
  GE000: {
129
191
  headline: 'Cannot read enrichment mirror file: {0}',
192
+ severity: 'error',
130
193
  detail: 'The drift check could not read this mirror file (permissions, a broken\nsymlink, or a race with a concurrent write).\n\nFix — make the file readable and re-run `ts-runtypes gen --check`.',
131
194
  },
132
195
  GE001: {
133
196
  headline: 'Mirror location drift — the source maps to `{0}` but this file lives at `{1}`; re-run `ts-runtypes gen` to relocate.',
197
+ severity: 'warning',
134
198
  detail: 'Each source file mirrors to ONE computed path per family under the\nenrich root (friendly/… and mock/…, plus per-locale translation twins).\nThis file is not at its computed location — usually after a source move,\nan enrich-dir change, or a pre-split combined mirror that still needs\nmigrating.\n\nFix — re-run the generator; it writes the per-family files at the right\npaths and migrates a legacy combined mirror:\n ts-runtypes gen <source.ts> <Type> --update',
135
199
  },
136
200
  GE002: {
137
201
  headline: 'Breadcrumb source `{0}` no longer exists ({1}) — the mirror is orphaned; delete it or re-run `ts-runtypes gen`.',
202
+ severity: 'error',
138
203
  detail: "The mirror's `import type { … } from '<source>'` breadcrumb resolves to\na file that is gone. Its consts describe types that no longer exist\nanywhere.\n\nFix — if the source was deleted, delete the mirror file (both family\nfiles and any translation twins).\nFix — if the source moved, re-run the generator from the new location\nand prune the old mirror.",
139
204
  },
140
205
  GE003: {
141
206
  headline: 'Source {0} no longer declares type `{1}` — re-run `ts-runtypes gen`.',
207
+ severity: 'error',
142
208
  detail: 'The mirror imports a type name its source file no longer declares (the\ntype was renamed or removed). The reconcile turns its consts into\n`@rtOrphan` carcasses so your authored values survive.\n\nFix — re-run the reconcile against the current source, then prune any\ncarcasses that should not come back:\n ts-runtypes gen <source.ts> <Type> --update\n ts-runtypes gen --prune',
143
209
  },
144
210
  HUK010: {
145
211
  headline: 'Property `{0}` is a function — `hasUnknownKeys` does not handle function values, so this property is silently not checked.',
212
+ severity: 'warning',
146
213
  detail: '`hasUnknownKeys` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
147
214
  },
148
215
  JCP001: {
149
216
  headline: 'Internal error: JSON composite `{0}` references primitive entry `{1}` (type `{2}`) which was never rendered — please file an issue.',
217
+ severity: 'error',
150
218
  },
151
219
  MD001: {
152
220
  headline: 'Unknown field `{0}` — the type does not declare it, so this MockData entry is dead.',
221
+ severity: 'error',
153
222
  detail: "The MockData map names a field the source type does not have (removed,\nrenamed, or a typo). Its pool/range can never feed a generated mock.\n\nExample — `nick` no longer exists on the type:\n interface User { name: string }\n export const mockUser: MockData<User> = {\n name: {pool: ['Ada', 'Linus']},\n- nick: {pool: ['ada99']},\n };\n\nFix — remove the entry, or re-run the reconcile so the mirror follows\nthe type:\n ts-runtypes gen <source.ts> <Type> --update",
154
223
  },
155
224
  MD011: {
156
225
  headline: 'Property `{0}` collides with the reserved `rt$` enrichment prefix — the type cannot be enriched.',
226
+ severity: 'error',
157
227
  detail: '`rt$`-prefixed keys are reserved for enrichment meta (`rt$items`,\n`rt$length`, `rt$optional`, …); a source property with that prefix is\nindistinguishable from node meta, so gen refuses the type and the\nMockData checker reports it here.\n\nFix — rename the property (a plain `$` prefix is fine; only `rt$` is\nreserved):\n interface Config {\n- rt$size: number;\n+ $size: number;\n }',
158
228
  },
159
229
  MD020: {
160
230
  headline: 'Unfilled `@todo` placeholder — fill in the real sample pools/ranges, then delete the `@todo` line.',
231
+ severity: 'error',
161
232
  detail: "The generator stamps a `@todo` line on every freshly-scaffolded const in\na MockData mirror file. It means \"this skeleton still carries generated\nblanks\". A clean, committed mirror has none.\n\nExample — a fresh scaffold:\n /** @rtType User#a1b2c3 @rtIds {name: d4e5f6} */\n- // @todo: generated skeleton — fill in real data, then delete this line\n export const mockUser: MockData<User> = {\n- name: {pool: []},\n+ name: {pool: ['Ada Lovelace', 'Linus Torvalds']},\n };\n\nFix — author realistic sample pools/ranges for the const, then delete\nthe whole `@todo` line (the compiler never removes it for you).",
162
233
  },
163
234
  MD021: {
164
235
  headline: 'Stale `@rtOrphan` carcass — run `ts-runtypes gen --prune` to remove it (or restore the type).',
236
+ severity: 'error',
165
237
  detail: 'The reconcile commented this MockData const out because its source type\nwas deleted or renamed. The carcass preserves your authored pools and\nranges so a reappearing type can restore them — but a clean, committed\nmirror has none.\n\nFix — if the type is really gone, prune the carcass:\n ts-runtypes gen --prune\n\nFix — if the type was renamed, re-run the reconcile; a matching carcass\nis restored with your values intact:\n ts-runtypes gen <source.ts> <NewName> --update',
166
238
  },
167
239
  MD022: {
168
240
  headline: 'Stale `@rtOrphanChild` field carcass — run `ts-runtypes gen --prune` to remove it (or restore the field).',
241
+ severity: 'error',
169
242
  detail: "The reconcile commented this field out because the source type no longer\ndeclares it. The carcass preserves your authored value inline — but a\nclean, committed mirror has none.\n\nExample:\n export const mockUser: MockData<User> = {\n- /* @rtOrphanChild nick: {pool: ['ada99']}, */\n name: {pool: ['Ada', 'Linus']},\n };\n\nFix — if the field is really gone: `ts-runtypes gen --prune`.\nFix — if the field was renamed, re-run `--update`; the authored value\nmoves to the renamed field when the ids match.",
170
243
  },
171
244
  MKR001: {
172
245
  headline: '`{0}()` is being called at runtime just so the marker can read its return type — side effects, throws, or async work run for nothing.',
246
+ severity: 'warning',
173
247
  detail: 'Reflect-form markers (`createValidate(value)`, `getRunTypeId(value)`)\ninvoke their argument expression at runtime; the value is then discarded —\nonly its inferred type is used.\n\nFix — use the static form with `ReturnType<>`:\n - const isUser = createValidate({0}());\n+ const isUser = createValidate<ReturnType<typeof {0}>>();\n\nFix — pass an existing value of the desired type:\n const existingUser: User = ...;\n const isUser = getRunTypeId(existingUser);',
174
248
  },
175
249
  MKR003: {
176
250
  headline: 'Marker call is inside a generic function — the type argument is unresolved, so no id can be computed at build time.',
251
+ severity: 'error',
177
252
  detail: "The build can only compute an id for a concrete type (`User`,\n`{name: string}`, etc.). A type parameter like `T` is abstract — it\ntakes a different value at each call site of the surrounding function,\nso a single id can't represent it.\n\nFix — inline the marker at each concrete call site:\n function isUser(value: unknown) {\n return createValidate<User>()(value);\n }\n\nFix — accept a pre-computed id from the caller:\n function makeChecker<T>(id: InjectRunTypeId<T>) {\n return createValidate<T>(id);\n }\n const isUser = makeChecker<User>(getRunTypeId<User>());",
178
253
  },
179
254
  MKR004: {
180
255
  headline: "`noLiterals: true` has no effect here — the type argument doesn't resolve to literal values.",
256
+ severity: 'warning',
181
257
  detail: "The `noLiterals` validate option skips the exact-value check that literal\ntypes (`'admin'`, `42`, `true`) compile to. This call's type argument\nresolves to a non-literal type, so there is no literal check to skip and\nthe option is a silent no-op.\n\nFix — drop the option:\n- const isRole = createValidate<string>({noLiterals: true});\n+ const isRole = createValidate<string>();\n\nOr, if you meant to relax a literal union, point the option at the type\nthat actually carries the literals:\n const isRole = createValidate<'admin' | 'user'>({noLiterals: true});",
182
258
  },
183
259
  MKR005: {
184
260
  headline: '`noIsArrayCheck: true` has no effect here — the type argument is not an array type.',
261
+ severity: 'warning',
185
262
  detail: "The `noIsArrayCheck` validate option skips the `Array.isArray` guard that\narray types compile to. This call's type argument resolves to a non-array\ntype, so there is no guard to skip and the option is a silent no-op.\n\nFix — drop the option:\n- const isUser = createValidate<User>({noIsArrayCheck: true});\n+ const isUser = createValidate<User>();\n\nOr point it at the array type you meant:\n const isUsers = createValidate<User[]>({noIsArrayCheck: true});",
186
263
  },
187
264
  MKR006: {
188
265
  headline: '`InjectTypeFnArgs` names the function family `{0}` more than once — remove the duplicate key.',
266
+ severity: 'error',
189
267
  detail: "An `InjectTypeFnArgs<T, …>` marker names each function family it needs for\n`T` once, in declaration order; the build injects one entry-module tuple\nper name and the wrapper forwards each to its factory. Naming a family\ntwice would inject a redundant identical tuple with no consumer, so it is\nalmost always a copy-paste slip and the build stops.\n\nFix — name each family at most once:\n- id?: InjectTypeFnArgs<T, 'verr', 'jsonDecoder', 'verr'>;\n+ id?: InjectTypeFnArgs<T, 'verr', 'jsonDecoder', 'jsonEncoder'>;",
190
268
  },
191
269
  MKR007: {
192
270
  headline: 'Marker type resolved to `any` because this file has an unresolved import (`{0}`) — the generated functions would silently accept anything.',
271
+ severity: 'error',
193
272
  detail: "TypeScript could not resolve the import, so the type it should have\nprovided checked as `any` at this marker call. A validator over `any` is\nthe always-true identity, a mock over `any` is `undefined`, and encoders\npass values through untouched — with no runtime signal that anything is\nwrong. This usually means the build tool and the type scanner resolve\nmodules differently (e.g. an extensionless relative import under\n`moduleResolution: NodeNext`, a missing dependency, or a `paths` alias the\nscan tsconfig doesn't declare).\n\nFix — make the import resolve for the type scanner:\n- import {User} from './user.runtype';\n+ import {User} from './user.runtype.ts';\n\nOr align the tsconfig the plugin scans with the one your bundler uses.\nIf the `any` is genuinely intentional, write the marker over an alias\ndeclared in resolving code (e.g. `type Loose = any`) in a file with no\nfailing imports.",
194
273
  },
195
274
  NE001: {
196
275
  headline: 'Property `{0}` is tagged @nonEnumerable but is required — the guard only applies to optional properties, so the tag has no effect. Make it optional (`{0}?`) or remove the tag.',
276
+ severity: 'error',
197
277
  detail: "The runtime enumerability guard (which lets a value omit a property from\nthe wire when it isn't an enumerable own property) is applied ONLY to\noptional properties. That keeps the decoder's `DataOnly<T>` return type\nhonest: a guarded property is always one the type already allows to be\nabsent. A `@nonEnumerable` tag on a REQUIRED property is therefore ignored\n— the property still serializes unconditionally.\n\nFix — make the property optional:\n- /** @nonEnumerable */ token: string;\n+ /** @nonEnumerable */ token?: string;",
198
278
  },
199
279
  OVR001: {
200
280
  headline: 'Duplicate override for `{0}` — there can be exactly one override per (type, function).',
281
+ severity: 'error',
201
282
  detail: 'Two `overrideX<T>()` declarations target the same type and the same\nfunction family. Which one wins would depend on scan order, so a second\noverride is rejected regardless of its body. The Related: line above\npoints at the override that was registered first.\n\nFix — keep one canonical override and delete the other:\n- overrideValidate<User>((utl) => (value) => checkA(value)); // first\n- overrideValidate<User>((utl) => (value) => checkB(value)); // duplicate\n+ overrideValidate<User>((utl) => (value) => checkA(value) && checkB(value));',
202
283
  },
203
284
  OVR002: {
204
285
  headline: 'Override entry `{0}` references compiled function `{1}` which did not render — this would throw at runtime, so the build stops.',
286
+ severity: 'error',
205
287
  detail: "An override redirect body loads its compiled function from the cache\n(`usePureFn('cfn::…')`), but that module never rendered into the entry\ngraph. Calling the override would throw at runtime, so the build surfaces\nthe miss now. This is an internal emitter tripwire and should never fire\nin normal operation.\n\nFix — re-run with a clean cache first (delete the .runtypes cache dir /\nrestart the dev server). If it persists, the emitter dropped a module it\nshould have rendered: please open an issue with the type + override that\ntriggers it.",
206
288
  },
207
289
  OVR010: {
208
290
  headline: 'Overriding `validate` for this type also changes how JSON and binary decoders narrow unions containing it.',
291
+ severity: 'warning',
209
292
  detail: "`validate` is a shared dependency across function families: JSON and\nbinary union decoders call the member validators to pick the matching\nbranch. An `overrideValidate<T>()` therefore reaches past\n`createValidate<T>()` — decoders of any union containing T now narrow\nwith YOUR function.\n\nThis is informational; the build proceeds. If the override should only\naffect direct validation, give the union members a discriminant so\ndecoders never fall back to member validation:\n type Event = {kind: 'click'; x: number} | {kind: 'key'; code: string};",
210
293
  },
211
294
  PFE9004: {
212
295
  headline: 'Duplicate `registerPureFnFactory` for `{0}` with a different body — only one definition can win.',
296
+ severity: 'error',
213
297
  detail: 'Two calls register the same `namespace::functionId` key but the factory\nbodies differ. The cache can only hold one definition, so one call site\nsilently loses its version at runtime.\n\nFix — make all registrations identical, or pick one canonical site and\ndelete the others. The Related: line above points at the first\nregistration the extractor saw.',
214
298
  },
215
299
  PFE9005: {
216
300
  headline: 'Pure-fn factory `{0}` uses destructured parameters — only simple identifier params are supported.',
301
+ severity: 'error',
217
302
  detail: "The build inlines parameter references by name when it materialises the\nfactory. Destructuring patterns (`({a, b})`, `([x, y])`) don't have a\nsingle name to substitute.\n\nFix — destructure inside the body:\n - registerPureFnFactory('ns::fn', (utl) => ({a, b}) => ...);\n+ registerPureFnFactory('ns::fn', (utl) => (params) => {\n+ const {a, b} = params;\n+ return ...;\n+ });",
218
303
  },
219
304
  PFE9006: {
220
305
  headline: "`this` is not allowed inside a `registerPureFnFactory` factory body — pure functions can't depend on a calling context.",
306
+ severity: 'error',
221
307
  detail: "Pure functions are materialised standalone at build time; there's no\n`this` to bind to.\n\nFix — replace `this` with an explicit parameter, or move the function\nout of the class/object method that owns the `this`:\n registerPureFnFactory('ns::fn', (utl) => (self, input) => {\n return self.field + input;\n });",
222
308
  },
223
309
  PFE9007: {
224
310
  headline: '`async`/`await` is not allowed inside a `registerPureFnFactory` factory body.',
311
+ severity: 'error',
225
312
  detail: "Pure functions must run synchronously so the build can call them at\ncompile time. `async` introduces a Promise that won't resolve until\nruntime.\n\nFix — make the factory synchronous; move async work to the caller:\n registerPureFnFactory('ns::fn', (utl) => {\n- return async (input) => { const r = await heavy(); return r; };\n+ return (resolvedValue) => transform(resolvedValue);\n });",
226
313
  },
227
314
  PFE9008: {
228
315
  headline: '`yield` / generators are not allowed inside a `registerPureFnFactory` factory body.',
316
+ severity: 'error',
229
317
  detail: "Generators carry resumption state that can't be materialised\nstatically.\n\nFix — return an array or a plain iterable instead:\n registerPureFnFactory('ns::fn', (utl) => (input) => {\n return [...computeAll(input)];\n });",
230
318
  },
231
319
  PFE9009: {
232
320
  headline: '`import()` is not allowed inside a `registerPureFnFactory` factory body.',
321
+ severity: 'error',
233
322
  detail: 'Dynamic imports load modules at runtime — the build needs every\ndependency available statically.\n\nFix — use a top-level `import` statement, or pass the imported module\nin as a parameter.',
234
323
  },
235
324
  PFE9010: {
236
325
  headline: '`{0}` is not allowed inside a `registerPureFnFactory` factory body.',
326
+ severity: 'error',
237
327
  detail: "Globals like `eval`, `Function`, `fetch`, `XMLHttpRequest`, `require`,\n`process`, `globalThis`, `window`, `document` are blocked from pure-fn\nbodies — they either execute arbitrary code or depend on a runtime\nenvironment the build can't reproduce.\n\nFix — remove the reference, or pass the needed value in as a parameter.",
238
328
  },
239
329
  PFE9011: {
240
330
  headline: "`{0}` is captured from outer scope inside a `registerPureFnFactory` factory — pure functions can't reach outside their own body.",
331
+ severity: 'error',
241
332
  detail: "The build inlines factory bodies without their lexical environment, so\nany free variable becomes `undefined` at runtime.\n\nFix — pass `{0}` in as a parameter:\n registerPureFnFactory('ns::fn', (utl) => ({0}, value) => ...);\n\nFix — inline its value if it's a known constant:\n registerPureFnFactory('ns::fn', (utl) => (value) => {\n const {0} = 42;\n ...\n });\n\nFix — import `{0}` directly inside the factory if it's a module export.",
242
333
  },
243
334
  PFE9012: {
244
335
  headline: "Pure-fn `{0}` is referenced by a RT function but never registered — call `registerPureFnFactory('{1}::{2}', …)` first.",
336
+ severity: 'error',
245
337
  detail: "A RT validator/encoder calls `utl.usePureFn('{0}')` (or similar) but\nno `registerPureFnFactory` call with that namespace+function pair was\nfound in any scanned source file.\n\nFix — register the function in the expected location ({3}, if known).\nMake sure the file is included in the scan set.",
246
338
  },
247
339
  PFE9013: {
248
340
  headline: '`{0}.{1}` dependency argument must be a string literal or a same-scope `const` string.',
341
+ severity: 'error',
249
342
  detail: "`utl.usePureFn` / `utl.getPureFn` need a static key so the build can\nverify the referenced pure-fn is registered.\n\nFix:\n - const key = buildKey();\n- return utl.usePureFn(key)(input);\n+ return utl.usePureFn('rt::myFn')(input);",
250
343
  },
251
344
  PFN001: {
252
345
  headline: '`PureFunction<F>` argument must be an INLINE arrow or function expression.',
346
+ severity: 'error',
253
347
  detail: "The build extracts and AOT-compiles the function body, so it must see the\nliteral inline at the call site. A named reference — even a module-private\n`const f = …` or `function f(){}` — is not accepted, because the literal\nmust have no handle anything else can reach; the compiled copy is then the\nonly one that can run. (An imported or exported literal is rejected as PFN002.)\n\nFix — inline the function at the call site:\n- const validate = (v: unknown) => typeof v === 'string';\n- registerValidator(validate);\n+ registerValidator((v: unknown) => typeof v === 'string');",
254
348
  },
255
349
  PFN002: {
256
350
  headline: '`PureFunction<F>` literal must not be imported or exported — the compiled copy must be the only one that can run.',
351
+ severity: 'error',
257
352
  detail: "The build extracts and AOT-compiles the function body, and the compiled\ncopy is the single source of truth. If the original literal stays reachable\nas a value — imported from another module, or exported so another module can\nimport it — a caller could invoke the un-compiled function and diverge from\nthe compiled behaviour.\n\nUnder the literal-only rule a named binding isn't allowed at all (see PFN001),\nso the fix is to inline the function at the call site:\n- import {validate} from './validators'; // imported — rejected\n- export const validate = (v) => …; // exported — rejected\n+ registerValidator((v: unknown) => typeof v === 'string'); // inline — ok",
258
353
  },
259
354
  PJ001: {
260
- headline: 'Cannot encode `{0}` to JSON.',
355
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
356
+ severity: 'error',
261
357
  detail: "`never` is the empty type — no value can ever inhabit it. A field\ntyped `never` cannot carry a runtime value, so there is nothing to\nencode/decode/validate.\n\nFix — use `unknown` if you really want to accept any value:\n interface User {\n- tag: never;\n+ tag: unknown; // narrow before use\n }\n\nFix — pick a concrete type matching your real data:\n interface User {\n- tag: never;\n+ tag: 'pending' | 'active' | 'done';\n }",
262
358
  },
263
359
  PJ002: {
264
- headline: 'Cannot encode `{0}` to JSON.',
360
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
361
+ severity: 'error',
265
362
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
266
363
  },
267
364
  PJ003: {
268
- headline: 'Cannot encode `{0}` to JSON.',
365
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
366
+ severity: 'error',
269
367
  detail: "Functions have no value form to serialise — their closure, prototype,\nand bound state aren't representable in JSON or binary.\n\nFix — drop the function from your type, or replace it with the data the\nfunction would produce:\n interface User {\n- getName: () => string;\n+ name: string;\n }",
270
368
  },
271
369
  PJ004: {
272
- headline: 'Cannot encode `{0}` to JSON.',
370
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
371
+ severity: 'error',
273
372
  detail: "Arrays of un-serialisable elements (`symbol[]`, `(() => void)[]`,\n`Map<K, V>[]`, etc.) can't be encoded — every element would need to be\nrepresentable, and these aren't. Dropping individual elements would\nchange the array length, so the encoder refuses rather than silently\nshipping a different shape.\n\nFix — change the element type to something serialisable:\n - type Items = (() => void)[];\n+ type Items = string[];",
274
373
  },
275
374
  PJ005: {
276
- headline: 'Cannot encode `{0}` to JSON.',
375
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
376
+ severity: 'error',
277
377
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
278
378
  },
279
379
  PJ010: {
280
380
  headline: 'Property `{0}` is a function — `prepareForJson` does not handle function values, so this property is silently not encoded.',
381
+ severity: 'warning',
281
382
  detail: '`prepareForJson` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
282
383
  },
283
384
  PJ011: {
284
385
  headline: "Method `{0}` is silently not encoded by `prepareForJson` — methods aren't data.",
386
+ severity: 'warning',
285
387
  detail: "Class and object methods aren't part of the serialisable shape, so\n`prepareForJson` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
286
388
  },
287
389
  PJ012: {
288
390
  headline: "Static member `{0}` is silently not encoded by `prepareForJson` — statics aren't part of instance data.",
391
+ severity: 'warning',
289
392
  detail: 'Class static members live on the class, not on individual instances.\n`prepareForJson` operates on instance shape, so statics are excluded.',
290
393
  },
291
394
  PJ013: {
292
395
  headline: "Symbol-keyed property `{0}` is silently not encoded by `prepareForJson` — symbol keys aren't JSON-representable.",
396
+ severity: 'warning',
293
397
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `prepareForJson` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
294
398
  },
295
399
  PJ014: {
296
400
  headline: "Union member(s) of type `{0}` can't be represented as data — `prepareForJson` drops them, so the union is encoded as its remaining members.",
401
+ severity: 'warning',
297
402
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `prepareForJson` encoded only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `prepareForJson` throws at build time instead.',
298
403
  },
299
404
  PJ015: {
300
405
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `prepareForJson` drops it, so this property is silently not encoded.',
406
+ severity: 'warning',
301
407
  detail: '`prepareForJson` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `prepareForJson` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
302
408
  },
303
409
  PJS001: {
304
- headline: 'Cannot encode `{0}` to JSON.',
410
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
411
+ severity: 'error',
305
412
  detail: "`never` is the empty type — no value can ever inhabit it. A field\ntyped `never` cannot carry a runtime value, so there is nothing to\nencode/decode/validate.\n\nFix — use `unknown` if you really want to accept any value:\n interface User {\n- tag: never;\n+ tag: unknown; // narrow before use\n }\n\nFix — pick a concrete type matching your real data:\n interface User {\n- tag: never;\n+ tag: 'pending' | 'active' | 'done';\n }",
306
413
  },
307
414
  PJS002: {
308
- headline: 'Cannot encode `{0}` to JSON.',
415
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
416
+ severity: 'error',
309
417
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
310
418
  },
311
419
  PJS003: {
312
- headline: 'Cannot encode `{0}` to JSON.',
420
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
421
+ severity: 'error',
313
422
  detail: "Functions have no value form to serialise — their closure, prototype,\nand bound state aren't representable in JSON or binary.\n\nFix — drop the function from your type, or replace it with the data the\nfunction would produce:\n interface User {\n- getName: () => string;\n+ name: string;\n }",
314
423
  },
315
424
  PJS004: {
316
- headline: 'Cannot encode `{0}` to JSON.',
425
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
426
+ severity: 'error',
317
427
  detail: "Arrays of un-serialisable elements (`symbol[]`, `(() => void)[]`,\n`Map<K, V>[]`, etc.) can't be encoded — every element would need to be\nrepresentable, and these aren't. Dropping individual elements would\nchange the array length, so the encoder refuses rather than silently\nshipping a different shape.\n\nFix — change the element type to something serialisable:\n - type Items = (() => void)[];\n+ type Items = string[];",
318
428
  },
319
429
  PJS005: {
320
- headline: 'Cannot encode `{0}` to JSON.',
430
+ headline: 'Type `{0}` can never be encoded to JSON — the generated function will always fail.',
431
+ severity: 'error',
321
432
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
322
433
  },
323
434
  PJS010: {
324
435
  headline: 'Property `{0}` is a function — `prepareForJsonSafe` does not handle function values, so this property is silently not encoded.',
436
+ severity: 'warning',
325
437
  detail: '`prepareForJsonSafe` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
326
438
  },
327
439
  PJS011: {
328
440
  headline: "Method `{0}` is silently not encoded by `prepareForJsonSafe` — methods aren't data.",
441
+ severity: 'warning',
329
442
  detail: "Class and object methods aren't part of the serialisable shape, so\n`prepareForJsonSafe` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
330
443
  },
331
444
  PJS012: {
332
445
  headline: "Static member `{0}` is silently not encoded by `prepareForJsonSafe` — statics aren't part of instance data.",
446
+ severity: 'warning',
333
447
  detail: 'Class static members live on the class, not on individual instances.\n`prepareForJsonSafe` operates on instance shape, so statics are excluded.',
334
448
  },
335
449
  PJS013: {
336
450
  headline: "Symbol-keyed property `{0}` is silently not encoded by `prepareForJsonSafe` — symbol keys aren't JSON-representable.",
451
+ severity: 'warning',
337
452
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `prepareForJsonSafe` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
338
453
  },
339
454
  PJS014: {
340
455
  headline: "Union member(s) of type `{0}` can't be represented as data — `prepareForJsonSafe` drops them, so the union is encoded as its remaining members.",
456
+ severity: 'warning',
341
457
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `prepareForJsonSafe` encoded only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `prepareForJsonSafe` throws at build time instead.',
342
458
  },
343
459
  PJS015: {
344
460
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `prepareForJsonSafe` drops it, so this property is silently not encoded.',
461
+ severity: 'warning',
345
462
  detail: '`prepareForJsonSafe` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `prepareForJsonSafe` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
346
463
  },
347
464
  RJ001: {
348
- headline: 'Cannot decode `{0}` from JSON.',
465
+ headline: 'Type `{0}` can never be decoded from JSON — the generated function will always fail.',
466
+ severity: 'error',
349
467
  detail: "`never` is the empty type — no value can ever inhabit it. A field\ntyped `never` cannot carry a runtime value, so there is nothing to\nencode/decode/validate.\n\nFix — use `unknown` if you really want to accept any value:\n interface User {\n- tag: never;\n+ tag: unknown; // narrow before use\n }\n\nFix — pick a concrete type matching your real data:\n interface User {\n- tag: never;\n+ tag: 'pending' | 'active' | 'done';\n }",
350
468
  },
351
469
  RJ002: {
352
- headline: 'Cannot decode `{0}` from JSON.',
470
+ headline: 'Type `{0}` can never be decoded from JSON — the generated function will always fail.',
471
+ severity: 'error',
353
472
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
354
473
  },
355
474
  RJ003: {
356
- headline: 'Cannot decode `{0}` from JSON.',
475
+ headline: 'Type `{0}` can never be decoded from JSON — the generated function will always fail.',
476
+ severity: 'error',
357
477
  detail: "Functions have no value form to serialise — their closure, prototype,\nand bound state aren't representable in JSON or binary.\n\nFix — drop the function from your type, or replace it with the data the\nfunction would produce:\n interface User {\n- getName: () => string;\n+ name: string;\n }",
358
478
  },
359
479
  RJ004: {
360
- headline: 'Cannot decode `{0}` from JSON.',
480
+ headline: 'Type `{0}` can never be decoded from JSON — the generated function will always fail.',
481
+ severity: 'error',
361
482
  detail: "Arrays of un-serialisable elements (`symbol[]`, `(() => void)[]`,\n`Map<K, V>[]`, etc.) can't be encoded — every element would need to be\nrepresentable, and these aren't. Dropping individual elements would\nchange the array length, so the encoder refuses rather than silently\nshipping a different shape.\n\nFix — change the element type to something serialisable:\n - type Items = (() => void)[];\n+ type Items = string[];",
362
483
  },
363
484
  RJ005: {
364
- headline: 'Cannot decode `{0}` from JSON.',
485
+ headline: 'Type `{0}` can never be decoded from JSON — the generated function will always fail.',
486
+ severity: 'error',
365
487
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
366
488
  },
367
489
  RJ010: {
368
490
  headline: 'Property `{0}` is a function — `restoreFromJson` does not handle function values, so this property is silently not decoded.',
491
+ severity: 'warning',
369
492
  detail: '`restoreFromJson` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
370
493
  },
371
494
  RJ011: {
372
495
  headline: "Method `{0}` is silently not decoded by `restoreFromJson` — methods aren't data.",
496
+ severity: 'warning',
373
497
  detail: "Class and object methods aren't part of the serialisable shape, so\n`restoreFromJson` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
374
498
  },
375
499
  RJ012: {
376
500
  headline: "Static member `{0}` is silently not decoded by `restoreFromJson` — statics aren't part of instance data.",
501
+ severity: 'warning',
377
502
  detail: 'Class static members live on the class, not on individual instances.\n`restoreFromJson` operates on instance shape, so statics are excluded.',
378
503
  },
379
504
  RJ013: {
380
505
  headline: "Symbol-keyed property `{0}` is silently not decoded by `restoreFromJson` — symbol keys aren't JSON-representable.",
506
+ severity: 'warning',
381
507
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `restoreFromJson` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
382
508
  },
383
509
  RJ014: {
384
510
  headline: "Union member(s) of type `{0}` can't be represented as data — `restoreFromJson` drops them, so the union is decoded as its remaining members.",
511
+ severity: 'warning',
385
512
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `restoreFromJson` decoded only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `restoreFromJson` throws at build time instead.',
386
513
  },
387
514
  RJ015: {
388
515
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `restoreFromJson` drops it, so this property is silently not decoded.',
516
+ severity: 'warning',
389
517
  detail: '`restoreFromJson` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `restoreFromJson` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
390
518
  },
391
519
  SJ001: {
392
- headline: 'Cannot stringify `{0}` to a JSON string.',
520
+ headline: 'Type `{0}` can never be stringified to JSON — the generated function will always fail.',
521
+ severity: 'error',
393
522
  detail: "`never` is the empty type — no value can ever inhabit it. A field\ntyped `never` cannot carry a runtime value, so there is nothing to\nencode/decode/validate.\n\nFix — use `unknown` if you really want to accept any value:\n interface User {\n- tag: never;\n+ tag: unknown; // narrow before use\n }\n\nFix — pick a concrete type matching your real data:\n interface User {\n- tag: never;\n+ tag: 'pending' | 'active' | 'done';\n }",
394
523
  },
395
524
  SJ002: {
396
- headline: 'Cannot stringify `{0}` to a JSON string.',
525
+ headline: 'Type `{0}` can never be stringified to JSON — the generated function will always fail.',
526
+ severity: 'error',
397
527
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
398
528
  },
399
529
  SJ003: {
400
- headline: 'Cannot stringify `{0}` to a JSON string.',
530
+ headline: 'Type `{0}` can never be stringified to JSON — the generated function will always fail.',
531
+ severity: 'error',
401
532
  detail: "Functions have no value form to serialise — their closure, prototype,\nand bound state aren't representable in JSON or binary.\n\nFix — drop the function from your type, or replace it with the data the\nfunction would produce:\n interface User {\n- getName: () => string;\n+ name: string;\n }",
402
533
  },
403
534
  SJ004: {
404
- headline: 'Cannot stringify `{0}` to a JSON string.',
535
+ headline: 'Type `{0}` can never be stringified to JSON — the generated function will always fail.',
536
+ severity: 'error',
405
537
  detail: "Arrays of un-serialisable elements (`symbol[]`, `(() => void)[]`,\n`Map<K, V>[]`, etc.) can't be encoded — every element would need to be\nrepresentable, and these aren't. Dropping individual elements would\nchange the array length, so the encoder refuses rather than silently\nshipping a different shape.\n\nFix — change the element type to something serialisable:\n - type Items = (() => void)[];\n+ type Items = string[];",
406
538
  },
407
539
  SJ005: {
408
- headline: 'Cannot stringify `{0}` to a JSON string.',
540
+ headline: 'Type `{0}` can never be stringified to JSON — the generated function will always fail.',
541
+ severity: 'error',
409
542
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
410
543
  },
411
544
  SJ010: {
412
545
  headline: 'Property `{0}` is a function — `stringifyJson` does not handle function values, so this property is silently not stringified.',
546
+ severity: 'warning',
413
547
  detail: '`stringifyJson` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
414
548
  },
415
549
  SJ011: {
416
550
  headline: "Method `{0}` is silently not stringified by `stringifyJson` — methods aren't data.",
551
+ severity: 'warning',
417
552
  detail: "Class and object methods aren't part of the serialisable shape, so\n`stringifyJson` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
418
553
  },
419
554
  SJ012: {
420
555
  headline: "Static member `{0}` is silently not stringified by `stringifyJson` — statics aren't part of instance data.",
556
+ severity: 'warning',
421
557
  detail: 'Class static members live on the class, not on individual instances.\n`stringifyJson` operates on instance shape, so statics are excluded.',
422
558
  },
423
559
  SJ013: {
424
560
  headline: "Symbol-keyed property `{0}` is silently not stringified by `stringifyJson` — symbol keys aren't JSON-representable.",
561
+ severity: 'warning',
425
562
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `stringifyJson` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
426
563
  },
427
564
  SJ014: {
428
565
  headline: "Union member(s) of type `{0}` can't be represented as data — `stringifyJson` drops them, so the union is stringified as its remaining members.",
566
+ severity: 'warning',
429
567
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `stringifyJson` stringified only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `stringifyJson` throws at build time instead.',
430
568
  },
431
569
  SJ015: {
432
570
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `stringifyJson` drops it, so this property is silently not stringified.',
571
+ severity: 'warning',
433
572
  detail: '`stringifyJson` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `stringifyJson` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
434
573
  },
435
- SUK010: {
436
- headline: 'Property `{0}` is a function — `stripUnknownKeys` does not handle function values, so this property is silently not stripped.',
437
- detail: '`stripUnknownKeys` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
438
- },
439
574
  TB001: {
440
- headline: 'Cannot serialise `{0}` to binary.',
575
+ headline: 'Type `{0}` can never be serialised to binary — the generated function will always fail.',
576
+ severity: 'error',
441
577
  detail: "`never` is the empty type — no value can ever inhabit it. A field\ntyped `never` cannot carry a runtime value, so there is nothing to\nencode/decode/validate.\n\nFix — use `unknown` if you really want to accept any value:\n interface User {\n- tag: never;\n+ tag: unknown; // narrow before use\n }\n\nFix — pick a concrete type matching your real data:\n interface User {\n- tag: never;\n+ tag: 'pending' | 'active' | 'done';\n }",
442
578
  },
443
579
  TB002: {
444
- headline: 'Cannot serialise `{0}` to binary.',
580
+ headline: 'Type `{0}` can never be serialised to binary — the generated function will always fail.',
581
+ severity: 'error',
445
582
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
446
583
  },
447
584
  TB003: {
448
- headline: 'Cannot serialise `{0}` to binary.',
585
+ headline: 'Type `{0}` can never be serialised to binary — the generated function will always fail.',
586
+ severity: 'error',
449
587
  detail: "Functions have no value form to serialise — their closure, prototype,\nand bound state aren't representable in JSON or binary.\n\nFix — drop the function from your type, or replace it with the data the\nfunction would produce:\n interface User {\n- getName: () => string;\n+ name: string;\n }",
450
588
  },
451
589
  TB004: {
452
- headline: 'Cannot serialise `{0}` to binary.',
590
+ headline: 'Type `{0}` can never be serialised to binary — the generated function will always fail.',
591
+ severity: 'error',
453
592
  detail: "Arrays of un-serialisable elements (`symbol[]`, `(() => void)[]`,\n`Map<K, V>[]`, etc.) can't be encoded — every element would need to be\nrepresentable, and these aren't. Dropping individual elements would\nchange the array length, so the encoder refuses rather than silently\nshipping a different shape.\n\nFix — change the element type to something serialisable:\n - type Items = (() => void)[];\n+ type Items = string[];",
454
593
  },
455
594
  TB005: {
456
- headline: 'Cannot serialise `{0}` to binary.',
595
+ headline: 'Type `{0}` can never be serialised to binary — the generated function will always fail.',
596
+ severity: 'error',
457
597
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
458
598
  },
459
599
  TB006: {
460
- headline: 'Cannot serialise `{0}` to binary.',
600
+ headline: 'Type `{0}` can never be serialised to binary — the generated function will always fail.',
601
+ severity: 'error',
461
602
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
462
603
  },
463
604
  TB010: {
464
605
  headline: 'Property `{0}` is a function — `toBinary` does not handle function values, so this property is silently not serialised.',
606
+ severity: 'warning',
465
607
  detail: '`toBinary` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
466
608
  },
467
609
  TB011: {
468
610
  headline: "Method `{0}` is silently not serialised by `toBinary` — methods aren't data.",
611
+ severity: 'warning',
469
612
  detail: "Class and object methods aren't part of the serialisable shape, so\n`toBinary` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
470
613
  },
471
614
  TB012: {
472
615
  headline: "Static member `{0}` is silently not serialised by `toBinary` — statics aren't part of instance data.",
616
+ severity: 'warning',
473
617
  detail: 'Class static members live on the class, not on individual instances.\n`toBinary` operates on instance shape, so statics are excluded.',
474
618
  },
475
619
  TB013: {
476
620
  headline: "Symbol-keyed property `{0}` is silently not serialised by `toBinary` — symbol keys aren't JSON-representable.",
621
+ severity: 'warning',
477
622
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `toBinary` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
478
623
  },
479
624
  TB014: {
480
625
  headline: "Union member(s) of type `{0}` can't be represented as data — `toBinary` drops them, so the union is serialised as its remaining members.",
626
+ severity: 'warning',
481
627
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `toBinary` serialised only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `toBinary` throws at build time instead.',
482
628
  },
483
629
  TB015: {
484
630
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `toBinary` drops it, so this property is silently not serialised.',
631
+ severity: 'warning',
485
632
  detail: '`toBinary` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `toBinary` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
486
633
  },
487
634
  TMP001: {
488
635
  headline: "Temporal type `{0}` resolved to `any` — the Temporal lib isn't in your tsconfig `lib`, so the generated validator would accept any value.",
636
+ severity: 'error',
489
637
  detail: 'ts-runtypes reads types through TypeScript\'s lib definitions, so it\ncan only validate `Temporal.*` types when the Temporal namespace is loaded.\nWith the lib missing, `{0}` silently degrades to `any` and the validator\nbecomes a no-op that accepts everything — almost never what you intended.\n\nFix — add "ESNext.Temporal" to your tsconfig:\n {\n "compilerOptions": {\n "lib": ["ES2023", "ESNext.Temporal"]\n }\n }',
490
638
  },
491
639
  UKE010: {
492
640
  headline: 'Property `{0}` is a function — `unknownKeyErrors` does not handle function values, so this property is silently not checked.',
641
+ severity: 'warning',
493
642
  detail: '`unknownKeyErrors` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
494
643
  },
495
644
  UKU010: {
496
645
  headline: 'Property `{0}` is a function — `unknownKeysToUndefined` does not handle function values, so this property is silently not cleared.',
646
+ severity: 'warning',
497
647
  detail: '`unknownKeysToUndefined` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
498
648
  },
499
649
  UKW010: {
500
650
  headline: 'Property `{0}` is a function — `unknownKeysToUndefinedWire` does not handle function values, so this property is silently not cleared.',
651
+ severity: 'warning',
501
652
  detail: '`unknownKeysToUndefinedWire` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
502
653
  },
503
654
  VE001: {
504
- headline: 'Cannot validate `{0}`.',
655
+ headline: 'Type `{0}` can never be validated — the generated function will always fail.',
656
+ severity: 'error',
505
657
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
506
658
  },
507
659
  VE002: {
508
- headline: 'Cannot validate `{0}`.',
660
+ headline: 'Type `{0}` can never be validated — the generated function will always fail.',
661
+ severity: 'error',
509
662
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
510
663
  },
511
664
  VE010: {
512
665
  headline: 'Property `{0}` is a function — `validationErrors` does not handle function values, so this property is silently not checked.',
666
+ severity: 'warning',
513
667
  detail: '`validationErrors` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
514
668
  },
515
669
  VE011: {
516
670
  headline: "Method `{0}` is silently not checked by `validationErrors` — methods aren't data.",
671
+ severity: 'warning',
517
672
  detail: "Class and object methods aren't part of the serialisable shape, so\n`validationErrors` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
518
673
  },
519
674
  VE012: {
520
675
  headline: "Static member `{0}` is silently not checked by `validationErrors` — statics aren't part of instance data.",
676
+ severity: 'warning',
521
677
  detail: 'Class static members live on the class, not on individual instances.\n`validationErrors` operates on instance shape, so statics are excluded.',
522
678
  },
523
679
  VE013: {
524
680
  headline: "Symbol-keyed property `{0}` is silently not checked by `validationErrors` — symbol keys aren't JSON-representable.",
681
+ severity: 'warning',
525
682
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `validationErrors` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
526
683
  },
527
684
  VE015: {
528
685
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `validationErrors` drops it, so this property is silently not checked.',
686
+ severity: 'warning',
529
687
  detail: '`validationErrors` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `validationErrors` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
530
688
  },
531
689
  VE020: {
532
690
  headline: '`validationErrors` on `any` / `unknown` always returns an empty error array — nothing is checked.',
691
+ severity: 'warning',
533
692
  detail: 'Same reason as VL021: `any` and `unknown` describe "anything", so the\nchecker has no structure to compare against. The returned error array\nwill always be empty.\n\nFix — narrow the type to the actual shape you expect:\n - const errors = createGetValidationErrors<unknown>()(value);\n+ const errors = createGetValidationErrors<User>()(value);',
534
693
  },
535
694
  VL001: {
536
- headline: 'Cannot validate `{0}`.',
695
+ headline: 'Type `{0}` can never be validated — the generated function will always fail.',
696
+ severity: 'error',
537
697
  detail: "Built-in classes like `Map`, `Set`, `WeakMap`, `WeakSet`, `Int8Array`,\n`Uint8Array`, `Buffer`, and `Promise` carry runtime state that doesn't\nsurvive a JSON or binary round-trip. Their instance identity is lost the\nmoment they're serialised.\n\nFix — convert to a plain object/array before serialising:\n // for Map<K, V>:\n const data = Object.fromEntries(yourMap);\n // for Set<T>:\n const data = [...yourSet];\n // for typed arrays:\n const data = Array.from(yourBuffer);\n\nFix — change the field type to a serialisable shape:\n interface User {\n- tags: Set<string>;\n+ tags: string[];\n }",
538
698
  },
539
699
  VL002: {
540
- headline: 'Cannot validate `{0}`.',
700
+ headline: 'Type `{0}` can never be validated — the generated function will always fail.',
701
+ severity: 'error',
541
702
  detail: "Every `symbol` value carries a unique runtime identity (`Symbol() !==\nSymbol()` even with the same description). That identity disappears the\nmoment it's serialised, and two symbols can't be compared across realms,\nworkers, or process boundaries. A validator that asserts \"this is a\nsymbol\" gives a false sense of safety — the value can't actually\nround-trip.\n\nFix — use a stable string key (often a literal union):\n - type Status = symbol;\n+ type Status = 'pending' | 'active' | 'done';",
542
703
  },
543
704
  VL010: {
544
705
  headline: 'Property `{0}` is a function — `validate` does not handle function values, so this property is silently not validated.',
706
+ severity: 'warning',
545
707
  detail: '`validate` works on JSON-shaped data; functions don\'t survive JSON, so\nthe emitter drops them. The rest of the object\'s behaviour is unaffected.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If you need a stricter checker that fails on\nmissing/extra function-typed members, watch the project roadmap.',
546
708
  },
547
709
  VL011: {
548
710
  headline: "Method `{0}` is silently not validated by `validate` — methods aren't data.",
711
+ severity: 'warning',
549
712
  detail: "Class and object methods aren't part of the serialisable shape, so\n`validate` excludes them. The rest of the type still works.\n\nIf you wanted the method's return value validated/serialised, expose it\nas a data property instead.",
550
713
  },
551
714
  VL012: {
552
715
  headline: "Static member `{0}` is silently not validated by `validate` — statics aren't part of instance data.",
716
+ severity: 'warning',
553
717
  detail: 'Class static members live on the class, not on individual instances.\n`validate` operates on instance shape, so statics are excluded.',
554
718
  },
555
719
  VL013: {
556
720
  headline: "Symbol-keyed property `{0}` is silently not validated by `validate` — symbol keys aren't JSON-representable.",
721
+ severity: 'warning',
557
722
  detail: "JSON only supports string keys; symbol-keyed properties are dropped\nfrom the serialised form. `validate` follows the same rule.\n\nFix — use a string key:\n - [Symbol.for('id')]: string;\n+ id: string;",
558
723
  },
559
724
  VL014: {
560
725
  headline: "Union member(s) of type `{0}` can't be represented as data — `validate` drops them, so the union is validated as its remaining members.",
726
+ severity: 'warning',
561
727
  detail: 'A union projects to its serialisable members only: `DataOnly<Date | symbol>`\nis `Date`. The dropped member(s) ({0}) carry no JSON-shaped value (symbol,\nfunction, Promise, or a non-serialisable built-in like `Map` / `Set` /\ntyped arrays), so `validate` validated only the members that remain.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md. If EVERY member of the union is non-serialisable the\nprojection is `never`, and `validate` throws at build time instead.',
562
728
  },
563
729
  VL015: {
564
730
  headline: 'Property `{0}` has a non-serialisable value type (symbol, Promise, or a non-serialisable built-in) — `validate` drops it, so this property is silently not validated.',
731
+ severity: 'warning',
565
732
  detail: '`validate` works on JSON-shaped data. A property whose value is a symbol,\na Promise, or a non-serialisable built-in (typed array, ArrayBuffer, …) carries\nno JSON-shaped value, so it is dropped: `DataOnly<{ {0}: symbol }>` is `{}`.\nThe rest of the object\'s behaviour is unaffected.\n\nNote the difference from a property that is only STRUCTURALLY unserialisable —\n`{0}: symbol[]` or `{0}: Map<string, symbol>` — which CANNOT be safely\ndropped (DataOnly keeps it as `never[]`): there `validate` throws at build\ntime instead.\n\nThis is by design — see the "validate contract — serializable data only"\nsection in CLAUDE.md.',
566
733
  },
567
734
  VL021: {
568
735
  headline: '`validate` on `any` / `unknown` always returns true — the validator accepts every value.',
736
+ severity: 'warning',
569
737
  detail: '`any` and `unknown` describe "anything", so a structural validator has\nnothing to check. The resulting function passes for every input —\nincluding the ones you probably wanted to reject.\n\nFix — narrow the type to the actual shape you expect:\n - const isUser = createValidate<unknown>();\n+ const isUser = createValidate<User>();',
570
738
  },
571
739
  };