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.
Files changed (67) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/changelog.json +14 -0
  3. package/dist/commands/migrateDatabase.d.ts +6 -0
  4. package/dist/commands/migrateDatabase.js +53 -0
  5. package/dist/data.d.ts +1 -1
  6. package/dist/data.js +1 -0
  7. package/dist/generators/configurations/generateDrizzleConfig.d.ts +5 -3
  8. package/dist/generators/configurations/generateDrizzleConfig.js +40 -14
  9. package/dist/generators/configurations/generateEnv.js +6 -0
  10. package/dist/generators/configurations/generatePackageJson.js +30 -3
  11. package/dist/generators/db/drizzleTargets.d.ts +5 -0
  12. package/dist/generators/db/drizzleTargets.js +27 -0
  13. package/dist/generators/db/generateDatabaseTypes.js +10 -6
  14. package/dist/generators/db/generateDockerContainer.d.ts +5 -0
  15. package/dist/generators/db/generateDockerContainer.js +22 -4
  16. package/dist/generators/db/generateDrizzleSchema.js +65 -81
  17. package/dist/generators/db/handlerTemplates.d.ts +4 -4
  18. package/dist/generators/db/handlerTemplates.js +7 -10
  19. package/dist/generators/db/scaffoldDatabase.js +17 -4
  20. package/dist/generators/db/scaffoldDocker.d.ts +2 -1
  21. package/dist/generators/db/scaffoldDocker.js +7 -3
  22. package/dist/generators/project/generateDBBlock.js +11 -2
  23. package/dist/generators/project/generateImportsBlock.js +11 -6
  24. package/dist/generators/project/generateServer.js +2 -1
  25. package/dist/index.js +2 -1
  26. package/dist/messages.d.ts +2 -1
  27. package/dist/messages.js +2 -1
  28. package/dist/questions/authOption.js +4 -1
  29. package/dist/questions/databaseEngine.d.ts +1 -1
  30. package/dist/scaffold.d.ts +1 -0
  31. package/dist/scaffold.js +21 -3
  32. package/dist/templates/configurations/eslint.config.example.mjs +80 -41
  33. package/dist/templates/db/migrations/cockroach-count-history/20260926210314_init/migration.sql +5 -0
  34. package/dist/templates/db/migrations/cockroach-count-history/20260926210314_init/snapshot.json +69 -0
  35. package/dist/templates/db/migrations/cockroach-users/20260926210315_init/migration.sql +5 -0
  36. package/dist/templates/db/migrations/cockroach-users/20260926210315_init/snapshot.json +62 -0
  37. package/dist/templates/db/migrations/mssql-count-history/20260926210316_init/migration.sql +6 -0
  38. package/dist/templates/db/migrations/mssql-count-history/20260926210316_init/snapshot.json +64 -0
  39. package/dist/templates/db/migrations/mssql-users/20260926210317_init/migration.sql +6 -0
  40. package/dist/templates/db/migrations/mssql-users/20260926210317_init/snapshot.json +70 -0
  41. package/dist/templates/db/migrations/mysql-count-history/20260926210318_init/migration.sql +5 -0
  42. package/dist/templates/db/migrations/mysql-count-history/20260926210318_init/snapshot.json +61 -0
  43. package/dist/templates/db/migrations/mysql-users/20260926210319_init/migration.sql +5 -0
  44. package/dist/templates/db/migrations/mysql-users/20260926210319_init/snapshot.json +61 -0
  45. package/dist/templates/db/migrations/postgresql-count-history/20260926210320_init/migration.sql +5 -0
  46. package/dist/templates/db/migrations/postgresql-count-history/20260926210320_init/snapshot.json +71 -0
  47. package/dist/templates/db/migrations/postgresql-users/20260926210320_init/migration.sql +5 -0
  48. package/dist/templates/db/migrations/postgresql-users/20260926210320_init/snapshot.json +62 -0
  49. package/dist/templates/db/migrations/singlestore-count-history/20260926210321_init/migration.sql +5 -0
  50. package/dist/templates/db/migrations/singlestore-count-history/20260926210321_init/snapshot.json +61 -0
  51. package/dist/templates/db/migrations/singlestore-users/20260926210322_init/migration.sql +5 -0
  52. package/dist/templates/db/migrations/singlestore-users/20260926210322_init/snapshot.json +61 -0
  53. package/dist/templates/db/migrations/sqlite-count-history/20260926210323_init/migration.sql +5 -0
  54. package/dist/templates/db/migrations/sqlite-count-history/20260926210323_init/snapshot.json +50 -0
  55. package/dist/templates/db/migrations/sqlite-users/20260926210324_init/migration.sql +5 -0
  56. package/dist/templates/db/migrations/sqlite-users/20260926210324_init/snapshot.json +50 -0
  57. package/dist/templates/db/migrations/turso-count-history/20260926210324_init/migration.sql +5 -0
  58. package/dist/templates/db/migrations/turso-count-history/20260926210324_init/snapshot.json +50 -0
  59. package/dist/templates/db/migrations/turso-users/20260926210325_init/migration.sql +5 -0
  60. package/dist/templates/db/migrations/turso-users/20260926210325_init/snapshot.json +50 -0
  61. package/dist/templates/git/gitignore +0 -3
  62. package/dist/typeGuards.js +2 -2
  63. package/dist/utils/parseCommandLineOptions.js +16 -25
  64. package/dist/utils/validateDatabaseSelection.d.ts +11 -0
  65. package/dist/utils/validateDatabaseSelection.js +31 -0
  66. package/package.json +3 -1
  67. package/dist/templates/configurations/drizzle.config.ts +0 -13
