kitcn 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # kitcn
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#422](https://github.com/udecode/kitcn/pull/422) [`f677251`](https://github.com/udecode/kitcn/commit/f6772514d2525104ac6878c0eeb7334a84af981b) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
8
+
9
+ - Fix soft cascade delete re-reading every child it had already processed. New scheduled campaigns require and resume through an exact foreign-key index, keeping reads linear without allowing mutable trailing index fields to strand a child. Queued jobs reselect a newly available exact index or drain through the legacy replay path.
10
+
3
11
  ## 0.29.0
4
12
 
5
13
  ### Minor Changes
@@ -1,5 +1,5 @@
1
1
  import { Vn as ConvexTextBuilderInitial, Zt as ConvexTableWithColumns } from "../capabilities-6PZBmvc8.js";
2
- import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-CX5-0M5p.js";
2
+ import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-DvYSTJXe.js";
3
3
  import * as convex_values0 from "convex/values";
4
4
  import { GenericId, Infer, Value } from "convex/values";
5
5
  import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
@@ -1,6 +1,6 @@
1
1
  import { t as DirectAggregate } from "../../runtime-CcOvOf4K.js";
2
2
  import { a as Columns } from "../../table-CX2lnX7e.js";
3
- import { Dt as usesSystemCreatedAtAlias, Et as PUBLIC_CREATED_AT_FIELD, Q as normalizeTemporalComparableValue, Tt as INTERNAL_CREATION_TIME_FIELD, a as AGGREGATE_STATE_TABLE, c as getAggregateIndexDefinitions, d as COUNT_ERROR, ht as mapWithConcurrency, i as AGGREGATE_RANK_TREE_TABLE, l as getRankIndexDefinitions, m as createError, n as AGGREGATE_EXTREMA_TABLE, r as AGGREGATE_MEMBER_TABLE, s as rankAggregateName, t as AGGREGATE_BUCKET_TABLE, u as AGGREGATE_ERROR } from "../../schema-CzdjX7nx.js";
3
+ import { Dt as PUBLIC_CREATED_AT_FIELD, Et as INTERNAL_CREATION_TIME_FIELD, Ot as usesSystemCreatedAtAlias, Q as normalizeTemporalComparableValue, a as AGGREGATE_STATE_TABLE, c as getAggregateIndexDefinitions, d as COUNT_ERROR, ht as mapWithConcurrency, i as AGGREGATE_RANK_TREE_TABLE, l as getRankIndexDefinitions, m as createError, n as AGGREGATE_EXTREMA_TABLE, r as AGGREGATE_MEMBER_TABLE, s as rankAggregateName, t as AGGREGATE_BUCKET_TABLE, u as AGGREGATE_ERROR } from "../../schema-D8ZZMSKd.js";
4
4
 
5
5
  //#region src/orm/transaction-cache.ts
