@massalabs/gossip-sdk 0.0.2-dev.20260220143015 → 0.0.2-dev.20260223065033

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 (73) hide show
  1. package/README.md +26 -3
  2. package/dist/core/SdkEventEmitter.d.ts +2 -0
  3. package/dist/core/SdkEventEmitter.js +2 -0
  4. package/dist/{db.d.ts → db/db.d.ts} +14 -3
  5. package/dist/{db.js → db/db.js} +17 -8
  6. package/dist/db/exec-utils.d.ts +19 -0
  7. package/dist/db/exec-utils.js +48 -0
  8. package/dist/db/generated-ddl.d.ts +1 -0
  9. package/dist/db/generated-ddl.js +31 -0
  10. package/dist/db/index.d.ts +3 -0
  11. package/dist/db/index.js +3 -0
  12. package/dist/db/queries/activeSeekers.d.ts +1 -0
  13. package/dist/{queries → db/queries}/activeSeekers.js +0 -5
  14. package/dist/{queries → db/queries}/messages.d.ts +2 -2
  15. package/dist/{queries → db/queries}/messages.js +3 -3
  16. package/dist/{queries → db/queries}/userProfile.d.ts +1 -1
  17. package/dist/{queries → db/queries}/userProfile.js +1 -1
  18. package/dist/db/schema/_helpers.d.ts +26 -0
  19. package/dist/db/schema/_helpers.js +15 -0
  20. package/dist/db/schema/activeSeekers.d.ts +43 -0
  21. package/dist/db/schema/activeSeekers.js +6 -0
  22. package/dist/db/schema/announcementCursors.d.ts +45 -0
  23. package/dist/db/schema/announcementCursors.js +5 -0
  24. package/dist/db/schema/contacts.d.ts +170 -0
  25. package/dist/db/schema/contacts.js +16 -0
  26. package/dist/db/schema/discussions.d.ts +336 -0
  27. package/dist/db/schema/discussions.js +31 -0
  28. package/dist/db/schema/index.d.ts +8 -0
  29. package/dist/db/schema/index.js +8 -0
  30. package/dist/db/schema/messages.d.ts +309 -0
  31. package/dist/db/schema/messages.js +29 -0
  32. package/dist/db/schema/pendingAnnouncements.d.ts +79 -0
  33. package/dist/db/schema/pendingAnnouncements.js +11 -0
  34. package/dist/db/schema/pendingEncryptedMessages.d.ts +79 -0
  35. package/dist/db/schema/pendingEncryptedMessages.js +11 -0
  36. package/dist/db/schema/userProfile.d.ts +208 -0
  37. package/dist/db/schema/userProfile.js +20 -0
  38. package/dist/{sqlite-worker.js → db/sqlite-worker.js} +2 -30
  39. package/dist/db/sqlite.d.ts +77 -0
  40. package/dist/db/sqlite.js +254 -0
  41. package/dist/gossip.d.ts +1 -41
  42. package/dist/gossip.js +8 -10
  43. package/dist/index.d.ts +3 -3
  44. package/dist/index.js +3 -3
  45. package/dist/services/announcement.js +1 -1
  46. package/dist/services/discussion.js +1 -1
  47. package/dist/services/message.d.ts +1 -1
  48. package/dist/services/message.js +2 -3
  49. package/dist/services/refresh.js +1 -1
  50. package/dist/utils/contacts.d.ts +11 -0
  51. package/dist/utils/contacts.js +30 -2
  52. package/dist/utils/discussions.d.ts +1 -1
  53. package/dist/utils/discussions.js +1 -1
  54. package/dist/utils/validation.js +1 -1
  55. package/package.json +2 -1
  56. package/dist/contacts.d.ts +0 -120
  57. package/dist/contacts.js +0 -160
  58. package/dist/queries/activeSeekers.d.ts +0 -2
  59. package/dist/schema.d.ts +0 -1280
  60. package/dist/schema.js +0 -164
  61. package/dist/sqlite.d.ts +0 -79
  62. package/dist/sqlite.js +0 -448
  63. /package/dist/{queries → db/queries}/announcementCursors.d.ts +0 -0
  64. /package/dist/{queries → db/queries}/announcementCursors.js +0 -0
  65. /package/dist/{queries → db/queries}/contacts.d.ts +0 -0
  66. /package/dist/{queries → db/queries}/contacts.js +0 -0
  67. /package/dist/{queries → db/queries}/discussions.d.ts +0 -0
  68. /package/dist/{queries → db/queries}/discussions.js +0 -0
  69. /package/dist/{queries → db/queries}/index.d.ts +0 -0
  70. /package/dist/{queries → db/queries}/index.js +0 -0
  71. /package/dist/{queries → db/queries}/pendingAnnouncements.d.ts +0 -0
  72. /package/dist/{queries → db/queries}/pendingAnnouncements.js +0 -0
  73. /package/dist/{sqlite-worker.d.ts → db/sqlite-worker.d.ts} +0 -0
