kitcn 0.23.0 → 0.25.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.
Files changed (85) hide show
  1. package/dist/aggregate/index.d.ts +11 -1
  2. package/dist/aggregate/index.js +1 -1
  3. package/dist/auth/generated/index.d.ts +1 -1
  4. package/dist/auth/generated/index.js +1 -1
  5. package/dist/auth/http/index.d.ts +1 -1
  6. package/dist/auth/index.d.ts +4 -4
  7. package/dist/auth/index.js +10 -9
  8. package/dist/auth/nextjs/index.d.ts +2 -2
  9. package/dist/auth/nextjs/index.js +3 -3
  10. package/dist/auth/start/server/index.d.ts +1 -1
  11. package/dist/auth/start/server/index.js +1 -1
  12. package/dist/{builder-CsxVc5xC.js → builder-1Vx-tEnS.js} +3 -3
  13. package/dist/{caller-factory-CWa0ELLD.js → caller-factory-Dd3H7j3V.js} +3 -3
  14. package/dist/{where-clause-compiler-B_H3oio5.d.ts → capabilities-Bem7xvGK.d.ts} +2958 -4145
  15. package/dist/cli.mjs +155 -60
  16. package/dist/{convex-plugin-D8B0oCFq.js → convex-plugin-DfOhBU9g.js} +1 -1
  17. package/dist/{create-schema-orm-B3f2Kc8O.js → create-schema-orm-Bu2JCiBS.js} +1 -1
  18. package/dist/crpc/index.d.ts +3 -3
  19. package/dist/crpc/index.js +4 -4
  20. package/dist/{customFunctions-DxEEO4Dq.js → customFunctions-BbhgdRGl.js} +2 -2
  21. package/dist/definitions-D9vhJ6OV.js +215 -0
  22. package/dist/extensions-DzjCJXYl.js +55 -0
  23. package/dist/filter-expression-Dydt8wS0.js +402 -0
  24. package/dist/{generated-contract-disabled-B-nmd7Ne.d.ts → generated-contract-disabled-BEc4d98x.d.ts} +2 -2
  25. package/dist/{http-types-BoSDAh4Y.d.ts → http-types-90mapMP9.d.ts} +33 -3
  26. package/dist/id-Bibg34Yb.js +37 -0
  27. package/dist/{local-env-yFKub75x.mjs → local-env-Du24tdbp.mjs} +2243 -2113
  28. package/dist/{middleware-Br5zubu7.d.ts → middleware-CE_MC6Gi.d.ts} +1 -1
  29. package/dist/{middleware-DUd1Sj39.js → middleware-cJDRHTRY.js} +1 -1
  30. package/dist/orm/aggregate-index/index.d.ts +20 -0
  31. package/dist/orm/aggregate-index/index.js +2136 -0
  32. package/dist/orm/index.d.ts +5 -4
  33. package/dist/orm/index.js +126 -4292
  34. package/dist/orm/migrations/index.d.ts +3 -0
  35. package/dist/orm/migrations/index.js +506 -0
  36. package/dist/plugins/index.d.ts +1 -1
  37. package/dist/plugins/index.js +1 -1
  38. package/dist/{procedure-caller-DQxnLBS_.js → procedure-caller-DIzXfX2E.js} +2 -2
  39. package/dist/{procedure-name-C55TynK3.d.ts → procedure-name-B09-cf1-.d.ts} +3 -3
  40. package/dist/{query-context-BzihIpnM.js → query-context-DOm5Xm3H.js} +1 -402
  41. package/dist/{query-options-C_eBSIXG.js → query-options-DUiOj01a.js} +33 -1
  42. package/dist/ratelimit/index.d.ts +47 -11
  43. package/dist/ratelimit/index.js +256 -102
  44. package/dist/react/index.d.ts +9 -6
  45. package/dist/react/index.js +36 -17
  46. package/dist/rsc/index.d.ts +6 -9
  47. package/dist/rsc/index.js +7 -11
  48. package/dist/runtime-DfGsZtx3.js +1074 -0
  49. package/dist/schema-Besl-rpv.js +1411 -0
  50. package/dist/server/index.d.ts +4 -4
  51. package/dist/server/index.js +4 -4
  52. package/dist/solid/index.d.ts +9 -6
  53. package/dist/solid/index.js +36 -17
  54. package/dist/{runtime-6DJxpDfb.js → table-rmJm5Qio.js} +3 -1095
  55. package/dist/{token-xlpENMVn.js → token-DcV_0fkF.js} +1 -1
  56. package/dist/{types-C0Xl7P8K.d.ts → types-In-ZZOsd.d.ts} +1 -1
  57. package/dist/{validators-C7LelqTN.js → validators-Dmbhsd3U.js} +1 -1
  58. package/dist/watcher.mjs +1 -1
  59. package/dist/where-clause-compiler-mohWFiG0.d.ts +2042 -0
  60. package/package.json +3 -2
  61. package/skills/kitcn/SKILL.md +19 -18
  62. package/skills/kitcn/references/features/aggregates.md +21 -0
  63. package/skills/kitcn/references/features/auth-organizations.md +3 -3
  64. package/skills/kitcn/references/features/auth-polar.md +2 -1
  65. package/skills/kitcn/references/features/auth.md +8 -1
  66. package/skills/kitcn/references/features/http.md +26 -1
  67. package/skills/kitcn/references/features/ratelimit.md +11 -0
  68. package/skills/kitcn/references/features/react.md +1 -1
  69. package/skills/kitcn/references/setup/auth.md +52 -42
  70. package/skills/kitcn/references/setup/index.md +1 -1
  71. package/skills/kitcn/references/setup/server.md +37 -6
  72. /package/dist/{api-entry-N3nBOlI2.js → api-entry-CkDpGYVg.js} +0 -0
  73. /package/dist/{context-utils-BBUtBqjN.d.ts → context-utils-DwZ3Cam1.d.ts} +0 -0
  74. /package/dist/{create-schema-BXrKE2YY.js → create-schema-BRFjdgRr.js} +0 -0
  75. /package/dist/{error-Bvo7YEhk.js → error-CMLeCadS.js} +0 -0
  76. /package/dist/{generated-contract-disabled-BXaz7JCE.js → generated-contract-disabled-CZa0iyV0.js} +0 -0
  77. /package/dist/{meta-utils-D9K4fICl.js → meta-utils-D2lqN2Lg.js} +0 -0
  78. /package/dist/{query-context-CNo9ffvI.d.ts → query-context-DJONf8X5.d.ts} +0 -0
  79. /package/dist/{token-B9Bjcqug.d.ts → token-kQaqFby4.d.ts} +0 -0
  80. /package/dist/{transformer-yZuBWo8v.js → transformer-D8wO-kEj.js} +0 -0
  81. /package/dist/{types-BCl8gfGy.d.ts → types-Bf3XQex5.d.ts} +0 -0
  82. /package/dist/{types-DF2cg_w0.d.ts → types-MHYXboyy.d.ts} +0 -0
  83. /package/dist/{types-CnTpHR1F.d.ts → types-eNzQmbqM.d.ts} +0 -0
  84. /package/dist/{upstream-BR6sBLg3.js → upstream-BCgGZX6q.js} +0 -0
  85. /package/dist/{validators-BhsByJeg.d.ts → validators-V4eM2-Jg.d.ts} +0 -0
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as createSystemFields, S as TableName, _ as getSchemaRelations, a as generateMeta, b as OrmSchemaExtensions, d as loadBabelParser, f as loadClackPrompts, g as isColorEnabled, h as highlighter, i as resolveConfiguredBackend, l as createProjectJiti, m as loadEsbuild, n as withLocalCodegenEnv, o as getConvexConfig, p as loadDotenv, r as loadCliConfig, s as logger, t as getLocalBackendEnvVars, u as CRPC_BUILDER_STUB_SOURCE, v as Columns, x as RlsPolicies, y as EnableRLS } from "./local-env-yFKub75x.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-Du24tdbp.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";
@@ -12,21 +12,6 @@ import { diffWords, structuredPatch } from "diff";
12
12
  import { parseEnv } from "node:util";
