@bitcobblers/wod-wiki-storage 0.43.2150 → 0.43.2152

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.
@@ -7,6 +7,35 @@ import '@bitcobblers/wod-wiki-core';
7
7
  import '@bitcobblers/wod-wiki-lang';
8
8
  import '@libsql/client';
9
9
 
10
+ /**
11
+ * Local database catalog — enumerate/delete the browser-local IndexedDB
12
+ * databases behind the active-profile selector. The default app database is
13
+ * never created or destroyed here: the catalog only manages the auxiliary
14
+ * (restore/scratch) databases that the database switcher offers.
15
+ *
16
+ * This module deliberately does NOT import the storage adapter: the adapter
17
+ * lives in ./index, which re-exports this catalog, so an import back would
18
+ * close a module cycle. Creating a database (which needs the adapter's
19
+ * schema upgrade path) therefore lives beside the adapter in ./index.
20
+ */
21
+ interface LocalDatabaseInfo {
22
+ name: string;
23
+ version: number | null;
24
+ }
25
+ /** Canonical app-database name ("wodwiki-db"). Defined HERE, not imported
26
+ * from ./index, because the re-export cycle would read it before index.ts
27
+ * initialized it (index.ts: export const IDB_DB_NAME = DEFAULT_LOCAL_DATABASE_NAME). */
28
+ declare const DEFAULT_LOCAL_DATABASE_NAME: string;
29
+ /** Same rule as the app-side isValidLocalDbName — one name vocabulary
30
+ * everywhere (restore, catalog, settings persistence). */
31
+ declare function isValidLocalDatabaseName(name: unknown): name is string;
32
+ /** Native enumeration when the browser exposes it; [] otherwise (old
33
+ * Safari/Firefox lack databases()) — never throws. */
34
+ declare function listLocalDatabases(): Promise<LocalDatabaseInfo[]>;
35
+ /** Delete one named database only; a blocked delete resolves (an open
36
+ * connection elsewhere wins — the scratch data is disposable). */
37
+ declare function deleteLocalDatabase(name: string): Promise<void>;
38
+
10
39
  /**
11
40
  * IndexedDB adapter — the browser data plane (moved verbatim from the
12
41
  * playground app; the shared user policy and the schema32 sync cutover live
@@ -20,7 +49,17 @@ import '@libsql/client';
20
49
  * bootstrap runs (durable retry identities), all per backup target.
21
50
  */
22
51
 
23
- declare const IDB_DB_NAME = "wodwiki-db";
52
+ declare const IDB_DB_NAME: string;
53
+
54
+ /** Create a named database at the current schema (DB_VERSION) by opening it
55
+ * through the regular adapter, then close. Refuses the default database —
56
+ * the catalog never touches the live app profile.
57
+ *
58
+ * Lives beside the adapter (not in ./catalog) because it needs the
59
+ * adapter's schema upgrade path, and the adapter's module re-exports the
60
+ * catalog — importing the adapter back from the catalog would close a
61
+ * module cycle. */
62
+ declare function createLocalDatabase(name: string): Promise<LocalDatabaseInfo>;
24
63
  type Mode = 'readonly' | 'readwrite';
25
64
  /** Native tx handle; write methods are guarded at runtime by `mutable`, so
26
65
  * the handle is typed readwrite to keep put/delete/clear defined. */