package/README.md CHANGED
@@ -345,6 +345,26 @@ import type {
345
345
  import { SessionStatus, SdkEventType } from '@massalabs/gossip-sdk';
346
346
  ```
347
347
 
348
+ ## Database
349
+
350
+ SQLite via [wa-sqlite](https://github.com/nicolo-ribaudo/wa-sqlite) (WASM) with [Drizzle ORM](https://orm.drizzle.team). Data is persisted to IndexedDB using `IDBBatchAtomicVFS`.
351
+
352
+ ### Schema
353
+
354
+ Schema is defined in `src/db/schema/` with one file per table. Drizzle-kit generates SQL migrations from the schema.
355
+
356
+ ### Migrations
357
+
358
+ Migrations live in `drizzle/` and are applied automatically on `initDb()` via Drizzle's built-in migrator.
359
+
360
+ When you change the schema, regenerate migrations:
361
+
362
+ ```bash
363
+ npm run db:generate
364
+ ```
365
+
366
+ This runs `drizzle-kit generate`, which diffs the schema against existing migrations and outputs a new `.sql` file in `drizzle/`. Commit the generated migration alongside your schema change.
367
+
348
368
  ## Testing
349
369
 
350
370
  ```bash
@@ -358,10 +378,14 @@ Tests use wa-sqlite with in-memory databases for fast, isolated execution.
358
378
 
359
379
  ```
360
380
  gossip-sdk/
381
+ ├── drizzle/ # Generated SQL migrations
361
382
  ├── src/
362
383
  │ ├── gossipSdk.ts # SDK class & factory
363
- │ ├── db.ts # Database (SQLite) implementation
364
- │ ├── contacts.ts # Contact operations
384
+ │ ├── db/
385
+ ├── index.ts # Barrel export (all DB access goes through here)
386
+ │ │ ├── schema/ # Drizzle table definitions (one file per table)
387
+ │ │ ├── queries/ # Query functions
388
+ │ │ └── sqlite.ts # SQLite init, migration, connection
365
389
  │ ├── api/
366
390
  │ │ └── messageProtocol/ # REST protocol implementation
367
391
  │ ├── config/
@@ -376,7 +400,6 @@ gossip-sdk/
376
400
  │ │ ├── discussion.ts # Discussion service
377
401
  │ │ ├── announcement.ts # Announcement service
378
402
  │ │ └── refresh.ts # Session refresh service
379
- │ ├── types/ # Type definitions
380
403
  │ ├── utils/ # Utility modules
381
404
  │ └── wasm/ # WASM module wrappers
382
405
  └── test/ # Test files
@@ -13,6 +13,7 @@ export declare enum SdkEventType {
13
13
  SESSION_CREATED = "sessionCreated",
14
14
  SESSION_RENEWED = "sessionRenewed",
15
15
  SESSION_ACCEPTED = "sessionAccepted",
16
+ SEEKERS_UPDATED = "seekersUpdated",
16
17
  ERROR = "error"
17
18
  }
18
19
  export interface SdkEventHandlers {
@@ -24,6 +25,7 @@ export interface SdkEventHandlers {
24
25
  [SdkEventType.SESSION_CREATED]: (discussion: Discussion) => void;
25
26
  [SdkEventType.SESSION_RENEWED]: (discussion: Discussion) => void;
26
27
  [SdkEventType.SESSION_ACCEPTED]: (contactUserId: string) => void;
28
+ [SdkEventType.SEEKERS_UPDATED]: (seekers: Uint8Array[]) => void;
27
29
  [SdkEventType.ERROR]: (error: Error, context: string) => void;
28
30
  }
29
31
  export declare class SdkEventEmitter {
@@ -16,6 +16,7 @@ export var SdkEventType;
16
16
  SdkEventType["SESSION_CREATED"] = "sessionCreated";
17
17
  SdkEventType["SESSION_RENEWED"] = "sessionRenewed";
18
18
  SdkEventType["SESSION_ACCEPTED"] = "sessionAccepted";
19
+ SdkEventType["SEEKERS_UPDATED"] = "seekersUpdated";
19
20
  SdkEventType["ERROR"] = "error";
20
21
  })(SdkEventType || (SdkEventType = {}));
21
22
  // ─────────────────────────────────────────────────────────────────────────────
@@ -36,6 +37,7 @@ export class SdkEventEmitter {
36
37
  [SdkEventType.SESSION_CREATED]: new Set(),
37
38
  [SdkEventType.SESSION_RENEWED]: new Set(),
38
39
  [SdkEventType.SESSION_ACCEPTED]: new Set(),
40
+ [SdkEventType.SEEKERS_UPDATED]: new Set(),
39
41
  [SdkEventType.ERROR]: new Set(),
40
42
  }
41
43
  });
@@ -106,11 +106,21 @@ export interface ReadyAnnouncement {
106
106
  export type SendAnnouncement = null | ReadyAnnouncement;
107
107
  /** Serialize a SendAnnouncement to a JSON string for SQLite text column */
108
108
  export declare function serializeSendAnnouncement(announcement: ReadyAnnouncement): string;
109
- /** Deserialize a SendAnnouncement JSON string from SQLite back to an object */
109
+ /** Deserialize a SendAnnouncement JSON string from SQLite back to an object.
110
+ * Throws a descriptive error if the stored JSON is malformed. */
110
111
  export declare function deserializeSendAnnouncement(json: string): ReadyAnnouncement;
111
- /** Convert a raw SQLite discussion row to a Discussion object.
112
+ /**
113
+ * Shape required by rowToDiscussion — matches Drizzle's inferred DiscussionRow
114
+ * without importing from schema (avoids circular dependency).
115
+ */
116
+ interface DiscussionRowLike {
117
+ sendAnnouncement: string | null;
118
+ announcementMessage: string | null;
119
+ [key: string]: unknown;
120
+ }
121
+ /** Convert a Drizzle discussion row to a domain Discussion object.
112
122
  * Deserializes sendAnnouncement from JSON text to SendAnnouncement. */
113
- export declare function rowToDiscussion(row: Record<string, unknown>): Discussion;
123
+ export declare function rowToDiscussion(row: DiscussionRowLike): Discussion;
114
124
  export interface Discussion {
115
125
  id?: number;
116
126
  ownerUserId: string;
@@ -148,3 +158,4 @@ export interface ActiveSeeker {
148
158
  id?: number;
149
159
  seeker: Uint8Array;
150
160
  }
161
+ export {};
@@ -53,20 +53,29 @@ export function serializeSendAnnouncement(announcement) {
53
53
  when_to_send: announcement.when_to_send.toISOString(),
54
54
  });
55
55
  }
56
- /** Deserialize a SendAnnouncement JSON string from SQLite back to an object */
56
+ /** Deserialize a SendAnnouncement JSON string from SQLite back to an object.
57
+ * Throws a descriptive error if the stored JSON is malformed. */
57
58
  export function deserializeSendAnnouncement(json) {
58
- const parsed = JSON.parse(json);
59
- return {
60
- announcement_bytes: new Uint8Array(parsed.announcement_bytes),
61
- when_to_send: new Date(parsed.when_to_send),
62
- };
59
+ try {
60
+ const parsed = JSON.parse(json);
61
+ if (!parsed || !parsed.announcement_bytes || !parsed.when_to_send) {
62
+ throw new Error('missing required fields');
63
+ }
64
+ return {
65
+ announcement_bytes: new Uint8Array(parsed.announcement_bytes),
66
+ when_to_send: new Date(parsed.when_to_send),
67
+ };
68
+ }
69
+ catch (e) {
70
+ throw new Error(`Failed to deserialize SendAnnouncement: ${e instanceof Error ? e.message : e}`);
71
+ }
63
72
  }
64
- /** Convert a raw SQLite discussion row to a Discussion object.
73
+ /** Convert a Drizzle discussion row to a domain Discussion object.
65
74
  * Deserializes sendAnnouncement from JSON text to SendAnnouncement. */
66
75
  export function rowToDiscussion(row) {
67
76
  return {
68
77
  ...row,
69
- sendAnnouncement: typeof row.sendAnnouncement === 'string'
78
+ sendAnnouncement: row.sendAnnouncement !== null
70
79
  ? deserializeSendAnnouncement(row.sendAnnouncement)
71
80
  : null,
72
81
  lastAnnouncementMessage: row.announcementMessage ?? undefined,
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared SQLite execution utilities.
3
+ *
4
+ * Used by both sqlite.ts (in-process path) and sqlite-worker.ts (Worker path)
5
+ * to avoid duplicating the wa-sqlite prepare/bind/step/finalize logic.
6
+ */
7
+ import * as SQLite from 'wa-sqlite';
8
+ export type SqliteAPI = ReturnType<typeof SQLite.Factory>;
9
+ /**
10
+ * Copy blob values out of WASM linear memory.
11
+ * wa-sqlite's column_blob() returns a Uint8Array VIEW into Module.HEAPU8.
12
+ * These views become stale after finalize() or memory growth.
13
+ */
14
+ export declare function copyRow(row: unknown[]): unknown[];
15
+ /**
16
+ * Execute a SQL statement against a wa-sqlite database handle.
17
+ * Handles both parameterless (exec) and parameterized (prepare_v2) paths.
18
+ */
19
+ export declare function execStatements(sqlite3: SqliteAPI, dbHandle: number, sql: string, params?: unknown[]): Promise<unknown[][]>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shared SQLite execution utilities.
3
+ *
4
+ * Used by both sqlite.ts (in-process path) and sqlite-worker.ts (Worker path)
5
+ * to avoid duplicating the wa-sqlite prepare/bind/step/finalize logic.
6
+ */
7
+ import * as SQLite from 'wa-sqlite';
8
+ /**
9
+ * Copy blob values out of WASM linear memory.
10
+ * wa-sqlite's column_blob() returns a Uint8Array VIEW into Module.HEAPU8.
11
+ * These views become stale after finalize() or memory growth.
12
+ */
13
+ export function copyRow(row) {
14
+ return row.map(v => (v instanceof Uint8Array ? new Uint8Array(v) : v));
15
+ }
16
+ /**
17
+ * Execute a SQL statement against a wa-sqlite database handle.
18
+ * Handles both parameterless (exec) and parameterized (prepare_v2) paths.
19
+ */
20
+ export async function execStatements(sqlite3, dbHandle, sql, params = []) {
21
+ if (params.length === 0) {
22
+ const rows = [];
23
+ await sqlite3.exec(dbHandle, sql, (row) => {
24
+ rows.push(copyRow(row));
25
+ });
26
+ return rows;
27
+ }
28
+ const str = sqlite3.str_new(dbHandle, sql);
29
+ try {
30
+ const prepared = await sqlite3.prepare_v2(dbHandle, sqlite3.str_value(str));
31
+ if (!prepared)
32
+ return [];
33
+ try {
34
+ sqlite3.bind_collection(prepared.stmt, params);
35
+ const rows = [];
36
+ while ((await sqlite3.step(prepared.stmt)) === SQLite.SQLITE_ROW) {
37
+ rows.push(copyRow(sqlite3.row(prepared.stmt)));
38
+ }
39
+ return rows;
40
+ }
41
+ finally {
42
+ await sqlite3.finalize(prepared.stmt);
43
+ }
44
+ }
45
+ finally {
46
+ sqlite3.str_finish(str);
47
+ }
48
+ }
@@ -0,0 +1 @@
1
+ export declare const DDL: string[];
@@ -0,0 +1,31 @@
1
+ // Auto-generated from drizzle migrations — do not edit manually.
2
+ // Regenerate with: npm run db:generate
3
+ export const DDL = [
4
+ 'CREATE TABLE IF NOT EXISTS `activeSeekers` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`seeker` blob NOT NULL\n);',
5
+ 'CREATE INDEX IF NOT EXISTS `active_seekers_seeker_idx` ON `activeSeekers` (`seeker`);',
6
+ 'CREATE TABLE IF NOT EXISTS `announcementCursors` (\n\t`userId` text PRIMARY KEY NOT NULL,\n\t`counter` text NOT NULL\n);',
7
+ 'CREATE TABLE IF NOT EXISTS `contacts` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`ownerUserId` text NOT NULL,\n\t`userId` text NOT NULL,\n\t`name` text NOT NULL,\n\t`avatar` text,\n\t`publicKeys` blob NOT NULL,\n\t`isOnline` integer NOT NULL,\n\t`lastSeen` integer NOT NULL,\n\t`createdAt` integer NOT NULL\n);',
8
+ 'CREATE INDEX IF NOT EXISTS `contacts_owner_user_idx` ON `contacts` (`ownerUserId`,`userId`);',
9
+ 'CREATE INDEX IF NOT EXISTS `contacts_owner_name_idx` ON `contacts` (`ownerUserId`,`name`);',
10
+ 'CREATE TABLE IF NOT EXISTS `discussions` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`ownerUserId` text NOT NULL,\n\t`contactUserId` text NOT NULL,\n\t`weAccepted` integer DEFAULT false NOT NULL,\n\t`sendAnnouncement` text,\n\t`direction` text NOT NULL,\n\t`status` text NOT NULL,\n\t`nextSeeker` blob,\n\t`initiationAnnouncement` blob,\n\t`announcementMessage` text,\n\t`lastSyncTimestamp` integer,\n\t`customName` text,\n\t`lastMessageId` integer,\n\t`lastMessageContent` text,\n\t`lastMessageTimestamp` integer,\n\t`unreadCount` integer DEFAULT 0 NOT NULL,\n\t`createdAt` integer NOT NULL,\n\t`updatedAt` integer NOT NULL\n);',
11
+ 'CREATE UNIQUE INDEX IF NOT EXISTS `discussions_owner_contact_idx` ON `discussions` (`ownerUserId`,`contactUserId`);',
12
+ 'CREATE INDEX IF NOT EXISTS `discussions_owner_status_idx` ON `discussions` (`ownerUserId`,`status`);',
13
+ 'CREATE TABLE IF NOT EXISTS `messages` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`ownerUserId` text NOT NULL,\n\t`contactUserId` text NOT NULL,\n\t`messageId` blob,\n\t`content` text NOT NULL,\n\t`serializedContent` blob,\n\t`type` text NOT NULL,\n\t`direction` text NOT NULL,\n\t`status` text NOT NULL,\n\t`timestamp` integer NOT NULL,\n\t`metadata` text,\n\t`seeker` blob,\n\t`replyTo` text,\n\t`forwardOf` text,\n\t`encryptedMessage` blob,\n\t`whenToSend` integer\n);',
14
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_contact_idx` ON `messages` (`ownerUserId`,`contactUserId`);',
15
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_status_idx` ON `messages` (`ownerUserId`,`status`);',
16
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_contact_status_idx` ON `messages` (`ownerUserId`,`contactUserId`,`status`);',
17
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_seeker_idx` ON `messages` (`ownerUserId`,`seeker`);',
18
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_contact_dir_idx` ON `messages` (`ownerUserId`,`contactUserId`,`direction`);',
19
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_dir_status_idx` ON `messages` (`ownerUserId`,`direction`,`status`);',
20
+ 'CREATE INDEX IF NOT EXISTS `messages_timestamp_idx` ON `messages` (`timestamp`);',
21
+ 'CREATE TABLE IF NOT EXISTS `pendingAnnouncements` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`announcement` blob NOT NULL,\n\t`fetchedAt` integer NOT NULL,\n\t`counter` text\n);',
22
+ 'CREATE UNIQUE INDEX IF NOT EXISTS `pending_announcements_announcement_idx` ON `pendingAnnouncements` (`announcement`);',
23
+ 'CREATE INDEX IF NOT EXISTS `pending_announcements_fetchedAt_idx` ON `pendingAnnouncements` (`fetchedAt`);',
24
+ 'CREATE TABLE IF NOT EXISTS `pendingEncryptedMessages` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`seeker` blob NOT NULL,\n\t`ciphertext` blob NOT NULL,\n\t`fetchedAt` integer NOT NULL\n);',
25
+ 'CREATE INDEX IF NOT EXISTS `pending_encrypted_seeker_idx` ON `pendingEncryptedMessages` (`seeker`);',
26
+ 'CREATE INDEX IF NOT EXISTS `pending_encrypted_fetchedAt_idx` ON `pendingEncryptedMessages` (`fetchedAt`);',
27
+ 'CREATE TABLE IF NOT EXISTS `userProfile` (\n\t`userId` text PRIMARY KEY NOT NULL,\n\t`username` text NOT NULL,\n\t`avatar` text,\n\t`bio` text,\n\t`status` text NOT NULL,\n\t`lastSeen` integer NOT NULL,\n\t`createdAt` integer NOT NULL,\n\t`updatedAt` integer NOT NULL,\n\t`lastPublicKeyPush` integer,\n\t`security` text NOT NULL,\n\t`session` blob NOT NULL\n);',
28
+ 'CREATE INDEX IF NOT EXISTS `userProfile_username_idx` ON `userProfile` (`username`);',
29
+ 'CREATE INDEX IF NOT EXISTS `userProfile_status_idx` ON `userProfile` (`status`);',
30
+ 'CREATE INDEX IF NOT EXISTS `messages_owner_contact_msgid_idx` ON `messages` (`ownerUserId`,`contactUserId`,`messageId`);',
31
+ ];
@@ -0,0 +1,3 @@
1
+ export * from './db';
2
+ export * from './queries';
3
+ export * from './sqlite';
@@ -0,0 +1,3 @@
1
+ export * from './db';
2
+ export * from './queries';
3
+ export * from './sqlite';
@@ -0,0 +1 @@
1
+ export declare function replaceActiveSeekers(seekers: Uint8Array[]): Promise<void>;
@@ -1,9 +1,5 @@
1
1
  import * as schema from '../schema';
2
2
  import { getSqliteDb, withTransaction } from '../sqlite';
3
- let onSeekersUpdated = null;
4
- export function setOnSeekersUpdated(cb) {
5
- onSeekersUpdated = cb;
6
- }
7
3
  export async function replaceActiveSeekers(seekers) {
8
4
  await withTransaction(async () => {
9
5
  const db = getSqliteDb();
@@ -14,5 +10,4 @@ export async function replaceActiveSeekers(seekers) {
14
10
  .values(seekers.map(seeker => ({ seeker })));
15
11
  }
16
12
  });
17
- onSeekersUpdated?.(seekers);
18
13
  }
@@ -1,5 +1,5 @@
1
1
  import * as schema from '../schema';
2
- import { MessageStatus } from '../db';
2
+ import { MessageStatus } from '../../db/db';
3
3
  export type MessageRow = typeof schema.messages.$inferSelect;
4
4
  export type MessageInsert = typeof schema.messages.$inferInsert;
5
5
  export declare function getMessageById(id: number): Promise<MessageRow | undefined>;
@@ -14,7 +14,7 @@ export declare function deleteDeliveredKeepAliveMessages(ownerUserId: string): P
14
14
  export declare function getOutgoingSentMessagesByOwner(ownerUserId: string): Promise<MessageRow[]>;
15
15
  export declare function getWaitingMessageCount(ownerUserId: string, contactUserId: string): Promise<number>;
16
16
  export declare function getSendQueueMessages(ownerUserId: string, contactUserId: string): Promise<MessageRow[]>;
17
- export declare function getMessagesByStatus(status: MessageStatus): Promise<MessageRow[]>;
17
+ export declare function getMessagesByStatus(ownerUserId: string, status: MessageStatus): Promise<MessageRow[]>;
18
18
  /**
19
19
  * Reset outgoing messages to WAITING_SESSION, clearing encryption data.
20
20
  * @param statuses - If provided, only reset messages with these statuses.
@@ -1,7 +1,7 @@
1
1
  import { eq, and, sql, inArray, asc } from 'drizzle-orm';
2
2
  import * as schema from '../schema';
3
3
  import { getSqliteDb, getLastInsertRowId } from '../sqlite';
4
- import { MessageDirection, MessageStatus, MessageType } from '../db';
4
+ import { MessageDirection, MessageStatus, MessageType } from '../../db/db';
5
5
  export async function getMessageById(id) {
6
6
  return getSqliteDb()
7
7
  .select()
@@ -82,11 +82,11 @@ export async function getSendQueueMessages(ownerUserId, contactUserId) {
82
82
  .orderBy(asc(schema.messages.timestamp), asc(schema.messages.id))
83
83
  .all();
84
84
  }
85
- export async function getMessagesByStatus(status) {
85
+ export async function getMessagesByStatus(ownerUserId, status) {
86
86
  return getSqliteDb()
87
87
  .select()
88
88
  .from(schema.messages)
89
- .where(eq(schema.messages.status, status))
89
+ .where(and(eq(schema.messages.ownerUserId, ownerUserId), eq(schema.messages.status, status)))
90
90
  .all();
91
91
  }
92
92
  /**
@@ -6,7 +6,7 @@ export type UserProfileInsert = typeof schema.userProfile.$inferInsert;
6
6
  export declare function rowToUserProfile(row: UserProfileRow): UserProfile;
7
7
  /** Convert a domain UserProfile to a DB-ready insert row (security as JSON string). */
8
8
  export declare function userProfileToRow(profile: UserProfile): UserProfileInsert;
9
- export declare function getUserProfileField(userId: string): Promise<UserProfileRow | undefined>;
9
+ export declare function getUserProfileById(userId: string): Promise<UserProfileRow | undefined>;
10
10
  export declare function updateUserProfileById(userId: string, data: Partial<UserProfileInsert>): Promise<void>;
11
11
  export declare function getUserProfileByUsernameLower(username: string): Promise<{
12
12
  userId: string;
@@ -51,7 +51,7 @@ export function userProfileToRow(profile) {
51
51
  lastPublicKeyPush: profile.lastPublicKeyPush ?? null,
52
52
  };
53
53
  }
54
- export async function getUserProfileField(userId) {
54
+ export async function getUserProfileById(userId) {
55
55
  return getSqliteDb()
56
56
  .select()
57
57
  .from(schema.userProfile)
@@ -0,0 +1,26 @@
1
+ export declare const bytes: {
2
+ (): import("drizzle-orm/sqlite-core").SQLiteCustomColumnBuilder<{
3
+ name: "";
4
+ dataType: "custom";
5
+ columnType: "SQLiteCustomColumn";
6
+ data: Uint8Array<ArrayBufferLike>;
7
+ driverParam: Uint8Array<ArrayBufferLike>;
8
+ enumValues: undefined;
9
+ }>;
10
+ <TConfig extends Record<string, any>>(fieldConfig?: TConfig | undefined): import("drizzle-orm/sqlite-core").SQLiteCustomColumnBuilder<{
11
+ name: "";
12
+ dataType: "custom";
13
+ columnType: "SQLiteCustomColumn";
14
+ data: Uint8Array<ArrayBufferLike>;
15
+ driverParam: Uint8Array<ArrayBufferLike>;
16
+ enumValues: undefined;
17
+ }>;
18
+ <TName extends string>(dbName: TName, fieldConfig?: unknown): import("drizzle-orm/sqlite-core").SQLiteCustomColumnBuilder<{
19
+ name: TName;
20
+ dataType: "custom";
21
+ columnType: "SQLiteCustomColumn";
22
+ data: Uint8Array<ArrayBufferLike>;
23
+ driverParam: Uint8Array<ArrayBufferLike>;
24
+ enumValues: undefined;
25
+ }>;
26
+ };
@@ -0,0 +1,15 @@
1
+ import { customType } from 'drizzle-orm/sqlite-core';
2
+ // Custom blob type — wa-sqlite returns Uint8Array natively for BLOB columns.
3
+ // Drizzle's built-in blob mode converts to/from hex strings, which breaks
4
+ // with wa-sqlite's direct Uint8Array handling. This custom type passes through.
5
+ export const bytes = customType({
6
+ dataType() {
7
+ return 'blob';
8
+ },
9
+ fromDriver(value) {
10
+ return value;
11
+ },
12
+ toDriver(value) {
13
+ return value;
14
+ },
15
+ });
@@ -0,0 +1,43 @@
1
+ export declare const activeSeekers: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
+ name: "activeSeekers";
3
+ schema: undefined;
4
+ columns: {
5
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
+ name: "id";
7
+ tableName: "activeSeekers";
8
+ dataType: "number";
9
+ columnType: "SQLiteInteger";
10
+ data: number;
11
+ driverParam: number;
12
+ notNull: true;
13
+ hasDefault: true;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: undefined;
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {}>;
22
+ seeker: import("drizzle-orm/sqlite-core").SQLiteColumn<{
23
+ name: "seeker";
24
+ tableName: "activeSeekers";
25
+ dataType: "custom";
26
+ columnType: "SQLiteCustomColumn";
27
+ data: Uint8Array<ArrayBufferLike>;
28
+ driverParam: Uint8Array<ArrayBufferLike>;
29
+ notNull: true;
30
+ hasDefault: false;
31
+ isPrimaryKey: false;
32
+ isAutoincrement: false;
33
+ hasRuntimeDefault: false;
34
+ enumValues: undefined;
35
+ baseColumn: never;
36
+ identity: undefined;
37
+ generated: undefined;
38
+ }, {}, {
39
+ sqliteColumnBuilderBrand: "SQLiteCustomColumnBuilderBrand";
40
+ }>;
41
+ };
42
+ dialect: "sqlite";
43
+ }>;
@@ -0,0 +1,6 @@
1
+ import { sqliteTable, integer, index } from 'drizzle-orm/sqlite-core';
2
+ import { bytes } from './_helpers';
3
+ export const activeSeekers = sqliteTable('activeSeekers', {
4
+ id: integer('id').primaryKey({ autoIncrement: true }),
5
+ seeker: bytes('seeker').notNull(),
6
+ }, table => [index('active_seekers_seeker_idx').on(table.seeker)]);
@@ -0,0 +1,45 @@
1
+ export declare const announcementCursors: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
2
+ name: "announcementCursors";
3
+ schema: undefined;
4
+ columns: {
5
+ userId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
6
+ name: "userId";
7
+ tableName: "announcementCursors";
8
+ dataType: "string";
9
+ columnType: "SQLiteText";
10
+ data: string;
11
+ driverParam: string;
12
+ notNull: true;
13
+ hasDefault: false;
14
+ isPrimaryKey: true;
15
+ isAutoincrement: false;
16
+ hasRuntimeDefault: false;
17
+ enumValues: [string, ...string[]];
18
+ baseColumn: never;
19
+ identity: undefined;
20
+ generated: undefined;
21
+ }, {}, {
22
+ length: number | undefined;
23
+ }>;
24
+ counter: import("drizzle-orm/sqlite-core").SQLiteColumn<{
25
+ name: "counter";
26
+ tableName: "announcementCursors";
27
+ dataType: "string";
28
+ columnType: "SQLiteText";
29
+ data: string;
30
+ driverParam: string;
31
+ notNull: true;
32
+ hasDefault: false;
33
+ isPrimaryKey: false;
34
+ isAutoincrement: false;
35
+ hasRuntimeDefault: false;
36
+ enumValues: [string, ...string[]];
37
+ baseColumn: never;
38
+ identity: undefined;
39
+ generated: undefined;
40
+ }, {}, {
41
+ length: number | undefined;
42
+ }>;
43
+ };
44
+ dialect: "sqlite";
45
+ }>;
@@ -0,0 +1,5 @@
1
+ import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+ export const announcementCursors = sqliteTable('announcementCursors', {
3
+ userId: text('userId').primaryKey(),
4
+ counter: text('counter').notNull(),
5
+ });