@schemic/core 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +212 -0
- package/lib/authoring.d.ts +89 -0
- package/lib/authoring.js +187 -0
- package/lib/authoring.js.map +1 -0
- package/lib/chunk-C4D6JWSE.js +54 -0
- package/lib/chunk-C4D6JWSE.js.map +1 -0
- package/lib/chunk-T23RNU7G.js +304 -0
- package/lib/chunk-T23RNU7G.js.map +1 -0
- package/lib/config-TIiKDd9t.d.ts +97 -0
- package/lib/config.d.ts +1 -0
- package/lib/config.js +8 -0
- package/lib/config.js.map +1 -0
- package/lib/driver-Dh5hLKHm.d.ts +736 -0
- package/lib/driver.d.ts +150 -0
- package/lib/driver.js +47 -0
- package/lib/driver.js.map +1 -0
- package/lib/index.d.ts +84 -0
- package/lib/index.js +794 -0
- package/lib/index.js.map +1 -0
- package/lib/testing.d.ts +29 -0
- package/lib/testing.js +111 -0
- package/lib/testing.js.map +1 -0
- package/package.json +93 -0
- package/src/authoring.ts +304 -0
- package/src/cli-kit/config.ts +179 -0
- package/src/cli-kit/diff.ts +230 -0
- package/src/cli-kit/filter.ts +159 -0
- package/src/cli-kit/merge.ts +380 -0
- package/src/cli-kit/meta.ts +123 -0
- package/src/cli-kit/pager.ts +42 -0
- package/src/cli-kit/schema.ts +186 -0
- package/src/cli-kit/style.ts +24 -0
- package/src/config.ts +51 -0
- package/src/connection.ts +78 -0
- package/src/driver/driver.ts +300 -0
- package/src/driver/index.ts +31 -0
- package/src/driver/portable-ir.ts +51 -0
- package/src/driver/portable.ts +124 -0
- package/src/driver/sdk.ts +66 -0
- package/src/index.ts +145 -0
- package/src/kind/index.ts +28 -0
- package/src/kind/plan.ts +390 -0
- package/src/kind/registry.ts +225 -0
- package/src/testing.ts +181 -0
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
import * as jiti from 'jiti';
|
|
2
|
+
import { S as SchemicConfig, C as ConnectionConfigBase } from './config-TIiKDd9t.js';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A resolved, per-CONNECTION config — the dialect-NEUTRAL shape every command operates on (one
|
|
7
|
+
* connection at a time). `params` are the driver-specific connection params (opaque to core; the
|
|
8
|
+
* driver's `connect` reads them). Built by resolving one entry of `config.connections`.
|
|
9
|
+
*/
|
|
10
|
+
interface ResolvedConfig {
|
|
11
|
+
/** Resolved connection name (e.g. `default`, or `tenants:abc` within a collection). */
|
|
12
|
+
connection: string;
|
|
13
|
+
/** The driver this connection uses (the package the CLI dynamically loads). */
|
|
14
|
+
driver: string;
|
|
15
|
+
/** Project root (the directory containing the config file). */
|
|
16
|
+
root: string;
|
|
17
|
+
/** Absolute schema path — a single `.ts` module, or a directory of them. */
|
|
18
|
+
schemaPath: string;
|
|
19
|
+
/** Whether `schemaPath` is a single file (vs a directory of schema modules). */
|
|
20
|
+
schemaIsFile: boolean;
|
|
21
|
+
/** Absolute migrations directory (per connection's schema). */
|
|
22
|
+
migrationsDir: string;
|
|
23
|
+
/** Absolute migration meta directory (the snapshot). */
|
|
24
|
+
metaDir: string;
|
|
25
|
+
/** Name of the table that records applied migrations. */
|
|
26
|
+
migrationsTable: string;
|
|
27
|
+
/** Driver-specific connection params (url/namespace/… or whatever the driver defines). Opaque to core. */
|
|
28
|
+
params: Record<string, unknown>;
|
|
29
|
+
/** Optional seed script (project-level). */
|
|
30
|
+
seed?: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A jiti instance for loading the project's TS/ESM modules. Caches are off so `--watch` re-reads
|
|
34
|
+
* edited schema files. (Bare deps like `@schemic/core` are native-imported, so registries stay shared.)
|
|
35
|
+
*/
|
|
36
|
+
declare function makeJiti(): jiti.Jiti;
|
|
37
|
+
/** Find + load `schemic.config.ts` into the dialect-neutral {@link SchemicConfig}. */
|
|
38
|
+
declare function loadProject(opts?: {
|
|
39
|
+
config?: string;
|
|
40
|
+
cwd?: string;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
config: SchemicConfig;
|
|
43
|
+
root: string;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Build the {@link ResolvedConfig} for one connection of the project. `ctx` carries the lazy
|
|
47
|
+
* cross-connection proxy + CLI `--arg`s (the CLI provides the real one; a static connection ignores
|
|
48
|
+
* it). A resolver returning a COLLECTION yields one ResolvedConfig per keyed entry.
|
|
49
|
+
*
|
|
50
|
+
* NOTE (WIP — multi-connection): the full resolution engine (lazy proxy DAG, `--connection`/`--all`
|
|
51
|
+
* addressing, collection fan-out) lives in `@schemic/cli`; this builder handles a single resolved
|
|
52
|
+
* connection config. See docs/MULTI-CONNECTION.md.
|
|
53
|
+
*/
|
|
54
|
+
declare function resolveConnectionConfig(config: SchemicConfig, connection: string, conn: ConnectionConfigBase, driver: string, root: string): ResolvedConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Load the project and resolve the DEFAULT connection to a {@link ResolvedConfig} — the single-
|
|
57
|
+
* connection convenience path. (Multi-connection addressing + resolver context are added by the CLI;
|
|
58
|
+
* here a static default connection is resolved with an empty context.)
|
|
59
|
+
*/
|
|
60
|
+
declare function loadConfig(opts?: {
|
|
61
|
+
config?: string;
|
|
62
|
+
cwd?: string;
|
|
63
|
+
}): Promise<ResolvedConfig>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* An authored definable, tagged with the KIND that owns it. Core dispatches on `kind` alone — every
|
|
67
|
+
* other field is the kind's own business, handed straight to {@link KindEngine.lower}. This is the
|
|
68
|
+
* neutral upper bound for a kind's authoring-object type (a driver's concrete `TableDef`/`FnDef` is a
|
|
69
|
+
* structural subtype).
|
|
70
|
+
*/
|
|
71
|
+
interface Definable {
|
|
72
|
+
readonly kind: string;
|
|
73
|
+
readonly name: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A kind's PORTABLE object — the dialect-independent data shape core stores + diffs. A kind chooses
|
|
77
|
+
* how structured this is: a table's portable form carries fields/indexes (so core can field-level
|
|
78
|
+
* diff it); an opaque kind (function/access) carries a neutral identity + a `native` payload it
|
|
79
|
+
* round-trips. Either way it is tagged with `kind`/`name` for cross-kind dispatch + ordering.
|
|
80
|
+
*/
|
|
81
|
+
interface PortableObject {
|
|
82
|
+
readonly kind: string;
|
|
83
|
+
readonly name: string;
|
|
84
|
+
}
|
|
85
|
+
/** A reference to another object in the schema graph — the unit of cross-kind dependency ordering. */
|
|
86
|
+
interface Ref {
|
|
87
|
+
readonly kind: string;
|
|
88
|
+
readonly name: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* What core needs to orchestrate ONE kind generically — it never inspects the specifics. The
|
|
93
|
+
* change-vocabulary (`emit`/`remove`/`overwrite`) mirrors the Driver contract's, so a kind's behavior
|
|
94
|
+
* is parity-checkable against the fixed-slot engine. `A` is the kind's authoring object, `P` its
|
|
95
|
+
* portable object; both are opaque to core beyond the {@link Definable}/{@link PortableObject} bounds.
|
|
96
|
+
*/
|
|
97
|
+
interface KindEngine<A extends Definable = Definable, P extends PortableObject = PortableObject> {
|
|
98
|
+
/** Authoring object -> this kind's portable object (normalized; both lowerings must converge here). */
|
|
99
|
+
lower(authored: A): P;
|
|
100
|
+
/** CREATE DDL for one portable object (a fresh apply / migration `up` for an added object). */
|
|
101
|
+
emit(portable: P): string[];
|
|
102
|
+
/** DROP DDL for one portable object (`up` for a removed object, `down` for an added one). */
|
|
103
|
+
remove(portable: P): string[];
|
|
104
|
+
/**
|
|
105
|
+
* In-place CHANGE DDL taking `prev` to `next` (the dialect's ALTER/OVERWRITE). The spine calls
|
|
106
|
+
* `overwrite(next, prev)` to roll a change back. A kind with no in-place form recreates: implement
|
|
107
|
+
* as `[...remove(prev), ...emit(next)]`. Default (omitted) = recreate via emit(next).
|
|
108
|
+
*/
|
|
109
|
+
overwrite?(prev: P, next: P): string[];
|
|
110
|
+
/**
|
|
111
|
+
* The CANONICAL change-detection key: the spine treats prev/next of the same object as a CHANGE iff
|
|
112
|
+
* their `canonical` differs. Default (omitted) = `emit(portable).join("\n")` — so a kind whose `emit`
|
|
113
|
+
* is already its canonical form needs nothing. Override when `emit` is FAITHFUL but some clauses must
|
|
114
|
+
* be EXCLUDED from equality — because the DB rewrites them on read (PG `'x'` -> `'x'::text`, `a>0` ->
|
|
115
|
+
* `(a>0)`) or never introspects them (a COMMENT, an index) — so a faithful `emit` would phantom-diff a
|
|
116
|
+
* freshly-applied schema against `introspect`. Return `emit` MINUS those clauses: they stay create-time
|
|
117
|
+
* faithful in `emit`, but don't count as changes. `canonical(a) === canonical(b)` MUST mean "no
|
|
118
|
+
* migration needed". Affects ONLY classification; `emit`/`overwrite` (the DDL) are unaffected.
|
|
119
|
+
*/
|
|
120
|
+
canonical?(portable: P): string;
|
|
121
|
+
/**
|
|
122
|
+
* Fine-grained DISPLAY items for a change of this object — so `schemic diff` shows per-SUB-OBJECT
|
|
123
|
+
* changes (a table decomposes into per-FIELD items: `field:user:name` changed), each carrying its
|
|
124
|
+
* owner `table` so the display GROUPS them hierarchically under it, instead of one coarse whole-object
|
|
125
|
+
* item. Called `(prev, next)`: a change diffs the two; `(undefined, next)` lists the object's
|
|
126
|
+
* sub-items as adds — the `--full` projection core uses for the full desired-state view. Default
|
|
127
|
+
* (omitted) = ONE whole-object item. DISPLAY ONLY — never affects up/down DDL (that is
|
|
128
|
+
* `emit`/`overwrite`); a structured driver reuses the per-field diff it already computes. Leave
|
|
129
|
+
* `DiffItem.file` unset (the caller attaches source linkage).
|
|
130
|
+
*/
|
|
131
|
+
displayItems?(prev: P | undefined, next: P | undefined): DiffItem[];
|
|
132
|
+
/**
|
|
133
|
+
* Objects this one must be emitted AFTER — the cross-kind dependency edges (a field/index -> its
|
|
134
|
+
* table; an edge table -> its in/out tables; an event -> its table + any function it calls). Drives
|
|
135
|
+
* the topological sort in ./plan.ts. Omitted = no dependencies.
|
|
136
|
+
*/
|
|
137
|
+
deps?(portable: P): Ref[];
|
|
138
|
+
/**
|
|
139
|
+
* The owning object to CLUSTER next to in the emitted order (an index's table) — readability only,
|
|
140
|
+
* never overrides {@link deps}. Omitted = a top-level object.
|
|
141
|
+
*/
|
|
142
|
+
owner?(portable: P): Ref | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* Live connection -> all portable objects of THIS kind (the reverse direction). Introspection is
|
|
145
|
+
* often one `INFO`/`pg_catalog` read yielding every kind at once; a driver backs all of its kinds'
|
|
146
|
+
* `introspect` with one shared (memoized) read and slices out this kind's objects. Omitted -> this
|
|
147
|
+
* kind isn't introspectable (diff/emit still work from authored state).
|
|
148
|
+
*/
|
|
149
|
+
introspect?(conn: unknown): Promise<P[]>;
|
|
150
|
+
/**
|
|
151
|
+
* How this kind is PRESENTED — its human labels and the folder its objects render into. All optional
|
|
152
|
+
* with sensible defaults off the kind name (see {@link KindRegistry.display}), so a kind only declares
|
|
153
|
+
* what the defaults get wrong (e.g. `plural: "Indexes"`, or `folder: "access"`). DISPLAY ONLY.
|
|
154
|
+
*/
|
|
155
|
+
display?: KindDisplay;
|
|
156
|
+
}
|
|
157
|
+
/** Per-kind presentation metadata (labels + output folder). All optional; core fills defaults. */
|
|
158
|
+
interface KindDisplay {
|
|
159
|
+
/** Title-Case singular, e.g. `"Table"`, `"Field"`. Default: the kind name, capitalized. */
|
|
160
|
+
label?: string;
|
|
161
|
+
/** Title-Case plural, e.g. `"Tables"`, `"Indexes"`. Default: the English plural of `label`. */
|
|
162
|
+
plural?: string;
|
|
163
|
+
/** The directory this kind's objects render into. Default: the lowercase slug of `plural`. */
|
|
164
|
+
folder?: string;
|
|
165
|
+
}
|
|
166
|
+
/** A kind's resolved presentation — every field filled (the shape {@link KindRegistry.display} returns). */
|
|
167
|
+
type ResolvedDisplay = Required<KindDisplay>;
|
|
168
|
+
/** A kind's full spec: its `name`, its `build` (the driver's authoring entry), and its engine. */
|
|
169
|
+
type KindSpec<Build extends (...args: never[]) => unknown, A extends Definable, P extends PortableObject> = {
|
|
170
|
+
name: string;
|
|
171
|
+
build: Build;
|
|
172
|
+
} & KindEngine<A, P>;
|
|
173
|
+
/**
|
|
174
|
+
* A driver's set of registered kinds + the generic behavior the spine reads off them. Built once per
|
|
175
|
+
* driver; `define` registers a kind and returns the driver's OWN `build` function UNCHANGED — so the
|
|
176
|
+
* driver writes `export const defineTable = registry.define({ name: "table", build, ...engine })` and
|
|
177
|
+
* keeps full type-safety + DX (TS preserves a generic `build`'s parameters across the passthrough).
|
|
178
|
+
*/
|
|
179
|
+
declare class KindRegistry {
|
|
180
|
+
private readonly kinds;
|
|
181
|
+
/**
|
|
182
|
+
* Register a KIND. `build` is the driver's own authoring entry — ANY shape/chain — and its type
|
|
183
|
+
* flows through unchanged (type-safety + DX are the driver's to design). The engine fns give core
|
|
184
|
+
* the generic behavior. Registration ORDER is the kind's ordinal (the stable tie-break among
|
|
185
|
+
* independent objects in {@link orderObjects}), so register coarse-to-fine (table before index).
|
|
186
|
+
*/
|
|
187
|
+
define<Build extends (...args: never[]) => unknown, A extends Definable, P extends PortableObject>(spec: KindSpec<Build, A, P>): Build;
|
|
188
|
+
/** The engine for `kind`, or undefined if no such kind is registered. */
|
|
189
|
+
engine(kind: string): KindEngine<any, any> | undefined;
|
|
190
|
+
/**
|
|
191
|
+
* A kind's resolved presentation — `label`/`plural`/`folder`, with defaults derived from the kind
|
|
192
|
+
* name for whatever the driver left unset. Works for unregistered display sub-kinds too (e.g. the
|
|
193
|
+
* `"field"` items a table's `displayItems` emits) — they just get the name-derived defaults.
|
|
194
|
+
*/
|
|
195
|
+
display(kind: string): ResolvedDisplay;
|
|
196
|
+
/** Registered kind names, in registration order (== ordinal order). */
|
|
197
|
+
names(): string[];
|
|
198
|
+
/**
|
|
199
|
+
* A kind's ORDINAL = its registration index. Used ONLY as a tie-break among objects with no
|
|
200
|
+
* dependency relation, so independent objects come out stably layered (readability); it never
|
|
201
|
+
* overrides the dependency graph. An unknown kind sorts last.
|
|
202
|
+
*/
|
|
203
|
+
ordinal(kind: string): number;
|
|
204
|
+
/** [name, engine] pairs in registration order — the spine iterates these. */
|
|
205
|
+
entries(): [string, KindEngine<any, any>][];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** A node in the dependency graph: identity + the edges/owner used to order it. */
|
|
209
|
+
interface OrderNode {
|
|
210
|
+
readonly kind: string;
|
|
211
|
+
readonly name: string;
|
|
212
|
+
/** Objects this node must come AFTER (only intra-set refs constrain; external refs are ignored). */
|
|
213
|
+
readonly deps: Ref[];
|
|
214
|
+
/** Owning object to cluster next to (readability tie-break only; never overrides `deps`). */
|
|
215
|
+
readonly owner?: Ref;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Kahn's topological sort with two presentation tweaks among the nodes whose deps are all satisfied:
|
|
219
|
+
* prefer one OWNED by the currently-open cluster (so a table's children follow it), then lowest
|
|
220
|
+
* (kind-ordinal, then name). Correctness (deps) always wins — an owned/low-ordinal node can't jump a
|
|
221
|
+
* dependency. A genuine cycle throws (a named error). Refs to nodes outside `nodes` are ignored (an
|
|
222
|
+
* object may depend on something untouched by this diff — it already exists / isn't changing).
|
|
223
|
+
*/
|
|
224
|
+
declare function orderObjects<T extends OrderNode>(nodes: T[], ordinalOf: (kind: string) => number): T[];
|
|
225
|
+
/**
|
|
226
|
+
* Author -> portable: lower each definable through its kind's engine (skipping unregistered kinds).
|
|
227
|
+
* The single place authoring becomes portable; everything downstream (diff/emit/snapshot) is portable.
|
|
228
|
+
*/
|
|
229
|
+
declare function lowerSchema(registry: KindRegistry, defs: Definable[]): PortableObject[];
|
|
230
|
+
/**
|
|
231
|
+
* The registry SNAPSHOT — portable objects grouped by kind. The open, generic replacement for
|
|
232
|
+
* `PortableDb`'s fixed slots; serializes as plain JSON (it is plain data). Pre-launch: the format is
|
|
233
|
+
* free to change, no version migration.
|
|
234
|
+
*/
|
|
235
|
+
interface KindSnapshot {
|
|
236
|
+
kinds: Record<string, PortableObject[]>;
|
|
237
|
+
}
|
|
238
|
+
/** Group a flat portable schema into a snapshot (by kind). */
|
|
239
|
+
declare function snapshotKinds(schema: PortableObject[]): KindSnapshot;
|
|
240
|
+
/** Flatten a snapshot back into a portable schema (the inverse of {@link snapshotKinds}). */
|
|
241
|
+
declare function snapshotObjects(snap: KindSnapshot): PortableObject[];
|
|
242
|
+
/** An up/down DDL program (each a list of statements). */
|
|
243
|
+
interface KindPlan {
|
|
244
|
+
up: string[];
|
|
245
|
+
down: string[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Diff two portable schema states into an executable up/down program, generically over the registry.
|
|
249
|
+
*
|
|
250
|
+
* `up` runs creates/changes parent-first (the dependency graph) then drops child-first; `down` is the
|
|
251
|
+
* mirror: recreate drops parent-first, then undo creates/changes child-first. We invert PER OBJECT (not
|
|
252
|
+
* by reversing the flat DDL list) so a kind's multi-line block — a table emitted with its fields —
|
|
253
|
+
* keeps its internal order in both directions.
|
|
254
|
+
*/
|
|
255
|
+
declare function planKinds(registry: KindRegistry, prev: PortableObject[], next: PortableObject[]): KindPlan;
|
|
256
|
+
/**
|
|
257
|
+
* The full {@link Diff} the CLI + migration model consume — up/down DDL + per-object display items +
|
|
258
|
+
* the whole desired schema (`full`, for `--full`). This is what a driver's `Driver.diff` returns once
|
|
259
|
+
* its kinds are on the registry (the generic counterpart of the fixed-slot `buildDiff`). Source-file
|
|
260
|
+
* linkage on the items is attached by the caller (the snapshot's `files` map), so `file` is left unset.
|
|
261
|
+
*/
|
|
262
|
+
declare function buildKindDiff(registry: KindRegistry, prev: PortableObject[], next: PortableObject[]): Diff;
|
|
263
|
+
/**
|
|
264
|
+
* Fresh-apply DDL for a portable schema: every object created, ordered across kinds by the graph.
|
|
265
|
+
* (The `up` of a diff from an empty state.) Lower authoring first via {@link lowerSchema}.
|
|
266
|
+
*/
|
|
267
|
+
declare function emitKinds(registry: KindRegistry, schema: PortableObject[]): string[];
|
|
268
|
+
/**
|
|
269
|
+
* Reverse direction, fanned out across kinds: introspect every introspectable kind off one live
|
|
270
|
+
* connection and flatten into portable objects. The RESOLUTION of "per-kind vs one driver read":
|
|
271
|
+
* the contract is per-kind ({@link KindEngine.introspect}), but a driver backs all of its kinds with
|
|
272
|
+
* ONE shared (memoized) read of `conn` and slices out each kind's objects — so the fan-out here costs
|
|
273
|
+
* a single round-trip, not N. A kind without `introspect` contributes nothing (not introspectable).
|
|
274
|
+
*/
|
|
275
|
+
declare function introspectKinds(registry: KindRegistry, conn: unknown): Promise<PortableObject[]>;
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* One object's change, for display. `kind` is the object kind, `table` its owner (a table name,
|
|
279
|
+
* or the object's own name for db-level objects). `add` carries the new DDL; `remove` carries the
|
|
280
|
+
* `REMOVE` statement (`ddl`) plus the dropped object's prior DDL (`old`, for the unified patch);
|
|
281
|
+
* `change` pairs old↔new.
|
|
282
|
+
*/
|
|
283
|
+
type DiffItem = {
|
|
284
|
+
key: string;
|
|
285
|
+
table: string;
|
|
286
|
+
/** Dialect-defined object kind (e.g. "table"/"field"/"index") — opaque to the display. */
|
|
287
|
+
kind: string;
|
|
288
|
+
/** The source file this object lives in (or lived in, for a removal). Absent if unknown. */
|
|
289
|
+
file?: string;
|
|
290
|
+
} & ({
|
|
291
|
+
op: "add";
|
|
292
|
+
ddl: string;
|
|
293
|
+
} | {
|
|
294
|
+
op: "remove";
|
|
295
|
+
ddl: string;
|
|
296
|
+
old: string;
|
|
297
|
+
} | {
|
|
298
|
+
op: "change";
|
|
299
|
+
before: string;
|
|
300
|
+
after: string;
|
|
301
|
+
});
|
|
302
|
+
interface Diff {
|
|
303
|
+
up: string[];
|
|
304
|
+
down: string[];
|
|
305
|
+
/** Structured per-object changes for the human display (word-level diff). */
|
|
306
|
+
items?: DiffItem[];
|
|
307
|
+
/** Every desired statement (the `next` schema), for the `--full` context view. */
|
|
308
|
+
full?: {
|
|
309
|
+
key: string;
|
|
310
|
+
table: string;
|
|
311
|
+
ddl: string;
|
|
312
|
+
}[];
|
|
313
|
+
}
|
|
314
|
+
/** `true` if the two snapshots define the same objects with identical DDL. */
|
|
315
|
+
declare function isEmptyDiff(diff: Diff): boolean;
|
|
316
|
+
/**
|
|
317
|
+
* Inline word-level diff of two statements: shared tokens dim, removed tokens red, added tokens
|
|
318
|
+
* green (LCS over space-separated tokens). So a changed field shows the whole statement with only
|
|
319
|
+
* the changed words highlighted.
|
|
320
|
+
*/
|
|
321
|
+
declare function tokenDiff(before: string, after: string): string;
|
|
322
|
+
/** Render display items as a git-style file diff: each group headed by its source file path. */
|
|
323
|
+
declare function formatItems(items: DiffItem[], inline?: boolean): string;
|
|
324
|
+
/**
|
|
325
|
+
* A standard **unified diff** of the change, grouped one section per source file (git-style) — for
|
|
326
|
+
* piping through a diff viewer (git's pager / delta). Objects with no file fall back to a section
|
|
327
|
+
* headed by the object's bare name. Each object is a single-line DDL statement, so hunks are
|
|
328
|
+
* line-for-line.
|
|
329
|
+
*/
|
|
330
|
+
declare function formatPatch(diff: Diff): string;
|
|
331
|
+
/** A human-readable view of a diff's forward (and optionally reverse) changes. */
|
|
332
|
+
declare function formatDiff(diff: Diff, opts?: {
|
|
333
|
+
down?: boolean;
|
|
334
|
+
full?: boolean;
|
|
335
|
+
inline?: boolean;
|
|
336
|
+
}): string;
|
|
337
|
+
/**
|
|
338
|
+
* A per-kind breakdown of a set of changes, e.g. `1 Table, 2 Fields`. Counts each item by its
|
|
339
|
+
* structured `kind` and labels it from the registry's per-kind {@link KindRegistry.display} (singular
|
|
340
|
+
* when the count is one), so the summary is correct for every dialect — no DDL parsing.
|
|
341
|
+
*/
|
|
342
|
+
declare function summarizeKinds(registry: KindRegistry, items: readonly {
|
|
343
|
+
kind: string;
|
|
344
|
+
}[]): string;
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* The STORED snapshot (`_snapshot.json`): the canonical schema is portable objects grouped by kind
|
|
348
|
+
* (a {@link KindSnapshot}); DDL is derived generically via the kind registry (`buildKindDiff`/
|
|
349
|
+
* `emitKinds`). Diffed against the next `generate`. `files` maps each object's name to its
|
|
350
|
+
* project-root-relative source file (display-only; attached to diff items by the CLI). Pre-launch:
|
|
351
|
+
* the format is free to change, so there is no on-disk version migration — an unrecognized snapshot
|
|
352
|
+
* is treated as empty (regenerate via `schemic gen --baseline`).
|
|
353
|
+
*/
|
|
354
|
+
interface StoredSnapshot {
|
|
355
|
+
version: 3;
|
|
356
|
+
/** The driver that authored this snapshot ("surrealdb", "postgres", …). */
|
|
357
|
+
driver: string;
|
|
358
|
+
/** Portable objects grouped by kind. */
|
|
359
|
+
schema: KindSnapshot;
|
|
360
|
+
files?: Record<string, string>;
|
|
361
|
+
}
|
|
362
|
+
/** A migration file on disk. The filename is the source of truth — there's no journal. */
|
|
363
|
+
interface Migration {
|
|
364
|
+
/** Filename without the `.surql` extension, e.g. `20260607153045_add_users`. */
|
|
365
|
+
tag: string;
|
|
366
|
+
/** Filename, e.g. `20260607153045_add_users.surql`. */
|
|
367
|
+
file: string;
|
|
368
|
+
}
|
|
369
|
+
/** The empty STORED snapshot — used as `prev` for a `--baseline` generate (diff against nothing). */
|
|
370
|
+
declare const EMPTY_STORED: StoredSnapshot;
|
|
371
|
+
/**
|
|
372
|
+
* Read the stored snapshot. Pre-launch: any snapshot that isn't the current `version: 3` shape (a
|
|
373
|
+
* pre-portable v1/v2, or absent) is treated as EMPTY — regenerate with `schemic gen --baseline`.
|
|
374
|
+
*/
|
|
375
|
+
declare function readSnapshot(metaDir: string): StoredSnapshot;
|
|
376
|
+
declare function writeSnapshot(metaDir: string, snapshot: StoredSnapshot): void;
|
|
377
|
+
/**
|
|
378
|
+
* All migration files in `migrationsDir`, in apply order. Filenames are timestamp-prefixed, so
|
|
379
|
+
* a plain ascending sort is chronological (and legacy `0001_` names sort before timestamped
|
|
380
|
+
* ones). The `meta/` directory and any file not ending in `ext` (the driver's migration extension)
|
|
381
|
+
* are ignored.
|
|
382
|
+
*/
|
|
383
|
+
declare function listMigrations(migrationsDir: string, ext?: string): Migration[];
|
|
384
|
+
/** A sortable UTC timestamp prefix for a new migration, e.g. `20260607153045`. */
|
|
385
|
+
declare function timestamp(date: Date): string;
|
|
386
|
+
/** sha256 of a migration file's contents (drift detection / apply-time bookkeeping). */
|
|
387
|
+
declare function checksum(content: string): string;
|
|
388
|
+
/** Turn a free-form migration name into a filename-safe slug. */
|
|
389
|
+
declare function slug(name: string): string;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Per-kind object filter for `pull`/`diff`/`sync`/`generate`. Each kind is independently
|
|
393
|
+
* included (optionally name-restricted). `DEFINE ACCESS` is OPT-IN everywhere — excluded
|
|
394
|
+
* unless `--access` is given — so an introspection (which redacts access signing keys) can't
|
|
395
|
+
* silently rotate them. Table-scoped objects (fields/indexes/events) follow their table.
|
|
396
|
+
*/
|
|
397
|
+
interface Cat {
|
|
398
|
+
on: boolean;
|
|
399
|
+
/** When set, only these names of the kind are included. */
|
|
400
|
+
names?: Set<string>;
|
|
401
|
+
}
|
|
402
|
+
interface Filter {
|
|
403
|
+
tables: Cat;
|
|
404
|
+
functions: Cat;
|
|
405
|
+
events: Cat;
|
|
406
|
+
access: Cat;
|
|
407
|
+
}
|
|
408
|
+
/** Commander's parse of one `--kind [names]` / `--no-kind` flag: `undefined`/`true`/string/`false`. */
|
|
409
|
+
type FlagValue = string | boolean | undefined;
|
|
410
|
+
interface FilterOpts {
|
|
411
|
+
tables?: FlagValue;
|
|
412
|
+
functions?: FlagValue;
|
|
413
|
+
events?: FlagValue;
|
|
414
|
+
access?: FlagValue;
|
|
415
|
+
}
|
|
416
|
+
/** Build a {@link Filter} from CLI flags. Access is opt-in (`--access`); the rest default to on. */
|
|
417
|
+
declare function parseFilter(o: FilterOpts): Filter;
|
|
418
|
+
/** Attach the per-kind `--tables/--functions/--events/--access [names]` (+ `--no-*`) options. */
|
|
419
|
+
declare function kindFlags(cmd: Command): Command;
|
|
420
|
+
/** Whether a name passes a category gate (kind on + name allowed). Shared with the surreal filters. */
|
|
421
|
+
declare const inCat: (c: Cat, name: string) => boolean;
|
|
422
|
+
/**
|
|
423
|
+
* Whether a portable object passes the filter. A TOP-LEVEL object (no owner) is gated by its kind's
|
|
424
|
+
* category + name. An OWNED object (owner set — an index/event/constraint) FOLLOWS its owner table's
|
|
425
|
+
* inclusion; an `event` is ADDITIONALLY gated by the `events` category. (Fields are substrate nested
|
|
426
|
+
* in their table object, never standalone here.)
|
|
427
|
+
*/
|
|
428
|
+
declare function passesFilter(registry: KindRegistry, o: PortableObject, f: Filter): boolean;
|
|
429
|
+
/** Keep only the portable objects that pass the filter (the {@link filterStructured} analog). */
|
|
430
|
+
declare function filterKinds(registry: KindRegistry, objects: PortableObject[], f: Filter): PortableObject[];
|
|
431
|
+
/**
|
|
432
|
+
* The stored snapshot to persist after a filtered `generate`: INCLUDED objects take their new state
|
|
433
|
+
* from `next`, EXCLUDED objects keep `prev`'s. Dedup by `kind:name` (an included `next` object wins).
|
|
434
|
+
* `files` overlays next on prev.
|
|
435
|
+
*/
|
|
436
|
+
declare function mergeStored(registry: KindRegistry, prev: StoredSnapshot, next: StoredSnapshot, f: Filter): StoredSnapshot;
|
|
437
|
+
/**
|
|
438
|
+
* Restrict the `disk` objects to those that ALSO exist `live` (intersect by `kind:name`) AND pass the
|
|
439
|
+
* filter — for `baseline`. Hand-written schema not yet in the DB stays pending; what's really there is
|
|
440
|
+
* captured. Each field/index/event/constraint is its own object, so intersect-by-key handles them.
|
|
441
|
+
*/
|
|
442
|
+
declare function intersectKinds(registry: KindRegistry, disk: PortableObject[], live: PortableObject[], f: Filter): PortableObject[];
|
|
443
|
+
|
|
444
|
+
/** One rendered object (table / function / access): its const statement + the imports it needs. */
|
|
445
|
+
interface RenderedUnit {
|
|
446
|
+
kind: "table" | "function" | "access";
|
|
447
|
+
/** DB object name (drives the file path). */
|
|
448
|
+
name: string;
|
|
449
|
+
/** The exported const identifier (`User`, `math_add`, …). */
|
|
450
|
+
exportName: string;
|
|
451
|
+
/** The `export const … = define…(…);` statement source (no import lines). */
|
|
452
|
+
code: string;
|
|
453
|
+
/** The `import …` lines this unit needs. */
|
|
454
|
+
imports: string[];
|
|
455
|
+
}
|
|
456
|
+
/** Local-only content a merge would drop when mirroring the DB. */
|
|
457
|
+
interface LocalOnly {
|
|
458
|
+
/** Per existing const: field keys present locally but absent from the DB object. */
|
|
459
|
+
fields: {
|
|
460
|
+
exportName: string;
|
|
461
|
+
fields: string[];
|
|
462
|
+
}[];
|
|
463
|
+
/** Whole consts present locally whose object the DB no longer has. */
|
|
464
|
+
objects: string[];
|
|
465
|
+
}
|
|
466
|
+
interface MergeResult {
|
|
467
|
+
content: string;
|
|
468
|
+
localOnly: LocalOnly;
|
|
469
|
+
}
|
|
470
|
+
/** Options governing what happens to local-only content. */
|
|
471
|
+
interface MergeOptions {
|
|
472
|
+
/** Keep local-only fields (graft them back into the merged object). */
|
|
473
|
+
keepLocalFields: boolean;
|
|
474
|
+
/** Keep local-only consts (objects the DB no longer has). */
|
|
475
|
+
keepLocalObjects: boolean;
|
|
476
|
+
}
|
|
477
|
+
/** What pulling would do to one schema file. */
|
|
478
|
+
interface PullFilePlan {
|
|
479
|
+
/** Path relative to the project root (for display). */
|
|
480
|
+
rel: string;
|
|
481
|
+
/** Absolute path on disk. */
|
|
482
|
+
abs: string;
|
|
483
|
+
/**
|
|
484
|
+
* `create` (new file), `update` (merged edits), `unchanged` (already matches the DB), or `delete`
|
|
485
|
+
* (a file that is purely local-only entities the DB doesn't have — removed when mirroring).
|
|
486
|
+
*/
|
|
487
|
+
action: "create" | "update" | "unchanged" | "delete";
|
|
488
|
+
/** Current file contents (`""` for a new file). */
|
|
489
|
+
before: string;
|
|
490
|
+
/** Contents after the pull (the merged result). */
|
|
491
|
+
after: string;
|
|
492
|
+
/** Local-only content this file would drop when mirroring the DB. */
|
|
493
|
+
localOnly: LocalOnly;
|
|
494
|
+
}
|
|
495
|
+
/** A driver's introspection rendered into a per-file write plan (see a driver's `planPull`). */
|
|
496
|
+
interface PullPlan {
|
|
497
|
+
files: PullFilePlan[];
|
|
498
|
+
}
|
|
499
|
+
/** Apply a plan: write created/updated files, delete local-only files. Returns the paths touched. */
|
|
500
|
+
declare function applyPull(plan: PullPlan): string[];
|
|
501
|
+
/**
|
|
502
|
+
* Merge `units` into `existingSrc`. Each unit replaces the matching `export const` in place (DB wins),
|
|
503
|
+
* preserving everything else in the file. New units are appended; needed imports are unioned in.
|
|
504
|
+
* Local-only fields/objects are reported, and kept or dropped per `opts`.
|
|
505
|
+
*/
|
|
506
|
+
declare function mergeUnits(existingSrc: string, units: RenderedUnit[], opts: MergeOptions): MergeResult;
|
|
507
|
+
/**
|
|
508
|
+
* A compact colored line diff for previews. A new file renders as all-green additions; an edit
|
|
509
|
+
* renders removed (red `-`) / added (green `+`) lines with a little surrounding context (long
|
|
510
|
+
* unchanged runs collapse to `…`).
|
|
511
|
+
*/
|
|
512
|
+
declare function lineDiff(before: string, after: string): string;
|
|
513
|
+
/**
|
|
514
|
+
* A git-style unified diff between two texts, headed by `label` as the file path — for piping to a
|
|
515
|
+
* diff viewer (delta / git's pager). Returns "" when there's no change.
|
|
516
|
+
*/
|
|
517
|
+
declare function unifiedDiff(before: string, after: string, label: string): string;
|
|
518
|
+
/** Colored verb for a pull action (`new` / `update` / `delete` / `unchanged`). */
|
|
519
|
+
declare function actionLabel(action: "create" | "update" | "unchanged" | "delete"): string;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* The dialect-NEUTRAL authoring contract — the only structure the orchestration reads off an
|
|
523
|
+
* authored object (everything else is opaque and handed straight to {@link Driver.explode}). A table
|
|
524
|
+
* contributes just its `name`; this is the upper bound for a driver's table-authoring type. The
|
|
525
|
+
* Surreal `TableDef` is a structural subtype, as is any future dialect's table builder.
|
|
526
|
+
*/
|
|
527
|
+
interface Authored {
|
|
528
|
+
readonly name: string;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* The neutral contract for a standalone (non-table) authored object — an event/function/access. It
|
|
532
|
+
* adds a `kind` discriminant and, for objects owned by a table (e.g. an event), the owner `table`
|
|
533
|
+
* name (so the snapshot can file-link a child object under its parent). The Surreal `StandaloneDef`
|
|
534
|
+
* union is a structural subtype.
|
|
535
|
+
*/
|
|
536
|
+
interface AuthoredDef extends Authored {
|
|
537
|
+
readonly kind: string;
|
|
538
|
+
readonly table?: string;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* A single emitted DDL statement, structured: object identity (`kind`/`name`/`table`) + the dialect
|
|
542
|
+
* `ddl` string, plus an optional clause map (each value an `ALTER … <set>` form) for dialects that
|
|
543
|
+
* diff clause-level. `kind` is a dialect-defined string the orchestration treats opaquely — the
|
|
544
|
+
* SurrealDB `DefineStatement` (with its fixed kind union) is a structural subtype of this.
|
|
545
|
+
*/
|
|
546
|
+
interface Statement {
|
|
547
|
+
kind: string;
|
|
548
|
+
name: string;
|
|
549
|
+
table?: string;
|
|
550
|
+
ddl: string;
|
|
551
|
+
clauses?: Record<string, string>;
|
|
552
|
+
}
|
|
553
|
+
/** Options for {@link Driver.emit} — mirrors the existing `DefineOptions` (e.g. IF NOT EXISTS). */
|
|
554
|
+
interface EmitOptions {
|
|
555
|
+
ifNotExists?: boolean;
|
|
556
|
+
overwrite?: boolean;
|
|
557
|
+
}
|
|
558
|
+
/** Options for {@link Driver.apply}. */
|
|
559
|
+
interface ApplyOptions {
|
|
560
|
+
/**
|
|
561
|
+
* Run the whole batch atomically. `migrate` wraps up/down + `_migrations` bookkeeping in one
|
|
562
|
+
* transaction; a driver that can't MUST surface that (the migration model degrades to best-effort).
|
|
563
|
+
*/
|
|
564
|
+
transactional?: boolean;
|
|
565
|
+
}
|
|
566
|
+
/** Per-connection overrides (url/namespace/credentials) — superset across dialects. */
|
|
567
|
+
interface ConnectionOverrides {
|
|
568
|
+
url?: string;
|
|
569
|
+
namespace?: string;
|
|
570
|
+
database?: string;
|
|
571
|
+
username?: string;
|
|
572
|
+
password?: string;
|
|
573
|
+
authLevel?: string;
|
|
574
|
+
}
|
|
575
|
+
/** The direction a migration is applied in. */
|
|
576
|
+
type MigrationDirection = "up" | "down";
|
|
577
|
+
/** A migration's bookkeeping identity, recorded in the migrations-tracking table. */
|
|
578
|
+
interface MigrationRecord {
|
|
579
|
+
tag: string;
|
|
580
|
+
file: string;
|
|
581
|
+
/** sha of the migration file at apply time (drift detection). */
|
|
582
|
+
checksum: string;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* The apply-time, dialect-specific half of the migration runner. The orchestration (which
|
|
586
|
+
* migrations are pending, ordering, the lock-then-loop) stays driver-neutral in cli/migrate.ts;
|
|
587
|
+
* this capability owns the SQL: the tracking table, the applied-records, the advisory lock, and the
|
|
588
|
+
* atomic apply+record. A driver WITHOUT it can't run migrations (diff/gen still work). `Conn` is the
|
|
589
|
+
* driver's own connection type.
|
|
590
|
+
*/
|
|
591
|
+
interface MigrationStore<Conn = unknown> {
|
|
592
|
+
/** This dialect's migration-file extension, e.g. `".surql"` (SurrealDB) or `".sql"` (Postgres). */
|
|
593
|
+
readonly extension: string;
|
|
594
|
+
/** Render a diff as this dialect's migration-file body (e.g. SurrealQL `IF $direction` up/down). */
|
|
595
|
+
render(tag: string, diff: Diff): string;
|
|
596
|
+
/** Ensure the migrations-tracking table exists. */
|
|
597
|
+
ensure(conn: Conn, table: string): Promise<void>;
|
|
598
|
+
/** Applied migrations: tag -> checksum recorded at apply time. */
|
|
599
|
+
applied(conn: Conn, table: string): Promise<Map<string, string>>;
|
|
600
|
+
/**
|
|
601
|
+
* Apply one migration's `up`/`down` PROGRAM plus its bookkeeping write atomically: on `up` record
|
|
602
|
+
* the migration, on `down` erase it — so the record is written iff the DDL actually applied.
|
|
603
|
+
*/
|
|
604
|
+
apply(conn: Conn, table: string, m: {
|
|
605
|
+
content: string;
|
|
606
|
+
direction: MigrationDirection;
|
|
607
|
+
record: MigrationRecord;
|
|
608
|
+
}): Promise<void>;
|
|
609
|
+
/** Record a migration as applied WITHOUT running its DDL (baseline of an existing DB). */
|
|
610
|
+
record(conn: Conn, table: string, record: MigrationRecord): Promise<void>;
|
|
611
|
+
/** Drop all applied records (baseline-squash reconcile). */
|
|
612
|
+
clear(conn: Conn, table: string): Promise<void>;
|
|
613
|
+
/** Take an advisory lock so two runs can't race — throws if already held. */
|
|
614
|
+
lock(conn: Conn, table: string): Promise<void>;
|
|
615
|
+
/** Release the advisory lock (idempotent). */
|
|
616
|
+
unlock(conn: Conn, table: string): Promise<void>;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* An OPTIONAL throwaway-instance capability for round-trip canonicalization and `sz check`'s
|
|
620
|
+
* migration replay. Absent -> `check`/replay-verification is degraded/unavailable (diff/apply still
|
|
621
|
+
* work, since a kind's `lower`/`introspectAll` already canonicalize).
|
|
622
|
+
*/
|
|
623
|
+
interface ShadowCapability<Conn> {
|
|
624
|
+
/** Apply `ddl` to a fresh scratch DB, introspect it back to portable objects, then drop it. */
|
|
625
|
+
roundTrip(conn: Conn, config: ResolvedConfig, ddl: string): Promise<PortableObject[]>;
|
|
626
|
+
/** Spin up a fully-isolated ephemeral instance (for migration replay). Caller must `stop()`. */
|
|
627
|
+
ephemeral?(): Promise<{
|
|
628
|
+
conn: Conn;
|
|
629
|
+
stop: () => Promise<void>;
|
|
630
|
+
}>;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* A database dialect, expressed as a SET OF KINDS (core-v2). The driver registers its object kinds on
|
|
634
|
+
* `registry`; core orchestrates schema ops GENERICALLY over it (`lowerSchema`/`buildKindDiff`/
|
|
635
|
+
* `emitKinds`/`orderObjects`) — it never names a kind. The driver owns only what isn't generic: the
|
|
636
|
+
* authoring -> kinded `explode`, a single-read `introspectAll`, the connection lifecycle, and the
|
|
637
|
+
* dialect-specific command capabilities. The field/type substrate (`PortableType`/`s.*`) stays core.
|
|
638
|
+
* See docs/kind-registry-flip-plan.md.
|
|
639
|
+
*/
|
|
640
|
+
interface Driver<Conn = unknown, Tbl extends Authored = Authored, Def extends AuthoredDef = AuthoredDef> {
|
|
641
|
+
readonly name: string;
|
|
642
|
+
/** This driver's registered KINDS. Core runs lower/diff/emit/order generically over it. */
|
|
643
|
+
readonly registry: KindRegistry;
|
|
644
|
+
/**
|
|
645
|
+
* Authoring (loaded `defineTable`/standalone defs) -> kinded {@link Definable}s. The driver-side
|
|
646
|
+
* fan-out: one inline-authored table explodes into `[table, ...index, ...event/constraint]`, each
|
|
647
|
+
* tagged with its `kind`. Core then lowers via `lowerSchema(registry, explode(...))` — so
|
|
648
|
+
* `KindEngine.lower` stays 1:1 and the contract needs no explode hook.
|
|
649
|
+
*/
|
|
650
|
+
explode(tables: Tbl[], defs: Def[]): Definable[];
|
|
651
|
+
/**
|
|
652
|
+
* Live connection -> ALL portable objects, fanned across kinds from ONE read (INFO STRUCTURE /
|
|
653
|
+
* pg_catalog). Must canonicalize IDENTICALLY to lowering (a clean apply round-trips to a zero diff)
|
|
654
|
+
* and be COMPLETE (return every diffable kind, else presence-phantom-diffs). `exclude` skips tables
|
|
655
|
+
* by name.
|
|
656
|
+
*/
|
|
657
|
+
introspectAll(conn: Conn, exclude?: Set<string>): Promise<PortableObject[]>;
|
|
658
|
+
connect(config: ResolvedConfig, over?: ConnectionOverrides): Promise<Conn>;
|
|
659
|
+
apply(conn: Conn, statements: string[], opts?: ApplyOptions): Promise<void>;
|
|
660
|
+
/** Tear down a connection opened by {@link connect} (the orchestration owns the lifecycle). */
|
|
661
|
+
close(conn: Conn): Promise<void>;
|
|
662
|
+
readonly shadow?: ShadowCapability<Conn>;
|
|
663
|
+
/** Apply-time migration bookkeeping. Absent -> this driver can't run migrations (diff/gen still do). */
|
|
664
|
+
readonly migrations?: MigrationStore<Conn>;
|
|
665
|
+
/**
|
|
666
|
+
* Diff the LIVE database against the loaded schema into executable up/down DDL. Owns every
|
|
667
|
+
* dialect-specific normalization and apply-time fixup (Surreal: a shadow-DB round-trip to cancel
|
|
668
|
+
* formatting noise, the redacted-access-key swap, and the implicit-wildcard OVERWRITE re-mark), so
|
|
669
|
+
* the result is safe to apply as-is. Backs `diff --live`, `push`, and the baseline reconcile.
|
|
670
|
+
*/
|
|
671
|
+
diffLive?(conn: Conn, config: ResolvedConfig, filter: Filter): Promise<Diff>;
|
|
672
|
+
/** Reduce a live diff (from {@link diffLive}) to the statements `push` applies; `prune: false` keeps removals. */
|
|
673
|
+
syncPlan?(diff: Diff, prune?: boolean): string[];
|
|
674
|
+
/**
|
|
675
|
+
* Render portable objects to per-file source in THIS dialect's `s.*` syntax, filtered — the codegen
|
|
676
|
+
* behind the offline `diff --ts` and `pull`. Takes `PortableObject[]` (this driver's own portable
|
|
677
|
+
* shape): `diff --ts` renders the SNAPSHOT side (stored portable) and the DESIRED side
|
|
678
|
+
* (`lowerSchema(explode(...))`) at MATCHING fidelity so an in-sync schema yields identical files;
|
|
679
|
+
* `pull` renders the introspected DB. The driver re-derives its structured form from the portable
|
|
680
|
+
* objects (parsing its own DDL where needed — docs/kind-registry-flip-plan.md §6b). `single` (a file
|
|
681
|
+
* key) folds everything into one module; otherwise `fileFor` maps each object to its own file.
|
|
682
|
+
*/
|
|
683
|
+
renderSchema?(objects: PortableObject[], filter: Filter, fileFor: (kind: string, name: string) => string, single?: string): Map<string, string>;
|
|
684
|
+
/**
|
|
685
|
+
* The two sides of `diff --ts --live` rendered to per-file source: the live DB (`current`) and the
|
|
686
|
+
* declared schema (`desired`), both normalized through the dialect so an unchanged schema yields
|
|
687
|
+
* identical files.
|
|
688
|
+
*/
|
|
689
|
+
diffTsLive?(conn: Conn, config: ResolvedConfig, filter: Filter, fileFor: (kind: string, name: string) => string, single?: string): Promise<{
|
|
690
|
+
current: Map<string, string>;
|
|
691
|
+
desired: Map<string, string>;
|
|
692
|
+
}>;
|
|
693
|
+
/**
|
|
694
|
+
* Replay every migration into a throwaway engine and diff the result against the schema (`check`).
|
|
695
|
+
* Owns ephemeral-engine selection + setup; `log` receives progress lines. Needs a {@link shadow}-
|
|
696
|
+
* class capability. An empty diff means the migrations reproduce the schema.
|
|
697
|
+
*/
|
|
698
|
+
checkReplay?(config: ResolvedConfig, over: ConnectionOverrides, filter: Filter, log: (msg: string) => void): Promise<Diff>;
|
|
699
|
+
/** Introspect the live DB and plan schema-file codegen (`pull`); writing is the neutral `applyPull`. */
|
|
700
|
+
planPull?(conn: Conn, config: ResolvedConfig, opts: {
|
|
701
|
+
filter: Filter;
|
|
702
|
+
keepLocal?: boolean;
|
|
703
|
+
}): Promise<PullPlan>;
|
|
704
|
+
/** A human-readable server identity for `doctor` (e.g. "SurrealDB 3.1.3"); throws if unreachable. */
|
|
705
|
+
serverInfo?(conn: Conn): Promise<string>;
|
|
706
|
+
/**
|
|
707
|
+
* Run a raw READ query and return rows — for connection RESOLVERS (a multi-connection resolver's
|
|
708
|
+
* `ctx.connections.<name>.query(...)`) and `seed`. The `sql` is this dialect's query language; the
|
|
709
|
+
* orchestration treats the rows opaquely. Absent -> a resolver can't read from this connection.
|
|
710
|
+
*/
|
|
711
|
+
query?<T = unknown>(conn: Conn, sql: string, vars?: Record<string, unknown>): Promise<T[]>;
|
|
712
|
+
/**
|
|
713
|
+
* The dialect-specific files `schemic init` scaffolds, keyed by project-relative path: a
|
|
714
|
+
* connections-only `schemic.config.ts` (using this driver's `<driver>Connection` factory), a sample
|
|
715
|
+
* schema module in this dialect's `s.*`, a seed stub, a `.env.example`, … The CLI writes them
|
|
716
|
+
* verbatim (never overwriting) alongside the dialect-neutral migration snapshot it records itself.
|
|
717
|
+
* Absent -> `schemic init` can't scaffold a project for this driver.
|
|
718
|
+
*/
|
|
719
|
+
initScaffold?(): Record<string, string>;
|
|
720
|
+
/**
|
|
721
|
+
* Scaffold a NEW entity file's contents — the starter `s.*` / `define*` module for an object of
|
|
722
|
+
* `kind` named `name` (e.g. `("table", "user")` -> a `defineTable("user", { … })` module in this
|
|
723
|
+
* dialect's authoring). Returns the file text; the CLI writes it under the kind's
|
|
724
|
+
* {@link KindRegistry.display} folder. THROW for a kind this driver can't author (the CLI surfaces
|
|
725
|
+
* the message). Absent -> `schemic new` is unavailable for this driver.
|
|
726
|
+
*/
|
|
727
|
+
scaffoldEntity?(kind: string, name: string): string;
|
|
728
|
+
}
|
|
729
|
+
/** Register a driver under its `name` (idempotent; last write wins). */
|
|
730
|
+
declare function registerDriver(driver: Driver<unknown>): void;
|
|
731
|
+
/** Look up a registered driver, or throw with the list of known names. */
|
|
732
|
+
declare function getDriver(name: string): Driver<unknown>;
|
|
733
|
+
/** All registered driver names (for help text / config validation). */
|
|
734
|
+
declare function driverNames(): string[];
|
|
735
|
+
|
|
736
|
+
export { registerDriver as $, type Authored as A, type PullPlan as B, type RenderedUnit as C, type Driver as D, unifiedDiff as E, type Filter as F, checksum as G, EMPTY_STORED as H, listMigrations as I, type Migration as J, readSnapshot as K, type LocalOnly as L, type MergeOptions as M, slug as N, timestamp as O, type PullFilePlan as P, writeSnapshot as Q, type ResolvedConfig as R, type StoredSnapshot as S, type ApplyOptions as T, type ConnectionOverrides as U, driverNames as V, type EmitOptions as W, getDriver as X, type MigrationDirection as Y, type MigrationRecord as Z, type MigrationStore as _, type AuthoredDef as a, type ShadowCapability as a0, type Statement as a1, buildKindDiff as a2, type Definable as a3, emitKinds as a4, introspectKinds as a5, type KindDisplay as a6, type KindEngine as a7, type KindPlan as a8, KindRegistry as a9, type KindSnapshot as aa, type KindSpec as ab, lowerSchema as ac, type OrderNode as ad, orderObjects as ae, type PortableObject as af, planKinds as ag, type Ref as ah, type ResolvedDisplay as ai, snapshotKinds as aj, snapshotObjects as ak, loadProject as b, type Diff as c, type DiffItem as d, formatItems as e, formatDiff as f, formatPatch as g, type FilterOpts as h, isEmptyDiff as i, filterKinds as j, inCat as k, loadConfig as l, makeJiti as m, intersectKinds as n, kindFlags as o, mergeStored as p, parseFilter as q, resolveConnectionConfig as r, summarizeKinds as s, tokenDiff as t, passesFilter as u, actionLabel as v, applyPull as w, lineDiff as x, type MergeResult as y, mergeUnits as z };
|