@wirecat/cli-core 0.432.0 → 0.434.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 (46) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cli/messenger/store-maintenance-command.js +5 -2
  3. package/dist/cli/upgrade-command.js +1 -1
  4. package/dist/entities/index.d.ts +10 -3
  5. package/dist/entities/store-maintenance.d.ts +10 -3
  6. package/dist/entities/store-maintenance.js +10 -1
  7. package/dist/errors/reasons.js +7 -0
  8. package/dist/models/anthropic.js +2 -4
  9. package/dist/store/drivers/bun-sqlite.d.ts +6 -0
  10. package/dist/store/drivers/bun-sqlite.js +16 -1
  11. package/dist/store/drivers/node-sqlite.d.ts +5 -1
  12. package/dist/store/drivers/node-sqlite.js +16 -1
  13. package/dist/store/exports.d.ts +1 -0
  14. package/dist/store/exports.js +1 -0
  15. package/dist/store/sqlite/document-pictures.js +4 -1
  16. package/dist/store/sqlite/drizzle/bun.js +58 -14
  17. package/dist/store/sqlite/drizzle/bun.js.map +3 -3
  18. package/dist/store/sqlite/drizzle/core.js +173 -7
  19. package/dist/store/sqlite/drizzle/core.js.map +4 -4
  20. package/dist/store/sqlite/drizzle/node.js +57 -13
  21. package/dist/store/sqlite/drizzle/node.js.map +3 -3
  22. package/dist/store/sqlite/drizzle/shared-55XJMTPA.js +6514 -0
  23. package/dist/store/sqlite/drizzle/shared-55XJMTPA.js.map +7 -0
  24. package/dist/store/sqlite/image-chunks.d.ts +1 -1
  25. package/dist/store/sqlite/image-chunks.js +46 -13
  26. package/dist/store/sqlite/manifest.js +2 -0
  27. package/dist/store/sqlite/message-chunks.d.ts +5 -2
  28. package/dist/store/sqlite/message-chunks.js +42 -13
  29. package/dist/store/sqlite/migrations.generated.js +22 -0
  30. package/dist/store/sqlite/open-store.js +4 -0
  31. package/dist/store/sqlite/open.js +5 -6
  32. package/dist/store/sqlite/schema/canonical-tags.d.ts +1 -0
  33. package/dist/store/sqlite/schema/document-index-pending.d.ts +1 -0
  34. package/dist/store/sqlite/schema/document-transfers.d.ts +1 -0
  35. package/dist/store/sqlite/schema/tag-consolidations.d.ts +2 -0
  36. package/dist/store/sqlite/schema.d.ts +214 -0
  37. package/dist/store/sqlite/schema.js +17 -0
  38. package/dist/store/sqlite/vector-index.d.ts +67 -0
  39. package/dist/store/sqlite/vector-index.js +180 -0
  40. package/dist/store/sqlite/vectors.d.ts +6 -3
  41. package/dist/store/sqlite/vectors.js +71 -31
  42. package/dist/store/vector-extension.d.ts +10 -0
  43. package/dist/store/vector-extension.js +53 -0
  44. package/package.json +24 -25
  45. package/dist/store/sqlite/drizzle/shared-HKXF6HKX.js +0 -4846
  46. package/dist/store/sqlite/drizzle/shared-HKXF6HKX.js.map +0 -7