6
6
  /**
@@ -1,5 +1,5 @@
1
1
  import { $n as TableName, $t as OrmLifecycleChange, An as ConvexCheckBuilder, At as UpdateSet, Bn as ConvexTextBuilder, Br as IsPrimaryKey, Bt as RelationsBuilderColumnBase, C as MigrationStep, Cn as ConvexVectorIndexConfig, Cr as not, Ct as OrderByClause, D as defineMigration, Dn as searchIndex, Dr as startsWith, Dt as ReturningAll, E as buildMigrationPlan, En as rankIndex, Er as or, Et as PredicateWhereIndexConfig, Fn as ConvexUniqueConstraintBuilderOn, Fr as ColumnBuilderTypeConfig, Ft as ExtractTablesFromSchema, Gn as Columns, Gt as TablesRelationalConfig, H as RlsMode, Hn as text, Hr as NotNull, In as ConvexUniqueConstraintConfig, Ir as ColumnBuilderWithTableName, It as ExtractTablesWithRelations, Jn as OrmSchemaExtensionTables, Jt as ConvexDeletionBuilder, Kt as defineRelations, Ln as check, Lr as ColumnDataType, Lt as ManyConfig, M as DatabaseWithQuery, Mn as ConvexForeignKeyBuilder, Mr as ColumnBuilder, Mt as VectorSearchProvider, N as OrmReader$1, Nn as ConvexForeignKeyConfig, Nr as ColumnBuilderBaseConfig, Nt as unsetToken, O as defineMigrationSet, On as uniqueIndex, Ot as ReturningResult, P as OrmWriter$1, Pn as ConvexUniqueConstraintBuilder, Pr as ColumnBuilderRuntimeConfig, Qn as OrmSchemaTriggers, Qt as DiscriminatorBuilderConfig, Rn as foreignKey, Rr as DrizzleEntity, Rt as OneConfig, S as MigrationStateMap, Sn as ConvexVectorIndexBuilderOn, Sr as ne, St as MutationRunMode, T as MigrationWriteMode, Tn as index, Tr as notInArray, Tt as PaginatedResult, U as EdgeMetadata, V as RlsContext, Vn as ConvexTextBuilderInitial, Vr as IsUnique, Vt as RelationsBuilderColumnConfig, W as extractRelationsConfig, Wn as Brand, Wt as TableRelationalConfig, Xn as OrmSchemaExtensions, Xt as ConvexTable, Yt as ConvexDeletionConfig, Zn as OrmSchemaRelations, Zt as ConvexTableWithColumns, _ as MigrationManifestEntry, _n as ConvexRankIndexBuilderOn, _r as isNotNull, _t as MutationExecutionMode, an as RlsPolicy, ar as UnaryExpression, at as BuildRelationResult, b as MigrationRunStatus, bn as ConvexSearchIndexConfig, br as lt, bt as MutationResult, cn as rlsPolicy, cr as contains, ct as DBQueryConfig, d as MigrationAppliedState, dn as rlsRole, dr as fieldRef, dt as InferInsertModel, en as OrmLifecycleOperation, er as BinaryExpression, f as MigrationDefinition, fn as ConvexAggregateIndexBuilder, fr as gt, ft as InferModelFromColumns, g as MigrationDriftIssue, gn as ConvexRankIndexBuilder, gr as isFieldReference, gt as MutationExecuteResult, h as MigrationDocContext, hn as ConvexIndexBuilderOn, hr as inArray, ht as MutationExecuteConfig, i as OrmMigrationCapability, in as discriminator, ir as LogicalExpression, it as BuildQueryResult, j as DatabaseWithMutations, jn as ConvexCheckConfig, jr as AnyColumn, jt as VectorQueryConfig, k as detectMigrationDrift, kn as vectorIndex, kr as SystemFields, kt as ReturningSelection, ln as RlsRole, lr as endsWith, lt as FilterOperators, m as MigrationDoc, mn as ConvexIndexBuilder, mr as ilike, mt as InsertValue, n as OrmCapabilities, nn as convexTable, nr as FieldReference, nt as AggregateFieldValue, on as RlsPolicyConfig, or as and, ot as CountConfig, p as MigrationDirection, pn as ConvexAggregateIndexBuilderOn, pr as gte, pt as InferSelectModel, qt as defineRelationsPart, r as OrmCapability, rn as deletion, rr as FilterExpression, rt as AggregateResult, sn as RlsPolicyToOption, sr as between, st as CountResult, t as OrmAggregateCapability, tn as TableConfig, tr as ExpressionVisitor, tt as AggregateConfig, un as RlsRoleConfig, ur as eq, ut as GetColumnData, v as MigrationMigrateOne, vn as ConvexSearchIndexBuilder, vr as isNull, vt as MutationPaginateConfig, w as MigrationTableName, wn as aggregateIndex, wr as notBetween, wt as OrderDirection, x as MigrationSet, xn as ConvexVectorIndexBuilder, xr as lte, xt as MutationReturning, y as MigrationPlan, yn as ConvexSearchIndexBuilderOn, yr as like, yt as MutationPaginatedResult, zn as unique, zr as HasDefault, zt as RelationsBuilder } from "../capabilities-6PZBmvc8.js";
2
- import { $ as OrmClientWithApi$1, A as ConvexDateMode, B as ConvexBytesBuilderInitial, C as ConvexNumberBuilderInitial, D as id, E as ConvexIdBuilderInitial, F as custom, G as ConvexBigIntBuilder, H as ConvexBooleanBuilder, I as json, J as CreateOrmOptions, K as ConvexBigIntBuilderInitial, L as objectOf, M as ConvexCustomBuilder, N as ConvexCustomBuilderInitial, O as ConvexDateBuilder, P as arrayOf, Q as OrmClientBase$1, R as unionOf, S as ConvexNumberBuilder, St as defineTriggers, T as ConvexIdBuilder, U as ConvexBooleanBuilderInitial, V as bytes, W as boolean, X as GenericOrmCtx$1, Y as GenericOrm$1, Z as OrmApiResult, _ as ConvexTimestampMode, _t as OrmBeforeResult, a as requireSchemaRelations, at as ScheduledMutationBatchArgs, b as ConvexTextEnumBuilderInitial, bt as OrmTriggerContext, c as TableConfigResult, ct as scheduledDeleteFactory, d as OrmNotFoundError, et as OrmFunctions, f as ConvexVectorBuilder, g as ConvexTimestampBuilderInitial, gt as defineSchemaExtension, h as ConvexTimestampBuilder, ht as SchemaExtension, i as getSchemaTriggers, it as createOrm, j as date, k as ConvexDateBuilderInitial, l as getTableColumns, m as vector, n as defineSchema, nt as OrmWriterCtx, o as asc, ot as scheduledMutationBatchFactory, p as ConvexVectorBuilderInitial, q as bigint, r as getSchemaRelations, rt as ResolveOrmSchema, s as desc, st as ScheduledDeleteArgs, t as WhereClauseResult, tt as OrmReaderCtx, u as getTableConfig, v as timestamp, vt as OrmTableTriggers, w as integer, x as textEnum, xt as OrmTriggers, y as ConvexTextEnumBuilder, yt as OrmTriggerChange, z as ConvexBytesBuilder } from "../where-clause-compiler-CX5-0M5p.js";
2
+ import { $ as OrmClientWithApi$1, A as ConvexDateMode, B as ConvexBytesBuilderInitial, C as ConvexNumberBuilderInitial, D as id, E as ConvexIdBuilderInitial, F as custom, G as ConvexBigIntBuilder, H as ConvexBooleanBuilder, I as json, J as CreateOrmOptions, K as ConvexBigIntBuilderInitial, L as objectOf, M as ConvexCustomBuilder, N as ConvexCustomBuilderInitial, O as ConvexDateBuilder, P as arrayOf, Q as OrmClientBase$1, R as unionOf, S as ConvexNumberBuilder, St as defineTriggers, T as ConvexIdBuilder, U as ConvexBooleanBuilderInitial, V as bytes, W as boolean, X as GenericOrmCtx$1, Y as GenericOrm$1, Z as OrmApiResult, _ as ConvexTimestampMode, _t as OrmBeforeResult, a as requireSchemaRelations, at as ScheduledMutationBatchArgs, b as ConvexTextEnumBuilderInitial, bt as OrmTriggerContext, c as TableConfigResult, ct as scheduledDeleteFactory, d as OrmNotFoundError, et as OrmFunctions, f as ConvexVectorBuilder, g as ConvexTimestampBuilderInitial, gt as defineSchemaExtension, h as ConvexTimestampBuilder, ht as SchemaExtension, i as getSchemaTriggers, it as createOrm, j as date, k as ConvexDateBuilderInitial, l as getTableColumns, m as vector, n as defineSchema, nt as OrmWriterCtx, o as asc, ot as scheduledMutationBatchFactory, p as ConvexVectorBuilderInitial, q as bigint, r as getSchemaRelations, rt as ResolveOrmSchema, s as desc, st as ScheduledDeleteArgs, t as WhereClauseResult, tt as OrmReaderCtx, u as getTableConfig, v as timestamp, vt as OrmTableTriggers, w as integer, x as textEnum, xt as OrmTriggers, y as ConvexTextEnumBuilder, yt as OrmTriggerChange, z as ConvexBytesBuilder } from "../where-clause-compiler-DvYSTJXe.js";
3
3
  import { i as pretendRequired, n as deprecated, r as pretend } from "../validators-wOIjhkfN.js";
4
4
  import { a as QueryCtxWithPreferredOrmQueryTable, i as QueryCtxWithOrmQueryTable, n as LookupByIdResultByCtx, o as getByIdWithOrmQueryFallback, r as QueryCtxWithOptionalOrmQueryTable, t as DocByCtx } from "../query-context-DJONf8X5.js";
5
5
  import { DefineSchemaOptions, GenericDatabaseReader, GenericDatabaseWriter, GenericSchema, SchemaDefinition } from "convex/server";
package/dist/orm/index.js CHANGED
@@ -4,7 +4,7 @@ import { a as pretendRequired, i as pretend, n as deprecated } from "../validato
4
4
  import { t as id } from "../id-CuSfWa5q.js";
5
5
  import { A as or, C as matchLikePattern, D as notIlike, E as notBetween, O as notInArray, S as lte, T as not, _ as isFieldReference, a as between, b as like, c as endsWith, d as filterValueInList, f as filterValuesEqual, g as inArray, h as ilike, i as arrayOverlaps, j as startsWith, k as notLike, l as eq, m as gte, n as arrayContained, o as column, p as gt, r as arrayContains, s as contains, t as and, u as fieldRef, v as isNotNull, w as ne, x as lt, y as isNull } from "../filter-expression-Dydt8wS0.js";
6
6
  import { a as indexKeyWithinBounds, c as streamIndexRange, i as getIndexFields, l as isUnsetToken, n as EmptyStream, o as mergedStream, r as QueryStream, s as stream, t as getByIdWithOrmQueryFallback, u as unsetToken } from "../query-context-DOm5Xm3H.js";
7
- import { $ as patchReferencingRows, A as enforcePolymorphicWrite, B as getForeignKeys, C as collectPrimaryIdLookupRows, Ct as resolveIndexOrderPushdown, D as encodeUndefinedDeep, Dt as usesSystemCreatedAtAlias, E as deserializeFilterExpression, Et as PUBLIC_CREATED_AT_FIELD, F as evaluateCheckConstraintTriState, G as getTableColumns$2, H as getMutationCollectionLimits, I as evaluateFilter, J as getUniqueIndexes, K as getTableDeleteConfig, L as extractPrimaryIdLookup, M as ensureDefaultColumns, N as ensureNonNullValues, O as enforceCheckConstraints, P as ensureNullableColumns, Q as normalizeTemporalComparableValue, R as getChecks, S as collectMutationRowsBounded, St as getRankIndexes, T as decodeUndefinedDeep, Tt as INTERNAL_CREATION_TIME_FIELD, U as getMutationExecutionMode, V as getMutationAsyncDelayMs, W as getOrmContext, X as hydrateDateFieldsForRead, Y as hardDeleteRow, Z as normalizeDateFieldsForWrite, _ as applyDefaults, _t as findRelationIndex, at as splitReturningSelection, b as buildForeignKeyGraph, bt as getAggregateIndexes, c as getAggregateIndexDefinitions, ct as toConvexFilter, d as COUNT_ERROR, dt as markLifecycleHookedTables, et as resolveOrmRuntimeDefaults, f as createAggregateError, ft as compileConvexFilter, g as ensureCountAllowedForRls, gt as findIndexForColumns, h as ensureAggregateAllowedForRls, ht as mapWithConcurrency, it as softDeleteRow, j as enforceUniqueIndexes, k as enforceForeignKeys, l as getRankIndexDefinitions, lt as unsetFieldsOf, mt as isConvexEnforceableFilter, nt as selectReturningRowWithHydration, o as aggregateExtension, ot as stripUnsetFields, p as createCountError, pt as convexAnd, q as getTableName, rt as serializeFilterExpression, st as takeRowsWithinByteBudget, tt as returningSelectionReadsCreationTime, u as AGGREGATE_ERROR, ut as hasLifecycleHooks, v as applyIncomingForeignKeyActionsOnDelete, vt as findSearchIndexByName, w as createForeignKeyProbeMemo, wt as CREATED_AT_MIGRATION_MESSAGE, x as canUsePrimaryIdLookupCursor, xt as getIndexes, y as applyIncomingForeignKeyActionsOnUpdate, yt as findVectorIndexByName, z as getColumnName$1 } from "../schema-CzdjX7nx.js";
7
+ import { $ as patchReferencingRows, A as enforcePolymorphicWrite, B as getForeignKeys, C as collectPrimaryIdLookupRows, Ct as getRankIndexes, D as encodeUndefinedDeep, Dt as PUBLIC_CREATED_AT_FIELD, E as deserializeFilterExpression, Et as INTERNAL_CREATION_TIME_FIELD, F as evaluateCheckConstraintTriState, G as getTableColumns$2, H as getMutationCollectionLimits, I as evaluateFilter, J as getUniqueIndexes, K as getTableDeleteConfig, L as extractPrimaryIdLookup, M as ensureDefaultColumns, N as ensureNonNullValues, O as enforceCheckConstraints, Ot as usesSystemCreatedAtAlias, P as ensureNullableColumns, Q as normalizeTemporalComparableValue, R as getChecks, S as collectMutationRowsBounded, St as getIndexes, T as decodeUndefinedDeep, Tt as CREATED_AT_MIGRATION_MESSAGE, U as getMutationExecutionMode, V as getMutationAsyncDelayMs, W as getOrmContext, X as hydrateDateFieldsForRead, Y as hardDeleteRow, Z as normalizeDateFieldsForWrite, _ as applyDefaults, _t as findIndexForColumns, at as splitReturningSelection, b as buildForeignKeyGraph, bt as findVectorIndexByName, c as getAggregateIndexDefinitions, ct as toConvexFilter, d as COUNT_ERROR, dt as markLifecycleHookedTables, et as resolveOrmRuntimeDefaults, f as createAggregateError, ft as compileConvexFilter, g as ensureCountAllowedForRls, gt as findExactIndexForColumns, h as ensureAggregateAllowedForRls, ht as mapWithConcurrency, it as softDeleteRow, j as enforceUniqueIndexes, k as enforceForeignKeys, l as getRankIndexDefinitions, lt as unsetFieldsOf, mt as isConvexEnforceableFilter, nt as selectReturningRowWithHydration, o as aggregateExtension, ot as stripUnsetFields, p as createCountError, pt as convexAnd, q as getTableName, rt as serializeFilterExpression, st as takeRowsWithinByteBudget, tt as returningSelectionReadsCreationTime, u as AGGREGATE_ERROR, ut as hasLifecycleHooks, v as applyIncomingForeignKeyActionsOnDelete, vt as findRelationIndex, w as createForeignKeyProbeMemo, wt as resolveIndexOrderPushdown, x as canUsePrimaryIdLookupCursor, xt as getAggregateIndexes, y as applyIncomingForeignKeyActionsOnUpdate, yt as findSearchIndexByName, z as getColumnName$1 } from "../schema-D8ZZMSKd.js";
8
8
  import { t as defineSchemaExtension } from "../extensions-Blzsyekm.js";
9
9
  import { compareValues, convexToJson, jsonToConvex, v } from "convex/values";
10
10
  import { defineSchema as defineSchema$1, internalActionGeneric, internalMutationGeneric, internalQueryGeneric } from "convex/server";
@@ -7360,6 +7360,7 @@ function scheduledDeleteFactory(schema, edgeMetadata, scheduledMutationBatch) {
7360
7360
  //#region src/orm/scheduled-mutation-batch.ts
7361
7361
  /** Column stamped by `softDeleteRow`; see mutation-utils.ts. */
