Socket
Socket
Sign inDemoInstall

arktype

Package Overview
Dependencies
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

arktype - npm Package Compare versions

Comparing version 2.0.0-dev.1 to 2.0.0-dev.2

__tests__/match.bench.ts

46

__tests__/array.test.ts

@@ -6,3 +6,3 @@ import { attest } from "@arktype/attest"

import {
prematureRestMessage,
multipleVariadicMesage,
writeNonArrayRestMessage

@@ -13,10 +13,19 @@ } from "../parser/tuple.js"

describe("base", () => {
it("base", () => {
it("allows and apply", () => {
const t = type("string[]")
attest<string[]>(t.infer)
attest(t.allows([])).equals(true)
attest(t([]).out).snap([])
attest(t.allows(["foo", "bar"])).equals(true)
attest(t(["foo", "bar"]).out).snap(["foo", "bar"])
attest(t.allows(["foo", "bar", 5])).equals(false)
attest(t(["foo", "bar", 5]).errors?.summary).snap(
"Value at [2] must be a string (was number)"
)
attest(t.allows([5, "foo", "bar"])).equals(false)
attest(t([5, "foo", "bar"]).errors?.summary).snap(
"Value at [0] must be a string (was number)"
)
})
it("nested", () => {

@@ -26,5 +35,13 @@ const t = type("string[][]")

attest(t.allows([])).equals(true)
attest(t([]).out).snap([])
attest(t.allows([["foo"]])).equals(true)
attest(t([["foo"]]).out).snap([["foo"]])
attest(t.allows(["foo"])).equals(false)
attest(t(["foo"]).errors?.summary).snap(
"Value at [0] must be an array (was string)"
)
attest(t.allows([["foo", 5]])).equals(false)
attest(t([["foo", 5]]).errors?.summary).snap(
"Value at [0][1] must be a string (was number)"
)
})

@@ -37,2 +54,3 @@

})
describe("optional tuple literals", () => {

@@ -98,8 +116,13 @@ it("string optional", () => {

attest(t.allows(["", 0])).equals(true)
attest(t(["", 0]).out).snap()
attest(t.allows([true, 0])).equals(false)
attest(t([true, 0]).errors?.summary).snap()
attest(t.allows([0, false])).equals(false)
attest(t([0, false]).errors?.summary).snap()
// too short
attest(t.allows([""])).equals(false)
attest(t([""]).errors?.summary).snap()
// too long
attest(t.allows(["", 0, 1])).equals(false)
attest(t(["", 0, 1]).errors?.summary).snap()
// non-array

@@ -113,3 +136,11 @@ attest(

).equals(false)
attest(
t({
length: 2,
0: "",
1: 0
}).errors?.summary
).snap()
})
it("nested", () => {

@@ -130,3 +161,10 @@ const t = type([["string", "number"], [{ a: "boolean", b: ["null"] }]])

attest(t.allows([["", 0], [{ a: true, b: [null] }]])).equals(true)
attest(t([["foo", 1], [{ a: true, b: [null] }]]).out).snap([
["foo", 1],
[{ a: true, b: [null] }]
])
attest(t.allows([["", 0], [{ a: true, b: [undefined] }]])).equals(false)
attest(
t([["foo", 1], [{ a: true, b: [undefined] }]]).errors?.summary
).snap("[1][0].b[0] must be null (was undefined)")
})

@@ -181,7 +219,7 @@ })

type(["...number[]", "string"])
).throwsAndHasTypeError(prematureRestMessage)
).throwsAndHasTypeError(multipleVariadicMesage)
attest(() =>
// @ts-expect-error
type([["...", "string[]"], "number"])
).throwsAndHasTypeError(prematureRestMessage)
).throwsAndHasTypeError(multipleVariadicMesage)
})

@@ -188,0 +226,0 @@ })

241

__tests__/config.test.ts

@@ -0,168 +1,77 @@

import { attest } from "@arktype/attest"
import { scope, type } from "arktype"
describe("config traversal", () => {
// it("tuple expression", () => {
// const mustBe = "a series of characters"
// const types = scope({
// a: ["string", ":", { mustBe }],
// b: {
// a: "a"
// }
// }).compile()
// attest<string>(types.a.infer)
// // attest(types.a.flat).snap([
// // [
// // "config",
// // {
// // config: [["mustBe", "a series of characters"]],
// // node: "string"
// // }
// // ]
// // ])
// attest(types.a(1).errors?.summary).snap(
// "Must be a series of characters (was number)"
// )
// attest<{ a: string }>(types.b.infer)
// // attest(types.b.flat).equals([
// // ["domain", "object"],
// // [
// // "requiredProp",
// // [
// // "a",
// // [
// // [
// // "config",
// // {
// // config: [["mustBe", mustBe]],
// // node: "string"
// // }
// // ]
// // ]
// // ]
// // ]
// // ])
// attest(types.b({ a: true }).errors?.summary).snap(
// "a must be a series of characters (was boolean)"
// )
// })
// it("tuple expression at path", () => {
// const t = type({
// monster: [
// "196883",
// ":",
// {
// mustBe: "the number of dimensions in the monster group"
// }
// ]
// })
// attest<{ monster: 196883 }>(t.infer)
// // attest(t.node).snap({
// // object: {
// // props: {
// // monster: {
// // node: { number: { value: 196883 } },
// // config: {
// // mustBe: "the number of dimensions in the monster group"
// // }
// // }
// // }
// // }
// // })
// // attest(t.flat).snap([
// // ["domain", "object"],
// // [
// // "requiredProp",
// // [
// // "monster",
// // [
// // [
// // "config",
// // {
// // config: [
// // [
// // "mustBe",
// // "the number of dimensions in the monster group"
// // ]
// // ],
// // node: [["value", 196883]]
// // }
// // ]
// // ]
// // ]
// // ]
// // ])
// attest(t({ monster: 196882 }).errors?.summary).snap(
// "monster must be the number of dimensions in the monster group (was 196882)"
// )
// })
// it("anonymous type config", () => {
// const t = type(type("true", { mustBe: "unfalse" }))
// attest<true>(t.infer)
// // attest(t.flat).snap([
// // [
// // "config",
// // { config: [["mustBe", "unfalse"]], node: [["value", true]] }
// // ]
// // ])
// attest(t(false).errors?.summary).snap("Must be unfalse (was false)")
// })
// it("anonymous type config at path", () => {
// const unfalse = type("true", { mustBe: "unfalse" })
// const t = type({ myKey: unfalse })
// // attest(t.flat).snap([
// // ["domain", "object"],
// // [
// // "requiredProp",
// // [
// // "myKey",
// // [
// // [
// // "config",
// // {
// // config: [["mustBe", "unfalse"]],
// // node: [["value", true]]
// // }
// // ]
// // ]
// // ]
// // ]
// // ])
// attest(t({ myKey: "500" }).errors?.summary).snap(
// "myKey must be unfalse (was '500')"
// )
// // config only applies within myKey
// attest(t({ yourKey: "500" }).errors?.summary).snap(
// "myKey must be defined"
// )
// })
// it("anonymous type thunk", () => {
// const t = type(() => type("false", { mustBe: "untrue" }))
// attest<false>(t.infer)
// // attest(t.flat).snap([
// // [
// // "config",
// // { config: [["mustBe", "untrue"]], node: [["value", false]] }
// // ]
// // ])
// })
// it("anonymous type thunk at path", () => {
// const t = type({ myKey: () => type("false", { mustBe: "untrue" }) })
// attest<{ myKey: false }>(t.infer)
// // attest(t.flat).snap([
// // ["domain", "object"],
// // [
// // "requiredProp",
// // [
// // "myKey",
// // [
// // [
// // "config",
// // {
// // config: [["mustBe", "untrue"]],
// // node: [["value", false]]
// // }
// // ]
// // ]
// // ]
// // ]
// // ])
// })
it("tuple expression", () => {
const description = "a series of characters"
const types = scope({
a: ["string", "@", description],
b: {
a: "a"
}
}).export()
attest<string>(types.a.infer)
attest(types.a.description).equals(description)
attest(types.a(1).errors?.summary).snap(
"Must be a series of characters (was number)"
)
attest<{ a: string }>(types.b.infer)
attest(types.b({ a: true }).errors?.summary).snap(
"a must be a series of characters (was boolean)"
)
})
it("tuple expression at path", () => {
const description = "the number of dimensions in the monster group"
const t = type({
monster: ["196883", "@", description]
})
attest<{ monster: 196883 }>(t.infer)
attest(t.description).equals(description)
attest(t({ monster: 196882 }).errors?.summary).snap(
"monster must be the number of dimensions in the monster group (was 196882)"
)
})
it("anonymous type config", () => {
const t = type(type("true", "@", { description: "unfalse" }))
attest<true>(t.infer)
attest(t(false).errors?.summary).snap("Must be unfalse (was false)")
})
it("anonymous type config at path", () => {
const unfalse = type("true", "@", { description: "unfalse" })
const t = type({ myKey: unfalse })
attest(t({ myKey: "500" }).errors?.summary).snap(
`myKey must be unfalse (was "500")`
)
})
it("anonymous type thunk", () => {
const t = type(() => type("false", "@", { description: "untrue" }))
attest<false>(t.infer)
attest(t.description).snap("untrue")
})
it("anonymous type thunk at path", () => {
const t = type({
myKey: () => type("false", "@", { description: "untrue" })
})
attest<{ myKey: false }>(t.infer)
attest(t({ myKey: true }).errors?.summary).snap(
"myKey must be untrue (was true)"
)
})
it("applies config to shallow descendants", () => {
const user = type({
name: "string",
age: "number"
}).describe("a valid user")
// should give the original error at a path
attest(
user({
name: "david",
age: true
}).errors?.summary
).snap("age must be a number (was boolean)")
// should give the shallow custom error
attest(user(null).errors?.summary).snap("Must be a valid user (was null)")
})
})

