@massalabs/gossip-sdk 0.0.2-dev.20260223131319 → 0.0.2-dev.20260224143454
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/dist/db/generated-migrations.d.ts +7 -0
- package/dist/db/generated-migrations.js +45 -0
- package/dist/db/migrate.d.ts +11 -0
- package/dist/db/migrate.js +26 -0
- package/dist/db/node-fs-vfs.d.ts +32 -0
- package/dist/db/node-fs-vfs.js +165 -0
- package/dist/db/queries/activeSeekers.d.ts +6 -1
- package/dist/db/queries/activeSeekers.js +19 -11
- package/dist/db/queries/announcementCursors.d.ts +7 -2
- package/dist/db/queries/announcementCursors.js +26 -17
- package/dist/db/queries/contacts.d.ts +13 -8
- package/dist/db/queries/contacts.js +45 -36
- package/dist/db/queries/discussions.d.ts +15 -16
- package/dist/db/queries/discussions.js +73 -70
- package/dist/db/queries/index.d.ts +31 -7
- package/dist/db/queries/index.js +73 -7
- package/dist/db/queries/messages.d.ts +23 -23
- package/dist/db/queries/messages.js +120 -116
- package/dist/db/queries/pendingAnnouncements.d.ts +7 -2
- package/dist/db/queries/pendingAnnouncements.js +17 -8
- package/dist/db/queries/userProfile.d.ts +19 -14
- package/dist/db/queries/userProfile.js +72 -63
- package/dist/db/sqlite.d.ts +45 -68
- package/dist/db/sqlite.js +219 -218
- package/dist/gossip.d.ts +35 -86
- package/dist/gossip.js +115 -130
- package/dist/services/announcement.d.ts +7 -1
- package/dist/services/announcement.js +42 -28
- package/dist/services/contact.d.ts +24 -0
- package/dist/services/contact.js +51 -0
- package/dist/services/discussion.d.ts +29 -1
- package/dist/services/discussion.js +99 -15
- package/dist/services/message.d.ts +28 -1
- package/dist/services/message.js +93 -36
- package/dist/services/profile.d.ts +22 -0
- package/dist/services/profile.js +85 -0
- package/dist/services/refresh.d.ts +3 -1
- package/dist/services/refresh.js +10 -4
- package/dist/utils/contacts.d.ts +5 -4
- package/dist/utils/contacts.js +13 -14
- package/dist/utils/discussions.d.ts +2 -1
- package/dist/utils/discussions.js +3 -4
- package/dist/utils/validation.d.ts +3 -2
- package/dist/utils/validation.js +4 -5
- package/package.json +1 -1
- package/dist/db/generated-ddl.d.ts +0 -1
- package/dist/db/generated-ddl.js +0 -31
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Auto-generated from drizzle migrations — do not edit manually.
|
|
2
|
+
// Regenerate with: npm run db:generate
|
|
3
|
+
export const MIGRATIONS = [
|
|
4
|
+
{
|
|
5
|
+
idx: 0,
|
|
6
|
+
tag: '0000_nifty_molly_hayes',
|
|
7
|
+
when: 1771825473415,
|
|
8
|
+
statements: [
|
|
9
|
+
'CREATE TABLE `activeSeekers` (\n\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\n\t`seeker` blob NOT NULL\n);',
|
|
10
|
+
'CREATE INDEX `active_seekers_seeker_idx` ON `activeSeekers` (`seeker`);',
|
|
11
|
+
'CREATE TABLE `announcementCursors` (\n\t`userId` text PRIMARY KEY NOT NULL,\n\t`counter` text NOT NULL\n);',
|
|
12
|
+
'CREATE TABLE `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);',
|
|
13
|
+
'CREATE INDEX `contacts_owner_user_idx` ON `contacts` (`ownerUserId`,`userId`);',
|
|
14
|
+
'CREATE INDEX `contacts_owner_name_idx` ON `contacts` (`ownerUserId`,`name`);',
|
|
15
|
+
'CREATE TABLE `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);',
|
|
16
|
+
'CREATE UNIQUE INDEX `discussions_owner_contact_idx` ON `discussions` (`ownerUserId`,`contactUserId`);',
|
|
17
|
+
'CREATE INDEX `discussions_owner_status_idx` ON `discussions` (`ownerUserId`,`status`);',
|
|
18
|
+
'CREATE TABLE `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);',
|
|
19
|
+
'CREATE INDEX `messages_owner_contact_idx` ON `messages` (`ownerUserId`,`contactUserId`);',
|
|
20
|
+
'CREATE INDEX `messages_owner_status_idx` ON `messages` (`ownerUserId`,`status`);',
|
|
21
|
+
'CREATE INDEX `messages_owner_contact_status_idx` ON `messages` (`ownerUserId`,`contactUserId`,`status`);',
|
|
22
|
+
'CREATE INDEX `messages_owner_seeker_idx` ON `messages` (`ownerUserId`,`seeker`);',
|
|
23
|
+
'CREATE INDEX `messages_owner_contact_dir_idx` ON `messages` (`ownerUserId`,`contactUserId`,`direction`);',
|
|
24
|
+
'CREATE INDEX `messages_owner_dir_status_idx` ON `messages` (`ownerUserId`,`direction`,`status`);',
|
|
25
|
+
'CREATE INDEX `messages_timestamp_idx` ON `messages` (`timestamp`);',
|
|
26
|
+
'CREATE TABLE `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);',
|
|
27
|
+
'CREATE UNIQUE INDEX `pending_announcements_announcement_idx` ON `pendingAnnouncements` (`announcement`);',
|
|
28
|
+
'CREATE INDEX `pending_announcements_fetchedAt_idx` ON `pendingAnnouncements` (`fetchedAt`);',
|
|
29
|
+
'CREATE TABLE `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);',
|
|
30
|
+
'CREATE INDEX `pending_encrypted_seeker_idx` ON `pendingEncryptedMessages` (`seeker`);',
|
|
31
|
+
'CREATE INDEX `pending_encrypted_fetchedAt_idx` ON `pendingEncryptedMessages` (`fetchedAt`);',
|
|
32
|
+
'CREATE TABLE `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);',
|
|
33
|
+
'CREATE INDEX `userProfile_username_idx` ON `userProfile` (`username`);',
|
|
34
|
+
'CREATE INDEX `userProfile_status_idx` ON `userProfile` (`status`);',
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
idx: 1,
|
|
39
|
+
tag: '0001_flowery_storm',
|
|
40
|
+
when: 1771827090330,
|
|
41
|
+
statements: [
|
|
42
|
+
'CREATE INDEX `messages_owner_contact_msgid_idx` ON `messages` (`ownerUserId`,`contactUserId`,`messageId`);',
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration runner for the Gossip SDK SQLite database.
|
|
3
|
+
*
|
|
4
|
+
* Tracks applied migrations in a `_migrations` table and runs only
|
|
5
|
+
* pending ones. Each migration executes in its own transaction so
|
|
6
|
+
* a failure at migration N leaves 0..N-1 committed.
|
|
7
|
+
*/
|
|
8
|
+
type ExecRaw = (sql: string, params?: unknown[]) => Promise<unknown[][]>;
|
|
9
|
+
type WithTransaction = <T>(fn: () => Promise<T>) => Promise<T>;
|
|
10
|
+
export declare function runMigrations(execRaw: ExecRaw, withTransaction: WithTransaction): Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration runner for the Gossip SDK SQLite database.
|
|
3
|
+
*
|
|
4
|
+
* Tracks applied migrations in a `_migrations` table and runs only
|
|
5
|
+
* pending ones. Each migration executes in its own transaction so
|
|
6
|
+
* a failure at migration N leaves 0..N-1 committed.
|
|
7
|
+
*/
|
|
8
|
+
import { MIGRATIONS } from './generated-migrations.js';
|
|
9
|
+
export async function runMigrations(execRaw, withTransaction) {
|
|
10
|
+
await execRaw(`CREATE TABLE IF NOT EXISTS _migrations (
|
|
11
|
+
idx INTEGER PRIMARY KEY,
|
|
12
|
+
tag TEXT NOT NULL,
|
|
13
|
+
applied_at INTEGER NOT NULL
|
|
14
|
+
)`);
|
|
15
|
+
const rows = await execRaw('SELECT MAX(idx) FROM _migrations');
|
|
16
|
+
const maxApplied = rows[0][0];
|
|
17
|
+
const pending = MIGRATIONS.filter(m => m.idx > (maxApplied ?? -1));
|
|
18
|
+
for (const migration of pending) {
|
|
19
|
+
await withTransaction(async () => {
|
|
20
|
+
for (const stmt of migration.statements) {
|
|
21
|
+
await execRaw(stmt);
|
|
22
|
+
}
|
|
23
|
+
await execRaw('INSERT INTO _migrations (idx, tag, applied_at) VALUES (?, ?, ?)', [migration.idx, migration.tag, Date.now()]);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js file-system VFS for wa-sqlite.
|
|
3
|
+
*
|
|
4
|
+
* Extends VFS.Base using Node.js `fs` sync APIs for file persistence.
|
|
5
|
+
* Follows the MemoryVFS pattern but writes to real files on disk.
|
|
6
|
+
*
|
|
7
|
+
* Locking: inherited no-ops from VFS.Base (sufficient for single-process).
|
|
8
|
+
* This VFS will later be replaceable by the encrypted VFS from the storage branch.
|
|
9
|
+
*/
|
|
10
|
+
import * as VFS from 'wa-sqlite/src/VFS.js';
|
|
11
|
+
export declare class NodeFsVFS extends VFS.Base {
|
|
12
|
+
name: string;
|
|
13
|
+
private directory;
|
|
14
|
+
private mapIdToFile;
|
|
15
|
+
constructor(directory: string);
|
|
16
|
+
private resolvePath;
|
|
17
|
+
xOpen(name: string | null, fileId: number, flags: number, pOutFlags: DataView): number;
|
|
18
|
+
xClose(fileId: number): number;
|
|
19
|
+
xRead(fileId: number, pData_: {
|
|
20
|
+
size: number;
|
|
21
|
+
value: Uint8Array;
|
|
22
|
+
}, iOffset: number): number;
|
|
23
|
+
xWrite(fileId: number, pData_: {
|
|
24
|
+
size: number;
|
|
25
|
+
value: Uint8Array;
|
|
26
|
+
}, iOffset: number): number;
|
|
27
|
+
xTruncate(fileId: number, iSize: number): number;
|
|
28
|
+
xSync(fileId: number, _flags: number): number;
|
|
29
|
+
xFileSize(fileId: number, pSize64: DataView): number;
|
|
30
|
+
xDelete(name: string, _syncDir: number): number;
|
|
31
|
+
xAccess(name: string, _flags: number, pResOut: DataView): number;
|
|
32
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js file-system VFS for wa-sqlite.
|
|
3
|
+
*
|
|
4
|
+
* Extends VFS.Base using Node.js `fs` sync APIs for file persistence.
|
|
5
|
+
* Follows the MemoryVFS pattern but writes to real files on disk.
|
|
6
|
+
*
|
|
7
|
+
* Locking: inherited no-ops from VFS.Base (sufficient for single-process).
|
|
8
|
+
* This VFS will later be replaceable by the encrypted VFS from the storage branch.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import * as VFS from 'wa-sqlite/src/VFS.js';
|
|
13
|
+
export class NodeFsVFS extends VFS.Base {
|
|
14
|
+
constructor(directory) {
|
|
15
|
+
super();
|
|
16
|
+
Object.defineProperty(this, "name", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: 'node-fs'
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "directory", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(this, "mapIdToFile", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: new Map()
|
|
33
|
+
});
|
|
34
|
+
this.mxPathName = 255;
|
|
35
|
+
this.directory = directory;
|
|
36
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
resolvePath(name) {
|
|
39
|
+
return path.join(this.directory, name);
|
|
40
|
+
}
|
|
41
|
+
xOpen(name, fileId, flags, pOutFlags) {
|
|
42
|
+
name =
|
|
43
|
+
name || Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);
|
|
44
|
+
const filePath = this.resolvePath(name);
|
|
45
|
+
let openFlags = fs.constants.O_RDWR;
|
|
46
|
+
if (flags & VFS.SQLITE_OPEN_CREATE) {
|
|
47
|
+
openFlags |= fs.constants.O_CREAT;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const fd = fs.openSync(filePath, openFlags, 0o644);
|
|
51
|
+
this.mapIdToFile.set(fileId, { name, fd, flags });
|
|
52
|
+
pOutFlags.setInt32(0, flags, true);
|
|
53
|
+
return VFS.SQLITE_OK;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return VFS.SQLITE_CANTOPEN;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
xClose(fileId) {
|
|
60
|
+
const file = this.mapIdToFile.get(fileId);
|
|
61
|
+
if (!file)
|
|
62
|
+
return VFS.SQLITE_OK;
|
|
63
|
+
this.mapIdToFile.delete(fileId);
|
|
64
|
+
try {
|
|
65
|
+
fs.closeSync(file.fd);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// ignore close errors
|
|
69
|
+
}
|
|
70
|
+
if (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
|
|
71
|
+
try {
|
|
72
|
+
fs.unlinkSync(this.resolvePath(file.name));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// ignore delete errors
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return VFS.SQLITE_OK;
|
|
79
|
+
}
|
|
80
|
+
// wa-sqlite d.ts declares pData as { size; value } but runtime passes
|
|
81
|
+
// a plain Uint8Array. Cast to match the base class signature.
|
|
82
|
+
xRead(fileId, pData_, iOffset) {
|
|
83
|
+
const pData = pData_;
|
|
84
|
+
const file = this.mapIdToFile.get(fileId);
|
|
85
|
+
if (!file)
|
|
86
|
+
return VFS.SQLITE_IOERR;
|
|
87
|
+
try {
|
|
88
|
+
const nRead = fs.readSync(file.fd, pData, 0, pData.byteLength, iOffset);
|
|
89
|
+
if (nRead < pData.byteLength) {
|
|
90
|
+
pData.fill(0, nRead);
|
|
91
|
+
return VFS.SQLITE_IOERR_SHORT_READ;
|
|
92
|
+
}
|
|
93
|
+
return VFS.SQLITE_OK;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return VFS.SQLITE_IOERR;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
xWrite(fileId, pData_, iOffset) {
|
|
100
|
+
const pData = pData_;
|
|
101
|
+
const file = this.mapIdToFile.get(fileId);
|
|
102
|
+
if (!file)
|
|
103
|
+
return VFS.SQLITE_IOERR;
|
|
104
|
+
try {
|
|
105
|
+
fs.writeSync(file.fd, pData, 0, pData.byteLength, iOffset);
|
|
106
|
+
return VFS.SQLITE_OK;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return VFS.SQLITE_IOERR;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
xTruncate(fileId, iSize) {
|
|
113
|
+
const file = this.mapIdToFile.get(fileId);
|
|
114
|
+
if (!file)
|
|
115
|
+
return VFS.SQLITE_IOERR;
|
|
116
|
+
try {
|
|
117
|
+
fs.ftruncateSync(file.fd, iSize);
|
|
118
|
+
return VFS.SQLITE_OK;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return VFS.SQLITE_IOERR;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
xSync(fileId, _flags) {
|
|
125
|
+
const file = this.mapIdToFile.get(fileId);
|
|
126
|
+
if (!file)
|
|
127
|
+
return VFS.SQLITE_IOERR;
|
|
128
|
+
try {
|
|
129
|
+
fs.fsyncSync(file.fd);
|
|
130
|
+
return VFS.SQLITE_OK;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return VFS.SQLITE_IOERR;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
xFileSize(fileId, pSize64) {
|
|
137
|
+
const file = this.mapIdToFile.get(fileId);
|
|
138
|
+
if (!file)
|
|
139
|
+
return VFS.SQLITE_IOERR;
|
|
140
|
+
try {
|
|
141
|
+
const size = fs.fstatSync(file.fd).size;
|
|
142
|
+
pSize64.setBigInt64(0, BigInt(size), true);
|
|
143
|
+
return VFS.SQLITE_OK;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return VFS.SQLITE_IOERR;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
xDelete(name, _syncDir) {
|
|
150
|
+
try {
|
|
151
|
+
fs.unlinkSync(this.resolvePath(name));
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
if (e.code !== 'ENOENT') {
|
|
155
|
+
return VFS.SQLITE_IOERR;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return VFS.SQLITE_OK;
|
|
159
|
+
}
|
|
160
|
+
xAccess(name, _flags, pResOut) {
|
|
161
|
+
const exists = fs.existsSync(this.resolvePath(name));
|
|
162
|
+
pResOut.setInt32(0, exists ? 1 : 0, true);
|
|
163
|
+
return VFS.SQLITE_OK;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { DatabaseConnection } from '../sqlite';
|
|
2
|
+
export declare class ActiveSeekerQueries {
|
|
3
|
+
private conn;
|
|
4
|
+
constructor(conn: DatabaseConnection);
|
|
5
|
+
replaceAll(seekers: Uint8Array[]): Promise<void>;
|
|
6
|
+
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import * as schema from '../schema';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
export class ActiveSeekerQueries {
|
|
3
|
+
constructor(conn) {
|
|
4
|
+
Object.defineProperty(this, "conn", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: conn
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async replaceAll(seekers) {
|
|
12
|
+
await this.conn.withTransaction(async () => {
|
|
13
|
+
await this.conn.db.delete(schema.activeSeekers);
|
|
14
|
+
if (seekers.length > 0) {
|
|
15
|
+
await this.conn.db
|
|
16
|
+
.insert(schema.activeSeekers)
|
|
17
|
+
.values(seekers.map(seeker => ({ seeker })));
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
13
21
|
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
1
|
+
import type { DatabaseConnection } from '../sqlite';
|
|
2
|
+
export declare class AnnouncementCursorQueries {
|
|
3
|
+
private conn;
|
|
4
|
+
constructor(conn: DatabaseConnection);
|
|
5
|
+
get(userId: string): Promise<string | undefined>;
|
|
6
|
+
upsert(userId: string, counter: string): Promise<void>;
|
|
7
|
+
}
|
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import { eq } from 'drizzle-orm';
|
|
2
2
|
import * as schema from '../schema';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
3
|
+
export class AnnouncementCursorQueries {
|
|
4
|
+
constructor(conn) {
|
|
5
|
+
Object.defineProperty(this, "conn", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: conn
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async get(userId) {
|
|
13
|
+
const row = await this.conn.db
|
|
14
|
+
.select({ counter: schema.announcementCursors.counter })
|
|
15
|
+
.from(schema.announcementCursors)
|
|
16
|
+
.where(eq(schema.announcementCursors.userId, userId))
|
|
17
|
+
.get();
|
|
18
|
+
return row?.counter;
|
|
19
|
+
}
|
|
20
|
+
async upsert(userId, counter) {
|
|
21
|
+
await this.conn.db
|
|
22
|
+
.insert(schema.announcementCursors)
|
|
23
|
+
.values({ userId, counter })
|
|
24
|
+
.onConflictDoUpdate({
|
|
25
|
+
target: schema.announcementCursors.userId,
|
|
26
|
+
set: { counter },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
20
29
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import * as schema from '../schema';
|
|
2
|
+
import type { DatabaseConnection } from '../sqlite';
|
|
2
3
|
import { Contact } from '../db';
|
|
3
4
|
export type ContactRow = typeof schema.contacts.$inferSelect;
|
|
4
5
|
type ContactInsert = typeof schema.contacts.$inferInsert;
|
|
5
|
-
export declare
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
export declare class ContactQueries {
|
|
7
|
+
private conn;
|
|
8
|
+
constructor(conn: DatabaseConnection);
|
|
9
|
+
getByOwnerAndUser(ownerUserId: string, userId: string): Promise<ContactRow | undefined>;
|
|
10
|
+
getByOwner(ownerUserId: string): Promise<Contact[]>;
|
|
11
|
+
insert(values: ContactInsert): Promise<number>;
|
|
12
|
+
updateByOwnerAndUser(ownerUserId: string, userId: string, data: Partial<ContactInsert>): Promise<void>;
|
|
13
|
+
deleteByOwnerAndUser(ownerUserId: string, userId: string): Promise<void>;
|
|
14
|
+
getNamesByPrefix(ownerUserId: string, prefix: string): Promise<{
|
|
15
|
+
name: string;
|
|
16
|
+
}[]>;
|
|
17
|
+
}
|
|
13
18
|
export {};
|
|
@@ -1,43 +1,52 @@
|
|
|
1
1
|
import { eq, and, sql } from 'drizzle-orm';
|
|
2
2
|
import * as schema from '../schema';
|
|
3
|
-
import { getSqliteDb, getLastInsertRowId } from '../sqlite';
|
|
4
|
-
export async function getContactByOwnerAndUser(ownerUserId, userId) {
|
|
5
|
-
return getSqliteDb()
|
|
6
|
-
.select()
|
|
7
|
-
.from(schema.contacts)
|
|
8
|
-
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), eq(schema.contacts.userId, userId)))
|
|
9
|
-
.get();
|
|
10
|
-
}
|
|
11
|
-
export async function getContactsByOwner(ownerUserId) {
|
|
12
|
-
return getSqliteDb()
|
|
13
|
-
.select()
|
|
14
|
-
.from(schema.contacts)
|
|
15
|
-
.where(eq(schema.contacts.ownerUserId, ownerUserId))
|
|
16
|
-
.all();
|
|
17
|
-
}
|
|
18
|
-
export async function insertContact(values) {
|
|
19
|
-
await getSqliteDb().insert(schema.contacts).values(values);
|
|
20
|
-
return getLastInsertRowId();
|
|
21
|
-
}
|
|
22
|
-
export async function updateContactByOwnerAndUser(ownerUserId, userId, data) {
|
|
23
|
-
await getSqliteDb()
|
|
24
|
-
.update(schema.contacts)
|
|
25
|
-
.set(data)
|
|
26
|
-
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), eq(schema.contacts.userId, userId)));
|
|
27
|
-
}
|
|
28
|
-
export async function deleteContactByOwnerAndUser(ownerUserId, userId) {
|
|
29
|
-
await getSqliteDb()
|
|
30
|
-
.delete(schema.contacts)
|
|
31
|
-
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), eq(schema.contacts.userId, userId)));
|
|
32
|
-
}
|
|
33
3
|
/** Escape LIKE wildcard characters so they match literally. */
|
|
34
4
|
function escapeLike(value) {
|
|
35
5
|
return value.replace(/[%_]/g, '\\$&');
|
|
36
6
|
}
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
7
|
+
export class ContactQueries {
|
|
8
|
+
constructor(conn) {
|
|
9
|
+
Object.defineProperty(this, "conn", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: conn
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async getByOwnerAndUser(ownerUserId, userId) {
|
|
17
|
+
return this.conn.db
|
|
18
|
+
.select()
|
|
19
|
+
.from(schema.contacts)
|
|
20
|
+
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), eq(schema.contacts.userId, userId)))
|
|
21
|
+
.get();
|
|
22
|
+
}
|
|
23
|
+
async getByOwner(ownerUserId) {
|
|
24
|
+
return this.conn.db
|
|
25
|
+
.select()
|
|
26
|
+
.from(schema.contacts)
|
|
27
|
+
.where(eq(schema.contacts.ownerUserId, ownerUserId))
|
|
28
|
+
.all();
|
|
29
|
+
}
|
|
30
|
+
async insert(values) {
|
|
31
|
+
await this.conn.db.insert(schema.contacts).values(values);
|
|
32
|
+
return this.conn.getLastInsertRowId();
|
|
33
|
+
}
|
|
34
|
+
async updateByOwnerAndUser(ownerUserId, userId, data) {
|
|
35
|
+
await this.conn.db
|
|
36
|
+
.update(schema.contacts)
|
|
37
|
+
.set(data)
|
|
38
|
+
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), eq(schema.contacts.userId, userId)));
|
|
39
|
+
}
|
|
40
|
+
async deleteByOwnerAndUser(ownerUserId, userId) {
|
|
41
|
+
await this.conn.db
|
|
42
|
+
.delete(schema.contacts)
|
|
43
|
+
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), eq(schema.contacts.userId, userId)));
|
|
44
|
+
}
|
|
45
|
+
async getNamesByPrefix(ownerUserId, prefix) {
|
|
46
|
+
return this.conn.db
|
|
47
|
+
.select({ name: schema.contacts.name })
|
|
48
|
+
.from(schema.contacts)
|
|
49
|
+
.where(and(eq(schema.contacts.ownerUserId, ownerUserId), sql `${schema.contacts.name} LIKE ${escapeLike(prefix) + '%'} ESCAPE '\\'`))
|
|
50
|
+
.all();
|
|
51
|
+
}
|
|
43
52
|
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import * as schema from '../schema';
|
|
2
|
+
import type { DatabaseConnection } from '../sqlite';
|
|
2
3
|
import type { DiscussionStatus } from '../db';
|
|
3
4
|
export type DiscussionRow = typeof schema.discussions.$inferSelect;
|
|
4
5
|
type DiscussionInsert = typeof schema.discussions.$inferInsert;
|
|
5
|
-
export declare
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export declare function decrementUnreadCount(discussionId: number): Promise<void>;
|
|
20
|
-
export declare function getDiscussionsByOwnerAndStatus(ownerUserId: string, status: DiscussionStatus): Promise<DiscussionRow[]>;
|
|
6
|
+
export declare class DiscussionQueries {
|
|
7
|
+
private conn;
|
|
8
|
+
constructor(conn: DatabaseConnection);
|
|
9
|
+
getByOwnerAndContact(ownerUserId: string, contactUserId: string): Promise<DiscussionRow | undefined>;
|
|
10
|
+
getByOwner(ownerUserId: string): Promise<DiscussionRow[]>;
|
|
11
|
+
getById(id: number): Promise<DiscussionRow | undefined>;
|
|
12
|
+
insert(values: DiscussionInsert): Promise<number>;
|
|
13
|
+
updateById(id: number, data: Partial<DiscussionInsert>): Promise<void>;
|
|
14
|
+
deleteById(id: number): Promise<void>;
|
|
15
|
+
deleteByOwnerAndContact(ownerUserId: string, contactUserId: string): Promise<void>;
|
|
16
|
+
incrementUnreadCount(discussionId: number): Promise<void>;
|
|
17
|
+
decrementUnreadCount(discussionId: number): Promise<void>;
|
|
18
|
+
getByOwnerAndStatus(ownerUserId: string, status: DiscussionStatus): Promise<DiscussionRow[]>;
|
|
19
|
+
}
|
|
21
20
|
export {};
|