@holo-js/db 0.1.1 → 0.1.3

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +256 -206
  2. package/dist/index.mjs +535 -585
  3. package/package.json +20 -4
package/dist/index.mjs CHANGED
@@ -119,6 +119,151 @@ function redactSql(sql, policy) {
119
119
  return "[SQL REDACTED]";
120
120
  }
121
121
 
122
+ // src/cache.ts
123
+ import { createHash } from "crypto";
124
+ function getQueryCacheBridgeState() {
125
+ const runtime = globalThis;
126
+ runtime.__holoDbQueryCacheBridge__ ??= {};
127
+ return runtime.__holoDbQueryCacheBridge__;
128
+ }
129
+ function configureDatabaseQueryCacheBridge(bridge) {
130
+ getQueryCacheBridgeState().bridge = bridge;
131
+ }
132
+ function getDatabaseQueryCacheBridge() {
133
+ return getQueryCacheBridgeState().bridge;
134
+ }
135
+ function resetDatabaseQueryCacheBridge() {
136
+ getQueryCacheBridgeState().bridge = void 0;
137
+ }
138
+ function normalizeQueryCacheConfig(input) {
139
+ if (typeof input === "number" || input instanceof Date) {
140
+ return Object.freeze({
141
+ ttl: input
142
+ });
143
+ }
144
+ const ttl = input.ttl;
145
+ const flexible = input.flexible;
146
+ if (typeof ttl === "undefined" && typeof flexible === "undefined") {
147
+ throw new ConfigurationError('[@holo-js/db] Query cache config requires "ttl" or "flexible".');
148
+ }
149
+ if (typeof ttl !== "undefined" && typeof flexible !== "undefined") {
150
+ throw new ConfigurationError('[@holo-js/db] Query cache config cannot define both "ttl" and "flexible".');
151
+ }
152
+ const key = input.key?.trim();
153
+ if (typeof input.key !== "undefined" && !key) {
154
+ throw new ConfigurationError("[@holo-js/db] Query cache keys must be non-empty strings.");
155
+ }
156
+ const driver = input.driver?.trim();
157
+ if (typeof input.driver !== "undefined" && !driver) {
158
+ throw new ConfigurationError("[@holo-js/db] Query cache driver names must be non-empty strings.");
159
+ }
160
+ const invalidate = input.invalidate ? Object.freeze(input.invalidate.map((dependency) => {
161
+ const normalized = dependency.trim();
162
+ if (!normalized) {
163
+ throw new ConfigurationError("[@holo-js/db] Query cache invalidation dependencies must be non-empty strings.");
164
+ }
165
+ return normalized;
166
+ })) : void 0;
167
+ return Object.freeze({
168
+ ttl,
169
+ key,
170
+ driver,
171
+ flexible,
172
+ invalidate
173
+ });
174
+ }
175
+ function createDeterministicQueryCacheKey(statement, connectionName) {
176
+ const digest = createHash("sha256").update(JSON.stringify({
177
+ connectionName,
178
+ sql: statement.sql,
179
+ bindings: statement.bindings ?? []
180
+ })).digest("hex");
181
+ return `db:query:${digest}`;
182
+ }
183
+ function resolveQueryCacheKey(statement, connectionName, config) {
184
+ return config.key ?? createDeterministicQueryCacheKey(statement, connectionName);
185
+ }
186
+ function createTableCacheDependency(connectionName, tableName) {
187
+ return `db:${connectionName}:${tableName}`;
188
+ }
189
+ function normalizeQueryCacheDependencies(connectionName, dependencies) {
190
+ return Object.freeze(dependencies.map((dependency) => {
191
+ return dependency.startsWith("db:") ? dependency : createTableCacheDependency(connectionName, dependency);
192
+ }));
193
+ }
194
+ function supportsAutomaticPredicateInvalidation(predicate) {
195
+ switch (predicate.kind) {
196
+ case "comparison":
197
+ case "column":
198
+ case "null":
199
+ case "date":
200
+ case "json":
201
+ case "fulltext":
202
+ case "vector":
203
+ return true;
204
+ case "group":
205
+ return predicate.predicates.every((child) => supportsAutomaticPredicateInvalidation(child));
206
+ case "exists":
207
+ case "subquery":
208
+ case "raw":
209
+ return false;
210
+ default:
211
+ return false;
212
+ }
213
+ }
214
+ function supportsAutomaticQueryCacheInvalidation(plan) {
215
+ if (plan.joins.length > 0 || plan.unions.length > 0 || plan.having.length > 0) {
216
+ return false;
217
+ }
218
+ if (plan.selections.some((selection) => selection.kind === "raw" || selection.kind === "subquery")) {
219
+ return false;
220
+ }
221
+ if (plan.orderBy.some((order) => order.kind === "raw")) {
222
+ return false;
223
+ }
224
+ return plan.predicates.every((predicate) => supportsAutomaticPredicateInvalidation(predicate));
225
+ }
226
+ function inferAutomaticQueryCacheDependencies(plan, connectionName) {
227
+ if (!supportsAutomaticQueryCacheInvalidation(plan)) {
228
+ return void 0;
229
+ }
230
+ return Object.freeze([
231
+ createTableCacheDependency(connectionName, plan.source.tableName)
232
+ ]);
233
+ }
234
+ function resolveQueryCacheDependencies(plan, connectionName, explicit) {
235
+ if (explicit && explicit.length > 0) {
236
+ return normalizeQueryCacheDependencies(connectionName, explicit);
237
+ }
238
+ return inferAutomaticQueryCacheDependencies(plan, connectionName);
239
+ }
240
+ async function invalidateQueryCacheDependencies(connection, dependencies) {
241
+ const bridge = getDatabaseQueryCacheBridge();
242
+ if (!bridge || dependencies.length === 0) {
243
+ return;
244
+ }
245
+ if (connection.getScope().kind === "root") {
246
+ await bridge.invalidateDependencies(dependencies);
247
+ return;
248
+ }
249
+ connection.afterCommit(async () => {
250
+ await bridge.invalidateDependencies(dependencies);
251
+ });
252
+ }
253
+ var queryCacheInternals = {
254
+ configureDatabaseQueryCacheBridge,
255
+ createDeterministicQueryCacheKey,
256
+ createTableCacheDependency,
257
+ getQueryCacheBridgeState,
258
+ inferAutomaticQueryCacheDependencies,
259
+ normalizeQueryCacheConfig,
260
+ normalizeQueryCacheDependencies,
261
+ resolveQueryCacheDependencies,
262
+ resolveQueryCacheKey,
263
+ supportsAutomaticPredicateInvalidation,
264
+ supportsAutomaticQueryCacheInvalidation
265
+ };
266
+
122
267
  // src/query/chunkOrdering.ts
123
268
  function compareChunkValuesAscending(a, b) {
124
269
  if (a === b) return 0;
@@ -228,6 +373,43 @@ function attachMethods(target, methods) {
228
373
  );
229
374
  }
230
375
 
376
+ // src/query/pagination.ts
377
+ function assertPositiveInteger(value, kind, createError) {
378
+ if (!Number.isInteger(value) || value <= 0) {
379
+ throw createError(`${kind} must be a positive integer.`);
380
+ }
381
+ }
382
+ function normalizePaginationParameterName(value, fallback, createError) {
383
+ if (typeof value === "undefined") {
384
+ return fallback;
385
+ }
386
+ const trimmed = value.trim();
387
+ if (typeof value !== "string" || trimmed.length === 0) {
388
+ throw createError(
389
+ `${fallback === "cursor" ? "Cursor" : "Page"} parameter name must be a non-empty string.`
390
+ );
391
+ }
392
+ return trimmed;
393
+ }
394
+ function encodeOffsetCursor(offset) {
395
+ return Buffer.from(JSON.stringify({ offset }), "utf8").toString("base64url");
396
+ }
397
+ function decodeOffsetCursor(cursor, createError) {
398
+ if (cursor === null) {
399
+ return 0;
400
+ }
401
+ try {
402
+ const decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
403
+ const offset = decoded.offset;
404
+ if (typeof offset !== "number" || !Number.isInteger(offset) || offset < 0) {
405
+ throw new Error("invalid offset");
406
+ }
407
+ return offset;
408
+ } catch {
409
+ throw createError("Cursor is malformed.");
410
+ }
411
+ }
412
+
231
413
  // src/query/ast.ts
