1import { Effect, Result, type SchemaAST, SchemaParser } from "effect"
2import type * as CompilerRegistryModule from "effect/internal/schema/compilerRegistry"
3import assert from "node:assert/strict"
4import { join } from "node:path"
5import { pathToFileURL } from "node:url"
6import { asyncFixture, events, lazy, proof, roots, schemas, suspendEvaluations, synchronous } from "./aot.ts"
7import {
8 Constructed,
9 constructionCases,
10 constructionEvents,
11 constructionOptions,
12 constructionSchemas
13} from "./construction.ts"
14
15const CompilerRegistry: typeof CompilerRegistryModule = await import(
16 new URL("../../../src/internal/schema/compilerRegistry.ts", import.meta.url).href
17)
18
19const options: ReadonlyArray<SchemaAST.ParseOptions | undefined> = [
20 undefined,
21 { errors: "all", reportInput: true },
22 { onExcessProperty: "error" },
23 { disableChecks: true }
24]
25
26const snapshot = () =>
27 Object.fromEntries(
28 Object.entries(synchronous).map(([name, { inputs, schema }]) => {
29 const is = SchemaParser.is(schema)
30 const results = options.map((option) => {
31 const decode = SchemaParser.decodeUnknownResult(schema, option)
32 return inputs.map((input) => {
33 events.length = 0
34 const result = decode(input)
35 const calls = [...events]
36 return {
37 // Template diagnostics construct internal ASTs with fresh functions.
38 result: name === "templateLiteral" || name === "templateLiteralParser"
39 ? Result.mapError(result, (issue) => JSON.stringify(issue))
40 : result,
41 calls,
42 is: is(input)
43 }
44 })
45 })
46 return [name, results]
47 })
48 )
49
50const snapshotAsync = async () => {
51 const results = []
52 const decode = SchemaParser.decodeUnknownEffect(asyncFixture.schema)
53 for (const input of asyncFixture.inputs) {
54 events.length = 0
55 const result = await Effect.runPromise(Effect.result(decode(input)))
56 results.push({ result, calls: [...events] })
57 }
58 return results
59}
60
61assert.throws(() => new Function("return true"), EvalError)
62const interpreted = snapshot()
63const interpretedAsync = await snapshotAsync()
64const snapshotConstruction = async () => {
65 const out = []
66 for (const fixture of Object.values(constructionCases)) {
67 const make = SchemaParser.makeEffect(fixture.schema)
68 for (const parseOptions of constructionOptions) {
69 for (const input of fixture.inputs) {
70 constructionEvents.length = 0
71 const result = await Effect.runPromise(Effect.result(make(input as never, { parseOptions })))
72 out.push({ result, events: [...constructionEvents] })
73 }
74 }
75 }
76 return out
77}
78const interpretedConstruction = await snapshotConstruction()
79assert.equal(suspendEvaluations, 0)
80
81const before = CompilerRegistry.resolve(schemas.struct.ast)
82const empty = await import(pathToFileURL(join(process.argv[2], "empty.mjs")).href)
83assert.equal(empty.install([]), undefined)
84assert.equal(CompilerRegistry.resolve(schemas.struct.ast), before)
85
86if (process.argv[3] === "multiple") {
87 const generated = await import(pathToFileURL(join(process.argv[2], "all.mjs")).href)
88 assert.equal(CompilerRegistry.resolve(schemas.struct.ast), before)
89 assert.equal(generated.install(roots), undefined)
90} else {
91 for (const [name, schema] of Object.entries(schemas)) {
92 const before = name === "struct" ? CompilerRegistry.resolve(schema.ast) : undefined
93 const generated = await import(pathToFileURL(join(process.argv[2], `${name}.mjs`)).href)
94 if (before !== undefined) assert.equal(CompilerRegistry.resolve(schema.ast), before)
95 assert.equal(generated.install([schema.ast]), undefined)
96 }
97}
98assert.equal(suspendEvaluations, 0)
99
100for (
101 const name of [
102 "struct",
103 "array",
104 "tuple",
105 "tagged",
106 "sentinel",
107 "sentinelLookup",
108 "record",
109 "transformed",
110 "transformedStruct",
111 "pureTransformedStruct",
112 "checkedTransformedStruct",
113 "encodingCheckedTransformedStruct",
114 "asynchronous",
115 "middleware"
116 ]
117) {
118 assert.equal(typeof CompilerRegistry.resolve(schemas[name].ast).compiled, "function", name)
119}
120
121assert.deepEqual(snapshot(), interpreted)
122assert.deepEqual(await snapshotAsync(), interpretedAsync)
123for (const [name, schema] of Object.entries(constructionSchemas)) {
124 if (name === "construct-declaration") continue
125 assert.equal(typeof CompilerRegistry.resolve(schema.ast).compiled, "function", name)
126}
127assert.deepEqual(await snapshotConstruction(), interpretedConstruction)
128const instance = Constructed.make({})
129constructionEvents.length = 0
130assert.equal(Constructed.make(instance), instance)
131assert.equal(await Effect.runPromise(Constructed.makeEffect(instance)), instance)
132assert.deepEqual(constructionEvents, [])
133
134const transform = SchemaParser.decodeUnknownResult(schemas.transformed)
135events.length = 0
136assert.deepEqual(transform("2"), Result.succeed(2))
137assert.deepEqual(events, ["transform"])
138events.length = 0
139assert.ok(Result.isFailure(transform("-1")))
140assert.deepEqual(events, ["transform"])
141
142events.length = 0
143assert.deepEqual(SchemaParser.decodeUnknownResult(schemas.middleware)("-1"), Result.succeed(1))
144assert.deepEqual(events, ["transform", "middleware", "recover"])
145
146assert.deepEqual(SchemaParser.decodeUnknownSync(proof)({ value: "a", extra: true }), { value: "a" })
147assert.deepEqual(SchemaParser.make(proof)({ value: "constructed" }), { value: "constructed" })
148assert.ok(Result.isFailure(SchemaParser.decodeUnknownResult(proof)({ value: 1 })))
149assert.equal(SchemaParser.is(proof)({ value: "a" }), true)
150assert.equal(SchemaParser.is(proof)({ value: 1 }), false)
151assert.deepEqual(SchemaParser.decodeUnknownSync(schemas.proofArray)([{ value: "a", extra: true }]), [{ value: "a" }])
152assert.ok(Result.isFailure(SchemaParser.decodeUnknownResult(schemas.proofArray)([{ value: 1 }])))
153let invalidReads = 0
154assert.ok(Result.isFailure(
155 SchemaParser.decodeUnknownResult(schemas.proofArray)([{
156 get value() {
157 invalidReads++
158 return 1
159 }
160 }])
161))
162assert.equal(invalidReads, 2)
163
164assert.deepEqual(SchemaParser.decodeUnknownSync(lazy)({ value: "lazy" }), { value: "lazy" })
165assert.equal(suspendEvaluations, 1)
166process.stdout.write("AOT integration passed\n")