chore: update dependencies

This commit is contained in:
Samuel 2025-01-20 14:58:10 +01:00
parent 2d9f108ed2
commit 0e1eed664d
14 changed files with 1371 additions and 640 deletions

141
src/db/db-queries.ts Normal file
View file

@ -0,0 +1,141 @@
import { sql, type NotNull } from "kysely";
import { db, kyselyDb, SELF_ID } from "./db";
import { cached } from "../lib/db-cache";
export const loadDb = (statements: string[], progressCallback?: (percentage: number) => void) => {
const length = statements.length;
let percentage = 0;
for (let i = 0; i < length; i++) {
const statement = statements[i];
const newPercentage = Math.round((i / length) * 100);
try {
if (progressCallback && newPercentage !== percentage) {
progressCallback(newPercentage);
percentage = newPercentage;
}
db.exec(statement);
} catch (e) {
throw new Error(`statement failed: ${statement}`, {
cause: e,
});
}
}
};
const allThreadsOverviewQueryRaw = () =>
kyselyDb
.selectFrom("thread")
.innerJoin(
(eb) =>
eb
.selectFrom("message")
.select((eb) => ["message.thread_id", eb.fn.countAll().as("message_count")])
.where((eb) => {
return eb.and([eb("message.body", "is not", null), eb("message.body", "is not", "")]);
})
.groupBy("message.thread_id")
.as("message"),
(join) => join.onRef("message.thread_id", "=", "thread._id"),
)
.innerJoin("recipient", "thread.recipient_id", "recipient._id")
.leftJoin("groups", "recipient._id", "groups.recipient_id")
.select([
"thread._id as thread_id",
"thread.recipient_id",
"thread.archived",
"recipient.profile_joined_name",
"recipient.system_joined_name",
"groups.title",
"message_count",
"thread.date as last_message_date",
"recipient.nickname_joined_name",
])
.where("message_count", ">", 0)
.$narrowType<{
thread_id: NotNull;
archived: NotNull;
message_count: number;
}>()
.execute();
export const allThreadsOverviewQuery = cached(allThreadsOverviewQueryRaw);
const overallSentMessagesQueryRaw = (recipientId: number) =>
kyselyDb
.selectFrom("message")
.select((eb) => eb.fn.countAll().as("messageCount"))
.where((eb) =>
eb.and([
eb("message.from_recipient_id", "=", recipientId),
eb("message.body", "is not", null),
eb("message.body", "!=", ""),
]),
)
.executeTakeFirst();
export const overallSentMessagesQuery = cached(overallSentMessagesQueryRaw);
const dmPartnerRecipientQueryRaw = (dmId: number) =>
kyselyDb
.selectFrom("recipient")
.select([
"recipient._id",
"recipient.system_joined_name",
"recipient.profile_joined_name",
"recipient.nickname_joined_name",
])
.innerJoin("thread", "recipient._id", "thread.recipient_id")
.where((eb) => eb.and([eb("thread._id", "=", dmId), eb("recipient._id", "!=", SELF_ID)]))
.$narrowType<{
_id: number;
}>()
.executeTakeFirst();
export const dmPartnerRecipientQuery = cached(dmPartnerRecipientQueryRaw);
const threadSentMessagesOverviewQueryRaw = (threadId: number) =>
kyselyDb
.selectFrom("message")
.select(["from_recipient_id", sql<string>`datetime(date_sent / 1000, 'unixepoch')`.as("message_datetime")])
.orderBy(["message_datetime"])
.where((eb) => eb.and([eb("body", "is not", null), eb("body", "!=", ""), eb("thread_id", "=", threadId)]))
.execute();
export const threadSentMessagesOverviewQuery = cached(threadSentMessagesOverviewQueryRaw);
const threadMostUsedWordsQueryRaw = (threadId: number, limit = 10) =>
kyselyDb
.withRecursive("words", (eb) => {
return eb
.selectFrom("message")
.select([
sql`LOWER(substr(body, 1, instr(body || ' ', ' ') - 1))`.as("word"),
sql`substr(body, instr(body || ' ', ' ') + 1)`.as("rest"),
])
.where((eb) => eb.and([eb("body", "is not", null), eb("thread_id", "=", threadId)]))
.unionAll((ebInner) => {
return ebInner
.selectFrom("words")
.select([
sql`LOWER(substr(rest, 1, instr(rest || ' ', ' ') - 1))`.as("word"),
sql`substr(rest, instr(rest || ' ', ' ') + 1)`.as("rest"),
])
.where("rest", "<>", "");
});
})
.selectFrom("words")
.select((eb) => ["word", eb.fn.countAll().as("count")])
.where("word", "<>", "")
.groupBy("word")
.orderBy("count desc")
.limit(limit)
.$narrowType<{
count: number;
}>()
.execute();
export const threadMostUsedWordsQuery = cached(threadMostUsedWordsQueryRaw);

