better-convex 0.6.4 → 0.7.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.
- package/dist/aggregate/index.d.ts +388 -0
- package/dist/aggregate/index.js +37 -0
- package/dist/{auth-client → auth/client}/index.js +1 -1
- package/dist/auth/http/index.d.ts +63 -0
- package/dist/auth/http/index.js +429 -0
- package/dist/auth/index.d.ts +18991 -175
- package/dist/auth/index.js +373 -686
- package/dist/{auth-nextjs → auth/nextjs}/index.d.ts +3 -5
- package/dist/{auth-nextjs → auth/nextjs}/index.js +3 -5
- package/dist/{caller-factory-B1FvYSKr.js → caller-factory-Dmgv8MLS.js} +15 -12
- package/dist/cli.mjs +2601 -13
- package/dist/codegen-Cz1idI3-.mjs +969 -0
- package/dist/{create-schema-orm-DplxTtYj.js → create-schema-orm-69VF4CFV.js} +4 -3
- package/dist/crpc/index.d.ts +2 -2
- package/dist/crpc/index.js +3 -3
- package/dist/{http-types-BRLY10NX.d.ts → http-types-BCf2wCgp.d.ts} +25 -25
- package/dist/meta-utils-DDVYp9Xf.js +117 -0
- package/dist/orm/index.d.ts +4 -3012
- package/dist/orm/index.js +9631 -2
- package/dist/{index-BQkhP2ny.d.ts → procedure-caller-CcjtUFvL.d.ts} +211 -74
- package/dist/query-context-BDSis9rT.js +1518 -0
- package/dist/query-context-DGExXZIV.d.ts +42 -0
- package/dist/react/index.d.ts +31 -35
- package/dist/react/index.js +145 -58
- package/dist/rsc/index.d.ts +4 -7
- package/dist/rsc/index.js +14 -10
- package/dist/runtime-B9xQFY8W.js +2280 -0
- package/dist/server/index.d.ts +3 -4
- package/dist/server/index.js +384 -10
- package/dist/{types-o-5rYcTr.d.ts → types-CIBGEYXq.d.ts} +4 -3
- package/dist/types-DgwvxKbT.d.ts +4 -0
- package/dist/watcher.mjs +8 -8
- package/dist/where-clause-compiler-CRP-i1Qa.d.ts +3463 -0
- package/package.json +14 -10
- package/dist/codegen-DkpPBVPn.mjs +0 -189
- package/dist/context-utils-DSuX99Da.d.ts +0 -17
- package/dist/meta-utils-DCpLSBWB.js +0 -41
- package/dist/orm-BKc-pwj_.js +0 -8821
- /package/dist/{auth-client → auth/client}/index.d.ts +0 -0
- /package/dist/{auth-config → auth/config}/index.d.ts +0 -0
- /package/dist/{auth-config → auth/config}/index.js +0 -0
- /package/dist/{create-schema-DhWXOhnU.js → create-schema-BdZOL6ns.js} +0 -0
- /package/dist/{customFunctions-C1okqCzL.js → customFunctions-CZnCwoR3.js} +0 -0
- /package/dist/{error-BZUhlhYz.js → error-Be4OcwwD.js} +0 -0
- /package/dist/{query-options-BL1Q0X7q.js → query-options-B0c1b6pZ.js} +0 -0
- /package/dist/{transformer-CTNSPjwp.js → transformer-Dh0w2py0.js} +0 -0
- /package/dist/{types-jftzhhuc.d.ts → types-DwGkkq2s.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-convex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Better Convex - React Query integration and CLI tools for Convex",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"convex",
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"type": "module",
|
|
14
14
|
"exports": {
|
|
15
|
+
"./aggregate": "./dist/aggregate/index.js",
|
|
15
16
|
"./auth": "./dist/auth/index.js",
|
|
16
|
-
"./auth
|
|
17
|
-
"./auth
|
|
18
|
-
"./auth
|
|
17
|
+
"./auth/client": "./dist/auth/client/index.js",
|
|
18
|
+
"./auth/config": "./dist/auth/config/index.js",
|
|
19
|
+
"./auth/http": "./dist/auth/http/index.js",
|
|
20
|
+
"./auth/nextjs": "./dist/auth/nextjs/index.js",
|
|
19
21
|
"./crpc": "./dist/crpc/index.js",
|
|
20
22
|
"./orm": "./dist/orm/index.js",
|
|
21
23
|
"./react": "./dist/react/index.js",
|
|
@@ -37,27 +39,29 @@
|
|
|
37
39
|
"typecheck:types": "tsc --project ../../convex/test-types/tsconfig.json"
|
|
38
40
|
},
|
|
39
41
|
"dependencies": {
|
|
42
|
+
"@clack/prompts": "^0.11.0",
|
|
40
43
|
"@convex-dev/better-auth": "^0.10.10",
|
|
41
44
|
"chokidar": "^5.0.0",
|
|
42
45
|
"common-tags": "^1.8.2",
|
|
43
|
-
"dotenv": "^17.
|
|
46
|
+
"dotenv": "^17.3.1",
|
|
47
|
+
"esbuild": "^0.27.3",
|
|
44
48
|
"execa": "^9.6.1",
|
|
45
49
|
"jiti": "^2.6.1",
|
|
46
|
-
"jotai": "^2.
|
|
50
|
+
"jotai": "^2.18.0",
|
|
47
51
|
"jotai-x": "^2.3.3",
|
|
48
52
|
"picocolors": "^1.1.1",
|
|
49
|
-
"remeda": "^2.33.
|
|
50
|
-
"type-fest": "^5.4.
|
|
53
|
+
"remeda": "^2.33.6",
|
|
54
|
+
"type-fest": "^5.4.4"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
57
|
"@rollup/plugin-babel": "^6.1.0",
|
|
54
58
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
55
|
-
"tsdown": "^0.20.
|
|
59
|
+
"tsdown": "^0.20.3"
|
|
56
60
|
},
|
|
57
61
|
"peerDependencies": {
|
|
58
62
|
"@tanstack/react-query": ">=5",
|
|
59
63
|
"better-auth": ">=1.4.9",
|
|
60
|
-
"convex": ">=1.
|
|
64
|
+
"convex": ">=1.32",
|
|
61
65
|
"hono": ">=4",
|
|
62
66
|
"next": ">=14",
|
|
63
67
|
"react": ">=18",
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { createJiti } from "jiti";
|
|
5
|
-
|
|
6
|
-
//#region src/shared/meta-utils.ts
|
|
7
|
-
/** Files to exclude from meta generation */
|
|
8
|
-
const EXCLUDED_FILES = new Set([
|
|
9
|
-
"schema.ts",
|
|
10
|
-
"convex.config.ts",
|
|
11
|
-
"auth.config.ts"
|
|
12
|
-
]);
|
|
13
|
-
/**
|
|
14
|
-
* Check if a file path should be included in meta generation.
|
|
15
|
-
* Filters out private files/directories (prefixed with _) and config files.
|
|
16
|
-
*/
|
|
17
|
-
function isValidConvexFile(file) {
|
|
18
|
-
if (file.startsWith("_") || file.includes("/_")) return false;
|
|
19
|
-
const basename = file.split("/").pop() ?? "";
|
|
20
|
-
if (EXCLUDED_FILES.has(basename)) return false;
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/cli/codegen.ts
|
|
26
|
-
/** Valid JS identifier pattern for object keys */
|
|
27
|
-
const VALID_IDENTIFIER_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
28
|
-
/** Pattern to strip .ts extension */
|
|
29
|
-
const TS_EXTENSION_RE = /\.ts$/;
|
|
30
|
-
function listFilesRecursive(cwd, relDir = "") {
|
|
31
|
-
const absDir = path.join(cwd, relDir);
|
|
32
|
-
const entries = fs.readdirSync(absDir, { withFileTypes: true });
|
|
33
|
-
const files = [];
|
|
34
|
-
for (const entry of entries) {
|
|
35
|
-
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
36
|
-
if (entry.isDirectory()) {
|
|
37
|
-
files.push(...listFilesRecursive(cwd, relPath));
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (entry.isFile()) files.push(relPath);
|
|
41
|
-
}
|
|
42
|
-
return files;
|
|
43
|
-
}
|
|
44
|
-
function getConvexConfig(outputDir) {
|
|
45
|
-
const convexConfigPath = path.join(process.cwd(), "convex.json");
|
|
46
|
-
const functionsDir = (fs.existsSync(convexConfigPath) ? JSON.parse(fs.readFileSync(convexConfigPath, "utf-8")) : {}).functions || "convex";
|
|
47
|
-
return {
|
|
48
|
-
functionsDir: path.join(process.cwd(), functionsDir),
|
|
49
|
-
outputFile: path.join(process.cwd(), outputDir || "convex/shared", "meta.ts")
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Check if a value is a CRPCHttpRouter (has _def.router === true)
|
|
54
|
-
*/
|
|
55
|
-
function isCRPCHttpRouter(value) {
|
|
56
|
-
return typeof value === "object" && value !== null && "_def" in value && value._def?.router === true;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Import a module using jiti and extract CRPC metadata from exports
|
|
60
|
-
*/
|
|
61
|
-
async function parseModuleRuntime(filePath, jitiInstance) {
|
|
62
|
-
const result = {};
|
|
63
|
-
const httpRoutes = {};
|
|
64
|
-
const isHttp = filePath.endsWith("http.ts");
|
|
65
|
-
const module = await jitiInstance.import(filePath);
|
|
66
|
-
if (!module || typeof module !== "object") {
|
|
67
|
-
if (isHttp) console.error(" http.ts: module is empty or not an object");
|
|
68
|
-
return {
|
|
69
|
-
meta: null,
|
|
70
|
-
httpRoutes: {}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
for (const [name, value] of Object.entries(module)) {
|
|
74
|
-
if (name.startsWith("_")) continue;
|
|
75
|
-
const meta = value?._crpcMeta;
|
|
76
|
-
if (meta?.type) {
|
|
77
|
-
if (meta.internal) continue;
|
|
78
|
-
const fnMeta = { type: meta.type };
|
|
79
|
-
if (meta.auth) fnMeta.auth = meta.auth;
|
|
80
|
-
for (const [key, val] of Object.entries(meta)) if (key !== "type" && key !== "internal" && val !== void 0) fnMeta[key] = val;
|
|
81
|
-
result[name] = fnMeta;
|
|
82
|
-
}
|
|
83
|
-
const httpRoute = value?._crpcHttpRoute;
|
|
84
|
-
if (httpRoute?.path && httpRoute?.method) httpRoutes[name] = {
|
|
85
|
-
path: httpRoute.path,
|
|
86
|
-
method: httpRoute.method
|
|
87
|
-
};
|
|
88
|
-
if (isCRPCHttpRouter(value)) for (const [procPath, procedure] of Object.entries(value._def.procedures)) {
|
|
89
|
-
const route = procedure._crpcHttpRoute;
|
|
90
|
-
if (route?.path && route?.method) httpRoutes[procPath] = {
|
|
91
|
-
path: route.path,
|
|
92
|
-
method: route.method
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
meta: Object.keys(result).length > 0 ? result : null,
|
|
98
|
-
httpRoutes
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
async function generateMeta(outputDir, options) {
|
|
102
|
-
const startTime = Date.now();
|
|
103
|
-
const { functionsDir, outputFile } = getConvexConfig(outputDir);
|
|
104
|
-
const debug = options?.debug ?? false;
|
|
105
|
-
const silent = options?.silent ?? false;
|
|
106
|
-
if (debug) console.info("🔍 Scanning Convex functions for metadata...\n");
|
|
107
|
-
const jitiInstance = createJiti(process.cwd(), {
|
|
108
|
-
interopDefault: true,
|
|
109
|
-
moduleCache: false
|
|
110
|
-
});
|
|
111
|
-
const meta = {};
|
|
112
|
-
const allHttpRoutes = {};
|
|
113
|
-
let totalFunctions = 0;
|
|
114
|
-
const files = listFilesRecursive(functionsDir).filter((file) => file.endsWith(".ts") && isValidConvexFile(file));
|
|
115
|
-
for (const file of files) {
|
|
116
|
-
const filePath = path.join(functionsDir, file);
|
|
117
|
-
const moduleName = file.replace(TS_EXTENSION_RE, "");
|
|
118
|
-
try {
|
|
119
|
-
const { meta: moduleMeta, httpRoutes } = await parseModuleRuntime(filePath, jitiInstance);
|
|
120
|
-
if (moduleMeta) {
|
|
121
|
-
meta[moduleName] = moduleMeta;
|
|
122
|
-
const fnCount = Object.keys(moduleMeta).length;
|
|
123
|
-
totalFunctions += fnCount;
|
|
124
|
-
if (debug) console.info(` ✓ ${moduleName}: ${fnCount} functions`);
|
|
125
|
-
}
|
|
126
|
-
if (Object.keys(httpRoutes).length > 0 && debug) console.info(` ✓ ${moduleName}: ${Object.keys(httpRoutes).length} HTTP routes`);
|
|
127
|
-
Object.assign(allHttpRoutes, httpRoutes);
|
|
128
|
-
} catch (error) {
|
|
129
|
-
if (debug || file === "http.ts") console.error(` ⚠ Failed to parse ${file}:`, error);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
const formatKey = (key) => VALID_IDENTIFIER_RE.test(key) ? key : `'${key}'`;
|
|
133
|
-
const metaEntries = Object.entries(meta).map(([module, fns]) => {
|
|
134
|
-
const fnEntries = Object.entries(fns).map(([fn, fnMeta]) => {
|
|
135
|
-
const metaProps = [];
|
|
136
|
-
for (const [key, value] of Object.entries(fnMeta).sort()) {
|
|
137
|
-
if (value === void 0) continue;
|
|
138
|
-
if (typeof value === "string") metaProps.push(`${key}: '${value}'`);
|
|
139
|
-
else if (typeof value === "boolean") metaProps.push(`${key}: ${value}`);
|
|
140
|
-
else if (typeof value === "number") metaProps.push(`${key}: ${value}`);
|
|
141
|
-
}
|
|
142
|
-
return ` ${fn}: ${`{ ${metaProps.join(", ")} }`}`;
|
|
143
|
-
}).join(",\n");
|
|
144
|
-
return ` ${formatKey(module)}: {\n${fnEntries},\n }`;
|
|
145
|
-
}).join(",\n");
|
|
146
|
-
const metaContent = metaEntries ? `\n${metaEntries},\n` : "";
|
|
147
|
-
const routesByPath = /* @__PURE__ */ new Map();
|
|
148
|
-
for (const [key, route] of Object.entries(allHttpRoutes)) {
|
|
149
|
-
const pathKey = `${route.path}:${route.method}`;
|
|
150
|
-
const existing = routesByPath.get(pathKey) || [];
|
|
151
|
-
existing.push({
|
|
152
|
-
key,
|
|
153
|
-
route
|
|
154
|
-
});
|
|
155
|
-
routesByPath.set(pathKey, existing);
|
|
156
|
-
}
|
|
157
|
-
const dedupedRoutes = {};
|
|
158
|
-
for (const entries of routesByPath.values()) {
|
|
159
|
-
const best = entries.reduce((a, b) => a.key.length >= b.key.length ? a : b);
|
|
160
|
-
dedupedRoutes[best.key] = best.route;
|
|
161
|
-
}
|
|
162
|
-
const httpEntries = Object.entries(dedupedRoutes).sort(([a], [b]) => a.localeCompare(b)).map(([name, route]) => ` ${formatKey(name)}: { path: '${route.path}', method: '${route.method}' }`).join(",\n");
|
|
163
|
-
const httpContent = httpEntries ? `\n${httpEntries},\n ` : "";
|
|
164
|
-
const output = `// biome-ignore-all format: generated
|
|
165
|
-
// This file is auto-generated by better-convex
|
|
166
|
-
// Do not edit manually. Run \`better-convex codegen\` to regenerate.
|
|
167
|
-
|
|
168
|
-
export const meta = {${metaContent ? `${metaContent} _http: {${httpContent}},\n` : `\n _http: {${httpContent}},\n`}} as const;
|
|
169
|
-
|
|
170
|
-
export type Meta = typeof meta;
|
|
171
|
-
`;
|
|
172
|
-
const metaDir = path.dirname(outputFile);
|
|
173
|
-
if (!fs.existsSync(metaDir)) fs.mkdirSync(metaDir, { recursive: true });
|
|
174
|
-
fs.writeFileSync(outputFile, output);
|
|
175
|
-
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
176
|
-
const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
|
|
177
|
-
hour12: false,
|
|
178
|
-
hour: "2-digit",
|
|
179
|
-
minute: "2-digit",
|
|
180
|
-
second: "2-digit"
|
|
181
|
-
});
|
|
182
|
-
if (!silent) if (debug) {
|
|
183
|
-
console.info(`\n✅ Generated ${outputFile}`);
|
|
184
|
-
console.info(` ${Object.keys(meta).length} modules, ${totalFunctions} functions`);
|
|
185
|
-
} else console.info(`✔ ${time} Convex meta ready! (${elapsed}s)`);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
//#endregion
|
|
189
|
-
export { getConvexConfig as n, generateMeta as t };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { GenericActionCtx, GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server";
|
|
2
|
-
|
|
3
|
-
//#region src/server/context-utils.d.ts
|
|
4
|
-
type GenericCtx<DataModel extends GenericDataModel = GenericDataModel> = GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel> | GenericActionCtx<DataModel>;
|
|
5
|
-
type RunMutationCtx<DataModel extends GenericDataModel> = (GenericMutationCtx<DataModel> | GenericActionCtx<DataModel>) & {
|
|
6
|
-
runMutation: GenericMutationCtx<DataModel>['runMutation'];
|
|
7
|
-
};
|
|
8
|
-
declare const isQueryCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => ctx is GenericQueryCtx<DataModel>;
|
|
9
|
-
declare const isMutationCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => ctx is GenericMutationCtx<DataModel>;
|
|
10
|
-
declare const isActionCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => ctx is GenericActionCtx<DataModel>;
|
|
11
|
-
declare const isRunMutationCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => ctx is RunMutationCtx<DataModel>;
|
|
12
|
-
declare const requireQueryCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => GenericQueryCtx<DataModel>;
|
|
13
|
-
declare const requireMutationCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => GenericMutationCtx<DataModel>;
|
|
14
|
-
declare const requireActionCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => GenericActionCtx<DataModel>;
|
|
15
|
-
declare const requireRunMutationCtx: <DataModel extends GenericDataModel>(ctx: GenericCtx<DataModel>) => RunMutationCtx<DataModel>;
|
|
16
|
-
//#endregion
|
|
17
|
-
export { isQueryCtx as a, requireMutationCtx as c, isMutationCtx as i, requireQueryCtx as l, RunMutationCtx as n, isRunMutationCtx as o, isActionCtx as r, requireActionCtx as s, GenericCtx as t, requireRunMutationCtx as u };
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
//#region src/shared/meta-utils.ts
|
|
2
|
-
/**
|
|
3
|
-
* Get a function reference from the API object by traversing the path.
|
|
4
|
-
*/
|
|
5
|
-
function getFuncRef(api, path) {
|
|
6
|
-
let current = api;
|
|
7
|
-
for (const key of path) if (current && typeof current === "object") current = current[key];
|
|
8
|
-
else throw new Error(`Invalid path: ${path.join(".")}`);
|
|
9
|
-
return current;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Get function type from meta using path.
|
|
13
|
-
* Supports nested paths like ['items', 'queries', 'list'] → namespace='items/queries', fn='list'
|
|
14
|
-
*
|
|
15
|
-
* @param path - Path segments like ['todos', 'create'] or ['items', 'queries', 'list']
|
|
16
|
-
* @param meta - The meta object from codegen
|
|
17
|
-
* @returns Function type or 'query' as default
|
|
18
|
-
*/
|
|
19
|
-
function getFunctionType(path, meta) {
|
|
20
|
-
if (path.length < 2) return "query";
|
|
21
|
-
const fnName = path.at(-1);
|
|
22
|
-
const fnType = meta[path.slice(0, -1).join("/")]?.[fnName]?.type;
|
|
23
|
-
if (fnType === "query" || fnType === "mutation" || fnType === "action") return fnType;
|
|
24
|
-
return "query";
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Get function metadata from meta using path.
|
|
28
|
-
* Supports nested paths like ['items', 'queries', 'list'] → namespace='items/queries', fn='list'
|
|
29
|
-
*
|
|
30
|
-
* @param path - Path segments like ['todos', 'create'] or ['items', 'queries', 'list']
|
|
31
|
-
* @param meta - The meta object from codegen
|
|
32
|
-
* @returns Function metadata or undefined
|
|
33
|
-
*/
|
|
34
|
-
function getFunctionMeta(path, meta) {
|
|
35
|
-
if (path.length < 2) return;
|
|
36
|
-
const fnName = path.at(-1);
|
|
37
|
-
return meta[path.slice(0, -1).join("/")]?.[fnName];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
//#endregion
|
|
41
|
-
export { getFunctionMeta as n, getFunctionType as r, getFuncRef as t };
|