13
13
  import { createServer } from "node:http";
14
14
 
15
- //#region src/orm/index-utils.ts
16
- function getIndexes(table) {
17
- const indexes = table.getIndexes?.();
18
- return Array.isArray(indexes) ? indexes : [];
19
- }
20
- function getAggregateIndexes(table) {
21
- const indexes = table.getAggregateIndexes?.();
22
- return Array.isArray(indexes) ? indexes : [];
23
- }
24
- function getRankIndexes(table) {
25
- const indexes = table.getRankIndexes?.();
26
- return Array.isArray(indexes) ? indexes : [];
27
- }
28
-
29
- //#endregion
30
15
  //#region src/orm/mutation-utils.ts
31
16
  const UTF8_ENCODER = new TextEncoder();
32
17
  function getTableName(table) {
@@ -2506,7 +2491,7 @@ function QueryProvider({ children }: { children: ReactNode }) {
2506
2491
  const FUNCTIONS_DIR_IMPORT_PLACEHOLDER$5 = "__KITCN_FUNCTIONS_DIR__";
2507
2492
  const PLUGIN_CONFIG_IMPORT_PLACEHOLDER$1 = "__KITCN_PLUGIN_CONFIG_IMPORT__";
2508
2493
  const PLUGIN_SCHEMA_IMPORT_PLACEHOLDER$1 = "__KITCN_PLUGIN_SCHEMA_IMPORT__";
2509
- const PROJECT_CRPC_IMPORT_PLACEHOLDER$3 = "__KITCN_PROJECT_CRPC_IMPORT__";
2494
+ const PROJECT_CRPC_IMPORT_PLACEHOLDER$4 = "__KITCN_PROJECT_CRPC_IMPORT__";
2510
2495
  const PROJECT_SHARED_API_IMPORT_PLACEHOLDER = "__KITCN_PROJECT_SHARED_API_IMPORT__";
2511
2496
  const PROJECT_GET_ENV_IMPORT_PLACEHOLDER$2 = "__KITCN_PROJECT_GET_ENV_IMPORT__";
2512
2497
 
@@ -2536,7 +2521,7 @@ const INIT_HTTP_IMPORT_MARKER = "// __KITCN_HTTP_IMPORTS__";
2536
2521
  const INIT_HTTP_ROUTE_MARKER = " // __KITCN_HTTP_ROUTES__";
2537
2522
  const INIT_HTTP_TEMPLATE = `import { createHttpRouter } from 'kitcn/server';
2538
2523
  import { Hono } from 'hono';
2539
- import { router } from '${PROJECT_CRPC_IMPORT_PLACEHOLDER$3}';
2524
+ import { router } from '${PROJECT_CRPC_IMPORT_PLACEHOLDER$4}';
2540
2525
  ${INIT_HTTP_IMPORT_MARKER}
2541
2526
 
2542
2527
  const app = new Hono();
@@ -2849,7 +2834,7 @@ function resolveRelativeImportPath(filePath, targetFilePath) {
2849
2834
  return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
2850
2835
  }
2851
2836
  function renderInitTemplateContent(params) {
2852
- return params.template.replaceAll(FUNCTIONS_DIR_IMPORT_PLACEHOLDER$5, resolveRelativeImportPath(params.filePath, params.functionsDir)).replaceAll(PROJECT_CRPC_IMPORT_PLACEHOLDER$3, resolveRelativeImportPath(params.filePath, params.crpcFilePath));
2837
+ return params.template.replaceAll(FUNCTIONS_DIR_IMPORT_PLACEHOLDER$5, resolveRelativeImportPath(params.filePath, params.functionsDir)).replaceAll(PROJECT_CRPC_IMPORT_PLACEHOLDER$4, resolveRelativeImportPath(params.filePath, params.crpcFilePath));
2853
2838
  }
2854
2839
 
2855
2840
  //#endregion
@@ -3424,7 +3409,7 @@ const resolvePluginScaffoldFiles = (templates, roots, functionsDir, existingTemp
3424
3409
  const lockfilePath = normalizeLockfileScaffoldPath(mappedLockfilePath) ?? normalizePath$2(relative(process.cwd(), join(rootDir, template.path)));
3425
3410
  const filePath = resolve(process.cwd(), lockfilePath);
3426
3411
  const getEnvImportPath = resolveProjectGetEnvImportPrefix(filePath, roots.envFilePath);
3427
- const content = template.content.replaceAll(FUNCTIONS_DIR_IMPORT_PLACEHOLDER$5, resolveFunctionsDirImportPrefix(filePath, functionsDir)).replaceAll(PROJECT_CRPC_IMPORT_PLACEHOLDER$3, resolveProjectCrpcImportPrefix(filePath, roots.crpcFilePath)).replaceAll(PROJECT_SHARED_API_IMPORT_PLACEHOLDER, resolveProjectSharedApiImportPrefix(filePath, roots.sharedApiFilePath)).replaceAll(PROJECT_GET_ENV_IMPORT_PLACEHOLDER$2, `import { getEnv } from '${getEnvImportPath}';`).replaceAll(PLUGIN_CONFIG_IMPORT_PLACEHOLDER$1, resolvePluginConfigImportPrefix(filePath, roots.libRootDir)).replaceAll(PLUGIN_SCHEMA_IMPORT_PLACEHOLDER$1, resolvePluginSchemaImportPrefix(filePath, roots.libRootDir));
3412
+ const content = template.content.replaceAll(FUNCTIONS_DIR_IMPORT_PLACEHOLDER$5, resolveFunctionsDirImportPrefix(filePath, functionsDir)).replaceAll(PROJECT_CRPC_IMPORT_PLACEHOLDER$4, resolveProjectCrpcImportPrefix(filePath, roots.crpcFilePath)).replaceAll(PROJECT_SHARED_API_IMPORT_PLACEHOLDER, resolveProjectSharedApiImportPrefix(filePath, roots.sharedApiFilePath)).replaceAll(PROJECT_GET_ENV_IMPORT_PLACEHOLDER$2, `import { getEnv } from '${getEnvImportPath}';`).replaceAll(PLUGIN_CONFIG_IMPORT_PLACEHOLDER$1, resolvePluginConfigImportPrefix(filePath, roots.libRootDir)).replaceAll(PLUGIN_SCHEMA_IMPORT_PLACEHOLDER$1, resolvePluginSchemaImportPrefix(filePath, roots.libRootDir));
3428
3413
  if (!(descriptor.key === "auth" && preset === "convex") && (template.target === "functions" || template.target === "lib") && content.includes("process.env")) throw new Error(`Scaffold template "${descriptor.key}/${template.id}" contains process.env. Use getEnv() instead.`);
3429
3414
  return {
3430
3415
  templateId: template.id,
@@ -6776,10 +6761,46 @@ const authRegistryItem = defineInternalRegistryItem({
6776
6761
  }
6777
6762
  });
6778
6763
 
6764
+ //#endregion
6765
+ //#region src/cli/registry/items/ratelimit/ratelimit-functions.template.ts
6766
+ const PROJECT_CRPC_IMPORT_PLACEHOLDER$3 = "__KITCN_PROJECT_CRPC_IMPORT__";
6767
+ const RATELIMIT_FUNCTIONS_TEMPLATE = `import { cleanupRatelimitState } from "kitcn/ratelimit";
6768
+ import { z } from "zod";
6769
+ import { privateMutation } from "${PROJECT_CRPC_IMPORT_PLACEHOLDER$3}";
6770
+
6771
+ const DEFAULT_BATCH_SIZE = 500;
6772
+
6773
+ export const cleanup = privateMutation
6774
+ .input(
6775
+ z.object({
6776
+ olderThanMs: z.number().positive(),
6777
+ limit: z.number().int().min(1).max(1000).optional(),
6778
+ })
6779
+ )
6780
+ .output(
6781
+ z.object({
6782
+ deleted: z.number(),
6783
+ hasMore: z.boolean(),
6784
+ })
6785
+ )
6786
+ .mutation(async ({ ctx, input }) =>
6787
+ cleanupRatelimitState(ctx.db, {
6788
+ before: Date.now() - input.olderThanMs,
6789
+ limit: input.limit ?? DEFAULT_BATCH_SIZE,
6790
+ })
6791
+ );
6792
+ `;
6793
+
6779
6794
  //#endregion
6780
6795
  //#region src/cli/registry/items/ratelimit/ratelimit-plugin.template.ts
6781
6796
  const FUNCTIONS_DIR_IMPORT_PLACEHOLDER$4 = "__KITCN_FUNCTIONS_DIR__";
6782
- const RATELIMIT_PLUGIN_TEMPLATE = `import { MINUTE, Ratelimit, RatelimitPlugin } from "kitcn/ratelimit";
6797
+ const RATELIMIT_PLUGIN_TEMPLATE = `import {
6798
+ type LimitRequest,
6799
+ MINUTE,
6800
+ Ratelimit,
6801
+ RatelimitPlugin,
6802
+ SECOND,
6803
+ } from "kitcn/ratelimit";
6783
6804
  import type { MutationCtx } from "${FUNCTIONS_DIR_IMPORT_PLACEHOLDER$4}/generated/server";
6784
6805
 
6785
6806
  const fixed = (rate: number) => Ratelimit.fixedWindow(rate, MINUTE);
@@ -6790,6 +6811,11 @@ export const ratelimitBuckets = {
6790
6811
  free: fixed(60),
6791
6812
  premium: fixed(200),
6792
6813
  },
6814
+ interactive: {
6815
+ public: Ratelimit.fixedWindow(3, 30 * SECOND),
6816
+ free: Ratelimit.fixedWindow(3, 30 * SECOND),
6817
+ premium: Ratelimit.fixedWindow(3, 30 * SECOND),
6818
+ },
6793
6819
  } as const;
6794
6820
 
6795
6821
  type RatelimitTier = keyof (typeof ratelimitBuckets)["default"];
@@ -6829,14 +6855,38 @@ async function getRequestSignals(ctx: RatelimitCtx) {
6829
6855
  };
6830
6856
  }
6831
6857
 
6858
+ /**
6859
+ * The identifier is the rate-limit partition key: everything that resolves to
6860
+ * the same string shares one budget and one \`ratelimitState\` document.
6861
+ *
6862
+ * Unauthenticated traffic is keyed by request IP so one visitor cannot spend
6863
+ * every other visitor's budget. Calls without request metadata all fall back
6864
+ * to one key.
6865
+ */
6866
+ function getRequestIdentifier(
6867
+ user: RatelimitUser | null,
6868
+ signals: LimitRequest | undefined
6869
+ ) {
6870
+ if (user) {
6871
+ return user.id;
6872
+ }
6873
+
6874
+ return signals?.ip ? \`ip:\${signals.ip}\` : "ip:unknown";
6875
+ }
6876
+
6832
6877
  export const ratelimit = RatelimitPlugin.configure({
6833
6878
  buckets: ratelimitBuckets,
6834
6879
  getBucket: ({ meta }: { meta: RatelimitMeta }) => meta.ratelimit ?? "default",
6835
6880
  getUser: ({ ctx }: { ctx: RatelimitCtx }) => ctx.user ?? null,
6836
- getIdentifier: ({ user }: { user: RatelimitUser | null }) =>
6837
- user?.id ?? "anonymous",
6838
6881
  getTier: getUserTier,
6839
6882
  getSignals: ({ ctx }: { ctx: RatelimitCtx }) => getRequestSignals(ctx),
6883
+ getIdentifier: ({
6884
+ user,
6885
+ signals,
6886
+ }: {
6887
+ user: RatelimitUser | null;
6888
+ signals: LimitRequest | undefined;
6889
+ }) => getRequestIdentifier(user, signals),
6840
6890
  prefix: ({ bucket, tier }) => \`ratelimit:\${bucket}:\${tier}\`,
6841
6891
  failureMode: "closed",
6842
6892
  enableProtection: true,
@@ -6869,6 +6919,7 @@ export const ratelimitStateTable = convexTable(
6869
6919
  (t) => [
6870
6920
  index("by_name_key_shard").on(t.name, t.key, t.shard),
6871
6921
  index("by_name_key").on(t.name, t.key),
6922
+ index("by_ts").on(t.ts),
6872
6923
  ],
6873
6924
  );
6874
6925
 
@@ -6909,18 +6960,28 @@ export function ratelimitExtension() {
6909
6960
 
6910
6961
  //#endregion
6911
6962
  //#region src/cli/registry/items/ratelimit/ratelimit-item.ts
6912
- const RATELIMIT_FILES = [createRegistryFile({
6913
- id: "ratelimit-schema",
6914
- path: "schema.ts",
6915
- target: "lib",
6916
- content: RATELIMIT_SCHEMA_TEMPLATE
6917
- }), createRegistryFile({
6918
- id: "ratelimit-plugin",
6919
- path: "plugin.ts",
6920
- target: "lib",
6921
- content: RATELIMIT_PLUGIN_TEMPLATE,
6922
- requires: ["ratelimit-schema"]
6923
- })];
6963
+ const RATELIMIT_FILES = [
6964
+ createRegistryFile({
6965
+ id: "ratelimit-schema",
6966
+ path: "schema.ts",
6967
+ target: "lib",
6968
+ content: RATELIMIT_SCHEMA_TEMPLATE
6969
+ }),
6970
+ createRegistryFile({
6971
+ id: "ratelimit-functions",
6972
+ path: "ratelimit.ts",
6973
+ target: "functions",
6974
+ content: RATELIMIT_FUNCTIONS_TEMPLATE,
6975
+ requires: ["ratelimit-schema"]
6976
+ }),
6977
+ createRegistryFile({
6978
+ id: "ratelimit-plugin",
6979
+ path: "plugin.ts",
6980
+ target: "lib",
6981
+ content: RATELIMIT_PLUGIN_TEMPLATE,
6982
+ requires: ["ratelimit-schema"]
6983
+ })
6984
+ ];
6924
6985
  function buildRatelimitCrpcRegistrationPlanFile(params) {
6925
6986
  const crpcPath = getCrpcFilePath(params.config);
6926
6987
  const baselineCrpcSource = renderInitTemplateContent({
@@ -6966,7 +7027,7 @@ const ratelimitRegistryItem = defineInternalRegistryItem({
6966
7027
  presets: [{
6967
7028
  name: "server-first",
6968
7029
  description: "Scaffold a reusable ratelimit plugin and auto-register schema extension.",
6969
- registryDependencies: ["ratelimit-schema", "ratelimit-plugin"]
7030
+ registryDependencies: RATELIMIT_FILES.map((file) => file.meta.id)
6970
7031
  }, {
6971
7032
  name: "schema-only",
6972
7033
  description: "Only register ratelimit extension in schema.",
@@ -11971,6 +12032,10 @@ function normalizeStringList(values) {
11971
12032
  if (!Array.isArray(values)) return [];
11972
12033
  return [...new Set(values.filter((value) => typeof value === "string"))].sort();
11973
12034
  }
12035
+ function normalizeOrderedStringList(values) {
12036
+ if (!Array.isArray(values)) return [];
12037
+ return [...new Set(values.filter((value) => typeof value === "string"))];
12038
+ }
11974
12039
  function readOptionalCliFlagValue(args, flag) {
11975
12040
  for (let i = 0; i < args.length; i += 1) {
11976
12041
  const arg = args[i];
@@ -11986,24 +12051,6 @@ function readOptionalCliFlagValue(args, flag) {
11986
12051
  }
11987
12052
  }
11988
12053
  }
11989
- function resolveSchemaDefaultExport(schemaModule) {
11990
- const schemaValue = schemaModule.default !== void 0 ? schemaModule.default : schemaModule;
11991
- if (!schemaValue || typeof schemaValue !== "object") return null;
11992
- return schemaValue;
11993
- }
11994
- function collectSchemaTables(schemaValue) {
11995
- const allTables = /* @__PURE__ */ new Set();
11996
- const relations = getSchemaRelations(schemaValue);
11997
- if (relations && typeof relations === "object" && !Array.isArray(relations)) for (const relationConfig of Object.values(relations)) {
11998
- const table = relationConfig?.table;
11999
- if (table) allTables.add(table);
12000
- }
12001
- const tables = schemaValue.tables;
12002
- if (tables && typeof tables === "object" && !Array.isArray(tables)) {
12003
- for (const table of Object.values(tables)) if (table) allTables.add(table);
12004
- }
12005
- return [...allTables];
12006
- }
12007
12054
  function buildAggregateFingerprintPayload(tables) {
12008
12055
  return tables.map((table) => getTableConfig(table)).map((tableConfig) => ({
12009
12056
  tableName: tableConfig.name,
@@ -12015,18 +12062,60 @@ function buildAggregateFingerprintPayload(tables) {
12015
12062
  avgFields: normalizeStringList(index.avgFields),
12016
12063
  minFields: normalizeStringList(index.minFields),
12017
12064
  maxFields: normalizeStringList(index.maxFields)
12065
+ })).sort((a, b) => a.name.localeCompare(b.name)),
12066
+ rankIndexes: tableConfig.rankIndexes.map((index) => ({
12067
+ name: index.name,
12068
+ partitionFields: normalizeOrderedStringList(index.partitionFields),
12069
+ orderFields: (index.orderFields ?? []).map((field) => `${field.field}:${field.direction}`),
12070
+ sumField: index.sumField ?? ""
12018
12071
  })).sort((a, b) => a.name.localeCompare(b.name))
12019
12072
  })).sort((a, b) => a.tableName.localeCompare(b.tableName));
12020
12073
  }
12021
- async function computeAggregateIndexFingerprint(functionsDir) {
12074
+ async function loadBackendSchemaValue(functionsDir) {
12022
12075
  const schemaPath = join(functionsDir, "schema.ts");
12023
12076
  if (!fs.existsSync(schemaPath)) return null;
12024
12077
  const schemaModule = await createProjectJiti().import(schemaPath);
12025
12078
  if (!schemaModule || typeof schemaModule !== "object") return null;
12026
- const schemaValue = resolveSchemaDefaultExport(schemaModule);
12079
+ return resolveSchemaDefaultExport(schemaModule);
12080
+ }
12081
+ /**
12082
+ * Whether the deployed ORM can serve the aggregate procedures at all.
12083
+ *
12084
+ * `kitcn codegen` registers `aggregateCapability()` only for schemas that
12085
+ * declare an `aggregateIndex`/`rankIndex`, so calling `aggregateBackfill`
12086
+ * against a schema without one is a guaranteed error rather than a no-op.
12087
+ *
12088
+ * No schema file means no ORM and no aggregate procedures to call. A schema
12089
+ * that exists but fails to load is treated as using them, so a real backfill
12090
+ * is never skipped over a transient load error.
12091
+ */
12092
+ async function backendUsesAggregateIndexes() {
12093
+ try {
12094
+ const { functionsDir } = getConvexConfig();
12095
+ const schemaValue = await loadBackendSchemaValue(functionsDir);
12096
+ if (!schemaValue) return false;
12097
+ return schemaDeclaresAggregateIndexes(schemaValue);
12098
+ } catch {
12099
+ return true;
12100
+ }
12101
+ }
12102
+ /**
12103
+ * Whether the deployed ORM registered `migrationCapability()`. Codegen keys
12104
+ * that off the generated manifest, so the flows key off the same file.
12105
+ */
12106
+ function backendUsesMigrations() {
12107
+ try {
12108
+ const { functionsDir } = getConvexConfig();
12109
+ return fs.existsSync(join(functionsDir, "migrations", "manifest.ts"));
12110
+ } catch {
12111
+ return true;
12112
+ }
12113
+ }
12114
+ async function computeAggregateIndexFingerprint(functionsDir) {
12115
+ const schemaValue = await loadBackendSchemaValue(functionsDir);
12027
12116
  if (!schemaValue) return null;
12028
12117
  const tables = collectSchemaTables(schemaValue);
12029
- if (tables.length === 0) return null;
12118
+ if (tables.length === 0 || !schemaDeclaresAggregateIndexes(schemaValue)) return null;
12030
12119
  const payload = buildAggregateFingerprintPayload(tables);
12031
12120
  return createHash("sha256").update(JSON.stringify(payload)).digest("hex");
12032
12121
  }
@@ -12924,7 +13013,8 @@ async function runAggregateBackfillFlow(params) {
12924
13013
  const { execaFn, backendAdapter, backfillConfig, mode, targetArgs, env, signal, context } = params;
12925
13014
  if (signal?.aborted) return 0;
12926
13015
  if (backfillConfig.enabled === "off") return 0;
12927
- const kickoff = await runBackendFunction(execaFn, backendAdapter, "generated/server:aggregateBackfill", {
13016
+ if (!await backendUsesAggregateIndexes()) return 0;
13017
+ const kickoff = await runBackendFunction(execaFn, backendAdapter, "generated/aggregate:aggregateBackfill", {
12928
13018
  mode,
12929
13019
  batchSize: backfillConfig.batchSize
12930
13020
  }, targetArgs, {
@@ -12957,7 +13047,7 @@ async function runAggregateBackfillFlow(params) {
12957
13047
  const deadline = Date.now() + backfillConfig.timeoutMs;
12958
13048
  let lastProgress = "";
12959
13049
  while (!signal?.aborted) {
12960
- const statusResult = await runBackendFunction(execaFn, backendAdapter, "generated/server:aggregateBackfillStatus", {}, targetArgs, {
13050
+ const statusResult = await runBackendFunction(execaFn, backendAdapter, "generated/aggregate:aggregateBackfillStatus", {}, targetArgs, {
12961
13051
  echoOutput: false,
12962
13052
  env
12963
13053
  });
@@ -12991,7 +13081,7 @@ async function runAggregateBackfillFlow(params) {
12991
13081
  }
12992
13082
  async function runAggregatePruneFlow(params) {
12993
13083
  const { execaFn, backendAdapter, targetArgs, env } = params;
12994
- const result = await runBackendFunction(execaFn, backendAdapter, "generated/server:aggregateBackfill", { mode: "prune" }, targetArgs, {
13084
+ const result = await runBackendFunction(execaFn, backendAdapter, "generated/aggregate:aggregateBackfill", { mode: "prune" }, targetArgs, {
12995
13085
  echoOutput: false,
12996
13086
  env
12997
13087
  });
@@ -13102,6 +13192,7 @@ export const migration = defineMigration({
13102
13192
  async function runMigrationFlow(params) {
13103
13193
  const { execaFn, backendAdapter, migrationConfig, targetArgs, env, signal, context, direction, steps, to } = params;
13104
13194
  if (signal?.aborted || migrationConfig.enabled === "off") return 0;
13195
+ if (!backendUsesMigrations()) return 0;
13105
13196
  const kickoff = await runBackendFunction(execaFn, backendAdapter, "generated/server:migrationRun", {
13106
13197
  direction,
13107
13198
  batchSize: migrationConfig.batchSize,
@@ -15318,6 +15409,10 @@ const handleMigrateCommand = async (argv, deps = {}) => {
15318
15409
  to
15319
15410
  });
15320
15411
  }
15412
+ if (!backendUsesMigrations()) {
15413
+ logger.info("No migrations manifest. Run `kitcn migrate create <name>` to add one.");
15414
+ return 0;
15415
+ }
15321
15416
  if (migrateArgs.subcommand === "status") return (await runBackendFunction(execaFn, backendAdapter, "generated/server:migrationStatus", {}, targetArgs, { env: commandEnv })).exitCode;
15322
15417
  let runId;
15323
15418
  const cancelArgs = [];
@@ -1,4 +1,4 @@
1
- import { r as omit } from "./upstream-BR6sBLg3.js";
1
+ import { r as omit } from "./upstream-BCgGZX6q.js";
2
2
  import { createAuthEndpoint, createAuthMiddleware, sessionMiddleware } from "better-auth/api";
3
3
  import { bearer } from "better-auth/plugins/bearer";
4
4
  import { jwt } from "better-auth/plugins/jwt";
@@ -1,4 +1,4 @@
1
- import { augmentBetterAuthTables, indexFields } from "./create-schema-BXrKE2YY.js";
1
+ import { augmentBetterAuthTables, indexFields } from "./create-schema-BRFjdgRr.js";
2
2
 
3
3
  //#region src/auth/create-schema-orm.ts
4
4
  const specialFields = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => {
@@ -1,5 +1,5 @@
1
- import { A as DataTransformer, F as decodeWire, I as defaultCRPCTransformer, L as encodeWire, M as WireCodec, N as createTaggedTransformer, O as CombinedDataTransformer, P as dateWireCodec, R as getTransformer, a as HttpProcedureCall, c as InferHttpInput, i as HttpErrorCode, j as DataTransformerOptions, k as DATE_CODEC_TAG, l as InferHttpOutput, n as HttpClientError, o as HttpRouteInfo, r as HttpClientFromRouter, s as HttpRouteMap, t as HttpClient, u as isHttpClientError, z as identityTransformer } from "../http-types-BoSDAh4Y.js";
2
- import { A as HttpInputArgs, C as ReservedMutationOptions, D as VanillaMutation, E as VanillaAction, F as replaceUrlParam, M as RESERVED_KEYS, N as buildSearchParams, O as HttpClientOptions, P as executeHttpRequest, S as ReservedInfiniteQueryOptions, T as StaticQueryOptsParam, _ as IsPaginated, a as BaseInfiniteQueryOptsParam, b as PaginatedFnMeta, c as ConvexMutationKey, d as ConvexQueryMeta, f as EmptyObject, g as InfiniteQueryInput, h as FnMeta, i as BaseConvexQueryOptions, j as HttpProxyBaseOptions, k as HttpFormValue, l as ConvexQueryHookOptions, m as FUNC_REF_SYMBOL, n as BaseConvexActionOptions, o as ConvexActionKey, p as ExtractPaginatedItem, r as BaseConvexInfiniteQueryOptions, s as ConvexInfiniteQueryMeta, t as AuthType, u as ConvexQueryKey, v as Meta, w as ReservedQueryOptions, x as PaginationOpts, y as MutationVariables } from "../types-C0Xl7P8K.js";
1
+ import { B as dateWireCodec, F as DATE_CODEC_TAG, G as identityTransformer, H as defaultCRPCTransformer, I as DataTransformer, L as DataTransformerOptions, P as CombinedDataTransformer, R as WireCodec, U as encodeWire, V as decodeWire, W as getTransformer, a as HttpErrorCode, c as HttpQueryKey, d as HttpRouteMap, f as InferHttpInput, g as isHttpClientError, h as buildHttpQueryPrefixKey, i as HttpClientFromRouter, l as HttpQueryPrefixKey, m as buildHttpQueryKey, n as HttpClient, o as HttpMutationKey, p as InferHttpOutput, r as HttpClientError, s as HttpProcedureCall, t as HTTP_DEFAULT_STALE_TIME, u as HttpRouteInfo, z as createTaggedTransformer } from "../http-types-90mapMP9.js";
2
+ import { A as HttpInputArgs, C as ReservedMutationOptions, D as VanillaMutation, E as VanillaAction, F as replaceUrlParam, M as RESERVED_KEYS, N as buildSearchParams, O as HttpClientOptions, P as executeHttpRequest, S as ReservedInfiniteQueryOptions, T as StaticQueryOptsParam, _ as IsPaginated, a as BaseInfiniteQueryOptsParam, b as PaginatedFnMeta, c as ConvexMutationKey, d as ConvexQueryMeta, f as EmptyObject, g as InfiniteQueryInput, h as FnMeta, i as BaseConvexQueryOptions, j as HttpProxyBaseOptions, k as HttpFormValue, l as ConvexQueryHookOptions, m as FUNC_REF_SYMBOL, n as BaseConvexActionOptions, o as ConvexActionKey, p as ExtractPaginatedItem, r as BaseConvexInfiniteQueryOptions, s as ConvexInfiniteQueryMeta, t as AuthType, u as ConvexQueryKey, v as Meta, w as ReservedQueryOptions, x as PaginationOpts, y as MutationVariables } from "../types-In-ZZOsd.js";
3
3
  import { FunctionArgs, FunctionReference } from "convex/server";
4
4
 
5
5
  //#region src/crpc/auth-error.d.ts
@@ -102,4 +102,4 @@ declare function convexAction<T extends FunctionReference<'action'>>(funcRef: T,
102
102
  */
103
103
  declare function convexInfiniteQueryOptions<T extends FunctionReference<'query'>>(funcRef: T, args: Record<string, unknown> | 'skip', opts?: BaseInfiniteQueryOptsParam<T> & Record<string, unknown>, meta?: Meta): BaseConvexInfiniteQueryOptions<T> & Record<string, unknown>;
104
104
  //#endregion
105
- export { AuthMutationError, AuthType, BaseConvexActionOptions, BaseConvexInfiniteQueryOptions, BaseConvexQueryOptions, BaseInfiniteQueryOptsParam, CRPCClientError, CombinedDataTransformer, ConvexActionKey, ConvexInfiniteQueryMeta, ConvexMutationKey, ConvexQueryHookOptions, ConvexQueryKey, ConvexQueryMeta, DATE_CODEC_TAG, DataTransformer, DataTransformerOptions, EmptyObject, ExtractPaginatedItem, FUNC_REF_SYMBOL, FnMeta, HttpClient, HttpClientError, HttpClientFromRouter, HttpClientOptions, HttpErrorCode, HttpFormValue, HttpInputArgs, HttpProcedureCall, HttpProxyBaseOptions, HttpRouteInfo, HttpRouteMap, InferHttpInput, InferHttpOutput, InfiniteQueryInput, IsPaginated, Meta, MutationVariables, PaginatedFnMeta, PaginationOpts, RESERVED_KEYS, ReservedInfiniteQueryOptions, ReservedMutationOptions, ReservedQueryOptions, StaticQueryOptsParam, VanillaAction, VanillaMutation, WireCodec, buildSearchParams, convexAction, convexInfiniteQueryOptions, convexQuery, createTaggedTransformer, dateWireCodec, decodeWire, defaultCRPCTransformer, defaultIsUnauthorized, encodeWire, executeHttpRequest, getTransformer, identityTransformer, isAuthMutationError, isCRPCClientError, isCRPCError, isCRPCErrorCode, isHttpClientError, replaceUrlParam };
105
+ export { AuthMutationError, AuthType, BaseConvexActionOptions, BaseConvexInfiniteQueryOptions, BaseConvexQueryOptions, BaseInfiniteQueryOptsParam, CRPCClientError, CombinedDataTransformer, ConvexActionKey, ConvexInfiniteQueryMeta, ConvexMutationKey, ConvexQueryHookOptions, ConvexQueryKey, ConvexQueryMeta, DATE_CODEC_TAG, DataTransformer, DataTransformerOptions, EmptyObject, ExtractPaginatedItem, FUNC_REF_SYMBOL, FnMeta, HTTP_DEFAULT_STALE_TIME, HttpClient, HttpClientError, HttpClientFromRouter, HttpClientOptions, HttpErrorCode, HttpFormValue, HttpInputArgs, HttpMutationKey, HttpProcedureCall, HttpProxyBaseOptions, HttpQueryKey, HttpQueryPrefixKey, HttpRouteInfo, HttpRouteMap, InferHttpInput, InferHttpOutput, InfiniteQueryInput, IsPaginated, Meta, MutationVariables, PaginatedFnMeta, PaginationOpts, RESERVED_KEYS, ReservedInfiniteQueryOptions, ReservedMutationOptions, ReservedQueryOptions, StaticQueryOptsParam, VanillaAction, VanillaMutation, WireCodec, buildHttpQueryKey, buildHttpQueryPrefixKey, buildSearchParams, convexAction, convexInfiniteQueryOptions, convexQuery, createTaggedTransformer, dateWireCodec, decodeWire, defaultCRPCTransformer, defaultIsUnauthorized, encodeWire, executeHttpRequest, getTransformer, identityTransformer, isAuthMutationError, isCRPCClientError, isCRPCError, isCRPCErrorCode, isHttpClientError, replaceUrlParam };
@@ -1,6 +1,6 @@
1
- import { a as isCRPCErrorCode, i as isCRPCError, n as defaultIsUnauthorized, r as isCRPCClientError, t as CRPCClientError } from "../error-Bvo7YEhk.js";
2
- import { a as defaultCRPCTransformer, c as identityTransformer, i as decodeWire, n as createTaggedTransformer, o as encodeWire, r as dateWireCodec, s as getTransformer, t as DATE_CODEC_TAG } from "../transformer-yZuBWo8v.js";
3
- import { a as buildSearchParams, c as HttpClientError, i as RESERVED_KEYS, l as isHttpClientError, n as convexInfiniteQueryOptions, o as executeHttpRequest, r as convexQuery, s as replaceUrlParam, t as convexAction } from "../query-options-C_eBSIXG.js";
1
+ import { a as isCRPCErrorCode, i as isCRPCError, n as defaultIsUnauthorized, r as isCRPCClientError, t as CRPCClientError } from "../error-CMLeCadS.js";
2
+ import { a as defaultCRPCTransformer, c as identityTransformer, i as decodeWire, n as createTaggedTransformer, o as encodeWire, r as dateWireCodec, s as getTransformer, t as DATE_CODEC_TAG } from "../transformer-D8wO-kEj.js";
3
+ import { a as buildSearchParams, c as HTTP_DEFAULT_STALE_TIME, d as buildHttpQueryPrefixKey, f as isHttpClientError, i as RESERVED_KEYS, l as HttpClientError, n as convexInfiniteQueryOptions, o as executeHttpRequest, r as convexQuery, s as replaceUrlParam, t as convexAction, u as buildHttpQueryKey } from "../query-options-DUiOj01a.js";
4
4
 
5
5
  //#region src/crpc/auth-error.ts
6
6
  /**
@@ -40,4 +40,4 @@ function isAuthMutationError(error) {
40
40
  const FUNC_REF_SYMBOL = Symbol.for("convex.funcRef");
41
41
 
42
42
  //#endregion
43
- export { AuthMutationError, CRPCClientError, DATE_CODEC_TAG, FUNC_REF_SYMBOL, HttpClientError, RESERVED_KEYS, buildSearchParams, convexAction, convexInfiniteQueryOptions, convexQuery, createTaggedTransformer, dateWireCodec, decodeWire, defaultCRPCTransformer, defaultIsUnauthorized, encodeWire, executeHttpRequest, getTransformer, identityTransformer, isAuthMutationError, isCRPCClientError, isCRPCError, isCRPCErrorCode, isHttpClientError, replaceUrlParam };
43
+ export { AuthMutationError, CRPCClientError, DATE_CODEC_TAG, FUNC_REF_SYMBOL, HTTP_DEFAULT_STALE_TIME, HttpClientError, RESERVED_KEYS, buildHttpQueryKey, buildHttpQueryPrefixKey, buildSearchParams, convexAction, convexInfiniteQueryOptions, convexQuery, createTaggedTransformer, dateWireCodec, decodeWire, defaultCRPCTransformer, defaultIsUnauthorized, encodeWire, executeHttpRequest, getTransformer, identityTransformer, isAuthMutationError, isCRPCClientError, isCRPCError, isCRPCErrorCode, isHttpClientError, replaceUrlParam };
@@ -1,5 +1,5 @@
1
- import { i as pick, r as omit } from "./upstream-BR6sBLg3.js";
2
- import { t as addFieldsToValidator } from "./validators-C7LelqTN.js";
1
+ import { i as pick, r as omit } from "./upstream-BCgGZX6q.js";
2
+ import { t as addFieldsToValidator } from "./validators-Dmbhsd3U.js";
3
3
 
4
4
  //#region src/internal/upstream/server/customFunctions.ts
5
5
  /**
@@ -0,0 +1,215 @@
1
+ import { A as integer, C as index, I as ConvexColumnBuilder, L as entityKind, M as custom, O as text, t as convexTable } from "./table-rmJm5Qio.js";
2
+ import { t as defineSchemaExtension } from "./extensions-DzjCJXYl.js";
3
+ import { v } from "convex/values";
4
+
5
+ //#region src/orm/builders/boolean.ts
6
+ /**
7
+ * Boolean column builder class
8
+ * Compiles to v.boolean() or v.optional(v.boolean())
9
+ */
10
+ var ConvexBooleanBuilder = class extends ConvexColumnBuilder {
11
+ static [entityKind] = "ConvexBooleanBuilder";
12
+ constructor(name) {
13
+ super(name, "boolean", "ConvexBoolean");
14
+ }
15
+ /**
16
+ * Expose Convex validator for schema integration
17
+ */
18
+ get convexValidator() {
19
+ if (this.config.notNull) return v.boolean();
20
+ return v.optional(v.union(v.null(), v.boolean()));
21
+ }
22
+ /**
23
+ * Compile to Convex validator
24
+ * .notNull() → v.boolean()
25
+ * nullable → v.optional(v.boolean())
26
+ */
27
+ build() {
28
+ return this.convexValidator;
29
+ }
30
+ };
31
+ function boolean(name) {
32
+ return new ConvexBooleanBuilder(name ?? "");
33
+ }
34
+
35
+ //#endregion
36
+ //#region src/orm/migrations/schema.ts
37
+ const MIGRATION_STATE_TABLE = "migration_state";
38
+ const MIGRATION_RUN_TABLE = "migration_run";
39
+ const migrationStateTable = convexTable(MIGRATION_STATE_TABLE, {
40
+ migrationId: text().notNull(),
41
+ checksum: text().notNull(),
42
+ applied: boolean().notNull(),
43
+ status: text().notNull(),
44
+ direction: text(),
45
+ runId: text(),
46
+ cursor: text(),
47
+ processed: integer().notNull(),
48
+ startedAt: integer(),
49
+ updatedAt: integer().notNull(),
50
+ completedAt: integer(),
51
+ lastError: text(),
52
+ writeMode: text().notNull()
53
+ }, (t) => [index("by_migration_id").on(t.migrationId), index("by_status").on(t.status)]);
54
+ const migrationRunTable = convexTable(MIGRATION_RUN_TABLE, {
55
+ runId: text().notNull(),
56
+ direction: text().notNull(),
57
+ status: text().notNull(),
58
+ dryRun: boolean().notNull(),
59
+ allowDrift: boolean().notNull(),
60
+ migrationIds: custom(v.array(v.string())).notNull(),
61
+ currentIndex: integer().notNull(),
62
+ startedAt: integer().notNull(),
63
+ updatedAt: integer().notNull(),
64
+ completedAt: integer(),
65
+ cancelRequested: boolean().notNull(),
66
+ lastError: text()
67
+ }, (t) => [index("by_run_id").on(t.runId), index("by_status").on(t.status)]);
68
+ const migrationStorageTables = {
69
+ [MIGRATION_STATE_TABLE]: migrationStateTable,
70
+ [MIGRATION_RUN_TABLE]: migrationRunTable
71
+ };
72
+ const MIGRATION_STORAGE_TABLE_NAMES = new Set([MIGRATION_STATE_TABLE, MIGRATION_RUN_TABLE]);
73
+ function migrationExtension() {
74
+ return defineSchemaExtension("migration", migrationStorageTables);
75
+ }
76
+ function injectMigrationStorageTables(schema) {
77
+ const merged = { ...schema };
78
+ for (const [tableName, tableDef] of Object.entries(migrationStorageTables)) {
79
+ if (tableName in schema && schema[tableName] !== tableDef) throw new Error(`defineSchema cannot inject internal table '${tableName}' because the name is already in use.`);
80
+ merged[tableName] = tableDef;
81
+ }
82
+ return merged;
83
+ }
84
+
85
+ //#endregion
86
+ //#region src/orm/migrations/definitions.ts
87
+ const MIGRATION_ID_RE = /^[a-zA-Z0-9_:-]+$/;
88
+ const FUNCTION_SOURCE_WHITESPACE_RE = /\s+/g;
89
+ const FUNCTION_SOURCE_PUNCTUATION_SPACE_RE = /\s*([{}();,:])\s*/g;
90
+ function defineMigration(migration) {
91
+ validateMigrationId(migration.id);
92
+ validateMigrationStep("up", migration.up);
93
+ if (migration.down) validateMigrationStep("down", migration.down);
94
+ return migration;
95
+ }
96
+ function defineMigrationSet(migrations) {
97
+ const normalized = [...migrations].map((migration) => {
98
+ const defined = defineMigration(migration);
99
+ return {
100
+ ...defined,
101
+ checksum: defined.checksum ?? computeMigrationChecksum(defined)
102
+ };
103
+ });
104
+ normalized.sort((a, b) => a.id.localeCompare(b.id));
105
+ const byId = {};
106
+ for (const migration of normalized) {
107
+ if (byId[migration.id]) throw new Error(`defineMigrationSet received duplicate migration id '${migration.id}'.`);
108
+ byId[migration.id] = migration;
109
+ }
110
+ return {
111
+ migrations: normalized,
112
+ ids: normalized.map((migration) => migration.id),
113
+ byId
114
+ };
115
+ }
116
+ function detectMigrationDrift(params) {
117
+ const { migrationSet, appliedState } = params;
118
+ const issues = [];
119
+ for (const [migrationId, state] of Object.entries(appliedState)) {
120
+ if (!state.applied) continue;
121
+ const migration = migrationSet.byId[migrationId];
122
+ if (!migration) {
123
+ issues.push({
124
+ kind: "missing_from_manifest",
125
+ migrationId,
126
+ message: `Applied migration '${migrationId}' is missing from the current migration manifest.`
127
+ });
128
+ continue;
129
+ }
130
+ if (state.checksum && state.checksum !== migration.checksum) issues.push({
131
+ kind: "checksum_mismatch",
132
+ migrationId,
133
+ expectedChecksum: migration.checksum,
134
+ actualChecksum: state.checksum,
135
+ message: `Applied migration '${migrationId}' checksum drift detected.`
136
+ });
137
+ }
138
+ return issues;
139
+ }
140
+ function buildMigrationPlan(params) {
141
+ const { direction, migrationSet, appliedState, steps, to } = params;
142
+ if (direction === "up") return {
143
+ direction,
144
+ migrations: migrationSet.migrations.filter((migration) => !appliedState[migration.id]?.applied)
145
+ };
146
+ if (steps !== void 0 && to !== void 0) throw new Error("Use either down steps or down to, not both.");
147
+ const appliedInOrder = migrationSet.migrations.filter((migration) => appliedState[migration.id]?.applied);
148
+ const ensureDownSteps = (selected) => {
149
+ const missingDown = selected.find((migration) => !migration.down);
150
+ if (missingDown) throw new Error(`Cannot execute down migration for '${missingDown.id}': missing down migration handler.`);
151
+ return selected;
152
+ };
153
+ if (to) {
154
+ const targetIndex = migrationSet.ids.indexOf(to);
155
+ if (targetIndex === -1) throw new Error(`Unknown migration id '${to}' for down --to.`);
156
+ return {
157
+ direction,
158
+ migrations: ensureDownSteps(appliedInOrder.filter((migration) => {
159
+ return migrationSet.ids.indexOf(migration.id) > targetIndex;
160
+ })).reverse()
161
+ };
162
+ }
163
+ const resolvedSteps = steps ?? 1;
164
+ if (!Number.isInteger(resolvedSteps) || resolvedSteps < 1) throw new Error("Down steps must be a positive integer.");
165
+ return {
166
+ direction,
167
+ migrations: ensureDownSteps(appliedInOrder.slice(-resolvedSteps).reverse())
168
+ };
169
+ }
170
+ function validateMigrationId(id) {
171
+ if (!id || typeof id !== "string") throw new Error("Migration id must be a non-empty string.");
172
+ if (!MIGRATION_ID_RE.test(id)) throw new Error(`Migration id '${id}' is invalid. Use alphanumeric characters, '_' ':' or '-'.`);
173
+ }
174
+ function validateMigrationStep(direction, step) {
175
+ if (!step || typeof step !== "object") throw new Error(`Migration ${direction} step must be an object.`);
176
+ if (!step.table || typeof step.table !== "string") throw new Error(`Migration ${direction} step.table must be a string.`);
177
+ if (typeof step.migrateOne !== "function") throw new Error(`Migration ${direction} step.migrateOne must be a function.`);
178
+ if (step.batchSize !== void 0 && (!Number.isInteger(step.batchSize) || step.batchSize < 1)) throw new Error(`Migration ${direction} step.batchSize must be a positive integer.`);
179
+ if (step.writeMode !== void 0 && step.writeMode !== "safe_bypass" && step.writeMode !== "normal") throw new Error(`Migration ${direction} step.writeMode must be 'safe_bypass' or 'normal'.`);
180
+ }
181
+ function computeMigrationChecksum(migration) {
182
+ return simpleStableHash(JSON.stringify({
183
+ id: migration.id,
184
+ name: migration.name ?? null,
185
+ description: migration.description ?? null,
186
+ up: serializeStep(migration.up),
187
+ down: migration.down ? serializeStep(migration.down) : null
188
+ }));
189
+ }
190
+ function serializeStep(step) {
191
+ return {
192
+ table: step.table,
193
+ batchSize: step.batchSize ?? null,
194
+ writeMode: step.writeMode ?? "safe_bypass",
195
+ source: normalizeFunctionSource(step.migrateOne)
196
+ };
197
+ }
198
+ function normalizeFunctionSource(fn) {
199
+ return fn.toString().replace(FUNCTION_SOURCE_WHITESPACE_RE, " ").replace(FUNCTION_SOURCE_PUNCTUATION_SPACE_RE, "$1").trim();
200
+ }
201
+ function simpleStableHash(value) {
202
+ let hashA = 2166136261;
203
+ let hashB = 16777619;
204
+ for (let i = 0; i < value.length; i += 1) {
205
+ const code = value.charCodeAt(i);
206
+ hashA ^= code;
207
+ hashA = Math.imul(hashA, 16777619);
208
+ hashB ^= code + i;
209
+ hashB = Math.imul(hashB, 2246822507);
210
+ }
211
+ return `m_${(hashA >>> 0).toString(16).padStart(8, "0")}${(hashB >>> 0).toString(16).padStart(8, "0")}`;
212
+ }
213
+
214
+ //#endregion
215
+ export { MIGRATION_RUN_TABLE as a, injectMigrationStorageTables as c, boolean as d, detectMigrationDrift as i, migrationExtension as l, defineMigration as n, MIGRATION_STATE_TABLE as o, defineMigrationSet as r, MIGRATION_STORAGE_TABLE_NAMES as s, buildMigrationPlan as t, migrationStorageTables as u };