@maroonedog/luq 2.5.0 → 2.7.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.
- package/README.md +35 -8
- package/dist/chain/slot-type-guard.js +20 -6
- package/dist/chain/slot-type-guard.mjs +20 -6
- package/dist/compile/resolve-presence.js +36 -5
- package/dist/compile/resolve-presence.mjs +36 -5
- package/dist/json-schema/assert-supported-dialect.d.ts +37 -0
- package/dist/json-schema/assert-supported-dialect.js +113 -0
- package/dist/json-schema/assert-supported-dialect.mjs +109 -0
- package/dist/json-schema/build-from-schema.d.ts +22 -3
- package/dist/json-schema/build-from-schema.js +27 -5
- package/dist/json-schema/build-from-schema.mjs +27 -5
- package/dist/json-schema/declare-value-keywords.d.ts +0 -2
- package/dist/json-schema/declare-value-keywords.js +7 -10
- package/dist/json-schema/declare-value-keywords.mjs +7 -9
- package/dist/json-schema/extensions/json-schema/index.d.ts +2 -2
- package/dist/json-schema/extensions/json-schema/index.js +3 -1
- package/dist/json-schema/extensions/json-schema/index.mjs +1 -1
- package/dist/json-schema/extensions/json-schema/json-schema.d.ts +2 -2
- package/dist/json-schema/extensions/json-schema/json-schema.js +3 -0
- package/dist/json-schema/extensions/json-schema/json-schema.mjs +4 -1
- package/dist/json-schema/extensions/json-schema-full-feature/bundle-coverage.js +6 -1
- package/dist/json-schema/extensions/json-schema-full-feature/bundle-coverage.mjs +6 -1
- package/dist/json-schema/extensions/json-schema-full-feature/index.d.ts +3 -1
- package/dist/json-schema/extensions/json-schema-full-feature/index.js +18 -1
- package/dist/json-schema/extensions/json-schema-full-feature/index.mjs +11 -0
- package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.d.ts +9 -1
- package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.js +9 -2
- package/dist/json-schema/extensions/json-schema-full-feature/json-schema-full-feature.mjs +9 -2
- package/dist/json-schema/index.d.ts +3 -0
- package/dist/json-schema/index.js +7 -1
- package/dist/json-schema/index.mjs +2 -0
- package/dist/json-schema/keyword-map-core.js +3 -1
- package/dist/json-schema/keyword-map-core.mjs +3 -1
- package/dist/json-schema/keyword-map.js +3 -0
- package/dist/json-schema/keyword-map.mjs +3 -0
- package/dist/json-schema/schema-to-declarations.js +1 -4
- package/dist/json-schema/schema-to-declarations.mjs +2 -5
- package/dist/json-schema/unsupported-dialect-error.d.ts +21 -0
- package/dist/json-schema/unsupported-dialect-error.js +58 -0
- package/dist/json-schema/unsupported-dialect-error.mjs +54 -0
- package/dist/runtime/create-issue.d.ts +9 -0
- package/dist/runtime/create-issue.js +9 -2
- package/dist/runtime/create-issue.mjs +9 -2
- package/dist/runtime/run-field.js +1 -0
- package/dist/runtime/run-field.mjs +1 -0
- package/dist/types/index.d.ts +12 -0
- package/package.json +20 -8
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ===========================================================================
|
|
3
|
+
// L8 src/json-schema/unsupported-dialect-error.ts
|
|
4
|
+
//
|
|
5
|
+
// The third refusal, and the only one with no keyword to hang on.
|
|
6
|
+
// UnsupportedKeywordError refuses a keyword NAME; MalformedSchemaError refuses
|
|
7
|
+
// a keyword VALUE. Both need the document to write something recognisable, and
|
|
8
|
+
// there is one divergence that writes nothing at all:
|
|
9
|
+
//
|
|
10
|
+
// {"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
11
|
+
// "$defs": {"name": {"type": "string"}},
|
|
12
|
+
// "properties": {"nick": {"$ref": "#/$defs/name", "minLength": 5}}}
|
|
13
|
+
//
|
|
14
|
+
// Every keyword there is a Draft-07 keyword spelled the Draft-07 way, so the
|
|
15
|
+
// keyword table sees nothing to refuse. The meaning is not Draft-07: §8.3 has
|
|
16
|
+
// `$ref` REPLACE the object it appears in, and from 2019-09 on `$ref` is an
|
|
17
|
+
// ordinary applicator whose siblings are applied. Read as Draft-07 the document
|
|
18
|
+
// above builds a validator that accepts `{"nick":"ab"}` — the `minLength` is
|
|
19
|
+
// gone, silently, and nothing in the result says a constraint went missing.
|
|
20
|
+
// `$schema` is the only signal the document offers, so it is the one read.
|
|
21
|
+
//
|
|
22
|
+
// It stays a separate class from the other two, neither extending the other,
|
|
23
|
+
// for the reason written in malformed-schema-error.ts: each answers a different
|
|
24
|
+
// question. "Luq cannot honour this keyword", "this document is not valid
|
|
25
|
+
// Draft-07", and now "this document is not Draft-07 AT ALL" are three distinct
|
|
26
|
+
// facts, and a caller catching one must not silently catch another.
|
|
27
|
+
// ===========================================================================
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.UnsupportedDialectError = exports.DRAFT07_DIALECT_URI = void 0;
|
|
30
|
+
/** The dialect this library implements, in the spelling the draft publishes. */
|
|
31
|
+
exports.DRAFT07_DIALECT_URI = "http://json-schema.org/draft-07/schema#";
|
|
32
|
+
/**
|
|
33
|
+
* Raised when a document's root `$schema` names a dialect Luq does not
|
|
34
|
+
* implement. Thrown at build time, before a validator that would read the
|
|
35
|
+
* document under the wrong dialect's rules can exist.
|
|
36
|
+
*
|
|
37
|
+
* `reason` follows the convention the other two refusals use: a sentence
|
|
38
|
+
* fragment with no terminal punctuation, saying what the dialect does that
|
|
39
|
+
* Draft-07 does not, so that two refusals from two different dialects read as
|
|
40
|
+
* one library rather than as two unrelated messages.
|
|
41
|
+
*/
|
|
42
|
+
class UnsupportedDialectError extends Error {
|
|
43
|
+
constructor(declared, reason) {
|
|
44
|
+
super(`JSON Schema dialect "${declared}" is not supported: ${reason}. Luq ` +
|
|
45
|
+
`implements Draft-07 (${exports.DRAFT07_DIALECT_URI}) and would read this ` +
|
|
46
|
+
"document under Draft-07 rules, which can enforce less than it " +
|
|
47
|
+
"states. Convert the document to Draft-07, or pass " +
|
|
48
|
+
"`{ assumeDraft07: true }` to take the Draft-07 reading deliberately.");
|
|
49
|
+
/** The dialect Luq reads every document under. */
|
|
50
|
+
this.implemented = exports.DRAFT07_DIALECT_URI;
|
|
51
|
+
this.name = "UnsupportedDialectError";
|
|
52
|
+
this.declared = declared;
|
|
53
|
+
this.reason = reason;
|
|
54
|
+
// Without this, `instanceof` fails when the package is compiled to ES5.
|
|
55
|
+
Object.setPrototypeOf(this, UnsupportedDialectError.prototype);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.UnsupportedDialectError = UnsupportedDialectError;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ===========================================================================
|
|
2
|
+
// L8 src/json-schema/unsupported-dialect-error.ts
|
|
3
|
+
//
|
|
4
|
+
// The third refusal, and the only one with no keyword to hang on.
|
|
5
|
+
// UnsupportedKeywordError refuses a keyword NAME; MalformedSchemaError refuses
|
|
6
|
+
// a keyword VALUE. Both need the document to write something recognisable, and
|
|
7
|
+
// there is one divergence that writes nothing at all:
|
|
8
|
+
//
|
|
9
|
+
// {"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
10
|
+
// "$defs": {"name": {"type": "string"}},
|
|
11
|
+
// "properties": {"nick": {"$ref": "#/$defs/name", "minLength": 5}}}
|
|
12
|
+
//
|
|
13
|
+
// Every keyword there is a Draft-07 keyword spelled the Draft-07 way, so the
|
|
14
|
+
// keyword table sees nothing to refuse. The meaning is not Draft-07: §8.3 has
|
|
15
|
+
// `$ref` REPLACE the object it appears in, and from 2019-09 on `$ref` is an
|
|
16
|
+
// ordinary applicator whose siblings are applied. Read as Draft-07 the document
|
|
17
|
+
// above builds a validator that accepts `{"nick":"ab"}` — the `minLength` is
|
|
18
|
+
// gone, silently, and nothing in the result says a constraint went missing.
|
|
19
|
+
// `$schema` is the only signal the document offers, so it is the one read.
|
|
20
|
+
//
|
|
21
|
+
// It stays a separate class from the other two, neither extending the other,
|
|
22
|
+
// for the reason written in malformed-schema-error.ts: each answers a different
|
|
23
|
+
// question. "Luq cannot honour this keyword", "this document is not valid
|
|
24
|
+
// Draft-07", and now "this document is not Draft-07 AT ALL" are three distinct
|
|
25
|
+
// facts, and a caller catching one must not silently catch another.
|
|
26
|
+
// ===========================================================================
|
|
27
|
+
/** The dialect this library implements, in the spelling the draft publishes. */
|
|
28
|
+
export const DRAFT07_DIALECT_URI = "http://json-schema.org/draft-07/schema#";
|
|
29
|
+
/**
|
|
30
|
+
* Raised when a document's root `$schema` names a dialect Luq does not
|
|
31
|
+
* implement. Thrown at build time, before a validator that would read the
|
|
32
|
+
* document under the wrong dialect's rules can exist.
|
|
33
|
+
*
|
|
34
|
+
* `reason` follows the convention the other two refusals use: a sentence
|
|
35
|
+
* fragment with no terminal punctuation, saying what the dialect does that
|
|
36
|
+
* Draft-07 does not, so that two refusals from two different dialects read as
|
|
37
|
+
* one library rather than as two unrelated messages.
|
|
38
|
+
*/
|
|
39
|
+
export class UnsupportedDialectError extends Error {
|
|
40
|
+
constructor(declared, reason) {
|
|
41
|
+
super(`JSON Schema dialect "${declared}" is not supported: ${reason}. Luq ` +
|
|
42
|
+
`implements Draft-07 (${DRAFT07_DIALECT_URI}) and would read this ` +
|
|
43
|
+
"document under Draft-07 rules, which can enforce less than it " +
|
|
44
|
+
"states. Convert the document to Draft-07, or pass " +
|
|
45
|
+
"`{ assumeDraft07: true }` to take the Draft-07 reading deliberately.");
|
|
46
|
+
/** The dialect Luq reads every document under. */
|
|
47
|
+
this.implemented = DRAFT07_DIALECT_URI;
|
|
48
|
+
this.name = "UnsupportedDialectError";
|
|
49
|
+
this.declared = declared;
|
|
50
|
+
this.reason = reason;
|
|
51
|
+
// Without this, `instanceof` fails when the package is compiled to ES5.
|
|
52
|
+
Object.setPrototypeOf(this, UnsupportedDialectError.prototype);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -13,6 +13,15 @@ export interface IssueRequest {
|
|
|
13
13
|
* sees one call shape and needs no rule kind to dispatch on.
|
|
14
14
|
*/
|
|
15
15
|
render(context: MessageContext): string;
|
|
16
|
+
/**
|
|
17
|
+
* The branch failures behind a composite, when the rule that failed is one.
|
|
18
|
+
*
|
|
19
|
+
* Passed rather than read off the detail because this layer never sees a
|
|
20
|
+
* rule kind: a presence policy closes over nothing and a check closes over
|
|
21
|
+
* its own IssueDetail, and keeping that asymmetry out of here is what lets
|
|
22
|
+
* one call shape serve both.
|
|
23
|
+
*/
|
|
24
|
+
readonly causes?: readonly ValidationIssue[] | undefined;
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
27
|
* A plugin whose message factory throws must not take the validation down
|
|
@@ -17,12 +17,19 @@ function createIssue(request) {
|
|
|
17
17
|
value: request.value,
|
|
18
18
|
code: request.code,
|
|
19
19
|
};
|
|
20
|
-
|
|
20
|
+
const issue = {
|
|
21
21
|
path: request.path,
|
|
22
22
|
code: request.code,
|
|
23
23
|
message: renderMessageOrFallback(request, context),
|
|
24
24
|
severity: request.severity,
|
|
25
|
-
}
|
|
25
|
+
};
|
|
26
|
+
// Spread CONDITIONALLY. Writing `causes: request.causes` unconditionally
|
|
27
|
+
// would put the key on every issue the library reports, with the value
|
|
28
|
+
// undefined — and `"causes" in issue` would then answer true for a plain
|
|
29
|
+
// stringMin failure, which is exactly the question a caller asks it.
|
|
30
|
+
return Object.freeze(request.causes === undefined || request.causes.length === 0
|
|
31
|
+
? issue
|
|
32
|
+
: { ...issue, causes: request.causes });
|
|
26
33
|
}
|
|
27
34
|
function renderMessageOrFallback(request, context) {
|
|
28
35
|
try {
|
|
@@ -13,12 +13,19 @@ export function createIssue(request) {
|
|
|
13
13
|
value: request.value,
|
|
14
14
|
code: request.code,
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
const issue = {
|
|
17
17
|
path: request.path,
|
|
18
18
|
code: request.code,
|
|
19
19
|
message: renderMessageOrFallback(request, context),
|
|
20
20
|
severity: request.severity,
|
|
21
|
-
}
|
|
21
|
+
};
|
|
22
|
+
// Spread CONDITIONALLY. Writing `causes: request.causes` unconditionally
|
|
23
|
+
// would put the key on every issue the library reports, with the value
|
|
24
|
+
// undefined — and `"causes" in issue` would then answer true for a plain
|
|
25
|
+
// stringMin failure, which is exactly the question a caller asks it.
|
|
26
|
+
return Object.freeze(request.causes === undefined || request.causes.length === 0
|
|
27
|
+
? issue
|
|
28
|
+
: { ...issue, causes: request.causes });
|
|
22
29
|
}
|
|
23
30
|
function renderMessageOrFallback(request, context) {
|
|
24
31
|
try {
|
|
@@ -78,6 +78,7 @@ function reportCheckFailure(check, detail, value, ruleContext, context) {
|
|
|
78
78
|
severity: check.severity,
|
|
79
79
|
value,
|
|
80
80
|
render: (ctx) => check.describe(detail, ctx),
|
|
81
|
+
causes: detail.causes,
|
|
81
82
|
}));
|
|
82
83
|
}
|
|
83
84
|
function runChecks(field, value, ruleContext, context, mark) {
|
|
@@ -74,6 +74,7 @@ function reportCheckFailure(check, detail, value, ruleContext, context) {
|
|
|
74
74
|
severity: check.severity,
|
|
75
75
|
value,
|
|
76
76
|
render: (ctx) => check.describe(detail, ctx),
|
|
77
|
+
causes: detail.causes,
|
|
77
78
|
}));
|
|
78
79
|
}
|
|
79
80
|
function runChecks(field, value, ruleContext, context, mark) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -26,6 +26,18 @@ export interface ValidationIssue {
|
|
|
26
26
|
/** Never optional: the chain resolves it once at build time, so a reader
|
|
27
27
|
* never has to re-implement the "absent means error" fallback. */
|
|
28
28
|
readonly severity: IssueSeverity;
|
|
29
|
+
/**
|
|
30
|
+
* Why a composite failed, when the failing rule is one.
|
|
31
|
+
*
|
|
32
|
+
* `allOf`, `anyOf` and `oneOf` report their own code, which says WHICH
|
|
33
|
+
* applicator failed and never why. The branch failures behind it land here,
|
|
34
|
+
* so a caller can reach "minLength" without re-running the sub-schemas.
|
|
35
|
+
*
|
|
36
|
+
* ABSENT, not undefined, on an ordinary issue. A key present everywhere
|
|
37
|
+
* would make `"causes" in issue` answer true for every issue the library
|
|
38
|
+
* reports, which is the opposite of what it is for.
|
|
39
|
+
*/
|
|
40
|
+
readonly causes?: readonly ValidationIssue[];
|
|
29
41
|
}
|
|
30
42
|
/** branch / index / causes let a composite failure explain itself. */
|
|
31
43
|
export interface IssueDetail {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maroonedog/luq",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "Validate the TypeScript types you already have. Rules are declared against your own type's field paths, so a rule that does not apply to its field is a compile error.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -459,18 +459,20 @@
|
|
|
459
459
|
"typecheck:src": "tsc --noEmit -p tsconfig.json",
|
|
460
460
|
"typecheck:scripts": "tsc --noEmit -p scripts/tsconfig.json",
|
|
461
461
|
"typecheck:bench": "tsc --noEmit -p bench/tsconfig.json",
|
|
462
|
-
"generate": "npm run generate:sources && npm run generate:exports && npm run generate:lock",
|
|
462
|
+
"generate": "npm run generate:sources && npm run generate:exports && npm run generate:lock && npm run generate:issue-codes",
|
|
463
463
|
"generate:sources": "npm run generate:manifest && npm run generate:barrel && npm run generate:slot-catalog",
|
|
464
464
|
"generate:slot-catalog": "npx ts-node --project scripts/tsconfig.json scripts/generate-slot-catalog.ts",
|
|
465
465
|
"generate:manifest": "npx ts-node --project scripts/tsconfig.json scripts/generate-plugin-manifest.ts",
|
|
466
466
|
"generate:barrel": "npx ts-node --project scripts/tsconfig.json scripts/generate-plugin-barrel.ts",
|
|
467
467
|
"generate:exports": "npx ts-node --project scripts/tsconfig.json scripts/generate-package-exports.ts",
|
|
468
468
|
"generate:lock": "npx ts-node --project scripts/tsconfig.json scripts/generate-plugin-catalog.ts",
|
|
469
|
+
"generate:issue-codes": "npx ts-node --project scripts/tsconfig.json scripts/generate-issue-code-catalog.ts",
|
|
469
470
|
"check:exports": "npx ts-node --project scripts/tsconfig.json scripts/check-exports.ts --mode=exact",
|
|
470
471
|
"check:catalog-lock": "npx ts-node --project scripts/tsconfig.json scripts/check-catalog-lock.ts",
|
|
472
|
+
"check:issue-code-lock": "npx ts-node --project scripts/tsconfig.json scripts/check-issue-code-lock.ts",
|
|
471
473
|
"check:plugin-isolation": "npx ts-node --project scripts/tsconfig.json scripts/check-plugin-isolation.ts",
|
|
472
474
|
"check:plugin-uniqueness": "npx ts-node --project scripts/tsconfig.json scripts/check-plugin-uniqueness.ts",
|
|
473
|
-
"check:catalog": "npm run check:plugin-uniqueness && npm run check:plugin-isolation && npm run check:exports && npm run check:catalog-lock",
|
|
475
|
+
"check:catalog": "npm run check:plugin-uniqueness && npm run check:plugin-isolation && npm run check:exports && npm run check:catalog-lock && npm run check:issue-code-lock",
|
|
474
476
|
"check:size": "npx ts-node --project scripts/tsconfig.json scripts/measure-bundle-size.ts",
|
|
475
477
|
"check:barrel-equivalence": "npx ts-node --project scripts/tsconfig.json scripts/check-barrel-equivalence.ts",
|
|
476
478
|
"check:no-dynamic-code": "npx ts-node --project scripts/tsconfig.json scripts/check-no-dynamic-code.ts",
|
|
@@ -493,6 +495,7 @@
|
|
|
493
495
|
"check:conformance-figures": "npx ts-node --project scripts/tsconfig.json scripts/check-conformance-figures.ts",
|
|
494
496
|
"bench:competitors": "npx ts-node --project bench/tsconfig.json bench/competitors/report-competitors.ts",
|
|
495
497
|
"bench:competitors:check": "npx ts-node --project bench/tsconfig.json bench/competitors/check-competitors.ts",
|
|
498
|
+
"bench:megamorphism": "npx ts-node --project bench/tsconfig.json bench/megamorphism/report-megamorphism.ts",
|
|
496
499
|
"lint:ox": "oxlint -c .oxlintrc.json src bench",
|
|
497
500
|
"lint:naming": "eslint \"src/**/*.ts\" \"bench/**/*.ts\"",
|
|
498
501
|
"generate:perf-figures": "npx ts-node --project scripts/tsconfig.json scripts/check-perf-figures.ts --write",
|
|
@@ -507,13 +510,22 @@
|
|
|
507
510
|
"url": "https://github.com/maroonedog/luq/issues"
|
|
508
511
|
},
|
|
509
512
|
"keywords": [
|
|
510
|
-
"form",
|
|
511
|
-
"validation",
|
|
512
513
|
"typescript",
|
|
514
|
+
"validation",
|
|
513
515
|
"validator",
|
|
514
|
-
"
|
|
516
|
+
"schema",
|
|
517
|
+
"json-schema",
|
|
518
|
+
"draft-07",
|
|
519
|
+
"openapi",
|
|
520
|
+
"standard-schema",
|
|
521
|
+
"standardschema",
|
|
522
|
+
"type-safe",
|
|
523
|
+
"form-validation",
|
|
524
|
+
"runtime-validation",
|
|
525
|
+
"content-security-policy",
|
|
526
|
+
"zero-dependencies"
|
|
515
527
|
],
|
|
516
|
-
"author": "",
|
|
528
|
+
"author": "maroonedog (https://github.com/maroonedog)",
|
|
517
529
|
"license": "MIT",
|
|
518
530
|
"files": [
|
|
519
531
|
"dist"
|