kitcn 0.16.0 → 0.16.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.
@@ -1,4 +1,4 @@
1
- import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial, dn as ConvexTableWithColumns, tr as ConvexTextBuilderInitial } from "../where-clause-compiler-WF9UcrAB.js";
1
+ import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial, dn as ConvexTableWithColumns, tr as ConvexTextBuilderInitial } from "../where-clause-compiler-C8UKgzTO.js";
2
2
  import * as convex_values0 from "convex/values";
3
3
  import { GenericId, Infer, Value } from "convex/values";
4
4
  import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
@@ -3,7 +3,7 @@ import { r as partial } from "../validators-C7LelqTN.js";
3
3
  import { i as defineAuth, n as createDisabledAuthRuntime, r as getGeneratedAuthDisabledReason, t as DEFAULT_AUTH_DEFINITION_PATH } from "../generated-contract-disabled-BXaz7JCE.js";
4
4
  import { n as createGeneratedFunctionReference, o as isQueryCtx, s as isRunMutationCtx } from "../api-entry-N3nBOlI2.js";
5
5
  import { n as customCtx, r as customMutation } from "../customFunctions-DxEEO4Dq.js";
6
- import { a as mergedStream, l as unsetToken, o as stream, t as getByIdWithOrmQueryFallback, v as eq } from "../query-context-ydn9kb6P.js";
6
+ import { a as mergedStream, l as unsetToken, o as stream, t as getByIdWithOrmQueryFallback, v as eq } from "../query-context-B47_3n97.js";
7
7
  import { n as convex } from "../convex-plugin-BHVCqWTH.js";
8
8
  import { v } from "convex/values";