7362
7362
  const DELETION_TIME_FIELD = "deletionTime";
7363
+ const isPending = (row) => row[DELETION_TIME_FIELD] === void 0 || row[DELETION_TIME_FIELD] === null;
7363
7364
  const isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
7364
7365
  function scheduledMutationBatchFactory(schema, edgeMetadata, scheduledMutationBatch) {
7365
7366
  const tableByName = /* @__PURE__ */ new Map();
@@ -7424,23 +7425,30 @@ function scheduledMutationBatchFactory(schema, edgeMetadata, scheduledMutationBa
7424
7425
  if (!targetValues || !Array.isArray(targetValues)) throw new Error("scheduledMutationBatch: targetValues are required for cascade work.");
7425
7426
  if (!args.foreignIndexName) throw new Error("scheduledMutationBatch: foreignIndexName is required for cascade work.");
7426
7427
  const action = args.foreignAction ?? "no action";
7427
- const skipSoftDeletedRows = workType === "cascade-delete" && action === "cascade" && (args.cascadeMode ?? "hard") === "soft";
7428
+ const isSoftCascade = workType === "cascade-delete" && action === "cascade" && (args.cascadeMode ?? "hard") === "soft";
7429
+ const exactIndexName = findExactIndexForColumns(getIndexes(table), sourceColumns);
7430
+ if (isSoftCascade && args.softCascadeCursorVersion === 1 && !exactIndexName) throw new Error(`scheduledMutationBatch: async soft cascade on '${args.table}' requires an exact foreign-key index on (${sourceColumns.join(", ")}).`);
7431
+ const effectiveIndexName = isSoftCascade && exactIndexName ? exactIndexName : args.foreignIndexName;
7432
+ const recoversQueuedJob = isSoftCascade && exactIndexName !== null && (args.softCascadeCursorVersion !== 1 || args.foreignIndexName !== exactIndexName);
7433
+ const pageStartCursor = isSoftCascade && exactIndexName ? recoversQueuedJob ? null : args.cursor : null;
7434
+ const replaysQueuedPrefixJob = isSoftCascade && !exactIndexName;
7428
7435
  const queryWithIndex = () => {
7429
- const indexed = ctx.db.query(args.table).withIndex(args.foreignIndexName, (q) => {
7436
+ const indexed = ctx.db.query(args.table).withIndex(effectiveIndexName, (q) => {
7430
7437
  let builder = q.eq(sourceColumns[0], targetValues[0]);
7431
7438
  for (let i = 1; i < sourceColumns.length; i += 1) builder = builder.eq(sourceColumns[i], targetValues[i]);
7432
7439
  return builder;
7433
7440
  });
7434
- if (!skipSoftDeletedRows) return indexed;
7441
+ if (!replaysQueuedPrefixJob) return indexed;
7435
7442
  return indexed.filter((q) => q.or(q.eq(q.field(DELETION_TIME_FIELD), void 0), q.eq(q.field(DELETION_TIME_FIELD), null)));
7436
7443
  };
7437
7444
  const paged = await queryWithIndex().paginate({
7438
- cursor: null,
7445
+ cursor: pageStartCursor,
7439
7446
  numItems: args.batchSize
7440
7447
  });
7441
7448
  const resolvedMaxBytesPerBatch = args.maxBytesPerBatch ?? maxBytesPerBatch;
7442
7449
  const bounded = takeRowsWithinByteBudget(paged.page, resolvedMaxBytesPerBatch);
7443
- const rows = bounded.rows;
7450
+ const consumedRows = bounded.rows;
7451
+ const rows = isSoftCascade ? consumedRows.filter(isPending) : consumedRows;
7444
7452
  const hitByteLimit = bounded.hitLimit;
7445
7453
  const scheduleState = {
7446
7454
  remainingCalls: scheduleCallCap,
@@ -7498,6 +7506,29 @@ function scheduledMutationBatchFactory(schema, edgeMetadata, scheduledMutationBa
7498
7506
  await patchReferencingRows(ctx.db, args.table, rows, patchValues);
7499
7507
  }
7500
7508
  }
7509
+ if (isSoftCascade && exactIndexName) {
7510
+ if (hitByteLimit) {
7511
+ await ctx.scheduler.runAfter(args.delayMs, scheduledMutationBatch, {
7512
+ ...args,
7513
+ workType,
7514
+ foreignIndexName: exactIndexName,
7515
+ softCascadeCursorVersion: 1,
7516
+ cursor: pageStartCursor,
7517
+ batchSize: consumedRows.length,
7518
+ maxBytesPerBatch: resolvedMaxBytesPerBatch
7519
+ });
7520
+ return;
7521
+ }
7522
+ if (!paged.isDone && paged.continueCursor !== null) await ctx.scheduler.runAfter(args.delayMs, scheduledMutationBatch, {
7523
+ ...args,
7524
+ workType,
7525
+ foreignIndexName: exactIndexName,
7526
+ softCascadeCursorVersion: 1,
7527
+ cursor: paged.continueCursor,
7528
+ maxBytesPerBatch: resolvedMaxBytesPerBatch
7529
+ });
7530
+ return;
7531
+ }
7501
7532
  if (await queryWithIndex().first() !== null || hitByteLimit) await ctx.scheduler.runAfter(args.delayMs, scheduledMutationBatch, {
7502
7533
  ...args,
7503
7534
  workType,
@@ -1,3 +1,3 @@
1
1
  import { C as MigrationStep, D as defineMigration, E as buildMigrationPlan, O as defineMigrationSet, S as MigrationStateMap, T as MigrationWriteMode, _ as MigrationManifestEntry, a as MAX_STATUS_RUN_LIMIT, b as MigrationRunStatus, c as MigrationRunChunkArgs, d as MigrationAppliedState, f as MigrationDefinition, g as MigrationDriftIssue, h as MigrationDocContext, k as detectMigrationDrift, l as MigrationStatusArgs, m as MigrationDoc, o as MigrationCancelArgs, p as MigrationDirection, s as MigrationRunArgs, u as createMigrationHandlers, v as MigrationMigrateOne, w as MigrationTableName, x as MigrationSet, y as MigrationPlan } from "../../capabilities-6PZBmvc8.js";
2
- import { Ct as migrationCapability, dt as MIGRATION_STORAGE_TABLE_NAMES, ft as injectMigrationStorageTables, lt as MIGRATION_RUN_TABLE, mt as migrationStorageTables, pt as migrationExtension, ut as MIGRATION_STATE_TABLE } from "../../where-clause-compiler-CX5-0M5p.js";
2
+ import { Ct as migrationCapability, dt as MIGRATION_STORAGE_TABLE_NAMES, ft as injectMigrationStorageTables, lt as MIGRATION_RUN_TABLE, mt as migrationStorageTables, pt as migrationExtension, ut as MIGRATION_STATE_TABLE } from "../../where-clause-compiler-DvYSTJXe.js";
3
3
  export { MAX_STATUS_RUN_LIMIT, MIGRATION_RUN_TABLE, MIGRATION_STATE_TABLE, MIGRATION_STORAGE_TABLE_NAMES, type MigrationAppliedState, type MigrationCancelArgs, type MigrationDefinition, type MigrationDirection, type MigrationDoc, type MigrationDocContext, type MigrationDriftIssue, type MigrationManifestEntry, type MigrationMigrateOne, type MigrationPlan, type MigrationRunArgs, type MigrationRunChunkArgs, type MigrationRunStatus, type MigrationSet, type MigrationStateMap, type MigrationStatusArgs, type MigrationStep, type MigrationTableName, type MigrationWriteMode, buildMigrationPlan, createMigrationHandlers, defineMigration, defineMigrationSet, detectMigrationDrift, injectMigrationStorageTables, migrationCapability, migrationExtension, migrationStorageTables };
@@ -47,6 +47,9 @@ function findIndexForColumns(indexes, columns) {
47
47
  for (const index of indexes) if (hasColumnPrefix(index, columns)) return index.name;
48
48
  return null;
49
49
  }
50
+ function findExactIndexForColumns(indexes, columns) {
51
+ return indexes.find((candidate) => candidate.fields.length === columns.length && candidate.fields.every((field, position) => field === columns[position]))?.name ?? null;
52
+ }
50
53
  const hasColumnPrefix = (index, columns) => {
51
54
  if (index.fields.length < columns.length) return false;
52
55
  for (let i = 0; i < columns.length; i += 1) if (index.fields[i] !== columns[i]) return false;
@@ -880,8 +883,9 @@ async function enforceForeignKeys(db, table, candidate, options) {
880
883
  }).first()) throw new Error(`Foreign key violation on '${tableName}': missing document in '${foreignKey.foreignTableName}'.`);
881
884
  }
882
885
  }
883
- function getIndexForForeignKey(foreignKey) {
884
- return findIndexForColumns(getIndexes(foreignKey.sourceTable), foreignKey.sourceColumns);
886
+ function getIndexForForeignKey(foreignKey, exact = false) {
887
+ const indexes = getIndexes(foreignKey.sourceTable);
888
+ return exact ? findExactIndexForColumns(indexes, foreignKey.sourceColumns) : findIndexForColumns(indexes, foreignKey.sourceColumns);
885
889
  }
886
890
  function foreignKeyIndexError(foreignKey) {
887
891
  return /* @__PURE__ */ new Error(`Foreign key on '${foreignKey.sourceTableName}' requires index on '${foreignKey.sourceTableName}(${foreignKey.sourceColumns.join(", ")})' for cascading actions.`);
@@ -969,7 +973,9 @@ async function applyIncomingForeignKeyActionsOnDelete(db, table, row, options) {
969
973
  const action = foreignKey.onDelete ?? "no action";
970
974
  const targetValues = foreignKey.targetColumns.map((column) => row[column]);
971
975
  if (targetValues.some((value) => value === void 0 || value === null)) continue;
972
- const indexName = getIndexForForeignKey(foreignKey);
976
+ const requiresExactIndex = options.executionMode === "async" && options.cascadeMode === "soft" && action === "cascade";
977
+ const indexName = getIndexForForeignKey(foreignKey, requiresExactIndex);
978
+ if (requiresExactIndex && !indexName) throw new Error(`Async soft cascade on '${foreignKey.sourceTableName}' requires an exact foreign-key index on (${foreignKey.sourceColumns.join(", ")}). Prefix indexes with trailing fields cannot provide stable scheduled continuation.`);
973
979
  if (action === "restrict" || action === "no action") {
974
980
  if (!indexName && !options.allowFullScan) throw foreignKeyIndexError(foreignKey);
975
981
  if (!indexName && options.strict) console.warn(`Foreign key check running without index (allowFullScan: true) on '${foreignKey.sourceTableName}'.`);
@@ -1001,6 +1007,7 @@ async function applyIncomingForeignKeyActionsOnDelete(db, table, row, options) {
1001
1007
  foreignAction: action,
1002
1008
  deleteMode: options.deleteMode,
1003
1009
  cascadeMode: options.cascadeMode,
1010
+ softCascadeCursorVersion: requiresExactIndex ? 1 : void 0,
1004
1011
  cursor: null,
1005
1012
  batchSize: asyncBatchSize,
1006
1013
  maxBytesPerBatch: options.maxBytesPerBatch,
@@ -1526,4 +1533,4 @@ function aggregateExtension() {
1526
1533
  }
1527
1534
 
1528
1535
  //#endregion
1529
- export { patchReferencingRows as $, enforcePolymorphicWrite as A, getForeignKeys as B, collectPrimaryIdLookupRows as C, resolveIndexOrderPushdown as Ct, encodeUndefinedDeep as D, usesSystemCreatedAtAlias as Dt, deserializeFilterExpression as E, PUBLIC_CREATED_AT_FIELD as Et, 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, getRankIndexes as St, decodeUndefinedDeep as T, INTERNAL_CREATION_TIME_FIELD as Tt, getMutationExecutionMode as U, getMutationAsyncDelayMs as V, getOrmContext as W, hydrateDateFieldsForRead as X, hardDeleteRow as Y, normalizeDateFieldsForWrite as Z, applyDefaults as _, findRelationIndex as _t, AGGREGATE_STATE_TABLE as a, splitReturningSelection as at, buildForeignKeyGraph as b, getAggregateIndexes as bt, 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, findIndexForColumns as gt, 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, findSearchIndexByName as vt, createForeignKeyProbeMemo as w, CREATED_AT_MIGRATION_MESSAGE as wt, canUsePrimaryIdLookupCursor as x, getIndexes as xt, applyIncomingForeignKeyActionsOnUpdate as y, findVectorIndexByName as yt, getColumnName as z };
1536
+ export { patchReferencingRows as $, enforcePolymorphicWrite as A, getForeignKeys as B, collectPrimaryIdLookupRows as C, getRankIndexes as Ct, encodeUndefinedDeep as D, PUBLIC_CREATED_AT_FIELD as Dt, deserializeFilterExpression as E, INTERNAL_CREATION_TIME_FIELD as Et, 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, usesSystemCreatedAtAlias as Ot, ensureNullableColumns as P, normalizeTemporalComparableValue as Q, getChecks as R, collectMutationRowsBounded as S, getIndexes as St, decodeUndefinedDeep as T, CREATED_AT_MIGRATION_MESSAGE as Tt, getMutationExecutionMode as U, getMutationAsyncDelayMs as V, getOrmContext as W, hydrateDateFieldsForRead as X, hardDeleteRow as Y, normalizeDateFieldsForWrite as Z, applyDefaults as _, findIndexForColumns as _t, AGGREGATE_STATE_TABLE as a, splitReturningSelection as at, buildForeignKeyGraph as b, findVectorIndexByName as bt, 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, findExactIndexForColumns as gt, 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, findRelationIndex as vt, createForeignKeyProbeMemo as w, resolveIndexOrderPushdown as wt, canUsePrimaryIdLookupCursor as x, getAggregateIndexes as xt, applyIncomingForeignKeyActionsOnUpdate as y, findSearchIndexByName as yt, getColumnName as z };
@@ -427,6 +427,7 @@ type ScheduledMutationBatchArgs = {
427
427
  update?: Record<string, unknown>;
428
428
  deleteMode?: DeleteMode;
429
429
  cascadeMode?: CascadeMode;
430
+ softCascadeCursorVersion?: 1;
430
431
  foreignIndexName?: string;
431
432
  foreignSourceColumns?: string[];
432
433
  targetValues?: unknown;
@@ -1023,7 +1024,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1023
1024
  fieldName: "updatedAt";
1024
1025
  };
1025
1026
  };
1026
- count: ConvexNumberBuilderInitial<""> & {
1027
+ indexName: ConvexTextBuilderInitial<""> & {
1027
1028
  _: {
1028
1029
  notNull: true;
1029
1030
  };
@@ -1033,7 +1034,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1033
1034
  };
1034
1035
  } & {
1035
1036
  _: {
1036
- fieldName: "count";
1037
+ fieldName: "indexName";
1037
1038
  };
1038
1039
  };
1039
1040
  tableKey: ConvexTextBuilderInitial<""> & {
@@ -1049,7 +1050,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1049
1050
  fieldName: "tableKey";
1050
1051
  };
1051
1052
  };
1052
- indexName: ConvexTextBuilderInitial<""> & {
1053
+ count: ConvexNumberBuilderInitial<""> & {
1053
1054
  _: {
1054
1055
  notNull: true;
1055
1056
  };
@@ -1059,7 +1060,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1059
1060
  };
1060
1061
  } & {
1061
1062
  _: {
1062
- fieldName: "indexName";
1063
+ fieldName: "count";
1063
1064
  };
1064
1065
  };
1065
1066
  keyHash: ConvexTextBuilderInitial<""> & {
@@ -1161,7 +1162,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1161
1162
  fieldName: "updatedAt";
1162
1163
  };
1163
1164
  };
1164
- tableKey: ConvexTextBuilderInitial<""> & {
1165
+ indexName: ConvexTextBuilderInitial<""> & {
1165
1166
  _: {
1166
1167
  notNull: true;
1167
1168
  };
@@ -1171,10 +1172,10 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1171
1172
  };
1172
1173
  } & {
1173
1174
  _: {
1174
- fieldName: "tableKey";
1175
+ fieldName: "indexName";
1175
1176
  };
1176
1177
  };
1177
- indexName: ConvexTextBuilderInitial<""> & {
1178
+ tableKey: ConvexTextBuilderInitial<""> & {
1178
1179
  _: {
1179
1180
  notNull: true;
1180
1181
  };
@@ -1184,7 +1185,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1184
1185
  };
1185
1186
  } & {
1186
1187
  _: {
1187
- fieldName: "indexName";
1188
+ fieldName: "tableKey";
1188
1189
  };
1189
1190
  };
1190
1191
  keyHash: ConvexTextBuilderInitial<""> & {
@@ -1355,7 +1356,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1355
1356
  fieldName: "updatedAt";
1356
1357
  };
1357
1358
  };
1358
- count: ConvexNumberBuilderInitial<""> & {
1359
+ indexName: ConvexTextBuilderInitial<""> & {
1359
1360
  _: {
1360
1361
  notNull: true;
1361
1362
  };
@@ -1365,7 +1366,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1365
1366
  };
1366
1367
  } & {
1367
1368
  _: {
1368
- fieldName: "count";
1369
+ fieldName: "indexName";
1369
1370
  };
1370
1371
  };
1371
1372
  tableKey: ConvexTextBuilderInitial<""> & {
@@ -1381,7 +1382,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1381
1382
  fieldName: "tableKey";
1382
1383
  };
1383
1384
  };
1384
- indexName: ConvexTextBuilderInitial<""> & {
1385
+ count: ConvexNumberBuilderInitial<""> & {
1385
1386
  _: {
1386
1387
  notNull: true;
1387
1388
  };
@@ -1391,7 +1392,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1391
1392
  };
1392
1393
  } & {
1393
1394
  _: {
1394
- fieldName: "indexName";
1395
+ fieldName: "count";
1395
1396
  };
1396
1397
  };
1397
1398
  keyHash: ConvexTextBuilderInitial<""> & {
@@ -1670,7 +1671,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1670
1671
  fieldName: "lastError";
1671
1672
  };
1672
1673
  };
1673
- tableKey: ConvexTextBuilderInitial<""> & {
1674
+ indexName: ConvexTextBuilderInitial<""> & {
1674
1675
  _: {
1675
1676
  notNull: true;
1676
1677
  };
@@ -1680,10 +1681,10 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1680
1681
  };
1681
1682
  } & {
1682
1683
  _: {
1683
- fieldName: "tableKey";
1684
+ fieldName: "indexName";
1684
1685
  };
1685
1686
  };
1686
- indexName: ConvexTextBuilderInitial<""> & {
1687
+ keyDefinitionHash: ConvexTextBuilderInitial<""> & {
1687
1688
  _: {
1688
1689
  notNull: true;
1689
1690
  };
@@ -1693,10 +1694,10 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1693
1694
  };
1694
1695
  } & {
1695
1696
  _: {
1696
- fieldName: "indexName";
1697
+ fieldName: "keyDefinitionHash";
1697
1698
  };
1698
1699
  };
1699
- keyDefinitionHash: ConvexTextBuilderInitial<""> & {
1700
+ metricDefinitionHash: ConvexTextBuilderInitial<""> & {
1700
1701
  _: {
1701
1702
  notNull: true;
1702
1703
  };
@@ -1706,10 +1707,10 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1706
1707
  };
1707
1708
  } & {
1708
1709
  _: {
1709
- fieldName: "keyDefinitionHash";
1710
+ fieldName: "metricDefinitionHash";
1710
1711
  };
1711
1712
  };
1712
- metricDefinitionHash: ConvexTextBuilderInitial<""> & {
1713
+ tableKey: ConvexTextBuilderInitial<""> & {
1713
1714
  _: {
1714
1715
  notNull: true;
1715
1716
  };
@@ -1719,7 +1720,7 @@ declare const BUILTIN_SCHEMA_EXTENSIONS: readonly [SchemaExtension<{
1719
1720
  };
1720
1721
  } & {
1721
1722
  _: {
1722
- fieldName: "metricDefinitionHash";
1723
+ fieldName: "tableKey";
1723
1724
  };
1724
1725
  };
1725
1726
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitcn",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "kitcn - React Query integration and CLI tools for Convex",
5
5
  "keywords": [
6
6
  "convex",
@@ -89,6 +89,11 @@ import { foreignKey } from "kitcn/orm";
89
89
  (t) => [foreignKey({ columns: [t.userSlug], foreignColumns: [users.slug] })];
90
90
  ```
91
91
 
92
+ Delta from parity: async soft cascade requires an exact child index on the
93
+ referencing columns. `index("by_author").on(t.authorId)` qualifies;
94
+ `index("by_author_rank").on(t.authorId, t.rank)` does not, because a mutable
95
+ trailing field cannot provide stable continuation across scheduled mutations.
96
+
92
97
  ### Check Constraints
93
98
 
94
99
  ```ts