Skip to content

Commit

Permalink
fix: disallow scope-level typed configs (#1271)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad authored Jan 24, 2025
1 parent 949e4fa commit b227e45
Show file tree
Hide file tree
Showing 33 changed files with 586 additions and 303 deletions.
2 changes: 1 addition & 1 deletion ark/attest/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ const parseTsVersions = (aliases: TsVersionAliases): TsVersionData[] => {
throw new Error(
`Specified TypeScript version ${alias} does not exist.` +
` It should probably be specified in package.json like:
"typescript-${alias}": "npm:typescript@latest"`
"@ark/attest-ts-${alias}": "npm:typescript@latest"`
)
}
return matching
Expand Down
9 changes: 3 additions & 6 deletions ark/attest/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import { ensureCacheDirs, getConfig, type AttestConfig } from "./config.ts"
import { forTypeScriptVersions } from "./tsVersioning.ts"

export const setup = (options?: Partial<AttestConfig>): typeof teardown => {
if (options) {
const existing =
process.env.ATTEST_CONFIG ? JSON.parse(process.env.ATTEST_CONFIG) : {}
process.env.ATTEST_CONFIG = JSON.stringify(Object.assign(existing, options))
}
const config = getConfig()
const { ...config } = getConfig()
if (options) Object.assign(config, options)
process.env.ATTEST_CONFIG = JSON.stringify(config)
rmSync(config.cacheDir, { recursive: true, force: true })
ensureCacheDirs()
if (config.skipTypes) return teardown
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.40.0",
"version": "0.41.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
13 changes: 6 additions & 7 deletions ark/docs/app/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
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,
MessageCircleWarning,
MessageSquareTextIcon,
RocketIcon,
SearchIcon
} from "lucide-react"
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"

export default () => (
<div className="flex-1 pt-40 container relative pb-20">
Expand Down
15 changes: 8 additions & 7 deletions ark/docs/components/AutoplayDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ export type AutoplayDemoProps = React.DetailedHTMLProps<
React.VideoHTMLAttributes<HTMLVideoElement>,
HTMLVideoElement
> & { src: string }

export const MainAutoplayDemo = () => (
<AutoplayDemo src="https://github.com/user-attachments/assets/eaace5f0-310e-4fc8-9a95-1c0afc6fd110" />
)

export const AutoplayDemo = (props: AutoplayDemoProps) => (
<div style={{ opacity: 0.8 }}>
<div>
<video
autoPlay
loop
controls={true}
playsInline
muted
disablePictureInPicture={true}
style={{
borderRadius: "1rem",
boxShadow: "0 0 10px rgba(0, 0, 0, 0.3)"
}}
{...props}
/>
<p style={{ fontSize: "1rem" }}>
Type-level feedback on keystroke-{" "}
<p>
Type-level feedback with each keystroke-{" "}
<b>no plugins or build steps required</b>.
</p>
</div>
Expand Down
59 changes: 36 additions & 23 deletions ark/docs/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
import { ArrowRightIcon } from "lucide-react"
import Link from "next/link"
import { AutoplayDemo } from "./AutoplayDemo.tsx"
import { MainAutoplayDemo } from "./AutoplayDemo.tsx"
import { PlatformCloud } from "./PlatformCloud.tsx"

export const Hero = () => (
<div className="flex flex-col md:flex-row justify-between">
<div className="absolute top-2 left-0 right-0">
<div className="flex justify-between">
<PlatformCloud main="ts" right="vscode" top="neovim" left="intellij" />
<PlatformCloud main="js" right="chromium" top="node" left="bun" />
<div>
<div className="flex flex-col md:flex-row justify-between">
<div className="absolute top-2 left-0 right-0">
<div className="flex justify-between">
<PlatformCloud
main="ts"
right="vscode"
top="neovim"
left="intellij"
/>
<PlatformCloud main="js" right="chromium" top="node" left="bun" />
</div>
</div>

<div className="relative w-full flex flex-col md:items-start text-center md:text-left">
<h1 className="mb-4 text-3xl md:text-8xl">ArkType</h1>
<p className="text-fd-muted-foreground text-2xl leading-relaxed">
Optimized runtime validation from familiar, type-safe syntax.
</p>

{/* This wrapper grows to fill remaining vertical space, placing the link in the centered area */}
<div className="flex-1 flex items-center justify-center md:justify-start mt-6">
<Link
tabIndex={1}
href="/docs/intro/setup"
className="bg-highlight text-black focus-within:outline focus-within:outline-2 outline-white hover:bg-highlight/80 p-5 rounded-full flex gap-2 text-sm items-center"
>
Set Sail
<ArrowRightIcon />
</Link>
</div>
</div>

<div style={{ padding: "2rem", position: "relative" }}>
<MainAutoplayDemo />
</div>
</div>
<div className="relative w-full flex flex-col items-center text-center md:items-start md:text-left">
<h1 className="mb-4 text-3xl md:text-8xl">ArkType</h1>
<p className="text-fd-muted-foreground text-lg">
Typescript&apos;s 1:1 validator, optimized from editor to runtime
</p>
<Link
tabIndex={1}
href="/docs/intro/setup"
className="bg-highlight text-black focus-within:outline focus-within:outline-2 outline-white hover:bg-highlight/80 p-5 rounded-full w-fit flex gap-2 my-3 text-sm items-center"
>
Set Sail
<ArrowRightIcon />
</Link>
</div>
<div style={{ padding: "2rem", position: "relative" }}>
<AutoplayDemo src="https://github.com/user-attachments/assets/895c439e-fcd3-4420-93fa-299e829fcf26" />
</div>
</div>
)
4 changes: 2 additions & 2 deletions ark/docs/content/docs/blog/2.0.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: Announcing ArkType 2.0
description: 100x faster validation with DX that will blow your mind
---

import { AutoplayDemo } from "../../../components/AutoplayDemo.tsx"
import { MainAutoplayDemo } from "../../../components/AutoplayDemo.tsx"
import { CodeBlock } from "../../../components/CodeBlock.tsx"
import { RuntimeBenchmarksGraph } from "../../../components/RuntimeBenchmarksGraph.tsx"

Expand All @@ -13,7 +13,7 @@ ArkType 2.0 brings types to runtime JS in a way that, until today, has been a pi

Whether you're a first-time TypeScript dev trying to validate a form or a library author introspecting relationships, ArkType offers fundamentally better tools for navigating the perils of JavaScript.

<AutoplayDemo src="https://github.com/user-attachments/assets/895c439e-fcd3-4420-93fa-299e829fcf26" />
<MainAutoplayDemo />

### Unparalleled DX

Expand Down
24 changes: 14 additions & 10 deletions ark/docs/content/docs/configuration/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,22 @@ type.number.allows(Number.NaN)
<td>all Types parsed in the configured Scope</td>
<td>
```ts
const closedObjectScope = scope(
{ user: { name: "string" } },
{ onUndeclaredKey: "reject" }
const myScope = scope(
{ user: { age: "number < 100" } },
{
max: {
actual: () => "unacceptably large"
}
}
)
const types = closedObjectScope.export()
// ArkErrors: age must be removed
types.user({ name: "Alice", age: 99 })
const parsedAfter = closedObjectScope.type({
name: "string"
const types = myScope.export()
// ArkErrors: age must be less than 100 (was unacceptably large)
types.user({ name: "Alice", age: 101 })
const parsedAfter = myScope.type({
age: "number <= 100"
})
// ArkErrors: age must be removed
parsedAfter({ name: "Alice", age: 99 })
// ArkErrors: age must be at most 100 (was unacceptably large)
parsedAfter({ age: 101 })
```
</td>
</tr>
Expand Down
29 changes: 23 additions & 6 deletions ark/docs/content/docs/scopes/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,33 @@ const coolScope = scope({

`coolScope` is an object with reusable methods like `type` and `generic`. You can use it to create additional `Type`s that can reference your **aliases**- `id`, `user` and `usersById`.

<Callout type="warn" title="Don't wrap your scoped definitions in type!">

Even if you reference it as part of your scope definition, the global 'type' parser only knows about builtin keywords.

```ts
// @errors: 2322
const badScope = scope({
id: "string",
// ❌ wrapping this definition in `type` will fail
badEntity: type({
id: "id"
}),
// ✅ reference scoped definitions directly instead of wrapping them
goodEntity: {
id: "id"
}
})
```

If you need access to fluent syntax from within a Scope, see [thunks](/docs/scopes#thunks).

</Callout>

```ts
const coolScope = scope({
// keywords are still available in your scope
id: "string",
// but you can also reference your own aliases directly!
user: { id: "id", friends: "id[]" },
// your aliases will be autocompleted and validated alongside ArkType's keywords
usersById: {
"[id]": "user | undefined"
}
Expand All @@ -61,11 +81,8 @@ To use the scoped types directly, you must `.export()` your `Scope` to a `Module

```ts
const coolScope = scope({
// keywords are still available in your scope
id: "string",
// but you can also reference your own aliases directly!
user: { id: "id", friends: "id[]" },
// your aliases will be autocompleted and validated alongside ArkType's keywords
usersById: {
"[id]": "user | undefined"
}
Expand Down
2 changes: 1 addition & 1 deletion ark/docs/lib/shiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export const shikiConfig = {
}
},
twoslash,
transformerNotationErrorLevel()
transformerNotationErrorLevel({ matchAlgorithm: "v3" })
]
} as const satisfies RehypeCodeOptions

Expand Down
24 changes: 12 additions & 12 deletions ark/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,30 @@
"@ark/fs": "workspace:*",
"@ark/util": "workspace:*",
"@fumadocs/cli": "0.0.7",
"@icons-pack/react-simple-icons": "11.0.1",
"@shikijs/transformers": "1.27.2",
"@icons-pack/react-simple-icons": "11.1.0",
"@shikijs/transformers": "2.1.0",
"@types/mdx": "2.0.13",
"@types/react": "19.0.7",
"@types/react": "19.0.8",
"@types/react-dom": "19.0.3",
"arkdark": "workspace:*",
"arktype": "workspace:*",
"autoprefixer": "10.4.20",
"class-variance-authority": "0.7.1",
"framer-motion": "11.18.0",
"fumadocs-core": "14.7.4",
"framer-motion": "12.0.3",
"fumadocs-core": "14.7.7",
"fumadocs-mdx": "11.3.1",
"fumadocs-twoslash": "2.0.2",
"fumadocs-ui": "14.7.4",
"fumadocs-twoslash": "2.0.3",
"fumadocs-ui": "14.7.7",
"hast-util-to-jsx-runtime": "2.3.2",
"linkinator": "6.1.2",
"lucide-react": "0.471.1",
"next": "15.1.4",
"lucide-react": "0.474.0",
"next": "15.1.6",
"postcss": "8.5.1",
"posthog-js": "^1.207.0",
"prettier-plugin-tailwindcss": "0.6.10",
"posthog-js": "1.209.0",
"prettier-plugin-tailwindcss": "0.6.11",
"react": "19.0.0",
"react-dom": "19.0.0",
"shiki": "1.27.2",
"shiki": "2.1.0",
"tailwindcss": "3.4.17",
"ts-morph": "25.0.0",
"twoslash": "0.2.12",
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.9",
"version": "0.0.10",
"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.36.0",
"version": "0.37.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
4 changes: 2 additions & 2 deletions ark/repo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"dependencies": {
"@ark/attest": "workspace:*",
"@ark/fs": "workspace:*",
"@sinclair/typebox": "0.34.13",
"@sinclair/typebox": "0.34.14",
"@trpc/server": "10.45.2",
"arktype": "workspace:*",
"ts-morph": "25.0.0",
"type-fest": "4.32.0",
"type-fest": "4.33.0",
"typescript": "catalog:",
"zod": "3.24.1"
},
Expand Down
22 changes: 16 additions & 6 deletions ark/repo/scratch.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { type } from "arktype"
import { scope, type } from "arktype"

const user = type({
name: "string",
data: "number | (bigint | string)[]"
const myScope = scope(
{ user: { age: "number < 100" } },
{
max: {
actual: () => "unacceptably large"
}
}
)
const types = myScope.export()
// ArkErrors: age must be less than 100 (was unacceptably large)
types.user({ name: "Alice", age: 101 })
const parsedAfter = myScope.type({
age: "number <= 100"
})

export type User = typeof user.infer
// ArkErrors: age must be at most 100 (was unacceptably large)
parsedAfter({ age: 101 })
Loading

0 comments on commit b227e45

Please sign in to comment.