@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.
- package/README.md +26 -3
- package/dist/core/SdkEventEmitter.d.ts +2 -0
- package/dist/core/SdkEventEmitter.js +2 -0
- package/dist/{db.d.ts → db/db.d.ts} +14 -3
- package/dist/{db.js → db/db.js} +17 -8
- package/dist/db/exec-utils.d.ts +19 -0
- package/dist/db/exec-utils.js +48 -0
- package/dist/db/generated-ddl.d.ts +1 -0
- package/dist/db/generated-ddl.js +31 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.js +3 -0
- package/dist/db/queries/activeSeekers.d.ts +1 -0
- package/dist/{queries → db/queries}/activeSeekers.js +0 -5
- package/dist/{queries → db/queries}/messages.d.ts +2 -2
- package/dist/{queries → db/queries}/messages.js +3 -3
- package/dist/{queries → db/queries}/userProfile.d.ts +1 -1
- package/dist/{queries → db/queries}/userProfile.js +1 -1
- package/dist/db/schema/_helpers.d.ts +26 -0
- package/dist/db/schema/_helpers.js +15 -0
- package/dist/db/schema/activeSeekers.d.ts +43 -0
- package/dist/db/schema/activeSeekers.js +6 -0
- package/dist/db/schema/announcementCursors.d.ts +45 -0
- package/dist/db/schema/announcementCursors.js +5 -0
- package/dist/db/schema/contacts.d.ts +170 -0
- package/dist/db/schema/contacts.js +16 -0
- package/dist/db/schema/discussions.d.ts +336 -0
- package/dist/db/schema/discussions.js +31 -0
- package/dist/db/schema/index.d.ts +8 -0
- package/dist/db/schema/index.js +8 -0
- package/dist/db/schema/messages.d.ts +309 -0
- package/dist/db/schema/messages.js +29 -0
- package/dist/db/schema/pendingAnnouncements.d.ts +79 -0
- package/dist/db/schema/pendingAnnouncements.js +11 -0
- package/dist/db/schema/pendingEncryptedMessages.d.ts +79 -0
- package/dist/db/schema/pendingEncryptedMessages.js +11 -0
- package/dist/db/schema/userProfile.d.ts +208 -0
- package/dist/db/schema/userProfile.js +20 -0
- package/dist/{sqlite-worker.js → db/sqlite-worker.js} +2 -30
- package/dist/db/sqlite.d.ts +77 -0
- package/dist/db/sqlite.js +254 -0
- package/dist/gossip.d.ts +1 -41
- package/dist/gossip.js +8 -10
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/services/announcement.js +1 -1
- package/dist/services/discussion.js +1 -1
- package/dist/services/message.d.ts +1 -1
- package/dist/services/message.js +2 -3
- package/dist/services/refresh.js +1 -1
- package/dist/utils/contacts.d.ts +11 -0
- package/dist/utils/contacts.js +30 -2
- package/dist/utils/discussions.d.ts +1 -1
- package/dist/utils/discussions.js +1 -1
- package/dist/utils/validation.js +1 -1
- package/package.json +2 -1
- package/dist/contacts.d.ts +0 -120
- package/dist/contacts.js +0 -160
- package/dist/queries/activeSeekers.d.ts +0 -2
- package/dist/schema.d.ts +0 -1280
- package/dist/schema.js +0 -164
- package/dist/sqlite.d.ts +0 -79
- package/dist/sqlite.js +0 -448
- /package/dist/{queries → db/queries}/announcementCursors.d.ts +0 -0
- /package/dist/{queries → db/queries}/announcementCursors.js +0 -0
- /package/dist/{queries → db/queries}/contacts.d.ts +0 -0
- /package/dist/{queries → db/queries}/contacts.js +0 -0
- /package/dist/{queries → db/queries}/discussions.d.ts +0 -0
- /package/dist/{queries → db/queries}/discussions.js +0 -0
- /package/dist/{queries → db/queries}/index.d.ts +0 -0
- /package/dist/{queries → db/queries}/index.js +0 -0
- /package/dist/{queries → db/queries}/pendingAnnouncements.d.ts +0 -0
- /package/dist/{queries → db/queries}/pendingAnnouncements.js +0 -0
- /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
|
|
364
|
-
│ ├──
|
|
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
|
-
/**
|
|
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:
|
|
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 {};
|
package/dist/{db.js → db/db.js}
RENAMED
|
@@ -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
|
-
|
|
59
|
-
|
|
60
|
-
announcement_bytes
|
|
61
|
-
|
|
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
|
|
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:
|
|
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
|
+
];
|
package/dist/db/index.js
ADDED
|
@@ -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 '
|
|
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 '
|
|
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
|
|
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
|
|
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
|
+
}>;
|