@active-record-ts/active-model 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +766 -0
- package/dist/index.js +1808 -0
- package/dist/index.js.map +1 -0
- package/package.json +16 -7
- package/src/AttributeSet.ts +0 -221
- package/src/Callbacks.ts +0 -158
- package/src/Errors.ts +0 -398
- package/src/Model.ts +0 -546
- package/src/Name.ts +0 -69
- package/src/Type.ts +0 -320
- package/src/Validator.ts +0 -371
- package/src/index.ts +0 -59
- package/src/inflector.ts +0 -74
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Type.ts","../src/AttributeSet.ts","../src/Callbacks.ts","../src/Errors.ts","../src/Validator.ts","../src/Model.ts","../src/inflector.ts","../src/Name.ts"],"sourcesContent":["/**\n * Type system for ActiveModel attributes.\n *\n * Each Type owns three coercion functions:\n * - `cast` converts a value coming from user code (`record.name = 1`)\n * into the canonical attribute value (e.g. `\"1\"` for a string column).\n * - `serialize` converts an attribute value into a driver-compatible\n * value for INSERT/UPDATE statements.\n * - `deserialize` converts a value coming back from the driver into an\n * attribute value.\n *\n * The named type registry mirrors Rails' `ActiveModel::Type` registry.\n * Adapters can register adapter-specific types (e.g. PostgreSQL's `uuid`\n * or `jsonb`) by calling `Type.register(name, factory)`.\n */\n\n/** Base interface every type implementation satisfies. */\nexport interface Type<T = unknown> {\n /** Name of the type as registered (e.g. `'string'`, `'integer'`). */\n readonly type: string;\n cast(value: unknown): T | null;\n serialize(value: T | null): unknown;\n deserialize(value: unknown): T | null;\n /** Returns true when the two values represent the same logical attribute value. */\n equals?(a: T | null, b: T | null): boolean;\n}\n\n/** Helper to bail out of casting NULL-ish values uniformly. */\nconst isNullish = (value: unknown): boolean => value === null || value === undefined;\n\nexport class StringType implements Type<string> {\n readonly type = 'string';\n cast(value: unknown): string | null {\n if (isNullish(value)) return null;\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'bigint' || typeof value === 'boolean') return String(value);\n if (value instanceof Date) return value.toISOString();\n return String(value);\n }\n serialize(value: string | null) {\n return value;\n }\n deserialize(value: unknown): string | null {\n return this.cast(value);\n }\n}\n\nexport class IntegerType implements Type<number> {\n readonly type = 'integer';\n cast(value: unknown): number | null {\n if (isNullish(value) || value === '') return null;\n if (typeof value === 'number') return Number.isFinite(value) ? Math.trunc(value) : null;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'boolean') return value ? 1 : 0;\n if (typeof value === 'string') {\n const parsed = Number.parseInt(value, 10);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n }\n serialize(value: number | null) {\n return value;\n }\n deserialize(value: unknown): number | null {\n return this.cast(value);\n }\n}\n\nexport class BigIntType implements Type<bigint> {\n readonly type = 'bigint';\n cast(value: unknown): bigint | null {\n if (isNullish(value) || value === '') return null;\n if (typeof value === 'bigint') return value;\n if (typeof value === 'number') return Number.isFinite(value) ? BigInt(Math.trunc(value)) : null;\n if (typeof value === 'string') {\n try {\n return BigInt(value);\n } catch {\n return null;\n }\n }\n return null;\n }\n serialize(value: bigint | null) {\n return value;\n }\n deserialize(value: unknown): bigint | null {\n return this.cast(value);\n }\n}\n\nexport class FloatType implements Type<number> {\n readonly type = 'float';\n cast(value: unknown): number | null {\n if (isNullish(value) || value === '') return null;\n if (typeof value === 'number') return Number.isFinite(value) ? value : null;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'string') {\n const parsed = Number.parseFloat(value);\n return Number.isNaN(parsed) ? null : parsed;\n }\n if (typeof value === 'boolean') return value ? 1 : 0;\n return null;\n }\n serialize(value: number | null) {\n return value;\n }\n deserialize(value: unknown): number | null {\n return this.cast(value);\n }\n}\n\n/**\n * Stored as a string to preserve precision. Drivers commonly return decimals\n * as strings; we keep them that way for callers to feed into a decimal lib\n * of choice (we don't ship one).\n */\nexport class DecimalType implements Type<string> {\n readonly type = 'decimal';\n cast(value: unknown): string | null {\n if (isNullish(value) || value === '') return null;\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'bigint') return String(value);\n return String(value);\n }\n serialize(value: string | null) {\n return value;\n }\n deserialize(value: unknown): string | null {\n return this.cast(value);\n }\n}\n\nconst TRUTHY = new Set(['1', 't', 'T', 'true', 'TRUE', 'on', 'ON', 1, true]);\nconst FALSY = new Set(['0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF', 0, false]);\n\nexport class BooleanType implements Type<boolean> {\n readonly type = 'boolean';\n cast(value: unknown): boolean | null {\n if (isNullish(value) || value === '') return null;\n if (TRUTHY.has(value as never)) return true;\n if (FALSY.has(value as never)) return false;\n return Boolean(value);\n }\n serialize(value: boolean | null) {\n return value;\n }\n deserialize(value: unknown): boolean | null {\n return this.cast(value);\n }\n}\n\nconst dateOnly = (date: Date): Date => {\n const d = new Date(date.getTime());\n d.setUTCHours(0, 0, 0, 0);\n return d;\n};\n\nexport class DateType implements Type<Date> {\n readonly type = 'date';\n cast(value: unknown): Date | null {\n if (isNullish(value) || value === '') return null;\n if (value instanceof Date) return dateOnly(value);\n if (typeof value === 'string') {\n const d = new Date(/^\\d{4}-\\d{2}-\\d{2}$/.test(value) ? `${value}T00:00:00.000Z` : value);\n return Number.isNaN(d.getTime()) ? null : dateOnly(d);\n }\n if (typeof value === 'number') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : dateOnly(d);\n }\n return null;\n }\n serialize(value: Date | null): string | null {\n if (value == null) return null;\n return value.toISOString().slice(0, 10);\n }\n deserialize(value: unknown): Date | null {\n return this.cast(value);\n }\n equals(a: Date | null, b: Date | null): boolean {\n if (a == null || b == null) return a === b;\n return a.getTime() === b.getTime();\n }\n}\n\nexport class DateTimeType implements Type<Date> {\n readonly type = 'datetime';\n cast(value: unknown): Date | null {\n if (isNullish(value) || value === '') return null;\n if (value instanceof Date) return new Date(value.getTime());\n if (typeof value === 'string') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n if (typeof value === 'number') {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n }\n serialize(value: Date | null): string | null {\n if (value == null) return null;\n return value.toISOString();\n }\n deserialize(value: unknown): Date | null {\n return this.cast(value);\n }\n equals(a: Date | null, b: Date | null): boolean {\n if (a == null || b == null) return a === b;\n return a.getTime() === b.getTime();\n }\n}\n\nexport class JSONType<T = unknown> implements Type<T> {\n readonly type = 'json';\n cast(value: unknown): T | null {\n if (isNullish(value)) return null;\n if (typeof value === 'string') {\n try {\n return JSON.parse(value) as T;\n } catch {\n return null;\n }\n }\n return value as T;\n }\n serialize(value: T | null): string | null {\n if (value == null) return null;\n return JSON.stringify(value);\n }\n deserialize(value: unknown): T | null {\n return this.cast(value);\n }\n}\n\nexport class BinaryType implements Type<Uint8Array> {\n readonly type = 'binary';\n cast(value: unknown): Uint8Array | null {\n if (isNullish(value)) return null;\n if (value instanceof Uint8Array) return value;\n if (typeof value === 'string') return new TextEncoder().encode(value);\n if (Array.isArray(value)) return new Uint8Array(value as number[]);\n return null;\n }\n serialize(value: Uint8Array | null) {\n return value;\n }\n deserialize(value: unknown): Uint8Array | null {\n return this.cast(value);\n }\n equals(a: Uint8Array | null, b: Uint8Array | null): boolean {\n if (a == null || b == null) return a === b;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n }\n}\n\n/**\n * Pass-through type for unmapped or adapter-specific values. Used as the\n * default when schema reflection finds a column type we don't know about.\n */\nexport class ValueType implements Type<unknown> {\n readonly type = 'value';\n cast(value: unknown) {\n return isNullish(value) ? null : value;\n }\n serialize(value: unknown) {\n return value;\n }\n deserialize(value: unknown) {\n return isNullish(value) ? null : value;\n }\n}\n\n/** Compare two values via the type's `equals` hook if present, else `===`. */\nexport const valuesEqual = <T>(type: Type<T>, a: T | null, b: T | null): boolean =>\n type.equals ? type.equals(a, b) : a === b;\n\ntype Factory = () => Type;\n\nconst REGISTRY = new Map<string, Factory>();\n\n/** Register a named type (factory invoked per `Type.lookup`). */\nexport const registerType = (name: string, factory: Factory): void => {\n REGISTRY.set(name, factory);\n};\n\n/** Resolve a named type. Throws if unregistered. */\nexport const lookupType = (name: string): Type => {\n const factory = REGISTRY.get(name);\n if (!factory) throw new Error(`Unknown attribute type: ${name}`);\n return factory();\n};\n\n/** True when the named type has been registered. */\nexport const hasType = (name: string): boolean => REGISTRY.has(name);\n\nregisterType('string', () => new StringType());\nregisterType('text', () => new StringType());\nregisterType('integer', () => new IntegerType());\nregisterType('int', () => new IntegerType());\nregisterType('bigint', () => new BigIntType());\nregisterType('float', () => new FloatType());\nregisterType('double', () => new FloatType());\nregisterType('decimal', () => new DecimalType());\nregisterType('numeric', () => new DecimalType());\nregisterType('boolean', () => new BooleanType());\nregisterType('bool', () => new BooleanType());\nregisterType('date', () => new DateType());\nregisterType('datetime', () => new DateTimeType());\nregisterType('timestamp', () => new DateTimeType());\nregisterType('time', () => new DateTimeType());\nregisterType('json', () => new JSONType());\nregisterType('jsonb', () => new JSONType());\nregisterType('binary', () => new BinaryType());\nregisterType('blob', () => new BinaryType());\nregisterType('bytea', () => new BinaryType());\nregisterType('value', () => new ValueType());\n","/**\n * Per-instance attribute storage with dirty tracking.\n *\n * Two layers of values:\n * - `original` — the value when the record was loaded from the DB (or\n * last `save`/`commit`). Used to compute the dirty diff.\n * - `current` — the working value, updated by every assignment.\n *\n * Dirty surface (mirrors Rails' `ActiveModel::Dirty`):\n * - `changed()` — list of attribute names that differ from original\n * - `changes()` — `{ name: [from, to] }` map of pending changes\n * - `attributeChanged(name)` — boolean\n * - `attributeWas(name)` — original value\n * - `savedChanges()` — changes that were applied during the last save\n * - `attributePreviouslyChanged(name)` — boolean (post-save introspection)\n */\n\nimport { type Type, valuesEqual } from './Type';\n\n/** Declaration of a single attribute on a model. */\nexport type AttributeDefinition = {\n name: string;\n type: Type;\n /** Default applied when a record is new and the attribute is not assigned. */\n default?: unknown;\n};\n\nexport class AttributeSet {\n private readonly definitions = new Map<string, AttributeDefinition>();\n /** Insertion-ordered list of attribute names, for stable iteration. */\n private readonly names: string[] = [];\n\n /** Register an attribute (or replace an existing one). */\n define(definition: AttributeDefinition): void {\n if (!this.definitions.has(definition.name)) this.names.push(definition.name);\n this.definitions.set(definition.name, definition);\n }\n\n has(name: string): boolean {\n return this.definitions.has(name);\n }\n get(name: string): AttributeDefinition | undefined {\n return this.definitions.get(name);\n }\n keys(): string[] {\n return this.names.slice();\n }\n /** Stable iteration of definitions in declaration order. */\n *[Symbol.iterator](): IterableIterator<AttributeDefinition> {\n for (const name of this.names) yield this.definitions.get(name)!;\n }\n size(): number {\n return this.definitions.size;\n }\n clone(): AttributeSet {\n const copy = new AttributeSet();\n for (const def of this) copy.define(def);\n return copy;\n }\n}\n\n/** Per-instance attribute state — values + originals + last-save snapshot. */\nexport class Attributes {\n private readonly current = new Map<string, unknown>();\n private readonly original = new Map<string, unknown>();\n private previousChanges: Map<string, [unknown, unknown]> = new Map();\n private readonly accessedNames = new Set<string>();\n\n constructor(private readonly set: AttributeSet) {}\n\n /** Hydrate attributes after loading from the DB. Skips dirty tracking. */\n hydrate(raw: Record<string, unknown>): void {\n for (const def of this.set) {\n const incoming = raw[def.name];\n const value = def.type.deserialize(incoming);\n this.current.set(def.name, value);\n this.original.set(def.name, value);\n }\n }\n\n /** Hydrate attributes for a brand-new record (applies defaults). */\n hydrateDefaults(overrides: Record<string, unknown> = {}): void {\n for (const def of this.set) {\n const provided = def.name in overrides;\n const raw = provided ? overrides[def.name] : def.default;\n const value = def.type.cast(raw);\n this.current.set(def.name, value);\n this.original.set(def.name, value);\n }\n }\n\n /** Read an attribute by name, returning the canonical (cast) value. */\n read(name: string): unknown {\n this.accessedNames.add(name);\n return this.current.get(name);\n }\n\n /** Names that have been read since hydration. Mirrors Rails' `accessed_attributes`. */\n accessed(): string[] {\n return [...this.accessedNames].filter((n) => this.set.has(n));\n }\n\n /** Write an attribute by name. Type-casts before storing. */\n write(name: string, value: unknown): void {\n const def = this.set.get(name);\n if (!def) {\n this.current.set(name, value);\n return;\n }\n this.current.set(name, def.type.cast(value));\n }\n\n /**\n * Explicitly mark `name` as having been mutated in place — without\n * actually writing a new value. Mirrors Rails' `name_will_change!`\n * which captures the current value into the original snapshot for\n * later mutation detection.\n */\n willChange(name: string): void {\n if (!this.current.has(name) && !this.original.has(name)) return;\n // The next read sees the current value as \"the new value\"; rewind\n // original to a snapshot taken BEFORE further mutation.\n const value = this.current.get(name);\n // Stash a deep-ish copy of the current value as the original so subsequent\n // in-place mutation to `current` is detectable as a change.\n const snapshot =\n typeof value === 'object' && value !== null ? (Array.isArray(value) ? [...value] : { ...value }) : value;\n this.original.set(name, snapshot);\n }\n\n /** Was this attribute changed since the last commit? */\n changed(name: string): boolean {\n if (!this.current.has(name)) return false;\n const def = this.set.get(name);\n const original = this.original.get(name);\n const value = this.current.get(name);\n if (!def) return original !== value;\n return !valuesEqual(def.type, original as never, value as never);\n }\n\n /** List of attribute names that differ from their originals. */\n changedAttributes(): string[] {\n const result: string[] = [];\n for (const name of this.set.keys()) {\n if (this.changed(name)) result.push(name);\n }\n return result;\n }\n\n /** `{ name: [from, to] }` for every changed attribute. */\n changes(): Record<string, [unknown, unknown]> {\n const out: Record<string, [unknown, unknown]> = {};\n for (const name of this.changedAttributes()) {\n out[name] = [this.original.get(name), this.current.get(name)];\n }\n return out;\n }\n\n /** Original value of `name` (current value if unchanged). */\n was(name: string): unknown {\n return this.original.get(name);\n }\n\n /** Snapshot for SAVE — return the canonical values for each attribute. */\n toHash(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const name of this.set.keys()) out[name] = this.current.get(name);\n return out;\n }\n\n /** Snapshot of only the dirty attributes (for UPDATE statements). */\n dirtyHash(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const name of this.changedAttributes()) out[name] = this.current.get(name);\n return out;\n }\n\n /** Serialized snapshot of all attributes (driver-ready values). */\n serializedHash(): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const def of this.set) {\n out[def.name] = def.type.serialize(this.current.get(def.name) as never);\n }\n return out;\n }\n\n /** Mark the current values as the new baseline. Captures previous changes. */\n commit(): void {\n const previous = new Map<string, [unknown, unknown]>();\n for (const name of this.changedAttributes()) {\n previous.set(name, [this.original.get(name), this.current.get(name)]);\n this.original.set(name, this.current.get(name));\n }\n this.previousChanges = previous;\n }\n\n /** Drop all pending and recorded changes — used by `clearChangesInformation`. */\n clearChanges(): void {\n for (const [name, value] of this.current) this.original.set(name, value);\n this.previousChanges = new Map();\n }\n\n /** Revert all pending changes. */\n restore(): void {\n for (const name of this.changedAttributes()) {\n this.current.set(name, this.original.get(name));\n }\n }\n\n /** Changes that were applied during the last `commit`. */\n savedChanges(): Record<string, [unknown, unknown]> {\n const out: Record<string, [unknown, unknown]> = {};\n for (const [name, pair] of this.previousChanges) out[name] = pair;\n return out;\n }\n\n attributeSet(): AttributeSet {\n return this.set;\n }\n}\n","/**\n * Per-class callback chains for save/create/update/destroy/validation.\n *\n * Each chain holds an ordered list of callbacks. A `before` callback may\n * abort the chain by returning `false`. `around` callbacks receive a\n * `yield` function that runs the rest of the chain.\n *\n * The shape mirrors `ActiveSupport::Callbacks` enough to express Rails-y\n * lifecycle hooks, without trying to be a general callback framework.\n */\n\nexport type CallbackKind = 'before' | 'after' | 'around';\nexport type CallbackEvent =\n | 'validation'\n | 'save'\n | 'create'\n | 'update'\n | 'destroy'\n | 'commit'\n | 'rollback'\n | 'initialize'\n | 'find'\n | 'touch';\n\nexport type CallbackFn<T> = (record: T) => void | boolean | Promise<void | boolean>;\nexport type AroundCallbackFn<T> = (record: T, run: () => Promise<void>) => Promise<void>;\n\ntype CallbackEntry<T> = {\n kind: CallbackKind;\n fn: CallbackFn<T> | AroundCallbackFn<T>;\n /** Conditional guard — if it returns false, the callback is skipped. */\n if?: (record: T) => boolean;\n unless?: (record: T) => boolean;\n /** Limit the callback to one or more contexts (e.g. validation context). */\n on?: string | string[];\n};\n\n/** Sentinel — throw inside a callback to cleanly halt the chain. */\nexport class HaltError extends Error {\n constructor() {\n super('Callback chain halted');\n }\n}\n\n/**\n * Symbol-shaped sentinel callers can throw to halt the chain — mirrors\n * Rails' `throw :abort` semantics. The chain swallows it and treats it\n * as a `false` return from a `before_*` callback.\n */\nexport const ABORT_SENTINEL = Symbol.for('@active-record-ts/active-model:abort');\n\n/** Convenience helper for chain implementations: convert \"throw :abort\" to a clean halt. */\nexport const throwAbort = (): never => {\n throw ABORT_SENTINEL;\n};\n\nexport class CallbackChain<T> {\n private readonly chains: Record<CallbackEvent, CallbackEntry<T>[]> = {\n validation: [],\n save: [],\n create: [],\n update: [],\n destroy: [],\n commit: [],\n rollback: [],\n initialize: [],\n find: [],\n touch: [],\n };\n\n /** Register a new callback under `event` of `kind`. */\n add(\n event: CallbackEvent,\n kind: CallbackKind,\n fn: CallbackFn<T> | AroundCallbackFn<T>,\n options?: { if?: (record: T) => boolean; unless?: (record: T) => boolean; on?: string | string[] },\n ): void {\n this.chains[event].push({ kind, fn, if: options?.if, unless: options?.unless, on: options?.on });\n }\n\n /** Copy chains from a parent so subclasses inherit before extending. */\n inheritFrom(parent: CallbackChain<T>): void {\n for (const event of Object.keys(this.chains) as CallbackEvent[]) {\n this.chains[event] = [...parent.chains[event]];\n }\n }\n\n /**\n * Run the chain around `body`. Returns `false` when a `before` callback\n * halts; otherwise returns the return value of `body`. Pass a `context`\n * to filter callbacks registered with `on:` — primarily used for\n * validation contexts (`create`/`update`) but available everywhere.\n */\n async run(event: CallbackEvent, record: T, body: () => Promise<void | boolean>, context?: string): Promise<boolean> {\n const entries = this.chains[event];\n const befores = entries.filter((e) => e.kind === 'before');\n const afters = entries.filter((e) => e.kind === 'after');\n const arounds = entries.filter((e) => e.kind === 'around');\n\n for (const entry of befores) {\n if (!guard(entry, record, context)) continue;\n try {\n const result = await (entry.fn as CallbackFn<T>)(record);\n if (result === false) return false;\n } catch (err) {\n if (err instanceof HaltError) return false;\n if (err === ABORT_SENTINEL) return false;\n throw err;\n }\n }\n\n // Build the inner runner as a chain of around callbacks wrapping `body`.\n // Capture body's return value so after-callbacks can be skipped when it\n // returns false (mirrors Rails' \"after callbacks skipped when block\n // returns false\" behavior). When there are no around callbacks, we\n // skip the wrapper to keep the microtask cost identical to a bare\n // `await body()` — some callers (after_initialize) rely on that.\n let bodyHalted = false;\n if (arounds.length === 0) {\n const r = await body();\n if (r === false) bodyHalted = true;\n } else {\n const bodyRunner = async (): Promise<void> => {\n const r = await body();\n if (r === false) bodyHalted = true;\n };\n let runner: () => Promise<void> = bodyRunner;\n for (let i = arounds.length - 1; i >= 0; i--) {\n const around = arounds[i];\n if (!around || !guard(around, record, context)) continue;\n const inner = runner;\n runner = () => (around.fn as AroundCallbackFn<T>)(record, inner);\n }\n await runner();\n }\n\n if (bodyHalted) return false;\n\n for (const entry of afters) {\n if (!guard(entry, record, context)) continue;\n try {\n await (entry.fn as CallbackFn<T>)(record);\n } catch (err) {\n if (err instanceof HaltError) break;\n if (err === ABORT_SENTINEL) break;\n throw err;\n }\n }\n return true;\n }\n}\n\nconst guard = <T>(entry: CallbackEntry<T>, record: T, context?: string): boolean => {\n if (entry.on !== undefined) {\n const wanted = Array.isArray(entry.on) ? entry.on : [entry.on];\n if (context === undefined) return false;\n if (!wanted.includes(context)) return false;\n }\n if (entry.if && !entry.if(record)) return false;\n if (entry.unless && entry.unless(record)) return false;\n return true;\n};\n","/**\n * Error collection on a model instance. Mirrors `ActiveModel::Errors`\n * minus i18n — message strings are stored verbatim. The full set of\n * Rails behaviors we now support:\n *\n * - `add(attribute, typeOrMessage, options?)` where the second argument\n * can be a known symbol-style type (e.g. `'blank'`, `'too_short'`)\n * that maps to a default message string, or a free-form message.\n * - `on(attribute)` returns the list of messages for an attribute.\n * - `attributeNames` returns the unique attributes carrying an error.\n * - `added(attribute, type?, options?)` predicate.\n * - `where(attribute, type?, options?)` returns the matching entries.\n * - `messagesFor` / `fullMessagesFor` with optional type filter.\n * - `merge(other)` / `copy(other)`.\n */\n\nexport type ErrorEntry = {\n attribute: string;\n message: string;\n /** Optional discriminator for the validator that produced this error. */\n type?: string;\n /** Optional structured payload, e.g. `{ count: 2 }` for length validators. */\n options?: Record<string, unknown>;\n};\n\n/**\n * Rich error object exposed by `errors.objects` / `errors.first` / iteration.\n * Mirrors a subset of Rails' `ActiveModel::Error` — enough for callers\n * who want to introspect (attribute / type / options / full message) rather\n * than work with bare strings.\n */\nexport class ErrorObject {\n constructor(private readonly entry: ErrorEntry) {}\n get attribute(): string {\n return this.entry.attribute;\n }\n get message(): string {\n return this.entry.message;\n }\n get type(): string | undefined {\n return this.entry.type;\n }\n get options(): Record<string, unknown> | undefined {\n return this.entry.options;\n }\n fullMessage(): string {\n return this.entry.attribute === BASE\n ? this.entry.message\n : `${humanize(this.entry.attribute)} ${this.entry.message}`;\n }\n /** Internal accessor for `Errors#import` — returns the wrapped entry. */\n toEntry(): ErrorEntry {\n return this.entry;\n }\n}\n\n/** Special attribute name for errors that aren't tied to a specific column. */\nexport const BASE = 'base';\n\n/**\n * Default messages for known error types. Mirrors a small subset of\n * Rails' `errors.messages.*` locale entries — enough to keep tests\n * green without pulling in a full i18n backend. Format strings expand\n * `%{key}` against the options hash at lookup time.\n */\nconst DEFAULT_MESSAGES: Record<string, string> = {\n invalid: 'is invalid',\n blank: \"can't be blank\",\n present: 'must be blank',\n too_short: 'is too short (minimum is %{count} characters)',\n too_long: 'is too long (maximum is %{count} characters)',\n wrong_length: 'is the wrong length (should be %{count} characters)',\n taken: 'has already been taken',\n not_a_number: 'is not a number',\n greater_than: 'must be greater than %{count}',\n greater_than_or_equal_to: 'must be greater than or equal to %{count}',\n less_than: 'must be less than %{count}',\n less_than_or_equal_to: 'must be less than or equal to %{count}',\n equal_to: 'must be equal to %{count}',\n odd: 'must be odd',\n even: 'must be even',\n inclusion: 'is not included in the list',\n exclusion: 'is reserved',\n accepted: 'must be accepted',\n confirmation: \"doesn't match %{attribute}\",\n empty: \"can't be empty\",\n};\n\nconst isKnownType = (value: string): boolean => Object.hasOwn(DEFAULT_MESSAGES, value);\n\nconst interpolate = (template: string, options: Record<string, unknown> = {}): string =>\n template.replace(/%\\{(\\w+)\\}/g, (_, key) => (key in options ? String(options[key]) : `%{${key}}`));\n\nexport type AddOptions = {\n type?: string;\n options?: Record<string, unknown>;\n};\n\nexport class Errors {\n private readonly entries: ErrorEntry[] = [];\n\n /**\n * Record an error. The second argument can be:\n * - a free-form message string (e.g. `errors.add('name', \"can't be empty\")`)\n * - a known symbol-style type that resolves to a default message\n * (e.g. `errors.add('name', 'blank')` -> \"can't be blank\")\n *\n * Pass extra interpolation values or a custom `type` via the third arg.\n */\n add(\n attribute: string,\n messageOrType: string = 'invalid',\n options: AddOptions & Record<string, unknown> = {},\n ): ErrorEntry {\n const {\n type: explicitType,\n options: explicitOptions,\n message: explicitMessage,\n ...payload\n } = options as {\n type?: string;\n options?: Record<string, unknown>;\n message?: string;\n };\n const mergedOptions = { ...payload, ...(explicitOptions ?? {}) };\n let type: string | undefined;\n let message: string;\n if (explicitType) {\n type = explicitType;\n message =\n explicitMessage ??\n (isKnownType(messageOrType) ? interpolate(DEFAULT_MESSAGES[messageOrType]!, mergedOptions) : messageOrType);\n } else if (isKnownType(messageOrType)) {\n type = messageOrType;\n message = explicitMessage ?? interpolate(DEFAULT_MESSAGES[messageOrType]!, mergedOptions);\n } else {\n message = explicitMessage ?? messageOrType;\n }\n const entry: ErrorEntry = {\n attribute,\n message,\n ...(type !== undefined ? { type } : {}),\n ...(Object.keys(mergedOptions).length > 0 ? { options: mergedOptions } : {}),\n };\n this.entries.push(entry);\n return entry;\n }\n\n /** True when there are no errors at all. */\n get empty(): boolean {\n return this.entries.length === 0;\n }\n /** True when at least one error has been recorded. */\n get any(): boolean {\n return this.entries.length > 0;\n }\n\n clear(): void {\n this.entries.length = 0;\n }\n delete(attribute: string, type?: string, matchOptions?: Record<string, unknown>): string[] {\n const deleted: string[] = [];\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const e = this.entries[i]!;\n if (e.attribute !== attribute) continue;\n if (type !== undefined && e.type !== type) continue;\n if (matchOptions && !matchesOptions(e.options, matchOptions)) continue;\n deleted.unshift(e.message);\n this.entries.splice(i, 1);\n }\n return deleted;\n }\n\n on(attribute: string): string[] {\n return this.entries.filter((e) => e.attribute === attribute).map((e) => e.message);\n }\n includes(attribute: string): boolean {\n return this.entries.some((e) => e.attribute === attribute);\n }\n\n /** All `[attribute, message]` pairs, in insertion order. */\n get messages(): Record<string, string[]> {\n const out: Record<string, string[]> = {};\n for (const entry of this.entries) {\n const bucket = out[entry.attribute] ?? [];\n bucket.push(entry.message);\n out[entry.attribute] = bucket;\n }\n return out;\n }\n\n /** Distinct attributes that currently carry at least one error. */\n get attributeNames(): string[] {\n const seen = new Set<string>();\n const ordered: string[] = [];\n for (const entry of this.entries) {\n if (!seen.has(entry.attribute)) {\n seen.add(entry.attribute);\n ordered.push(entry.attribute);\n }\n }\n return ordered;\n }\n\n /** Predicate: was an error matching the given attribute (and optional type / options) added? */\n added(attribute: string, type?: string, matchOptions?: Record<string, unknown>): boolean {\n return this.entries.some((e) => {\n if (e.attribute !== attribute) return false;\n if (type !== undefined && e.type !== type) return false;\n if (matchOptions && !matchesOptions(e.options, matchOptions)) return false;\n return true;\n });\n }\n\n /** Filter entries by attribute / type / options. */\n where(attribute: string, type?: string, matchOptions?: Record<string, unknown>): ErrorEntry[] {\n return this.entries.filter((e) => {\n if (e.attribute !== attribute) return false;\n if (type !== undefined && e.type !== type) return false;\n if (matchOptions && !matchesOptions(e.options, matchOptions)) return false;\n return true;\n });\n }\n\n /** Messages for a specific attribute, optionally filtered by type. */\n messagesFor(attribute: string, type?: string): string[] {\n return this.where(attribute, type).map((e) => e.message);\n }\n\n /** Human-readable strings like `\"Name can't be blank\"`. */\n get fullMessages(): string[] {\n return this.entries.map((e) => (e.attribute === BASE ? e.message : `${humanize(e.attribute)} ${e.message}`));\n }\n\n fullMessagesFor(attribute: string, type?: string): string[] {\n return this.where(attribute, type).map((e) =>\n attribute === BASE ? e.message : `${humanize(attribute)} ${e.message}`,\n );\n }\n\n /** Standalone full-message formatter (no entries side effect). */\n fullMessage(attribute: string, message: string): string {\n return attribute === BASE ? message : `${humanize(attribute)} ${message}`;\n }\n\n /** Append every entry from `other` to this collection. */\n merge(other: Errors): this {\n if (other === this) return this;\n for (const entry of other.entries)\n this.entries.push({ ...entry, options: entry.options ? { ...entry.options } : undefined });\n return this;\n }\n\n /** Replace this collection's entries with copies of another's. */\n copy(other: Errors): this {\n this.clear();\n return this.merge(other);\n }\n\n /**\n * Return a new `Errors` collection with the same entries. Subsequent\n * modifications on either side don't affect the other.\n */\n dup(): Errors {\n const copy = new Errors();\n copy.merge(this);\n return copy;\n }\n\n /** Rich `ErrorObject` instances for each entry, in insertion order. */\n get objects(): ErrorObject[] {\n return this.entries.map((e) => new ErrorObject(e));\n }\n\n /** First error (rich object) or `null`. */\n get first(): ErrorObject | null {\n return this.entries[0] ? new ErrorObject(this.entries[0]) : null;\n }\n\n /**\n * Structured details payload. Each attribute maps to a list of\n * `{ error: type, ...options }` records. Mirrors Rails' `errors.details`.\n */\n get details(): Record<string, Array<{ error: string | undefined } & Record<string, unknown>>> {\n const out: Record<string, Array<{ error: string | undefined } & Record<string, unknown>>> = {};\n for (const entry of this.entries) {\n const bucket = out[entry.attribute] ?? [];\n bucket.push({ error: entry.type, ...(entry.options ?? {}) });\n out[entry.attribute] = bucket;\n }\n return out;\n }\n\n /** Group rich error objects by attribute. */\n groupByAttribute(): Record<string, ErrorObject[]> {\n const out: Record<string, ErrorObject[]> = {};\n for (const entry of this.entries) {\n const bucket = out[entry.attribute] ?? [];\n bucket.push(new ErrorObject(entry));\n out[entry.attribute] = bucket;\n }\n return out;\n }\n\n /** Indifferent indexer — `errors.get('name')` is equivalent to `errors.on('name')`. */\n get(attribute: string): string[] {\n return this.on(attribute);\n }\n\n /**\n * Remove duplicate entries — same attribute + message + type. Mirrors\n * Rails' `errors.uniq!`.\n */\n uniq(): this {\n const seen = new Set<string>();\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const e = this.entries[i]!;\n const key = `${e.attribute}\u0000${e.message}\u0000${e.type ?? ''}`;\n if (seen.has(key)) this.entries.splice(i, 1);\n else seen.add(key);\n }\n return this;\n }\n\n /** Import a foreign `ErrorEntry` or `ErrorObject`, optionally overriding attribute/type. */\n import(error: ErrorEntry | ErrorObject, overrides: { attribute?: string; type?: string } = {}): ErrorEntry {\n const base: ErrorEntry = error instanceof ErrorObject ? error.toEntry() : error;\n const next: ErrorEntry = {\n attribute: overrides.attribute ?? base.attribute,\n message: base.message,\n type: overrides.type ?? base.type,\n options: base.options ? { ...base.options } : undefined,\n };\n this.entries.push(next);\n return next;\n }\n\n /**\n * Indifferent indexer — returns `errors.on(attribute)` via a Proxy\n * pseudo-property, supporting both `errors.byAttribute('name')` and\n * the bracket-access shape `(errors as any)['name']`. Mirrors Rails'\n * `errors[:name]` / `errors['name']`. (We can't do real bracket access\n * without wrapping every Errors instance in a Proxy; this method-style\n * accessor is the idiomatic TS equivalent.)\n */\n byAttribute(attribute: string): string[] {\n return this.on(attribute);\n }\n\n /**\n * Rails-style `as_json`. With `{ fullMessages: true }`, each entry is\n * the humanized \"Attribute msg\" form. Default returns the same shape\n * as `messages`.\n */\n asJson(options: { fullMessages?: boolean } = {}): Record<string, string[]> {\n if (!options.fullMessages) return this.messages;\n const out: Record<string, string[]> = {};\n for (const entry of this.entries) {\n const full = entry.attribute === BASE ? entry.message : `${humanize(entry.attribute)} ${entry.message}`;\n const bucket = out[entry.attribute] ?? [];\n bucket.push(full);\n out[entry.attribute] = bucket;\n }\n return out;\n }\n\n /**\n * `to_hash(true)` returns full-message variant; otherwise plain messages.\n * Convenience shim over `asJson({ fullMessages })`.\n */\n toHash(fullMessages = false): Record<string, string[]> {\n return this.asJson({ fullMessages });\n }\n\n /**\n * `of_kind?` — true when an entry exists whose attribute and type-or-\n * message both match. Mirrors Rails' `errors.of_kind?(:name, :blank)`.\n */\n ofKind(attribute: string, typeOrMessage?: string): boolean {\n if (typeOrMessage === undefined) return this.includes(attribute);\n return this.entries.some((e) => {\n if (e.attribute !== attribute) return false;\n // Match either the type discriminator or the literal message.\n return e.type === typeOrMessage || e.message === typeOrMessage;\n });\n }\n\n /** Debug-friendly string representation. */\n inspect(): string {\n const parts = this.entries.map((e) => `#<Error attribute=${e.attribute}, message=${JSON.stringify(e.message)}>`);\n return `#<Errors:[${parts.join(', ')}]>`;\n }\n\n get count(): number {\n return this.entries.length;\n }\n get size(): number {\n return this.entries.length;\n }\n\n [Symbol.iterator](): IterableIterator<ErrorEntry> {\n return this.entries[Symbol.iterator]();\n }\n\n toJSON(): Record<string, string[]> {\n return this.messages;\n }\n}\n\n/** Two options hashes match when every key in `expected` has the same value in `actual`. */\nconst matchesOptions = (actual: Record<string, unknown> | undefined, expected: Record<string, unknown>): boolean => {\n if (!actual) return Object.keys(expected).length === 0;\n for (const [k, v] of Object.entries(expected)) {\n if (actual[k] !== v) return false;\n }\n return true;\n};\n\n/** Rails-style humanize: split camelCase / snake_case, lower-case everything, then upcase only the first letter. */\nconst humanize = (attribute: string): string => {\n // Dotted attribute names like `replies.name` (nested attributes) collapse\n // to a single phrase: dots become underscores, then snake/camel split.\n // Matches `String#humanize` in Rails.\n const flattened = attribute.replace(/\\./g, '_');\n const spaced = flattened\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n};\n","/**\n * Built-in validators. Each validator implements `validate(record, errors)`.\n * Adapter-level validators (e.g. uniqueness) live in active-record.\n */\n\nimport type { Errors } from './Errors';\n\n/**\n * Optional context name passed to `Model#validate(context)`. Mirrors Rails'\n * `valid?(:create)`/`valid?(:update)` — validators with `on` matching the\n * context (or with no `on` at all) are evaluated; others are skipped.\n */\nexport type ValidationContext = string;\n\nexport interface Validator<T = unknown> {\n validate(record: T, errors: Errors, context?: ValidationContext): void | Promise<void>;\n}\n\ntype Reader<T> = (record: T) => unknown;\n\nconst blank = (value: unknown): boolean => {\n if (value === null || value === undefined) return true;\n if (typeof value === 'string') return value.trim() === '';\n if (Array.isArray(value)) return value.length === 0;\n if (value instanceof Set || value instanceof Map) return value.size === 0;\n return false;\n};\n\n/** Common options shared across all validators. */\nexport type ValidatorOptions<T> = {\n /**\n * Custom error message. Can be a string or a function that receives\n * `(record, data)` like Rails' Proc messages, where `data` is\n * `{ attribute, value, type }`.\n */\n message?: string | ((record: T, data: { attribute: string; value: unknown; type?: string }) => string);\n if?: (record: T) => boolean;\n unless?: (record: T) => boolean;\n allowNull?: boolean;\n allowBlank?: boolean;\n /** Limit the validator to one or more validation contexts (e.g. `'create'`). */\n on?: ValidationContext | ValidationContext[];\n /** Skip the validator when the validation context matches one of these. */\n exceptOn?: ValidationContext | ValidationContext[];\n /**\n * When set, validator failures throw immediately instead of accumulating\n * in `record.errors`. Pass `true` for a generic `StrictValidationFailed`\n * or a custom Error subclass to use that instead. Mirrors Rails'\n * `validates(..., strict: true)`.\n */\n strict?: boolean | (new (message: string) => Error);\n};\n\n/** Thrown when a validator marked `strict: true` fails. */\nexport class StrictValidationFailed extends Error {\n constructor(message: string) {\n super(message);\n }\n}\n\nconst shouldValidate = <T>(record: T, options: ValidatorOptions<T> = {}, context?: ValidationContext): boolean => {\n if (options.on !== undefined) {\n const wanted = Array.isArray(options.on) ? options.on : [options.on];\n if (context === undefined) return false;\n if (!wanted.includes(context)) return false;\n }\n if (options.exceptOn !== undefined && context !== undefined) {\n const excluded = Array.isArray(options.exceptOn) ? options.exceptOn : [options.exceptOn];\n if (excluded.includes(context)) return false;\n }\n if (options.if && !options.if(record)) return false;\n if (options.unless && options.unless(record)) return false;\n return true;\n};\n\nconst skipForNullable = (value: unknown, options: { allowNull?: boolean; allowBlank?: boolean } = {}): boolean => {\n if (options.allowNull && (value === null || value === undefined)) return true;\n if (options.allowBlank && blank(value)) return true;\n return false;\n};\n\n/** Read a value from the record by attribute name, type-safe enough. */\nconst reader =\n <T>(attribute: string): Reader<T> =>\n (r: T) =>\n (r as Record<string, unknown>)[attribute];\n\n/**\n * Record an error, honoring `strict` if set. With `strict: true`, throws a\n * `StrictValidationFailed` whose message is the humanized \"Attribute message\".\n * With `strict: SomeError`, throws an instance of that error class instead.\n */\nconst recordError = <T>(\n options: ValidatorOptions<T>,\n errors: Errors,\n attribute: string,\n defaultMessage: string,\n meta: { type?: string; options?: Record<string, unknown> } = {},\n record?: T,\n value?: unknown,\n): void => {\n // Resolve a Proc-style message if the user supplied one; otherwise use\n // their string override; otherwise fall back to the default for this\n // validator.\n let message: string;\n if (typeof options.message === 'function') {\n if (record === undefined) {\n // No record context — fall back to default.\n message = defaultMessage;\n } else {\n message = options.message(record, { attribute, value, type: meta.type });\n }\n } else if (typeof options.message === 'string') {\n message = options.message;\n } else {\n message = defaultMessage;\n }\n if (options.strict) {\n const fullMessage = errors.fullMessage(attribute, message);\n if (typeof options.strict === 'function') {\n throw new options.strict(fullMessage);\n }\n throw new StrictValidationFailed(fullMessage);\n }\n errors.add(attribute, message, meta as never);\n};\n\n/**\n * Free-form block validator — wraps a `(record, errors) => void` callback.\n * Exposes `attributes: []` and `kind: 'block'` so introspection (`validators`,\n * `validatorsOn`) can still surface it.\n */\nexport class BlockValidator<T> implements Validator<T> {\n readonly kind = 'block';\n readonly attributes: string[] = [];\n constructor(\n private readonly fn: (record: T, errors: Errors, context?: ValidationContext) => void | Promise<void>,\n private readonly options: ValidatorOptions<T> = {},\n ) {}\n async validate(record: T, errors: Errors, context?: ValidationContext): Promise<void> {\n if (!shouldValidate(record, this.options, context)) return;\n await this.fn(record, errors, context);\n }\n}\n\nexport class PresenceValidator<T> implements Validator<T> {\n readonly kind = 'presence';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: ValidatorOptions<T> = {},\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (blank(value)) {\n recordError(this.options, errors, this.attribute, \"can't be blank\", { type: 'presence' }, record, value);\n }\n }\n}\n\nexport class AbsenceValidator<T> implements Validator<T> {\n readonly kind = 'absence';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: ValidatorOptions<T> = {},\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (!blank(value)) {\n recordError(this.options, errors, this.attribute, 'must be blank', { type: 'absence' }, record, value);\n }\n }\n}\n\nexport type LengthOptions<T> = ValidatorOptions<T> & {\n minimum?: number;\n maximum?: number;\n is?: number;\n in?: [number, number];\n tooShort?: string;\n tooLong?: string;\n wrongLength?: string;\n};\n\nexport class LengthValidator<T> implements Validator<T> {\n readonly kind = 'length';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: LengthOptions<T> = {},\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (skipForNullable(value, this.options)) return;\n const len = lengthOf(value);\n const o = this.options;\n if (o.in) {\n const [min, max] = o.in;\n if (len < min) {\n recordError(\n this.options,\n errors,\n this.attribute,\n o.tooShort ?? `is too short (minimum is ${min} characters)`,\n { type: 'length' },\n record,\n value,\n );\n return;\n }\n if (len > max) {\n recordError(\n this.options,\n errors,\n this.attribute,\n o.tooLong ?? `is too long (maximum is ${max} characters)`,\n { type: 'length' },\n record,\n value,\n );\n return;\n }\n }\n if (o.is !== undefined && len !== o.is) {\n recordError(\n this.options,\n errors,\n this.attribute,\n o.wrongLength ?? `is the wrong length (should be ${o.is} characters)`,\n { type: 'length' },\n record,\n value,\n );\n return;\n }\n if (o.minimum !== undefined && len < o.minimum) {\n recordError(\n this.options,\n errors,\n this.attribute,\n o.tooShort ?? `is too short (minimum is ${o.minimum} characters)`,\n { type: 'length' },\n record,\n value,\n );\n return;\n }\n if (o.maximum !== undefined && len > o.maximum) {\n recordError(\n this.options,\n errors,\n this.attribute,\n o.tooLong ?? `is too long (maximum is ${o.maximum} characters)`,\n { type: 'length' },\n record,\n value,\n );\n return;\n }\n }\n}\n\nconst lengthOf = (value: unknown): number => {\n if (value == null) return 0;\n if (typeof value === 'string' || Array.isArray(value)) return value.length;\n if (value instanceof Set || value instanceof Map) return value.size;\n return String(value).length;\n};\n\nexport type FormatOptions<T> = ValidatorOptions<T> & { with?: RegExp; without?: RegExp };\n\nexport class FormatValidator<T> implements Validator<T> {\n readonly kind = 'format';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: FormatOptions<T>,\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (skipForNullable(value, this.options)) return;\n const str = value == null ? '' : String(value);\n if (this.options.with && !this.options.with.test(str)) {\n recordError(this.options, errors, this.attribute, 'is invalid', { type: 'format' }, record, value);\n }\n if (this.options.without && this.options.without.test(str)) {\n recordError(this.options, errors, this.attribute, 'is invalid', { type: 'format' }, record, value);\n }\n }\n}\n\nexport type InclusionOptions<T> = ValidatorOptions<T> & { in: readonly unknown[] };\n\nexport class InclusionValidator<T> implements Validator<T> {\n readonly kind = 'inclusion';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: InclusionOptions<T>,\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (skipForNullable(value, this.options)) return;\n if (!this.options.in.includes(value)) {\n recordError(\n this.options,\n errors,\n this.attribute,\n 'is not included in the list',\n { type: 'inclusion' },\n record,\n value,\n );\n }\n }\n}\n\nexport type ExclusionOptions<T> = ValidatorOptions<T> & { in: readonly unknown[] };\n\nexport class ExclusionValidator<T> implements Validator<T> {\n readonly kind = 'exclusion';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: ExclusionOptions<T>,\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (skipForNullable(value, this.options)) return;\n if (this.options.in.includes(value)) {\n recordError(this.options, errors, this.attribute, 'is reserved', { type: 'exclusion' }, record, value);\n }\n }\n}\n\nexport type NumericalityOptions<T> = ValidatorOptions<T> & {\n onlyInteger?: boolean;\n greaterThan?: number;\n greaterThanOrEqualTo?: number;\n lessThan?: number;\n lessThanOrEqualTo?: number;\n equalTo?: number;\n odd?: boolean;\n even?: boolean;\n};\n\nexport class NumericalityValidator<T> implements Validator<T> {\n readonly kind = 'numericality';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: NumericalityOptions<T> = {},\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n if (skipForNullable(value, this.options)) return;\n const num = Number(value);\n if (Number.isNaN(num) || !Number.isFinite(num)) {\n recordError(this.options, errors, this.attribute, 'is not a number', { type: 'numericality' }, record, value);\n return;\n }\n const o = this.options;\n if (o.onlyInteger && !Number.isInteger(num)) {\n recordError(this.options, errors, this.attribute, 'must be an integer', { type: 'numericality.only_integer' });\n }\n if (o.greaterThan !== undefined && !(num > o.greaterThan)) {\n recordError(\n this.options,\n errors,\n this.attribute,\n `must be greater than ${o.greaterThan}`,\n { type: 'numericality.greater_than' },\n record,\n value,\n );\n }\n if (o.greaterThanOrEqualTo !== undefined && !(num >= o.greaterThanOrEqualTo)) {\n recordError(\n this.options,\n errors,\n this.attribute,\n `must be greater than or equal to ${o.greaterThanOrEqualTo}`,\n { type: 'numericality.greater_than_or_equal_to' },\n record,\n value,\n );\n }\n if (o.lessThan !== undefined && !(num < o.lessThan)) {\n recordError(\n this.options,\n errors,\n this.attribute,\n `must be less than ${o.lessThan}`,\n { type: 'numericality.less_than' },\n record,\n value,\n );\n }\n if (o.lessThanOrEqualTo !== undefined && !(num <= o.lessThanOrEqualTo)) {\n recordError(\n this.options,\n errors,\n this.attribute,\n `must be less than or equal to ${o.lessThanOrEqualTo}`,\n { type: 'numericality.less_than_or_equal_to' },\n record,\n value,\n );\n }\n if (o.equalTo !== undefined && num !== o.equalTo) {\n recordError(\n this.options,\n errors,\n this.attribute,\n `must be equal to ${o.equalTo}`,\n { type: 'numericality.equal_to' },\n record,\n value,\n );\n }\n if (o.odd && num % 2 === 0)\n recordError(this.options, errors, this.attribute, 'must be odd', { type: 'numericality.odd' });\n if (o.even && num % 2 !== 0)\n recordError(this.options, errors, this.attribute, 'must be even', { type: 'numericality.even' });\n }\n}\n\nexport type AcceptanceOptions<T> = ValidatorOptions<T> & { accept?: readonly unknown[] };\n\nexport class AcceptanceValidator<T> implements Validator<T> {\n readonly kind = 'acceptance';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: AcceptanceOptions<T> = {},\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const accept = this.options.accept ?? [true, '1', 1];\n const value = reader<T>(this.attribute)(record);\n if (!accept.includes(value)) {\n recordError(this.options, errors, this.attribute, 'must be accepted', { type: 'acceptance' }, record, value);\n }\n }\n}\n\nexport type ConfirmationOptions<T> = ValidatorOptions<T> & { caseSensitive?: boolean };\n\nexport class ConfirmationValidator<T> implements Validator<T> {\n readonly kind = 'confirmation';\n readonly attributes: string[];\n constructor(\n private readonly attribute: string,\n private readonly options: ConfirmationOptions<T> = {},\n ) {\n this.attributes = [attribute];\n }\n validate(record: T, errors: Errors, context?: ValidationContext): void {\n if (!shouldValidate(record, this.options, context)) return;\n const value = reader<T>(this.attribute)(record);\n const confirmation = reader<T>(`${this.attribute}Confirmation`)(record);\n if (confirmation === undefined) return;\n const a = value == null ? '' : String(value);\n const b = confirmation == null ? '' : String(confirmation);\n const equal = this.options.caseSensitive === false ? a.toLowerCase() === b.toLowerCase() : a === b;\n if (!equal) {\n recordError(\n this.options,\n errors,\n `${this.attribute}Confirmation`,\n \"doesn't match\",\n { type: 'confirmation' },\n record,\n value,\n );\n }\n }\n}\n","/**\n * `Model` is the active-model base — handles attribute storage, dirty\n * tracking, validation, and callbacks. ActiveRecord's `Base` subclasses\n * this to add persistence.\n *\n * Subclasses declare attributes with `static attribute(name, type)` or\n * by populating the static `attributesSchema` (the AR layer does this\n * automatically from `loadSchema`).\n */\n\nimport { Attributes, AttributeSet } from './AttributeSet';\nimport {\n CallbackChain,\n type CallbackEvent,\n type CallbackFn,\n type AroundCallbackFn,\n type CallbackKind,\n} from './Callbacks';\nimport { Errors } from './Errors';\nimport { lookupType, type Type } from './Type';\nimport {\n AcceptanceValidator,\n type AcceptanceOptions,\n AbsenceValidator,\n BlockValidator,\n ConfirmationValidator,\n type ConfirmationOptions,\n ExclusionValidator,\n type ExclusionOptions,\n FormatValidator,\n type FormatOptions,\n InclusionValidator,\n type InclusionOptions,\n LengthValidator,\n type LengthOptions,\n NumericalityValidator,\n type NumericalityOptions,\n PresenceValidator,\n type ValidationContext,\n type Validator,\n type ValidatorOptions,\n} from './Validator';\n\n/** A type reference accepted by `Model.attribute` — either a name or a Type instance. */\nexport type TypeRef = string | Type;\n\n/** Thrown by `Model#validateOrThrow` (mirrors Rails' `ActiveModel::ValidationError`). */\nexport class ValidationError extends Error {\n constructor(public record: Model) {\n super(`Validation failed: ${record.errors.fullMessages.join(', ')}`);\n }\n}\n\nconst resolveType = (ref: TypeRef): Type => (typeof ref === 'string' ? lookupType(ref) : ref);\n\n/**\n * Hidden registry attached to each Model subclass. Stored as a non-\n * enumerable property keyed by a symbol so it doesn't leak into the\n * subclass shape — but discoverable from any prototype.\n */\nconst REGISTRY = Symbol.for('@active-record-ts/active-model:registry');\n\ntype Registry<T extends Model> = {\n attributeSet: AttributeSet;\n validators: Validator<T>[];\n callbacks: CallbackChain<T>;\n};\n\nconst getRegistry = <T extends Model>(ctor: typeof Model): Registry<T> => {\n // biome-ignore lint/suspicious/noExplicitAny: registry is constructor-owned\n const own = (ctor as any)[REGISTRY] as Registry<T> | undefined;\n if (own && Object.hasOwn(ctor, REGISTRY)) return own;\n // Walk the prototype chain to inherit then copy down.\n const parent = Object.getPrototypeOf(ctor) as typeof Model | null;\n const parentReg = parent && parent !== Function.prototype ? getRegistry<T>(parent) : null;\n const fresh: Registry<T> = {\n attributeSet: parentReg ? parentReg.attributeSet.clone() : new AttributeSet(),\n validators: parentReg ? [...parentReg.validators] : [],\n callbacks: new CallbackChain<T>(),\n };\n if (parentReg) fresh.callbacks.inheritFrom(parentReg.callbacks);\n Object.defineProperty(ctor, REGISTRY, { value: fresh, enumerable: false, configurable: true, writable: false });\n return fresh;\n};\n\n/**\n * Camelize a Rails-style snake_case name into a method-friendly suffix\n * (e.g. `'first_name'` -> `'FirstName'`). Used to derive per-attribute\n * dirty helper names like `firstNameChanged()`.\n */\nconst camelizeSuffix = (name: string): string =>\n name.replace(/(?:^|[_-])([a-z0-9])/gi, (_, c: string) => c.toUpperCase()).replace(/[^A-Za-z0-9]/g, '');\n\n/**\n * Install per-attribute dirty helpers on the prototype: `nameChanged()`,\n * `nameWas()`, `nameChange()`, `restoreName()`, `namePreviouslyChanged()`,\n * `namePreviousChange()`. Mirrors a slice of `ActiveModel::Dirty`'s\n * generated methods.\n */\nconst definePerAttributeDirty = (target: typeof Model, names: string[]): void => {\n for (const name of names) {\n const suffix = camelizeSuffix(name);\n const camelName = lowerFirst(suffix);\n const helpers: Record<string, (this: Model, ...args: unknown[]) => unknown> = {\n [`${camelName}Changed`](this: Model) {\n return this.attributeChanged(name);\n },\n [`${camelName}Was`](this: Model) {\n return this.attributeWas(name);\n },\n [`${camelName}Change`](this: Model): [unknown, unknown] | null {\n if (!this.attributeChanged(name)) return null;\n return [this.attributeWas(name), this.readAttribute(name)];\n },\n [`restore${suffix}`](this: Model) {\n this.writeAttribute(name, this.attributeWas(name));\n },\n [`${camelName}WillChange`](this: Model) {\n this.willChange(name);\n },\n [`${camelName}PreviouslyChanged`](this: Model): boolean {\n return Object.hasOwn(this.savedChanges(), name);\n },\n [`${camelName}PreviousChange`](this: Model): [unknown, unknown] | null {\n const saved = this.savedChanges();\n return Object.hasOwn(saved, name) ? saved[name]! : null;\n },\n };\n for (const [methodName, fn] of Object.entries(helpers)) {\n if (Object.hasOwn(target.prototype, methodName)) continue;\n Object.defineProperty(target.prototype, methodName, {\n configurable: true,\n enumerable: false,\n writable: true,\n value: fn,\n });\n }\n }\n};\n\nconst lowerFirst = (s: string): string => (s.length === 0 ? s : s.charAt(0).toLowerCase() + s.slice(1));\n\n/** Accessor proxy installed on subclass prototypes so `record.name` reads `attributes`. */\nconst defineAccessors = (target: typeof Model, names: string[]): void => {\n for (const name of names) {\n if (Object.hasOwn(target.prototype, name)) continue;\n // Don't shadow an existing getter/setter inherited from an ancestor —\n // e.g. `Base#id` is a composite-aware getter on Base.prototype and\n // we shouldn't override it with a per-attribute accessor that always\n // reads the single `id` column.\n let proto: object | null = Object.getPrototypeOf(target.prototype);\n let inheritedAccessor = false;\n while (proto) {\n const desc = Object.getOwnPropertyDescriptor(proto, name);\n if (desc && (desc.get || desc.set)) {\n inheritedAccessor = true;\n break;\n }\n proto = Object.getPrototypeOf(proto);\n }\n if (inheritedAccessor) continue;\n Object.defineProperty(target.prototype, name, {\n configurable: true,\n enumerable: true,\n get(this: Model) {\n return this.readAttribute(name);\n },\n set(this: Model, value: unknown) {\n this.writeAttribute(name, value);\n },\n });\n }\n};\n\nexport class Model {\n /** Per-instance attribute state, lazily initialized. */\n protected _attributes!: Attributes;\n /** Per-instance error collection. */\n public readonly errors: Errors = new Errors();\n\n // biome-ignore lint/complexity/noBannedTypes: constructor body sets up attributes uniformly\n constructor(values: Record<string, unknown> = {}) {\n const ctor = this.constructor as typeof Model;\n const reg = getRegistry<this>(ctor);\n this._attributes = new Attributes(reg.attributeSet);\n this._attributes.hydrateDefaults(values);\n const names = reg.attributeSet.keys();\n defineAccessors(ctor, names);\n definePerAttributeDirty(ctor, names);\n // Fire after_initialize callbacks. We use the synchronous chain via\n // a promise so async hooks still run, but constructors can't await —\n // any errors will surface as unhandled rejections, which matches\n // Rails' \"don't put expensive logic in after_initialize\" expectation.\n void reg.callbacks.run('initialize', this, async () => {\n /* body */\n });\n }\n\n // ──────────────────────────── attribute IO ────────────────────────────\n\n readAttribute(name: string): unknown {\n return this._attributes.read(name);\n }\n writeAttribute(name: string, value: unknown): void {\n this._attributes.write(name, value);\n }\n /** Plain object snapshot. */\n attributes(): Record<string, unknown> {\n return this._attributes.toHash();\n }\n assignAttributes(values: Record<string, unknown>): this {\n for (const [name, value] of Object.entries(values)) this.writeAttribute(name, value);\n return this;\n }\n\n // ──────────────────────────── dirty tracking ────────────────────────────\n\n changed(): string[] {\n return this._attributes.changedAttributes();\n }\n changes(): Record<string, [unknown, unknown]> {\n return this._attributes.changes();\n }\n attributeChanged(name: string): boolean {\n return this._attributes.changed(name);\n }\n attributeWas(name: string): unknown {\n return this._attributes.was(name);\n }\n savedChanges(): Record<string, [unknown, unknown]> {\n return this._attributes.savedChanges();\n }\n /**\n * Tell the dirty tracker that `name` is about to be mutated in place,\n * so subsequent reads detect it as a change. Mirrors Rails'\n * `name_will_change!`.\n */\n willChange(name: string): void {\n this._attributes.willChange(name);\n }\n\n /**\n * Revert pending changes. With no argument restores every changed\n * attribute; with `names` restores only the listed ones. Mirrors\n * Rails' `restore_attributes(['name'])`.\n */\n restoreAttributes(names?: string[]): void {\n if (!names) {\n this._attributes.restore();\n return;\n }\n for (const name of names) {\n this._attributes.write(name, this._attributes.was(name));\n }\n }\n /** Reset both pending and last-saved changes — Rails' `clear_changes_information`. */\n clearChangesInformation(): void {\n this._attributes.clearChanges();\n }\n\n // ──────────────────────────── validation ────────────────────────────\n\n async validate(context?: ValidationContext): Promise<boolean> {\n this.errors.clear();\n const ctor = this.constructor as typeof Model;\n const reg = getRegistry<this>(ctor);\n await reg.callbacks.run(\n 'validation',\n this,\n async () => {\n for (const v of reg.validators) await v.validate(this, this.errors, context);\n },\n context,\n );\n return this.errors.empty;\n }\n async isValid(context?: ValidationContext): Promise<boolean> {\n return this.validate(context);\n }\n async isInvalid(context?: ValidationContext): Promise<boolean> {\n return !(await this.validate(context));\n }\n /** Mirrors Rails' `validate!`. Throws when invalid, returns true otherwise. */\n async validateOrThrow(context?: ValidationContext): Promise<boolean> {\n const ok = await this.validate(context);\n if (!ok) throw new ValidationError(this);\n return true;\n }\n\n // ──────────────────────────── helpers ────────────────────────────\n\n toJSON(options: { except?: readonly string[]; only?: readonly string[] } = {}): Record<string, unknown> {\n const all = this.attributes();\n if (options.only) {\n const out: Record<string, unknown> = {};\n for (const name of options.only) if (name in all) out[name] = all[name];\n return out;\n }\n if (options.except) {\n const out: Record<string, unknown> = {};\n const exclude = new Set(options.except);\n for (const [name, value] of Object.entries(all)) if (!exclude.has(name)) out[name] = value;\n return out;\n }\n return all;\n }\n\n /**\n * Return a new instance of the same class with the same attribute\n * values. Mirrors Rails' `record.dup`. The AR layer overrides this\n * to also clear the primary key so the duplicate looks like a fresh\n * (unsaved) record.\n */\n dup(): this {\n const ctor = this.constructor as new () => this;\n const copy = new ctor();\n // biome-ignore lint/suspicious/noExplicitAny: protected hydrate\n (copy as any)._attributes.hydrate(this.attributes());\n return copy;\n }\n\n // ──────────────────────────── class-side configuration ────────────────────────────\n\n /** Register an attribute on this subclass. Returns the constructor for chaining. */\n static attribute<This extends typeof Model>(\n this: This,\n name: string,\n type: TypeRef,\n options?: { default?: unknown },\n ): This {\n const reg = getRegistry(this);\n reg.attributeSet.define({ name, type: resolveType(type), default: options?.default });\n defineAccessors(this, [name]);\n definePerAttributeDirty(this, [name]);\n return this;\n }\n\n /** Access the per-class `AttributeSet`. */\n static attributesSchema<This extends typeof Model>(this: This): AttributeSet {\n return getRegistry(this).attributeSet;\n }\n\n /** Register a validator instance. */\n static validatesWith<This extends typeof Model>(this: This, validator: Validator<InstanceType<This>>): This {\n getRegistry(this).validators.push(validator as Validator<Model>);\n return this;\n }\n\n /**\n * Register a free-form block validator — `Klass.validate((record, errors) => ...)`.\n * The function may be async and receives the same `(record, errors, context)`\n * arguments every built-in validator does.\n */\n static validate<This extends typeof Model>(\n this: This,\n fn: (record: InstanceType<This>, errors: Errors, context?: ValidationContext) => void | Promise<void>,\n options: ValidatorOptions<InstanceType<This>> = {},\n ): This {\n return this.validatesWith(new BlockValidator(fn, options));\n }\n\n /** All registered validators in declaration order (inherited + own). */\n static validators<This extends typeof Model>(this: This): ReadonlyArray<Validator<InstanceType<This>>> {\n return getRegistry(this).validators as unknown as ReadonlyArray<Validator<InstanceType<This>>>;\n }\n\n /** Validators that target any of the given attribute names. */\n static validatorsOn<This extends typeof Model>(\n this: This,\n ...attributes: string[]\n ): ReadonlyArray<Validator<InstanceType<This>>> {\n const wanted = new Set(attributes);\n return this.validators().filter((v) => {\n const attrs = (v as unknown as { attributes?: readonly string[] }).attributes;\n if (!attrs) return false;\n for (const a of attrs) if (wanted.has(a)) return true;\n return false;\n });\n }\n\n /** Reset all per-class validators. Rails' `clear_validators!`. */\n static clearValidators<This extends typeof Model>(this: This): This {\n getRegistry(this).validators.length = 0;\n return this;\n }\n\n /**\n * Run a function once per attribute, accumulating errors. Mirrors Rails'\n * `validates_each :a, :b do |record, attr, value| ... end`.\n *\n * Klass.validatesEach(['a', 'b'], (record, attr, value, errors) => {\n * if (!ok(value)) errors.add(attr, 'bad');\n * });\n */\n static validatesEach<This extends typeof Model>(\n this: This,\n attributes: readonly string[],\n fn: (record: InstanceType<This>, attribute: string, value: unknown, errors: Errors) => void | Promise<void>,\n options: ValidatorOptions<InstanceType<This>> = {},\n ): This {\n return this.validate(async (record, errors) => {\n for (const attr of attributes) {\n const value = (record as unknown as Record<string, unknown>)[attr];\n await fn(record, attr, value, errors);\n }\n }, options);\n }\n\n /** Add a presence validator. */\n static validatesPresenceOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: ValidatorOptions<InstanceType<This>> = {},\n ): This {\n return this.validatesWith(new PresenceValidator(attribute, options));\n }\n static validatesAbsenceOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: ValidatorOptions<InstanceType<This>> = {},\n ): This {\n return this.validatesWith(new AbsenceValidator(attribute, options));\n }\n static validatesLengthOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: LengthOptions<InstanceType<This>>,\n ): This {\n return this.validatesWith(new LengthValidator(attribute, options));\n }\n static validatesFormatOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: FormatOptions<InstanceType<This>>,\n ): This {\n return this.validatesWith(new FormatValidator(attribute, options));\n }\n static validatesInclusionOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: InclusionOptions<InstanceType<This>>,\n ): This {\n return this.validatesWith(new InclusionValidator(attribute, options));\n }\n static validatesExclusionOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: ExclusionOptions<InstanceType<This>>,\n ): This {\n return this.validatesWith(new ExclusionValidator(attribute, options));\n }\n static validatesNumericalityOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: NumericalityOptions<InstanceType<This>> = {},\n ): This {\n return this.validatesWith(new NumericalityValidator(attribute, options));\n }\n static validatesAcceptanceOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: AcceptanceOptions<InstanceType<This>> = {},\n ): This {\n return this.validatesWith(new AcceptanceValidator(attribute, options));\n }\n static validatesConfirmationOf<This extends typeof Model>(\n this: This,\n attribute: string,\n options: ConfirmationOptions<InstanceType<This>> = {},\n ): This {\n return this.validatesWith(new ConfirmationValidator(attribute, options));\n }\n\n /**\n * Sugar mirroring Rails' `validates :name, presence: true, length: { minimum: 2 }`.\n */\n static validates<This extends typeof Model>(\n this: This,\n attribute: string,\n rules: {\n presence?: boolean | ValidatorOptions<InstanceType<This>>;\n absence?: boolean | ValidatorOptions<InstanceType<This>>;\n length?: LengthOptions<InstanceType<This>>;\n format?: FormatOptions<InstanceType<This>>;\n inclusion?: InclusionOptions<InstanceType<This>>;\n exclusion?: ExclusionOptions<InstanceType<This>>;\n numericality?: boolean | NumericalityOptions<InstanceType<This>>;\n acceptance?: boolean | AcceptanceOptions<InstanceType<This>>;\n confirmation?: boolean | ConfirmationOptions<InstanceType<This>>;\n },\n ): This {\n if (rules.presence) this.validatesPresenceOf(attribute, rules.presence === true ? {} : rules.presence);\n if (rules.absence) this.validatesAbsenceOf(attribute, rules.absence === true ? {} : rules.absence);\n if (rules.length) this.validatesLengthOf(attribute, rules.length);\n if (rules.format) this.validatesFormatOf(attribute, rules.format);\n if (rules.inclusion) this.validatesInclusionOf(attribute, rules.inclusion);\n if (rules.exclusion) this.validatesExclusionOf(attribute, rules.exclusion);\n if (rules.numericality)\n this.validatesNumericalityOf(attribute, rules.numericality === true ? {} : rules.numericality);\n if (rules.acceptance) this.validatesAcceptanceOf(attribute, rules.acceptance === true ? {} : rules.acceptance);\n if (rules.confirmation)\n this.validatesConfirmationOf(attribute, rules.confirmation === true ? {} : rules.confirmation);\n return this;\n }\n\n // ──────────────────────────── callbacks ────────────────────────────\n\n /** Options that can be passed to any callback registration helper. */\n static setCallback<This extends typeof Model>(\n this: This,\n event: CallbackEvent,\n kind: CallbackKind,\n fn: CallbackFn<InstanceType<This>> | AroundCallbackFn<InstanceType<This>>,\n options?: {\n if?: (record: InstanceType<This>) => boolean;\n unless?: (record: InstanceType<This>) => boolean;\n on?: string | string[];\n },\n ): This {\n getRegistry(this).callbacks.add(event, kind, fn as never, options as never);\n return this;\n }\n\n static beforeValidation<This extends typeof Model>(\n this: This,\n fn: CallbackFn<InstanceType<This>>,\n options?: {\n on?: string | string[];\n if?: (record: InstanceType<This>) => boolean;\n unless?: (record: InstanceType<This>) => boolean;\n },\n ): This {\n return this.setCallback('validation', 'before', fn, options);\n }\n static afterValidation<This extends typeof Model>(\n this: This,\n fn: CallbackFn<InstanceType<This>>,\n options?: {\n on?: string | string[];\n if?: (record: InstanceType<This>) => boolean;\n unless?: (record: InstanceType<This>) => boolean;\n },\n ): This {\n return this.setCallback('validation', 'after', fn, options);\n }\n static beforeSave<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('save', 'before', fn);\n }\n static afterSave<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('save', 'after', fn);\n }\n static aroundSave<This extends typeof Model>(this: This, fn: AroundCallbackFn<InstanceType<This>>): This {\n return this.setCallback('save', 'around', fn);\n }\n static beforeCreate<This extends typeof Model>(this: This, ...fns: CallbackFn<InstanceType<This>>[]): This {\n for (const fn of fns) this.setCallback('create', 'before', fn);\n return this;\n }\n static afterCreate<This extends typeof Model>(this: This, ...fns: CallbackFn<InstanceType<This>>[]): This {\n for (const fn of fns) this.setCallback('create', 'after', fn);\n return this;\n }\n static beforeUpdate<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('update', 'before', fn);\n }\n static afterUpdate<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('update', 'after', fn);\n }\n static beforeDestroy<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('destroy', 'before', fn);\n }\n static afterDestroy<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('destroy', 'after', fn);\n }\n\n /**\n * Run `fn` after the outermost transaction surrounding this record's\n * save / destroy commits. When the record is touched outside a\n * transaction, the callback fires immediately after the save.\n */\n static afterCommit<This extends typeof Model>(\n this: This,\n fn: CallbackFn<InstanceType<This>>,\n options?: { on?: 'create' | 'update' | 'destroy' | Array<'create' | 'update' | 'destroy'> },\n ): This {\n return this.setCallback('commit', 'after', fn, options as never);\n }\n\n /** Run `fn` when the surrounding transaction rolls back. */\n static afterRollback<This extends typeof Model>(\n this: This,\n fn: CallbackFn<InstanceType<This>>,\n options?: { on?: 'create' | 'update' | 'destroy' | Array<'create' | 'update' | 'destroy'> },\n ): This {\n return this.setCallback('rollback', 'after', fn, options as never);\n }\n\n /** Run `fn` whenever a new instance is constructed. */\n static afterInitialize<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('initialize', 'after', fn);\n }\n\n /** Run `fn` when a record is hydrated from the database. */\n static afterFind<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('find', 'after', fn);\n }\n\n /** Run `fn` after `touch()`. */\n static afterTouch<This extends typeof Model>(this: This, fn: CallbackFn<InstanceType<This>>): This {\n return this.setCallback('touch', 'after', fn);\n }\n\n /** Run a registered callback chain — typically used by ActiveRecord persistence. */\n static async runCallbacks<This extends typeof Model>(\n this: This,\n event: CallbackEvent,\n record: InstanceType<This>,\n body: () => Promise<void | boolean>,\n context?: string,\n ): Promise<boolean> {\n return getRegistry(this).callbacks.run(event, record, body, context);\n }\n}\n","/**\n * Tiny inflector — enough to convert class names into Rails-style table\n * names (`User` -> `users`, `Person` -> `people`, `Octopus` -> `octopi`).\n * Not a substitute for a full inflector (no `inflect.rb` here); add cases\n * as needed.\n */\n\nconst IRREGULAR: Array<[string, string]> = [\n ['person', 'people'],\n ['man', 'men'],\n ['woman', 'women'],\n ['child', 'children'],\n ['ox', 'oxen'],\n ['mouse', 'mice'],\n ['octopus', 'octopi'],\n ['cactus', 'cacti'],\n ['goose', 'geese'],\n ['foot', 'feet'],\n ['tooth', 'teeth'],\n];\n\nconst UNCOUNTABLE = new Set(['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep']);\n\nconst PLURAL_RULES: Array<[RegExp, string]> = [\n [/(quiz)$/i, '$1zes'],\n [/^(ox)$/i, '$1en'],\n [/([m|l])ouse$/i, '$1ice'],\n [/(matr|vert|ind)ix|ex$/i, '$1ices'],\n [/(x|ch|ss|sh)$/i, '$1es'],\n [/([^aeiouy]|qu)y$/i, '$1ies'],\n [/(hive)$/i, '$1s'],\n [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],\n [/sis$/i, 'ses'],\n [/([ti])um$/i, '$1a'],\n [/(buffal|tomat)o$/i, '$1oes'],\n [/(bu)s$/i, '$1ses'],\n [/(alias|status)$/i, '$1es'],\n [/(octop|vir)us$/i, '$1i'],\n [/(ax|test)is$/i, '$1es'],\n [/s$/i, 's'],\n [/$/, 's'],\n];\n\nexport const pluralize = (word: string): string => {\n if (!word) return word;\n const lower = word.toLowerCase();\n if (UNCOUNTABLE.has(lower)) return word;\n for (const [singular, plural] of IRREGULAR) {\n if (lower === singular) return plural;\n if (lower === plural) return plural;\n }\n for (const [pattern, replacement] of PLURAL_RULES) {\n if (pattern.test(word)) return word.replace(pattern, replacement);\n }\n return `${word}s`;\n};\n\nexport const underscore = (word: string): string =>\n word\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n .replace(/([a-z\\d])([A-Z])/g, '$1_$2')\n .replace(/-/g, '_')\n .toLowerCase();\n\nexport const camelize = (word: string, lower = false): string => {\n const camel = word\n .split(/[_-]/)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('');\n return lower ? camel.charAt(0).toLowerCase() + camel.slice(1) : camel;\n};\n\n/** Convert a class name into a Rails-style table name (`UserAccount` -> `user_accounts`). */\nexport const tableize = (className: string): string => pluralize(underscore(className));\n","/**\n * `Name` — Rails-style `ActiveModel::Name`. Exposes singular/plural/\n * element/collection/route_key/param_key/i18n_key/human/uncountable\n * derivations for a class name. We compute these purely from the class\n * name via the inflector — no i18n, no class introspection.\n */\n\nimport { pluralize, underscore } from './inflector';\n\nconst UNCOUNTABLE = new Set(['equipment', 'information', 'rice', 'money', 'species', 'series', 'fish', 'sheep']);\n\nconst humanize = (word: string): string => {\n const spaced = word.replace(/_/g, ' ').toLowerCase();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n};\n\nexport class Name {\n /** The class itself (or its name, when called with a class-name string). */\n readonly klass: { name: string };\n /** The fully-qualified class name as written. */\n readonly name: string;\n /** Snake-case version of the (possibly namespaced) class name. e.g. `Post::TrackBack` -> `post_track_back`. */\n readonly singular: string;\n /** Plural form of `singular`. e.g. `post_track_back` -> `post_track_backs`. */\n readonly plural: string;\n /** Last segment, snake-cased. e.g. `Post::TrackBack` -> `track_back`. */\n readonly element: string;\n /** Forward-slash-joined collection name. e.g. `Post::TrackBack` -> `post/track_backs`. */\n readonly collection: string;\n /** Rails routing helper — plural element with optional namespace prefix. */\n readonly route_key: string;\n /** `singular` minus internal namespace separators. */\n readonly param_key: string;\n /** Slash-joined namespace key. e.g. `Post::TrackBack` -> `post/track_back`. */\n readonly i18n_key: string;\n /** Human-friendly element name. e.g. `track_back` -> `Track back`. */\n readonly human: string;\n\n /**\n * Build a `Name` from a class (uses `klass.name`) or directly from a\n * Ruby-style namespaced string (\"Post::TrackBack\").\n */\n constructor(klassOrName: string | { name: string }) {\n this.klass = typeof klassOrName === 'string' ? { name: klassOrName } : klassOrName;\n this.name = this.klass.name;\n const parts = this.name.split('::');\n const last = parts[parts.length - 1]!;\n const nsParts = parts.slice(0, -1).map((p) => underscore(p));\n const lastSnake = underscore(last);\n this.singular = [...nsParts, lastSnake].join('_');\n this.plural = pluralize(this.singular);\n this.element = lastSnake;\n const elementPlural = pluralize(lastSnake);\n this.collection = nsParts.length === 0 ? elementPlural : `${nsParts.join('/')}/${elementPlural}`;\n this.route_key = nsParts.length === 0 ? elementPlural : `${nsParts.join('_')}_${elementPlural}`;\n this.param_key = this.singular;\n this.i18n_key = nsParts.length === 0 ? lastSnake : `${nsParts.join('/')}/${lastSnake}`;\n this.human = humanize(lastSnake);\n }\n\n /** Whether the singular form is uncountable (plural === singular). */\n get uncountable(): boolean {\n return UNCOUNTABLE.has(this.singular.toLowerCase());\n }\n\n toString(): string {\n return this.name;\n }\n}\n"],"mappings":"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","names":["REGISTRY","UNCOUNTABLE","humanize"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@active-record-ts/active-model",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Rails-style ActiveModel for TypeScript — typed attributes, dirty tracking, validations, callbacks.",
|
|
5
5
|
"author": "Alexander Stathis <stathis.alexanderj@gmail.com> (github.com/stathis-alexander)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
10
16
|
"repository": {
|
|
11
17
|
"type": "git",
|
|
12
18
|
"url": "git+https://github.com/stathis-alexander/active-record-ts.git",
|
|
@@ -15,19 +21,22 @@
|
|
|
15
21
|
"bugs": "https://github.com/stathis-alexander/active-record-ts/issues",
|
|
16
22
|
"homepage": "https://github.com/stathis-alexander/active-record-ts/tree/main/packages/active-model#readme",
|
|
17
23
|
"publishConfig": {
|
|
18
|
-
"access": "public"
|
|
24
|
+
"access": "public",
|
|
25
|
+
"provenance": true
|
|
19
26
|
},
|
|
20
27
|
"exports": {
|
|
21
28
|
".": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
29
|
+
"bun": "./src/index.ts",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
25
32
|
}
|
|
26
33
|
},
|
|
27
34
|
"peerDependencies": {
|
|
28
35
|
"typescript": "^5"
|
|
29
36
|
},
|
|
30
37
|
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"prepack": "tsup",
|
|
31
40
|
"test": "bun test",
|
|
32
41
|
"typecheck": "bun tsc --noEmit"
|
|
33
42
|
}
|
package/src/AttributeSet.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-instance attribute storage with dirty tracking.
|
|
3
|
-
*
|
|
4
|
-
* Two layers of values:
|
|
5
|
-
* - `original` — the value when the record was loaded from the DB (or
|
|
6
|
-
* last `save`/`commit`). Used to compute the dirty diff.
|
|
7
|
-
* - `current` — the working value, updated by every assignment.
|
|
8
|
-
*
|
|
9
|
-
* Dirty surface (mirrors Rails' `ActiveModel::Dirty`):
|
|
10
|
-
* - `changed()` — list of attribute names that differ from original
|
|
11
|
-
* - `changes()` — `{ name: [from, to] }` map of pending changes
|
|
12
|
-
* - `attributeChanged(name)` — boolean
|
|
13
|
-
* - `attributeWas(name)` — original value
|
|
14
|
-
* - `savedChanges()` — changes that were applied during the last save
|
|
15
|
-
* - `attributePreviouslyChanged(name)` — boolean (post-save introspection)
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { type Type, valuesEqual } from './Type';
|
|
19
|
-
|
|
20
|
-
/** Declaration of a single attribute on a model. */
|
|
21
|
-
export type AttributeDefinition = {
|
|
22
|
-
name: string;
|
|
23
|
-
type: Type;
|
|
24
|
-
/** Default applied when a record is new and the attribute is not assigned. */
|
|
25
|
-
default?: unknown;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export class AttributeSet {
|
|
29
|
-
private readonly definitions = new Map<string, AttributeDefinition>();
|
|
30
|
-
/** Insertion-ordered list of attribute names, for stable iteration. */
|
|
31
|
-
private readonly names: string[] = [];
|
|
32
|
-
|
|
33
|
-
/** Register an attribute (or replace an existing one). */
|
|
34
|
-
define(definition: AttributeDefinition): void {
|
|
35
|
-
if (!this.definitions.has(definition.name)) this.names.push(definition.name);
|
|
36
|
-
this.definitions.set(definition.name, definition);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
has(name: string): boolean {
|
|
40
|
-
return this.definitions.has(name);
|
|
41
|
-
}
|
|
42
|
-
get(name: string): AttributeDefinition | undefined {
|
|
43
|
-
return this.definitions.get(name);
|
|
44
|
-
}
|
|
45
|
-
keys(): string[] {
|
|
46
|
-
return this.names.slice();
|
|
47
|
-
}
|
|
48
|
-
/** Stable iteration of definitions in declaration order. */
|
|
49
|
-
*[Symbol.iterator](): IterableIterator<AttributeDefinition> {
|
|
50
|
-
for (const name of this.names) yield this.definitions.get(name)!;
|
|
51
|
-
}
|
|
52
|
-
size(): number {
|
|
53
|
-
return this.definitions.size;
|
|
54
|
-
}
|
|
55
|
-
clone(): AttributeSet {
|
|
56
|
-
const copy = new AttributeSet();
|
|
57
|
-
for (const def of this) copy.define(def);
|
|
58
|
-
return copy;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Per-instance attribute state — values + originals + last-save snapshot. */
|
|
63
|
-
export class Attributes {
|
|
64
|
-
private readonly current = new Map<string, unknown>();
|
|
65
|
-
private readonly original = new Map<string, unknown>();
|
|
66
|
-
private previousChanges: Map<string, [unknown, unknown]> = new Map();
|
|
67
|
-
private readonly accessedNames = new Set<string>();
|
|
68
|
-
|
|
69
|
-
constructor(private readonly set: AttributeSet) {}
|
|
70
|
-
|
|
71
|
-
/** Hydrate attributes after loading from the DB. Skips dirty tracking. */
|
|
72
|
-
hydrate(raw: Record<string, unknown>): void {
|
|
73
|
-
for (const def of this.set) {
|
|
74
|
-
const incoming = raw[def.name];
|
|
75
|
-
const value = def.type.deserialize(incoming);
|
|
76
|
-
this.current.set(def.name, value);
|
|
77
|
-
this.original.set(def.name, value);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Hydrate attributes for a brand-new record (applies defaults). */
|
|
82
|
-
hydrateDefaults(overrides: Record<string, unknown> = {}): void {
|
|
83
|
-
for (const def of this.set) {
|
|
84
|
-
const provided = def.name in overrides;
|
|
85
|
-
const raw = provided ? overrides[def.name] : def.default;
|
|
86
|
-
const value = def.type.cast(raw);
|
|
87
|
-
this.current.set(def.name, value);
|
|
88
|
-
this.original.set(def.name, value);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** Read an attribute by name, returning the canonical (cast) value. */
|
|
93
|
-
read(name: string): unknown {
|
|
94
|
-
this.accessedNames.add(name);
|
|
95
|
-
return this.current.get(name);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Names that have been read since hydration. Mirrors Rails' `accessed_attributes`. */
|
|
99
|
-
accessed(): string[] {
|
|
100
|
-
return [...this.accessedNames].filter((n) => this.set.has(n));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Write an attribute by name. Type-casts before storing. */
|
|
104
|
-
write(name: string, value: unknown): void {
|
|
105
|
-
const def = this.set.get(name);
|
|
106
|
-
if (!def) {
|
|
107
|
-
this.current.set(name, value);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
this.current.set(name, def.type.cast(value));
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Explicitly mark `name` as having been mutated in place — without
|
|
115
|
-
* actually writing a new value. Mirrors Rails' `name_will_change!`
|
|
116
|
-
* which captures the current value into the original snapshot for
|
|
117
|
-
* later mutation detection.
|
|
118
|
-
*/
|
|
119
|
-
willChange(name: string): void {
|
|
120
|
-
if (!this.current.has(name) && !this.original.has(name)) return;
|
|
121
|
-
// The next read sees the current value as "the new value"; rewind
|
|
122
|
-
// original to a snapshot taken BEFORE further mutation.
|
|
123
|
-
const value = this.current.get(name);
|
|
124
|
-
// Stash a deep-ish copy of the current value as the original so subsequent
|
|
125
|
-
// in-place mutation to `current` is detectable as a change.
|
|
126
|
-
const snapshot = typeof value === 'object' && value !== null
|
|
127
|
-
? (Array.isArray(value) ? [...value] : { ...value })
|
|
128
|
-
: value;
|
|
129
|
-
this.original.set(name, snapshot);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Was this attribute changed since the last commit? */
|
|
133
|
-
changed(name: string): boolean {
|
|
134
|
-
if (!this.current.has(name)) return false;
|
|
135
|
-
const def = this.set.get(name);
|
|
136
|
-
const original = this.original.get(name);
|
|
137
|
-
const value = this.current.get(name);
|
|
138
|
-
if (!def) return original !== value;
|
|
139
|
-
return !valuesEqual(def.type, original as never, value as never);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** List of attribute names that differ from their originals. */
|
|
143
|
-
changedAttributes(): string[] {
|
|
144
|
-
const result: string[] = [];
|
|
145
|
-
for (const name of this.set.keys()) {
|
|
146
|
-
if (this.changed(name)) result.push(name);
|
|
147
|
-
}
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** `{ name: [from, to] }` for every changed attribute. */
|
|
152
|
-
changes(): Record<string, [unknown, unknown]> {
|
|
153
|
-
const out: Record<string, [unknown, unknown]> = {};
|
|
154
|
-
for (const name of this.changedAttributes()) {
|
|
155
|
-
out[name] = [this.original.get(name), this.current.get(name)];
|
|
156
|
-
}
|
|
157
|
-
return out;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Original value of `name` (current value if unchanged). */
|
|
161
|
-
was(name: string): unknown {
|
|
162
|
-
return this.original.get(name);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** Snapshot for SAVE — return the canonical values for each attribute. */
|
|
166
|
-
toHash(): Record<string, unknown> {
|
|
167
|
-
const out: Record<string, unknown> = {};
|
|
168
|
-
for (const name of this.set.keys()) out[name] = this.current.get(name);
|
|
169
|
-
return out;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** Snapshot of only the dirty attributes (for UPDATE statements). */
|
|
173
|
-
dirtyHash(): Record<string, unknown> {
|
|
174
|
-
const out: Record<string, unknown> = {};
|
|
175
|
-
for (const name of this.changedAttributes()) out[name] = this.current.get(name);
|
|
176
|
-
return out;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Serialized snapshot of all attributes (driver-ready values). */
|
|
180
|
-
serializedHash(): Record<string, unknown> {
|
|
181
|
-
const out: Record<string, unknown> = {};
|
|
182
|
-
for (const def of this.set) {
|
|
183
|
-
out[def.name] = def.type.serialize(this.current.get(def.name) as never);
|
|
184
|
-
}
|
|
185
|
-
return out;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** Mark the current values as the new baseline. Captures previous changes. */
|
|
189
|
-
commit(): void {
|
|
190
|
-
const previous = new Map<string, [unknown, unknown]>();
|
|
191
|
-
for (const name of this.changedAttributes()) {
|
|
192
|
-
previous.set(name, [this.original.get(name), this.current.get(name)]);
|
|
193
|
-
this.original.set(name, this.current.get(name));
|
|
194
|
-
}
|
|
195
|
-
this.previousChanges = previous;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** Drop all pending and recorded changes — used by `clearChangesInformation`. */
|
|
199
|
-
clearChanges(): void {
|
|
200
|
-
for (const [name, value] of this.current) this.original.set(name, value);
|
|
201
|
-
this.previousChanges = new Map();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/** Revert all pending changes. */
|
|
205
|
-
restore(): void {
|
|
206
|
-
for (const name of this.changedAttributes()) {
|
|
207
|
-
this.current.set(name, this.original.get(name));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** Changes that were applied during the last `commit`. */
|
|
212
|
-
savedChanges(): Record<string, [unknown, unknown]> {
|
|
213
|
-
const out: Record<string, [unknown, unknown]> = {};
|
|
214
|
-
for (const [name, pair] of this.previousChanges) out[name] = pair;
|
|
215
|
-
return out;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
attributeSet(): AttributeSet {
|
|
219
|
-
return this.set;
|
|
220
|
-
}
|
|
221
|
-
}
|