create-better-t-stack 3.5.3 → 3.6.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/cli.js +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +1 -1
- package/dist/{src-C_MeUxmG.js → src-CWiiwWrU.js} +104 -11
- package/package.json +1 -1
- package/templates/auth/better-auth/server/db/drizzle/mysql/src/schema/auth.ts.hbs +83 -41
- package/templates/auth/better-auth/server/db/drizzle/postgres/src/schema/auth.ts.hbs +88 -42
- package/templates/auth/better-auth/server/db/drizzle/sqlite/src/schema/auth.ts.hbs +96 -44
- package/templates/auth/better-auth/server/db/prisma/mongodb/prisma/schema/auth.prisma.hbs +13 -10
- package/templates/auth/better-auth/server/db/prisma/mysql/prisma/schema/auth.prisma.hbs +15 -12
- package/templates/auth/better-auth/server/db/prisma/postgres/prisma/schema/auth.prisma.hbs +16 -13
- package/templates/auth/better-auth/server/db/prisma/sqlite/prisma/schema/auth.prisma.hbs +16 -13
- package/templates/frontend/native/bare/package.json.hbs +1 -0
- package/templates/frontend/native/uniwind/package.json.hbs +1 -0
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -122,8 +122,16 @@ declare const DirectoryConflictSchema: z.ZodEnum<{
|
|
|
122
122
|
increment: "increment";
|
|
123
123
|
}>;
|
|
124
124
|
type DirectoryConflict = z.infer<typeof DirectoryConflictSchema>;
|
|
125
|
+
declare const TemplateSchema: z.ZodEnum<{
|
|
126
|
+
mern: "mern";
|
|
127
|
+
pern: "pern";
|
|
128
|
+
t3: "t3";
|
|
129
|
+
none: "none";
|
|
130
|
+
}>;
|
|
131
|
+
type Template = z.infer<typeof TemplateSchema>;
|
|
125
132
|
type CreateInput = {
|
|
126
133
|
projectName?: string;
|
|
134
|
+
template?: Template;
|
|
127
135
|
yes?: boolean;
|
|
128
136
|
yolo?: boolean;
|
|
129
137
|
verbose?: boolean;
|
|
@@ -209,6 +217,12 @@ interface InitResult {
|
|
|
209
217
|
//#region src/index.d.ts
|
|
210
218
|
declare const router: {
|
|
211
219
|
init: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, z$1.ZodTuple<[z$1.ZodOptional<z$1.ZodString>, z$1.ZodObject<{
|
|
220
|
+
template: z$1.ZodOptional<z$1.ZodEnum<{
|
|
221
|
+
mern: "mern";
|
|
222
|
+
pern: "pern";
|
|
223
|
+
t3: "t3";
|
|
224
|
+
none: "none";
|
|
225
|
+
}>>;
|
|
212
226
|
yes: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
|
|
213
227
|
yolo: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
|
|
214
228
|
verbose: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodBoolean>>;
|
|
@@ -470,4 +484,4 @@ declare function sponsors(): Promise<void>;
|
|
|
470
484
|
declare function docs(): Promise<void>;
|
|
471
485
|
declare function builder(): Promise<void>;
|
|
472
486
|
//#endregion
|
|
473
|
-
export { type API, type AddInput, type Addons, type Backend, type BetterTStackConfig, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, type Examples, type Frontend, type InitResult, type ORM, type PackageManager, type ProjectConfig, type Runtime, type ServerDeploy, type WebDeploy, builder, createBtsCli, docs, init, router, sponsors };
|
|
487
|
+
export { type API, type AddInput, type Addons, type Backend, type BetterTStackConfig, type CreateInput, type Database, type DatabaseSetup, type DirectoryConflict, type Examples, type Frontend, type InitResult, type ORM, type PackageManager, type ProjectConfig, type Runtime, type ServerDeploy, type Template, type WebDeploy, builder, createBtsCli, docs, init, router, sponsors };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-
|
|
2
|
+
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-CWiiwWrU.js";
|
|
3
3
|
|
|
4
4
|
export { builder, createBtsCli, docs, init, router, sponsors };
|
|
@@ -62,8 +62,8 @@ function getDefaultConfig() {
|
|
|
62
62
|
}
|
|
63
63
|
const DEFAULT_CONFIG = getDefaultConfig();
|
|
64
64
|
const dependencyVersionMap = {
|
|
65
|
-
"better-auth": "^1.
|
|
66
|
-
"@better-auth/expo": "^1.
|
|
65
|
+
"better-auth": "^1.4.0",
|
|
66
|
+
"@better-auth/expo": "^1.4.0",
|
|
67
67
|
"@clerk/nextjs": "^6.31.5",
|
|
68
68
|
"@clerk/clerk-react": "^5.45.0",
|
|
69
69
|
"@clerk/tanstack-react-start": "^0.26.3",
|
|
@@ -291,6 +291,12 @@ const DirectoryConflictSchema = z.enum([
|
|
|
291
291
|
"increment",
|
|
292
292
|
"error"
|
|
293
293
|
]).describe("How to handle existing directory conflicts");
|
|
294
|
+
const TemplateSchema = z.enum([
|
|
295
|
+
"mern",
|
|
296
|
+
"pern",
|
|
297
|
+
"t3",
|
|
298
|
+
"none"
|
|
299
|
+
]).describe("Predefined project template");
|
|
294
300
|
|
|
295
301
|
//#endregion
|
|
296
302
|
//#region src/utils/compatibility.ts
|
|
@@ -1578,6 +1584,71 @@ const renderTitle = () => {
|
|
|
1578
1584
|
else console.log(gradient(Object.values(catppuccinTheme)).multiline(TITLE_TEXT));
|
|
1579
1585
|
};
|
|
1580
1586
|
|
|
1587
|
+
//#endregion
|
|
1588
|
+
//#region src/utils/templates.ts
|
|
1589
|
+
const TEMPLATE_PRESETS = {
|
|
1590
|
+
mern: {
|
|
1591
|
+
database: "mongodb",
|
|
1592
|
+
orm: "mongoose",
|
|
1593
|
+
backend: "express",
|
|
1594
|
+
runtime: "node",
|
|
1595
|
+
frontend: ["react-router"],
|
|
1596
|
+
api: "orpc",
|
|
1597
|
+
auth: "better-auth",
|
|
1598
|
+
payments: "none",
|
|
1599
|
+
addons: ["turborepo"],
|
|
1600
|
+
examples: ["todo"],
|
|
1601
|
+
dbSetup: "mongodb-atlas",
|
|
1602
|
+
webDeploy: "none",
|
|
1603
|
+
serverDeploy: "none"
|
|
1604
|
+
},
|
|
1605
|
+
pern: {
|
|
1606
|
+
database: "postgres",
|
|
1607
|
+
orm: "drizzle",
|
|
1608
|
+
backend: "express",
|
|
1609
|
+
runtime: "node",
|
|
1610
|
+
frontend: ["tanstack-router"],
|
|
1611
|
+
api: "trpc",
|
|
1612
|
+
auth: "better-auth",
|
|
1613
|
+
payments: "none",
|
|
1614
|
+
addons: ["turborepo"],
|
|
1615
|
+
examples: ["todo"],
|
|
1616
|
+
dbSetup: "none",
|
|
1617
|
+
webDeploy: "none",
|
|
1618
|
+
serverDeploy: "none"
|
|
1619
|
+
},
|
|
1620
|
+
t3: {
|
|
1621
|
+
database: "postgres",
|
|
1622
|
+
orm: "prisma",
|
|
1623
|
+
backend: "self",
|
|
1624
|
+
runtime: "none",
|
|
1625
|
+
frontend: ["next"],
|
|
1626
|
+
api: "trpc",
|
|
1627
|
+
auth: "better-auth",
|
|
1628
|
+
payments: "none",
|
|
1629
|
+
addons: ["biome", "turborepo"],
|
|
1630
|
+
examples: ["none"],
|
|
1631
|
+
dbSetup: "none",
|
|
1632
|
+
webDeploy: "none",
|
|
1633
|
+
serverDeploy: "none"
|
|
1634
|
+
},
|
|
1635
|
+
none: null
|
|
1636
|
+
};
|
|
1637
|
+
function getTemplateConfig(template) {
|
|
1638
|
+
if (template === "none" || !template) return null;
|
|
1639
|
+
const config = TEMPLATE_PRESETS[template];
|
|
1640
|
+
if (!config) throw new Error(`Unknown template: ${template}`);
|
|
1641
|
+
return config;
|
|
1642
|
+
}
|
|
1643
|
+
function getTemplateDescription(template) {
|
|
1644
|
+
return {
|
|
1645
|
+
mern: "MongoDB + Express + React + Node.js - Classic MERN stack",
|
|
1646
|
+
pern: "PostgreSQL + Express + React + Node.js - Popular PERN stack",
|
|
1647
|
+
t3: "T3 Stack - Next.js + tRPC + Prisma + PostgreSQL + Better Auth",
|
|
1648
|
+
none: "No template - Full customization"
|
|
1649
|
+
}[template] || "";
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1581
1652
|
//#endregion
|
|
1582
1653
|
//#region src/utils/config-processing.ts
|
|
1583
1654
|
function processArrayOption(options) {
|
|
@@ -1829,6 +1900,7 @@ const CORE_STACK_FLAGS = new Set([
|
|
|
1829
1900
|
]);
|
|
1830
1901
|
function validateYesFlagCombination(options, providedFlags) {
|
|
1831
1902
|
if (!options.yes) return;
|
|
1903
|
+
if (options.template && options.template !== "none") return;
|
|
1832
1904
|
const coreStackFlagsProvided = Array.from(providedFlags).filter((flag) => CORE_STACK_FLAGS.has(flag));
|
|
1833
1905
|
if (coreStackFlagsProvided.length > 0) exitWithError(`Cannot combine --yes with core stack configuration flags: ${coreStackFlagsProvided.map((f) => `--${f}`).join(", ")}. The --yes flag uses default configuration. Remove these flags or use --yes without them.`);
|
|
1834
1906
|
}
|
|
@@ -3784,6 +3856,7 @@ async function setupCatalogs(projectDir, options) {
|
|
|
3784
3856
|
const packagePaths = [
|
|
3785
3857
|
"apps/server",
|
|
3786
3858
|
"apps/web",
|
|
3859
|
+
"apps/native",
|
|
3787
3860
|
"apps/fumadocs",
|
|
3788
3861
|
"apps/docs",
|
|
3789
3862
|
"packages/api",
|
|
@@ -3822,15 +3895,17 @@ function findDuplicateDependencies(packagesInfo, projectName) {
|
|
|
3822
3895
|
if (depName.startsWith(projectScope)) continue;
|
|
3823
3896
|
if (version.startsWith("workspace:")) continue;
|
|
3824
3897
|
const existing = depCount.get(depName);
|
|
3825
|
-
if (existing)
|
|
3826
|
-
|
|
3827
|
-
|
|
3898
|
+
if (existing) {
|
|
3899
|
+
existing.versions.add(version);
|
|
3900
|
+
existing.packages.push(pkg.path);
|
|
3901
|
+
} else depCount.set(depName, {
|
|
3902
|
+
versions: new Set([version]),
|
|
3828
3903
|
packages: [pkg.path]
|
|
3829
3904
|
});
|
|
3830
3905
|
}
|
|
3831
3906
|
}
|
|
3832
3907
|
const catalog = {};
|
|
3833
|
-
for (const [depName, info] of depCount.entries()) if (info.packages.length > 1) catalog[depName] = info.
|
|
3908
|
+
for (const [depName, info] of depCount.entries()) if (info.packages.length > 1 && info.versions.size === 1) catalog[depName] = Array.from(info.versions)[0];
|
|
3834
3909
|
return catalog;
|
|
3835
3910
|
}
|
|
3836
3911
|
async function setupBunCatalogs(projectDir, catalog) {
|
|
@@ -6825,13 +6900,31 @@ async function createProjectHandler(input) {
|
|
|
6825
6900
|
};
|
|
6826
6901
|
}
|
|
6827
6902
|
const { finalResolvedPath, finalBaseName } = await setupProjectDirectory(finalPathInput, shouldClearDirectory);
|
|
6828
|
-
const
|
|
6903
|
+
const originalInput = {
|
|
6829
6904
|
...input,
|
|
6830
6905
|
projectDirectory: input.projectName
|
|
6831
6906
|
};
|
|
6832
|
-
const providedFlags = getProvidedFlags(
|
|
6907
|
+
const providedFlags = getProvidedFlags(originalInput);
|
|
6908
|
+
let cliInput = originalInput;
|
|
6909
|
+
if (input.template && input.template !== "none") {
|
|
6910
|
+
const templateConfig = getTemplateConfig(input.template);
|
|
6911
|
+
if (templateConfig) {
|
|
6912
|
+
const templateName = input.template.toUpperCase();
|
|
6913
|
+
const templateDescription = getTemplateDescription(input.template);
|
|
6914
|
+
log.message(pc.bold(pc.cyan(`Using template: ${pc.white(templateName)}`)));
|
|
6915
|
+
log.message(pc.dim(` ${templateDescription}`));
|
|
6916
|
+
const userOverrides = {};
|
|
6917
|
+
for (const [key, value] of Object.entries(originalInput)) if (value !== void 0) userOverrides[key] = value;
|
|
6918
|
+
cliInput = {
|
|
6919
|
+
...templateConfig,
|
|
6920
|
+
...userOverrides,
|
|
6921
|
+
template: input.template,
|
|
6922
|
+
projectDirectory: originalInput.projectDirectory
|
|
6923
|
+
};
|
|
6924
|
+
}
|
|
6925
|
+
}
|
|
6833
6926
|
let config;
|
|
6834
|
-
if (
|
|
6927
|
+
if (cliInput.yes) {
|
|
6835
6928
|
const flagConfig = processProvidedFlagsWithoutValidation(cliInput, finalBaseName);
|
|
6836
6929
|
config = {
|
|
6837
6930
|
...getDefaultConfig(),
|
|
@@ -6843,7 +6936,6 @@ async function createProjectHandler(input) {
|
|
|
6843
6936
|
validateConfigCompatibility(config, providedFlags, cliInput);
|
|
6844
6937
|
log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
|
|
6845
6938
|
log.message(displayConfig(config));
|
|
6846
|
-
log.message("");
|
|
6847
6939
|
} else {
|
|
6848
6940
|
const flagConfig = processAndValidateFlags(cliInput, providedFlags, finalBaseName);
|
|
6849
6941
|
const { projectName: _projectNameFromFlags,...otherFlags } = flagConfig;
|
|
@@ -6854,7 +6946,7 @@ async function createProjectHandler(input) {
|
|
|
6854
6946
|
}
|
|
6855
6947
|
config = await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput);
|
|
6856
6948
|
}
|
|
6857
|
-
await createProject(config, { manualDb: input.manualDb });
|
|
6949
|
+
await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
|
|
6858
6950
|
const reproducibleCommand = generateReproducibleCommand(config);
|
|
6859
6951
|
log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
|
|
6860
6952
|
await trackProjectCreation(config, input.disableAnalytics);
|
|
@@ -7045,6 +7137,7 @@ const router = os.router({
|
|
|
7045
7137
|
default: true,
|
|
7046
7138
|
negateBooleans: true
|
|
7047
7139
|
}).input(z$1.tuple([ProjectNameSchema.optional(), z$1.object({
|
|
7140
|
+
template: TemplateSchema.optional().describe("Use a predefined template"),
|
|
7048
7141
|
yes: z$1.boolean().optional().default(false).describe("Use default configuration"),
|
|
7049
7142
|
yolo: z$1.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
|
|
7050
7143
|
verbose: z$1.boolean().optional().default(false).describe("Show detailed result information"),
|
package/package.json
CHANGED
|
@@ -1,58 +1,100 @@
|
|
|
1
|
+
import { relations } from "drizzle-orm";
|
|
1
2
|
import {
|
|
2
3
|
mysqlTable,
|
|
3
4
|
varchar,
|
|
4
5
|
text,
|
|
5
6
|
timestamp,
|
|
6
7
|
boolean,
|
|
8
|
+
index,
|
|
7
9
|
} from "drizzle-orm/mysql-core";
|
|
8
10
|
|
|
9
11
|
export const user = mysqlTable("user", {
|
|
10
12
|
id: varchar("id", { length: 36 }).primaryKey(),
|
|
11
|
-
name:
|
|
13
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
12
14
|
email: varchar("email", { length: 255 }).notNull().unique(),
|
|
13
|
-
emailVerified: boolean("email_verified").notNull(),
|
|
15
|
+
emailVerified: boolean("email_verified").default(false).notNull(),
|
|
14
16
|
image: text("image"),
|
|
15
|
-
createdAt: timestamp("created_at").notNull(),
|
|
16
|
-
updatedAt: timestamp("updated_at")
|
|
17
|
+
createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
|
|
18
|
+
updatedAt: timestamp("updated_at", { fsp: 3 })
|
|
19
|
+
.defaultNow()
|
|
20
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
21
|
+
.notNull(),
|
|
17
22
|
});
|
|
18
23
|
|
|
19
|
-
export const session = mysqlTable(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
export const session = mysqlTable(
|
|
25
|
+
"session",
|
|
26
|
+
{
|
|
27
|
+
id: varchar("id", { length: 36 }).primaryKey(),
|
|
28
|
+
expiresAt: timestamp("expires_at", { fsp: 3 }).notNull(),
|
|
29
|
+
token: varchar("token", { length: 255 }).notNull().unique(),
|
|
30
|
+
createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
|
|
31
|
+
updatedAt: timestamp("updated_at", { fsp: 3 })
|
|
32
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
33
|
+
.notNull(),
|
|
34
|
+
ipAddress: text("ip_address"),
|
|
35
|
+
userAgent: text("user_agent"),
|
|
36
|
+
userId: varchar("user_id", { length: 36 })
|
|
37
|
+
.notNull()
|
|
38
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
39
|
+
},
|
|
40
|
+
(table) => [index("session_userId_idx").on(table.userId)],
|
|
41
|
+
);
|
|
31
42
|
|
|
32
|
-
export const account = mysqlTable(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.notNull()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
export const account = mysqlTable(
|
|
44
|
+
"account",
|
|
45
|
+
{
|
|
46
|
+
id: varchar("id", { length: 36 }).primaryKey(),
|
|
47
|
+
accountId: text("account_id").notNull(),
|
|
48
|
+
providerId: text("provider_id").notNull(),
|
|
49
|
+
userId: varchar("user_id", { length: 36 })
|
|
50
|
+
.notNull()
|
|
51
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
52
|
+
accessToken: text("access_token"),
|
|
53
|
+
refreshToken: text("refresh_token"),
|
|
54
|
+
idToken: text("id_token"),
|
|
55
|
+
accessTokenExpiresAt: timestamp("access_token_expires_at", { fsp: 3 }),
|
|
56
|
+
refreshTokenExpiresAt: timestamp("refresh_token_expires_at", { fsp: 3 }),
|
|
57
|
+
scope: text("scope"),
|
|
58
|
+
password: text("password"),
|
|
59
|
+
createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
|
|
60
|
+
updatedAt: timestamp("updated_at", { fsp: 3 })
|
|
61
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
62
|
+
.notNull(),
|
|
63
|
+
},
|
|
64
|
+
(table) => [index("account_userId_idx").on(table.userId)],
|
|
65
|
+
);
|
|
42
66
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})
|
|
67
|
+
export const verification = mysqlTable(
|
|
68
|
+
"verification",
|
|
69
|
+
{
|
|
70
|
+
id: varchar("id", { length: 36 }).primaryKey(),
|
|
71
|
+
identifier: varchar("identifier", { length: 255 }).notNull(),
|
|
72
|
+
value: text("value").notNull(),
|
|
73
|
+
expiresAt: timestamp("expires_at", { fsp: 3 }).notNull(),
|
|
74
|
+
createdAt: timestamp("created_at", { fsp: 3 }).defaultNow().notNull(),
|
|
75
|
+
updatedAt: timestamp("updated_at", { fsp: 3 })
|
|
76
|
+
.defaultNow()
|
|
77
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
78
|
+
.notNull(),
|
|
79
|
+
},
|
|
80
|
+
(table) => [index("verification_identifier_idx").on(table.identifier)],
|
|
81
|
+
);
|
|
50
82
|
|
|
51
|
-
export const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
export const userRelations = relations(user, ({ many }) => ({
|
|
84
|
+
sessions: many(session),
|
|
85
|
+
accounts: many(account),
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
export const sessionRelations = relations(session, ({ one }) => ({
|
|
89
|
+
user: one(user, {
|
|
90
|
+
fields: [session.userId],
|
|
91
|
+
references: [user.id],
|
|
92
|
+
}),
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
export const accountRelations = relations(account, ({ one }) => ({
|
|
96
|
+
user: one(user, {
|
|
97
|
+
fields: [account.userId],
|
|
98
|
+
references: [user.id],
|
|
99
|
+
}),
|
|
100
|
+
}));
|
|
@@ -1,47 +1,93 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { relations } from "drizzle-orm";
|
|
2
|
+
import { pgTable, text, timestamp, boolean, index } from "drizzle-orm/pg-core";
|
|
2
3
|
|
|
3
4
|
export const user = pgTable("user", {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
id: text("id").primaryKey(),
|
|
6
|
+
name: text("name").notNull(),
|
|
7
|
+
email: text("email").notNull().unique(),
|
|
8
|
+
emailVerified: boolean("email_verified").default(false).notNull(),
|
|
9
|
+
image: text("image"),
|
|
10
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
11
|
+
updatedAt: timestamp("updated_at")
|
|
12
|
+
.defaultNow()
|
|
13
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
14
|
+
.notNull(),
|
|
15
|
+
});
|
|
12
16
|
|
|
13
|
-
export const session = pgTable(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
export const session = pgTable(
|
|
18
|
+
"session",
|
|
19
|
+
{
|
|
20
|
+
id: text("id").primaryKey(),
|
|
21
|
+
expiresAt: timestamp("expires_at").notNull(),
|
|
22
|
+
token: text("token").notNull().unique(),
|
|
23
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
24
|
+
updatedAt: timestamp("updated_at")
|
|
25
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
26
|
+
.notNull(),
|
|
27
|
+
ipAddress: text("ip_address"),
|
|
28
|
+
userAgent: text("user_agent"),
|
|
29
|
+
userId: text("user_id")
|
|
30
|
+
.notNull()
|
|
31
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
32
|
+
},
|
|
33
|
+
(table) => [index("session_userId_idx").on(table.userId)],
|
|
34
|
+
);
|
|
23
35
|
|
|
24
|
-
export const account = pgTable(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
export const account = pgTable(
|
|
37
|
+
"account",
|
|
38
|
+
{
|
|
39
|
+
id: text("id").primaryKey(),
|
|
40
|
+
accountId: text("account_id").notNull(),
|
|
41
|
+
providerId: text("provider_id").notNull(),
|
|
42
|
+
userId: text("user_id")
|
|
43
|
+
.notNull()
|
|
44
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
45
|
+
accessToken: text("access_token"),
|
|
46
|
+
refreshToken: text("refresh_token"),
|
|
47
|
+
idToken: text("id_token"),
|
|
48
|
+
accessTokenExpiresAt: timestamp("access_token_expires_at"),
|
|
49
|
+
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
|
|
50
|
+
scope: text("scope"),
|
|
51
|
+
password: text("password"),
|
|
52
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
53
|
+
updatedAt: timestamp("updated_at")
|
|
54
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
55
|
+
.notNull(),
|
|
56
|
+
},
|
|
57
|
+
(table) => [index("account_userId_idx").on(table.userId)],
|
|
58
|
+
);
|
|
39
59
|
|
|
40
|
-
export const verification = pgTable(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
60
|
+
export const verification = pgTable(
|
|
61
|
+
"verification",
|
|
62
|
+
{
|
|
63
|
+
id: text("id").primaryKey(),
|
|
64
|
+
identifier: text("identifier").notNull(),
|
|
65
|
+
value: text("value").notNull(),
|
|
66
|
+
expiresAt: timestamp("expires_at").notNull(),
|
|
67
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
68
|
+
updatedAt: timestamp("updated_at")
|
|
69
|
+
.defaultNow()
|
|
70
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
71
|
+
.notNull(),
|
|
72
|
+
},
|
|
73
|
+
(table) => [index("verification_identifier_idx").on(table.identifier)],
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
export const userRelations = relations(user, ({ many }) => ({
|
|
77
|
+
sessions: many(session),
|
|
78
|
+
accounts: many(account),
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
export const sessionRelations = relations(session, ({ one }) => ({
|
|
82
|
+
user: one(user, {
|
|
83
|
+
fields: [session.userId],
|
|
84
|
+
references: [user.id],
|
|
85
|
+
}),
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
export const accountRelations = relations(account, ({ one }) => ({
|
|
89
|
+
user: one(user, {
|
|
90
|
+
fields: [account.userId],
|
|
91
|
+
references: [user.id],
|
|
92
|
+
}),
|
|
93
|
+
}));
|
|
@@ -1,55 +1,107 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { relations, sql } from "drizzle-orm";
|
|
2
|
+
import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
|
|
2
3
|
|
|
3
4
|
export const user = sqliteTable("user", {
|
|
4
5
|
id: text("id").primaryKey(),
|
|
5
6
|
name: text("name").notNull(),
|
|
6
7
|
email: text("email").notNull().unique(),
|
|
7
|
-
emailVerified: integer("email_verified", { mode: "boolean" })
|
|
8
|
+
emailVerified: integer("email_verified", { mode: "boolean" })
|
|
9
|
+
.default(false)
|
|
10
|
+
.notNull(),
|
|
8
11
|
image: text("image"),
|
|
9
|
-
createdAt: integer("created_at", { mode: "
|
|
10
|
-
|
|
12
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
13
|
+
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
|
14
|
+
.notNull(),
|
|
15
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
|
16
|
+
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
|
17
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
18
|
+
.notNull(),
|
|
11
19
|
});
|
|
12
20
|
|
|
13
|
-
export const session = sqliteTable(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
export const session = sqliteTable(
|
|
22
|
+
"session",
|
|
23
|
+
{
|
|
24
|
+
id: text("id").primaryKey(),
|
|
25
|
+
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
|
|
26
|
+
token: text("token").notNull().unique(),
|
|
27
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
28
|
+
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
|
29
|
+
.notNull(),
|
|
30
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
|
31
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
32
|
+
.notNull(),
|
|
33
|
+
ipAddress: text("ip_address"),
|
|
34
|
+
userAgent: text("user_agent"),
|
|
35
|
+
userId: text("user_id")
|
|
36
|
+
.notNull()
|
|
37
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
38
|
+
},
|
|
39
|
+
(table) => [index("session_userId_idx").on(table.userId)],
|
|
40
|
+
);
|
|
25
41
|
|
|
26
|
-
export const account = sqliteTable(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.notNull()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
export const account = sqliteTable(
|
|
43
|
+
"account",
|
|
44
|
+
{
|
|
45
|
+
id: text("id").primaryKey(),
|
|
46
|
+
accountId: text("account_id").notNull(),
|
|
47
|
+
providerId: text("provider_id").notNull(),
|
|
48
|
+
userId: text("user_id")
|
|
49
|
+
.notNull()
|
|
50
|
+
.references(() => user.id, { onDelete: "cascade" }),
|
|
51
|
+
accessToken: text("access_token"),
|
|
52
|
+
refreshToken: text("refresh_token"),
|
|
53
|
+
idToken: text("id_token"),
|
|
54
|
+
accessTokenExpiresAt: integer("access_token_expires_at", {
|
|
55
|
+
mode: "timestamp_ms",
|
|
56
|
+
}),
|
|
57
|
+
refreshTokenExpiresAt: integer("refresh_token_expires_at", {
|
|
58
|
+
mode: "timestamp_ms",
|
|
59
|
+
}),
|
|
60
|
+
scope: text("scope"),
|
|
61
|
+
password: text("password"),
|
|
62
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
63
|
+
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
|
64
|
+
.notNull(),
|
|
65
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
|
66
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
67
|
+
.notNull(),
|
|
68
|
+
},
|
|
69
|
+
(table) => [index("account_userId_idx").on(table.userId)],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export const verification = sqliteTable(
|
|
73
|
+
"verification",
|
|
74
|
+
{
|
|
75
|
+
id: text("id").primaryKey(),
|
|
76
|
+
identifier: text("identifier").notNull(),
|
|
77
|
+
value: text("value").notNull(),
|
|
78
|
+
expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(),
|
|
79
|
+
createdAt: integer("created_at", { mode: "timestamp_ms" })
|
|
80
|
+
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
|
81
|
+
.notNull(),
|
|
82
|
+
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
|
|
83
|
+
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
|
|
84
|
+
.$onUpdate(() => /* @__PURE__ */ new Date())
|
|
85
|
+
.notNull(),
|
|
86
|
+
},
|
|
87
|
+
(table) => [index("verification_identifier_idx").on(table.identifier)],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
export const userRelations = relations(user, ({ many }) => ({
|
|
91
|
+
sessions: many(session),
|
|
92
|
+
accounts: many(account),
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
export const sessionRelations = relations(session, ({ one }) => ({
|
|
96
|
+
user: one(user, {
|
|
97
|
+
fields: [session.userId],
|
|
98
|
+
references: [user.id],
|
|
41
99
|
}),
|
|
42
|
-
|
|
43
|
-
password: text("password"),
|
|
44
|
-
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
|
|
45
|
-
updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
|
|
46
|
-
});
|
|
100
|
+
}));
|
|
47
101
|
|
|
48
|
-
export const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
updatedAt: integer("updated_at", { mode: "timestamp" }),
|
|
55
|
-
});
|
|
102
|
+
export const accountRelations = relations(account, ({ one }) => ({
|
|
103
|
+
user: one(user, {
|
|
104
|
+
fields: [account.userId],
|
|
105
|
+
references: [user.id],
|
|
106
|
+
}),
|
|
107
|
+
}));
|
|
@@ -2,10 +2,10 @@ model User {
|
|
|
2
2
|
id String @id @map("_id")
|
|
3
3
|
name String
|
|
4
4
|
email String
|
|
5
|
-
emailVerified Boolean
|
|
5
|
+
emailVerified Boolean @default(false)
|
|
6
6
|
image String?
|
|
7
|
-
createdAt DateTime
|
|
8
|
-
updatedAt DateTime
|
|
7
|
+
createdAt DateTime @default(now())
|
|
8
|
+
updatedAt DateTime @updatedAt
|
|
9
9
|
sessions Session[]
|
|
10
10
|
accounts Account[]
|
|
11
11
|
|
|
@@ -17,14 +17,15 @@ model Session {
|
|
|
17
17
|
id String @id @map("_id")
|
|
18
18
|
expiresAt DateTime
|
|
19
19
|
token String
|
|
20
|
-
createdAt DateTime
|
|
21
|
-
updatedAt DateTime
|
|
20
|
+
createdAt DateTime @default(now())
|
|
21
|
+
updatedAt DateTime @updatedAt
|
|
22
22
|
ipAddress String?
|
|
23
23
|
userAgent String?
|
|
24
24
|
userId String
|
|
25
25
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
26
26
|
|
|
27
27
|
@@unique([token])
|
|
28
|
+
@@index([userId])
|
|
28
29
|
@@map("session")
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -41,19 +42,21 @@ model Account {
|
|
|
41
42
|
refreshTokenExpiresAt DateTime?
|
|
42
43
|
scope String?
|
|
43
44
|
password String?
|
|
44
|
-
createdAt DateTime
|
|
45
|
-
updatedAt DateTime
|
|
45
|
+
createdAt DateTime @default(now())
|
|
46
|
+
updatedAt DateTime @updatedAt
|
|
46
47
|
|
|
48
|
+
@@index([userId])
|
|
47
49
|
@@map("account")
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
model Verification {
|
|
51
|
-
id String
|
|
53
|
+
id String @id @map("_id")
|
|
52
54
|
identifier String
|
|
53
55
|
value String
|
|
54
56
|
expiresAt DateTime
|
|
55
|
-
createdAt DateTime
|
|
56
|
-
updatedAt DateTime
|
|
57
|
+
createdAt DateTime @default(now())
|
|
58
|
+
updatedAt DateTime @updatedAt
|
|
57
59
|
|
|
60
|
+
@@index([identifier])
|
|
58
61
|
@@map("verification")
|
|
59
62
|
}
|
|
@@ -2,10 +2,10 @@ model User {
|
|
|
2
2
|
id String @id
|
|
3
3
|
name String @db.Text
|
|
4
4
|
email String
|
|
5
|
-
emailVerified Boolean
|
|
5
|
+
emailVerified Boolean @default(false)
|
|
6
6
|
image String? @db.Text
|
|
7
|
-
createdAt DateTime
|
|
8
|
-
updatedAt DateTime
|
|
7
|
+
createdAt DateTime @default(now())
|
|
8
|
+
updatedAt DateTime @updatedAt
|
|
9
9
|
sessions Session[]
|
|
10
10
|
accounts Account[]
|
|
11
11
|
|
|
@@ -17,14 +17,15 @@ model Session {
|
|
|
17
17
|
id String @id
|
|
18
18
|
expiresAt DateTime
|
|
19
19
|
token String
|
|
20
|
-
createdAt DateTime
|
|
21
|
-
updatedAt DateTime
|
|
20
|
+
createdAt DateTime @default(now())
|
|
21
|
+
updatedAt DateTime @updatedAt
|
|
22
22
|
ipAddress String? @db.Text
|
|
23
23
|
userAgent String? @db.Text
|
|
24
24
|
userId String
|
|
25
25
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
26
26
|
|
|
27
27
|
@@unique([token])
|
|
28
|
+
@@index([userId(length: 191)])
|
|
28
29
|
@@map("session")
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -41,19 +42,21 @@ model Account {
|
|
|
41
42
|
refreshTokenExpiresAt DateTime?
|
|
42
43
|
scope String? @db.Text
|
|
43
44
|
password String? @db.Text
|
|
44
|
-
createdAt DateTime
|
|
45
|
-
updatedAt DateTime
|
|
45
|
+
createdAt DateTime @default(now())
|
|
46
|
+
updatedAt DateTime @updatedAt
|
|
46
47
|
|
|
48
|
+
@@index([userId(length: 191)])
|
|
47
49
|
@@map("account")
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
model Verification {
|
|
51
|
-
id String
|
|
52
|
-
identifier String
|
|
53
|
-
value String
|
|
53
|
+
id String @id
|
|
54
|
+
identifier String @db.Text
|
|
55
|
+
value String @db.Text
|
|
54
56
|
expiresAt DateTime
|
|
55
|
-
createdAt DateTime
|
|
56
|
-
updatedAt DateTime
|
|
57
|
+
createdAt DateTime @default(now())
|
|
58
|
+
updatedAt DateTime @updatedAt
|
|
57
59
|
|
|
60
|
+
@@index([identifier(length: 191)])
|
|
58
61
|
@@map("verification")
|
|
59
62
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
model User {
|
|
2
|
-
id String @id
|
|
2
|
+
id String @id
|
|
3
3
|
name String
|
|
4
4
|
email String
|
|
5
|
-
emailVerified Boolean
|
|
5
|
+
emailVerified Boolean @default(false)
|
|
6
6
|
image String?
|
|
7
|
-
createdAt DateTime
|
|
8
|
-
updatedAt DateTime
|
|
7
|
+
createdAt DateTime @default(now())
|
|
8
|
+
updatedAt DateTime @updatedAt
|
|
9
9
|
sessions Session[]
|
|
10
10
|
accounts Account[]
|
|
11
11
|
|
|
@@ -14,22 +14,23 @@ model User {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
model Session {
|
|
17
|
-
id String @id
|
|
17
|
+
id String @id
|
|
18
18
|
expiresAt DateTime
|
|
19
19
|
token String
|
|
20
|
-
createdAt DateTime
|
|
21
|
-
updatedAt DateTime
|
|
20
|
+
createdAt DateTime @default(now())
|
|
21
|
+
updatedAt DateTime @updatedAt
|
|
22
22
|
ipAddress String?
|
|
23
23
|
userAgent String?
|
|
24
24
|
userId String
|
|
25
25
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
26
26
|
|
|
27
27
|
@@unique([token])
|
|
28
|
+
@@index([userId])
|
|
28
29
|
@@map("session")
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
model Account {
|
|
32
|
-
id String @id
|
|
33
|
+
id String @id
|
|
33
34
|
accountId String
|
|
34
35
|
providerId String
|
|
35
36
|
userId String
|
|
@@ -41,19 +42,21 @@ model Account {
|
|
|
41
42
|
refreshTokenExpiresAt DateTime?
|
|
42
43
|
scope String?
|
|
43
44
|
password String?
|
|
44
|
-
createdAt DateTime
|
|
45
|
-
updatedAt DateTime
|
|
45
|
+
createdAt DateTime @default(now())
|
|
46
|
+
updatedAt DateTime @updatedAt
|
|
46
47
|
|
|
48
|
+
@@index([userId])
|
|
47
49
|
@@map("account")
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
model Verification {
|
|
51
|
-
id String
|
|
53
|
+
id String @id
|
|
52
54
|
identifier String
|
|
53
55
|
value String
|
|
54
56
|
expiresAt DateTime
|
|
55
|
-
createdAt DateTime
|
|
56
|
-
updatedAt DateTime
|
|
57
|
+
createdAt DateTime @default(now())
|
|
58
|
+
updatedAt DateTime @updatedAt
|
|
57
59
|
|
|
60
|
+
@@index([identifier])
|
|
58
61
|
@@map("verification")
|
|
59
62
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
model User {
|
|
2
|
-
id String @id
|
|
2
|
+
id String @id
|
|
3
3
|
name String
|
|
4
4
|
email String
|
|
5
|
-
emailVerified Boolean
|
|
5
|
+
emailVerified Boolean @default(false)
|
|
6
6
|
image String?
|
|
7
|
-
createdAt DateTime
|
|
8
|
-
updatedAt DateTime
|
|
7
|
+
createdAt DateTime @default(now())
|
|
8
|
+
updatedAt DateTime @updatedAt
|
|
9
9
|
sessions Session[]
|
|
10
10
|
accounts Account[]
|
|
11
11
|
|
|
@@ -14,22 +14,23 @@ model User {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
model Session {
|
|
17
|
-
id String @id
|
|
17
|
+
id String @id
|
|
18
18
|
expiresAt DateTime
|
|
19
19
|
token String
|
|
20
|
-
createdAt DateTime
|
|
21
|
-
updatedAt DateTime
|
|
20
|
+
createdAt DateTime @default(now())
|
|
21
|
+
updatedAt DateTime @updatedAt
|
|
22
22
|
ipAddress String?
|
|
23
23
|
userAgent String?
|
|
24
24
|
userId String
|
|
25
25
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
26
26
|
|
|
27
27
|
@@unique([token])
|
|
28
|
+
@@index([userId])
|
|
28
29
|
@@map("session")
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
model Account {
|
|
32
|
-
id String @id
|
|
33
|
+
id String @id
|
|
33
34
|
accountId String
|
|
34
35
|
providerId String
|
|
35
36
|
userId String
|
|
@@ -41,19 +42,21 @@ model Account {
|
|
|
41
42
|
refreshTokenExpiresAt DateTime?
|
|
42
43
|
scope String?
|
|
43
44
|
password String?
|
|
44
|
-
createdAt DateTime
|
|
45
|
-
updatedAt DateTime
|
|
45
|
+
createdAt DateTime @default(now())
|
|
46
|
+
updatedAt DateTime @updatedAt
|
|
46
47
|
|
|
48
|
+
@@index([userId])
|
|
47
49
|
@@map("account")
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
model Verification {
|
|
51
|
-
id String
|
|
53
|
+
id String @id
|
|
52
54
|
identifier String
|
|
53
55
|
value String
|
|
54
56
|
expiresAt DateTime
|
|
55
|
-
createdAt DateTime
|
|
56
|
-
updatedAt DateTime
|
|
57
|
+
createdAt DateTime @default(now())
|
|
58
|
+
updatedAt DateTime @updatedAt
|
|
57
59
|
|
|
60
|
+
@@index([identifier])
|
|
58
61
|
@@map("verification")
|
|
59
62
|
}
|