@arktype/schema
Advanced tools
Comparing version 0.3.3 to 0.4.0
@@ -5,2 +5,3 @@ import type { ArkRegistry, mutable, requireKeys, show } from "@ark/util"; | ||
import { type DescriptionWriter, type NodeKind } from "./shared/implement.ts"; | ||
import type { UndeclaredKeyBehavior } from "./structure/structure.ts"; | ||
export interface ArkSchemaRegistry extends ArkRegistry { | ||
@@ -33,7 +34,10 @@ intrinsic: typeof intrinsic; | ||
export declare const mergeConfigs: (base: ArkConfig, extensions: ArkConfig) => mutable<ArkConfig>; | ||
export type CloneImplementation = <original extends object>(original: original) => original; | ||
export interface ArkConfig extends Partial<Readonly<NodeConfigsByKind>> { | ||
jitless?: boolean; | ||
clone?: boolean | CloneImplementation; | ||
onUndeclaredKey?: UndeclaredKeyBehavior; | ||
} | ||
export type resolveConfig<config extends ArkConfig> = show<{ | ||
[k in keyof ArkConfig]-?: k extends NodeKind ? Required<config[k]> : config[k]; | ||
[k in keyof ArkConfig]-?: k extends NodeKind ? Required<config[k]> : k extends "clone" ? CloneImplementation | false : config[k]; | ||
} & Omit<config, keyof ArkConfig>>; | ||
@@ -40,0 +44,0 @@ export type ResolvedArkConfig = resolveConfig<ArkConfig>; |
import { Callable, type array, type Hkt, type Json } from "@ark/util"; | ||
import type { RootSchema } from "./kinds.ts"; | ||
import type { BaseNode } from "./node.ts"; | ||
@@ -62,7 +63,10 @@ import type { BaseRoot } from "./roots/root.ts"; | ||
export type genericHktToConstraints<hkt extends abstract new () => Hkt> = InstanceType<hkt>["constraints"]; | ||
export type GenericHktSchemaParser = <const paramsDef extends readonly GenericParamDef[]>(...params: paramsDef) => GenericHktSchemaBodyParser<genericParamSchemasToAst<paramsDef>>; | ||
export type GenericHktSchemaBodyParser<params extends array<GenericParamAst>> = <hkt extends Hkt.constructor>(instantiateDef: LazyGenericBody<GenericArgResolutions<params>>, hkt: hkt) => GenericRoot<{ | ||
[i in keyof params]: [params[i][0], genericHktToConstraints<hkt>[i]]; | ||
}, InstanceType<hkt>>; | ||
export type GenericRootParser = <const paramsDef extends readonly GenericParamDef[]>(...params: paramsDef) => GenericRootBodyParser<genericParamSchemasToAst<paramsDef>>; | ||
export type GenericRootBodyParser<params extends array<GenericParamAst>> = { | ||
<const body>(body: RootSchema): GenericRoot<params, body>; | ||
<hkt extends Hkt.constructor>(instantiateDef: LazyGenericBody<GenericArgResolutions<params>>, hkt: hkt): GenericRoot<{ | ||
[i in keyof params]: [params[i][0], genericHktToConstraints<hkt>[i]]; | ||
}, InstanceType<hkt>>; | ||
}; | ||
export declare const writeUnsatisfiedParameterConstraintMessage: <name extends string, constraint extends string, arg extends string>(name: name, constraint: constraint, arg: arg) => writeUnsatisfiedParameterConstraintMessage<name, constraint, arg>; | ||
export type writeUnsatisfiedParameterConstraintMessage<name extends string, constraint extends string, arg extends string> = `${name} must be assignable to ${constraint} (was ${arg})`; |
@@ -1,2 +0,2 @@ | ||
import { envHasCsp, flatMorph } from "@ark/util"; | ||
import { deepClone, envHasCsp, flatMorph } from "@ark/util"; | ||
import { Predicate } from "./predicate.js"; | ||
@@ -41,3 +41,5 @@ import { Divisor } from "./refinements/divisor.js"; | ||
]), { | ||
jitless: envHasCsp() | ||
jitless: envHasCsp(), | ||
clone: deepClone, | ||
onUndeclaredKey: "ignore" | ||
}); | ||
@@ -44,0 +46,0 @@ export const nodeClassesByKind = { |
@@ -1,2 +0,2 @@ | ||
import { DynamicBase, type anyOrNever, type inferred } from "@ark/util"; | ||
import { DynamicBase, type anyOrNever } from "@ark/util"; | ||
import type { BaseRoot } from "./roots/root.ts"; | ||
@@ -8,6 +8,3 @@ import type { BaseScope, InternalResolution, InternalResolutions } from "./scope.ts"; | ||
}; | ||
type inferrableIfRooted<exports> = exports & ("$root" extends keyof exports ? { | ||
[inferred]: exports["$root"]; | ||
} : {}); | ||
export declare class RootModule<exports extends {} = {}> extends DynamicBase<inferrableIfRooted<exports>> { | ||
export declare class RootModule<exports extends {} = {}> extends DynamicBase<exports> { | ||
get [arkKind](): "module"; | ||
@@ -20,6 +17,7 @@ } | ||
type exportSchemaScope<$> = { | ||
[k in keyof $]: $[k] extends InternalResolution ? [ | ||
$[k] | ||
] extends [anyOrNever] ? BaseRoot : $[k] : BaseRoot; | ||
[k in keyof $]: instantiateRoot<$[k]>; | ||
}; | ||
export type instantiateRoot<t> = t extends InternalResolution ? [ | ||
t | ||
] extends [anyOrNever] ? BaseRoot : t : BaseRoot; | ||
export declare const SchemaModule: new <$ = {}>(types: exportSchemaScope<$>) => SchemaModule<$>; | ||
@@ -26,0 +24,0 @@ export interface SchemaModule<$ = {}> extends RootModule<exportSchemaScope<$>> { |
@@ -23,2 +23,3 @@ import { Callable, type Guardable, type Json, type Key, type array, type conform, type listable, type mutable } from "@ark/util"; | ||
bindScope($: BaseScope): this; | ||
withMeta(meta: ArkEnv.meta | ((currentMeta: ArkEnv.meta) => ArkEnv.meta)): this; | ||
abstract traverseAllows: TraverseAllows<d["prerequisite"]>; | ||
@@ -25,0 +26,0 @@ abstract traverseApply: TraverseApply<d["prerequisite"]>; |
@@ -34,2 +34,11 @@ import { Callable, appendUnique, flatMorph, includes, isArray, isEmptyObject, throwError } from "@ark/util"; | ||
} | ||
withMeta(meta) { | ||
const newMeta = typeof meta === "function" ? | ||
meta({ ...this.meta }) | ||
: { ...this.meta, ...meta }; | ||
return this.$.node(this.kind, { | ||
...this.inner, | ||
meta: newMeta | ||
}); | ||
} | ||
includesMorph = this.kind === "morph" || | ||
@@ -127,3 +136,3 @@ (this.hasKind("optional") && this.hasDefault()) || | ||
assertHasKind(kind) { | ||
if (!this.kind === kind) | ||
if (this.kind !== kind) | ||
throwError(`${this.kind} node was not of asserted kind ${kind}`); | ||
@@ -130,0 +139,0 @@ return this; |
@@ -9,3 +9,4 @@ import { type Domain as _Domain, type array, type Key } from "@ark/util"; | ||
export declare namespace Domain { | ||
type NonEnumerable = Exclude<Domain, "undefined" | "null" | "boolean">; | ||
type Enumerable = "undefined" | "null" | "boolean"; | ||
type NonEnumerable = Exclude<Domain, Enumerable>; | ||
interface Inner<domain extends NonEnumerable = NonEnumerable> { | ||
@@ -12,0 +13,0 @@ readonly domain: domain; |
@@ -16,3 +16,3 @@ import { domainDescriptions, domainOf, getBaseDomainKeys, throwParseError } from "@ark/util"; | ||
description: node => domainDescriptions[node.domain], | ||
actual: data => (typeof data === "boolean" ? `${data}` : domainOf(data)) | ||
actual: data => domainDescriptions[domainOf(data)] | ||
}, | ||
@@ -19,0 +19,0 @@ intersections: { |
import { type array, type listable } from "@ark/util"; | ||
import type { nodeOfKind, NodeSchema } from "../kinds.ts"; | ||
import type { NodeSchema, nodeOfKind } from "../kinds.ts"; | ||
import type { NodeCompiler } from "../shared/compile.ts"; | ||
@@ -14,8 +14,12 @@ import type { BaseNormalizedSchema, declareNode } from "../shared/declare.ts"; | ||
interface Inner { | ||
readonly in: ChildNode; | ||
readonly in?: ChildNode; | ||
readonly morphs: array<Morph | BaseRoot>; | ||
readonly declaredIn?: ChildNode; | ||
readonly declaredOut?: BaseRoot; | ||
} | ||
interface Schema extends BaseNormalizedSchema { | ||
readonly in: ChildSchema; | ||
readonly in?: ChildSchema; | ||
readonly morphs: listable<Morph | BaseRoot>; | ||
readonly declaredIn?: ChildNode; | ||
readonly declaredOut?: BaseRoot; | ||
} | ||
@@ -36,15 +40,17 @@ interface Declaration extends declareNode<{ | ||
compiledMorphs: string; | ||
structure: import("../index.ts").StructureNode | undefined; | ||
traverseAllows: TraverseAllows; | ||
traverseApply: TraverseApply; | ||
lastMorph: BaseRoot<import("./root.ts").InternalRootDeclaration> | Morph<any, unknown> | undefined; | ||
validatedIn: BaseRoot | undefined; | ||
validatedOut: BaseRoot | undefined; | ||
get in(): Morph.ChildNode; | ||
get out(): BaseRoot; | ||
declareIn(declaredIn: Morph.ChildNode): MorphNode; | ||
declareOut(declaredOut: BaseRoot): MorphNode; | ||
expression: string; | ||
get shortDescription(): string; | ||
protected innerToJsonSchema(): JsonSchema; | ||
compile(js: NodeCompiler): void; | ||
get in(): Morph.ChildNode; | ||
get out(): BaseRoot; | ||
traverseAllows: TraverseAllows; | ||
traverseApply: TraverseApply; | ||
/** Check if the morphs of r are equal to those of this node */ | ||
hasEqualMorphs(r: MorphNode): boolean; | ||
lastMorph: BaseRoot<import("./root.ts").InternalRootDeclaration> | Morph<any, unknown> | undefined; | ||
validatedOut: BaseRoot | undefined; | ||
expression: string; | ||
} | ||
@@ -51,0 +57,0 @@ export declare const Morph: { |
@@ -28,2 +28,10 @@ import { arrayEquals, liftArray, throwParseError } from "@ark/util"; | ||
serialize: morphs => morphs.map(m => hasArkKind(m, "root") ? m.json : registeredReference(m)) | ||
}, | ||
declaredIn: { | ||
child: false, | ||
serialize: node => node.json | ||
}, | ||
declaredOut: { | ||
child: false, | ||
serialize: node => node.json | ||
} | ||
@@ -43,6 +51,25 @@ }, | ||
return inTersection; | ||
const baseInner = { | ||
morphs: l.morphs | ||
}; | ||
if (l.declaredIn || r.declaredIn) { | ||
const declaredIn = intersectNodes(l.in, r.in, ctx); | ||
// we can't treat this as a normal Disjoint since it's just declared | ||
// it should only happen if someone's essentially trying to create a broken type | ||
if (declaredIn instanceof Disjoint) | ||
return declaredIn.throw(); | ||
else | ||
baseInner.declaredIn = declaredIn; | ||
} | ||
if (l.declaredOut || r.declaredOut) { | ||
const declaredOut = intersectNodes(l.out, r.out, ctx); | ||
if (declaredOut instanceof Disjoint) | ||
return declaredOut.throw(); | ||
else | ||
baseInner.declaredOut = declaredOut; | ||
} | ||
// in case from is a union, we need to distribute the branches | ||
// to can be a union as any schema is allowed | ||
return inTersection.distribute(inBranch => ctx.$.node("morph", { | ||
morphs: l.morphs, | ||
...baseInner, | ||
in: inBranch | ||
@@ -63,8 +90,29 @@ }), ctx.$.rootNode); | ||
compiledMorphs = `[${this.serializedMorphs}]`; | ||
structure = this.in.structure; | ||
traverseAllows = (data, ctx) => this.in.traverseAllows(data, ctx); | ||
traverseApply = (data, ctx) => { | ||
this.in.traverseApply(data, ctx); | ||
ctx.queueMorphs(this.morphs); | ||
}; | ||
lastMorph = this.inner.morphs.at(-1); | ||
validatedIn = this.inner.in; | ||
validatedOut = hasArkKind(this.lastMorph, "root") ? | ||
Object.assign(this.referencesById, this.lastMorph.out.referencesById) && | ||
this.lastMorph.out | ||
: undefined; | ||
get in() { | ||
return (this.declaredIn ?? | ||
this.inner.in ?? | ||
$ark.intrinsic.unknown.internal); | ||
} | ||
get out() { | ||
return (this.declaredOut ?? this.validatedOut ?? $ark.intrinsic.unknown.internal); | ||
} | ||
declareIn(declaredIn) { | ||
return this.$.node("morph", { | ||
...this.inner, | ||
declaredIn | ||
}); | ||
} | ||
declareOut(declaredOut) { | ||
return this.$.node("morph", { | ||
...this.inner, | ||
declaredOut | ||
}); | ||
} | ||
expression = `(In: ${this.in.expression}) => Out<${this.out.expression}>`; | ||
get shortDescription() { | ||
@@ -78,14 +126,17 @@ return this.in.shortDescription; | ||
if (js.traversalKind === "Allows") { | ||
js.return(js.invoke(this.in)); | ||
if (!this.validatedIn) | ||
return; | ||
js.return(js.invoke(this.validatedIn)); | ||
return; | ||
} | ||
js.line(js.invoke(this.in)); | ||
if (this.validatedIn) | ||
js.line(js.invoke(this.validatedIn)); | ||
js.line(`ctx.queueMorphs(${this.compiledMorphs})`); | ||
} | ||
get in() { | ||
return this.inner.in; | ||
} | ||
get out() { | ||
return this.validatedOut ?? $ark.intrinsic.unknown.internal; | ||
} | ||
traverseAllows = (data, ctx) => !this.validatedIn || this.validatedIn.traverseAllows(data, ctx); | ||
traverseApply = (data, ctx) => { | ||
if (this.validatedIn) | ||
this.validatedIn.traverseApply(data, ctx); | ||
ctx.queueMorphs(this.morphs); | ||
}; | ||
/** Check if the morphs of r are equal to those of this node */ | ||
@@ -100,8 +151,2 @@ hasEqualMorphs(r) { | ||
} | ||
lastMorph = this.inner.morphs.at(-1); | ||
validatedOut = hasArkKind(this.lastMorph, "root") ? | ||
Object.assign(this.referencesById, this.lastMorph.out.referencesById) && | ||
this.lastMorph.out | ||
: undefined; | ||
expression = `(In: ${this.in.expression}) => Out<${this.out.expression}>`; | ||
} | ||
@@ -108,0 +153,0 @@ export const Morph = { |
@@ -94,2 +94,4 @@ import { includes, inferred, omit, throwInternalError, throwParseError } from "@ark/util"; | ||
return structure.keyof(); | ||
if (operation === "get") | ||
return structure.get(...args); | ||
const structuralMethodName = operation === "required" ? "require" | ||
@@ -107,8 +109,3 @@ : operation === "partial" ? "optionalize" | ||
return this; | ||
if (this.hasKind("union")) { | ||
return this.branches.reduce((acc, b) => acc.or(b.get(...path)), $ark.intrinsic.never.internal); | ||
} | ||
const branch = this; | ||
return (branch.structure?.get(...path) ?? | ||
throwParseError(writeNonStructuralOperandMessage("get", this.expression))); | ||
return this.$.rootNode(this.applyStructuralOperation("get", path)); | ||
} | ||
@@ -144,3 +141,3 @@ extract(r) { | ||
describe(description) { | ||
return this.configure(description); | ||
return this.configure({ description }); | ||
} | ||
@@ -147,0 +144,0 @@ from(input) { |
@@ -1,2 +0,2 @@ | ||
import { type Json, type Key, type SerializedPrimitive, type show } from "@ark/util"; | ||
import { type JsTypeOf, type Json, type Key, type SerializedPrimitive, type show } from "@ark/util"; | ||
import type { NodeSchema, nodeOfKind } from "../kinds.ts"; | ||
@@ -9,4 +9,4 @@ import { type NodeCompiler } from "../shared/compile.ts"; | ||
import type { JsonSchema } from "../shared/jsonSchema.ts"; | ||
import { type RegisteredReference } from "../shared/registry.ts"; | ||
import type { TraverseAllows, TraverseApply } from "../shared/traversal.ts"; | ||
import type { Domain } from "./domain.ts"; | ||
import { BaseRoot } from "./root.ts"; | ||
@@ -77,4 +77,4 @@ export declare namespace Union { | ||
export type DiscriminantKinds = { | ||
domain: Domain; | ||
unit: SerializedPrimitive; | ||
typeOf: JsTypeOf; | ||
identity: SerializedPrimitive | RegisteredReference; | ||
}; | ||
@@ -81,0 +81,0 @@ export type DiscriminantKind = show<keyof DiscriminantKinds>; |
@@ -1,2 +0,2 @@ | ||
import { appendUnique, arrayEquals, domainDescriptions, flatMorph, groupBy, isArray, isKeyOf, printable, throwInternalError, throwParseError } from "@ark/util"; | ||
import { appendUnique, arrayEquals, domainDescriptions, domainToJsTypesOf, flatMorph, groupBy, isArray, jsTypeOfDescriptions, printable, throwInternalError, throwParseError } from "@ark/util"; | ||
import { typePathToPropString } from "../node.js"; | ||
@@ -158,3 +158,6 @@ import { compileLiteralPropAccess, compileSerializedValue } from "../shared/compile.js"; | ||
// we need to access the path as optional so we don't throw if it isn't present | ||
const condition = this.discriminant.path.reduce((acc, k) => acc + compileLiteralPropAccess(k, true), this.discriminant.kind === "domain" ? "typeof data" : "data"); | ||
const condition = this.discriminant.path.reduce((acc, k) => acc + compileLiteralPropAccess(k, true), | ||
// eventually, we should calculate the discriminant based on native typeof | ||
// to avoid this overhead | ||
this.discriminant.kind === "typeOf" ? "typeof data" : "data"); | ||
const cases = this.discriminant.cases; | ||
@@ -174,9 +177,18 @@ const caseKeys = Object.keys(cases); | ||
} | ||
const expected = describeBranches(this.discriminant.kind === "domain" ? | ||
caseKeys.map(k => domainDescriptions[k.slice(1, -1)]) | ||
const expected = describeBranches(this.discriminant.kind === "typeOf" ? | ||
caseKeys.map(k => { | ||
const jsTypeOf = k.slice(1, -1); | ||
return jsTypeOf === "function" ? | ||
domainDescriptions.object | ||
: domainDescriptions[jsTypeOf]; | ||
}) | ||
: caseKeys); | ||
const serializedPathSegments = this.discriminant.path.map(k => typeof k === "string" ? JSON.stringify(k) : registeredReference(k)); | ||
const serializedExpected = JSON.stringify(expected); | ||
const serializedActual = this.discriminant.kind === "typeOf" ? | ||
`${serializedTypeOfDescriptions}[${condition}]` | ||
: `${serializedPrintable}(${condition})`; | ||
js.line(`ctx.error({ | ||
expected: ${JSON.stringify(expected)}, | ||
actual: ${condition}, | ||
expected: ${serializedExpected}, | ||
actual: ${serializedActual}, | ||
relativePath: [${serializedPathSegments}] | ||
@@ -214,3 +226,3 @@ })`); | ||
return { | ||
kind: "unit", | ||
kind: "identity", | ||
path: [], | ||
@@ -230,25 +242,35 @@ propString: "", | ||
for (const entry of result) { | ||
if (!isKeyOf(entry.kind, discriminantKinds) || entry.optional) | ||
if (!entry.discriminantKind || entry.optional) | ||
continue; | ||
let lSerialized; | ||
let rSerialized; | ||
if (entry.kind === "domain") { | ||
lSerialized = `"${entry.l.domain}"`; | ||
rSerialized = `"${entry.r.domain}"`; | ||
switch (entry.discriminantKind) { | ||
case "typeOf": { | ||
const lValue = entry.l; | ||
const rValue = entry.r; | ||
const lDomain = typeof lValue === "string" ? lValue : lValue.domain; | ||
const rDomain = typeof rValue === "string" ? rValue : rValue.domain; | ||
if ((lDomain === "null" && rDomain === "object") || | ||
(rDomain === "null" && lDomain === "object")) | ||
// since we use typeof as a discriminant, we can't use this domain disjoint | ||
continue; | ||
lSerialized = domainToJsTypesOf(lDomain).map(typeOf => `"${typeOf}"`); | ||
rSerialized = domainToJsTypesOf(rDomain).map(typeOf => `"${typeOf}"`); | ||
break; | ||
} | ||
case "identity": { | ||
lSerialized = [entry.l.serializedValue]; | ||
rSerialized = [entry.r.serializedValue]; | ||
break; | ||
} | ||
default: | ||
entry.discriminantKind; | ||
throwInternalError(`Unexpected discriminant kind ${entry.discriminantKind}`); | ||
} | ||
else if (entry.kind === "unit") { | ||
lSerialized = entry.l.serializedValue; | ||
rSerialized = entry.r.serializedValue; | ||
} | ||
else { | ||
return throwInternalError(`Unexpected attempt to discriminate disjoint kind '${entry.kind}'`); | ||
} | ||
const matching = candidates.find(d => arrayEquals(d.path, entry.path) && d.kind === entry.kind); | ||
const matching = candidates.find(d => arrayEquals(d.path, entry.path) && | ||
d.kind === entry.discriminantKind); | ||
if (!matching) { | ||
candidates.push({ | ||
kind: entry.kind, | ||
cases: { | ||
[lSerialized]: [l], | ||
[rSerialized]: [r] | ||
}, | ||
kind: entry.discriminantKind, | ||
cases: Object.assign(flatMorph(lSerialized, (i, k) => [k, [l]]), flatMorph(rSerialized, (i, k) => [k, [r]])), | ||
path: entry.path | ||
@@ -258,4 +280,4 @@ }); | ||
} | ||
matching.cases[lSerialized] = appendUnique(matching.cases[lSerialized], l); | ||
matching.cases[rSerialized] = appendUnique(matching.cases[rSerialized], r); | ||
lSerialized.forEach(lSerialized => (matching.cases[lSerialized] = appendUnique(matching.cases[lSerialized], l))); | ||
rSerialized.forEach(rSerialized => (matching.cases[rSerialized] = appendUnique(matching.cases[rSerialized], r))); | ||
} | ||
@@ -306,2 +328,4 @@ } | ||
} | ||
const serializedTypeOfDescriptions = registeredReference(jsTypeOfDescriptions); | ||
const serializedPrintable = registeredReference(printable); | ||
export const Union = { | ||
@@ -338,17 +362,7 @@ implementation, | ||
return "boolean"; | ||
let description = ""; | ||
// keep track of seen descriptions to avoid duplication | ||
const seen = {}; | ||
for (let i = 0; i < descriptions.length - 1; i++) { | ||
if (seen[descriptions[i]]) | ||
continue; | ||
seen[descriptions[i]] = true; | ||
description += descriptions[i]; | ||
if (i < descriptions.length - 2) | ||
description += delimiter; | ||
} | ||
const lastDescription = descriptions.at(-1); | ||
if (!seen[lastDescription]) | ||
description += `${finalDelimiter}${descriptions[descriptions.length - 1]}`; | ||
return description; | ||
const unique = descriptions.filter(s => (seen[s] ? false : (seen[s] = true))); | ||
const last = unique.pop(); | ||
return `${unique.join(delimiter)}${unique.length ? finalDelimiter : ""}${last}`; | ||
}; | ||
@@ -455,6 +469,2 @@ export const intersectBranches = (l, r, ctx) => { | ||
}; | ||
const discriminantKinds = { | ||
domain: 1, | ||
unit: 1 | ||
}; | ||
export const pruneDiscriminant = (discriminantBranch, discriminantCtx) => discriminantBranch.transform((nodeKind, inner, ctx) => { | ||
@@ -469,3 +479,3 @@ // if we've already checked a path at least as long as the current one, | ||
// (or a unit literal, implying a domain), we don't need to recheck it | ||
if ((nodeKind === "domain" || discriminantCtx.kind === "unit") && | ||
if ((nodeKind === "domain" || discriminantCtx.kind === "identity") && | ||
typePathToPropString(ctx.path) === discriminantCtx.propString) | ||
@@ -472,0 +482,0 @@ return null; |
@@ -28,3 +28,3 @@ import { type Domain, type JsonPrimitive, type Key, type array } from "@ark/util"; | ||
compiledValue: JsonPrimitive; | ||
serializedValue: JsonPrimitive; | ||
serializedValue: string; | ||
literalKeys: array<Key>; | ||
@@ -31,0 +31,0 @@ compiledCondition: string; |
@@ -26,5 +26,23 @@ import { domainDescriptions, domainOf, printable, prototypeKeysOf, throwParseError } from "@ark/util"; | ||
unit: (l, r) => Disjoint.init("unit", l, r), | ||
...defineRightwardIntersections("unit", (l, r) => r.allows(l.unit) ? l : (Disjoint.init("assignability", l, r.hasKind("intersection") ? | ||
r.children.find(rConstraint => !rConstraint.allows(l.unit)) | ||
: r))) | ||
...defineRightwardIntersections("unit", (l, r) => { | ||
if (r.allows(l.unit)) | ||
return l; | ||
// will always be a disjoint at this point, but we try to use | ||
// a domain Disjoint if possible since it's better for discrimination | ||
const rBasis = r.hasKind("intersection") ? r.basis : r; | ||
if (rBasis) { | ||
const rDomain = rBasis.hasKind("domain") ? rBasis : $ark.intrinsic.object; | ||
if (l.domain !== rDomain.domain) { | ||
const lDomainDisjointValue = (l.domain === "undefined" || | ||
l.domain === "null" || | ||
l.domain === "boolean") ? | ||
l.domain | ||
: $ark.intrinsic[l.domain]; | ||
return Disjoint.init("domain", lDomainDisjointValue, rDomain); | ||
} | ||
} | ||
return Disjoint.init("assignability", l, r.hasKind("intersection") ? | ||
r.children.find(rConstraint => !rConstraint.allows(l.unit)) | ||
: r); | ||
}) | ||
} | ||
@@ -36,3 +54,3 @@ }); | ||
JSON.stringify(this.compiledValue) | ||
: this.compiledValue; | ||
: `${this.compiledValue}`; | ||
literalKeys = prototypeKeysOf(this.unit); | ||
@@ -39,0 +57,0 @@ compiledCondition = compileEqualityCheck(this.unit, this.serializedValue); |
@@ -1,6 +0,6 @@ | ||
import { ParseError, type Json, type anyOrNever, type array, type conform, type flattenListable, type listable, type noSuggest, type show } from "@ark/util"; | ||
import { ParseError, type Json, type anyOrNever, type array, type conform, type flattenListable, type listable, type noSuggest } from "@ark/util"; | ||
import { resolveConfig, type ArkConfig } from "./config.ts"; | ||
import { GenericRoot, type GenericHktSchemaBodyParser, type GenericParamDef, type genericParamSchemasToAst } from "./generic.ts"; | ||
import { GenericRoot, type GenericRootParser } from "./generic.ts"; | ||
import { type NodeSchema, type RootSchema, type nodeOfKind, type reducibleKindOf } from "./kinds.ts"; | ||
import { RootModule, type InternalModule, type PreparsedNodeResolution, type SchemaModule } from "./module.ts"; | ||
import { RootModule, type InternalModule, type PreparsedNodeResolution, type SchemaModule, type instantiateRoot } from "./module.ts"; | ||
import type { BaseNode } from "./node.ts"; | ||
@@ -58,3 +58,3 @@ import { type NodeParseContext, type NodeParseOptions } from "./parse.ts"; | ||
defineSchema<def extends RootSchema>(def: def): def; | ||
generic: <const paramsDef extends readonly GenericParamDef[]>(...params: paramsDef) => GenericHktSchemaBodyParser<genericParamSchemasToAst<paramsDef>>; | ||
generic: GenericRootParser; | ||
units: (values: array, opts?: NodeParseOptions) => BaseRoot; | ||
@@ -76,7 +76,17 @@ protected lazyResolutions: Alias.Node[]; | ||
maybeShallowResolve(name: string): CachedResolution | undefined; | ||
import<names extends exportedNameOf<$>[]>(...names: names): SchemaModule<show<destructuredImportContext<$, names>>>; | ||
import(): SchemaModule<{ | ||
[k in exportedNameOf<$> as PrivateDeclaration<k>]: $[k]; | ||
}>; | ||
import<names extends exportedNameOf<$>[]>(...names: names): SchemaModule<{ | ||
[k in names[number] as PrivateDeclaration<k>]: $[k]; | ||
} & unknown>; | ||
private _exportedResolutions; | ||
private _exports; | ||
export<names extends exportedNameOf<$>[]>(...names: names): SchemaModule<show<destructuredExportContext<$, names>>>; | ||
resolve<name extends exportedNameOf<$>>(name: name): destructuredExportContext<$, []>[name]; | ||
export(): SchemaModule<{ | ||
[k in exportedNameOf<$>]: $[k]; | ||
}>; | ||
export<names extends exportedNameOf<$>[]>(...names: names): SchemaModule<{ | ||
[k in names[number]]: $[k]; | ||
} & unknown>; | ||
resolve<name extends exportedNameOf<$>>(name: name): instantiateRoot<$[name]>; | ||
abstract parseRoot(schema: unknown, opts?: NodeParseOptions): BaseRoot; | ||
@@ -87,3 +97,6 @@ } | ||
} & unknown; | ||
export declare const schemaScope: <const aliases extends { [k in keyof aliases]: conform<aliases[k], RootSchema | PreparsedNodeResolution>; }>(aliases: aliases, config?: ArkScopeConfig) => SchemaScope<instantiateAliases<aliases>>; | ||
export type SchemaScopeParser = <const aliases>(aliases: { | ||
[k in keyof aliases]: conform<aliases[k], RootSchema | PreparsedNodeResolution>; | ||
}, config?: ArkScopeConfig) => SchemaScope<instantiateAliases<aliases>>; | ||
export declare const schemaScope: SchemaScopeParser; | ||
export declare class SchemaScope<$ extends InternalResolutions = InternalResolutions> extends BaseScope<$> { | ||
@@ -93,13 +106,3 @@ parseRoot: (schema: RootSchema, opts?: NodeParseOptions) => BaseRoot; | ||
export declare const rootSchemaScope: SchemaScope; | ||
export declare const rootNode: SchemaScope["rootNode"]; | ||
export declare const node: SchemaScope["node"]; | ||
export declare const defineSchema: SchemaScope["defineSchema"]; | ||
export declare const genericNode: SchemaScope["generic"]; | ||
export declare const parseAsSchema: (def: unknown, opts?: NodeParseOptions) => BaseRoot | ParseError; | ||
export type destructuredExportContext<$, names extends exportedNameOf<$>[]> = { | ||
[k in names["length"] extends 0 ? exportedNameOf<$> : names[number]]: $[k]; | ||
}; | ||
export type destructuredImportContext<$, names extends exportedNameOf<$>[]> = { | ||
[k in names["length"] extends 0 ? exportedNameOf<$> : names[number] as `#${k & string}`]: $[k]; | ||
}; | ||
export type RootExportCache = Record<string, BaseRoot | GenericRoot | RootModule | undefined>; | ||
@@ -113,2 +116,6 @@ export declare const writeUnresolvableMessage: <token extends string>(token: token) => writeUnresolvableMessage<token>; | ||
export declare const bindCompiledScope: (references: readonly BaseNode[]) => void; | ||
export declare const rootNode: SchemaScope["rootNode"]; | ||
export declare const node: SchemaScope["node"]; | ||
export declare const defineSchema: SchemaScope["defineSchema"]; | ||
export declare const genericNode: SchemaScope["generic"]; | ||
export {}; |
@@ -63,3 +63,3 @@ import { ParseError, flatMorph, hasDomain, isArray, printable, throwInternalError, throwParseError } from "@ark/util"; | ||
const $ = this; | ||
return instantiateDef => new GenericRoot(params, new LazyGenericBody(instantiateDef), $, $); | ||
return (def, possibleHkt) => new GenericRoot(params, possibleHkt ? new LazyGenericBody(def) : def, $, $); | ||
}; | ||
@@ -105,3 +105,3 @@ units = (values, opts) => { | ||
const impl = nodeImplementationsByKind[kind]; | ||
const normalizedSchema = impl.normalize?.(schema) ?? schema; | ||
const normalizedSchema = impl.normalize?.(schema, this.resolvedConfig) ?? schema; | ||
// check again after normalization in case a node is a valid collapsed | ||
@@ -283,4 +283,6 @@ // schema for the kind (e.g. sequence can collapse to element accepting a Node') | ||
return resolution; | ||
if (hasArkKind(resolution, "module")) | ||
return throwParseError(writeMissingSubmoduleAccessMessage(name)); | ||
if (hasArkKind(resolution, "module")) { | ||
return (resolution.$root ?? | ||
throwParseError(writeMissingSubmoduleAccessMessage(name))); | ||
} | ||
throwInternalError(`Unexpected resolution for alias '${name}': ${printable(resolution)}`); | ||
@@ -296,6 +298,2 @@ }; | ||
export const rootSchemaScope = new SchemaScope({}); | ||
export const rootNode = rootSchemaScope.rootNode; | ||
export const node = rootSchemaScope.node; | ||
export const defineSchema = rootSchemaScope.defineSchema; | ||
export const genericNode = rootSchemaScope.generic; | ||
export const parseAsSchema = (def, opts) => { | ||
@@ -361,1 +359,7 @@ try { | ||
.compile()(); | ||
// ensure the scope is resolved so JIT will be applied to future types | ||
rootSchemaScope.export(); | ||
export const rootNode = rootSchemaScope.rootNode; | ||
export const node = rootSchemaScope.node; | ||
export const defineSchema = rootSchemaScope.defineSchema; | ||
export const genericNode = rootSchemaScope.generic; |
import { type Key } from "@ark/util"; | ||
import type { nodeOfKind } from "../kinds.ts"; | ||
import type { BaseNode } from "../node.ts"; | ||
import type { Domain } from "../roots/domain.ts"; | ||
import type { BaseRoot } from "../roots/root.ts"; | ||
import type { DiscriminantKind } from "../roots/union.ts"; | ||
import type { Prop } from "../structure/prop.ts"; | ||
@@ -9,2 +11,3 @@ import type { BoundKind } from "./implement.ts"; | ||
kind: kind; | ||
discriminantKind: DiscriminantKind | undefined; | ||
l: OperandsByDisjointKind[kind]; | ||
@@ -16,3 +19,3 @@ r: OperandsByDisjointKind[kind]; | ||
type OperandsByDisjointKind = { | ||
domain: nodeOfKind<"domain">; | ||
domain: nodeOfKind<"domain"> | Domain.Enumerable; | ||
unit: nodeOfKind<"unit">; | ||
@@ -19,0 +22,0 @@ proto: nodeOfKind<"proto">; |
@@ -8,2 +8,3 @@ import { isArray, throwParseError } from "@ark/util"; | ||
kind, | ||
discriminantKind: discriminatKindsByDisjointKind[kind], | ||
l, | ||
@@ -18,2 +19,3 @@ r, | ||
kind, | ||
discriminantKind: discriminatKindsByDisjointKind[kind], | ||
l, | ||
@@ -60,1 +62,5 @@ r, | ||
export const writeUnsatisfiableExpressionError = (expression) => `${expression} results in an unsatisfiable type`; | ||
const discriminatKindsByDisjointKind = { | ||
domain: "typeOf", | ||
unit: "identity" | ||
}; |
@@ -7,3 +7,3 @@ import { type Entry, type Json, type JsonData, type arrayIndexOf, type keySet, type keySetOf, type listable, type requireKeys, type show } from "@ark/util"; | ||
import type { BaseRoot, schemaKindOrRightOf, schemaKindRightOf } from "../roots/root.ts"; | ||
import type { BaseScope } from "../scope.ts"; | ||
import type { BaseScope, ResolvedArkScopeConfig } from "../scope.ts"; | ||
import type { Structure } from "../structure/structure.ts"; | ||
@@ -92,3 +92,3 @@ import type { BaseErrorContext, BaseMeta, BaseNodeDeclaration, BaseNormalizedSchema } from "./declare.ts"; | ||
preserveUndefined?: true; | ||
child?: true; | ||
child?: boolean; | ||
serialize?: (schema: instantiated) => JsonData; | ||
@@ -100,3 +100,3 @@ parse?: (schema: Exclude<d["normalizedSchema"][k], undefined>, ctx: NodeParseContext<d["kind"]>) => instantiated | undefined; | ||
keys: keySchemaDefinitions<d>; | ||
normalize: (schema: d["schema"]) => d["normalizedSchema"]; | ||
normalize: (schema: d["schema"], ctx: ResolvedArkScopeConfig) => d["normalizedSchema"]; | ||
hasAssociatedError: d["errorContext"] extends null ? false : true; | ||
@@ -103,0 +103,0 @@ finalizeInnerJson?: (json: { |
@@ -30,2 +30,8 @@ import { ArkError, ArkErrors } from "./errors.js"; | ||
return this.errors; | ||
if (!this.queuedMorphs.length) | ||
return this.root; | ||
if (typeof this.root === "object" && | ||
this.root !== null && | ||
this.config.clone) | ||
this.root = this.config.clone(this.root); | ||
// invoking morphs that are Nodes will reuse this context, potentially | ||
@@ -32,0 +38,0 @@ // adding additional morphs, so we have to continue looping until |
@@ -29,3 +29,11 @@ import { append, flatMorph, printable, spliterate, throwParseError } from "@ark/util"; | ||
hasAssociatedError: false, | ||
normalize: schema => schema, | ||
normalize: (schema, ctx) => { | ||
if (!schema.undeclared && ctx.onUndeclaredKey !== "ignore") { | ||
return { | ||
...schema, | ||
undeclared: ctx.onUndeclaredKey | ||
}; | ||
} | ||
return schema; | ||
}, | ||
keys: { | ||
@@ -65,2 +73,3 @@ required: { | ||
kind: "presence", | ||
discriminantKind: undefined, | ||
l: $ark.intrinsic.never.internal, | ||
@@ -98,2 +107,3 @@ r: r.propsByKey[k].value, | ||
kind: "presence", | ||
discriminantKind: undefined, | ||
l: l.propsByKey[k].value, | ||
@@ -100,0 +110,0 @@ r: $ark.intrinsic.never.internal, |
{ | ||
"name": "@arktype/schema", | ||
"version": "0.3.3", | ||
"version": "0.4.0", | ||
"license": "MIT", | ||
@@ -32,3 +32,3 @@ "author": { | ||
"dependencies": { | ||
"@ark/util": "0.2.2" | ||
"@ark/util": "0.3.0" | ||
}, | ||
@@ -35,0 +35,0 @@ "publishConfig": { |
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
326444
7669
+ Added@ark/util@0.3.0(transitive)
- Removed@ark/util@0.2.2(transitive)
Updated@ark/util@0.3.0