@voltro/sql-mssql 0.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/CHANGELOG.md +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +2458 -0
- package/dist/index.d.ts +252 -0
- package/dist/index.js +618 -0
- package/package.json +46 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { CdcConfig } from '@voltro/database';
|
|
2
|
+
import { ChangeEvent } from '@voltro/database';
|
|
3
|
+
import { ChangeStrategy } from '@voltro/database';
|
|
4
|
+
import { ConfigError } from 'effect';
|
|
5
|
+
import { ConnectionConfig } from '@voltro/database';
|
|
6
|
+
import { Context } from 'effect';
|
|
7
|
+
import { DataStore } from '@voltro/database';
|
|
8
|
+
import { DialectReplicationAdapter } from '@voltro/database';
|
|
9
|
+
import { Effect } from 'effect';
|
|
10
|
+
import { EventEmitter } from 'node:events';
|
|
11
|
+
import { Layer } from 'effect';
|
|
12
|
+
import { ManagedRuntime } from 'effect';
|
|
13
|
+
import { MssqlClient } from '@effect/sql-mssql';
|
|
14
|
+
import { Predicate } from '@voltro/database';
|
|
15
|
+
import { QueryDescriptor } from '@voltro/database';
|
|
16
|
+
import { QueryNamespace } from '@voltro/database';
|
|
17
|
+
import { RawSqlFragment } from '@voltro/database/sql';
|
|
18
|
+
import { RetryDecision } from '@voltro/database';
|
|
19
|
+
import { Row } from '@voltro/database';
|
|
20
|
+
import { SqlClient } from '@effect/sql';
|
|
21
|
+
import { SqlClient as SqlClient_2 } from '@effect/sql/SqlClient';
|
|
22
|
+
import { SqlDialect } from '@voltro/database';
|
|
23
|
+
import { SqlError } from '@effect/sql';
|
|
24
|
+
import { TableLike } from '@voltro/database';
|
|
25
|
+
import { TransactionConnection } from '@effect/sql/SqlClient';
|
|
26
|
+
|
|
27
|
+
export declare const CDC_OFFSETS_TABLE = "_voltro_cdc_offsets";
|
|
28
|
+
|
|
29
|
+
export declare interface ChangeTrackingCdcHandle {
|
|
30
|
+
readonly stop: () => Promise<void>;
|
|
31
|
+
/** The last CT version the reader has advanced past (for a final checkpoint). */
|
|
32
|
+
readonly currentVersion: () => string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare interface ChangeTrackingCdcOptions {
|
|
36
|
+
readonly run: MssqlEffectRunner;
|
|
37
|
+
/** Unqualified table names to tail. Each must have CT enabled (the
|
|
38
|
+
* reader enables them at start via `ensureChangeTracking`). */
|
|
39
|
+
readonly tables: ReadonlyArray<string>;
|
|
40
|
+
/** Poll cadence. Default 500ms. */
|
|
41
|
+
readonly pollIntervalMs?: number;
|
|
42
|
+
/** Resume cursor (decimal string of the CT version). Omit / null →
|
|
43
|
+
* start at the CURRENT version (only future changes). */
|
|
44
|
+
readonly startVersion?: string | null;
|
|
45
|
+
readonly onChange: (event: ChangeEvent) => void;
|
|
46
|
+
/** Called with the CT version after each processed poll (checkpointing). */
|
|
47
|
+
readonly onVersion?: (version: string) => void;
|
|
48
|
+
readonly onError?: (err: unknown) => void;
|
|
49
|
+
/** Called when the reader had to skip forward because the persisted
|
|
50
|
+
* cursor fell below the CT retention floor (auto-cleanup outran us).
|
|
51
|
+
* The caller should treat live state as possibly-stale + re-query. */
|
|
52
|
+
readonly onResync?: () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Parse a `ConnectionConfig` into the mssql-specific shape.
|
|
57
|
+
*
|
|
58
|
+
* Resolution order:
|
|
59
|
+
* 1. `url` — `mssql://user:pw@server:port/database` (also `sqlserver://`)
|
|
60
|
+
* 2. Discrete `host`/`port`/`username`/`password`/`database`
|
|
61
|
+
* 3. Defaults: server `localhost`, port 1433
|
|
62
|
+
*
|
|
63
|
+
* Sane defaults for dev: `trustServer: true` so the docker SQL Server
|
|
64
|
+
* Developer edition's self-signed cert doesn't trip TLS. Production
|
|
65
|
+
* setups override via env.
|
|
66
|
+
*/
|
|
67
|
+
export declare const connectionFromConfig: (config: ConnectionConfig) => MssqlConnection;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Enable Change Tracking on `tables` (idempotent) + confirm the database
|
|
71
|
+
* itself has CT on. Called by the store at CDC start. Enabling per-table
|
|
72
|
+
* CT is a cheap `ALTER TABLE … ENABLE CHANGE_TRACKING`; re-enabling an
|
|
73
|
+
* already-tracked table is a no-op guarded by `sys.change_tracking_tables`.
|
|
74
|
+
*/
|
|
75
|
+
export declare const ensureChangeTracking: (run: MssqlEffectRunner, tables: ReadonlyArray<string>) => Promise<void>;
|
|
76
|
+
|
|
77
|
+
export declare const makeMssqlSqlLayer: (options: MssqlConnection) => ReturnType<typeof MssqlClient.layerConfig>;
|
|
78
|
+
|
|
79
|
+
export declare const makeMssqlSqlLayerFromConfig: (config: ConnectionConfig) => ReturnType<typeof MssqlClient.layerConfig>;
|
|
80
|
+
|
|
81
|
+
export declare const makeMssqlStore: (options: MssqlStoreOptions) => Promise<MssqlStore>;
|
|
82
|
+
|
|
83
|
+
declare interface MssqlAdapterFriend {
|
|
84
|
+
readonly runEffect: <A, E>(effect: Effect.Effect<A, E, SqlClient_2>) => Promise<A>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { MssqlClient }
|
|
88
|
+
|
|
89
|
+
export declare interface MssqlConnection {
|
|
90
|
+
readonly server: string;
|
|
91
|
+
readonly port?: number;
|
|
92
|
+
readonly database?: string;
|
|
93
|
+
readonly username?: string;
|
|
94
|
+
readonly password?: string;
|
|
95
|
+
readonly encrypt?: boolean;
|
|
96
|
+
readonly trustServer?: boolean;
|
|
97
|
+
readonly maxConnections?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export declare const mssqlDialect: SqlDialect;
|
|
101
|
+
|
|
102
|
+
/** Effect runner bound to the store's managed runtime + connection pool
|
|
103
|
+
* (the same `__mssqlReplicationFriend.runEffect` seam the replication
|
|
104
|
+
* adapter uses) — so the CT reader needs no second pool. */
|
|
105
|
+
declare type MssqlEffectRunner = <A, E>(effect: Effect.Effect<A, E, SqlClient_2>) => Promise<A>;
|
|
106
|
+
|
|
107
|
+
export declare const mssqlReplicationAdapter: () => DialectReplicationAdapter;
|
|
108
|
+
|
|
109
|
+
export declare const mssqlRetryFilter: (err: unknown) => RetryDecision;
|
|
110
|
+
|
|
111
|
+
export declare class MssqlStore implements DataStore {
|
|
112
|
+
private readonly sql;
|
|
113
|
+
private readonly runtime;
|
|
114
|
+
private readonly changeStrategy;
|
|
115
|
+
private readonly namespace;
|
|
116
|
+
private readonly emitter;
|
|
117
|
+
private inflightTxns;
|
|
118
|
+
private cdcHandle;
|
|
119
|
+
private cdcCheckpointTimer;
|
|
120
|
+
private cdcPendingVersion;
|
|
121
|
+
private cdcReplicaId;
|
|
122
|
+
private cdcStreamName;
|
|
123
|
+
constructor(sql: SqlClient.SqlClient, runtime: ManagedRuntime.ManagedRuntime<SqlClient.SqlClient | MssqlClient.MssqlClient, never>, changeStrategy?: ChangeStrategy, namespace?: QueryNamespace, sharedEmitter?: EventEmitter);
|
|
124
|
+
/** True when the write path emits its own deltas inline. In 'cdc' mode
|
|
125
|
+
* the CT reader is the sole emitter, so the write path stays silent. */
|
|
126
|
+
private get inlineEmit();
|
|
127
|
+
/**
|
|
128
|
+
* Bind a per-request tenant namespace (a SCHEMA), returning a view that
|
|
129
|
+
* qualifies every table reference to `<schema>.<table>`. Shares this
|
|
130
|
+
* store's pool + emitter + CDC. The schema must already exist — see the
|
|
131
|
+
* migrator's namespace fan-out.
|
|
132
|
+
*/
|
|
133
|
+
withNamespace(namespace: string | null): DataStore;
|
|
134
|
+
/** Qualify a table to this view's namespace (no-op when unset). */
|
|
135
|
+
private nsT;
|
|
136
|
+
/**
|
|
137
|
+
* Friend-handle the mssql replication adapter reads via a structural
|
|
138
|
+
* check on `DataStore` (declared in `./replicationAdapter.ts`). The
|
|
139
|
+
* adapter needs raw SQL access against THIS store's managed runtime
|
|
140
|
+
* to query `sys.dm_tran_database_transactions` /
|
|
141
|
+
* `sys.dm_hadr_database_replica_states` — without a friend handle
|
|
142
|
+
* the adapter would need its own connection pool + config, doubling
|
|
143
|
+
* the deployment cost.
|
|
144
|
+
*
|
|
145
|
+
* The handle stays on the store class as a public-but-prefixed
|
|
146
|
+
* property so the type cast in `adapterFriend(store)` resolves
|
|
147
|
+
* without exporting the runtime to the wider API surface.
|
|
148
|
+
*/
|
|
149
|
+
readonly __mssqlReplicationFriend: MssqlAdapterFriend;
|
|
150
|
+
private executeQuery;
|
|
151
|
+
private executeInsert;
|
|
152
|
+
private executeUpdate;
|
|
153
|
+
private executeInsertMany;
|
|
154
|
+
private executePatchJson;
|
|
155
|
+
private executeDelete;
|
|
156
|
+
private routeEvent;
|
|
157
|
+
query(d: QueryDescriptor): Promise<readonly Readonly<Record<string, unknown>>[]>;
|
|
158
|
+
raw<T extends object = Row>(fragment: RawSqlFragment, _opts?: {
|
|
159
|
+
dependsOn?: ReadonlyArray<string>;
|
|
160
|
+
}): Promise<ReadonlyArray<T>>;
|
|
161
|
+
private runWithEager;
|
|
162
|
+
/* Excluded from this release type: getInternalRunWithEager */
|
|
163
|
+
insert(t: string, r: Row): Promise<Readonly<Record<string, unknown>>>;
|
|
164
|
+
insertMany(t: string, rows: ReadonlyArray<Row>): Promise<readonly Readonly<Record<string, unknown>>[]>;
|
|
165
|
+
patchJson(t: string, pk: string, path: string, value: unknown): Promise<Readonly<Record<string, unknown>> | null>;
|
|
166
|
+
update(t: string, pk: string, p: Readonly<Record<string, unknown>>): Promise<Readonly<Record<string, unknown>> | null>;
|
|
167
|
+
delete(t: string, pk: string): Promise<boolean>;
|
|
168
|
+
updateMany(table: string, patch: Readonly<Record<string, unknown>>, options: {
|
|
169
|
+
where: Predicate;
|
|
170
|
+
}): Promise<number>;
|
|
171
|
+
deleteMany(table: string, options: {
|
|
172
|
+
where: Predicate;
|
|
173
|
+
}): Promise<number>;
|
|
174
|
+
upsert(t: string, r: Row, options: {
|
|
175
|
+
conflictColumns: ReadonlyArray<string>;
|
|
176
|
+
update?: ReadonlyArray<string> | ((existing: Row) => Readonly<Record<string, unknown>>);
|
|
177
|
+
}): Promise<Readonly<Record<string, unknown>>>;
|
|
178
|
+
insertIgnore(t: string, r: Row, options: {
|
|
179
|
+
conflictColumns: ReadonlyArray<string>;
|
|
180
|
+
}): Promise<Readonly<Record<string, unknown>>>;
|
|
181
|
+
private upsertPatch;
|
|
182
|
+
private executeUpsert;
|
|
183
|
+
/**
|
|
184
|
+
* Single-statement native MERGE upsert. Builds a parameterised
|
|
185
|
+
* `MERGE <t> WITH (HOLDLOCK) AS tgt USING (VALUES (…)) AS src (cols)
|
|
186
|
+
* ON tgt.<ck> = src.<ck>
|
|
187
|
+
* WHEN MATCHED THEN UPDATE SET tgt.<c> = src.<c>
|
|
188
|
+
* WHEN NOT MATCHED THEN INSERT (cols) VALUES (src.cols)
|
|
189
|
+
* OUTPUT $action, INSERTED.*;`
|
|
190
|
+
* — every value binds as a parameter (no interpolation), `$action`
|
|
191
|
+
* distinguishes the insert vs update branch so the change event is
|
|
192
|
+
* emitted with the right `op`, and `INSERTED.*` returns the post-image
|
|
193
|
+
* in the SAME round-trip. Only reached for the column-list / default
|
|
194
|
+
* `update` form; the function form can't express its patch in a MERGE.
|
|
195
|
+
*/
|
|
196
|
+
private mergeUpsert;
|
|
197
|
+
private executeInsertIgnore;
|
|
198
|
+
private findByConflict;
|
|
199
|
+
/* Excluded from this release type: emitChange */
|
|
200
|
+
/* Excluded from this release type: startCdcConsumer */
|
|
201
|
+
/* Excluded from this release type: readCdcOffset */
|
|
202
|
+
/* Excluded from this release type: flushCdcOffset */
|
|
203
|
+
/* Excluded from this release type: getInternalExecuteQuery */
|
|
204
|
+
/* Excluded from this release type: getInternalExecuteInsert */
|
|
205
|
+
/* Excluded from this release type: getInternalExecuteInsertMany */
|
|
206
|
+
/* Excluded from this release type: getInternalExecutePatchJson */
|
|
207
|
+
/* Excluded from this release type: getInternalExecuteUpdate */
|
|
208
|
+
/* Excluded from this release type: getInternalExecuteDelete */
|
|
209
|
+
/* Excluded from this release type: getInternalExecuteUpsert */
|
|
210
|
+
/* Excluded from this release type: getInternalExecuteInsertIgnore */
|
|
211
|
+
transactional<T>(work: (tx: DataStore) => Promise<T>): Promise<T>;
|
|
212
|
+
onChange(listener: (event: ChangeEvent) => void): () => void;
|
|
213
|
+
/** 'fleet' under Change Tracking CDC (every replica gets the full
|
|
214
|
+
* stream); 'local' under inline emission. See DataStore.changeScope. */
|
|
215
|
+
get changeScope(): 'local' | 'fleet';
|
|
216
|
+
/** Cross-instance reactivity seam — emit an externally-sourced event to
|
|
217
|
+
* local subscribers without re-persisting. The Change Tracking CDC
|
|
218
|
+
* consumer and `@voltro/plugin-broadcast` route through here. See
|
|
219
|
+
* `DataStore.injectExternalChange`. */
|
|
220
|
+
injectExternalChange(event: ChangeEvent): void;
|
|
221
|
+
run<A, E>(effect: Effect.Effect<A, E, SqlClient.SqlClient>): Promise<A>;
|
|
222
|
+
close(gracePeriodMs?: number): Promise<void>;
|
|
223
|
+
/** Liveness probe — `SELECT 1`. Rejects if the pool can't answer. */
|
|
224
|
+
ping(): Promise<void>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export declare interface MssqlStoreOptions {
|
|
228
|
+
readonly sqlLayer: Layer.Layer<SqlClient.SqlClient | MssqlClient.MssqlClient, ConfigError.ConfigError | SqlError.SqlError, never>;
|
|
229
|
+
readonly tracerLayer?: Layer.Layer<never, never, never>;
|
|
230
|
+
/** `'inline'` (default): the writing instance emits its own deltas.
|
|
231
|
+
* `'cdc'`: a SQL Server Change Tracking reader is the sole emitter, so
|
|
232
|
+
* a write on any replica surfaces on every replica's `onChange`. */
|
|
233
|
+
readonly changeStrategy?: ChangeStrategy;
|
|
234
|
+
/** Change Tracking CDC wiring — required when `changeStrategy: 'cdc'`.
|
|
235
|
+
* `serverId` is unused by mssql (CT has no per-reader identity like a
|
|
236
|
+
* binlog `server_id`); `replicaId` keys the per-pod resume offset and
|
|
237
|
+
* `includeTables` scopes which tables the reader tails. */
|
|
238
|
+
readonly cdcConfig?: CdcConfig;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Start the Change Tracking reader. Resolves once the initial cursor is
|
|
243
|
+
* resolved + the first poll is armed (so the caller can guarantee
|
|
244
|
+
* "reader attached before subscriptions"). The returned handle stops it.
|
|
245
|
+
*/
|
|
246
|
+
export declare const startChangeTrackingCdc: (opts: ChangeTrackingCdcOptions) => Promise<ChangeTrackingCdcHandle>;
|
|
247
|
+
|
|
248
|
+
declare type TxnContext = Context.Tag.Service<typeof TransactionConnection>;
|
|
249
|
+
|
|
250
|
+
export declare const _voltroMssqlCdcOffsetsTable: TableLike;
|
|
251
|
+
|
|
252
|
+
export { }
|