@amritk/mjst 0.26.1 → 0.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI.md +3 -3
- package/README.md +31 -303
- package/dist/cli-config.d.ts +17 -9
- package/package.json +2 -2
package/AI.md
CHANGED
|
@@ -68,9 +68,9 @@ add one more entry point to the validator half and work under either flag.
|
|
|
68
68
|
## Common flags
|
|
69
69
|
|
|
70
70
|
`--schema` / `--schema-dir`, `--out-dir` / `--out-file`, `--input`,
|
|
71
|
-
`--validators` / `--validators-only`, `--
|
|
72
|
-
`--discriminator`, `--types-only`, `--build`, `--strict`,
|
|
73
|
-
`--unknown-keys count-keys|count-enumerable`, `--readonly`,
|
|
71
|
+
`--validators` / `--validators-only`, `--branch-errors`, `--examples`,
|
|
72
|
+
`--message-contracts`, `--discriminator`, `--types-only`, `--build`, `--strict`,
|
|
73
|
+
`--strip-unknown`, `--unknown-keys count-keys|count-enumerable`, `--readonly`,
|
|
74
74
|
`--import-ext ts|js`, `--config <path>`, `--resolve-remote` /
|
|
75
75
|
`--allowed-hosts` (SSRF-guarded remote `$ref`s).
|
|
76
76
|
|
package/README.md
CHANGED
|
@@ -117,13 +117,20 @@ carries both halves — and with `--build`. It cannot be combined with `--types-
|
|
|
117
117
|
Ask for no validators and there is nothing to collide with, so the parser keeps `schema.ts` and
|
|
118
118
|
its own type, and no `validation-result.ts` is emitted at all.
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
Pass `--validators-only` instead to emit the validator half with no parser at all: the type,
|
|
121
|
+
`isX`, `validateX`, and whichever of the flags below were asked for. It implies `--validators`,
|
|
122
|
+
and is the shape to reach for when the generated code only ever judges input it did not produce.
|
|
123
|
+
|
|
124
|
+
Four flags shape the validators, and each needs `--validators` (or `--validators-only`)
|
|
125
|
+
alongside it:
|
|
121
126
|
|
|
122
127
|
- `--check` adds `checkX`, the same `ValidationResult` as `validateX` but stopped at the first
|
|
123
128
|
violation, so it costs one error object rather than however many the document earns.
|
|
124
129
|
- `--coerce` adds `coerceX`, which moves scalars toward the declared type and then validates.
|
|
125
130
|
- `--repair` adds `repairX`, which coerces, validates, and then repairs each rejected position
|
|
126
131
|
to a value the schema supplies.
|
|
132
|
+
- `--branch-errors` explains a failing `anyOf`/`oneOf` with the errors of the branch it meant,
|
|
133
|
+
instead of the bare "must match a schema in anyOf".
|
|
127
134
|
|
|
128
135
|
### Running the generated output
|
|
129
136
|
|
|
@@ -264,6 +271,8 @@ rules:
|
|
|
264
271
|
|
|
265
272
|
By default `mjst lint` dereferences `$ref`, `$dynamicRef`/`$dynamicAnchor`, and `$recursiveRef`/`$recursiveAnchor` before running rules, so rules with `resolved: true` (the ruleset default) see through references. Internal and local cross-file refs resolve from disk — a finding on a cross-file node reports that file's own `line:column` — as long as they stay inside the linted document's own directory; use `--allowed-roots` to let a document reach a shared sibling folder. Remote refs are only fetched when you opt in with `--resolve-remote` or `--allowed-hosts`.
|
|
266
273
|
|
|
274
|
+
A YAML file holding several `---` documents is linted as an array of them, and each document resolves as a root of its own: a `#/…` ref points into the document it is written in, and its findings land on that document's own lines. A `$ref` to a YAML file that does not parse cleanly (a syntax error, or more than one document) is reported as an unresolved ref at the `$ref`, like a missing file.
|
|
275
|
+
|
|
267
276
|
The exit code is `0` when no finding reaches `--fail-severity`, `1` when one does, and `2` on a usage error. Run `mjst lint --help` for the full list.
|
|
268
277
|
|
|
269
278
|
---
|
|
@@ -315,7 +324,7 @@ npx mjst markdown ./config.schema.json --table --readme ./README.md
|
|
|
315
324
|
| --- | --- |
|
|
316
325
|
| `<schema>` | The JSON Schema to document (positional, required). |
|
|
317
326
|
| `--out-dir` | Directory the prose reference pages are written to (default: the current directory). |
|
|
318
|
-
| `--file` | Output path of the index page (default: the schema's `x-mjst.file`, then `index.md`). |
|
|
327
|
+
| `--file` | Output path of the index page (default: the schema's `x-mjst.markdown.file`, then `index.md`). |
|
|
319
328
|
| `--title` | Page title (default: the schema's `title`). |
|
|
320
329
|
| `--language` | Fence language for derived examples and literals (default: `json`). |
|
|
321
330
|
| `--layout` | Default layout for nested properties: `headings` (default), `table`, or `none`. |
|
|
@@ -329,7 +338,23 @@ npx mjst markdown ./config.schema.json --table --readme ./README.md
|
|
|
329
338
|
| `--table` | Render the HTML config table instead of the prose pages. |
|
|
330
339
|
| `--readme` | Markdown file the table is spliced into (default: `README.md`). Requires `--table`. |
|
|
331
340
|
|
|
332
|
-
Under `--table` only the content between
|
|
341
|
+
Under `--table` only the content between the `config-table-start` and
|
|
342
|
+
`config-table-end` HTML comment markers is replaced; a file that exists without
|
|
343
|
+
both markers is left untouched and the run fails, rather than clobbering
|
|
344
|
+
hand-written prose.
|
|
345
|
+
The prose pages have no such deal — the generator owns them and overwrites them
|
|
346
|
+
wholesale. The table and page flags are mutually exclusive: passing a page flag
|
|
347
|
+
with `--table` is a usage error rather than a silently ignored option.
|
|
348
|
+
|
|
349
|
+
The exit code is `0` on success, `1` when generation fails (malformed schema, a
|
|
350
|
+
readme without markers, a page declaration the schema contradicts), and `2` on a
|
|
351
|
+
usage error. Run `mjst markdown --help` for details.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Configuration reference
|
|
356
|
+
|
|
357
|
+
<!-- config-table-start -->
|
|
333
358
|
<table>
|
|
334
359
|
<thead>
|
|
335
360
|
<tr>
|
|
@@ -437,7 +462,7 @@ Under `--table` only the content between `<!-- config-table-start -->
|
|
|
437
462
|
<td align="center"><code>false</code></td>
|
|
438
463
|
</tr>
|
|
439
464
|
<tr>
|
|
440
|
-
<td colspan="4">Also emit a coerceX alongside each validateX. It moves scalars toward the type the schema declares and then runs the very same validateX over the result, so a config value written the YAML way reads the same whether it came from a config file or an extension. The input is never modified and nothing is substituted: a value that cannot be coerced into a valid one reaches the validator untouched, so the error names what the caller actually wrote, with the keyword and params that rejected it. The table is Ajv's coerceTypes minus the cells where Ajv guesses — no whitespace-to-zero, no hex or Infinity strings, and nothing coerced to or from null, deliberately, since null usually means not set — so every scalar this coerces, Ajv coerces to the same value, and the rest become errors rather than silent repairs. Coercion reaches through $ref, allOf, if/then/else and unions. At a union a value that already matches a branch as written is left alone; otherwise each branch coerces it its own way, and the result is taken only when every branch that then accepts it agrees on it. So the answer does not depend on the order the union was written in, and a const false branch keeps false where Ajv turns it into the string false through an earlier string branch. A valid document comes back as the very same object
|
|
465
|
+
<td colspan="4">Also emit a coerceX alongside each validateX. It moves scalars toward the type the schema declares and then runs the very same validateX over the result, so a config value written the YAML way reads the same whether it came from a config file or an extension. The input is never modified and nothing is substituted: a value that cannot be coerced into a valid one reaches the validator untouched, so the error names what the caller actually wrote, with the keyword and params that rejected it. The table is Ajv's coerceTypes minus the cells where Ajv guesses — no whitespace-to-zero, no hex or Infinity strings, and nothing coerced to or from null, deliberately, since null usually means not set — so every scalar this coerces, Ajv coerces to the same value, and the rest become errors rather than silent repairs. Coercion reaches through $ref, allOf, if/then/else and unions. At a union a value that already matches a branch as written is left alone; otherwise each branch coerces it its own way, and the result is taken only when every branch that then accepts it agrees on it. So the answer does not depend on the order the union was written in, and a const false branch keeps false where Ajv turns it into the string false through an earlier string branch. A valid document comes back as the very same object. Where the caller cannot let Ajv mutate its input, so Ajv has to clone it first, coerceX runs 5–36× faster on valid input and 1.4–2.1× faster on input that needs coercing; where the caller lets Ajv coerce its document in place there is no clone to save, and on a large document Ajv can come out modestly ahead. Requires validators.</td>
|
|
441
466
|
</tr>
|
|
442
467
|
<tr>
|
|
443
468
|
<td>🔧 <code>repair</code></td>
|
|
@@ -446,7 +471,7 @@ Under `--table` only the content between `<!-- config-table-start -->
|
|
|
446
471
|
<td align="center"><code>false</code></td>
|
|
447
472
|
</tr>
|
|
448
473
|
<tr>
|
|
449
|
-
<td colspan="4">Also emit a repairX alongside each validateX. It coerces, runs the very same validateX, and then repairs each position the validator rejected to a value the schema itself supplies — a default, a const, the first enum member, or a fallback built to satisfy that position's own bounds — re-validating until the document is accepted or nothing further can be repaired. The repairs it reports are the validator's own errors, the ones a repair was found for, so a caller logs the same path, keyword and params it would have been rejected with and the two can never drift apart. A document needing nothing comes back valid with an empty repairs list; one fully repaired comes back valid with a non-empty one, leaving the caller to decide whether that is acceptable; one that could not be fully repaired comes back invalid carrying both the repairs applied and the errors still outstanding. The input is never modified and everything the repair did not touch is shared. Implies coerce, so a value merely written in the wrong type is right before the validator sees it and never counts as a repair. Requires validators.</td>
|
|
474
|
+
<td colspan="4">Also emit a repairX alongside each validateX. It coerces, runs the very same validateX, and then repairs each position the validator rejected to a value the schema itself supplies — a default, a const, the first enum member, or a fallback built to satisfy that position's own bounds — re-validating until the document is accepted or nothing further can be repaired. The repairs it reports are the validator's own errors, the ones a repair was found for, so a caller logs the same path, keyword and params it would have been rejected with and the two can never drift apart. A document needing nothing comes back valid with an empty repairs list; one fully repaired comes back valid with a non-empty one, leaving the caller to decide whether that is acceptable; one that could not be fully repaired comes back invalid carrying both the repairs applied and the errors still outstanding. The input is never modified and everything the repair did not touch is shared. Repair is an autofix tool, not a stricter validator: it accepts documents validateX rejects by repairing them, and accepting only a valid result with an empty repairs list gives exactly the verdicts coerceX gives, so a loader that must reject a typo wants coerceX and repairX is for producing a fixed document. Implies coerce, so a value merely written in the wrong type is right before the validator sees it and never counts as a repair. Requires validators.</td>
|
|
450
475
|
</tr>
|
|
451
476
|
<tr>
|
|
452
477
|
<td>🌿 <code>branchErrors</code></td>
|
|
@@ -455,304 +480,7 @@ Under `--table` only the content between `<!-- config-table-start -->
|
|
|
455
480
|
<td align="center"><code>false</code></td>
|
|
456
481
|
</tr>
|
|
457
482
|
<tr>
|
|
458
|
-
<td colspan="4">Explain a failing anyOf/oneOf with the errors of the branch it meant, instead of the bare "must match a schema in anyOf" that names no field and no reason. The branch that rejected the value's kind is dropped first, so a string-or-object union is left with the one branch that was talking about this value; where several remain, a discriminator breaks the tie; where none stands out, nothing extra is reported. Off by default and off costs nothing — the generated code is exactly what it would be without the option. On,
|
|
459
|
-
</tr>
|
|
460
|
-
<tr>
|
|
461
|
-
<td>🎲 <code>examples</code></td>
|
|
462
|
-
<td><code>--examples</code></td>
|
|
463
|
-
<td><code>boolean</code></td>
|
|
464
|
-
<td align="center"><code>false</code></td>
|
|
465
|
-
</tr>
|
|
466
|
-
<tr>
|
|
467
|
-
<td colspan="4">Also emit test-data files for every schema into an examples/ subdirectory of the output destination: a fast-check arbitrary (FooArbitrary) that produces schema-valid values and a concrete fooExample value. Under schemaDir the examples mirror the schema layout so they never collide with the parser output. The generated arbitraries import fast-check, which consumers must install as a (dev) dependency; the static example values have no runtime dependencies.</td>
|
|
468
|
-
</tr>
|
|
469
|
-
<tr>
|
|
470
|
-
<td>🔌 <code>messageContracts</code></td>
|
|
471
|
-
<td><code>--message-contracts</code></td>
|
|
472
|
-
<td><code>boolean</code></td>
|
|
473
|
-
<td align="center"><code>false</code></td>
|
|
474
|
-
</tr>
|
|
475
|
-
<tr>
|
|
476
|
-
<td colspan="4">Also emit a @amritk/api channel contract per AsyncAPI channel into a contracts/ subdirectory: one defineMessages({ ... }) module naming every message that channel carries — keyed by the value its discriminator property carries on the wire, with that property stripped out of each payload because the runtime removes it before validating — plus a barrel re-exporting them. Requires input 'asyncapi', which is the only input that has channels and operations to project from. Incompatible with typesOnly: a contract is a runtime value, not a type. The generated modules import @amritk/api, which the consuming project must install; it is not a dependency of the generated parsers.</td>
|
|
477
|
-
</tr>
|
|
478
|
-
<tr>
|
|
479
|
-
<td>🏷️ <code>discriminator</code></td>
|
|
480
|
-
<td><code>--discriminator <prop></code></td>
|
|
481
|
-
<td><code>string</code></td>
|
|
482
|
-
<td align="center"><code>"type"</code></td>
|
|
483
|
-
</tr>
|
|
484
|
-
<tr>
|
|
485
|
-
<td colspan="4">Property name that says which message a frame is, for the contracts messageContracts emits. Defaults to 'type', matching @amritk/api. A channel that declares its own with 'x-mjst': { 'discriminator': '...' } keeps it — one flag covers a whole run, and a run may span channels that disagree. Only meaningful together with messageContracts.<br><strong>Examples:</strong> <code>"event"</code></td>
|
|
486
|
-
</tr>
|
|
487
|
-
<tr>
|
|
488
|
-
<td>🔨 <code>build</code></td>
|
|
489
|
-
<td><code>--build</code></td>
|
|
490
|
-
<td><code>boolean</code></td>
|
|
491
|
-
<td align="center"><code>false</code></td>
|
|
492
|
-
</tr>
|
|
493
|
-
<tr>
|
|
494
|
-
<td colspan="4">Compile the generated TypeScript files to .js and .d.ts output. A temporary tsconfig is written to the output directory, tsc is invoked, and the intermediate .ts source files are removed when compilation succeeds.</td>
|
|
495
|
-
</tr>
|
|
496
|
-
<tr>
|
|
497
|
-
<td>💥 <code>force</code></td>
|
|
498
|
-
<td><code>--force</code></td>
|
|
499
|
-
<td><code>boolean</code></td>
|
|
500
|
-
<td align="center"><code>false</code></td>
|
|
501
|
-
</tr>
|
|
502
|
-
<tr>
|
|
503
|
-
<td colspan="4">Deprecated and ignored. Generated files always replace whatever is at their path, so this flag no longer does anything; it is still accepted so existing scripts and config files keep running. Reverting an unwanted replacement is version control's job. Compilation under build still only removes the intermediate sources the run itself generated.</td>
|
|
504
|
-
</tr>
|
|
505
|
-
<tr>
|
|
506
|
-
<td>⚠️ <code>logWarnings</code></td>
|
|
507
|
-
<td><code>--log-warnings</code></td>
|
|
508
|
-
<td><code>boolean</code></td>
|
|
509
|
-
<td align="center"><code>false</code></td>
|
|
510
|
-
</tr>
|
|
511
|
-
<tr>
|
|
512
|
-
<td colspan="4">Emit a console.warn in the generated parsers for every input key that is not declared in the schema's properties. Useful for detecting schema drift or unexpected data shapes at runtime.</td>
|
|
513
|
-
</tr>
|
|
514
|
-
<tr>
|
|
515
|
-
<td>🚫 <code>strict</code></td>
|
|
516
|
-
<td><code>--strict</code></td>
|
|
517
|
-
<td><code>boolean</code></td>
|
|
518
|
-
<td align="center"><code>false</code></td>
|
|
519
|
-
</tr>
|
|
520
|
-
<tr>
|
|
521
|
-
<td colspan="4">Generate parsers that throw on type/shape mismatches (wrong type, missing required property, enum/pattern/min/max violations) instead of coercing invalid input to default values. When a schema sets additionalProperties: false, undeclared keys throw too; otherwise they are still allowed.</td>
|
|
522
|
-
</tr>
|
|
523
|
-
<tr>
|
|
524
|
-
<td>🧹 <code>stripUnknown</code></td>
|
|
525
|
-
<td><code>--strip-unknown</code></td>
|
|
526
|
-
<td><code>boolean</code></td>
|
|
527
|
-
<td align="center"><code>false</code></td>
|
|
528
|
-
</tr>
|
|
529
|
-
<tr>
|
|
530
|
-
<td colspan="4">Build each parser's result from the schema's declared properties only, silently dropping undeclared input keys at every nesting level (zod's .strip()). Extras are never a validation error, so this composes with strict (which still throws on wrong types and missing required properties) and yields to additionalProperties: false, which rejects rather than strips in strict mode.</td>
|
|
531
|
-
</tr>
|
|
532
|
-
<tr>
|
|
533
|
-
<td>🔡 <code>caseInsensitive</code></td>
|
|
534
|
-
<td><code>--case-insensitive</code></td>
|
|
535
|
-
<td><code>boolean</code></td>
|
|
536
|
-
<td align="center"><code>false</code></td>
|
|
537
|
-
</tr>
|
|
538
|
-
<tr>
|
|
539
|
-
<td colspan="4">Normalize a mis-cased string to the exact casing of a declared enum/const member it matches case-insensitively (e.g. hElLo → hello) instead of coercing to the default. Coerce mode only — strict parsers still reject a casing mismatch. Correctly-cased input keeps the exact-match fast path, so the hot path is unaffected.</td>
|
|
540
|
-
</tr>
|
|
541
|
-
<tr>
|
|
542
|
-
<td>🔒 <code>readonly</code></td>
|
|
543
|
-
<td><code>--readonly</code></td>
|
|
544
|
-
<td><code>boolean</code></td>
|
|
545
|
-
<td align="center"><code>false</code></td>
|
|
546
|
-
</tr>
|
|
547
|
-
<tr>
|
|
548
|
-
<td colspan="4">Emit every property, array, and record in the generated type definitions as readonly, producing deeply immutable types. Affects type definitions only; the generated parsers still build and return plain objects.</td>
|
|
549
|
-
</tr>
|
|
550
|
-
<tr>
|
|
551
|
-
<td>🔢 <code>unknownKeys</code></td>
|
|
552
|
-
<td><code>--unknown-keys <strategy></code></td>
|
|
553
|
-
<td><code>string</code></td>
|
|
554
|
-
<td align="center"><code>"count-keys"</code></td>
|
|
555
|
-
</tr>
|
|
556
|
-
<tr>
|
|
557
|
-
<td colspan="4">How generated fast paths (parsers and validators alike) prove a closed object — additionalProperties: false, or a stripUnknown build — carries no undeclared key. 'count-keys' compares Object.keys(obj).length and is the faster form on JavaScriptCore (Bun), where a for…in over a non-extensible object takes the engine's slow path and halves strict-parse throughput. 'count-enumerable' counts with for…in and allocates nothing; it was the faster form on Node 22, but on Node 26 'count-keys' ties or wins there too. Keep the default unless you are pinned to an older V8 and have measured your own shapes; the generated code never detects its runtime.<br><strong>Allowed:</strong> <code>"count-enumerable"</code>, <code>"count-keys"</code></td>
|
|
558
|
-
</tr>
|
|
559
|
-
<tr>
|
|
560
|
-
<td>🧪 <code>formats</code></td>
|
|
561
|
-
<td><code>--formats <list></code></td>
|
|
562
|
-
<td><code>string | array</code></td>
|
|
563
|
-
<td align="center"></td>
|
|
564
|
-
</tr>
|
|
565
|
-
<tr>
|
|
566
|
-
<td colspan="4">String formats the generated validators enforce: "all", or a comma-separated list (e.g. uuid,date-time,email). Omitted, format stays an annotation — JSON Schema's own reading, and what @amritk/runtime-validators does when given no formats. Set it to whatever validates the same schemas at runtime (@amritk/lint and createApi({ formats }) run the interpreter with formats on) so the build-time and runtime answers agree. Both validateX and the flat isX check them, against a formats.ts emitted alongside the validators.</td>
|
|
567
|
-
</tr>
|
|
568
|
-
<tr>
|
|
569
|
-
<td>🧰 <code>helpers</code></td>
|
|
570
|
-
<td><code>--helpers <mode></code></td>
|
|
571
|
-
<td><code>string</code></td>
|
|
572
|
-
<td align="center"></td>
|
|
573
|
-
</tr>
|
|
574
|
-
<tr>
|
|
575
|
-
<td colspan="4">Controls how generated parsers reference their runtime helpers. 'package' emits imports from @amritk/helpers (requires it to be installed in the consumer project). 'embedded' ships the helper source under outDir/_helpers/ so the output is self-contained. When omitted, the CLI auto-detects: it picks 'package' only when @amritk/helpers is a declared dependency (dependencies/devDependencies) of the nearest package.json above outDir, otherwise 'embedded'. Requiring a declaration (rather than mere resolvability) keeps auto-detected 'package' output portable across pnpm/isolated installs, where an undeclared transitive @amritk/helpers is not reachable at runtime.<br><strong>Allowed:</strong> <code>"package"</code>, <code>"embedded"</code></td>
|
|
576
|
-
</tr>
|
|
577
|
-
<tr>
|
|
578
|
-
<td>🏷️ <code>typeSuffix</code></td>
|
|
579
|
-
<td><code>--type-suffix <suffix></code></td>
|
|
580
|
-
<td><code>string</code></td>
|
|
581
|
-
<td align="center"><code>""</code></td>
|
|
582
|
-
</tr>
|
|
583
|
-
<tr>
|
|
584
|
-
<td colspan="4">Suffix appended to every generated type name derived from a $ref (e.g. 'Object' turns Contact into ContactObject). Defaults to no suffix. The root type name is used verbatim and is unaffected.</td>
|
|
585
|
-
</tr>
|
|
586
|
-
<tr>
|
|
587
|
-
<td>🪧 <code>banner</code></td>
|
|
588
|
-
<td><code>--banner [text]</code></td>
|
|
589
|
-
<td><code>boolean | string</code></td>
|
|
590
|
-
<td align="center"><code>false</code></td>
|
|
591
|
-
</tr>
|
|
592
|
-
<tr>
|
|
593
|
-
<td colspan="4">Prepend a comment header to every generated file (excluding _helpers/). true uses the default message ('This file was auto-generated by @amritk/mjst…'); a string uses that text as the message body, wrapped in a JSDoc block; false or omitted emits no header. On the CLI, --banner alone enables the default message and --banner <text> sets a custom one.</td>
|
|
594
|
-
</tr>
|
|
595
|
-
<tr>
|
|
596
|
-
<td>🔗 <code>importExt</code></td>
|
|
597
|
-
<td><code>--import-ext <ext></code></td>
|
|
598
|
-
<td><code>string</code></td>
|
|
599
|
-
<td align="center"><code>"ts"</code></td>
|
|
600
|
-
</tr>
|
|
601
|
-
<tr>
|
|
602
|
-
<td colspan="4">Extension emitted on every relative import specifier in the generated output (cross-file $ref imports, the index barrel, and embedded-helper imports). 'ts' (default) emits the literal on-disk paths so the generated .ts sources load directly under Bun, Node's type stripping (Node 22.6+ with --experimental-strip-types, unflagged from 22.18/23), and tsc when the consumer sets allowImportingTsExtensions — no build step required. 'js' is the standard TS NodeNext form ('./x.js' resolving to a sibling x.ts) for output you compile; it is selected automatically under 'build' since tsc refuses to emit from .ts specifiers. Passing --import-ext ts together with --build is an error.<br><strong>Allowed:</strong> <code>"js"</code>, <code>"ts"</code></td>
|
|
603
|
-
</tr>
|
|
604
|
-
<tr>
|
|
605
|
-
<td>🌱 <code>rootType</code></td>
|
|
606
|
-
<td><code>--root-type <name></code></td>
|
|
607
|
-
<td><code>string</code></td>
|
|
608
|
-
<td align="center"></td>
|
|
609
|
-
</tr>
|
|
610
|
-
<tr>
|
|
611
|
-
<td colspan="4">Name for the root type of a single --schema run (e.g. 'Program' yields parseProgram / validateProgram). When omitted, the name is derived from the schema's title, falling back to the schema filename in PascalCase (spec-plan.json → SpecPlan) and then to 'Document'. Not supported with --schema-dir, where each schema derives its own root type from its filename.<br><strong>Examples:</strong> <code>"Program"</code></td>
|
|
612
|
-
</tr>
|
|
613
|
-
<tr>
|
|
614
|
-
<td>🌐 <code>resolveRemote</code></td>
|
|
615
|
-
<td><code>--resolve-remote</code></td>
|
|
616
|
-
<td><code>boolean</code></td>
|
|
617
|
-
<td align="center"><code>false</code></td>
|
|
618
|
-
</tr>
|
|
619
|
-
<tr>
|
|
620
|
-
<td colspan="4">Allow fetching http(s) $refs while dereferencing a schema that contains cross-file or remote references. Off by default, so generation stays offline unless asked: a schema with a remote $ref fails rather than making a network call. Local cross-file $refs are always resolved and do not need this flag. A non-empty allowedHosts implies this.</td>
|
|
621
|
-
</tr>
|
|
622
|
-
<tr>
|
|
623
|
-
<td>🛡️ <code>allowedHosts</code></td>
|
|
624
|
-
<td><code>--allowed-hosts <hosts></code></td>
|
|
625
|
-
<td><code>array</code></td>
|
|
626
|
-
<td align="center"></td>
|
|
627
|
-
</tr>
|
|
628
|
-
<tr>
|
|
629
|
-
<td colspan="4">Restrict remote $ref fetches to these hosts (e.g. api.example.com). Accepts a comma-separated list or the flag repeated. Providing an allow-list is itself an opt-in to remote fetching (it implies resolveRemote), and each listed host bypasses the private-host guard.<br><strong>Examples:</strong> <code>["schemas.example.com"]</code></td>
|
|
630
|
-
</tr>
|
|
631
|
-
<tr>
|
|
632
|
-
<td>🔓 <code>allowPrivateHosts</code></td>
|
|
633
|
-
<td><code>--allow-private-hosts</code></td>
|
|
634
|
-
<td><code>boolean</code></td>
|
|
635
|
-
<td align="center"><code>false</code></td>
|
|
636
|
-
</tr>
|
|
637
|
-
<tr>
|
|
638
|
-
<td colspan="4">Permit remote $refs to private, loopback, and link-local hosts. Off by default: such targets are refused as a best-effort SSRF guard (notably the 169.254.169.254 cloud-metadata endpoint). An explicit allowedHosts entry always bypasses this guard.</td>
|
|
639
|
-
</tr>
|
|
640
|
-
<tr>
|
|
641
|
-
<td>📂 <code>allowedRoots</code></td>
|
|
642
|
-
<td><code>--allowed-roots <dirs></code></td>
|
|
643
|
-
<td><code>array</code></td>
|
|
644
|
-
<td align="center"></td>
|
|
645
|
-
</tr>
|
|
646
|
-
<tr>
|
|
647
|
-
<td colspan="4">Extra directories a local (cross-file) $ref may resolve into. By default a local $ref is confined to the schema's own directory, so '{"$ref": "../../../secrets.env"}' (or an absolute path) reads nothing. List a root here for the ordinary split-spec layout where v1/api.json refers to a shared common/user.json one level up. The schema's own directory is always allowed, so these roots widen the default rather than replacing it, and a $ref that still lands outside every root is refused. Accepts a comma-separated list or the flag repeated; relative entries resolve against the current working directory, from a config file as well as from the flag.<br><strong>Examples:</strong> <code>["./specs"]</code></td>
|
|
648
|
-
</tr>
|
|
649
|
-
<tr>
|
|
650
|
-
<td>⚙️ <code>config</code></td>
|
|
651
|
-
<td><code>--config <path></code></td>
|
|
652
|
-
<td><code>string</code></td>
|
|
653
|
-
<td align="center"></td>
|
|
654
|
-
</tr>
|
|
655
|
-
<tr>
|
|
656
|
-
<td colspan="4">Path to a JSON config file. Keys match the option names in this schema (schema, schemaDir, input, export, outDir, outFile, typesOnly, validators, examples, build, force, logWarnings, strict, stripUnknown, caseInsensitive, readonly, unknownKeys, helpers, typeSuffix, banner, importExt, rootType, resolveRemote, allowedHosts, allowPrivateHosts, allowedRoots) and are validated: an unknown key or a wrong value type fails the run instead of being ignored. CLI flags take precedence over config file values.<br><strong>Examples:</strong> <code>"./mjst.config.json"</code></td>
|
|
657
|
-
</tr>
|
|
658
|
-
</tbody>
|
|
659
|
-
</table>
|
|
660
|
-
<!-- config-table-end -->` is replaced; a file that exists without both markers
|
|
661
|
-
is left untouched and the run fails, rather than clobbering hand-written prose.
|
|
662
|
-
The prose pages have no such deal — the generator owns them and overwrites them
|
|
663
|
-
wholesale. The table and page flags are mutually exclusive: passing a page flag
|
|
664
|
-
with `--table` is a usage error rather than a silently ignored option.
|
|
665
|
-
|
|
666
|
-
The exit code is `0` on success, `1` when generation fails (malformed schema, a
|
|
667
|
-
readme without markers, a page declaration the schema contradicts), and `2` on a
|
|
668
|
-
usage error. Run `mjst markdown --help` for details.
|
|
669
|
-
|
|
670
|
-
---
|
|
671
|
-
|
|
672
|
-
## Configuration reference
|
|
673
|
-
|
|
674
|
-
<!-- config-table-start -->
|
|
675
|
-
<table>
|
|
676
|
-
<thead>
|
|
677
|
-
<tr>
|
|
678
|
-
<th>Property</th>
|
|
679
|
-
<th>CLI Flag</th>
|
|
680
|
-
<th>Type</th>
|
|
681
|
-
<th align="center">Default</th>
|
|
682
|
-
</tr>
|
|
683
|
-
</thead>
|
|
684
|
-
<tbody>
|
|
685
|
-
<tr>
|
|
686
|
-
<td>📄 <code>schema</code></td>
|
|
687
|
-
<td><code>--schema <path></code></td>
|
|
688
|
-
<td><code>string</code></td>
|
|
689
|
-
<td align="center"></td>
|
|
690
|
-
</tr>
|
|
691
|
-
<tr>
|
|
692
|
-
<td colspan="4">Path to the schema to process. With the default 'json' input this is a JSON Schema file that is read and parsed. With any other input format it is a JS/TS module that exports a schema, which is loaded and converted to JSON Schema via the matching adapter. Either 'schema' or 'schemaDir' is required.<br><strong>Examples:</strong> <code>"./schema.json"</code></td>
|
|
693
|
-
</tr>
|
|
694
|
-
<tr>
|
|
695
|
-
<td>🗂️ <code>schemaDir</code></td>
|
|
696
|
-
<td><code>--schema-dir <dir></code></td>
|
|
697
|
-
<td><code>string</code></td>
|
|
698
|
-
<td align="center"></td>
|
|
699
|
-
</tr>
|
|
700
|
-
<tr>
|
|
701
|
-
<td colspan="4">Path to a directory of JSON Schema files. When set, the CLI walks the directory recursively, generates parsers for every '*.json' schema it finds, and mirrors the directory layout under outDir (each schema lands in its own subdirectory). The runtime helpers are emitted once into a shared outDir/_helpers/ that every nested parser imports from. Mutually exclusive with 'schema'; when both are present 'schemaDir' wins. Only JSON Schema input is supported in this mode.<br><strong>Examples:</strong> <code>"./schemas"</code></td>
|
|
702
|
-
</tr>
|
|
703
|
-
<tr>
|
|
704
|
-
<td>🔌 <code>input</code></td>
|
|
705
|
-
<td><code>--input <format></code></td>
|
|
706
|
-
<td><code>string</code></td>
|
|
707
|
-
<td align="center"><code>"json"</code></td>
|
|
708
|
-
</tr>
|
|
709
|
-
<tr>
|
|
710
|
-
<td colspan="4">Source format of the schema. 'json' (default) reads a JSON Schema file directly. 'avro' reads an Apache Avro '.avsc' JSON document and converts it, needing no extra library. 'asyncapi' reads an AsyncAPI 2.x/3.0 document (JSON or YAML) and generates from every message payload/headers schema it declares. Any other format loads 'schema' as a module and converts it to JSON Schema with the matching adapter. Supported: 'typebox', 'zod' (zod v4+), 'valibot' (with @valibot/to-json-schema), and 'effect' — each requires the corresponding library installed in your project.<br><strong>Allowed:</strong> <code>"json"</code>, <code>"typebox"</code>, <code>"zod"</code>, <code>"valibot"</code>, <code>"effect"</code>, <code>"avro"</code>, <code>"asyncapi"</code></td>
|
|
711
|
-
</tr>
|
|
712
|
-
<tr>
|
|
713
|
-
<td>📦 <code>export</code></td>
|
|
714
|
-
<td><code>--export <name></code></td>
|
|
715
|
-
<td><code>string</code></td>
|
|
716
|
-
<td align="center"></td>
|
|
717
|
-
</tr>
|
|
718
|
-
<tr>
|
|
719
|
-
<td colspan="4">Which export of the schema module to use when 'input' names a module format (anything but 'json', 'avro', and 'asyncapi', which are read as data). Defaults to the default export, or the sole named export when the module has exactly one.</td>
|
|
720
|
-
</tr>
|
|
721
|
-
<tr>
|
|
722
|
-
<td>📁 <code>outDir</code></td>
|
|
723
|
-
<td><code>--out-dir <dir></code></td>
|
|
724
|
-
<td><code>string</code></td>
|
|
725
|
-
<td align="center"></td>
|
|
726
|
-
</tr>
|
|
727
|
-
<tr>
|
|
728
|
-
<td colspan="4">Output directory for generated TypeScript files. The directory is created automatically if it does not exist. Subdirectories are created as needed when a generated file includes a nested path. Mutually exclusive with 'outFile'.<br><strong>Examples:</strong> <code>"./generated"</code></td>
|
|
729
|
-
</tr>
|
|
730
|
-
<tr>
|
|
731
|
-
<td>📄 <code>outFile</code></td>
|
|
732
|
-
<td><code>--out-file <file></code></td>
|
|
733
|
-
<td><code>string</code></td>
|
|
734
|
-
<td align="center"></td>
|
|
735
|
-
</tr>
|
|
736
|
-
<tr>
|
|
737
|
-
<td colspan="4">Output everything to a single TypeScript file instead of a directory. Every generated definition is concatenated into one self-contained file and the cross-file imports are dropped. Mutually exclusive with 'outDir'. Currently supported only together with 'typesOnly'.<br><strong>Examples:</strong> <code>"./generated/schema.ts"</code></td>
|
|
738
|
-
</tr>
|
|
739
|
-
<tr>
|
|
740
|
-
<td>🏷️ <code>typesOnly</code></td>
|
|
741
|
-
<td><code>--types-only</code></td>
|
|
742
|
-
<td><code>boolean</code></td>
|
|
743
|
-
<td align="center"><code>false</code></td>
|
|
744
|
-
</tr>
|
|
745
|
-
<tr>
|
|
746
|
-
<td colspan="4">Generate only TypeScript type definitions without parser functions. Useful when you only need the type shapes and do not need runtime validation.</td>
|
|
747
|
-
</tr>
|
|
748
|
-
<tr>
|
|
749
|
-
<td>✅ <code>validators</code></td>
|
|
750
|
-
<td><code>--validators</code></td>
|
|
751
|
-
<td><code>boolean</code></td>
|
|
752
|
-
<td align="center"><code>false</code></td>
|
|
753
|
-
</tr>
|
|
754
|
-
<tr>
|
|
755
|
-
<td colspan="4">Also emit validation functions alongside the parsers. For every generated type X the CLI writes a validateX (returning a rich ValidationResult with JSON-Pointer error paths) and an isX boolean type guard. They share one directory and one declaration of X with the parser: the type and the validators land in x.ts, and the parser moves to x.parse.ts beside them, importing the type from there. Works with both schema and schemaDir. Incompatible with typesOnly and outFile, which produce no runtime code.</td>
|
|
483
|
+
<td colspan="4">Explain a failing anyOf/oneOf with the errors of the branch it meant, instead of the bare "must match a schema in anyOf" that names no field and no reason. The branch that rejected the value's kind is dropped first, so a string-or-object union is left with the one branch that was talking about this value; where several remain, a discriminator breaks the tie; where none stands out, nothing extra is reported. Off by default and off costs nothing — the generated code is exactly what it would be without the option. On, a combinator that passes costs what it costs with the option off, since its branches are tested by the same functions; only an anyOf/oneOf that has already failed asks its branches again for what each one complained about, so the price is paid on invalid input alone. Requires validators.</td>
|
|
756
484
|
</tr>
|
|
757
485
|
<tr>
|
|
758
486
|
<td>🎲 <code>examples</code></td>
|
package/dist/cli-config.d.ts
CHANGED
|
@@ -112,10 +112,12 @@ export type CliConfig = {
|
|
|
112
112
|
* branch listed before a `const: false` one.
|
|
113
113
|
*
|
|
114
114
|
* A document that is already valid is answered by `isX` alone where that is a
|
|
115
|
-
* standalone guard, and handed back as the very same object.
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
115
|
+
* standalone guard, and handed back as the very same object. Where the caller
|
|
116
|
+
* cannot let Ajv mutate its input, so Ajv has to clone it first, `coerceX` runs
|
|
117
|
+
* 5–36× faster on valid input and 1.4–2.1× faster on input that needs coercing
|
|
118
|
+
* (`bench:validators:coerce`). Where the caller owns the document and lets Ajv
|
|
119
|
+
* coerce it in place, there is no clone to save, and on a large document Ajv
|
|
120
|
+
* can come out modestly ahead.
|
|
119
121
|
*
|
|
120
122
|
* Requires `validators`. `validateX` and `isX` are unchanged either way.
|
|
121
123
|
*/
|
|
@@ -135,6 +137,12 @@ export type CliConfig = {
|
|
|
135
137
|
* comes back invalid carrying both the repairs applied and the errors still
|
|
136
138
|
* outstanding.
|
|
137
139
|
*
|
|
140
|
+
* Repair is an autofix tool, not a stricter validator: it accepts documents
|
|
141
|
+
* `validateX` rejects, by repairing them. Accepting only `valid` with an empty
|
|
142
|
+
* `repairs` gives exactly the verdicts `coerceX` gives, so a config loader that
|
|
143
|
+
* must reject a typo wants `coerceX`, and `repairX` is for producing a fixed
|
|
144
|
+
* document.
|
|
145
|
+
*
|
|
138
146
|
* Implies `coerce` — repairing runs after coercion, so a value merely written
|
|
139
147
|
* in the wrong type is right before the validator sees it and never counts as
|
|
140
148
|
* a repair. Requires `validators`.
|
|
@@ -146,11 +154,11 @@ export type CliConfig = {
|
|
|
146
154
|
* in anyOf" that names no field and no reason.
|
|
147
155
|
*
|
|
148
156
|
* Off by default, and off costs nothing — the generated code is exactly what
|
|
149
|
-
* it would be without the option. On,
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
157
|
+
* it would be without the option. On, a combinator that passes costs what it
|
|
158
|
+
* costs with the option off: its branches are tested by the same functions.
|
|
159
|
+
* Only an `anyOf` / `oneOf` that has already failed asks its branches again,
|
|
160
|
+
* this time for what each one complained about, so the price is paid on
|
|
161
|
+
* invalid input alone.
|
|
154
162
|
*
|
|
155
163
|
* Requires `validators`.
|
|
156
164
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/mjst",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@amritk/generate-markdown": "^0.12.0",
|
|
57
57
|
"@amritk/helpers": "^0.24.0",
|
|
58
58
|
"@amritk/lint": "^0.7.0",
|
|
59
|
-
"@amritk/validation": "^0.4.
|
|
59
|
+
"@amritk/validation": "^0.4.1",
|
|
60
60
|
"@amritk/resolve-refs": "^0.9.0",
|
|
61
61
|
"@amritk/yaml": "^0.8.0",
|
|
62
62
|
"fast-glob": "^3.3.2",
|