kitcn 0.26.1 → 0.26.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # kitcn
2
2
 
3
+ ## 0.26.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#394](https://github.com/udecode/kitcn/pull/394) [`1317349`](https://github.com/udecode/kitcn/commit/13173495fa8db3d8a0568642981c1cdef4dcdf2b) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Features
8
+
9
+ - Support a per-source `index: { name, range }` on `select().union([...])`, so each source walks its own index range instead of re-walking one shared range and filtering the misses in JS.
10
+
11
+ ```ts
12
+ const page = await db.query.messages
13
+ .select()
14
+ .union([
15
+ {
16
+ index: {
17
+ name: "by_from_to",
18
+ range: (q) => q.eq("from", me).eq("to", them),
19
+ },
20
+ },
21
+ {
22
+ index: {
23
+ name: "by_from_to",
24
+ range: (q) => q.eq("from", them).eq("to", me),
25
+ },
26
+ },
27
+ ])
28
+ .interleaveBy(["createdAt", "id"])
29
+ .paginate({ cursor: null, limit: 20 });
30
+ ```
31
+
32
+ - Support union sources anchored on different indexes, as long as each source pins its leading fields with `eq` and ends up ordered by the `interleaveBy` fields.
33
+
34
+ ## 0.26.2
35
+
36
+ ### Patch Changes
37
+
38
+ - [#393](https://github.com/udecode/kitcn/pull/393) [`5ebba20`](https://github.com/udecode/kitcn/commit/5ebba205900489ab204901f5487788a60b9d0dd4) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
39
+
40
+ - Speed up `kitcn codegen`. Each Convex module is now read once per run instead
41
+ of up to four times, and the functions directory is listed once instead of
42
+ twice. On an 82-module app that is 57 fewer file reads and 10 fewer directory
43
+ listings per run, with identical generated output.
44
+
3
45
  ## 0.26.1
4
46
 
5
47
  ### Patch Changes
@@ -1,5 +1,5 @@
1
- import { Bn as ConvexTextBuilderInitial, Xt as ConvexTableWithColumns } from "../capabilities-DtDfpdcH.js";
2
- import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-3fKontPx.js";
1
+ import { Bn as ConvexTextBuilderInitial, Xt as ConvexTableWithColumns } from "../capabilities-QNGgWHQd.js";
2
+ import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-DfFcNKtn.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";
@@ -1430,6 +1430,16 @@ type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y
1430
1430
  type Merge<A, B> = { [K in keyof A | keyof B]: K extends keyof B ? B[K] : K extends keyof A ? A[K] : never };
1431
1431
  type IndexKey = (Value | undefined)[];
1432
1432
  type FindManyUnionSource<TTableConfig extends TableRelationalConfig = TableRelationalConfig> = {
1433
+ /**
1434
+ * Index anchor for this source alone. Overrides the chain-level
1435
+ * `.withIndex(...)`, so each source can walk its own range instead of
1436
+ * re-walking one shared range and discarding the misses in JS.
1437
+ *
1438
+ * Sources may pin different indexes: `interleaveBy` re-orders every source by
1439
+ * the same trailing fields before merging, so what has to match across
1440
+ * sources is that ordering suffix, not the index name.
1441
+ */
1442
+ index?: PredicateWhereIndexConfig<TTableConfig>;
1433
1443
  where?: RelationsFilter<TTableConfig, any> | WhereCallback<TTableConfig>;
1434
1444
  };
1435
1445
  type PipelineRelationName<TTableConfig extends TableRelationalConfig> = Extract<keyof TTableConfig['relations'], string>;
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as Columns, D as TableName, E as RlsPolicies, O as createSystemFields, S as getRankIndexes, T as OrmSchemaExtensions, _ as loadEsbuild, a as generateMeta, b as getAggregateIndexes, c as resolveSchemaDefaultExport, f as createProjectJiti, g as loadDotenv, h as loadClackPrompts, i as resolveConfiguredBackend, l as schemaDeclaresAggregateIndexes, m as loadBabelParser, n as withLocalCodegenEnv, o as getConvexConfig, p as CRPC_BUILDER_STUB_SOURCE, r as loadCliConfig, s as collectSchemaTables, t as getLocalBackendEnvVars, u as logger, v as highlighter, w as EnableRLS, x as getIndexes, y as isColorEnabled } from "./local-env-9Qtfn3wR.mjs";
2
+ import { C as Columns, D as TableName, E as RlsPolicies, O as createSystemFields, S as getRankIndexes, T as OrmSchemaExtensions, _ as loadEsbuild, a as generateMeta, b as getAggregateIndexes, c as resolveSchemaDefaultExport, f as createProjectJiti, g as loadDotenv, h as loadClackPrompts, i as resolveConfiguredBackend, l as schemaDeclaresAggregateIndexes, m as loadBabelParser, n as withLocalCodegenEnv, o as getConvexConfig, p as CRPC_BUILDER_STUB_SOURCE, r as loadCliConfig, s as collectSchemaTables, t as getLocalBackendEnvVars, u as logger, v as highlighter, w as EnableRLS, x as getIndexes, y as isColorEnabled } from "./local-env-Z8jj6ecD.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import fs, { existsSync, readFileSync } from "node:fs";
5
5
  import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve } from "node:path";
@@ -2563,6 +2563,60 @@ function hasPotentialCodegenExports(source, filePath) {
2563
2563
  return false;
2564
2564
  }
2565
2565
 
2566
+ //#endregion
2567
+ //#region src/cli/utils/codegen-file-cache.ts
2568
+ const MISSING_FILE_ERROR_CODES = new Set([
2569
+ "ENOENT",
2570
+ "ENOTDIR",
2571
+ "EISDIR"
2572
+ ]);
2573
+ function isMissingFileError(error) {
2574
+ const code = error?.code;
2575
+ return code !== void 0 && MISSING_FILE_ERROR_CODES.has(code);
2576
+ }
2577
+ function createCodegenFileCache() {
2578
+ const contentsByPath = /* @__PURE__ */ new Map();
2579
+ const read = (filePath) => {
2580
+ const key = path.resolve(filePath);
2581
+ const cached = contentsByPath.get(key);
2582
+ if (cached !== void 0) return cached;
2583
+ let content;
2584
+ try {
2585
+ content = fs.readFileSync(key, "utf8");
2586
+ } catch (error) {
2587
+ if (!isMissingFileError(error)) throw error;
2588
+ content = null;
2589
+ }
2590
+ contentsByPath.set(key, content);
2591
+ return content;
2592
+ };
2593
+ const write = (filePath, content) => {
2594
+ fs.writeFileSync(filePath, content);
2595
+ contentsByPath.set(path.resolve(filePath), content);
2596
+ };
2597
+ return {
2598
+ read,
2599
+ write,
2600
+ writeIfChanged: (filePath, content) => {
2601
+ if (read(filePath) === content) return false;
2602
+ write(filePath, content);
2603
+ return true;
2604
+ },
2605
+ remove: (filePath) => {
2606
+ fs.rmSync(filePath, { force: true });
2607
+ contentsByPath.set(path.resolve(filePath), null);
2608
+ },
2609
+ removeDirectory: (dirPath) => {
2610
+ fs.rmSync(dirPath, {
2611
+ force: true,
2612
+ recursive: true
2613
+ });
2614
+ const prefix = `${path.resolve(dirPath)}${path.sep}`;
2615
+ for (const key of contentsByPath.keys()) if (key.startsWith(prefix)) contentsByPath.set(key, null);
2616
+ }
2617
+ };
2618
+ }
2619
+
2566
2620
  //#endregion
