@vantreeseba/drizzle-graphql 15.1.5 → 15.1.6

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/dist/index.js CHANGED
@@ -1235,6 +1235,39 @@ const remapToGraphQLCore = (key, value, tableName, column, relationMap) => {
1235
1235
  }
1236
1236
  return value;
1237
1237
  };
1238
+ /**
1239
+ * Where a remapped row keeps the values its columns held before remapping, by property name —
1240
+ * only for the columns remapping changed. The output form is not always reversible: a
1241
+ * microsecond timestamp read in `'string'` mode leaves as an ISO string the `DateTime` input
1242
+ * side would cut to milliseconds. A relation field correlating on the row's join columns reads
1243
+ * them from here (see `parentJoinValues`). Non-enumerable, so the row's own keys, JSON form and
1244
+ * equality are unchanged.
1245
+ */
1246
+ const RAW_COLUMN_VALUES = Symbol("drizzle-graphql.rawColumnValues");
1247
+ /**
1248
+ * The value column `key` held in `row` before remapping: `{ found: true, value }` when remapping
1249
+ * changed it, `{ found: false }` when it did not (or the row was never remapped), in which case
1250
+ * `row[key]` is still the column's own value.
1251
+ */
1252
+ const rawColumnValue = (row, key) => {
1253
+ const raw = row[RAW_COLUMN_VALUES];
1254
+ return raw && key in raw ? {
1255
+ found: true,
1256
+ value: raw[key]
1257
+ } : { found: false };
1258
+ };
1259
+ const keepRawColumnValue = (row, key, value) => {
1260
+ let raw = row[RAW_COLUMN_VALUES];
1261
+ if (!raw) {
1262
+ raw = Object.create(null);
1263
+ Object.defineProperty(row, RAW_COLUMN_VALUES, {
1264
+ value: raw,
1265
+ enumerable: false,
1266
+ configurable: true
1267
+ });
1268
+ }
1269
+ if (!(key in raw)) raw[key] = value;
1270
+ };
1238
1271
  const remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap) => {
1239
1272
  const columns = getColumns(table);
1240
1273
  for (const [key, value] of Object.entries(queryOutput)) {
@@ -1252,7 +1285,9 @@ const remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap)
1252
1285
  delete queryOutput[key];
1253
1286
  continue;
1254
1287
  }
1255
- queryOutput[key] = remapToGraphQLCore(key, value, tableName, column, relationMap);
1288
+ const remapped = remapToGraphQLCore(key, value, tableName, column, relationMap);
1289
+ if (column && remapped !== value) keepRawColumnValue(queryOutput, key, value);
1290
+ queryOutput[key] = remapped;
1256
1291
  }
1257
1292
  return queryOutput;
1258
1293
  };
@@ -1831,17 +1866,104 @@ const extractRelationJoinColumnPairs = (relEntry, parentTable, targetTable) => {
1831
1866
  }
1832
1867
  return pairs;
1833
1868
  };
