kitcn 0.14.2 → 0.15.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.
@@ -5964,6 +5964,51 @@ async function pullEnv(options = {}, deps = {}) {
5964
5964
  }
5965
5965
  const syncEnv = pushEnv;
5966
5966
 
5967
+ //#endregion
5968
+ //#region src/cli/package-manager.ts
5969
+ function detectPackageManagerFromUserAgent(env = process.env) {
5970
+ const userAgent = env.npm_config_user_agent?.trim().toLowerCase();
5971
+ if (!userAgent) return null;
5972
+ if (userAgent.startsWith("bun/")) return "bun";
5973
+ if (userAgent.startsWith("pnpm/")) return "pnpm";
5974
+ if (userAgent.startsWith("yarn/")) return "yarn";
5975
+ if (userAgent.startsWith("npm/")) return "npm";
5976
+ return null;
5977
+ }
5978
+ function detectPackageManager(projectDir, env = process.env) {
5979
+ let current = resolve(projectDir);
5980
+ while (true) {
5981
+ const packageJsonPath = join(current, "package.json");
5982
+ if (fs.existsSync(packageJsonPath)) try {
5983
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
5984
+ if (typeof pkg.packageManager === "string") {
5985
+ if (pkg.packageManager.startsWith("bun@")) return "bun";
5986
+ if (pkg.packageManager.startsWith("pnpm@")) return "pnpm";
5987
+ if (pkg.packageManager.startsWith("yarn@")) return "yarn";
5988
+ if (pkg.packageManager.startsWith("npm@")) return "npm";
5989
+ }
5990
+ } catch {}
5991
+ if (fs.existsSync(join(current, "bun.lock")) || fs.existsSync(join(current, "bun.lockb"))) return "bun";
5992
+ if (fs.existsSync(join(current, "pnpm-lock.yaml")) || fs.existsSync(join(current, "pnpm-workspace.yaml"))) return "pnpm";
5993
+ if (fs.existsSync(join(current, "yarn.lock"))) return "yarn";
5994
+ if (fs.existsSync(join(current, "package-lock.json"))) return "npm";
5995
+ const parent = dirname(current);
5996
+ if (parent === current) break;
5997
+ current = parent;
5998
+ }
5999
+ return detectPackageManagerFromUserAgent(env) ?? "bun";
6000
+ }
6001
+ function resolveDependencyInstallCommand(packageManager, packageSpecs) {
6002
+ return {
6003
+ command: packageManager,
6004
+ args: packageManager === "npm" ? ["install", ...packageSpecs] : ["add", ...packageSpecs]
6005
+ };
6006
+ }
6007
+ function formatDependencyInstallCommand(packageManager, packageSpecs) {
6008
+ const { args, command } = resolveDependencyInstallCommand(packageManager, packageSpecs);
6009
+ return `${command} ${args.join(" ")}`;
6010
+ }
6011
+
5967
6012
  //#endregion
5968
6013
  //#region src/cli/project-context.ts
5969
6014
  function isReactScaffoldFramework(framework) {
@@ -6188,7 +6233,7 @@ const EXACT_VERSION_RE = /^(\d+)\.(\d+)\.\d+$/;
6188
6233
  const VERSION_IN_SPEC_RE = /(\d+)\.(\d+)(?:\.\d+)?/;
6189
6234
  const PLAIN_VERSION_SPEC_RE = /^[\^~]?v?\d+\.\d+(?:\.\d+)?$/;
6190
6235
  const UPPER_BOUND_RE = /(?:^|\s)<={0,1}\s*v?(\d+)\.(\d+)(?:\.\d+)?/g;
6191
- const SUPPORTED_CONVEX_VERSION = "1.36.1";
6236
+ const SUPPORTED_CONVEX_VERSION = "1.38.0";
6192
6237
  const SUPPORTED_BETTER_AUTH_VERSION = "1.6.9";
6193
6238
  const SUPPORTED_HONO_VERSION = "4.12.9";
6194
6239
  const SUPPORTED_OPENTELEMETRY_API_VERSION = "1.9.0";
@@ -6378,6 +6423,14 @@ const resolvePackageJsonInstallTarget = () => {
6378
6423
  packageJson: packageJsonPath ? JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) : null
6379
6424
  };
6380
6425
  };
