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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
7448
|
-
command:
|
|
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 {
|
|
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 }) =>
|
|
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:
|
|
14873
|
-
args:
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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).
|