@@ -269,14 +269,12 @@ const mysqlDrizzleQueryOperations = {
269
269
  .where(eq(schema.countHistory.uid, uid));
270
270
 
271
271
  return newHistory;`,
272
- insertUser: `const [row] = await db
273
- .insert(schema.users)
274
- .values({ auth_sub: authSub, metadata: userIdentity });
272
+ insertUser: `await db.insert(schema.users).values(newUserData);
275
273
 
276
274
  const [newUser] = await db
277
275
  .select()
278
276
  .from(schema.users)
279
- .where(eq(schema.users.auth_sub, authSub));
277
+ .where(eq(schema.users.auth_sub, newUserData.auth_sub));
280
278
 
281
279
  if (!newUser) throw new Error('Failed to create user');
282
280
  return newUser;`,
@@ -337,16 +335,15 @@ const mysqlPlanetScaleQueryOperations = {
337
335
  `
338
336
  };
339
337
  const driverConfigurations = {
338
+ 'cockroachdb:drizzle:local': {
339
+ importLines: `import { eq } from 'drizzle-orm'
340
+ import { schema } from '../../../db/schema'`,
341
+ queries: drizzleQueryOperations
342
+ },
340
343
  'cockroachdb:sql:local': {
341
344
  importLines: ``,
342
345
  queries: postgresSqlQueryOperations
343
346
  },
344
- 'gel:drizzle:local': {
345
- importLines: `import { eq } from 'drizzle-orm'
346
- import { schema } from '../../../db/schema'
347
- `,
348
- queries: drizzleQueryOperations
349
- },
350
347
  'gel:sql:local': {
351
348
  importLines: ``,
352
349
  queries: gelClientQueryOperations
@@ -1,9 +1,11 @@
1
- import { mkdirSync, writeFileSync } from 'fs';
2
- import { join } from 'path';
1
+ import { cpSync, mkdirSync, writeFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { fileURLToPath } from 'url';
3
4
  import { dim, yellow } from 'picocolors';
4
5
  import { isDrizzleDialect } from '../../typeGuards';
5
6
  import { checkSqliteInstalled } from '../../utils/checkSqliteInstalled';
6
7
  import { createDrizzleConfig } from '../configurations/generateDrizzleConfig';
8
+ import { getDrizzleKitDialect, getMigrationTemplateName } from './drizzleTargets';
7
9
  import { generateDatabaseTypes } from './generateDatabaseTypes';
8
10
  import { generateDrizzleSchema } from './generateDrizzleSchema';
9
11
  import { generateDBHandlers } from './generateHandlers';
@@ -50,7 +52,10 @@ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHo
50
52
  supportsRelationalSchema(databaseEngine)) {
51
53
  writeFileSync(join(projectDatabaseDirectory, 'schema.sql'), generateRelationalSchema(databaseEngine, authOption));
52
54
  }
53
- if (databaseEngine === 'sqlite') {
55
+ /* Drizzle projects get their tables from the committed initial migration
56
+ (`db:migrate`), never from hand-written DDL, so the migration history and
57
+ the database always agree. */
58
+ if (databaseEngine === 'sqlite' && orm !== 'drizzle') {
54
59
  // The sqlite3 CLI is a developer convenience (a db shell), never a
55
60
  // scaffold requirement — seeding goes through bun:sqlite below, which
56
61
  // ships with the runtime. The check only nudges interactive users;
@@ -72,6 +77,7 @@ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHo
72
77
  const result = await scaffoldDocker({
73
78
  authOption,
74
79
  databaseEngine,
80
+ initializeSchema: orm !== 'drizzle',
75
81
  projectDatabaseDirectory,
76
82
  projectName,
77
83
  verifyLocalDatabase
@@ -87,7 +93,14 @@ export const scaffoldDatabase = async ({ projectName, databaseEngine, databaseHo
87
93
  databaseEngine
88
94
  });
89
95
  writeFileSync(join(projectDatabaseDirectory, 'schema.ts'), drizzleSchema);
90
- createDrizzleConfig({ databaseDirectory, databaseEngine, projectName });
96
+ createDrizzleConfig({
97
+ databaseDirectory,
98
+ databaseEngine,
99
+ databaseHost,
100
+ projectName
101
+ });
102
+ const templatesDirectory = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'templates');
103
+ cpSync(join(templatesDirectory, 'db', 'migrations', getMigrationTemplateName(getDrizzleKitDialect(databaseEngine, databaseHost), usesAuth)), join(projectDatabaseDirectory, 'migrations'), { recursive: true });
91
104
  const drizzleTypes = generateDatabaseTypes({
92
105
  authOption,
93
106
  databaseEngine,
@@ -1,12 +1,13 @@
1
1
  import { AuthOption, DatabaseEngine } from '../../types';
2
2
  type ScaffoldDockerProps = {
3
+ initializeSchema?: boolean;
3
4
  verifyLocalDatabase?: boolean;
4
5
  databaseEngine: DatabaseEngine;
5
6
  projectDatabaseDirectory: string;
6
7
  authOption: AuthOption;
7
8
  projectName: string;
8
9
  };
9
- export declare const scaffoldDocker: ({ databaseEngine, projectDatabaseDirectory, projectName, authOption, verifyLocalDatabase }: ScaffoldDockerProps) => Promise<{
10
+ export declare const scaffoldDocker: ({ databaseEngine, projectDatabaseDirectory, projectName, authOption, initializeSchema, verifyLocalDatabase }: ScaffoldDockerProps) => Promise<{
10
11
  dockerFreshInstall: boolean;
11
12
  }>;
12
13
  export {};
@@ -5,7 +5,7 @@ import { $ } from 'bun';
5
5
  import { green, red } from 'picocolors';
6
6
  import { checkDockerInstalled, ensureDockerDaemonRunning, resolveDockerExe, shutdownDockerDaemon } from '../../utils/checkDockerInstalled';
7
7
  import { countHistoryTables, initTemplates, userTables } from './dockerInitTemplates';
8
- import { generateDockerContainer } from './generateDockerContainer';
8
+ import { generateDockerContainer, getDockerInitScript } from './generateDockerContainer';
9
9
  const initDockerSchema = async ({ authOption, databaseEngine, docker, projectName, spin }) => {
10
10
  const dbKey = databaseEngine;
11
11
  const { wait, cli } = initTemplates[dbKey];
@@ -33,7 +33,7 @@ const verifyDockerContainer = async ({ databaseEngine, docker, projectName, spin
33
33
  .cwd(projectName)
34
34
  .quiet();
35
35
  };
36
- export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory, projectName, authOption, verifyLocalDatabase = true }) => {
36
+ export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory, projectName, authOption, initializeSchema = true, verifyLocalDatabase = true }) => {
37
37
  if (databaseEngine === undefined ||
38
38
  databaseEngine === 'none' ||
39
39
  databaseEngine === 'sqlite') {
@@ -41,6 +41,10 @@ export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory,
41
41
  }
42
42
  const dbContainer = generateDockerContainer(databaseEngine);
43
43
  writeFileSync(join(projectDatabaseDirectory, 'docker-compose.db.yml'), dbContainer, 'utf-8');
44
+ const initScript = getDockerInitScript(databaseEngine);
45
+ if (initScript) {
46
+ writeFileSync(join(projectDatabaseDirectory, initScript.fileName), initScript.sql, 'utf-8');
47
+ }
44
48
  if (!verifyLocalDatabase)
45
49
  return { dockerFreshInstall: false };
46
50
  const { freshInstall } = await checkDockerInstalled(databaseEngine);
@@ -48,7 +52,7 @@ export const scaffoldDocker = async ({ databaseEngine, projectDatabaseDirectory,
48
52
  const docker = resolveDockerExe();
49
53
  const spin = spinner();
50
54
  spin.start(`Starting ${databaseEngine} container`);
51
- const dockerAction = databaseEngine in userTables
55
+ const dockerAction = initializeSchema && databaseEngine in userTables
52
56
  ? () => initDockerSchema({
53
57
  authOption,
54
58
  databaseEngine,
@@ -1,4 +1,7 @@
1
1
  import { availableDrizzleDialects } from '../../data';
2
+ /* A Turso database needs its auth token; a local libSQL `file:` URL has none,
3
+ so the token stays optional. */
4
+ const tursoClient = 'createClient({ authToken: env.DATABASE_AUTH_TOKEN, url: getEnv("DATABASE_URL") })';
2
5
  const connectionMap = {
3
6
  cockroachdb: {
4
7
  none: { expr: 'new SQL(getEnv("DATABASE_URL"))' }
@@ -36,13 +39,13 @@ const connectionMap = {
36
39
  },
37
40
  sqlite: {
38
41
  none: { expr: 'new Database("db/database.sqlite")' },
39
- turso: { expr: 'createClient({ url: getEnv("DATABASE_URL") })' }
42
+ turso: { expr: tursoClient }
40
43
  }
41
44
  };
42
45
  const remoteDrizzleInit = {
43
46
  neon: 'neon(getEnv("DATABASE_URL"));',
44
47
  planetscale: 'new Client({ url: getEnv("DATABASE_URL") })',
45
- turso: 'createClient({ url: getEnv("DATABASE_URL") })'
48
+ turso: tursoClient
46
49
  };
47
50
  const drizzleDialectSet = new Set([...availableDrizzleDialects]);
48
51
  export const generateDBBlock = ({ databaseEngine, orm, databaseHost }) => {
@@ -67,6 +70,12 @@ export const generateDBBlock = ({ databaseEngine, orm, databaseHost }) => {
67
70
  return `
68
71
  const pool = createPool(getEnv("DATABASE_URL"))
69
72
  const db = drizzle({ client: pool })
73
+ `;
74
+ }
75
+ if (databaseEngine === 'cockroachdb') {
76
+ return `
77
+ const pool = new Pool({ connectionString: getEnv("DATABASE_URL") })
78
+ const db = drizzle({ client: pool })
70
79
  `;
71
80
  }
72
81
  if (databaseEngine === 'mssql' && hostKey === 'none') {
@@ -42,7 +42,10 @@ export const generateImportsBlock = ({ deps, flags, orm, authOption, databaseEng
42
42
  const connectorImports = {
43
43
  neon: [`import { neon } from '@neondatabase/serverless'`],
44
44
  planetscale: [`import { Client } from '@planetscale/database'`],
45
- turso: [`import { createClient } from '@libsql/client'`]
45
+ turso: [
46
+ `import { createClient } from '@libsql/client'`,
47
+ `import { env } from 'bun'`
48
+ ]
46
49
  };
47
50
  const dialectImports = {
48
51
  neon: [`import { drizzle } from 'drizzle-orm/neon-http'`],
@@ -77,13 +80,15 @@ export const generateImportsBlock = ({ deps, flags, orm, authOption, databaseEng
77
80
  };
78
81
  const ormDatabaseImports = {
79
82
  drizzle: {
80
- gel: [
81
- `import { createClient } from 'gel'`,
82
- `import { drizzle } from 'drizzle-orm/gel'`
83
+ cockroachdb: [
84
+ `import { drizzle } from 'drizzle-orm/cockroach'`,
85
+ `import { Pool } from 'pg'`
83
86
  ],
87
+ /* drizzle-orm/mysql2 1.0 sets options on the pool's `config`, which
88
+ only mysql2's callback pool has; it wraps it in .promise() itself. */
84
89
  mariadb: [
85
90
  `import { drizzle } from 'drizzle-orm/mysql2'`,
86
- `import { createPool } from 'mysql2/promise'`
91
+ `import { createPool } from 'mysql2'`
87
92
  ],
88
93
  mssql: [
89
94
  `import { connect } from 'mssql'`,
@@ -92,7 +97,7 @@ export const generateImportsBlock = ({ deps, flags, orm, authOption, databaseEng
92
97
  mysql: !isRemoteHost
93
98
  ? [
94
99
  `import { drizzle } from 'drizzle-orm/mysql2'`,
95
- `import { createPool } from 'mysql2/promise'`
100
+ `import { createPool } from 'mysql2'`
96
101
  ]
97
102
  : [],
98
103
  postgresql: getPostgresqlOrmDatabaseImports(),
@@ -34,7 +34,8 @@ export const generateServerFile = ({ tailwind, authOption, plugins, buildDirecto
34
34
  const useBlock = deps
35
35
  .flatMap((dependency) => dependency.imports ?? [])
36
36
  .filter((pluginImport) => pluginImport.isPlugin &&
37
- pluginImport.packageName !== 'networking' && pluginImport.packageName !== 'openapi')
37
+ pluginImport.packageName !== 'networking' &&
38
+ pluginImport.packageName !== 'openapi')
38
39
  .map((pluginImport) => {
39
40
  if (pluginImport.packageName === 'auth') {
40
41
  return 'authPlugin';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ if (help === true) {
14
14
  exit(0);
15
15
  }
16
16
  const response = await prompt(argumentConfiguration);
17
- const { dockerFreshInstall } = await scaffold({
17
+ const { databaseMigrationPending, dockerFreshInstall } = await scaffold({
18
18
  envVariables,
19
19
  latest,
20
20
  packageManager,
@@ -27,6 +27,7 @@ const debugMessage = debug !== false
27
27
  })
28
28
  : '';
29
29
  const outroMessage = getOutroMessage({
30
+ databaseMigrationPending,
30
31
  installDependenciesNow: response.installDependenciesNow,
31
32
  packageManager,
32
33
  projectName: response.projectName
@@ -4,8 +4,9 @@ type OutroMessageProps = {
4
4
  projectName: string;
5
5
  packageManager: string;
6
6
  installDependenciesNow: boolean;
7
+ databaseMigrationPending: boolean;
7
8
  };
8
- export declare const getOutroMessage: ({ projectName, packageManager, installDependenciesNow }: OutroMessageProps) => string;
9
+ export declare const getOutroMessage: ({ databaseMigrationPending, projectName, packageManager, installDependenciesNow }: OutroMessageProps) => string;
9
10
  type DebugMessageProps = {
10
11
  response: CreateConfiguration;
11
12
  packageManager: string;
package/dist/messages.js CHANGED
@@ -46,9 +46,10 @@ Options:
46
46
  ${cyan('--vue')} Include a Vue frontend
47
47
  ${cyan('--vue-dir')} ${dim(cyan('<directory>'))} Specify the directory for and use the Vue frontend
48
48
  `;
49
- export const getOutroMessage = ({ projectName, packageManager, installDependenciesNow }) => `${green('Created successfully')}, you can now run:\n\n` +
49
+ export const getOutroMessage = ({ databaseMigrationPending, projectName, packageManager, installDependenciesNow }) => `${green('Created successfully')}, you can now run:\n\n` +
50
50
  `${cyan('cd')} ${projectName}\n` +
51
51
  `${installDependenciesNow ? '' : `${cyan(`${packageManager} install`)}\n`}` +
52
+ `${databaseMigrationPending ? `${cyan(`${packageManager} run db:migrate`)}\n` : ''}` +
52
53
  `${cyan(`${packageManager} dev`)}`; // TODO: Some package managers need run
53
54
  export const getDebugMessage = ({ response: { agentic, projectName, codeQualityTool, directoryConfig, useTailwind, tailwind, frontends, includeExamples, useHTMLScripts, frontendDirectories, buildDirectory, assetsDirectory, databaseEngine, databaseHost, databaseDirectory, orm, authOption, plugins, initializeGitNow, installDependenciesNow }, packageManager }) => {
54
55
  const htmlScriptingValue = useHTMLScripts
@@ -6,7 +6,10 @@ export const getAuthOption = async () => {
6
6
  message: 'Auth provider:',
7
7
  options: [
8
8
  { label: 'None', value: 'none' },
9
- { label: cyan('Absolute Auth (Google; requires a database)'), value: 'abs' }
9
+ {
10
+ label: cyan('Absolute Auth (Google; requires a database)'),
11
+ value: 'abs'
12
+ }
10
13
  ]
11
14
  });
12
15
  if (isCancel(authOption))
@@ -1 +1 @@
1
- export declare const getDatabaseEngine: () => Promise<"mariadb" | "mssql" | "mysql" | "postgresql" | "singlestore" | "sqlite" | "mongodb" | "cockroachdb" | "gel" | undefined>;
1
+ export declare const getDatabaseEngine: () => Promise<"cockroachdb" | "mariadb" | "mssql" | "mysql" | "postgresql" | "singlestore" | "sqlite" | "mongodb" | "gel" | undefined>;
@@ -7,6 +7,7 @@ type ScaffoldProps = {
7
7
  verifyLocalDatabase?: boolean;
8
8
  };
9
9
  export declare const scaffold: ({ response: { agentic, projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, verifyLocalDatabase, envVariables, packageManager }: ScaffoldProps) => Promise<{
10
+ databaseMigrationPending: boolean;
10
11
  dockerFreshInstall: boolean;
11
12
  }>;
12
13
  export {};
package/dist/scaffold.js CHANGED
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'url';
4
4
  import { formatProject } from './commands/formatProject';
5
5
  import { initializeGit } from './commands/initializeGit';
6
6
  import { installDependencies } from './commands/installDependencies';
7
+ import { migrateDatabase } from './commands/migrateDatabase';
7
8
  import { createPackageJson } from './generators/configurations/generatePackageJson';
8
9
  import { initalizeRoot } from './generators/configurations/initializeRoot';
9
10
  import { scaffoldConfigurationFiles } from './generators/configurations/scaffoldConfigurationFiles';
@@ -11,9 +12,15 @@ import { scaffoldDatabase } from './generators/db/scaffoldDatabase';
11
12
  import { scaffoldAgentic } from './generators/project/scaffoldAgentic';
12
13
  import { scaffoldBackend } from './generators/project/scaffoldBackend';
13
14
  import { scaffoldFrontends } from './generators/project/scaffoldFrontends';
15
+ import { validateDatabaseSelection } from './utils/validateDatabaseSelection';
14
16
  export const scaffold = async ({ response: { agentic, projectName, codeQualityTool, initializeGitNow, githubLink, githubRepoUrl, githubVisibility, databaseEngine, databaseHost, useHTMLScripts, useTailwind, databaseDirectory, absProviders, includeExamples, orm, frontends, plugins, authOption, buildDirectory, assetsDirectory, tailwind, installDependenciesNow, frontendDirectories }, latest, verifyLocalDatabase = true, envVariables, packageManager }) => {
15
- if (orm === 'drizzle' && databaseEngine === 'gel')
16
- throw new Error('Drizzle 1 no longer supports Gel. Choose no ORM for Gel.');
17
+ const { errors: databaseErrors } = validateDatabaseSelection({
18
+ databaseEngine,
19
+ databaseHost,
20
+ orm
21
+ });
22
+ if (databaseErrors.length > 0)
23
+ throw new Error(databaseErrors.join('\n'));
17
24
  if (orm === 'prisma')
18
25
  throw new Error('Prisma scaffolding is not implemented. Choose Drizzle or no ORM.');
19
26
  if (authOption === 'abs' && (!databaseEngine || databaseEngine === 'none'))
@@ -114,6 +121,14 @@ export const server = treaty<Api>(serverUrl)
114
121
  if (installDependenciesNow) {
115
122
  await installDependencies(packageManager, projectName);
116
123
  }
124
+ const databaseMigrated = orm === 'drizzle' &&
125
+ installDependenciesNow &&
126
+ (await migrateDatabase({
127
+ databaseEngine,
128
+ databaseHost,
129
+ projectName,
130
+ verifyLocalDatabase
131
+ }));
117
132
  await formatProject({
118
133
  installDependenciesNow,
119
134
  packageManager,
@@ -127,5 +142,8 @@ export const server = treaty<Api>(serverUrl)
127
142
  projectName
128
143
  });
129
144
  }
130
- return { dockerFreshInstall };
145
+ return {
146
+ databaseMigrationPending: orm === 'drizzle' && !databaseMigrated,
147
+ dockerFreshInstall
148
+ };
131
149
  };
@@ -1,48 +1,87 @@
1
-
2
1
  import { defineConfig } from 'eslint/config';
3
2
  import absolute from 'eslint-plugin-absolute';
4
3
  import reactHooks from 'eslint-plugin-react-hooks';
5
4
  import tseslint from 'typescript-eslint';
6
5
 
7
6
  export default defineConfig([
8
- { ignores: ['node_modules/**', 'build/**', 'dist/**', '.absolutejs/**', '.data/**', '**/.absolutejs-hmr-*', 'drizzle/**'] },
9
- { linterOptions: { noInlineConfig: true, reportUnusedDisableDirectives: 'error' } },
10
- ...tseslint.configs.recommended,
11
- {
12
- files: ['**/*.{ts,tsx,js,jsx,mjs}'],
13
- plugins: { absolute },
14
- rules: {
15
- '@typescript-eslint/ban-ts-comment': ['error', { 'ts-check': true, 'ts-expect-error': true, 'ts-ignore': true, 'ts-nocheck': true }],
16
- 'absolute/button-icon-is-hidden': 'error',
17
- 'absolute/elysia-composition-boundaries': 'error',
18
- 'absolute/icon-button-has-accessible-name': 'error',
19
- 'absolute/loading-indicator-has-aria-busy': 'error',
20
- 'absolute/no-chained-type-assertions': 'error',
21
- 'absolute/no-nondeterministic-render': 'error',
22
- 'absolute/no-unsafe-schema-types': 'error',
23
- 'absolute/prefer-drizzle-query-builders': 'error',
24
- 'absolute/progressbar-has-state': 'error',
25
- },
26
- },
27
- {
28
- files: ['src/backend/**/*.{ts,tsx}'],
29
- rules: { 'absolute/elysia-no-response-return': 'error' },
30
- },
31
- {
32
- files: ['src/frontend/**/*.{ts,tsx,js,jsx}'],
33
- rules: {
34
- 'absolute/eden-requires-react-query': 'error',
35
- 'no-restricted-syntax': ['error',
36
- { message: 'Use a directly typed Eden subapp client.', selector: "CallExpression[callee.name='fetch']" },
37
- { message: 'Use a directly typed Eden subapp client.', selector: "CallExpression[callee.property.name='fetch']" },
38
- ],
39
- },
40
- },
41
- { files: ['**/*.{html,vue,svelte,gjs,gts}'], plugins: { absolute }, processor: 'absolute/template-source' },
42
- {
43
- // React Hooks rules do not apply to Angular's unrelated usePageContext API.
44
- files: ['src/frontend/**/*.{tsx,jsx}', 'src/frontend/**/hooks/**/*.{ts,js}'],
45
- plugins: { 'react-hooks': reactHooks },
46
- rules: { 'react-hooks/exhaustive-deps': 'error', 'react-hooks/rules-of-hooks': 'error' },
47
- },
7
+ {
8
+ ignores: [
9
+ 'node_modules/**',
10
+ 'build/**',
11
+ 'dist/**',
12
+ '.absolutejs/**',
13
+ '.data/**',
14
+ '**/.absolutejs-hmr-*',
15
+ 'drizzle/**'
16
+ ]
17
+ },
18
+ {
19
+ linterOptions: {
20
+ noInlineConfig: true,
21
+ reportUnusedDisableDirectives: 'error'
22
+ }
23
+ },
24
+ ...tseslint.configs.recommended,
25
+ {
26
+ files: ['**/*.{ts,tsx,js,jsx,mjs}'],
27
+ plugins: { absolute },
28
+ rules: {
29
+ '@typescript-eslint/ban-ts-comment': [
30
+ 'error',
31
+ {
32
+ 'ts-check': true,
33
+ 'ts-expect-error': true,
34
+ 'ts-ignore': true,
35
+ 'ts-nocheck': true
36
+ }
37
+ ],
38
+ 'absolute/button-icon-is-hidden': 'error',
39
+ 'absolute/elysia-composition-boundaries': 'error',
40
+ 'absolute/icon-button-has-accessible-name': 'error',
41
+ 'absolute/loading-indicator-has-aria-busy': 'error',
42
+ 'absolute/no-chained-type-assertions': 'error',
43
+ 'absolute/no-nondeterministic-render': 'error',
44
+ 'absolute/no-unsafe-schema-types': 'error',
45
+ 'absolute/prefer-drizzle-query-builders': 'error',
46
+ 'absolute/progressbar-has-state': 'error'
47
+ }
48
+ },
49
+ {
50
+ files: ['src/backend/**/*.{ts,tsx}'],
51
+ rules: { 'absolute/elysia-no-response-return': 'error' }
52
+ },
53
+ {
54
+ files: ['src/frontend/**/*.{ts,tsx,js,jsx}'],
55
+ rules: {
56
+ 'absolute/eden-requires-react-query': 'error',
57
+ 'no-restricted-syntax': [
58
+ 'error',
59
+ {
60
+ message: 'Use a directly typed Eden subapp client.',
61
+ selector: "CallExpression[callee.name='fetch']"
62
+ },
63
+ {
64
+ message: 'Use a directly typed Eden subapp client.',
65
+ selector: "CallExpression[callee.property.name='fetch']"
66
+ }
67
+ ]
68
+ }
69
+ },
70
+ {
71
+ files: ['**/*.{html,vue,svelte,gjs,gts}'],
72
+ plugins: { absolute },
73
+ processor: 'absolute/template-source'
74
+ },
75
+ {
76
+ // React Hooks rules do not apply to Angular's unrelated usePageContext API.
77
+ files: [
78
+ 'src/frontend/**/*.{tsx,jsx}',
79
+ 'src/frontend/**/hooks/**/*.{ts,js}'
80
+ ],
81
+ plugins: { 'react-hooks': reactHooks },
82
+ rules: {
83
+ 'react-hooks/exhaustive-deps': 'error',
84
+ 'react-hooks/rules-of-hooks': 'error'
85
+ }
86
+ }
48
87
  ]);
@@ -0,0 +1,5 @@
1
+ CREATE TABLE "count_history" (
2
+ "uid" int4 PRIMARY KEY GENERATED ALWAYS AS IDENTITY (INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
3
+ "count" int4 NOT NULL,
4
+ "created_at" timestamp DEFAULT now() NOT NULL
5
+ );
@@ -0,0 +1,69 @@
1
+ {
2
+ "version": "1",
3
+ "dialect": "cockroach",
4
+ "id": "25a4568e-927c-4251-8f0c-145bd20ef6cf",
5
+ "prevIds": ["00000000-0000-0000-0000-000000000000"],
6
+ "ddl": [
7
+ {
8
+ "isRlsEnabled": false,
9
+ "name": "count_history",
10
+ "entityType": "tables",
11
+ "schema": "public"
12
+ },
13
+ {
14
+ "type": "int4",
15
+ "typeSchema": null,
16
+ "notNull": true,
17
+ "dimensions": 0,
18
+ "default": null,
19
+ "generated": null,
20
+ "identity": {
21
+ "type": "always",
22
+ "increment": "1",
23
+ "startWith": "1",
24
+ "minValue": "1",
25
+ "maxValue": "2147483647",
26
+ "cache": 1
27
+ },
28
+ "name": "uid",
29
+ "entityType": "columns",
30
+ "schema": "public",
31
+ "table": "count_history"
32
+ },
33
+ {
34
+ "type": "int4",
35
+ "typeSchema": null,
36
+ "notNull": true,
37
+ "dimensions": 0,
38
+ "default": null,
39
+ "generated": null,
40
+ "identity": null,
41
+ "name": "count",
42
+ "entityType": "columns",
43
+ "schema": "public",
44
+ "table": "count_history"
45
+ },
46
+ {
47
+ "type": "timestamp",
48
+ "typeSchema": null,
49
+ "notNull": true,
50
+ "dimensions": 0,
51
+ "default": "now()",
52
+ "generated": null,
53
+ "identity": null,
54
+ "name": "created_at",
55
+ "entityType": "columns",
56
+ "schema": "public",
57
+ "table": "count_history"
58
+ },
59
+ {
60
+ "columns": ["uid"],
61
+ "nameExplicit": false,
62
+ "name": "count_history_pkey",
63
+ "schema": "public",
64
+ "table": "count_history",
65
+ "entityType": "pks"
66
+ }
67
+ ],
68
+ "renames": []
69
+ }
@@ -0,0 +1,5 @@
1
+ CREATE TABLE "users" (
2
+ "auth_sub" varchar(255) PRIMARY KEY,
3
+ "created_at" timestamp DEFAULT now() NOT NULL,
4
+ "metadata" jsonb DEFAULT '{}'
5
+ );
@@ -0,0 +1,62 @@
1
+ {
2
+ "version": "1",
3
+ "dialect": "cockroach",
4
+ "id": "b7fed725-7fff-4c26-8562-5dbb3a0e1a7a",
5
+ "prevIds": ["00000000-0000-0000-0000-000000000000"],
6
+ "ddl": [
7
+ {
8
+ "isRlsEnabled": false,
9
+ "name": "users",
10
+ "entityType": "tables",
11
+ "schema": "public"
12
+ },
13
+ {
14
+ "type": "varchar(255)",
15
+ "typeSchema": null,
16
+ "notNull": true,
17
+ "dimensions": 0,
18
+ "default": null,
19
+ "generated": null,
20
+ "identity": null,
21
+ "name": "auth_sub",
22
+ "entityType": "columns",
23
+ "schema": "public",
24
+ "table": "users"
25
+ },
26
+ {
27
+ "type": "timestamp",
28
+ "typeSchema": null,
29
+ "notNull": true,
30
+ "dimensions": 0,
31
+ "default": "now()",
32
+ "generated": null,
33
+ "identity": null,
34
+ "name": "created_at",
35
+ "entityType": "columns",
36
+ "schema": "public",
37
+ "table": "users"
38
+ },
39
+ {
40
+ "type": "jsonb",
41
+ "typeSchema": null,
42
+ "notNull": false,
43
+ "dimensions": 0,
44
+ "default": "'{}'",
45
+ "generated": null,
46
+ "identity": null,
47
+ "name": "metadata",
48
+ "entityType": "columns",
49
+ "schema": "public",
50
+ "table": "users"
51
+ },
52
+ {
53
+ "columns": ["auth_sub"],
54
+ "nameExplicit": false,
55
+ "name": "users_pkey",
56
+ "schema": "public",
57
+ "table": "users",
58
+ "entityType": "pks"
59
+ }
60
+ ],
61
+ "renames": []
62
+ }
@@ -0,0 +1,6 @@
1
+ CREATE TABLE [count_history] (
2
+ [uid] int IDENTITY(1, 1),
3
+ [count] int NOT NULL,
4
+ [created_at] datetime2 NOT NULL CONSTRAINT [count_history_created_at_default] DEFAULT (sysdatetime()),
5
+ CONSTRAINT [count_history_pkey] PRIMARY KEY([uid])
6
+ );