@axium/server 0.36.6 → 0.37.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/database.js CHANGED
@@ -52,36 +52,26 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
52
52
  });
53
53
  import * as io from '@axium/core/node/io';
54
54
  import { plugins } from '@axium/core/plugins';
55
- import { Kysely, PostgresDialect, sql } from 'kysely';
55
+ import { sql } from 'kysely';
56
56
  import { jsonObjectFrom } from 'kysely/helpers/postgres';
57
57
  import { randomBytes } from 'node:crypto';
58
58
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
59
- import { join } from 'node:path/posix';
59
+ import { join } from 'node:path';
60
60
  import { styleText } from 'node:util';
61
61
  import pg from 'pg';
62
62
  import * as z from 'zod';
63
63
  import config from './config.js';
64
- import rawSchema from './db.json' with { type: 'json' };
65
64
  import { dirs, systemDir } from './io.js';
65
+ import { connect, database } from './db/connection.js';
66
+ export { connect, database };
67
+ import * as schema from './db/schema.js';
68
+ export * as delta from './db/delta.js';
69
+ export * as schema from './db/schema.js';
66
70
  pg.types.setTypeParser(pg.types.builtins.INT8, BigInt);
67
71
  // @ts-expect-error 2339
68
72
  BigInt.prototype.toJSON = function () {
69
73
  return Number(this);
70
74
  };
71
- const sym = Symbol.for('Axium:database');
72
- export let database;
73
- export function connect() {
74
- if (database)
75
- return database;
76
- if (globalThis[sym])
77
- return (database = globalThis[sym]);
78
- database = new Kysely({
79
- dialect: new PostgresDialect({ pool: new pg.Pool(config.db) }),
80
- });
81
- globalThis[sym] = database;
82
- io.debug('Connected to database!');
83
- return database;
84
- }
85
75
  // Helpers
86
76
  export async function count(...tables) {
87
77
  return await database
@@ -235,187 +225,6 @@ export async function init(opt) {
235
225
  await result_1;
236
226
  }
237
227
  }
