better-convex 0.7.2 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate/index.d.ts +1 -1
- package/dist/auth/http/index.d.ts +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/index.js +5 -4
- package/dist/auth/nextjs/index.d.ts +2 -2
- package/dist/auth/nextjs/index.js +2 -2
- package/dist/{caller-factory-D3OuR1eI.js → caller-factory-4uND4vnj.js} +2 -2
- package/dist/cli.mjs +413 -5
- package/dist/{codegen-Cz1idI3-.mjs → codegen-BS36cYTH.mjs} +88 -5
- package/dist/{create-schema-orm-69VF4CFV.js → create-schema-orm-DtuyK2RB.js} +1 -1
- package/dist/crpc/index.d.ts +2 -2
- package/dist/crpc/index.js +3 -3
- package/dist/customFunctions-C_i_0joT.js +167 -0
- package/dist/{http-types-BCf2wCgp.d.ts → http-types-BsnDV7Je.d.ts} +1 -1
- package/dist/orm/index.d.ts +4 -3
- package/dist/orm/index.js +707 -4
- package/dist/{procedure-caller-CcjtUFvL.d.ts → procedure-caller-Cj_lgUev.d.ts} +4 -19
- package/dist/rsc/index.d.ts +3 -3
- package/dist/rsc/index.js +4 -4
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +4 -3
- package/dist/{types-CIBGEYXq.d.ts → types-DZFvhoPJ.d.ts} +1 -1
- package/dist/{customFunctions-CZnCwoR3.js → validators-B7oIJCAp.js} +67 -165
- package/dist/validators-BDrWGp4M.d.ts +88 -0
- package/dist/watcher.mjs +1 -1
- package/dist/{where-clause-compiler-CRP-i1Qa.d.ts → where-clause-compiler-HUa2223D.d.ts} +106 -2
- package/package.json +1 -1
- /package/dist/{create-schema-BdZOL6ns.js → create-schema-DE9ZtH8n.js} +0 -0
- /package/dist/{error-Be4OcwwD.js → error-C7AOPlv2.js} +0 -0
- /package/dist/{meta-utils-DDVYp9Xf.js → meta-utils-C9_6WIzj.js} +0 -0
- /package/dist/{query-context-DGExXZIV.d.ts → query-context-BMXt2TKe.d.ts} +0 -0
- /package/dist/{query-context-BDSis9rT.js → query-context-yQVARct0.js} +0 -0
- /package/dist/{query-options-B0c1b6pZ.js → query-options-Bjo6j5cC.js} +0 -0
- /package/dist/{transformer-Dh0w2py0.js → transformer-BsX4RWes.js} +0 -0
- /package/dist/{types-DgwvxKbT.d.ts → types-DarApWtO.d.ts} +0 -0
- /package/dist/{types-DwGkkq2s.d.ts → types-kgwiK-xe.d.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { At as ConvexCustomBuilderInitial, Ct as ConvexIdBuilderInitial, _t as ConvexTextBuilderInitial, bt as ConvexNumberBuilderInitial, in as ConvexTableWithColumns } from "../where-clause-compiler-HUa2223D.js";
|
|
2
2
|
import * as convex_values0 from "convex/values";
|
|
3
3
|
import { GenericId, Infer, Value } from "convex/values";
|
|
4
4
|
import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as QueryCtxWithPreferredOrmQueryTable, n as LookupByIdResultByCtx, t as DocByCtx } from "../query-context-
|
|
2
|
-
import { t as GetAuth } from "../types-
|
|
3
|
-
import { A as GenericCtx } from "../procedure-caller-
|
|
4
|
-
import "../http-types-
|
|
1
|
+
import { a as QueryCtxWithPreferredOrmQueryTable, n as LookupByIdResultByCtx, t as DocByCtx } from "../query-context-BMXt2TKe.js";
|
|
2
|
+
import { t as GetAuth } from "../types-DarApWtO.js";
|
|
3
|
+
import { A as GenericCtx } from "../procedure-caller-Cj_lgUev.js";
|
|
4
|
+
import "../http-types-BsnDV7Je.js";
|
|
5
5
|
import "../server/index.js";
|
|
6
6
|
import * as convex_values0 from "convex/values";
|
|
7
7
|
import { Infer } from "convex/values";
|
package/dist/auth/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { r as partial, s as asyncMap } from "../validators-B7oIJCAp.js";
|
|
2
|
+
import { n as customCtx, o as isQueryCtx, r as customMutation, s as isRunMutationCtx } from "../customFunctions-C_i_0joT.js";
|
|
3
|
+
import { a as mergedStream, l as unsetToken, o as stream, t as getByIdWithOrmQueryFallback, v as eq } from "../query-context-yQVARct0.js";
|
|
3
4
|
import { v } from "convex/values";
|
|
4
5
|
import { internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
|
|
5
6
|
import { convex } from "@convex-dev/better-auth/plugins";
|
|
@@ -772,13 +773,13 @@ const ORM_SCHEMA_OPTIONS = Symbol.for("better-convex:OrmSchemaOptions");
|
|
|
772
773
|
const hasOrmSchemaMetadata = (schema) => !!schema && typeof schema === "object" && ORM_SCHEMA_OPTIONS in schema;
|
|
773
774
|
const createAuthSchema = async ({ file, schema, tables }) => {
|
|
774
775
|
if (hasOrmSchemaMetadata(schema)) {
|
|
775
|
-
const { createSchemaOrm } = await import("../create-schema-orm-
|
|
776
|
+
const { createSchemaOrm } = await import("../create-schema-orm-DtuyK2RB.js");
|
|
776
777
|
return createSchemaOrm({
|
|
777
778
|
file,
|
|
778
779
|
tables
|
|
779
780
|
});
|
|
780
781
|
}
|
|
781
|
-
const { createSchema } = await import("../create-schema-
|
|
782
|
+
const { createSchema } = await import("../create-schema-DE9ZtH8n.js");
|
|
782
783
|
return createSchema({
|
|
783
784
|
file,
|
|
784
785
|
tables
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as ConvexContext, H as LazyCaller } from "../../procedure-caller-
|
|
2
|
-
import "../../http-types-
|
|
1
|
+
import { B as ConvexContext, H as LazyCaller } from "../../procedure-caller-Cj_lgUev.js";
|
|
2
|
+
import "../../http-types-BsnDV7Je.js";
|
|
3
3
|
import "../../server/index.js";
|
|
4
4
|
import { GetTokenOptions } from "@convex-dev/better-auth/utils";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as defaultIsUnauthorized } from "../../error-
|
|
2
|
-
import { t as createCallerFactory } from "../../caller-factory-
|
|
1
|
+
import { n as defaultIsUnauthorized } from "../../error-C7AOPlv2.js";
|
|
2
|
+
import { t as createCallerFactory } from "../../caller-factory-4uND4vnj.js";
|
|
3
3
|
import { getToken } from "@convex-dev/better-auth/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/auth-nextjs/index.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as getFunctionType, n as getFuncRef, t as buildMetaIndex } from "./meta-utils-
|
|
2
|
-
import { s as getTransformer } from "./transformer-
|
|
1
|
+
import { i as getFunctionType, n as getFuncRef, t as buildMetaIndex } from "./meta-utils-C9_6WIzj.js";
|
|
2
|
+
import { s as getTransformer } from "./transformer-BsX4RWes.js";
|
|
3
3
|
import { fetchAction, fetchMutation, fetchQuery } from "convex/nextjs";
|
|
4
4
|
|
|
5
5
|
//#region src/server/caller.ts
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as getConvexConfig, t as generateMeta } from "./codegen-
|
|
2
|
+
import { n as getConvexConfig, t as generateMeta } from "./codegen-BS36cYTH.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import fs from "node:fs";
|
|
@@ -1784,6 +1784,15 @@ function createDefaultConfig() {
|
|
|
1784
1784
|
pollIntervalMs: 1e3,
|
|
1785
1785
|
timeoutMs: 9e5,
|
|
1786
1786
|
strict: false
|
|
1787
|
+
},
|
|
1788
|
+
migrations: {
|
|
1789
|
+
enabled: "auto",
|
|
1790
|
+
wait: true,
|
|
1791
|
+
batchSize: 256,
|
|
1792
|
+
pollIntervalMs: 1e3,
|
|
1793
|
+
timeoutMs: 9e5,
|
|
1794
|
+
strict: false,
|
|
1795
|
+
allowDrift: true
|
|
1787
1796
|
}
|
|
1788
1797
|
},
|
|
1789
1798
|
codegen: {
|
|
@@ -1799,6 +1808,15 @@ function createDefaultConfig() {
|
|
|
1799
1808
|
pollIntervalMs: 1e3,
|
|
1800
1809
|
timeoutMs: 9e5,
|
|
1801
1810
|
strict: true
|
|
1811
|
+
},
|
|
1812
|
+
migrations: {
|
|
1813
|
+
enabled: "auto",
|
|
1814
|
+
wait: true,
|
|
1815
|
+
batchSize: 256,
|
|
1816
|
+
pollIntervalMs: 1e3,
|
|
1817
|
+
timeoutMs: 9e5,
|
|
1818
|
+
strict: true,
|
|
1819
|
+
allowDrift: false
|
|
1802
1820
|
}
|
|
1803
1821
|
}
|
|
1804
1822
|
};
|
|
@@ -1843,6 +1861,18 @@ function parseAggregateBackfillConfig(value, fieldName, configPath) {
|
|
|
1843
1861
|
if ("strict" in value) parsed.strict = parseBoolean(value.strict, `${fieldName}.strict`, configPath);
|
|
1844
1862
|
return parsed;
|
|
1845
1863
|
}
|
|
1864
|
+
function parseMigrationConfig(value, fieldName, configPath) {
|
|
1865
|
+
if (!isRecord(value)) throw new Error(`Invalid ${fieldName} in ${configPath}: expected object.`);
|
|
1866
|
+
const parsed = {};
|
|
1867
|
+
if ("enabled" in value) parsed.enabled = parseBackfillEnabled(value.enabled, `${fieldName}.enabled`, configPath);
|
|
1868
|
+
if ("wait" in value) parsed.wait = parseBoolean(value.wait, `${fieldName}.wait`, configPath);
|
|
1869
|
+
if ("batchSize" in value) parsed.batchSize = parsePositiveInteger(value.batchSize, `${fieldName}.batchSize`, configPath);
|
|
1870
|
+
if ("pollIntervalMs" in value) parsed.pollIntervalMs = parsePositiveInteger(value.pollIntervalMs, `${fieldName}.pollIntervalMs`, configPath);
|
|
1871
|
+
if ("timeoutMs" in value) parsed.timeoutMs = parsePositiveInteger(value.timeoutMs, `${fieldName}.timeoutMs`, configPath);
|
|
1872
|
+
if ("strict" in value) parsed.strict = parseBoolean(value.strict, `${fieldName}.strict`, configPath);
|
|
1873
|
+
if ("allowDrift" in value) parsed.allowDrift = parseBoolean(value.allowDrift, `${fieldName}.allowDrift`, configPath);
|
|
1874
|
+
return parsed;
|
|
1875
|
+
}
|
|
1846
1876
|
function parseCommandConfig(value, fieldName, configPath) {
|
|
1847
1877
|
if (!isRecord(value)) throw new Error(`Invalid ${fieldName} in ${configPath}: expected object.`);
|
|
1848
1878
|
const parsed = {};
|
|
@@ -1850,6 +1880,7 @@ function parseCommandConfig(value, fieldName, configPath) {
|
|
|
1850
1880
|
if ("convexArgs" in value) parsed.convexArgs = parseStringArray(value.convexArgs, `${fieldName}.convexArgs`, configPath);
|
|
1851
1881
|
if (fieldName === "codegen" && "scope" in value && value.scope !== void 0) parsed.scope = parseScope(value.scope, `${fieldName}.scope`, configPath);
|
|
1852
1882
|
if (fieldName === "dev" && "aggregateBackfill" in value && value.aggregateBackfill !== void 0) parsed.aggregateBackfill = parseAggregateBackfillConfig(value.aggregateBackfill, `${fieldName}.aggregateBackfill`, configPath);
|
|
1883
|
+
if (fieldName === "dev" && "migrations" in value && value.migrations !== void 0) parsed.migrations = parseMigrationConfig(value.migrations, `${fieldName}.migrations`, configPath);
|
|
1853
1884
|
return parsed;
|
|
1854
1885
|
}
|
|
1855
1886
|
function parseDeployConfig(value, configPath) {
|
|
@@ -1857,6 +1888,7 @@ function parseDeployConfig(value, configPath) {
|
|
|
1857
1888
|
const parsed = {};
|
|
1858
1889
|
if ("convexArgs" in value) parsed.convexArgs = parseStringArray(value.convexArgs, "deploy.convexArgs", configPath);
|
|
1859
1890
|
if ("aggregateBackfill" in value && value.aggregateBackfill !== void 0) parsed.aggregateBackfill = parseAggregateBackfillConfig(value.aggregateBackfill, "deploy.aggregateBackfill", configPath);
|
|
1891
|
+
if ("migrations" in value && value.migrations !== void 0) parsed.migrations = parseMigrationConfig(value.migrations, "deploy.migrations", configPath);
|
|
1860
1892
|
return parsed;
|
|
1861
1893
|
}
|
|
1862
1894
|
function loadBetterConvexConfig(configPathArg) {
|
|
@@ -1885,6 +1917,10 @@ function loadBetterConvexConfig(configPathArg) {
|
|
|
1885
1917
|
...config.dev.aggregateBackfill,
|
|
1886
1918
|
...parsed.aggregateBackfill
|
|
1887
1919
|
};
|
|
1920
|
+
if (parsed.migrations !== void 0) config.dev.migrations = {
|
|
1921
|
+
...config.dev.migrations,
|
|
1922
|
+
...parsed.migrations
|
|
1923
|
+
};
|
|
1888
1924
|
}
|
|
1889
1925
|
if ("codegen" in rawConfig) {
|
|
1890
1926
|
const parsed = parseCommandConfig(rawConfig.codegen, "codegen", resolvedConfigPath);
|
|
@@ -1899,6 +1935,10 @@ function loadBetterConvexConfig(configPathArg) {
|
|
|
1899
1935
|
...config.deploy.aggregateBackfill,
|
|
1900
1936
|
...parsed.aggregateBackfill
|
|
1901
1937
|
};
|
|
1938
|
+
if (parsed.migrations !== void 0) config.deploy.migrations = {
|
|
1939
|
+
...config.deploy.migrations,
|
|
1940
|
+
...parsed.migrations
|
|
1941
|
+
};
|
|
1902
1942
|
}
|
|
1903
1943
|
return config;
|
|
1904
1944
|
}
|
|
@@ -2009,6 +2049,7 @@ const __dirname = dirname(__filename);
|
|
|
2009
2049
|
const realConvex = join(dirname(createRequire(import.meta.url).resolve("convex/package.json")), "bin/main.js");
|
|
2010
2050
|
const MISSING_BACKFILL_FUNCTION_RE = /could not find function|function .* was not found|unknown function/i;
|
|
2011
2051
|
const GITIGNORE_CONVEX_ENTRY_RE = /(^|\r?\n)\.convex\/?\s*(\r?\n|$)/m;
|
|
2052
|
+
const TS_EXTENSION_RE = /\.ts$/;
|
|
2012
2053
|
const AGGREGATE_STATE_RELATIVE_PATH = join(".convex", "better-convex", "aggregate-backfill-state.json");
|
|
2013
2054
|
const AGGREGATE_STATE_VERSION = 1;
|
|
2014
2055
|
const VALID_SCOPES = new Set([
|
|
@@ -2290,6 +2331,85 @@ function extractBackfillCliOptions(args) {
|
|
|
2290
2331
|
overrides
|
|
2291
2332
|
};
|
|
2292
2333
|
}
|
|
2334
|
+
function extractMigrationCliOptions(args) {
|
|
2335
|
+
const remainingArgs = [];
|
|
2336
|
+
const overrides = {};
|
|
2337
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
2338
|
+
const arg = args[i];
|
|
2339
|
+
if (arg === "--migrations") {
|
|
2340
|
+
const { value, nextIndex } = readFlagValue(args, i, "--migrations");
|
|
2341
|
+
if (!VALID_BACKFILL_ENABLED.has(value)) throw new Error("Invalid --migrations value. Expected auto, on, or off.");
|
|
2342
|
+
overrides.enabled = value;
|
|
2343
|
+
i = nextIndex;
|
|
2344
|
+
continue;
|
|
2345
|
+
}
|
|
2346
|
+
if (arg.startsWith("--migrations=")) {
|
|
2347
|
+
const value = arg.slice(13);
|
|
2348
|
+
if (!VALID_BACKFILL_ENABLED.has(value)) throw new Error("Invalid --migrations value. Expected auto, on, or off.");
|
|
2349
|
+
overrides.enabled = value;
|
|
2350
|
+
continue;
|
|
2351
|
+
}
|
|
2352
|
+
if (arg === "--migrations-wait") {
|
|
2353
|
+
overrides.wait = true;
|
|
2354
|
+
continue;
|
|
2355
|
+
}
|
|
2356
|
+
if (arg === "--no-migrations-wait") {
|
|
2357
|
+
overrides.wait = false;
|
|
2358
|
+
continue;
|
|
2359
|
+
}
|
|
2360
|
+
if (arg === "--migrations-strict") {
|
|
2361
|
+
overrides.strict = true;
|
|
2362
|
+
continue;
|
|
2363
|
+
}
|
|
2364
|
+
if (arg === "--no-migrations-strict") {
|
|
2365
|
+
overrides.strict = false;
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
if (arg === "--migrations-allow-drift") {
|
|
2369
|
+
overrides.allowDrift = true;
|
|
2370
|
+
continue;
|
|
2371
|
+
}
|
|
2372
|
+
if (arg === "--no-migrations-allow-drift") {
|
|
2373
|
+
overrides.allowDrift = false;
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
if (arg === "--migrations-batch-size") {
|
|
2377
|
+
const { value, nextIndex } = readFlagValue(args, i, "--migrations-batch-size");
|
|
2378
|
+
overrides.batchSize = parsePositiveIntegerArg("--migrations-batch-size", value);
|
|
2379
|
+
i = nextIndex;
|
|
2380
|
+
continue;
|
|
2381
|
+
}
|
|
2382
|
+
if (arg.startsWith("--migrations-batch-size=")) {
|
|
2383
|
+
overrides.batchSize = parsePositiveIntegerArg("--migrations-batch-size", arg.slice(24));
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
if (arg === "--migrations-timeout-ms") {
|
|
2387
|
+
const { value, nextIndex } = readFlagValue(args, i, "--migrations-timeout-ms");
|
|
2388
|
+
overrides.timeoutMs = parsePositiveIntegerArg("--migrations-timeout-ms", value);
|
|
2389
|
+
i = nextIndex;
|
|
2390
|
+
continue;
|
|
2391
|
+
}
|
|
2392
|
+
if (arg.startsWith("--migrations-timeout-ms=")) {
|
|
2393
|
+
overrides.timeoutMs = parsePositiveIntegerArg("--migrations-timeout-ms", arg.slice(24));
|
|
2394
|
+
continue;
|
|
2395
|
+
}
|
|
2396
|
+
if (arg === "--migrations-poll-ms") {
|
|
2397
|
+
const { value, nextIndex } = readFlagValue(args, i, "--migrations-poll-ms");
|
|
2398
|
+
overrides.pollIntervalMs = parsePositiveIntegerArg("--migrations-poll-ms", value);
|
|
2399
|
+
i = nextIndex;
|
|
2400
|
+
continue;
|
|
2401
|
+
}
|
|
2402
|
+
if (arg.startsWith("--migrations-poll-ms=")) {
|
|
2403
|
+
overrides.pollIntervalMs = parsePositiveIntegerArg("--migrations-poll-ms", arg.slice(21));
|
|
2404
|
+
continue;
|
|
2405
|
+
}
|
|
2406
|
+
remainingArgs.push(arg);
|
|
2407
|
+
}
|
|
2408
|
+
return {
|
|
2409
|
+
remainingArgs,
|
|
2410
|
+
overrides
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2293
2413
|
function extractResetCliOptions(args) {
|
|
2294
2414
|
const remainingArgs = [];
|
|
2295
2415
|
let confirmed = false;
|
|
@@ -2355,6 +2475,27 @@ function resolveBackfillConfig(base, overrides) {
|
|
|
2355
2475
|
strict: overrides.strict ?? resolvedBase.strict
|
|
2356
2476
|
};
|
|
2357
2477
|
}
|
|
2478
|
+
function resolveMigrationConfig(base, overrides) {
|
|
2479
|
+
const resolvedBase = base ?? {
|
|
2480
|
+
enabled: "auto",
|
|
2481
|
+
wait: true,
|
|
2482
|
+
batchSize: 256,
|
|
2483
|
+
timeoutMs: 9e5,
|
|
2484
|
+
pollIntervalMs: 1e3,
|
|
2485
|
+
strict: false,
|
|
2486
|
+
allowDrift: true
|
|
2487
|
+
};
|
|
2488
|
+
return {
|
|
2489
|
+
...resolvedBase,
|
|
2490
|
+
enabled: overrides.enabled ?? resolvedBase.enabled,
|
|
2491
|
+
wait: overrides.wait ?? resolvedBase.wait,
|
|
2492
|
+
batchSize: overrides.batchSize ?? resolvedBase.batchSize,
|
|
2493
|
+
timeoutMs: overrides.timeoutMs ?? resolvedBase.timeoutMs,
|
|
2494
|
+
pollIntervalMs: overrides.pollIntervalMs ?? resolvedBase.pollIntervalMs,
|
|
2495
|
+
strict: overrides.strict ?? resolvedBase.strict,
|
|
2496
|
+
allowDrift: overrides.allowDrift ?? resolvedBase.allowDrift
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2358
2499
|
function extractRunDeploymentArgs(args) {
|
|
2359
2500
|
const deploymentArgs = [];
|
|
2360
2501
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -2505,6 +2646,179 @@ async function runAggregatePruneFlow(params) {
|
|
|
2505
2646
|
else console.info("ℹ️ aggregateBackfill prune no-op");
|
|
2506
2647
|
return 0;
|
|
2507
2648
|
}
|
|
2649
|
+
function slugifyMigrationName(name) {
|
|
2650
|
+
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
2651
|
+
}
|
|
2652
|
+
function createMigrationTimestamp(now = /* @__PURE__ */ new Date()) {
|
|
2653
|
+
return `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}${String(now.getUTCDate()).padStart(2, "0")}_${String(now.getUTCHours()).padStart(2, "0")}${String(now.getUTCMinutes()).padStart(2, "0")}${String(now.getUTCSeconds()).padStart(2, "0")}`;
|
|
2654
|
+
}
|
|
2655
|
+
function extractMigrationDownOptions(args) {
|
|
2656
|
+
const remainingArgs = [];
|
|
2657
|
+
let steps;
|
|
2658
|
+
let to;
|
|
2659
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
2660
|
+
const arg = args[i];
|
|
2661
|
+
if (arg === "--steps") {
|
|
2662
|
+
const { value, nextIndex } = readFlagValue(args, i, "--steps");
|
|
2663
|
+
steps = parsePositiveIntegerArg("--steps", value);
|
|
2664
|
+
i = nextIndex;
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
if (arg.startsWith("--steps=")) {
|
|
2668
|
+
steps = parsePositiveIntegerArg("--steps", arg.slice(8));
|
|
2669
|
+
continue;
|
|
2670
|
+
}
|
|
2671
|
+
if (arg === "--to") {
|
|
2672
|
+
const { value, nextIndex } = readFlagValue(args, i, "--to");
|
|
2673
|
+
to = value;
|
|
2674
|
+
i = nextIndex;
|
|
2675
|
+
continue;
|
|
2676
|
+
}
|
|
2677
|
+
if (arg.startsWith("--to=")) {
|
|
2678
|
+
const value = arg.slice(5);
|
|
2679
|
+
if (!value) throw new Error("Missing value for --to.");
|
|
2680
|
+
to = value;
|
|
2681
|
+
continue;
|
|
2682
|
+
}
|
|
2683
|
+
remainingArgs.push(arg);
|
|
2684
|
+
}
|
|
2685
|
+
if (steps !== void 0 && to !== void 0) throw new Error("Use either --steps or --to, not both.");
|
|
2686
|
+
return {
|
|
2687
|
+
remainingArgs,
|
|
2688
|
+
steps,
|
|
2689
|
+
to
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
function renderMigrationManifest(ids) {
|
|
2693
|
+
const sorted = [...new Set(ids)].sort((a, b) => a.localeCompare(b));
|
|
2694
|
+
const importLines = sorted.map((id, index) => `import { migration as migration_${index} } from './${id}';`);
|
|
2695
|
+
const entryLines = sorted.map((_, index) => ` migration_${index},`);
|
|
2696
|
+
return `// biome-ignore-all format: generated
|
|
2697
|
+
// This file is auto-generated by better-convex migrate create.
|
|
2698
|
+
// Do not edit manually.
|
|
2699
|
+
|
|
2700
|
+
import { defineMigrationSet } from 'better-convex/orm';
|
|
2701
|
+
${importLines.join("\n")}
|
|
2702
|
+
|
|
2703
|
+
export const migrations = defineMigrationSet([
|
|
2704
|
+
${entryLines.join("\n")}
|
|
2705
|
+
]);
|
|
2706
|
+
`;
|
|
2707
|
+
}
|
|
2708
|
+
async function runMigrationCreate(params) {
|
|
2709
|
+
const { migrationName, functionsDir } = params;
|
|
2710
|
+
const normalizedName = slugifyMigrationName(migrationName);
|
|
2711
|
+
if (!normalizedName) throw new Error("Migration name must include at least one letter or digit.");
|
|
2712
|
+
const migrationId = `${createMigrationTimestamp()}_${normalizedName}`;
|
|
2713
|
+
const migrationsDir = join(functionsDir, "migrations");
|
|
2714
|
+
const migrationFile = join(migrationsDir, `${migrationId}.ts`);
|
|
2715
|
+
const manifestFile = join(migrationsDir, "manifest.ts");
|
|
2716
|
+
fs.mkdirSync(migrationsDir, { recursive: true });
|
|
2717
|
+
if (fs.existsSync(migrationFile)) throw new Error(`Migration file already exists for '${migrationId}'. Wait one second and retry.`);
|
|
2718
|
+
const migrationSource = `import { defineMigration } from '../generated/migrations.gen';
|
|
2719
|
+
|
|
2720
|
+
export const migration = defineMigration({
|
|
2721
|
+
id: '${migrationId}',
|
|
2722
|
+
description: '${migrationName.replaceAll("'", "\\'")}',
|
|
2723
|
+
up: {
|
|
2724
|
+
table: 'replace_with_table_name',
|
|
2725
|
+
migrateOne: async () => {
|
|
2726
|
+
// TODO: implement migration logic.
|
|
2727
|
+
},
|
|
2728
|
+
},
|
|
2729
|
+
down: {
|
|
2730
|
+
table: 'replace_with_table_name',
|
|
2731
|
+
migrateOne: async () => {
|
|
2732
|
+
// TODO: implement rollback logic.
|
|
2733
|
+
},
|
|
2734
|
+
},
|
|
2735
|
+
});
|
|
2736
|
+
`;
|
|
2737
|
+
fs.writeFileSync(migrationFile, migrationSource);
|
|
2738
|
+
const existingMigrationIds = fs.readdirSync(migrationsDir).filter((file) => file.endsWith(".ts")).map((file) => file.replace(TS_EXTENSION_RE, "")).filter((id) => id !== "manifest").sort((a, b) => a.localeCompare(b));
|
|
2739
|
+
fs.writeFileSync(manifestFile, renderMigrationManifest(existingMigrationIds));
|
|
2740
|
+
console.info(`ℹ️ created migration ${migrationId}`);
|
|
2741
|
+
console.info(`ℹ️ file: ${migrationFile}`);
|
|
2742
|
+
console.info(`ℹ️ manifest: ${manifestFile}`);
|
|
2743
|
+
}
|
|
2744
|
+
async function runMigrationFlow(params) {
|
|
2745
|
+
const { execaFn, realConvexPath, migrationConfig, deploymentArgs, signal, context, direction, steps, to } = params;
|
|
2746
|
+
if (signal?.aborted || migrationConfig.enabled === "off") return 0;
|
|
2747
|
+
const kickoff = await runConvexFunction(execaFn, realConvexPath, "generated/server:migrationRun", {
|
|
2748
|
+
direction,
|
|
2749
|
+
batchSize: migrationConfig.batchSize,
|
|
2750
|
+
allowDrift: migrationConfig.allowDrift,
|
|
2751
|
+
...steps !== void 0 ? { steps } : {},
|
|
2752
|
+
...to !== void 0 ? { to } : {}
|
|
2753
|
+
}, deploymentArgs, { echoOutput: false });
|
|
2754
|
+
if (kickoff.exitCode !== 0) {
|
|
2755
|
+
const combinedOutput = `${kickoff.stdout}\n${kickoff.stderr}`;
|
|
2756
|
+
if (migrationConfig.enabled === "auto" && isMissingBackfillFunctionOutput(combinedOutput)) {
|
|
2757
|
+
if (context === "deploy") console.info("ℹ️ migration runtime not found in this deployment; skipping (auto mode).");
|
|
2758
|
+
return 0;
|
|
2759
|
+
}
|
|
2760
|
+
return kickoff.exitCode;
|
|
2761
|
+
}
|
|
2762
|
+
const payload = parseConvexRunJson(kickoff.stdout);
|
|
2763
|
+
const kickoffStatus = typeof payload === "object" && payload !== null && !Array.isArray(payload) && typeof payload.status === "string" ? payload.status : "running";
|
|
2764
|
+
const driftMessages = typeof payload === "object" && payload !== null && !Array.isArray(payload) && Array.isArray(payload.drift) ? payload.drift.map((entry) => entry?.message).filter((entry) => typeof entry === "string") : [];
|
|
2765
|
+
if (kickoffStatus === "drift_blocked") {
|
|
2766
|
+
const message = driftMessages[0] ?? "Migration drift detected and blocked by current policy.";
|
|
2767
|
+
if (migrationConfig.strict) {
|
|
2768
|
+
console.error(`❌ ${message}`);
|
|
2769
|
+
return 1;
|
|
2770
|
+
}
|
|
2771
|
+
console.warn(`⚠️ ${message}`);
|
|
2772
|
+
return 0;
|
|
2773
|
+
}
|
|
2774
|
+
if (kickoffStatus === "noop") {
|
|
2775
|
+
const noopMessage = direction === "down" ? "No applied migrations to roll back." : "No pending migrations to apply.";
|
|
2776
|
+
console.info(`ℹ️ ${noopMessage}`);
|
|
2777
|
+
return 0;
|
|
2778
|
+
}
|
|
2779
|
+
if (kickoffStatus === "dry_run") {
|
|
2780
|
+
console.info("ℹ️ migration dry run completed (no writes committed).");
|
|
2781
|
+
return 0;
|
|
2782
|
+
}
|
|
2783
|
+
const runId = typeof payload === "object" && payload !== null && !Array.isArray(payload) && typeof payload.runId === "string" ? payload.runId : void 0;
|
|
2784
|
+
if (!migrationConfig.wait || signal?.aborted || !runId) return 0;
|
|
2785
|
+
const deadline = Date.now() + migrationConfig.timeoutMs;
|
|
2786
|
+
let lastStatusLine = "";
|
|
2787
|
+
while (!signal?.aborted) {
|
|
2788
|
+
const statusResult = await runConvexFunction(execaFn, realConvexPath, "generated/server:migrationStatus", { runId }, deploymentArgs, { echoOutput: false });
|
|
2789
|
+
if (statusResult.exitCode !== 0) return statusResult.exitCode;
|
|
2790
|
+
const statusPayload = parseConvexRunJson(statusResult.stdout);
|
|
2791
|
+
const runStatus = typeof statusPayload === "object" && statusPayload !== null && !Array.isArray(statusPayload) ? statusPayload.activeRun?.status ?? statusPayload.runs?.[0]?.status ?? "unknown" : "unknown";
|
|
2792
|
+
const currentIndex = typeof statusPayload === "object" && statusPayload !== null && !Array.isArray(statusPayload) && typeof statusPayload.runs?.[0]?.currentIndex === "number" ? statusPayload.runs[0].currentIndex : 0;
|
|
2793
|
+
const total = typeof statusPayload === "object" && statusPayload !== null && !Array.isArray(statusPayload) && Array.isArray(statusPayload.runs?.[0]?.migrationIds) ? statusPayload.runs?.[0]?.migrationIds?.length ?? 0 : 0;
|
|
2794
|
+
const statusLine = `${runStatus}:${currentIndex}/${total}`;
|
|
2795
|
+
if (statusLine !== lastStatusLine && total > 0) {
|
|
2796
|
+
lastStatusLine = statusLine;
|
|
2797
|
+
console.info(`ℹ️ migration ${runStatus} ${currentIndex}/${total}`);
|
|
2798
|
+
}
|
|
2799
|
+
if (runStatus === "completed" || runStatus === "noop") return 0;
|
|
2800
|
+
if (runStatus === "failed" || runStatus === "canceled") {
|
|
2801
|
+
const message = `Migrations ${runStatus} for run ${runId}.`;
|
|
2802
|
+
if (migrationConfig.strict) {
|
|
2803
|
+
console.error(`❌ ${message}`);
|
|
2804
|
+
return 1;
|
|
2805
|
+
}
|
|
2806
|
+
console.warn(`⚠️ ${message}`);
|
|
2807
|
+
return 0;
|
|
2808
|
+
}
|
|
2809
|
+
if (Date.now() > deadline) {
|
|
2810
|
+
const timeoutMessage = `Migrations timed out after ${migrationConfig.timeoutMs}ms.`;
|
|
2811
|
+
if (migrationConfig.strict) {
|
|
2812
|
+
console.error(`❌ ${timeoutMessage}`);
|
|
2813
|
+
return 1;
|
|
2814
|
+
}
|
|
2815
|
+
console.warn(`⚠️ ${timeoutMessage}`);
|
|
2816
|
+
return 0;
|
|
2817
|
+
}
|
|
2818
|
+
await sleep(migrationConfig.pollIntervalMs, signal);
|
|
2819
|
+
}
|
|
2820
|
+
return 0;
|
|
2821
|
+
}
|
|
2508
2822
|
async function runDevSchemaBackfillIfNeeded(params) {
|
|
2509
2823
|
const { execaFn, realConvexPath, backfillConfig, functionsDir, deploymentArgs, signal } = params;
|
|
2510
2824
|
const fingerprint = await computeAggregateIndexFingerprint(functionsDir);
|
|
@@ -2551,13 +2865,15 @@ async function run(argv, deps) {
|
|
|
2551
2865
|
if (command === "dev") {
|
|
2552
2866
|
if (cliScope) throw new Error("`--scope` is not supported for `better-convex dev`. Use `better-convex codegen --scope <all|auth|orm>` for scoped generation.");
|
|
2553
2867
|
const config = loadBetterConvexConfigFn(configPath);
|
|
2554
|
-
const { remainingArgs:
|
|
2868
|
+
const { remainingArgs: devArgsWithoutMigrationFlags, overrides: devMigrationOverrides } = extractMigrationCliOptions(convexArgs);
|
|
2869
|
+
const { remainingArgs: devCommandArgs, overrides: devBackfillOverrides } = extractBackfillCliOptions(devArgsWithoutMigrationFlags);
|
|
2555
2870
|
const outputDir = cliOutputDir ?? config.outputDir;
|
|
2556
2871
|
const debug = cliDebug || config.dev.debug;
|
|
2557
2872
|
const generateApi = config.api;
|
|
2558
2873
|
const generateAuth = config.auth;
|
|
2559
2874
|
const convexDevArgs = [...config.dev.convexArgs, ...devCommandArgs];
|
|
2560
2875
|
const devBackfillConfig = resolveBackfillConfig(config.dev.aggregateBackfill, devBackfillOverrides);
|
|
2876
|
+
const devMigrationConfig = resolveMigrationConfig(config.dev.migrations, devMigrationOverrides);
|
|
2561
2877
|
const { functionsDir } = getConvexConfigFn(outputDir);
|
|
2562
2878
|
const schemaPath = join(functionsDir, "schema.ts");
|
|
2563
2879
|
const deploymentArgs = extractRunDeploymentArgs(convexDevArgs);
|
|
@@ -2627,6 +2943,21 @@ async function run(argv, deps) {
|
|
|
2627
2943
|
schemaBackfillInFlight = null;
|
|
2628
2944
|
});
|
|
2629
2945
|
};
|
|
2946
|
+
if (devMigrationConfig.enabled !== "off") (async () => {
|
|
2947
|
+
try {
|
|
2948
|
+
if (await runMigrationFlow({
|
|
2949
|
+
execaFn,
|
|
2950
|
+
realConvexPath,
|
|
2951
|
+
migrationConfig: devMigrationConfig,
|
|
2952
|
+
deploymentArgs,
|
|
2953
|
+
signal: backfillAbortController.signal,
|
|
2954
|
+
context: "dev",
|
|
2955
|
+
direction: "up"
|
|
2956
|
+
}) !== 0 && !backfillAbortController.signal.aborted) console.warn("⚠️ migration up failed in dev (continuing without blocking).");
|
|
2957
|
+
} catch (error) {
|
|
2958
|
+
if (!backfillAbortController.signal.aborted) console.warn(`⚠️ migration up errored in dev: ${error.message}`);
|
|
2959
|
+
}
|
|
2960
|
+
})();
|
|
2630
2961
|
if (devBackfillConfig.enabled !== "off") (async () => {
|
|
2631
2962
|
try {
|
|
2632
2963
|
if (await runAggregateBackfillFlow({
|
|
@@ -2760,7 +3091,8 @@ async function run(argv, deps) {
|
|
|
2760
3091
|
}
|
|
2761
3092
|
if (command === "deploy") {
|
|
2762
3093
|
const config = loadBetterConvexConfigFn(configPath);
|
|
2763
|
-
const { remainingArgs:
|
|
3094
|
+
const { remainingArgs: deployArgsWithoutMigrationFlags, overrides: deployMigrationOverrides } = extractMigrationCliOptions(convexArgs);
|
|
3095
|
+
const { remainingArgs: deployCommandArgs, overrides: deployBackfillOverrides } = extractBackfillCliOptions(deployArgsWithoutMigrationFlags);
|
|
2764
3096
|
const deployArgs = [...config.deploy.convexArgs, ...deployCommandArgs];
|
|
2765
3097
|
const deployResult = await execaFn("node", [
|
|
2766
3098
|
realConvexPath,
|
|
@@ -2772,15 +3104,91 @@ async function run(argv, deps) {
|
|
|
2772
3104
|
reject: false
|
|
2773
3105
|
});
|
|
2774
3106
|
if ((deployResult.exitCode ?? 1) !== 0) return deployResult.exitCode ?? 1;
|
|
3107
|
+
const migrationConfig = resolveMigrationConfig(config.deploy.migrations, deployMigrationOverrides);
|
|
3108
|
+
const backfillConfig = resolveBackfillConfig(config.deploy.aggregateBackfill, deployBackfillOverrides);
|
|
3109
|
+
const deploymentArgs = extractRunDeploymentArgs(deployArgs);
|
|
3110
|
+
const migrationExitCode = await runMigrationFlow({
|
|
3111
|
+
execaFn,
|
|
3112
|
+
realConvexPath,
|
|
3113
|
+
migrationConfig,
|
|
3114
|
+
deploymentArgs,
|
|
3115
|
+
context: "deploy",
|
|
3116
|
+
direction: "up"
|
|
3117
|
+
});
|
|
3118
|
+
if (migrationExitCode !== 0) return migrationExitCode;
|
|
2775
3119
|
return runAggregateBackfillFlow({
|
|
2776
3120
|
execaFn,
|
|
2777
3121
|
realConvexPath,
|
|
2778
|
-
backfillConfig
|
|
3122
|
+
backfillConfig,
|
|
2779
3123
|
mode: "resume",
|
|
2780
|
-
deploymentArgs
|
|
3124
|
+
deploymentArgs,
|
|
2781
3125
|
context: "deploy"
|
|
2782
3126
|
});
|
|
2783
3127
|
}
|
|
3128
|
+
if (command === "migrate") {
|
|
3129
|
+
const subcommand = restArgs[0];
|
|
3130
|
+
if (subcommand !== "create" && subcommand !== "up" && subcommand !== "down" && subcommand !== "status" && subcommand !== "cancel") throw new Error("Unknown migrate command. Use: `better-convex migrate create|up|down|status|cancel`.");
|
|
3131
|
+
const config = loadBetterConvexConfigFn(configPath);
|
|
3132
|
+
if (subcommand === "create") {
|
|
3133
|
+
const rawName = restArgs.slice(1).join(" ").trim();
|
|
3134
|
+
if (!rawName) throw new Error("Missing migration name. Usage: `better-convex migrate create <name>`.");
|
|
3135
|
+
const { functionsDir } = getConvexConfigFn(cliOutputDir ?? config.outputDir);
|
|
3136
|
+
await runMigrationCreate({
|
|
3137
|
+
migrationName: rawName,
|
|
3138
|
+
functionsDir
|
|
3139
|
+
});
|
|
3140
|
+
return 0;
|
|
3141
|
+
}
|
|
3142
|
+
const { remainingArgs: migrationCommandArgs, overrides: migrationOverrides } = extractMigrationCliOptions(restArgs.slice(1));
|
|
3143
|
+
const migrationConfig = {
|
|
3144
|
+
...resolveMigrationConfig(config.deploy.migrations, migrationOverrides),
|
|
3145
|
+
enabled: "on"
|
|
3146
|
+
};
|
|
3147
|
+
const commandArgs = [...config.deploy.convexArgs, ...migrationCommandArgs];
|
|
3148
|
+
const deploymentArgs = extractRunDeploymentArgs(commandArgs);
|
|
3149
|
+
if (subcommand === "up") return runMigrationFlow({
|
|
3150
|
+
execaFn,
|
|
3151
|
+
realConvexPath,
|
|
3152
|
+
migrationConfig,
|
|
3153
|
+
deploymentArgs,
|
|
3154
|
+
context: "migration",
|
|
3155
|
+
direction: "up"
|
|
3156
|
+
});
|
|
3157
|
+
if (subcommand === "down") {
|
|
3158
|
+
const { remainingArgs, steps, to } = extractMigrationDownOptions(commandArgs);
|
|
3159
|
+
return runMigrationFlow({
|
|
3160
|
+
execaFn,
|
|
3161
|
+
realConvexPath,
|
|
3162
|
+
migrationConfig,
|
|
3163
|
+
deploymentArgs: extractRunDeploymentArgs(remainingArgs),
|
|
3164
|
+
context: "migration",
|
|
3165
|
+
direction: "down",
|
|
3166
|
+
steps,
|
|
3167
|
+
to
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
if (subcommand === "status") return (await runConvexFunction(execaFn, realConvexPath, "generated/server:migrationStatus", {}, deploymentArgs)).exitCode;
|
|
3171
|
+
let runId;
|
|
3172
|
+
const cancelArgs = [];
|
|
3173
|
+
for (let i = 0; i < commandArgs.length; i += 1) {
|
|
3174
|
+
const arg = commandArgs[i];
|
|
3175
|
+
if (arg === "--run-id") {
|
|
3176
|
+
const { value, nextIndex } = readFlagValue(commandArgs, i, "--run-id");
|
|
3177
|
+
runId = value;
|
|
3178
|
+
i = nextIndex;
|
|
3179
|
+
continue;
|
|
3180
|
+
}
|
|
3181
|
+
if (arg.startsWith("--run-id=")) {
|
|
3182
|
+
const value = arg.slice(9);
|
|
3183
|
+
if (!value) throw new Error("Missing value for --run-id.");
|
|
3184
|
+
runId = value;
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
cancelArgs.push(arg);
|
|
3188
|
+
}
|
|
3189
|
+
const cancelDeploymentArgs = extractRunDeploymentArgs(cancelArgs);
|
|
3190
|
+
return (await runConvexFunction(execaFn, realConvexPath, "generated/server:migrationCancel", runId ? { runId } : {}, cancelDeploymentArgs)).exitCode;
|
|
3191
|
+
}
|
|
2784
3192
|
if (command === "aggregate") {
|
|
2785
3193
|
const subcommand = restArgs[0];
|
|
2786
3194
|
if (subcommand !== "rebuild" && subcommand !== "backfill" && subcommand !== "prune") throw new Error("Unknown aggregate command. Use: `better-convex aggregate backfill`, `better-convex aggregate rebuild`, or `better-convex aggregate prune`.");
|