@hozon/db 0.4.0 → 0.5.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/lib/db.js +12 -5
- package/lib/errors.d.ts +3 -0
- package/lib/errors.js +6 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/preflight.d.ts +2 -0
- package/lib/preflight.js +10 -5
- package/package.json +2 -2
package/lib/db.js
CHANGED
|
@@ -2,9 +2,9 @@ import { getLogger } from '@sozai/log';
|
|
|
2
2
|
import { Kysely, sql } from 'kysely';
|
|
3
3
|
import { Migrator } from 'kysely/migration';
|
|
4
4
|
import { withTransactionalDDL } from './dialect.js';
|
|
5
|
-
import { HozonDBClosedError, InvalidTablePrefixError, SavepointOverlapError, SavepointReleasedError } from './errors.js';
|
|
5
|
+
import { HozonDBClosedError, InvalidTablePrefixError, SavepointOverlapError, SavepointReleasedError, StoreAlreadyRegisteredError } from './errors.js';
|
|
6
6
|
import { LoaderRegistry } from './loader-registry.js';
|
|
7
|
-
import { checkStore } from './preflight.js';
|
|
7
|
+
import { checkStore, readAppliedMigrations } from './preflight.js';
|
|
8
8
|
import { bindSyncProvider, compareWrite, settleSyncProvider } from './sync.js';
|
|
9
9
|
import { TablePrefixPlugin } from './table-prefix.js';
|
|
10
10
|
// biome-ignore lint/complexity/noBannedTypes: The default adds no context fields.
|
|
@@ -60,9 +60,7 @@ export class HozonDB {
|
|
|
60
60
|
const existing = this.#stores.get(store.name);
|
|
61
61
|
if (existing != null) {
|
|
62
62
|
if (existing !== store) {
|
|
63
|
-
|
|
64
|
-
name: store.name
|
|
65
|
-
});
|
|
63
|
+
throw new StoreAlreadyRegisteredError(store.name);
|
|
66
64
|
}
|
|
67
65
|
return;
|
|
68
66
|
}
|
|
@@ -448,6 +446,15 @@ export class HozonDB {
|
|
|
448
446
|
if (Object.keys(migrations).length === 0) {
|
|
449
447
|
return;
|
|
450
448
|
}
|
|
449
|
+
// Kysely's migrator opens a transaction even when nothing is pending, which fails while
|
|
450
|
+
// another database instance holds the same single SQLite connection in a transaction.
|
|
451
|
+
const applied = await readAppliedMigrations(this.#db, this.#tablePrefix, name);
|
|
452
|
+
if (applied != null && Object.keys(migrations).every((key)=>applied.has(key))) {
|
|
453
|
+
this.#logger.debug('store {name} migrations already applied', {
|
|
454
|
+
name
|
|
455
|
+
});
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
451
458
|
const migrator = new Migrator({
|
|
452
459
|
db: this.#db,
|
|
453
460
|
provider: {
|
package/lib/errors.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ export declare class InvalidTablePrefixError extends Error {
|
|
|
16
16
|
export declare class HozonDBClosedError extends Error {
|
|
17
17
|
constructor();
|
|
18
18
|
}
|
|
19
|
+
export declare class StoreAlreadyRegisteredError extends Error {
|
|
20
|
+
constructor(store: string);
|
|
21
|
+
}
|
|
19
22
|
export declare class BatchLimitError extends Error {
|
|
20
23
|
constructor(size: number, limit: number);
|
|
21
24
|
}
|
package/lib/errors.js
CHANGED
|
@@ -34,6 +34,12 @@ export class HozonDBClosedError extends Error {
|
|
|
34
34
|
this.name = 'HozonDBClosedError';
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
export class StoreAlreadyRegisteredError extends Error {
|
|
38
|
+
constructor(store){
|
|
39
|
+
super(`Store "${store}" is already registered with a different definition`);
|
|
40
|
+
this.name = 'StoreAlreadyRegisteredError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
37
43
|
export class BatchLimitError extends Error {
|
|
38
44
|
constructor(size, limit){
|
|
39
45
|
super(`Batch of ${size} keys exceeds limit ${limit}`);
|
package/lib/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { assertBatchSize, type BatchReadOptions, type IterateBatchesOptions, ite
|
|
|
5
5
|
export { type BatchLoader, type BatchLoaderParams, createBatchLoader, } from './batch-loader.js';
|
|
6
6
|
export type { BaseStoreContext, ContextFactory, HozonDBParams, MigrationContext, StoreContext, StoreDefinition, StoreProvider, } from './db.js';
|
|
7
7
|
export { HozonDB } from './db.js';
|
|
8
|
-
export { BatchLimitError, HozonDBClosedError, InvalidTablePrefixError, KeyLockScopeError, SavepointOverlapError, SavepointReleasedError, SchemaVersionError, } from './errors.js';
|
|
8
|
+
export { BatchLimitError, HozonDBClosedError, InvalidTablePrefixError, KeyLockScopeError, SavepointOverlapError, SavepointReleasedError, SchemaVersionError, StoreAlreadyRegisteredError, } from './errors.js';
|
|
9
9
|
export { chunk, IN_LIST_CHUNK, withKeepSet } from './keep-set.js';
|
|
10
10
|
export { withStoreTransaction } from './store-transaction.js';
|
|
11
11
|
export { type CompareDefinition, compareText, withKeyLock, withKeyLocks } from './sync.js';
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { Kysely, sql } from 'kysely';
|
|
|
2
2
|
export { assertBatchSize, iterateBatches } from './batch.js';
|
|
3
3
|
export { createBatchLoader } from './batch-loader.js';
|
|
4
4
|
export { HozonDB } from './db.js';
|
|
5
|
-
export { BatchLimitError, HozonDBClosedError, InvalidTablePrefixError, KeyLockScopeError, SavepointOverlapError, SavepointReleasedError, SchemaVersionError } from './errors.js';
|
|
5
|
+
export { BatchLimitError, HozonDBClosedError, InvalidTablePrefixError, KeyLockScopeError, SavepointOverlapError, SavepointReleasedError, SchemaVersionError, StoreAlreadyRegisteredError } from './errors.js';
|
|
6
6
|
export { chunk, IN_LIST_CHUNK, withKeepSet } from './keep-set.js';
|
|
7
7
|
export { withStoreTransaction } from './store-transaction.js';
|
|
8
8
|
export { compareText, withKeyLock, withKeyLocks } from './sync.js';
|
package/lib/preflight.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { Kysely } from 'kysely';
|
|
2
|
+
/** Names of the migrations recorded for a store, or `null` when its migration table is absent. */
|
|
3
|
+
export declare function readAppliedMigrations<DB>(db: Kysely<DB>, prefix: string, name: string): Promise<Set<string> | null>;
|
|
2
4
|
export declare function checkStore<DB>(db: Kysely<DB>, prefix: string, name: string, keys: Array<string>): Promise<void>;
|
package/lib/preflight.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { sql } from 'kysely';
|
|
2
2
|
import { SchemaVersionError } from './errors.js';
|
|
3
|
-
export async function
|
|
4
|
-
let rows;
|
|
3
|
+
/** Names of the migrations recorded for a store, or `null` when its migration table is absent. */ export async function readAppliedMigrations(db, prefix, name) {
|
|
5
4
|
try {
|
|
6
5
|
const result = await sql`SELECT name FROM ${sql.table(`${prefix}_${name}_migration`)}`.execute(db);
|
|
7
|
-
|
|
6
|
+
return new Set(result.rows.map((row)=>row.name));
|
|
8
7
|
} catch (error) {
|
|
9
8
|
// Only a missing table is a fresh schema. Connection and permission errors must surface.
|
|
10
9
|
if (error instanceof Error && ('code' in error && error.code === '42P01' || /no such table:/i.test(error.message))) {
|
|
11
|
-
return;
|
|
10
|
+
return null;
|
|
12
11
|
}
|
|
13
12
|
throw error;
|
|
14
13
|
}
|
|
14
|
+
}
|
|
15
|
+
export async function checkStore(db, prefix, name, keys) {
|
|
16
|
+
const applied = await readAppliedMigrations(db, prefix, name);
|
|
17
|
+
if (applied == null) return;
|
|
15
18
|
const known = new Set(keys);
|
|
16
|
-
const unknown =
|
|
19
|
+
const unknown = [
|
|
20
|
+
...applied
|
|
21
|
+
].filter((key)=>!known.has(key)).sort();
|
|
17
22
|
if (unknown.length > 0) throw new SchemaVersionError(name, unknown);
|
|
18
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hozon/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Database registry, migrations and transactions for Hozon",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"lib/*"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@hozon/adapter": "^0.2.
|
|
20
|
+
"@hozon/adapter": "^0.2.1",
|
|
21
21
|
"@sozai/async": "^0.4.1",
|
|
22
22
|
"@sozai/log": "^0.4.0",
|
|
23
23
|
"kysely": "^0.29.7"
|