@@ -54,6 +54,7 @@ import { attest } from "@arktype/attest"

it("regexp", () => {
const t = declare<string>().type(/.*/)
attest<string>(t.infer)
})
// TODO: figure out narrowing
// it("regexp", () => {
// const t = declare<string>().type(/.*/)
// attest<string>(t.infer)
// })

@@ -60,0 +61,0 @@ it("Inferred<t>", () => {

import { attest } from "@arktype/attest"
import { type } from "arktype"
describe("key traversal", () => {
const getExtraneousB = () => ({ a: "ok", b: "why?" })
it("loose by default", () => {
const t = type({
a: "string"
})
const dataWithExtraneousB = getExtraneousB()
attest(t(dataWithExtraneousB).out).equals(dataWithExtraneousB)
})
it("invalid union", () => {
const o = type([{ a: "string" }, "|", { b: "boolean" }]).configure({
keys: "strict"
})
attest(o({ a: 2 }).errors?.summary).snap(
'a must be a string or removed (was {"a":2})'
)
})
it("distilled type", () => {
const t = type({
a: "string"
}).configure({ keys: "distilled" })
attest(t({ a: "ok" }).out).equals({ a: "ok" })
attest(t(getExtraneousB()).out).snap({ a: "ok" })
})
it("distilled array", () => {
const o = type({ a: "email[]" }).configure({
keys: "distilled"
})
attest(o({ a: ["shawn@arktype.io"] }).out).snap({
a: ["shawn@arktype.io"]
})
attest(o({ a: ["notAnEmail"] }).errors?.summary).snap(
"a/0 must be a valid email (was 'notAnEmail')"
)
// can handle missing keys
attest(o({ b: ["shawn"] }).errors?.summary).snap("a must be defined")
})
it("distilled union", () => {
const o = type([{ a: "string" }, "|", { b: "boolean" }]).configure({
keys: "distilled"
})
// can distill to first branch
attest(o({ a: "to", z: "bra" }).out).snap({ a: "to" })
// can distill to second branch
attest(o({ b: true, c: false }).out).snap({ b: true })
// can handle missing keys
attest(o({ a: 2 }).errors?.summary).snap(
'a must be a string or b must be defined (was {"a":2})'
)
})
it("strict type", () => {
const t = type({
a: "string"
}).configure({ keys: "strict" })
attest(t({ a: "ok" }).out).equals({ a: "ok" })
attest(t(getExtraneousB()).errors?.summary).snap("b must be removed")
})
it("strict array", () => {
const o = type({ a: "string[]" }).configure({
keys: "strict"
})
attest(o({ a: ["shawn"] }).out).snap({ a: ["shawn"] })
attest(o({ a: [2] }).errors?.summary).snap(
"a/0 must be a string (was number)"
)
attest(o({ b: ["shawn"] }).errors?.summary).snap(
"b must be removed\na must be defined"
)
})
})
// TODO: reenable
// describe("key traversal", () => {
// const getExtraneousB = () => ({ a: "ok", b: "why?" })
// it("loose by default", () => {
// const t = type({
// a: "string"
// })
// const dataWithExtraneousB = getExtraneousB()
// attest(t(dataWithExtraneousB).out).equals(dataWithExtraneousB)
// })
// it("invalid union", () => {
// const o = type([{ a: "string" }, "|", { b: "boolean" }]).configure({
// keys: "strict"
// })
// attest(o({ a: 2 }).errors?.summary).snap(
// 'a must be a string or removed (was {"a":2})'
// )
// })
// it("distilled type", () => {
// const t = type({
// a: "string"
// }).configure({ keys: "distilled" })
// attest(t({ a: "ok" }).out).equals({ a: "ok" })
// attest(t(getExtraneousB()).out).snap({ a: "ok" })
// })
// it("distilled array", () => {
// const o = type({ a: "email[]" }).configure({
// keys: "distilled"
// })
// attest(o({ a: ["shawn@arktype.io"] }).out).snap({
// a: ["shawn@arktype.io"]
// })
// attest(o({ a: ["notAnEmail"] }).errors?.summary).snap(
// "a/0 must be a valid email (was 'notAnEmail')"
// )
// // can handle missing keys
// attest(o({ b: ["shawn"] }).errors?.summary).snap("a must be defined")
// })
// it("distilled union", () => {
// const o = type([{ a: "string" }, "|", { b: "boolean" }]).configure({
// keys: "distilled"
// })
// // can distill to first branch
// attest(o({ a: "to", z: "bra" }).out).snap({ a: "to" })
// // can distill to second branch
// attest(o({ b: true, c: false }).out).snap({ b: true })
// // can handle missing keys
// attest(o({ a: 2 }).errors?.summary).snap(
// 'a must be a string or b must be defined (was {"a":2})'
// )
// })
// it("strict type", () => {
// const t = type({
// a: "string"
// }).configure({ keys: "strict" })
// attest(t({ a: "ok" }).out).equals({ a: "ok" })
// attest(t(getExtraneousB()).errors?.summary).snap("b must be removed")
// })
// it("strict array", () => {
// const o = type({ a: "string[]" }).configure({
// keys: "strict"
// })
// attest(o({ a: ["shawn"] }).out).snap({ a: ["shawn"] })
// attest(o({ a: [2] }).errors?.summary).snap(
// "a/0 must be a string (was number)"
// )
// attest(o({ b: ["shawn"] }).errors?.summary).snap(
// "b must be removed\na must be defined"
// )
// })
// })

@@ -1,34 +0,224 @@

// import { match } from "arktype"
import { attest } from "@arktype/attest"
import { match } from "arktype"
import { scope } from "../ark.js"
// const matcher = match({
// boolean: (b) => !b,
// semver: (s) => s.length
// }).when({ condition: "true" }, (data) => {
// return data.condition
// })
describe("match", () => {
it("properly infers types of inputs/outputs", () => {
const matcher = match({ string: (s) => s, number: (n) => n })
.when("boolean", (b) => b)
.orThrow()
// const greeting = match({
// "integer<12": () => "Good morning",
// "integer<18": () => "Good afternoon",
// default: () => "Good evening"
// })
// properly infers the type of the output based on the input
attest<string>(matcher("abc")).equals("abc")
attest<number>(matcher(4)).equals(4)
attest<boolean>(matcher(true)).equals(true)
// const result = greeting(5)
// and properly handles unions in the input type
attest<string | number>(matcher(0 as string | number))
})
// const base = matcher({} as unknown) //=>?
it("`.when` errors on redundant cases", () => {
const matcher = match().when("string", (s) => s)
// const oneResult = matcher({} as object) //=>?
// @ts-expect-error
attest(() => matcher.when("string", (s) => s)).throwsAndHasTypeError(
"This branch is redundant and will never be reached" // TODO: rewrite error message
)
})
// const twoResults = matcher({} as boolean | string) //=>?
it("errors on cases redundant to a previous `cases` block", () => {
const matcher = match({ string: (s) => s })
// const validMatcher = match({
// // ^?
// number: (data) => data,
// "string|unknown[]": (data) => data.length
// })
// @ts-expect-error
attest(() => matcher.cases({ string: (s) => s })).throwsAndHasTypeError(
"This branch is redundant and will never be reached"
)
})
// const invalidMatcher = match({
// // ^?
// "string|numbr": (data) => data,
// "1<boolean<5": (data) => data
// })
describe("constraint handling", () => {
it("properly considers constrained types as different from their base", () => {
const matcher = match
.only<number>()
.when("number>2", (n) => {
attest<number>(n)
return n
})
.when("number", (n) => n)
.finalize()
// for assertions
matcher(5)
})
})
describe('"finalizations"', () => {
it(".orThrow()", () => {
const matcher = match()
.when("string", (s) => s)
.orThrow()
// properly returns the `never` type and throws given a guaranteed-to-be-invalid input
attest<never>(matcher(4))
attest(() => matcher(4)).throws("TODO: what's the error message?") // TODO
})
describe(".default", () => {
it("chained, given a callback", () => {
const matcher = match()
.when("string", (s) => s)
.default((_) => 0)
attest<string>(matcher("abc")).equals("abc")
attest<number>(matcher(4)).equals(0)
attest<string | number>(matcher(0 as unknown))
})
it("chained, given a value", () => {
const matcher = match()
.when("string", (s) => s)
.default(0)
attest<string>(matcher("abc")).equals("abc")
attest<number>(matcher(4)).equals(0)
attest<string | number>(matcher(0 as unknown))
})
it("in `cases`, given a callback", () => {
const matcher = match({ string: (s) => s, default: (_) => 0 })
attest<string>(matcher("abc")).equals("abc")
attest<number>(matcher(4)).equals(0)
attest<string | number>(matcher(0 as unknown))
})
})
it("errors when attempting to `.finalize()` a non-exhaustive matcher", () => {
const matcher = match().when("string", (s) => s)
// @ts-expect-error
attest(() => matcher.finalize()).throwsAndHasTypeError(
"Cannot manually finalize a non-exhaustive matcher: consider adding a `.default` case, using one of the `.orX` methods, or using `match.only<T>`" // TODO: rewrite message
)
})
it("considers `unknown` exhaustive", () => {
const matcher = match()
.when("unknown", (x) => x)
.finalize()
attest(matcher(4)).equals(4)
})
})
describe(".only<T>", () => {
it("does not accept invalid inputs at a type-level", () => {
const matcher = match
.only<string | number>()
.when("string", (s) => s)
.when("number", (n) => n)
.finalize()
// @ts-expect-error
attest(() => matcher(true)).throwsAndHasTypeError(
"Argument of type 'true' is not assignable to parameter of type 'string | number'"
) // TODO: what's the runtime error?
})
it("errors when attempting to `.finalize()` a non-exhaustive matcher", () => {
const matcher = match.only<string | number>().when("string", (s) => s)
// @ts-expect-error
attest(() => matcher.finalize()).throwsAndHasTypeError(
"Cannot manually finalize a non-exhaustive matcher: consider adding a `.default` case, using one of the `.orX` methods, or handling the cases explicitly" // TODO: rewrite message. at runtime can we even show a counterexample (serialize the cases not handled)?
)
})
it("allows finalizing exhaustive matchers", (_) => {
const matcher = match
.only<string | number>()
.when("string", (s) => s)
.when("number", (n) => n)
.finalize()
attest<string>(matcher("abc")).equals("abc")
attest<number>(matcher(4)).equals(4)
attest<string | number>(matcher(0 as string | number))
})
it("infers the parameter to chained .default as the remaining cases", () => {
const matcher = match
.only<string | number | boolean>()
.when("string", (s) => s)
.default((n) => {
attest<number | boolean>(n)
return n
})
// for assertions
matcher(4)
})
it("infers the parameter to in-cases .default", () => {
const matcher = match.only<string | number | boolean>().cases({
string: (s) => s,
default: (n) => {
// TS doesn't understand sequentiality in cases, so it's inferred as the in-type
attest<string | number | boolean>(n)
return n
}
})
// for assertions
matcher(4)
})
it("returns `never` on only the specific cases handled by `.orThrow`", () => {
const matcher = match
.only<string | number>()
.when("string", (s) => s)
.orThrow()
attest<never>(matcher(4))
})
})
it("within scope", () => {
const threeSixtyNoScope = scope({ three: "3", sixty: "60", no: "'no'" })
const matcher = threeSixtyNoScope
.match({
three: (three) => {
attest<3>(three)
return 3
}
})
.when("sixty", (sixty) => {
attest<60>(sixty)
return 60
})
.orThrow()
// for assertions
matcher(3)
matcher(60)
})
it("properly propagates errors from invalid type definitions in `when`", () => {
// @ts-expect-error
attest(() => match().when("strong", (s) => s)).type.errors(
"'strong' is unresolvable"
)
})
it("properly propagates errors from invalid type definitions in `cases`", () => {
// @ts-expect-error
attest(() => match({ strong: (s) => s })).type.errors(
"'strong' is unresolvable"
)
})
})
import { attest } from "@arktype/attest"
import type { Out } from "@arktype/schema"
import type { Out, is } from "@arktype/schema"
import type { equals } from "@arktype/util"

@@ -55,3 +55,3 @@ import { type, type Type } from "arktype"

const one = type(["number", ":", (n): n is 1 => n === 1])
attest<Type<1>>(one)
attest<1>(one.infer)
})