1869
+ /** A decimal written as text, in a form `canonicalDecimal` can normalise. */
1870
+ const DECIMAL_TEXT = /^([+-]?)(\d*)(?:\.(\d*))?$/;
1871
+ /**
1872
+ * Decimal text without the zeros that do not change its value: `1.500` and `01.5` are both
1873
+ * `1.5`, as SQL compares them. Anything else (an exponent, `NaN`) is returned unchanged.
1874
+ */
1875
+ const canonicalDecimal = (value) => {
1876
+ const match = DECIMAL_TEXT.exec(value);
1877
+ if (!match || !match[2] && !match[3]) return value;
1878
+ const whole = match[2].replace(/^0+(?=\d)/, "") || "0";
1879
+ const fraction = (match[3] ?? "").replace(/0+$/, "");
1880
+ const digits = fraction ? `${whole}.${fraction}` : whole;
1881
+ return match[1] === "-" && digits !== "0" ? `-${digits}` : digits;
1882
+ };
1883
+ /** An ISO instant without trailing zeros in its fraction: `…:00.100Z` and `…:00.1Z` agree. */
1884
+ const trimIsoFraction = (iso) => iso.replace(/(\.\d*?)0+Z$/, "$1Z").replace(/\.Z$/, "Z");
1885
+ /**
1886
+ * One join value in a form both sides of a relation produce for the same SQL value. The parent's
1887
+ * key and a related row's can arrive in different JS forms — a `Date` and the text of the same
1888
+ * instant, `1.50` and `1.500` from decimals of different scale — and `String()` would tell them
1889
+ * apart (it even drops a `Date`'s milliseconds).
1890
+ */
1891
+ const joinValueKey = (value, column) => {
1892
+ if (value instanceof Date) {
1893
+ if (Number.isNaN(value.getTime())) return String(value);
1894
+ if (column && isDateOnlyColumn(column)) return dateToCalendarDate(value) ?? value.toISOString();
1895
+ return trimIsoFraction(value.toISOString());
1896
+ }
1897
+ if (typeof value === "bigint") return value.toString();
1898
+ if (value instanceof Uint8Array) return Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString("hex");
1899
+ if (typeof value === "string" && column) {
1900
+ if (isMySqlDateTimeStringColumn(column)) return trimIsoFraction(mySqlDateTimeTextToIso(value));
1901
+ if (isPgTimestampStringColumn(column)) return trimIsoFraction(pgTimestampTextToIso(value));
1902
+ if ((column.dataType ?? "") === "string numeric") return canonicalDecimal(value);
1903
+ }
1904
+ return String(value);
1905
+ };
1834
1906
  /**
1835
1907
  * A string identifying one combination of join-column values, for grouping a batch's rows by
1836
- * parent. Values are compared by their string form, as a single-column key always has been.
1908
+ * parent. `columns` are the columns the values were read from, in the same order: the parent's
1909
+ * join columns for a parent key, the target's (or the junction's) for a related row. Values the
1910
+ * database compares equal give the same key — see {@link joinValueKey}.
1911
+ */
1912
+ const joinKeyOf = (values, columns) => values.length === 1 ? joinValueKey(values[0], columns?.[0]) : JSON.stringify(values.map((value, i) => joinValueKey(value, columns?.[i])));
1913
+ /**
1914
+ * The parent row's values for the relation's join columns, as the database holds them. The row
1915
+ * a relation field resolves under has been remapped to its GraphQL form — a `Date` is an ISO
1916
+ * string by then, a `bigint` a decimal string — which can neither be bound back through every
1917
+ * column's encoder (SQLite's integer timestamp calls `getTime()` on it) nor be compared with the
1918
+ * related rows' decoded values. The values from before remapping are used when the row kept
1919
+ * them; otherwise the output form is mapped back the way an input value would be.
1920
+ */
1921
+ const parentJoinValues = (parent, pairs) => pairs.map(({ localColPropName, localCol }) => {
1922
+ const raw = rawColumnValue(parent, localColPropName);
1923
+ if (raw.found) return raw.value;
1924
+ const value = parent[localColPropName];
1925
+ if (value == null) return value;
1926
+ try {
1927
+ return remapFromGraphQLCore(value, localCol, localColPropName);
1928
+ } catch {
1929
+ return value;
1930
+ }
1931
+ });
1932
+ /**
1933
+ * At most this many bound parameters for the parent keys of one batched relation query. SQLite
1934
+ * accepts 32,766 per statement, Postgres and MySQL 65,535; the rest is left to the request's own
1935
+ * filters. A batch with more parents runs as several queries.
1936
+ */
1937
+ const MAX_PARENT_KEY_PARAMS = 1e4;
1938
+ /**
1939
+ * The parent keys of one batch split into the groups each query reads, so that no query binds
1940
+ * more than {@link MAX_PARENT_KEY_PARAMS} key values. Every parent lands in exactly one group, so
1941
+ * a per-parent result — its rows, its window, its count — is the same whichever query made it.
1837
1942
  */
1838
- const joinKeyOf = (values) => values.length === 1 ? String(values[0]) : JSON.stringify(values.map((value) => String(value)));
1943
+ const parentKeyChunks = (keys, width) => {
1944
+ const size = Math.max(1, Math.floor(MAX_PARENT_KEY_PARAMS / Math.max(1, width)));
1945
+ const chunks = [];
1946
+ for (let i = 0; i < keys.length; i += size) chunks.push(keys.slice(i, i + size));
1947
+ return chunks;
1948
+ };
1949
+ /**
1950
+ * `or` over `conditions` as a balanced tree. A flat `a OR b OR c …` is parsed left-deep, and
1951
+ * SQLite refuses an expression more than 1,000 levels deep; balanced, a batch of thousands of
1952
+ * parents stays a few levels deep.
1953
+ */
1954
+ const balancedOr = (conditions) => {
1955
+ if (conditions.length <= 2) return or(...conditions);
1956
+ const middle = Math.ceil(conditions.length / 2);
1957
+ return or(balancedOr(conditions.slice(0, middle)), balancedOr(conditions.slice(middle)));
1958
+ };
1839
1959
  /**
1840
1960
  * The rows whose `cols` hold one of `keys`: `col IN (…)` for a single join column, and an `OR`
1841
1961
  * of per-parent equalities for several — `(a = 1 AND b = 1) OR (a = 1 AND b = 2)` — which
1842
- * every dialect accepts, unlike a row-value `IN` list.
1962
+ * every dialect accepts, unlike a row-value `IN` list. The `OR` is nested as a balanced tree.
1963
+ * Callers split a large batch with {@link parentKeyChunks} first, which keeps either form within
1964
+ * the dialects' parameter limits.
1843
1965
  */