@@ -46,7 +46,7 @@ export interface NearestImage {
46
46
  * The images of the account nearest to `query` by the vector of `model`, best first. Position 0 of an image is
47
47
  * its context text, position 1 its picture (`PICTURE_POSITION`): a model's vectors only ever sit at one.
48
48
  */
49
- export declare const nearestImages: ({ orm }: StoreContext, accountPk: number, { chatKey, model, since, limit, query, position, }: {
49
+ export declare const nearestImages: (context: StoreContext, accountPk: number, { chatKey, model, since, limit, query, position, }: {
50
50
  chatKey?: number;
51
51
  model: string;
52
52
  since?: number;
@@ -1,4 +1,5 @@
1
1
  import { sql } from "./drizzle/core.js";
2
+ import { searchIndex } from "./vector-index.js";
2
3
  import { dot } from "./vectors.js";
3
4
  const IS_IMAGE = sql `(a.kind = 'photo' OR a.mime LIKE 'image/%')`;
4
5
  const neighbours = ({ orm }, chatKey, messagePk, sentAt, { messages, withinMs }, side) => orm.all(sql `SELECT sender_name AS sender, text, sent_at AS sentAt FROM messages
@@ -62,32 +63,64 @@ export const embeddedHashes = ({ orm }, model, hashes) => new Set(hashes.length
62
63
  WHERE model = ${model} AND content_hash IN (SELECT value FROM json_each(${JSON.stringify(hashes)}))`)
63
64
  .map(({ hash }) => hash));
64
65
  const SCAN_PAGE = 5_000;
66
+ /** The scan's rows: one page after `after`, or the `ids` the index picked. */
67
+ const imageRows = ({ orm }, accountPk, { chatKey, model, since, position }, rows) => orm.all(sql `SELECT k.id, k.content_hash AS hash, ch.external_id AS chatId, m.external_id AS messageId, v.vector
68
+ FROM chunks k CROSS JOIN attachments a ON a.id = k.chunkable_id AND a.attachable_type = 'message'
69
+ JOIN messages m ON m.id = a.attachable_id AND m.deleted_at IS NULL
70
+ JOIN chats ch ON ch.id = m.chat_id
71
+ JOIN embeddings v ON v.model = ${model} AND v.content_hash = k.content_hash
72
+ WHERE k.chunkable_type = 'image' AND k.position = ${position} AND ch.account_id = ${accountPk}
73
+ ${chatKey === undefined ? sql `AND ch.searchable = 1` : sql `AND m.chat_id = ${chatKey}`}
74
+ ${since === undefined ? sql `` : sql `AND k.occurred_at >= ${since}`}
75
+ ${"ids" in rows
76
+ ? sql `AND k.id IN (SELECT value FROM json_each(${JSON.stringify(rows.ids)})) ORDER BY k.id`
77
+ : sql `AND k.id > ${rows.after} ORDER BY k.id LIMIT ${SCAN_PAGE}`}`);
65
78
  /**
66
79
  * The images of the account nearest to `query` by the vector of `model`, best first. Position 0 of an image is
67
80
  * its context text, position 1 its picture (`PICTURE_POSITION`): a model's vectors only ever sit at one.
68
81
  */
69
- export const nearestImages = ({ orm }, accountPk, { chatKey, model, since, limit, query, position = 0, }) => {
82
+ export const nearestImages = (context, accountPk, { chatKey, model, since, limit, query, position = 0, }) => {
83
+ const filter = {
84
+ model,
85
+ position,
86
+ ...(chatKey === undefined ? {} : { chatKey }),
87
+ ...(since === undefined ? {} : { since }),
88
+ };
89
+ const score = (row) => ({
90
+ id: row.id,
91
+ chatId: row.chatId,
92
+ messageId: row.messageId,
93
+ score: dot(query, row.vector),
94
+ });
95
+ // Stable, so equal scores keep the scan's order, by chunk id.
96
+ const best = (found) => found.sort((a, b) => b.score - a.score).slice(0, limit);
97
+ const indexed = searchIndex(context.database, model, {
98
+ kind: "image",
99
+ account: accountPk,
100
+ ...(chatKey === undefined ? {} : { chat: chatKey }),
101
+ ...(since === undefined ? {} : { since }),
102
+ }, query, limit, (candidates) => {
103
+ const hashes = new Map(candidates.map(({ id, hash }) => [id, hash]));
104
+ const results = best(imageRows(context, accountPk, filter, { ids: [...hashes.keys()] })
105
+ .filter((row) => hashes.get(row.id) === row.hash)
106
+ .map(score));
107
+ return { results, best: results.map(({ id }) => id) };
108
+ });
109
+ const plain = (found) => found.map(({ chatId, messageId, score }) => ({ chatId, messageId, score }));
110
+ if (indexed)
111
+ return plain(indexed);
70
112
  const found = [];
71
113
  let after = 0;
72
114
  for (;;) {
73
- const rows = orm.all(sql `SELECT k.id, ch.external_id AS chatId, m.external_id AS messageId, v.vector
74
- FROM chunks k CROSS JOIN attachments a ON a.id = k.chunkable_id AND a.attachable_type = 'message'
75
- JOIN messages m ON m.id = a.attachable_id AND m.deleted_at IS NULL
76
- JOIN chats ch ON ch.id = m.chat_id
77
- JOIN embeddings v ON v.model = ${model} AND v.content_hash = k.content_hash
78
- WHERE k.chunkable_type = 'image' AND k.position = ${position} AND ch.account_id = ${accountPk}
79
- AND k.id > ${after}
80
- ${chatKey === undefined ? sql `AND ch.searchable = 1` : sql `AND m.chat_id = ${chatKey}`}
81
- ${since === undefined ? sql `` : sql `AND k.occurred_at >= ${since}`}
82
- ORDER BY k.id LIMIT ${SCAN_PAGE}`);
115
+ const rows = imageRows(context, accountPk, filter, { after });
83
116
  for (const row of rows)
84
- found.push({ chatId: row.chatId, messageId: row.messageId, score: dot(query, row.vector) });
117
+ found.push(score(row));
85
118
  const last = rows.at(-1);
86
119
  if (!last || rows.length < SCAN_PAGE)
87
120
  break;
88
121
  after = last.id;
89
122
  }
90
- return found.sort((a, b) => b.score - a.score).slice(0, limit);
123
+ return plain(best(found));
91
124
  };
92
125
  /** An image's own vector is its chunk at this position; position 0 is the text around it. */
93
126
  export const PICTURE_POSITION = 1;
@@ -43,6 +43,8 @@ export const MANIFEST = [
43
43
  { name: "20261025071850_version-32-files-data", version: 32, minCompatible: 32 },
44
44
  { name: "20261025072643_version-33-calendars", version: 33, minCompatible: 32 },
45
45
  { name: "20261025072644_version-33-calendars-triggers", version: 33, minCompatible: 32 },
46
+ { name: "20261025182500_version-34-vector-index", version: 34, minCompatible: 34 },
47
+ { name: "20261025182501_version-34-vector-index-triggers", version: 34, minCompatible: 34 },
46
48
  ];
47
49
  export const generatedMigrations = (manifest = MANIFEST, generated = GENERATED) => {
48
50
  const migrations = [];
@@ -33,12 +33,15 @@ export interface NearestMessage {
33
33
  messageId: string;
34
34
  score: number;
35
35
  }
36
- /** The account's live messages nearest to `query` by their vector of `model`, best first: a scan of every vector. */
37
- export declare const nearestMessages: ({ orm }: StoreContext, accountPk: number, { chatKey, model, since, before, limit, query, }: {
36
+ type MessageFilter = {
38
37
  chatKey?: number;
39
38
  model: string;
40
39
  since?: number;
41
40
  before?: number;
41
+ };
42
+ /** The account's live messages nearest to `query` by their vector of `model`, best first. */
43
+ export declare const nearestMessages: (context: StoreContext, accountPk: number, { limit, query, ...filter }: MessageFilter & {
42
44
  limit: number;
43
45
  query: Float32Array;
44
46
  }) => NearestMessage[];
47
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { sql } from "./drizzle/core.js";
2
+ import { searchIndex } from "./vector-index.js";
2
3
  import { dot } from "./vectors.js";
3
4
  /** Live messages with text of the account, by pk after `after`; the caller compares hashes. */
4
5
  export const messagesToEmbed = ({ orm }, accountPk, { chatKey, model, after, limit }) => orm
@@ -51,26 +52,54 @@ export const dropGoneMessageChunks = (context, accountPk) => {
51
52
  export const hasMessageVectors = ({ orm }, accountPk, model) => orm.get(sql `SELECT 1 FROM chunks k JOIN embeddings v ON v.model = ${model} AND v.content_hash = k.content_hash
52
53
  WHERE k.chunkable_type = 'message' AND k.account_id = ${accountPk} LIMIT 1`) !== undefined;
53
54
  const SCAN_PAGE = 5_000;
54
- /** The account's live messages nearest to `query` by their vector of `model`, best first: a scan of every vector. */
55
- export const nearestMessages = ({ orm }, accountPk, { chatKey, model, since, before, limit, query, }) => {
55
+ /** The scan's rows: one page after `after`, or the `ids` the index picked. */
56
+ const messageRows = ({ orm }, accountPk, { chatKey, model, since, before }, rows) => orm.all(sql `SELECT k.id, k.content_hash AS hash, ch.external_id AS chatId, m.external_id AS messageId, v.vector
57
+ FROM chunks k CROSS JOIN messages m ON m.id = k.chunkable_id AND m.deleted_at IS NULL
58
+ JOIN chats ch ON ch.id = m.chat_id
59
+ JOIN embeddings v ON v.model = ${model} AND v.content_hash = k.content_hash
60
+ WHERE k.chunkable_type = 'message' AND k.position = 0 AND k.account_id = ${accountPk}
61
+ ${chatKey === undefined ? sql `AND ch.searchable = 1` : sql `AND m.chat_id = ${chatKey}`}
62
+ ${since === undefined ? sql `` : sql `AND m.sent_at >= ${since}`}
63
+ ${before === undefined ? sql `` : sql `AND m.sent_at < ${before}`}
64
+ ${"ids" in rows
65
+ ? sql `AND k.id IN (SELECT value FROM json_each(${JSON.stringify(rows.ids)})) ORDER BY k.id`
66
+ : sql `AND k.id > ${rows.after} ORDER BY k.id LIMIT ${SCAN_PAGE}`}`);
67
+ /** The account's live messages nearest to `query` by their vector of `model`, best first. */
68
+ export const nearestMessages = (context, accountPk, { limit, query, ...filter }) => {
69
+ const score = (row) => ({
70
+ id: row.id,
71
+ chatId: row.chatId,
72
+ messageId: row.messageId,
73
+ score: dot(query, row.vector),
74
+ });
75
+ // Stable, so equal scores keep the scan's order, by chunk id.
76
+ const best = (found) => found.sort((a, b) => b.score - a.score).slice(0, limit);
77
+ const indexed = searchIndex(context.database, filter.model, {
78
+ kind: "message",
79
+ account: accountPk,
80
+ ...(filter.chatKey === undefined ? {} : { chat: filter.chatKey }),
81
+ ...(filter.since === undefined ? {} : { since: filter.since }),
82
+ ...(filter.before === undefined ? {} : { before: filter.before }),
83
+ }, query, limit, (candidates) => {
84
+ const hashes = new Map(candidates.map(({ id, hash }) => [id, hash]));
85
+ const results = best(messageRows(context, accountPk, filter, { ids: [...hashes.keys()] })
86
+ .filter((row) => hashes.get(row.id) === row.hash)
87
+ .map(score));
88
+ return { results, best: results.map(({ id }) => id) };
89
+ });
90
+ const plain = (found) => found.map(({ chatId, messageId, score }) => ({ chatId, messageId, score }));
91
+ if (indexed)
92
+ return plain(indexed);
56
93
  const found = [];
57
94
  let after = 0;
58
95
  for (;;) {
59
- const rows = orm.all(sql `SELECT k.id, ch.external_id AS chatId, m.external_id AS messageId, v.vector
60
- FROM chunks k CROSS JOIN messages m ON m.id = k.chunkable_id AND m.deleted_at IS NULL
61
- JOIN chats ch ON ch.id = m.chat_id
62
- JOIN embeddings v ON v.model = ${model} AND v.content_hash = k.content_hash
63
- WHERE k.chunkable_type = 'message' AND k.position = 0 AND k.account_id = ${accountPk} AND k.id > ${after}
64
- ${chatKey === undefined ? sql `AND ch.searchable = 1` : sql `AND m.chat_id = ${chatKey}`}
65
- ${since === undefined ? sql `` : sql `AND m.sent_at >= ${since}`}
66
- ${before === undefined ? sql `` : sql `AND m.sent_at < ${before}`}
67
- ORDER BY k.id LIMIT ${SCAN_PAGE}`);
96
+ const rows = messageRows(context, accountPk, filter, { after });
68
97
  for (const row of rows)
69
- found.push({ chatId: row.chatId, messageId: row.messageId, score: dot(query, row.vector) });
98
+ found.push(score(row));
70
99
  const last = rows.at(-1);
71
100
  if (!last || rows.length < SCAN_PAGE)
72
101
  break;
73
102
  after = last.id;
74
103
  }
75
- return found.sort((a, b) => b.score - a.score).slice(0, limit);
104
+ return plain(best(found));
76
105
  };
@@ -739,5 +739,27 @@ export const GENERATED = [
739
739
  "INSERT OR IGNORE INTO search_index_state (name, watermark, filled_through, terms_through, normalizer_version, built_at, analyzer)\n VALUES ('event_index', 0, 0, 0, 1, NULL, NULL);",
740
740
  "INSERT OR IGNORE INTO event_index_pending (indexable_type, id) SELECT 'event', id FROM events;"
741
741
  ]
742
+ },
743
+ {
744
+ "name": "20261025182500_version-34-vector-index",
745
+ "statements": [
746
+ "CREATE TABLE `vector_index_redo` (\n\t`chunk_id` integer NOT NULL\n);",
747
+ "CREATE TABLE `vector_indexes` (\n\t`id` integer PRIMARY KEY,\n\t`model` text NOT NULL UNIQUE,\n\t`dims` integer NOT NULL,\n\t`ready` integer NOT NULL,\n\t`filled_through` text NOT NULL\n);"
748
+ ]
749
+ },
750
+ {
751
+ "name": "20261025182501_version-34-vector-index-triggers",
752
+ "statements": [
753
+ "-- Every change that can add, drop or move a chunk's vector passes the chunk's id through vector_index_redo, where\n-- each model's own trigger (made with its vec0 table, `vector-index.ts`) re-derives that row. Writes in TypeScript\n-- and the schema's own delete triggers (document_bd, note_bd, …) all reach the index this way.\n-- A chunk usually comes before its vector; the vector's own trigger indexes it then.\n-- CROSS JOIN: led by the vectors, SQLite read every one of them per chunk (7 ms each at 100k).\nCREATE TRIGGER vector_index_chunk_ai AFTER INSERT ON chunks WHEN EXISTS (SELECT 1 FROM vector_indexes i\n CROSS JOIN embeddings v ON v.model = i.model AND v.content_hash = new.content_hash) BEGIN\n INSERT INTO vector_index_redo (chunk_id) VALUES (new.id);\n DELETE FROM vector_index_redo;\nEND;",
754
+ "CREATE TRIGGER vector_index_chunk_ad AFTER DELETE ON chunks BEGIN\n INSERT INTO vector_index_redo (chunk_id) VALUES (old.id);\n DELETE FROM vector_index_redo;\nEND;",
755
+ "CREATE TRIGGER vector_index_chunk_au AFTER UPDATE OF content_hash, chunkable_type, chunkable_id, account_id, occurred_at\n ON chunks WHEN old.content_hash IS NOT new.content_hash OR old.chunkable_type IS NOT new.chunkable_type\n OR old.chunkable_id IS NOT new.chunkable_id OR old.account_id IS NOT new.account_id\n OR old.occurred_at IS NOT new.occurred_at BEGIN\n INSERT INTO vector_index_redo (chunk_id) VALUES (new.id);\n DELETE FROM vector_index_redo;\nEND;",
756
+ "CREATE TRIGGER vector_index_embedding_ai AFTER INSERT ON embeddings\n WHEN EXISTS (SELECT 1 FROM vector_indexes WHERE model = new.model) BEGIN\n INSERT INTO vector_index_redo (chunk_id) SELECT id FROM chunks WHERE content_hash = new.content_hash;\n DELETE FROM vector_index_redo;\nEND;",
757
+ "CREATE TRIGGER vector_index_embedding_ad AFTER DELETE ON embeddings\n WHEN EXISTS (SELECT 1 FROM vector_indexes WHERE model = old.model) BEGIN\n INSERT INTO vector_index_redo (chunk_id) SELECT id FROM chunks WHERE content_hash = old.content_hash;\n DELETE FROM vector_index_redo;\nEND;",
758
+ "CREATE TRIGGER vector_index_embedding_au AFTER UPDATE OF model, content_hash, vector ON embeddings BEGIN\n INSERT INTO vector_index_redo (chunk_id)\n SELECT id FROM chunks WHERE content_hash = old.content_hash OR content_hash = new.content_hash;\n DELETE FROM vector_index_redo;\nEND;",
759
+ "-- A message's chunk and its images' chunks are indexed under its chat and send time; a chat merge moves messages.\nCREATE TRIGGER vector_index_message_au AFTER UPDATE OF chat_id, sent_at ON messages\n WHEN old.chat_id IS NOT new.chat_id OR old.sent_at IS NOT new.sent_at BEGIN\n INSERT INTO vector_index_redo (chunk_id)\n SELECT id FROM chunks WHERE chunkable_type = 'message' AND chunkable_id = new.id\n UNION ALL SELECT k.id FROM attachments a JOIN chunks k ON k.chunkable_type = 'image' AND k.chunkable_id = a.id\n WHERE a.attachable_type = 'message' AND a.attachable_id = new.id;\n DELETE FROM vector_index_redo;\nEND;",
760
+ "-- An image's chat and account come from its message; a chat merge moves attachments to the kept copy.\nCREATE TRIGGER vector_index_attachment_au AFTER UPDATE OF attachable_type, attachable_id ON attachments\n WHEN old.attachable_type IS NOT new.attachable_type OR old.attachable_id IS NOT new.attachable_id BEGIN\n INSERT INTO vector_index_redo (chunk_id) SELECT id FROM chunks WHERE chunkable_type = 'image' AND chunkable_id = new.id;\n DELETE FROM vector_index_redo;\nEND;",
761
+ "CREATE TRIGGER vector_index_conversation_au AFTER UPDATE OF chat_id ON conversations\n WHEN old.chat_id IS NOT new.chat_id BEGIN\n INSERT INTO vector_index_redo (chunk_id)\n SELECT id FROM chunks WHERE chunkable_type = 'conversation' AND chunkable_id = new.id;\n DELETE FROM vector_index_redo;\nEND;",
762
+ "-- Images and conversations are indexed under their chat's account; messages carry their own.\nCREATE TRIGGER vector_index_chat_au AFTER UPDATE OF account_id ON chats WHEN old.account_id IS NOT new.account_id BEGIN\n INSERT INTO vector_index_redo (chunk_id)\n SELECT k.id FROM messages m JOIN attachments a ON a.attachable_type = 'message' AND a.attachable_id = m.id\n JOIN chunks k ON k.chunkable_type = 'image' AND k.chunkable_id = a.id WHERE m.chat_id = new.id\n UNION ALL SELECT k.id FROM conversations c JOIN chunks k ON k.chunkable_type = 'conversation' AND k.chunkable_id = c.id\n WHERE c.chat_id = new.id;\n DELETE FROM vector_index_redo;\nEND;"
763
+ ]
742
764
  }
743
765
  ];
@@ -15,6 +15,7 @@ import { loadProviders } from "./providers.js";
15
15
  import { assertReadCompatible } from "./read-compatibility.js";
16
16
  import { stemsState } from "./stems.js";
17
17
  import { fillStems, followDefaultStemmers } from "./stems-fill.js";
18
+ import { fillVectorIndexes } from "./vector-index.js";
18
19
  export const BACKFILL_ON_OPEN = 5_000;
19
20
  const NOT_WRITABLE = new Set(["EACCES", "EPERM", "EROFS"]);
20
21
  const createWritable = (file) => {
@@ -62,6 +63,9 @@ export const openStoreContext = async ({ path, env, now = Date.now, command, ver
62
63
  stemming.watermark - stemming.filledThrough + stemming.pending <= BACKFILL_ON_OPEN)
63
64
  fillStems(database, { now });
64
65
  logPruner(database, now)();
66
+ fillVectorIndexes(database, {
67
+ notice: (model) => warn(`indexing the stored vectors of ${model} for search by meaning — once; a large archive takes minutes`),
68
+ });
65
69
  }
66
70
  loadProviders(database);
67
71
  return { ...opened, now, sharedConfig: sharedConfigPath(file, env ?? process.env) };
@@ -1,3 +1,4 @@
1
+ // biome-ignore-all lint/plugin/no-sqlite-driver: cli-core owns the store, so it opens the driver itself
1
2
  import { PRAGMAS } from "../driver.js";
2
3
  import { quietSqliteWarning } from "../sqlite-warning.js";
3
4
  /**
@@ -19,10 +20,9 @@ export const openSqlite = async (path, { readOnly = false } = {}) => {
19
20
  };
20
21
  const openUnderNode = async (path, readOnly) => {
21
22
  quietSqliteWarning();
22
- const { DatabaseSync } = await import("node:sqlite");
23
23
  const { drizzle } = await import("./drizzle/node.js");
24
- const { cacheOverNodeSqlite } = await import("../drivers/node-sqlite.js");
25
- const client = new DatabaseSync(path, { readOnly });
24
+ const { cacheOverNodeSqlite, nodeClient } = await import("../drivers/node-sqlite.js");
25
+ const client = await nodeClient(path, { readOnly });
26
26
  try {
27
27
  return { database: cacheOverNodeSqlite(client), orm: drizzle({ client }) };
28
28
  }
@@ -32,10 +32,9 @@ const openUnderNode = async (path, readOnly) => {
32
32
  }
33
33
  };
34
34
  const openUnderBun = async (path, readOnly) => {
35
- const { bunDatabase, cacheOverBunSqlite } = await import("../drivers/bun-sqlite.js");
36
- const Database = await bunDatabase();
35
+ const { bunClient, cacheOverBunSqlite } = await import("../drivers/bun-sqlite.js");
37
36
  const { drizzle } = (await import("./drizzle/bun.js"));
38
- const client = readOnly ? new Database(path, { readonly: true, create: false }) : new Database(path);
37
+ const client = await bunClient(path, readOnly ? { readonly: true, create: false } : undefined);
39
38
  try {
40
39
  return { database: cacheOverBunSqlite(client), orm: drizzle({ client }) };
41
40
  }
@@ -84,4 +84,5 @@ export declare const canonicalTags: import("drizzle-orm/sqlite-core").SQLiteTabl
84
84
  }, {}>;
85
85
  };
86
86
  dialect: 'sqlite';
87
+ isAlias: false;
87
88
  }>;
@@ -36,4 +36,5 @@ export declare const documentIndexPending: import("drizzle-orm/sqlite-core").SQL
36
36
  }, {}>;
37
37
  };
38
38
  dialect: 'sqlite';
39
+ isAlias: false;
39
40
  }>;
@@ -100,4 +100,5 @@ export declare const documentAnnotationTransfers: import("drizzle-orm/sqlite-cor
100
100
  }, {}>;
101
101
  };
102
102
  dialect: 'sqlite';
103
+ isAlias: false;
103
104
  }>;
@@ -52,6 +52,7 @@ export declare const tagRedirects: import("drizzle-orm/sqlite-core").SQLiteTable
52
52
  }, {}>;
53
53
  };
54
54
  dialect: 'sqlite';
55
+ isAlias: false;
55
56
  }>;
56
57
  export declare const tagConsolidations: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
57
58
  name: "tag_consolidations";
@@ -267,4 +268,5 @@ export declare const tagConsolidations: import("drizzle-orm/sqlite-core").SQLite
267
268
  }, {}>;
268
269
  };
269
270
  dialect: 'sqlite';
271
+ isAlias: false;
270
272
  }>;