@@ -92,3 +92,3 @@ it("functional parameter inference", () => {

.narrow((n): n is 5 => n === 5)
attest<Type<(In: string) => Out<5>>>(t)
attest<Type<(In: string) => Out<is<5, { anonymousPredicate: true }>>>>(t)
attest(t.json).snap({ domain: "string" })

@@ -95,0 +95,0 @@ })

import { attest } from "@arktype/attest"
import { registry } from "@arktype/schema"
import { printable } from "@arktype/util"
import { printable, reference } from "@arktype/util"
import { scope, type } from "arktype"

@@ -39,3 +38,3 @@ import {

const s = Symbol()
const name = registry.register(s)
const name = reference(s)
const t = type({

@@ -52,3 +51,3 @@ [s]: "string"

const s = Symbol()
const name = registry.register(s)
const name = reference(s)
const t = type({

@@ -274,8 +273,9 @@ [s]: "number?"

})
it("traverse optional", () => {
const o = type({ "a?": "string" }).configure({ keys: "strict" })
attest(o({ a: "a" }).out).snap({ a: "a" })
attest(o({}).out).snap({})
attest(o({ a: 1 }).errors?.summary).snap("a must be a string (was number)")
})
// TODO: reenable
// it("traverse optional", () => {
// const o = type({ "a?": "string" }).configure({ keys: "strict" })
// attest(o({ a: "a" }).out).snap({ a: "a" })
// attest(o({}).out).snap({})
// attest(o({ a: 1 }).errors?.summary).snap("a must be a string (was number)")
// })
it("intersection", () => {

@@ -295,10 +295,11 @@ const t = type({ a: "number" }).and({ b: "boolean" })

})
it("multiple bad strict", () => {
const t = type({ a: "string", b: "boolean" }).configure({
keys: "strict"
})
attest(t({ a: 1, b: 2 }).errors?.summary).snap(
"a must be a string (was number)\nb must be boolean (was number)"
)
})
// TODO: reenable
// it("multiple bad strict", () => {
// const t = type({ a: "string", b: "boolean" }).configure({
// keys: "strict"
// })
// attest(t({ a: 1, b: 2 }).errors?.summary).snap(
// "a must be a string (was number)\nb must be boolean (was number)"
// )
// })
})

@@ -1,7 +0,14 @@

import type { Morph } from "@arktype/schema"
import type {
Fn,
AnonymousRefinementKey,
Morph,
distill,
intersectConstrainables,
is
} from "@arktype/schema"
import type {
ErrorMessage,
UnknownUnion,
isDisjoint,
numericStringKeyOf,
replaceKey,
returnOf,
unionToTuple,

@@ -13,36 +20,119 @@ valueOf

type MatchContext = {
inConstraint: unknown
outConstraint: unknown
thens: readonly Fn[]
type MatchParserContext = {
thens: readonly ((In: unknown) => unknown)[]
$: unknown
exhaustiveOver: unknown
}
type validateCases<cases, ctx extends MatchContext> = {
[k in keyof cases | keyof ctx["$"] | "default"]?: k extends "default"
? (In: ctx["inConstraint"]) => ctx["outConstraint"]
: k extends validateTypeRoot<k, ctx["$"]>
? (
In: ctx["inConstraint"] & inferTypeRoot<k, ctx["$"]>
) => ctx["outConstraint"]
: validateTypeRoot<k, ctx["$"]>
export type MatchParser<$> = CaseMatchParser<{
thens: []
// "match()" is the same as "match.only<unknown>()"
exhaustiveOver: unknown
$: $
}> & {
(): ChainableMatchParser<{
thens: []
// "match()" is the same as "match.only<unknown>()"
exhaustiveOver: unknown
$: $
}>
only: <inputs>() => ChainableMatchParser<{
thens: []
exhaustiveOver: inputs
$: $
}>
}
type errorCases<cases, ctx extends MatchContext> = {
[k in keyof cases]?: k extends "default"
? (In: ctx["inConstraint"]) => ctx["outConstraint"]
: k extends validateTypeRoot<k, ctx["$"]>
? (
In: ctx["inConstraint"] & inferTypeRoot<k, ctx["$"]>
) => ctx["outConstraint"]
: validateTypeRoot<k, ctx["$"]>
type matcherInputs<ctx extends MatchParserContext> = Parameters<
ctx["thens"][number]
>[0]
type AnonymouslyRefined = {
[k in AnonymousRefinementKey]: { [_ in k]: true }
}[AnonymousRefinementKey]
type getHandledBranches<ctx extends MatchParserContext> = Exclude<
matcherInputs<ctx>,
is<unknown, AnonymouslyRefined>
>
type getUnhandledBranches<ctx extends MatchParserContext> = Exclude<
unknown extends ctx["exhaustiveOver"] ? UnknownUnion : ctx["exhaustiveOver"],
getHandledBranches<ctx>
>
type addBranches<
ctx extends MatchParserContext,
branches extends unknown[]
> = replaceKey<ctx, "thens", [...ctx["thens"], ...branches]>
type validateWhenDefinition<
def,
ctx extends MatchParserContext
> = def extends validateTypeRoot<def, ctx["$"]>
? inferMatchBranch<def, ctx> extends getHandledBranches<ctx>
? ErrorMessage<"This branch is redundant and will never be reached">
: def
: validateTypeRoot<def, ctx["$"]>
// infer the types handled by a match branch, which is identical to `inferTypeRoot` while properly
// excluding cases that are already handled by other branches
type inferMatchBranch<
def,
ctx extends MatchParserContext
> = intersectConstrainables<
getUnhandledBranches<ctx>,
inferTypeRoot<def, ctx["$"]>
>
export type ChainableMatchParser<ctx extends MatchParserContext> = {
// chainable methods
when: <def, ret>(
when: validateWhenDefinition<def, ctx>,
then: (In: distill<inferMatchBranch<def, ctx>>) => ret
) => ChainableMatchParser<
addBranches<ctx, [(In: inferMatchBranch<def, ctx>) => ret]>
>
cases: CaseMatchParser<ctx>
// finalizing methods
orThrow: () => finalizeMatchParser<
addBranches<ctx, [(In: getHandledBranches<ctx>) => never]>
>
default: MatchParserDefaultInvocation<ctx>
finalize: (
this: getUnhandledBranches<ctx> extends never
? ChainableMatchParser<ctx>
: ErrorMessage<"Cannot manually finalize a non-exhaustive matcher: consider adding a `.default` case, using one of the `.orX` methods, or using `match.only<T>`">
) => finalizeMatchParser<ctx>
}
type MatchParserDefaultInvocation<ctx extends MatchParserContext> = {
<f extends (In: getUnhandledBranches<ctx>) => unknown>(
f: f
): finalizeWithDefault<ctx, ReturnType<f>>
<const value>(value: value): finalizeWithDefault<ctx, value>
}
type validateCases<cases, ctx extends MatchParserContext> = {
[def in keyof cases | keyof ctx["$"] | "default"]?: def extends "default"
? (In: distill<getUnhandledBranches<ctx>>) => unknown
: def extends validateWhenDefinition<def, ctx>
? (In: distill<inferMatchBranch<def, ctx>>) => unknown
: validateWhenDefinition<def, ctx>
}
type errorCases<cases, ctx extends MatchParserContext> = {
[def in keyof cases]?: def extends "default"
? (In: distill<getUnhandledBranches<ctx>>) => unknown
: def extends validateWhenDefinition<def, ctx>
? (In: distill<inferMatchBranch<def, ctx>>) => unknown
: validateWhenDefinition<def, ctx>
} & {
[k in Exclude<keyof ctx["$"], keyof cases>]?: (
In: ctx["inConstraint"] & ctx["$"][k]
) => ctx["outConstraint"]
In: distill<intersectConstrainables<getUnhandledBranches<ctx>, ctx["$"][k]>>
) => unknown
} & {
default?: (In: ctx["inConstraint"]) => ctx["outConstraint"]
default?: (In: distill<getUnhandledBranches<ctx>>) => unknown
}
export type CaseMatchParser<ctx extends MatchContext> = {
export type CaseMatchParser<ctx extends MatchParserContext> = {
<cases>(

@@ -52,67 +142,104 @@ def: cases extends validateCases<cases, ctx>

: errorCases<cases, ctx>
): ChainableMatchParser<
replaceKey<ctx, "thens", [...ctx["thens"], ...unionToTuple<valueOf<cases>>]>
>
): cases extends { default: (...args: never[]) => infer defaultReturn }
? finalizeWithDefault<
addBranches<ctx, unionToTuple<cases[Exclude<keyof cases, "default">]>>,
defaultReturn
>
: ChainableMatchParser<addBranches<ctx, unionToTuple<valueOf<cases>>>>
}
export type MatchParser<$> = CaseMatchParser<{
inConstraint: unknown
outConstraint: unknown
thens: []
$: $
}> & {
<In = unknown, Out = unknown>(): ChainableMatchParser<{
inConstraint: In
outConstraint: Out
thens: []
$: $
}>
type finalizeWithDefault<
ctx extends MatchParserContext,
defaultReturn
> = finalizeMatchParser<
addBranches<ctx, [(_: getUnhandledBranches<ctx>) => defaultReturn]>
>
type finalizeMatchParser<ctx extends MatchParserContext> = MatchInvocation<{
thens: ctx["thens"]
initialInputs: ctx["exhaustiveOver"]
}>
type MatchInvocationContext = {
thens: readonly ((...args: never[]) => unknown)[]
initialInputs: unknown
}
export type WhenMatchParser<ctx extends MatchContext> = <
def,
then extends (
In: ctx["inConstraint"] & inferTypeRoot<def, ctx["$"]>
) => ctx["outConstraint"]
export type MatchInvocation<ctx extends MatchInvocationContext> = <
data extends ctx["initialInputs"]
>(
when: validateTypeRoot<def, ctx["$"]>,
then: then
) => ChainableMatchParser<replaceKey<ctx, "thens", [...ctx["thens"], then]>>
export type MatchInvocation<ctx extends MatchContext> = <
data extends ctx["inConstraint"]
>(
data: data
) => {
[i in Extract<keyof ctx["thens"], `${number}`>]: ctx["thens"][i] extends Fn<
[infer In],
infer Out
>
? isDisjoint<data, In> extends true
? never
: Out
: returnOf<ctx["thens"][i]>
}[Extract<keyof ctx["thens"], `${number}`>]
[i in numericStringKeyOf<ctx["thens"]>]: isDisjoint<
data,
Parameters<ctx["thens"][i]>[0]
> extends true
? never
: ReturnType<ctx["thens"][i]>
}[numericStringKeyOf<ctx["thens"]>]
export type ChainableMatchParser<ctx extends MatchContext> =
MatchInvocation<ctx> & {
cases: CaseMatchParser<ctx>
when: WhenMatchParser<ctx>
}
export const createMatchParser = <$>(scope: Scope): MatchParser<$> => {
const matchParser = (isRestricted: boolean) => {
const handledCases: { when: Type; then: Morph }[] = []
let defaultCase: ((x: unknown) => unknown) | null = null
export const createMatchParser = <$>(scope: Scope): MatchParser<$> => {
const parser = (cases: Record<string, Morph>) => {
const caseArray = Object.entries(cases).map(([def, morph]) => ({
when: new Type(def, scope).allows,
then: morph
}))
return (data: unknown) => {
for (const c of caseArray) {
if (c.when(data)) {
return c.then(data, {} as never)
const parser = {
when: (when: unknown, then: Morph) => {
handledCases.push({ when: new Type(when, scope), then })
return parser
},
finalize: () => {
// TODO: exhaustiveness checking
// const branches = handledCases.flatMap(({ when, then }) => {
// if (when.root.kind === "union") {
// return when.root.branches.map((branch) => ({
// in: branch,
// morph: then
// }))
// }
// if (when.root.kind === "morph") {
// return [{ in: when, morph: [when.root.morph, then] }]
// }
// return [{ in: when.root, morph: then }]
// })
// if (defaultCase) {
// branches.push({ in: new Type("unknown", scope), morph: defaultCase })
// }
// const matchers = schema.union({
// branches,
// ordered: true
// })
// return (data: unknown) => {
// const result = matchers.apply(data)
// if (result.errors) {
// throw result.errors
// }
// return result.out
// }
},
orThrow: () => {
// implicitly finalize, we don't need to do anything else because we throw either way
return parser.finalize()
},
default: (x: unknown) => {
if (x instanceof Function) {
defaultCase = x as never
} else {
defaultCase = () => x
}
return parser.finalize()
}
}
return parser
}
return parser as never
return Object.assign(() => matchParser(false), {
only: () => matchParser(true)
}) as never
}

@@ -1,48 +0,77 @@

import type { Fn, isDisjoint, replaceKey, returnOf, unionToTuple, valueOf } from "@arktype/util";
import type { AnonymousRefinementKey, distill, intersectConstrainables, is } from "@arktype/schema";
import type { ErrorMessage, UnknownUnion, isDisjoint, numericStringKeyOf, replaceKey, unionToTuple, valueOf } from "@arktype/util";
import type { Scope } from "./scope.js";
import { type inferTypeRoot, type validateTypeRoot } from "./type.js";
type MatchContext = {
inConstraint: unknown;
outConstraint: unknown;
thens: readonly Fn[];
type MatchParserContext = {
thens: readonly ((In: unknown) => unknown)[];
$: unknown;
exhaustiveOver: unknown;
};
type validateCases<cases, ctx extends MatchContext> = {
[k in keyof cases | keyof ctx["$"] | "default"]?: k extends "default" ? (In: ctx["inConstraint"]) => ctx["outConstraint"] : k extends validateTypeRoot<k, ctx["$"]> ? (In: ctx["inConstraint"] & inferTypeRoot<k, ctx["$"]>) => ctx["outConstraint"] : validateTypeRoot<k, ctx["$"]>;
};
type errorCases<cases, ctx extends MatchContext> = {
[k in keyof cases]?: k extends "default" ? (In: ctx["inConstraint"]) => ctx["outConstraint"] : k extends validateTypeRoot<k, ctx["$"]> ? (In: ctx["inConstraint"] & inferTypeRoot<k, ctx["$"]>) => ctx["outConstraint"] : validateTypeRoot<k, ctx["$"]>;
} & {
[k in Exclude<keyof ctx["$"], keyof cases>]?: (In: ctx["inConstraint"] & ctx["$"][k]) => ctx["outConstraint"];
} & {
default?: (In: ctx["inConstraint"]) => ctx["outConstraint"];
};
export type CaseMatchParser<ctx extends MatchContext> = {
<cases>(def: cases extends validateCases<cases, ctx> ? cases : errorCases<cases, ctx>): ChainableMatchParser<replaceKey<ctx, "thens", [...ctx["thens"], ...unionToTuple<valueOf<cases>>]>>;
};
export type MatchParser<$> = CaseMatchParser<{
inConstraint: unknown;
outConstraint: unknown;
thens: [];
exhaustiveOver: unknown;
$: $;
}> & {
<In = unknown, Out = unknown>(): ChainableMatchParser<{
inConstraint: In;
outConstraint: Out;
(): ChainableMatchParser<{
thens: [];
exhaustiveOver: unknown;
$: $;
}>;
only: <inputs>() => ChainableMatchParser<{
thens: [];
exhaustiveOver: inputs;
$: $;
}>;
};
export type WhenMatchParser<ctx extends MatchContext> = <def, then extends (In: ctx["inConstraint"] & inferTypeRoot<def, ctx["$"]>) => ctx["outConstraint"]>(when: validateTypeRoot<def, ctx["$"]>, then: then) => ChainableMatchParser<replaceKey<ctx, "thens", [...ctx["thens"], then]>>;
export type MatchInvocation<ctx extends MatchContext> = <data extends ctx["inConstraint"]>(data: data) => {
[i in Extract<keyof ctx["thens"], `${number}`>]: ctx["thens"][i] extends Fn<[
infer In
], infer Out> ? isDisjoint<data, In> extends true ? never : Out : returnOf<ctx["thens"][i]>;
}[Extract<keyof ctx["thens"], `${number}`>];
export type ChainableMatchParser<ctx extends MatchContext> = MatchInvocation<ctx> & {
type matcherInputs<ctx extends MatchParserContext> = Parameters<ctx["thens"][number]>[0];
type AnonymouslyRefined = {
[k in AnonymousRefinementKey]: {
[_ in k]: true;
};
}[AnonymousRefinementKey];
type getHandledBranches<ctx extends MatchParserContext> = Exclude<matcherInputs<ctx>, is<unknown, AnonymouslyRefined>>;
type getUnhandledBranches<ctx extends MatchParserContext> = Exclude<unknown extends ctx["exhaustiveOver"] ? UnknownUnion : ctx["exhaustiveOver"], getHandledBranches<ctx>>;
type addBranches<ctx extends MatchParserContext, branches extends unknown[]> = replaceKey<ctx, "thens", [...ctx["thens"], ...branches]>;
type validateWhenDefinition<def, ctx extends MatchParserContext> = def extends validateTypeRoot<def, ctx["$"]> ? inferMatchBranch<def, ctx> extends getHandledBranches<ctx> ? ErrorMessage<"This branch is redundant and will never be reached"> : def : validateTypeRoot<def, ctx["$"]>;
type inferMatchBranch<def, ctx extends MatchParserContext> = intersectConstrainables<getUnhandledBranches<ctx>, inferTypeRoot<def, ctx["$"]>>;
export type ChainableMatchParser<ctx extends MatchParserContext> = {
when: <def, ret>(when: validateWhenDefinition<def, ctx>, then: (In: distill<inferMatchBranch<def, ctx>>) => ret) => ChainableMatchParser<addBranches<ctx, [(In: inferMatchBranch<def, ctx>) => ret]>>;
cases: CaseMatchParser<ctx>;
when: WhenMatchParser<ctx>;
orThrow: () => finalizeMatchParser<addBranches<ctx, [(In: getHandledBranches<ctx>) => never]>>;
default: MatchParserDefaultInvocation<ctx>;
finalize: (this: getUnhandledBranches<ctx> extends never ? ChainableMatchParser<ctx> : ErrorMessage<"Cannot manually finalize a non-exhaustive matcher: consider adding a `.default` case, using one of the `.orX` methods, or using `match.only<T>`">) => finalizeMatchParser<ctx>;
};
type MatchParserDefaultInvocation<ctx extends MatchParserContext> = {
<f extends (In: getUnhandledBranches<ctx>) => unknown>(f: f): finalizeWithDefault<ctx, ReturnType<f>>;
<const value>(value: value): finalizeWithDefault<ctx, value>;
};
type validateCases<cases, ctx extends MatchParserContext> = {
[def in keyof cases | keyof ctx["$"] | "default"]?: def extends "default" ? (In: distill<getUnhandledBranches<ctx>>) => unknown : def extends validateWhenDefinition<def, ctx> ? (In: distill<inferMatchBranch<def, ctx>>) => unknown : validateWhenDefinition<def, ctx>;
};
type errorCases<cases, ctx extends MatchParserContext> = {
[def in keyof cases]?: def extends "default" ? (In: distill<getUnhandledBranches<ctx>>) => unknown : def extends validateWhenDefinition<def, ctx> ? (In: distill<inferMatchBranch<def, ctx>>) => unknown : validateWhenDefinition<def, ctx>;
} & {
[k in Exclude<keyof ctx["$"], keyof cases>]?: (In: distill<intersectConstrainables<getUnhandledBranches<ctx>, ctx["$"][k]>>) => unknown;
} & {
default?: (In: distill<getUnhandledBranches<ctx>>) => unknown;
};
export type CaseMatchParser<ctx extends MatchParserContext> = {
<cases>(def: cases extends validateCases<cases, ctx> ? cases : errorCases<cases, ctx>): cases extends {
default: (...args: never[]) => infer defaultReturn;
} ? finalizeWithDefault<addBranches<ctx, unionToTuple<cases[Exclude<keyof cases, "default">]>>, defaultReturn> : ChainableMatchParser<addBranches<ctx, unionToTuple<valueOf<cases>>>>;
};
type finalizeWithDefault<ctx extends MatchParserContext, defaultReturn> = finalizeMatchParser<addBranches<ctx, [(_: getUnhandledBranches<ctx>) => defaultReturn]>>;
type finalizeMatchParser<ctx extends MatchParserContext> = MatchInvocation<{
thens: ctx["thens"];
initialInputs: ctx["exhaustiveOver"];
}>;
type MatchInvocationContext = {
thens: readonly ((...args: never[]) => unknown)[];
initialInputs: unknown;
};
export type MatchInvocation<ctx extends MatchInvocationContext> = <data extends ctx["initialInputs"]>(data: data) => {
[i in numericStringKeyOf<ctx["thens"]>]: isDisjoint<data, Parameters<ctx["thens"][i]>[0]> extends true ? never : ReturnType<ctx["thens"][i]>;
}[numericStringKeyOf<ctx["thens"]>];
export declare const createMatchParser: <$>(scope: Scope) => MatchParser<$>;
export {};
//# sourceMappingURL=match.d.ts.map
import { Type } from "./type.js";
export const createMatchParser = (scope) => {
const parser = (cases) => {
const caseArray = Object.entries(cases).map(([def, morph]) => ({
when: new Type(def, scope).allows,
then: morph
}));
return (data) => {
for (const c of caseArray) {
if (c.when(data)) {
return c.then(data, {});
const matchParser = (isRestricted) => {
const handledCases = [];
let defaultCase = null;
const parser = {
when: (when, then) => {
handledCases.push({ when: new Type(when, scope), then });
return parser;
},
finalize: () => {
// TODO: exhaustiveness checking
// const branches = handledCases.flatMap(({ when, then }) => {
// if (when.root.kind === "union") {
// return when.root.branches.map((branch) => ({
// in: branch,
// morph: then
// }))
// }
// if (when.root.kind === "morph") {
// return [{ in: when, morph: [when.root.morph, then] }]
// }
// return [{ in: when.root, morph: then }]
// })
// if (defaultCase) {
// branches.push({ in: new Type("unknown", scope), morph: defaultCase })
// }
// const matchers = schema.union({
// branches,
// ordered: true
// })
// return (data: unknown) => {
// const result = matchers.apply(data)
// if (result.errors) {
// throw result.errors
// }
// return result.out
// }
},
orThrow: () => {
// implicitly finalize, we don't need to do anything else because we throw either way
return parser.finalize();
},
default: (x) => {
if (x instanceof Function) {
defaultCase = x;
}
else {
defaultCase = () => x;
}
return parser.finalize();
}
};
return parser;
};
return parser;
return Object.assign(() => matchParser(false), {
only: () => matchParser(true)
});
};
//# sourceMappingURL=match.js.map

@@ -1,3 +0,3 @@

import { type TypeNode } from "@arktype/schema";
import { type defined, type equals, type ErrorMessage, type evaluate, type isAny, type isUnknown, type objectKindOrDomainOf, type optionalKeyOf, type Primitive, type requiredKeyOf } from "@arktype/util";
import { type TypeNode, type is } from "@arktype/schema";
import { type ErrorMessage, type Primitive, type defined, type equals, type evaluate, type isAny, type isUnknown, type objectKindOrDomainOf, type optionalKeyOf, type requiredKeyOf } from "@arktype/util";
import type { type } from "../ark.js";

@@ -8,5 +8,7 @@ import type { ParseContext } from "../scope.js";

import type { BaseCompletions, inferString } from "./string/string.js";
import { type inferTuple, type TupleExpression, type validateTuple } from "./tuple.js";
import { type TupleExpression, type inferTuple, type validateTuple } from "./tuple.js";
export declare const parseObject: (def: object, ctx: ParseContext) => TypeNode;
export type inferDefinition<def, $, args> = isAny<def> extends true ? never : def extends type.cast<infer t> | ThunkCast<infer t> ? t : def extends string ? inferString<def, $, args> : def extends readonly unknown[] ? inferTuple<def, $, args> : def extends RegExp ? string : def extends object ? inferObjectLiteral<def, $, args> : never;
export type inferDefinition<def, $, args> = isAny<def> extends true ? never : def extends type.cast<infer t> | ThunkCast<infer t> ? t : def extends string ? inferString<def, $, args> : def extends readonly unknown[] ? inferTuple<def, $, args> : def extends RegExp ? is<string, {
anonymousPattern: true;
}> : def extends object ? inferObjectLiteral<def, $, args> : never;
export type validateDefinition<def, $, args> = null extends undefined ? ErrorMessage<`'strict' or 'strictNullChecks' must be set to true in your tsconfig's 'compilerOptions'`> : [def] extends [Terminal] ? unknown extends def ? never : def : def extends string ? validateString<def, $, args> : def extends readonly unknown[] ? validateTuple<def, $, args> : def extends BadDefinitionType ? writeBadDefinitionTypeMessage<objectKindOrDomainOf<def>> : isUnknown<def> extends true ? // this allows the initial list of autocompletions to be populated when a user writes "type()",

@@ -13,0 +15,0 @@ BaseCompletions<$, args> | {} : validateObjectLiteral<def, $, args>;

@@ -33,4 +33,4 @@ import { keywords, schema } from "@arktype/schema";

// because the currently parsed definition will overwrite them.
const requiredEntriesFromSpread = (spreadNode.required ?? []).filter((e) => !entries.some(([k]) => k === e.key));
const optionalEntriesFromSpread = (spreadNode.optional ?? []).filter((e) => !entries.some(([k]) => k === e.key));
const requiredEntriesFromSpread = (spreadNode.props?.required ?? []).filter((e) => !entries.some(([k]) => k === e.key));
const optionalEntriesFromSpread = (spreadNode.props?.optional ?? []).filter((e) => !entries.some(([k]) => k === e.key));
required.push(...requiredEntriesFromSpread);

@@ -37,0 +37,0 @@ optional.push(...optionalEntriesFromSpread);

import type { DateLiteral, Refinements, RegexLiteral, distill, is } from "@arktype/schema";
import type { BigintLiteral, List, NumberLiteral, evaluate, extend } from "@arktype/util";
import type { BigintLiteral, List, NumberLiteral, evaluate } from "@arktype/util";
import type { UnparsedScope, resolve, tryInferSubmoduleReference } from "../../scope.js";

@@ -28,7 +28,7 @@ import type { GenericProps } from "../../type.js";

export type InfixExpression<operator extends InfixOperator = InfixOperator, l = unknown, r = unknown> = [l, operator, r];
export type inferTerminal<token, $, args, refinements extends Refinements> = token extends keyof args | keyof $ ? {} extends refinements ? resolve<token, $, args> : is<resolve<token, $, args>, evaluate<refinements>> : token extends StringLiteral<infer text> ? text : token extends RegexLiteral ? is<string, extend<refinements, {
export type inferTerminal<token, $, args, refinements extends Refinements> = token extends keyof args | keyof $ ? {} extends refinements ? resolve<token, $, args> : is<resolve<token, $, args>, evaluate<refinements>> : token extends StringLiteral<infer text> ? text : token extends RegexLiteral ? is<string, evaluate<refinements & {
[_ in token]: true;
}>> : token extends DateLiteral ? is<Date, extend<refinements, {
}>> : token extends DateLiteral ? is<Date, evaluate<refinements & {
[_ in token]: true;
}>> : token extends NumberLiteral<infer value> ? value : token extends BigintLiteral<infer value> ? value : tryInferSubmoduleReference<$, token>;
//# sourceMappingURL=semantic.d.ts.map

@@ -1,2 +0,3 @@

import type { extend } from "@arktype/util";
import type { BaseMeta, Node } from "@arktype/schema";
import type { evaluate } from "@arktype/util";
import type { validateDefinition } from "./definition.js";

@@ -10,3 +11,3 @@ import { Scanner } from "./string/shift/scanner.js";

};
export type EntryParseResult<kind extends ParsedKeyKind = ParsedKeyKind> = extend<KeyParseResult<kind>, {
export type EntryParseResult<kind extends ParsedKeyKind = ParsedKeyKind> = evaluate<KeyParseResult<kind> & {
innerValue: unknown;

@@ -54,3 +55,4 @@ }>;

}>;
export declare const configureShallowDescendants: <node extends Node>(node: node, configOrDescription: BaseMeta | string) => node;
export {};
//# sourceMappingURL=shared.d.ts.map

@@ -47,2 +47,10 @@ import { Scanner } from "./string/shift/scanner.js";

};
export const configureShallowDescendants = (node, configOrDescription) => {
const config = typeof configOrDescription === "string"
? { description: configOrDescription }
: configOrDescription;
return node.transform((kind, inner) => ({ ...inner, ...config }),
// TODO: change to props
(node) => node.kind !== "required" && node.kind !== "optional");
};
//# sourceMappingURL=shared.js.map

@@ -12,3 +12,2 @@ import type { Completion, defined, ErrorMessage } from "@arktype/util";

};
export type AutocompletePrefix = `${StringifiablePrefixOperator} `;
type BranchState = {

@@ -20,2 +19,3 @@ prefixes: StringifiablePrefixOperator[];

};
export type AutocompletePrefix = `${StringifiablePrefixOperator} `;
export declare namespace state {

@@ -22,0 +22,0 @@ export type initialize<def extends string> = from<{

@@ -26,4 +26,4 @@ import { type BaseMeta, type Morph, type Out, type Predicate, type TypeNode, type extractIn, type extractOut, type inferMorphOut, type inferNarrow } from "@arktype/schema";

type writeNonArrayRestMessage<operand> = `Rest element ${operand extends string ? `'${operand}'` : ""} must be an array`;
export declare const prematureRestMessage = "Rest elements are only allowed at the end of a tuple";
type prematureRestMessage = typeof prematureRestMessage;
export declare const multipleVariadicMesage = "A tuple may have at most one variadic element";
type prematureRestMessage = typeof multipleVariadicMesage;
type inferTupleLiteral<def extends List, $, args, result extends unknown[] = []> = def extends readonly [infer head, ...infer tail] ? parseEntry<result["length"], head extends variadicExpression<infer operand> ? operand : head> extends infer entryParseResult extends EntryParseResult ? inferDefinition<entryParseResult["innerValue"], $, args> extends infer element ? head extends variadicExpression ? element extends readonly unknown[] ? inferTupleLiteral<tail, $, args, [...result, ...element]> : never : inferTupleLiteral<tail, $, args, entryParseResult["kind"] extends "optional" ? [...result, element?] : [...result, element]> : never : never : result;

@@ -30,0 +30,0 @@ type variadicExpression<operandDef = unknown> = variadicStringExpression<operandDef & string> | variadicTupleExpression<operandDef>;

import { keywords, schema } from "@arktype/schema";
import { isArray, objectKindOrDomainOf, printable, throwParseError } from "@arktype/util";
import { writeUnsatisfiableExpressionError } from "./semantic/validate.js";
import { parseEntry } from "./shared.js";
import { configureShallowDescendants, parseEntry } from "./shared.js";
import { writeMissingRightOperandMessage } from "./string/shift/operand/unenclosed.js";

@@ -9,3 +9,3 @@ export const parseTuple = (def, ctx) => maybeParseTupleExpression(def, ctx) ?? parseTupleLiteral(def, ctx);

const props = [];
let isVariadic = false;
let variadicIndex;
for (let i = 0; i < def.length; i++) {

@@ -16,3 +16,6 @@ let elementDef = def[i];

elementDef = elementDef.slice(3);
isVariadic = true;
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage);
}
variadicIndex = i;
}

@@ -23,13 +26,13 @@ else if (isArray(elementDef) &&

elementDef = elementDef[1];
isVariadic = true;
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage);
}
variadicIndex = i;
}
const parsedEntry = parseEntry([`${i}`, elementDef]);
const value = ctx.scope.parse(parsedEntry.innerValue, ctx);
if (isVariadic) {
if (variadicIndex === i) {
if (!value.extends(keywords.Array)) {
return throwParseError(writeNonArrayRestMessage(elementDef));
}
if (i !== def.length - 1) {
return throwParseError(prematureRestMessage);
}
// TODO: Fix builtins.arrayIndexTypeNode()

@@ -52,3 +55,3 @@ const elementType = value.getPath();

}
if (!isVariadic) {
if (variadicIndex === undefined) {
props.push({

@@ -60,7 +63,5 @@ key: {

},
// , ctx
value: schema({ unit: def.length })
});
}
// props , ctx
return schema(Array);

@@ -83,3 +84,3 @@ };

export const writeNonArrayRestMessage = (operand) => `Rest element ${typeof operand === "string" ? `'${operand}'` : ""} must be an array`;
export const prematureRestMessage = `Rest elements are only allowed at the end of a tuple`;
export const multipleVariadicMesage = `A tuple may have at most one variadic element`;
export const parseKeyOfTuple = (def, ctx) => ctx.scope.parse(def[1], ctx).keyof();

@@ -115,5 +116,3 @@ const parseBranchTuple = (def, ctx) => {

};
const parseAttributeTuple = (def, ctx) => {
return ctx.scope.parse(def[0], ctx);
};
const parseAttributeTuple = (def, ctx) => configureShallowDescendants(ctx.scope.parse(def[0], ctx), def[2]);
const indexOneParsers = {

@@ -120,0 +119,0 @@ "|": parseBranchTuple,

@@ -1,2 +0,2 @@

import { type ArkErrorCode, type KeyCheckKind, type TypeNode, type extractIn, type extractOut } from "@arktype/schema";
import { type ArkConfig, type TypeNode, type extractIn, type extractOut } from "@arktype/schema";
import { type Dict, type evaluate, type isAny, type nominal } from "@arktype/util";

@@ -7,3 +7,3 @@ import type { type } from "./ark.js";

import { parseGenericParams, type GenericDeclaration, type GenericParamsParseError } from "./parser/generic.js";
import { Type, type DeclarationParser, type DefinitionParser, type Generic, type GenericProps, type TypeConfig, type TypeParser } from "./type.js";
import { Type, type DeclarationParser, type DefinitionParser, type Generic, type GenericProps, type TypeParser } from "./type.js";
import { type arkKind } from "./util.js";

@@ -25,9 +25,5 @@ export type ScopeParser<parent, ambient> = {

};
export type ScopeConfig = {
export interface ArkScopeConfig extends ArkConfig {
ambient?: Scope | null;
codes?: Record<ArkErrorCode, {
mustBe?: string;
}>;
keys?: KeyCheckKind;
};
}
type validateScope<def, $> = {

@@ -91,3 +87,3 @@ [k in keyof def]: parseScopeKey<k>["params"] extends [] ? def[k] extends Type | PreparsedResolution ? def[k] : validateDefinition<def[k], $ & bootstrap<def>, {}> : parseScopeKey<k>["params"] extends GenericParamsParseError ? parseScopeKey<k>["params"][0] : validateDefinition<def[k], $ & bootstrap<def>, {

inferIn: extractIn<r["exports"]>;
config: TypeConfig;
config: ArkScopeConfig;
private parseCache;

@@ -100,3 +96,3 @@ private resolutions;

private ambient;
constructor(def: Dict, config: ScopeConfig);
constructor(def: Dict, config: ArkScopeConfig);
static root: ScopeParser<{}, {}>;

@@ -123,3 +119,2 @@ type: TypeParser<$<r>>;

import<names extends exportedName<r>[]>(...names: names): destructuredImportContext<r, names extends [] ? keyof r["exports"] & string : names[number]>;
compile(): string;
bindThis(): {

@@ -126,0 +121,0 @@ this: import("@arktype/schema").IntersectionNode<unknown>;

@@ -154,23 +154,2 @@ import { BaseType, keywords } from "@arktype/schema";

}
compile() {
return "";
// this.export()
// const references: Set<Root> = new Set()
// for (const k in this.exportedResolutions!) {
// const resolution = this.exportedResolutions[k]
// if (hasArkKind(resolution, "node") && !references.has(resolution)) {
// for (const reference of resolution.references) {
// references.add(reference)
// }
// }
// }
// return [...references]
// .map(
// (ref) => `const ${ref.alias} = (${In}) => {
// ${ref.condition}
// return true
// }`
// )
// .join("\n")
}
bindThis() {

@@ -177,0 +156,0 @@ // TODO: fix

@@ -1,2 +0,2 @@

import { inferred, type ArkResult, type BaseMeta, type KeyCheckKind, type Morph, type Out, type Predicate, type TypeNode, type distill, type extractIn, type extractOut, type includesMorphs, type inferMorphOut, type inferNarrow } from "@arktype/schema";
import { inferred, type ArkResult, type BaseMeta, type Morph, type Out, type Predicate, type TypeNode, type distill, type extractIn, type extractOut, type includesMorphs, type inferMorphOut, type inferNarrow } from "@arktype/schema";
import { Callable, type Constructor, type Json, type conform } from "@arktype/util";

@@ -23,6 +23,2 @@ import type { inferDefinition, validateDeclared, validateDefinition } from "./parser/definition.js";

export type DefinitionParser<$> = <def>(def: validateDefinition<def, $, bindThis<def>>) => def;
export type TypeConfig = {
keys?: KeyCheckKind;
mustBe?: string;
};
export declare class Type<t = unknown, $ = any> extends Callable<(data: unknown) => ArkResult<distill<extractOut<t>>>> {

@@ -33,9 +29,9 @@ definition: unknown;

infer: distill<extractOut<t>>;
config: TypeConfig;
root: TypeNode<t>;
allows: this["root"]["allows"];
expected: string;
description: string;
json: Json;
constructor(definition: unknown, scope: Scope);
configure(config: TypeConfig): this;
configure(configOrDescription: BaseMeta | string): this;
describe(description: string): this;
from(literal: this["in"]["infer"]): this["in"]["infer"];

@@ -56,2 +52,3 @@ and<def>(def: validateTypeRoot<def, $>): Type<inferIntersection<t, inferTypeRoot<def, $>>, $>;

get out(): Type<extractOut<t>, $>;
toString(): string;
}

@@ -58,0 +55,0 @@ export type validateTypeRoot<def, $> = validateDefinition<def, $, bindThis<def>>;

import { inferred, keywords } from "@arktype/schema";
import { Callable, CastableBase, map } from "@arktype/util";
import { Callable, map } from "@arktype/util";
import { parseGenericParams } from "./parser/generic.js";
import { configureShallowDescendants } from "./parser/shared.js";
import { arkKind } from "./util.js";

@@ -31,6 +32,5 @@ export const createTypeParser = (scope) => {

scope;
config;
root;
allows;
expected;
description;
json;

@@ -44,10 +44,11 @@ constructor(definition, scope) {

this.allows = root.allows;
this.config = scope.config;
this.json = root.json;
this.expected = this.root.description;
this.description = this.root.description;
}
configure(config) {
this.config = { ...this.config, ...config };
return this;
configure(configOrDescription) {
return new Type(configureShallowDescendants(this.root, configOrDescription), this.scope);
}
describe(description) {
return this.configure(description);
}
// TODO: should return out

@@ -103,2 +104,5 @@ from(literal) {

}
toString() {
return this.description;
}
}

@@ -105,0 +109,0 @@ const parseTypeRoot = (def, scope, args) => scope.parseDefinition(def, {

{
"name": "arktype",
"description": "TypeScript's 1:1 validator, optimized from editor to runtime",
"version": "2.0.0-dev.1",
"version": "2.0.0-dev.2",
"license": "MIT",

@@ -37,5 +37,5 @@ "author": {

"dependencies": {
"@arktype/util": "0.0.17",
"@arktype/schema": "0.0.3"
"@arktype/util": "0.0.20",
"@arktype/schema": "0.0.4"
}
}

@@ -1,2 +0,2 @@

import { isNode, schema, type TypeNode } from "@arktype/schema"
import { isNode, schema, type TypeNode, type is } from "@arktype/schema"
import {

@@ -7,13 +7,13 @@ isThunk,

throwParseError,
type Dict,
type ErrorMessage,
type List,
type Primitive,
type defined,
type Dict,
type equals,
type ErrorMessage,
type evaluate,
type isAny,
type isUnknown,
type List,
type objectKindOrDomainOf,
type optionalKeyOf,
type Primitive,
type requiredKeyOf

@@ -33,4 +33,4 @@ } from "@arktype/util"

parseTuple,
type TupleExpression,
type inferTuple,
type TupleExpression,
type validateTuple

@@ -76,3 +76,3 @@ } from "./tuple.js"

: def extends RegExp
? string
? is<string, { anonymousPattern: true }>
: def extends object

@@ -79,0 +79,0 @@ ? inferObjectLiteral<def, $, args>

@@ -65,9 +65,9 @@ import { keywords, schema, type Inner, type TypeNode } from "@arktype/schema"

// because the currently parsed definition will overwrite them.
const requiredEntriesFromSpread = (spreadNode.required ?? []).filter(
(e) => !entries.some(([k]) => k === e.key)
)
const requiredEntriesFromSpread = (
spreadNode.props?.required ?? []
).filter((e) => !entries.some(([k]) => k === e.key))
const optionalEntriesFromSpread = (spreadNode.optional ?? []).filter(
(e) => !entries.some(([k]) => k === e.key)
)
const optionalEntriesFromSpread = (
spreadNode.props?.optional ?? []
).filter((e) => !entries.some(([k]) => k === e.key))

@@ -74,0 +74,0 @@ required.push(...requiredEntriesFromSpread)

@@ -12,4 +12,3 @@ import type {

NumberLiteral,
evaluate,
extend
evaluate
} from "@arktype/util"

@@ -135,5 +134,5 @@ import type {

: token extends RegexLiteral
? is<string, extend<refinements, { [_ in token]: true }>>
? is<string, evaluate<refinements & { [_ in token]: true }>>
: token extends DateLiteral
? is<Date, extend<refinements, { [_ in token]: true }>>
? is<Date, evaluate<refinements & { [_ in token]: true }>>
: token extends NumberLiteral<infer value>

@@ -140,0 +139,0 @@ ? value

@@ -1,2 +0,3 @@

import type { extend } from "@arktype/util"
import type { BaseMeta, Node } from "@arktype/schema"
import type { evaluate } from "@arktype/util"
import type { validateDefinition } from "./definition.js"

@@ -20,5 +21,4 @@ import { Scanner } from "./string/shift/scanner.js"

export type EntryParseResult<kind extends ParsedKeyKind = ParsedKeyKind> =
extend<
KeyParseResult<kind>,
{
evaluate<
KeyParseResult<kind> & {
innerValue: unknown

@@ -154,1 +154,16 @@ }

}>
export const configureShallowDescendants = <node extends Node>(
node: node,
configOrDescription: BaseMeta | string
): node => {
const config: BaseMeta =
typeof configOrDescription === "string"
? { description: configOrDescription }
: (configOrDescription as never)
return node.transform(
(kind, inner) => ({ ...inner, ...config }),
// TODO: change to props
(node) => node.kind !== "required" && node.kind !== "optional"
) as never
}

@@ -27,4 +27,2 @@ import type { Completion, defined, ErrorMessage } from "@arktype/util"

export type AutocompletePrefix = `${StringifiablePrefixOperator} `
type BranchState = {

@@ -37,2 +35,4 @@ prefixes: StringifiablePrefixOperator[]

export type AutocompletePrefix = `${StringifiablePrefixOperator} `
export namespace state {

@@ -39,0 +39,0 @@ export type initialize<def extends string> = from<{

@@ -6,2 +6,3 @@ import {

type Morph,
type MorphChildKind,
type Out,

@@ -11,3 +12,2 @@ type Predicate,

type TypeNode,
type ValidatorKind,
type extractIn,

@@ -37,2 +37,3 @@ type extractOut,

import {
configureShallowDescendants,
parseEntry,

@@ -50,3 +51,3 @@ type EntryParseResult,

const props: unknown[] = []
let isVariadic = false
let variadicIndex: number | undefined
for (let i = 0; i < def.length; i++) {

@@ -57,3 +58,6 @@ let elementDef = def[i]

elementDef = elementDef.slice(3)
isVariadic = true
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage)
}
variadicIndex = i
} else if (

@@ -65,13 +69,13 @@ isArray(elementDef) &&

elementDef = elementDef[1]
isVariadic = true
if (variadicIndex !== undefined) {
return throwParseError(multipleVariadicMesage)
}
variadicIndex = i
}
const parsedEntry = parseEntry([`${i}`, elementDef])
const value = ctx.scope.parse(parsedEntry.innerValue, ctx)
if (isVariadic) {
if (variadicIndex === i) {
if (!value.extends(keywords.Array)) {
return throwParseError(writeNonArrayRestMessage(elementDef))
}
if (i !== def.length - 1) {
return throwParseError(prematureRestMessage)
}
// TODO: Fix builtins.arrayIndexTypeNode()

@@ -93,3 +97,3 @@ const elementType = value.getPath()

}
if (!isVariadic) {
if (variadicIndex === undefined) {
props.push({

@@ -101,7 +105,5 @@ key: {

},
// , ctx
value: schema({ unit: def.length })
})
}
// props , ctx
return schema(Array)

@@ -212,5 +214,5 @@ }

export const prematureRestMessage = `Rest elements are only allowed at the end of a tuple`
export const multipleVariadicMesage = `A tuple may have at most one variadic element`
type prematureRestMessage = typeof prematureRestMessage
type prematureRestMessage = typeof multipleVariadicMesage

@@ -399,3 +401,3 @@ type inferTupleLiteral<

return schema({
in: ctx.scope.parse(def[0], ctx) as Schema<ValidatorKind>,
in: ctx.scope.parse(def[0], ctx) as Schema<MorphChildKind>,
morph: def[2] as Morph

@@ -430,5 +432,4 @@ })

const parseAttributeTuple: PostfixParser<"@"> = (def, ctx) => {
return ctx.scope.parse(def[0], ctx)
}
const parseAttributeTuple: PostfixParser<"@"> = (def, ctx) =>
configureShallowDescendants(ctx.scope.parse(def[0], ctx), def[2] as never)

@@ -435,0 +436,0 @@ const indexOneParsers: {

import {
BaseType,
keywords,
type ArkErrorCode,
type KeyCheckKind,
type ArkConfig,
type TypeNode,

@@ -50,3 +49,2 @@ type extractIn,

type GenericProps,
type TypeConfig,
type TypeParser

@@ -72,6 +70,4 @@ } from "./type.js"

export type ScopeConfig = {
export interface ArkScopeConfig extends ArkConfig {
ambient?: Scope | null
codes?: Record<ArkErrorCode, { mustBe?: string }>
keys?: KeyCheckKind
}

@@ -233,3 +229,3 @@

config: TypeConfig
config: ArkScopeConfig

@@ -245,3 +241,3 @@ private parseCache: Record<string, TypeNode> = {}

constructor(def: Dict, config: ScopeConfig) {
constructor(def: Dict, config: ArkScopeConfig) {
for (const k in def) {

@@ -280,3 +276,3 @@ const parsedKey = parseScopeKey(k)

def: Dict,
config: TypeConfig = {}
config: ArkScopeConfig = {}
) => {

@@ -419,24 +415,2 @@ return new Scope(def, {

compile() {
return ""
// this.export()
// const references: Set<Root> = new Set()
// for (const k in this.exportedResolutions!) {
// const resolution = this.exportedResolutions[k]
// if (hasArkKind(resolution, "node") && !references.has(resolution)) {
// for (const reference of resolution.references) {
// references.add(reference)
// }
// }
// }
// return [...references]
// .map(
// (ref) => `const ${ref.alias} = (${In}) => {
// ${ref.condition}
// return true
// }`
// )
// .join("\n")
}
bindThis() {

@@ -443,0 +417,0 @@ // TODO: fix

@@ -6,3 +6,2 @@ import {

type BaseMeta,
type KeyCheckKind,
type Morph,

@@ -21,3 +20,2 @@ type Out,

Callable,
CastableBase,
map,

@@ -38,2 +36,3 @@ type Constructor,

import type { inferIntersection } from "./parser/semantic/intersections.js"
import { configureShallowDescendants } from "./parser/shared.js"
import type {

@@ -127,7 +126,2 @@ IndexOneOperator,

export type TypeConfig = {
keys?: KeyCheckKind
mustBe?: string
}
export class Type<t = unknown, $ = any> extends Callable<

@@ -140,6 +134,5 @@ (data: unknown) => ArkResult<distill<extractOut<t>>>

config: TypeConfig
root: TypeNode<t>
allows: this["root"]["allows"]
expected: string
description: string
json: Json

@@ -155,12 +148,17 @@

this.allows = root.allows
this.config = scope.config
this.json = root.json
this.expected = this.root.description
this.description = this.root.description
}
configure(config: TypeConfig) {
this.config = { ...this.config, ...config }
return this
configure(configOrDescription: BaseMeta | string): this {
return new Type(
configureShallowDescendants(this.root, configOrDescription),
this.scope
) as never
}
describe(description: string): this {
return this.configure(description)
}
// TODO: should return out

@@ -261,2 +259,6 @@ from(literal: this["in"]["infer"]) {

}
toString() {
return this.description
}
}

@@ -263,0 +265,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc