kitcn 0.32.2 → 0.33.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/dist/aggregate/index.d.ts +2 -2
  3. package/dist/auth/generated/index.d.ts +1 -1
  4. package/dist/auth/index.d.ts +16 -16
  5. package/dist/auth/index.js +1 -1
  6. package/dist/auth/nextjs/index.d.ts +2 -2
  7. package/dist/auth/start/server/index.d.ts +2 -2
  8. package/dist/{builder-Cyd5nCxZ.js → builder-DDYTlmTB.js} +12 -3
  9. package/dist/caller-factory-C3pcbzo0.d.ts +140 -0
  10. package/dist/{capabilities-BJm_VSDT.d.ts → capabilities-Bxzoofl4.d.ts} +149 -9
  11. package/dist/crpc/index.d.ts +3 -2
  12. package/dist/{generated-contract-disabled-BRm1dNQE.d.ts → generated-contract-disabled-B_EaHGCP.d.ts} +33 -33
  13. package/dist/{http-types-BhhPZtwa.d.ts → http-types-8FBr10yl.d.ts} +3 -79
  14. package/dist/{middleware-BBS7oLtT.d.ts → middleware-D2hKJuSZ.d.ts} +1 -1
  15. package/dist/{middleware-D61Nm-iB.js → middleware-LvZov-hA.js} +1 -1
  16. package/dist/orm/aggregate-index/index.d.ts +1 -1
  17. package/dist/orm/aggregate-index/index.js +226 -96
  18. package/dist/orm/index.d.ts +2 -2
  19. package/dist/orm/index.js +364 -108
  20. package/dist/orm/migrations/index.d.ts +2 -2
  21. package/dist/plugins/index.d.ts +1 -1
  22. package/dist/plugins/index.js +1 -1
  23. package/dist/{procedure-caller-DgMiD0-a.js → procedure-caller-DgGF_hA8.js} +1 -1
  24. package/dist/{procedure-name-DPYyLiVS.d.ts → procedure-name-kn-awLk1.d.ts} +12 -142
  25. package/dist/{query-context-D4z1CnDH.js → query-context-DQLv58LH.js} +34 -1
  26. package/dist/ratelimit/index.d.ts +2 -2
  27. package/dist/ratelimit/index.js +2 -2
  28. package/dist/react/index.d.ts +5 -6
  29. package/dist/react/index.js +24 -69
  30. package/dist/rsc/index.d.ts +4 -3
  31. package/dist/{schema-Bh7AmJwY.js → schema-BF4P0ZjS.js} +322 -1
  32. package/dist/server/index.d.ts +5 -4
  33. package/dist/server/index.js +2 -2
  34. package/dist/solid/index.d.ts +5 -6
  35. package/dist/solid/index.js +20 -66
  36. package/dist/transformer-BEkxlnss.d.ts +78 -0
  37. package/dist/{types-BXpu7xIU.d.ts → types-Cl0utsTL.d.ts} +4 -3
  38. package/dist/{where-clause-compiler-Dgf4lrO-.d.ts → where-clause-compiler-CoPVAVDO.d.ts} +136 -136
  39. package/package.json +1 -1
  40. package/skills/kitcn/SKILL.md +2 -1
  41. package/skills/kitcn/references/features/aggregates.md +13 -0
  42. package/skills/kitcn/references/features/auth-admin.md +1 -0
  43. package/skills/kitcn/references/features/orm.md +24 -4
  44. package/skills/kitcn/references/features/react.md +4 -5
  45. /package/dist/{context-utils-Cbv4r0AA.d.ts → context-utils-ax351iFc.d.ts} +0 -0
  46. /package/dist/{token-tlbNKQS0.d.ts → token-CZpDQUHd.d.ts} +0 -0
  47. /package/dist/{types-Bkeicpo6.d.ts → types-CzGsrLrY.d.ts} +0 -0
@@ -1313,6 +1313,327 @@ const ensureAggregateAllowedForRls = (tableConfig, rlsMode, methodName) => {
1313
1313
  assertAggregateAllowedForRls(tableConfig, rlsMode, AGGREGATE_ERROR.RLS_UNSUPPORTED, methodName);
1314
1314
  };
1315
1315
 
1316
+ //#endregion
1317
+ //#region src/orm/transaction-cache.ts
1318
+ /**
1319
+ * Per-transaction memo storage for the ORM.
1320
+ *
1321
+ * The ORM already has isolate-, execution-, statement- and row-scoped memos.
1322
+ * A hook needs transaction lifetime: the write barrier is built inside
1323
+ * `createOrmDbLifecycle`, which `createOrm` runs at module scope,
1324
+ * so a flag in that closure lives as long as the isolate and would leak an
1325
+ * answer from one transaction into the next.
1326
+ *
1327
+ * Deliberately dependency-free, for the same reason as `write-fanout`:
1328
+ * `aggregate-index/runtime` is contractually unreachable from `orm/index`
1329
+ * (`import-graph.test.ts`), so importing `lifecycle` here to read one symbol
1330
+ * would drag the trigger runtime into the aggregate entry's bundle.
1331
+ * `Symbol.for` is registry-based, so re-declaring the key resolves to the same
1332
+ * symbol `lifecycle` installs.
1333
+ */
1334
+ const ORMLIFECYCLE_INNER_DB = Symbol.for("kitcn:OrmLifecycleInnerDB");
1335
+ const ORM_TRANSACTION_ANCHOR = Symbol.for("kitcn:OrmTransactionAnchor");
1336
+ /**
1337
+ * Pins `anchor`'s transaction onto `target`, for a db built from a writer that
1338
+ * cannot be resolved back to it.
1339
+ *
1340
+ * `withoutTriggers` re-roots the ORM on the raw `ctx.db`, which is the one
1341
+ * object in the chain that carries no inner-db symbol — so a db derived from it
1342
+ * would otherwise stand for a transaction of its own and miss every memo and
1343
+ * queued write filed under the real one.
1344
+ */
1345
+ const markOrmTransactionAnchor = (target, db) => {
1346
+ const anchor = resolveOrmTransactionAnchor(db);
1347
+ if (!anchor || Object.hasOwn(target, ORM_TRANSACTION_ANCHOR)) return target;
1348
+ Object.defineProperty(target, ORM_TRANSACTION_ANCHOR, {
1349
+ configurable: false,
1350
+ enumerable: false,
1351
+ value: anchor,
1352
+ writable: false
1353
+ });
1354
+ return target;
1355
+ };
1356
+ /**
1357
+ * The object whose identity stands in for "this transaction".
1358
+ *
1359
+ * Convex builds `ctx.db` fresh on every UDF invocation, so it can never be
1360
+ * shared by two transactions. `getOrmLifecycleInnerDb` cannot be used on its
1361
+ * own: the lifecycle refuses to wrap readers and returns a no-op wrapper for
1362
+ * schemas with no triggers and no aggregate indexes, so the inner-db symbol is
1363
+ * absent for every query and for most mutations.
1364
+ *
1365
+ * Following the symbol to a fixed point, rather than one hop, is what makes the
1366
+ * answer canonical: `orm.with(hookCtx)` wraps a writer that is already a hook
1367
+ * wrapper, so one hop lands on the intermediate wrapper instead of the raw
1368
+ * writer the outer scope filed its work under. A pinned anchor wins outright,
1369
+ * because it is the only way a db rooted on the raw writer can name the
1370
+ * transaction at all.
1371
+ *
1372
+ * A nested `ctx.runMutation` shares the transaction but gets its own writer
1373
+ * and JS context. It cannot see caller-local memos or queued writes, nor can it
1374
+ * invalidate caller snapshots. Callers must flush deferred writes and expire
1375
+ * row snapshots before handing control to arbitrary user code.
1376
+ */
1377
+ const resolveOrmTransactionAnchor = (db) => {
1378
+ if (typeof db !== "object" || db === null) return;
1379
+ let current = db;
1380
+ const seen = new Set([current]);
1381
+ for (;;) {
1382
+ const pinned = current[ORM_TRANSACTION_ANCHOR];
1383
+ if (typeof pinned === "object" && pinned !== null) return pinned;
1384
+ const inner = current[ORMLIFECYCLE_INNER_DB];
1385
+ if (typeof inner !== "object" || inner === null || seen.has(inner)) return current;
1386
+ seen.add(inner);
1387
+ current = inner;
1388
+ }
1389
+ };
1390
+ /**
1391
+ * One memo namespace with transaction lifetime.
1392
+ *
1393
+ * The store is a `WeakMap` keyed on the anchor rather than a slot on the db,
1394
+ * because `createDatabase` promises not to mutate the `ctx.db` it was handed.
1395
+ * Entries die with the transaction's db object.
1396
+ *
1397
+ * Callers own staleness: only memoize a fact that nothing inside the
1398
+ * transaction can invalidate.
1399
+ */
1400
+ const createOrmTransactionMemo = () => {
1401
+ const byTransaction = /* @__PURE__ */ new WeakMap();
1402
+ return {
1403
+ get(db, key) {
1404
+ const anchor = resolveOrmTransactionAnchor(db);
1405
+ return anchor ? byTransaction.get(anchor)?.get(key) : void 0;
1406
+ },
1407
+ set(db, key, value) {
1408
+ const anchor = resolveOrmTransactionAnchor(db);
1409
+ if (!anchor) return;
1410
+ const existing = byTransaction.get(anchor);
1411
+ if (existing) {
1412
+ existing.set(key, value);
1413
+ return;
1414
+ }
1415
+ byTransaction.set(anchor, new Map([[key, value]]));
1416
+ }
1417
+ };
1418
+ };
1419
+
1420
+ //#endregion
1421
+ //#region src/orm/write-cache.ts
1422
+ const scopes = createOrmTransactionMemo();
1423
+ const activeScopes = /* @__PURE__ */ new Set();
1424
+ let suspended$1 = 0;
1425
+ /** Snapshots may survive ORM-owned work, never a return to arbitrary code. */
1426
+ const runInOrmWriteScope = async (db, fn) => {
1427
+ let scope = scopes.get(db, "write");
1428
+ if (!scope) {
1429
+ scope = {
1430
+ depth: 0,
1431
+ entries: /* @__PURE__ */ new Map()
1432
+ };
1433
+ scopes.set(db, "write", scope);
1434
+ }
1435
+ scope.entries.clear();
1436
+ scope.depth += 1;
1437
+ activeScopes.add(scope);
1438
+ try {
1439
+ return await fn();
1440
+ } finally {
1441
+ scope.entries.clear();
1442
+ scope.depth -= 1;
1443
+ if (scope.depth === 0) activeScopes.delete(scope);
1444
+ }
1445
+ };
1446
+ /**
1447
+ * User code can call a nested UDF whose isolated JS context cannot invalidate
1448
+ * our snapshots. Suspend every active scope, including reentrant statements,
1449
+ * until the callback settles; never restore pre-callback entries.
1450
+ */
1451
+ const withoutOrmWriteCache = async (fn) => {
1452
+ for (const scope of activeScopes) scope.entries.clear();
1453
+ suspended$1 += 1;
1454
+ try {
1455
+ return await fn();
1456
+ } finally {
1457
+ suspended$1 -= 1;
1458
+ }
1459
+ };
1460
+ const createOrmWriteMemo = () => {
1461
+ const namespace = Symbol("orm-write-memo");
1462
+ return {
1463
+ get(db, key) {
1464
+ const scope = scopes.get(db, "write");
1465
+ return !suspended$1 && scope?.depth ? scope.entries.get(namespace)?.get(key) : void 0;
1466
+ },
1467
+ set(db, key, value) {
1468
+ const scope = scopes.get(db, "write");
1469
+ if (suspended$1 || !scope?.depth) return;
1470
+ let entries = scope.entries.get(namespace);
1471
+ if (!entries) {
1472
+ entries = /* @__PURE__ */ new Map();
1473
+ scope.entries.set(namespace, entries);
1474
+ }
1475
+ entries.set(key, value);
1476
+ }
1477
+ };
1478
+ };
1479
+
1480
+ //#endregion
1481
+ //#region src/orm/write-batch.ts
1482
+ /**
1483
+ * Statement-scoped deferral for the ORM write path.
1484
+ *
1485
+ * The lifecycle fires its hooks once per written document, so a subsystem that
1486
+ * maintains derived storage — aggregate index buckets, today — reconciles one
1487
+ * document at a time and rewrites the same storage document once per row. The
1488
+ * fold that would collapse those writes exists; what it lacks is a boundary
1489
+ * wide enough to fold across. A mutation statement is that boundary: one
1490
+ * `update()` / `delete()` / `insert()` is one unit of work whose derived writes
1491
+ * nobody can observe half-applied unless they read them, and a reader can be
1492
+ * made to drain first.
1493
+ *
1494
+ * Depends only on the anchor and row-cache lifetime, for the same reason as
1495
+ * `write-fanout`: the mutation builders are reachable from `orm/index`, the
1496
+ * aggregate runtime contractually is not (`import-graph.test.ts`), and both
1497
+ * ends have to name this module.
1498
+ *
1499
+ * Three invariants make it safe:
1500
+ *
1501
+ * 1. Every queued write is applied by `flushOrmWriteBatch` and nowhere else.
1502
+ * Deltas are commutative but the writes that apply them are absolute
1503
+ * read-modify-writes, so two of them interleaving is a lost update. Callers
1504
+ * that have no batch open still enqueue and flush immediately rather than
1505
+ * writing around the queue, which makes the drain the single writer and
1506
+ * therefore the serialization point — the role `lifecycle`'s write lock
1507
+ * plays for the documents it wraps.
1508
+ * 2. Aggregate readers and user callbacks drain first. Callback execution
1509
+ * suspends deferral because nested UDFs cannot access the caller's queue.
1510
+ * 3. A flush callback must never itself drain. It runs inside the drain, and a
1511
+ * drain in progress is awaited rather than skipped, so re-entering it would
1512
+ * deadlock instead of returning stale rows. Keep the read barrier on the
1513
+ * query-path entry points, which no flush callback calls.
1514
+ */
1515
+ const batches = /* @__PURE__ */ new WeakMap();
1516
+ const activeBatches = /* @__PURE__ */ new Set();
1517
+ let suspended = 0;
1518
+ const getState = (db) => {
1519
+ const anchor = resolveOrmTransactionAnchor(db);
1520
+ if (!anchor) return;
1521
+ const existing = batches.get(anchor);
1522
+ if (existing) return existing;
1523
+ const created = {
1524
+ depth: 0,
1525
+ flushers: /* @__PURE__ */ new Set(),
1526
+ draining: null
1527
+ };
1528
+ batches.set(anchor, created);
1529
+ return created;
1530
+ };
1531
+ /**
1532
+ * Runs every queued flush callback until none is left.
1533
+ *
1534
+ * A callback is removed before it runs, so work enqueued while the drain is in
1535
+ * flight is picked up by this same loop instead of being dropped or applied
1536
+ * twice. `draining` is published before the first callback and cleared in the
1537
+ * same synchronous step as the emptiness check that ends the loop, so a
1538
+ * caller either sees a drain it can wait for or starts its own — never a
1539
+ * window where its work is queued against a drain that has already left.
1540
+ */
1541
+ const drain = (state) => {
1542
+ if (state.draining) return state.draining;
1543
+ let settled = false;
1544
+ const running = (async () => {
1545
+ try {
1546
+ for (;;) {
1547
+ const next = state.flushers.values().next();
1548
+ if (next.done) return;
1549
+ state.flushers.delete(next.value);
1550
+ await next.value();
1551
+ }
1552
+ } finally {
1553
+ state.draining = null;
1554
+ settled = true;
1555
+ }
1556
+ })();
1557
+ if (!settled) state.draining = running;
1558
+ return running;
1559
+ };
1560
+ /**
1561
+ * Opens a write batch for the duration of `fn`.
1562
+ *
1563
+ * Nesting is reference-counted: a cascade that runs another statement inside
1564
+ * this one folds into the same batch rather than flushing the outer statement's
1565
+ * pending work early. The flush runs whether or not `fn` threw, so rows that
1566
+ * landed before a mid-statement throw still get their derived writes.
1567
+ *
1568
+ * When both throw, `fn`'s error wins and the flush failure rides along as its
1569
+ * `cause`. A plain `finally { await drain() }` would report the consequence and
1570
+ * swallow the reason — the statement's own failure is what a caller can act on.
1571
+ */
1572
+ const runInOrmWriteBatch = async (db, fn) => {
1573
+ const state = getState(db);
1574
+ if (!state) return await fn();
1575
+ state.depth += 1;
1576
+ activeBatches.add(state);
1577
+ let failure;
1578
+ let result;
1579
+ try {
1580
+ result = await fn();
1581
+ } catch (error) {
1582
+ failure = { error };
1583
+ }
1584
+ state.depth -= 1;
1585
+ if (state.depth > 0) {
1586
+ if (failure) throw failure.error;
1587
+ return result;
1588
+ }
1589
+ try {
1590
+ await drain(state);
1591
+ } catch (flushError) {
1592
+ if (!failure) throw flushError;
1593
+ if (failure.error instanceof Error && failure.error.cause === void 0) failure.error.cause = flushError;
1594
+ } finally {
1595
+ if (state.depth === 0) activeBatches.delete(state);
1596
+ }
1597
+ if (failure) throw failure.error;
1598
+ return result;
1599
+ };
1600
+ /**
1601
+ * Queues `flush` for the next drain.
1602
+ *
1603
+ * Returns `false` only when `db` has no resolvable transaction, which is the
1604
+ * caller's signal that this module cannot help and the work has to be done
1605
+ * inline.
1606
+ */
1607
+ const enqueueOrmWriteBatch = (db, flush) => {
1608
+ const state = getState(db);
1609
+ if (!state) return false;
1610
+ state.flushers.add(flush);
1611
+ return true;
1612
+ };
1613
+ /** True while a statement scope is holding the queue back. */
1614
+ const isOrmWriteBatchOpen = (db) => suspended === 0 && (getState(db)?.depth ?? 0) > 0;
1615
+ /** Nested UDFs cannot read or drain queues in the caller's JS context. */
1616
+ const runInOrmUserCallback = async (fn) => await withoutOrmWriteCache(async () => {
1617
+ suspended += 1;
1618
+ try {
1619
+ for (const state of activeBatches) await drain(state);
1620
+ return await fn();
1621
+ } finally {
1622
+ suspended -= 1;
1623
+ }
1624
+ });
1625
+ /**
1626
+ * Applies everything queued. Both the read barrier and the only writer.
1627
+ *
1628
+ * Callers must be outside any flush callback; see this module's header.
1629
+ */
1630
+ const flushOrmWriteBatch = async (db) => {
1631
+ const anchor = resolveOrmTransactionAnchor(db);
1632
+ const state = anchor ? batches.get(anchor) : void 0;
1633
+ if (!state || !state.draining && state.flushers.size === 0) return;
1634
+ await drain(state);
1635
+ };
1636
+
1316
1637
  //#endregion
1317
1638
  //#region src/orm/aggregate-index/definitions.ts
1318
1639
  const getAggregateIndexDefinitions = (tableConfig) => {
@@ -1426,4 +1747,4 @@ function aggregateExtension() {
1426
1747
  }
1427
1748
 
1428
1749
  //#endregion
1429
- export { patchReferencingRows as $, enforcePolymorphicWrite as A, getForeignKeys as B, collectPrimaryIdLookupRows as C, encodeUndefinedDeep as D, deserializeFilterExpression as E, evaluateCheckConstraintTriState as F, getTableColumns as G, getMutationCollectionLimits as H, evaluateFilter as I, getUniqueIndexes as J, getTableDeleteConfig as K, extractPrimaryIdLookup as L, ensureDefaultColumns as M, ensureNonNullValues as N, enforceCheckConstraints as O, ensureNullableColumns as P, normalizeTemporalComparableValue as Q, getChecks as R, collectMutationRowsBounded as S, decodeUndefinedDeep as T, getMutationExecutionMode as U, getMutationAsyncDelayMs as V, getOrmContext as W, hydrateDateFieldsForRead as X, hardDeleteRow as Y, normalizeDateFieldsForWrite as Z, applyDefaults as _, AGGREGATE_STATE_TABLE as a, splitReturningSelection as at, buildForeignKeyGraph as b, getAggregateIndexDefinitions as c, toConvexFilter as ct, COUNT_ERROR as d, markLifecycleHookedTables as dt, resolveOrmRuntimeDefaults as et, createAggregateError as f, compileConvexFilter as ft, ensureCountAllowedForRls as g, ensureAggregateAllowedForRls as h, mapWithConcurrency as ht, AGGREGATE_RANK_TREE_TABLE as i, softDeleteRow as it, enforceUniqueIndexes as j, enforceForeignKeys as k, getRankIndexDefinitions as l, unsetFieldsOf as lt, createError as m, isConvexEnforceableFilter as mt, AGGREGATE_EXTREMA_TABLE as n, selectReturningRowWithHydration as nt, aggregateExtension as o, stripUnsetFields as ot, createCountError as p, convexAnd as pt, getTableName as q, AGGREGATE_MEMBER_TABLE as r, serializeFilterExpression as rt, rankAggregateName as s, takeRowsWithinByteBudget as st, AGGREGATE_BUCKET_TABLE as t, returningSelectionReadsCreationTime as tt, AGGREGATE_ERROR as u, hasLifecycleHooks as ut, applyIncomingForeignKeyActionsOnDelete as v, createForeignKeyProbeMemo as w, canUsePrimaryIdLookupCursor as x, applyIncomingForeignKeyActionsOnUpdate as y, getColumnName as z };
1750
+ export { getOrmContext as $, canUsePrimaryIdLookupCursor as A, enforceUniqueIndexes as B, createError as C, isConvexEnforceableFilter as Ct, applyIncomingForeignKeyActionsOnDelete as D, applyDefaults as E, deserializeFilterExpression as F, evaluateFilter as G, ensureNonNullValues as H, encodeUndefinedDeep as I, getColumnName as J, extractPrimaryIdLookup as K, enforceCheckConstraints as L, collectPrimaryIdLookupRows as M, createForeignKeyProbeMemo as N, applyIncomingForeignKeyActionsOnUpdate as O, decodeUndefinedDeep as P, getMutationExecutionMode as Q, enforceForeignKeys as R, createCountError as S, convexAnd as St, ensureCountAllowedForRls as T, ensureNullableColumns as U, ensureDefaultColumns as V, evaluateCheckConstraintTriState as W, getMutationAsyncDelayMs as X, getForeignKeys as Y, getMutationCollectionLimits as Z, createOrmTransactionMemo as _, toConvexFilter as _t, AGGREGATE_STATE_TABLE as a, hydrateDateFieldsForRead as at, COUNT_ERROR as b, markLifecycleHookedTables as bt, getAggregateIndexDefinitions as c, patchReferencingRows as ct, flushOrmWriteBatch as d, selectReturningRowWithHydration as dt, getTableColumns as et, isOrmWriteBatchOpen as f, serializeFilterExpression as ft, runInOrmWriteScope as g, takeRowsWithinByteBudget as gt, createOrmWriteMemo as h, stripUnsetFields as ht, AGGREGATE_RANK_TREE_TABLE as i, hardDeleteRow as it, collectMutationRowsBounded as j, buildForeignKeyGraph as k, getRankIndexDefinitions as l, resolveOrmRuntimeDefaults as lt, runInOrmWriteBatch as m, splitReturningSelection as mt, AGGREGATE_EXTREMA_TABLE as n, getTableName as nt, aggregateExtension as o, normalizeDateFieldsForWrite as ot, runInOrmUserCallback as p, softDeleteRow as pt, getChecks as q, AGGREGATE_MEMBER_TABLE as r, getUniqueIndexes as rt, rankAggregateName as s, normalizeTemporalComparableValue as st, AGGREGATE_BUCKET_TABLE as t, getTableDeleteConfig as tt, enqueueOrmWriteBatch as u, returningSelectionReadsCreationTime as ut, markOrmTransactionAnchor as v, unsetFieldsOf as vt, ensureAggregateAllowedForRls as w, mapWithConcurrency as wt, createAggregateError as x, compileConvexFilter as xt, AGGREGATE_ERROR as y, hasLifecycleHooks as yt, enforcePolymorphicWrite as z };
@@ -1,5 +1,6 @@
1
- import { $ as ActionProcedureBuilder, A as CRPCError, B as RuntimeEnv, C as createProcedureHandlerFactory, Ct as zCustomQuery, D as WithHttpRouter, Dt as zodToConvex, E as typedProcedureResolver, Et as zodOutputToConvexFields, F as getCRPCErrorFromUnknown, G as createLazyCaller, H as ConvexContext, I as getHTTPStatusCodeFromError, J as ServerCaller, K as CallerMeta, L as isCRPCError, M as CRPCErrorData, N as CRPC_ERROR_CODES_BY_KEY, O as inferApiInputs, Ot as zodToConvexFields, P as CRPC_ERROR_CODE_TO_HTTP, Q as getGeneratedValue, R as toCRPCError, S as createProcedureCallerFactory, St as zCustomMutation, T as getGeneratedFunctionReference, Tt as zodOutputToConvex, U as createCallerFactory, V as createEnv, W as LazyCaller, X as createApiLeaf, Y as createServerCaller, Z as createGeneratedFunctionReference, _ as ProcedureSchedulableCallerFromRegistry, _t as ZodValidatorFromConvex, a as CreateProcedureCallerFactoryOptions, at as initCRPC, b as createGenericCallerFactory, bt as withSystemFields, c as GeneratedRegistryCallerFactory, ct as extractPathParams, d as GeneratedRegistryHandlerForContext, dt as ConvexValidatorFromZod, et as CRPCFunctionTypeHint, f as ProcedureActionCallerFromRegistry, ft as ConvexValidatorFromZodOutput, g as ProcedureFromFunctionReference, gt as ZodFromValidatorBase, h as ProcedureDefinition, ht as Zid, i as registerProcedureNameLookup, it as createMiddlewareFactory, j as CRPCErrorCode, k as inferApiOutputs, l as GeneratedRegistryCallerForContext, lt as handleHttpError, m as ProcedureCallerFromRegistry, mt as ZCustomCtx, n as ProcedureNameLookup, nt as ProcedureBuilder, o as GeneratedProcedureRegistry, ot as HttpProcedureBuilder, p as ProcedureCaller, pt as CustomBuilder, q as CallerOpts, r as inferProcedureNameFromCallsite, rt as QueryProcedureBuilder, s as GeneratedProcedureRegistryEntry, st as createHttpProcedureBuilder, t as ProcedureNameEntry, tt as MutationProcedureBuilder, u as GeneratedRegistryHandlerFactory, ut as matchPathParams, v as ProcedureScheduleCallerFromRegistry, vt as convexToZod, w as defineProcedure, wt as zid, x as createGenericHandlerFactory, xt as zCustomAction, y as createGeneratedRegistryRuntime, yt as convexToZodFields, z as CreateEnvOptions } from "../procedure-name-DPYyLiVS.js";
2
- import { A as HttpProcedureBuilderDef, C as extractRouteMap, D as HttpHandlerOpts, E as HttpActionHandler, M as InferHttpInput, N as ProcedureMeta, O as HttpMethod, S as createHttpRouterFactory, T as HttpActionConstructor, _ as CRPCHttpRouter, b as HttpRouterWithHono, j as HttpRouteDefinition, k as HttpProcedure, v as HttpRouterDef, w as CRPCHonoHandler, x as createHttpRouter, y as HttpRouterRecord } from "../http-types-BhhPZtwa.js";
3
- import { a as MergeZodObjects, c as MiddlewareMarker, d as MiddlewareProcedureType, f as MiddlewareResult, g as UnsetMarker, h as Simplify, i as IntersectIfDefined, l as MiddlewareNext, m as ResolveIfSet, n as AnyMiddlewareBuilder, o as MiddlewareBuilder, p as Overwrite, r as GetRawInputFn, s as MiddlewareFunction, t as AnyMiddleware, u as MiddlewareProcedureInfo } from "../types-Bkeicpo6.js";
4
- import { a as isMutationCtx, c as isSchedulerCtx, d as requireQueryCtx, f as requireRunMutationCtx, i as isActionCtx, l as requireActionCtx, n as RunMutationCtx, o as isQueryCtx, p as requireSchedulerCtx, r as SchedulerCtx, s as isRunMutationCtx, t as GenericCtx, u as requireMutationCtx } from "../context-utils-Cbv4r0AA.js";
1
+ import { a as CallerMeta, c as createServerCaller, i as createLazyCaller, n as createCallerFactory, o as CallerOpts, r as LazyCaller, s as ServerCaller, t as ConvexContext } from "../caller-factory-C3pcbzo0.js";
2
+ import { $ as createHttpProcedureBuilder, A as CRPCError, B as RuntimeEnv, C as createProcedureHandlerFactory, D as WithHttpRouter, E as typedProcedureResolver, F as getCRPCErrorFromUnknown, G as ActionProcedureBuilder, H as createApiLeaf, I as getHTTPStatusCodeFromError, J as ProcedureBuilder, K as CRPCFunctionTypeHint, L as isCRPCError, M as CRPCErrorData, N as CRPC_ERROR_CODES_BY_KEY, O as inferApiInputs, P as CRPC_ERROR_CODE_TO_HTTP, Q as HttpProcedureBuilder, R as toCRPCError, S as createProcedureCallerFactory, T as getGeneratedFunctionReference, U as createGeneratedFunctionReference, V as createEnv, W as getGeneratedValue, X as createMiddlewareFactory, Y as QueryProcedureBuilder, Z as initCRPC, _ as ProcedureSchedulableCallerFromRegistry, _t as zodOutputToConvex, a as CreateProcedureCallerFactoryOptions, at as CustomBuilder, b as createGenericCallerFactory, bt as zodToConvexFields, c as GeneratedRegistryCallerFactory, ct as ZodFromValidatorBase, d as GeneratedRegistryHandlerForContext, dt as convexToZodFields, et as extractPathParams, f as ProcedureActionCallerFromRegistry, ft as withSystemFields, g as ProcedureFromFunctionReference, gt as zid, h as ProcedureDefinition, ht as zCustomQuery, i as registerProcedureNameLookup, it as ConvexValidatorFromZodOutput, j as CRPCErrorCode, k as inferApiOutputs, l as GeneratedRegistryCallerForContext, lt as ZodValidatorFromConvex, m as ProcedureCallerFromRegistry, mt as zCustomMutation, n as ProcedureNameLookup, nt as matchPathParams, o as GeneratedProcedureRegistry, ot as ZCustomCtx, p as ProcedureCaller, pt as zCustomAction, q as MutationProcedureBuilder, r as inferProcedureNameFromCallsite, rt as ConvexValidatorFromZod, s as GeneratedProcedureRegistryEntry, st as Zid, t as ProcedureNameEntry, tt as handleHttpError, u as GeneratedRegistryHandlerFactory, ut as convexToZod, v as ProcedureScheduleCallerFromRegistry, vt as zodOutputToConvexFields, w as defineProcedure, x as createGenericHandlerFactory, y as createGeneratedRegistryRuntime, yt as zodToConvex, z as CreateEnvOptions } from "../procedure-name-kn-awLk1.js";
3
+ import { a as MergeZodObjects, c as MiddlewareMarker, d as MiddlewareProcedureType, f as MiddlewareResult, g as UnsetMarker, h as Simplify, i as IntersectIfDefined, l as MiddlewareNext, m as ResolveIfSet, n as AnyMiddlewareBuilder, o as MiddlewareBuilder, p as Overwrite, r as GetRawInputFn, s as MiddlewareFunction, t as AnyMiddleware, u as MiddlewareProcedureInfo } from "../types-CzGsrLrY.js";
4
+ import { A as HttpProcedureBuilderDef, C as extractRouteMap, D as HttpHandlerOpts, E as HttpActionHandler, M as InferHttpInput, N as ProcedureMeta, O as HttpMethod, S as createHttpRouterFactory, T as HttpActionConstructor, _ as CRPCHttpRouter, b as HttpRouterWithHono, j as HttpRouteDefinition, k as HttpProcedure, v as HttpRouterDef, w as CRPCHonoHandler, x as createHttpRouter, y as HttpRouterRecord } from "../http-types-8FBr10yl.js";
5
+ import { a as isMutationCtx, c as isSchedulerCtx, d as requireQueryCtx, f as requireRunMutationCtx, i as isActionCtx, l as requireActionCtx, n as RunMutationCtx, o as isQueryCtx, p as requireSchedulerCtx, r as SchedulerCtx, s as isRunMutationCtx, t as GenericCtx, u as requireMutationCtx } from "../context-utils-ax351iFc.js";
5
6
  export { ActionProcedureBuilder, AnyMiddleware, AnyMiddlewareBuilder, CRPCError, CRPCErrorCode, CRPCErrorData, CRPCFunctionTypeHint, CRPCHonoHandler, CRPCHttpRouter, CRPC_ERROR_CODES_BY_KEY, CRPC_ERROR_CODE_TO_HTTP, CallerMeta, CallerOpts, ConvexContext, ConvexValidatorFromZod, ConvexValidatorFromZodOutput, CreateEnvOptions, CreateProcedureCallerFactoryOptions, CustomBuilder, GeneratedProcedureRegistry, GeneratedProcedureRegistryEntry, GeneratedRegistryCallerFactory, GeneratedRegistryCallerForContext, GeneratedRegistryHandlerFactory, GeneratedRegistryHandlerForContext, GenericCtx, GetRawInputFn, HttpActionConstructor, HttpActionHandler, HttpHandlerOpts, HttpMethod, HttpProcedure, HttpProcedureBuilder, HttpProcedureBuilderDef, HttpRouteDefinition, HttpRouterDef, HttpRouterRecord, HttpRouterWithHono, InferHttpInput, IntersectIfDefined, LazyCaller, MergeZodObjects, MiddlewareBuilder, MiddlewareFunction, MiddlewareMarker, MiddlewareNext, MiddlewareProcedureInfo, MiddlewareProcedureType, MiddlewareResult, MutationProcedureBuilder, Overwrite, ProcedureActionCallerFromRegistry, ProcedureBuilder, ProcedureCaller, ProcedureCallerFromRegistry, ProcedureDefinition, ProcedureFromFunctionReference, ProcedureMeta, ProcedureNameEntry, ProcedureNameLookup, ProcedureSchedulableCallerFromRegistry, ProcedureScheduleCallerFromRegistry, QueryProcedureBuilder, ResolveIfSet, RunMutationCtx, RuntimeEnv, SchedulerCtx, ServerCaller, Simplify, UnsetMarker, WithHttpRouter, ZCustomCtx, Zid, ZodFromValidatorBase, ZodValidatorFromConvex, convexToZod, convexToZodFields, createApiLeaf, createCallerFactory, createEnv, createGeneratedFunctionReference, createGeneratedRegistryRuntime, createGenericCallerFactory, createGenericHandlerFactory, createHttpProcedureBuilder, createHttpRouter, createHttpRouterFactory, createLazyCaller, createMiddlewareFactory, createProcedureCallerFactory, createProcedureHandlerFactory, createServerCaller, defineProcedure, extractPathParams, extractRouteMap, getCRPCErrorFromUnknown, getGeneratedFunctionReference, getGeneratedValue, getHTTPStatusCodeFromError, handleHttpError, inferApiInputs, inferApiOutputs, inferProcedureNameFromCallsite, initCRPC, isActionCtx, isCRPCError, isMutationCtx, isQueryCtx, isRunMutationCtx, isSchedulerCtx, matchPathParams, registerProcedureNameLookup, requireActionCtx, requireMutationCtx, requireQueryCtx, requireRunMutationCtx, requireSchedulerCtx, toCRPCError, typedProcedureResolver, withSystemFields, zCustomAction, zCustomMutation, zCustomQuery, zid, zodOutputToConvex, zodOutputToConvexFields, zodToConvex, zodToConvexFields };
@@ -1,6 +1,6 @@
1
1
  import { a as isMutationCtx, c as isSchedulerCtx, d as requireQueryCtx, f as requireRunMutationCtx, i as isActionCtx, l as requireActionCtx, n as createGeneratedFunctionReference, o as isQueryCtx, p as requireSchedulerCtx, r as getGeneratedValue, s as isRunMutationCtx, t as createApiLeaf, u as requireMutationCtx } from "../api-entry-Buvjl9hn.js";
2
2
  import { n as createLazyCaller, r as createServerCaller, t as createCallerFactory } from "../caller-factory-C-N540BC.js";
3
- import { A as zid, C as toCRPCError, D as zCustomAction, E as withSystemFields, M as zodOutputToConvexFields, N as zodToConvex, O as zCustomMutation, P as zodToConvexFields, S as isCRPCError, T as convexToZodFields, _ as CRPCError, a as createMiddlewareFactory, b as getCRPCErrorFromUnknown, c as registerProcedureNameLookup, d as createHttpRouterFactory, f as extractRouteMap, g as matchPathParams, h as handleHttpError, i as QueryProcedureBuilder, j as zodOutputToConvex, k as zCustomQuery, l as HttpRouterWithHono, m as extractPathParams, n as MutationProcedureBuilder, o as initCRPC, p as createHttpProcedureBuilder, r as ProcedureBuilder, s as inferProcedureNameFromCallsite, t as ActionProcedureBuilder, u as createHttpRouter, v as CRPC_ERROR_CODES_BY_KEY, w as convexToZod, x as getHTTPStatusCodeFromError, y as CRPC_ERROR_CODE_TO_HTTP } from "../builder-Cyd5nCxZ.js";
4
- import { a as createProcedureHandlerFactory, c as typedProcedureResolver, i as createProcedureCallerFactory, l as createEnv, n as createGenericCallerFactory, o as defineProcedure, r as createGenericHandlerFactory, s as getGeneratedFunctionReference, t as createGeneratedRegistryRuntime } from "../procedure-caller-DgMiD0-a.js";
3
+ import { A as zid, C as toCRPCError, D as zCustomAction, E as withSystemFields, M as zodOutputToConvexFields, N as zodToConvex, O as zCustomMutation, P as zodToConvexFields, S as isCRPCError, T as convexToZodFields, _ as CRPCError, a as createMiddlewareFactory, b as getCRPCErrorFromUnknown, c as registerProcedureNameLookup, d as createHttpRouterFactory, f as extractRouteMap, g as matchPathParams, h as handleHttpError, i as QueryProcedureBuilder, j as zodOutputToConvex, k as zCustomQuery, l as HttpRouterWithHono, m as extractPathParams, n as MutationProcedureBuilder, o as initCRPC, p as createHttpProcedureBuilder, r as ProcedureBuilder, s as inferProcedureNameFromCallsite, t as ActionProcedureBuilder, u as createHttpRouter, v as CRPC_ERROR_CODES_BY_KEY, w as convexToZod, x as getHTTPStatusCodeFromError, y as CRPC_ERROR_CODE_TO_HTTP } from "../builder-DDYTlmTB.js";
4
+ import { a as createProcedureHandlerFactory, c as typedProcedureResolver, i as createProcedureCallerFactory, l as createEnv, n as createGenericCallerFactory, o as defineProcedure, r as createGenericHandlerFactory, s as getGeneratedFunctionReference, t as createGeneratedRegistryRuntime } from "../procedure-caller-DgGF_hA8.js";
5
5
 
6
6
  export { ActionProcedureBuilder, CRPCError, CRPC_ERROR_CODES_BY_KEY, CRPC_ERROR_CODE_TO_HTTP, HttpRouterWithHono, MutationProcedureBuilder, ProcedureBuilder, QueryProcedureBuilder, convexToZod, convexToZodFields, createApiLeaf, createCallerFactory, createEnv, createGeneratedFunctionReference, createGeneratedRegistryRuntime, createGenericCallerFactory, createGenericHandlerFactory, createHttpProcedureBuilder, createHttpRouter, createHttpRouterFactory, createLazyCaller, createMiddlewareFactory, createProcedureCallerFactory, createProcedureHandlerFactory, createServerCaller, defineProcedure, extractPathParams, extractRouteMap, getCRPCErrorFromUnknown, getGeneratedFunctionReference, getGeneratedValue, getHTTPStatusCodeFromError, handleHttpError, inferProcedureNameFromCallsite, initCRPC, isActionCtx, isCRPCError, isMutationCtx, isQueryCtx, isRunMutationCtx, isSchedulerCtx, matchPathParams, registerProcedureNameLookup, requireActionCtx, requireMutationCtx, requireQueryCtx, requireRunMutationCtx, requireSchedulerCtx, toCRPCError, typedProcedureResolver, withSystemFields, zCustomAction, zCustomMutation, zCustomQuery, zid, zodOutputToConvex, zodOutputToConvexFields, zodToConvex, zodToConvexFields };
@@ -60,8 +60,8 @@ type ConvexQueryHookOptions = {
60
60
  /** Skip query silently when unauthenticated (default: false, calls onQueryUnauthorized) */skipUnauth?: boolean; /** Set to false to fetch once without subscribing (default: true) */
61
61
  subscribe?: boolean;
62
62
  };
63
- /** Extract input args without cursor/limit (user's filter args only) */
64
- type InfiniteQueryInput<TInput> = Omit<TInput, 'cursor' | 'limit'>;
63
+ /** Extract user args without hook-owned pagination transport fields */
64
+ type InfiniteQueryInput<TInput> = Omit<TInput, 'cursor' | 'endCursor' | 'limit'>;
65
65
  /** Extract item type from PaginationResult<T> */
66
66
  type ExtractPaginatedItem<TOutput> = TOutput extends {
67
67
  page: (infer T)[];
@@ -1429,7 +1429,7 @@ declare const getConvexQueryClientSingleton: ({
1429
1429
  * Pagination state persisted in queryClient.
1430
1430
  * Enables scroll restoration when navigating back to a paginated list.
1431
1431
  *
1432
- * Uses flat { cursor, limit } structure like tRPC.
1432
+ * Uses flat { cursor, endCursor, limit } structure like tRPC.
1433
1433
  */
1434
1434
  type PaginationState = {
1435
1435
  id: number;
@@ -1438,13 +1438,12 @@ type PaginationState = {
1438
1438
  queries: Record<number, {
1439
1439
  /** Flat pagination args - tRPC style */args: Record<string, unknown> & {
1440
1440
  cursor: string | null;
1441
+ endCursor?: string | null;
1441
1442
  limit?: number; /** Internal pagination ID for subscription management */
1442
1443
  __paginationId?: number;
1443
1444
  };
1444
- endCursor?: string | null;
1445
1445
  }>;
1446
- version: number; /** Recovery key to prevent infinite recovery loops */
1447
- autoRecoveryAttempted?: string;
1446
+ version: number;
1448
1447
  };
1449
1448
  type PaginationStatus = 'CanLoadMore' | 'Exhausted' | 'LoadingFirstPage' | 'LoadingMore';
1450
1449
  /** Return type for infinite query hooks */
@@ -2935,6 +2935,12 @@ const getConvexQueryClientSingleton = ({ authStore, convex, queryClient, symbolK
2935
2935
  //#endregion
2936
2936
  //#region src/internal/pagination.ts
2937
2937
  const shouldSplitPaginationPage = (page, initialNumItems) => Boolean(page.splitCursor) && (page.pageStatus === "SplitRecommended" || page.pageStatus === "SplitRequired" || initialNumItems !== void 0 && page.page.length > initialNumItems * 2);
2938
+ const isInvalidPaginationCursor = (error) => {
2939
+ if (error instanceof Error && error.message.includes("InvalidCursor")) return true;
2940
+ if (!error || typeof error !== "object" || !("data" in error)) return false;
2941
+ const data = error.data;
2942
+ return Boolean(data && typeof data === "object" && "isConvexSystemError" in data && data.isConvexSystemError === true && "paginationError" in data && data.paginationError === "InvalidCursor");
2943
+ };
2938
2944
 
2939
2945
  //#endregion
2940
2946
  //#region src/solid/create-queries-results.ts
@@ -3020,7 +3026,7 @@ const aggregatePages = (results, hasPlaceholderData) => {
3020
3026
  error,
3021
3027
  failureReason: error,
3022
3028
  isError: results.some((r) => r.isError),
3023
- isFetchNextPageError: results.length > 1 && (results.at(-1)?.isError ?? false),
3029
+ isFetchNextPageError: results.slice(1).some((result) => result.isError),
3024
3030
  isFetching,
3025
3031
  isLoading: status === "LoadingFirstPage",
3026
3032
  isPlaceholderData,
@@ -3030,72 +3036,18 @@ const aggregatePages = (results, hasPlaceholderData) => {
3030
3036
  status
3031
3037
  };
3032
3038
  };
3033
- /** Build a unique key for recovery attempt detection */
3034
- const buildRecoveryKey = (pageKeys, page0Cursor, page0UpdatedAt) => JSON.stringify({
3035
- pageKeys,
3036
- page0Cursor,
3037
- page0UpdatedAt
3038
- });
3039
3039
  /**
3040
3040
  * Hook for auto-recovering from stale cursors after WebSocket reconnection.
3041
3041
  *
3042
3042
  * When Convex WebSocket reconnects, page 0 (cursor: null) resubscribes and
3043
3043
  * gets fresh data. However, pages 1+ may have stale cursors that fail.
3044
3044
  *
3045
- * This hook detects this pattern and creates a recovery page that fetches
3046
- * enough items to cover the lost pages, preserving the user's scroll position.
3045
+ * This hook discards the invalid cursor chain and restarts from page one.
3047
3046
  */
3048
- const useStaleCursorRecovery = ({ argsObject, combined, limit, pageResults, setState, state }) => {
3049
- createEffect(on([
3050
- () => combined.isFetchNextPageError,
3051
- pageResults,
3052
- () => state().pageKeys,
3053
- () => state().queries,
3054
- () => state().autoRecoveryAttempted,
3055
- argsObject
3056
- ], () => {
3047
+ const useStaleCursorRecovery = ({ combined, pageResults, resetPagination }) => {
3048
+ createEffect(on([() => combined.isFetchNextPageError, pageResults], () => {
3057
3049
  if (!combined.isFetchNextPageError) return;
3058
- const results = pageResults();
3059
- const page0Result = results[0];
3060
- const page0Data = page0Result?.data;
3061
- const page0UpdatedAt = page0Result?.dataUpdatedAt ?? 0;
3062
- const hasPage0Data = page0Data !== void 0 && !page0Result?.isError;
3063
- const hasSubsequentErrors = results.slice(1).some((q) => q?.isError && !q?.isFetching);
3064
- if (!hasPage0Data || !hasSubsequentErrors || !page0Data?.continueCursor) return;
3065
- const currentState = state();
3066
- const recoveryKey = buildRecoveryKey(currentState.pageKeys, page0Data.continueCursor, page0UpdatedAt);
3067
- if (currentState.autoRecoveryAttempted === recoveryKey) return;
3068
- const erroredPageKeys = currentState.pageKeys.filter((_, i) => i > 0 && results[i]?.isError);
3069
- const itemsToRecover = erroredPageKeys.reduce((sum, key) => {
3070
- return sum + (currentState.queries[key]?.args?.limit ?? limit ?? 20);
3071
- }, 0);
3072
- console.warn("[Pagination] Auto-recovering from stale cursors", {
3073
- erroredPages: erroredPageKeys.length,
3074
- itemsToRecover
3075
- });
3076
- setState((prev) => ({
3077
- ...prev,
3078
- id: prev.id,
3079
- nextPageKey: 2,
3080
- pageKeys: [prev.pageKeys[0], 1],
3081
- queries: {
3082
- [prev.pageKeys[0]]: prev.queries[prev.pageKeys[0]],
3083
- 1: { args: {
3084
- ...argsObject(),
3085
- cursor: page0Data.continueCursor,
3086
- limit: Math.min(itemsToRecover + (limit ?? 20), 500),
3087
- __paginationId: prev.id
3088
- } }
3089
- },
3090
- version: prev.version + 1,
3091
- autoRecoveryAttempted: recoveryKey
3092
- }));
3093
- }));
3094
- createEffect(on([() => combined.status, () => state().autoRecoveryAttempted], () => {
3095
- if ((combined.status === "CanLoadMore" || combined.status === "Exhausted") && state().autoRecoveryAttempted) setState((prev) => ({
3096
- ...prev,
3097
- autoRecoveryAttempted: void 0
3098
- }));
3050
+ if (pageResults().slice(1).some((result) => result?.isError && !result.isFetching && isInvalidPaginationCursor(result.error))) resetPagination();
3099
3051
  }));
3100
3052
  };
3101
3053
  /**
@@ -3232,12 +3184,9 @@ const useInfiniteQueryInternal = (query, args, options) => {
3232
3184
  const [combined, setCombined] = createStore(derive());
3233
3185
  createRenderEffect(() => setCombined(derive()));
3234
3186
  useStaleCursorRecovery({
3235
- argsObject,
3236
3187
  combined,
3237
- limit,
3238
3188
  pageResults,
3239
- setState,
3240
- state
3189
+ resetPagination: () => setState(createInitialState())
3241
3190
  });
3242
3191
  createEffect(on([
3243
3192
  pageResults,
@@ -3252,15 +3201,17 @@ const useInfiniteQueryInternal = (query, args, options) => {
3252
3201
  const page = pageQuery.data;
3253
3202
  const pageKey = state().pageKeys[i];
3254
3203
  const pageState = state().queries[pageKey];
3255
- if (shouldSplitPaginationPage(page, limit) && pageState && !pageState.endCursor) {
3204
+ if (shouldSplitPaginationPage(page, limit) && pageState && pageState.args.endCursor !== page.splitCursor) {
3256
3205
  setState((prev) => {
3257
3206
  const currentPageState = prev.queries[pageKey];
3258
- if (!currentPageState || currentPageState.endCursor) return prev;
3207
+ if (!currentPageState || currentPageState.args.endCursor === page.splitCursor) return prev;
3259
3208
  const newKey = prev.nextPageKey;
3260
3209
  const splitCursor = page.splitCursor;
3210
+ const endCursor = currentPageState.args.endCursor ?? page.continueCursor;
3261
3211
  const splitPageArgs = {
3262
3212
  ...argsObject(),
3263
3213
  cursor: splitCursor,
3214
+ endCursor,
3264
3215
  limit: currentPageState.args.limit,
3265
3216
  __paginationId: prev.id
3266
3217
  };
@@ -3275,7 +3226,10 @@ const useInfiniteQueryInternal = (query, args, options) => {
3275
3226
  ...prev.queries,
3276
3227
  [pageKey]: {
3277
3228
  ...currentPageState,
3278
- endCursor: splitCursor
3229
+ args: {
3230
+ ...currentPageState.args,
3231
+ endCursor: splitCursor
3232
+ }
3279
3233
  },
3280
3234
  [newKey]: { args: splitPageArgs }
3281
3235
  }
@@ -0,0 +1,78 @@
1
+ //#region src/crpc/transformer.d.ts
2
+ /**
3
+ * Generic transformer contract (mirrors tRPC shape).
4
+ */
5
+ interface DataTransformer {
6
+ deserialize(object: any): any;
7
+ serialize(object: any): any;
8
+ }
9
+ /**
10
+ * Separate input/output transformers.
11
+ */
12
+ interface CombinedDataTransformer {
13
+ input: DataTransformer;
14
+ output: DataTransformer;
15
+ }
16
+ /**
17
+ * Transformer config accepted by cRPC.
18
+ */
19
+ type DataTransformerOptions = CombinedDataTransformer | DataTransformer;
20
+ /**
21
+ * Extensible tagged wire codec.
22
+ */
23
+ interface WireCodec {
24
+ decode(value: unknown): unknown;
25
+ encode(value: unknown): unknown;
26
+ isType(value: unknown): boolean;
27
+ /**
28
+ * Declares that `isType` only ever claims a value where
29
+ * `typeof value === 'object' && value !== null` - never a primitive, a
30
+ * function, `null` or `undefined`.
31
+ *
32
+ * Lets `serialize` skip codec dispatch on primitives, which are the majority
33
+ * of visited nodes. It is opt-in because an arbitrary predicate cannot be
34
+ * classified by sampling values: a codec that claims, say, one specific
35
+ * number would be misread as object-only and silently lose its encoding.
36
+ * Codecs that leave it unset keep full dispatch.
37
+ */
38
+ readonly objectsOnly?: boolean;
39
+ readonly tag: `$${string}`;
40
+ }
41
+ /**
42
+ * Date wire tag (Convex-style reserved key).
43
+ */
44
+ declare const DATE_CODEC_TAG = "$date";
45
+ /**
46
+ * Built-in Date codec.
47
+ */
48
+ declare const dateWireCodec: WireCodec;
49
+ /**
50
+ * Build a recursive tagged transformer from codecs.
51
+ */
52
+ declare const createTaggedTransformer: (codecs: readonly WireCodec[]) => DataTransformer;
53
+ /**
54
+ * Default cRPC transformer (Date-enabled).
55
+ */
56
+ declare const defaultCRPCTransformer: DataTransformer;
57
+ /**
58
+ * Normalize transformer config to split input/output shape.
59
+ * User transformers are additive and always composed with default Date handling.
60
+ *
61
+ * Idempotent: passing a transformer this function already resolved returns it
62
+ * unchanged.
63
+ */
64
+ declare const getTransformer: (transformer?: DataTransformerOptions) => CombinedDataTransformer;
65
+ /**
66
+ * Encode request payloads (input direction).
67
+ */
68
+ declare const encodeWire: (value: unknown, transformer?: DataTransformerOptions) => unknown;
69
+ /**
70
+ * Decode response payloads (output direction).
71
+ */
72
+ declare const decodeWire: (value: unknown, transformer?: DataTransformerOptions) => unknown;
73
+ /**
74
+ * Exposed identity transformer for advanced composition.
75
+ */
76
+ declare const identityTransformer: CombinedDataTransformer;
77
+ //#endregion
78
+ export { WireCodec as a, decodeWire as c, getTransformer as d, identityTransformer as f, DataTransformerOptions as i, defaultCRPCTransformer as l, DATE_CODEC_TAG as n, createTaggedTransformer as o, DataTransformer as r, dateWireCodec as s, CombinedDataTransformer as t, encodeWire as u };
@@ -1,4 +1,5 @@
1
- import { P as CombinedDataTransformer, r as HttpClientError } from "./http-types-BhhPZtwa.js";
1
+ import { t as CombinedDataTransformer } from "./transformer-BEkxlnss.js";
2
+ import { r as HttpClientError } from "./http-types-8FBr10yl.js";
2
3
  import { FunctionArgs, FunctionReference, FunctionReturnType } from "convex/server";
3
4
 
4
5
  //#region src/crpc/http-client.d.ts
@@ -148,8 +149,8 @@ type PaginationOpts = {
148
149
  maximumRowsRead?: number;
149
150
  maximumBytesRead?: number;
150
151
  };
151
- /** Extract input args without cursor/limit (user's filter args only) */
152
- type InfiniteQueryInput<TInput> = Omit<TInput, 'cursor' | 'limit'>;
152
+ /** Extract user args without hook-owned pagination transport fields */
153
+ type InfiniteQueryInput<TInput> = Omit<TInput, 'cursor' | 'endCursor' | 'limit'>;
153
154
  /** Extract item type from PaginationResult<T> */
154
155
  type ExtractPaginatedItem<TOutput> = TOutput extends {
155
156
  page: (infer T)[];