232
414
  function createTableSource(table) {
233
415
  if (typeof table === "string") {
@@ -1047,7 +1229,8 @@ var SQLQueryCompiler = class {
1047
1229
  const groupByClause = plan.groupBy.length === 0 ? "" : ` GROUP BY ${plan.groupBy.map((column2) => this.compileColumnReference(column2)).join(", ")}`;
1048
1230
  const havingClause = this.compileHaving(plan.having, bindings);
1049
1231
  const orderClause = plan.orderBy.length === 0 ? "" : ` ORDER BY ${plan.orderBy.map((orderBy) => this.compileOrderBy(orderBy, bindings)).join(", ")}`;
1050
- const lockClause = plan.lockMode ? ` ${this.compileLockClause(plan.lockMode)}` : "";
1232
+ const compiledLockClause = plan.lockMode ? this.compileLockClause(plan.lockMode) : "";
1233
+ const lockClause = compiledLockClause ? ` ${compiledLockClause}` : "";
1051
1234
  const limitClause = typeof plan.limit === "number" ? ` LIMIT ${plan.limit}` : "";
1052
1235
  const offsetClause = typeof plan.offset === "number" ? ` OFFSET ${plan.offset}` : "";
1053
1236
  return `${selectKeyword} ${selectList} FROM ${this.compileSource(plan.source)}${joinClause}${whereClause}${groupByClause}${havingClause}${unionClause}${orderClause}${lockClause}${limitClause}${offsetClause}`;
@@ -1516,6 +1699,9 @@ var SQLITE_JSON_HELPERS = Object.freeze({
1516
1699
  });
1517
1700
  var SQLITE_EMPTY_JSON_OBJECT = "json('{}')";
1518
1701
  var SQLiteQueryCompiler = class extends SQLQueryCompiler {
1702
+ compileLockClause(_lockMode) {
1703
+ return "";
1704
+ }
1519
1705
  compileJsonPredicate(predicate, bindings) {
1520
1706
  const column2 = this.compileColumnReference(predicate.column);
1521
1707
  const pathLiteral = this.createJsonPathLiteral(predicate.path);
@@ -1844,9 +2030,21 @@ function normalizeDialectWriteValue(dialect, column2, value) {
1844
2030
  }
1845
2031
 
1846
2032
  // src/query/TableQueryBuilder.ts
2033
+ function normalizeAtomicQueryCacheTtl(ttl) {
2034
+ if (ttl instanceof Date) {
2035
+ const expiresAt = ttl.getTime();
2036
+ if (Number.isNaN(expiresAt)) {
2037
+ throw new ConfigurationError("[@holo-js/db] Query cache Date TTL must be valid.");
2038
+ }
2039
+ const seconds = Math.max(1, Math.ceil((expiresAt - Date.now()) / 1e3));
2040
+ return [seconds, seconds];
2041
+ }
2042
+ return [ttl, ttl];
2043
+ }
1847
2044
  var TableQueryBuilder = class _TableQueryBuilder {
1848
- constructor(table, connection, plan) {
2045
+ constructor(table, connection, plan, queryCacheConfig) {
1849
2046
  this.connection = connection;
2047
+ this.queryCacheConfig = queryCacheConfig;
1850
2048
  this.source = createTableSource(table);
1851
2049
  this.plan = plan ?? createSelectQueryPlan(this.source);
1852
2050
  }
@@ -1868,7 +2066,8 @@ var TableQueryBuilder = class _TableQueryBuilder {
1868
2066
  return new _TableQueryBuilder(
1869
2067
  table,
1870
2068
  this.connection,
1871
- withSource(this.plan, createTableSource(table))
2069
+ withSource(this.plan, createTableSource(table)),
2070
+ this.queryCacheConfig
1872
2071
  );
1873
2072
  }
1874
2073
  select(...columns) {
@@ -2485,8 +2684,8 @@ var TableQueryBuilder = class _TableQueryBuilder {
2485
2684
  return this.limit(value);
2486
2685
  }
2487
2686
  forPage(page, perPage = 15) {
2488
- this.assertPositiveInteger(page, "Page");
2489
- this.assertPositiveInteger(perPage, "Per-page value");
2687
+ assertPositiveInteger(page, "Page", (message) => new SecurityError(message));
2688
+ assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
2490
2689
  return this.limit(perPage).offset((page - 1) * perPage);
2491
2690
  }
2492
2691
  toSQL() {
@@ -2517,9 +2716,60 @@ var TableQueryBuilder = class _TableQueryBuilder {
2517
2716
  console.log(this.debug());
2518
2717
  return this;
2519
2718
  }
2719
+ cache(config) {
2720
+ return new _TableQueryBuilder(
2721
+ this.source.table ?? this.source.tableName,
2722
+ this.connection,
2723
+ this.plan,
2724
+ normalizeQueryCacheConfig(config)
2725
+ );
2726
+ }
2520
2727
  async get() {
2521
- const result = await this.connection.queryCompiled(this.toSQL());
2522
- return result.rows;
2728
+ const statement = this.toSQL();
2729
+ const cacheConfig = this.queryCacheConfig;
2730
+ if (!cacheConfig || this.plan.lockMode) {
2731
+ const result = await this.connection.queryCompiled(statement);
2732
+ return result.rows;
2733
+ }
2734
+ const bridge = getDatabaseQueryCacheBridge();
2735
+ if (!bridge) {
2736
+ throw new ConfigurationError("[@holo-js/db] Query caching requires @holo-js/cache to be installed and configured.");
2737
+ }
2738
+ const cacheKey = resolveQueryCacheKey(statement, this.connection.getConnectionName(), cacheConfig);
2739
+ const dependencies = resolveQueryCacheDependencies(
2740
+ this.plan,
2741
+ this.connection.getConnectionName(),
2742
+ cacheConfig.invalidate
2743
+ );
2744
+ if (cacheConfig.flexible) {
2745
+ return bridge.flexible(
2746
+ cacheKey,
2747
+ cacheConfig.flexible,
2748
+ async () => {
2749
+ const result = await this.connection.queryCompiled(statement);
2750
+ return result.rows;
2751
+ },
2752
+ {
2753
+ driver: cacheConfig.driver,
2754
+ dependencies
2755
+ }
2756
+ );
2757
+ }
2758
+ if (typeof cacheConfig.ttl === "undefined") {
2759
+ throw new ConfigurationError('[@holo-js/db] Query cache config requires "ttl" or "flexible".');
2760
+ }
2761
+ return bridge.flexible(
2762
+ cacheKey,
2763
+ normalizeAtomicQueryCacheTtl(cacheConfig.ttl),
2764
+ async () => {
2765
+ const result = await this.connection.queryCompiled(statement);
2766
+ return result.rows;
2767
+ },
2768
+ {
2769
+ driver: cacheConfig.driver,
2770
+ dependencies
2771
+ }
2772
+ );
2523
2773
  }
2524
2774
  async first() {
2525
2775
  const rows = await this.limit(1).get();
@@ -2533,9 +2783,9 @@ var TableQueryBuilder = class _TableQueryBuilder {
2533
2783
  return rows[0];
2534
2784
  }
2535
2785
  async paginate(perPage = 15, page = 1, options = {}) {
2536
- this.assertPositiveInteger(perPage, "Per-page value");
2537
- this.assertPositiveInteger(page, "Page");
2538
- const pageName = this.normalizePaginationParameterName(options.pageName, "page");
2786
+ assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
2787
+ assertPositiveInteger(page, "Page", (message) => new SecurityError(message));
2788
+ const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new SecurityError(message));
2539
2789
  const rows = await this.getUnpaginatedRows();
2540
2790
  const total = rows.length;
2541
2791
  const offset = (page - 1) * perPage;
@@ -2554,9 +2804,9 @@ var TableQueryBuilder = class _TableQueryBuilder {
2554
2804
  });
2555
2805
  }
2556
2806
  async simplePaginate(perPage = 15, page = 1, options = {}) {
2557
- this.assertPositiveInteger(perPage, "Per-page value");
2558
- this.assertPositiveInteger(page, "Page");
2559
- const pageName = this.normalizePaginationParameterName(options.pageName, "page");
2807
+ assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
2808
+ assertPositiveInteger(page, "Page", (message) => new SecurityError(message));
2809
+ const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new SecurityError(message));
2560
2810
  const rows = await this.getUnpaginatedRows();
2561
2811
  const offset = (page - 1) * perPage;
2562
2812
  const pageRows = rows.slice(offset, offset + perPage + 1);
@@ -2574,9 +2824,9 @@ var TableQueryBuilder = class _TableQueryBuilder {
2574
2824
  });
2575
2825
  }
2576
2826
  async cursorPaginate(perPage = 15, cursor = null, options = {}) {
2577
- this.assertPositiveInteger(perPage, "Per-page value");
2578
- const cursorName = this.normalizePaginationParameterName(options.cursorName, "cursor");
2579
- const offset = this.decodeCursor(cursor);
2827
+ assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
2828
+ const cursorName = normalizePaginationParameterName(options.cursorName, "cursor", (message) => new SecurityError(message));
2829
+ const offset = decodeOffsetCursor(cursor, (message) => new SecurityError(message));
2580
2830
  const orderedQuery = this.prepareCursorPaginationQuery();
2581
2831
  const rows = await orderedQuery.getUnpaginatedRows();
2582
2832
  const pageRows = rows.slice(offset, offset + perPage + 1);
@@ -2585,12 +2835,12 @@ var TableQueryBuilder = class _TableQueryBuilder {
2585
2835
  return createCursorPaginator(data, {
2586
2836
  perPage,
2587
2837
  cursorName,
2588
- nextCursor: hasMorePages ? this.encodeCursor(offset + perPage) : null,
2838
+ nextCursor: hasMorePages ? encodeOffsetCursor(offset + perPage) : null,
2589
2839
  prevCursor: cursor
2590
2840
  });
2591
2841
  }
2592
2842
  async chunk(size, callback) {
2593
- this.assertPositiveInteger(size, "Chunk size");
2843
+ assertPositiveInteger(size, "Chunk size", (message) => new SecurityError(message));
2594
2844
  const rows = await this.getUnpaginatedRows();
2595
2845
  let page = 1;
2596
2846
  for (let index = 0; index < rows.length; index += size) {
@@ -2602,7 +2852,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2602
2852
  }
2603
2853
  }
2604
2854
  async chunkById(size, callback, column2 = "id") {
2605
- this.assertPositiveInteger(size, "Chunk size");
2855
+ assertPositiveInteger(size, "Chunk size", (message) => new SecurityError(message));
2606
2856
  const rows = await this.getUnpaginatedRows();
2607
2857
  const sortedRows = [...rows].sort((left, right) => {
2608
2858
  const a = left[column2];
@@ -2619,7 +2869,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2619
2869
  }
2620
2870
  }
2621
2871
  async chunkByIdDesc(size, callback, column2 = "id") {
2622
- this.assertPositiveInteger(size, "Chunk size");
2872
+ assertPositiveInteger(size, "Chunk size", (message) => new SecurityError(message));
2623
2873
  const rows = await this.getUnpaginatedRows();
2624
2874
  const sortedRows = [...rows].sort((left, right) => {
2625
2875
  const a = left[column2];
@@ -2636,7 +2886,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2636
2886
  }
2637
2887
  }
2638
2888
  async *lazy(size = 1e3) {
2639
- this.assertPositiveInteger(size, "Chunk size");
2889
+ assertPositiveInteger(size, "Chunk size", (message) => new SecurityError(message));
2640
2890
  const rows = await this.getUnpaginatedRows();
2641
2891
  for (let index = 0; index < rows.length; index += size) {
2642
2892
  for (const row of rows.slice(index, index + size)) {
@@ -2713,15 +2963,19 @@ var TableQueryBuilder = class _TableQueryBuilder {
2713
2963
  }
2714
2964
  async insert(values) {
2715
2965
  const rows = Array.isArray(values) ? values.map((value) => this.normalizeWriteRecord(value)) : [this.normalizeWriteRecord(values)];
2716
- return this.connection.executeCompiled(this.getCompiler().compile(
2966
+ const result = await this.connection.executeCompiled(this.getCompiler().compile(
2717
2967
  createInsertQueryPlan(this.source, rows)
2718
2968
  ));
2969
+ await this.invalidateSourceTableQueries();
2970
+ return result;
2719
2971
  }
2720
2972
  async insertOrIgnore(values) {
2721
2973
  const rows = Array.isArray(values) ? values.map((value) => this.normalizeWriteRecord(value)) : [this.normalizeWriteRecord(values)];
2722
- return this.connection.executeCompiled(this.getCompiler().compile(
2974
+ const result = await this.connection.executeCompiled(this.getCompiler().compile(
2723
2975
  createInsertQueryPlan(this.source, rows, { ignoreConflicts: true })
2724
2976
  ));
2977
+ await this.invalidateSourceTableQueries();
2978
+ return result;
2725
2979
  }
2726
2980
  async insertGetId(values) {
2727
2981
  const result = await this.insert(values);
@@ -2729,9 +2983,11 @@ var TableQueryBuilder = class _TableQueryBuilder {
2729
2983
  }
2730
2984
  async upsert(values, uniqueBy, updateColumns = []) {
2731
2985
  const rows = Array.isArray(values) ? values.map((value) => this.normalizeWriteRecord(value)) : [this.normalizeWriteRecord(values)];
2732
- return this.connection.executeCompiled(this.getCompiler().compile(
2986
+ const result = await this.connection.executeCompiled(this.getCompiler().compile(
2733
2987
  createUpsertQueryPlan(this.source, rows, uniqueBy, updateColumns)
2734
2988
  ));
2989
+ await this.invalidateSourceTableQueries();
2990
+ return result;
2735
2991
  }
2736
2992
  async increment(column2, amount = 1, extraValues = {}) {
2737
2993
  return this.adjustNumericColumn(column2, amount, extraValues);
@@ -2740,17 +2996,25 @@ var TableQueryBuilder = class _TableQueryBuilder {
2740
2996
  return this.adjustNumericColumn(column2, -amount, extraValues);
2741
2997
  }
2742
2998
  async update(values) {
2743
- return this.connection.executeCompiled(this.getCompiler().compile(
2999
+ const result = await this.connection.executeCompiled(this.getCompiler().compile(
2744
3000
  createUpdateQueryPlan(this.source, this.plan.predicates, this.normalizeUpdateValues(values))
2745
3001
  ));
3002
+ if (result.affectedRows !== 0) {
3003
+ await this.invalidateSourceTableQueries();
3004
+ }
3005
+ return result;
2746
3006
  }
2747
3007
  async updateJson(columnPath, value) {
2748
3008
  return this.update({ [columnPath]: value });
2749
3009
  }
2750
3010
  async delete() {
2751
- return this.connection.executeCompiled(this.getCompiler().compile(
3011
+ const result = await this.connection.executeCompiled(this.getCompiler().compile(
2752
3012
  createDeleteQueryPlan(this.source, this.plan.predicates)
2753
3013
  ));
3014
+ if (result.affectedRows !== 0) {
3015
+ await this.invalidateSourceTableQueries();
3016
+ }
3017
+ return result;
2754
3018
  }
2755
3019
  async unsafeQuery(statement) {
2756
3020
  return this.connection.unsafeQuery({
@@ -2768,7 +3032,12 @@ var TableQueryBuilder = class _TableQueryBuilder {
2768
3032
  }
2769
3033
  clone(plan) {
2770
3034
  const table = this.source.table ?? this.source.tableName;
2771
- return new _TableQueryBuilder(table, this.connection, plan);
3035
+ return new _TableQueryBuilder(table, this.connection, plan, this.queryCacheConfig);
3036
+ }
3037
+ async invalidateSourceTableQueries() {
3038
+ await invalidateQueryCacheDependencies(this.connection, [
3039
+ `db:${this.connection.getConnectionName()}:${this.source.tableName}`
3040
+ ]);
2772
3041
  }
2773
3042
  getCompiler() {
2774
3043
  const dialect = this.connection.getDialect();
@@ -2816,7 +3085,12 @@ var TableQueryBuilder = class _TableQueryBuilder {
2816
3085
  throw new SecurityError("Increment/decrement amount must be a valid number.");
2817
3086
  }
2818
3087
  const primaryKey = this.resolvePrimaryKeyColumn();
2819
- const rows = await this.select(primaryKey, column2).get();
3088
+ const uncachedSelection = new _TableQueryBuilder(
3089
+ this.source.table ?? this.source.tableName,
3090
+ this.connection,
3091
+ withSelections(this.plan, [primaryKey, column2])
3092
+ );
3093
+ const rows = await uncachedSelection.get();
2820
3094
  let affectedRows = 0;
2821
3095
  let lastInsertId;
2822
3096
  for (const row of rows) {
@@ -2874,22 +3148,6 @@ var TableQueryBuilder = class _TableQueryBuilder {
2874
3148
  minSimilarity
2875
3149
  })).orderByVectorSimilarity(column2, vector);
2876
3150
  }
2877
- assertPositiveInteger(value, kind) {
2878
- if (!Number.isInteger(value) || value <= 0) {
2879
- throw new SecurityError(`${kind} must be a positive integer.`);
2880
- }
2881
- }
2882
- normalizePaginationParameterName(value, fallback) {
2883
- if (typeof value === "undefined") {
2884
- return fallback;
2885
- }
2886
- if (typeof value !== "string" || value.trim().length === 0) {
2887
- throw new SecurityError(
2888
- `${fallback === "cursor" ? "Cursor" : "Page"} parameter name must be a non-empty string.`
2889
- );
2890
- }
2891
- return value;
2892
- }
2893
3151
  prepareCursorPaginationQuery() {
2894
3152
  if (this.plan.orderBy.some((orderBy) => orderBy.kind === "random")) {
2895
3153
  throw new SecurityError("Cursor pagination cannot use random ordering.");
@@ -2902,24 +3160,6 @@ var TableQueryBuilder = class _TableQueryBuilder {
2902
3160
  }
2903
3161
  return this;
2904
3162
  }
2905
- encodeCursor(offset) {
2906
- return Buffer.from(JSON.stringify({ offset }), "utf8").toString("base64url");
2907
- }
2908
- decodeCursor(cursor) {
2909
- if (cursor === null) {
2910
- return 0;
2911
- }
2912
- try {
2913
- const decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
2914
- const offset = decoded.offset;
2915
- if (typeof offset !== "number" || !Number.isInteger(offset) || offset < 0) {
2916
- throw new Error("invalid offset");
2917
- }
2918
- return offset;
2919
- } catch {
2920
- throw new SecurityError("Cursor is malformed.");
2921
- }
2922
- }
2923
3163
  async aggregateNumeric(column2, kind) {
2924
3164
  const rows = await this.get();
2925
3165
  if (rows.length === 0) {
@@ -6147,13 +6387,17 @@ function createModelEventService() {
6147
6387
 
6148
6388
  // src/concurrency/AsyncConnectionContext.ts
6149
6389
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
6390
+ function getAsyncConnectionStorage() {
6391
+ const runtime = globalThis;
6392
+ runtime.__holoAsyncConnectionStorage__ ??= new AsyncLocalStorage2();
6393
+ return runtime.__holoAsyncConnectionStorage__;
6394
+ }
6150
6395
  var AsyncConnectionContext = class {
6151
- storage = new AsyncLocalStorage2();
6152
6396
  run(scope, callback) {
6153
- return this.storage.run(scope, callback);
6397
+ return getAsyncConnectionStorage().run(scope, callback);
6154
6398
  }
6155
6399
  getActive() {
6156
- return this.storage.getStore();
6400
+ return getAsyncConnectionStorage().getStore();
6157
6401
  }
6158
6402
  };
6159
6403
  var connectionAsyncContext = new AsyncConnectionContext();
@@ -6203,19 +6447,24 @@ function resetMorphRegistry() {
6203
6447
  }
6204
6448
 
6205
6449
  // src/facade/DB.ts
6450
+ function getDatabaseFacadeState() {
6451
+ const runtime = globalThis;
6452
+ runtime.__holoDatabaseFacade__ ??= {};
6453
+ return runtime.__holoDatabaseFacade__;
6454
+ }
6206
6455
  var DatabaseFacade = class {
6207
- manager;
6208
6456
  configure(manager) {
6209
- this.manager = manager;
6457
+ getDatabaseFacadeState().manager = manager;
6210
6458
  }
6211
6459
  reset() {
6212
- this.manager = void 0;
6460
+ getDatabaseFacadeState().manager = void 0;
6213
6461
  }
6214
6462
  getManager() {
6215
- if (!this.manager) {
6463
+ const manager = getDatabaseFacadeState().manager;
6464
+ if (!manager) {
6216
6465
  throw new ConfigurationError("DB facade is not configured with a ConnectionManager.");
6217
6466
  }
6218
- return this.manager;
6467
+ return manager;
6219
6468
  }
6220
6469
  connection(name) {
6221
6470
  if (!name) {
@@ -7528,40 +7777,16 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7528
7777
  return this.orWhereHas(relation, (query) => query.where(column2, operator, value));
7529
7778
  }
7530
7779
  whereMorphRelation(relation, types, column2, operator, value) {
7531
- this.assertMorphToRelation(relation);
7532
- return this.withRelationFilter({
7533
- relation,
7534
- negate: false,
7535
- boolean: "and",
7536
- morphTypes: this.normalizeMorphTypes(types),
7537
- constraint: (query) => query.where(column2, operator, value)
7538
- });
7780
+ return this.applyMorphRelationFilter(relation, types, column2, operator, value, "and");
7539
7781
  }
7540
7782
  orWhereMorphRelation(relation, types, column2, operator, value) {
7541
- this.assertMorphToRelation(relation);
7542
- return this.withRelationFilter({
7543
- relation,
7544
- negate: false,
7545
- boolean: "or",
7546
- morphTypes: this.normalizeMorphTypes(types),
7547
- constraint: (query) => query.where(column2, operator, value)
7548
- });
7783
+ return this.applyMorphRelationFilter(relation, types, column2, operator, value, "or");
7549
7784
  }
7550
7785
  whereBelongsTo(relatedEntity, relationName) {
7551
- const relation = this.resolveBelongsToRelation(relatedEntity, relationName);
7552
- const ownerValue = relatedEntity.get(relation.ownerKey);
7553
- return ownerValue === null || typeof ownerValue === "undefined" ? this.whereDoesntHave(this.resolveBelongsToRelationName(relatedEntity, relationName)) : this.whereHas(
7554
- this.resolveBelongsToRelationName(relatedEntity, relationName),
7555
- (query) => query.where(relation.ownerKey, ownerValue)
7556
- );
7786
+ return this.applyBelongsToFilter(relatedEntity, relationName, "and");
7557
7787
  }
7558
7788
  orWhereBelongsTo(relatedEntity, relationName) {
7559
- const relation = this.resolveBelongsToRelation(relatedEntity, relationName);
7560
- const ownerValue = relatedEntity.get(relation.ownerKey);
7561
- return ownerValue === null || typeof ownerValue === "undefined" ? this.orWhereDoesntHave(this.resolveBelongsToRelationName(relatedEntity, relationName)) : this.orWhereHas(
7562
- this.resolveBelongsToRelationName(relatedEntity, relationName),
7563
- (query) => query.where(relation.ownerKey, ownerValue)
7564
- );
7789
+ return this.applyBelongsToFilter(relatedEntity, relationName, "or");
7565
7790
  }
7566
7791
  whereMorphedTo(relation, target) {
7567
7792
  return this.applyMorphedToFilter(relation, target, false, "and");
@@ -7606,6 +7831,9 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7606
7831
  this.tableQuery.dump();
7607
7832
  return this;
7608
7833
  }
7834
+ cache(config) {
7835
+ return this.clone(this.tableQuery.cache(config));
7836
+ }
7609
7837
  async get() {
7610
7838
  const rows = await this.tableQuery.get();
7611
7839
  const hasQueryCasts = Object.keys(this.queryCasts).length > 0;
@@ -7633,9 +7861,9 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7633
7861
  return entities[0];
7634
7862
  }
7635
7863
  async paginate(perPage = 15, page = 1, options = {}) {
7636
- this.assertPositiveInteger(perPage, "Per-page value");
7637
- this.assertPositiveInteger(page, "Page");
7638
- const pageName = this.normalizePaginationParameterName(options.pageName, "page");
7864
+ assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
7865
+ assertPositiveInteger(page, "Page", (message) => new HydrationError(message));
7866
+ const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new HydrationError(message));
7639
7867
  const entities = await this.getUnpaginatedEntities();
7640
7868
  const total = entities.length;
7641
7869
  const offset = (page - 1) * perPage;
@@ -7654,9 +7882,9 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7654
7882
  });
7655
7883
  }
7656
7884
  async simplePaginate(perPage = 15, page = 1, options = {}) {
7657
- this.assertPositiveInteger(perPage, "Per-page value");
7658
- this.assertPositiveInteger(page, "Page");
7659
- const pageName = this.normalizePaginationParameterName(options.pageName, "page");
7885
+ assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
7886
+ assertPositiveInteger(page, "Page", (message) => new HydrationError(message));
7887
+ const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new HydrationError(message));
7660
7888
  const entities = await this.getUnpaginatedEntities();
7661
7889
  const offset = (page - 1) * perPage;
7662
7890
  const pageEntities = entities.slice(offset, offset + perPage + 1);
@@ -7674,9 +7902,9 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7674
7902
  });
7675
7903
  }
7676
7904
  async cursorPaginate(perPage = 15, cursor = null, options = {}) {
7677
- this.assertPositiveInteger(perPage, "Per-page value");
7678
- const cursorName = this.normalizePaginationParameterName(options.cursorName, "cursor");
7679
- const offset = this.decodeCursor(cursor);
7905
+ assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
7906
+ const cursorName = normalizePaginationParameterName(options.cursorName, "cursor", (message) => new HydrationError(message));
7907
+ const offset = decodeOffsetCursor(cursor, (message) => new HydrationError(message));
7680
7908
  const orderedQuery = this.prepareCursorPaginationQuery();
7681
7909
  const entities = await orderedQuery.getUnpaginatedEntities();
7682
7910
  const pageEntities = entities.slice(offset, offset + perPage + 1);
@@ -7685,12 +7913,12 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7685
7913
  return createCursorPaginator(this.repository.createCollection(data), {
7686
7914
  perPage,
7687
7915
  cursorName,
7688
- nextCursor: hasMorePages ? this.encodeCursor(offset + perPage) : null,
7916
+ nextCursor: hasMorePages ? encodeOffsetCursor(offset + perPage) : null,
7689
7917
  prevCursor: cursor
7690
7918
  });
7691
7919
  }
7692
7920
  async chunk(size, callback) {
7693
- this.assertPositiveInteger(size, "Chunk size");
7921
+ assertPositiveInteger(size, "Chunk size", (message) => new HydrationError(message));
7694
7922
  const entities = await this.getUnpaginatedEntities();
7695
7923
  let page = 1;
7696
7924
  for (let index = 0; index < entities.length; index += size) {
@@ -7702,7 +7930,7 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7702
7930
  }
7703
7931
  }
7704
7932
  async chunkById(size, callback, column2 = this.repository.definition.primaryKey) {
7705
- this.assertPositiveInteger(size, "Chunk size");
7933
+ assertPositiveInteger(size, "Chunk size", (message) => new HydrationError(message));
7706
7934
  const entities = await this.getUnpaginatedEntities();
7707
7935
  const sorted = [...entities].sort((left, right) => {
7708
7936
  const a = left.get(column2);
@@ -7719,7 +7947,7 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7719
7947
  }
7720
7948
  }
7721
7949
  async chunkByIdDesc(size, callback, column2 = this.repository.definition.primaryKey) {
7722
- this.assertPositiveInteger(size, "Chunk size");
7950
+ assertPositiveInteger(size, "Chunk size", (message) => new HydrationError(message));
7723
7951
  const entities = await this.getUnpaginatedEntities();
7724
7952
  const sorted = [...entities].sort((left, right) => {
7725
7953
  const a = left.get(column2);
@@ -7736,7 +7964,7 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
7736
7964
  }
7737
7965
  }
7738
7966
  async *lazy(size = 1e3) {
7739
- this.assertPositiveInteger(size, "Chunk size");
7967
+ assertPositiveInteger(size, "Chunk size", (message) => new HydrationError(message));
7740
7968
  const entities = await this.getUnpaginatedEntities();
7741
7969
  for (let index = 0; index < entities.length; index += size) {
7742
7970
  for (const entity of entities.slice(index, index + size)) {
@@ -8019,6 +8247,16 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8019
8247
  constraint
8020
8248
  });
8021
8249
  }
8250
+ applyMorphRelationFilter(relation, types, column2, operator, value, boolean) {
8251
+ this.assertMorphToRelation(relation);
8252
+ return this.withRelationFilter({
8253
+ relation,
8254
+ negate: false,
8255
+ boolean,
8256
+ morphTypes: this.normalizeMorphTypes(types),
8257
+ constraint: (query) => query.where(column2, operator, value)
8258
+ });
8259
+ }
8022
8260
  normalizeMorphedToTarget(target) {
8023
8261
  if (typeof target === "object" && target !== null && "definition" in target && target.definition) {
8024
8262
  return {
@@ -8090,6 +8328,15 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8090
8328
  }
8091
8329
  return relation;
8092
8330
  }
8331
+ applyBelongsToFilter(relatedEntity, relationName, boolean) {
8332
+ const resolvedRelationName = this.resolveBelongsToRelationName(relatedEntity, relationName);
8333
+ const relation = this.resolveBelongsToRelation(relatedEntity, resolvedRelationName);
8334
+ const ownerValue = relatedEntity.get(relation.ownerKey);
8335
+ if (ownerValue === null || typeof ownerValue === "undefined") {
8336
+ return boolean === "and" ? this.whereDoesntHave(resolvedRelationName) : this.orWhereDoesntHave(resolvedRelationName);
8337
+ }
8338
+ return boolean === "and" ? this.whereHas(resolvedRelationName, (query) => query.where(relation.ownerKey, ownerValue)) : this.orWhereHas(resolvedRelationName, (query) => query.where(relation.ownerKey, ownerValue));
8339
+ }
8093
8340
  resolveBelongsToRelationName(relatedEntity, relationName) {
8094
8341
  if (relationName) {
8095
8342
  return relationName;
@@ -8124,40 +8371,6 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8124
8371
  async getUnpaginatedEntities() {
8125
8372
  return this.clone(this.tableQuery.limit(void 0).offset(void 0)).get();
8126
8373
  }
8127
- encodeCursor(offset) {
8128
- return Buffer.from(JSON.stringify({ offset }), "utf8").toString("base64url");
8129
- }
8130
- decodeCursor(cursor) {
8131
- if (cursor === null) {
8132
- return 0;
8133
- }
8134
- try {
8135
- const decoded = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
8136
- const offset = decoded.offset;
8137
- if (typeof offset !== "number" || !Number.isInteger(offset) || offset < 0) {
8138
- throw new Error("invalid offset");
8139
- }
8140
- return offset;
8141
- } catch {
8142
- throw new HydrationError("Cursor is malformed.");
8143
- }
8144
- }
8145
- assertPositiveInteger(value, kind) {
8146
- if (!Number.isInteger(value) || value <= 0) {
8147
- throw new HydrationError(`${kind} must be a positive integer.`);
8148
- }
8149
- }
8150
- normalizePaginationParameterName(value, fallback) {
8151
- if (typeof value === "undefined") {
8152
- return fallback;
8153
- }
8154
- if (typeof value !== "string" || value.trim().length === 0) {
8155
- throw new HydrationError(
8156
- `${fallback === "cursor" ? "Cursor" : "Page"} parameter name must be a non-empty string.`
8157
- );
8158
- }
8159
- return value;
8160
- }
8161
8374
  prepareCursorPaginationQuery() {
8162
8375
  const plan = this.tableQuery.getPlan();
8163
8376
  if (plan.orderBy.some((orderBy) => orderBy.kind === "random")) {
@@ -11615,512 +11828,243 @@ function createConnectionManager(options) {
11615
11828
  return new ConnectionManager(options);
11616
11829
  }
11617
11830
 
11618
- // src/drivers/SQLiteAdapter.ts
11619
- import Database from "better-sqlite3";
11620
- var SQLiteAdapter = class {
11621
- database;
11622
- connected;
11623
- filename;
11624
- createDatabaseInstance;
11625
- constructor(options = {}) {
11626
- this.database = options.database;
11627
- this.connected = !!options.database;
11628
- this.filename = options.filename ?? ":memory:";
11629
- this.createDatabaseInstance = options.createDatabase ?? ((filename) => new Database(filename));
11831
+ // src/drivers/index.ts
11832
+ function isModuleNotFoundError(error) {
11833
+ return !!error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND";
11834
+ }
11835
+ function dynamicImport(specifier) {
11836
+ return import(
11837
+ /* webpackIgnore: true */
11838
+ specifier
11839
+ );
11840
+ }
11841
+ async function importDriverModule(specifier, errorMessage) {
11842
+ try {
11843
+ return await dynamicImport(specifier);
11844
+ } catch (error) {
11845
+ if (isModuleNotFoundError(error)) {
11846
+ throw new Error(errorMessage, { cause: error });
11847
+ }
11848
+ throw error;
11849
+ }
11850
+ }
11851
+ var driverModuleInternals = {
11852
+ importDriverModule
11853
+ };
11854
+ function unsupportedDriverMethod(name, method) {
11855
+ return new Error(`[@holo-js/db] ${name} does not support ${method}().`);
11856
+ }
11857
+ var LazyDriverAdapter = class {
11858
+ adapter;
11859
+ pending;
11860
+ connected = false;
11861
+ async resolveAdapter() {
11862
+ if (this.adapter) {
11863
+ return this.adapter;
11864
+ }
11865
+ this.pending ??= this.createConcreteAdapter().then((adapter) => {
11866
+ this.adapter = adapter;
11867
+ return adapter;
11868
+ }).finally(() => {
11869
+ this.pending = void 0;
11870
+ });
11871
+ return this.pending;
11630
11872
  }
11631
11873
  async initialize() {
11632
- if (this.connected) {
11633
- return;
11634
- }
11635
- this.database = this.createDatabaseInstance(this.filename);
11874
+ await (await this.resolveAdapter()).initialize();
11636
11875
  this.connected = true;
11637
11876
  }
11638
11877
  async disconnect() {
11639
- if (!this.connected || !this.database) {
11878
+ if (!this.adapter && !this.pending) {
11879
+ this.connected = false;
11640
11880
  return;
11641
11881
  }
11642
- this.database.close();
11643
- this.database = void 0;
11882
+ await (await this.resolveAdapter()).disconnect();
11644
11883
  this.connected = false;
11645
11884
  }
11646
11885
  isConnected() {
11647
- return this.connected;
11886
+ return this.adapter?.isConnected() ?? this.connected;
11648
11887
  }
11649
- async query(sql, bindings = []) {
11650
- const statement = this.getDatabase().prepare(sql);
11651
- const rows = this.invokeStatement(statement, "all", bindings);
11652
- return {
11653
- rows,
11654
- rowCount: rows.length
11655
- };
11888
+ async runWithTransactionScope(callback) {
11889
+ const adapter = await this.resolveAdapter();
11890
+ if (typeof adapter.runWithTransactionScope !== "function") {
11891
+ return callback();
11892
+ }
11893
+ return adapter.runWithTransactionScope(callback);
11656
11894
  }
11657
- async introspect(sql, bindings = []) {
11658
- return this.query(sql, bindings);
11895
+ async introspect(sql, bindings, options) {
11896
+ const adapter = await this.resolveAdapter();
11897
+ if (typeof adapter.introspect !== "function") {
11898
+ return adapter.query(sql, bindings, options);
11899
+ }
11900
+ return adapter.introspect(sql, bindings, options);
11659
11901
  }
11660
- async execute(sql, bindings = []) {
11661
- const statement = this.getDatabase().prepare(sql);
11662
- const result = this.invokeStatement(statement, "run", bindings);
11663
- return {
11664
- affectedRows: result.changes,
11665
- lastInsertId: typeof result.lastInsertRowid === "bigint" ? Number(result.lastInsertRowid) : result.lastInsertRowid
11666
- };
11902
+ async query(sql, bindings, options) {
11903
+ return (await this.resolveAdapter()).query(sql, bindings, options);
11667
11904
  }
11668
- async beginTransaction() {
11669
- this.getDatabase().exec("BEGIN");
11905
+ async execute(sql, bindings, options) {
11906
+ return (await this.resolveAdapter()).execute(sql, bindings, options);
11670
11907
  }
11671
- async commit() {
11672
- this.getDatabase().exec("COMMIT");
11908
+ async beginTransaction(options) {
11909
+ await (await this.resolveAdapter()).beginTransaction(options);
11673
11910
  }
11674
- async rollback() {
11675
- this.getDatabase().exec("ROLLBACK");
11911
+ async commit(options) {
11912
+ await (await this.resolveAdapter()).commit(options);
11676
11913
  }
11677
- async createSavepoint(name) {
11678
- this.getDatabase().exec(`SAVEPOINT ${this.normalizeSavepointName(name)}`);
11914
+ async rollback(options) {
11915
+ await (await this.resolveAdapter()).rollback(options);
11679
11916
  }
11680
- async rollbackToSavepoint(name) {
11681
- this.getDatabase().exec(`ROLLBACK TO SAVEPOINT ${this.normalizeSavepointName(name)}`);
11917
+ async createSavepoint(name, options) {
11918
+ const adapter = await this.resolveAdapter();
11919
+ if (typeof adapter.createSavepoint !== "function") {
11920
+ throw unsupportedDriverMethod(this.driverLabel, "createSavepoint");
11921
+ }
11922
+ await adapter.createSavepoint(name, options);
11682
11923
  }
11683
- async releaseSavepoint(name) {
11684
- this.getDatabase().exec(`RELEASE SAVEPOINT ${this.normalizeSavepointName(name)}`);
11924
+ async rollbackToSavepoint(name, options) {
11925
+ const adapter = await this.resolveAdapter();
11926
+ if (typeof adapter.rollbackToSavepoint !== "function") {
11927
+ throw unsupportedDriverMethod(this.driverLabel, "rollbackToSavepoint");
11928
+ }
11929
+ await adapter.rollbackToSavepoint(name, options);
11685
11930
  }
11686
- getDatabase() {
11687
- if (!this.connected || !this.database) {
11688
- this.database = this.createDatabaseInstance(this.filename);
11689
- this.connected = true;
11931
+ async releaseSavepoint(name, options) {
11932
+ const adapter = await this.resolveAdapter();
11933
+ if (typeof adapter.releaseSavepoint !== "function") {
11934
+ throw unsupportedDriverMethod(this.driverLabel, "releaseSavepoint");
11690
11935
  }
11691
- return this.database;
11936
+ await adapter.releaseSavepoint(name, options);
11692
11937
  }
11693
- normalizeSavepointName(name) {
11938
+ };
11939
+ var SQLiteAdapter = class extends LazyDriverAdapter {
11940
+ constructor(options = {}) {
11941
+ super();
11942
+ this.options = options;
11943
+ this.filename = options.filename ?? ":memory:";
11944
+ this.connected = !!options.database;
11945
+ }
11946
+ driverLabel = "SQLiteAdapter";
11947
+ filename;
11948
+ async createConcreteAdapter() {
11949
+ const module = await driverModuleInternals.importDriverModule(
11950
+ "@holo-js/db-sqlite",
11951
+ "[@holo-js/db] SQLite support requires @holo-js/db-sqlite to be installed."
11952
+ );
11953
+ return module.createSQLiteAdapter(this.options);
11954
+ }
11955
+ async createSavepoint(name, options) {
11694
11956
  if (!/^[A-Z_]\w*$/i.test(name)) {
11695
11957
  throw new TransactionError(`Invalid savepoint name "${name}".`);
11696
11958
  }
11697
- return name;
11959
+ await super.createSavepoint(name, options);
11698
11960
  }
11699
- invokeStatement(statement, method, bindings) {
11700
- try {
11701
- return statement[method](...bindings);
11702
- } catch (error) {
11703
- if (bindings.length > 0 && this.isBindingArityError(error)) {
11704
- return statement[method](bindings);
11705
- }
11706
- throw error;
11961
+ async rollbackToSavepoint(name, options) {
11962
+ if (!/^[A-Z_]\w*$/i.test(name)) {
11963
+ throw new TransactionError(`Invalid savepoint name "${name}".`);
11707
11964
  }
11965
+ await super.rollbackToSavepoint(name, options);
11708
11966
  }
11709
- isBindingArityError(error) {
11710
- return error instanceof RangeError && (error.message.includes("Too many parameter values were provided") || error.message.includes("Too few parameter values were provided"));
11967
+ async releaseSavepoint(name, options) {
11968
+ if (!/^[A-Z_]\w*$/i.test(name)) {
11969
+ throw new TransactionError(`Invalid savepoint name "${name}".`);
11970
+ }
11971
+ await super.releaseSavepoint(name, options);
11711
11972
  }
11712
11973
  };
11713
11974
  function createSQLiteAdapter(options = {}) {
11714
11975
  return new SQLiteAdapter(options);
11715
11976
  }
11716
-
11717
- // src/drivers/PostgresAdapter.ts
11718
- import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
11719
- import { Pool } from "pg";
11720
- var PostgresAdapter = class {
11721
- pool;
11722
- directClient;
11723
- createPoolInstance;
11724
- config;
11725
- connected;
11726
- transactionClient;
11727
- leasedTransactionClient = false;
11728
- transactionScope = new AsyncLocalStorage3();
11977
+ var PostgresAdapter = class extends LazyDriverAdapter {
11729
11978
  constructor(options = {}) {
11730
- this.directClient = options.client;
11731
- this.pool = options.pool;
11732
- this.createPoolInstance = options.createPool ?? (options.client || options.pool ? void 0 : (config) => new Pool(config));
11979
+ super();
11980
+ this.options = options;
11733
11981
  this.config = options.config ?? (options.connectionString ? { connectionString: options.connectionString } : void 0);
11734
11982
  this.connected = !!(options.client || options.pool);
11735
11983
  }
11736
- async initialize() {
11737
- if (this.connected) {
11738
- return;
11739
- }
11740
- if (this.createPoolInstance) {
11741
- this.pool = this.createPoolInstance(this.config);
11742
- }
11743
- this.connected = true;
11984
+ driverLabel = "PostgresAdapter";
11985
+ config;
11986
+ async createConcreteAdapter() {
11987
+ const module = await driverModuleInternals.importDriverModule(
11988
+ "@holo-js/db-postgres",
11989
+ "[@holo-js/db] Postgres support requires @holo-js/db-postgres to be installed."
11990
+ );
11991
+ const createPostgresAdapter2 = module.createPostgresAdapter;
11992
+ return createPostgresAdapter2(this.options);
11744
11993
  }
11745
- async disconnect() {
11746
- if (!this.connected) {
11994
+ releaseScopedTransaction(state) {
11995
+ if (state.released) {
11747
11996
  return;
11748
11997
  }
11749
- if (this.transactionClient && this.leasedTransactionClient) {
11750
- this.transactionClient.release?.();
11751
- this.transactionClient = void 0;
11752
- this.leasedTransactionClient = false;
11753
- }
11754
- if (this.pool) {
11755
- await this.pool.end();
11756
- this.pool = void 0;
11757
- } else if (this.directClient?.end) {
11758
- await this.directClient.end();
11759
- }
11760
- this.connected = false;
11761
- }
11762
- isConnected() {
11763
- return this.connected;
11764
- }
11765
- async runWithTransactionScope(callback) {
11766
- const active = this.transactionScope.getStore();
11767
- if (active) {
11768
- return callback();
11769
- }
11770
- await this.initialize();
11771
- if (this.directClient) {
11772
- return this.transactionScope.run({
11773
- client: this.directClient,
11774
- leased: false,
11775
- released: false
11776
- }, callback);
11777
- }
11778
- if (!this.pool) {
11779
- throw new TransactionError("Postgres adapter is not initialized with a pool or client.");
11780
- }
11781
- const state = {
11782
- client: await this.pool.connect(),
11783
- leased: true,
11784
- released: false
11785
- };
11786
- return this.transactionScope.run(state, async () => {
11787
- try {
11788
- return await callback();
11789
- } finally {
11790
- this.releaseScopedTransaction(state);
11791
- }
11792
- });
11793
- }
11794
- async query(sql, bindings = []) {
11795
- const client = await this.getQueryable();
11796
- const result = await client.query(sql, bindings);
11797
- return {
11798
- rows: result.rows,
11799
- rowCount: result.rowCount ?? result.rows.length
11800
- };
11801
- }
11802
- async introspect(sql, bindings = []) {
11803
- return this.query(sql, bindings);
11804
- }
11805
- async execute(sql, bindings = []) {
11806
- const client = await this.getQueryable();
11807
- const result = await client.query(sql, bindings);
11808
- const firstRow = result.rows[0];
11809
- const firstValue = firstRow ? Object.values(firstRow)[0] : void 0;
11810
- return {
11811
- affectedRows: result.rowCount ?? 0,
11812
- ...typeof firstValue !== "undefined" ? { lastInsertId: firstValue } : {}
11813
- };
11814
- }
11815
- async beginTransaction() {
11816
- const client = await this.leaseTransactionClient();
11817
- await client.query("BEGIN");
11818
- }
11819
- async commit() {
11820
- const client = this.requireTransactionClient();
11821
- await client.query("COMMIT");
11822
- this.releaseTransactionClient();
11823
- }
11824
- async rollback() {
11825
- const client = this.requireTransactionClient();
11826
- await client.query("ROLLBACK");
11827
- this.releaseTransactionClient();
11828
- }
11829
- async createSavepoint(name) {
11830
- await this.requireTransactionClient().query(`SAVEPOINT ${this.normalizeSavepointName(name)}`);
11831
- }
11832
- async rollbackToSavepoint(name) {
11833
- await this.requireTransactionClient().query(`ROLLBACK TO SAVEPOINT ${this.normalizeSavepointName(name)}`);
11834
- }
11835
- async releaseSavepoint(name) {
11836
- await this.requireTransactionClient().query(`RELEASE SAVEPOINT ${this.normalizeSavepointName(name)}`);
11837
- }
11838
- async getQueryable() {
11839
- const scoped = this.transactionScope.getStore();
11840
- if (scoped) {
11841
- return scoped.client;
11842
- }
11843
- if (this.transactionClient) {
11844
- return this.transactionClient;
11845
- }
11846
- await this.initialize();
11847
- if (this.directClient) {
11848
- return this.directClient;
11849
- }
11850
- if (!this.pool) {
11851
- throw new TransactionError("Postgres adapter is not initialized with a pool or client.");
11998
+ if (state.leased) {
11999
+ state.client.release?.();
11852
12000
  }
11853
- return this.pool;
11854
- }
11855
- async leaseTransactionClient() {
11856
- const scoped = this.transactionScope.getStore();
11857
- if (scoped) {
11858
- return scoped.client;
11859
- }
11860
- if (this.transactionClient) {
11861
- return this.transactionClient;
11862
- }
11863
- await this.initialize();
11864
- if (this.directClient) {
11865
- this.transactionClient = this.directClient;
11866
- this.leasedTransactionClient = false;
11867
- return this.transactionClient;
11868
- }
11869
- if (!this.pool) {
11870
- throw new TransactionError("Postgres adapter is not initialized with a pool or client.");
11871
- }
11872
- this.transactionClient = await this.pool.connect();
11873
- this.leasedTransactionClient = true;
11874
- return this.transactionClient;
11875
- }
11876
- requireTransactionClient() {
11877
- const scoped = this.transactionScope.getStore();
11878
- if (scoped) {
11879
- return scoped.client;
11880
- }
11881
- if (!this.transactionClient) {
11882
- throw new TransactionError("No active Postgres transaction client is available.");
11883
- }
11884
- return this.transactionClient;
12001
+ state.released = true;
11885
12002
  }
11886
- releaseTransactionClient() {
11887
- if (this.transactionClient && this.leasedTransactionClient) {
11888
- this.transactionClient.release?.();
12003
+ async createSavepoint(name, options) {
12004
+ if (!/^[A-Z_]\w*$/i.test(name)) {
12005
+ throw new TransactionError(`Invalid savepoint name "${name}".`);
11889
12006
  }
11890
- this.transactionClient = void 0;
11891
- this.leasedTransactionClient = false;
12007
+ await super.createSavepoint(name, options);
11892
12008
  }
11893
- releaseScopedTransaction(state) {
11894
- if (!state.leased || state.released) {
11895
- return;
12009
+ async rollbackToSavepoint(name, options) {
12010
+ if (!/^[A-Z_]\w*$/i.test(name)) {
12011
+ throw new TransactionError(`Invalid savepoint name "${name}".`);
11896
12012
  }
11897
- state.client.release?.();
11898
- state.released = true;
12013
+ await super.rollbackToSavepoint(name, options);
11899
12014
  }
11900
- normalizeSavepointName(name) {
12015
+ async releaseSavepoint(name, options) {
11901
12016
  if (!/^[A-Z_]\w*$/i.test(name)) {
11902
12017
  throw new TransactionError(`Invalid savepoint name "${name}".`);
11903
12018
  }
11904
- return name;
12019
+ await super.releaseSavepoint(name, options);
11905
12020
  }
11906
12021
  };
11907
12022
  function createPostgresAdapter(options = {}) {
11908
12023
  return new PostgresAdapter(options);
11909
12024
  }
11910
-
11911
- // src/drivers/MySQLAdapter.ts
11912
- import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
11913
- import mysql from "mysql2/promise";
11914
- function toMutableBindings(bindings = []) {
11915
- return [...bindings];
11916
- }
11917
- function wrapMySQLClient(client) {
11918
- const rawClient = client;
11919
- return {
11920
- async query(sql, bindings = []) {
11921
- return rawClient.query(sql, toMutableBindings(bindings));
11922
- },
11923
- release: rawClient.release?.bind(rawClient),
11924
- end: rawClient.end?.bind(rawClient)
11925
- };
11926
- }
11927
- function wrapMySQLPool(pool) {
11928
- const rawPool = pool;
11929
- return {
11930
- async query(sql, bindings = []) {
11931
- return rawPool.query(sql, toMutableBindings(bindings));
11932
- },
11933
- async getConnection() {
11934
- return wrapMySQLClient(await rawPool.getConnection());
11935
- },
11936
- end: rawPool.end.bind(rawPool)
11937
- };
11938
- }
11939
- var MySQLAdapter = class {
11940
- pool;
11941
- directClient;
11942
- createPoolInstance;
11943
- config;
11944
- connected;
11945
- transactionClient;
11946
- leasedTransactionClient = false;
11947
- transactionScope = new AsyncLocalStorage4();
12025
+ var MySQLAdapter = class extends LazyDriverAdapter {
11948
12026
  constructor(options = {}) {
11949
- this.directClient = options.client ? wrapMySQLClient(options.client) : void 0;
11950
- this.pool = options.pool ? wrapMySQLPool(options.pool) : void 0;
11951
- this.createPoolInstance = options.createPool ?? (options.client || options.pool ? void 0 : (config) => wrapMySQLPool(mysql.createPool(config)));
12027
+ super();
12028
+ this.options = options;
11952
12029
  this.config = options.config ?? (options.uri ? { uri: options.uri } : {});
11953
12030
  this.connected = !!(options.client || options.pool);
11954
12031
  }
11955
- async initialize() {
11956
- if (this.connected) {
11957
- return;
11958
- }
11959
- if (this.createPoolInstance) {
11960
- this.pool = this.createPoolInstance(this.config);
11961
- }
11962
- this.connected = true;
12032
+ driverLabel = "MySQLAdapter";
12033
+ config;
12034
+ async createConcreteAdapter() {
12035
+ const module = await driverModuleInternals.importDriverModule(
12036
+ "@holo-js/db-mysql",
12037
+ "[@holo-js/db] MySQL support requires @holo-js/db-mysql to be installed."
12038
+ );
12039
+ const createMySQLAdapter2 = module.createMySQLAdapter;
12040
+ return createMySQLAdapter2(this.options);
11963
12041
  }
11964
- async disconnect() {
11965
- if (!this.connected) {
12042
+ releaseScopedTransaction(state) {
12043
+ if (state.released) {
11966
12044
  return;
11967
12045
  }
11968
- if (this.transactionClient && this.leasedTransactionClient) {
11969
- this.transactionClient.release?.();
11970
- this.transactionClient = void 0;
11971
- this.leasedTransactionClient = false;
11972
- }
11973
- if (this.pool) {
11974
- await this.pool.end();
11975
- this.pool = void 0;
11976
- } else if (this.directClient?.end) {
11977
- await this.directClient.end();
11978
- }
11979
- this.connected = false;
11980
- }
11981
- isConnected() {
11982
- return this.connected;
11983
- }
11984
- async runWithTransactionScope(callback) {
11985
- const active = this.transactionScope.getStore();
11986
- if (active) {
11987
- return callback();
11988
- }
11989
- await this.initialize();
11990
- if (this.directClient) {
11991
- return this.transactionScope.run({
11992
- client: this.directClient,
11993
- leased: false,
11994
- released: false
11995
- }, callback);
11996
- }
11997
- if (!this.pool) {
11998
- throw new TransactionError("MySQL adapter is not initialized with a pool or client.");
11999
- }
12000
- const state = {
12001
- client: await this.pool.getConnection(),
12002
- leased: true,
12003
- released: false
12004
- };
12005
- return this.transactionScope.run(state, async () => {
12006
- try {
12007
- return await callback();
12008
- } finally {
12009
- this.releaseScopedTransaction(state);
12010
- }
12011
- });
12012
- }
12013
- async query(sql, bindings = []) {
12014
- const queryable = await this.getQueryable();
12015
- const [rows] = await queryable.query(sql, bindings);
12016
- const normalized = rows;
12017
- return {
12018
- rows: Array.isArray(normalized) ? [...normalized] : [],
12019
- rowCount: Array.isArray(normalized) ? normalized.length : 0
12020
- };
12021
- }
12022
- async introspect(sql, bindings = []) {
12023
- return this.query(sql, bindings);
12024
- }
12025
- async execute(sql, bindings = []) {
12026
- const queryable = await this.getQueryable();
12027
- const [result] = await queryable.query(sql, bindings);
12028
- const execution = result;
12029
- return {
12030
- affectedRows: typeof execution.affectedRows === "number" ? execution.affectedRows : 0,
12031
- lastInsertId: execution.insertId
12032
- };
12033
- }
12034
- async beginTransaction() {
12035
- const client = await this.leaseTransactionClient();
12036
- await client.query("START TRANSACTION");
12037
- }
12038
- async commit() {
12039
- const client = this.requireTransactionClient();
12040
- await client.query("COMMIT");
12041
- this.releaseTransactionClient();
12042
- }
12043
- async rollback() {
12044
- const client = this.requireTransactionClient();
12045
- await client.query("ROLLBACK");
12046
- this.releaseTransactionClient();
12047
- }
12048
- async createSavepoint(name) {
12049
- await this.requireTransactionClient().query(`SAVEPOINT ${this.normalizeSavepointName(name)}`);
12050
- }
12051
- async rollbackToSavepoint(name) {
12052
- await this.requireTransactionClient().query(`ROLLBACK TO SAVEPOINT ${this.normalizeSavepointName(name)}`);
12053
- }
12054
- async releaseSavepoint(name) {
12055
- await this.requireTransactionClient().query(`RELEASE SAVEPOINT ${this.normalizeSavepointName(name)}`);
12056
- }
12057
- async getQueryable() {
12058
- const scoped = this.transactionScope.getStore();
12059
- if (scoped) {
12060
- return scoped.client;
12046
+ if (state.leased) {
12047
+ state.client.release?.();
12061
12048
  }
12062
- if (this.transactionClient) {
12063
- return this.transactionClient;
12064
- }
12065
- await this.initialize();
12066
- if (this.directClient) {
12067
- return this.directClient;
12068
- }
12069
- if (!this.pool) {
12070
- throw new TransactionError("MySQL adapter is not initialized with a pool or client.");
12071
- }
12072
- return this.pool;
12073
- }
12074
- async leaseTransactionClient() {
12075
- const scoped = this.transactionScope.getStore();
12076
- if (scoped) {
12077
- return scoped.client;
12078
- }
12079
- if (this.transactionClient) {
12080
- return this.transactionClient;
12081
- }
12082
- await this.initialize();
12083
- if (this.directClient) {
12084
- this.transactionClient = this.directClient;
12085
- this.leasedTransactionClient = false;
12086
- return this.transactionClient;
12087
- }
12088
- if (!this.pool) {
12089
- throw new TransactionError("MySQL adapter is not initialized with a pool or client.");
12090
- }
12091
- this.transactionClient = await this.pool.getConnection();
12092
- this.leasedTransactionClient = true;
12093
- return this.transactionClient;
12094
- }
12095
- requireTransactionClient() {
12096
- const scoped = this.transactionScope.getStore();
12097
- if (scoped) {
12098
- return scoped.client;
12099
- }
12100
- if (!this.transactionClient) {
12101
- throw new TransactionError("No active MySQL transaction client is available.");
12102
- }
12103
- return this.transactionClient;
12049
+ state.released = true;
12104
12050
  }
12105
- releaseTransactionClient() {
12106
- if (this.transactionClient && this.leasedTransactionClient) {
12107
- this.transactionClient.release?.();
12051
+ async createSavepoint(name, options) {
12052
+ if (!/^[A-Z_]\w*$/i.test(name)) {
12053
+ throw new TransactionError(`Invalid savepoint name "${name}".`);
12108
12054
  }
12109
- this.transactionClient = void 0;
12110
- this.leasedTransactionClient = false;
12055
+ await super.createSavepoint(name, options);
12111
12056
  }
12112
- releaseScopedTransaction(state) {
12113
- if (!state.leased || state.released) {
12114
- return;
12057
+ async rollbackToSavepoint(name, options) {
12058
+ if (!/^[A-Z_]\w*$/i.test(name)) {
12059
+ throw new TransactionError(`Invalid savepoint name "${name}".`);
12115
12060
  }
12116
- state.client.release?.();
12117
- state.released = true;
12061
+ await super.rollbackToSavepoint(name, options);
12118
12062
  }
12119
- normalizeSavepointName(name) {
12063
+ async releaseSavepoint(name, options) {
12120
12064
  if (!/^[A-Z_]\w*$/i.test(name)) {
12121
12065
  throw new TransactionError(`Invalid savepoint name "${name}".`);
12122
12066
  }
12123
- return name;
12067
+ await super.releaseSavepoint(name, options);
12124
12068
  }
12125
12069
  };
12126
12070
  function createMySQLAdapter(options = {}) {
@@ -13051,7 +12995,7 @@ function defineFactory(model, definition) {
13051
12995
  }
13052
12996
 
13053
12997
  // src/model/casts.ts
13054
- import { createCipheriv, createDecipheriv, createHash, randomBytes } from "crypto";
12998
+ import { createCipheriv, createDecipheriv, createHash as createHash2, randomBytes } from "crypto";
13055
12999
  function enumCast(enumObject) {
13056
13000
  const values = Object.entries(enumObject).filter(([key]) => !/^\d+$/.test(key)).map(([, value]) => value).filter((value, index, items) => items.indexOf(value) === index);
13057
13001
  return Object.freeze({
@@ -13089,7 +13033,7 @@ function binaryCast() {
13089
13033
  });
13090
13034
  }
13091
13035
  function encryptedCast(secret) {
13092
- const key = createHash("sha256").update(secret).digest();
13036
+ const key = createHash2("sha256").update(secret).digest();
13093
13037
  return Object.freeze({
13094
13038
  get(value) {
13095
13039
  if (value == null) {
@@ -14136,7 +14080,9 @@ var DEFAULT_HOLO_PROJECT_PATHS = Object.freeze({
14136
14080
  commands: "server/commands",
14137
14081
  jobs: "server/jobs",
14138
14082
  events: "server/events",
14139
- listeners: "server/listeners"
14083
+ listeners: "server/listeners",
14084
+ authorizationPolicies: "server/policies",
14085
+ authorizationAbilities: "server/abilities"
14140
14086
  });
14141
14087
  function normalizeHoloProjectConfig(config = {}) {
14142
14088
  return Object.freeze({
@@ -14215,6 +14161,7 @@ export {
14215
14161
  column,
14216
14162
  compileDialectDefaultLiteral,
14217
14163
  configureDB,
14164
+ configureDatabaseQueryCacheBridge,
14218
14165
  connectionAsyncContext,
14219
14166
  createAdapter,
14220
14167
  createCapabilities,
@@ -14267,6 +14214,7 @@ export {
14267
14214
  generateSnowflake,
14268
14215
  generateUlid,
14269
14216
  generateUuidV7,
14217
+ getDatabaseQueryCacheBridge,
14270
14218
  getGeneratedTableDefinition,
14271
14219
  getModelDefinition,
14272
14220
  hasMany,
@@ -14293,6 +14241,7 @@ export {
14293
14241
  oldestMorphOne,
14294
14242
  oldestOfMany,
14295
14243
  parseDatabaseDriver,
14244
+ queryCacheInternals,
14296
14245
  redactBindings,
14297
14246
  redactSql,
14298
14247
  registerGeneratedTables,
@@ -14302,6 +14251,7 @@ export {
14302
14251
  renderGeneratedSchemaModule,
14303
14252
  renderGeneratedSchemaPlaceholder,
14304
14253
  resetDB,
14254
+ resetDatabaseQueryCacheBridge,
14305
14255
  resetMorphRegistry,
14306
14256
  resolveDialectColumnType,
14307
14257
  resolveDialectIdStrategyType,