1844
- const parentKeyCondition = (cols, keys) => cols.length === 1 ? inArray(cols[0], keys.map((key) => key[0])) : or(...keys.map((key) => and(...cols.map((col, i) => eq(col, key[i])))));
1966
+ const parentKeyCondition = (cols, keys) => cols.length === 1 ? inArray(cols[0], keys.map((key) => key[0])) : balancedOr(keys.map((key) => and(...cols.map((col, i) => eq(col, key[i])))));
1845
1967
  /**
1846
1968
  * The junction table and its two columns for a `.through()` (many-to-many) relation:
1847
1969
  * `sourceCol` holds the parent's key, `targetCol` the target's. `undefined` for a relation
@@ -4937,7 +5059,7 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
4937
5059
  if (!parentTable || !targetTable) return;
4938
5060
  const joinPairs = extractRelationJoinColumnPairs(relEntry, parentTable, targetTable);
4939
5061
  if (!joinPairs) return;
4940
- const localColPropNames = joinPairs.map((pair) => pair.localColPropName);
5062
+ const localCols = joinPairs.map((pair) => pair.localCol);
4941
5063
  const foreignCol = joinPairs[0].foreignCol;
4942
5064
  const junction = extractJunctionColumns(relEntry);
4943
5065
  const groupCols = junction ? [junction.sourceCol] : joinPairs.map((pair) => pair.foreignCol);
@@ -4983,7 +5105,7 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
4983
5105
  };
4984
5106
  const resolve = async (parent, args, context, info) => {
4985
5107
  if (parent[relationName] !== void 0) return parent[relationName];
4986
- const localValues = localColPropNames.map((name) => parent[name]);
5108
+ const localValues = parentJoinValues(parent, joinPairs);
4987
5109
  if (localValues.some((value) => value == null)) return isOne ? null : [];
4988
5110
  const { where: whereArg, limit: requestedLimit, offset, after, deleted } = args ?? {};
4989
5111
  const { orderByArg, limit, distinct, cursorEntries, cursorValues, columns, loaderKey } = getOrCreateRequestValue(context, info?.fieldNodes?.[0], `relation:${tableName}::${relationName}`, () => {
@@ -5027,55 +5149,60 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
5027
5149
  if (junction === null) throw drizzleError(`Relation '${tableName}.${relationName}' goes through a junction with a composite key, which can only be loaded together with its parent. Leave it to eager loading (eagerLoadRelations).`, { code: "DRIZZLE_UNSUPPORTED_RELATION" });
5028
5150
  return getOrCreateLoader(context, loaderKey, async (parentKeys) => {
5029
5151
  const executor = resolveExecutor(db, context);
5030
- const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key), key])).values()];
5152
+ const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key, localCols), key])).values()];
5031
5153
  const scope = resolveScope(policies, context, filterCtx);
5032
- let whereCondition = withScope(scope, targetTableName, targetTable, and(parentKeyCondition(groupCols, uniqueKeys), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0, cursorValues ? buildCursorCondition(targetTable, cursorEntries, cursorValues, nullOrdering) : void 0), deleted ?? defaultDeleted);
5033
- if (distinct) {
5034
- const keys = await selectDistinctKeys({
5035
- db: executor,
5036
- table: targetTable,
5037
- tableName: targetTableName,
5038
- distinct,
5039
- pkNames: targetPkNames,
5040
- where: whereCondition,
5041
- orderBy: orderByArg,
5042
- partitionBy: groupCols,
5043
- junction: junction ? {
5044
- table: junction.table,
5045
- on: eq(junction.targetCol, foreignCol),
5046
- parentKey: junction.sourceCol
5047
- } : void 0
5048
- });
5049
- if (!keys.length) return parentKeys.map(() => isOne ? null : []);
5050
- whereCondition = and(whereCondition, junction ? or(...keys.map((key) => and(eq(junction.sourceCol, key[DISTINCT_PARENT]), primaryKeyRestriction(targetTable, targetPkNames, [key])))) : primaryKeyRestriction(targetTable, targetPkNames, keys));
5051
- }
5052
- const selection = junction ? {
5053
- ...columns ?? getColumns(targetTable),
5054
- [PARENT_KEY]: junction.sourceCol
5055
- } : columns;
5056
- let rows;
5057
- if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, groupCols, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames, selection, relationFilterCtx(filterCtx, targetTableName, scope), whereArg, junction);
5058
- else {
5059
- let q = withJunction((selection ? executor.select(selection) : executor.select()).from(targetTable), junction, foreignCol).where(whereCondition);
5060
- const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName, scope), whereArg) : [];
5061
- if (orderExprs.length) q = q.orderBy(...orderExprs);
5062
- rows = await q;
5063
- }
5064
- if (cursorEntries) attachRowCursors(rows, cursorEntries);
5065
- const keyOf = (row) => {
5066
- const key = joinKeyOf(groupKeys.map((name) => row[name]));
5067
- if (junction) delete row[PARENT_KEY];
5068
- return key;
5069
- };
5070
- if (isOne) {
5071
- const byKey = new Map(rows.map((row) => [keyOf(row), row]));
5154
+ const loadChunk = async (chunkKeys) => {
5155
+ let whereCondition = withScope(scope, targetTableName, targetTable, and(parentKeyCondition(groupCols, chunkKeys), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0, cursorValues ? buildCursorCondition(targetTable, cursorEntries, cursorValues, nullOrdering) : void 0), deleted ?? defaultDeleted);
5156
+ if (distinct) {
5157
+ const keys = await selectDistinctKeys({
5158
+ db: executor,
5159
+ table: targetTable,
5160
+ tableName: targetTableName,
5161
+ distinct,
5162
+ pkNames: targetPkNames,
5163
+ where: whereCondition,
5164
+ orderBy: orderByArg,
5165
+ partitionBy: groupCols,
5166
+ junction: junction ? {
5167
+ table: junction.table,
5168
+ on: eq(junction.targetCol, foreignCol),
5169
+ parentKey: junction.sourceCol
5170
+ } : void 0
5171
+ });
5172
+ if (!keys.length) return /* @__PURE__ */ new Map();
5173
+ whereCondition = and(whereCondition, junction ? or(...keys.map((key) => and(eq(junction.sourceCol, key[DISTINCT_PARENT]), primaryKeyRestriction(targetTable, targetPkNames, [key])))) : primaryKeyRestriction(targetTable, targetPkNames, keys));
5174
+ }
5175
+ const selection = junction ? {
5176
+ ...columns ?? getColumns(targetTable),
5177
+ [PARENT_KEY]: junction.sourceCol
5178
+ } : columns;
5179
+ let rows;
5180
+ if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, groupCols, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames, selection, relationFilterCtx(filterCtx, targetTableName, scope), whereArg, junction);
5181
+ else {
5182
+ let q = withJunction((selection ? executor.select(selection) : executor.select()).from(targetTable), junction, foreignCol).where(whereCondition);
5183
+ const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName, scope), whereArg) : [];
5184
+ if (orderExprs.length) q = q.orderBy(...orderExprs);
5185
+ rows = await q;
5186
+ }
5187
+ if (cursorEntries) attachRowCursors(rows, cursorEntries);
5188
+ const keyOf = (row) => {
5189
+ const key = joinKeyOf(groupKeys.map((name) => row[name]), groupCols);
5190
+ if (junction) delete row[PARENT_KEY];
5191
+ return key;
5192
+ };
5193
+ if (isOne) {
5194
+ const byKey = new Map(rows.map((row) => [keyOf(row), row]));
5195
+ remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
5196
+ return byKey;
5197
+ }
5198
+ const grouped = new Map(chunkKeys.map((key) => [joinKeyOf(key, localCols), []]));
5199
+ for (const row of rows) grouped.get(keyOf(row))?.push(row);
5072
5200
  remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
5073
- return parentKeys.map((key) => byKey.get(joinKeyOf(key)) ?? null);
5074
- }
5075
- const grouped = new Map(uniqueKeys.map((key) => [joinKeyOf(key), []]));
5076
- for (const row of rows) grouped.get(keyOf(row))?.push(row);
5077
- remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
5078
- return parentKeys.map((key) => grouped.get(joinKeyOf(key)) ?? []);
5201
+ return grouped;
5202
+ };
5203
+ const results = /* @__PURE__ */ new Map();
5204
+ for (const chunk of parentKeyChunks(uniqueKeys, groupCols.length)) for (const [key, value] of await loadChunk(chunk)) results.set(key, value);
5205
+ return parentKeys.map((key) => results.get(joinKeyOf(key, localCols)) ?? (isOne ? null : []));
5079
5206
  }).load(localValues);
5080
5207
  };
5081
5208
  return async (parent, args, context, info) => {
@@ -6161,7 +6288,7 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
6161
6288
  }
6162
6289
  return;
6163
6290
  }
6164
- const localColPropNames = joinPairs.map((pair) => pair.localColPropName);
6291
+ const localCols = joinPairs.map((pair) => pair.localCol);
6165
6292
  const foreignCol = joinPairs[0].foreignCol;
6166
6293
  const junction = extractJunctionColumns(relEntry);
6167
6294
  if (junction === null) return;
@@ -6191,23 +6318,26 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
6191
6318
  };
6192
6319
  });
6193
6320
  if (!Object.keys(request.selection).length) return {};
6194
- const localValues = localColPropNames.map((name) => parent[name]);
6321
+ const localValues = parentJoinValues(parent, joinPairs);
6195
6322
  if (localValues.some((value) => value == null)) return assembleAggregateRow({}, request, target);
6196
6323
  const whereArg = args?.where;
6197
6324
  const deleted = args?.deleted;
