@voltro/sql-mssql 0.65.0 → 0.66.1

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 CHANGED
@@ -39,6 +39,108 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.66.1] — 2026-09-06
43
+
44
+ ### Fixed
45
+
46
+ - **@voltro/cli** — API serve and web start bundles preserve esbuild's caught-import handling: a missing `require()` inside `try/catch` or a caught dynamic import can use its source fallback without a package.json optional-dependency declaration. This fixes the 0.66.0 minified MySQL/MariaDB serve-build regression at mysql2's optional `cardinal` debug highlighter; no direct `cardinal` dependency is needed after upgrading and rebuilding the API. Unhandled missing dependencies and broken installed exports still fail the build. Regression tests execute minified fallback code and build/import production serve bundles for every registered SQL-driver package, plus MariaDB, without requiring database services.
47
+
48
+ ---
49
+
50
+ ## [0.66.0] — 2026-09-06
51
+
52
+ ### ⚠ BREAKING
53
+
54
+ - **@voltro/client, @voltro/cli** — Socket reconnects retry immediately, then after 500 ms with factor 1.5 and a 5 s cap; configure `web.api.recovery.retry: { initialMs, thenMs, factor, maxMs }` beside `graceMs` and rebuild the web app. Web and React Native share the policy, reset it on protocol readiness, preserve subscription bases/revisions and report every scheduled retry in client diagnostics. One transport loop owns retries, preventing competing supervisor rebuilds. Migration for custom `startApiSupervisor`/`BuildClient` adapters: own socket retries inside the transport (or use `buildApiRuntime`); `onSocketIssue` now reports health/backstop signals only, and `retryDelayMs` overrides construction failures only. Header resolvers are evaluated per runtime build, so do not rely on socket cuts to refresh a rotated token: the web host retains bounded auth-error refresh, and custom hosts can call `SupervisorHandle.refreshAuth()`. Explicit `refreshAuth()`/`reconnect()` still rebuild. Ordinary web/RN mounts require no adapter changes.
55
+
56
+ **`voltro update` carries you across this** — codemod `0.66.0/02_socket-retry-owner`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.66.0).
57
+ - **@voltro/database, @voltro/runtime, @voltro/voltro** — `id({ scheme: 'numeric' })` declares the numeric row key it always emitted. The builder was typed `ColumnBuilder<string, 'id'>` for every scheme and the row decoder used `Schema.String` for every id, so a BIGSERIAL / AUTO_INCREMENT / IDENTITY column arrived in application code as a decimal string. It is now `number`, decoded as `Schema.Number`, and `InMemoryDataStore` allocates numbers so the in-memory store and a SQL store agree.
58
+
59
+ Migration — the codemod prints these, and they are ordered by how the failures surface. String operations on such an id stop compiling and the compiler names every one; wrap with `String(id)` at whatever boundary wants text. Equality against a quoted literal (`row.id === '4'`) is the half that keeps compiling and is now permanently false — drop the quotes. Seeds and fixtures spelling the id as a string fail to decode; write them as numbers. Template interpolation needs no change.
60
+
61
+ `reference(target, { storage: 'numeric' })` carries the same numeric type.
62
+
63
+ **`voltro update` carries you across this** — codemod `0.66.0/01_numeric-id-is-a-number`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.66.0).
64
+
65
+ ### Added
66
+
67
+ - **@voltro/client, @voltro/protocol** — Add typed `useConnectionSetup(tag)` and `useMutation(tag).withConnectionSetup(handle)` for acknowledged connection-local setup before initial writes and replay. The Effect-scoped barrier follows physical sockets, restores acknowledged intent, shares concurrent setup attempts and clears on leave, unmount or subject/tenant change. Setup descriptors must explicitly declare connection-scoped idempotency; normal writes keep their keys and replay limits. This is not a durable outbox or an authorization grant. Existing mutation identity helpers retain their signatures.
68
+ - **@voltro/cli** — Generated production Compose stacks run declared boot-lifecycle seeds in the migration job, after the reviewed schema plan has applied. Seeding is therefore one pre-deploy operation rather than something every serving replica repeats on boot.
69
+ - **@voltro/protocol, @voltro/runtime, @voltro/cli, @voltro/voltro** — Mutations can declare `idempotencyScope: 'connection'` for native WebSocket side effects: repeat keys deduplicate within that server-owned connection but execute again on a replacement connection. HTTP RPCs fail with typed `MutationConnectionRequired`, including when dedup is disabled; internal, public REST and agent-tool projections are refused. The default subject-scoped replay behavior of ordinary business mutations is unchanged. This does not replace application authorization, reconnect ordering, durable pending writes or disconnect cleanup.
70
+ - **@voltro/runtime, @voltro/cli, @voltro/testing** — `ctx.connectionState` keeps server-owned, structured-cloneable values for one native RPC connection: mutations defer writes/deletes to the existing commit queue, rollback/retry discards them, and transport close clears the state without allowing late commits to resurrect it. Dev, serve and `makeTestContext({ connection })` share the implementation; `withRpcConnection` selects another connection against the same test store and services. This is neither authentication nor durable membership storage.
71
+ - **@voltro/database, @voltro/runtime, @voltro/sql-postgres, @voltro/sql-mysql, @voltro/sql-sqlite, @voltro/sql-mssql, @voltro/testing, @voltro/voltro** — `store.update(table, key, patch)`, `delete`, `patchJson` and `hardDelete` take EITHER an id string or the columns that identify the row:
72
+
73
+ await store.update('memberships', { userId: u, orgId: o }, { role: 'member' }) await store.delete('memberships', { userId: u, orgId: o })
74
+
75
+ A table declared `.primaryKey(['userId','orgId'])` — or with one explicit key column, which is how a one-to-one shares its parent's key — has no `id`, so it could not be reached by the keyed forms at all. Measured before this, against the in-memory store: `update('memberships', 'u1', …)` returned `null`, `delete('memberships', 'u1')` returned `false`, and the row was untouched. Those are the SAME values a missing row returns.
76
+
77
+ The five store implementations did not agree about it either. The SQL stores emitted `WHERE id = ?` literally — 3 sites in postgres, 8 in mysql, 3 in sqlite, 3 in mssql — so a dialect answered `column "id" does not exist`, while the in-memory store keys rows internally and simply missed. A suite on `makeTestApp` runs the quiet one, so it went green over a call that fails in production.
78
+
79
+ A key is resolved ONCE, by `rowKeyColumns` in `@voltro/database`, so the five implementations cannot drift about what a key is. A bare string still means the `id` column, unchanged; on a table that has none it is refused with the key columns named, rather than answering `null`.
80
+
81
+ Two further defects surfaced when a composite-key table joined the dialect parity harness, both of them older than this change and invisible while the feature had no users:
82
+
83
+ - **MySQL could not INSERT such a row.** A row arriving without an `id` was assumed to be an AUTO_INCREMENT table, so the store recovered the key via `LAST_INSERT_ID()` and failed with "is the primary key AUTO_INCREMENT?" — a question about a column the table never declared. It now reads the post-image back by the values just written when nothing was generated. - **MSSQL could not CREATE one over `text()` columns.** `text()` renders as `NVARCHAR(MAX)` there, which SQL Server refuses as a key column. The emitter already bounds indexed text to `NVARCHAR(450)` for exactly this reason; the primary key's own columns were missing from the set that drives it.
84
+
85
+ `runDialectParity` gained three composite-key scenarios, so all five dialects assert it — the harness that found both of the above.
86
+
87
+ `apiSurface: compatible` — the golden lines that are REPLACED rather than added are the three keyed signatures widening `primaryKey: string` to `primaryKey: RowKey`. A widened parameter accepts everything it did before, so no call site can fail; `@voltro/voltro` is named because the two aggregate goldens carry the same surface and a source package's own report does not pull them along.
88
+ - **@voltro/cli** — `voltro create-project` can add multiple independently cloud-bound projects to one workspace. `add-app --to` and `cloud project link --project` select a target explicitly when needed, and default project port ranges never overlap. API apps now receive manifest/config ports alongside web apps. While the `compose` baseline is active, project/app changes and `voltro baseline sync` regenerate every service plus per-project PostgreSQL databases from those manifests. The generated stack pins PostgreSQL 18.6 and lets containerized web dev resolve its API proxy through `VOLTRO_API_ORIGIN` service DNS.
89
+ - **@voltro/database** — `.primaryKey(['memberId'])` accepts a single explicit natural or reference column as well as a composite set. It previously refused one field and pointed the caller at `id()`, which made a one-to-one shared primary-key/foreign-key impossible to declare without inventing a second key column.
90
+ - **@voltro/database** — `reference(target, { storage: 'numeric' })` declares a foreign key to a table whose primary key is numeric, without disguising it as a plain integer column: row schemas decode it as a number, every SQL renderer emits the dialect's 64-bit integer form, snapshots and introspection round-trip the storage kind, and the migration planner no longer churns it back to text.
91
+
92
+ `reference(target, { column: 'key' })` targets a unique alternate key instead of assuming `id`. Deferred cyclic constraints, subject graphs and test factories carry that target column through. The default remains text-and-`id`.
93
+ - **@voltro/protocol, @voltro/cli, @voltro/testing** — Custom REST handlers receive the per-request application context as `ctx.app`. Development, production `serve` and `makeTestApp` bind the same surface, so a REST webhook can publish a typed event or reach cache, KV, workflows and webhooks exactly as an RPC handler does — without a protocol-to-runtime dependency and without stepping outside the framework to do it.
94
+ - **@voltro/protocol** — Typed REST handlers can shape their own response. `response(body, { status, headers })` selects a status and headers, including repeated values such as the several `Set-Cookie` lines a login emits; `restError(status, body, headers)` preserves an application-defined JSON error contract instead of flattening it to the framework's shape; binary responses can pick status and headers the same way. `RestRouteContext` exposes the byte-exact request body alongside method, path and query, so a signed webhook or a multipart protocol no longer has to drop down to a raw plugin route to see the bytes it must verify.
95
+
96
+ `requireAuthenticated()` gives a protected REST route one consistent 401 path, distinct from the 403 a scope check produces.
97
+
98
+ `apiSurface: compatible` — the golden's one replaced line is the handler's return type widening from `Promise<O> | O` to `Promise<O | RestResponse<O>> | O | RestResponse<O>`. A handler that returns `O` is still assignable, so no declaration site breaks. The one place it is observable is a consumer who reads the descriptor's `handler` type and calls it directly, whose result is now the wider union; the supported way to exercise a route is `makeTestApp`, which is unaffected.
99
+ - **@voltro/database, @voltro/runtime, @voltro/sql-postgres, @voltro/sql-mysql, @voltro/sql-mssql, @voltro/sql-sqlite, @voltro/sql-turso, @voltro/testing, @voltro/voltro** — Upserts accept `updateValues` for additional conflict-only assignments without modifying the INSERT row or changing native column-list upserts into read/compute callbacks. Defined values override the selected update projection; undefined is omitted and identity rewrites are refused. The typed, Effect, replication, transaction and codec surfaces preserve the option, including row-bound encryption. Covered by the shared live-dialect parity suite, including both Turso backends. Existing option fields and calls remain compatible; this is the persistence foundation, not activation of column `.onUpdate()` callbacks.
100
+
101
+ ### Fixed
102
+
103
+ - **@voltro/client** — Keep a transport-interrupted mutation pending through repeated native `MutationInFlight` replies while its original replay window still permits it. Preserve one key, pending slot and optimistic patch; recheck identity, capability, transport and connection setup before each attempt. Do not renew the window, silently accept a failed write, retry other handler failures or busy-loop on malformed non-positive server delays.
104
+ - **@voltro/client** — `useOutbox` drains online enqueues and restored writes, joins concurrent replay calls, includes writes appended during an acknowledgement, and stops at every failed predecessor without a render-driven retry loop. A subsequent explicit retry or reconnection can retry transient failures; conflicts still require resolution. This scheduling fix does not add a durable-save acknowledgement or cross-tab ownership.
105
+ - **@voltro/client** — Keep late failures from ended physical mutation attempts from marking a healed replacement socket disconnected. Native mutation error events record `affectsTransport: false` when their lifetime evidence proves this distinction; the original failure, commit uncertainty and raw error reporting remain intact. Emitters without native lifetime evidence retain the conservative fallback.
106
+ - **@voltro/database, @voltro/runtime, @voltro/sql-postgres, @voltro/sql-mysql, @voltro/sql-mssql, @voltro/sql-sqlite, @voltro/testing, voltro** — Request-scoped stores evaluate column `.onUpdate()` before validation and encryption for keyed/bulk updates, soft deletes, upsert conflicts and JSON patches. `patchJson` accepts optional companion assignments in the same database UPDATE; object merges are shallow and retain JSON nulls. SQLite-family escaped object keys are compared and preserved after exactly one decoding step, based on a constant in-statement engine probe. Raw stores do not run application callbacks. Scalar JSON roots on SQL Server remain a separate schema-validation limitation.
107
+ - **@voltro/cli** — A generated multi-project Compose stack probes each API's declared port. The generator added one but probed the neighbouring web app's port instead, so an API could be marked healthy without ever being tested — and a web service that correctly waited for API readiness deadlocked behind a check that was never going to describe it.
108
+ - **@voltro/cli** — Production serve/start and SSR bundles now resolve dependencies with the bundler's actual import/require conditions. Installed ESM-only transitive packages no longer become bare external imports that fail after strict-pnpm deployment or pruning. Missing required dependencies and broken installed exports fail the build instead of being reported as optional peers; only actually absent packages declared optional by their importer use that fallback. Native runtime shims remain intact. If this exposes a previously hidden missing dependency, install/fix it in its importing package rather than labelling a required import optional. Real esbuild and Vite tests cover conditional exports, strict transitive resolution, optional and required failures, and execution after relocation without node_modules.
109
+ - **@voltro/cli** — `voltro doctor`'s `cron/bucket-on-wall-clock` follows value references into relative app helpers, including renamed imports, namespace members and re-exports. A `new Date()` in a called `midnightUtcForToday()` is now attributed to its cron in text and `--json` output. Unused helper exports, comments, strings and types do not supply either the wall clock or a clearing `scheduledAt` read. This remains a static advisory, not bucket-key data-flow proof; package imports, tsconfig aliases and dynamic imports are not followed.
110
+ - **@voltro/i18n** — `placeholdersOf` is now exported from `@voltro/i18n`'s public entry, including the built JavaScript and declarations. The 0.64.0 changelog promised that export, but it existed only in the internal catalog module; its tests imported that module and missed the absent package export. Those tests now exercise the public entry. No application change is needed unless you want to use the helper.
111
+ - **@voltro/runtime** — `InMemoryDataStore` keeps seeded rows from tables that deliberately have no `id` column — natural and composite primary keys — and inserts into those tables no longer fabricate one. The store assigns an internal map identity instead, which is what a SQL store does, rather than leaking an undeclared field into application rows.
112
+
113
+ Its numeric sequence now starts above the highest seeded id, so the first generated insert can no longer land on top of a seed row.
114
+ - **@voltro/sql-turso** — File-backed libsql connections enable WAL at startup so readers from completed interactive transactions do not block subsequent writers' commits. Remote transports retain server-owned journal configuration. Local-file concurrent-transaction tests require no Turso Cloud account; network and replica synchronization still need separate live validation.
115
+ - **@voltro/runtime** — The in-memory store now applies registered schema literal defaults (including timestamp `now`, JSON and arrays) and implicit nullable values on insert, including transactional, bulk, upsert-insert and insert-ignore-insert paths. Returned rows and change events contain the populated values; explicit values, seeded rows and conflict-update fields are preserved. Mutable defaults are copied per row. App-side factories/computed values remain owned by the shared mutation wrapper; this does not emulate generated SQL expressions or certify database-dialect parity.
116
+ - **@voltro/sql-mssql** — Bind Date parameters as `DateTime2` and strings as `NVarChar`, matching the schema before values reach SQL Server. This preserves milliseconds, dates before 1753, Unicode text and JSON keys/values across row writes, predicates and raw SQL. Explicit NULL remains `NVarChar`. The direct `tedious` dependency uses the version already locked for the SQL client.
117
+ - **@voltro/runtime, @voltro/cli, @voltro/protocol, @voltro/plugin-audit** — Every trace id now comes from one function, `newTraceId()` in `@voltro/runtime` — 128 random bits as 32 hex, the shape a `traceparent` parser, a collector and `voltro traces` all accept.
118
+
119
+ Seventeen call sites minted their own, and three of the shapes were not ids. `incoming:<webhookId>`, `reaction:<workflow>`, `schedule:<name>-<ms>` and the literal `'webhooks:trigger'` were CONSTANTS — five of them wrote the identical string into `traceId` and `source`, so the trace id was the caller's label wearing a second hat and every delivery a process handled shared one trace, with the waterfall merging unrelated work. Six more carried `public-`/`grpc-`/`rest-` plus eight base36 characters, about 41 bits, which collides well inside a busy day. One passed `randomUUID()` unstripped, 36 characters with dashes, which no `traceparent` parser accepts — including this framework's own.
120
+
121
+ The entry-point kind is not lost: it lives in the `source` field beside the id, which is where it was already being written.
122
+
123
+ Plugin HTTP routes now carry a real one too. `PluginHttpRouteEvent` gains `traceId` — the caller's `traceparent` where one arrived, freshly minted otherwise — and the audit trail records it instead of the empty string it wrote before, so an incident review can join a login to what it caused. `apiSurface: compatible`: an observer that ignores the new field is unaffected, and the framework is the only producer of the event.
124
+
125
+ A guard in `@voltro/runtime` now asks the question over the whole tree rather than per call site, because the previous test pinned exactly one of them and the same defect went on living at sixteen others.
126
+ - **@voltro/database, @voltro/runtime** — Declarative migrations now retain partial-index predicates and partial uniqueness on new and existing tables, introspect native filters on PostgreSQL/SQLite/SQL Server, detect same-name predicate changes and invalid PostgreSQL online indexes, and include predicates in drift fingerprints. A partial unique index is never emitted as a full UNIQUE constraint. Existing qualifying duplicates refuse the apply without automatic data repair; a pre-DDL conflict check protects replacements when the current column shape supports it. MySQL/MariaDB uniqueActive lowering remains; unlowered partial indexes are refused by the declarative path. SQL normalization is conservative, not a general expression-equivalence prover.
127
+
128
+ Memory upsert now addresses matched rows through their actual conflict-column key instead of assuming an id column, both directly and transactionally. Natural/composite primary-key rows preserve independent fields, rollback and post-commit events. The schema/API spelling is unchanged and needs no source codemod; projects upgrading must review and apply the newly visible missing constraints, resolving conflicting historical data explicitly rather than deleting it automatically.
129
+ - **@voltro/cli** — Production workspace WebSocket proxies preserve the browser's Host header, so the native same-origin guard accepts legitimate requests behind internal Docker or cluster hostnames while continuing to reject foreign and opaque origins.
130
+ - **@voltro/protocol** — REST descriptors reject undeclared descendant paths before decoding, resolving identity or executing their handler, instead of accidentally serving parent data or performing parent writes. Named parameters, trailing slashes, explicit splats and raw plugin HTTP prefix routes keep their declared behavior.
131
+ - **@voltro/runtime** — Isolate connection credentials across HTTP/WebSocket protocols and listeners, and deliver every disconnect to both runtime cleanup and the RPC core. Executors receive the server-owned `ctx.request.connection` identity, transport and close signal; numeric client IDs remain opaque process-local handles. Connection state is shared across bundled and external module copies.
132
+ - **@voltro/cli** — `voltro prune-runtime` now preserves package-resolution metadata for rooted native dependencies, so packages such as `argon2` remain importable in pruned production images.
133
+ - **@voltro/sql-sqlite, @voltro/sql-turso, @voltro/testing** — SQLite-family raw stores now encode registered JSON and array columns on every row-write path, including insertMany, updateMany, callback upserts and conflict-only updateValues. Previously objects could fail binding or reach Turso as invalid JSON when a request codec had not already serialized them. Shared live-dialect tests cover direct and transactional writes over SQLite and both Turso clients as well as the other SQL stores.
134
+ - **@voltro/testing** — Declared events published by a mutation under `invoke` now use the transaction's commit queue, including subject/tenant re-scopes; rollback and deadlock replay discard failed-attempt notifications, while untransacted actions still publish immediately.
135
+ - **@voltro/cli** — Apply the shared page-safe security-header policy to both web listeners, including streaming, assets and errors, with `security.headers` configuration and route-owned overrides. Do not derive HSTS from untrusted forwarded headers. Proxy native runtime capability discovery in production as well as dev: one public GET per configured workspace API, not the development inspect surface. Named and custom socket paths reach the actual server's replay capability; ambiguous custom socket parents fail explicitly instead of advertising the wrong API. No replay support or successful writes are synthesized by the proxy.
136
+
137
+ ### Internal (no consumer-facing effect)
138
+
139
+ - **@voltro/cli** — Building the CLI package now gives its declaration-rollup process a bounded 6144 MiB heap, and packing delegates to the same build script. The seven-entry rollup exhausted the default roughly 4 GiB heap even in an isolated Node 24 build; the measured build succeeds with the scoped allowance. This does not change the memory settings of application builds or serving processes, and does not disable declaration validation or increase build concurrency.
140
+ - The local release gate now executes the CI test planner with all packages and both CLI projects, rather than skipping its GitHub-specific preparation and reading missing shard files. Missing filters or project selectors fail before tests start; the coverage verifier also refuses a missing expected-package plan. Selftests execute the workflow's actual producer/consumer handoff. MySQL test fixtures check authenticated TCP readiness and distinguish cold initialization from failed steady-state healthchecks. The Postgres partial-index contract creates and cleans up its own database on the regular fixture server, so all seven assertions run without an additional opt-in flag in both local and CI gates. Row-history test files run serially because their live fixtures install and write the same fixed-name Postgres table; parallel setup could deadlock before the history assertions ran when the schema was cold.
141
+
142
+ ---
143
+
42
144
  ## [0.65.0] — 2026-09-04
