Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions ark/attest/__tests__/unwrap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { attest, contextualize } from "@ark/attest"

contextualize(() => {
it("unwraps unversioned", () => {
attest(attest({ foo: "bar" }).unwrap()).equals({
foo: "bar"
})
})

it("unwraps serialized", () => {
attest(
attest({ foo: Symbol("unwrappedSymbol") }).unwrap({ serialize: true })
).snap({ foo: "Symbol(unwrappedSymbol)" })
})
})
12 changes: 9 additions & 3 deletions ark/attest/assert/attest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
type VersionedTypeAssertion
} from "../cache/getCachedAssertions.ts"
import { getConfig, type AttestConfig } from "../config.ts"
import { assertEquals, typeEqualityMapping } from "./assertions.ts"
import {
assertEquals,
typeEqualityMapping,
type TypeAssertionMapping
} from "./assertions.ts"
import {
ChainableAssertions,
type AssertionKind,
Expand All @@ -29,8 +33,10 @@ export type AttestFn = {
instantiations: (count?: Measure<"instantiations"> | undefined) => void
}

export type VersionableActual = {} | null | undefined | TypeAssertionMapping

export type AssertionContext = {
actual: unknown
versionableActual: VersionableActual
originalAssertedValue: unknown
cfg: AttestConfig
allowRegex: boolean
Expand All @@ -54,7 +60,7 @@ export const attestInternal = (
const position = caller()
const cfg = { ...getConfig(), ...cfgHooks }
const ctx: AssertionContext = {
actual: value,
versionableActual: value,
allowRegex: false,
originalAssertedValue: value,
position,
Expand Down
61 changes: 38 additions & 23 deletions ark/attest/assert/chainableAssertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
getThrownMessage,
throwAssertionError
} from "./assertions.ts"
import type { AssertionContext } from "./attest.ts"
import type { AssertionContext, VersionableActual } from "./attest.ts"

export type ChainableAssertionOptions = {
allowRegex?: boolean
Expand All @@ -31,29 +31,40 @@ export type ChainableAssertionOptions = {

type AssertionRecord = Record<keyof rootAssertions<any, AssertionKind>, unknown>

export type UnwrapOptions = {
versionable?: boolean
serialize?: boolean
}

export class ChainableAssertions implements AssertionRecord {
private ctx: AssertionContext

constructor(ctx: AssertionContext) {
this.ctx = ctx
}

private serialize(value: unknown) {
return snapshot(value)
}

private get unversionedActual() {
if (this.ctx.actual instanceof TypeAssertionMapping) {
return this.ctx.actual.fn(
private get unversionedActual(): unknown {
if (this.versionableActual instanceof TypeAssertionMapping) {
return this.versionableActual.fn(
this.ctx.typeRelationshipAssertionEntries![0][1],
this.ctx
)!.actual
}
return this.ctx.actual
return this.versionableActual
}

private get versionableActual(): VersionableActual {
return this.ctx.versionableActual
}

private get serializedActual(): unknown {
return snapshot(this.unversionedActual)
}

private get serializedActual() {
return this.serialize(this.unversionedActual)
unwrap(opts?: UnwrapOptions): unknown {
const value =
opts?.versionable ? this.versionableActual : this.unversionedActual
return opts?.serialize ? snapshot(value) : value
}

private snapRequiresUpdate(expectedSerialized: unknown) {
Expand All @@ -75,22 +86,25 @@ export class ChainableAssertions implements AssertionRecord {
}

equals(expected: unknown): this {
assertEquals(expected, this.ctx.actual, this.ctx)
assertEquals(expected, this.versionableActual, this.ctx)
return this
}

satisfies(def: unknown): this {
assertSatisfies(type.raw(def), this.ctx.actual, this.ctx)
assertSatisfies(type.raw(def), this.versionableActual, this.ctx)
return this
}

instanceOf(expected: Constructor): this {
if (!(this.ctx.actual instanceof expected)) {
if (!(this.versionableActual instanceof expected)) {
throwAssertionError({
stack: this.ctx.assertionStack,
message: `Expected an instance of ${expected.name} (was ${
typeof this.ctx.actual === "object" && this.ctx.actual !== null ?
this.ctx.actual.constructor.name
(
typeof this.versionableActual === "object" &&
this.versionableActual !== null
) ?
this.versionableActual.constructor.name
: this.serializedActual
})`
})
Expand All @@ -102,7 +116,7 @@ export class ChainableAssertions implements AssertionRecord {
// Use variadic args to distinguish undefined being passed explicitly from no args
const inline = (...args: unknown[]) => {
const snapName = this.ctx.lastSnapName ?? "snap"
const expectedSerialized = this.serialize(args[0])
const expectedSerialized = snapshot(args[0])
if (!args.length || this.ctx.cfg.updateSnapshots) {
if (this.snapRequiresUpdate(expectedSerialized)) {
const snapshotArgs: SnapshotArgs = {
Expand Down Expand Up @@ -157,8 +171,8 @@ export class ChainableAssertions implements AssertionRecord {
}
}
if (this.ctx.allowRegex)
assertEqualOrMatching(expected, this.ctx.actual, this.ctx)
else assertEquals(expected, this.ctx.actual, this.ctx)
assertEqualOrMatching(expected, this.versionableActual, this.ctx)
else assertEquals(expected, this.versionableActual, this.ctx)

return this
}
Expand All @@ -169,7 +183,7 @@ export class ChainableAssertions implements AssertionRecord {

get throws(): unknown {
const result = callAssertedFunction(this.unversionedActual as Function)
this.ctx.actual = getThrownMessage(result, this.ctx)
this.ctx.versionableActual = getThrownMessage(result, this.ctx)
this.ctx.allowRegex = true
this.ctx.defaultExpected = ""
return this.immediateOrChained()
Expand Down Expand Up @@ -198,7 +212,7 @@ export class ChainableAssertions implements AssertionRecord {
get completions(): any {
if (this.ctx.cfg.skipTypes) return chainableNoOpProxy

this.ctx.actual = new TypeAssertionMapping(data => {
this.ctx.versionableActual = new TypeAssertionMapping(data => {
if (typeof data.completions === "string") {
// if the completions were ambiguously defined, e.g. two string
// literals with the same value, they are writen as an error
Expand All @@ -218,14 +232,14 @@ export class ChainableAssertions implements AssertionRecord {
const self = this
return {
get toString() {
self.ctx.actual = new TypeAssertionMapping(data => ({
self.ctx.versionableActual = new TypeAssertionMapping(data => ({
actual: formatTypeString(data.args[0].type)
}))
self.ctx.allowRegex = true
return self.immediateOrChained()
},
get errors() {
self.ctx.actual = new TypeAssertionMapping(data => ({
self.ctx.versionableActual = new TypeAssertionMapping(data => ({
actual: data.errors.join("\n")
}))
self.ctx.allowRegex = true
Expand Down Expand Up @@ -311,6 +325,7 @@ export type comparableValueAssertion<expected, kind extends AssertionKind> = {
satisfies: <def>(def: type.validate<def>) => nextAssertions<kind>
// This can be used to assert values without type constraints
unknown: Omit<comparableValueAssertion<unknown, kind>, "unknown">
unwrap: (opts?: UnwrapOptions) => unknown
}

export type TypeAssertionsRoot = {
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.26.0",
"version": "0.27.0",
"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.22.0",
"version": "0.23.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
7 changes: 3 additions & 4 deletions ark/repo/ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const versionedFlags =
),
"--import tsx")

execSync(
`node --conditions ark-ts ${versionedFlags} ${process.argv.slice(2).join(" ")}`,
{ stdio: "inherit" }
)
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ?? ""} --conditions ark-ts ${versionedFlags}`

execSync(`node ${process.argv.slice(2).join(" ")}`, { stdio: "inherit" })
2 changes: 1 addition & 1 deletion ark/schema/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ark/schema",
"version": "0.22.0",
"version": "0.23.0",
"license": "MIT",
"author": {
"name": "David Blass",
Expand Down
29 changes: 14 additions & 15 deletions ark/schema/roots/root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ import {
import { intersectNodesRoot, pipeNodesRoot } from "../shared/intersections.ts"
import type { JsonSchema } from "../shared/jsonSchema.ts"
import { $ark } from "../shared/registry.ts"
import type { StandardSchema } from "../shared/standardSchema.ts"
import type {
ArkTypeStandardSchemaProps,
StandardSchema
} from "../shared/standardSchema.ts"
import { arkKind, hasArkKind } from "../shared/utils.ts"
import { assertDefaultValueAssignability } from "../structure/optional.ts"
import type { Prop } from "../structure/prop.ts"
Expand Down Expand Up @@ -92,22 +95,18 @@ export abstract class BaseRoot<
return this
}

get "~standard"(): 1 {
return 1
}

get "~vendor"(): "arktype" {
return "arktype"
}

"~validate"(input: StandardSchema.Input): StandardSchema.Result<unknown> {
const out = this(input.value)
if (out instanceof ArkErrors) return out
return { value: out }
get "~standard"(): ArkTypeStandardSchemaProps {
return {
vendor: "arktype",
version: 1,
validate: input => {
const out = this(input)
if (out instanceof ArkErrors) return out
return { value: out }
}
}
}

declare "~types": StandardSchema.Types<unknown, unknown>

get optionalMeta(): boolean {
return this.cacheGetter(
"optionalMeta",
Expand Down
4 changes: 2 additions & 2 deletions ark/schema/shared/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import type { ResolvedArkConfig } from "../config.ts"
import type { Prerequisite, errorContext } from "../kinds.ts"
import type { NodeKind } from "./implement.ts"
import type { StandardSchema } from "./standardSchema.ts"
import type { StandardFailureResult } from "./standardSchema.ts"
import type { TraversalContext } from "./traversal.ts"
import { arkKind } from "./utils.ts"

Expand Down Expand Up @@ -84,7 +84,7 @@ export class ArkError<

export class ArkErrors
extends ReadonlyArray<ArkError>
implements StandardSchema.Failure
implements StandardFailureResult
{
protected ctx: TraversalContext

Expand Down
Loading