238
- const numberTypes = [
239
- 'integer',
240
- 'int2',
241
- 'int4',
242
- 'int8',
243
- 'smallint',
244
- 'real',
245
- 'double precision',
246
- 'float4',
247
- 'float8',
248
- 'decimal',
249
- 'numeric',
250
- 'serial',
251
- ];
252
- const bigintTypes = ['bigint', 'bigserial'];
253
- const booleanTypes = ['boolean', 'bool'];
254
- const stringTypes = ['varchar', 'char', 'text'];
255
- const dateTypes = ['date', 'datetime', 'time', 'timetz', 'timestamp', 'timestamptz'];
256
- const binaryTypes = ['binary', 'bytea', 'varbinary', 'blob'];
257
- const numericRangeTypes = ['int4range', 'numrange'];
258
- const stringRangeTypes = ['tsrange', 'tstzrange', 'daterange'];
259
- const multirangeTypes = ['int4multirange', 'int8multirange', 'nummultirange', 'tsmultirange', 'tstzmultirange', 'datemultirange'];
260
- const _primitive = z.literal([
261
- ...numberTypes,
262
- ...bigintTypes,
263
- ...booleanTypes,
264
- ...stringTypes,
265
- ...dateTypes,
266
- ...binaryTypes,
267
- ...numericRangeTypes,
268
- ...stringRangeTypes,
269
- ...multirangeTypes,
270
- 'uuid',
271
- 'json',
272
- 'jsonb',
273
- ]);
274
- const _ColumnType = z.union([
275
- _primitive,
276
- z.templateLiteral([
277
- z.literal(['char', 'varchar', 'binary', 'varbinary', 'datetime', 'time', 'timetz', 'timestamp', 'timestamptz']),
278
- '(',
279
- z.int().nonnegative(),
280
- ')',
281
- ]),
282
- z.templateLiteral([z.literal(['decimal', 'numeric']), '(', z.int().nonnegative(), z.literal([',', ', ']), z.int().nonnegative(), ')']),
283
- ]);
284
- const ColumnType = z.union([_ColumnType, z.templateLiteral([_ColumnType, '[', z.int().nonnegative().optional(), ']'])]);
285
- export const Column = z.strictObject({
286
- type: ColumnType,
287
- required: z.boolean().default(false),
288
- unique: z.boolean().default(false),
289
- primary: z.boolean().default(false),
290
- references: z.string().optional(),
291
- onDelete: z.enum(['cascade', 'restrict', 'no action', 'set null', 'set default']).optional(),
292
- default: z.any().optional(),
293
- check: z.string().optional(),
294
- });
295
- export const Constraint = z.discriminatedUnion('type', [
296
- z.strictObject({
297
- type: z.literal('primary_key'),
298
- on: z.string().array(),
299
- }),
300
- z.strictObject({
301
- type: z.literal('foreign_key'),
302
- on: z.string().array(),
303
- target: z.string(),
304
- references: z.string().array(),
305
- }),
306
- z.strictObject({
307
- type: z.literal('unique'),
308
- on: z.string().array(),
309
- nulls_not_distinct: z.boolean().optional(),
310
- }),
311
- z.strictObject({
312
- type: z.literal('check'),
313
- check: z.string(),
314
- }),
315
- ]);
316
- export const Table = z.strictObject({
317
- columns: z.record(z.string(), Column),
318
- constraints: z.record(z.string(), Constraint).optional().default({}),
319
- });
320
- export const IndexString = z.templateLiteral([z.string(), ':', z.string()]);
321
- export function parseIndex(value) {
322
- const [table, column] = value.split(':');
323
- return { table, column };
324
- }
325
- export const SchemaDecl = z.strictObject({
326
- tables: z.record(z.string(), Table),
327
- indexes: IndexString.array().optional().default([]),
328
- });
329
- export const ColumnDelta = z.strictObject({
330
- type: ColumnType.optional(),
331
- default: z.string().optional(),
332
- ops: z.literal(['drop_default', 'set_required', 'drop_required']).array().optional(),
333
- });
334
- export const TableDelta = z.strictObject({
335
- add_columns: z.record(z.string(), Column).optional().default({}),
336
- drop_columns: z.string().array().optional().default([]),
337
- alter_columns: z.record(z.string(), ColumnDelta).optional().default({}),
338
- add_constraints: z.record(z.string(), Constraint).optional().default({}),
339
- drop_constraints: z.string().array().optional().default([]),
340
- });
341
- export const VersionDelta = z.strictObject({
342
- delta: z.literal(true),
343
- add_tables: z.record(z.string(), Table).optional().default({}),
344
- drop_tables: z.string().array().optional().default([]),
345
- alter_tables: z.record(z.string(), TableDelta).optional().default({}),
346
- add_indexes: IndexString.array().optional().default([]),
347
- drop_indexes: IndexString.array().optional().default([]),
348
- });
349
- export const SchemaFile = z.object({
350
- format: z.literal(0),
351
- versions: z.discriminatedUnion('delta', [SchemaDecl.extend({ delta: z.literal(false) }), VersionDelta]).array(),
352
- /** List of tables to wipe */
353
- wipe: z.string().array().optional().default([]),
354
- /** Set the latest version, defaults to the last one */
355
- latest: z.int32().nonnegative().optional(),
356
- /** Maps tables to their ACL tables, e.g. `"storage": "acl.storage"` */
357
- acl_tables: z.record(z.string(), z.string()).optional().default({}),
358
- });
359
- const { data, error } = SchemaFile.safeParse(rawSchema);
360
- if (error)
361
- io.error('Invalid base database schema:\n' + z.prettifyError(error));
362
- const schema = data;
363
- export function* getSchemaFiles() {
364
- yield ['@axium/server', schema];
365
- for (const [name, plugin] of plugins) {
366
- if (!plugin._db)
367
- continue;
368
- try {
369
- yield [name, SchemaFile.parse(plugin._db)];
370
- }
371
- catch (e) {
372
- throw `Invalid database configuration for plugin "${name}":\n${io.errorText(e)}`;
373
- }
374
- }
375
- }
376
- /**
377
- * Get the active schema
378
- */
379
- export function getFullSchema(opt = {}) {
380
- const fullSchema = { tables: {}, indexes: [], versions: {} };
381
- for (const [pluginName, file] of getSchemaFiles()) {
382
- if (opt.exclude?.includes(pluginName))
383
- continue;
384
- file.latest ??= file.versions.length - 1;
385
- let currentSchema = { tables: {}, indexes: [] };
386
- fullSchema.versions[pluginName] = file.latest;
387
- for (const [version, schema] of file.versions.entries()) {
388
- if (!schema.delta)
389
- currentSchema = schema;
390
- else {
391
- try {
392
- applyDeltaToSchema(currentSchema, schema);
393
- }
394
- catch (e) {
395
- throw `Failed to apply version ${version - 1}->${version} delta to ${pluginName}: ${io.errorText(e)}`;
396
- }
397
- }
398
- if (version === file.latest)
399
- break;
400
- }
401
- for (const name of Object.keys(currentSchema.tables)) {
402
- if (name in fullSchema.tables)
403
- throw 'Duplicate table name in database schema: ' + name;
404
- fullSchema.tables[name] = currentSchema.tables[name];
405
- }
406
- for (const index of currentSchema.indexes) {
407
- if (fullSchema.indexes.includes(index))
408
- throw 'Duplicate index in database schema: ' + index;
409
- fullSchema.indexes.push(index);
410
- }
411
- }
412
- return fullSchema;
413
- }
414
- const schemaToIntrospected = {
415
- boolean: 'bool',
416
- integer: 'int4',
417
- 'text[]': '_text',
418
- };
419
228
  const VersionMap = z.record(z.string(), z.int32().nonnegative());