9
9
  import { internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
@@ -1,4 +1,4 @@
1
- import { $ as GenericOrmCtx$1, $n as unique, $r as endsWith, $t as ManyConfig, A as ConvexDateMode, An as ConvexRankIndexBuilder, Ar as ReturningResult, At as MigrationManifestEntry, B as ConvexBytesBuilderInitial, Bn as rankIndex, Br as OrmSchemaRelations, Bt as defineMigration, C as ConvexNumberBuilderInitial, Ci as ColumnBuilderWithTableName, Cn as RlsRole, Cr as MutationReturning, Ct as MigrationStatusArgs, D as id, Di as IsPrimaryKey, Dn as ConvexAggregateIndexBuilderOn, Dr as PaginatedResult, Dt as MigrationDoc, E as ConvexIdBuilderInitial, Ei as HasDefault, En as ConvexAggregateIndexBuilder, Er as OrderDirection, Et as MigrationDirection, F as custom, Fn as ConvexVectorIndexBuilder, Fr as unsetToken, Ft as MigrationStateMap, G as ConvexBigIntBuilder, Gn as ConvexCheckConfig, Gr as ExpressionVisitor, Gt as OrmReader$1, H as ConvexBooleanBuilder, Hn as uniqueIndex, Hr as TableName, Ht as detectMigrationDrift, I as json, In as ConvexVectorIndexBuilderOn, Ir as Brand, It as MigrationStep, J as CountBackfillChunkArgs, Jn as ConvexUniqueConstraintBuilder, Jr as LogicalExpression, Jt as RlsMode, K as ConvexBigIntBuilderInitial, Kn as ConvexForeignKeyBuilder, Kr as FieldReference, Kt as OrmWriter$1, L as objectOf, Ln as ConvexVectorIndexConfig, Lr as Columns, Lt as MigrationTableName, M as ConvexCustomBuilder, Mn as ConvexSearchIndexBuilder, Mr as UpdateSet, Mt as MigrationPlan, N as ConvexCustomBuilderInitial, Nn as ConvexSearchIndexBuilderOn, Nr as VectorQueryConfig, Nt as MigrationRunStatus, O as ConvexDateBuilder, Oi as IsUnique, On as ConvexIndexBuilder, Or as PredicateWhereIndexConfig, Ot as MigrationDocContext, P as arrayOf, Pn as ConvexSearchIndexConfig, Pr as VectorSearchProvider, Pt as MigrationSet, Q as GenericOrm$1, Qn as foreignKey, Qr as contains, Qt as ExtractTablesWithRelations, R as unionOf, Rn as aggregateIndex, Rr as OrmSchemaExtensionTables, Rt as MigrationWriteMode, S as ConvexNumberBuilder, Si as ColumnBuilderTypeConfig, Sn as rlsPolicy, Sr as MutationResult, St as MigrationRunChunkArgs, T as ConvexIdBuilder, Ti as DrizzleEntity, Tn as rlsRole, Tr as OrderByClause, Tt as MigrationDefinition, U as ConvexBooleanBuilderInitial, Un as vectorIndex, Ur as SystemFields, Ut as DatabaseWithMutations, V as bytes, Vn as searchIndex, Vr as OrmSchemaTriggers, Vt as defineMigrationSet, W as boolean, Wn as ConvexCheckBuilder, Wr as BinaryExpression, Wt as DatabaseWithQuery, X as CountBackfillStatusArgs, Xn as ConvexUniqueConstraintConfig, Xr as and, Xt as extractRelationsConfig, Y as CountBackfillKickoffArgs, Yn as ConvexUniqueConstraintBuilderOn, Yr as UnaryExpression, Yt as EdgeMetadata, Z as CreateOrmOptions, Zn as check, Zr as between, Zt as ExtractTablesFromSchema, _ as ConvexTimestampMode, _i as startsWith, _n as deletion, _r as MutationExecuteConfig, _t as OrmTriggerContext, a as requireSchemaRelations, ai as inArray, an as TablesRelationalConfig, ar as AggregateResult, at as OrmWriterCtx, b as ConvexTextEnumBuilderInitial, bi as ColumnBuilderBaseConfig, bn as RlsPolicyConfig, br as MutationPaginateConfig, bt as MigrationCancelArgs, c as TableConfigResult, ci as isNull, cn as ConvexDeletionBuilder, cr as CountConfig, ct as ScheduledMutationBatchArgs, d as OrmNotFoundError, di as lte, dn as ConvexTableWithColumns, dr as FilterOperators, dt as scheduledDeleteFactory, ei as eq, en as OneConfig, er as ConvexTextBuilder, et as OrmApiResult, f as ConvexVectorBuilder, fi as ne, fn as DiscriminatorBuilderConfig, fr as GetColumnData, ft as SchemaExtension, g as ConvexTimestampBuilderInitial, gi as or, gn as convexTable, gr as InsertValue, gt as OrmTriggerChange, h as ConvexTimestampBuilder, hi as notInArray, hn as TableConfig, hr as InferSelectModel, ht as OrmTableTriggers, i as getSchemaTriggers, ii as ilike, in as TableRelationalConfig, ir as AggregateFieldValue, it as OrmReaderCtx, j as date, jn as ConvexRankIndexBuilderOn, jr as ReturningSelection, jt as MigrationMigrateOne, k as ConvexDateBuilderInitial, ki as NotNull, kn as ConvexIndexBuilderOn, kr as ReturningAll, kt as MigrationDriftIssue, l as getTableColumns, li as like, ln as ConvexDeletionConfig, lr as CountResult, lt as scheduledMutationBatchFactory, m as vector, mi as notBetween, mn as OrmLifecycleOperation, mr as InferModelFromColumns, mt as OrmBeforeResult, n as defineSchema, ni as gt, nn as RelationsBuilderColumnBase, nr as text, nt as OrmClientWithApi$1, o as asc, oi as isFieldReference, on as defineRelations, or as BuildQueryResult, ot as ResolveOrmSchema, p as ConvexVectorBuilderInitial, pi as not, pn as OrmLifecycleChange, pr as InferInsertModel, pt as defineSchemaExtension, q as bigint, qn as ConvexForeignKeyConfig, qr as FilterExpression, qt as RlsContext, r as getSchemaRelations, ri as gte, rn as RelationsBuilderColumnConfig, rr as AggregateConfig, rt as OrmFunctions, s as desc, si as isNotNull, sn as defineRelationsPart, sr as BuildRelationResult, st as createOrm, t as WhereClauseResult, ti as fieldRef, tn as RelationsBuilder, tr as ConvexTextBuilderInitial, tt as OrmClientBase$1, u as getTableConfig, ui as lt, un as ConvexTable, ur as DBQueryConfig, ut as ScheduledDeleteArgs, v as timestamp, vi as AnyColumn, vn as discriminator, vr as MutationExecuteResult, vt as OrmTriggers, w as integer, wi as ColumnDataType, wn as RlsRoleConfig, wr as MutationRunMode, wt as MigrationAppliedState, x as textEnum, xi as ColumnBuilderRuntimeConfig, xn as RlsPolicyToOption, xr as MutationPaginatedResult, xt as MigrationRunArgs, y as ConvexTextEnumBuilder, yi as ColumnBuilder, yn as RlsPolicy, yr as MutationExecutionMode, yt as defineTriggers, z as ConvexBytesBuilder, zn as index, zr as OrmSchemaExtensions, zt as buildMigrationPlan } from "../where-clause-compiler-WF9UcrAB.js";
1
+ import { $ as GenericOrmCtx$1, $n as unique, $r as endsWith, $t as ManyConfig, A as ConvexDateMode, An as ConvexRankIndexBuilder, Ar as ReturningResult, At as MigrationManifestEntry, B as ConvexBytesBuilderInitial, Bn as rankIndex, Br as OrmSchemaRelations, Bt as defineMigration, C as ConvexNumberBuilderInitial, Ci as ColumnBuilderWithTableName, Cn as RlsRole, Cr as MutationReturning, Ct as MigrationStatusArgs, D as id, Di as IsPrimaryKey, Dn as ConvexAggregateIndexBuilderOn, Dr as PaginatedResult, Dt as MigrationDoc, E as ConvexIdBuilderInitial, Ei as HasDefault, En as ConvexAggregateIndexBuilder, Er as OrderDirection, Et as MigrationDirection, F as custom, Fn as ConvexVectorIndexBuilder, Fr as unsetToken, Ft as MigrationStateMap, G as ConvexBigIntBuilder, Gn as ConvexCheckConfig, Gr as ExpressionVisitor, Gt as OrmReader$1, H as ConvexBooleanBuilder, Hn as uniqueIndex, Hr as TableName, Ht as detectMigrationDrift, I as json, In as ConvexVectorIndexBuilderOn, Ir as Brand, It as MigrationStep, J as CountBackfillChunkArgs, Jn as ConvexUniqueConstraintBuilder, Jr as LogicalExpression, Jt as RlsMode, K as ConvexBigIntBuilderInitial, Kn as ConvexForeignKeyBuilder, Kr as FieldReference, Kt as OrmWriter$1, L as objectOf, Ln as ConvexVectorIndexConfig, Lr as Columns, Lt as MigrationTableName, M as ConvexCustomBuilder, Mn as ConvexSearchIndexBuilder, Mr as UpdateSet, Mt as MigrationPlan, N as ConvexCustomBuilderInitial, Nn as ConvexSearchIndexBuilderOn, Nr as VectorQueryConfig, Nt as MigrationRunStatus, O as ConvexDateBuilder, Oi as IsUnique, On as ConvexIndexBuilder, Or as PredicateWhereIndexConfig, Ot as MigrationDocContext, P as arrayOf, Pn as ConvexSearchIndexConfig, Pr as VectorSearchProvider, Pt as MigrationSet, Q as GenericOrm$1, Qn as foreignKey, Qr as contains, Qt as ExtractTablesWithRelations, R as unionOf, Rn as aggregateIndex, Rr as OrmSchemaExtensionTables, Rt as MigrationWriteMode, S as ConvexNumberBuilder, Si as ColumnBuilderTypeConfig, Sn as rlsPolicy, Sr as MutationResult, St as MigrationRunChunkArgs, T as ConvexIdBuilder, Ti as DrizzleEntity, Tn as rlsRole, Tr as OrderByClause, Tt as MigrationDefinition, U as ConvexBooleanBuilderInitial, Un as vectorIndex, Ur as SystemFields, Ut as DatabaseWithMutations, V as bytes, Vn as searchIndex, Vr as OrmSchemaTriggers, Vt as defineMigrationSet, W as boolean, Wn as ConvexCheckBuilder, Wr as BinaryExpression, Wt as DatabaseWithQuery, X as CountBackfillStatusArgs, Xn as ConvexUniqueConstraintConfig, Xr as and, Xt as extractRelationsConfig, Y as CountBackfillKickoffArgs, Yn as ConvexUniqueConstraintBuilderOn, Yr as UnaryExpression, Yt as EdgeMetadata, Z as CreateOrmOptions, Zn as check, Zr as between, Zt as ExtractTablesFromSchema, _ as ConvexTimestampMode, _i as startsWith, _n as deletion, _r as MutationExecuteConfig, _t as OrmTriggerContext, a as requireSchemaRelations, ai as inArray, an as TablesRelationalConfig, ar as AggregateResult, at as OrmWriterCtx, b as ConvexTextEnumBuilderInitial, bi as ColumnBuilderBaseConfig, bn as RlsPolicyConfig, br as MutationPaginateConfig, bt as MigrationCancelArgs, c as TableConfigResult, ci as isNull, cn as ConvexDeletionBuilder, cr as CountConfig, ct as ScheduledMutationBatchArgs, d as OrmNotFoundError, di as lte, dn as ConvexTableWithColumns, dr as FilterOperators, dt as scheduledDeleteFactory, ei as eq, en as OneConfig, er as ConvexTextBuilder, et as OrmApiResult, f as ConvexVectorBuilder, fi as ne, fn as DiscriminatorBuilderConfig, fr as GetColumnData, ft as SchemaExtension, g as ConvexTimestampBuilderInitial, gi as or, gn as convexTable, gr as InsertValue, gt as OrmTriggerChange, h as ConvexTimestampBuilder, hi as notInArray, hn as TableConfig, hr as InferSelectModel, ht as OrmTableTriggers, i as getSchemaTriggers, ii as ilike, in as TableRelationalConfig, ir as AggregateFieldValue, it as OrmReaderCtx, j as date, jn as ConvexRankIndexBuilderOn, jr as ReturningSelection, jt as MigrationMigrateOne, k as ConvexDateBuilderInitial, ki as NotNull, kn as ConvexIndexBuilderOn, kr as ReturningAll, kt as MigrationDriftIssue, l as getTableColumns, li as like, ln as ConvexDeletionConfig, lr as CountResult, lt as scheduledMutationBatchFactory, m as vector, mi as notBetween, mn as OrmLifecycleOperation, mr as InferModelFromColumns, mt as OrmBeforeResult, n as defineSchema, ni as gt, nn as RelationsBuilderColumnBase, nr as text, nt as OrmClientWithApi$1, o as asc, oi as isFieldReference, on as defineRelations, or as BuildQueryResult, ot as ResolveOrmSchema, p as ConvexVectorBuilderInitial, pi as not, pn as OrmLifecycleChange, pr as InferInsertModel, pt as defineSchemaExtension, q as bigint, qn as ConvexForeignKeyConfig, qr as FilterExpression, qt as RlsContext, r as getSchemaRelations, ri as gte, rn as RelationsBuilderColumnConfig, rr as AggregateConfig, rt as OrmFunctions, s as desc, si as isNotNull, sn as defineRelationsPart, sr as BuildRelationResult, st as createOrm, t as WhereClauseResult, ti as fieldRef, tn as RelationsBuilder, tr as ConvexTextBuilderInitial, tt as OrmClientBase$1, u as getTableConfig, ui as lt, un as ConvexTable, ur as DBQueryConfig, ut as ScheduledDeleteArgs, v as timestamp, vi as AnyColumn, vn as discriminator, vr as MutationExecuteResult, vt as OrmTriggers, w as integer, wi as ColumnDataType, wn as RlsRoleConfig, wr as MutationRunMode, wt as MigrationAppliedState, x as textEnum, xi as ColumnBuilderRuntimeConfig, xn as RlsPolicyToOption, xr as MutationPaginatedResult, xt as MigrationRunArgs, y as ConvexTextEnumBuilder, yi as ColumnBuilder, yn as RlsPolicy, yr as MutationExecutionMode, yt as defineTriggers, z as ConvexBytesBuilder, zn as index, zr as OrmSchemaExtensions, zt as buildMigrationPlan } from "../where-clause-compiler-C8UKgzTO.js";
2
2
  import { i as pretendRequired, n as deprecated, r as pretend } from "../validators-BhsByJeg.js";
3
3
  import { a as QueryCtxWithPreferredOrmQueryTable, i as QueryCtxWithOrmQueryTable, n as LookupByIdResultByCtx, o as getByIdWithOrmQueryFallback, r as QueryCtxWithOptionalOrmQueryTable, t as DocByCtx } from "../query-context-CNo9ffvI.js";
4
4
  import { DefineSchemaOptions, GenericDatabaseReader, GenericDatabaseWriter, GenericSchema, SchemaDefinition } from "convex/server";
package/dist/orm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { A as vectorIndex, B as ConvexColumnBuilder, C as RlsPolicy, D as rankIndex, E as index, F as arrayOf, I as custom, L as json, M as createSystemFields, N as integer, O as searchIndex, P as id, R as objectOf, S as TablePolymorphic, T as aggregateIndex, V as entityKind, _ as OrmSchemaRelations, a as deletion, b as TableDeleteConfig, c as Columns, d as OrmSchemaDefinition, f as OrmSchemaExtensionRelations, g as OrmSchemaOptions, h as OrmSchemaExtensions, i as convexTable, j as text, k as uniqueIndex, l as EnableRLS, m as OrmSchemaExtensionTriggers, o as discriminator, p as OrmSchemaExtensionTables, s as Brand, t as DirectAggregate, u as OrmContext, v as OrmSchemaTriggers, w as rlsPolicy, x as TableName, y as RlsPolicies, z as unionOf } from "../runtime-DmVSOe24.js";
2
2
  import { a as pretendRequired, i as pretend, n as deprecated } from "../validators-C7LelqTN.js";
3
- import { A as ne, C as inArray, D as like, E as isNull, F as notLike, I as or, L as startsWith, M as notBetween, N as notIlike, O as lt, P as notInArray, S as ilike, T as isNotNull, _ as endsWith, a as mergedStream, b as gt, c as isUnsetToken, d as arrayContained, f as arrayContains, g as contains, h as column, i as getIndexFields, j as not, k as lte, l as unsetToken, m as between, n as EmptyStream, o as stream, p as arrayOverlaps, r as QueryStream, s as streamIndexRange, t as getByIdWithOrmQueryFallback, u as and, v as eq, w as isFieldReference, x as gte, y as fieldRef } from "../query-context-ydn9kb6P.js";
3
+ import { A as ne, C as inArray, D as like, E as isNull, F as notLike, I as or, L as startsWith, M as notBetween, N as notIlike, O as lt, P as notInArray, S as ilike, T as isNotNull, _ as endsWith, a as mergedStream, b as gt, c as isUnsetToken, d as arrayContained, f as arrayContains, g as contains, h as column, i as getIndexFields, j as not, k as lte, l as unsetToken, m as between, n as EmptyStream, o as stream, p as arrayOverlaps, r as QueryStream, s as streamIndexRange, t as getByIdWithOrmQueryFallback, u as and, v as eq, w as isFieldReference, x as gte, y as fieldRef } from "../query-context-B47_3n97.js";
4
4
  import { v } from "convex/values";
5
5
  import { defineSchema as defineSchema$1, internalActionGeneric, internalMutationGeneric } from "convex/server";
6
6
 
@@ -3849,11 +3849,11 @@ var WhereClauseCompiler = class {
3849
3849
  const selectedIndex = this.findLeadingIndex(operand.fieldName);
3850
3850
  if (!selectedIndex) return null;
3851
3851
  return {
3852
- strategy: "singleIndex",
3852
+ strategy: "rangeIndex",
3853
3853
  selectedIndex,
3854
- indexFilters: [eq(fieldRef(operand.fieldName), null)],
3854
+ indexFilters: [lte(fieldRef(operand.fieldName), null)],
3855
3855
  probeFilters: [],
3856
- postFilters: []
3856
+ postFilters: [expression]
3857
3857
  };
3858
3858
  }
3859
3859
  tryCompileIsNotNull(expression) {
@@ -4255,6 +4255,24 @@ var WhereClauseCompiler = class {
4255
4255
 
4256
4256
  //#endregion
4257
4257
  //#region src/orm/query.ts
4258
+ /**
4259
+ * Operators that Convex's `.filter()` cannot express, so `_toConvexExpression`
4260
+ * compiles them to a `() => true` placeholder and `_evaluatePostFetchFilter`
4261
+ * applies the real predicate in JavaScript after the rows are read.
4262
+ * Keep in sync with the string/array cases in `_toConvexExpression`.
4263
+ */
4264
+ const POST_FETCH_ONLY_OPERATORS = new Set([
4265
+ "like",
4266
+ "ilike",
4267
+ "notLike",
4268
+ "notIlike",
4269
+ "startsWith",
4270
+ "endsWith",
4271
+ "contains",
4272
+ "arrayContains",
4273
+ "arrayContained",
4274
+ "arrayOverlaps"
4275
+ ]);
4258
4276
  const DEFAULT_RELATION_FAN_OUT_MAX_KEYS = 1e3;
4259
4277
  const DEFAULT_AGGREGATE_CARTESIAN_MAX_KEYS = 4096;
4260
4278
  const DEFAULT_AGGREGATE_WORK_BUDGET = 16384;
@@ -4531,6 +4549,28 @@ var GelRelationalQuery = class extends QueryPromise {
4531
4549
  return targetValue === targetPattern;
4532
4550
  }
4533
4551
  /**
4552
+ * True when the expression can be fully enforced by Convex's own `.filter()`.
4553
+ *
4554
+ * `_toConvexExpression` compiles the string/array operators to `() => true`
4555
+ * because Convex filters cannot run JavaScript string methods; those are
4556
+ * evaluated later by `_evaluatePostFetchFilter`. Pushing such an expression
4557
+ * into `.filter()` is not merely useless, it is wrong in two ways: a `take()`
4558
+ * downstream spends its budget on rows that have not been filtered yet, and a
4559
+ * surrounding `NOT` turns the `true` placeholder into `q.not(true)`, which
4560
+ * matches nothing. So the caller must know whether Convex can carry the whole
4561
+ * expression before relying on it.
4562
+ */
4563
+ _isConvexEnforceableFilter(filter) {
4564
+ if (filter.type === "binary") return !POST_FETCH_ONLY_OPERATORS.has(filter.operator);
4565
+ if (filter.type === "unary") {
4566
+ const [operand] = filter.operands;
4567
+ if (isFieldReference(operand)) return true;
4568
+ return this._isConvexEnforceableFilter(operand);
4569
+ }
4570
+ if (filter.type === "logical") return filter.operands.every((operand) => this._isConvexEnforceableFilter(operand));
4571
+ return true;
4572
+ }
4573
+ /**
4534
4574
  * Evaluate a filter expression against a fetched row
4535
4575
  * Used for post-fetch filtering (string operators, etc.)
4536
4576
  */
@@ -5340,6 +5380,38 @@ var GelRelationalQuery = class extends QueryPromise {
5340
5380
  });
5341
5381
  return streamQuery;
5342
5382
  }
5383
+ /**
5384
+ * Stream equivalent of the `db.query(...)` chain, used when a post-fetch
5385
+ * filter has to run in JavaScript before `limit` or a page boundary can be
5386
+ * applied. `filterWith` evaluates the predicate as rows are pulled, so
5387
+ * `take`/`paginate` size by matches instead of by scanned rows.
5388
+ *
5389
+ * It deliberately mirrors the index and order decisions the caller already
5390
+ * made for the plain query rather than re-deriving them, so switching to the
5391
+ * stream cannot change which index is scanned or in what direction.
5392
+ *
5393
+ * Returns null when the schema definition needed by `stream()` is missing;
5394
+ * the caller then falls back to its plain-query path.
5395
+ */
5396
+ _buildResidualFilterStream(params) {
5397
+ const schemaDefinition = this.schema[OrmSchemaDefinition];
5398
+ if (!schemaDefinition) return null;
5399
+ const { queryConfig, configuredIndex, membershipFilter, orderIndexName, primaryOrder, fallbackOrder } = params;
5400
+ let streamQuery = stream(this.db, schemaDefinition).query(this.tableConfig.name);
5401
+ if (queryConfig.index) streamQuery = streamQuery.withIndex(queryConfig.index.name, (q) => {
5402
+ let indexQuery = q;
5403
+ for (const filter of queryConfig.index.filters) indexQuery = this._applyFilterToQuery(indexQuery, filter);
5404
+ return indexQuery;
5405
+ });
5406
+ else if (configuredIndex?.name) streamQuery = streamQuery.withIndex(configuredIndex.name, configuredIndex.range ? configuredIndex.range : (q) => q);
5407
+ else if (orderIndexName) streamQuery = streamQuery.withIndex(orderIndexName, (q) => q);
5408
+ streamQuery = streamQuery.order(primaryOrder ? primaryOrder.direction : fallbackOrder);
5409
+ streamQuery = streamQuery.filterWith(async (row) => {
5410
+ for (const filter of queryConfig.postFilters) if (!this._evaluatePostFetchFilter(row, filter)) return false;
5411
+ return membershipFilter ? await membershipFilter(row) : true;
5412
+ });
5413
+ return streamQuery;
5414
+ }
5343
5415
  _buildUnionSourceStream(source, fallbackOrder) {
5344
5416
  const configuredIndex = this.configuredIndex;
5345
5417
  this._assertWhereIndexRequirement({
@@ -6652,6 +6724,12 @@ var GelRelationalQuery = class extends QueryPromise {
6652
6724
  const selectedRows = await this._finalizeRows(rows);
6653
6725
  return this._returnSelectedRows(selectedRows);
6654
6726
  }
6727
+ const matchesPostFetchMembership = async (row) => {
6728
+ const visibleRows = await this._applyRlsSelectFilter([row], this.tableConfig);
6729
+ if (visibleRows.length === 0) return false;
6730
+ if (!whereFilter) return true;
6731
+ return (await this._applyRelationsFilterToRows(visibleRows, this.tableConfig, whereFilter, this.edgeMetadata, 0, 3, this.config.with)).length > 0;
6732
+ };
6655
6733
  if (isCursorPaginated) {
6656
6734
  if (queryConfig.strategy === "multiProbe") if (maxScan === void 0) {
6657
6735
  if (strict) throw new Error("Pagination with multi-probe index-union filters requires maxScan when strict=true. Add maxScan or make the query indexable.");
@@ -6716,9 +6794,40 @@ var GelRelationalQuery = class extends QueryPromise {
6716
6794
  splitCursor: paginationResult.splitCursor
6717
6795
  };
6718
6796
  }
6719
- if (queryConfig.postFilters.length > 0) query = query.filter((q) => {
6797
+ const convexPostFilters = queryConfig.postFilters.filter((filter) => this._isConvexEnforceableFilter(filter));
6798
+ const hasResidualPostFilter = convexPostFilters.length !== queryConfig.postFilters.length;
6799
+ const residualPageStream = hasResidualPostFilter ? this._buildResidualFilterStream({
6800
+ queryConfig,
6801
+ configuredIndex,
6802
+ membershipFilter: matchesPostFetchMembership,
6803
+ orderIndexName,
6804
+ primaryOrder,
6805
+ fallbackOrder: "desc"
6806
+ }) : null;
6807
+ if (hasResidualPostFilter && !residualPageStream) this._getSchemaDefinitionOrThrow();
6808
+ if (residualPageStream) {
6809
+ if (queryConfig.order && primaryOrder) {
6810
+ if (needsPostFetchSortForPrimary && strict) throw new Error(`Pagination: Field '${primaryOrder.field}' has no index. Add an index or disable strict.`);
6811
+ if (needsPostFetchSortForPrimary) console.warn(`Pagination: Field '${primaryOrder.field}' has no index. Falling back to _creationTime ordering.`);
6812
+ if (hasSecondaryOrders) console.warn("Pagination: Only the first orderBy field is used for cursor ordering. Secondary orderBy fields are applied per page and may be unstable across pages.");
6813
+ }
6814
+ const paginationResult = await residualPageStream.paginate({
6815
+ cursor: cursor ?? null,
6816
+ endCursor: endCursor ?? void 0,
6817
+ limit: config.limit,
6818
+ maxScan
6819
+ });
6820
+ return {
6821
+ page: await this._finalizeRows(paginationResult.page),
6822
+ continueCursor: paginationResult.continueCursor,
6823
+ isDone: paginationResult.isDone,
6824
+ pageStatus: paginationResult.pageStatus,
6825
+ splitCursor: paginationResult.splitCursor
6826
+ };
6827
+ }
6828
+ if (convexPostFilters.length > 0) query = query.filter((q) => {
6720
6829
  let result = null;
6721
- for (const filter of queryConfig.postFilters) {
6830
+ for (const filter of convexPostFilters) {
6722
6831
  const expr = this._toConvexExpression(filter)(q);
6723
6832
  result = result ? q.and(result, expr) : expr;
6724
6833
  }
@@ -6745,9 +6854,11 @@ var GelRelationalQuery = class extends QueryPromise {
6745
6854
  isDone: paginationResult.isDone
6746
6855
  };
6747
6856
  }
6748
- if (queryConfig.postFilters.length > 0) query = query.filter((q) => {
6857
+ const convexPostFilters = queryConfig.postFilters.filter((filter) => this._isConvexEnforceableFilter(filter));
6858
+ const hasResidualPostFilter = convexPostFilters.length !== queryConfig.postFilters.length;
6859
+ if (convexPostFilters.length > 0) query = query.filter((q) => {
6749
6860
  let result = null;
6750
- for (const filter of queryConfig.postFilters) {
6861
+ for (const filter of convexPostFilters) {
6751
6862
  const expr = this._toConvexExpression(filter)(q);
6752
6863
  result = result ? q.and(result, expr) : expr;
6753
6864
  }
@@ -6757,11 +6868,29 @@ var GelRelationalQuery = class extends QueryPromise {
6757
6868
  if (typeof offset !== "number") throw new Error("Only numeric offset is supported in kitcn ORM.");
6758
6869
  const limit = this._resolveNonPaginatedLimit(config);
6759
6870
  const paginateAfterPostFetchSort = usePostFetchSort && postFetchOrders.length > 0;
6760
- let rows = limit === void 0 || paginateAfterPostFetchSort ? await query.collect() : await query.take(offset > 0 ? offset + limit : limit);
6761
- if (!paginateAfterPostFetchSort && offset > 0) rows = rows.slice(offset);
6871
+ const sizeAfterPostFilter = hasResidualPostFilter && !paginateAfterPostFetchSort;
6872
+ const residualLimitStream = sizeAfterPostFilter && limit !== void 0 ? this._buildResidualFilterStream({
6873
+ queryConfig,
6874
+ configuredIndex,
6875
+ membershipFilter: matchesPostFetchMembership,
6876
+ orderIndexName,
6877
+ primaryOrder,
6878
+ fallbackOrder: "asc"
6879
+ }) : null;
6880
+ let rows;
6881
+ if (residualLimitStream) rows = await residualLimitStream.take(offset > 0 ? offset + limit : limit);
6882
+ else if (limit === void 0 || paginateAfterPostFetchSort || hasResidualPostFilter) rows = await query.collect();
6883
+ else rows = await query.take(offset > 0 ? offset + limit : limit);
6884
+ if (!(paginateAfterPostFetchSort || sizeAfterPostFilter) && offset > 0) rows = rows.slice(offset);
6762
6885
  if (queryConfig.postFilters.length > 0) rows = rows.filter((row) => queryConfig.postFilters.every((filter) => this._evaluatePostFetchFilter(row, filter)));
6763
- rows = await this._applyRlsSelectFilter(rows, this.tableConfig);
6764
- if (whereFilter) rows = await this._applyRelationsFilterToRows(rows, this.tableConfig, whereFilter, this.edgeMetadata, 0, 3, this.config.with);
6886
+ if (!residualLimitStream) {
6887
+ rows = await this._applyRlsSelectFilter(rows, this.tableConfig);
6888
+ if (whereFilter) rows = await this._applyRelationsFilterToRows(rows, this.tableConfig, whereFilter, this.edgeMetadata, 0, 3, this.config.with);
6889
+ }
6890
+ if (sizeAfterPostFilter) {
6891
+ if (offset > 0) rows = rows.slice(offset);
6892
+ if (limit !== void 0) rows = rows.slice(0, limit);
6893
+ }
6765
6894
  if (usePostFetchSort && postFetchOrders.length > 0) rows = rows.sort((a, b) => this._compareByOrderSpecs(a, b, postFetchOrders));
6766
6895
  if (paginateAfterPostFetchSort) {
6767
6896
  if (offset > 0) rows = rows.slice(offset);
@@ -10122,6 +10251,8 @@ function scheduledDeleteFactory(schema, edgeMetadata, scheduledMutationBatch) {
10122
10251
 
10123
10252
  //#endregion
10124
10253
  //#region src/orm/scheduled-mutation-batch.ts
10254
+ /** Column stamped by `softDeleteRow`; see mutation-utils.ts. */
10255
+ const DELETION_TIME_FIELD = "deletionTime";
10125
10256
  const isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
10126
10257
  function scheduledMutationBatchFactory(schema, edgeMetadata, scheduledMutationBatch) {
10127
10258
  const tableByName = /* @__PURE__ */ new Map();
@@ -10185,12 +10316,17 @@ function scheduledMutationBatchFactory(schema, edgeMetadata, scheduledMutationBa
10185
10316
  const targetValues = decodeUndefinedDeep(args.targetValues);
10186
10317
  if (!targetValues || !Array.isArray(targetValues)) throw new Error("scheduledMutationBatch: targetValues are required for cascade work.");
10187
10318
  if (!args.foreignIndexName) throw new Error("scheduledMutationBatch: foreignIndexName is required for cascade work.");
10188
- const queryWithIndex = () => ctx.db.query(args.table).withIndex(args.foreignIndexName, (q) => {
10189
- let builder = q.eq(sourceColumns[0], targetValues[0]);
10190
- for (let i = 1; i < sourceColumns.length; i += 1) builder = builder.eq(sourceColumns[i], targetValues[i]);
10191
- return builder;
10192
- });
10193
10319
  const action = args.foreignAction ?? "no action";
10320
+ const skipSoftDeletedRows = workType === "cascade-delete" && action === "cascade" && (args.cascadeMode ?? "hard") === "soft";
10321
+ const queryWithIndex = () => {
10322
+ const indexed = ctx.db.query(args.table).withIndex(args.foreignIndexName, (q) => {
10323
+ let builder = q.eq(sourceColumns[0], targetValues[0]);
10324
+ for (let i = 1; i < sourceColumns.length; i += 1) builder = builder.eq(sourceColumns[i], targetValues[i]);
10325
+ return builder;
10326
+ });
10327
+ if (!skipSoftDeletedRows) return indexed;
10328
+ return indexed.filter((q) => q.or(q.eq(q.field(DELETION_TIME_FIELD), void 0), q.eq(q.field(DELETION_TIME_FIELD), null)));
10329
+ };
10194
10330
  const paged = await queryWithIndex().paginate({
10195
10331
  cursor: null,
10196
10332
  numItems: args.batchSize
@@ -1136,7 +1136,7 @@ var FlatMapStreamIterator = class {
1136
1136
  let innerStream;
1137
1137
  if (t === null) innerStream = this.singletonSkipInnerStream();
1138
1138
  else {
1139
- innerStream = await this.#mapper(t);
1139
+ innerStream = await this.#mapper(t, indexKey);
1140
1140
  if (!equalIndexFields(innerStream.getIndexFields(), this.#mappedIndexFields)) throw new Error(`FlatMapStream: inner stream has different index fields than expected: ${JSON.stringify(innerStream.getIndexFields())} vs ${JSON.stringify(this.#mappedIndexFields)}`);
1141
1141
  if (innerStream.getOrder() !== this.#outerStream.getOrder()) throw new Error(`FlatMapStream: inner stream has different order than outer stream: ${innerStream.getOrder()} vs ${this.#outerStream.getOrder()}`);
1142
1142
  }
@@ -1210,14 +1210,32 @@ var FlatMapStream = class FlatMapStream extends QueryStream {
1210
1210
  upperBound: outerUpperBound,
1211
1211
  upperBoundInclusive: innerUpperBound.length === 0 ? indexBounds.upperBoundInclusive : true
1212
1212
  };
1213
- const innerIndexBounds = {
1214
- lowerBound: innerLowerBound,
1215
- lowerBoundInclusive: innerLowerBound.length === 0 ? true : indexBounds.lowerBoundInclusive,
1216
- upperBound: innerUpperBound,
1217
- upperBoundInclusive: innerUpperBound.length === 0 ? true : indexBounds.upperBoundInclusive
1213
+ const unrestricted = {
1214
+ lowerBound: [],
1215
+ lowerBoundInclusive: true,
1216
+ upperBound: [],
1217
+ upperBoundInclusive: true
1218
1218
  };
1219
- return new FlatMapStream(this.#stream.narrow(outerIndexBounds), async (t) => {
1220
- return (await this.#mapper(t)).narrow(innerIndexBounds);
1219
+ const isBoundaryParent = (parentKey, bound) => bound.length === outerLength && compareKeys({
1220
+ value: parentKey,
1221
+ kind: "exact"
1222
+ }, {
1223
+ value: bound,
1224
+ kind: "exact"
1225
+ }) === 0;
1226
+ const innerBoundsForParent = (parentKey) => {
1227
+ const atLowerBound = innerLowerBound.length > 0 && isBoundaryParent(parentKey, outerLowerBound);
1228
+ const atUpperBound = innerUpperBound.length > 0 && isBoundaryParent(parentKey, outerUpperBound);
1229
+ if (!(atLowerBound || atUpperBound)) return unrestricted;
1230
+ return {
1231
+ lowerBound: atLowerBound ? innerLowerBound : unrestricted.lowerBound,
1232
+ lowerBoundInclusive: atLowerBound ? indexBounds.lowerBoundInclusive : true,
1233
+ upperBound: atUpperBound ? innerUpperBound : unrestricted.upperBound,
1234
+ upperBoundInclusive: atUpperBound ? indexBounds.upperBoundInclusive : true
1235
+ };
1236
+ };
1237
+ return new FlatMapStream(this.#stream.narrow(outerIndexBounds), async (t, parentKey) => {
1238
+ return (await this.#mapper(t, parentKey)).narrow(innerBoundsForParent(parentKey));
1221
1239
  }, this.#mappedIndexFields);
1222
1240
  }
1223
1241
  };
@@ -2436,6 +2436,19 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
2436
2436
  private _compareByOrderSpecs;
2437
2437
  private _getTableConfigByDbName;
2438
2438
  private _matchLike;
2439
+ /**
2440
+ * True when the expression can be fully enforced by Convex's own `.filter()`.
2441
+ *
2442
+ * `_toConvexExpression` compiles the string/array operators to `() => true`
2443
+ * because Convex filters cannot run JavaScript string methods; those are
2444
+ * evaluated later by `_evaluatePostFetchFilter`. Pushing such an expression
2445
+ * into `.filter()` is not merely useless, it is wrong in two ways: a `take()`
2446
+ * downstream spends its budget on rows that have not been filtered yet, and a
2447
+ * surrounding `NOT` turns the `true` placeholder into `q.not(true)`, which
2448
+ * matches nothing. So the caller must know whether Convex can carry the whole
2449
+ * expression before relying on it.
2450
+ */
2451
+ private _isConvexEnforceableFilter;
2439
2452
  /**
2440
2453
  * Evaluate a filter expression against a fetched row
2441
2454
  * Used for post-fetch filtering (string operators, etc.)
@@ -2471,6 +2484,20 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
2471
2484
  private _createFilterOperators;
2472
2485
  private _resolveWhereCallbackExpression;
2473
2486
  private _buildBasePipelineStream;
2487
+ /**
2488
+ * Stream equivalent of the `db.query(...)` chain, used when a post-fetch
2489
+ * filter has to run in JavaScript before `limit` or a page boundary can be
2490
+ * applied. `filterWith` evaluates the predicate as rows are pulled, so
2491
+ * `take`/`paginate` size by matches instead of by scanned rows.
2492
+ *
2493
+ * It deliberately mirrors the index and order decisions the caller already
2494
+ * made for the plain query rather than re-deriving them, so switching to the
2495
+ * stream cannot change which index is scanned or in what direction.
2496
+ *
2497
+ * Returns null when the schema definition needed by `stream()` is missing;
2498
+ * the caller then falls back to its plain-query path.
2499
+ */
2500
+ private _buildResidualFilterStream;
2474
2501
  private _buildUnionSourceStream;
2475
2502
  private _applyFlatMapStage;
2476
2503
  private _applyPipelineStages;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitcn",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "description": "kitcn - React Query integration and CLI tools for Convex",
5
5
  "keywords": [
6
6
  "convex",