@aventara/core 1.0.0-rc.0 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1140 -0
- package/README.md +224 -57
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +9 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -29
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/{json → avz}/avz.js +150 -8
- package/dist/{json → avz}/index.d.ts +2 -1
- package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
- package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
- package/dist/{json → avz}/zod-issue.translator.js +7 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +548 -441
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +74 -31
- package/dist/config/config.resolver.js +108 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +581 -72
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +110 -39
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
- package/dist/contracts/client-contract-structure.validator.js +152 -8
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +141 -28
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +24 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +52 -42
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +12 -1
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +15 -15
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +213 -147
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +47 -23
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +40 -20
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -50
- package/dist/index.js +6 -6
- package/dist/internal.d.ts +15 -10
- package/dist/internal.js +3 -1
- package/dist/operations/operation-arguments.d.ts +267 -202
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +10 -10
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +13 -2
- package/dist/operations/operation-validator.d.ts +9 -9
- package/dist/operations/projection-scope.resolver.d.ts +6 -6
- package/dist/operations/structural-limits.walker.d.ts +6 -6
- package/dist/operations/structural-limits.walker.js +145 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -12
- package/dist/protocol/absent-route.responder.js +19 -19
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +17 -13
- package/dist/protocol/http-request.decoder.js +10 -10
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +50 -27
- package/dist/protocol/protocol.binder.js +12 -9
- package/dist/protocol/protocol.handler.d.ts +22 -13
- package/dist/protocol/protocol.handler.js +44 -9
- package/dist/protocol/unmounted-route.error.d.ts +4 -4
- package/dist/protocol/unmounted-route.error.js +2 -2
- package/dist/runtime/caller-data.copier.d.ts +5 -5
- package/dist/runtime/caller-data.copier.js +13 -13
- package/dist/runtime/caller-data.door.d.ts +6 -6
- package/dist/runtime/caller-data.door.js +6 -4
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -48
- package/dist/runtime/computed-value.error.d.ts +6 -6
- package/dist/runtime/computed-value.error.js +3 -3
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +10 -81
- package/dist/runtime/create-requirement.js +80 -57
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +53 -27
- package/dist/runtime/diagnostics-reporter.js +24 -0
- package/dist/runtime/error-boundary.d.ts +65 -8
- package/dist/runtime/error-boundary.js +45 -19
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +68 -8
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +12 -12
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +54 -30
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +33 -6
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +113 -25
- package/dist/runtime/operation-runtime.js +395 -74
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +23 -6
- package/dist/runtime/output-validator.d.ts +10 -10
- package/dist/runtime/output-validator.js +3 -3
- package/dist/runtime/owned-execution.registry.d.ts +6 -6
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +196 -34
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/request-envelope.validator.d.ts +3 -3
- package/dist/runtime/request-envelope.validator.js +12 -7
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +31 -16
- package/dist/runtime/scalar-runtime.js +16 -18
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
- package/dist/transactions/transaction-plan-assembler.js +148 -34
- package/dist/transactions/transaction-plan-validator.d.ts +29 -20
- package/dist/transactions/transaction-plan-validator.js +70 -38
- package/dist/transactions/transaction-reference-collector.d.ts +5 -5
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +26 -14
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +58 -26
- package/dist/transactions/transaction-reference.d.ts +13 -13
- package/dist/transactions/transaction-request.d.ts +18 -5
- package/dist/transactions/transaction-response.d.ts +6 -6
- package/dist/transactions/transaction-runtime.d.ts +8 -7
- package/dist/transactions/transaction-runtime.js +166 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -42
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -42
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/json/avz.d.ts +0 -96
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/{json → avz}/index.js +0 -0
- /package/dist/{json → avz}/json-shape.registry.js +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VALIDATION_CODE_MESSAGES, VALIDATION_CODES, } from "../operations/validation-code.js";
|
|
2
|
-
import {
|
|
2
|
+
import { AvError } from "./framework-error.js";
|
|
3
3
|
import { INTERNAL_FAILURE_MESSAGE, internalFailure, OPERATION_CODES, operationFailure, } from "./operation-response.js";
|
|
4
4
|
import { PIPE_OUTPUT_FAILURE_MESSAGE } from "./pipe-output.validator.js";
|
|
5
5
|
export const ADAPTER_FAILURE_MESSAGE = "Adapter execution failed.";
|
|
@@ -207,7 +207,7 @@ function readProperty(value, key) {
|
|
|
207
207
|
}
|
|
208
208
|
function isFrameworkErrorInstance(error) {
|
|
209
209
|
try {
|
|
210
|
-
return error instanceof
|
|
210
|
+
return error instanceof AvError;
|
|
211
211
|
}
|
|
212
212
|
catch {
|
|
213
213
|
return false;
|
|
@@ -269,24 +269,50 @@ export function sanitizeOperationFailure(error, origin, report, view) {
|
|
|
269
269
|
}
|
|
270
270
|
return deliberateFailure(outcome);
|
|
271
271
|
}
|
|
272
|
-
export async function resolveOperationFailure(error, filters, context, boundary, report, view) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
272
|
+
export async function resolveOperationFailure(error, filters, context, boundary, report, view, outer) {
|
|
273
|
+
const failure = { error, view, boundary, report };
|
|
274
|
+
const handled = await filterChain(filters, context).attempt(error, view, boundary);
|
|
275
|
+
if (handled !== undefined) {
|
|
276
|
+
return answerHandled(failure, handled);
|
|
277
|
+
}
|
|
278
|
+
return outer === undefined
|
|
279
|
+
? sanitizeUnhandled(failure)
|
|
280
|
+
: outer.defer(failure);
|
|
281
|
+
}
|
|
282
|
+
function answerHandled(failure, handled) {
|
|
283
|
+
if (handled.outcome === MALFORMED_OUTCOME) {
|
|
284
|
+
failure.report(failure.error, "A3000");
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
reportDeliberate(handled.returned, handled.outcome, failure.report);
|
|
288
288
|
}
|
|
289
|
-
return
|
|
289
|
+
return deliberateFailure(handled.outcome);
|
|
290
|
+
}
|
|
291
|
+
function sanitizeUnhandled(failure) {
|
|
292
|
+
return sanitizeOperationFailure(failure.error, failure.boundary.originOf(failure.error), failure.report, failure.view);
|
|
293
|
+
}
|
|
294
|
+
export async function resolveUnhandledFailure(failure, chain) {
|
|
295
|
+
const handled = await chain.attempt(failure.error, failure.view, failure.boundary);
|
|
296
|
+
return handled === undefined
|
|
297
|
+
? sanitizeUnhandled(failure)
|
|
298
|
+
: answerHandled(failure, handled);
|
|
299
|
+
}
|
|
300
|
+
export function filterChain(filters, context) {
|
|
301
|
+
return {
|
|
302
|
+
attempt: async (error, view, boundary) => {
|
|
303
|
+
for (let index = filters.length - 1; index >= 0; index -= 1) {
|
|
304
|
+
const filter = filters[index];
|
|
305
|
+
if (filter === undefined) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const handled = await attemptFilter(filter, error, context, view, boundary);
|
|
309
|
+
if (handled !== undefined) {
|
|
310
|
+
return handled;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return undefined;
|
|
314
|
+
},
|
|
315
|
+
};
|
|
290
316
|
}
|
|
291
317
|
async function attemptFilter(filter, error, context, view, boundary) {
|
|
292
318
|
try {
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import type { AvState } from "./request-state.js";
|
|
1
2
|
/**
|
|
2
3
|
* Options for one in-process call: the optional second argument of every
|
|
3
4
|
* `framework.application.*` / `framework.client.*` call and of
|
|
4
5
|
* `framework.transaction`.
|
|
5
6
|
*/
|
|
6
|
-
export type
|
|
7
|
+
export type AvExecutionOptions = {
|
|
7
8
|
/**
|
|
8
9
|
* The id every pipeline context and every diagnostic of this call or run
|
|
9
|
-
* carries
|
|
10
|
+
* carries, such as a correlation id the caller already holds. Omitted, the Framework
|
|
10
11
|
* mints one; the empty string counts as omitted.
|
|
11
12
|
*/
|
|
12
13
|
readonly requestId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The call's or run's `ctx.$state`: the one object every stage of it, and
|
|
16
|
+
* every ctx call those stages make, reads and writes. It is used as it is, not
|
|
17
|
+
* copied, so the caller sees what the stages wrote. Omitted (or not an
|
|
18
|
+
* object), the Framework starts the request with `{}`.
|
|
19
|
+
*/
|
|
20
|
+
readonly state?: AvState & object;
|
|
13
21
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AvFieldCheck } from "../contracts/field-check.js";
|
|
2
|
+
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
3
|
+
/** What a check judges: the runtime form of the value it is given. */
|
|
4
|
+
export type FieldCheckSubject = "string" | "number" | "bigint" | "decimal" | "datetime" | "bytes";
|
|
5
|
+
/**
|
|
6
|
+
* What a check judges on a field of each scalar type that takes checks; a field
|
|
7
|
+
* of any other type (an enum's) holds a string.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FIELD_CHECK_SUBJECTS: Readonly<Record<string, FieldCheckSubject>>;
|
|
10
|
+
type Path = readonly (string | number)[];
|
|
11
|
+
/**
|
|
12
|
+
* Whether `value` passes `check`; `true` for a value the check does not judge
|
|
13
|
+
* (a value of another form, which validation refused before the checks run).
|
|
14
|
+
*/
|
|
15
|
+
export declare function passesFieldCheck(check: AvFieldCheck, value: unknown, subject: FieldCheckSubject): boolean;
|
|
16
|
+
/** The issue of one failed check at `path`: its V-code, zod's check and its parameters. */
|
|
17
|
+
export declare function fieldCheckIssue(check: AvFieldCheck, subject: FieldCheckSubject, path: Path): AvValidationIssue;
|
|
18
|
+
/**
|
|
19
|
+
* The issues of the checks `value` breaks, one per broken check, in the checks'
|
|
20
|
+
* order. On a list (`list`), a `list: true` length check judges the list at
|
|
21
|
+
* `path` and every other check judges each entry, at its index; `null` is no
|
|
22
|
+
* value and breaks nothing.
|
|
23
|
+
*/
|
|
24
|
+
export declare function fieldCheckIssues(checks: readonly AvFieldCheck[], value: unknown, subject: FieldCheckSubject, path: Path, list: boolean): readonly AvValidationIssue[];
|
|
25
|
+
/** The issues of the checks one value — a whole value, or a list's entry — breaks. */
|
|
26
|
+
export declare function entryCheckIssues(checks: readonly AvFieldCheck[], value: unknown, subject: FieldCheckSubject, path: Path): readonly AvValidationIssue[];
|
|
27
|
+
/** Whether a check judges a list's own length (`AvZ.minItems`/`AvZ.maxItems`). */
|
|
28
|
+
export declare function isListCheck(check: AvFieldCheck): boolean;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
export const FIELD_CHECK_SUBJECTS = {
|
|
2
|
+
string: "string",
|
|
3
|
+
int: "number",
|
|
4
|
+
float: "number",
|
|
5
|
+
bigint: "bigint",
|
|
6
|
+
decimal: "decimal",
|
|
7
|
+
datetime: "datetime",
|
|
8
|
+
bytes: "bytes",
|
|
9
|
+
};
|
|
10
|
+
const DATE_SOURCE = "(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))";
|
|
11
|
+
const HHMM = "(?:[01]\\d|2[0-3]):[0-5]\\d";
|
|
12
|
+
const IPV6_ALPHABET = /^[0-9a-fA-F:.]+$/;
|
|
13
|
+
const CREDIT_CARD = /^\d(?:[ -]?\d){11,18}$/;
|
|
14
|
+
const IBAN = /^[A-Z]{2}(?!00|01|99)\d{2}[A-Z0-9]{11,30}$/;
|
|
15
|
+
const FORMAT_PATTERNS = {
|
|
16
|
+
email: /^(?:[A-Za-z0-9_'+-]+\.)*[A-Za-z0-9_'+-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$/,
|
|
17
|
+
emoji: new RegExp("^(?=[\\s\\S]*[\\p{Extended_Pictographic}\\p{Regional_Indicator}\\u20E3])[\\p{Extended_Pictographic}\\p{Emoji_Component}]+$", "u"),
|
|
18
|
+
uuid: /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,
|
|
19
|
+
guid: /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,
|
|
20
|
+
nanoid: /^[a-zA-Z0-9_-]{21}$/,
|
|
21
|
+
cuid: /^[cC][0-9a-z]{6,}$/,
|
|
22
|
+
cuid2: /^[0-9a-z]+$/,
|
|
23
|
+
ulid: /^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$/,
|
|
24
|
+
xid: /^[0-9a-vA-V]{20}$/,
|
|
25
|
+
ksuid: /^[A-Za-z0-9]{27}$/,
|
|
26
|
+
datetime: new RegExp(`^${DATE_SOURCE}T(?:${HHMM}:[0-5]\\d(?:\\.\\d+)?(?:Z))$`),
|
|
27
|
+
date: new RegExp(`^${DATE_SOURCE}$`),
|
|
28
|
+
time: new RegExp(`^${HHMM}(?::[0-5]\\d(?:\\.\\d+)?)?$`),
|
|
29
|
+
duration: /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,
|
|
30
|
+
ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,
|
|
31
|
+
cidrv4: /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,
|
|
32
|
+
e164: /^\+[1-9]\d{6,14}$/,
|
|
33
|
+
};
|
|
34
|
+
const platform = globalThis;
|
|
35
|
+
function canParseUrl(input) {
|
|
36
|
+
const Url = platform.URL;
|
|
37
|
+
if (Url === undefined) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
if (typeof Url.canParse === "function") {
|
|
42
|
+
return Url.canParse(input);
|
|
43
|
+
}
|
|
44
|
+
new Url(input);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function decodeBase64(value) {
|
|
52
|
+
if (platform.atob === undefined) {
|
|
53
|
+
throw new TypeError("No atob is available here.");
|
|
54
|
+
}
|
|
55
|
+
return platform.atob(value);
|
|
56
|
+
}
|
|
57
|
+
function isIpv6(value) {
|
|
58
|
+
return IPV6_ALPHABET.test(value) && canParseUrl(`http://[${value}]`);
|
|
59
|
+
}
|
|
60
|
+
function isCidrv6(value) {
|
|
61
|
+
const parts = value.split("/");
|
|
62
|
+
if (parts.length !== 2) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const [address, prefix] = parts;
|
|
66
|
+
const bits = Number(prefix);
|
|
67
|
+
return (prefix !== "" &&
|
|
68
|
+
`${bits}` === prefix &&
|
|
69
|
+
bits >= 0 &&
|
|
70
|
+
bits <= 128 &&
|
|
71
|
+
isIpv6(address));
|
|
72
|
+
}
|
|
73
|
+
function isBase64(value) {
|
|
74
|
+
if (value === "") {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (/\s/.test(value) || value.length % 4 !== 0) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
decodeBase64(value);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function isBase64Url(value) {
|
|
89
|
+
if (!/^[A-Za-z0-9_-]*$/.test(value)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const base64 = value.replace(/[-_]/g, (char) => (char === "-" ? "+" : "/"));
|
|
93
|
+
return isBase64(base64.padEnd(Math.ceil(base64.length / 4) * 4, "="));
|
|
94
|
+
}
|
|
95
|
+
function isLuhn(digits) {
|
|
96
|
+
let length = digits.length;
|
|
97
|
+
let bit = 1;
|
|
98
|
+
let sum = 0;
|
|
99
|
+
while (length) {
|
|
100
|
+
const value = digits.charCodeAt(--length) - 48;
|
|
101
|
+
bit ^= 1;
|
|
102
|
+
sum += bit ? ([0, 2, 4, 6, 8, 1, 3, 5, 7, 9][value] ?? 0) : value;
|
|
103
|
+
}
|
|
104
|
+
return sum % 10 === 0;
|
|
105
|
+
}
|
|
106
|
+
function isIbanChecksum(iban) {
|
|
107
|
+
let remainder = 0;
|
|
108
|
+
const step = (code) => {
|
|
109
|
+
remainder =
|
|
110
|
+
(code >= 65
|
|
111
|
+
? remainder * 100 + (code - 55)
|
|
112
|
+
: remainder * 10 + (code - 48)) % 97;
|
|
113
|
+
};
|
|
114
|
+
for (let index = 4; index < iban.length; index += 1) {
|
|
115
|
+
step(iban.charCodeAt(index));
|
|
116
|
+
}
|
|
117
|
+
for (let index = 0; index < 4; index += 1) {
|
|
118
|
+
step(iban.charCodeAt(index));
|
|
119
|
+
}
|
|
120
|
+
return remainder === 1;
|
|
121
|
+
}
|
|
122
|
+
function isJwt(token) {
|
|
123
|
+
try {
|
|
124
|
+
const parts = token.split(".");
|
|
125
|
+
if (parts.length !== 3 || !parts[0]) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const header = JSON.parse(decodeBase64(parts[0]));
|
|
129
|
+
if ("typ" in header && header.typ !== "JWT") {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return Boolean(header.alg);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function isJsonString(value) {
|
|
139
|
+
try {
|
|
140
|
+
JSON.parse(value);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function matchesFormat(format, value) {
|
|
148
|
+
switch (format) {
|
|
149
|
+
case "url":
|
|
150
|
+
return canParseUrl(value.trim());
|
|
151
|
+
case "ipv6":
|
|
152
|
+
return isIpv6(value);
|
|
153
|
+
case "cidrv6":
|
|
154
|
+
return isCidrv6(value);
|
|
155
|
+
case "base64":
|
|
156
|
+
return isBase64(value);
|
|
157
|
+
case "base64url":
|
|
158
|
+
return isBase64Url(value);
|
|
159
|
+
case "credit_card":
|
|
160
|
+
return CREDIT_CARD.test(value) && isLuhn(value.replace(/[- ]/g, ""));
|
|
161
|
+
case "iban":
|
|
162
|
+
return IBAN.test(value) && isIbanChecksum(value);
|
|
163
|
+
case "jwt":
|
|
164
|
+
return isJwt(value);
|
|
165
|
+
case "json_string":
|
|
166
|
+
return isJsonString(value);
|
|
167
|
+
default: {
|
|
168
|
+
return FORMAT_PATTERNS[format]?.test(value) === true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const DECIMAL = /^(-?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
|
|
173
|
+
function decimalParts(text) {
|
|
174
|
+
const match = DECIMAL.exec(text);
|
|
175
|
+
if (match === null) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
const [, sign, whole = "", fraction = "", power = "0"] = match;
|
|
179
|
+
const all = `${whole}${fraction}`;
|
|
180
|
+
const leading = all.length - all.replace(/^0+/, "").length;
|
|
181
|
+
const digits = all.slice(leading).replace(/0+$/, "");
|
|
182
|
+
if (digits === "") {
|
|
183
|
+
return { negative: false, digits: "", exponent: 0 };
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
negative: sign === "-",
|
|
187
|
+
digits,
|
|
188
|
+
exponent: whole.length - 1 - leading + Number(power),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function compareDecimals(a, b) {
|
|
192
|
+
const signOf = (parts) => parts.digits === "" ? 0 : parts.negative ? -1 : 1;
|
|
193
|
+
if (signOf(a) !== signOf(b)) {
|
|
194
|
+
return Math.sign(signOf(a) - signOf(b));
|
|
195
|
+
}
|
|
196
|
+
if (signOf(a) === 0) {
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
let magnitude = 0;
|
|
200
|
+
if (a.exponent !== b.exponent) {
|
|
201
|
+
magnitude = a.exponent > b.exponent ? 1 : -1;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
const width = Math.max(a.digits.length, b.digits.length);
|
|
205
|
+
const left = a.digits.padEnd(width, "0");
|
|
206
|
+
const right = b.digits.padEnd(width, "0");
|
|
207
|
+
magnitude = left === right ? 0 : left > right ? 1 : -1;
|
|
208
|
+
}
|
|
209
|
+
return a.negative ? -magnitude : magnitude;
|
|
210
|
+
}
|
|
211
|
+
function modPow(base, power, modulus) {
|
|
212
|
+
let result = BigInt(1) % modulus;
|
|
213
|
+
let factor = base % modulus;
|
|
214
|
+
let remaining = power;
|
|
215
|
+
while (remaining > 0) {
|
|
216
|
+
if (remaining % 2 === 1) {
|
|
217
|
+
result = (result * factor) % modulus;
|
|
218
|
+
}
|
|
219
|
+
factor = (factor * factor) % modulus;
|
|
220
|
+
remaining = Math.floor(remaining / 2);
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
function isDecimalMultiple(a, b) {
|
|
225
|
+
if (b.digits === "") {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
if (a.digits === "") {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
const scaleA = a.exponent - (a.digits.length - 1);
|
|
232
|
+
const scaleB = b.exponent - (b.digits.length - 1);
|
|
233
|
+
if (!Number.isSafeInteger(scaleA) || !Number.isSafeInteger(scaleB)) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const mantissaA = BigInt(a.digits);
|
|
237
|
+
const mantissaB = BigInt(b.digits);
|
|
238
|
+
if (scaleA >= scaleB) {
|
|
239
|
+
return (((mantissaA % mantissaB) *
|
|
240
|
+
modPow(BigInt(10), scaleA - scaleB, mantissaB)) %
|
|
241
|
+
mantissaB ===
|
|
242
|
+
BigInt(0));
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
function floatRemainder(value, step) {
|
|
247
|
+
const ratio = value / step;
|
|
248
|
+
const rounded = Math.round(ratio);
|
|
249
|
+
const tolerance = 4 * Number.EPSILON * Math.max(Math.abs(ratio), 1);
|
|
250
|
+
return Math.abs(ratio - rounded) < tolerance ? 0 : ratio - rounded;
|
|
251
|
+
}
|
|
252
|
+
function codePoints(value) {
|
|
253
|
+
let count = value.length;
|
|
254
|
+
for (let index = 0; index < value.length - 1; index += 1) {
|
|
255
|
+
if ((value.charCodeAt(index) & 0xfc00) === 0xd800 &&
|
|
256
|
+
(value.charCodeAt(index + 1) & 0xfc00) === 0xdc00) {
|
|
257
|
+
count -= 1;
|
|
258
|
+
index += 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return count;
|
|
262
|
+
}
|
|
263
|
+
const patterns = new WeakMap();
|
|
264
|
+
function patternOf(check) {
|
|
265
|
+
let pattern = patterns.get(check);
|
|
266
|
+
if (pattern === undefined) {
|
|
267
|
+
pattern = new RegExp(check.pattern.source, check.pattern.flags);
|
|
268
|
+
patterns.set(check, pattern);
|
|
269
|
+
}
|
|
270
|
+
pattern.lastIndex = 0;
|
|
271
|
+
return pattern;
|
|
272
|
+
}
|
|
273
|
+
function passesFormat(check, value) {
|
|
274
|
+
switch (check.format) {
|
|
275
|
+
case "regex":
|
|
276
|
+
return patternOf(check).test(value);
|
|
277
|
+
case "includes":
|
|
278
|
+
return value.includes(check.includes);
|
|
279
|
+
case "starts_with":
|
|
280
|
+
return value.startsWith(check.prefix);
|
|
281
|
+
case "ends_with":
|
|
282
|
+
return value.endsWith(check.suffix);
|
|
283
|
+
case "lowercase":
|
|
284
|
+
return /^[^A-Z]*$/.test(value);
|
|
285
|
+
case "uppercase":
|
|
286
|
+
return /^[^a-z]*$/.test(value);
|
|
287
|
+
default:
|
|
288
|
+
return matchesFormat(check.format, value);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function compareToBound(value, bound, subject) {
|
|
292
|
+
switch (subject) {
|
|
293
|
+
case "number":
|
|
294
|
+
return typeof value === "number" && typeof bound === "number"
|
|
295
|
+
? Math.sign(value - bound)
|
|
296
|
+
: undefined;
|
|
297
|
+
case "bigint": {
|
|
298
|
+
if (typeof value !== "bigint") {
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
const limit = BigInt(bound);
|
|
302
|
+
return value === limit ? 0 : value > limit ? 1 : -1;
|
|
303
|
+
}
|
|
304
|
+
case "decimal": {
|
|
305
|
+
const left = decimalParts(String(value));
|
|
306
|
+
const right = decimalParts(String(bound));
|
|
307
|
+
return left === undefined || right === undefined
|
|
308
|
+
? undefined
|
|
309
|
+
: compareDecimals(left, right);
|
|
310
|
+
}
|
|
311
|
+
case "datetime":
|
|
312
|
+
return value instanceof Date
|
|
313
|
+
? Math.sign(value.getTime() - Date.parse(String(bound)))
|
|
314
|
+
: undefined;
|
|
315
|
+
default:
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function isMultiple(value, step, subject) {
|
|
320
|
+
switch (subject) {
|
|
321
|
+
case "number":
|
|
322
|
+
return typeof value === "number" && typeof step === "number"
|
|
323
|
+
? floatRemainder(value, step) === 0
|
|
324
|
+
: undefined;
|
|
325
|
+
case "bigint": {
|
|
326
|
+
if (typeof value !== "bigint") {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
const divisor = BigInt(step);
|
|
330
|
+
return divisor !== BigInt(0) && value % divisor === BigInt(0);
|
|
331
|
+
}
|
|
332
|
+
case "decimal": {
|
|
333
|
+
const left = decimalParts(String(value));
|
|
334
|
+
const right = decimalParts(String(step));
|
|
335
|
+
return left === undefined || right === undefined
|
|
336
|
+
? undefined
|
|
337
|
+
: isDecimalMultiple(left, right);
|
|
338
|
+
}
|
|
339
|
+
default:
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
export function passesFieldCheck(check, value, subject) {
|
|
344
|
+
switch (check.check) {
|
|
345
|
+
case "less_than":
|
|
346
|
+
case "greater_than": {
|
|
347
|
+
const order = compareToBound(value, check.value, subject);
|
|
348
|
+
if (order === undefined) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
const signed = check.check === "less_than" ? -order : order;
|
|
352
|
+
return signed > 0 || (check.inclusive && signed === 0);
|
|
353
|
+
}
|
|
354
|
+
case "multiple_of":
|
|
355
|
+
return isMultiple(value, check.value, subject) ?? true;
|
|
356
|
+
case "max_length":
|
|
357
|
+
case "min_length":
|
|
358
|
+
case "length_equals": {
|
|
359
|
+
const length = Array.isArray(value)
|
|
360
|
+
? value.length
|
|
361
|
+
: typeof value === "string"
|
|
362
|
+
? codePoints(value)
|
|
363
|
+
: undefined;
|
|
364
|
+
if (length === undefined) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
return check.check === "max_length"
|
|
368
|
+
? length <= check.maximum
|
|
369
|
+
: check.check === "min_length"
|
|
370
|
+
? length >= check.minimum
|
|
371
|
+
: length === check.length;
|
|
372
|
+
}
|
|
373
|
+
case "string_format":
|
|
374
|
+
return typeof value === "string" ? passesFormat(check, value) : true;
|
|
375
|
+
case "max_size":
|
|
376
|
+
case "min_size":
|
|
377
|
+
case "size_equals": {
|
|
378
|
+
if (!(value instanceof Uint8Array)) {
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
const size = value.byteLength;
|
|
382
|
+
return check.check === "max_size"
|
|
383
|
+
? size <= check.maximum
|
|
384
|
+
: check.check === "min_size"
|
|
385
|
+
? size >= check.minimum
|
|
386
|
+
: size === check.size;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const FORMAT_LABELS = {
|
|
391
|
+
email: "an email address",
|
|
392
|
+
url: "a URL",
|
|
393
|
+
emoji: "emoji",
|
|
394
|
+
uuid: "a UUID",
|
|
395
|
+
guid: "a GUID",
|
|
396
|
+
nanoid: "a Nano ID",
|
|
397
|
+
cuid: "a CUID",
|
|
398
|
+
cuid2: "a CUID2",
|
|
399
|
+
ulid: "a ULID",
|
|
400
|
+
xid: "an XID",
|
|
401
|
+
ksuid: "a KSUID",
|
|
402
|
+
datetime: "an ISO 8601 date-time in UTC",
|
|
403
|
+
date: "an ISO 8601 date",
|
|
404
|
+
time: "an ISO 8601 time",
|
|
405
|
+
duration: "an ISO 8601 duration",
|
|
406
|
+
ipv4: "an IPv4 address",
|
|
407
|
+
ipv6: "an IPv6 address",
|
|
408
|
+
cidrv4: "an IPv4 CIDR block",
|
|
409
|
+
cidrv6: "an IPv6 CIDR block",
|
|
410
|
+
base64: "base64",
|
|
411
|
+
base64url: "base64url",
|
|
412
|
+
json_string: "a JSON string",
|
|
413
|
+
e164: "an E.164 phone number",
|
|
414
|
+
credit_card: "a credit card number",
|
|
415
|
+
iban: "an IBAN",
|
|
416
|
+
jwt: "a JWT",
|
|
417
|
+
};
|
|
418
|
+
function plural(count, one, many) {
|
|
419
|
+
return `${count} ${count === 1 ? one : many}`;
|
|
420
|
+
}
|
|
421
|
+
function formatMessage(check) {
|
|
422
|
+
switch (check.format) {
|
|
423
|
+
case "regex":
|
|
424
|
+
return `Value must match the pattern /${check.pattern.source}/${check.pattern.flags}.`;
|
|
425
|
+
case "includes":
|
|
426
|
+
return `Value must include ${JSON.stringify(check.includes)}.`;
|
|
427
|
+
case "starts_with":
|
|
428
|
+
return `Value must start with ${JSON.stringify(check.prefix)}.`;
|
|
429
|
+
case "ends_with":
|
|
430
|
+
return `Value must end with ${JSON.stringify(check.suffix)}.`;
|
|
431
|
+
case "lowercase":
|
|
432
|
+
return "Value must be lowercase.";
|
|
433
|
+
case "uppercase":
|
|
434
|
+
return "Value must be uppercase.";
|
|
435
|
+
default:
|
|
436
|
+
return `Value must be ${FORMAT_LABELS[check.format]}.`;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function boundMessage(check, subject) {
|
|
440
|
+
const bound = String(check.value);
|
|
441
|
+
if (subject === "datetime") {
|
|
442
|
+
return check.check === "less_than"
|
|
443
|
+
? `Value must be ${check.inclusive ? "on or before" : "before"} ${bound}.`
|
|
444
|
+
: `Value must be ${check.inclusive ? "on or after" : "after"} ${bound}.`;
|
|
445
|
+
}
|
|
446
|
+
return check.check === "less_than"
|
|
447
|
+
? `Value must be ${check.inclusive ? "at most" : "less than"} ${bound}.`
|
|
448
|
+
: `Value must be ${check.inclusive ? "at least" : "greater than"} ${bound}.`;
|
|
449
|
+
}
|
|
450
|
+
function messageOf(check, subject) {
|
|
451
|
+
switch (check.check) {
|
|
452
|
+
case "less_than":
|
|
453
|
+
case "greater_than":
|
|
454
|
+
return boundMessage(check, subject);
|
|
455
|
+
case "multiple_of":
|
|
456
|
+
return `Value must be a multiple of ${check.value}.`;
|
|
457
|
+
case "max_length":
|
|
458
|
+
return check.list === true
|
|
459
|
+
? `List must have at most ${plural(check.maximum, "entry", "entries")}.`
|
|
460
|
+
: `Value must be at most ${plural(check.maximum, "character", "characters")} long.`;
|
|
461
|
+
case "min_length":
|
|
462
|
+
return check.list === true
|
|
463
|
+
? `List must have at least ${plural(check.minimum, "entry", "entries")}.`
|
|
464
|
+
: `Value must be at least ${plural(check.minimum, "character", "characters")} long.`;
|
|
465
|
+
case "length_equals":
|
|
466
|
+
return `Value must be exactly ${plural(check.length, "character", "characters")} long.`;
|
|
467
|
+
case "string_format":
|
|
468
|
+
return formatMessage(check);
|
|
469
|
+
case "max_size":
|
|
470
|
+
return `Value must be at most ${plural(check.maximum, "byte", "bytes")}.`;
|
|
471
|
+
case "min_size":
|
|
472
|
+
return `Value must be at least ${plural(check.minimum, "byte", "bytes")}.`;
|
|
473
|
+
case "size_equals":
|
|
474
|
+
return `Value must be exactly ${plural(check.size, "byte", "bytes")}.`;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
export function fieldCheckIssue(check, subject, path) {
|
|
478
|
+
const { check: name, ...params } = check;
|
|
479
|
+
const code = name === "string_format" ? "V1003" : "V1004";
|
|
480
|
+
return {
|
|
481
|
+
code,
|
|
482
|
+
path,
|
|
483
|
+
message: messageOf(check, subject),
|
|
484
|
+
check: name,
|
|
485
|
+
params,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
export function fieldCheckIssues(checks, value, subject, path, list) {
|
|
489
|
+
if (checks.length === 0 || value === null || value === undefined) {
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
const issues = [];
|
|
493
|
+
if (list && Array.isArray(value)) {
|
|
494
|
+
for (const check of checks) {
|
|
495
|
+
if (isListCheck(check) && !passesFieldCheck(check, value, subject)) {
|
|
496
|
+
issues.push(fieldCheckIssue(check, subject, path));
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
for (const [index, entry] of value.entries()) {
|
|
500
|
+
issues.push(...entryCheckIssues(checks, entry, subject, [...path, index]));
|
|
501
|
+
}
|
|
502
|
+
return issues;
|
|
503
|
+
}
|
|
504
|
+
return entryCheckIssues(checks, value, subject, path);
|
|
505
|
+
}
|
|
506
|
+
export function entryCheckIssues(checks, value, subject, path) {
|
|
507
|
+
if (value === null || value === undefined) {
|
|
508
|
+
return [];
|
|
509
|
+
}
|
|
510
|
+
const issues = [];
|
|
511
|
+
for (const check of checks) {
|
|
512
|
+
if (!isListCheck(check) && !passesFieldCheck(check, value, subject)) {
|
|
513
|
+
issues.push(fieldCheckIssue(check, subject, path));
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return issues;
|
|
517
|
+
}
|
|
518
|
+
export function isListCheck(check) {
|
|
519
|
+
return ((check.check === "max_length" || check.check === "min_length") &&
|
|
520
|
+
check.list === true);
|
|
521
|
+
}
|