420
229
  export const UpgradesInfo = z.object({
421
230
  current: VersionMap.default({}),
@@ -430,364 +239,6 @@ export function getUpgradeInfo() {
430
239
  export function setUpgradeInfo(info) {
431
240
  io.writeJSON(upgradesFilePath, info);
432
241
  }
433
- export function applyTableDeltaToSchema(table, delta) {
434
- for (const column of delta.drop_columns) {
435
- if (column in table.columns)
436
- delete table.columns[column];
437
- else
438
- throw `can't drop column ${column} because it does not exist`;
439
- }
440
- for (const [name, column] of Object.entries(delta.add_columns)) {
441
- if (name in table.columns)
442
- throw `can't add column ${name} because it already exists`;
443
- table.columns[name] = column;
444
- }
445
- for (const [name, columnDelta] of Object.entries(delta.alter_columns)) {
446
- const column = table.columns[name];
447
- if (!column)
448
- throw `can't modify column ${name} because it does not exist`;
449
- if (columnDelta.type)
450
- column.type = columnDelta.type;
451
- if (columnDelta.default)
452
- column.default = columnDelta.default;
453
- for (const op of columnDelta.ops || []) {
454
- switch (op) {
455
- case 'drop_default':
456
- delete column.default;
457
- break;
458
- case 'set_required':
459
- column.required = true;
460
- break;
461
- case 'drop_required':
462
- column.required = false;
463
- break;
464
- }
465
- }
466
- }
467
- for (const name of delta.drop_constraints) {
468
- if (table.constraints[name])
469
- delete table.constraints[name];
470
- else
471
- throw `can't drop constraint ${name} because it does not exist`;
472
- }
473
- for (const [name, constraint] of Object.entries(delta.add_constraints)) {
474
- if (table.constraints[name])
475
- throw `can't add constraint ${name} because it already exists`;
476
- table.constraints[name] = constraint;
477
- }
478
- }
479
- export function applyDeltaToSchema(schema, delta) {
480
- for (const tableName of delta.drop_tables) {
481
- if (tableName in schema.tables)
482
- delete schema.tables[tableName];
483
- else
484
- throw `can't drop table ${tableName} because it does not exist`;
485
- }
486
- for (const [tableName, table] of Object.entries(delta.add_tables)) {
487
- if (tableName in schema.tables)
488
- throw `can't add table ${tableName} because it already exists`;
489
- else
490
- schema.tables[tableName] = table;
491
- }
492
- for (const [tableName, tableDelta] of Object.entries(delta.alter_tables)) {
493
- if (tableName in schema.tables)
494
- applyTableDeltaToSchema(schema.tables[tableName], tableDelta);
495
- else
496
- throw `can't modify table ${tableName} because it does not exist`;
497
- }
498
- }
499
- export function validateDelta(delta) {
500
- const tableNames = [...Object.keys(delta.add_tables), ...Object.keys(delta.alter_tables), delta.drop_tables];
501
- const uniqueTables = new Set(tableNames);
502
- for (const table of uniqueTables) {
503
- tableNames.splice(tableNames.indexOf(table), 1);
504
- }
505
- if (tableNames.length) {
506
- throw `Duplicate table name(s): ${tableNames.join(', ')}`;
507
- }
508
- for (const [tableName, table] of Object.entries(delta.alter_tables)) {
509
- const columnNames = [...Object.keys(table.add_columns), ...table.drop_columns];
510
- const uniqueColumns = new Set(columnNames);
511
- for (const column of uniqueColumns) {
512
- columnNames.splice(columnNames.indexOf(column), 1);
513
- }
514
- if (columnNames.length) {
515
- throw `Duplicate column name(s) in table ${tableName}: ${columnNames.join(', ')}`;
516
- }
517
- }
518
- }
519
- export function computeDelta(from, to) {
520
- const fromTables = new Set(Object.keys(from.tables));
521
- const toTables = new Set(Object.keys(to.tables));
522
- const fromIndexes = new Set(from.indexes);
523
- const toIndexes = new Set(to.indexes);
524
- const add_tables = Object.fromEntries(toTables
525
- .difference(fromTables)
526
- .keys()
527
- .map(name => [name, to.tables[name]]));
528
- const alter_tables = {};
529
- for (const name of fromTables.intersection(toTables)) {
530
- const fromTable = from.tables[name], toTable = to.tables[name];
531
- const fromColumns = new Set(Object.keys(fromTable));
532
- const toColumns = new Set(Object.keys(toTable));
533
- const drop_columns = fromColumns.difference(toColumns);
534
- const add_columns = Object.fromEntries(toColumns
535
- .difference(fromColumns)
536
- .keys()
537
- .map(colName => [colName, toTable.columns[colName]]));
538
- const alter_columns = Object.fromEntries(toColumns
539
- .intersection(fromColumns)
540
- .keys()
541
- .map(name => {
542
- const fromCol = fromTable.columns[name], toCol = toTable.columns[name];
543
- const alter = { ops: [] };
544
- if ('default' in fromCol && !('default' in toCol))
545
- alter.ops.push('drop_default');
546
- else if (fromCol.default !== toCol.default)
547
- alter.default = toCol.default;
548
- if (fromCol.type != toCol.type)
549
- alter.type = toCol.type;
550
- if (fromCol.required != toCol.required)
551
- alter.ops.push(toCol.required ? 'set_required' : 'drop_required');
552
- return [name, alter];
553
- }));
554
- const fromConstraints = new Set(Object.keys(fromTable.constraints || {}));
555
- const toConstraints = new Set(Object.keys(toTable.constraints || {}));
556
- const drop_constraints = fromConstraints.difference(toConstraints);
557
- const add_constraints = Object.fromEntries(toConstraints
558
- .difference(fromConstraints)
559
- .keys()
560
- .map(constName => [constName, toTable.constraints[constName]]));
561
- alter_tables[name] = {
562
- add_columns,
563
- drop_columns: Array.from(drop_columns),
564
- alter_columns,
565
- add_constraints,
566
- drop_constraints: Array.from(drop_constraints),
567
- };
568
- }
569
- return {
570
- delta: true,
571
- add_tables,
572
- drop_tables: Array.from(fromTables.difference(toTables)),
573
- alter_tables,
574
- drop_indexes: Array.from(fromIndexes.difference(toIndexes)),
575
- add_indexes: Array.from(toIndexes.difference(fromIndexes)),
576
- };
577
- }
578
- export function collapseDeltas(deltas) {
579
- const add_tables = {}, drop_tables = [], alter_tables = {}, add_indexes = [], drop_indexes = [];
580
- for (const delta of deltas) {
581
- validateDelta(delta);
582
- for (const [name, table] of Object.entries(delta.alter_tables)) {
583
- if (name in add_tables) {
584
- applyTableDeltaToSchema(add_tables[name], table);
585
- }
586
- else if (name in alter_tables) {
587
- const existing = alter_tables[name];
588
- for (const [colName, column] of Object.entries(table.add_columns)) {
589
- existing.add_columns[colName] = column;
590
- }
591
- for (const colName of table.drop_columns) {
592
- if (colName in existing.add_columns)
593
- delete existing.add_columns[colName];
594
- else
595
- existing.drop_columns.push(colName);
596
- }
597
- }
598
- else
599
- alter_tables[name] = table;
600
- }
601
- for (const table of delta.drop_tables) {
602
- if (table in add_tables)
603
- delete add_tables[table];
604
- else
605
- drop_tables.push(table);
606
- }
607
- for (const [name, table] of Object.entries(delta.add_tables)) {
608
- if (drop_tables.includes(name))
609
- throw `Can't add and drop table "${name}" in the same change`;
610
- if (name in alter_tables)
611
- throw `Can't add and modify table "${name}" in the same change`;
612
- add_tables[name] = table;
613
- }
614
- for (const index of delta.add_indexes) {
615
- if (drop_indexes.includes(index))
616
- throw `Can't add and drop index "${index}" in the same change`;
617
- add_indexes.push(index);
618
- }
619
- for (const index of delta.drop_indexes) {
620
- if (add_indexes.includes(index))
621
- throw `Can't add and drop index "${index}" in the same change`;
622
- drop_indexes.push(index);
623
- }
624
- }
625
- return { delta: true, add_tables, drop_tables, alter_tables, add_indexes, drop_indexes };
626
- }
627
- export function deltaIsEmpty(delta) {
628
- return (!Object.keys(delta.add_tables).length &&
629
- !delta.drop_tables.length &&
630
- !Object.keys(delta.alter_tables).length &&
631
- !delta.add_indexes.length &&
632
- !delta.drop_indexes.length);
633
- }
634
- const deltaColors = {
635
- '+': 'green',
636
- '-': 'red',
637
- '*': 'white',
638
- };
639
- export function* displayDelta(delta) {
640
- const tables = [
641
- ...Object.keys(delta.add_tables).map(name => ({ op: '+', name })),
642
- ...Object.entries(delta.alter_tables).map(([name, changes]) => ({ op: '*', name, changes })),
643
- ...delta.drop_tables.map(name => ({ op: '-', name })),
644
- ];
645
- tables.sort((a, b) => a.name.localeCompare(b.name));
646
- for (const table of tables) {
647
- yield styleText(deltaColors[table.op], `${table.op} table ${table.name}`);
648
- if (table.op != '*')
649
- continue;
650
- const columns = [
651
- ...Object.keys(table.changes.add_columns).map(name => ({ op: '+', name })),
652
- ...table.changes.drop_columns.map(name => ({ op: '-', name })),
653
- ...Object.entries(table.changes.alter_columns).map(([name, changes]) => ({ op: '*', name, ...changes })),
654
- ];
655
- columns.sort((a, b) => a.name.localeCompare(b.name));
656
- for (const column of columns) {
657
- const columnChanges = column.op == '*'
658
- ? [...(column.ops ?? []), 'default' in column && 'set_default', 'type' in column && 'set_type']
659
- .filter((e) => !!e)
660
- .map(e => e.replaceAll('_', ' '))
661
- .join(', ')
662
- : null;
663
- yield '\t' +
664
- styleText(deltaColors[column.op], `${column.op} column ${column.name}${column.op != '*' ? '' : ': ' + columnChanges}`);
665
- }
666
- const constraints = [
667
- ...Object.keys(table.changes.add_constraints).map(name => ({ op: '+', name })),
668
- ...table.changes.drop_constraints.map(name => ({ op: '-', name })),
669
- ];
670
- for (const con of constraints) {
671
- yield '\t' + styleText(deltaColors[con.op], `${con.op} constraint ${con.name}`);
672
- }
673
- }
674
- const indexes = [
675
- ...delta.add_indexes.map(raw => ({ op: '+', ...parseIndex(raw) })),
676
- ...delta.drop_indexes.map(raw => ({ op: '-', ...parseIndex(raw) })),
677
- ];
678
- indexes.sort((a, b) => a.table.localeCompare(b.table) || a.column.localeCompare(b.column));
679
- for (const index of indexes) {
680
- yield styleText(deltaColors[index.op], `${index.op} index on ${index.table}.${index.column}`);
681
- }
682
- }
683
- function columnFromSchema(column, allowPK) {
684
- return function _addColumn(col) {
685
- if (column.primary && allowPK)
686
- col = col.primaryKey();
687
- if (column.unique)
688
- col = col.unique();
689
- if (column.required)
690
- col = col.notNull();
691
- else if (column.unique)
692
- col = col.nullsNotDistinct();
693
- if (column.references)
694
- col = col.references(column.references);
695
- if (column.onDelete)
696
- col = col.onDelete(column.onDelete);
697
- if ('default' in column)
698
- col = col.defaultTo(sql.raw(String(column.default)));
699
- if (column.check)
700
- col = col.check(sql.raw(column.check));
701
- return col;
702
- };
703
- }
704
- export async function applyDelta(delta, forceAbort = false) {
705
- const tx = await database.startTransaction().execute();
706
- try {
707
- for (const [tableName, table] of Object.entries(delta.add_tables)) {
708
- io.start('Adding table ' + tableName);
709
- let query = tx.schema.createTable(tableName);
710
- const columns = Object.entries(table.columns);
711
- const pkColumns = columns.filter(([, column]) => column.primary).map(([name, column]) => ({ name, ...column }));
712
- const needsSpecialConstraint = pkColumns.length > 1 || pkColumns.some(col => !col.required);
713
- for (const [colName, column] of columns) {
714
- query = query.addColumn(colName, sql.raw(column.type), columnFromSchema(column, !needsSpecialConstraint));
715
- }
716
- if (needsSpecialConstraint) {
717
- query = query.addPrimaryKeyConstraint('PK_' + tableName.replaceAll('.', '_'), pkColumns.map(col => col.name));
718
- }
719
- await query.execute();
720
- io.done();
721
- }
722
- for (const tableName of delta.drop_tables) {
723
- io.start('Dropping table ' + tableName);
724
- await tx.schema.dropTable(tableName).execute();
725
- io.done();
726
- }
727
- for (const [tableName, tableDelta] of Object.entries(delta.alter_tables)) {
728
- io.start(`Modifying table ${tableName}`);
729
- const query = tx.schema.alterTable(tableName);
730
- for (const constraint of tableDelta.drop_constraints) {
731
- await query.dropConstraint(constraint).execute();
732
- }
733
- for (const colName of tableDelta.drop_columns) {
734
- await query.dropColumn(colName).execute();
735
- }
736
- for (const [colName, column] of Object.entries(tableDelta.add_columns)) {
737
- await query.addColumn(colName, sql.raw(column.type), columnFromSchema(column, false)).execute();
738
- }
739
- for (const [colName, column] of Object.entries(tableDelta.alter_columns)) {
740
- if (column.default)
741
- await query.alterColumn(colName, col => col.setDefault(sql.raw(String(column.default)))).execute();
742
- if (column.type)
743
- await query.alterColumn(colName, col => col.setDataType(sql.raw(column.type))).execute();
744
- for (const op of column.ops ?? []) {
745
- switch (op) {
746
- case 'drop_default':
747
- if (column.default)
748
- throw 'Cannot set and drop default at the same time';
749
- await query.alterColumn(colName, col => col.dropDefault()).execute();
750
- break;
751
- case 'set_required':
752
- await query.alterColumn(colName, col => col.setNotNull()).execute();
753
- break;
754
- case 'drop_required':
755
- await query.alterColumn(colName, col => col.dropNotNull()).execute();
756
- break;
757
- }
758
- }
759
- }
760
- for (const [name, con] of Object.entries(tableDelta.add_constraints)) {
761
- switch (con.type) {
762
- case 'unique':
763
- await query.addUniqueConstraint(name, con.on, b => (con.nulls_not_distinct ? b.nullsNotDistinct() : b)).execute();
764
- break;
765
- case 'check':
766
- await query.addCheckConstraint(name, sql.raw(con.check)).execute();
767
- break;
768
- case 'foreign_key':
769
- await query.addForeignKeyConstraint(name, con.on, con.target, con.references, b => b).execute();
770
- break;
771
- case 'primary_key':
772
- await query.addPrimaryKeyConstraint(name, con.on).execute();
773
- break;
774
- }
775
- }
776
- io.done();
777
- }
778
- if (forceAbort)
779
- throw 'Rolling back due to --abort';
780
- io.start('Committing');
781
- await tx.commit().execute();
782
- io.done();
783
- }
784
- catch (e) {
785
- await tx.rollback().execute();
786
- if (e instanceof SuppressedError)
787
- io.error(e.suppressed);
788
- throw e;
789
- }
790
- }
791
242
  /**
792
243
  * Checks that a table has the expected column types, nullability, and default values.
793
244
  */
@@ -802,7 +253,7 @@ export async function checkTableTypes(tableName, types, opt, tableMetadata) {
802
253
  for (const [i, [key, { type, required = false, default: _default }]] of _types.entries()) {
803
254
  io.progress(i, _types.length, key);
804
255
  const col = columns[key];
805
- const actualType = type in schemaToIntrospected ? schemaToIntrospected[type] : type;
256
+ const actualType = type in schema.toIntrospected ? schema.toIntrospected[type] : type;
806
257
  const hasDefault = _default !== undefined;
807
258
  try {
808
259
  if (!col)
@@ -0,0 +1,5 @@
1
+ import { Kysely } from 'kysely';
2
+ import type { Schema } from '../database.js';
3
+ export type Database = Kysely<Schema> & AsyncDisposable;
4
+ export declare let database: Database;
5
+ export declare function connect(): Database;
@@ -0,0 +1,18 @@
1
+ import * as io from '@axium/core/node/io';
2
+ import { Kysely, PostgresDialect } from 'kysely';
3
+ import pg from 'pg';
4
+ import config from '../config.js';
5
+ const sym = Symbol.for('Axium:database');
6
+ export let database;
7
+ export function connect() {
8
+ if (database)
9
+ return database;
10
+ if (globalThis[sym])
11
+ return (database = globalThis[sym]);
12
+ database = new Kysely({
13
+ dialect: new PostgresDialect({ pool: new pg.Pool(config.db) }),
14
+ });
15
+ globalThis[sym] = database;
16
+ io.debug('Connected to database!');
17
+ return database;
18
+ }