kitcn 0.0.1 → 0.12.1
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/bin/intent.js +3 -0
- package/dist/aggregate/index.d.ts +388 -0
- package/dist/aggregate/index.js +37 -0
- package/dist/api-entry-BckXqaLb.js +66 -0
- package/dist/auth/client/index.d.ts +37 -0
- package/dist/auth/client/index.js +217 -0
- package/dist/auth/config/index.d.ts +45 -0
- package/dist/auth/config/index.js +24 -0
- package/dist/auth/generated/index.d.ts +2 -0
- package/dist/auth/generated/index.js +3 -0
- package/dist/auth/http/index.d.ts +64 -0
- package/dist/auth/http/index.js +461 -0
- package/dist/auth/index.d.ts +221 -0
- package/dist/auth/index.js +1398 -0
- package/dist/auth/nextjs/index.d.ts +50 -0
- package/dist/auth/nextjs/index.js +81 -0
- package/dist/auth-store-Cljlmdmi.js +197 -0
- package/dist/builder-CBdG5W6A.js +1974 -0
- package/dist/caller-factory-cTXNvYdz.js +216 -0
- package/dist/cli.mjs +13264 -0
- package/dist/codegen-lF80HSWu.mjs +3416 -0
- package/dist/context-utils-HPC5nXzx.d.ts +17 -0
- package/dist/create-schema-odyF4kCy.js +156 -0
- package/dist/create-schema-orm-DOyiNDCx.js +246 -0
- package/dist/crpc/index.d.ts +105 -0
- package/dist/crpc/index.js +169 -0
- package/dist/customFunctions-C0voKmtx.js +144 -0
- package/dist/error-BZEnI7Sq.js +41 -0
- package/dist/generated-contract-disabled-Cih4eITO.js +50 -0
- package/dist/generated-contract-disabled-D-sOFy92.d.ts +354 -0
- package/dist/http-types-DqJubRPJ.d.ts +292 -0
- package/dist/meta-utils-0Pu0Nrap.js +117 -0
- package/dist/middleware-BUybuv9n.d.ts +34 -0
- package/dist/middleware-C2qTZ3V7.js +84 -0
- package/dist/orm/index.d.ts +17 -0
- package/dist/orm/index.js +10713 -0
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.js +3 -0
- package/dist/procedure-caller-DtxLmGwA.d.ts +1467 -0
- package/dist/procedure-caller-MWcxhQDv.js +349 -0
- package/dist/query-context-B8o6-8kC.js +1518 -0
- package/dist/query-context-CFZqIvD7.d.ts +42 -0
- package/dist/query-options-Dw7cOyXl.js +121 -0
- package/dist/ratelimit/index.d.ts +269 -0
- package/dist/ratelimit/index.js +856 -0
- package/dist/ratelimit/react/index.d.ts +76 -0
- package/dist/ratelimit/react/index.js +183 -0
- package/dist/react/index.d.ts +1284 -0
- package/dist/react/index.js +2526 -0
- package/dist/rsc/index.d.ts +276 -0
- package/dist/rsc/index.js +233 -0
- package/dist/runtime-CtvJPkur.js +2453 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +6 -0
- package/dist/solid/index.d.ts +1221 -0
- package/dist/solid/index.js +2940 -0
- package/dist/transformer-DtDhR3Lc.js +194 -0
- package/dist/types-BTb_4BaU.d.ts +42 -0
- package/dist/types-BiJE7qxR.d.ts +4 -0
- package/dist/types-DEJpkIhw.d.ts +88 -0
- package/dist/types-HhO_R6pd.d.ts +213 -0
- package/dist/validators-B7oIJCAp.js +279 -0
- package/dist/validators-vzRKjBJC.d.ts +88 -0
- package/dist/watcher.mjs +96 -0
- package/dist/where-clause-compiler-DdjN63Io.d.ts +4756 -0
- package/package.json +107 -34
- package/skills/convex/SKILL.md +486 -0
- package/skills/convex/references/features/aggregates.md +353 -0
- package/skills/convex/references/features/auth-admin.md +446 -0
- package/skills/convex/references/features/auth-organizations.md +1141 -0
- package/skills/convex/references/features/auth-polar.md +579 -0
- package/skills/convex/references/features/auth.md +470 -0
- package/skills/convex/references/features/create-plugins.md +153 -0
- package/skills/convex/references/features/http.md +676 -0
- package/skills/convex/references/features/migrations.md +162 -0
- package/skills/convex/references/features/orm.md +1166 -0
- package/skills/convex/references/features/react.md +657 -0
- package/skills/convex/references/features/scheduling.md +267 -0
- package/skills/convex/references/features/testing.md +209 -0
- package/skills/convex/references/setup/auth.md +501 -0
- package/skills/convex/references/setup/biome.md +190 -0
- package/skills/convex/references/setup/doc-guidelines.md +145 -0
- package/skills/convex/references/setup/index.md +761 -0
- package/skills/convex/references/setup/next.md +116 -0
- package/skills/convex/references/setup/react.md +175 -0
- package/skills/convex/references/setup/server.md +473 -0
- package/skills/convex/references/setup/start.md +67 -0
- package/LICENSE +0 -21
- package/README.md +0 -0
- package/dist/index.d.mts +0 -5
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -6
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
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 };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
//#region src/auth/create-schema.ts
|
|
2
|
+
const indexFields = {
|
|
3
|
+
account: [
|
|
4
|
+
"accountId",
|
|
5
|
+
["accountId", "providerId"],
|
|
6
|
+
["providerId", "userId"]
|
|
7
|
+
],
|
|
8
|
+
oauthConsent: [["clientId", "userId"]],
|
|
9
|
+
passkey: ["credentialID"],
|
|
10
|
+
ratelimit: ["key"],
|
|
11
|
+
rateLimit: ["key"],
|
|
12
|
+
session: ["expiresAt", ["expiresAt", "userId"]],
|
|
13
|
+
user: [["email", "name"], "name"],
|
|
14
|
+
verification: ["expiresAt", "identifier"]
|
|
15
|
+
};
|
|
16
|
+
const cloneTables = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => [key, {
|
|
17
|
+
...table,
|
|
18
|
+
fields: { ...table.fields }
|
|
19
|
+
}]));
|
|
20
|
+
const ensureField = (tables, tableKey, fieldKey, fieldPatch) => {
|
|
21
|
+
const table = tables[tableKey];
|
|
22
|
+
if (!table) return;
|
|
23
|
+
const existingField = table.fields[fieldKey];
|
|
24
|
+
if (!existingField) {
|
|
25
|
+
table.fields[fieldKey] = fieldPatch;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
table.fields[fieldKey] = {
|
|
29
|
+
...fieldPatch,
|
|
30
|
+
...existingField,
|
|
31
|
+
references: existingField.references ?? fieldPatch.references
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const augmentBetterAuthTables = (sourceTables) => {
|
|
35
|
+
const tables = cloneTables(sourceTables);
|
|
36
|
+
if (tables.organization && tables.user) {
|
|
37
|
+
const organizationReference = {
|
|
38
|
+
field: "id",
|
|
39
|
+
model: "organization"
|
|
40
|
+
};
|
|
41
|
+
ensureField(tables, "user", "lastActiveOrganizationId", {
|
|
42
|
+
references: organizationReference,
|
|
43
|
+
required: false,
|
|
44
|
+
type: "string"
|
|
45
|
+
});
|
|
46
|
+
ensureField(tables, "user", "personalOrganizationId", {
|
|
47
|
+
references: organizationReference,
|
|
48
|
+
required: false,
|
|
49
|
+
type: "string"
|
|
50
|
+
});
|
|
51
|
+
ensureField(tables, "session", "activeOrganizationId", {
|
|
52
|
+
references: organizationReference,
|
|
53
|
+
required: false,
|
|
54
|
+
type: "string"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (tables.team) {
|
|
58
|
+
const teamReference = {
|
|
59
|
+
field: "id",
|
|
60
|
+
model: "team"
|
|
61
|
+
};
|
|
62
|
+
ensureField(tables, "session", "activeTeamId", {
|
|
63
|
+
references: teamReference,
|
|
64
|
+
required: false,
|
|
65
|
+
type: "string"
|
|
66
|
+
});
|
|
67
|
+
ensureField(tables, "invitation", "teamId", {
|
|
68
|
+
references: teamReference,
|
|
69
|
+
required: false,
|
|
70
|
+
type: "string"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return tables;
|
|
74
|
+
};
|
|
75
|
+
const specialFields = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => {
|
|
76
|
+
return [key, Object.fromEntries(Object.entries(table.fields).map(([fieldKey, field]) => [field.fieldName ?? fieldKey, {
|
|
77
|
+
...field.sortable ? { sortable: true } : {},
|
|
78
|
+
...field.unique ? { unique: true } : {},
|
|
79
|
+
...field.references ? { references: field.references } : {}
|
|
80
|
+
}]).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true))];
|
|
81
|
+
}).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true));
|
|
82
|
+
const mergedIndexFields = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => {
|
|
83
|
+
const resolveIndexField = (fieldKey) => {
|
|
84
|
+
const field = table.fields[fieldKey];
|
|
85
|
+
return field ? field.fieldName ?? fieldKey : null;
|
|
86
|
+
};
|
|
87
|
+
const manualIndexes = indexFields[key]?.reduce((indexes, index) => {
|
|
88
|
+
if (typeof index === "string") {
|
|
89
|
+
const resolved = resolveIndexField(index);
|
|
90
|
+
if (resolved) indexes.push(resolved);
|
|
91
|
+
return indexes;
|
|
92
|
+
}
|
|
93
|
+
const resolved = index.map((fieldKey) => resolveIndexField(fieldKey)).filter((fieldName) => fieldName !== null);
|
|
94
|
+
if (resolved.length === index.length) indexes.push(resolved);
|
|
95
|
+
return indexes;
|
|
96
|
+
}, []) || [];
|
|
97
|
+
const specialFieldIndexes = Object.keys(specialFields(tables)[key] || {}).filter((index) => !manualIndexes.some((m) => Array.isArray(m) ? m[0] === index : m === index));
|
|
98
|
+
return [key, manualIndexes.concat(specialFieldIndexes)];
|
|
99
|
+
}));
|
|
100
|
+
const createSchema = async ({ exportName = "tables", file, regenerateCommand, tables }) => {
|
|
101
|
+
const path = await import(Buffer.from("cGF0aA==", "base64").toString());
|
|
102
|
+
if (path.basename(path.resolve(process.cwd(), file ?? "")) === "convex") throw new Error("Better Auth schema must be generated in the Better Auth component directory.");
|
|
103
|
+
tables = augmentBetterAuthTables(tables);
|
|
104
|
+
let code = `// This file is auto-generated. Do not edit this file manually.
|
|
105
|
+
// To regenerate the schema, run:
|
|
106
|
+
// \`${regenerateCommand ?? `npx @better-auth/cli generate --output ${file} -y`}\`
|
|
107
|
+
|
|
108
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
109
|
+
import { v } from "convex/values";
|
|
110
|
+
|
|
111
|
+
export const ${exportName} = {
|
|
112
|
+
`;
|
|
113
|
+
for (const [tableKey, table] of Object.entries(tables)) {
|
|
114
|
+
const modelName = table.modelName;
|
|
115
|
+
const fields = Object.fromEntries(Object.entries(table.fields).filter(([key]) => key !== "id"));
|
|
116
|
+
function getType(_name, field) {
|
|
117
|
+
const type = field.type;
|
|
118
|
+
return {
|
|
119
|
+
boolean: "v.boolean()",
|
|
120
|
+
date: "v.number()",
|
|
121
|
+
json: "v.string()",
|
|
122
|
+
number: "v.number()",
|
|
123
|
+
"number[]": "v.array(v.number())",
|
|
124
|
+
string: "v.string()",
|
|
125
|
+
"string[]": "v.array(v.string())"
|
|
126
|
+
}[type];
|
|
127
|
+
}
|
|
128
|
+
const indexes = mergedIndexFields(tables)[tableKey]?.map((index) => {
|
|
129
|
+
const indexArray = Array.isArray(index) ? index.sort() : [index];
|
|
130
|
+
return `.index("${indexArray.join("_")}", ${JSON.stringify(indexArray)})`;
|
|
131
|
+
}) || [];
|
|
132
|
+
const schema = `${modelName}: defineTable({
|
|
133
|
+
${Object.keys(fields).map((field) => {
|
|
134
|
+
const attr = fields[field];
|
|
135
|
+
const type = getType(field, attr);
|
|
136
|
+
const optional = (fieldSchema) => attr.required ? fieldSchema : `v.optional(v.union(v.null(), ${fieldSchema}))`;
|
|
137
|
+
return ` ${attr.fieldName ?? field}: ${optional(type)},`;
|
|
138
|
+
}).join("\n")}
|
|
139
|
+
})${indexes.length > 0 ? `\n ${indexes.join("\n ")}` : ""},\n`;
|
|
140
|
+
code += ` ${schema}`;
|
|
141
|
+
}
|
|
142
|
+
code += `};
|
|
143
|
+
|
|
144
|
+
const schema = defineSchema(${exportName});
|
|
145
|
+
|
|
146
|
+
export default schema;
|
|
147
|
+
`;
|
|
148
|
+
return {
|
|
149
|
+
code,
|
|
150
|
+
overwrite: true,
|
|
151
|
+
path: file ?? "./schema.ts"
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
//#endregion
|
|
156
|
+
export { augmentBetterAuthTables, createSchema, indexFields };
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { augmentBetterAuthTables, indexFields } from "./create-schema-odyF4kCy.js";
|
|
2
|
+
|
|
3
|
+
//#region src/auth/create-schema-orm.ts
|
|
4
|
+
const specialFields = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => {
|
|
5
|
+
return [key, Object.fromEntries(Object.entries(table.fields).map(([fieldKey, field]) => [field.fieldName ?? fieldKey, {
|
|
6
|
+
...field.sortable ? { sortable: true } : {},
|
|
7
|
+
...field.unique ? { unique: true } : {},
|
|
8
|
+
...field.references ? { references: field.references } : {}
|
|
9
|
+
}]).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true))];
|
|
10
|
+
}).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true));
|
|
11
|
+
const mergedIndexFields = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => {
|
|
12
|
+
const tableSpecialFields = specialFields(tables)[key] || {};
|
|
13
|
+
const resolveIndexField = (fieldKey) => {
|
|
14
|
+
const field = table.fields[fieldKey];
|
|
15
|
+
return field ? field.fieldName ?? fieldKey : null;
|
|
16
|
+
};
|
|
17
|
+
const manualIndexes = indexFields[key]?.reduce((indexes, index) => {
|
|
18
|
+
if (typeof index === "string") {
|
|
19
|
+
const resolved = resolveIndexField(index);
|
|
20
|
+
if (resolved) indexes.push(resolved);
|
|
21
|
+
return indexes;
|
|
22
|
+
}
|
|
23
|
+
const resolved = index.map((fieldKey) => resolveIndexField(fieldKey)).filter((fieldName) => fieldName !== null);
|
|
24
|
+
if (resolved.length === index.length) indexes.push(resolved);
|
|
25
|
+
return indexes;
|
|
26
|
+
}, []) || [];
|
|
27
|
+
const specialFieldIndexes = Object.entries(tableSpecialFields).filter(([, fieldMeta]) => fieldMeta.unique !== true).map(([fieldName]) => fieldName).filter((index) => !manualIndexes.some((m) => Array.isArray(m) ? m[0] === index : m === index));
|
|
28
|
+
return [key, manualIndexes.concat(specialFieldIndexes)];
|
|
29
|
+
}));
|
|
30
|
+
const VALID_IDENTIFIER_REGEX = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
31
|
+
const LEADING_DIGIT_REGEX = /^[0-9]/;
|
|
32
|
+
const PLURALIZE_ES_SUFFIX_REGEX = /(?:s|x|z|ch|sh)$/i;
|
|
33
|
+
const PLURALIZE_IES_SUFFIX_REGEX = /[^aeiou]y$/i;
|
|
34
|
+
const TABLE_IDENTIFIER_SUFFIX_REGEX = /(?:Table|_table)$/i;
|
|
35
|
+
const renderObjectKey = (value) => VALID_IDENTIFIER_REGEX.test(value) ? value : JSON.stringify(value);
|
|
36
|
+
const renderPropertyAccess = (objectName, propertyName) => VALID_IDENTIFIER_REGEX.test(propertyName) ? `${objectName}.${propertyName}` : `${objectName}[${JSON.stringify(propertyName)}]`;
|
|
37
|
+
const toIdentifier = (value) => {
|
|
38
|
+
const normalized = value.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
39
|
+
if (!normalized) return "_table";
|
|
40
|
+
if (LEADING_DIGIT_REGEX.test(normalized)) return `_${normalized}`;
|
|
41
|
+
return normalized;
|
|
42
|
+
};
|
|
43
|
+
const toTableIdentifier = (value) => {
|
|
44
|
+
const identifier = toIdentifier(value);
|
|
45
|
+
return TABLE_IDENTIFIER_SUFFIX_REGEX.test(identifier) ? identifier : `${identifier}Table`;
|
|
46
|
+
};
|
|
47
|
+
const getTableEntries = (tables) => {
|
|
48
|
+
const usedNames = /* @__PURE__ */ new Map();
|
|
49
|
+
return Object.entries(tables).map(([key, table]) => {
|
|
50
|
+
const modelName = table.modelName;
|
|
51
|
+
const baseName = toTableIdentifier(modelName);
|
|
52
|
+
const count = usedNames.get(baseName) ?? 0;
|
|
53
|
+
usedNames.set(baseName, count + 1);
|
|
54
|
+
return {
|
|
55
|
+
key,
|
|
56
|
+
modelName,
|
|
57
|
+
table,
|
|
58
|
+
varName: count === 0 ? baseName : `${baseName}_${count + 1}`
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const findTableEntryByModel = (entries, tables, model) => entries.find((entry) => entry.modelName === model) ?? entries.find((entry) => entry.key === model) ?? entries.find((entry) => tables[entry.key]?.modelName === model);
|
|
63
|
+
const getReferencedFieldName = (tables, entries, model, field) => {
|
|
64
|
+
if (field === "id") return "id";
|
|
65
|
+
const entry = findTableEntryByModel(entries, tables, model);
|
|
66
|
+
if (!entry) return field;
|
|
67
|
+
return entry.table.fields[field]?.fieldName ?? field;
|
|
68
|
+
};
|
|
69
|
+
const stripIdSuffix = (value) => value.endsWith("Id") && value.length > 2 ? value.slice(0, -2) : value;
|
|
70
|
+
const capitalize = (value) => value ? `${value.slice(0, 1).toUpperCase()}${value.slice(1)}` : value;
|
|
71
|
+
const pluralize = (value) => {
|
|
72
|
+
if (PLURALIZE_ES_SUFFIX_REGEX.test(value)) return `${value}es`;
|
|
73
|
+
if (PLURALIZE_IES_SUFFIX_REGEX.test(value)) return `${value.slice(0, -1)}ies`;
|
|
74
|
+
return `${value}s`;
|
|
75
|
+
};
|
|
76
|
+
const buildRelationEntries = (tables, entries) => {
|
|
77
|
+
const rawRelations = entries.flatMap((source) => Object.entries(source.table.fields).map(([fieldKey, field]) => {
|
|
78
|
+
const attr = field;
|
|
79
|
+
if (!attr.references) return null;
|
|
80
|
+
const target = findTableEntryByModel(entries, tables, attr.references.model) ?? null;
|
|
81
|
+
if (!target) return null;
|
|
82
|
+
return {
|
|
83
|
+
source,
|
|
84
|
+
fieldKey,
|
|
85
|
+
sourceFieldName: attr.fieldName ?? fieldKey,
|
|
86
|
+
target,
|
|
87
|
+
targetFieldName: getReferencedFieldName(tables, entries, attr.references.model, attr.references.field)
|
|
88
|
+
};
|
|
89
|
+
}).filter((value) => value !== null));
|
|
90
|
+
const pairCounts = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const relation of rawRelations) {
|
|
92
|
+
const key = `${relation.source.modelName}->${relation.target.modelName}`;
|
|
93
|
+
pairCounts.set(key, (pairCounts.get(key) ?? 0) + 1);
|
|
94
|
+
}
|
|
95
|
+
return rawRelations.map((relation) => {
|
|
96
|
+
const pairKey = `${relation.source.modelName}->${relation.target.modelName}`;
|
|
97
|
+
const needsAlias = (pairCounts.get(pairKey) ?? 0) > 1;
|
|
98
|
+
const oneName = toIdentifier(stripIdSuffix(relation.fieldKey) || relation.target.modelName);
|
|
99
|
+
return {
|
|
100
|
+
alias: needsAlias ? oneName : void 0,
|
|
101
|
+
manyName: toIdentifier(needsAlias ? `${pluralize(relation.source.modelName)}As${capitalize(oneName)}` : pluralize(relation.source.modelName)),
|
|
102
|
+
oneName,
|
|
103
|
+
source: relation.source,
|
|
104
|
+
sourceFieldName: relation.sourceFieldName,
|
|
105
|
+
target: relation.target,
|
|
106
|
+
targetFieldName: relation.targetFieldName
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
const getTypeExpression = (field, state) => {
|
|
111
|
+
const type = field.type;
|
|
112
|
+
if (Array.isArray(type)) {
|
|
113
|
+
state.ormImports.add("textEnum");
|
|
114
|
+
return `textEnum(${`[${type.map((value) => JSON.stringify(value)).join(", ")}]`})`;
|
|
115
|
+
}
|
|
116
|
+
switch (type) {
|
|
117
|
+
case "boolean":
|
|
118
|
+
state.ormImports.add("boolean");
|
|
119
|
+
return "boolean()";
|
|
120
|
+
case "date":
|
|
121
|
+
state.ormImports.add("timestamp");
|
|
122
|
+
return "timestamp()";
|
|
123
|
+
case "json":
|
|
124
|
+
state.ormImports.add("text");
|
|
125
|
+
return "text()";
|
|
126
|
+
case "number":
|
|
127
|
+
if (field.bigint) {
|
|
128
|
+
state.ormImports.add("bigint");
|
|
129
|
+
return "bigint()";
|
|
130
|
+
}
|
|
131
|
+
state.ormImports.add("integer");
|
|
132
|
+
return "integer()";
|
|
133
|
+
case "number[]":
|
|
134
|
+
state.ormImports.add("arrayOf");
|
|
135
|
+
state.ormImports.add("integer");
|
|
136
|
+
return "arrayOf(integer().notNull())";
|
|
137
|
+
case "string":
|
|
138
|
+
state.ormImports.add("text");
|
|
139
|
+
return "text()";
|
|
140
|
+
case "string[]":
|
|
141
|
+
state.ormImports.add("arrayOf");
|
|
142
|
+
state.ormImports.add("text");
|
|
143
|
+
return "arrayOf(text().notNull())";
|
|
144
|
+
default: throw new Error(`Unsupported Better Auth field type: ${String(type)}`);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const createSchemaOrm = async ({ file, regenerateCommand, tables }) => {
|
|
148
|
+
return renderSchemaOrmFile({
|
|
149
|
+
file,
|
|
150
|
+
mode: "schema",
|
|
151
|
+
regenerateCommand,
|
|
152
|
+
tables
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const renderSchemaOrmFile = async ({ extensionKey, exportName, file, mode, regenerateCommand, tables }) => {
|
|
156
|
+
const path = await import(Buffer.from("cGF0aA==", "base64").toString());
|
|
157
|
+
if (path.basename(path.resolve(process.cwd(), file ?? "")) === "convex") throw new Error("Better Auth schema must be generated in the Better Auth component directory.");
|
|
158
|
+
tables = augmentBetterAuthTables(tables);
|
|
159
|
+
const entries = getTableEntries(tables);
|
|
160
|
+
const relationEntries = buildRelationEntries(tables, entries);
|
|
161
|
+
const state = { ormImports: new Set(["convexTable", mode === "extension" ? "defineSchemaExtension" : "defineSchema"]) };
|
|
162
|
+
const tableBlocks = [];
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const fieldLines = Object.entries(entry.table.fields).filter(([fieldKey]) => fieldKey !== "id").map(([fieldKey, field]) => {
|
|
165
|
+
const attr = field;
|
|
166
|
+
const key = renderObjectKey(attr.fieldName ?? fieldKey);
|
|
167
|
+
let expression = getTypeExpression(attr, state);
|
|
168
|
+
if (attr.required) expression += ".notNull()";
|
|
169
|
+
if (attr.unique) expression += ".unique()";
|
|
170
|
+
if (attr.references) {
|
|
171
|
+
const referencedEntry = findTableEntryByModel(entries, tables, attr.references.model) ?? { varName: toIdentifier(attr.references.model) };
|
|
172
|
+
const targetField = getReferencedFieldName(tables, entries, attr.references.model, attr.references.field);
|
|
173
|
+
expression += `.references(() => ${renderPropertyAccess(referencedEntry.varName, targetField)})`;
|
|
174
|
+
}
|
|
175
|
+
return ` ${key}: ${expression},`;
|
|
176
|
+
});
|
|
177
|
+
const indexes = mergedIndexFields(tables)[entry.key]?.map((indexSpec) => {
|
|
178
|
+
const indexArray = Array.isArray(indexSpec) ? [...indexSpec].sort() : [indexSpec];
|
|
179
|
+
const indexName = indexArray.join("_");
|
|
180
|
+
state.ormImports.add("index");
|
|
181
|
+
const fieldsCall = indexArray.map((fieldName) => renderPropertyAccess(entry.varName, fieldName)).join(", ");
|
|
182
|
+
return `index(${JSON.stringify(indexName)}).on(${fieldsCall})`;
|
|
183
|
+
}) || [];
|
|
184
|
+
const extraConfig = indexes.length > 0 ? `,\n (${entry.varName}) => [\n ${indexes.join(",\n ")},\n ]` : "";
|
|
185
|
+
tableBlocks.push(`export const ${entry.varName} = convexTable(\n ${JSON.stringify(entry.modelName)},\n {\n${fieldLines.join("\n")}\n }${extraConfig}\n);`);
|
|
186
|
+
}
|
|
187
|
+
const imports = `import {\n ${Array.from(state.ormImports).sort().join(",\n ")},\n} from "kitcn/orm";`;
|
|
188
|
+
const tableObject = `{
|
|
189
|
+
${entries.map((entry) => {
|
|
190
|
+
if (renderObjectKey(entry.modelName) === entry.varName) return ` ${entry.varName},`;
|
|
191
|
+
return ` ${renderObjectKey(entry.modelName)}: ${entry.varName},`;
|
|
192
|
+
}).join("\n")}
|
|
193
|
+
}`;
|
|
194
|
+
const relationBlocksByTable = /* @__PURE__ */ new Map();
|
|
195
|
+
for (const relation of relationEntries) {
|
|
196
|
+
const sourceLines = [
|
|
197
|
+
` ${renderObjectKey(relation.oneName)}: ${renderPropertyAccess("r.one", relation.target.modelName)}({`,
|
|
198
|
+
` from: ${renderPropertyAccess(renderPropertyAccess("r", relation.source.modelName), relation.sourceFieldName)},`,
|
|
199
|
+
` to: ${renderPropertyAccess(renderPropertyAccess("r", relation.target.modelName), relation.targetFieldName)},`,
|
|
200
|
+
...relation.alias ? [` alias: ${JSON.stringify(relation.alias)},`] : [],
|
|
201
|
+
" }),"
|
|
202
|
+
];
|
|
203
|
+
relationBlocksByTable.set(relation.source.modelName, [...relationBlocksByTable.get(relation.source.modelName) ?? [], sourceLines.join("\n")]);
|
|
204
|
+
const targetLines = [
|
|
205
|
+
` ${renderObjectKey(relation.manyName)}: ${renderPropertyAccess("r.many", relation.source.modelName)}({`,
|
|
206
|
+
` from: ${renderPropertyAccess(renderPropertyAccess("r", relation.target.modelName), relation.targetFieldName)},`,
|
|
207
|
+
` to: ${renderPropertyAccess(renderPropertyAccess("r", relation.source.modelName), relation.sourceFieldName)},`,
|
|
208
|
+
...relation.alias ? [` alias: ${JSON.stringify(relation.alias)},`] : [],
|
|
209
|
+
" }),"
|
|
210
|
+
];
|
|
211
|
+
relationBlocksByTable.set(relation.target.modelName, [...relationBlocksByTable.get(relation.target.modelName) ?? [], targetLines.join("\n")]);
|
|
212
|
+
}
|
|
213
|
+
const relationObjectEntries = entries.map((entry) => {
|
|
214
|
+
const relationBlocks = relationBlocksByTable.get(entry.modelName);
|
|
215
|
+
if (!relationBlocks || relationBlocks.length === 0) return null;
|
|
216
|
+
return ` ${renderObjectKey(entry.modelName)}: {\n${relationBlocks.join("\n")}\n },`;
|
|
217
|
+
}).filter((value) => value !== null);
|
|
218
|
+
const relationChain = relationObjectEntries.length > 0 ? `.relations((r) => ({\n${relationObjectEntries.join("\n")}\n}))` : "";
|
|
219
|
+
const output = mode === "extension" ? `
|
|
220
|
+
export function ${exportName ?? "authExtension"}() {
|
|
221
|
+
return defineSchemaExtension(${JSON.stringify(extensionKey ?? "auth")}, ${tableObject})${relationChain};
|
|
222
|
+
}
|
|
223
|
+
` : `
|
|
224
|
+
export const tables = ${tableObject};
|
|
225
|
+
|
|
226
|
+
const schema = defineSchema(tables)${relationChain};
|
|
227
|
+
|
|
228
|
+
export default schema;
|
|
229
|
+
`;
|
|
230
|
+
return {
|
|
231
|
+
code: `// This file is auto-generated. Do not edit this file manually.
|
|
232
|
+
// To regenerate the schema, run:
|
|
233
|
+
// \`${regenerateCommand ?? `npx @better-auth/cli generate --output ${file} -y`}\`
|
|
234
|
+
|
|
235
|
+
${imports}
|
|
236
|
+
|
|
237
|
+
${tableBlocks.join("\n\n")}
|
|
238
|
+
${output}
|
|
239
|
+
`,
|
|
240
|
+
overwrite: true,
|
|
241
|
+
path: file ?? "./schema.ts"
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
//#endregion
|
|
246
|
+
export { createSchemaOrm };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { A as DataTransformer, F as decodeWire, I as defaultCRPCTransformer, L as encodeWire, M as WireCodec, N as createTaggedTransformer, O as CombinedDataTransformer, P as dateWireCodec, R as getTransformer, a as HttpProcedureCall, c as InferHttpInput, i as HttpErrorCode, j as DataTransformerOptions, k as DATE_CODEC_TAG, l as InferHttpOutput, n as HttpClientError, o as HttpRouteInfo, r as HttpClientFromRouter, s as HttpRouteMap, t as HttpClient, u as isHttpClientError, z as identityTransformer } from "../http-types-DqJubRPJ.js";
|
|
2
|
+
import { A as HttpInputArgs, C as ReservedMutationOptions, D as VanillaMutation, E as VanillaAction, F as replaceUrlParam, M as RESERVED_KEYS, N as buildSearchParams, O as HttpClientOptions, P as executeHttpRequest, S as ReservedInfiniteQueryOptions, T as StaticQueryOptsParam, _ as IsPaginated, a as BaseInfiniteQueryOptsParam, b as PaginatedFnMeta, c as ConvexMutationKey, d as ConvexQueryMeta, f as EmptyObject, g as InfiniteQueryInput, h as FnMeta, i as BaseConvexQueryOptions, j as HttpProxyBaseOptions, k as HttpFormValue, l as ConvexQueryHookOptions, m as FUNC_REF_SYMBOL, n as BaseConvexActionOptions, o as ConvexActionKey, p as ExtractPaginatedItem, r as BaseConvexInfiniteQueryOptions, s as ConvexInfiniteQueryMeta, t as AuthType, u as ConvexQueryKey, v as Meta, w as ReservedQueryOptions, x as PaginationOpts, y as MutationVariables } from "../types-HhO_R6pd.js";
|
|
3
|
+
import { FunctionArgs, FunctionReference } from "convex/server";
|
|
4
|
+
|
|
5
|
+
//#region src/crpc/auth-error.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Auth Mutation Error
|
|
8
|
+
*
|
|
9
|
+
* Framework-agnostic error class for Better Auth mutations.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when a Better Auth mutation fails.
|
|
13
|
+
* Contains the original error details from Better Auth.
|
|
14
|
+
*/
|
|
15
|
+
declare class AuthMutationError extends Error {
|
|
16
|
+
/** Error code from Better Auth (e.g., 'INVALID_PASSWORD', 'EMAIL_ALREADY_REGISTERED') */
|
|
17
|
+
code?: string;
|
|
18
|
+
/** HTTP status code */
|
|
19
|
+
status: number;
|
|
20
|
+
/** HTTP status text */
|
|
21
|
+
statusText: string;
|
|
22
|
+
constructor(authError: {
|
|
23
|
+
message?: string;
|
|
24
|
+
status: number;
|
|
25
|
+
statusText: string;
|
|
26
|
+
code?: string;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Type guard to check if an error is an AuthMutationError.
|
|
31
|
+
*/
|
|
32
|
+
declare function isAuthMutationError(error: unknown): error is AuthMutationError;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/crpc/error.d.ts
|
|
35
|
+
/**
|
|
36
|
+
* Client error codes (subset of CRPC codes for client-side use)
|
|
37
|
+
*/
|
|
38
|
+
type ClientErrorCode = 'UNAUTHORIZED' | 'FORBIDDEN' | 'NOT_FOUND' | 'BAD_REQUEST' | 'TOO_MANY_REQUESTS';
|
|
39
|
+
/**
|
|
40
|
+
* Client-side CRPC error.
|
|
41
|
+
* Mirrors backend CRPCError pattern with typed error codes.
|
|
42
|
+
*/
|
|
43
|
+
declare class CRPCClientError extends Error {
|
|
44
|
+
readonly name = "CRPCClientError";
|
|
45
|
+
readonly code: ClientErrorCode;
|
|
46
|
+
readonly functionName: string;
|
|
47
|
+
constructor(opts: {
|
|
48
|
+
code: ClientErrorCode;
|
|
49
|
+
functionName: string;
|
|
50
|
+
message?: string;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/** Type guard for CRPCClientError */
|
|
54
|
+
declare const isCRPCClientError: (error: unknown) => error is CRPCClientError;
|
|
55
|
+
/**
|
|
56
|
+
* Unified check for any deterministic CRPC error (Convex or HTTP).
|
|
57
|
+
* Use in retry logic to skip retrying client errors (4xx).
|
|
58
|
+
*/
|
|
59
|
+
declare const isCRPCError: (error: unknown) => boolean;
|
|
60
|
+
/** Type guard for specific error code */
|
|
61
|
+
declare const isCRPCErrorCode: (error: unknown, code: ClientErrorCode) => error is CRPCClientError;
|
|
62
|
+
/** Default unauthorized detection - checks UNAUTHORIZED code */
|
|
63
|
+
declare const defaultIsUnauthorized: (error: unknown) => boolean;
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/crpc/query-options.d.ts
|
|
66
|
+
/**
|
|
67
|
+
* Query options factory for Convex query function subscriptions.
|
|
68
|
+
* Requires `convexQueryClient.queryFn()` set as the default `queryFn` globally.
|
|
69
|
+
*/
|
|
70
|
+
declare function convexQuery<T extends FunctionReference<'query'>>(funcRef: T, args?: FunctionArgs<T> | 'skip', meta?: Meta, opts?: {
|
|
71
|
+
skipUnauth?: boolean;
|
|
72
|
+
}): BaseConvexQueryOptions<T> & {
|
|
73
|
+
meta: ConvexQueryMeta;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Query options factory for Convex action functions.
|
|
77
|
+
* Actions are NOT reactive - they follow normal TanStack Query semantics.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* useQuery(convexAction(api.ai.generate, { prompt }))
|
|
82
|
+
* ```
|
|
83
|
+
*
|
|
84
|
+
* @example With additional options (use spread):
|
|
85
|
+
* ```ts
|
|
86
|
+
* useQuery({
|
|
87
|
+
* ...convexAction(api.files.process, { fileId }),
|
|
88
|
+
* staleTime: 60_000
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
declare function convexAction<T extends FunctionReference<'action'>>(funcRef: T, args?: FunctionArgs<T> | 'skip', meta?: Meta, opts?: {
|
|
93
|
+
skipUnauth?: boolean;
|
|
94
|
+
}): BaseConvexActionOptions<T> & {
|
|
95
|
+
meta: ConvexQueryMeta;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Infinite query options factory for paginated Convex queries.
|
|
99
|
+
* Server-safe (non-hook) - can be used in RSC.
|
|
100
|
+
*
|
|
101
|
+
* Uses flat { cursor, limit } input like tRPC.
|
|
102
|
+
*/
|
|
103
|
+
declare function convexInfiniteQueryOptions<T extends FunctionReference<'query'>>(funcRef: T, args: Record<string, unknown> | 'skip', opts?: BaseInfiniteQueryOptsParam<T> & Record<string, unknown>, meta?: Meta): BaseConvexInfiniteQueryOptions<T> & Record<string, unknown>;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { AuthMutationError, AuthType, BaseConvexActionOptions, BaseConvexInfiniteQueryOptions, BaseConvexQueryOptions, BaseInfiniteQueryOptsParam, CRPCClientError, CombinedDataTransformer, ConvexActionKey, ConvexInfiniteQueryMeta, ConvexMutationKey, ConvexQueryHookOptions, ConvexQueryKey, ConvexQueryMeta, DATE_CODEC_TAG, DataTransformer, DataTransformerOptions, EmptyObject, ExtractPaginatedItem, FUNC_REF_SYMBOL, FnMeta, HttpClient, HttpClientError, HttpClientFromRouter, HttpClientOptions, HttpErrorCode, HttpFormValue, HttpInputArgs, HttpProcedureCall, HttpProxyBaseOptions, HttpRouteInfo, HttpRouteMap, InferHttpInput, InferHttpOutput, InfiniteQueryInput, IsPaginated, Meta, MutationVariables, PaginatedFnMeta, PaginationOpts, RESERVED_KEYS, ReservedInfiniteQueryOptions, ReservedMutationOptions, ReservedQueryOptions, StaticQueryOptsParam, VanillaAction, VanillaMutation, WireCodec, buildSearchParams, convexAction, convexInfiniteQueryOptions, convexQuery, createTaggedTransformer, dateWireCodec, decodeWire, defaultCRPCTransformer, defaultIsUnauthorized, encodeWire, executeHttpRequest, getTransformer, identityTransformer, isAuthMutationError, isCRPCClientError, isCRPCError, isCRPCErrorCode, isHttpClientError, replaceUrlParam };
|