708
src/db/db-schema.d.ts vendored Normal file
View file

@ -0,0 +1,708 @@
/**
* This file was generated by kysely-codegen.
* Please do not edit it manually.
*/
import type { ColumnType } from "kysely";
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>;
export interface Attachment {
_id: Generated<number | null>;
archive_cdn: Generated<number | null>;
archive_media_id: Generated<string | null>;
archive_media_name: Generated<string | null>;
archive_thumbnail_media_id: Generated<string | null>;
archive_transfer_file: Generated<string | null>;
archive_transfer_state: Generated<number | null>;
attachment_uuid: Generated<string | null>;
blur_hash: Generated<string | null>;
borderless: Generated<number | null>;
caption: Generated<string | null>;
cdn_number: Generated<number | null>;
content_type: string | null;
data_file: string | null;
data_hash_end: Generated<string | null>;
data_hash_start: Generated<string | null>;
data_random: Buffer | null;
data_size: number | null;
display_order: Generated<number | null>;
fast_preflight_id: string | null;
file_name: string | null;
height: Generated<number | null>;
message_id: number | null;
offload_restored_at: Generated<number | null>;
quote: Generated<number | null>;
remote_digest: Buffer | null;
remote_incremental_digest: Buffer | null;
remote_incremental_digest_chunk_size: Generated<number | null>;
remote_iv: Generated<Buffer | null>;
remote_key: string | null;
remote_location: string | null;
sticker_emoji: Generated<string | null>;
sticker_id: Generated<number | null>;
sticker_pack_id: Generated<string | null>;
sticker_pack_key: Generated<string | null>;
thumbnail_file: Generated<string | null>;
thumbnail_random: Generated<Buffer | null>;
thumbnail_restore_state: Generated<number | null>;
transfer_file: Generated<string | null>;
transfer_state: number | null;
transform_properties: Generated<string | null>;
upload_timestamp: Generated<number | null>;
video_gif: Generated<number | null>;
voice_note: Generated<number | null>;
width: Generated<number | null>;
}
export interface AvatarPicker {
_id: Generated<number | null>;
avatar: Buffer;
group_id: Generated<string | null>;
last_used: Generated<number | null>;
}
export interface Call {
_id: number | null;
call_id: number;
deletion_timestamp: Generated<number | null>;
direction: number;
event: number;
group_call_active: Generated<number | null>;
local_joined: Generated<number | null>;
message_id: Generated<number | null>;
peer: number;
read: Generated<number | null>;
ringer: Generated<number | null>;
timestamp: number;
type: number;
}
export interface CallLink {
_id: number | null;
admin_key: Buffer | null;
deletion_timestamp: Generated<number>;
expiration: number;
name: string;
recipient_id: number | null;
restrictions: number;
revoked: number;
room_id: string;
root_key: Buffer | null;
}
export interface Cds {
_id: number | null;
e164: string;
last_seen_at: Generated<number | null>;
}
export interface ChatColors {
_id: Generated<number | null>;
chat_colors: Buffer | null;
}
export interface ChatFolder {
_id: Generated<number | null>;
folder_type: Generated<number | null>;
is_muted: Generated<number | null>;
name: Generated<string | null>;
position: Generated<number | null>;
show_groups: Generated<number | null>;
show_individual: Generated<number | null>;
show_muted: Generated<number | null>;
show_unread: Generated<number | null>;
}
export interface ChatFolderMembership {
_id: Generated<number | null>;
chat_folder_id: number;
membership_type: Generated<number | null>;
thread_id: number;
}
export interface DistributionList {
_id: Generated<number | null>;
allows_replies: Generated<number | null>;
deletion_timestamp: Generated<number | null>;
distribution_id: string;
is_unknown: Generated<number | null>;
name: string;
privacy_mode: Generated<number | null>;
recipient_id: number | null;
}
export interface DistributionListMember {
_id: Generated<number | null>;
list_id: number;
privacy_mode: Generated<number | null>;
recipient_id: number;
}
export interface DonationReceipt {
_id: Generated<number | null>;
amount: string;
currency: string;
receipt_date: number;
receipt_type: string;
subscription_level: number;
}
export interface Drafts {
_id: number | null;
thread_id: number | null;
type: string | null;
value: string | null;
}
export interface EmojiSearch {
_id: number | null;
emoji: string;
label: string;
rank: Generated<number | null>;
}
export interface GroupMembership {
_id: number | null;
endorsement: Generated<Buffer | null>;
group_id: string;
recipient_id: number;
}
export interface GroupReceipts {
_id: number | null;
address: number | null;
mms_id: number | null;
status: number | null;
timestamp: number | null;
unidentified: Generated<number | null>;
}
export interface Groups {
_id: number | null;
active: Generated<number | null>;
avatar_content_type: Generated<string | null>;
avatar_digest: Generated<Buffer | null>;
avatar_id: Generated<number | null>;
avatar_key: Generated<Buffer | null>;
decrypted_group: Generated<Buffer | null>;
distribution_id: Generated<string | null>;
expected_v2_id: Generated<string | null>;
group_id: string;
group_send_endorsements_expiration: Generated<number | null>;
last_force_update_timestamp: Generated<number | null>;
master_key: Generated<Buffer | null>;
mms: Generated<number | null>;
recipient_id: number;
revision: Generated<Buffer | null>;
show_as_story_state: Generated<number | null>;
timestamp: Generated<number | null>;
title: Generated<string | null>;
unmigrated_v1_members: Generated<string | null>;
}
export interface Identities {
_id: Generated<number | null>;
address: number | null;
first_use: Generated<number | null>;
identity_key: string | null;
nonblocking_approval: Generated<number | null>;
timestamp: Generated<number | null>;
verified: Generated<number | null>;
}
export interface InAppPayment {
_id: number | null;
data: Buffer;
end_of_period: Generated<number | null>;
inserted_at: number;
notified: Generated<number | null>;
state: number;
subscriber_id: string | null;
type: number;
updated_at: number;
}
export interface InAppPaymentSubscriber {
_id: number | null;
currency_code: string;
payment_method_type: Generated<number | null>;
requires_cancel: Generated<number | null>;
subscriber_id: string;
type: number;
}
export interface KyberPrekey {
_id: number | null;
account_id: string;
key_id: number;
last_resort: number;
serialized: Buffer;
stale_timestamp: Generated<number>;
timestamp: number;
}
export interface Mention {
_id: Generated<number | null>;
message_id: number | null;
range_length: number | null;
range_start: number | null;
recipient_id: number | null;
thread_id: number | null;
}
export interface Message {
_id: Generated<number | null>;
body: string | null;
ct_l: string | null;
date_received: number;
date_sent: number;
date_server: Generated<number | null>;
exp: number | null;
expire_started: Generated<number | null>;
expire_timer_version: Generated<number>;
expires_in: Generated<number | null>;
export_state: Generated<Buffer | null>;
exported: Generated<number | null>;
from_device_id: number | null;
from_recipient_id: number;
has_delivery_receipt: Generated<number | null>;
has_read_receipt: Generated<number | null>;
latest_revision_id: Generated<number | null>;
link_previews: Generated<string | null>;
m_size: number | null;
m_type: number | null;
mentions_self: Generated<number | null>;
message_extras: Generated<Buffer | null>;
message_ranges: Generated<Buffer | null>;
mismatched_identities: Generated<string | null>;
network_failures: Generated<string | null>;
notified: Generated<number | null>;
notified_timestamp: Generated<number | null>;
original_message_id: Generated<number | null>;
parent_story_id: Generated<number | null>;
quote_author: Generated<number | null>;
quote_body: Generated<string | null>;
quote_id: Generated<number | null>;
quote_mentions: Generated<Buffer | null>;
quote_missing: Generated<number | null>;
quote_type: Generated<number | null>;
reactions_last_seen: Generated<number | null>;
reactions_unread: Generated<number | null>;
read: Generated<number | null>;
receipt_timestamp: Generated<number | null>;
remote_deleted: Generated<number | null>;
revision_number: Generated<number | null>;
scheduled_date: Generated<number | null>;
server_guid: Generated<string | null>;
shared_contacts: Generated<string | null>;
st: number | null;
story_type: Generated<number | null>;
subscription_id: Generated<number | null>;
thread_id: number;
to_recipient_id: number;
tr_id: string | null;
type: number;
unidentified: Generated<number | null>;
view_once: Generated<number | null>;
viewed: Generated<number | null>;
}
export interface MessageFts {
body: string | null;
thread_id: string | null;
}
export interface MessageFtsConfig {
k: string;
v: string | null;
}
export interface MessageFtsData {
block: Buffer | null;
id: number | null;
}
export interface MessageFtsDocsize {
id: number | null;
sz: Buffer | null;
}
export interface MessageFtsIdx {
pgno: string | null;
segid: string;
term: string;
}
export interface MslMessage {
_id: number | null;
message_id: number;
payload_id: number;
}
export interface MslPayload {
_id: number | null;
content: Buffer;
content_hint: number;
date_sent: number;
urgent: Generated<number>;
}
export interface MslRecipient {
_id: number | null;
device: number;
payload_id: number;
recipient_id: number;
}
export interface NameCollision {
_id: Generated<number | null>;
dismissed: Generated<number | null>;
hash: Generated<string | null>;
thread_id: number;
}
export interface NameCollisionMembership {
_id: Generated<number | null>;
collision_id: number;
profile_change_details: Generated<Buffer | null>;
recipient_id: number;
}
export interface NotificationProfile {
_id: Generated<number | null>;
allow_all_calls: Generated<number>;
allow_all_mentions: Generated<number>;
color: string;
created_at: number;
emoji: string;
name: string;
}
export interface NotificationProfileAllowedMembers {
_id: Generated<number | null>;
notification_profile_id: number;
recipient_id: number;
}
export interface NotificationProfileSchedule {
_id: Generated<number | null>;
days_enabled: string;
enabled: Generated<number>;
end: number;
notification_profile_id: number;
start: number;
}
export interface OneTimePrekeys {
_id: number | null;
account_id: string;
key_id: number;
private_key: string;
public_key: string;
stale_timestamp: Generated<number>;
}
export interface Payments {
_id: number | null;
amount: Buffer;
block_index: Generated<number | null>;
block_timestamp: Generated<number | null>;
direction: number | null;
failure_reason: number | null;
fee: Buffer;
note: Generated<string | null>;
payment_metadata: Generated<Buffer | null>;
receipt: Generated<Buffer | null>;
receipt_public_key: Generated<string | null>;
recipient: Generated<number | null>;
recipient_address: Generated<string | null>;
seen: number | null;
state: number | null;
timestamp: number | null;
transaction_record: Generated<Buffer | null>;
uuid: Generated<string | null>;
}
export interface PendingPniSignatureMessage {
_id: number | null;
device_id: number;
recipient_id: number;
sent_timestamp: number;
}
export interface PendingRetryReceipts {
_id: Generated<number | null>;
author: string;
device: number;
received_timestamp: string;
sent_timestamp: number;
thread_id: number;
}
export interface Reaction {
_id: number | null;
author_id: number;
date_received: number;
date_sent: number;
emoji: string;
message_id: number;
}
export interface Recipient {
_id: Generated<number | null>;
about: Generated<string | null>;
about_emoji: Generated<string | null>;
aci: Generated<string | null>;
avatar_color: Generated<string | null>;
badges: Generated<Buffer | null>;
blocked: Generated<number | null>;
call_link_room_id: Generated<string | null>;
call_ringtone: Generated<string | null>;
call_vibrate: Generated<number | null>;
capabilities: Generated<number | null>;
chat_colors: Generated<Buffer | null>;
custom_chat_colors_id: Generated<number | null>;
distribution_list_id: Generated<number | null>;
e164: Generated<string | null>;
email: Generated<string | null>;
extras: Generated<Buffer | null>;
group_id: Generated<string | null>;
groups_in_common: Generated<number | null>;
hidden: Generated<number | null>;
last_profile_fetch: Generated<number | null>;
last_session_reset: Generated<Buffer | null>;
mention_setting: Generated<number | null>;
message_expiration_time: Generated<number | null>;
message_expiration_time_version: Generated<number>;
message_ringtone: Generated<string | null>;
message_vibrate: Generated<number | null>;
mute_until: Generated<number | null>;
needs_pni_signature: Generated<number | null>;
nickname_family_name: Generated<string | null>;
nickname_given_name: Generated<string | null>;
nickname_joined_name: Generated<string | null>;
note: Generated<string | null>;
notification_channel: Generated<string | null>;
phone_number_discoverable: Generated<number | null>;
phone_number_sharing: Generated<number | null>;
pni: Generated<string | null>;
pni_signature_verified: Generated<number | null>;
profile_avatar: Generated<string | null>;
profile_family_name: Generated<string | null>;
profile_given_name: Generated<string | null>;
profile_joined_name: Generated<string | null>;
profile_key: Generated<string | null>;
profile_key_credential: Generated<string | null>;
profile_sharing: Generated<number | null>;
registered: Generated<number | null>;
reporting_token: Generated<Buffer | null>;
sealed_sender_mode: Generated<number | null>;
storage_service_id: Generated<string | null>;
storage_service_proto: Generated<string | null>;
system_contact_uri: Generated<string | null>;
system_family_name: Generated<string | null>;
system_given_name: Generated<string | null>;
system_info_pending: Generated<number | null>;
system_joined_name: Generated<string | null>;
system_nickname: Generated<string | null>;
system_phone_label: Generated<string | null>;
system_phone_type: Generated<number | null>;
system_photo_uri: Generated<string | null>;
type: Generated<number | null>;
unregistered_timestamp: Generated<number | null>;
username: Generated<string | null>;
wallpaper: Generated<Buffer | null>;
wallpaper_uri: Generated<string | null>;
}
export interface RemappedRecipients {
_id: Generated<number | null>;
new_id: number | null;
old_id: number | null;
}
export interface RemappedThreads {
_id: Generated<number | null>;
new_id: number | null;
old_id: number | null;
}
export interface RemoteMegaphone {
_id: number | null;
body: string;
conditional_id: string | null;
countries: string | null;
dont_show_after: number;
dont_show_before: number;
finished_at: Generated<number | null>;
image_uri: Generated<string | null>;
image_url: string | null;
minimum_version: number;
primary_action_data: Generated<string | null>;
primary_action_id: string | null;
primary_action_text: string | null;
priority: number;
secondary_action_data: Generated<string | null>;
secondary_action_id: string | null;
secondary_action_text: string | null;
seen_count: Generated<number | null>;
show_for_days: number;
shown_at: Generated<number | null>;
snoozed_at: Generated<number | null>;
title: string;
uuid: string;
}
export interface SenderKeys {
_id: Generated<number | null>;
address: string;
created_at: number;
device: number;
distribution_id: string;
record: Buffer;
}
export interface SenderKeyShared {
_id: Generated<number | null>;
address: string;
device: number;
distribution_id: string;
timestamp: Generated<number | null>;
}
export interface Sessions {
_id: Generated<number | null>;
account_id: string;
address: string;
device: number;
record: Buffer;
}
export interface SignedPrekeys {
_id: number | null;
account_id: string;
key_id: number;
private_key: string;
public_key: string;
signature: string;
timestamp: Generated<number | null>;
}
export interface Sticker {
_id: Generated<number | null>;
content_type: Generated<string | null>;
cover: number | null;
emoji: string;
file_length: number | null;
file_path: string;
file_random: Buffer | null;
installed: number | null;
last_used: number | null;
pack_author: string;
pack_id: string;
pack_key: string;
pack_order: number | null;
pack_title: string;
sticker_id: number | null;
}
export interface StorageKey {
_id: Generated<number | null>;
key: string | null;
type: number | null;
}
export interface StorySends {
_id: number | null;
allows_replies: number;
distribution_id: string;
message_id: number;
recipient_id: number;
sent_timestamp: number;
}
export interface Thread {
_id: Generated<number | null>;
active: Generated<number | null>;
archived: Generated<number | null>;
date: Generated<number | null>;
error: Generated<number | null>;
expires_in: Generated<number | null>;
has_delivery_receipt: Generated<number | null>;
has_read_receipt: Generated<number | null>;
has_sent: Generated<number | null>;
last_scrolled: Generated<number | null>;
last_seen: Generated<number | null>;
meaningful_messages: Generated<number | null>;
pinned: Generated<number | null>;
read: Generated<number | null>;
recipient_id: number;
snippet: string | null;
snippet_content_type: Generated<string | null>;
snippet_extras: Generated<string | null>;
snippet_message_extras: Generated<Buffer | null>;
snippet_type: Generated<number | null>;
snippet_uri: Generated<string | null>;
status: Generated<number | null>;
type: Generated<number | null>;
unread_count: Generated<number | null>;
unread_self_mention_count: Generated<number | null>;
}
export interface DB {
attachment: Attachment;
avatar_picker: AvatarPicker;
call: Call;
call_link: CallLink;
cds: Cds;
chat_colors: ChatColors;
chat_folder: ChatFolder;
chat_folder_membership: ChatFolderMembership;
distribution_list: DistributionList;
distribution_list_member: DistributionListMember;
donation_receipt: DonationReceipt;
drafts: Drafts;
emoji_search: EmojiSearch;
group_membership: GroupMembership;
group_receipts: GroupReceipts;
groups: Groups;
identities: Identities;
in_app_payment: InAppPayment;
in_app_payment_subscriber: InAppPaymentSubscriber;
kyber_prekey: KyberPrekey;
mention: Mention;
message: Message;
message_fts: MessageFts;
message_fts_config: MessageFtsConfig;
message_fts_data: MessageFtsData;
message_fts_docsize: MessageFtsDocsize;
message_fts_idx: MessageFtsIdx;
msl_message: MslMessage;
msl_payload: MslPayload;
msl_recipient: MslRecipient;
name_collision: NameCollision;
name_collision_membership: NameCollisionMembership;
notification_profile: NotificationProfile;
notification_profile_allowed_members: NotificationProfileAllowedMembers;
notification_profile_schedule: NotificationProfileSchedule;
one_time_prekeys: OneTimePrekeys;
payments: Payments;
pending_pni_signature_message: PendingPniSignatureMessage;
pending_retry_receipts: PendingRetryReceipts;
reaction: Reaction;
recipient: Recipient;
remapped_recipients: RemappedRecipients;
remapped_threads: RemappedThreads;
remote_megaphone: RemoteMegaphone;
sender_key_shared: SenderKeyShared;
sender_keys: SenderKeys;
sessions: Sessions;
signed_prekeys: SignedPrekeys;
sticker: Sticker;
storage_key: StorageKey;
story_sends: StorySends;
thread: Thread;
}

25
src/db/db.ts Normal file
View file

@ -0,0 +1,25 @@
import { makePersisted } from "@solid-primitives/storage";
import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
import { Kysely } from "kysely";
import type { DB } from "./db-schema";
import { OfficialWasmDialect } from "kysely-wasm";
import { createSignal } from "solid-js";
export const SELF_ID = 2;
const sqlite3 = await sqlite3InitModule({
print: console.log,
printErr: console.error,
});
export const db = new sqlite3.oo1.DB("signal");
const dialect = new OfficialWasmDialect({
database: db,
});
export const kyselyDb = new Kysely<DB>({
dialect,
});
export const [dbHash, setDbHash] = makePersisted(createSignal<number>());

2
src/db/index.ts Normal file
View file

@ -0,0 +1,2 @@
export * from "./db";
export * from "./db-queries";