Changelog
Unreleased
in progressAdded
-
coredebuggable errors —SCHEMIC_DEBUG=1(or--stack) prints the full stack + the.causechain on any CLI failure (default output unchanged, now with a hint line), and a crashing schema module always reports the FAILING FILE path (original error ascause). -
coretyped cross-connection resolution — (A) a resolver'sctx.connections.<name>handle is now THENABLE to that sibling's FULL ORM client (const main = await ctx.connections.main; main.select(...)) while keeping direct.query; (B) the CHAINED config builder —defineConfig().connection(name, driverFactory, staticConfig | (ctx, args) => config | config[])— where the driver FACTORY itself is the marker and each resolver'sctx.connectionsis contextually typed with the ACCUMULATED prior connections (order = visibility = structural cycle prevention). The literaldefineConfig({ connections })form is unchanged. - table COMPOSITION + derived schemas (cross-driver convention, from real usage):
defineTable(name, s.object()), a public natives.object().fieldsmap (inverse of.shape),TableDef.extend(shape | s.object())typed cast-free column mixins, and derived Standard-Schema input schemas —TableDef.create(defaults/id optional, internal dropped) andTableDef.update(partial, id/readonly excluded) — composable via.partial/.extend/.refine/.or. -
coreORM client P1 foundation —OrmClientBase(disposable bound-client contract:close+[Symbol.asyncDispose], soawait using db = await connect()auto-closes), theasyncDisposablemixin, andresolveConnection(name?)(managed path over the project config). - the bound ORM client (P1 reads) at
@schemic/<driver>/client—connect(name?)MANAGED from the config /connect(client)BYO (close = no-op),db.select(table)pre-bound + awaitable (thenable builder; standalone.run(db)still works), AsyncDisposable; surreal adds a disposableforkSession(). -
coreconfig-as-factory with PARAMETERIZED connections —defineConfigis generic and returns the config with a typedconnect(name, args?): connection names autocomplete (a typo is a compile error),argsis the resolver's own declared 2nd param ((ctx, args) => config | config[], typed per connection; absent for static connections), and each entry's own client type is inferred (heterogeneous-driver projects type per-connection). An ARRAY resolution is bulk-only (migrations enumerate it) —connectthrows a teaching error; pass args selecting one.keyis a display label (not an address); entries may add a dialectlabelhook for bulk reporting. Resolvers can query sibling connections viactx.connectionsat runtime (lazy open through the entries' embedded client openers, cycle-detected, auto-closed). CLI:--args <json>+--arg k=vsugar feed resolver args. -
corethe config loader accepts a NAMEDschemicexport as well as a default — scaffolded form becomesexport const schemic = defineConfig(...)inschemic.config.ts(deterministicimport { schemic }->schemic.connect()auto-import, no file rename); a bareschemic.tsis also discovered (shape-guarded). - ORM P2 WRITES on the bound client — split builders (
db.create(T).content(data),db.update(T, id).merge(...)/.content(...)/.set(...),db.delete(T, id)— pg exportsremove), decoding through the codec channel fail-fast and returning typed rows that CARRY their id. - query Phase 1 READS — richer WHERE operators under the shared cross-driver op contract, pagination, and
one()/get()/count()terminals on the select builder. - table PRESETS —
defineTable.preset(...)reusable table fragments applied via the chained single-argTableDef.use(a).use(b)(ratified cross-driver form; columns + indexes).
Fixed
-
corethe DEFAULT migrations dir now follows the documented contract — RELATIVE TO THE SCHEMA (its siblingmigrationsdir) instead of a root-fixed./database/migrations. A nested schema (schema: "./src/database/schema") previously split state:initscaffolded the snapshot schema-relative whilegenwrote migrations + a second snapshot at the root default. Standard scaffold layouts are unchanged; an explicitmigrationsoverride still resolves from the root. - returned rows carry the implicit
id(select + writeRETURNING). -
coremulti-line DDL renders with PER-LINE diff indicators — now that drivers pretty-print display statements, every line of a statement gets its+/-insc diff(a bare continuation line read as context), unified-patch hunk counts count LINES not statements, the rollback block dims/indents per line, and the inline word-diff view collapses whitespace onto one line.
0.1.0-alpha.24
July 1, 2026Added
-
coreKindEngine.excludeFromMigrations— a kind can opt OUT of the migration pipeline entirely. Objects of an excluded kind are skipped by snapshot, diff, gen, and the introspect-compare, so they never enter a migration file nor phantom-diff; the kind is managed out-of-band by the driver's ownsc <kind> …commands. For secret-bearing kinds whose lifecycle doesn't fit committed migrations (SurrealDBDEFINE ACCESS: the DB redacts keys on introspection, and keys rotate independently). -
coreDriverCommandcontract — drivers can contribute dialect-specific CLI commands invoked assc <kind> <verb> [args](e.g. surrealaccess rotate <name>, postgresmatview refresh <name>). Core owns only the general mechanism: it discoversdriver.commands, parses argv (variadic positionals + value/boolean flags), resolves the connection, and dispatches torunwith aCommandContext({conn, config, io with prompt(), secrets}); the driver owns each kind/verb's meaning. -
clithe dispatch forDriverCommands — theschemic/scbin discovers the active driver'scommands(from the project config) and registers each assc <kind> <verb> [args], grouped by kind, with--help. It parses the invocation (variadic positionals + value/boolean flags), opens the connection, and runs the command with itsCommandContext. No project / no driver commands -> no-op (built-in commands unaffected). -
coresecret-bearing DDL foundations (Phase-2a of the DEFINE ACCESS secret contract) —SecretRef+env()/secret()author-time helpers + a pluggableSecretProvider(default readsprocess.env), and a write-onlybindingscarrier ($param->SecretRef) onStatement+Diff. The secret value never lives in the schema, snapshot, or migration: it is resolved at apply through the provider and passed as a bound parameter. Drivers re-exportenv/secret; apply-time resolution + migration persistence land next. -
cores.*fields now expose the Standard Schema~standardinterface (forwarded from the wrapped Zod schema onSFieldBase), so a Schemic field drops straight into any Standard Schema consumer (tRPC, TanStack Form/Router, …) without unwrapping to.schema.validateruns the decode direction (wire -> app). Postgres inherits it via core'sSFieldBase; surrealdb mirrors it on its own base (it does not yet share core'sSFieldBase). -
coreSFieldBase(thes.*base) gains the remaining Zod 4 shared-base methods for closer drop-in parity —nonoptional,exactOptional,isOptional,isNullable,toJSONSchema, adescriptiongetter,register, andspa.@schemic/postgresinherits them immediately (it composes core'sSFieldBase);@schemic/surrealdbmirrors them on its own base.
Changed (BREAKING — alpha) Breaking
-
connect()now fails loud on apostgres://(any non-file:URL scheme) connection url — it throws instead of silently spinning up an in-memory throwaway (a silent data-loss footgun where a user pointing at a real server "succeeded" against a disposable DB).connectis now async.file:<dir>(persistent) and""/omitted (in-memory) are unchanged; hostedpostgres://is reserved for a future node-postgres client.
Fixed
-
clibareschemic/sc(no args) now lists the active driver's contributed commands (sc <kind> <verb>) in its help, likesc --helpalready did. The no-arg help printed before driver commands had registered; registration now runs first.
0.1.0-alpha.22
June 26, 2026Changed
-
clithe driver loader now requires the@schemic/<driver>/driverentry (dropped the index fallback) — completes the M0.3 package split. Drivers must be >= 0.1.0-alpha.21.
Fixed
-
clischemic gennow shows the rendered migration before the title prompt (you review the actual DDL while naming it), instead of after writing.
0.1.0-alpha.21
June 23, 2026Changed (BREAKING — alpha) Breaking
- the authoring index (
@schemic/<driver>) is now side-effect-free —s.*/define*/surqlonly. Moved out: - the connection factory + connection types →@schemic/<driver>/connection(surrealConnection,postgresConnection/PgConn/pgSql). Updateschemic.config.tsimports. - theDriverimpl +emit*/lower/introspect+ theregisterDriverside-effect →@schemic/<driver>/driver(engine/CLI-only). So importings.*no longer drags the diff/emit engine or registers the driver. The query builder stays at@schemic/<driver>/query. (surrealdb also made its field registriesglobalThissingletons so the index and/drivermodule instances share state.)
Changed
-
clithe driver loader resolves a driver via its@schemic/<driver>/driversubpath first (falling back to the package index for not-yet-split drivers).
0.1.0-alpha.20
June 23, 2026Added
-
corecallFunctionin@schemic/core/query— invoke a defined DB function via thecallablecapability and decode the result through.returns(R)(the neutral half of the query layer's (B).call()).CallableFunctions.invokenow returns the raw function result forRto decode (no driver implementedcallableyet, so no break). -
clischemic pull --watch— poll the live DB (--interval, default 2s) and re-pull as it changes (preview, or apply with--write); a DB-poll loop, not fsWatch (which would self-trigger on pull's own file writes).
0.1.0-alpha.18
June 23, 2026Added
-
core@schemic/core/query— the neutral query toolkit driver builders compose:FieldRefBase(+brandRef),Project<P>projection inference,projectionSchema/decodeProjection. Plus thecallablecapability on theDrivercontract. - typed single-table
select()query builder at@schemic/<driver>/query(where/orderBy/limit/.returnprojection; decode-by-default;.raw()opts out) — the driver-owned builder composing the core toolkit. - standalone DDL objects —
defineSequence/defineDomain/defineExtension/defineMaterializedView. - functions, triggers, and RLS policies —
defineFunction/defineTrigger/definePolicy(auto-enables RLS). - composite + non-id foreign keys (
defineTable().foreignKey({ columns, refTable, refColumns })); richer indexes — access methods (gin/gist/brin/hash) + partial (where).
Older versions (pre-alpha.18) → repo CHANGELOG