@@ -153,4 +192,4 @@ declare class IndexedDBStorage implements IStorage {
153
192
  */
154
193
  declare function convertSyncSource(db: Database, fromVersion: number, legacy: boolean): Promise<SyncMutation[]>;
155
194
 
156
- export { IDB_DB_NAME, IndexedDBStorage, convertSyncSource, subscribeOutboxWrites };
195
+ export { DEFAULT_LOCAL_DATABASE_NAME, IDB_DB_NAME, IndexedDBStorage, type LocalDatabaseInfo, convertSyncSource, createLocalDatabase, deleteLocalDatabase, isValidLocalDatabaseName, listLocalDatabases, subscribeOutboxWrites };
@@ -7,6 +7,35 @@ import '@bitcobblers/wod-wiki-core';
7
7
  import '@bitcobblers/wod-wiki-lang';
8
8
  import '@libsql/client';
9
9
 
10
+ /**
11
+ * Local database catalog — enumerate/delete the browser-local IndexedDB
12
+ * databases behind the active-profile selector. The default app database is
13
+ * never created or destroyed here: the catalog only manages the auxiliary
14
+ * (restore/scratch) databases that the database switcher offers.
15
+ *
16
+ * This module deliberately does NOT import the storage adapter: the adapter
17
+ * lives in ./index, which re-exports this catalog, so an import back would
18
+ * close a module cycle. Creating a database (which needs the adapter's
19
+ * schema upgrade path) therefore lives beside the adapter in ./index.
20
+ */
21
+ interface LocalDatabaseInfo {
22
+ name: string;
23
+ version: number | null;
24
+ }
25
+ /** Canonical app-database name ("wodwiki-db"). Defined HERE, not imported
26
+ * from ./index, because the re-export cycle would read it before index.ts
27
+ * initialized it (index.ts: export const IDB_DB_NAME = DEFAULT_LOCAL_DATABASE_NAME). */
28
+ declare const DEFAULT_LOCAL_DATABASE_NAME: string;
29
+ /** Same rule as the app-side isValidLocalDbName — one name vocabulary
30
+ * everywhere (restore, catalog, settings persistence). */
31
+ declare function isValidLocalDatabaseName(name: unknown): name is string;
32
+ /** Native enumeration when the browser exposes it; [] otherwise (old
33
+ * Safari/Firefox lack databases()) — never throws. */
34
+ declare function listLocalDatabases(): Promise<LocalDatabaseInfo[]>;
35
+ /** Delete one named database only; a blocked delete resolves (an open
36
+ * connection elsewhere wins — the scratch data is disposable). */
37
+ declare function deleteLocalDatabase(name: string): Promise<void>;
38
+
10
39
  /**
11
40
  * IndexedDB adapter — the browser data plane (moved verbatim from the
12
41
  * playground app; the shared user policy and the schema32 sync cutover live
@@ -20,7 +49,17 @@ import '@libsql/client';
20
49
  * bootstrap runs (durable retry identities), all per backup target.
21
50
  */
22
51
 
23
- declare const IDB_DB_NAME = "wodwiki-db";
52
+ declare const IDB_DB_NAME: string;
53
+
54
+ /** Create a named database at the current schema (DB_VERSION) by opening it
55
+ * through the regular adapter, then close. Refuses the default database —
56
+ * the catalog never touches the live app profile.
57
+ *
58
+ * Lives beside the adapter (not in ./catalog) because it needs the
59
+ * adapter's schema upgrade path, and the adapter's module re-exports the
60
+ * catalog — importing the adapter back from the catalog would close a
61
+ * module cycle. */
62
+ declare function createLocalDatabase(name: string): Promise<LocalDatabaseInfo>;
24
63
  type Mode = 'readonly' | 'readwrite';
25
64
  /** Native tx handle; write methods are guarded at runtime by `mutable`, so
26
65
  * the handle is typed readwrite to keep put/delete/clear defined. */
@@ -153,4 +192,4 @@ declare class IndexedDBStorage implements IStorage {
153
192
  */
154
193
  declare function convertSyncSource(db: Database, fromVersion: number, legacy: boolean): Promise<SyncMutation[]>;
155
194
 
156
- export { IDB_DB_NAME, IndexedDBStorage, convertSyncSource, subscribeOutboxWrites };
195
+ export { DEFAULT_LOCAL_DATABASE_NAME, IDB_DB_NAME, IndexedDBStorage, type LocalDatabaseInfo, convertSyncSource, createLocalDatabase, deleteLocalDatabase, isValidLocalDatabaseName, listLocalDatabases, subscribeOutboxWrites };
@@ -6,7 +6,42 @@ import { openDB } from 'idb';
6
6
  import { parseDocumentSections, KNOWN_SECTION_WIDGETS, actionableSectionKind, matchDialectFence } from '@bitcobblers/wod-wiki-core';
7
7
  import { toEventRows, toSummaryEventRows } from '@bitcobblers/wod-wiki-wql';
8
8
 
9
- var IDB_DB_NAME = "wodwiki-db";
9
+ // src/idb/catalog.ts
10
+ var DEFAULT_LOCAL_DATABASE_NAME = "wodwiki-db";
11
+ function isValidLocalDatabaseName(name) {
12
+ return typeof name === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/.test(name);
13
+ }
14
+ async function listLocalDatabases() {
15
+ const idb = globalThis.indexedDB;
16
+ if (!idb || typeof idb.databases !== "function") return [];
17
+ const entries = await idb.databases().catch(() => []);
18
+ return entries.flatMap(
19
+ (entry) => typeof entry.name === "string" ? [{ name: entry.name, version: typeof entry.version === "number" ? entry.version : null }] : []
20
+ );
21
+ }
22
+ async function deleteLocalDatabase(name) {
23
+ if (!isValidLocalDatabaseName(name)) throw new Error(`Invalid local database name "${String(name)}".`);
24
+ const idb = globalThis.indexedDB;
25
+ if (!idb) return;
26
+ await new Promise((resolve, reject) => {
27
+ const request = idb.deleteDatabase(name);
28
+ request.onsuccess = () => resolve();
29
+ request.onerror = () => reject(request.error ?? new Error(`Failed to delete local database "${name}".`));
30
+ request.onblocked = () => resolve();
31
+ });
32
+ }
33
+ var IDB_DB_NAME = DEFAULT_LOCAL_DATABASE_NAME;
34
+ async function createLocalDatabase(name) {
35
+ if (!isValidLocalDatabaseName(name)) throw new Error(`Invalid local database name "${String(name)}".`);
36
+ if (name === DEFAULT_LOCAL_DATABASE_NAME) throw new Error("The default database cannot be created here.");
37
+ const storage = new IndexedDBStorage({ dbName: name });
38
+ try {
39
+ await storage.open();
40
+ } finally {
41
+ await storage.close().catch(() => void 0);
42
+ }
43
+ return { name, version: DB_VERSION };
44
+ }
10
45
  var OUTBOX_STORE = "sync_outbox";
11
46
  var SYNC_STATE_STORE = "sync_state";
12
47
  var LOCAL_SYNC_STORES = [
@@ -1074,6 +1109,6 @@ async function migrateSegmentsToBlocks(db, tx) {
1074
1109
  db.deleteObjectStore("segments");
1075
1110
  }
1076
1111
 
1077
- export { IDB_DB_NAME, IndexedDBStorage, convertSyncSource, subscribeOutboxWrites };
1112
+ export { DEFAULT_LOCAL_DATABASE_NAME, IDB_DB_NAME, IndexedDBStorage, convertSyncSource, createLocalDatabase, deleteLocalDatabase, isValidLocalDatabaseName, listLocalDatabases, subscribeOutboxWrites };
1078
1113
  //# sourceMappingURL=index.mjs.map
1079
1114
  //# sourceMappingURL=index.mjs.map