2567
2621
  //#region src/cli/utils/logger.ts
2568
2622
  const joinArgs = (args) => args.map(String).join(" ");
@@ -2827,7 +2881,8 @@ function findProcedureCallIndex(params) {
2827
2881
  }
2828
2882
  function buildProcedureNameLookupEntries(params) {
2829
2883
  if (params.procedures.length === 0) return [];
2830
- const source = fs.readFileSync(params.filePath, "utf8");
2884
+ const source = params.fileCache.read(params.filePath);
2885
+ if (source === null) return [];
2831
2886
  return params.procedures.flatMap((procedure) => {
2832
2887
  const callIndex = findProcedureCallIndex({
2833
2888
  exportName: procedure.exportName,
@@ -2872,9 +2927,9 @@ function extractObjectLiteral(source, startIndex) {
2872
2927
  }
2873
2928
  return null;
2874
2929
  }
2875
- function readLegacyProcedureNameLookup(serverOutputFile) {
2876
- if (!fs.existsSync(serverOutputFile)) return;
2877
- const source = fs.readFileSync(serverOutputFile, "utf8");
2930
+ function readLegacyProcedureNameLookup(fileCache, serverOutputFile) {
2931
+ const source = fileCache.read(serverOutputFile);
2932
+ if (source === null) return;
2878
2933
  const startMatch = LEGACY_PROCEDURE_LOOKUP_START_RE.exec(source);
2879
2934
  if (!startMatch || startMatch.index === void 0) return;
2880
2935
  const literal = extractObjectLiteral(source, startMatch.index + startMatch[0].lastIndexOf("{"));
@@ -3019,11 +3074,11 @@ function getRuntimeApiTypesImportPath(outputFile, functionsDir) {
3019
3074
  const runtimeApiFile = path.join(functionsDir, "_generated", "api");
3020
3075
  return ensureRelativeImportPath(normalizeImportPath(path.relative(path.dirname(outputFile), runtimeApiFile)));
3021
3076
  }
3022
- function moduleUsesOwnGeneratedRuntime(functionsDir, moduleName) {
3077
+ function moduleUsesOwnGeneratedRuntime(fileCache, functionsDir, moduleName) {
3023
3078
  if (moduleName === "generated/server") return true;
3024
3079
  const moduleFilePath = path.join(functionsDir, `${moduleName}.ts`);
3025
- if (!fs.existsSync(moduleFilePath)) return false;
3026
- const source = fs.readFileSync(moduleFilePath, "utf8");
3080
+ const source = fileCache.read(moduleFilePath);
3081
+ if (source === null) return false;
3027
3082
  const escapedRuntimeImportPath = escapeRegex(ensureRelativeImportPath(normalizeImportPath(path.relative(path.dirname(moduleFilePath), path.join(functionsDir, "generated", `${moduleName}.runtime`)))));
3028
3083
  return [new RegExp(`from\\s+['"]${escapedRuntimeImportPath}(?:\\.[jt]s)?['"]`), new RegExp(`require\\(\\s*['"]${escapedRuntimeImportPath}(?:\\.[jt]s)?['"]\\s*\\)`)].some((pattern) => pattern.test(source));
3029
3084
  }
@@ -3159,14 +3214,7 @@ function emitGeneratedMigrationsPlaceholderFile() {
3159
3214
  export { defineMigration } from 'kitcn/orm';
3160
3215
  `;
3161
3216
  }
3162
- function writeFileIfChanged(filePath, content) {
3163
- if (fs.existsSync(filePath)) {
3164
- if (fs.readFileSync(filePath, "utf8") === content) return false;
3165
- }
3166
- fs.writeFileSync(filePath, content);
3167
- return true;
3168
- }
3169
- function ensureGeneratedSupportPlaceholders(functionsDir, options) {
3217
+ function ensureGeneratedSupportPlaceholders(fileCache, functionsDir, options) {
3170
3218
  const createdPlaceholderFiles = [];
3171
3219
  const replacedFiles = [];
3172
3220
  const serverOutputFile = getGeneratedServerOutputFile(functionsDir);
@@ -3176,23 +3224,21 @@ function ensureGeneratedSupportPlaceholders(functionsDir, options) {
3176
3224
  const generatedDir = path.dirname(serverOutputFile);
3177
3225
  fs.mkdirSync(generatedDir, { recursive: true });
3178
3226
  const includeAuth = options?.includeAuth ?? true;
3179
- if (!fs.existsSync(serverOutputFile)) {
3180
- writeFileIfChanged(serverOutputFile, emitGeneratedServerPlaceholderFile(functionsDir));
3227
+ const existingServerContent = fileCache.read(serverOutputFile);
3228
+ if (existingServerContent === null) {
3229
+ fileCache.write(serverOutputFile, emitGeneratedServerPlaceholderFile(functionsDir));
3181
3230
  createdPlaceholderFiles.push(serverOutputFile);
3182
- } else if (options?.replaceServer) {
3183
- const content = fs.readFileSync(serverOutputFile, "utf8");
3184
- if (writeFileIfChanged(serverOutputFile, emitGeneratedServerPlaceholderFile(functionsDir))) replacedFiles.push({
3185
- content,
3186
- filePath: serverOutputFile
3187
- });
3188
- }
3189
- if (!fs.existsSync(procedureNamesOutputFile)) writeFileIfChanged(procedureNamesOutputFile, emitGeneratedProcedureNamesFile(options?.procedureNameLookup ?? {}));
3190
- if (includeAuth && !fs.existsSync(authOutputFile)) {
3191
- writeFileIfChanged(authOutputFile, emitGeneratedAuthPlaceholderFile());
3231
+ } else if (options?.replaceServer && fileCache.writeIfChanged(serverOutputFile, emitGeneratedServerPlaceholderFile(functionsDir))) replacedFiles.push({
3232
+ content: existingServerContent,
3233
+ filePath: serverOutputFile
3234
+ });
3235
+ if (fileCache.read(procedureNamesOutputFile) === null) fileCache.write(procedureNamesOutputFile, emitGeneratedProcedureNamesFile(options?.procedureNameLookup ?? {}));
3236
+ if (includeAuth && fileCache.read(authOutputFile) === null) {
3237
+ fileCache.write(authOutputFile, emitGeneratedAuthPlaceholderFile());
3192
3238
  createdPlaceholderFiles.push(authOutputFile);
3193
3239
  }
3194
- if (!fs.existsSync(migrationsHelperOutputFile)) {
3195
- writeFileIfChanged(migrationsHelperOutputFile, emitGeneratedMigrationsPlaceholderFile());
3240
+ if (fileCache.read(migrationsHelperOutputFile) === null) {
3241
+ fileCache.write(migrationsHelperOutputFile, emitGeneratedMigrationsPlaceholderFile());
3196
3242
  createdPlaceholderFiles.push(migrationsHelperOutputFile);
3197
3243
  }
3198
3244
  return {
@@ -3227,14 +3273,14 @@ export function ${handlerExportName}(_ctx: unknown) {
3227
3273
  }
3228
3274
  `;
3229
3275
  }
3230
- function ensureGeneratedRuntimePlaceholders(functionsDir, moduleNames, runtimeExportNames) {
3276
+ function ensureGeneratedRuntimePlaceholders(fileCache, functionsDir, moduleNames, runtimeExportNames) {
3231
3277
  const createdPlaceholderFiles = [];
3232
3278
  for (const moduleName of moduleNames) {
3233
3279
  const runtimeOutputFile = getGeneratedRuntimeOutputFile(functionsDir, moduleName);
3234
- if (fs.existsSync(runtimeOutputFile)) continue;
3280
+ if (fileCache.read(runtimeOutputFile) !== null) continue;
3235
3281
  const exportNames = runtimeExportNames.get(moduleName) ?? getModuleRuntimeExportNames(moduleName);
3236
3282
  fs.mkdirSync(path.dirname(runtimeOutputFile), { recursive: true });
3237
- writeFileIfChanged(runtimeOutputFile, emitGeneratedRuntimePlaceholderFile(exportNames));
3283
+ fileCache.write(runtimeOutputFile, emitGeneratedRuntimePlaceholderFile(exportNames));
3238
3284
  createdPlaceholderFiles.push(runtimeOutputFile);
3239
3285
  }
3240
3286
  return createdPlaceholderFiles;
@@ -3283,11 +3329,8 @@ function resolveHasAggregateIndexes(schemaValue, schemaPath, debug) {
3283
3329
  return true;
3284
3330
  }
3285
3331
  }
3286
- function cleanupGeneratedPluginArtifacts(functionsDir) {
3287
- fs.rmSync(path.join(functionsDir, GENERATED_DIR, "plugins"), {
3288
- recursive: true,
3289
- force: true
3290
- });
3332
+ function cleanupGeneratedPluginArtifacts(fileCache, functionsDir) {
3333
+ fileCache.removeDirectory(path.join(functionsDir, GENERATED_DIR, "plugins"));
3291
3334
  }
3292
3335
  /**
3293
3336
  * Every input here is known before codegen evaluates any project module, so
@@ -3605,9 +3648,9 @@ function renderRuntimeApiTypesImport(entries, importPath) {
3605
3648
  if (specifiers.length === 1) return `import type { ${specifiers[0]} } from '${importPath}';\n`;
3606
3649
  return `import type {\n${specifiers.map((specifier) => ` ${specifier},\n`).join("")}} from '${importPath}';\n`;
3607
3650
  }
3608
- function emitGeneratedModuleRuntimeFile(outputFile, functionsDir, moduleName, procedureEntries, runtimeExportNames) {
3651
+ function emitGeneratedModuleRuntimeFile(fileCache, outputFile, functionsDir, moduleName, procedureEntries, runtimeExportNames) {
3609
3652
  const { callerExportName, handlerExportName } = runtimeExportNames?.get(moduleName) ?? getModuleRuntimeExportNames(moduleName);
3610
- const useGeneratedApiTypes = moduleUsesOwnGeneratedRuntime(functionsDir, moduleName);
3653
+ const useGeneratedApiTypes = moduleUsesOwnGeneratedRuntime(fileCache, functionsDir, moduleName);
3611
3654
  const runtimeApiTypesImportPath = useGeneratedApiTypes ? getRuntimeApiTypesImportPath(outputFile, functionsDir) : null;
3612
3655
  const generatedServerImportPath = getGeneratedServerImportPath(outputFile, functionsDir);
3613
3656
  const { callerEntries, handlerEntries } = partitionRuntimeEntriesForEmission(procedureEntries);
@@ -3696,9 +3739,9 @@ export function ${callerExportName}<TCtx extends ProcedureCallerContext>(
3696
3739
  ${handlerExport}
3697
3740
  `;
3698
3741
  }
3699
- function hasNamedExport(filePath, exportName) {
3700
- if (!fs.existsSync(filePath)) return false;
3701
- const source = fs.readFileSync(filePath, "utf-8");
3742
+ function hasNamedExport(fileCache, filePath, exportName) {
3743
+ const source = fileCache.read(filePath);
3744
+ if (source === null) return false;
3702
3745
  if (new RegExp(`\\bexport\\s+(?:const|let|var|function|class|type|interface)\\s+${exportName}\\b`).test(source)) return true;
3703
3746
  for (const match of source.matchAll(/\bexport\s*{([^}]*)}/g)) {
3704
3747
  const exportList = match[1] ?? "";
@@ -3706,10 +3749,9 @@ function hasNamedExport(filePath, exportName) {
3706
3749
  }
3707
3750
  return false;
3708
3751
  }
3709
- function hasDefaultExport(filePath) {
3710
- if (!fs.existsSync(filePath)) return false;
3711
- const source = fs.readFileSync(filePath, "utf-8");
3712
- return DEFAULT_EXPORT_RE.test(source);
3752
+ function hasDefaultExport(fileCache, filePath) {
3753
+ const source = fileCache.read(filePath);
3754
+ return source !== null && DEFAULT_EXPORT_RE.test(source);
3713
3755
  }
3714
3756
  function createApiTree(meta) {
3715
3757
  const root = {
@@ -3865,8 +3907,9 @@ function isCRPCHttpRouter(value) {
3865
3907
  /**
3866
3908
  * Import a module using jiti and extract cRPC metadata from exports.
3867
3909
  */
3868
- async function parseModuleRuntime(filePath, jitiInstance, serverShimSpecifier) {
3869
- const source = fs.readFileSync(filePath, "utf8");
3910
+ async function parseModuleRuntime(fileCache, filePath, jitiInstance, serverShimSpecifier) {
3911
+ const source = fileCache.read(filePath);
3912
+ if (source === null) throw new Error(`kitcn codegen could not read ${filePath}`);
3870
3913
  const rewrittenSource = source.replaceAll(/from\s+(['"])kitcn\/server\1/g, `from ${JSON.stringify(serverShimSpecifier)}`);
3871
3914
  const result = {};
3872
3915
  const httpRoutes = {};
@@ -3951,61 +3994,62 @@ async function generateMeta(sharedDir, options) {
3951
3994
  };
3952
3995
  const runtimeFilesPreservedFromParseFailures = /* @__PURE__ */ new Set();
3953
3996
  let totalFunctions = 0;
3997
+ const fileCache = createCodegenFileCache();
3954
3998
  const authFilePath = path.join(functionsDir, "auth.ts");
3955
- const hasAuthFile = fs.existsSync(authFilePath);
3956
- const hasAuthDefaultExport = hasDefaultExport(authFilePath);
3999
+ const hasAuthFile = fileCache.read(authFilePath) !== null;
4000
+ const hasAuthDefaultExport = hasDefaultExport(fileCache, authFilePath);
3957
4001
  const authContract = {
3958
4002
  hasAuthFile,
3959
4003
  hasAuthDefaultExport
3960
4004
  };
3961
4005
  let sharedJitiInstance;
3962
4006
  const getSharedJitiInstance = () => sharedJitiInstance ??= createProjectJiti();
3963
- const schemaRuntimeModules = listFilesRecursive(functionsDir).filter((file) => file.endsWith(".ts") && isValidConvexFile(file)).map((file) => file.replace(TS_EXTENSION_RE, ""));
3964
- const schemaRuntimePlaceholders = ensureGeneratedRuntimePlaceholders(functionsDir, schemaRuntimeModules, resolveModuleRuntimeExportNames(schemaRuntimeModules, normalizedTrimSegments));
4007
+ const convexModuleFiles = listFilesRecursive(functionsDir).filter((file) => file.endsWith(".ts") && isValidConvexFile(file));
4008
+ const schemaRuntimeModules = convexModuleFiles.map((file) => file.replace(TS_EXTENSION_RE, ""));
4009
+ const schemaRuntimePlaceholders = ensureGeneratedRuntimePlaceholders(fileCache, functionsDir, schemaRuntimeModules, resolveModuleRuntimeExportNames(schemaRuntimeModules, normalizedTrimSegments));
3965
4010
  const schemaMetadata = await (async () => {
3966
4011
  try {
3967
4012
  return await withCodegenParseSentinel(() => resolveSchemaMetadataForCodegen(functionsDir, debug, getSharedJitiInstance));
3968
4013
  } finally {
3969
- for (const schemaRuntimePlaceholder of schemaRuntimePlaceholders) fs.rmSync(schemaRuntimePlaceholder, { force: true });
4014
+ for (const schemaRuntimePlaceholder of schemaRuntimePlaceholders) fileCache.remove(schemaRuntimePlaceholder);
3970
4015
  }
3971
4016
  })();
3972
4017
  const hasOrmSchemaMetadata = schemaMetadata.hasOrmSchema;
3973
4018
  const hasRelationsMetadata = schemaMetadata.hasRelations;
3974
- const hasRelationsExport = hasNamedExport(path.join(functionsDir, "schema.ts"), "relations");
3975
- const hasSchemaTriggersExport = hasNamedExport(path.join(functionsDir, "schema.ts"), "triggers");
3976
- const hasDedicatedTriggersExport = hasNamedExport(path.join(functionsDir, "triggers.ts"), "triggers");
4019
+ const hasRelationsExport = hasNamedExport(fileCache, path.join(functionsDir, "schema.ts"), "relations");
4020
+ const hasSchemaTriggersExport = hasNamedExport(fileCache, path.join(functionsDir, "schema.ts"), "triggers");
4021
+ const hasDedicatedTriggersExport = hasNamedExport(fileCache, path.join(functionsDir, "triggers.ts"), "triggers");
3977
4022
  const hasMigrationsManifest = fs.existsSync(path.join(functionsDir, "migrations", "manifest.ts"));
3978
4023
  if (hasRelationsExport) throw new Error("Codegen error: do not export `relations` from schema.ts. Chain relations on the default schema export with `defineSchema(...).relations(...)`.");
3979
4024
  if (hasSchemaTriggersExport || hasDedicatedTriggersExport) throw new Error("Codegen error: do not export `triggers` from schema.ts or triggers.ts. Chain triggers on the default schema export with `defineSchema(...).relations(...).triggers(...)`.");
3980
4025
  const hasOrmSchema = hasOrmSchemaMetadata;
3981
4026
  const convexGeneratedServerFile = getConvexGeneratedServerFile(functionsDir);
3982
- supportPlaceholderState = ensureGeneratedSupportPlaceholders(functionsDir, {
4027
+ supportPlaceholderState = ensureGeneratedSupportPlaceholders(fileCache, functionsDir, {
3983
4028
  includeAuth: generateAuth,
3984
- procedureNameLookup: fs.existsSync(procedureNamesOutputFile) ? void 0 : readLegacyProcedureNameLookup(serverOutputFile),
4029
+ procedureNameLookup: fileCache.read(procedureNamesOutputFile) === null ? readLegacyProcedureNameLookup(fileCache, serverOutputFile) : void 0,
3985
4030
  replaceServer: convexGeneratedServerFile === void 0
3986
4031
  });
3987
- const emitServerFile = () => writeFileIfChanged(serverOutputFile, emitGeneratedServerFile(serverOutputFile, functionsDir, hasOrmSchema, schemaMetadata.hasAggregateIndexes, hasMigrationsManifest));
4032
+ const emitServerFile = () => fileCache.writeIfChanged(serverOutputFile, emitGeneratedServerFile(serverOutputFile, functionsDir, hasOrmSchema, schemaMetadata.hasAggregateIndexes, hasMigrationsManifest));
3988
4033
  if (convexGeneratedServerFile) emitServerFile();
3989
4034
  if (generateApi) {
3990
4035
  globalThis.__KITCN_CODEGEN__ = true;
3991
4036
  try {
3992
4037
  const jitiInstance = getSharedJitiInstance();
3993
4038
  const serverShimSpecifier = normalizeImportPath(getProjectServerParserShimPath());
3994
- const files = listFilesRecursive(functionsDir).filter((file) => file.endsWith(".ts") && isValidConvexFile(file));
3995
- const parseCandidateFiles = files.filter((file) => hasPotentialCodegenExports(fs.readFileSync(path.join(functionsDir, file), "utf8"), file));
4039
+ const parseCandidateFiles = convexModuleFiles.filter((file) => hasPotentialCodegenExports(fileCache.read(path.join(functionsDir, file)) ?? "", file));
3996
4040
  const existingRuntimeFilesBeforeParse = new Set(listGeneratedRuntimeFiles(functionsDir));
3997
4041
  const runtimePlaceholderModules = [...new Set([
3998
- ...files.map((file) => file.replace(TS_EXTENSION_RE, "")),
4042
+ ...schemaRuntimeModules,
3999
4043
  ...hasOrmSchema ? ["generated/server"] : [],
4000
4044
  ...hasOrmSchema ? ["generated/aggregate"] : [],
4001
4045
  ...generateAuth ? [generatedAuthModuleName] : []
4002
4046
  ])];
4003
- createdRuntimePlaceholders = ensureGeneratedRuntimePlaceholders(functionsDir, runtimePlaceholderModules, resolveModuleRuntimeExportNames(runtimePlaceholderModules, normalizedTrimSegments));
4047
+ createdRuntimePlaceholders = ensureGeneratedRuntimePlaceholders(fileCache, functionsDir, runtimePlaceholderModules, resolveModuleRuntimeExportNames(runtimePlaceholderModules, normalizedTrimSegments));
4004
4048
  for (const file of parseCandidateFiles) {
4005
4049
  const filePath = path.join(functionsDir, file);
4006
4050
  const moduleName = file.replace(TS_EXTENSION_RE, "");
4007
4051
  try {
4008
- const { meta: moduleMeta, httpRoutes, procedures } = await parseModuleRuntime(filePath, jitiInstance, serverShimSpecifier);
4052
+ const { meta: moduleMeta, httpRoutes, procedures } = await parseModuleRuntime(fileCache, filePath, jitiInstance, serverShimSpecifier);
4009
4053
  if (moduleMeta) {
4010
4054
  meta[moduleName] = moduleMeta;
4011
4055
  const fnCount = Object.keys(moduleMeta).length;
@@ -4023,6 +4067,7 @@ async function generateMeta(sharedDir, options) {
4023
4067
  });
4024
4068
  const procedureNameEntries = buildProcedureNameLookupEntries({
4025
4069
  file,
4070
+ fileCache,
4026
4071
  filePath,
4027
4072
  moduleName,
4028
4073
  procedures
@@ -4045,13 +4090,13 @@ async function generateMeta(sharedDir, options) {
4045
4090
  }
4046
4091
  }
4047
4092
  if (fatalParseFailures.length > 0) {
4048
- for (const createdRuntimePlaceholder of createdRuntimePlaceholders) fs.rmSync(createdRuntimePlaceholder, { force: true });
4049
- for (const createdSupportPlaceholder of supportPlaceholderState.createdFiles) fs.rmSync(createdSupportPlaceholder, { force: true });
4050
- for (const replacedSupportFile of supportPlaceholderState.replacedFiles) fs.writeFileSync(replacedSupportFile.filePath, replacedSupportFile.content, "utf8");
4093
+ for (const createdRuntimePlaceholder of createdRuntimePlaceholders) fileCache.remove(createdRuntimePlaceholder);
4094
+ for (const createdSupportPlaceholder of supportPlaceholderState.createdFiles) fileCache.remove(createdSupportPlaceholder);
4095
+ for (const replacedSupportFile of supportPlaceholderState.replacedFiles) fileCache.write(replacedSupportFile.filePath, replacedSupportFile.content);
4051
4096
  const failureSummary = fatalParseFailures.map(({ file, error }) => `- ${file}: ${error instanceof Error ? error.message : String(error)}`).join("\n");
4052
4097
  throw new Error(`kitcn codegen aborted because module parsing failed:\n${failureSummary}`);
4053
4098
  }
4054
- cleanupGeneratedPluginArtifacts(functionsDir);
4099
+ cleanupGeneratedPluginArtifacts(fileCache, functionsDir);
4055
4100
  if (generateApi) {
4056
4101
  const routesByPath = /* @__PURE__ */ new Map();
4057
4102
  for (const [key, route] of Object.entries(allHttpRoutes)) {
@@ -4070,10 +4115,10 @@ async function generateMeta(sharedDir, options) {
4070
4115
  }
4071
4116
  const schemaImportPath = getSchemaImportPath(outputFile, functionsDir);
4072
4117
  const httpImportPath = getHttpImportPath(outputFile, functionsDir);
4073
- const hasTablesExport = hasNamedExport(path.join(functionsDir, "schema.ts"), "tables");
4118
+ const hasTablesExport = hasNamedExport(fileCache, path.join(functionsDir, "schema.ts"), "tables");
4074
4119
  const needsInferSelectModelImport = hasTablesExport;
4075
4120
  const needsInferInsertModelImport = hasTablesExport;
4076
- const hasHttpRouterExport = hasNamedExport(path.join(functionsDir, "http.ts"), "httpRouter");
4121
+ const hasHttpRouterExport = hasNamedExport(fileCache, path.join(functionsDir, "http.ts"), "httpRouter");
4077
4122
  const apiTree = createApiTree(meta);
4078
4123
  if (Object.hasOwn(apiTree.children, "http") || apiTree.functions.some((entry) => entry.fnName === "http")) throw new Error("Codegen conflict: root \"http\" namespace is reserved for generated HTTP router types. Rename your Convex module/function.");
4079
4124
  const apiObjectLines = emitApiObject(apiTree, [], outputFile, functionsDir, 1, dedupedRoutes, hasHttpRouterExport);
@@ -4107,23 +4152,26 @@ ${optionalTypeExports}
4107
4152
  `;
4108
4153
  const outputDirname = path.dirname(outputFile);
4109
4154
  if (!fs.existsSync(outputDirname)) fs.mkdirSync(outputDirname, { recursive: true });
4110
- writeFileIfChanged(outputFile, output);
4111
- } else fs.rmSync(outputFile, { force: true });
4155
+ fileCache.writeIfChanged(outputFile, output);
4156
+ } else fileCache.remove(outputFile);
4112
4157
  const generatedOutputDirname = path.dirname(serverOutputFile);
4113
4158
  if (!fs.existsSync(generatedOutputDirname)) fs.mkdirSync(generatedOutputDirname, { recursive: true });
4114
4159
  emitServerFile();
4115
- if (generateApi) writeFileIfChanged(procedureNamesOutputFile, emitGeneratedProcedureNamesFile(procedureNameLookup));
4116
- if (hasOrmSchema) writeFileIfChanged(aggregateOutputFile, emitGeneratedAggregateFile(aggregateOutputFile, functionsDir));
4117
- else fs.rmSync(aggregateOutputFile, { force: true });
4118
- fs.rmSync(ormOutputFile, { force: true });
4119
- fs.rmSync(crpcOutputFile, { force: true });
4120
- writeFileIfChanged(migrationsHelperOutputFile, emitGeneratedMigrationsFile(migrationsHelperOutputFile, functionsDir, hasRelationsMetadata));
4121
- fs.rmSync(legacyGeneratedMigrationsOutputFile, { force: true });
4122
- fs.rmSync(legacyGeneratedMigrationsRuntimeOutputFile, { force: true });
4123
- fs.rmSync(legacyGeneratedMigrationsUnderscoreOutputFile, { force: true });
4124
- if (generateAuth) writeFileIfChanged(authOutputFile, emitGeneratedAuthFile(authOutputFile, functionsDir, hasOrmSchema, authContract));
4125
- else fs.rmSync(authOutputFile, { force: true });
4126
- fs.rmSync(getLegacyGeneratedOutputFile(functionsDir), { force: true });
4160
+ if (generateApi) fileCache.writeIfChanged(procedureNamesOutputFile, emitGeneratedProcedureNamesFile(procedureNameLookup));
4161
+ if (hasOrmSchema) fileCache.writeIfChanged(aggregateOutputFile, emitGeneratedAggregateFile(aggregateOutputFile, functionsDir));
4162
+ else fileCache.remove(aggregateOutputFile);
4163
+ fileCache.remove(ormOutputFile);
4164
+ fileCache.remove(crpcOutputFile);
4165
+ const migrationsOutput = emitGeneratedMigrationsFile(migrationsHelperOutputFile, functionsDir, hasRelationsMetadata);
4166
+ fileCache.writeIfChanged(migrationsHelperOutputFile, migrationsOutput);
4167
+ fileCache.remove(legacyGeneratedMigrationsOutputFile);
4168
+ fileCache.remove(legacyGeneratedMigrationsRuntimeOutputFile);
4169
+ fileCache.remove(legacyGeneratedMigrationsUnderscoreOutputFile);
4170
+ if (generateAuth) {
4171
+ const authOutput = emitGeneratedAuthFile(authOutputFile, functionsDir, hasOrmSchema, authContract);
4172
+ fileCache.writeIfChanged(authOutputFile, authOutput);
4173
+ } else fileCache.remove(authOutputFile);
4174
+ fileCache.remove(getLegacyGeneratedOutputFile(functionsDir));
4127
4175
  const mergedProcedureEntries = dedupeProcedureEntries([
4128
4176
  ...hasOrmSchema ? buildGeneratedOrmRuntimeProcedureEntries("generated/server") : [],
4129
4177
  ...hasOrmSchema ? buildGeneratedAggregateRuntimeProcedureEntries("generated/aggregate") : [],
@@ -4144,20 +4192,20 @@ ${optionalTypeExports}
4144
4192
  const runtimeExportNames = resolveModuleRuntimeExportNames([...runtimeProcedureEntriesByModule.keys()], normalizedTrimSegments);
4145
4193
  for (const [moduleName, moduleEntries] of [...runtimeProcedureEntriesByModule].sort(([moduleA], [moduleB]) => moduleA.localeCompare(moduleB))) {
4146
4194
  const runtimeOutputFile = getGeneratedRuntimeOutputFile(functionsDir, moduleName);
4147
- const runtimeOutput = emitGeneratedModuleRuntimeFile(runtimeOutputFile, functionsDir, moduleName, moduleEntries, runtimeExportNames);
4195
+ const runtimeOutput = emitGeneratedModuleRuntimeFile(fileCache, runtimeOutputFile, functionsDir, moduleName, moduleEntries, runtimeExportNames);
4148
4196
  fs.mkdirSync(path.dirname(runtimeOutputFile), { recursive: true });
4149
- writeFileIfChanged(runtimeOutputFile, runtimeOutput);
4197
+ fileCache.writeIfChanged(runtimeOutputFile, runtimeOutput);
4150
4198
  runtimeOutputFiles.push(runtimeOutputFile);
4151
4199
  }
4152
4200
  const runtimeOutputFileSet = new Set(runtimeOutputFiles);
4153
4201
  const existingRuntimeFiles = listGeneratedRuntimeFiles(functionsDir);
4154
4202
  for (const existingRuntimeFile of existingRuntimeFiles) {
4155
4203
  if (runtimeOutputFileSet.has(existingRuntimeFile) || runtimeFilesPreservedFromParseFailures.has(existingRuntimeFile)) continue;
4156
- fs.rmSync(existingRuntimeFile, { force: true });
4204
+ fileCache.remove(existingRuntimeFile);
4157
4205
  }
4158
4206
  for (const createdRuntimePlaceholder of createdRuntimePlaceholders) {
4159
4207
  if (runtimeOutputFileSet.has(createdRuntimePlaceholder) || runtimeFilesPreservedFromParseFailures.has(createdRuntimePlaceholder)) continue;
4160
- fs.rmSync(createdRuntimePlaceholder, { force: true });
4208
+ fileCache.remove(createdRuntimePlaceholder);
4161
4209
  }
4162
4210
  const elapsed = ((Date.now() - startTime) / 1e3).toFixed(2);
4163
4211
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
@@ -1,4 +1,4 @@
1
- import { $ as RankOrderField, G as CountBackfillKickoffArgs, J as AggregateQueryPlan, K as CountBackfillMode, Q as RankIndexDefinition, W as CountBackfillChunkArgs, X as AggregateIndexDefinition, Y as CountQueryPlan, Z as CountIndexDefinition, q as CountBackfillStatusArgs, r as OrmCapability } from "../../capabilities-DtDfpdcH.js";
1
+ import { $ as RankOrderField, G as CountBackfillKickoffArgs, J as AggregateQueryPlan, K as CountBackfillMode, Q as RankIndexDefinition, W as CountBackfillChunkArgs, X as AggregateIndexDefinition, Y as CountQueryPlan, Z as CountIndexDefinition, q as CountBackfillStatusArgs, r as OrmCapability } from "../../capabilities-QNGgWHQd.js";
2
2
 
3
3
  //#region src/orm/aggregate-index/capability.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
- import { $n as BinaryExpression, $t as OrmLifecycleOperation, A as DatabaseWithMutations, An as ConvexCheckConfig, Ar as AnyColumn, At as VectorQueryConfig, B as RlsContext, Bn as ConvexTextBuilderInitial, Br as IsUnique, Bt as RelationsBuilderColumnConfig, C as MigrationTableName, Cn as aggregateIndex, Cr as notBetween, Ct as OrderDirection, D as defineMigrationSet, Dn as uniqueIndex, Dt as ReturningResult, E as defineMigration, En as searchIndex, Er as startsWith, Et as ReturningAll, Fn as ConvexUniqueConstraintConfig, Fr as ColumnBuilderWithTableName, Ft as ExtractTablesWithRelations, Gt as defineRelations, H as EdgeMetadata, In as check, Ir as ColumnDataType, It as ManyConfig, Jt as ConvexDeletionConfig, Kt as defineRelationsPart, Ln as foreignKey, Lr as DrizzleEntity, Lt as OneConfig, M as OrmReader$1, Mn as ConvexForeignKeyConfig, Mr as ColumnBuilderBaseConfig, Mt as unsetToken, N as OrmWriter$1, Nn as ConvexUniqueConstraintBuilder, Nr as ColumnBuilderRuntimeConfig, O as detectMigrationDrift, On as vectorIndex, Or as SystemFields, Ot as ReturningSelection, Pn as ConvexUniqueConstraintBuilderOn, Pr as ColumnBuilderTypeConfig, Pt as ExtractTablesFromSchema, Qn as TableName, Qt as OrmLifecycleChange, Rn as unique, Rr as HasDefault, Rt as RelationsBuilder, S as MigrationStep, Sn as ConvexVectorIndexConfig, Sr as not, St as OrderByClause, T as buildMigrationPlan, Tn as rankIndex, Tr as or, Tt as PredicateWhereIndexConfig, U as extractRelationsConfig, Un as Brand, Ut as TableRelationalConfig, V as RlsMode, Vn as text, Vr as NotNull, Wn as Columns, Wt as TablesRelationalConfig, Xn as OrmSchemaRelations, Xt as ConvexTableWithColumns, Yn as OrmSchemaExtensions, Yt as ConvexTable, Zn as OrmSchemaTriggers, Zt as DiscriminatorBuilderConfig, _ as MigrationMigrateOne, _n as ConvexSearchIndexBuilder, _r as isNull, _t as MutationPaginateConfig, an as RlsPolicyConfig, ar as and, at as CountConfig, b as MigrationSet, bn as ConvexVectorIndexBuilder, br as lte, bt as MutationReturning, cn as RlsRole, cr as endsWith, ct as FilterOperators, d as MigrationDefinition, dn as ConvexAggregateIndexBuilder, dr as gt, dt as InferModelFromColumns, en as TableConfig, er as ExpressionVisitor, et as AggregateConfig, f as MigrationDirection, fn as ConvexAggregateIndexBuilderOn, fr as gte, ft as InferSelectModel, g as MigrationManifestEntry, gn as ConvexRankIndexBuilderOn, gr as isNotNull, gt as MutationExecutionMode, h as MigrationDriftIssue, hn as ConvexRankIndexBuilder, hr as isFieldReference, ht as MutationExecuteResult, i as OrmMigrationCapability, in as RlsPolicy, ir as UnaryExpression, it as BuildRelationResult, j as DatabaseWithQuery, jn as ConvexForeignKeyBuilder, jr as ColumnBuilder, jt as VectorSearchProvider, kn as ConvexCheckBuilder, kt as UpdateSet, ln as RlsRoleConfig, lr as eq, lt as GetColumnData, m as MigrationDocContext, mn as ConvexIndexBuilderOn, mr as inArray, mt as MutationExecuteConfig, n as OrmCapabilities, nn as deletion, nr as FilterExpression, nt as AggregateResult, on as RlsPolicyToOption, or as between, ot as CountResult, p as MigrationDoc, pn as ConvexIndexBuilder, pr as ilike, pt as InsertValue, qn as OrmSchemaExtensionTables, qt as ConvexDeletionBuilder, r as OrmCapability, rn as discriminator, rr as LogicalExpression, rt as BuildQueryResult, sn as rlsPolicy, sr as contains, st as DBQueryConfig, t as OrmAggregateCapability, tn as convexTable, tr as FieldReference, tt as AggregateFieldValue, u as MigrationAppliedState, un as rlsRole, ur as fieldRef, ut as InferInsertModel, v as MigrationPlan, vn as ConvexSearchIndexBuilderOn, vr as like, vt as MutationPaginatedResult, w as MigrationWriteMode, wn as index, wr as notInArray, wt as PaginatedResult, x as MigrationStateMap, xn as ConvexVectorIndexBuilderOn, xr as ne, xt as MutationRunMode, y as MigrationRunStatus, yn as ConvexSearchIndexConfig, yr as lt, yt as MutationResult, zn as ConvexTextBuilder, zr as IsPrimaryKey, zt as RelationsBuilderColumnBase } from "../capabilities-DtDfpdcH.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-3fKontPx.js";
1
+ import { $n as BinaryExpression, $t as OrmLifecycleOperation, A as DatabaseWithMutations, An as ConvexCheckConfig, Ar as AnyColumn, At as VectorQueryConfig, B as RlsContext, Bn as ConvexTextBuilderInitial, Br as IsUnique, Bt as RelationsBuilderColumnConfig, C as MigrationTableName, Cn as aggregateIndex, Cr as notBetween, Ct as OrderDirection, D as defineMigrationSet, Dn as uniqueIndex, Dt as ReturningResult, E as defineMigration, En as searchIndex, Er as startsWith, Et as ReturningAll, Fn as ConvexUniqueConstraintConfig, Fr as ColumnBuilderWithTableName, Ft as ExtractTablesWithRelations, Gt as defineRelations, H as EdgeMetadata, In as check, Ir as ColumnDataType, It as ManyConfig, Jt as ConvexDeletionConfig, Kt as defineRelationsPart, Ln as foreignKey, Lr as DrizzleEntity, Lt as OneConfig, M as OrmReader$1, Mn as ConvexForeignKeyConfig, Mr as ColumnBuilderBaseConfig, Mt as unsetToken, N as OrmWriter$1, Nn as ConvexUniqueConstraintBuilder, Nr as ColumnBuilderRuntimeConfig, O as detectMigrationDrift, On as vectorIndex, Or as SystemFields, Ot as ReturningSelection, Pn as ConvexUniqueConstraintBuilderOn, Pr as ColumnBuilderTypeConfig, Pt as ExtractTablesFromSchema, Qn as TableName, Qt as OrmLifecycleChange, Rn as unique, Rr as HasDefault, Rt as RelationsBuilder, S as MigrationStep, Sn as ConvexVectorIndexConfig, Sr as not, St as OrderByClause, T as buildMigrationPlan, Tn as rankIndex, Tr as or, Tt as PredicateWhereIndexConfig, U as extractRelationsConfig, Un as Brand, Ut as TableRelationalConfig, V as RlsMode, Vn as text, Vr as NotNull, Wn as Columns, Wt as TablesRelationalConfig, Xn as OrmSchemaRelations, Xt as ConvexTableWithColumns, Yn as OrmSchemaExtensions, Yt as ConvexTable, Zn as OrmSchemaTriggers, Zt as DiscriminatorBuilderConfig, _ as MigrationMigrateOne, _n as ConvexSearchIndexBuilder, _r as isNull, _t as MutationPaginateConfig, an as RlsPolicyConfig, ar as and, at as CountConfig, b as MigrationSet, bn as ConvexVectorIndexBuilder, br as lte, bt as MutationReturning, cn as RlsRole, cr as endsWith, ct as FilterOperators, d as MigrationDefinition, dn as ConvexAggregateIndexBuilder, dr as gt, dt as InferModelFromColumns, en as TableConfig, er as ExpressionVisitor, et as AggregateConfig, f as MigrationDirection, fn as ConvexAggregateIndexBuilderOn, fr as gte, ft as InferSelectModel, g as MigrationManifestEntry, gn as ConvexRankIndexBuilderOn, gr as isNotNull, gt as MutationExecutionMode, h as MigrationDriftIssue, hn as ConvexRankIndexBuilder, hr as isFieldReference, ht as MutationExecuteResult, i as OrmMigrationCapability, in as RlsPolicy, ir as UnaryExpression, it as BuildRelationResult, j as DatabaseWithQuery, jn as ConvexForeignKeyBuilder, jr as ColumnBuilder, jt as VectorSearchProvider, kn as ConvexCheckBuilder, kt as UpdateSet, ln as RlsRoleConfig, lr as eq, lt as GetColumnData, m as MigrationDocContext, mn as ConvexIndexBuilderOn, mr as inArray, mt as MutationExecuteConfig, n as OrmCapabilities, nn as deletion, nr as FilterExpression, nt as AggregateResult, on as RlsPolicyToOption, or as between, ot as CountResult, p as MigrationDoc, pn as ConvexIndexBuilder, pr as ilike, pt as InsertValue, qn as OrmSchemaExtensionTables, qt as ConvexDeletionBuilder, r as OrmCapability, rn as discriminator, rr as LogicalExpression, rt as BuildQueryResult, sn as rlsPolicy, sr as contains, st as DBQueryConfig, t as OrmAggregateCapability, tn as convexTable, tr as FieldReference, tt as AggregateFieldValue, u as MigrationAppliedState, un as rlsRole, ur as fieldRef, ut as InferInsertModel, v as MigrationPlan, vn as ConvexSearchIndexBuilderOn, vr as like, vt as MutationPaginatedResult, w as MigrationWriteMode, wn as index, wr as notInArray, wt as PaginatedResult, x as MigrationStateMap, xn as ConvexVectorIndexBuilderOn, xr as ne, xt as MutationRunMode, y as MigrationRunStatus, yn as ConvexSearchIndexConfig, yr as lt, yt as MutationResult, zn as ConvexTextBuilder, zr as IsPrimaryKey, zt as RelationsBuilderColumnBase } from "../capabilities-QNGgWHQd.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-DfFcNKtn.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
@@ -2675,16 +2675,16 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
2675
2675
  return streamQuery;
2676
2676
  }
2677
2677
  _buildUnionSourceStream(source, fallbackOrder) {
2678
- const configuredIndex = this.configuredIndex;
2678
+ const sourceIndex = source.index ?? this.configuredIndex;
2679
2679
  this._assertWhereIndexRequirement({
2680
2680
  where: source.where,
2681
2681
  tableConfig: this.tableConfig,
2682
- hasConfiguredIndex: Boolean(configuredIndex?.name),
2682
+ hasConfiguredIndex: Boolean(sourceIndex?.name),
2683
2683
  context: "pipeline.union source"
2684
2684
  });
2685
2685
  const schemaDefinition = this._getSchemaDefinitionOrThrow();
2686
2686
  let sourceStream = stream(this.db, schemaDefinition).query(this.tableConfig.name);
2687
- if (configuredIndex?.name) sourceStream = sourceStream.withIndex(configuredIndex.name, configuredIndex.range ? configuredIndex.range : (q) => q);
2687
+ if (sourceIndex?.name) sourceStream = sourceStream.withIndex(sourceIndex.name, sourceIndex.range ? sourceIndex.range : (q) => q);
2688
2688
  sourceStream = sourceStream.order(fallbackOrder);
2689
2689
  const sourcePredicate = this._buildTableFilterPredicate(source.where, this.tableConfig);
2690
2690
  if (sourcePredicate) sourceStream = sourceStream.filterWith(sourcePredicate);
@@ -1,3 +1,3 @@
1
- import { C as MigrationTableName, D as defineMigrationSet, E as defineMigration, O as detectMigrationDrift, S as MigrationStep, T as buildMigrationPlan, _ as MigrationMigrateOne, a as MigrationCancelArgs, b as MigrationSet, c as MigrationStatusArgs, d as MigrationDefinition, f as MigrationDirection, g as MigrationManifestEntry, h as MigrationDriftIssue, l as createMigrationHandlers, m as MigrationDocContext, o as MigrationRunArgs, p as MigrationDoc, s as MigrationRunChunkArgs, u as MigrationAppliedState, v as MigrationPlan, w as MigrationWriteMode, x as MigrationStateMap, y as MigrationRunStatus } from "../../capabilities-DtDfpdcH.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-3fKontPx.js";
1
+ import { C as MigrationTableName, D as defineMigrationSet, E as defineMigration, O as detectMigrationDrift, S as MigrationStep, T as buildMigrationPlan, _ as MigrationMigrateOne, a as MigrationCancelArgs, b as MigrationSet, c as MigrationStatusArgs, d as MigrationDefinition, f as MigrationDirection, g as MigrationManifestEntry, h as MigrationDriftIssue, l as createMigrationHandlers, m as MigrationDocContext, o as MigrationRunArgs, p as MigrationDoc, s as MigrationRunChunkArgs, u as MigrationAppliedState, v as MigrationPlan, w as MigrationWriteMode, x as MigrationStateMap, y as MigrationRunStatus } from "../../capabilities-QNGgWHQd.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-DfFcNKtn.js";
3
3
  export { 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 };
package/dist/watcher.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as generateMeta, d as PARSE_SNAPSHOT_SUFFIX, i as resolveConfiguredBackend, n as withLocalCodegenEnv, o as getConvexConfig, r as loadCliConfig, u as logger } from "./local-env-9Qtfn3wR.mjs";
2
+ import { a as generateMeta, d as PARSE_SNAPSHOT_SUFFIX, i as resolveConfiguredBackend, n as withLocalCodegenEnv, o as getConvexConfig, r as loadCliConfig, u as logger } from "./local-env-Z8jj6ecD.mjs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
@@ -1,4 +1,4 @@
1
- import { Bn as ConvexTextBuilderInitial, Dr as SystemFieldAliases, F as DeleteMode, Gn as OrmRuntimeDefaults, H as EdgeMetadata, Hn as ConvexColumnBuilder, Hr as entityKind, Ht as RelationsConfigWithSchema, I as SerializedFilterExpression, Jn as OrmSchemaExtensionTriggers, Kn as OrmSchemaExtensionRelations, L as getChecks, M as OrmReader, Mr as ColumnBuilderBaseConfig, N as OrmWriter, Nt as AnyRelationsBuilderConfig, Or as SystemFields, P as CascadeMode, Pt as ExtractTablesFromSchema, Qt as OrmLifecycleChange, R as getForeignKeys, Rr as HasDefault, Rt as RelationsBuilder, St as OrderByClause, Ut as TableRelationalConfig, Vt as RelationsBuilderConfigValue, Wn as Columns, Wt as TablesRelationalConfig, Xn as OrmSchemaRelations, Xt as ConvexTableWithColumns, Yt as ConvexTable, Zn as OrmSchemaTriggers, b as MigrationSet, ft as InferSelectModel, in as RlsPolicy, jr as ColumnBuilder, jt as VectorSearchProvider, k as CreateDatabaseOptions, kr as $Type, nr as FilterExpression$1, r as OrmCapability, ut as InferInsertModel, z as getUniqueIndexes } from "./capabilities-DtDfpdcH.js";
1
+ import { Bn as ConvexTextBuilderInitial, Dr as SystemFieldAliases, F as DeleteMode, Gn as OrmRuntimeDefaults, H as EdgeMetadata, Hn as ConvexColumnBuilder, Hr as entityKind, Ht as RelationsConfigWithSchema, I as SerializedFilterExpression, Jn as OrmSchemaExtensionTriggers, Kn as OrmSchemaExtensionRelations, L as getChecks, M as OrmReader, Mr as ColumnBuilderBaseConfig, N as OrmWriter, Nt as AnyRelationsBuilderConfig, Or as SystemFields, P as CascadeMode, Pt as ExtractTablesFromSchema, Qt as OrmLifecycleChange, R as getForeignKeys, Rr as HasDefault, Rt as RelationsBuilder, St as OrderByClause, Ut as TableRelationalConfig, Vt as RelationsBuilderConfigValue, Wn as Columns, Wt as TablesRelationalConfig, Xn as OrmSchemaRelations, Xt as ConvexTableWithColumns, Yt as ConvexTable, Zn as OrmSchemaTriggers, b as MigrationSet, ft as InferSelectModel, in as RlsPolicy, jr as ColumnBuilder, jt as VectorSearchProvider, k as CreateDatabaseOptions, kr as $Type, nr as FilterExpression$1, r as OrmCapability, ut as InferInsertModel, z as getUniqueIndexes } from "./capabilities-QNGgWHQd.js";
2
2
  import * as convex_values0 from "convex/values";
3
3
  import { GenericId, Validator, Value } from "convex/values";
4
4
  import { DefineSchemaOptions, GenericDatabaseReader, GenericDatabaseWriter, GenericSchema, SchedulableFunctionReference, Scheduler, SchemaDefinition, internalActionGeneric, internalMutationGeneric } from "convex/server";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitcn",
3
- "version": "0.26.1",
3
+ "version": "0.26.3",
4
4
  "description": "kitcn - React Query integration and CLI tools for Convex",
5
5
  "keywords": [
6
6
  "convex",
@@ -543,45 +543,47 @@ return await ctx.orm.query.articles.findMany({
543
543
 
544
544
  ```ts
545
545
  return await ctx.orm.query.messages
546
- .withIndex("by_from_to")
547
- .select()
548
- .union([
549
- { where: { from: input.me, to: input.them } },
550
- { where: { from: input.them, to: input.me } },
551
- ])
552
- .interleaveBy(["createdAt", "id"])
553
- .filter(async (m) => !m.deletedAt)
554
- .map(async (m) => ({ ...m, body: m.body.slice(0, 240) }))
555
- .paginate({
556
- cursor: input.cursor,
557
- limit: input.limit,
558
- maxScan: 500,
559
- });
560
- ```
561
-
562
- ### Union with index ranges
563
-
564
- ```ts
565
- const page = await ctx.orm.query.messages
566
546
  .select()
567
547
  .union([
568
548
  {
569
549
  index: {
570
550
  name: "by_from_to",
571
- range: (q) => q.eq("from", me).eq("to", them),
551
+ range: (q) => q.eq("from", input.me).eq("to", input.them),
572
552
  },
573
553
  },
574
554
  {
575
555
  index: {
576
556
  name: "by_from_to",
577
- range: (q) => q.eq("from", them).eq("to", me),
557
+ range: (q) => q.eq("from", input.them).eq("to", input.me),
578
558
  },
579
559
  },
580
560
  ])
581
561
  .interleaveBy(["createdAt", "id"])
582
- .paginate({ cursor: null, limit: 20 });
562
+ .filter(async (m) => !m.deletedAt)
563
+ .map(async (m) => ({ ...m, body: m.body.slice(0, 240) }))
564
+ .paginate({
565
+ cursor: input.cursor,
566
+ limit: input.limit,
567
+ maxScan: 500,
568
+ });
583
569
  ```
584
570
 
571
+ Per source:
572
+
573
+ - `index: { name, range }` anchors that source on its own range. It overrides
574
+ the chain-level `.withIndex(...)`; sources that omit it use the chain index.
575
+ - `where` filters that source's rows after the read.
576
+
577
+ `interleaveBy` fields must be the trailing fields each source is already ordered
578
+ by, so every field before them has to be pinned with `eq` in that source's
579
+ range. Sources may name different indexes when they land on the same trailing
580
+ fields — e.g. `by_author_likes` (authorId, numLikes) with `eq("authorId", ...)`
581
+ merges with `numLikesAndType` (type, numLikes) with `eq("type", ...)` under
582
+ `interleaveBy(["numLikes"])`.
583
+
584
+ Anchor every source. A shared `.withIndex(...)` plus per-source `where` makes
585
+ each source walk the same range and discard the misses after reading them.
586
+
585
587
  ### Pre-pagination transforms
586
588
 
587
589
  ```ts