@percepta/create 3.1.5 → 3.3.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/index.js +53 -46
- package/dist/index.js.map +1 -1
- package/dist/{init-OeK4Yk6_.js → init-CtCp7Tv2.js} +3 -3
- package/dist/init-CtCp7Tv2.js.map +1 -0
- package/dist/{status-DC8mvHZj.js → status-CKe4aKso.js} +2 -2
- package/dist/{status-DC8mvHZj.js.map → status-CKe4aKso.js.map} +1 -1
- package/dist/{sync-C5Pd32VM.js → sync-D1vkoofl.js} +2 -2
- package/dist/{sync-C5Pd32VM.js.map → sync-D1vkoofl.js.map} +1 -1
- package/dist/{upstream-F6m8zRBQ.js → upstream-D-LH_1z4.js} +2 -2
- package/dist/{upstream-F6m8zRBQ.js.map → upstream-D-LH_1z4.js.map} +1 -1
- package/package.json +2 -2
- package/template-versions.json +1 -1
- package/templates/monorepo/.github/workflows/access-control.yml +38 -0
- package/templates/monorepo/README.md +42 -2
- package/templates/monorepo/access/README.md +39 -0
- package/templates/monorepo/access/bootstrap-grants.yaml.example +9 -0
- package/templates/monorepo/access/dev-grants.yaml.example +19 -0
- package/templates/monorepo/access/dev-groups.yaml.example +8 -0
- package/templates/monorepo/access/reconcile.yaml.example +11 -0
- package/templates/monorepo/auth/README.md +27 -0
- package/templates/monorepo/auth/drizzle.config.ts +13 -0
- package/templates/monorepo/auth/package.json +29 -0
- package/templates/monorepo/auth/scripts/setup-database.ts +11 -0
- package/templates/monorepo/auth/src/auth.ts +47 -0
- package/templates/monorepo/auth/src/config/database.ts +15 -0
- package/templates/monorepo/auth/src/drizzle/db.ts +8 -0
- package/templates/monorepo/auth/src/drizzle/migrations/0000_shared_auth.sql +89 -0
- package/templates/monorepo/auth/src/drizzle/migrations/meta/_journal.json +13 -0
- package/templates/monorepo/auth/src/drizzle/schema/auth/accounts.ts +7 -0
- package/templates/monorepo/auth/src/drizzle/schema/auth/sessions.ts +7 -0
- package/templates/monorepo/auth/src/drizzle/schema/auth/verifications.ts +6 -0
- package/templates/monorepo/auth/src/drizzle/schema/groups.ts +16 -0
- package/templates/monorepo/auth/src/drizzle/schema/index.ts +5 -0
- package/templates/monorepo/auth/src/drizzle/schema/users.ts +6 -0
- package/templates/monorepo/auth/src/index.ts +1 -0
- package/templates/monorepo/auth/src/scim/README.md +6 -0
- package/templates/monorepo/auth/tsconfig.json +12 -0
- package/templates/monorepo/package.json.template +18 -6
- package/templates/monorepo/pnpm-workspace.yaml +1 -0
- package/templates/webapp/AGENTS.md +13 -6
- package/templates/webapp/README.md +34 -18
- package/templates/webapp/agent-skills/access-control.md +301 -0
- package/templates/webapp/agent-skills/database.md +1 -1
- package/templates/webapp/docker-compose.yml +16 -0
- package/templates/webapp/env.example.template +9 -0
- package/templates/webapp/next.config.ts +1 -0
- package/templates/webapp/package.json.template +8 -4
- package/templates/webapp/scripts/seed.ts +87 -36
- package/templates/webapp/scripts/setup-database.ts +7 -1
- package/templates/webapp/scripts/start.sh +0 -9
- package/templates/webapp/src/access/access.manifest.ts +15 -0
- package/templates/webapp/src/access/schema.zed +7 -0
- package/templates/webapp/src/app/(app)/admin/_lib/PrincipalRoleTable.tsx +113 -0
- package/templates/webapp/src/app/(app)/admin/_lib/accessAdmin.ts +85 -0
- package/templates/webapp/src/app/(app)/admin/groups/page.tsx +117 -0
- package/templates/webapp/src/app/(app)/admin/users/page.tsx +79 -0
- package/templates/webapp/src/app/(app)/layout.tsx +16 -2
- package/templates/webapp/src/app/(app)/page.tsx +1 -12
- package/templates/webapp/src/app/(auth)/auth/signin/page.tsx +2 -5
- package/templates/webapp/src/app/(auth)/auth/signup/page.tsx +2 -5
- package/templates/webapp/src/config/getEnvConfig.ts +8 -0
- package/templates/webapp/src/drizzle/db.ts +3 -4
- package/templates/webapp/src/drizzle/migrations/0000_eager_grandmaster.sql +1 -57
- package/templates/webapp/src/drizzle/migrations/meta/0000_snapshot.json +1 -347
- package/templates/webapp/src/drizzle/schema/index.ts +3 -4
- package/templates/webapp/src/lib/auth/index.ts +6 -81
- package/templates/webapp/src/server/api/root.ts +4 -1
- package/templates/webapp/src/server/api/routers/access.ts +13 -0
- package/templates/webapp/src/server/trpc.ts +42 -8
- package/templates/webapp/src/services/DatabaseService.ts +4 -5
- package/templates/webapp/src/services/access/AppAccessControl.ts +39 -0
- package/dist/init-OeK4Yk6_.js.map +0 -1
- package/templates/webapp/scripts/create-user.ts +0 -47
- package/templates/webapp/src/drizzle/schema/auth/accounts.ts +0 -33
- package/templates/webapp/src/drizzle/schema/auth/sessions.ts +0 -25
- package/templates/webapp/src/drizzle/schema/auth/users.ts +0 -38
- package/templates/webapp/src/drizzle/schema/auth/verifications.ts +0 -19
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { type NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
3
3
|
import { db } from "../drizzle/db";
|
|
4
|
-
import type * as schema from "../drizzle/schema";
|
|
5
4
|
|
|
6
5
|
export class DatabaseService {
|
|
7
6
|
private static SINGLETON: DatabaseService | undefined;
|
|
@@ -15,10 +14,10 @@ export class DatabaseService {
|
|
|
15
14
|
|
|
16
15
|
private transactionAsyncLocalStorage = new AsyncLocalStorage<LocalStorage>();
|
|
17
16
|
|
|
18
|
-
private constructor(private database: NodePgDatabase
|
|
17
|
+
private constructor(private database: NodePgDatabase) {}
|
|
19
18
|
|
|
20
19
|
public async createTransaction<TReturn>(
|
|
21
|
-
callback: (txn: NodePgDatabase
|
|
20
|
+
callback: (txn: NodePgDatabase) => Promise<TReturn>,
|
|
22
21
|
): Promise<TReturn> {
|
|
23
22
|
const currentContext = this.transactionAsyncLocalStorage.getStore();
|
|
24
23
|
if (currentContext != null) {
|
|
@@ -47,8 +46,8 @@ export class DatabaseService {
|
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
type Database = Omit<NodePgDatabase
|
|
49
|
+
type Database = Omit<NodePgDatabase, "transaction">;
|
|
51
50
|
|
|
52
51
|
interface LocalStorage {
|
|
53
|
-
txn: NodePgDatabase
|
|
52
|
+
txn: NodePgDatabase;
|
|
54
53
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AppAccessRuntime,
|
|
3
|
+
createAppAccessRuntime,
|
|
4
|
+
} from "@percepta/access-control";
|
|
5
|
+
import { accessManifest } from "../../access/access.manifest";
|
|
6
|
+
import { getEnvConfig } from "../../config/getEnvConfig";
|
|
7
|
+
|
|
8
|
+
export type AppAccessControl = AppAccessRuntime<typeof accessManifest>;
|
|
9
|
+
|
|
10
|
+
const appAccessRuntime = createAppAccessRuntime({
|
|
11
|
+
manifest: accessManifest,
|
|
12
|
+
spicedb: () => {
|
|
13
|
+
const {
|
|
14
|
+
NODE_ENV: nodeEnv,
|
|
15
|
+
SPICEDB_ENDPOINT: endpoint,
|
|
16
|
+
SPICEDB_INSECURE: insecure,
|
|
17
|
+
SPICEDB_PRESHARED_KEY: presharedKey,
|
|
18
|
+
} = getEnvConfig();
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
endpoint,
|
|
22
|
+
insecure,
|
|
23
|
+
nodeEnv,
|
|
24
|
+
presharedKey,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export function canAccessApplication(userId: string): Promise<boolean> {
|
|
30
|
+
return appAccessRuntime.canAccessApplication(userId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getAccessControl(): AppAccessControl {
|
|
34
|
+
return appAccessRuntime.getAccessControl();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function toUserSubject(userId: string) {
|
|
38
|
+
return appAccessRuntime.toUserSubject(userId);
|
|
39
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init-OeK4Yk6_.js","names":[],"sources":["../src/commands/init.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport inquirer from \"inquirer\";\nimport {\n writeManifest,\n manifestExists,\n derivePlaceholders,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\nimport { isValidProjectType, VALID_PROJECT_TYPES } from \"../utils/prompts.js\";\n\nexport interface InitOptions {\n type?: string;\n templateVersion?: string;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n const cwd = process.cwd();\n\n if (await manifestExists(cwd)) {\n console.error(\n chalk.red(\".mosaic-template.json already exists in this directory.\"),\n );\n process.exit(1);\n }\n\n // Auto-detect app name from package.json\n const pkgPath = path.join(cwd, \"package.json\");\n let appName = path.basename(cwd);\n if (await fs.pathExists(pkgPath)) {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf-8\"));\n appName = pkg.name?.replace(/^@[^/]+\\//, \"\") || appName;\n }\n\n // Determine template type\n let templateType = options.type;\n if (templateType && !isValidProjectType(templateType)) {\n console.error(\n chalk.red(\n `Invalid template type \"${templateType}\". Valid types: ${VALID_PROJECT_TYPES.join(\", \")}`,\n ),\n );\n process.exit(1);\n }\n if (!templateType) {\n const answer = await inquirer.prompt([\n {\n type: \"list\",\n name: \"type\",\n message: \"Template type:\",\n choices: [\"webapp\", \"library\"],\n },\n ]);\n templateType = answer.type;\n }\n\n const templateVersion = options.templateVersion || \"1.0.0\";\n\n // Derive placeholders from app name\n const appTitle = appName\n .split(\"-\")\n .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n\n const manifest: MosaicManifest = {\n templateType: templateType!,\n templateVersion,\n templateCommit: \"unknown\",\n createdAt: new Date().toISOString(),\n placeholders: derivePlaceholders(appName, appTitle),\n source: {\n templatePath: `packages/create-mosaic-module/templates/${templateType}`,\n },\n };\n\n await writeManifest(cwd, manifest);\n\n // Create mosaic-template-notes.md if it doesn't exist\n const notesPath = path.join(cwd, \"mosaic-template-notes.md\");\n if (!(await fs.pathExists(notesPath))) {\n await fs.writeFile(\n notesPath,\n `# Mosaic Divergence Notes\\n\\nDocument intentional differences from the ${templateType} template here.\\nClaude reads this file during sync to preserve your customizations.\\n\\n## Intentional Divergences\\n\\n`,\n );\n }\n\n console.log();\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(\"Initialized .mosaic-template.json\"),\n );\n console.log();\n console.log(chalk.dim(\" Template:\"), templateType);\n console.log(chalk.dim(\" Version:\"), templateVersion);\n console.log(chalk.dim(\" App name:\"), appName);\n console.log();\n console.log(\n chalk.dim(\n \"Review .mosaic-template.json and mosaic-template-notes.md, then commit them.\",\n ),\n );\n console.log();\n}\n"],"mappings":";;;;;;AAiBA,eAAsB,YAAY,SAAqC;CACrE,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI,MAAM,eAAe,IAAI,EAAE;AAC7B,UAAQ,MACN,MAAM,IAAI,0DAA0D,CACrE;AACD,UAAQ,KAAK,EAAE;;CAIjB,MAAM,UAAU,KAAK,KAAK,KAAK,eAAe;CAC9C,IAAI,UAAU,KAAK,SAAS,IAAI;AAChC,KAAI,MAAM,GAAG,WAAW,QAAQ,CAE9B,WADY,KAAK,MAAM,MAAM,GAAG,SAAS,SAAS,QAAQ,CAC7C,CAAC,MAAM,QAAQ,aAAa,GAAG,IAAI;CAIlD,IAAI,eAAe,QAAQ;AAC3B,KAAI,gBAAgB,CAAC,mBAAmB,aAAa,EAAE;AACrD,UAAQ,MACN,MAAM,IACJ,0BAA0B,aAAa,kBAAkB,oBAAoB,KAAK,KAAK,GACxF,CACF;AACD,UAAQ,KAAK,EAAE;;AAEjB,KAAI,CAAC,aASH,iBAAe,MARM,SAAS,OAAO,CACnC;EACE,MAAM;EACN,MAAM;EACN,SAAS;EACT,SAAS,CAAC,UAAU,UAAU;EAC/B,CACF,CAAC,EACoB;CAGxB,MAAM,kBAAkB,QAAQ,mBAAmB;CAGnD,MAAM,WAAW,QACd,MAAM,IAAI,CACV,KAAK,MAAc,EAAE,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,CAAC,CAC1D,KAAK,IAAI;AAaZ,OAAM,cAAc,KAAK;EAVT;EACd;EACA,gBAAgB;EAChB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,cAAc,mBAAmB,SAAS,SAAS;EACnD,QAAQ,EACN,cAAc,2CAA2C,gBAC1D;EAG8B,CAAC;CAGlC,MAAM,YAAY,KAAK,KAAK,KAAK,2BAA2B;AAC5D,KAAI,CAAE,MAAM,GAAG,WAAW,UAAU,CAClC,OAAM,GAAG,UACP,WACA,0EAA0E,aAAa,wHACxF;AAGH,SAAQ,KAAK;AACb,SAAQ,IACN,MAAM,MAAM,IAAS,EACrB,MAAM,KAAK,oCAAoC,CAChD;AACD,SAAQ,KAAK;AACb,SAAQ,IAAI,MAAM,IAAI,cAAc,EAAE,aAAa;AACnD,SAAQ,IAAI,MAAM,IAAI,aAAa,EAAE,gBAAgB;AACrD,SAAQ,IAAI,MAAM,IAAI,cAAc,EAAE,QAAQ;AAC9C,SAAQ,KAAK;AACb,SAAQ,IACN,MAAM,IACJ,+EACD,CACF;AACD,SAAQ,KAAK"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
import { loadEnvConfig } from "@next/env";
|
|
5
|
-
import yargs from "yargs";
|
|
6
|
-
import { hideBin } from "yargs/helpers";
|
|
7
|
-
|
|
8
|
-
async function main(): Promise<void> {
|
|
9
|
-
loadEnvConfig(process.cwd());
|
|
10
|
-
globalThis.AsyncLocalStorage = AsyncLocalStorage;
|
|
11
|
-
|
|
12
|
-
const { email, password, name } = yargs(hideBin(process.argv))
|
|
13
|
-
.usage(
|
|
14
|
-
"Usage: tsx scripts/create-user.ts <email> <password> [--name <name>]",
|
|
15
|
-
)
|
|
16
|
-
.command("$0 <email> <password>", "Create a new user.")
|
|
17
|
-
.positional("email", {
|
|
18
|
-
type: "string",
|
|
19
|
-
demandOption: true,
|
|
20
|
-
})
|
|
21
|
-
.positional("password", {
|
|
22
|
-
type: "string",
|
|
23
|
-
demandOption: true,
|
|
24
|
-
})
|
|
25
|
-
.option("name", {
|
|
26
|
-
alias: "n",
|
|
27
|
-
describe: "Display name for the user.",
|
|
28
|
-
type: "string",
|
|
29
|
-
demandOption: true,
|
|
30
|
-
})
|
|
31
|
-
.strict()
|
|
32
|
-
.help(false)
|
|
33
|
-
.parseSync();
|
|
34
|
-
|
|
35
|
-
// Dynamically load because we need to load the environment variables before importing modules that depend on them.
|
|
36
|
-
const { auth } = await import("../src/lib/auth");
|
|
37
|
-
|
|
38
|
-
// Create user via Better Auth's signUpEmail API (handles password hashing + account creation)
|
|
39
|
-
const { user } = await auth.api.signUpEmail({
|
|
40
|
-
body: { email, password, name },
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
console.log(`User "${user.id}" created successfully.`);
|
|
44
|
-
process.exit(0);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void main();
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { integer, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
2
|
-
import { users } from "./users";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Better Auth account table.
|
|
6
|
-
* Stores OAuth provider links and credential password hashes.
|
|
7
|
-
* @see https://better-auth.com/docs/concepts/database
|
|
8
|
-
*/
|
|
9
|
-
export const accounts = pgTable("account", {
|
|
10
|
-
id: text("id")
|
|
11
|
-
.$defaultFn(() => crypto.randomUUID())
|
|
12
|
-
.primaryKey(),
|
|
13
|
-
userId: uuid("user_id")
|
|
14
|
-
.notNull()
|
|
15
|
-
.references(() => users.id, { onDelete: "cascade" }),
|
|
16
|
-
accountId: text("account_id").notNull(),
|
|
17
|
-
providerId: text("provider_id").notNull(),
|
|
18
|
-
accessToken: text("access_token"),
|
|
19
|
-
refreshToken: text("refresh_token"),
|
|
20
|
-
expiresAt: integer("expires_at"),
|
|
21
|
-
accessTokenExpiresAt: timestamp("access_token_expires_at", { mode: "date" }),
|
|
22
|
-
refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
|
|
23
|
-
mode: "date",
|
|
24
|
-
}),
|
|
25
|
-
scope: text("scope"),
|
|
26
|
-
idToken: text("id_token"),
|
|
27
|
-
password: text("password"),
|
|
28
|
-
createdAt: timestamp("created_at", { mode: "date" }).notNull(),
|
|
29
|
-
updatedAt: timestamp("updated_at", { mode: "date" }).notNull(),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
export type Account = typeof accounts.$inferSelect;
|
|
33
|
-
export type NewAccount = typeof accounts.$inferInsert;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
2
|
-
import { users } from "./users";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Better Auth session table.
|
|
6
|
-
* @see https://better-auth.com/docs/concepts/database
|
|
7
|
-
*/
|
|
8
|
-
export const sessions = pgTable("session", {
|
|
9
|
-
id: text("id")
|
|
10
|
-
.$defaultFn(() => crypto.randomUUID())
|
|
11
|
-
.primaryKey(),
|
|
12
|
-
userId: uuid("user_id")
|
|
13
|
-
.notNull()
|
|
14
|
-
.references(() => users.id, { onDelete: "cascade" }),
|
|
15
|
-
token: text("token").notNull().unique(),
|
|
16
|
-
expiresAt: timestamp("expires_at", { mode: "date" }).notNull(),
|
|
17
|
-
ipAddress: text("ip_address"),
|
|
18
|
-
userAgent: text("user_agent"),
|
|
19
|
-
impersonatedBy: text("impersonated_by"),
|
|
20
|
-
createdAt: timestamp("created_at", { mode: "date" }).notNull(),
|
|
21
|
-
updatedAt: timestamp("updated_at", { mode: "date" }).notNull(),
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export type Session = typeof sessions.$inferSelect;
|
|
25
|
-
export type NewSession = typeof sessions.$inferInsert;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { sql } from "drizzle-orm";
|
|
2
|
-
import {
|
|
3
|
-
boolean,
|
|
4
|
-
pgTable,
|
|
5
|
-
text,
|
|
6
|
-
timestamp,
|
|
7
|
-
uniqueIndex,
|
|
8
|
-
uuid,
|
|
9
|
-
} from "drizzle-orm/pg-core";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Users table for Better Auth.
|
|
13
|
-
* @see https://better-auth.com/docs/concepts/database
|
|
14
|
-
*/
|
|
15
|
-
export const users = pgTable(
|
|
16
|
-
"users",
|
|
17
|
-
{
|
|
18
|
-
id: uuid("id")
|
|
19
|
-
.$defaultFn(() => crypto.randomUUID())
|
|
20
|
-
.primaryKey(),
|
|
21
|
-
name: text("name").notNull(),
|
|
22
|
-
email: text("email").notNull().unique(),
|
|
23
|
-
emailVerified: boolean("email_verified").notNull().default(false),
|
|
24
|
-
image: text("image"),
|
|
25
|
-
role: text("role").notNull().default("user"),
|
|
26
|
-
banned: boolean("banned").default(false),
|
|
27
|
-
banReason: text("ban_reason"),
|
|
28
|
-
banExpires: timestamp("ban_expires", { mode: "date" }),
|
|
29
|
-
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
30
|
-
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
|
31
|
-
},
|
|
32
|
-
(table) => ({
|
|
33
|
-
emailIndex: uniqueIndex("lower_email_index").on(sql`lower(${table.email})`),
|
|
34
|
-
}),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
export type User = typeof users.$inferSelect;
|
|
38
|
-
export type NewUser = typeof users.$inferInsert;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Better Auth verification table.
|
|
5
|
-
* @see https://better-auth.com/docs/concepts/database
|
|
6
|
-
*/
|
|
7
|
-
export const verifications = pgTable("verification", {
|
|
8
|
-
id: text("id")
|
|
9
|
-
.$defaultFn(() => crypto.randomUUID())
|
|
10
|
-
.primaryKey(),
|
|
11
|
-
identifier: text("identifier").notNull(),
|
|
12
|
-
value: text("value").notNull(),
|
|
13
|
-
expiresAt: timestamp("expires_at", { mode: "date" }).notNull(),
|
|
14
|
-
createdAt: timestamp("created_at", { mode: "date" }),
|
|
15
|
-
updatedAt: timestamp("updated_at", { mode: "date" }),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export type Verification = typeof verifications.$inferSelect;
|
|
19
|
-
export type NewVerification = typeof verifications.$inferInsert;
|