@arktype/schema
Advanced tools
Comparing version 0.1.4 to 0.1.5
@@ -40,4 +40,5 @@ export * from "./ast.js"; | ||
export * from "./structure/index.js"; | ||
export * from "./structure/optional.js"; | ||
export * from "./structure/prop.js"; | ||
export * from "./structure/sequence.js"; | ||
export * from "./structure/structure.js"; |
@@ -40,4 +40,5 @@ export * from "./ast.js"; | ||
export * from "./structure/index.js"; | ||
export * from "./structure/optional.js"; | ||
export * from "./structure/prop.js"; | ||
export * from "./structure/sequence.js"; | ||
export * from "./structure/structure.js"; |
@@ -28,3 +28,3 @@ import type { Constructor, ErrorMessage, NonEnumerableDomain, array, conform, describe, inferDomain, instanceOf, isAny } from "@arktype/util"; | ||
type validateRootBranch<schema, $> = schema extends BaseNode ? schema : "morphs" extends keyof schema ? validateMorphRoot<schema, $> : validateMorphChild<schema, $>; | ||
type inferRootBranch<schema, $> = schema extends type.cast<infer to> ? to : schema extends MorphSchema ? (In: schema["from"] extends {} ? inferMorphChild<schema["from"], $> : unknown) => schema["to"] extends {} ? Out<inferMorphChild<schema["to"], $>> : schema["morphs"] extends infer morph extends Morph ? Out<inferMorphOut<morph>> : schema["morphs"] extends (readonly [...unknown[], infer morph extends Morph]) ? Out<inferMorphOut<morph>> : never : schema extends MorphInputSchema ? inferMorphChild<schema, $> : unknown; | ||
type inferRootBranch<schema, $> = schema extends type.cast<infer to> ? to : schema extends MorphSchema ? (In: schema["in"] extends {} ? inferMorphChild<schema["in"], $> : unknown) => schema["out"] extends {} ? Out<inferMorphChild<schema["out"], $>> : schema["morphs"] extends infer morph extends Morph ? Out<inferMorphOut<morph>> : schema["morphs"] extends (readonly [...unknown[], infer morph extends Morph]) ? Out<inferMorphOut<morph>> : never : schema extends MorphInputSchema ? inferMorphChild<schema, $> : unknown; | ||
type NonIntersectableBasisRoot = NonEnumerableDomain | Constructor | UnitSchema; | ||
@@ -31,0 +31,0 @@ type validateMorphChild<schema, $> = [ |
@@ -5,3 +5,3 @@ import { isWellFormedInteger, wellFormedIntegerMatcher, wellFormedNumberMatcher } from "@arktype/util"; | ||
const number = root.defineRoot({ | ||
from: { | ||
in: { | ||
domain: "string", | ||
@@ -14,3 +14,3 @@ regex: wellFormedNumberMatcher, | ||
const integer = root.defineRoot({ | ||
from: { | ||
in: { | ||
domain: "string", | ||
@@ -27,3 +27,3 @@ regex: wellFormedIntegerMatcher | ||
const url = root.defineRoot({ | ||
from: { | ||
in: { | ||
domain: "string", | ||
@@ -42,3 +42,3 @@ description: "a valid URL" | ||
const json = root.defineRoot({ | ||
from: { | ||
in: { | ||
domain: "string", | ||
@@ -50,3 +50,3 @@ description: "a JSON-parsable string" | ||
const date = root.defineRoot({ | ||
from: "string", | ||
in: "string", | ||
morphs: (s, ctx) => { | ||
@@ -53,0 +53,0 @@ const result = tryParseDatePattern(s); |
@@ -1,2 +0,1 @@ | ||
import { root } from "../../scope.js"; | ||
const dayDelimiterMatcher = /^[./-]$/; | ||
@@ -3,0 +2,0 @@ // ISO 8601 date/time modernized from https://github.com/validatorjs/validator.js/blob/master/src/lib/isISO8601.js |
@@ -33,9 +33,9 @@ import { Callable, type Guardable, type Json, type conform } from "@arktype/util"; | ||
traverse(data: d["prerequisite"]): unknown; | ||
private inCache?; | ||
private _in?; | ||
get in(): BaseNode; | ||
private outCache?; | ||
private _out?; | ||
get out(): BaseNode; | ||
private _description?; | ||
get description(): string; | ||
getIo(kind: "in" | "out"): BaseNode; | ||
private descriptionCache?; | ||
get description(): string; | ||
toJSON(): Json; | ||
@@ -42,0 +42,0 @@ toString(): string; |
@@ -24,3 +24,6 @@ import { Callable, flatMorph, includes, isArray, shallowClone, throwError } from "@arktype/util"; | ||
qualifiedId = `${this.$.id}${this.id}`; | ||
includesMorph = this.kind === "morph" || this.children.some(child => child.includesMorph); | ||
includesMorph = this.kind === "morph" || | ||
(this.hasKind("optional") && this.hasDefault()) || | ||
(this.hasKind("structure") && this.undeclared === "delete") || | ||
this.children.some(child => child.includesMorph); | ||
allowsRequiresContext = | ||
@@ -46,12 +49,22 @@ // if a predicate accepts exactly one arg, we can safely skip passing context | ||
} | ||
inCache; | ||
// unfortunately we can't use the @cached | ||
// decorator from @arktype/util on these for now | ||
// as they cause a deopt in V8 | ||
_in; | ||
get in() { | ||
this.inCache ??= this.getIo("in"); | ||
return this.inCache; | ||
this._in ??= this.getIo("in"); | ||
return this._in; | ||
} | ||
outCache; | ||
_out; | ||
get out() { | ||
this.outCache ??= this.getIo("out"); | ||
return this.outCache; | ||
this._out ??= this.getIo("out"); | ||
return this._out; | ||
} | ||
_description; | ||
get description() { | ||
this._description ??= | ||
this.inner.description ?? | ||
this.$.resolvedConfig[this.kind].description?.(this); | ||
return this._description; | ||
} | ||
getIo(kind) { | ||
@@ -77,9 +90,2 @@ if (!this.includesMorph) | ||
} | ||
descriptionCache; | ||
get description() { | ||
this.descriptionCache ??= | ||
this.inner.description ?? | ||
this.$.resolvedConfig[this.kind].description?.(this); | ||
return this.descriptionCache; | ||
} | ||
toJSON() { | ||
@@ -157,2 +163,3 @@ return this.json; | ||
]); | ||
delete ctx.seen[this.id]; | ||
const node = this.$.node(this.kind, mapper(this.kind, innerWithTransformedChildren, ctx)); | ||
@@ -159,0 +166,0 @@ return node; |
@@ -1,2 +0,2 @@ | ||
import { entriesOf, hasDomain, isArray, printable, throwParseError } from "@arktype/util"; | ||
import { entriesOf, hasDomain, isArray, printable, throwParseError, unset } from "@arktype/util"; | ||
import { nodeClassesByKind, nodeImplementationsByKind } from "./kinds.js"; | ||
@@ -67,3 +67,3 @@ import { Disjoint } from "./shared/disjoint.js"; | ||
const v = keyImpl.parse ? keyImpl.parse(entry[1], ctx) : entry[1]; | ||
if (v !== undefined || keyImpl.preserveUndefined) | ||
if (v !== unset && (v !== undefined || keyImpl.preserveUndefined)) | ||
inner[k] = v; | ||
@@ -149,5 +149,6 @@ } | ||
attachments.alias = ctx.alias; | ||
for (const k in inner) | ||
if (k !== "description") | ||
for (const k in inner) { | ||
if (k !== "description" && k !== "in" && k !== "out") | ||
attachments[k] = inner[k]; | ||
} | ||
const node = new nodeClassesByKind[kind](attachments); | ||
@@ -154,0 +155,0 @@ nodeCache[innerHash] = node; |
@@ -20,3 +20,2 @@ import type { NodeCompiler } from "../shared/compile.js"; | ||
readonly expression: string; | ||
private _resolution; | ||
get resolution(): BaseRoot; | ||
@@ -23,0 +22,0 @@ rawKeyOf(): BaseRoot<RawRootDeclaration>; |
@@ -1,2 +0,36 @@ | ||
import { append } from "@arktype/util"; | ||
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { | ||
var useValue = arguments.length > 2; | ||
for (var i = 0; i < initializers.length; i++) { | ||
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); | ||
} | ||
return useValue ? value : void 0; | ||
}; | ||
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { | ||
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } | ||
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; | ||
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; | ||
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); | ||
var _, done = false; | ||
for (var i = decorators.length - 1; i >= 0; i--) { | ||
var context = {}; | ||
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; | ||
for (var p in contextIn.access) context.access[p] = contextIn.access[p]; | ||
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; | ||
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); | ||
if (kind === "accessor") { | ||
if (result === void 0) continue; | ||
if (result === null || typeof result !== "object") throw new TypeError("Object expected"); | ||
if (_ = accept(result.get)) descriptor.get = _; | ||
if (_ = accept(result.set)) descriptor.set = _; | ||
if (_ = accept(result.init)) initializers.unshift(_); | ||
} | ||
else if (_ = accept(result)) { | ||
if (kind === "field") initializers.unshift(_); | ||
else descriptor[key] = _; | ||
} | ||
} | ||
if (target) Object.defineProperty(target, contextIn.name, descriptor); | ||
done = true; | ||
}; | ||
import { append, cached } from "@arktype/util"; | ||
import { Disjoint } from "../shared/disjoint.js"; | ||
@@ -7,32 +41,42 @@ import { implementNode } from "../shared/implement.js"; | ||
import { defineRightwardIntersections } from "./utils.js"; | ||
export class AliasNode extends BaseRoot { | ||
expression = this.alias; | ||
_resolution; | ||
get resolution() { | ||
this._resolution ??= this.resolve?.() ?? this.$.resolveRoot(this.alias); | ||
return this._resolution; | ||
} | ||
rawKeyOf() { | ||
return this.resolution.keyof(); | ||
} | ||
traverseAllows = (data, ctx) => { | ||
const seen = ctx.seen[this.id]; | ||
if (seen?.includes(data)) | ||
return true; | ||
ctx.seen[this.id] = append(seen, data); | ||
return this.resolution.traverseAllows(data, ctx); | ||
let AliasNode = (() => { | ||
let _classSuper = BaseRoot; | ||
let _instanceExtraInitializers = []; | ||
let _get_resolution_decorators; | ||
return class AliasNode extends _classSuper { | ||
static { | ||
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; | ||
_get_resolution_decorators = [cached]; | ||
__esDecorate(this, null, _get_resolution_decorators, { kind: "getter", name: "resolution", static: false, private: false, access: { has: obj => "resolution" in obj, get: obj => obj.resolution }, metadata: _metadata }, null, _instanceExtraInitializers); | ||
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); | ||
} | ||
expression = (__runInitializers(this, _instanceExtraInitializers), this.alias); | ||
get resolution() { | ||
return this.resolve?.() ?? this.$.resolveRoot(this.alias); | ||
} | ||
rawKeyOf() { | ||
return this.resolution.keyof(); | ||
} | ||
traverseAllows = (data, ctx) => { | ||
const seen = ctx.seen[this.id]; | ||
if (seen?.includes(data)) | ||
return true; | ||
ctx.seen[this.id] = append(seen, data); | ||
return this.resolution.traverseAllows(data, ctx); | ||
}; | ||
traverseApply = (data, ctx) => { | ||
const seen = ctx.seen[this.id]; | ||
if (seen?.includes(data)) | ||
return; | ||
ctx.seen[this.id] = append(seen, data); | ||
this.resolution.traverseApply(data, ctx); | ||
}; | ||
compile(js) { | ||
js.if(`ctx.seen.${this.id}?.includes(data)`, () => js.return(true)); | ||
js.line(`ctx.seen.${this.id} ??= []`).line(`ctx.seen.${this.id}.push(data)`); | ||
js.return(js.invoke(this.resolution)); | ||
} | ||
}; | ||
traverseApply = (data, ctx) => { | ||
const seen = ctx.seen[this.id]; | ||
if (seen?.includes(data)) | ||
return; | ||
ctx.seen[this.id] = append(seen, data); | ||
this.resolution.traverseApply(data, ctx); | ||
}; | ||
compile(js) { | ||
js.if(`ctx.seen.${this.id}?.includes(data)`, () => js.return(true)); | ||
js.line(`ctx.seen.${this.id} ??= []`).line(`ctx.seen.${this.id}.push(data)`); | ||
js.return(js.invoke(this.resolution)); | ||
} | ||
} | ||
})(); | ||
export { AliasNode }; | ||
export const normalizeAliasSchema = (schema) => typeof schema === "string" ? { alias: schema.slice(1) } : schema; | ||
@@ -39,0 +83,0 @@ export const aliasImplementation = implementNode({ |
@@ -9,3 +9,3 @@ import { type array, type listable, type show } from "@arktype/util"; | ||
import type { TraverseAllows, TraverseApply } from "../shared/traversal.js"; | ||
import type { ExtraneousKeyBehavior, StructureNode } from "../structure/structure.js"; | ||
import type { StructureNode, UndeclaredKeyBehavior } from "../structure/structure.js"; | ||
import type { DomainNode, DomainSchema } from "./domain.js"; | ||
@@ -26,3 +26,3 @@ import type { ProtoNode, ProtoSchema } from "./proto.js"; | ||
export type MutableIntersectionInner = MutableInner<"intersection">; | ||
export type NormalizedIntersectionSchema = Omit<IntersectionSchema, StructuralKind | "onExtraneousKey">; | ||
export type NormalizedIntersectionSchema = Omit<IntersectionSchema, StructuralKind | "undeclared">; | ||
export type IntersectionSchema<inferredBasis = any> = show<BaseMeta & { | ||
@@ -54,3 +54,3 @@ domain?: DomainSchema; | ||
export type ConditionalTerminalIntersectionRoot = { | ||
onExtraneousKey?: ExtraneousKeyBehavior; | ||
undeclared?: UndeclaredKeyBehavior; | ||
}; | ||
@@ -62,3 +62,3 @@ type ConditionalTerminalIntersectionKey = keyof ConditionalTerminalIntersectionRoot; | ||
}[ConstraintKind]; | ||
type conditionalIntersectionKeyOf<t> = constraintKindOf<t> | (t extends object ? "onExtraneousKey" : never); | ||
type conditionalIntersectionKeyOf<t> = constraintKindOf<t> | (t extends object ? "undeclared" : never); | ||
type intersectionChildRootValueOf<k extends IntersectionChildKind> = k extends OpenNodeKind ? listable<NodeSchema<k> | Inner<k>> : NodeSchema<k> | Inner<k>; | ||
@@ -65,0 +65,0 @@ type conditionalRootValueOfKey<k extends ConditionalIntersectionKey> = k extends IntersectionChildKind ? intersectionChildRootValueOf<k> : ConditionalTerminalIntersectionRoot[k & ConditionalTerminalIntersectionKey]; |
@@ -1,2 +0,2 @@ | ||
import { type BuiltinObjectKind, type BuiltinObjects, type Primitive, type array, type listable } from "@arktype/util"; | ||
import { type BuiltinObjectKind, type BuiltinObjects, type Primitive, type anyOrNever, type array, type listable, type show } from "@arktype/util"; | ||
import type { of } from "../ast.js"; | ||
@@ -6,3 +6,3 @@ import type { type } from "../inference.js"; | ||
import type { StaticArkOption } from "../scope.js"; | ||
import { NodeCompiler } from "../shared/compile.js"; | ||
import type { NodeCompiler } from "../shared/compile.js"; | ||
import type { BaseMeta, declareNode } from "../shared/declare.js"; | ||
@@ -13,2 +13,3 @@ import type { ArkError, ArkErrors } from "../shared/errors.js"; | ||
import type { TraversalContext, TraverseAllows, TraverseApply } from "../shared/traversal.js"; | ||
import type { DefaultableAst } from "../structure/optional.js"; | ||
import { BaseRoot, type schemaKindRightOf } from "./root.js"; | ||
@@ -22,9 +23,9 @@ export type MorphInputKind = schemaKindRightOf<"morph">; | ||
export interface MorphInner extends BaseMeta { | ||
readonly from: MorphInputNode; | ||
readonly to?: BaseRoot; | ||
readonly in: MorphInputNode; | ||
readonly out?: BaseRoot; | ||
readonly morphs: readonly Morph[]; | ||
} | ||
export interface MorphSchema extends BaseMeta { | ||
readonly from: MorphInputSchema; | ||
readonly to?: RootSchema | undefined; | ||
readonly in: MorphInputSchema; | ||
readonly out?: RootSchema | undefined; | ||
readonly morphs: listable<Morph>; | ||
@@ -45,3 +46,4 @@ } | ||
outValidator: TraverseApply | null; | ||
outValidatorReference: string; | ||
private queueArgs; | ||
private queueArgsReference; | ||
traverseAllows: TraverseAllows; | ||
@@ -51,3 +53,4 @@ traverseApply: TraverseApply; | ||
compile(js: NodeCompiler): void; | ||
getIo(kind: "in" | "out"): BaseRoot; | ||
get in(): BaseRoot; | ||
get out(): BaseRoot; | ||
rawKeyOf(): BaseRoot; | ||
@@ -72,5 +75,14 @@ } | ||
]) ? false : true; | ||
type _distill<t, io extends "in" | "out", distilledKind extends "base" | "constrainable"> = unknown extends t ? unknown : t extends MorphAst<infer i, infer o> ? io extends "in" ? i : o : t extends of<infer base, any> ? distilledKind extends "base" ? _distill<base, io, distilledKind> : t : t extends TerminallyInferredObjectKind | Primitive ? t : t extends array ? distillArray<t, io, distilledKind, []> : { | ||
type _distill<t, io extends "in" | "out", distilledKind extends "base" | "constrainable"> = t extends TerminallyInferredObjectKind | Primitive ? t : unknown extends t ? unknown : t extends MorphAst<infer i, infer o> ? io extends "in" ? _distill<i, io, distilledKind> : _distill<o, io, distilledKind> : t extends DefaultableAst<infer t> ? _distill<t, io, distilledKind> : t extends of<infer base, any> ? distilledKind extends "base" ? _distill<base, io, distilledKind> : t : t extends array ? distillArray<t, io, distilledKind, []> : t extends Function ? t : { | ||
[k in keyof t]: t[k]; | ||
} extends t ? io extends "in" ? show<{ | ||
[k in keyof t as k extends defaultableKeyOf<t> ? never : k]: _distill<t[k], io, distilledKind>; | ||
} & { | ||
[k in defaultableKeyOf<t>]?: _distill<t[k], io, distilledKind>; | ||
}> : { | ||
[k in keyof t]: _distill<t[k], io, distilledKind>; | ||
}; | ||
} : t; | ||
type defaultableKeyOf<t> = { | ||
[k in keyof t]: [t[k]] extends [anyOrNever] ? never : t[k] extends DefaultableAst ? k : never; | ||
}[keyof t]; | ||
type distillArray<t extends array, io extends "in" | "out", constraints extends "base" | "constrainable", prefix extends array> = t extends readonly [infer head, ...infer tail] ? distillArray<tail, io, constraints, [ | ||
@@ -87,3 +99,3 @@ ...prefix, | ||
/** Objects we don't want to expand during inference like Date or Promise */ | ||
type TerminallyInferredObjectKind = StaticArkOption<"preserve"> | BuiltinObjects[Exclude<BuiltinObjectKind, "Array">]; | ||
type TerminallyInferredObjectKind = StaticArkOption<"preserve"> | BuiltinObjects[Exclude<BuiltinObjectKind, "Array" | "Function">]; | ||
export {}; |
import { arrayFrom, registeredReference, throwParseError } from "@arktype/util"; | ||
import { NodeCompiler } from "../shared/compile.js"; | ||
import { Disjoint } from "../shared/disjoint.js"; | ||
@@ -18,7 +17,7 @@ import { implementNode } from "../shared/implement.js"; | ||
keys: { | ||
from: { | ||
in: { | ||
child: true, | ||
parse: (schema, ctx) => ctx.$.node(morphInputKinds, schema) | ||
}, | ||
to: { | ||
out: { | ||
child: true, | ||
@@ -28,7 +27,7 @@ parse: (schema, ctx) => { | ||
return; | ||
const to = ctx.$.schema(schema); | ||
return to.kind === "intersection" && to.children.length === 0 ? | ||
const out = ctx.$.schema(schema); | ||
return out.kind === "intersection" && out.children.length === 0 ? | ||
// ignore unknown as an output validator | ||
undefined | ||
: to; | ||
: out; | ||
} | ||
@@ -43,3 +42,3 @@ }, | ||
defaults: { | ||
description: node => `a morph from ${node.from.description} to ${node.to?.description ?? "unknown"}` | ||
description: node => `a morph from ${node.in.description} to ${node.out?.description ?? "unknown"}` | ||
}, | ||
@@ -51,31 +50,31 @@ intersections: { | ||
return throwParseError("Invalid intersection of morphs"); | ||
const from = intersectNodes(l.from, r.from, ctx); | ||
if (from instanceof Disjoint) | ||
return from; | ||
const to = l.to ? | ||
r.to ? | ||
intersectNodes(l.to, r.to, ctx) | ||
: l.to | ||
: r.to; | ||
if (to instanceof Disjoint) | ||
return to; | ||
const inTersection = intersectNodes(l.in, r.in, ctx); | ||
if (inTersection instanceof Disjoint) | ||
return inTersection; | ||
const out = l.out ? | ||
r.out ? | ||
intersectNodes(l.out, r.out, ctx) | ||
: l.out | ||
: r.out; | ||
if (out instanceof Disjoint) | ||
return out; | ||
// in case from is a union, we need to distribute the branches | ||
// to can be a union as any schema is allowed | ||
return ctx.$.schema(from.branches.map(fromBranch => ctx.$.node("morph", { | ||
return ctx.$.schema(inTersection.branches.map(inBranch => ctx.$.node("morph", { | ||
morphs: l.morphs, | ||
from: fromBranch, | ||
to | ||
in: inBranch, | ||
out | ||
}))); | ||
}, | ||
...defineRightwardIntersections("morph", (l, r, ctx) => { | ||
const from = intersectNodes(l.from, r, ctx); | ||
return (from instanceof Disjoint ? from | ||
: from.kind === "union" ? | ||
ctx.$.node("union", from.branches.map(branch => ({ | ||
const inTersection = intersectNodes(l.in, r, ctx); | ||
return (inTersection instanceof Disjoint ? inTersection | ||
: inTersection.kind === "union" ? | ||
ctx.$.node("union", inTersection.branches.map(branch => ({ | ||
...l.inner, | ||
from: branch | ||
in: branch | ||
}))) | ||
: ctx.$.node("morph", { | ||
...l.inner, | ||
from | ||
in: inTersection | ||
})); | ||
@@ -88,28 +87,31 @@ }) | ||
compiledMorphs = `[${this.serializedMorphs}]`; | ||
outValidator = this.to?.traverseApply ?? null; | ||
outValidatorReference = this.to ? | ||
new NodeCompiler("Apply").reference(this.to, { bind: "this" }) | ||
: "null"; | ||
traverseAllows = (data, ctx) => this.from.traverseAllows(data, ctx); | ||
outValidator = this.inner.out?.traverseApply ?? null; | ||
queueArgs = [ | ||
this.morphs, | ||
this.outValidator ? { outValidator: this.outValidator } : {} | ||
]; | ||
queueArgsReference = registeredReference(this.queueArgs); | ||
traverseAllows = (data, ctx) => this.in.traverseAllows(data, ctx); | ||
traverseApply = (data, ctx) => { | ||
ctx.queueMorphs(this.morphs, this.outValidator); | ||
this.from.traverseApply(data, ctx); | ||
ctx.queueMorphs(...this.queueArgs); | ||
this.in.traverseApply(data, ctx); | ||
}; | ||
expression = `(In: ${this.from.expression}) => Out<${this.to?.expression ?? "unknown"}>`; | ||
expression = `(In: ${this.in.expression}) => Out<${this.out?.expression ?? "unknown"}>`; | ||
compile(js) { | ||
if (js.traversalKind === "Allows") { | ||
js.return(js.invoke(this.from)); | ||
js.return(js.invoke(this.in)); | ||
return; | ||
} | ||
js.line(`ctx.queueMorphs(${this.compiledMorphs}, ${this.outValidatorReference})`); | ||
js.line(js.invoke(this.from)); | ||
js.line(`ctx.queueMorphs(...${this.queueArgsReference})`); | ||
js.line(js.invoke(this.in)); | ||
} | ||
getIo(kind) { | ||
return kind === "in" ? | ||
this.from | ||
: this.to?.out ?? this.$.keywords.unknown.raw; | ||
get in() { | ||
return this.inner.in; | ||
} | ||
get out() { | ||
return this.inner.out?.out ?? this.$.keywords.unknown.raw; | ||
} | ||
rawKeyOf() { | ||
return this.from.rawKeyOf(); | ||
return this.in.rawKeyOf(); | ||
} | ||
} |
@@ -11,5 +11,6 @@ import { type Callable, type Json, type conform } from "@arktype/util"; | ||
import { ArkErrors } from "../shared/errors.js"; | ||
import type { NodeKind, RootKind, kindRightOf } from "../shared/implement.js"; | ||
import { type NodeKind, type RootKind, type kindRightOf } from "../shared/implement.js"; | ||
import { type inferIntersection } from "../shared/intersections.js"; | ||
import { arkKind, type inferred, type internalImplementationOf } from "../shared/utils.js"; | ||
import type { UndeclaredKeyBehavior } from "../structure/structure.js"; | ||
import type { constraintKindOf } from "./intersection.js"; | ||
@@ -49,2 +50,3 @@ import type { Morph, distillConstrainableIn, distillConstrainableOut, distillIn, distillOut, inferMorphOut, inferPipes } from "./morph.js"; | ||
constrain<kind extends PrimitiveConstraintKind>(kind: kind, schema: NodeSchema<kind>): BaseRoot; | ||
onUndeclaredKey(undeclared: UndeclaredKeyBehavior): BaseRoot; | ||
} | ||
@@ -81,2 +83,3 @@ export declare abstract class InnerRoot<t = unknown, $ = any> extends Callable<(data: unknown) => distillOut<t> | ArkErrors> { | ||
describe(description: string): this; | ||
onUndeclaredKey(behavior: UndeclaredKeyBehavior): this; | ||
create(literal: this["inferIn"]): this["infer"]; | ||
@@ -83,0 +86,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { throwParseError } from "@arktype/util"; | ||
import { includes, omit, throwParseError } from "@arktype/util"; | ||
import { throwInvalidOperandError } from "../constraint.js"; | ||
@@ -6,2 +6,3 @@ import { BaseNode } from "../node.js"; | ||
import { ArkErrors } from "../shared/errors.js"; | ||
import { structuralKinds } from "../shared/implement.js"; | ||
import { intersectNodesRoot, pipeNodesRoot } from "../shared/intersections.js"; | ||
@@ -96,3 +97,3 @@ import { arkKind, hasArkKind } from "../shared/utils.js"; | ||
return this.$.node("morph", { | ||
from: this, | ||
in: this, | ||
morphs: [morph] | ||
@@ -115,2 +116,9 @@ }); | ||
} | ||
onUndeclaredKey(undeclared) { | ||
return this.transform((kind, inner) => kind === "structure" ? | ||
undeclared === "ignore" ? | ||
omit(inner, { undeclared: 1 }) | ||
: { ...inner, undeclared } | ||
: inner, node => !includes(structuralKinds, node.kind)); | ||
} | ||
} |
@@ -35,2 +35,3 @@ import type { Node, NodeSchema } from "../kinds.js"; | ||
export declare class UnionNode extends BaseRoot<UnionDeclaration> { | ||
isNever: boolean; | ||
isBoolean: boolean; | ||
@@ -37,0 +38,0 @@ discriminant: null; |
@@ -64,6 +64,5 @@ import { appendUnique, groupBy, isArray } from "@arktype/util"; | ||
union: (l, r, ctx) => { | ||
if ((l.branches.length === 0 || r.branches.length === 0) && | ||
l.branches.length !== r.branches.length) { | ||
if (l.isNever !== r.isNever) { | ||
// if exactly one operand is never, we can use it to discriminate based on presence | ||
return Disjoint.from("presence", l.branches.length !== 0, r.branches.length !== 0); | ||
return Disjoint.from("presence", l, r); | ||
} | ||
@@ -100,2 +99,3 @@ let resultBranches; | ||
export class UnionNode extends BaseRoot { | ||
isNever = this.branches.length === 0; | ||
isBoolean = this.branches.length === 2 && | ||
@@ -105,3 +105,5 @@ this.branches[0].hasUnit(false) && | ||
discriminant = null; | ||
expression = this.isBoolean ? "boolean" : (this.branches.map(branch => branch.nestableExpression).join(" | ")); | ||
expression = this.isNever ? "never" | ||
: this.isBoolean ? "boolean" | ||
: this.branches.map(branch => branch.nestableExpression).join(" | "); | ||
traverseAllows = (data, ctx) => this.branches.some(b => b.traverseAllows(data, ctx)); | ||
@@ -108,0 +110,0 @@ traverseApply = (data, ctx) => { |
@@ -95,5 +95,5 @@ import { DynamicBase, type Json, type array, type flattenListable, type requireKeys, type show } from "@arktype/util"; | ||
get raw(): this; | ||
schema: (def: RootSchema, opts?: NodeParseOptions) => BaseRoot; | ||
defineRoot: (def: RootSchema) => RootSchema; | ||
units: (values: array, opts?: NodeParseOptions) => BaseRoot; | ||
schema(def: RootSchema, opts?: NodeParseOptions): BaseRoot; | ||
defineRoot(def: RootSchema): RootSchema; | ||
units(values: array, opts?: NodeParseOptions): BaseRoot; | ||
protected lazyResolutions: AliasNode[]; | ||
@@ -100,0 +100,0 @@ lazilyResolve(resolve: () => BaseRoot, syntheticAlias?: string): AliasNode; |
469
out/scope.js
@@ -1,2 +0,36 @@ | ||
import { CompiledFunction, DynamicBase, flatMorph, hasDomain, isArray, printable, throwInternalError, throwParseError } from "@arktype/util"; | ||
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { | ||
var useValue = arguments.length > 2; | ||
for (var i = 0; i < initializers.length; i++) { | ||
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); | ||
} | ||
return useValue ? value : void 0; | ||
}; | ||
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { | ||
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } | ||
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; | ||
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; | ||
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); | ||
var _, done = false; | ||
for (var i = decorators.length - 1; i >= 0; i--) { | ||
var context = {}; | ||
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; | ||
for (var p in contextIn.access) context.access[p] = contextIn.access[p]; | ||
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; | ||
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); | ||
if (kind === "accessor") { | ||
if (result === void 0) continue; | ||
if (result === null || typeof result !== "object") throw new TypeError("Object expected"); | ||
if (_ = accept(result.get)) descriptor.get = _; | ||
if (_ = accept(result.set)) descriptor.set = _; | ||
if (_ = accept(result.init)) initializers.unshift(_); | ||
} | ||
else if (_ = accept(result)) { | ||
if (kind === "field") initializers.unshift(_); | ||
else descriptor[key] = _; | ||
} | ||
} | ||
if (target) Object.defineProperty(target, contextIn.name, descriptor); | ||
done = true; | ||
}; | ||
import { CompiledFunction, DynamicBase, bound, flatMorph, hasDomain, isArray, printable, throwInternalError, throwParseError } from "@arktype/util"; | ||
import { globalConfig, mergeConfigs } from "./config.js"; | ||
@@ -42,218 +76,239 @@ import { validateUninstantiatedGenericNode } from "./generic.js"; | ||
const scopesById = {}; | ||
export class RawRootScope { | ||
config; | ||
resolvedConfig; | ||
id = `$${++scopeCount}`; | ||
[arkKind] = "scope"; | ||
referencesById = {}; | ||
references = []; | ||
resolutions = {}; | ||
json = {}; | ||
exportedNames; | ||
aliases = {}; | ||
resolved = false; | ||
// these allow builtin types to be accessed during parsing without cyclic imports | ||
// they are populated as each scope is parsed with `registerKeywords` in its config | ||
/** @internal */ | ||
static keywords = {}; | ||
/** @internal */ | ||
get keywords() { | ||
return RawRootScope.keywords; | ||
} | ||
static ambient; | ||
get ambient() { | ||
return this.constructor.ambient; | ||
} | ||
constructor( | ||
/** The set of names defined at the root-level of the scope mapped to their | ||
* corresponding definitions.**/ | ||
def, config) { | ||
this.config = config ?? {}; | ||
this.resolvedConfig = resolveConfig(config); | ||
this.exportedNames = Object.keys(def).filter(k => { | ||
if (k[0] === "#") { | ||
const name = k.slice(1); | ||
if (name in this.aliases) | ||
throwParseError(writeDuplicateAliasError(name)); | ||
this.aliases[name] = def[k]; | ||
return false; | ||
let RawRootScope = (() => { | ||
let _instanceExtraInitializers = []; | ||
let _schema_decorators; | ||
let _defineRoot_decorators; | ||
let _units_decorators; | ||
return class RawRootScope { | ||
static { | ||
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; | ||
_schema_decorators = [bound]; | ||
_defineRoot_decorators = [bound]; | ||
_units_decorators = [bound]; | ||
__esDecorate(this, null, _schema_decorators, { kind: "method", name: "schema", static: false, private: false, access: { has: obj => "schema" in obj, get: obj => obj.schema }, metadata: _metadata }, null, _instanceExtraInitializers); | ||
__esDecorate(this, null, _defineRoot_decorators, { kind: "method", name: "defineRoot", static: false, private: false, access: { has: obj => "defineRoot" in obj, get: obj => obj.defineRoot }, metadata: _metadata }, null, _instanceExtraInitializers); | ||
__esDecorate(this, null, _units_decorators, { kind: "method", name: "units", static: false, private: false, access: { has: obj => "units" in obj, get: obj => obj.units }, metadata: _metadata }, null, _instanceExtraInitializers); | ||
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); | ||
} | ||
config = __runInitializers(this, _instanceExtraInitializers); | ||
resolvedConfig; | ||
id = `$${++scopeCount}`; | ||
[arkKind] = "scope"; | ||
referencesById = {}; | ||
references = []; | ||
resolutions = {}; | ||
json = {}; | ||
exportedNames; | ||
aliases = {}; | ||
resolved = false; | ||
// these allow builtin types to be accessed during parsing without cyclic imports | ||
// they are populated as each scope is parsed with `registerKeywords` in its config | ||
/** @internal */ | ||
static keywords = {}; | ||
/** @internal */ | ||
get keywords() { | ||
return RawRootScope.keywords; | ||
} | ||
static ambient; | ||
get ambient() { | ||
return this.constructor.ambient; | ||
} | ||
constructor( | ||
/** The set of names defined at the root-level of the scope mapped to their | ||
* corresponding definitions.**/ | ||
def, config) { | ||
this.config = config ?? {}; | ||
this.resolvedConfig = resolveConfig(config); | ||
this.exportedNames = Object.keys(def).filter(k => { | ||
if (k[0] === "#") { | ||
const name = k.slice(1); | ||
if (name in this.aliases) | ||
throwParseError(writeDuplicateAliasError(name)); | ||
this.aliases[name] = def[k]; | ||
return false; | ||
} | ||
if (k in this.aliases) | ||
throwParseError(writeDuplicateAliasError(k)); | ||
this.aliases[k] = def[k]; | ||
return true; | ||
}); | ||
if (this.ambient) { | ||
// ensure exportedResolutions is populated | ||
this.ambient.export(); | ||
// TODO: generics and modules | ||
this.resolutions = flatMorph(this.ambient.resolutions, (alias, resolution) => [ | ||
alias, | ||
hasArkKind(resolution, "root") ? | ||
resolution.bindScope(this) | ||
: resolution | ||
]); | ||
} | ||
if (k in this.aliases) | ||
throwParseError(writeDuplicateAliasError(k)); | ||
this.aliases[k] = def[k]; | ||
return true; | ||
}); | ||
if (this.ambient) { | ||
// ensure exportedResolutions is populated | ||
this.ambient.export(); | ||
// TODO: generics and modules | ||
this.resolutions = flatMorph(this.ambient.resolutions, (alias, resolution) => [ | ||
alias, | ||
hasArkKind(resolution, "root") ? | ||
resolution.bindScope(this) | ||
: resolution | ||
]); | ||
scopesById[this.id] = this; | ||
} | ||
scopesById[this.id] = this; | ||
} | ||
get raw() { | ||
return this; | ||
} | ||
schema = (((def, opts) => this.node(schemaKindOf(def), def, opts))).bind(this); | ||
defineRoot = ((def => def)).bind(this); | ||
units = (((values, opts) => { | ||
const uniqueValues = []; | ||
for (const value of values) | ||
if (!uniqueValues.includes(value)) | ||
uniqueValues.push(value); | ||
const branches = uniqueValues.map(unit => this.node("unit", { unit }, opts)); | ||
return this.node("union", branches, { | ||
...opts, | ||
prereduced: true | ||
}); | ||
})).bind(this); | ||
lazyResolutions = []; | ||
lazilyResolve(resolve, syntheticAlias) { | ||
if (!syntheticAlias) { | ||
nodeCountsByPrefix.synthetic ??= 0; | ||
syntheticAlias = `synthetic${++nodeCountsByPrefix.synthetic}`; | ||
get raw() { | ||
return this; | ||
} | ||
const node = this.node("alias", { | ||
alias: syntheticAlias, | ||
resolve | ||
}, { prereduced: true }); | ||
this.lazyResolutions.push(node); | ||
return node; | ||
} | ||
node = (((kinds, nodeSchema, opts) => { | ||
let kind = typeof kinds === "string" ? kinds : schemaKindOf(nodeSchema, kinds); | ||
let schema = nodeSchema; | ||
if (isNode(schema) && schema.kind === kind) | ||
return schema.bindScope(this); | ||
if (kind === "alias" && !opts?.prereduced) { | ||
const resolution = this.resolveRoot(normalizeAliasSchema(schema).alias); | ||
schema = resolution; | ||
kind = resolution.kind; | ||
schema(def, opts) { | ||
return this.node(schemaKindOf(def), def, opts); | ||
} | ||
else if (kind === "union" && hasDomain(schema, "object")) { | ||
const branches = schemaBranchesOf(schema); | ||
if (branches?.length === 1) { | ||
schema = branches[0]; | ||
kind = schemaKindOf(schema); | ||
defineRoot(def) { | ||
return def; | ||
} | ||
units(values, opts) { | ||
const uniqueValues = []; | ||
for (const value of values) | ||
if (!uniqueValues.includes(value)) | ||
uniqueValues.push(value); | ||
const branches = uniqueValues.map(unit => this.node("unit", { unit }, opts)); | ||
return this.node("union", branches, { | ||
...opts, | ||
prereduced: true | ||
}); | ||
} | ||
lazyResolutions = []; | ||
lazilyResolve(resolve, syntheticAlias) { | ||
if (!syntheticAlias) { | ||
nodeCountsByPrefix.synthetic ??= 0; | ||
syntheticAlias = `synthetic${++nodeCountsByPrefix.synthetic}`; | ||
} | ||
const node = this.node("alias", { | ||
alias: syntheticAlias, | ||
resolve | ||
}, { prereduced: true }); | ||
this.lazyResolutions.push(node); | ||
return node; | ||
} | ||
const impl = nodeImplementationsByKind[kind]; | ||
const normalizedSchema = impl.normalize?.(schema) ?? schema; | ||
// check again after normalization in case a node is a valid collapsed | ||
// schema for the kind (e.g. sequence can collapse to element accepting a Node) | ||
if (isNode(normalizedSchema)) { | ||
return normalizedSchema.kind === kind ? | ||
normalizedSchema.bindScope(this) | ||
: throwMismatchedNodeRootError(kind, normalizedSchema.kind); | ||
node = (((kinds, nodeSchema, opts) => { | ||
let kind = typeof kinds === "string" ? kinds : schemaKindOf(nodeSchema, kinds); | ||
let schema = nodeSchema; | ||
if (isNode(schema) && schema.kind === kind) | ||
return schema.bindScope(this); | ||
if (kind === "alias" && !opts?.prereduced) { | ||
const resolution = this.resolveRoot(normalizeAliasSchema(schema).alias); | ||
schema = resolution; | ||
kind = resolution.kind; | ||
} | ||
else if (kind === "union" && hasDomain(schema, "object")) { | ||
const branches = schemaBranchesOf(schema); | ||
if (branches?.length === 1) { | ||
schema = branches[0]; | ||
kind = schemaKindOf(schema); | ||
} | ||
} | ||
const impl = nodeImplementationsByKind[kind]; | ||
const normalizedSchema = impl.normalize?.(schema) ?? schema; | ||
// check again after normalization in case a node is a valid collapsed | ||
// schema for the kind (e.g. sequence can collapse to element accepting a Node) | ||
if (isNode(normalizedSchema)) { | ||
return normalizedSchema.kind === kind ? | ||
normalizedSchema.bindScope(this) | ||
: throwMismatchedNodeRootError(kind, normalizedSchema.kind); | ||
} | ||
const prefix = opts?.alias ?? kind; | ||
nodeCountsByPrefix[prefix] ??= 0; | ||
const id = `${prefix}${++nodeCountsByPrefix[prefix]}`; | ||
const node = parseNode(kind, { | ||
...opts, | ||
id, | ||
$: this, | ||
schema: normalizedSchema | ||
}).bindScope(this); | ||
nodesById[id] = node; | ||
if (this.resolved) { | ||
// this node was not part of the original scope, so compile an anonymous scope | ||
// including only its references | ||
if (!this.resolvedConfig.jitless) | ||
bindCompiledScope(node.contributesReferences); | ||
} | ||
else { | ||
// we're still parsing the scope itself, so defer compilation but | ||
// add the node as a reference | ||
Object.assign(this.referencesById, node.contributesReferencesById); | ||
} | ||
return node; | ||
})).bind(this); | ||
parseRoot(def, opts) { | ||
return this.schema(def, opts); | ||
} | ||
const prefix = opts?.alias ?? kind; | ||
nodeCountsByPrefix[prefix] ??= 0; | ||
const id = `${prefix}${++nodeCountsByPrefix[prefix]}`; | ||
const node = parseNode(kind, { | ||
...opts, | ||
id, | ||
$: this, | ||
schema: normalizedSchema | ||
}).bindScope(this); | ||
nodesById[id] = node; | ||
if (this.resolved) { | ||
// this node was not part of the original scope, so compile an anonymous scope | ||
// including only its references | ||
if (!this.resolvedConfig.jitless) | ||
bindCompiledScope(node.contributesReferences); | ||
resolveRoot(name) { | ||
return (this.maybeResolveRoot(name) ?? | ||
throwParseError(writeUnresolvableMessage(name))); | ||
} | ||
else { | ||
// we're still parsing the scope itself, so defer compilation but | ||
// add the node as a reference | ||
Object.assign(this.referencesById, node.contributesReferencesById); | ||
maybeResolveRoot(name) { | ||
const result = this.maybeResolveGenericOrRoot(name); | ||
if (hasArkKind(result, "generic")) | ||
return; | ||
return result; | ||
} | ||
return node; | ||
})).bind(this); | ||
parseRoot(def, opts) { | ||
return this.schema(def, opts); | ||
} | ||
resolveRoot(name) { | ||
return (this.maybeResolveRoot(name) ?? | ||
throwParseError(writeUnresolvableMessage(name))); | ||
} | ||
maybeResolveRoot(name) { | ||
const result = this.maybeResolveGenericOrRoot(name); | ||
if (hasArkKind(result, "generic")) | ||
return; | ||
return result; | ||
} | ||
maybeResolveGenericOrRoot(name) { | ||
const resolution = this.maybeResolve(name); | ||
if (hasArkKind(resolution, "module")) | ||
return throwParseError(writeMissingSubmoduleAccessMessage(name)); | ||
return resolution; | ||
} | ||
preparseRoot(def) { | ||
return def; | ||
} | ||
maybeResolve(name) { | ||
const resolution = this.maybeShallowResolve(name); | ||
return typeof resolution === "string" ? | ||
this.node("alias", { alias: resolution }, { prereduced: true }) | ||
: resolution; | ||
} | ||
maybeShallowResolve(name) { | ||
const cached = this.resolutions[name]; | ||
if (cached) | ||
return cached; | ||
let def = this.aliases[name]; | ||
if (!def) | ||
return this.maybeResolveSubalias(name); | ||
def = this.preparseRoot(def); | ||
if (hasArkKind(def, "generic")) | ||
return (this.resolutions[name] = validateUninstantiatedGenericNode(def)); | ||
if (hasArkKind(def, "module")) | ||
return (this.resolutions[name] = def); | ||
this.resolutions[name] = name; | ||
return (this.resolutions[name] = this.parseRoot(def)); | ||
} | ||
/** If name is a valid reference to a submodule alias, return its resolution */ | ||
maybeResolveSubalias(name) { | ||
return resolveSubalias(this.aliases, name); | ||
} | ||
import(...names) { | ||
return new RootModule(flatMorph(this.export(...names), (alias, value) => [ | ||
`#${alias}`, | ||
value | ||
])); | ||
} | ||
_exportedResolutions; | ||
_exports; | ||
export(...names) { | ||
if (!this._exports) { | ||
this._exports = {}; | ||
for (const name of this.exportedNames) | ||
this._exports[name] = this.maybeResolve(name); | ||
this.lazyResolutions.forEach(node => node.resolution); | ||
this._exportedResolutions = resolutionsOfModule(this, this._exports); | ||
// TODO: add generic json | ||
Object.assign(this.json, flatMorph(this._exportedResolutions, (k, v) => hasArkKind(v, "root") ? [k, v.json] : [])); | ||
Object.assign(this.resolutions, this._exportedResolutions); | ||
if (this.config.registerKeywords) | ||
Object.assign(RawRootScope.keywords, this._exportedResolutions); | ||
this.references = Object.values(this.referencesById); | ||
if (!this.resolvedConfig.jitless) | ||
bindCompiledScope(this.references); | ||
this.resolved = true; | ||
maybeResolveGenericOrRoot(name) { | ||
const resolution = this.maybeResolve(name); | ||
if (hasArkKind(resolution, "module")) | ||
return throwParseError(writeMissingSubmoduleAccessMessage(name)); | ||
return resolution; | ||
} | ||
const namesToExport = names.length ? names : this.exportedNames; | ||
return new RootModule(flatMorph(namesToExport, (_, name) => [ | ||
name, | ||
this._exports[name] | ||
])); | ||
} | ||
resolve(name) { | ||
return this.export()[name]; | ||
} | ||
} | ||
preparseRoot(def) { | ||
return def; | ||
} | ||
maybeResolve(name) { | ||
const resolution = this.maybeShallowResolve(name); | ||
return typeof resolution === "string" ? | ||
this.node("alias", { alias: resolution }, { prereduced: true }) | ||
: resolution; | ||
} | ||
maybeShallowResolve(name) { | ||
const cached = this.resolutions[name]; | ||
if (cached) | ||
return cached; | ||
let def = this.aliases[name]; | ||
if (!def) | ||
return this.maybeResolveSubalias(name); | ||
def = this.preparseRoot(def); | ||
if (hasArkKind(def, "generic")) | ||
return (this.resolutions[name] = validateUninstantiatedGenericNode(def)); | ||
if (hasArkKind(def, "module")) | ||
return (this.resolutions[name] = def); | ||
this.resolutions[name] = name; | ||
return (this.resolutions[name] = this.parseRoot(def)); | ||
} | ||
/** If name is a valid reference to a submodule alias, return its resolution */ | ||
maybeResolveSubalias(name) { | ||
return resolveSubalias(this.aliases, name); | ||
} | ||
import(...names) { | ||
return new RootModule(flatMorph(this.export(...names), (alias, value) => [ | ||
`#${alias}`, | ||
value | ||
])); | ||
} | ||
_exportedResolutions; | ||
_exports; | ||
export(...names) { | ||
if (!this._exports) { | ||
this._exports = {}; | ||
for (const name of this.exportedNames) | ||
this._exports[name] = this.maybeResolve(name); | ||
this.lazyResolutions.forEach(node => node.resolution); | ||
this._exportedResolutions = resolutionsOfModule(this, this._exports); | ||
// TODO: add generic json | ||
Object.assign(this.json, flatMorph(this._exportedResolutions, (k, v) => hasArkKind(v, "root") ? [k, v.json] : [])); | ||
Object.assign(this.resolutions, this._exportedResolutions); | ||
if (this.config.registerKeywords) | ||
Object.assign(RawRootScope.keywords, this._exportedResolutions); | ||
this.references = Object.values(this.referencesById); | ||
if (!this.resolvedConfig.jitless) | ||
bindCompiledScope(this.references); | ||
this.resolved = true; | ||
} | ||
const namesToExport = names.length ? names : this.exportedNames; | ||
return new RootModule(flatMorph(namesToExport, (_, name) => [ | ||
name, | ||
this._exports[name] | ||
])); | ||
} | ||
resolve(name) { | ||
return this.export()[name]; | ||
} | ||
}; | ||
})(); | ||
export { RawRootScope }; | ||
const resolveSubalias = (base, name) => { | ||
@@ -260,0 +315,0 @@ const dotIndex = name.indexOf("."); |
@@ -25,3 +25,3 @@ import { CompiledFunction } from "@arktype/util"; | ||
returnIfFailFast(): this; | ||
checkReferenceKey(keyExpression: string, node: BaseNode): this; | ||
traverseKey(keyExpression: string, accessExpression: string, node: BaseNode): this; | ||
check(node: BaseNode, opts?: InvokeOptions): this; | ||
@@ -28,0 +28,0 @@ compilePrimitive(node: Node<PrimitiveKind>): this; |
@@ -33,3 +33,3 @@ import { CompiledFunction } from "@arktype/util"; | ||
} | ||
checkReferenceKey(keyExpression, node) { | ||
traverseKey(keyExpression, accessExpression, node) { | ||
const requiresContext = this.requiresContextFor(node); | ||
@@ -39,3 +39,3 @@ if (requiresContext) | ||
this.check(node, { | ||
arg: `${this.data}${this.index(keyExpression)}` | ||
arg: accessExpression | ||
}); | ||
@@ -42,0 +42,0 @@ if (requiresContext) |
@@ -19,7 +19,4 @@ import { type entryOf } from "@arktype/util"; | ||
presence?: { | ||
l: true; | ||
r: false; | ||
} | { | ||
l: false; | ||
r: true; | ||
l: BaseRoot; | ||
r: BaseRoot; | ||
}; | ||
@@ -59,6 +56,7 @@ range?: { | ||
export type DisjointSourceEntry = entryOf<DisjointsSources>; | ||
export type DisjointSource = Required<DisjointKinds>[DisjointKind]; | ||
export type FlatDisjointEntry = { | ||
path: SerializedPath; | ||
kind: DisjointKind; | ||
disjoint: Required<DisjointKinds>[DisjointKind]; | ||
disjoint: DisjointSource; | ||
}; | ||
@@ -65,0 +63,0 @@ export type DisjointKind = keyof DisjointKinds; |
@@ -39,6 +39,6 @@ import { entriesOf, flatMorph, fromEntries, isArray, printable, register, throwInternalError, throwParseError } from "@arktype/util"; | ||
const pathString = JSON.parse(path).join("."); | ||
return `Intersection${pathString && ` at ${pathString}`} of ${describeReason(disjoint.l)} and ${describeReason(disjoint.r)} results in an unsatisfiable type`; | ||
return `Intersection${pathString && ` at ${pathString}`} of ${describeReasons(disjoint)} results in an unsatisfiable type`; | ||
} | ||
return `The following intersections result in unsatisfiable types:\n• ${reasons | ||
.map(({ path, disjoint }) => `${path}: ${describeReason(disjoint.l)} and ${describeReason(disjoint.r)}`) | ||
.map(({ path, disjoint }) => `${path}: ${describeReasons(disjoint)}`) | ||
.join("\n• ")}`; | ||
@@ -78,4 +78,5 @@ } | ||
} | ||
const describeReasons = (source) => `${describeReason(source.l)} and ${describeReason(source.r)}`; | ||
const describeReason = (value) => hasArkKind(value, "root") ? value.expression | ||
: isArray(value) ? value.map(describeReason).join(" | ") | ||
: String(value); |
@@ -1,2 +0,2 @@ | ||
import { CastableBase, ReadonlyArray, type show } from "@arktype/util"; | ||
import { CastableBase, ReadonlyArray, type propwiseXor, type show } from "@arktype/util"; | ||
import type { Prerequisite, errorContext } from "../kinds.js"; | ||
@@ -44,2 +44,7 @@ import type { NodeKind } from "./implement.js"; | ||
} | ||
export type DerivableErrorContextInput<code extends ArkErrorCode = ArkErrorCode> = Partial<DerivableErrorContext<code>> & propwiseXor<{ | ||
path?: TraversalPath; | ||
}, { | ||
relativePath?: TraversalPath; | ||
}>; | ||
export type ArkErrorCode = { | ||
@@ -49,3 +54,3 @@ [kind in NodeKind]: errorContext<kind> extends null ? never : kind; | ||
type ArkErrorContextInputsByCode = { | ||
[code in ArkErrorCode]: errorContext<code> & Partial<DerivableErrorContext<code>>; | ||
[code in ArkErrorCode]: errorContext<code> & DerivableErrorContextInput<code>; | ||
}; | ||
@@ -57,3 +62,3 @@ export type ArkErrorContextInput<code extends ArkErrorCode = ArkErrorCode> = ArkErrorContextInputsByCode[code]; | ||
code?: undefined; | ||
} & Partial<DerivableErrorContext>>; | ||
} & DerivableErrorContextInput>; | ||
export type ArkErrorInput = string | ArkErrorContextInput | CustomErrorInput; | ||
@@ -60,0 +65,0 @@ export type ProblemWriter<code extends ArkErrorCode = ArkErrorCode> = (context: ProblemContext<code>) => string; |
@@ -20,2 +20,4 @@ import { CastableBase, ReadonlyArray, defineProperties } from "@arktype/util"; | ||
this.path = input.path ?? [...ctx.path]; | ||
if (input.relativePath) | ||
this.path.push(...input.relativePath); | ||
this.data = "data" in input ? input.data : data; | ||
@@ -22,0 +24,0 @@ } |
@@ -38,3 +38,3 @@ import { compileSerializedValue, flatMorph, printable, throwParseError } from "@arktype/util"; | ||
export const constraintKeys = flatMorph(constraintKinds, (i, kind) => [kind, 1]); | ||
export const structureKeys = flatMorph([...structuralKinds, "onExtraneousKey"], (i, k) => [k, 1]); | ||
export const structureKeys = flatMorph([...structuralKinds, "undeclared"], (i, k) => [k, 1]); | ||
export const precedenceByKind = flatMorph(nodeKinds, (i, kind) => [kind, i]); | ||
@@ -41,0 +41,0 @@ export const isNodeKind = (value) => typeof value === "string" && value in precedenceByKind; |
@@ -65,3 +65,3 @@ import { Hkt } from "@arktype/util"; | ||
export const pipeFromMorph = (from, to, ctx) => { | ||
const out = from?.to ? intersectNodes(from.to, to, ctx) : to; | ||
const out = from?.out ? intersectNodes(from.out, to, ctx) : to; | ||
if (out instanceof Disjoint) | ||
@@ -71,8 +71,8 @@ return out; | ||
morphs: from.morphs, | ||
from: from.in, | ||
to: out | ||
in: from.in, | ||
out | ||
}); | ||
}; | ||
export const pipeToMorph = (from, to, ctx) => { | ||
const result = intersectNodes(from, to.from, ctx); | ||
const result = intersectNodes(from, to.in, ctx); | ||
if (result instanceof Disjoint) | ||
@@ -82,5 +82,5 @@ return result; | ||
morphs: to.morphs, | ||
from: result, | ||
to: to.out | ||
in: result, | ||
out: to.out | ||
}); | ||
}; |
@@ -9,3 +9,3 @@ import type { array } from "@arktype/util"; | ||
morphs: array<Morph>; | ||
to: TraverseApply | null; | ||
to?: TraverseApply; | ||
}; | ||
@@ -16,2 +16,5 @@ export type BranchTraversalContext = { | ||
}; | ||
export type QueueMorphOptions = { | ||
outValidator?: TraverseApply; | ||
}; | ||
export declare class TraversalContext { | ||
@@ -29,3 +32,3 @@ root: unknown; | ||
get currentBranch(): BranchTraversalContext | undefined; | ||
queueMorphs(morphs: array<Morph>, outValidator: TraverseApply | null): void; | ||
queueMorphs(morphs: array<Morph>, opts?: QueueMorphOptions): void; | ||
finalize(): unknown; | ||
@@ -32,0 +35,0 @@ get currentErrorCount(): number; |
@@ -17,8 +17,9 @@ import { ArkError, ArkErrors } from "./errors.js"; | ||
} | ||
queueMorphs(morphs, outValidator) { | ||
queueMorphs(morphs, opts) { | ||
const input = { | ||
path: [...this.path], | ||
morphs, | ||
to: outValidator | ||
morphs | ||
}; | ||
if (opts?.outValidator) | ||
input.to = opts?.outValidator; | ||
this.currentBranch?.queuedMorphs.push(input) ?? | ||
@@ -25,0 +26,0 @@ this.queuedMorphs.push(input); |
@@ -10,7 +10,7 @@ import { BaseConstraint } from "../constraint.js"; | ||
export interface IndexSchema extends BaseMeta { | ||
readonly index: RootSchema<IndexKeyKind>; | ||
readonly signature: RootSchema<IndexKeyKind>; | ||
readonly value: RootSchema; | ||
} | ||
export interface IndexInner extends BaseMeta { | ||
readonly index: IndexKeyNode; | ||
readonly signature: IndexKeyNode; | ||
readonly value: BaseRoot; | ||
@@ -17,0 +17,0 @@ } |
@@ -11,3 +11,3 @@ import { printable, stringAndSymbolicEntriesOf, throwParseError } from "@arktype/util"; | ||
keys: { | ||
index: { | ||
signature: { | ||
child: true, | ||
@@ -19,3 +19,2 @@ parse: (schema, ctx) => { | ||
} | ||
// TODO: explicit manual annotation once we can upgrade to 5.5 | ||
const enumerableBranches = key.branches.filter((b) => b.hasKind("unit")); | ||
@@ -35,7 +34,7 @@ if (enumerableBranches.length) { | ||
defaults: { | ||
description: node => `[${node.index.expression}]: ${node.value.description}` | ||
description: node => `[${node.signature.expression}]: ${node.value.description}` | ||
}, | ||
intersections: { | ||
index: (l, r, ctx) => { | ||
if (l.index.equals(r.index)) { | ||
if (l.signature.equals(r.signature)) { | ||
const valueIntersection = intersectNodes(l.value, r.value, ctx); | ||
@@ -45,9 +44,9 @@ const value = valueIntersection instanceof Disjoint ? | ||
: valueIntersection; | ||
return ctx.$.node("index", { index: l.index, value }); | ||
return ctx.$.node("index", { signature: l.signature, value }); | ||
} | ||
// if r constrains all of l's keys to a subtype of l's value, r is a subtype of l | ||
if (l.index.extends(r.index) && l.value.subsumes(r.value)) | ||
if (l.signature.extends(r.signature) && l.value.subsumes(r.value)) | ||
return r; | ||
// if l constrains all of r's keys to a subtype of r's value, l is a subtype of r | ||
if (r.index.extends(l.index) && r.value.subsumes(l.value)) | ||
if (r.signature.extends(l.signature) && r.value.subsumes(l.value)) | ||
return l; | ||
@@ -61,5 +60,5 @@ // other relationships between index signatures can't be generally reduced | ||
impliedBasis = this.$.keywords.object.raw; | ||
expression = `[${this.index.expression}]: ${this.value.expression}`; | ||
expression = `[${this.signature.expression}]: ${this.value.expression}`; | ||
traverseAllows = (data, ctx) => stringAndSymbolicEntriesOf(data).every(entry => { | ||
if (this.index.traverseAllows(entry[0], ctx)) { | ||
if (this.signature.traverseAllows(entry[0], ctx)) { | ||
// ctx will be undefined if this node isn't context-dependent | ||
@@ -74,3 +73,3 @@ ctx?.path.push(entry[0]); | ||
traverseApply = (data, ctx) => stringAndSymbolicEntriesOf(data).forEach(entry => { | ||
if (this.index.traverseAllows(entry[0], ctx)) { | ||
if (this.signature.traverseAllows(entry[0], ctx)) { | ||
ctx.path.push(entry[0]); | ||
@@ -77,0 +76,0 @@ this.value.traverseApply(entry[1], ctx); |
import type { declareNode } from "../shared/declare.js"; | ||
import { type nodeImplementationOf } from "../shared/implement.js"; | ||
import { BaseProp, type BasePropDeclaration } from "./prop.js"; | ||
export type OptionalDeclaration = declareNode<BasePropDeclaration<"optional">>; | ||
import { BaseProp, type BasePropDeclaration, type BasePropInner, type BasePropSchema } from "./prop.js"; | ||
export interface OptionalSchema extends BasePropSchema { | ||
default?: unknown; | ||
} | ||
export interface OptionalInner extends BasePropInner { | ||
default?: unknown; | ||
} | ||
export type Default<v = any> = ["=", v]; | ||
export type DefaultableAst<t = any, v = any> = (In?: t) => Default<v>; | ||
export type OptionalDeclaration = declareNode<BasePropDeclaration<"optional"> & { | ||
schema: OptionalSchema; | ||
normalizedSchema: OptionalSchema; | ||
inner: OptionalInner; | ||
}>; | ||
export declare const optionalImplementation: nodeImplementationOf<OptionalDeclaration>; | ||
@@ -6,0 +18,0 @@ export declare class OptionalNode extends BaseProp<"optional"> { |
@@ -12,2 +12,5 @@ import { implementNode } from "../shared/implement.js"; | ||
parse: (schema, ctx) => ctx.$.schema(schema) | ||
}, | ||
default: { | ||
preserveUndefined: true | ||
} | ||
@@ -14,0 +17,0 @@ }, |
@@ -10,11 +10,11 @@ import { type Key } from "@arktype/util"; | ||
import type { TraverseAllows, TraverseApply } from "../shared/traversal.js"; | ||
import type { OptionalDeclaration } from "./optional.js"; | ||
import type { OptionalDeclaration, OptionalNode } from "./optional.js"; | ||
import type { RequiredDeclaration } from "./required.js"; | ||
export type PropKind = "required" | "optional"; | ||
export type PropNode = Node<PropKind>; | ||
export interface PropSchema extends BaseMeta { | ||
export interface BasePropSchema extends BaseMeta { | ||
readonly key: Key; | ||
readonly value: RootSchema; | ||
} | ||
export interface PropInner extends PropSchema { | ||
export interface BasePropInner extends BasePropSchema { | ||
readonly value: BaseRoot; | ||
@@ -24,5 +24,2 @@ } | ||
kind: kind; | ||
schema: PropSchema; | ||
normalizedSchema: PropSchema; | ||
inner: PropInner; | ||
prerequisite: object; | ||
@@ -38,2 +35,7 @@ intersectionIsOpen: true; | ||
compiledKey: string; | ||
private defaultValueMorphs; | ||
private defaultValueMorphsReference; | ||
hasDefault(): this is OptionalNode & { | ||
default: unknown; | ||
}; | ||
traverseAllows: TraverseAllows<object>; | ||
@@ -40,0 +42,0 @@ traverseApply: TraverseApply<object>; |
@@ -1,2 +0,2 @@ | ||
import { compileSerializedValue } from "@arktype/util"; | ||
import { compileSerializedValue, printable, registeredReference, throwParseError, unset } from "@arktype/util"; | ||
import { BaseConstraint } from "../constraint.js"; | ||
@@ -17,5 +17,21 @@ import { Disjoint } from "../shared/disjoint.js"; | ||
} | ||
return ctx.$.node(kind, { | ||
if (kind === "required") { | ||
return ctx.$.node("required", { | ||
key, | ||
value | ||
}); | ||
} | ||
const defaultIntersection = l.hasDefault() ? | ||
r.hasDefault() ? | ||
l.default === r.default ? | ||
l.default | ||
: throwParseError(`Invalid intersection of default values ${printable(l.default)} & ${printable(r.default)}`) | ||
: l.default | ||
: r.hasDefault() ? r.default | ||
: unset; | ||
return ctx.$.node("optional", { | ||
key, | ||
value | ||
value, | ||
// unset is stripped during parsing | ||
default: defaultIntersection | ||
}); | ||
@@ -28,2 +44,12 @@ }; | ||
compiledKey = typeof this.key === "string" ? this.key : this.serializedKey; | ||
defaultValueMorphs = [ | ||
data => { | ||
data[this.key] = this.default; | ||
return data; | ||
} | ||
]; | ||
defaultValueMorphsReference = registeredReference(this.defaultValueMorphs); | ||
hasDefault() { | ||
return "default" in this; | ||
} | ||
traverseAllows = (data, ctx) => { | ||
@@ -40,16 +66,14 @@ if (this.key in data) { | ||
traverseApply = (data, ctx) => { | ||
ctx.path.push(this.key); | ||
if (this.key in data) | ||
if (this.key in data) { | ||
ctx.path.push(this.key); | ||
this.value.traverseApply(data[this.key], ctx); | ||
ctx.path.pop(); | ||
} | ||
else if (this.hasKind("required")) | ||
ctx.error(this.errorContext); | ||
ctx.path.pop(); | ||
else if (this.hasKind("optional") && this.hasDefault()) | ||
ctx.queueMorphs(this.defaultValueMorphs); | ||
}; | ||
compile(js) { | ||
const requiresContext = js.requiresContextFor(this.value); | ||
if (requiresContext) | ||
js.line(`ctx.path.push(${this.serializedKey})`); | ||
js.if(`${this.serializedKey} in ${js.data}`, () => js.check(this.value, { | ||
arg: `${js.data}${js.prop(this.key)}` | ||
})); | ||
js.if(`${this.serializedKey} in data`, () => js.traverseKey(this.serializedKey, `data${js.prop(this.key)}`, this.value)); | ||
if (this.hasKind("required")) { | ||
@@ -59,11 +83,9 @@ js.else(() => { | ||
return js.line(`ctx.error(${this.compiledErrorContext})`); | ||
else { | ||
if (requiresContext) | ||
js.line(`ctx.path.pop()`); | ||
else | ||
return js.return(false); | ||
} | ||
}); | ||
} | ||
if (requiresContext) | ||
js.line(`ctx.path.pop()`); | ||
else if (js.traversalKind === "Apply" && "default" in this) { | ||
js.else(() => js.line(`ctx.queueMorphs(${this.defaultValueMorphsReference})`)); | ||
} | ||
if (js.traversalKind === "Allows") | ||
@@ -70,0 +92,0 @@ js.return(true); |
import type { BaseErrorContext, declareNode } from "../shared/declare.js"; | ||
import type { ArkErrorContextInput } from "../shared/errors.js"; | ||
import { type nodeImplementationOf } from "../shared/implement.js"; | ||
import { BaseProp, type BasePropDeclaration } from "./prop.js"; | ||
import { BaseProp, type BasePropDeclaration, type BasePropInner, type BasePropSchema } from "./prop.js"; | ||
export interface RequiredErrorContext extends BaseErrorContext<"required"> { | ||
missingValueDescription: string; | ||
} | ||
export interface RequiredSchema extends BasePropSchema { | ||
} | ||
export interface RequiredInner extends BasePropInner { | ||
} | ||
export type RequiredDeclaration = declareNode<BasePropDeclaration<"required"> & { | ||
schema: RequiredSchema; | ||
normalizedSchema: RequiredSchema; | ||
inner: RequiredInner; | ||
errorContext: RequiredErrorContext; | ||
@@ -12,5 +20,5 @@ }>; | ||
expression: string; | ||
errorContext: RequiredErrorContext; | ||
errorContext: ArkErrorContextInput<"required">; | ||
compiledErrorContext: string; | ||
} | ||
export declare const requiredImplementation: nodeImplementationOf<RequiredDeclaration>; |
@@ -7,3 +7,4 @@ import { compileErrorContext, implementNode } from "../shared/implement.js"; | ||
code: "required", | ||
missingValueDescription: this.value.description | ||
missingValueDescription: this.value.description, | ||
relativePath: [this.key] | ||
}); | ||
@@ -10,0 +11,0 @@ compiledErrorContext = compileErrorContext(this.errorContext); |
@@ -165,5 +165,5 @@ import { append, throwInternalError, throwParseError } from "@arktype/util"; | ||
return this.prevariadic[index]; | ||
const postfixStartIndex = data.length - this.postfix.length; | ||
if (index >= postfixStartIndex) | ||
return this.postfix[index - postfixStartIndex]; | ||
const firstPostfixIndex = data.length - this.postfix.length; | ||
if (index >= firstPostfixIndex) | ||
return this.postfix[index - firstPostfixIndex]; | ||
return (this.variadic ?? | ||
@@ -188,12 +188,17 @@ throwInternalError(`Unexpected attempt to access index ${index} on ${this}`)); | ||
compile(js) { | ||
this.prefix.forEach((node, i) => js.checkReferenceKey(`${i}`, node)); | ||
this.prefix.forEach((node, i) => js.traverseKey(`${i}`, `data[${i}]`, node)); | ||
this.optionals.forEach((node, i) => { | ||
const dataIndex = `${i + this.prefix.length}`; | ||
js.if(`${dataIndex} >= ${js.data}.length`, () => js.traversalKind === "Allows" ? js.return(true) : js.return()); | ||
js.checkReferenceKey(dataIndex, node); | ||
js.traverseKey(dataIndex, `data[${dataIndex}]`, node); | ||
}); | ||
if (this.variadic) { | ||
js.const("lastVariadicIndex", `${js.data}.length${this.postfix ? `- ${this.postfix.length}` : ""}`); | ||
js.for("i < lastVariadicIndex", () => js.checkReferenceKey("i", this.variadic), this.prevariadic.length); | ||
this.postfix.forEach((node, i) => js.checkReferenceKey(`lastVariadicIndex + ${i + 1}`, node)); | ||
if (this.postfix.length) { | ||
js.const("firstPostfixIndex", `${js.data}.length${this.postfix.length ? `- ${this.postfix.length}` : ""}`); | ||
} | ||
js.for(`i < ${this.postfix.length ? "firstPostfixIndex" : "data.length"}`, () => js.traverseKey("i", "data[i]", this.variadic), this.prevariadic.length); | ||
this.postfix.forEach((node, i) => { | ||
const keyExpression = `firstPostfixIndex + ${i}`; | ||
js.traverseKey(keyExpression, `data[${keyExpression}]`, node); | ||
}); | ||
} | ||
@@ -200,0 +205,0 @@ if (js.traversalKind === "Allows") |
import { type array, type Key, type RegisteredReference } from "@arktype/util"; | ||
import { BaseConstraint } from "../constraint.js"; | ||
import type { BaseRoot } from "../roots/root.js"; | ||
import type { RawRootScope } from "../scope.js"; | ||
import type { NodeCompiler } from "../shared/compile.js"; | ||
import type { BaseMeta, declareNode } from "../shared/declare.js"; | ||
import { type nodeImplementationOf, type StructuralKind } from "../shared/implement.js"; | ||
import type { TraverseAllows, TraverseApply } from "../shared/traversal.js"; | ||
import type { TraversalContext, TraversalKind, TraverseAllows, TraverseApply } from "../shared/traversal.js"; | ||
import type { IndexNode, IndexSchema } from "./index.js"; | ||
import type { OptionalNode } from "./optional.js"; | ||
import type { PropNode, PropSchema } from "./prop.js"; | ||
import type { RequiredNode } from "./required.js"; | ||
import type { OptionalNode, OptionalSchema } from "./optional.js"; | ||
import type { PropNode } from "./prop.js"; | ||
import type { RequiredNode, RequiredSchema } from "./required.js"; | ||
import type { SequenceNode, SequenceSchema } from "./sequence.js"; | ||
export type ExtraneousKeyBehavior = "ignore" | ExtraneousKeyRestriction; | ||
export type ExtraneousKeyRestriction = "error" | "prune"; | ||
export type UndeclaredKeyBehavior = "ignore" | UndeclaredKeyHandling; | ||
export type UndeclaredKeyHandling = "reject" | "delete"; | ||
export interface StructureSchema extends BaseMeta { | ||
readonly optional?: readonly PropSchema[]; | ||
readonly required?: readonly PropSchema[]; | ||
readonly optional?: readonly OptionalSchema[]; | ||
readonly required?: readonly RequiredSchema[]; | ||
readonly index?: readonly IndexSchema[]; | ||
readonly sequence?: SequenceSchema; | ||
readonly onExtraneousKey?: ExtraneousKeyBehavior; | ||
readonly undeclared?: UndeclaredKeyBehavior; | ||
} | ||
@@ -27,3 +28,3 @@ export interface StructureInner extends BaseMeta { | ||
readonly sequence?: SequenceNode; | ||
readonly onExtraneousKey?: ExtraneousKeyRestriction; | ||
readonly undeclared?: UndeclaredKeyHandling; | ||
} | ||
@@ -49,14 +50,18 @@ export interface StructureDeclaration extends declareNode<{ | ||
literalKeys: Key[]; | ||
private _keyof; | ||
keyof(): BaseRoot; | ||
readonly exhaustive: boolean; | ||
omit(...keys: array<BaseRoot | Key>): StructureNode; | ||
merge(r: StructureNode): StructureNode; | ||
traverseAllows: TraverseAllows<object>; | ||
traverseApply: TraverseApply<object>; | ||
readonly exhaustive: boolean; | ||
protected _traverse: (traversalKind: TraversalKind, data: object, ctx: TraversalContext) => boolean; | ||
compile(js: NodeCompiler): void; | ||
omit(...keys: array<BaseRoot | Key>): StructureNode; | ||
merge(r: StructureNode): StructureNode; | ||
protected compileEnumerable(js: NodeCompiler): void; | ||
protected compileExhaustive(js: NodeCompiler): void; | ||
protected compileExhaustiveEntry(js: NodeCompiler): NodeCompiler; | ||
} | ||
export declare const structureImplementation: nodeImplementationOf<StructureDeclaration>; | ||
export type NormalizedIndex = { | ||
index?: IndexNode; | ||
required?: RequiredNode[]; | ||
}; | ||
/** extract enumerable named props from an index signature */ | ||
export declare const normalizeIndex: (signature: BaseRoot, value: BaseRoot, $: RawRootScope) => NormalizedIndex; |
@@ -1,114 +0,231 @@ | ||
import { append, flatMorph, registeredReference } from "@arktype/util"; | ||
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { | ||
var useValue = arguments.length > 2; | ||
for (var i = 0; i < initializers.length; i++) { | ||
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); | ||
} | ||
return useValue ? value : void 0; | ||
}; | ||
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { | ||
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } | ||
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; | ||
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; | ||
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); | ||
var _, done = false; | ||
for (var i = decorators.length - 1; i >= 0; i--) { | ||
var context = {}; | ||
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; | ||
for (var p in contextIn.access) context.access[p] = contextIn.access[p]; | ||
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; | ||
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); | ||
if (kind === "accessor") { | ||
if (result === void 0) continue; | ||
if (result === null || typeof result !== "object") throw new TypeError("Object expected"); | ||
if (_ = accept(result.get)) descriptor.get = _; | ||
if (_ = accept(result.set)) descriptor.set = _; | ||
if (_ = accept(result.init)) initializers.unshift(_); | ||
} | ||
else if (_ = accept(result)) { | ||
if (kind === "field") initializers.unshift(_); | ||
else descriptor[key] = _; | ||
} | ||
} | ||
if (target) Object.defineProperty(target, contextIn.name, descriptor); | ||
done = true; | ||
}; | ||
import { append, cached, flatMorph, registeredReference, spliterate } from "@arktype/util"; | ||
import { BaseConstraint, constraintKeyParser, flattenConstraints, intersectConstraints } from "../constraint.js"; | ||
import { Disjoint } from "../shared/disjoint.js"; | ||
import { implementNode } from "../shared/implement.js"; | ||
import { intersectNodesRoot } from "../shared/intersections.js"; | ||
import { makeRootAndArrayPropertiesMutable } from "../shared/utils.js"; | ||
import { arrayIndexMatcherReference } from "./shared.js"; | ||
export class StructureNode extends BaseConstraint { | ||
impliedBasis = this.$.keywords.object.raw; | ||
impliedSiblings = this.children.flatMap(n => n.impliedSiblings ?? []); | ||
props = this.required ? | ||
this.optional ? | ||
[...this.required, ...this.optional] | ||
: this.required | ||
: this.optional ?? []; | ||
propsByKey = flatMorph(this.props, (i, node) => [node.key, node]); | ||
propsByKeyReference = registeredReference(this.propsByKey); | ||
expression = structuralExpression(this); | ||
requiredLiteralKeys = this.required?.map(node => node.key) ?? []; | ||
optionalLiteralKeys = this.optional?.map(node => node.key) ?? []; | ||
literalKeys = [ | ||
...this.requiredLiteralKeys, | ||
...this.optionalLiteralKeys | ||
]; | ||
_keyof; | ||
keyof() { | ||
if (!this._keyof) { | ||
import { arrayIndexMatcher, arrayIndexMatcherReference } from "./shared.js"; | ||
let StructureNode = (() => { | ||
let _classSuper = BaseConstraint; | ||
let _instanceExtraInitializers = []; | ||
let _keyof_decorators; | ||
return class StructureNode extends _classSuper { | ||
static { | ||
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; | ||
_keyof_decorators = [cached]; | ||
__esDecorate(this, null, _keyof_decorators, { kind: "method", name: "keyof", static: false, private: false, access: { has: obj => "keyof" in obj, get: obj => obj.keyof }, metadata: _metadata }, null, _instanceExtraInitializers); | ||
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); | ||
} | ||
impliedBasis = (__runInitializers(this, _instanceExtraInitializers), this.$.keywords.object.raw); | ||
impliedSiblings = this.children.flatMap(n => n.impliedSiblings ?? []); | ||
props = this.required ? | ||
this.optional ? | ||
[...this.required, ...this.optional] | ||
: this.required | ||
: this.optional ?? []; | ||
propsByKey = flatMorph(this.props, (i, node) => [node.key, node]); | ||
propsByKeyReference = registeredReference(this.propsByKey); | ||
expression = structuralExpression(this); | ||
requiredLiteralKeys = this.required?.map(node => node.key) ?? []; | ||
optionalLiteralKeys = this.optional?.map(node => node.key) ?? []; | ||
literalKeys = [ | ||
...this.requiredLiteralKeys, | ||
...this.optionalLiteralKeys | ||
]; | ||
keyof() { | ||
let branches = this.$.units(this.literalKeys).branches; | ||
this.index?.forEach(({ index }) => { | ||
this.index?.forEach(({ signature: index }) => { | ||
branches = branches.concat(index.branches); | ||
}); | ||
this._keyof = this.$.node("union", branches); | ||
return this.$.node("union", branches); | ||
} | ||
return this._keyof; | ||
} | ||
// TODO: normalize this to match compiled check order | ||
traverseAllows = (data, ctx) => this.children.every(prop => prop.traverseAllows(data, ctx)); | ||
traverseApply = (data, ctx) => { | ||
const errorCount = ctx.currentErrorCount; | ||
for (let i = 0; i < this.children.length - 1; i++) { | ||
this.children[i].traverseApply(data, ctx); | ||
if (ctx.failFast && ctx.currentErrorCount > errorCount) | ||
return; | ||
exhaustive = this.undeclared !== undefined || this.index !== undefined; | ||
omit(...keys) { | ||
return this.$.node("structure", omitFromInner(this.inner, keys)); | ||
} | ||
this.children.at(-1)?.traverseApply(data, ctx); | ||
}; | ||
exhaustive = this.onExtraneousKey !== undefined || this.index !== undefined; | ||
compile(js) { | ||
if (this.exhaustive) | ||
this.compileExhaustive(js); | ||
else | ||
this.compileEnumerable(js); | ||
} | ||
omit(...keys) { | ||
return this.$.node("structure", omitFromInner(this.inner, keys)); | ||
} | ||
merge(r) { | ||
const inner = makeRootAndArrayPropertiesMutable(omitFromInner(this.inner, [r.keyof()])); | ||
if (r.required) | ||
inner.required = append(inner.required, r.required); | ||
if (r.optional) | ||
inner.optional = append(inner.optional, r.optional); | ||
if (r.index) | ||
inner.index = append(inner.index, r.index); | ||
if (r.sequence) | ||
inner.sequence = r.sequence; | ||
if (r.onExtraneousKey) | ||
inner.onExtraneousKey = r.onExtraneousKey; | ||
else | ||
delete inner.onExtraneousKey; | ||
return this.$.node("structure", inner); | ||
} | ||
compileEnumerable(js) { | ||
if (js.traversalKind === "Allows") { | ||
this.children.forEach(node => js.if(`!${js.invoke(node)}`, () => js.return(false))); | ||
js.return(true); | ||
merge(r) { | ||
const inner = makeRootAndArrayPropertiesMutable(omitFromInner(this.inner, [r.keyof()])); | ||
if (r.required) | ||
inner.required = append(inner.required, r.required); | ||
if (r.optional) | ||
inner.optional = append(inner.optional, r.optional); | ||
if (r.index) | ||
inner.index = append(inner.index, r.index); | ||
if (r.sequence) | ||
inner.sequence = r.sequence; | ||
if (r.undeclared) | ||
inner.undeclared = r.undeclared; | ||
else | ||
delete inner.undeclared; | ||
return this.$.node("structure", inner); | ||
} | ||
else { | ||
js.initializeErrorCount(); | ||
this.children.forEach(node => js.line(js.invoke(node)).returnIfFailFast()); | ||
traverseAllows = (data, ctx) => this._traverse("Allows", data, ctx); | ||
traverseApply = (data, ctx) => this._traverse("Apply", data, ctx); | ||
_traverse = (traversalKind, data, ctx) => { | ||
const errorCount = ctx?.currentErrorCount ?? 0; | ||
for (let i = 0; i < this.props.length; i++) { | ||
if (traversalKind === "Allows") { | ||
if (!this.props[i].traverseAllows(data, ctx)) | ||
return false; | ||
} | ||
else { | ||
this.props[i].traverseApply(data, ctx); | ||
if (ctx.failFast && ctx.currentErrorCount > errorCount) | ||
return false; | ||
} | ||
} | ||
if (this.sequence) { | ||
if (traversalKind === "Allows") { | ||
if (!this.sequence.traverseAllows(data, ctx)) | ||
return false; | ||
} | ||
else { | ||
this.sequence.traverseApply(data, ctx); | ||
if (ctx.failFast && ctx.currentErrorCount > errorCount) | ||
return false; | ||
} | ||
} | ||
if (!this.exhaustive) | ||
return true; | ||
const keys = Object.keys(data); | ||
keys.push(...Object.getOwnPropertySymbols(data)); | ||
for (let i = 0; i < keys.length; i++) { | ||
const k = keys[i]; | ||
let matched = false; | ||
if (this.index) { | ||
for (const node of this.index) { | ||
if (node.signature.traverseAllows(k, ctx)) { | ||
if (traversalKind === "Allows") { | ||
ctx?.path.push(k); | ||
const result = node.value.traverseAllows(data[k], ctx); | ||
ctx?.path.pop(); | ||
if (!result) | ||
return false; | ||
} | ||
else { | ||
ctx.path.push(k); | ||
node.value.traverseApply(data[k], ctx); | ||
ctx.path.pop(); | ||
if (ctx.failFast && ctx.currentErrorCount > errorCount) | ||
return false; | ||
} | ||
matched = true; | ||
} | ||
} | ||
} | ||
if (this.undeclared) { | ||
matched ||= k in this.propsByKey; | ||
matched ||= | ||
this.sequence !== undefined && | ||
typeof k === "string" && | ||
arrayIndexMatcher.test(k); | ||
if (!matched) { | ||
if (traversalKind === "Allows") | ||
return false; | ||
if (this.undeclared === "reject") | ||
ctx.error({ expected: "removed", actual: null, relativePath: [k] }); | ||
else { | ||
ctx.queueMorphs([ | ||
data => { | ||
delete data[k]; | ||
return data; | ||
} | ||
]); | ||
} | ||
if (ctx.failFast) | ||
return false; | ||
} | ||
} | ||
ctx?.path.pop(); | ||
} | ||
return true; | ||
}; | ||
compile(js) { | ||
if (js.traversalKind === "Apply") | ||
js.initializeErrorCount(); | ||
this.props.forEach(prop => { | ||
js.check(prop); | ||
if (js.traversalKind === "Apply") | ||
js.returnIfFailFast(); | ||
}); | ||
if (this.sequence) { | ||
js.check(this.sequence); | ||
if (js.traversalKind === "Apply") | ||
js.returnIfFailFast(); | ||
} | ||
if (this.exhaustive) { | ||
js.const("keys", "Object.keys(data)"); | ||
js.line("keys.push(...Object.getOwnPropertySymbols(data))"); | ||
js.for("i < keys.length", () => this.compileExhaustiveEntry(js)); | ||
} | ||
if (js.traversalKind === "Allows") | ||
js.return(true); | ||
} | ||
} | ||
compileExhaustive(js) { | ||
this.props.forEach(prop => js.check(prop)); | ||
if (this.sequence) | ||
js.check(this.sequence); | ||
js.const("keys", "Object.keys(data)"); | ||
js.const("symbols", "Object.getOwnPropertySymbols(data)"); | ||
js.if("symbols.length", () => js.line("keys.push(...symbols)")); | ||
js.for("i < keys.length", () => this.compileExhaustiveEntry(js)); | ||
} | ||
compileExhaustiveEntry(js) { | ||
js.const("k", "keys[i]"); | ||
if (this.onExtraneousKey) | ||
js.let("matched", false); | ||
this.index?.forEach(node => { | ||
js.if(`${js.invoke(node.index, { arg: "k", kind: "Allows" })}`, () => { | ||
js.checkReferenceKey("k", node.value); | ||
if (this.onExtraneousKey) | ||
js.set("matched", true); | ||
return js; | ||
compileExhaustiveEntry(js) { | ||
js.const("k", "keys[i]"); | ||
if (this.undeclared) | ||
js.let("matched", false); | ||
this.index?.forEach(node => { | ||
js.if(`${js.invoke(node.signature, { arg: "k", kind: "Allows" })}`, () => { | ||
js.traverseKey("k", "data[k]", node.value); | ||
if (this.undeclared) | ||
js.set("matched", true); | ||
return js; | ||
}); | ||
}); | ||
}); | ||
if (this.onExtraneousKey) { | ||
if (this.props?.length !== 0) | ||
js.line(`matched ||= k in ${this.propsByKeyReference}`); | ||
if (this.sequence) | ||
js.line(`matched ||= ${arrayIndexMatcherReference}.test(k)`); | ||
// TODO: replace error | ||
js.if("!matched", () => js.line(`throw new Error("strict")`)); | ||
if (this.undeclared) { | ||
if (this.props?.length !== 0) | ||
js.line(`matched ||= k in ${this.propsByKeyReference}`); | ||
if (this.sequence) { | ||
js.line(`matched ||= typeof k === "string" && ${arrayIndexMatcherReference}.test(k)`); | ||
} | ||
js.if("!matched", () => { | ||
if (js.traversalKind === "Allows") | ||
return js.return(false); | ||
return this.undeclared === "reject" ? | ||
js | ||
.line(`ctx.error({ expected: "removed", actual: null, relativePath: [k] })`) | ||
.if("ctx.failFast", () => js.return()) | ||
: js.line(`ctx.queueMorphs([data => { delete data[k]; return data }])`); | ||
}); | ||
} | ||
return js; | ||
} | ||
return js; | ||
} | ||
} | ||
}; | ||
})(); | ||
export { StructureNode }; | ||
const omitFromInner = (inner, keys) => { | ||
@@ -126,3 +243,3 @@ const result = { ...inner }; | ||
// literal keys should never subsume an index | ||
result.index = result.index.filter(n => !n.index.extends(k)); | ||
result.index = result.index.filter(n => !n.signature.extends(k)); | ||
} | ||
@@ -136,3 +253,5 @@ }); | ||
node.props.forEach(node => parts.push(node[childStringProp])); | ||
const objectLiteralDescription = `${node.onExtraneousKey ? "exact " : ""}{ ${parts.join(", ")} }`; | ||
if (node.undeclared) | ||
parts.push(`+ (undeclared): ${node.undeclared}`); | ||
const objectLiteralDescription = `{ ${parts.join(", ")} }`; | ||
return node.sequence ? | ||
@@ -167,3 +286,3 @@ `${objectLiteralDescription} & ${node.sequence.description}` | ||
}, | ||
onExtraneousKey: { | ||
undeclared: { | ||
parse: behavior => (behavior === "ignore" ? undefined : behavior) | ||
@@ -177,22 +296,62 @@ } | ||
structure: (l, r, ctx) => { | ||
if (l.onExtraneousKey) { | ||
const lInner = { ...l.inner }; | ||
const rInner = { ...r.inner }; | ||
if (l.undeclared) { | ||
const lKey = l.keyof(); | ||
const disjointRKeys = r.requiredLiteralKeys.filter(k => !lKey.allows(k)); | ||
if (disjointRKeys.length) { | ||
return Disjoint.from("presence", true, false).withPrefixKey(disjointRKeys[0]); | ||
return Disjoint.from("presence", ctx.$.keywords.never.raw, r.propsByKey[disjointRKeys[0]].value).withPrefixKey(disjointRKeys[0]); | ||
} | ||
if (rInner.optional) | ||
rInner.optional = rInner.optional.filter(n => lKey.allows(n.key)); | ||
if (rInner.index) { | ||
rInner.index = rInner.index.flatMap(n => { | ||
if (n.signature.extends(lKey)) | ||
return n; | ||
const indexOverlap = intersectNodesRoot(lKey, n.signature, ctx.$); | ||
if (indexOverlap instanceof Disjoint) | ||
return []; | ||
const normalized = normalizeIndex(indexOverlap, n.value, ctx.$); | ||
if (normalized.required) { | ||
rInner.required = | ||
rInner.required ? | ||
[...rInner.required, ...normalized.required] | ||
: normalized.required; | ||
} | ||
return normalized.index ?? []; | ||
}); | ||
} | ||
} | ||
if (r.onExtraneousKey) { | ||
if (r.undeclared) { | ||
const rKey = r.keyof(); | ||
const disjointLKeys = l.requiredLiteralKeys.filter(k => !rKey.allows(k)); | ||
if (disjointLKeys.length) { | ||
return Disjoint.from("presence", true, false).withPrefixKey(disjointLKeys[0]); | ||
return Disjoint.from("presence", l.propsByKey[disjointLKeys[0]].value, ctx.$.keywords.never.raw).withPrefixKey(disjointLKeys[0]); | ||
} | ||
if (lInner.optional) | ||
lInner.optional = lInner.optional.filter(n => rKey.allows(n.key)); | ||
if (lInner.index) { | ||
lInner.index = lInner.index.flatMap(n => { | ||
if (n.signature.extends(rKey)) | ||
return n; | ||
const indexOverlap = intersectNodesRoot(rKey, n.signature, ctx.$); | ||
if (indexOverlap instanceof Disjoint) | ||
return []; | ||
const normalized = normalizeIndex(indexOverlap, n.value, ctx.$); | ||
if (normalized.required) { | ||
lInner.required = | ||
lInner.required ? | ||
[...lInner.required, ...normalized.required] | ||
: normalized.required; | ||
} | ||
return normalized.index ?? []; | ||
}); | ||
} | ||
} | ||
const baseInner = {}; | ||
if (l.onExtraneousKey || r.onExtraneousKey) { | ||
baseInner.onExtraneousKey = | ||
l.onExtraneousKey === "error" || r.onExtraneousKey === "error" ? | ||
"error" | ||
: "prune"; | ||
if (l.undeclared || r.undeclared) { | ||
baseInner.undeclared = | ||
l.undeclared === "reject" || r.undeclared === "reject" ? | ||
"reject" | ||
: "delete"; | ||
} | ||
@@ -202,4 +361,4 @@ return intersectConstraints({ | ||
baseInner, | ||
l: flattenConstraints(l.inner), | ||
r: flattenConstraints(r.inner), | ||
l: flattenConstraints(lInner), | ||
r: flattenConstraints(rInner), | ||
roots: [], | ||
@@ -211,1 +370,16 @@ ctx | ||
}); | ||
/** extract enumerable named props from an index signature */ | ||
export const normalizeIndex = (signature, value, $) => { | ||
const [enumerableBranches, nonEnumerableBranches] = spliterate(signature.branches, (k) => k.hasKind("unit")); | ||
if (!enumerableBranches.length) | ||
return { index: $.node("index", { signature, value }) }; | ||
const normalized = {}; | ||
normalized.required = enumerableBranches.map(n => $.node("required", { key: n.unit, value })); | ||
if (nonEnumerableBranches.length) { | ||
normalized.index = $.node("index", { | ||
signature: nonEnumerableBranches, | ||
value | ||
}); | ||
} | ||
return normalized; | ||
}; |
{ | ||
"name": "@arktype/schema", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"license": "MIT", | ||
@@ -18,13 +18,5 @@ "author": { | ||
"exports": { | ||
".": { | ||
"types": "./out/api.d.ts", | ||
"default": "./out/api.js" | ||
}, | ||
"./config": { | ||
"types": "./out/config.d.ts", | ||
"default": "./out/config.js" | ||
}, | ||
"./internal/*": { | ||
"default": "./out/*" | ||
} | ||
".": "./out/api.js", | ||
"./config": "./out/config.js", | ||
"./internal/*": "./out/*" | ||
}, | ||
@@ -41,4 +33,4 @@ "files": [ | ||
"dependencies": { | ||
"@arktype/util": "0.0.41" | ||
"@arktype/util": "0.0.42" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
465803
183
10803
+ Added@arktype/util@0.0.42(transitive)
- Removed@arktype/util@0.0.41(transitive)
Updated@arktype/util@0.0.42