6426
+ const runDependencyInstall = async (packageJsonPath, packageSpecs, execaFn) => {
6427
+ const cwd = dirname(packageJsonPath);
6428
+ const { args, command } = resolveDependencyInstallCommand(detectPackageManager(cwd), packageSpecs);
6429
+ await execaFn(command, args, {
6430
+ cwd,
6431
+ stdio: "inherit"
6432
+ });
6433
+ };
6381
6434
  const resolveBunPeerWarningPreinstallSpecs = () => {
6382
6435
  const { packageJsonPath, packageJson } = resolvePackageJsonInstallTarget();
6383
6436
  if (!packageJsonPath || !packageJson) return [];
@@ -6392,10 +6445,7 @@ const applyBunPeerWarningPreinstall = async (execaFn) => {
6392
6445
  const dependencySpecs = resolveBunPeerWarningPreinstallSpecs();
6393
6446
  if (dependencySpecs.length === 0) return [];
6394
6447
  const { packageJsonPath } = resolvePackageJsonInstallTarget();
6395
- await execaFn("bun", ["add", ...dependencySpecs], {
6396
- cwd: dirname(packageJsonPath),
6397
- stdio: "inherit"
6398
- });
6448
+ await runDependencyInstall(packageJsonPath, dependencySpecs, execaFn);
6399
6449
  return dependencySpecs;
6400
6450
  };
6401
6451
  const inspectPluginDependencyInstall = async (params) => {
@@ -6435,10 +6485,7 @@ const applyDependencyHintsInstall = async (dependencyHints, execaFn) => {
6435
6485
  const installSpecs = resolveMissingDependencyHints(dependencyHints).filter((dependencyHint) => !preinstalledSpecs.includes(dependencyHint)).map((dependencyHint) => resolveSupportedDependencyInstallSpec(dependencyHint));
6436
6486
  if (installSpecs.length === 0) return preinstalledSpecs;
6437
6487
  const { packageJsonPath } = resolvePackageJsonInstallTarget();
6438
- await execaFn("bun", ["add", ...installSpecs], {
6439
- cwd: dirname(packageJsonPath),
6440
- stdio: "inherit"
6441
- });
6488
+ await runDependencyInstall(packageJsonPath, installSpecs, execaFn);
6442
6489
  return [...preinstalledSpecs, ...installSpecs];
6443
6490
  };
6444
6491
  const applyPlanningDependencyInstall = async (dependencySpecs, execaFn) => {
@@ -6446,20 +6493,14 @@ const applyPlanningDependencyInstall = async (dependencySpecs, execaFn) => {
6446
6493
  const installSpecs = resolveMissingDependencyHints(dependencySpecs).filter((dependencySpec) => !preinstalledSpecs.includes(dependencySpec)).map((dependencySpec) => resolveSupportedDependencyInstallSpec(dependencySpec));
6447
6494
  if (installSpecs.length === 0) return preinstalledSpecs;
6448
6495
  const { packageJsonPath } = resolvePackageJsonInstallTarget();
6449
- await execaFn("bun", ["add", ...installSpecs], {
6450
- cwd: dirname(packageJsonPath),
6451
- stdio: "inherit"
6452
- });
6496
+ await runDependencyInstall(packageJsonPath, installSpecs, execaFn);
6453
6497
  return [...preinstalledSpecs, ...installSpecs];
6454
6498
  };
6455
6499
  const applyPluginDependencyInstall = async (install, execaFn) => {
6456
6500
  if (install.skipped || !install.packageName || !install.packageJsonPath) return install;
6457
6501
  await applyBunPeerWarningPreinstall(execaFn);
6458
6502
  const packageSpec = install.packageSpec ?? install.packageName;
6459
- await execaFn("bun", ["add", packageSpec], {
6460
- cwd: dirname(install.packageJsonPath),
6461
- stdio: "inherit"
6462
- });
6503
+ await runDependencyInstall(install.packageJsonPath, [packageSpec], execaFn);
6463
6504
  return {
6464
6505
  packageName: install.packageName,
6465
6506
  packageSpec,
@@ -7361,8 +7402,10 @@ const buildPluginInstallPlan = async (params) => {
7361
7402
  scaffoldFiles
7362
7403
  }) ?? scaffoldFiles;
7363
7404
  const dependency = await inspectPluginDependencyInstall({ descriptor: params.descriptor });
7405
+ const dependencyPackageSpec = dependency.packageSpec ?? dependency.packageName;
7364
7406
  const dependencyHints = [...new Set(effectiveTemplates.flatMap((template) => template.dependencyHints))];
7365
- const dependencyHintCommand = dependencyHints.length > 0 ? `bun add ${dependencyHints.join(" ")}` : void 0;
7407
+ const dependencyPackageManager = detectPackageManager(dependency.packageJsonPath ? dirname(dependency.packageJsonPath) : process.cwd());
7408
+ const dependencyHintCommand = dependencyHints.length > 0 ? formatDependencyInstallCommand(dependencyPackageManager, dependencyHints) : void 0;
7366
7409
  const envReminders = rawConvexAuthPreset ? [] : resolvePluginEnvReminders(params.functionsDir, params.descriptor.envFields ?? []);
7367
7410
  const nextSteps = dependencyHintCommand ? [`Install scaffold dependencies: ${dependencyHintCommand}`] : [];
7368
7411
  const codegenCommand = rawConvexAuthPreset ? "kitcn codegen --scope auth" : "kitcn codegen";
@@ -7444,8 +7487,8 @@ const buildPluginInstallPlan = async (params) => {
7444
7487
  status: dependency.skipped ? "skipped" : "pending",
7445
7488
  reason: dependency.reason === "already_present" ? "Dependency already installed." : dependency.reason === "missing_package_json" ? "No package.json found for dependency installation." : `Install ${dependency.packageSpec ?? dependency.packageName}.`,
7446
7489
  path: dependency.packageJsonPath ? normalizePath$1(relative(process.cwd(), dependency.packageJsonPath)) : void 0,
7447
- packageName: dependency.packageSpec ?? dependency.packageName,
7448
- command: (dependency.packageSpec ?? dependency.packageName) && dependency.packageJsonPath && !dependency.skipped ? `bun add ${dependency.packageSpec ?? dependency.packageName}` : void 0
7490
+ packageName: dependencyPackageSpec,
7491
+ command: dependencyPackageSpec && dependency.packageJsonPath && !dependency.skipped ? formatDependencyInstallCommand(dependencyPackageManager, [dependencyPackageSpec]) : void 0
7449
7492
  },
7450
7493
  {
7451
7494
  kind: "codegen",
@@ -10635,8 +10678,7 @@ const authRegistryItem = defineInternalRegistryItem({
10635
10678
  //#endregion
10636
10679
  //#region src/cli/registry/items/ratelimit/ratelimit-plugin.template.ts
10637
10680
  const FUNCTIONS_DIR_IMPORT_PLACEHOLDER$4 = "__KITCN_FUNCTIONS_DIR__";
10638
- const RATELIMIT_PLUGIN_TEMPLATE = `import { getSessionNetworkSignals } from "kitcn/auth";
10639
- import { MINUTE, Ratelimit, RatelimitPlugin } from "kitcn/ratelimit";
10681
+ const RATELIMIT_PLUGIN_TEMPLATE = `import { MINUTE, Ratelimit, RatelimitPlugin } from "kitcn/ratelimit";
10640
10682
  import type { MutationCtx } from "${FUNCTIONS_DIR_IMPORT_PLACEHOLDER$4}/generated/server";
10641
10683
 
10642
10684
  const fixed = (rate: number) => Ratelimit.fixedWindow(rate, MINUTE);
@@ -10677,6 +10719,15 @@ export function getUserTier(user: RatelimitUser | null): RatelimitTier {
10677
10719
  return "free";
10678
10720
  }
10679
10721
 
10722
+ async function getRequestSignals(ctx: RatelimitCtx) {
10723
+ const { ip, userAgent } = await ctx.meta.getRequestMetadata();
10724
+
10725
+ return {
10726
+ ...(ip ? { ip } : {}),
10727
+ ...(userAgent ? { userAgent } : {}),
10728
+ };
10729
+ }
10730
+
10680
10731
  export const ratelimit = RatelimitPlugin.configure({
10681
10732
  buckets: ratelimitBuckets,
10682
10733
  getBucket: ({ meta }: { meta: RatelimitMeta }) => meta.ratelimit ?? "default",
@@ -10684,7 +10735,7 @@ export const ratelimit = RatelimitPlugin.configure({
10684
10735
  getIdentifier: ({ user }: { user: RatelimitUser | null }) =>
10685
10736
  user?.id ?? "anonymous",
10686
10737
  getTier: getUserTier,
10687
- getSignals: ({ ctx }: { ctx: RatelimitCtx }) => getSessionNetworkSignals(ctx),
10738
+ getSignals: ({ ctx }: { ctx: RatelimitCtx }) => getRequestSignals(ctx),
10688
10739
  prefix: ({ bucket, tier }) => \`ratelimit:\${bucket}:\${tier}\`,
10689
10740
  failureMode: "closed",
10690
10741
  enableProtection: true,
@@ -14821,29 +14872,6 @@ function buildTemplateInitializationPlanFiles(params) {
14821
14872
  buildInitReactMainPlanFile(projectContext)
14822
14873
  ];
14823
14874
  }
14824
- function detectPackageManager(projectDir) {
14825
- let current = resolve(projectDir);
14826
- while (true) {
14827
- const packageJsonPath = join(current, "package.json");
14828
- if (fs.existsSync(packageJsonPath)) try {
14829
- const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
14830
- if (typeof pkg.packageManager === "string") {
14831
- if (pkg.packageManager.startsWith("bun@")) return "bun";
14832
- if (pkg.packageManager.startsWith("pnpm@")) return "pnpm";
14833
- if (pkg.packageManager.startsWith("yarn@")) return "yarn";
14834
- if (pkg.packageManager.startsWith("npm@")) return "npm";
14835
- }
14836
- } catch {}
14837
- if (fs.existsSync(join(current, "bun.lock")) || fs.existsSync(join(current, "bun.lockb"))) return "bun";
14838
- if (fs.existsSync(join(current, "pnpm-lock.yaml")) || fs.existsSync(join(current, "pnpm-workspace.yaml"))) return "pnpm";
14839
- if (fs.existsSync(join(current, "yarn.lock"))) return "yarn";
14840
- if (fs.existsSync(join(current, "package-lock.json"))) return "npm";
14841
- const parent = dirname(current);
14842
- if (parent === current) break;
14843
- current = parent;
14844
- }
14845
- return "bun";
14846
- }
14847
14875
  function resolveShadcnScaffoldProjectDir(projectDir, template) {
14848
14876
  if (template !== "next") return projectDir;
14849
14877
  const appsDir = join(projectDir, "apps");
@@ -14867,10 +14895,11 @@ function buildDependencyInstallPlan(projectDir, dependencies) {
14867
14895
  if (missing.length === 0) return null;
14868
14896
  const packageManager = detectPackageManager(projectDir);
14869
14897
  const missingSpecs = missing.map((dependency) => dependency.installSpec);
14898
+ const installCommand = resolveDependencyInstallCommand(packageManager, missingSpecs);
14870
14899
  return {
14871
14900
  packageManager,
14872
- command: packageManager,
14873
- args: packageManager === "npm" ? ["install", ...missingSpecs] : ["add", ...missingSpecs],
14901
+ command: installCommand.command,
14902
+ args: installCommand.args,
14874
14903
  packages: missingSpecs,
14875
14904
  cwd: projectDir
14876
14905
  };
@@ -15141,7 +15170,6 @@ async function runInitCommandFlow(params) {
15141
15170
  realConcavePath: params.realConcavePath
15142
15171
  });
15143
15172
  }
15144
- if (existingProjectContext?.framework === "expo") throw new Error("Expo adoption is not supported yet. Start with `kitcn init -t expo --yes`.");
15145
15173
  if (!existingProjectContext) throw new Error("Could not detect a supported app scaffold. Use `kitcn init -t <next|expo|start|vite>` for a fresh app.");
15146
15174
  return runScaffoldCommandFlow({
15147
15175
  allowCodegenBootstrapFallback: !params.initArgs.json,
@@ -16541,4 +16569,4 @@ function isEntryPoint(entry, filename) {
16541
16569
  }
16542
16570
 
16543
16571
  //#endregion
16544
- export { promptForScaffoldTemplateSelection as $, resolveCodegenTrimSegments as A, runConfiguredCodegen as B, isEntryPoint as C, serializeEnvValue as Ct, parseInitCommandArgs as D, logger as Dt, parseBackendRunJson as E, getConvexConfig as Et, resolveRunDeps as F, runMigrationFlow as G, runDevSchemaBackfillIfNeeded as H, runAfterScaffoldScript as I, withWorkingDirectory as J, trackProcess as K, runAggregateBackfillFlow as L, resolveDocTopic as M, resolveInitProjectDir as N, readPackageVersions as O, highlighter as Ot, resolveMigrationConfig as P, promptForPluginSelection as Q, runAggregatePruneFlow as R, isConvexDevPreRunConflictFlag as S, resolveAuthEnvState as St, parseArgs as T, generateMeta as Tt, runInitCommandFlow as U, runConvexInitIfNeeded as V, runMigrationCreate as W, collectPluginScaffoldTemplates as X, createSpinner as Y, filterScaffoldTemplatePathMap as Z, formatInfoOutput as _, applyPlanningDependencyInstall as _t, cleanup as a, getPluginCatalogEntry as at, getDevAggregateBackfillStatePath as b, resolveSupportedDependencyWarnings as bt, createCommandEnv as c, buildPluginInstallPlan as ct, extractBackfillCliOptions as d, collectInstalledPluginKeys as dt, resolveAddTemplateDefaults as et, extractConcaveRunTargetArgs as f, getPluginLockfilePath as ft, formatDocsOutput as g, applyDependencyHintsInstall as gt, extractResetCliOptions as h, resolveSchemaInstalledPlugins as ht, buildInitializationPlan as i, resolveTemplatesByIdOrThrow as it, resolveConfiguredBackend as j, resolveBackfillConfig as k, ensureConvexGitignoreEntry as l, resolvePluginScaffoldRoots as lt, extractMigrationDownOptions as m, readPluginLockfile as mt, applyPluginInstallPlanFiles as n, resolvePresetScaffoldTemplates as nt, createBackendAdapter as o, getSupportedPluginKeys as ot, extractMigrationCliOptions as p, getSchemaFilePath as pt, withLocalCodegenEnv as q, assertNoRemovedDevPreRunFlag as r, resolveTemplateSelectionSource as rt, createBackendCommandEnv as s, isSupportedPluginKey as st, applyDependencyInstallPlan as t, resolvePluginPreset as tt, extractBackendRunTargetArgs as u, assertSchemaFileExists as ut, getAggregateBackfillDeploymentKey as v, applyPluginDependencyInstall as vt, isInitialized as w, stripConvexCommandNoise as wt, hasRemoteConvexDeploymentEnv as x, resolveProjectScaffoldContext as xt, getConvexDeploymentCommandEnv as y, inspectPluginDependencyInstall as yt, runBackendFunction as z };
16572
+ export { promptForScaffoldTemplateSelection as $, resolveCodegenTrimSegments as A, highlighter as At, runConfiguredCodegen as B, isEntryPoint as C, formatDependencyInstallCommand as Ct, parseInitCommandArgs as D, generateMeta as Dt, parseBackendRunJson as E, stripConvexCommandNoise as Et, resolveRunDeps as F, runMigrationFlow as G, runDevSchemaBackfillIfNeeded as H, runAfterScaffoldScript as I, withWorkingDirectory as J, trackProcess as K, runAggregateBackfillFlow as L, resolveDocTopic as M, resolveInitProjectDir as N, readPackageVersions as O, getConvexConfig as Ot, resolveMigrationConfig as P, promptForPluginSelection as Q, runAggregatePruneFlow as R, isConvexDevPreRunConflictFlag as S, detectPackageManager as St, parseArgs as T, serializeEnvValue as Tt, runInitCommandFlow as U, runConvexInitIfNeeded as V, runMigrationCreate as W, collectPluginScaffoldTemplates as X, createSpinner as Y, filterScaffoldTemplatePathMap as Z, formatInfoOutput as _, applyPlanningDependencyInstall as _t, cleanup as a, getPluginCatalogEntry as at, getDevAggregateBackfillStatePath as b, resolveSupportedDependencyWarnings as bt, createCommandEnv as c, buildPluginInstallPlan as ct, extractBackfillCliOptions as d, collectInstalledPluginKeys as dt, resolveAddTemplateDefaults as et, extractConcaveRunTargetArgs as f, getPluginLockfilePath as ft, formatDocsOutput as g, applyDependencyHintsInstall as gt, extractResetCliOptions as h, resolveSchemaInstalledPlugins as ht, buildInitializationPlan as i, resolveTemplatesByIdOrThrow as it, resolveConfiguredBackend as j, resolveBackfillConfig as k, logger as kt, ensureConvexGitignoreEntry as l, resolvePluginScaffoldRoots as lt, extractMigrationDownOptions as m, readPluginLockfile as mt, applyPluginInstallPlanFiles as n, resolvePresetScaffoldTemplates as nt, createBackendAdapter as o, getSupportedPluginKeys as ot, extractMigrationCliOptions as p, getSchemaFilePath as pt, withLocalCodegenEnv as q, assertNoRemovedDevPreRunFlag as r, resolveTemplateSelectionSource as rt, createBackendCommandEnv as s, isSupportedPluginKey as st, applyDependencyInstallPlan as t, resolvePluginPreset as tt, extractBackendRunTargetArgs as u, assertSchemaFileExists as ut, getAggregateBackfillDeploymentKey as v, applyPluginDependencyInstall as vt, isInitialized as w, resolveAuthEnvState as wt, hasRemoteConvexDeploymentEnv as x, resolveProjectScaffoldContext as xt, getConvexDeploymentCommandEnv as y, inspectPluginDependencyInstall as yt, runBackendFunction as z };
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as promptForScaffoldTemplateSelection, A as resolveCodegenTrimSegments, B as runConfiguredCodegen, C as isEntryPoint, Ct as serializeEnvValue, D as parseInitCommandArgs, Dt as logger, E as parseBackendRunJson, F as resolveRunDeps, G as runMigrationFlow, H as runDevSchemaBackfillIfNeeded, I as runAfterScaffoldScript, J as withWorkingDirectory, K as trackProcess, L as runAggregateBackfillFlow, M as resolveDocTopic, N as resolveInitProjectDir, O as readPackageVersions, Ot as highlighter, P as resolveMigrationConfig, Q as promptForPluginSelection, R as runAggregatePruneFlow, S as isConvexDevPreRunConflictFlag, St as resolveAuthEnvState, T as parseArgs, U as runInitCommandFlow, V as runConvexInitIfNeeded, W as runMigrationCreate, X as collectPluginScaffoldTemplates, Y as createSpinner, Z as filterScaffoldTemplatePathMap, _ as formatInfoOutput, _t as applyPlanningDependencyInstall, a as cleanup, at as getPluginCatalogEntry, b as getDevAggregateBackfillStatePath, bt as resolveSupportedDependencyWarnings, c as createCommandEnv, ct as buildPluginInstallPlan, d as extractBackfillCliOptions, dt as collectInstalledPluginKeys, et as resolveAddTemplateDefaults, f as extractConcaveRunTargetArgs, ft as getPluginLockfilePath, g as formatDocsOutput, gt as applyDependencyHintsInstall, h as extractResetCliOptions, ht as resolveSchemaInstalledPlugins, i as buildInitializationPlan, it as resolveTemplatesByIdOrThrow, j as resolveConfiguredBackend, k as resolveBackfillConfig, l as ensureConvexGitignoreEntry, lt as resolvePluginScaffoldRoots, m as extractMigrationDownOptions, mt as readPluginLockfile, n as applyPluginInstallPlanFiles, nt as resolvePresetScaffoldTemplates, o as createBackendAdapter, ot as getSupportedPluginKeys, p as extractMigrationCliOptions, pt as getSchemaFilePath, q as withLocalCodegenEnv, r as assertNoRemovedDevPreRunFlag, rt as resolveTemplateSelectionSource, s as createBackendCommandEnv, st as isSupportedPluginKey, t as applyDependencyInstallPlan, tt as resolvePluginPreset, u as extractBackendRunTargetArgs, ut as assertSchemaFileExists, v as getAggregateBackfillDeploymentKey, vt as applyPluginDependencyInstall, w as isInitialized, wt as stripConvexCommandNoise, x as hasRemoteConvexDeploymentEnv, xt as resolveProjectScaffoldContext, y as getConvexDeploymentCommandEnv, yt as inspectPluginDependencyInstall, z as runBackendFunction } from "./backend-core-DDs8CYH7.mjs";
2
+ import { $ as promptForScaffoldTemplateSelection, A as resolveCodegenTrimSegments, At as highlighter, B as runConfiguredCodegen, C as isEntryPoint, Ct as formatDependencyInstallCommand, D as parseInitCommandArgs, E as parseBackendRunJson, Et as stripConvexCommandNoise, F as resolveRunDeps, G as runMigrationFlow, H as runDevSchemaBackfillIfNeeded, I as runAfterScaffoldScript, J as withWorkingDirectory, K as trackProcess, L as runAggregateBackfillFlow, M as resolveDocTopic, N as resolveInitProjectDir, O as readPackageVersions, P as resolveMigrationConfig, Q as promptForPluginSelection, R as runAggregatePruneFlow, S as isConvexDevPreRunConflictFlag, St as detectPackageManager, T as parseArgs, Tt as serializeEnvValue, U as runInitCommandFlow, V as runConvexInitIfNeeded, W as runMigrationCreate, X as collectPluginScaffoldTemplates, Y as createSpinner, Z as filterScaffoldTemplatePathMap, _ as formatInfoOutput, _t as applyPlanningDependencyInstall, a as cleanup, at as getPluginCatalogEntry, b as getDevAggregateBackfillStatePath, bt as resolveSupportedDependencyWarnings, c as createCommandEnv, ct as buildPluginInstallPlan, d as extractBackfillCliOptions, dt as collectInstalledPluginKeys, et as resolveAddTemplateDefaults, f as extractConcaveRunTargetArgs, ft as getPluginLockfilePath, g as formatDocsOutput, gt as applyDependencyHintsInstall, h as extractResetCliOptions, ht as resolveSchemaInstalledPlugins, i as buildInitializationPlan, it as resolveTemplatesByIdOrThrow, j as resolveConfiguredBackend, k as resolveBackfillConfig, kt as logger, l as ensureConvexGitignoreEntry, lt as resolvePluginScaffoldRoots, m as extractMigrationDownOptions, mt as readPluginLockfile, n as applyPluginInstallPlanFiles, nt as resolvePresetScaffoldTemplates, o as createBackendAdapter, ot as getSupportedPluginKeys, p as extractMigrationCliOptions, pt as getSchemaFilePath, q as withLocalCodegenEnv, r as assertNoRemovedDevPreRunFlag, rt as resolveTemplateSelectionSource, s as createBackendCommandEnv, st as isSupportedPluginKey, t as applyDependencyInstallPlan, tt as resolvePluginPreset, u as extractBackendRunTargetArgs, ut as assertSchemaFileExists, v as getAggregateBackfillDeploymentKey, vt as applyPluginDependencyInstall, w as isInitialized, wt as resolveAuthEnvState, x as hasRemoteConvexDeploymentEnv, xt as resolveProjectScaffoldContext, y as getConvexDeploymentCommandEnv, yt as inspectPluginDependencyInstall, z as runBackendFunction } from "./backend-core-B091CyHN.mjs";
3
3
  import fs, { existsSync, readFileSync } from "node:fs";
4
4
  import path, { delimiter, dirname, join, relative, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -2765,7 +2765,11 @@ const printCommandHelp = (command, backend = "convex") => {
2765
2765
  };
2766
2766
  function warnSupportedDependencyIssues(command) {
2767
2767
  if (!DEPENDENCY_WARNING_COMMANDS.has(command)) return;
2768
- for (const warning of resolveSupportedDependencyWarnings()) logger.warn(`⚠️ kitcn expects ${warning.packageName} ${warning.minimum}; found ${warning.current}. Run \`bun add ${warning.installSpec}\` when you can.`);
2768
+ const packageManager = detectPackageManager(process.cwd());
2769
+ for (const warning of resolveSupportedDependencyWarnings()) {
2770
+ const installCommand = formatDependencyInstallCommand(packageManager, [warning.installSpec]);
2771
+ logger.warn(`⚠️ kitcn expects ${warning.packageName} ${warning.minimum}; found ${warning.current}. Run \`${installCommand}\` when you can.`);
2772
+ }
2769
2773
  }
2770
2774
  const handlePassthroughCommand = async (argv, deps) => {
2771
2775
  const parsed = parseArgs(argv);
package/dist/watcher.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Dt as logger, Et as getConvexConfig, F as resolveRunDeps, Tt as generateMeta, j as resolveConfiguredBackend, q as withLocalCodegenEnv } from "./backend-core-DDs8CYH7.mjs";
2
+ import { Dt as generateMeta, F as resolveRunDeps, Ot as getConvexConfig, j as resolveConfiguredBackend, kt as logger, q as withLocalCodegenEnv } from "./backend-core-B091CyHN.mjs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kitcn",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "description": "kitcn - React Query integration and CLI tools for Convex",
5
5
  "keywords": [
6
6
  "convex",
@@ -90,7 +90,7 @@
90
90
  "@tanstack/react-query": ">=5",
91
91
  "@tanstack/solid-query": ">=5",
92
92
  "better-auth": "1.6.9",
93
- "convex": ">=1.36",
93
+ "convex": ">=1.38",
94
94
  "hono": "4.12.9",
95
95
  "next": ">=14",
96
96
  "react": ">=18",
@@ -370,10 +370,8 @@ Create `convex/lib/plugins/ratelimit/plugin.ts` and call `ratelimit.middleware()
370
370
  Use `RatelimitPlugin` from `kitcn/ratelimit`:
371
371
 
372
372
  ```ts
373
- import { getSessionNetworkSignals } from "kitcn/auth";
374
373
  import { MINUTE, Ratelimit, RatelimitPlugin } from "kitcn/ratelimit";
375
374
  import type { MutationCtx } from "../../../functions/generated/server";
376
- import type { Select } from "../../../shared/api";
377
375
 
378
376
  const fixed = (rate: number) => Ratelimit.fixedWindow(rate, MINUTE);
379
377
 
@@ -392,7 +390,6 @@ type RatelimitUser = {
392
390
  id: string;
393
391
  isAdmin?: boolean;
394
392
  plan?: "premium" | "team" | null;
395
- session?: Select<"session"> | null;
396
393
  };
397
394
 
398
395
  type RatelimitCtx = MutationCtx & {
@@ -409,6 +406,15 @@ export function getUserTier(user: RatelimitUser | null): RatelimitTier {
409
406
  return "free";
410
407
  }
411
408
 
409
+ async function getRequestSignals(ctx: RatelimitCtx) {
410
+ const { ip, userAgent } = await ctx.meta.getRequestMetadata();
411
+
412
+ return {
413
+ ...(ip ? { ip } : {}),
414
+ ...(userAgent ? { userAgent } : {}),
415
+ };
416
+ }
417
+
412
418
  export const ratelimit = RatelimitPlugin.configure({
413
419
  buckets: ratelimitBuckets,
414
420
  getBucket: ({ meta }: { meta: RatelimitMeta }) => meta.ratelimit ?? "default",
@@ -416,13 +422,7 @@ export const ratelimit = RatelimitPlugin.configure({
416
422
  getIdentifier: ({ user }: { user: RatelimitUser | null }) =>
417
423
  user?.id ?? "anonymous",
418
424
  getTier: getUserTier,
419
- getSignals: ({
420
- ctx,
421
- user,
422
- }: {
423
- ctx: RatelimitCtx;
424
- user: RatelimitUser | null;
425
- }) => getSessionNetworkSignals(ctx, user?.session ?? null),
425
+ getSignals: ({ ctx }: { ctx: RatelimitCtx }) => getRequestSignals(ctx),
426
426
  prefix: ({ bucket, tier }) => `ratelimit:${bucket}:${tier}`,
427
427
  failureMode: "closed",
428
428
  enableProtection: true,
@@ -430,6 +430,8 @@ export const ratelimit = RatelimitPlugin.configure({
430
430
  });
431
431
  ```
432
432
 
433
+ Use `ctx.meta.getRequestMetadata()` on Convex 1.38.0+ for IP/user-agent signals in mutation rate limits. Convex also exposes this metadata in actions; route action-side enforcement through a mutation when database-backed ratelimit state is required.
434
+
433
435
  ### 9.5 Scheduling gate
434
436
 
435
437
  Create `convex/functions/crons.ts` with `cronJobs()` and use `caller.schedule.now/after/at` in mutations/actions for delayed procedure jobs (`ctx.scheduler.*` only for raw `internal.*` functions).