@percepta/create 3.1.4 → 3.2.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/README.md +8 -8
- package/dist/git-ops-C2CIjuce.js +51 -0
- package/dist/git-ops-C2CIjuce.js.map +1 -0
- package/dist/index.js +1085 -1072
- package/dist/index.js.map +1 -0
- package/dist/init-CtCp7Tv2.js +52 -0
- package/dist/init-CtCp7Tv2.js.map +1 -0
- package/dist/status-CKe4aKso.js +48 -0
- package/dist/status-CKe4aKso.js.map +1 -0
- package/dist/sync-D1vkoofl.js +101 -0
- package/dist/sync-D1vkoofl.js.map +1 -0
- package/dist/upstream-D-LH_1z4.js +85 -0
- package/dist/upstream-D-LH_1z4.js.map +1 -0
- package/package.json +23 -24
- package/template-versions.json +1 -1
- package/templates/monorepo/.github/workflows/access-control.yml +38 -0
- package/templates/monorepo/README.md +41 -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 +26 -0
- package/templates/monorepo/auth/drizzle.config.ts +13 -0
- package/templates/monorepo/auth/package.json +32 -0
- package/templates/monorepo/auth/scripts/setup-database.ts +57 -0
- package/templates/monorepo/auth/src/auth.ts +77 -0
- package/templates/monorepo/auth/src/config/database.ts +31 -0
- package/templates/monorepo/auth/src/drizzle/db.ts +9 -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/{webapp → monorepo/auth}/src/drizzle/schema/auth/accounts.ts +1 -6
- package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/sessions.ts +1 -5
- package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/verifications.ts +0 -4
- 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/chunk-CO3YWUD6.js +0 -139
- package/dist/chunk-DCM7JOSC.js +0 -49
- package/dist/chunk-V5EJIUBJ.js +0 -60
- package/dist/index.d.ts +0 -1
- package/dist/init-EQZ2TCSJ.js +0 -96
- package/dist/status-QW5TQDYY.js +0 -76
- package/dist/sync-RLBZDOFB.js +0 -136
- package/dist/upstream-TQFVPMEG.js +0 -144
- package/templates/webapp/scripts/create-user.ts +0 -47
- package/templates/webapp/src/drizzle/schema/auth/users.ts +0 -38
package/dist/sync-RLBZDOFB.js
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getLatestTemplateTag,
|
|
3
|
-
getTemplateDiff,
|
|
4
|
-
getTemplateVersionFromTag
|
|
5
|
-
} from "./chunk-DCM7JOSC.js";
|
|
6
|
-
import {
|
|
7
|
-
readManifest,
|
|
8
|
-
resolveMosaicTemplatePath
|
|
9
|
-
} from "./chunk-V5EJIUBJ.js";
|
|
10
|
-
|
|
11
|
-
// src/commands/sync.ts
|
|
12
|
-
import path from "path";
|
|
13
|
-
import fs from "fs-extra";
|
|
14
|
-
import chalk from "chalk";
|
|
15
|
-
function generateSyncContext(manifest, toVersion, diff, notes) {
|
|
16
|
-
let content = `# Mosaic Sync Context
|
|
17
|
-
|
|
18
|
-
## App Info
|
|
19
|
-
- **Template:** ${manifest.templateType}
|
|
20
|
-
- **Current version:** ${manifest.templateVersion}
|
|
21
|
-
- **Target version:** ${toVersion}
|
|
22
|
-
|
|
23
|
-
## Placeholder Mappings
|
|
24
|
-
|
|
25
|
-
When applying template changes, replace these placeholder tokens with the actual values:
|
|
26
|
-
|
|
27
|
-
| Placeholder | Value |
|
|
28
|
-
|------------|-------|
|
|
29
|
-
${Object.entries(manifest.placeholders).map(([k, v]) => `| \`${k}\` | \`${v}\` |`).join("\n")}
|
|
30
|
-
|
|
31
|
-
## Template Changes (${manifest.templateVersion} \u2192 ${toVersion})
|
|
32
|
-
|
|
33
|
-
\`\`\`diff
|
|
34
|
-
${diff}
|
|
35
|
-
\`\`\`
|
|
36
|
-
`;
|
|
37
|
-
if (notes.trim()) {
|
|
38
|
-
content += `
|
|
39
|
-
## Divergence Notes (from mosaic-template-notes.md)
|
|
40
|
-
|
|
41
|
-
${notes}
|
|
42
|
-
`;
|
|
43
|
-
}
|
|
44
|
-
content += `
|
|
45
|
-
## Instructions
|
|
46
|
-
|
|
47
|
-
1. Apply the template changes above to this app
|
|
48
|
-
2. When you see placeholder tokens (e.g. \`__APP_NAME__\`), replace them with the actual values from the mapping table
|
|
49
|
-
3. Check the divergence notes \u2014 preserve intentional divergences
|
|
50
|
-
4. For files not modified locally: apply changes directly
|
|
51
|
-
5. For files modified locally: merge intelligently, preserving local customizations
|
|
52
|
-
6. After applying all changes, run: \`pnpm install && pnpm build && pnpm lint\`
|
|
53
|
-
7. Update \`.mosaic-template.json\`: set \`templateVersion\` to \`"${toVersion}"\` and update \`templateCommit\`
|
|
54
|
-
8. If you made decisions about merge conflicts, add notes to \`mosaic-template-notes.md\`
|
|
55
|
-
9. Delete this file (\`.mosaic-sync-context.md\`) when done
|
|
56
|
-
`;
|
|
57
|
-
return content;
|
|
58
|
-
}
|
|
59
|
-
async function syncCommand(options) {
|
|
60
|
-
const cwd = process.cwd();
|
|
61
|
-
try {
|
|
62
|
-
const manifest = await readManifest(cwd);
|
|
63
|
-
const mosaicTemplatePath = resolveMosaicTemplatePath(options);
|
|
64
|
-
const fromTag = `template/${manifest.templateType}/${manifest.templateVersion}`;
|
|
65
|
-
let toTag;
|
|
66
|
-
if (options.to) {
|
|
67
|
-
toTag = `template/${manifest.templateType}/${options.to}`;
|
|
68
|
-
} else {
|
|
69
|
-
const latest = getLatestTemplateTag(
|
|
70
|
-
manifest.templateType,
|
|
71
|
-
mosaicTemplatePath
|
|
72
|
-
);
|
|
73
|
-
if (!latest) {
|
|
74
|
-
console.error(
|
|
75
|
-
chalk.red(
|
|
76
|
-
"No template tags found. Run 'pnpm template:tag' in the mosaic repo first."
|
|
77
|
-
)
|
|
78
|
-
);
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
toTag = latest;
|
|
82
|
-
}
|
|
83
|
-
const toVersion = getTemplateVersionFromTag(toTag);
|
|
84
|
-
if (toVersion === manifest.templateVersion) {
|
|
85
|
-
console.log(chalk.green("Already up to date."));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const diff = getTemplateDiff(
|
|
89
|
-
mosaicTemplatePath,
|
|
90
|
-
manifest.source.templatePath,
|
|
91
|
-
fromTag,
|
|
92
|
-
toTag
|
|
93
|
-
);
|
|
94
|
-
if (!diff.trim()) {
|
|
95
|
-
console.log(
|
|
96
|
-
chalk.green("No template file changes between versions.")
|
|
97
|
-
);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const notesPath = path.join(cwd, "mosaic-template-notes.md");
|
|
101
|
-
let notes = "";
|
|
102
|
-
if (await fs.pathExists(notesPath)) {
|
|
103
|
-
notes = await fs.readFile(notesPath, "utf-8");
|
|
104
|
-
}
|
|
105
|
-
const context = generateSyncContext(manifest, toVersion, diff, notes);
|
|
106
|
-
const contextPath = path.join(cwd, ".mosaic-sync-context.md");
|
|
107
|
-
await fs.writeFile(contextPath, context);
|
|
108
|
-
console.log();
|
|
109
|
-
console.log(chalk.bold("Sync Context Generated"));
|
|
110
|
-
console.log();
|
|
111
|
-
console.log(chalk.dim(" From:"), manifest.templateVersion);
|
|
112
|
-
console.log(chalk.dim(" To:"), toVersion);
|
|
113
|
-
console.log(chalk.dim(" Context file:"), ".mosaic-sync-context.md");
|
|
114
|
-
console.log();
|
|
115
|
-
console.log("Next steps:");
|
|
116
|
-
console.log(
|
|
117
|
-
chalk.dim(" 1."),
|
|
118
|
-
"Open Claude Code in this directory"
|
|
119
|
-
);
|
|
120
|
-
console.log(
|
|
121
|
-
chalk.dim(" 2."),
|
|
122
|
-
'Tell Claude: "Read .mosaic-sync-context.md and apply the template changes"'
|
|
123
|
-
);
|
|
124
|
-
console.log(
|
|
125
|
-
chalk.dim(" 3."),
|
|
126
|
-
"Review Claude's changes, then delete .mosaic-sync-context.md"
|
|
127
|
-
);
|
|
128
|
-
console.log();
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error(chalk.red(error.message));
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
export {
|
|
135
|
-
syncCommand
|
|
136
|
-
};
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getFileAtTag
|
|
3
|
-
} from "./chunk-DCM7JOSC.js";
|
|
4
|
-
import {
|
|
5
|
-
readManifest,
|
|
6
|
-
resolveMosaicTemplatePath
|
|
7
|
-
} from "./chunk-V5EJIUBJ.js";
|
|
8
|
-
|
|
9
|
-
// src/commands/upstream.ts
|
|
10
|
-
import path from "path";
|
|
11
|
-
import fs from "fs-extra";
|
|
12
|
-
import chalk from "chalk";
|
|
13
|
-
async function generateUpstreamContext(manifest, mosaicTemplatePath, tag, appDir, files) {
|
|
14
|
-
let content = `# Mosaic Upstream Context
|
|
15
|
-
|
|
16
|
-
## App Info
|
|
17
|
-
- **App name:** ${manifest.placeholders.__APP_NAME__ || "unknown"}
|
|
18
|
-
- **Template:** ${manifest.templateType}
|
|
19
|
-
- **Template version:** ${manifest.templateVersion}
|
|
20
|
-
|
|
21
|
-
## Placeholder Mappings
|
|
22
|
-
|
|
23
|
-
When generalizing app code back to template, replace these values with placeholder tokens:
|
|
24
|
-
|
|
25
|
-
| Value | Placeholder |
|
|
26
|
-
|-------|------------|
|
|
27
|
-
${Object.entries(manifest.placeholders).sort((a, b) => b[1].length - a[1].length).map(([k, v]) => `| \`${v}\` | \`${k}\` |`).join("\n")}
|
|
28
|
-
|
|
29
|
-
## Files to Review
|
|
30
|
-
|
|
31
|
-
`;
|
|
32
|
-
for (const file of files) {
|
|
33
|
-
const appFilePath = path.resolve(appDir, file);
|
|
34
|
-
const templateRelPath = `${manifest.source.templatePath}/${file}`;
|
|
35
|
-
const appContent = await fs.pathExists(appFilePath) ? await fs.readFile(appFilePath, "utf-8") : null;
|
|
36
|
-
const templateContent = getFileAtTag(
|
|
37
|
-
mosaicTemplatePath,
|
|
38
|
-
tag,
|
|
39
|
-
templateRelPath
|
|
40
|
-
);
|
|
41
|
-
content += `### ${file}
|
|
42
|
-
|
|
43
|
-
`;
|
|
44
|
-
if (!templateContent && appContent) {
|
|
45
|
-
content += `**New file** (not in template at ${manifest.templateVersion})
|
|
46
|
-
|
|
47
|
-
`;
|
|
48
|
-
content += `\`\`\`
|
|
49
|
-
${appContent}
|
|
50
|
-
\`\`\`
|
|
51
|
-
|
|
52
|
-
`;
|
|
53
|
-
} else if (templateContent && !appContent) {
|
|
54
|
-
content += `**Deleted** (exists in template but not in app)
|
|
55
|
-
|
|
56
|
-
`;
|
|
57
|
-
} else if (appContent && templateContent) {
|
|
58
|
-
content += `**App version:**
|
|
59
|
-
\`\`\`
|
|
60
|
-
${appContent}
|
|
61
|
-
\`\`\`
|
|
62
|
-
|
|
63
|
-
`;
|
|
64
|
-
content += `**Template version (at ${manifest.templateVersion}):**
|
|
65
|
-
\`\`\`
|
|
66
|
-
${templateContent}
|
|
67
|
-
\`\`\`
|
|
68
|
-
|
|
69
|
-
`;
|
|
70
|
-
} else {
|
|
71
|
-
content += `**Not found** (file does not exist in app or template)
|
|
72
|
-
|
|
73
|
-
`;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
content += `## Instructions
|
|
77
|
-
|
|
78
|
-
1. Review each file above
|
|
79
|
-
2. Determine which changes are generalizable (useful for all apps) vs app-specific
|
|
80
|
-
3. For generalizable changes: apply them to the template at \`${manifest.source.templatePath}/\`
|
|
81
|
-
4. When applying, replace app-specific values with placeholders using the mapping table above (replace longest values first)
|
|
82
|
-
5. After applying, bump the version in \`packages/create-mosaic-module/template-versions.json\`
|
|
83
|
-
6. Run \`pnpm template:tag\` to create the new version tag
|
|
84
|
-
7. Delete this file (\`.mosaic-upstream-context.md\`) when done
|
|
85
|
-
`;
|
|
86
|
-
return content;
|
|
87
|
-
}
|
|
88
|
-
async function upstreamCommand(options) {
|
|
89
|
-
const cwd = process.cwd();
|
|
90
|
-
try {
|
|
91
|
-
const manifest = await readManifest(cwd);
|
|
92
|
-
const mosaicTemplatePath = resolveMosaicTemplatePath(options);
|
|
93
|
-
if (!options.files || options.files.length === 0) {
|
|
94
|
-
console.error(
|
|
95
|
-
chalk.red("Specify files with --files <file1> <file2> ...")
|
|
96
|
-
);
|
|
97
|
-
console.log(
|
|
98
|
-
chalk.dim(
|
|
99
|
-
" Example: create upstream --files src/config/getEnvConfig.ts"
|
|
100
|
-
)
|
|
101
|
-
);
|
|
102
|
-
process.exit(1);
|
|
103
|
-
}
|
|
104
|
-
const tag = `template/${manifest.templateType}/${manifest.templateVersion}`;
|
|
105
|
-
const context = await generateUpstreamContext(
|
|
106
|
-
manifest,
|
|
107
|
-
mosaicTemplatePath,
|
|
108
|
-
tag,
|
|
109
|
-
cwd,
|
|
110
|
-
options.files
|
|
111
|
-
);
|
|
112
|
-
const contextPath = path.join(cwd, ".mosaic-upstream-context.md");
|
|
113
|
-
await fs.writeFile(contextPath, context);
|
|
114
|
-
console.log();
|
|
115
|
-
console.log(chalk.bold("Upstream Context Generated"));
|
|
116
|
-
console.log();
|
|
117
|
-
console.log(chalk.dim(" Files:"), options.files.join(", "));
|
|
118
|
-
console.log(
|
|
119
|
-
chalk.dim(" Context file:"),
|
|
120
|
-
".mosaic-upstream-context.md"
|
|
121
|
-
);
|
|
122
|
-
console.log();
|
|
123
|
-
console.log("Next steps:");
|
|
124
|
-
console.log(
|
|
125
|
-
chalk.dim(" 1."),
|
|
126
|
-
"Open Claude Code in the mosaic repo"
|
|
127
|
-
);
|
|
128
|
-
console.log(
|
|
129
|
-
chalk.dim(" 2."),
|
|
130
|
-
`Tell Claude: "Read ${path.resolve(cwd, ".mosaic-upstream-context.md")} and apply generalizable changes to the template"`
|
|
131
|
-
);
|
|
132
|
-
console.log(
|
|
133
|
-
chalk.dim(" 3."),
|
|
134
|
-
"Review Claude's changes to the template"
|
|
135
|
-
);
|
|
136
|
-
console.log();
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error(chalk.red(error.message));
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
export {
|
|
143
|
-
upstreamCommand
|
|
144
|
-
};
|
|
@@ -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,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;
|