43
145
 
44
146
  ### ⚠ BREAKING
package/dist/index.d.ts CHANGED
@@ -18,11 +18,13 @@ import { QueryNamespace } from '@voltro/database';
18
18
  import { RawSqlFragment } from '@voltro/database/sql';
19
19
  import { RetryDecision } from '@voltro/database';
20
20
  import { Row } from '@voltro/database';
21
+ import { RowKey } from '@voltro/database';
21
22
  import { SqlClient } from '@effect/sql';
22
23
  import { SqlClient as SqlClient_2 } from '@effect/sql/SqlClient';
23
24
  import { SqlDialect } from '@voltro/database';
24
25
  import { SqlError } from '@effect/sql';
25
26
  import { TransactionConnection } from '@effect/sql/SqlClient';
27
+ import { UpsertOptions } from '@voltro/database';
26
28
  import { _voltroMssqlCdcOffsetsTable } from '@voltro/database';
27
29
  import { WriteAttribution } from '@voltro/database';
28
30
 
@@ -246,7 +248,7 @@ export declare class MssqlStore implements DataStore {
246
248
  private writeWithRecorders;
247
249
  insert(t: string, r: Row): Promise<Readonly<Record<string, unknown>>>;
248
250
  insertMany(t: string, rows: ReadonlyArray<Row>): Promise<readonly Readonly<Record<string, unknown>>[]>;
249
- patchJson(t: string, pk: string, path: string, value: unknown): Promise<Readonly<Record<string, unknown>> | null>;
251
+ patchJson(t: string, pk: string, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>): Promise<Readonly<Record<string, unknown>> | null>;
250
252
  update(t: string, pk: string, p: Readonly<Record<string, unknown>>): Promise<Readonly<Record<string, unknown>> | null>;
251
253
  delete(t: string, pk: string): Promise<boolean>;
252
254
  updateMany(table: string, patch: Readonly<Record<string, unknown>>, options: {
@@ -257,14 +259,10 @@ export declare class MssqlStore implements DataStore {
257
259
  }): Promise<number>;
258
260
  /* Excluded from this release type: executeUpdateMany */
259
261
  /* Excluded from this release type: executeDeleteMany */
260
- upsert(t: string, r: Row, options: {
261
- conflictColumns: ReadonlyArray<string>;
262
- update?: ReadonlyArray<string> | ((existing: Row) => Readonly<Record<string, unknown>>);
263
- }): Promise<Readonly<Record<string, unknown>>>;
262
+ upsert(t: string, r: Row, options: UpsertOptions): Promise<Readonly<Record<string, unknown>>>;
264
263
  insertIgnore(t: string, r: Row, options: {
265
264
  conflictColumns: ReadonlyArray<string>;
266
265
  }): Promise<Readonly<Record<string, unknown>>>;
267
- private upsertPatch;
268
266
  private executeUpsert;
269
267
  /**
270
268
  * Single-statement native MERGE upsert. Builds a parameterised
package/dist/index.js CHANGED
@@ -1,31 +1,34 @@
1
1
  import { MssqlClient as e } from "@effect/sql-mssql";
2
- import { Config as t, Duration as n, Effect as r, Layer as i, ManagedRuntime as a, Option as o, Redacted as s } from "effect";
3
- import { CDC_OFFSETS_TABLE as c, DEFAULT_ACQUIRE_TIMEOUT_MS as l, EagerCardinalityError as u, MSSQL_TRANSIENT_NUMBERS as d, _voltroMssqlCdcOffsetsTable as f, attachEagerLoads as p, attributionFields as m, bulkInsertLimitsFor as h, causeBranches as g, chunkRowsForInsert as _, compileEagerJson as v, compilePredicate as y, compileRawFragment as ee, compileSelect as b, decodeRowsFromSchema as x, encodeRowForSchema as S, externalChangeEvent as te, hasEagerLoads as ne, isTableReactive as C, makeEagerFallbackReporter as re, observeDbOp as w, qualifyTable as T, quoteIdent as E, raiseChangeListenerCeiling as ie, recordsTable as D, requireTable as O, runStoreTransaction as k, runWriteRecorders as A, stampGeneratedId as j, stampGeneratedIds as M, withCapturedAttribution as ae } from "@voltro/database";
4
- import { EventEmitter as oe } from "node:events";
5
- import { createLogger as N } from "@voltro/logger";
6
- import { SqlClient as P, TransactionConnection as F } from "@effect/sql/SqlClient";
2
+ import { TYPES as t } from "tedious";
3
+ import { Config as n, Duration as r, Effect as i, Layer as a, ManagedRuntime as o, Option as s, Redacted as c } from "effect";
4
+ import { CDC_OFFSETS_TABLE as l, DEFAULT_ACQUIRE_TIMEOUT_MS as u, EagerCardinalityError as d, MSSQL_TRANSIENT_NUMBERS as f, _voltroMssqlCdcOffsetsTable as p, attachEagerLoads as m, attributionFields as h, bulkInsertLimitsFor as g, causeBranches as _, chunkRowsForInsert as v, compileEagerJson as y, compileJsonPatch as b, compilePredicate as x, compileRawFragment as ee, compileSelect as S, decodeRowsFromSchema as C, encodeRowForSchema as w, externalChangeEvent as te, hasEagerLoads as ne, isTableReactive as T, jsonPatchAssignments as re, makeEagerFallbackReporter as E, observeDbOp as D, qualifyTable as ie, quoteIdent as ae, raiseChangeListenerCeiling as oe, recordsTable as O, requireTable as se, rowKeyColumns as k, runStoreTransaction as A, runWriteRecorders as j, stampGeneratedId as M, stampGeneratedIds as ce, upsertUpdatePatch as N, upsertUpdateValues as P, withCapturedAttribution as le } from "@voltro/database";
5
+ import { EventEmitter as ue } from "node:events";
6
+ import { createLogger as F } from "@voltro/logger";
7
+ import { SqlClient as I, TransactionConnection as L } from "@effect/sql/SqlClient";
7
8
  //#region src/sqlLayer.ts
8
- var se = {
9
+ var R = {
9
10
  ...e.defaultParameterTypes,
10
- null: e.defaultParameterTypes.object
11
- }, ce = (e) => {
12
- let t = e.acquireTimeoutMs ?? l;
11
+ null: t.NVarChar,
12
+ Date: t.DateTime2,
13
+ string: t.NVarChar
14
+ }, de = (e) => {
15
+ let t = e.acquireTimeoutMs ?? u;
13
16
  return t > 0 ? t : void 0;
14
- }, I = (r) => {
15
- let i = ce(r);
17
+ }, z = (t) => {
18
+ let i = de(t);
16
19
  return e.layerConfig({
17
- server: t.succeed(r.server),
18
- parameterTypes: t.succeed(se),
19
- ...i === void 0 ? {} : { connectTimeout: t.succeed(n.millis(i)) },
20
- ...r.port === void 0 ? {} : { port: t.succeed(r.port) },
21
- ...r.database === void 0 ? {} : { database: t.succeed(r.database) },
22
- ...r.username === void 0 ? {} : { username: t.succeed(r.username) },
23
- ...r.password === void 0 ? {} : { password: t.succeed(s.make(r.password)) },
24
- ...r.encrypt === void 0 ? {} : { encrypt: t.succeed(r.encrypt) },
25
- ...r.trustServer === void 0 ? {} : { trustServer: t.succeed(r.trustServer) },
26
- ...r.maxConnections === void 0 ? {} : { maxConnections: t.succeed(r.maxConnections) }
20
+ server: n.succeed(t.server),
21
+ parameterTypes: n.succeed(R),
22
+ ...i === void 0 ? {} : { connectTimeout: n.succeed(r.millis(i)) },
23
+ ...t.port === void 0 ? {} : { port: n.succeed(t.port) },
24
+ ...t.database === void 0 ? {} : { database: n.succeed(t.database) },
25
+ ...t.username === void 0 ? {} : { username: n.succeed(t.username) },
26
+ ...t.password === void 0 ? {} : { password: n.succeed(c.make(t.password)) },
27
+ ...t.encrypt === void 0 ? {} : { encrypt: n.succeed(t.encrypt) },
28
+ ...t.trustServer === void 0 ? {} : { trustServer: n.succeed(t.trustServer) },
29
+ ...t.maxConnections === void 0 ? {} : { maxConnections: n.succeed(t.maxConnections) }
27
30
  });
28
- }, L = (e) => {
31
+ }, B = (e) => {
29
32
  let t = e.get("sslmode");
30
33
  if (t !== null) {
31
34
  if (t === "require") return !0;
@@ -40,7 +43,7 @@ var se = {
40
43
  throw Error(`DB_URL '?${t}=${n}' is not supported by the mssql dialect — use 'true'/'1' or 'false'/'0'.`);
41
44
  }
42
45
  }
43
- }, R = (e) => {
46
+ }, V = (e) => {
44
47
  let t = e.acquireTimeoutMs === void 0 ? {} : { acquireTimeoutMs: e.acquireTimeoutMs }, n = (e) => e === void 0 ? {} : {
45
48
  encrypt: e,
46
49
  ...e ? { trustServer: !0 } : {}
@@ -53,7 +56,7 @@ var se = {
53
56
  username: decodeURIComponent(r.username || "sa"),
54
57
  password: decodeURIComponent(r.password || ""),
55
58
  database: r.pathname.replace(/^\//, "") || "master",
56
- ...n(e.ssl ?? L(r.searchParams)),
59
+ ...n(e.ssl ?? B(r.searchParams)),
57
60
  ...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections },
58
61
  ...t
59
62
  };
@@ -68,7 +71,7 @@ var se = {
68
71
  ...e.maxConnections === void 0 ? {} : { maxConnections: e.maxConnections },
69
72
  ...t
70
73
  };
71
- }, z = (e) => I(R(e)), B = d, V = (e) => {
74
+ }, H = (e) => z(V(e)), fe = f, U = (e) => {
72
75
  let t = e;
73
76
  for (let e = 0; e < 5 && typeof t == "object" && t; e++) {
74
77
  let e = t.number;
@@ -77,36 +80,36 @@ var se = {
77
80
  if (typeof n == "string") return n;
78
81
  t = t.cause;
79
82
  }
80
- }, H = (e) => g(e).some((e) => {
81
- let t = V(e);
82
- return t !== void 0 && B.has(t);
83
- }), U = /* @__PURE__ */ new Set(["2601", "2627"]), W = (e) => {
84
- let t = V(e);
85
- return t !== void 0 && U.has(t);
86
- }, G = (e) => H(e) ? "retry" : "noRetry", le = {
83
+ }, W = (e) => _(e).some((e) => {
84
+ let t = U(e);
85
+ return t !== void 0 && fe.has(t);
86
+ }), pe = /* @__PURE__ */ new Set(["2601", "2627"]), G = (e) => {
87
+ let t = U(e);
88
+ return t !== void 0 && pe.has(t);
89
+ }, K = (e) => W(e) ? "retry" : "noRetry", me = {
87
90
  I: "insert",
88
91
  U: "update",
89
92
  D: "delete"
90
- }, K = (e, t) => e(r.gen(function* () {
91
- let e = yield* P;
93
+ }, q = (e, t) => e(i.gen(function* () {
94
+ let e = yield* I;
92
95
  if (((yield* e`
93
96
  SELECT COUNT(*) AS ${e("on")}
94
97
  FROM sys.change_tracking_databases
95
- WHERE database_id = DB_ID()`)[0]?.on ?? 0) === 0) return yield* r.fail(/* @__PURE__ */ Error("mssql Change Tracking CDC requires CT enabled on the database. Run `ALTER DATABASE <db> SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)`, or set CDC=0 for inline emit."));
98
+ WHERE database_id = DB_ID()`)[0]?.on ?? 0) === 0) return yield* i.fail(/* @__PURE__ */ Error("mssql Change Tracking CDC requires CT enabled on the database. Run `ALTER DATABASE <db> SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)`, or set CDC=0 for inline emit."));
96
99
  for (let n of t) yield* e.unsafe(`IF NOT EXISTS (
97
100
  SELECT 1 FROM sys.change_tracking_tables
98
- WHERE object_id = OBJECT_ID(${q(n)})
101
+ WHERE object_id = OBJECT_ID(${J(n)})
99
102
  )
100
- ALTER TABLE ${J(n)} ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = OFF)`);
101
- })).then(() => void 0), q = (e) => `'${e.replace(/'/g, "''")}'`, J = (e) => `[${e.replace(/\]/g, "]]")}]`, Y = async (e) => {
102
- let t = N({ scope: "voltro:mssql:cdc" }), n = e.pollIntervalMs ?? 500;
103
- await K(e.run, e.tables);
104
- let i = async () => (await e.run(r.gen(function* () {
105
- return yield* (yield* P)`SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_CURRENT_VERSION()) AS v`;
106
- })))[0]?.v ?? "0", a = e.startVersion ?? await i(), o = !1, s = null, c = !1, l = (e, t) => BigInt(e) > BigInt(t), u = async (n, o) => {
107
- let s = await e.run(r.gen(function* () {
108
- let e = yield* P, t = e.unsafe(J(n)), r = (yield* e`
109
- SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID(${e.unsafe(q(n))}))) AS floor`)[0]?.floor;
103
+ ALTER TABLE ${Y(n)} ENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = OFF)`);
104
+ })).then(() => void 0), J = (e) => `'${e.replace(/'/g, "''")}'`, Y = (e) => `[${e.replace(/\]/g, "]]")}]`, X = async (e) => {
105
+ let t = F({ scope: "voltro:mssql:cdc" }), n = e.pollIntervalMs ?? 500;
106
+ await q(e.run, e.tables);
107
+ let r = async () => (await e.run(i.gen(function* () {
108
+ return yield* (yield* I)`SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_CURRENT_VERSION()) AS v`;
109
+ })))[0]?.v ?? "0", a = e.startVersion ?? await r(), o = !1, s = null, c = !1, l = (e, t) => BigInt(e) > BigInt(t), u = async (n, o) => {
110
+ let s = await e.run(i.gen(function* () {
111
+ let e = yield* I, t = e.unsafe(Y(n)), r = (yield* e`
112
+ SELECT CONVERT(VARCHAR(40), CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID(${e.unsafe(J(n))}))) AS floor`)[0]?.floor;
110
113
  return r != null && BigInt(o) < BigInt(r) ? {
111
114
  resync: !0,
112
115
  rows: []
@@ -120,11 +123,11 @@ var se = {
120
123
  };
121
124
  }));
122
125
  if (s.resync) {
123
- t.warn("cdc: CT retention floor passed our cursor — resyncing to current version", { table: n }), a = await i(), e.onResync?.(), e.onVersion?.(a);
126
+ t.warn("cdc: CT retention floor passed our cursor — resyncing to current version", { table: n }), a = await r(), e.onResync?.(), e.onVersion?.(a);
124
127
  return;
125
128
  }
126
129
  for (let t of s.rows) {
127
- let r = le[t.__ct_op];
130
+ let r = me[t.__ct_op];
128
131
  if (!r) continue;
129
132
  let { __ct_op: i, __ct_id: a, ...o } = t;
130
133
  if (r === "delete") e.onChange({
@@ -134,7 +137,7 @@ var se = {
134
137
  new: null
135
138
  });
136
139
  else {
137
- let t = x([o], n, "mssql")[0];
140
+ let t = C([o], n, "mssql")[0];
138
141
  e.onChange({
139
142
  table: n,
140
143
  op: r,
@@ -147,7 +150,7 @@ var se = {
147
150
  if (!(o || c)) {
148
151
  c = !0;
149
152
  try {
150
- let t = await i();
153
+ let t = await r();
151
154
  if (l(t, a)) {
152
155
  for (let t of e.tables) {
153
156
  if (o) break;
@@ -175,26 +178,26 @@ var se = {
175
178
  },
176
179
  currentVersion: () => a
177
180
  };
178
- }, X = N({ scope: "voltro:mssql" }), Z = async (e) => {
181
+ }, Z = F({ scope: "voltro:mssql" }), Q = async (e) => {
179
182
  let t = e.changeStrategy ?? "inline", n = t;
180
- t === "cdc" && !e.cdcConfig && (X.warn("changeStrategy='cdc' requires cdcConfig (replicaId + includeTables); falling back to 'inline'."), n = "inline");
181
- let r = e.tracerLayer ? i.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, o = a.make(r), s = new Q(await o.runPromise(P), o, n);
183
+ t === "cdc" && !e.cdcConfig && (Z.warn("changeStrategy='cdc' requires cdcConfig (replicaId + includeTables); falling back to 'inline'."), n = "inline");
184
+ let r = e.tracerLayer ? a.mergeAll(e.sqlLayer, e.tracerLayer) : e.sqlLayer, i = o.make(r), s = new he(await i.runPromise(I), i, n);
182
185
  return n === "cdc" && e.cdcConfig && await s.startCdcConsumer(e.cdcConfig), s;
183
- }, Q = class e {
186
+ }, he = class e {
184
187
  sql;
185
188
  runtime;
186
189
  changeStrategy;
187
190
  namespace;
188
191
  emitter;
189
192
  inflightTxns = 0;
190
- reportEagerFallback = re(X);
193
+ reportEagerFallback = E(Z);
191
194
  cdcHandle = null;
192
195
  cdcCheckpointTimer = null;
193
196
  cdcPendingVersion = null;
194
197
  cdcReplicaId = "";
195
198
  cdcStreamName = "default";
196
199
  constructor(e, t, n = "inline", r = null, i) {
197
- this.sql = e, this.runtime = t, this.changeStrategy = n, this.namespace = r, this.emitter = i ?? new oe(), ie(this.emitter);
200
+ this.sql = e, this.runtime = t, this.changeStrategy = n, this.namespace = r, this.emitter = i ?? new ue(), oe(this.emitter);
198
201
  }
199
202
  get inlineEmit() {
200
203
  return this.changeStrategy === "inline";
@@ -203,19 +206,19 @@ var se = {
203
206
  return t === this.namespace ? this : new e(this.sql, this.runtime, this.changeStrategy, t, this.emitter);
204
207
  }
205
208
  nsT(e) {
206
- return T(this.namespace, e);
209
+ return ie(this.namespace, e);
207
210
  }
208
211
  __mssqlReplicationFriend = { runEffect: (e) => this.runtime.runPromise(e) };
209
212
  async executeQuery(e, t) {
210
- let n = b(e, this.sql, this.namespace), i = t ? r.provideService(n, F, t) : n, a = await w("mssql", "select", () => this.runtime.runPromise(i));
211
- return x(a, e.table, "mssql");
213
+ let n = S(e, this.sql, this.namespace), r = t ? i.provideService(n, L, t) : n, a = await D("mssql", "select", () => this.runtime.runPromise(r));
214
+ return C(a, e.table, "mssql");
212
215
  }
213
216
  decodeWritten(e, t) {
214
- return x([t], e, "mssql")[0] ?? t;
217
+ return C([t], e, "mssql")[0] ?? t;
215
218
  }
216
- async executeInsert(e, t, n, i, a) {
217
- t = j(e, t);
218
- let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(S(t, e)).returning("*")}`, c = n ? r.provideService(s, F, n) : s, l = (await this.runtime.runPromise(c))[0];
219
+ async executeInsert(e, t, n, r, a) {
220
+ t = M(e, t);
221
+ let o = this.sql, s = o`INSERT INTO ${o(this.nsT(e))} ${o.insert(w(t, e)).returning("*")}`, c = n ? i.provideService(s, L, n) : s, l = (await this.runtime.runPromise(c))[0];
219
222
  if (!l) throw Error(`MssqlStore.insert: row not returned post-insert in '${e}'`);
220
223
  let u = this.decodeWritten(e, l);
221
224
  return await this.routeEvent({
@@ -223,10 +226,10 @@ var se = {
223
226
  op: "insert",
224
227
  old: null,
225
228
  new: u
226
- }, i, n, a), u;
229
+ }, r, n, a), u;
227
230
  }
228
- async executeUpdate(e, t, n, i, a, o) {
229
- let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update(S(n, e)).returning("*")} WHERE ${s("id")} = ${t}`, l = i ? r.provideService(c, F, i) : c, u = (await this.runtime.runPromise(l))[0];
231
+ async executeUpdate(e, t, n, r, a, o) {
232
+ let s = this.sql, c = s`UPDATE ${s(this.nsT(e))} SET ${s.update(w(n, e)).returning("*")} WHERE ${s.and(k(t).map(([e, t]) => s`${s(e)} = ${t}`))}`, l = r ? i.provideService(c, L, r) : c, u = (await this.runtime.runPromise(l))[0];
230
233
  if (!u) return null;
231
234
  let d = this.decodeWritten(e, u);
232
235
  return await this.routeEvent({
@@ -234,39 +237,39 @@ var se = {
234
237
  op: "update",
235
238
  old: null,
236
239
  new: d
237
- }, a, i, o), d;
240
+ }, a, r, o), d;
238
241
  }
239
- async executeInsertMany(e, t, n, i, a) {
240
- if (t = M(e, t), t.length === 0) return [];
241
- let o = this.sql, s = _(t, h("mssql")), c = (t) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t.map((t) => S(t, e))).returning("*")}`, l;
242
+ async executeInsertMany(e, t, n, r, a) {
243
+ if (t = ce(e, t), t.length === 0) return [];
244
+ let o = this.sql, s = v(t, g("mssql")), c = (t) => o`INSERT INTO ${o(this.nsT(e))} ${o.insert(t.map((t) => w(t, e))).returning("*")}`, l;
242
245
  if (s.length === 1) {
243
- let e = c(s[0]), t = n ? r.provideService(e, F, n) : e;
246
+ let e = c(s[0]), t = n ? i.provideService(e, L, n) : e;
244
247
  l = await this.runtime.runPromise(t);
245
248
  } else if (n) {
246
249
  let e = [];
247
- for (let t of s) e.push(...await this.runtime.runPromise(r.provideService(c(t), F, n)));
250
+ for (let t of s) e.push(...await this.runtime.runPromise(i.provideService(c(t), L, n)));
248
251
  l = e;
249
- } else l = await this.runtime.runPromise(o.withTransaction(r.map(r.forEach(s, c, { concurrency: 1 }), (e) => e.flat())));
250
- let u = t.map((e) => e.id), d = new Map(x(l, e, "mssql").map((e) => [e.id, e])), f = u.map((e) => d.get(e)).filter((e) => e !== void 0), p = f.length === l.length ? f : [...l];
252
+ } else l = await this.runtime.runPromise(o.withTransaction(i.map(i.forEach(s, c, { concurrency: 1 }), (e) => e.flat())));
253
+ let u = t.map((e) => e.id), d = new Map(C(l, e, "mssql").map((e) => [e.id, e])), f = u.map((e) => d.get(e)).filter((e) => e !== void 0), p = f.length === l.length ? f : [...l];
251
254
  for (let t of p) await this.routeEvent({
252
255
  table: e,
253
256
  op: "insert",
254
257
  old: null,
255
258
  new: t
256
- }, i, n, a);
259
+ }, r, n, a);
257
260
  return p;
258
261
  }
259
- async executePatchJson(e, t, n, i, a, o, s) {
260
- let c = this.sql, l = n.split("."), u = l[0], d = l.slice(1), f = d.length === 0 ? "$" : `$.${d.join(".")}`, p = JSON.stringify(i ?? null), m = c`UPDATE ${c(this.nsT(e))} SET ${c(u)} = JSON_MODIFY(${c(u)}, ${f}, JSON_QUERY(${p})) OUTPUT INSERTED.* WHERE ${c("id")} = ${t}`, h = a ? r.provideService(m, F, a) : m, g = (await this.runtime.runPromise(h))[0];
261
- return g ? (await this.routeEvent({
262
+ async executePatchJson(e, t, n, r, a, o, s, c) {
263
+ let l = this.sql, u = w(re(t, n, c), e), d = l.csv([b(l, "mssql", n, r), ...Object.entries(u).map(([e, t]) => l`${l(e)} = ${t}`)]), f = l`UPDATE ${l(this.nsT(e))} SET ${d} OUTPUT INSERTED.* WHERE ${l.and(k(t).map(([e, t]) => l`${l(e)} = ${t}`))}`, p = a ? i.provideService(f, L, a) : f, m = (await this.runtime.runPromise(p))[0];
264
+ return m ? (await this.routeEvent({
262
265
  table: e,
263
266
  op: "update",
264
267
  old: null,
265
- new: g
266
- }, o, a, s), g) : null;
268
+ new: m
269
+ }, o, a, s), m) : null;
267
270
  }
268
- async executeDelete(e, t, n, i, a) {
269
- let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* WHERE ${o("id")} = ${t}`, c = n ? r.provideService(s, F, n) : s, l = (await this.runtime.runPromise(c))[0];
271
+ async executeDelete(e, t, n, r, a) {
272
+ let o = this.sql, s = o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* WHERE ${o.and(k(t).map(([e, t]) => o`${o(e)} = ${t}`))}`, c = n ? i.provideService(s, L, n) : s, l = (await this.runtime.runPromise(c))[0];
270
273
  if (!l) return !1;
271
274
  let u = this.decodeWritten(e, l);
272
275
  return await this.routeEvent({
@@ -274,21 +277,21 @@ var se = {
274
277
  op: "delete",
275
278
  old: u,
276
279
  new: null
277
- }, i, n, a), !0;
280
+ }, r, n, a), !0;
278
281
  }
279
282
  async appendInTxn(e, t, n) {
280
- let i = this.sql, a = i`INSERT INTO ${i(this.nsT(e))} ${i.insert(S(t, e))}`;
281
- await this.runtime.runPromise(n ? r.provideService(a, F, n) : a);
283
+ let r = this.sql, a = r`INSERT INTO ${r(this.nsT(e))} ${r.insert(w(t, e))}`;
284
+ await this.runtime.runPromise(n ? i.provideService(a, L, n) : a);
282
285
  }
283
- async maxInTxn(e, t, n, i) {
284
- let a = this.sql, o = Object.entries(n).map(([e, t]) => a`${a(e)} = ${t}`), s = a`SELECT MAX(${a(t)}) AS ${a("m")} FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = (await this.runtime.runPromise(i ? r.provideService(s, F, i) : s))[0]?.m;
286
+ async maxInTxn(e, t, n, r) {
287
+ let a = this.sql, o = Object.entries(n).map(([e, t]) => a`${a(e)} = ${t}`), s = a`SELECT MAX(${a(t)}) AS ${a("m")} FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = (await this.runtime.runPromise(r ? i.provideService(s, L, r) : s))[0]?.m;
285
288
  return c == null ? null : Number(c);
286
289
  }
287
290
  async routeEvent(e, t, n = null, r) {
288
291
  e = {
289
- ...m(r),
292
+ ...h(r),
290
293
  ...e
291
- }, D(e.table) && await A({
294
+ }, O(e.table) && await j({
292
295
  append: (e, t) => this.appendInTxn(e, t, n),
293
296
  maxOf: (e, t, r) => this.maxInTxn(e, t, r, n)
294
297
  }, {
@@ -298,7 +301,7 @@ var se = {
298
301
  prev: e.old,
299
302
  traceId: e.traceId,
300
303
  subjectId: e.subjectId
301
- }), C(e.table) && (t === null ? this.inlineEmit && this.emitter.emit("change", e) : t.push(e));
304
+ }), T(e.table) && (t === null ? this.inlineEmit && this.emitter.emit("change", e) : t.push(e));
302
305
  }
303
306
  query(e) {
304
307
  return this.runWithEager(e, null);
@@ -307,17 +310,17 @@ var se = {
307
310
  return this.rawOn(e, null);
308
311
  }
309
312
  rawOn(e, t) {
310
- let n = ee(e, this.sql), i = t ? r.provideService(n, F, t) : n;
311
- return w("mssql", "raw", () => this.runtime.runPromise(i));
313
+ let n = ee(e, this.sql), r = t ? i.provideService(n, L, t) : n;
314
+ return D("mssql", "raw", () => this.runtime.runPromise(r));
312
315
  }
313
316
  async runWithEager(e, t) {
314
317
  if (!ne(e)) return this.executeQuery(e, t);
315
- let n = this.namespace === null ? v(e, this.sql, "mssql") : null;
318
+ let n = this.namespace === null ? y(e, this.sql, "mssql") : null;
316
319
  if (n !== null) try {
317
- let e = t ? r.provideService(n.fragment, F, t) : n.fragment, i = await w("mssql", "select", () => this.runtime.runPromise(e));
318
- return n.decode(i);
320
+ let e = t ? i.provideService(n.fragment, L, t) : n.fragment, r = await D("mssql", "select", () => this.runtime.runPromise(e));
321
+ return n.decode(r);
319
322
  } catch (t) {
320
- if (t instanceof u) throw t;
323
+ if (t instanceof d) throw t;
321
324
  this.reportEagerFallback({
322
325
  dialect: "mssql",
323
326
  table: e.table,
@@ -330,19 +333,19 @@ var se = {
330
333
  table: e.table,
331
334
  reason: "not-compilable"
332
335
  });
333
- let i = await this.executeQuery(e, t);
334
- return p(i, e.eager, e.sourceTable ?? O(e.table), (e) => this.executeQuery(e, t));
336
+ let r = await this.executeQuery(e, t);
337
+ return m(r, e.eager, e.sourceTable ?? se(e.table), (e) => this.executeQuery(e, t));
335
338
  }
336
339
  getInternalRunWithEager() {
337
340
  return this.runWithEager.bind(this);
338
341
  }
339
342
  localWrite(e, t, n) {
340
- let r = (e) => D(t) ? this.writeWithRecorders(n, e) : n(e, null, null);
341
- return w("mssql", e, () => ae(r));
343
+ let r = (e) => O(t) ? this.writeWithRecorders(n, e) : n(e, null, null);
344
+ return D("mssql", e, () => le(r));
342
345
  }
343
346
  async writeWithRecorders(e, t) {
344
- let n = [], i = this.sql, a = await this.runtime.runPromise(i.withTransaction(r.flatMap(r.serviceOption(F), (i) => o.isNone(i) ? r.fail(/* @__PURE__ */ Error("MssqlStore.write: TransactionConnection missing.")) : r.tryPromise({
345
- try: () => e(t, i.value, n),
347
+ let n = [], r = this.sql, a = await this.runtime.runPromise(r.withTransaction(i.flatMap(i.serviceOption(L), (r) => s.isNone(r) ? i.fail(/* @__PURE__ */ Error("MssqlStore.write: TransactionConnection missing.")) : i.tryPromise({
348
+ try: () => e(t, r.value, n),
346
349
  catch: (e) => e
347
350
  }))));
348
351
  for (let e of n) this.emitChange(e);
@@ -354,8 +357,8 @@ var se = {
354
357
  insertMany(e, t) {
355
358
  return this.localWrite("insert", e, (n, r, i) => this.executeInsertMany(e, t, r, i, n));
356
359
  }
357
- patchJson(e, t, n, r) {
358
- return this.localWrite("update", e, (i, a, o) => this.executePatchJson(e, t, n, r, a, o, i));
360
+ patchJson(e, t, n, r, i) {
361
+ return this.localWrite("update", e, (a, o, s) => this.executePatchJson(e, t, n, r, o, s, a, i));
359
362
  }
360
363
  update(e, t, n) {
361
364
  return this.localWrite("update", e, (r, i, a) => this.executeUpdate(e, t, n, i, a, r));
@@ -369,24 +372,24 @@ var se = {
369
372
  deleteMany(e, t) {
370
373
  return this.executeDeleteMany(e, t, null, null);
371
374
  }
372
- async executeUpdateMany(e, t, n, i, a, o) {
373
- let s = this.sql, c = y(n.where, s, this.namespace), l = s`UPDATE ${s(this.nsT(e))} SET ${s.update(S(t, e))} OUTPUT INSERTED.* WHERE ${c}`, u = i ? r.provideService(l, F, i) : l, d = await w("mssql", "update", () => this.runtime.runPromise(u));
375
+ async executeUpdateMany(e, t, n, r, a, o) {
376
+ let s = this.sql, c = x(n.where, s, this.namespace), l = s`UPDATE ${s(this.nsT(e))} SET ${s.update(w(t, e))} OUTPUT INSERTED.* WHERE ${c}`, u = r ? i.provideService(l, L, r) : l, d = await D("mssql", "update", () => this.runtime.runPromise(u));
374
377
  for (let t of d) await this.routeEvent({
375
378
  table: e,
376
379
  op: "update",
377
380
  old: null,
378
381
  new: t
379
- }, a, i, o);
382
+ }, a, r, o);
380
383
  return d.length;
381
384
  }
382
- async executeDeleteMany(e, t, n, i, a) {
383
- let o = this.sql, s = y(t.where, o, this.namespace), c = o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* WHERE ${s}`, l = n ? r.provideService(c, F, n) : c, u = await w("mssql", "delete", () => this.runtime.runPromise(l));
385
+ async executeDeleteMany(e, t, n, r, a) {
386
+ let o = this.sql, s = x(t.where, o, this.namespace), c = o`DELETE FROM ${o(this.nsT(e))} OUTPUT DELETED.* WHERE ${s}`, l = n ? i.provideService(c, L, n) : c, u = await D("mssql", "delete", () => this.runtime.runPromise(l));
384
387
  for (let t of u) await this.routeEvent({
385
388
  table: e,
386
389
  op: "delete",
387
390
  old: t,
388
391
  new: null
389
- }, i, n, a);
392
+ }, r, n, a);
390
393
  return u.length;
391
394
  }
392
395
  upsert(e, t, n) {
@@ -395,66 +398,58 @@ var se = {
395
398
  insertIgnore(e, t, n) {
396
399
  return this.localWrite("upsert", e, (r, i, a) => this.executeInsertIgnore(e, t, n, i, a, r));
397
400
  }
398
- upsertPatch(e, t, n) {
399
- if (typeof n.update == "function") return n.update(t);
400
- if (Array.isArray(n.update)) {
401
- let t = {};
402
- for (let r of n.update) r in e && (t[r] = e[r]);
403
- return t;
404
- }
405
- let r = {};
406
- for (let [t, i] of Object.entries(e)) t !== "id" && (n.conflictColumns.includes(t) || (r[t] = i));
407
- return r;
408
- }
409
401
  async executeUpsert(e, t, n, r, i, a) {
410
- if (t = j(e, t), typeof n.update != "function" && n.conflictColumns.length > 0) return this.mergeUpsert(e, t, {
402
+ if (P(n), t = M(e, t), typeof n.update != "function" && n.conflictColumns.length > 0) return this.mergeUpsert(e, t, {
411
403
  conflictColumns: n.conflictColumns,
412
- ...n.update ? { update: n.update } : {}
404
+ ...n.update === void 0 ? {} : { update: n.update },
405
+ ...n.updateValues === void 0 ? {} : { updateValues: n.updateValues }
413
406
  }, r, i);
414
407
  let o = await this.findByConflict(e, t, n.conflictColumns, r);
415
- if (o) return await this.executeUpdate(e, o.id, this.upsertPatch(t, o, n), r, i) ?? o;
408
+ if (o) return await this.executeUpdate(e, o.id, N(t, o, n), r, i) ?? o;
416
409
  try {
417
410
  return await this.executeInsert(e, t, r, i);
418
411
  } catch (a) {
419
- if (!W(a)) throw a;
412
+ if (!G(a)) throw a;
420
413
  let o = await this.findByConflict(e, t, n.conflictColumns, r);
421
414
  if (!o) throw a;
422
- return await this.executeUpdate(e, o.id, this.upsertPatch(t, o, n), r, i) ?? o;
415
+ return await this.executeUpdate(e, o.id, N(t, o, n), r, i) ?? o;
423
416
  }
424
417
  }
425
- async mergeUpsert(e, t, n, i, a) {
426
- let o = this.sql, s = Object.keys(t), c = Array.isArray(n.update) ? n.update.filter((e) => s.includes(e)) : s.filter((e) => e !== "id" && !n.conflictColumns.includes(e)), l = S(t, e), u = s.map((e) => o`${l[e]}`), d = s.map((e) => o`${o(e)}`), f = n.conflictColumns.map((e) => o`tgt.${o(e)} = src.${o(e)}`), p = s.map((e) => o`${o(e)}`), m = s.map((e) => o`src.${o(e)}`), h = (c.length > 0 ? c : [n.conflictColumns[0]]).map((e) => o`tgt.${o(e)} = src.${o(e)}`), g = o`
418
+ async mergeUpsert(e, t, n, r, a) {
419
+ let o = this.sql, s = Object.keys(t), c = Array.isArray(n.update) ? n.update.filter((e) => s.includes(e)) : s.filter((e) => e !== "id" && !n.conflictColumns.includes(e)), l = w(t, e), u = s.map((e) => o`${l[e]}`), d = s.map((e) => o`${o(e)}`), f = n.conflictColumns.map((e) => o`tgt.${o(e)} = src.${o(e)}`), p = s.map((e) => o`${o(e)}`), m = s.map((e) => o`src.${o(e)}`), h = P(n), g = w(h, e), _ = [...c.filter((e) => !Object.hasOwn(h, e)).map((e) => o`tgt.${o(e)} = src.${o(e)}`), ...Object.entries(g).map(([e, t]) => o`tgt.${o(e)} = ${t}`)];
420
+ _.length === 0 && _.push(o`tgt.${o(n.conflictColumns[0])} = src.${o(n.conflictColumns[0])}`);
421
+ let v = o`
427
422
  MERGE ${o(this.nsT(e))} WITH (HOLDLOCK) AS tgt
428
423
  USING (VALUES (${o.csv(u)})) AS src (${o.csv(d)})
429
424
  ON ${o.and(f)}
430
- WHEN MATCHED THEN UPDATE SET ${o.csv(h)}
425
+ WHEN MATCHED THEN UPDATE SET ${o.csv(_)}
431
426
  WHEN NOT MATCHED THEN INSERT (${o.csv(p)}) VALUES (${o.csv(m)})
432
- OUTPUT $action AS ${o("__action")}, INSERTED.*;`, _ = i ? r.provideService(g, F, i) : g, v = (await this.runtime.runPromise(_))[0];
433
- if (!v) throw Error(`MssqlStore.upsert: MERGE returned no row for '${e}'`);
434
- let y = v.__action, { __action: ee, ...b } = v, x = this.decodeWritten(e, b);
427
+ OUTPUT $action AS ${o("__action")}, INSERTED.*;`, y = r ? i.provideService(v, L, r) : v, b = (await this.runtime.runPromise(y))[0];
428
+ if (!b) throw Error(`MssqlStore.upsert: MERGE returned no row for '${e}'`);
429
+ let x = b.__action, { __action: ee, ...S } = b, C = this.decodeWritten(e, S);
435
430
  return this.routeEvent({
436
431
  table: e,
437
- op: y === "INSERT" ? "insert" : "update",
432
+ op: x === "INSERT" ? "insert" : "update",
438
433
  old: null,
439
- new: x
440
- }, a), x;
434
+ new: C
435
+ }, a), C;
441
436
  }
442
437
  async executeInsertIgnore(e, t, n, r, i, a) {
443
- t = j(e, t);
438
+ t = M(e, t);
444
439
  let o = await this.findByConflict(e, t, n.conflictColumns, r);
445
440
  if (o) return o;
446
441
  try {
447
442
  return await this.executeInsert(e, t, r, i);
448
443
  } catch (i) {
449
- if (!W(i)) throw i;
444
+ if (!G(i)) throw i;
450
445
  let a = await this.findByConflict(e, t, n.conflictColumns, r);
451
446
  if (!a) throw i;
452
447
  return a;
453
448
  }
454
449
  }
455
- async findByConflict(e, t, n, i) {
450
+ async findByConflict(e, t, n, r) {
456
451
  if (n.length === 0) return;
457
- let a = this.sql, o = n.map((e) => a`${a(e)} = ${t[e]}`), s = a`SELECT TOP 1 * FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = i ? r.provideService(s, F, i) : s;
452
+ let a = this.sql, o = n.map((e) => a`${a(e)} = ${t[e]}`), s = a`SELECT TOP 1 * FROM ${a(this.nsT(e))} WHERE ${a.and(o)}`, c = r ? i.provideService(s, L, r) : s;
458
453
  return (await this.runtime.runPromise(c))[0];
459
454
  }
460
455
  emitChange(e) {
@@ -464,9 +459,9 @@ var se = {
464
459
  if (this.cdcHandle) return;
465
460
  this.cdcReplicaId = e.replicaId;
466
461
  let t = e.includeTables ?? [];
467
- t.length === 0 && X.warn("cdc: no reactive tables to tail; Change Tracking reader idle. Set includeTables to enable.");
462
+ t.length === 0 && Z.warn("cdc: no reactive tables to tail; Change Tracking reader idle. Set includeTables to enable.");
468
463
  let n = await this.readCdcOffset(e.replicaId);
469
- this.cdcHandle = await Y({
464
+ this.cdcHandle = await X({
470
465
  run: this.__mssqlReplicationFriend.runEffect,
471
466
  tables: t,
472
467
  startVersion: n,
@@ -474,9 +469,9 @@ var se = {
474
469
  onVersion: (e) => {
475
470
  this.cdcPendingVersion = e;
476
471
  },
477
- onError: (e) => X.warn("cdc: consumer error", {}, e),
472
+ onError: (e) => Z.warn("cdc: consumer error", {}, e),
478
473
  onResync: () => {
479
- X.warn("cdc: resynced to current CT version after retention gap — subscriptions self-heal on the next change");
474
+ Z.warn("cdc: resynced to current CT version after retention gap — subscriptions self-heal on the next change");
480
475
  }
481
476
  }), this.cdcCheckpointTimer = setInterval(() => {
482
477
  this.flushCdcOffset();
@@ -485,14 +480,14 @@ var se = {
485
480
  async readCdcOffset(e) {
486
481
  try {
487
482
  let t = this.sql, n = (await this.runtime.runPromise(t`
488
- SELECT TOP 1 ${t("ctVersion")} FROM ${t(c)}
483
+ SELECT TOP 1 ${t("ctVersion")} FROM ${t(l)}
489
484
  WHERE ${t("id")} = ${e}`))[0]?.ctVersion;
490
- return n ? (X.info("cdc: resuming from persisted CT version", {
485
+ return n ? (Z.info("cdc: resuming from persisted CT version", {
491
486
  replicaId: e,
492
487
  version: n
493
488
  }), n) : null;
494
489
  } catch (t) {
495
- return X.warn("cdc: no readable offset; starting at current CT version", { replicaId: e }, t), null;
490
+ return Z.warn("cdc: no readable offset; starting at current CT version", { replicaId: e }, t), null;
496
491
  }
497
492
  }
498
493
  async flushCdcOffset() {
@@ -501,7 +496,7 @@ var se = {
501
496
  this.cdcPendingVersion = null;
502
497
  try {
503
498
  let t = this.cdcReplicaId;
504
- await this.executeUpsert(c, {
499
+ await this.executeUpsert(l, {
505
500
  id: t,
506
501
  replicaId: t,
507
502
  streamName: this.cdcStreamName,
@@ -512,7 +507,7 @@ var se = {
512
507
  update: ["ctVersion", "updatedAt"]
513
508
  }, null, null);
514
509
  } catch (t) {
515
- this.cdcPendingVersion = e, X.warn("cdc: checkpoint write failed", {}, t);
510
+ this.cdcPendingVersion = e, Z.warn("cdc: checkpoint write failed", {}, t);
516
511
  }
517
512
  }
518
513
  }
@@ -567,16 +562,16 @@ var se = {
567
562
  }
568
563
  async emptyTablesOn(e, t) {
569
564
  if (e.length === 0) return;
570
- let n = this.sql, i = (e) => this.nsT(e), a = (e, t) => n.unsafe(`ALTER TABLE ${E(i(e), "mssql")} ${t ? "WITH CHECK CHECK" : "NOCHECK"} CONSTRAINT ALL`), o = r.acquireUseRelease(r.forEach(e, (e) => a(e, !1), { discard: !0 }), () => r.forEach(e, (e) => n`DELETE FROM ${n(i(e))}`, { discard: !0 }), () => r.orDie(r.ignore(r.forEach(e, (e) => a(e, !0), { discard: !0 })))), s = t === null ? n.withTransaction(o) : r.provideService(o, F, t);
565
+ let n = this.sql, r = (e) => this.nsT(e), a = (e, t) => n.unsafe(`ALTER TABLE ${ae(r(e), "mssql")} ${t ? "WITH CHECK CHECK" : "NOCHECK"} CONSTRAINT ALL`), o = i.acquireUseRelease(i.forEach(e, (e) => a(e, !1), { discard: !0 }), () => i.forEach(e, (e) => n`DELETE FROM ${n(r(e))}`, { discard: !0 }), () => i.orDie(i.ignore(i.forEach(e, (e) => a(e, !0), { discard: !0 })))), s = t === null ? n.withTransaction(o) : i.provideService(o, L, t);
571
566
  await this.runtime.runPromise(s);
572
567
  }
573
568
  async transactional(e) {
574
569
  this.inflightTxns++;
575
570
  try {
576
- return await k({
571
+ return await A({
577
572
  ...this.txnSpec("MssqlStore.transactional"),
578
573
  work: e,
579
- makeView: (e, t) => new ue(this, e, t)
574
+ makeView: (e, t) => new ge(this, e, t)
580
575
  });
581
576
  } finally {
582
577
  this.inflightTxns--;
@@ -588,7 +583,7 @@ var se = {
588
583
  dialect: "mssql",
589
584
  withTransaction: (e) => this.sql.withTransaction(e),
590
585
  runPromiseExit: (e) => this.runtime.runPromiseExit(e),
591
- isRetryable: H,
586
+ isRetryable: W,
592
587
  span: {
593
588
  name: "store.transactional",
594
589
  attributes: {
@@ -607,7 +602,7 @@ var se = {
607
602
  return this.changeStrategy === "cdc" ? "fleet" : "local";
608
603
  }
609
604
  injectExternalChange(e) {
610
- C(e.table) && this.emitter.emit("change", te(e));
605
+ T(e.table) && this.emitter.emit("change", te(e));
611
606
  }
612
607
  run(e) {
613
608
  return this.runtime.runPromise(e);
@@ -620,7 +615,7 @@ var se = {
620
615
  if (this.inflightTxns > 0) {
621
616
  let t = Date.now() + e;
622
617
  for (; this.inflightTxns > 0 && Date.now() < t;) await new Promise((e) => setTimeout(e, 25));
623
- this.inflightTxns > 0 && X.warn("close: grace period expired with in-flight transactions", {
618
+ this.inflightTxns > 0 && Z.warn("close: grace period expired with in-flight transactions", {
624
619
  gracePeriodMs: e,
625
620
  inflight: this.inflightTxns
626
621
  });
@@ -630,7 +625,7 @@ var se = {
630
625
  async ping() {
631
626
  await this.runtime.runPromise(this.sql`SELECT 1`);
632
627
  }
633
- }, ue = class {
628
+ }, ge = class {
634
629
  parent;
635
630
  txn;
636
631
  attr;
@@ -654,8 +649,8 @@ var se = {
654
649
  insertMany(e, t) {
655
650
  return this.parent.getInternalExecuteInsertMany()(e, t, this.txn, this.events, this.attr);
656
651
  }
657
- patchJson(e, t, n, r) {
658
- return this.parent.getInternalExecutePatchJson()(e, t, n, r, this.txn, this.events, this.attr);
652
+ patchJson(e, t, n, r, i) {
653
+ return this.parent.getInternalExecutePatchJson()(e, t, n, r, this.txn, this.events, this.attr, i);
659
654
  }
660
655
  update(e, t, n) {
661
656
  return this.parent.getInternalExecuteUpdate()(e, t, n, this.txn, this.events, this.attr);
@@ -691,12 +686,12 @@ var se = {
691
686
  this.events.length = 0;
692
687
  }
693
688
  }
694
- }, $ = (e) => e.__mssqlReplicationFriend ?? null, de = (e, t) => e === t ? 0 : e < t ? -1 : 1, fe = () => ({
689
+ }, $ = (e) => e.__mssqlReplicationFriend ?? null, _e = (e, t) => e === t ? 0 : e < t ? -1 : 1, ve = () => ({
695
690
  async capturePrimaryPosition(e) {
696
691
  let t = $(e);
697
692
  if (t === null) throw Error("mssqlReplicationAdapter: primary is not an MssqlStore (missing __mssqlReplicationFriend).");
698
- return t.runEffect(r.gen(function* () {
699
- return (yield* (yield* P)`
693
+ return t.runEffect(i.gen(function* () {
694
+ return (yield* (yield* I)`
700
695
  SELECT CONVERT(VARCHAR(40), end_of_log_lsn) AS lsn
701
696
  FROM sys.dm_hadr_database_replica_states
702
697
  WHERE database_id = DB_ID()
@@ -708,8 +703,8 @@ var se = {
708
703
  async probeReplicaPosition(e) {
709
704
  let t = $(e);
710
705
  if (t === null) throw Error("mssqlReplicationAdapter: replica is not an MssqlStore.");
711
- return t.runEffect(r.gen(function* () {
712
- return (yield* (yield* P)`
706
+ return t.runEffect(i.gen(function* () {
707
+ return (yield* (yield* I)`
713
708
  SELECT CONVERT(VARCHAR(40), last_hardened_lsn) AS lsn
714
709
  FROM sys.dm_hadr_database_replica_states
715
710
  WHERE database_id = DB_ID()
@@ -718,14 +713,14 @@ var se = {
718
713
  }));
719
714
  },
720
715
  compare(e, t) {
721
- return de(t, e) >= 0 ? "caught-up" : "behind";
716
+ return _e(t, e) >= 0 ? "caught-up" : "behind";
722
717
  }
723
- }), pe = {
718
+ }), ye = {
724
719
  id: "mssql",
725
- makeSqlLayer: (e) => z(e),
726
- makeStore: (e) => Z(e),
720
+ makeSqlLayer: (e) => H(e),
721
+ makeStore: (e) => Q(e),
727
722
  compileContains: (e, t, n) => n`${e} LIKE ${`%${t.replace(/[\\%_[\]]/g, (e) => `\\${e}`)}%`} ESCAPE '\\'`,
728
- retryFilter: G
723
+ retryFilter: K
729
724
  };
730
725
  //#endregion
731
- export { c as CDC_OFFSETS_TABLE, e as MssqlClient, f as _voltroMssqlCdcOffsetsTable, R as connectionFromConfig, K as ensureChangeTracking, I as makeMssqlSqlLayer, z as makeMssqlSqlLayerFromConfig, Z as makeMssqlStore, pe as mssqlDialect, fe as mssqlReplicationAdapter, G as mssqlRetryFilter, Y as startChangeTrackingCdc };
726
+ export { l as CDC_OFFSETS_TABLE, e as MssqlClient, p as _voltroMssqlCdcOffsetsTable, V as connectionFromConfig, q as ensureChangeTracking, z as makeMssqlSqlLayer, H as makeMssqlSqlLayerFromConfig, Q as makeMssqlStore, ye as mssqlDialect, ve as mssqlReplicationAdapter, K as mssqlRetryFilter, X as startChangeTrackingCdc };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/sql-mssql",
3
- "version": "0.65.0",
3
+ "version": "0.66.1",
4
4
  "description": "SQL Server (mssql) dialect adapter for Voltro's cross-dialect DataStore (OUTPUT-clause returning, OFFSET/FETCH paging, inline reactivity).",
5
5
  "keywords": [
6
6
  "voltro",
@@ -35,8 +35,9 @@
35
35
  "dependencies": {
36
36
  "@effect/sql": "^0.52.0",
37
37
  "@effect/sql-mssql": "^0.53.0",
38
- "@voltro/database": "0.65.0",
39
- "@voltro/logger": "0.65.0"
38
+ "@voltro/database": "0.66.1",
39
+ "@voltro/logger": "0.66.1",
40
+ "tedious": "18.6.2"
40
41
  },
41
42
  "peerDependencies": {
42
43
  "effect": "^3.22.0"