import type { StandardSchemaV1 } from '@standard-schema/spec'; import { Cause, Effect, Option, Schema } from 'effect'; import type { ESchemaDescriptor, Evolution, ForbidEmptyName, ForbidIdField, ForbidOptionalFields, ForbidUnderscorePrefix, Prettify, StructFieldsDecoded, StructFieldsEncoded, StructFieldsSchema, } from '../domain/schema-model/index.js'; import { INITIAL_VERSION } from '../domain/schema-model/index.js'; import { makeObjectSchemaRuntime } from '../domain/object-schema-runtime/index.js'; import { EntityESchemaBuilder } from './entity-eschema-builder.js'; function assertName(name: string): void { if (name === '') throw new Error('Schema name must not be empty.'); } const constructionToken = Symbol(); export class EntityESchema< TName extends string, TIdField extends string, TVersion extends string, TLatest extends StructFieldsSchema, > implements StandardSchemaV1>> { readonly #runtime; private constructor( token: typeof constructionToken, readonly name: TName, readonly idField: TIdField, readonly latestVersion: TVersion, evolutions: readonly Evolution[], ) { if (token !== constructionToken) { throw new TypeError('Invalid EntityESchema'); } this.#runtime = makeObjectSchemaRuntime({ owner: this, name, kind: 'entity', idField, latestVersion, evolutions, }); } static make< N extends string, Id extends string, I extends StructFieldsSchema, >( name: N & ForbidEmptyName, idField: Id, schema: I & ForbidUnderscorePrefix & ForbidIdField & ForbidOptionalFields, ) { assertName(name); const idSchema = Schema.String as Schema.Codec; const initial = { ...schema, [idField]: idSchema } as I & Record>; return new EntityESchemaBuilder( name, idField, idSchema, [{ version: INITIAL_VERSION, schema: initial, migration: null }], INITIAL_VERSION, ( version: V, evolutions: readonly Evolution[], ) => new EntityESchema( constructionToken, name, idField, version, evolutions, ), ); } Type = null as unknown as Prettify>; Encoded = null as unknown as Prettify> & { readonly _v: TVersion; }; get fields(): TLatest { return this.#runtime.fields(); } get schema(): Schema.Struct { return Schema.Struct(this.fields); } makePartial(value: Partial>) { return { ...value, _v: this.latestVersion }; } decode(value: unknown) { return this.#runtime.decode(value); } encode(value: StructFieldsDecoded) { return this.#runtime.encode(value); } getDescriptor(): ESchemaDescriptor { return this.#runtime.descriptor(); } '~standard' = { version: 1 as const, vendor: 'std-toolkit/eschema', types: { input: null as unknown as Prettify>, output: null as unknown as Prettify>, }, validate: (value: unknown) => { const result = Effect.runSyncExit(this.decode(value)); if (result._tag === 'Success') return { value: result.value }; const error = Cause.findErrorOption(result.cause); return Option.isSome(error) ? { issues: [{ message: error.value.message }] } : { issues: [{ message: 'Unknown error' }] }; }, }; }