create-absolutejs 0.18.0 → 0.19.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/CHANGELOG.md +10 -0
- package/changelog.json +14 -0
- package/dist/commands/migrateDatabase.d.ts +6 -0
- package/dist/commands/migrateDatabase.js +53 -0
- package/dist/data.d.ts +1 -1
- package/dist/data.js +1 -0
- package/dist/generators/configurations/generateDrizzleConfig.d.ts +5 -3
- package/dist/generators/configurations/generateDrizzleConfig.js +40 -14
- package/dist/generators/configurations/generateEnv.js +6 -0
- package/dist/generators/configurations/generatePackageJson.js +30 -3
- package/dist/generators/db/drizzleTargets.d.ts +5 -0
- package/dist/generators/db/drizzleTargets.js +27 -0
- package/dist/generators/db/generateDatabaseTypes.js +10 -6
- package/dist/generators/db/generateDockerContainer.d.ts +5 -0
- package/dist/generators/db/generateDockerContainer.js +22 -4
- package/dist/generators/db/generateDrizzleSchema.js +65 -81
- package/dist/generators/db/handlerTemplates.d.ts +4 -4
- package/dist/generators/db/handlerTemplates.js +7 -10
- package/dist/generators/db/scaffoldDatabase.js +17 -4
- package/dist/generators/db/scaffoldDocker.d.ts +2 -1
- package/dist/generators/db/scaffoldDocker.js +7 -3
- package/dist/generators/project/generateDBBlock.js +11 -2
- package/dist/generators/project/generateImportsBlock.js +11 -6
- package/dist/generators/project/generateServer.js +2 -1
- package/dist/index.js +2 -1
- package/dist/messages.d.ts +2 -1
- package/dist/messages.js +2 -1
- package/dist/questions/authOption.js +4 -1
- package/dist/questions/databaseEngine.d.ts +1 -1
- package/dist/scaffold.d.ts +1 -0
- package/dist/scaffold.js +21 -3
- package/dist/templates/configurations/eslint.config.example.mjs +80 -41
- package/dist/templates/db/migrations/cockroach-count-history/20260926210314_init/migration.sql +5 -0
- package/dist/templates/db/migrations/cockroach-count-history/20260926210314_init/snapshot.json +69 -0
- package/dist/templates/db/migrations/cockroach-users/20260926210315_init/migration.sql +5 -0
- package/dist/templates/db/migrations/cockroach-users/20260926210315_init/snapshot.json +62 -0
- package/dist/templates/db/migrations/mssql-count-history/20260926210316_init/migration.sql +6 -0
- package/dist/templates/db/migrations/mssql-count-history/20260926210316_init/snapshot.json +64 -0
- package/dist/templates/db/migrations/mssql-users/20260926210317_init/migration.sql +6 -0
- package/dist/templates/db/migrations/mssql-users/20260926210317_init/snapshot.json +70 -0
- package/dist/templates/db/migrations/mysql-count-history/20260926210318_init/migration.sql +5 -0
- package/dist/templates/db/migrations/mysql-count-history/20260926210318_init/snapshot.json +61 -0
- package/dist/templates/db/migrations/mysql-users/20260926210319_init/migration.sql +5 -0
- package/dist/templates/db/migrations/mysql-users/20260926210319_init/snapshot.json +61 -0
- package/dist/templates/db/migrations/postgresql-count-history/20260926210320_init/migration.sql +5 -0
- package/dist/templates/db/migrations/postgresql-count-history/20260926210320_init/snapshot.json +71 -0
- package/dist/templates/db/migrations/postgresql-users/20260926210320_init/migration.sql +5 -0
- package/dist/templates/db/migrations/postgresql-users/20260926210320_init/snapshot.json +62 -0
- package/dist/templates/db/migrations/singlestore-count-history/20260926210321_init/migration.sql +5 -0
- package/dist/templates/db/migrations/singlestore-count-history/20260926210321_init/snapshot.json +61 -0
- package/dist/templates/db/migrations/singlestore-users/20260926210322_init/migration.sql +5 -0
- package/dist/templates/db/migrations/singlestore-users/20260926210322_init/snapshot.json +61 -0
- package/dist/templates/db/migrations/sqlite-count-history/20260926210323_init/migration.sql +5 -0
- package/dist/templates/db/migrations/sqlite-count-history/20260926210323_init/snapshot.json +50 -0
- package/dist/templates/db/migrations/sqlite-users/20260926210324_init/migration.sql +5 -0
- package/dist/templates/db/migrations/sqlite-users/20260926210324_init/snapshot.json +50 -0
- package/dist/templates/db/migrations/turso-count-history/20260926210324_init/migration.sql +5 -0
- package/dist/templates/db/migrations/turso-count-history/20260926210324_init/snapshot.json +50 -0
- package/dist/templates/db/migrations/turso-users/20260926210325_init/migration.sql +5 -0
- package/dist/templates/db/migrations/turso-users/20260926210325_init/snapshot.json +50 -0
- package/dist/templates/git/gitignore +0 -3
- package/dist/typeGuards.js +2 -2
- package/dist/utils/parseCommandLineOptions.js +16 -25
- package/dist/utils/validateDatabaseSelection.d.ts +11 -0
- package/dist/utils/validateDatabaseSelection.js +31 -0
- package/package.json +3 -1
- package/dist/templates/configurations/drizzle.config.ts +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,16 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.19.0 — 2026-09-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Scaffold Drizzle for every Drizzle 1.0 dialect (PostgreSQL, MySQL, MariaDB, SQLite, Turso/libSQL, SingleStore, SQL Server, CockroachDB) with a committed initial migration and db:generate / db:migrate scripts**
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Accept --orm none as the help text documents, and refuse unsupported database, host and ORM combinations before writing any files**
|
|
18
|
+
|
|
9
19
|
## 0.18.0 — 2026-09-23
|
|
10
20
|
|
|
11
21
|
### Fixed
|
package/changelog.json
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
"contract": 1,
|
|
3
3
|
"name": "create-absolutejs",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "fixed",
|
|
9
|
+
"summary": "Accept --orm none as the help text documents, and refuse unsupported database, host and ORM combinations before writing any files"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "added",
|
|
13
|
+
"summary": "Scaffold Drizzle for every Drizzle 1.0 dialect (PostgreSQL, MySQL, MariaDB, SQLite, Turso/libSQL, SingleStore, SQL Server, CockroachDB) with a committed initial migration and db:generate / db:migrate scripts"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"date": "2026-09-26",
|
|
17
|
+
"version": "0.19.0"
|
|
18
|
+
},
|
|
5
19
|
{
|
|
6
20
|
"changes": [
|
|
7
21
|
{
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CreateConfiguration } from '../types';
|
|
2
|
+
type MigrateDatabaseProps = Pick<CreateConfiguration, 'databaseEngine' | 'databaseHost' | 'projectName'> & {
|
|
3
|
+
verifyLocalDatabase: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare const migrateDatabase: ({ databaseEngine, databaseHost, projectName, verifyLocalDatabase }: MigrateDatabaseProps) => Promise<boolean>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spinner } from '@clack/prompts';
|
|
2
|
+
import { $ } from 'bun';
|
|
3
|
+
import { green, yellow } from 'picocolors';
|
|
4
|
+
import { isDockerDaemonRunning, resolveDockerExe } from '../utils/checkDockerInstalled';
|
|
5
|
+
const applyMigrations = async (projectName) => (await $ `bun run db:migrate`.cwd(projectName).quiet().nothrow())
|
|
6
|
+
.exitCode === 0;
|
|
7
|
+
const migrateDockerDatabase = async (projectName, databaseEngine) => {
|
|
8
|
+
if (!(await isDockerDaemonRunning()))
|
|
9
|
+
return false;
|
|
10
|
+
const docker = resolveDockerExe();
|
|
11
|
+
const compose = [
|
|
12
|
+
'compose',
|
|
13
|
+
'-p',
|
|
14
|
+
databaseEngine,
|
|
15
|
+
'-f',
|
|
16
|
+
'db/docker-compose.db.yml'
|
|
17
|
+
];
|
|
18
|
+
const started = await $ `${docker} ${compose} up -d --wait db`
|
|
19
|
+
.cwd(projectName)
|
|
20
|
+
.quiet()
|
|
21
|
+
.nothrow();
|
|
22
|
+
if (started.exitCode !== 0)
|
|
23
|
+
return false;
|
|
24
|
+
const migrated = await applyMigrations(projectName);
|
|
25
|
+
await $ `${docker} ${compose} down`.cwd(projectName).quiet().nothrow();
|
|
26
|
+
return migrated;
|
|
27
|
+
};
|
|
28
|
+
/* Applies the project's committed initial migration once dependencies are
|
|
29
|
+
installed, so a fresh Drizzle project's example tables exist on first run.
|
|
30
|
+
Local SQLite and the default local libSQL file for Turso migrate directly;
|
|
31
|
+
a Docker database is migrated only when the daemon is already up and the
|
|
32
|
+
scaffold verified it. Hosted databases are left to `db:migrate`, which needs
|
|
33
|
+
the real DATABASE_URL. Returns whether the migration ran. */
|
|
34
|
+
export const migrateDatabase = async ({ databaseEngine, databaseHost, projectName, verifyLocalDatabase }) => {
|
|
35
|
+
const isLocal = databaseHost === undefined || databaseHost === 'none';
|
|
36
|
+
const isFileDatabase = databaseEngine === 'sqlite' && (isLocal || databaseHost === 'turso');
|
|
37
|
+
const isDockerDatabase = isLocal &&
|
|
38
|
+
verifyLocalDatabase &&
|
|
39
|
+
databaseEngine !== undefined &&
|
|
40
|
+
databaseEngine !== 'none' &&
|
|
41
|
+
databaseEngine !== 'sqlite';
|
|
42
|
+
if (!isFileDatabase && !isDockerDatabase)
|
|
43
|
+
return false;
|
|
44
|
+
const spin = spinner();
|
|
45
|
+
spin.start('Applying database migrations');
|
|
46
|
+
const migrated = isFileDatabase
|
|
47
|
+
? await applyMigrations(projectName)
|
|
48
|
+
: await migrateDockerDatabase(projectName, databaseEngine ?? 'db');
|
|
49
|
+
spin.stop(migrated
|
|
50
|
+
? green('Database migrated')
|
|
51
|
+
: yellow('Database not migrated — run db:migrate once it is up'));
|
|
52
|
+
return migrated;
|
|
53
|
+
};
|
package/dist/data.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare const availableCodeQualityTools: readonly ["eslint+prettier", "bi
|
|
|
5
5
|
export declare const availableDatabaseEngines: readonly ["postgresql", "mysql", "sqlite", "mongodb", "mariadb", "gel", "singlestore", "cockroachdb", "mssql", "none"];
|
|
6
6
|
export declare const availableDatabaseHosts: readonly ["neon", "planetscale", "turso", "none"];
|
|
7
7
|
export declare const availableDirectoryConfigurations: readonly ["default", "custom"];
|
|
8
|
-
export declare const availableDrizzleDialects: readonly ["mariadb", "mssql", "mysql", "postgresql", "singlestore", "sqlite"];
|
|
8
|
+
export declare const availableDrizzleDialects: readonly ["cockroachdb", "mariadb", "mssql", "mysql", "postgresql", "singlestore", "sqlite"];
|
|
9
9
|
export declare const availableFrontends: readonly ["react", "html", "svelte", "vue", "htmx", "angular"];
|
|
10
10
|
export declare const availableORMs: readonly ["drizzle", "prisma", "none"];
|
|
11
11
|
export declare const availablePlugins: AvailableDependency[];
|
package/dist/data.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvailableDrizzleDialect, DatabaseHost } from '../../types';
|
|
2
2
|
type CreateDrizzleConfigProps = {
|
|
3
3
|
projectName: string;
|
|
4
|
-
databaseEngine:
|
|
4
|
+
databaseEngine: AvailableDrizzleDialect;
|
|
5
|
+
databaseHost: DatabaseHost;
|
|
5
6
|
databaseDirectory: string;
|
|
6
7
|
};
|
|
7
|
-
export declare const createDrizzleConfig: ({ projectName,
|
|
8
|
+
export declare const createDrizzleConfig: ({ projectName, ...options }: CreateDrizzleConfigProps) => void;
|
|
9
|
+
export declare const generateDrizzleConfig: ({ databaseDirectory, databaseEngine, databaseHost }: Omit<CreateDrizzleConfigProps, "projectName">) => string;
|
|
8
10
|
export {};
|
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import { writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { env } from 'process';
|
|
6
|
-
|
|
7
|
-
if (!env.DATABASE_URL) {
|
|
3
|
+
import { getDrizzleKitDialect } from '../db/drizzleTargets';
|
|
4
|
+
const requireDatabaseUrl = `if (!env.DATABASE_URL) {
|
|
8
5
|
throw new Error('DATABASE_URL must be set in the environment variables');
|
|
9
6
|
}
|
|
7
|
+
`;
|
|
8
|
+
export const createDrizzleConfig = ({ projectName, ...options }) => {
|
|
9
|
+
writeFileSync(join(projectName, 'drizzle.config.ts'), generateDrizzleConfig(options));
|
|
10
|
+
};
|
|
11
|
+
export const generateDrizzleConfig = ({ databaseDirectory, databaseEngine, databaseHost }) => {
|
|
12
|
+
const dialect = getDrizzleKitDialect(databaseEngine, databaseHost);
|
|
13
|
+
const paths = `out: '${databaseDirectory}/migrations',
|
|
14
|
+
schema: '${databaseDirectory}/schema.ts'`;
|
|
15
|
+
/* Local SQLite lives in a file beside the schema — the same path the server
|
|
16
|
+
opens — so migrations need no environment at all. */
|
|
17
|
+
if (dialect === 'sqlite') {
|
|
18
|
+
return `import { defineConfig } from 'drizzle-kit';
|
|
19
|
+
|
|
20
|
+
export default defineConfig({
|
|
21
|
+
dbCredentials: {
|
|
22
|
+
url: '${databaseDirectory}/database.sqlite'
|
|
23
|
+
},
|
|
24
|
+
dialect: 'sqlite',
|
|
25
|
+
${paths}
|
|
26
|
+
});
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
/* Turso speaks libSQL: a \`libsql://\` URL plus an auth token in the cloud,
|
|
30
|
+
or a \`file:\` URL with no token for a local libSQL database. */
|
|
31
|
+
const credentials = dialect === 'turso'
|
|
32
|
+
? `authToken: env.DATABASE_AUTH_TOKEN,
|
|
33
|
+
url: env.DATABASE_URL`
|
|
34
|
+
: 'url: env.DATABASE_URL';
|
|
35
|
+
return `import { env } from 'process';
|
|
36
|
+
import { defineConfig } from 'drizzle-kit';
|
|
10
37
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
38
|
+
${requireDatabaseUrl}
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
dbCredentials: {
|
|
41
|
+
${credentials}
|
|
42
|
+
},
|
|
43
|
+
dialect: '${dialect}',
|
|
44
|
+
${paths}
|
|
45
|
+
});
|
|
19
46
|
`;
|
|
20
|
-
writeFileSync(join(projectName, 'drizzle.config.ts'), drizzleConfig);
|
|
21
47
|
};
|
|
@@ -18,6 +18,12 @@ export const generateEnv = ({ databaseEngine, databaseHost, envVariables = [], p
|
|
|
18
18
|
(databaseHost === 'none' || databaseHost === undefined)) {
|
|
19
19
|
vars.push(`DATABASE_URL=${databaseURLS[databaseEngine]}`);
|
|
20
20
|
}
|
|
21
|
+
/* A Turso project starts on a local libSQL file so it runs (and migrates)
|
|
22
|
+
before a cloud database exists; pointing it at Turso is two variables. */
|
|
23
|
+
const setsDatabaseUrl = vars.some((entry) => entry.startsWith('DATABASE_URL='));
|
|
24
|
+
if (databaseHost === 'turso' && !setsDatabaseUrl) {
|
|
25
|
+
vars.push('# Local libSQL file. For Turso use DATABASE_URL=libsql://<database>.turso.io and set DATABASE_AUTH_TOKEN.', 'DATABASE_URL=file:db/database.db');
|
|
26
|
+
}
|
|
21
27
|
if (vars.length === 0)
|
|
22
28
|
return;
|
|
23
29
|
const envPath = join(projectName, '.env');
|
|
@@ -118,6 +118,19 @@ export const createPackageJson = async ({ agentic, projectName, authOption, plug
|
|
|
118
118
|
packageNames.add('mssql');
|
|
119
119
|
packageNames.add('@types/mssql');
|
|
120
120
|
}
|
|
121
|
+
/* drizzle-orm/cockroach (and drizzle-kit's cockroach dialect) drive
|
|
122
|
+
CockroachDB through node-postgres. */
|
|
123
|
+
const usesCockroachDrizzle = isLocal && databaseEngine === 'cockroachdb' && orm === 'drizzle';
|
|
124
|
+
if (usesCockroachDrizzle) {
|
|
125
|
+
packageNames.add('pg');
|
|
126
|
+
packageNames.add('@types/pg');
|
|
127
|
+
}
|
|
128
|
+
/* drizzle-kit migrates through the first Postgres driver it can import,
|
|
129
|
+
trying pg before Neon's serverless driver (which @absolutejs/auth brings
|
|
130
|
+
in transitively and which cannot reach a local database). */
|
|
131
|
+
const needsKitPostgresDriver = isLocal && databaseEngine === 'postgresql' && orm === 'drizzle';
|
|
132
|
+
if (needsKitPostgresDriver)
|
|
133
|
+
packageNames.add('pg');
|
|
121
134
|
if (isLocal && databaseEngine === 'gel')
|
|
122
135
|
packageNames.add('gel');
|
|
123
136
|
if (databaseEngine === 'mongodb')
|
|
@@ -279,6 +292,13 @@ export const createPackageJson = async ({ agentic, projectName, authOption, plug
|
|
|
279
292
|
dependencies['mssql'] = resolveVersion('mssql', versions['mssql']);
|
|
280
293
|
devDependencies['@types/mssql'] = resolveVersion('@types/mssql', versions['@types/mssql']);
|
|
281
294
|
}
|
|
295
|
+
if (needsKitPostgresDriver) {
|
|
296
|
+
devDependencies['pg'] = resolveVersion('pg', versions['pg']);
|
|
297
|
+
}
|
|
298
|
+
if (usesCockroachDrizzle) {
|
|
299
|
+
dependencies['pg'] = resolveVersion('pg', versions['pg']);
|
|
300
|
+
devDependencies['@types/pg'] = resolveVersion('@types/pg', versions['@types/pg']);
|
|
301
|
+
}
|
|
282
302
|
if (isLocalDb && databaseEngine === 'gel') {
|
|
283
303
|
dependencies['gel'] = resolveVersion('gel', versions['gel']);
|
|
284
304
|
}
|
|
@@ -287,11 +307,18 @@ export const createPackageJson = async ({ agentic, projectName, authOption, plug
|
|
|
287
307
|
}
|
|
288
308
|
if (isLocalDb && databaseEngine === 'sqlite') {
|
|
289
309
|
scripts['db:sqlite'] = 'sqlite3 db/database.sqlite';
|
|
290
|
-
|
|
310
|
+
}
|
|
311
|
+
if (isLocalDb && databaseEngine === 'sqlite' && orm !== 'drizzle') {
|
|
312
|
+
scripts['db:init'] = 'sqlite3 db/database.sqlite < db/schema.sql';
|
|
291
313
|
}
|
|
292
314
|
if (orm === 'drizzle') {
|
|
293
|
-
|
|
294
|
-
|
|
315
|
+
/* Migrations are the schema's source of truth: `db:generate` diffs
|
|
316
|
+
db/schema.ts into a new committed migration and `db:migrate` applies
|
|
317
|
+
pending ones. drizzle-kit runs on Bun so it can use Bun's own SQL and
|
|
318
|
+
SQLite drivers, exactly as the server does. */
|
|
319
|
+
scripts['db:generate'] = 'bun --bun drizzle-kit generate';
|
|
320
|
+
scripts['db:migrate'] = 'bun --bun drizzle-kit migrate';
|
|
321
|
+
scripts['db:studio'] = 'bun --bun drizzle-kit studio';
|
|
295
322
|
/* `drizzle.config.ts` imports it and the scripts above shell out to it. */
|
|
296
323
|
devDependencies['drizzle-kit'] = resolveVersion('drizzle-kit', versions['drizzle-kit']);
|
|
297
324
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AvailableDrizzleDialect, DatabaseHost } from '../../types';
|
|
2
|
+
export declare const drizzleKitDialects: readonly ["cockroach", "mssql", "mysql", "postgresql", "singlestore", "sqlite", "turso"];
|
|
3
|
+
export type DrizzleKitDialect = (typeof drizzleKitDialects)[number];
|
|
4
|
+
export declare const getDrizzleKitDialect: (databaseEngine: AvailableDrizzleDialect, databaseHost: DatabaseHost) => "mssql" | "mysql" | "postgresql" | "singlestore" | "sqlite" | "turso" | "cockroach";
|
|
5
|
+
export declare const getMigrationTemplateName: (kitDialect: DrizzleKitDialect, usesAuth: boolean) => string;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* The `dialect` values drizzle-kit 1.0 accepts in drizzle.config.ts. Turso is
|
|
2
|
+
its own kit dialect (libSQL over HTTP/WebSocket with an auth token), even
|
|
3
|
+
though its tables are written with sqlite-core. */
|
|
4
|
+
export const drizzleKitDialects = [
|
|
5
|
+
'cockroach',
|
|
6
|
+
'mssql',
|
|
7
|
+
'mysql',
|
|
8
|
+
'postgresql',
|
|
9
|
+
'singlestore',
|
|
10
|
+
'sqlite',
|
|
11
|
+
'turso'
|
|
12
|
+
];
|
|
13
|
+
const engineKitDialects = {
|
|
14
|
+
cockroachdb: 'cockroach',
|
|
15
|
+
mariadb: 'mysql',
|
|
16
|
+
mssql: 'mssql',
|
|
17
|
+
mysql: 'mysql',
|
|
18
|
+
postgresql: 'postgresql',
|
|
19
|
+
singlestore: 'singlestore',
|
|
20
|
+
sqlite: 'sqlite'
|
|
21
|
+
};
|
|
22
|
+
export const getDrizzleKitDialect = (databaseEngine, databaseHost) => databaseEngine === 'sqlite' && databaseHost === 'turso'
|
|
23
|
+
? 'turso'
|
|
24
|
+
: engineKitDialects[databaseEngine];
|
|
25
|
+
/* The directory holding the committed initial migration for a kit dialect and
|
|
26
|
+
example table (users when auth is on, count_history otherwise). */
|
|
27
|
+
export const getMigrationTemplateName = (kitDialect, usesAuth) => `${kitDialect}-${usesAuth ? 'users' : 'count-history'}`;
|
|
@@ -127,16 +127,19 @@ export const generateDatabaseTypes = ({ databaseEngine, databaseHost, authOption
|
|
|
127
127
|
if ((!databaseHost || databaseHost === 'none') &&
|
|
128
128
|
isDrizzleDialect(databaseEngine)) {
|
|
129
129
|
switch (databaseEngine) {
|
|
130
|
-
case '
|
|
131
|
-
|
|
132
|
-
dbImport = `import type { drizzle } from 'drizzle-orm/mysql2';`;
|
|
130
|
+
case 'cockroachdb':
|
|
131
|
+
dbImport = `import type { drizzle } from 'drizzle-orm/cockroach';`;
|
|
133
132
|
dbTypeLine =
|
|
134
133
|
'export type DatabaseType = ReturnType<typeof drizzle>;';
|
|
135
134
|
break;
|
|
135
|
+
case 'mariadb':
|
|
136
|
+
case 'mysql':
|
|
137
|
+
dbImport = `import type { MySql2Database } from 'drizzle-orm/mysql2';`;
|
|
138
|
+
dbTypeLine = 'export type DatabaseType = MySql2Database;';
|
|
139
|
+
break;
|
|
136
140
|
case 'mssql':
|
|
137
141
|
dbImport = `import type { NodeMsSqlDatabase } from 'drizzle-orm/node-mssql';`;
|
|
138
|
-
dbTypeLine =
|
|
139
|
-
'export type DatabaseType = NodeMsSqlDatabase;';
|
|
142
|
+
dbTypeLine = 'export type DatabaseType = NodeMsSqlDatabase;';
|
|
140
143
|
break;
|
|
141
144
|
case 'postgresql':
|
|
142
145
|
dbImport = `import type { drizzle } from 'drizzle-orm/bun-sql';`;
|
|
@@ -145,7 +148,8 @@ export const generateDatabaseTypes = ({ databaseEngine, databaseHost, authOption
|
|
|
145
148
|
break;
|
|
146
149
|
case 'singlestore':
|
|
147
150
|
dbImport = `import type { SingleStoreDriverDatabase } from 'drizzle-orm/singlestore';`;
|
|
148
|
-
dbTypeLine =
|
|
151
|
+
dbTypeLine =
|
|
152
|
+
'export type DatabaseType = SingleStoreDriverDatabase;';
|
|
149
153
|
break;
|
|
150
154
|
case 'sqlite':
|
|
151
155
|
dbImport = `import type { drizzle } from 'drizzle-orm/bun-sqlite';`;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { DatabaseEngine } from '../../types';
|
|
2
2
|
export declare const generateDockerContainer: (databaseEngine: DatabaseEngine) => string;
|
|
3
|
+
export declare const getDockerInitScript: (databaseEngine: DatabaseEngine) => {
|
|
4
|
+
fileName: string;
|
|
5
|
+
mountPath: string;
|
|
6
|
+
sql: string;
|
|
7
|
+
} | undefined;
|
|
@@ -6,7 +6,9 @@ const templates = {
|
|
|
6
6
|
},
|
|
7
7
|
healthcheck: {
|
|
8
8
|
startPeriod: '5s',
|
|
9
|
-
|
|
9
|
+
/* Healthy once COCKROACH_DATABASE exists, not merely once the node
|
|
10
|
+
answers, so `db:up --wait` is followed safely by `db:migrate`. */
|
|
11
|
+
test: 'cockroach sql --insecure -e "SHOW DATABASES" 2>/dev/null | grep -q "^database"'
|
|
10
12
|
},
|
|
11
13
|
image: 'cockroachdb/cockroach:latest-v25.3',
|
|
12
14
|
port: '26257:26257',
|
|
@@ -101,9 +103,17 @@ const templates = {
|
|
|
101
103
|
},
|
|
102
104
|
healthcheck: {
|
|
103
105
|
startPeriod: '30s',
|
|
104
|
-
|
|
106
|
+
/* Healthy once the init script has created the database. */
|
|
107
|
+
test: 'singlestore -u root -ppassword -D database -e "SELECT 1" >/dev/null 2>&1'
|
|
105
108
|
},
|
|
106
109
|
image: 'ghcr.io/singlestore-labs/singlestoredb-dev', // NOTE: No tag specified due to data persistence
|
|
110
|
+
/* The dev image has no "create this database" variable; without it the
|
|
111
|
+
DATABASE_URL's database would not exist until something created it. */
|
|
112
|
+
initScript: {
|
|
113
|
+
fileName: 'singlestore-init.sql',
|
|
114
|
+
mountPath: '/init.sql',
|
|
115
|
+
sql: 'CREATE DATABASE IF NOT EXISTS `database`;\n'
|
|
116
|
+
},
|
|
107
117
|
platform: 'linux/amd64', // Required for ARM64 (Apple Silicon); no-op on amd64
|
|
108
118
|
port: '3306:3306',
|
|
109
119
|
volumePath: '/data'
|
|
@@ -115,7 +125,10 @@ export const generateDockerContainer = (databaseEngine) => {
|
|
|
115
125
|
databaseEngine === 'sqlite') {
|
|
116
126
|
throw new Error('Internal type error: Expected a valid local database engine');
|
|
117
127
|
}
|
|
118
|
-
const { command, env, healthcheck, image, platform, port, volumePath } = templates[databaseEngine];
|
|
128
|
+
const { command, env, healthcheck, image, initScript, platform, port, volumePath } = templates[databaseEngine];
|
|
129
|
+
const initScriptLine = initScript
|
|
130
|
+
? `\n - ./${initScript.fileName}:${initScript.mountPath}:ro`
|
|
131
|
+
: '';
|
|
119
132
|
const commandLine = command ? `\n command: ${command}` : '';
|
|
120
133
|
const platformLine = platform ? `\n platform: ${platform}` : '';
|
|
121
134
|
const envLines = Object.entries(env)
|
|
@@ -136,9 +149,14 @@ ${envLines}
|
|
|
136
149
|
retries: 30
|
|
137
150
|
start_period: ${healthcheck.startPeriod}
|
|
138
151
|
volumes:
|
|
139
|
-
- db_data:${volumePath}
|
|
152
|
+
- db_data:${volumePath}${initScriptLine}
|
|
140
153
|
|
|
141
154
|
volumes:
|
|
142
155
|
db_data:
|
|
143
156
|
`;
|
|
144
157
|
};
|
|
158
|
+
export const getDockerInitScript = (databaseEngine) => databaseEngine === undefined ||
|
|
159
|
+
databaseEngine === 'none' ||
|
|
160
|
+
databaseEngine === 'sqlite'
|
|
161
|
+
? undefined
|
|
162
|
+
: templates[databaseEngine].initScript;
|
|
@@ -1,121 +1,105 @@
|
|
|
1
|
+
const mysqlFamily = {
|
|
2
|
+
int: 'int',
|
|
3
|
+
json: 'json()',
|
|
4
|
+
string: 'varchar({ length: 255 })',
|
|
5
|
+
time: 'timestamp()',
|
|
6
|
+
timestampDefault: '.defaultNow()',
|
|
7
|
+
uid: "int('uid').primaryKey().autoincrement()"
|
|
8
|
+
};
|
|
1
9
|
const DIALECTS = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
json: '
|
|
5
|
-
pkg: '
|
|
6
|
-
string: 'text()',
|
|
7
|
-
table: 'gelTable',
|
|
8
|
-
time: 'timestamp()'
|
|
9
|
-
},
|
|
10
|
-
mariadb: {
|
|
11
|
-
builders: ['json', 'mysqlTable', 'timestamp', 'varchar', 'int'],
|
|
12
|
-
json: 'json()',
|
|
13
|
-
pkg: 'mysql-core',
|
|
10
|
+
cockroachdb: {
|
|
11
|
+
int: 'int4',
|
|
12
|
+
json: 'jsonb()',
|
|
13
|
+
pkg: 'cockroach-core',
|
|
14
14
|
string: 'varchar({ length: 255 })',
|
|
15
|
-
table: '
|
|
16
|
-
time: 'timestamp()'
|
|
15
|
+
table: 'cockroachTable',
|
|
16
|
+
time: 'timestamp()',
|
|
17
|
+
timestampDefault: '.defaultNow()',
|
|
18
|
+
uid: "int4('uid').primaryKey().generatedAlwaysAsIdentity()"
|
|
17
19
|
},
|
|
20
|
+
mariadb: { ...mysqlFamily, pkg: 'mysql-core', table: 'mysqlTable' },
|
|
18
21
|
mssql: {
|
|
19
|
-
|
|
22
|
+
int: 'int',
|
|
20
23
|
json: "nvarchar({ length: 'max', mode: 'json' })",
|
|
21
24
|
pkg: 'mssql-core',
|
|
22
25
|
string: 'nvarchar({ length: 255 })',
|
|
23
26
|
table: 'mssqlTable',
|
|
24
|
-
time: 'datetime2()'
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
builders: ['json', 'mysqlTable', 'timestamp', 'varchar', 'int'],
|
|
28
|
-
json: 'json()',
|
|
29
|
-
pkg: 'mysql-core',
|
|
30
|
-
string: 'varchar({ length: 255 })',
|
|
31
|
-
table: 'mysqlTable',
|
|
32
|
-
time: 'timestamp()'
|
|
27
|
+
time: 'datetime2()',
|
|
28
|
+
timestampDefault: '.default(sql`sysdatetime()`)',
|
|
29
|
+
uid: "int('uid').identity().primaryKey()"
|
|
33
30
|
},
|
|
31
|
+
mysql: { ...mysqlFamily, pkg: 'mysql-core', table: 'mysqlTable' },
|
|
34
32
|
postgresql: {
|
|
35
|
-
|
|
33
|
+
int: 'integer',
|
|
36
34
|
json: 'jsonb()',
|
|
37
35
|
pkg: 'pg-core',
|
|
38
36
|
string: 'varchar({ length: 255 })',
|
|
39
37
|
table: 'pgTable',
|
|
40
|
-
time: 'timestamp()'
|
|
38
|
+
time: 'timestamp()',
|
|
39
|
+
timestampDefault: '.defaultNow()',
|
|
40
|
+
uid: "integer('uid').primaryKey().generatedAlwaysAsIdentity()"
|
|
41
41
|
},
|
|
42
42
|
singlestore: {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
...mysqlFamily,
|
|
44
|
+
/* SingleStore rejects MySQL's parenthesised expression default
|
|
45
|
+
(DEFAULT ('{}')), which is what drizzle-kit emits for .default({}). */
|
|
46
|
+
jsonDefault: ".default(sql`'{}'`)",
|
|
45
47
|
pkg: 'singlestore-core',
|
|
46
|
-
|
|
47
|
-
table: 'singlestoreTable',
|
|
48
|
-
time: 'timestamp()'
|
|
48
|
+
table: 'singlestoreTable'
|
|
49
49
|
},
|
|
50
50
|
sqlite: {
|
|
51
|
-
|
|
51
|
+
int: 'integer',
|
|
52
52
|
json: "text('', { mode: 'json' })",
|
|
53
53
|
pkg: 'sqlite-core',
|
|
54
54
|
string: 'text()',
|
|
55
55
|
table: 'sqliteTable',
|
|
56
|
-
time: "integer({ mode: 'timestamp_ms' })"
|
|
56
|
+
time: "integer({ mode: 'timestamp_ms' })",
|
|
57
|
+
/* Milliseconds since the Unix epoch: julianday() counts days from
|
|
58
|
+
2440587.5 (the epoch's Julian day). drizzle-kit only accepts a
|
|
59
|
+
parameter-free SQL default, so the constants are inline. */
|
|
60
|
+
timestampDefault: ".default(sql`((julianday('now') - 2440587.5) * 86400000)`)",
|
|
61
|
+
uid: "integer('uid').primaryKey({ autoIncrement: true })"
|
|
57
62
|
}
|
|
58
63
|
};
|
|
59
|
-
const builder = (expr) => expr.split('(')[0];
|
|
64
|
+
const builder = (expr) => expr.split('(')[0] ?? expr;
|
|
60
65
|
export const generateDrizzleSchema = ({ databaseEngine, authOption }) => {
|
|
61
66
|
const cfg = DIALECTS[databaseEngine];
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
? 'int'
|
|
67
|
-
: 'integer';
|
|
68
|
-
const timeBuilder = builder(cfg.time);
|
|
69
|
-
const jsonBuilder = builder(cfg.json);
|
|
70
|
-
const stringBuilder = builder(cfg.string);
|
|
71
|
-
const importBuilders = authOption === 'abs'
|
|
72
|
-
? [cfg.table, stringBuilder, timeBuilder, jsonBuilder]
|
|
73
|
-
: [cfg.table, intBuilder, timeBuilder];
|
|
67
|
+
const usesAuth = authOption === 'abs';
|
|
68
|
+
const importBuilders = usesAuth
|
|
69
|
+
? [cfg.table, builder(cfg.string), builder(cfg.time), builder(cfg.json)]
|
|
70
|
+
: [cfg.table, cfg.int, builder(cfg.time)];
|
|
74
71
|
const uniqueBuilders = Array.from(new Set(importBuilders));
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
: ''
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else {
|
|
92
|
-
uidColumn = `integer('uid').primaryKey().generatedAlwaysAsIdentity()`;
|
|
93
|
-
}
|
|
94
|
-
const constsBlock = databaseEngine === 'sqlite'
|
|
95
|
-
? `const JULIAN_DAY_UNIX_EPOCH_OFFSET = 2440587.5;
|
|
96
|
-
const MILLIS_PER_DAY = 86400000;\n\n`
|
|
97
|
-
: '';
|
|
98
|
-
let timestampColumn = `${cfg.time}.notNull().defaultNow()`;
|
|
99
|
-
if (databaseEngine === 'sqlite')
|
|
100
|
-
timestampColumn = `${cfg.time}.notNull().default(sql\`(julianday('now') - \${JULIAN_DAY_UNIX_EPOCH_OFFSET}) * \${MILLIS_PER_DAY}\`)`;
|
|
101
|
-
if (databaseEngine === 'mssql')
|
|
102
|
-
timestampColumn = `${cfg.time}.notNull().default(sql\`sysdatetime()\`)`;
|
|
103
|
-
const tableBlock = authOption === 'abs'
|
|
72
|
+
const jsonDefault = cfg.jsonDefault ?? '.default({})';
|
|
73
|
+
const usesSqlDefault = [
|
|
74
|
+
cfg.timestampDefault,
|
|
75
|
+
usesAuth ? jsonDefault : ''
|
|
76
|
+
].some((expression) => expression.includes('sql`'));
|
|
77
|
+
const imports = [
|
|
78
|
+
usesSqlDefault ? "import { sql } from 'drizzle-orm';" : '',
|
|
79
|
+
usesAuth
|
|
80
|
+
? "import type { UserIdentity } from '../src/types/userIdentity';"
|
|
81
|
+
: '',
|
|
82
|
+
`import { ${uniqueBuilders.join(', ')} } from 'drizzle-orm/${cfg.pkg}';`
|
|
83
|
+
]
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.join('\n');
|
|
86
|
+
const timestampColumn = `${cfg.time}.notNull()${cfg.timestampDefault}`;
|
|
87
|
+
const tableBlock = usesAuth
|
|
104
88
|
? `export const users = ${cfg.table}('users', {
|
|
105
89
|
auth_sub: ${cfg.string}.primaryKey(),
|
|
106
90
|
created_at: ${timestampColumn},
|
|
107
|
-
metadata: ${cfg.json}.$type<UserIdentity>()
|
|
91
|
+
metadata: ${cfg.json}.$type<UserIdentity>()${jsonDefault}
|
|
108
92
|
});`
|
|
109
93
|
: `export const countHistory = ${cfg.table}('count_history', {
|
|
110
|
-
uid: ${
|
|
111
|
-
count: ${
|
|
94
|
+
uid: ${cfg.uid},
|
|
95
|
+
count: ${cfg.int}('count').notNull(),
|
|
112
96
|
created_at: ${timestampColumn}
|
|
113
97
|
});`;
|
|
114
|
-
const schemaKey =
|
|
98
|
+
const schemaKey = usesAuth ? 'users' : 'countHistory';
|
|
115
99
|
return `
|
|
116
|
-
${
|
|
100
|
+
${imports}
|
|
117
101
|
|
|
118
|
-
${
|
|
102
|
+
${tableBlock}
|
|
119
103
|
|
|
120
104
|
export const schema = {
|
|
121
105
|
${schemaKey}
|
|
@@ -5,12 +5,12 @@ type QueryOperations = {
|
|
|
5
5
|
insertHistory: string;
|
|
6
6
|
};
|
|
7
7
|
declare const driverConfigurations: {
|
|
8
|
-
readonly 'cockroachdb:
|
|
9
|
-
readonly importLines: "";
|
|
8
|
+
readonly 'cockroachdb:drizzle:local': {
|
|
9
|
+
readonly importLines: "import { eq } from 'drizzle-orm'\nimport { schema } from '../../../db/schema'";
|
|
10
10
|
readonly queries: QueryOperations;
|
|
11
11
|
};
|
|
12
|
-
readonly '
|
|
13
|
-
readonly importLines: "
|
|
12
|
+
readonly 'cockroachdb:sql:local': {
|
|
13
|
+
readonly importLines: "";
|
|
14
14
|
readonly queries: QueryOperations;
|
|
15
15
|
};
|
|
16
16
|
readonly 'gel:sql:local': {
|