6198
6325
  const loader = getOrCreateLoader(context, loaderKey, async (parentKeys) => {
6199
6326
  const executor = resolveExecutor(db, context);
6200
- const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key), key])).values()];
6327
+ const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key, localCols), key])).values()];
6201
6328
  const scope = resolveScope(policies, context, filterCtx);
6202
- const whereCondition = withScope(scope, targetTableName, targetTable, and(parentKeyCondition(groupCols, uniqueKeys), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0), deleted ?? defaultDeleted);
6203
6329
  const groupSelection = Object.fromEntries(groupAliases.map((alias, i) => [alias, groupCols[i]]));
6204
- const selected = executor.select({
6205
- ...groupSelection,
6206
- ...request.selection
6207
- }).from(targetTable);
6208
- const rows = await (junction ? selected.innerJoin(junction.table, eq(junction.targetCol, foreignCol)) : selected).where(whereCondition).groupBy(...groupCols);
6209
- const byKey = new Map(rows.map((row) => [joinKeyOf(groupAliases.map((alias) => row[alias])), row]));
6210
- return parentKeys.map((key) => byKey.get(joinKeyOf(key)) ?? {});
6330
+ const byKey = /* @__PURE__ */ new Map();
6331
+ for (const chunk of parentKeyChunks(uniqueKeys, groupCols.length)) {
6332
+ const whereCondition = withScope(scope, targetTableName, targetTable, and(parentKeyCondition(groupCols, chunk), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0), deleted ?? defaultDeleted);
6333
+ const selected = executor.select({
6334
+ ...groupSelection,
6335
+ ...request.selection
6336
+ }).from(targetTable);
6337
+ const rows = await (junction ? selected.innerJoin(junction.table, eq(junction.targetCol, foreignCol)) : selected).where(whereCondition).groupBy(...groupCols);
6338
+ for (const row of rows) byKey.set(joinKeyOf(groupAliases.map((alias) => row[alias]), groupCols), row);
6339
+ }
6340
+ return parentKeys.map((key) => byKey.get(joinKeyOf(key, localCols)) ?? {});
6211
6341
  });
6212
6342
  return assembleAggregateRow(await loader.load(localValues), request, target);
6213
6343
  } catch (e) {
@@ -8294,7 +8424,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
8294
8424
  serverOwned: Object.keys(contextColumns ?? {}),
8295
8425
  buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName, scope))
8296
8426
  });
8297
- const runBatch = async (executor, { values: rows, plan }) => {
8427
+ const batchQuery = (executor, { values: rows, plan }) => {
8298
8428
  let query = executor.insert(table).values(rows).returning(returning);
8299
8429
  const setWhere = withScope(scope, tableName, table, plan.setWhere);
8300
8430
  query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? {
@@ -8306,12 +8436,22 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
8306
8436
  set: plan.set,
8307
8437
  setWhere
8308
8438
  });
8309
- return await query;
8439
+ return query;
8310
8440
  };
8311
- if (batches.length === 1) return runBatch(target, batches[0]);
8312
- return target.transaction(async (tx) => {
8313
- const rows = [];
8314
- for (const batch of batches) rows.push(...await runBatch(tx, batch));
8441
+ if (batches.length === 1) return await batchQuery(target, batches[0]);
8442
+ return target.transaction((tx) => {
8443
+ const execute = (batch) => {
8444
+ const query = batchQuery(tx, batch);
8445
+ return typeof query.all === "function" ? query.all() : query;
8446
+ };
8447
+ const first = execute(batches[0]);
8448
+ if (typeof first?.then === "function") return (async () => {
8449
+ const rows = [...await first];
8450
+ for (const batch of batches.slice(1)) rows.push(...await execute(batch));
8451
+ return rows;
8452
+ })();
8453
+ const rows = [...first];
8454
+ for (const batch of batches.slice(1)) rows.push(...execute(batch));
8315
8455
  return rows;
8316
8456
  });
8317
8457
  };