Skip to content

Commit

Permalink
feat: tuple defaults, remove optional/default meta, remove inferred a…
Browse files Browse the repository at this point in the history
…ttributes (#1228)
  • Loading branch information
ssalbdivad authored Dec 18, 2024
1 parent 519f9ad commit de5bce8
Show file tree
Hide file tree
Showing 207 changed files with 3,426 additions and 4,132 deletions.
6 changes: 5 additions & 1 deletion ark/attest/__tests__/satisfies.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { attest, contextualize } from "@ark/attest"
import { nonOverlappingSatisfiesMessage } from "@ark/attest/internal/assert/chainableAssertions.js"

contextualize(() => {
it("can assert types", () => {
attest({ foo: "bar" }).satisfies({ foo: "string" })

attest(() => {
// @ts-expect-error
attest({ foo: "bar" }).satisfies({ foo: "number" })
}).throws("foo must be a number (was a string)")
})
.throws("foo must be a number (was a string)")
.type.errors(nonOverlappingSatisfiesMessage)
})
})
30 changes: 28 additions & 2 deletions ark/attest/assert/chainableAssertions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { caller } from "@ark/fs"
import { printable, snapshot, type Constructor } from "@ark/util"
import {
printable,
snapshot,
type Constructor,
type ErrorType,
type isDisjoint
} from "@ark/util"
import prettier from "@prettier/sync"
import { type } from "arktype"
import * as assert from "node:assert/strict"
Expand Down Expand Up @@ -322,13 +328,33 @@ type snapProperty<expected, kind extends AssertionKind> = {

export type Unwrapper<expected = unknown> = (opts?: UnwrapOptions) => expected

export const nonOverlappingSatisfiesMessage =
"The type of your actual value and expected satisfies constraint have no overlap"

export type nonOverlappingSatisfiesMessage =
typeof nonOverlappingSatisfiesMessage

type validateExpectedOverlaps<expected, satisfies> =
isDisjoint<expected, satisfies> extends true ?
ErrorType<
nonOverlappingSatisfiesMessage,
{
actual: expected
satisfies: satisfies
}
>
: unknown

export type comparableValueAssertion<expected, kind extends AssertionKind> = {
snap: snapProperty<expected, kind>
equals: (value: expected) => nextAssertions<kind>
instanceOf: (constructor: Constructor) => nextAssertions<kind>
is: (value: expected) => nextAssertions<kind>
completions: CompletionsSnap
satisfies: <def>(def: type.validate<def>) => nextAssertions<kind>
satisfies: <const def>(
def: type.validate<def> &
validateExpectedOverlaps<expected, type.infer.In<def>>
) => nextAssertions<kind>
// This can be used to assert values without type constraints
unknown: Omit<comparableValueAssertion<unknown, kind>, "unknown">
unwrap: Unwrapper<expected>
Expand Down
2 changes: 1 addition & 1 deletion ark/attest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/attest",
"version": "0.30.0",
"version": "0.31.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
11 changes: 9 additions & 2 deletions ark/dark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@
"name": "arkdark",
"displayName": "ArkDark",
"description": "Syntax highlighting, inline errors and theme for ArkType⛵",
"version": "5.12.2",
"version": "5.13.0",
"publisher": "arktypeio",
"type": "module",
"license": "MIT",
"scripts": {
"publishExtension": "pnpx vsce publish"
"publishExtension": "pnpm packageExtension && pnpm publishVsce && pnpm publishOvsx",
"packageExtension": "vsce package --out arkdark.vsix",
"publishVsce": "vsce publish -i arkdark.vsix",
"publishOvsx": "ovsx publish -i arkdark.vsix"
},
"devDependencies": {
"vsce": "2.15.0",
"ovsx": "0.10.1"
},
"files": [
"*.json"
Expand Down
16 changes: 8 additions & 8 deletions ark/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@
"@ark/util": "workspace:*",
"@astrojs/check": "0.9.4",
"@astrojs/react": "3.6.2",
"@astrojs/starlight": "0.28.3",
"@astrojs/ts-plugin": "1.10.3",
"@shikijs/transformers": "1.22.0",
"@shikijs/twoslash": "1.22.0",
"@astrojs/starlight": "0.29.0",
"@astrojs/ts-plugin": "1.10.4",
"@shikijs/transformers": "1.22.2",
"@shikijs/twoslash": "1.22.2",
"arkdark": "workspace:*",
"arktype": "workspace:*",
"astro": "4.16.6",
"astro": "4.16.17",
"astro-og-canvas": "0.5.4",
"canvaskit-wasm": "0.39.1",
"framer-motion": "11.11.9",
"framer-motion": "11.11.17",
"react": "18.3.1",
"react-dom": "18.3.1",
"sharp": "0.33.5",
"shiki": "1.22.0",
"shiki": "1.22.2",
"twoslash": "0.2.12"
},
"devDependencies": {
"@types/react": "18.3.11",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"typescript": "catalog:"
}
Expand Down
5 changes: 0 additions & 5 deletions ark/docs/src/content/docs/intro/adding-constraints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ In other words, **they just work**.
Let's create a new `contact` Type that enforces our example constraints.

```ts
// hover to see the type-level representation
const contact = type({
// many common constraints are available as built-in keywords
email: "string.email",
Expand All @@ -27,10 +26,6 @@ const contact = type({

// if you need the TS type, just infer it out as normal
type Contact = typeof contact.infer
// ---cut-start---
// this empty line prevents the source syntax highlighting from breaking

// ---cut-end---
```
## Compose
Expand Down
36 changes: 7 additions & 29 deletions ark/docs/src/content/docs/objects.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -159,47 +159,25 @@ const myObject = type({
const myObject = type({
defaultableKey: ["boolean", "=", false]
})
```

</SyntaxTab>

<SyntaxTab spread>

```ts
const myObject = type({
defaultableKey: type("boolean", "=", false)
})
```

</SyntaxTab>

</Tabs>

:::caution[Optional and default only work within objects!]
Adding a `optional` or `default` to a `Type` doesn't alter its standalone behavior.

Rather, it adds metadata that changes how it works when referenced from an object or tuple.
:::caution[Optional and default only work within objects and tuples!]
Unlike e.g. `number.array()`, `number.optional()` and `number.default(0)` don't return a new `Type`, but rather a tuple definition like `[Type<number>, "?"]` or `[Type<number>, "=", 0]`.

<details>
<summary>See an example</summary>
This reflects the fact that in ArkType's type system, optionality and defaultability are only meaningful in reference to a property. Attempting to create an optional or defaultable value outside an object like `type("string?")` will result in a `ParseError`.

```ts
const optionalString = type.string.optional()
To create a `Type` accepting `string` or `undefined`, use a union like `type("string | undefined")`.

optionalString.allows(undefined) // false

const objectWithOptionalKey = type({
foo: optionalString
})
To have it transform `undefined` to an empty string, use an explicit morph like:

objectWithOptionalKey.allows({}) // true
```ts
const fallbackString = type("string | undefined").pipe(v => v ?? "")
```

</details>

Prefer the key-embedded syntax (`"optionalKey?":`) where possible.
:::

<a name="properties/index" />
##### index

Expand Down
12 changes: 9 additions & 3 deletions ark/fast-check/__tests__/arktypeFastCheck.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { attest } from "@ark/attest"
import { attest, contextualize } from "@ark/attest"
import { arkToArbitrary } from "@ark/fast-check/internal/arktypeFastCheck.ts"
import { scope, type } from "arktype"
import { type Arbitrary, assert, property } from "fast-check"
import { describe } from "mocha"

describe("Arbitrary Generation", () => {
contextualize(() => {
describe("union", () => {
it("boolean", () => {
const t = type("boolean")
Expand All @@ -17,6 +17,7 @@ describe("Arbitrary Generation", () => {
assertProperty(arbitrary, t)
})
})

describe("number", () => {
it("number", () => {
const t = type("number")
Expand Down Expand Up @@ -66,6 +67,7 @@ describe("Arbitrary Generation", () => {
)
})
})

describe("string", () => {
it("string", () => {
const t = type("string")
Expand Down Expand Up @@ -98,6 +100,7 @@ describe("Arbitrary Generation", () => {
attest(() => arkToArbitrary(t)).throws("Bounded regex is not supported.")
})
})

describe("misc", () => {
it("unknown", () => {
const t = type("unknown")
Expand Down Expand Up @@ -178,6 +181,7 @@ describe("Arbitrary Generation", () => {
assertProperty(arbitrary, t)
})
})

describe("tuple", () => {
it("empty tuple", () => {
const t = type([])
Expand Down Expand Up @@ -216,6 +220,7 @@ describe("Arbitrary Generation", () => {
assertProperty(arbitrary, t)
})
})

describe("object", () => {
it("{}", () => {
const t = type({})
Expand Down Expand Up @@ -263,7 +268,7 @@ describe("Arbitrary Generation", () => {
})
it("multiple index signatures", () => {
const t = type({
"[string?]": "number|string",
"[string]": "number|string",
"[symbol]": "string"
})
const arbitrary = arkToArbitrary(t)
Expand Down Expand Up @@ -314,6 +319,7 @@ describe("Arbitrary Generation", () => {
assertProperty(arbitrary, t)
})
})

describe("proto", () => {
it("Set", () => {
const t = type("Set")
Expand Down
2 changes: 1 addition & 1 deletion ark/fast-check/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/fast-check",
"version": "0.0.2",
"version": "0.0.3",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
2 changes: 1 addition & 1 deletion ark/fs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/fs",
"version": "0.26.0",
"version": "0.27.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HomeLayout } from "fumadocs-ui/layouts/home"
import type { ReactNode } from "react"
import { FloatYourBoat } from "../../components/FloatYourBoat.jsx"
import { baseOptions } from "../layout.config.jsx"
import { FloatYourBoat } from "../../components/FloatYourBoat.tsx"
import { baseOptions } from "../layout.config.tsx"

export type LayoutProps = {
children: ReactNode
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ArkCard, ArkCards } from "../../components/ArkCard"
import { CodeBlock } from "../../components/CodeBlock"
import { Hero } from "../../components/Hero"
import { TsIcon } from "../../components/icons/ts"
import { LinkCard } from "../../components/LinkCard"
import { RuntimeBenchmarksGraph } from "../../components/RuntimeBenchmarksGraph"
import { ArkCard, ArkCards } from "../../components/ArkCard.tsx"
import { CodeBlock } from "../../components/CodeBlock.tsx"
import { Hero } from "../../components/Hero.tsx"
import { TsIcon } from "../../components/icons/ts.tsx"
import { LinkCard } from "../../components/LinkCard.tsx"
import { RuntimeBenchmarksGraph } from "../../components/RuntimeBenchmarksGraph.tsx"

import {
LightbulbIcon,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createFromSource } from "fumadocs-core/search/server"
import { source } from "../../../lib/source.js"
import { source } from "../../../lib/source.ts"
// it should be cached forever
export const revalidate = false
export const { staticGET: GET } = createFromSource(source)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { metadataImage } from "../../../lib/metadata.js"
import { metadataImage } from "../../../lib/metadata.ts"
// import { fromPackageRoot } from "@ark/fs"
import { generateOGImage } from "fumadocs-ui/og"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
DocsTitle
} from "fumadocs-ui/page"
import { notFound } from "next/navigation"
import { source } from "../../../lib/source.js"
import { source } from "../../../lib/source.ts"

export default async (props: { params: Promise<{ slug?: string[] }> }) => {
const params = await props.params
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DocsLayout } from "fumadocs-ui/layouts/docs"
import type { ReactNode } from "react"
import { source } from "../../lib/source.js"
import { baseOptions } from "../layout.config.jsx"
import { source } from "../../lib/source.ts"
import { baseOptions } from "../layout.config.tsx"

export default ({ children }: { children: ReactNode }) => (
<DocsLayout tree={source.pageTree} {...baseOptions}>
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
SiX
} from "@icons-pack/react-simple-icons"
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"
import { ArkTypeLogo } from "../components/icons/arktype-logo"
import { ArkTypeLogo } from "../components/icons/arktype-logo.tsx"

/**
* Shared layout configurations
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
Pre
} from "fumadocs-ui/components/codeblock"
import { getSingletonHighlighter } from "shiki"
import { shikiConfig } from "../lib/shiki.js"
import { shikiConfig } from "../lib/shiki.ts"

const snippetContentsById = {
betterErrors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { motion } from "framer-motion"
import React from "react"
import { BoatIcon } from "./icons/boat.jsx"
import { BoatIcon } from "./icons/boat.tsx"

const BOB_HEIGHT_PX = 2
const BOB_WIDTH_PX = 16
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ArrowRightIcon } from "lucide-react"
import Link from "next/link"
import { PlatformCloud } from "./PlatformCloud.jsx"
import { PlatformCloud } from "./PlatformCloud.tsx"

export const Hero = () => (
<div className="flex justify-between">
Expand Down
File renamed without changes.
Loading

0 comments on commit de5bce8

Please sign in to comment.