@leemour/max-cli 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -9
- package/dist/cache/schema.d.ts +10 -1
- package/dist/cache/schema.d.ts.map +1 -1
- package/dist/cache/schema.js +71 -7
- package/dist/cache/schema.js.map +1 -1
- package/dist/cache/store.d.ts +84 -3
- package/dist/cache/store.d.ts.map +1 -1
- package/dist/cache/store.js +155 -25
- package/dist/cache/store.js.map +1 -1
- package/dist/client.d.ts +68 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +286 -31
- package/dist/client.js.map +1 -1
- package/dist/commands/chats.d.ts.map +1 -1
- package/dist/commands/chats.js +5 -9
- package/dist/commands/chats.js.map +1 -1
- package/dist/commands/contacts.d.ts.map +1 -1
- package/dist/commands/contacts.js +46 -5
- package/dist/commands/contacts.js.map +1 -1
- package/dist/commands/login.d.ts +15 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +43 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +10 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +22 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/me.d.ts +3 -0
- package/dist/commands/me.d.ts.map +1 -0
- package/dist/commands/me.js +18 -0
- package/dist/commands/me.js.map +1 -0
- package/dist/commands/messages.d.ts.map +1 -1
- package/dist/commands/messages.js +18 -3
- package/dist/commands/messages.js.map +1 -1
- package/dist/commands/paging.d.ts +38 -0
- package/dist/commands/paging.d.ts.map +1 -0
- package/dist/commands/paging.js +43 -0
- package/dist/commands/paging.js.map +1 -0
- package/dist/commands/send.d.ts +10 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +30 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +3 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -1
- package/dist/domain/map.d.ts +1 -0
- package/dist/domain/map.d.ts.map +1 -1
- package/dist/domain/map.js +2 -0
- package/dist/domain/map.js.map +1 -1
- package/dist/domain/models.d.ts +18 -0
- package/dist/domain/models.d.ts.map +1 -1
- package/dist/generated/operations.generated.d.ts +1 -0
- package/dist/generated/operations.generated.d.ts.map +1 -1
- package/dist/protocol/session.d.ts +54 -0
- package/dist/protocol/session.d.ts.map +1 -0
- package/dist/protocol/session.js +57 -0
- package/dist/protocol/session.js.map +1 -0
- package/dist/session/adopt.d.ts +22 -0
- package/dist/session/adopt.d.ts.map +1 -0
- package/dist/session/adopt.js +29 -0
- package/dist/session/adopt.js.map +1 -0
- package/dist/session/handshake.d.ts +14 -1
- package/dist/session/handshake.d.ts.map +1 -1
- package/dist/session/handshake.js +5 -5
- package/dist/session/handshake.js.map +1 -1
- package/dist/spec/operations/auth.d.ts +42 -0
- package/dist/spec/operations/auth.d.ts.map +1 -0
- package/dist/spec/operations/session.d.ts +16 -0
- package/dist/spec/operations/session.d.ts.map +1 -1
- package/dist/spec/operations/session.js +21 -1
- package/dist/spec/operations/session.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -14,9 +14,6 @@ max chats list --limit 5
|
|
|
14
14
|
max messages list "Иван Петров"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
> ⚠ **Версия `0.1.0` ещё не опубликована.** Под именем `@leemour/max-cli` на npm пока нет ничего.
|
|
18
|
-
> Пока — [установка из исходников](docs/installation.md).
|
|
19
|
-
|
|
20
17
|
## Что это даёт
|
|
21
18
|
|
|
22
19
|
- **Один запуск — одна операция.** Соединиться, сделать, напечатать, отключиться. Ничего не висит
|
|
@@ -39,6 +36,9 @@ max messages list "Иван Петров"
|
|
|
39
36
|
хэшем.
|
|
40
37
|
- **По умолчанию не записывается ничего.** Мессенджер, который сам собирает каталог с историей
|
|
41
38
|
того, кого вы читали, — это чужая жизнь в чужом логе; запись включается флагом.
|
|
39
|
+
- **Каждый вход спрашивает только то, что изменилось.** Метка времени прошлого входа уходит
|
|
40
|
+
обратно, и MAX присылает дельту, а не весь список заново — так что контакты остаются свежими,
|
|
41
|
+
не стоя ничего.
|
|
42
42
|
- **Два рантайма, и это проверяется.** Node 22+ и Bun; под обоими в CI выполняется собранная
|
|
43
43
|
команда, а не только проверка типов.
|
|
44
44
|
- **Мы выглядим как официальный клиент.** На проводе нет ни нашего имени, ни своего user-agent:
|
|
@@ -59,11 +59,16 @@ max messages list "Иван Петров"
|
|
|
59
59
|
Пакет — **`@leemour/max-cli`**, команда, которую он ставит, — **`max`**. Имя без области занято
|
|
60
60
|
чужим пакетом с 2018 года.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Запустить, ничего не устанавливая:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
npx @leemour/max-cli --help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Поставить насовсем:
|
|
63
69
|
|
|
64
70
|
```sh
|
|
65
|
-
|
|
66
|
-
cd max-cli && pnpm install && pnpm build && pnpm link --global
|
|
71
|
+
npm install -g @leemour/max-cli
|
|
67
72
|
max --version # 0.1.0
|
|
68
73
|
```
|
|
69
74
|
|
|
@@ -93,12 +98,20 @@ export MAX_PROFILE=personal # или на всю сессию оболочки
|
|
|
93
98
|
|
|
94
99
|
```sh
|
|
95
100
|
max chats list --limit 5
|
|
96
|
-
max contacts list
|
|
101
|
+
max contacts list # люди, с кем есть личный чат
|
|
97
102
|
max messages list 0 --limit 50 # по id чата
|
|
98
103
|
max messages list "Иван Петров" # или по части названия
|
|
99
104
|
max messages send 0 "текст"
|
|
100
105
|
```
|
|
101
106
|
|
|
107
|
+
Списки листаются одинаково везде, а история — по времени, потому что она к нему и привязана:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
max contacts list --limit 5 --page 2 # шестой по десятый
|
|
111
|
+
max contacts list --all --order name # всё, по алфавиту
|
|
112
|
+
max messages list 0 --before 2026-09-20T01:00:00Z
|
|
113
|
+
```
|
|
114
|
+
|
|
102
115
|
Что делала команда:
|
|
103
116
|
|
|
104
117
|
```sh
|
|
@@ -126,8 +139,11 @@ max chats list --json
|
|
|
126
139
|
```
|
|
127
140
|
|
|
128
141
|
`--json` — это **ровно одно значение JSON на stdout и больше ничего**: ни спиннера, ни галочки, ни
|
|
129
|
-
предупреждения. То же самое включается само, когда stdout не терминал.
|
|
130
|
-
|
|
142
|
+
предупреждения. То же самое включается само, когда stdout не терминал. Любой список отвечает одним
|
|
143
|
+
объектом — `{ "items": […], "page": 1, "limit": 20, "hasMore": true }` — и `--all` с `--offline`
|
|
144
|
+
отвечают **тем же**, чтобы разбирающему ответ не приходилось ветвиться на две формы.
|
|
145
|
+
|
|
146
|
+
Ошибка уходит на stderr, а stdout остаётся пустым, поэтому отказ невозможно принять за результат:
|
|
131
147
|
|
|
132
148
|
```json
|
|
133
149
|
{"error":{"code":"authentication_error","message":"no session for profile \"default\" — run `max session start`"}}
|
package/dist/cache/schema.d.ts
CHANGED
|
@@ -3,12 +3,21 @@ import type { CacheDatabase } from "./driver.js";
|
|
|
3
3
|
* **Raise this on every change to the statements below.** The second schema change is the one that
|
|
4
4
|
* corrupts somebody's file, because the first is always made while the only copy is your own.
|
|
5
5
|
*/
|
|
6
|
-
export declare const SCHEMA_VERSION =
|
|
6
|
+
export declare const SCHEMA_VERSION = 2;
|
|
7
7
|
/**
|
|
8
8
|
* Brings a file up to date, and **refuses a file from the future** rather than writing to it.
|
|
9
9
|
*
|
|
10
10
|
* A newer `max` may have added a column this one does not know about. Reading it is survivable;
|
|
11
11
|
* writing to it is how one version quietly destroys what another stored.
|
|
12
|
+
*
|
|
13
|
+
* **An older file is rebuilt, not altered.** Everything in here comes back from MAX, so a
|
|
14
|
+
* column-by-column migration would be code that runs once, is tested never, and is how the second
|
|
15
|
+
* schema change corrupts somebody's file. What a rebuild costs is one full login — which is what
|
|
16
|
+
* every command did before the delta sync existed.
|
|
17
|
+
*
|
|
18
|
+
* ⚠ **This stops being the right answer the day the database holds something MAX cannot re-send.**
|
|
19
|
+
* Contacts that are in no chat would be exactly that (`RES-7`), and the day they arrive this
|
|
20
|
+
* becomes `ALTER TABLE … ADD COLUMN` and this paragraph gets rewritten.
|
|
12
21
|
*/
|
|
13
22
|
export declare const migrate: (database: CacheDatabase) => void;
|
|
14
23
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/cache/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;GAGG;AACH,eAAO,MAAM,cAAc,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/cache/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;GAGG;AACH,eAAO,MAAM,cAAc,IAAI,CAAA;AAyH/B;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAAI,UAAU,aAAa,KAAG,IAgBjD,CAAA"}
|
package/dist/cache/schema.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* **Raise this on every change to the statements below.** The second schema change is the one that
|
|
3
3
|
* corrupts somebody's file, because the first is always made while the only copy is your own.
|
|
4
4
|
*/
|
|
5
|
-
export const SCHEMA_VERSION =
|
|
5
|
+
export const SCHEMA_VERSION = 2;
|
|
6
6
|
/**
|
|
7
7
|
* Everything the cache holds, and the indexes are part of it rather than an afterthought — each
|
|
8
8
|
* one exists for a query that is actually made.
|
|
@@ -25,12 +25,48 @@ const STATEMENTS = [
|
|
|
25
25
|
fetched_at INTEGER NOT NULL
|
|
26
26
|
)`,
|
|
27
27
|
`CREATE INDEX IF NOT EXISTS chats_by_recency ON chats (last_message_at DESC)`,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
name
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
/**
|
|
29
|
+
* **Everyone MAX has named for us, whatever the reason** — not an address book and not a contact
|
|
30
|
+
* list. A group member gets a row here because their name has to live somewhere; whether they
|
|
31
|
+
* are a *contact* is a question the query answers (a dialog with them exists), never a flag
|
|
32
|
+
* somebody has to maintain (`NEED-105`).
|
|
33
|
+
*
|
|
34
|
+
* `last_messaged_at` is stored rather than derived because it is the default order: recomputing
|
|
35
|
+
* `MAX(chats.last_message_at)` per person on every listing is the work this table exists to
|
|
36
|
+
* avoid. `source` records how we met them and is diagnostic — it is not what the filter reads.
|
|
37
|
+
*/
|
|
38
|
+
`CREATE TABLE IF NOT EXISTS people (
|
|
39
|
+
id TEXT PRIMARY KEY,
|
|
40
|
+
name TEXT,
|
|
41
|
+
username TEXT,
|
|
42
|
+
description TEXT,
|
|
43
|
+
last_messaged_at INTEGER,
|
|
44
|
+
source TEXT NOT NULL,
|
|
45
|
+
fetched_at INTEGER NOT NULL
|
|
46
|
+
)`,
|
|
47
|
+
`CREATE INDEX IF NOT EXISTS people_by_recency ON people (last_messaged_at DESC)`,
|
|
48
|
+
/**
|
|
49
|
+
* Who is in what. It is what the client used to work out in memory and throw away, and it is how
|
|
50
|
+
* "which groups do I share with this person" becomes one indexed lookup — every chat in this
|
|
51
|
+
* database is one the owner is in, so the chats of a person *are* the shared set.
|
|
52
|
+
*/
|
|
53
|
+
`CREATE TABLE IF NOT EXISTS chat_members (
|
|
54
|
+
chat_id TEXT NOT NULL,
|
|
55
|
+
person_id TEXT NOT NULL,
|
|
56
|
+
PRIMARY KEY (chat_id, person_id)
|
|
57
|
+
)`,
|
|
58
|
+
`CREATE INDEX IF NOT EXISTS members_by_person ON chat_members (person_id)`,
|
|
59
|
+
/**
|
|
60
|
+
* Where the last login's `time` is kept, so the next one can ask for the delta instead of the
|
|
61
|
+
* whole collection. One row, and the `CHECK` is what keeps it that way.
|
|
62
|
+
*
|
|
63
|
+
* It lives here rather than in the profile's state file because a marker that outlives the rows
|
|
64
|
+
* it describes is a promise this database cannot keep: `max cache clear` has to forget both in
|
|
65
|
+
* the same breath, and a marker in another file would survive it.
|
|
66
|
+
*/
|
|
67
|
+
`CREATE TABLE IF NOT EXISTS sync_marker (
|
|
68
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
69
|
+
marker INTEGER NOT NULL
|
|
34
70
|
)`,
|
|
35
71
|
`CREATE TABLE IF NOT EXISTS messages (
|
|
36
72
|
chat_id TEXT NOT NULL,
|
|
@@ -47,6 +83,8 @@ const STATEMENTS = [
|
|
|
47
83
|
)`,
|
|
48
84
|
`CREATE INDEX IF NOT EXISTS messages_by_time ON messages (chat_id, time DESC)`,
|
|
49
85
|
`CREATE INDEX IF NOT EXISTS messages_by_update ON messages (chat_id, update_time)`,
|
|
86
|
+
/** `messages list --before <id>` resolves an id to its `time`, without knowing which chat. */
|
|
87
|
+
`CREATE INDEX IF NOT EXISTS messages_by_id ON messages (id)`,
|
|
50
88
|
/**
|
|
51
89
|
* The windows we claim to hold **completely**, which is what makes "absent means deleted" safe
|
|
52
90
|
* to believe. A message outside every range is a message we have never looked for; a message
|
|
@@ -81,6 +119,15 @@ const STATEMENTS = [
|
|
|
81
119
|
*
|
|
82
120
|
* A newer `max` may have added a column this one does not know about. Reading it is survivable;
|
|
83
121
|
* writing to it is how one version quietly destroys what another stored.
|
|
122
|
+
*
|
|
123
|
+
* **An older file is rebuilt, not altered.** Everything in here comes back from MAX, so a
|
|
124
|
+
* column-by-column migration would be code that runs once, is tested never, and is how the second
|
|
125
|
+
* schema change corrupts somebody's file. What a rebuild costs is one full login — which is what
|
|
126
|
+
* every command did before the delta sync existed.
|
|
127
|
+
*
|
|
128
|
+
* ⚠ **This stops being the right answer the day the database holds something MAX cannot re-send.**
|
|
129
|
+
* Contacts that are in no chat would be exactly that (`RES-7`), and the day they arrive this
|
|
130
|
+
* becomes `ALTER TABLE … ADD COLUMN` and this paragraph gets rewritten.
|
|
84
131
|
*/
|
|
85
132
|
export const migrate = (database) => {
|
|
86
133
|
const current = Number(database.prepare("PRAGMA user_version").get()?.user_version ?? 0);
|
|
@@ -88,8 +135,25 @@ export const migrate = (database) => {
|
|
|
88
135
|
throw new Error(`this cache was written by a newer max (schema ${current}, this one speaks ${SCHEMA_VERSION}) — ` +
|
|
89
136
|
"run `max cache clear`, or use the newer version");
|
|
90
137
|
}
|
|
138
|
+
if (current > 0 && current < SCHEMA_VERSION)
|
|
139
|
+
rebuild(database);
|
|
91
140
|
for (const statement of STATEMENTS)
|
|
92
141
|
database.exec(statement);
|
|
93
142
|
database.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);
|
|
94
143
|
};
|
|
144
|
+
/**
|
|
145
|
+
* Drops what is there by asking the file rather than by listing what we think a previous version
|
|
146
|
+
* wrote. A version that added a table we have since forgotten would otherwise survive the rebuild
|
|
147
|
+
* and collide with a later name.
|
|
148
|
+
*
|
|
149
|
+
* The `fetched` table goes with the rest on purpose: it records that a collection was complete,
|
|
150
|
+
* and keeping it would claim a sweep whose rows have just been thrown away.
|
|
151
|
+
*/
|
|
152
|
+
const rebuild = (database) => {
|
|
153
|
+
const tables = database
|
|
154
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
|
|
155
|
+
.all();
|
|
156
|
+
for (const { name } of tables)
|
|
157
|
+
database.exec(`DROP TABLE IF EXISTS "${String(name)}"`);
|
|
158
|
+
};
|
|
95
159
|
//# sourceMappingURL=schema.js.map
|
package/dist/cache/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/cache/schema.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAA;AAE/B;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG;IACjB;;;;;;;;;KASG;IACH,6EAA6E;IAE7E
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/cache/schema.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAA;AAE/B;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG;IACjB;;;;;;;;;KASG;IACH,6EAA6E;IAE7E;;;;;;;;;OASG;IACH;;;;;;;;KAQG;IACH,gFAAgF;IAEhF;;;;OAIG;IACH;;;;KAIG;IACH,0EAA0E;IAE1E;;;;;;;OAOG;IACH;;;KAGG;IAEH;;;;;;;;;;;;KAYG;IACH,8EAA8E;IAC9E,kFAAkF;IAClF,8FAA8F;IAC9F,4DAA4D;IAE5D;;;;OAIG;IACH;;;;;KAKG;IACH,0EAA0E;IAE1E;;;;OAIG;IACH;;;KAGG;IAEH;;;;;;KAMG;CACJ,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,QAAuB,EAAQ,EAAE;IACvD,MAAM,OAAO,GAAG,MAAM,CACnB,QAAQ,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAAgC,EAAE,YAAY,IAAI,CAAC,CAChG,CAAA;IAED,IAAI,OAAO,GAAG,cAAc,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,iDAAiD,OAAO,qBAAqB,cAAc,MAAM;YAC/F,iDAAiD,CACpD,CAAA;IACH,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO,GAAG,cAAc;QAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAE9D,KAAK,MAAM,SAAS,IAAI,UAAU;QAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAC5D,QAAQ,CAAC,IAAI,CAAC,yBAAyB,cAAc,EAAE,CAAC,CAAA;AAC1D,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG,CAAC,QAAuB,EAAQ,EAAE;IAChD,MAAM,MAAM,GAAG,QAAQ;SACpB,OAAO,CAAC,kFAAkF,CAAC;SAC3F,GAAG,EAAE,CAAA;IAER,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,MAAM;QAAE,QAAQ,CAAC,IAAI,CAAC,yBAAyB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxF,CAAC,CAAA"}
|
package/dist/cache/store.d.ts
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
import type { Chat, Contact, Id, Message } from "../domain/models.js";
|
|
2
2
|
import type { CacheDatabase } from "./driver.js";
|
|
3
|
+
/** How we came to know a person. Diagnostic — what makes somebody a *contact* is the query. */
|
|
4
|
+
export type PersonSource = "login" | "info" | "participant" | "sync";
|
|
5
|
+
export type PersonOrder = "recent" | "name";
|
|
6
|
+
export interface PageOptions {
|
|
7
|
+
order: PersonOrder;
|
|
8
|
+
limit: number;
|
|
9
|
+
offset: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* One login's worth of change: the chats that moved, the people that changed, who is in which
|
|
13
|
+
* chat, and the `time` MAX answered with.
|
|
14
|
+
*
|
|
15
|
+
* `members` is keyed by chat id and holds that chat's **whole** membership minus ourselves, so a
|
|
16
|
+
* chat named here has its rows replaced rather than added to. A chat absent from the map keeps
|
|
17
|
+
* the members it already had.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* What a merge did, in counts and **nothing else** — no name, no username, no description. It is
|
|
21
|
+
* what `max contacts sync` prints, and a summary that named anybody would be the one place the
|
|
22
|
+
* sixth constraint leaks.
|
|
23
|
+
*
|
|
24
|
+
* `changed` is "already known and sent again". A delta carries only what moved, so for an ordinary
|
|
25
|
+
* login that is exactly what it sounds like; for a full re-take, where MAX resends everything, it
|
|
26
|
+
* reads as "re-sent" instead — which is what `full` in the command's output is there to say.
|
|
27
|
+
*/
|
|
28
|
+
export interface SyncSummary {
|
|
29
|
+
known: number;
|
|
30
|
+
added: number;
|
|
31
|
+
changed: number;
|
|
32
|
+
}
|
|
33
|
+
export interface SyncDelta {
|
|
34
|
+
chats: Chat[];
|
|
35
|
+
people: Contact[];
|
|
36
|
+
members: Map<Id, Id[]>;
|
|
37
|
+
marker: number;
|
|
38
|
+
}
|
|
3
39
|
export interface CacheOptions {
|
|
4
40
|
database: CacheDatabase;
|
|
5
41
|
/** Injected so a freshness test costs nothing and does not wait. */
|
|
@@ -10,17 +46,62 @@ export interface CacheStore {
|
|
|
10
46
|
chats: {
|
|
11
47
|
read(freshForMs: number): Chat[] | undefined;
|
|
12
48
|
write(chats: Chat[]): void;
|
|
49
|
+
/** One page, newest first, in SQL rather than by building the whole list and slicing it. */
|
|
50
|
+
page(options: {
|
|
51
|
+
limit: number;
|
|
52
|
+
offset: number;
|
|
53
|
+
}): Chat[];
|
|
54
|
+
count(): number;
|
|
13
55
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
56
|
+
people: {
|
|
57
|
+
/**
|
|
58
|
+
* One page of **contacts** — the people a one-to-one chat exists with. `recent` is
|
|
59
|
+
* `last_messaged_at` newest first, the never-messaged last, then by name.
|
|
60
|
+
*
|
|
61
|
+
* A group member is not in this answer, and nothing marks them as excluded: they are simply
|
|
62
|
+
* not in the set the query asks for (`NEED-105`).
|
|
63
|
+
*/
|
|
64
|
+
contacts(options: PageOptions): Contact[];
|
|
65
|
+
/** How many that query would return, so a page can say whether another one exists. */
|
|
66
|
+
countContacts(): number;
|
|
67
|
+
/** Everyone we can put a name to, contact or not. */
|
|
68
|
+
page(options: PageOptions): Contact[];
|
|
69
|
+
/** Everyone, counted — what `max contacts sync` reports as known. */
|
|
70
|
+
count(): number;
|
|
71
|
+
/** Carries how we met them. Never deletes: absence from a delta means unchanged. */
|
|
72
|
+
upsert(people: Contact[], source: PersonSource): void;
|
|
73
|
+
/** The chats this person is in — which, every chat here being one we are in, is the shared set. */
|
|
74
|
+
chatsWith(personId: Id): Id[];
|
|
75
|
+
/**
|
|
76
|
+
* Recomputes `last_messaged_at` from the dialogs we hold.
|
|
77
|
+
*
|
|
78
|
+
* ⚠ **Call it after any write that adds people.** A person's recency cannot be set while
|
|
79
|
+
* writing the chats, because most people are not known yet at that moment: the login names a
|
|
80
|
+
* handful and the rest arrive from a `CONTACT_INFO` that has not been sent. Measured on the
|
|
81
|
+
* real account 2026-09-21: 6 of 22 people had a recency and the other 16 sorted as
|
|
82
|
+
* never-messaged, which is every dialog partner the login did not name.
|
|
83
|
+
*/
|
|
84
|
+
refreshRecency(): void;
|
|
17
85
|
};
|
|
86
|
+
/** The `time` the last login answered with, or `undefined` for a store that has never synced. */
|
|
87
|
+
syncMarker(): number | undefined;
|
|
88
|
+
/** Rows, memberships, recency and the marker — **in one transaction**. */
|
|
89
|
+
mergeDelta(delta: SyncDelta): SyncSummary;
|
|
90
|
+
/** Makes the next login ask for everything again. */
|
|
91
|
+
forgetSyncMarker(): void;
|
|
18
92
|
messages: {
|
|
19
93
|
/** The newest `limit` messages, but only if the window we hold is both fresh and contiguous. */
|
|
20
94
|
read(chatId: Id, limit: number, freshForMs: number): Message[] | undefined;
|
|
21
95
|
write(chatId: Id, messages: Message[]): void;
|
|
22
96
|
/** After a send, what we hold for that chat is missing the message we just added. */
|
|
23
97
|
invalidate(chatId: Id): void;
|
|
98
|
+
/**
|
|
99
|
+
* When a message was sent, for `messages list --before <id>`.
|
|
100
|
+
*
|
|
101
|
+
* `undefined` for an id we have never stored — which is exactly what a deleted message looks
|
|
102
|
+
* like, since it is no longer in the history MAX returns either.
|
|
103
|
+
*/
|
|
104
|
+
timeOf(id: Id): number | undefined;
|
|
24
105
|
};
|
|
25
106
|
/** True when this process may go to MAX for that window; false when somebody else already is. */
|
|
26
107
|
claim(chatId: Id, anchor: string, holder: string, forMs: number): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,CAAA;IACvB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE;QACL,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,SAAS,CAAA;QAC5C,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"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"}
|
package/dist/cache/store.js
CHANGED
|
@@ -14,11 +14,86 @@ export const openStore = ({ database, now = () => Date.now() }) => {
|
|
|
14
14
|
title = excluded.title, kind = excluded.kind, unread_count = excluded.unread_count,
|
|
15
15
|
last_message_at = excluded.last_message_at, participants_count = excluded.participants_count,
|
|
16
16
|
fetched_at = excluded.fetched_at, generation = chats.generation + 1`);
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* **`COALESCE`, not assignment, for the three names.** The same person arrives from several
|
|
19
|
+
* places — the login, `CONTACT_INFO`, a group's participant list — and not all of them carry a
|
|
20
|
+
* description or a username. Overwriting with what the latest one happened to omit would blank
|
|
21
|
+
* a name we already had, which is the one thing this table exists to prevent.
|
|
22
|
+
*/
|
|
23
|
+
const putPerson = database.prepare(`
|
|
24
|
+
INSERT INTO people (id, name, username, description, last_messaged_at, source, fetched_at)
|
|
25
|
+
VALUES (?, ?, ?, ?, NULL, ?, ?)
|
|
19
26
|
ON CONFLICT(id) DO UPDATE SET
|
|
20
|
-
name = excluded.name,
|
|
21
|
-
|
|
27
|
+
name = coalesce(excluded.name, people.name),
|
|
28
|
+
username = coalesce(excluded.username, people.username),
|
|
29
|
+
description = coalesce(excluded.description, people.description),
|
|
30
|
+
source = excluded.source, fetched_at = excluded.fetched_at`);
|
|
31
|
+
const toContact = (row) => ({
|
|
32
|
+
id: String(row.id),
|
|
33
|
+
name: row.name === null ? null : String(row.name),
|
|
34
|
+
username: row.username === null ? null : String(row.username),
|
|
35
|
+
description: row.description === null ? null : String(row.description),
|
|
36
|
+
lastMessagedAt: row.last_messaged_at === null ? null : new Date(Number(row.last_messaged_at)).toISOString(),
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* `NULLS LAST` spelled out as a sort key rather than as syntax: it arrived in SQLite 3.30 and
|
|
40
|
+
* this runs on whatever `node:sqlite` and `bun:sqlite` were built against.
|
|
41
|
+
*/
|
|
42
|
+
const ORDER = {
|
|
43
|
+
recent: "p.last_messaged_at IS NULL, p.last_messaged_at DESC, p.name IS NULL, p.name",
|
|
44
|
+
name: "p.name IS NULL, p.name",
|
|
45
|
+
};
|
|
46
|
+
/** People a *dialog* exists with. The join is the definition of "contact" (`NEED-105`). */
|
|
47
|
+
const CONTACTS_FROM = `
|
|
48
|
+
FROM people p
|
|
49
|
+
JOIN chat_members m ON m.person_id = p.id
|
|
50
|
+
JOIN chats c ON c.id = m.chat_id AND c.kind = 'dialog'`;
|
|
51
|
+
/**
|
|
52
|
+
* The default order, derived from the dialogs rather than accumulated as they arrive.
|
|
53
|
+
*
|
|
54
|
+
* It has to be a recompute rather than "raise it as each chat lands": a person's row often does
|
|
55
|
+
* not exist yet when their chat does, because the login names six people and the other sixteen
|
|
56
|
+
* come back from a request sent afterwards. An `UPDATE` at that moment reaches nobody.
|
|
57
|
+
*
|
|
58
|
+
* Derived from what we hold, so it is correct however the writes interleave, and cheap — one
|
|
59
|
+
* indexed join over the people who are in a dialog, once per write rather than per listing.
|
|
60
|
+
*/
|
|
61
|
+
const refreshRecency = () => {
|
|
62
|
+
database.exec(`
|
|
63
|
+
UPDATE people SET last_messaged_at = (
|
|
64
|
+
SELECT MAX(c.last_message_at) FROM chat_members m
|
|
65
|
+
JOIN chats c ON c.id = m.chat_id AND c.kind = 'dialog'
|
|
66
|
+
WHERE m.person_id = people.id
|
|
67
|
+
)
|
|
68
|
+
WHERE id IN (
|
|
69
|
+
SELECT m.person_id FROM chat_members m
|
|
70
|
+
JOIN chats c ON c.id = m.chat_id AND c.kind = 'dialog'
|
|
71
|
+
)`);
|
|
72
|
+
};
|
|
73
|
+
const upsertPeople = (people, source, at) => {
|
|
74
|
+
for (const person of people) {
|
|
75
|
+
if (!person.id)
|
|
76
|
+
continue;
|
|
77
|
+
putPerson.run(person.id, person.name, person.username, person.description, source, at);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* SQLite has no nested transactions and this is the only writer that needs one, so a plain
|
|
82
|
+
* `BEGIN` is enough. The rollback is what item 4 of the plan turns on: a marker saved over rows
|
|
83
|
+
* that were never written makes the next login ask for changes since data we do not have, and
|
|
84
|
+
* nothing downstream ever notices.
|
|
85
|
+
*/
|
|
86
|
+
const inTransaction = (body) => {
|
|
87
|
+
database.exec("BEGIN");
|
|
88
|
+
try {
|
|
89
|
+
body();
|
|
90
|
+
database.exec("COMMIT");
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
database.exec("ROLLBACK");
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
22
97
|
/**
|
|
23
98
|
* **Compares MAX's clock, never ours.** `update_time` is what MAX sets on an edit and is `NULL`
|
|
24
99
|
* on a message nobody has changed (measured 2026-09-20).
|
|
@@ -66,6 +141,11 @@ export const openStore = ({ database, now = () => Date.now() }) => {
|
|
|
66
141
|
return undefined;
|
|
67
142
|
return database.prepare("SELECT * FROM chats ORDER BY last_message_at DESC").all().map(toChat);
|
|
68
143
|
},
|
|
144
|
+
page: ({ limit, offset }) => database
|
|
145
|
+
.prepare("SELECT * FROM chats ORDER BY last_message_at DESC LIMIT ? OFFSET ?")
|
|
146
|
+
.all(limit, offset)
|
|
147
|
+
.map(toChat),
|
|
148
|
+
count: () => Number(database.prepare("SELECT COUNT(*) AS n FROM chats").get()?.n ?? 0),
|
|
69
149
|
write: (chats) => {
|
|
70
150
|
const at = now();
|
|
71
151
|
for (const chat of chats) {
|
|
@@ -74,27 +154,64 @@ export const openStore = ({ database, now = () => Date.now() }) => {
|
|
|
74
154
|
markFetched.run("chats", at);
|
|
75
155
|
},
|
|
76
156
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
157
|
+
people: {
|
|
158
|
+
contacts: ({ order, limit, offset }) => database
|
|
159
|
+
.prepare(`SELECT DISTINCT p.* ${CONTACTS_FROM} ORDER BY ${ORDER[order]} LIMIT ? OFFSET ?`)
|
|
160
|
+
.all(limit, offset)
|
|
161
|
+
.map(toContact),
|
|
162
|
+
countContacts: () => Number(database.prepare(`SELECT COUNT(DISTINCT p.id) AS n ${CONTACTS_FROM}`).get()?.n ?? 0),
|
|
163
|
+
page: ({ order, limit, offset }) => database
|
|
164
|
+
.prepare(`SELECT p.* FROM people p ORDER BY ${ORDER[order]} LIMIT ? OFFSET ?`)
|
|
165
|
+
.all(limit, offset)
|
|
166
|
+
.map(toContact),
|
|
167
|
+
upsert: (people, source) => upsertPeople(people, source, now()),
|
|
168
|
+
refreshRecency,
|
|
169
|
+
count: () => Number(database.prepare("SELECT COUNT(*) AS n FROM people").get()?.n ?? 0),
|
|
170
|
+
chatsWith: (personId) => database
|
|
171
|
+
.prepare("SELECT chat_id FROM chat_members WHERE person_id = ?")
|
|
172
|
+
.all(personId)
|
|
173
|
+
.map((row) => String(row.chat_id)),
|
|
174
|
+
},
|
|
175
|
+
syncMarker: () => database.prepare("SELECT marker FROM sync_marker WHERE id = 1").get()
|
|
176
|
+
?.marker,
|
|
177
|
+
mergeDelta: ({ chats, people, members, marker }) => {
|
|
178
|
+
const at = now();
|
|
179
|
+
const summary = { known: 0, added: 0, changed: 0 };
|
|
180
|
+
inTransaction(() => {
|
|
181
|
+
const known = database.prepare("SELECT 1 FROM people WHERE id = ?");
|
|
182
|
+
for (const person of people) {
|
|
183
|
+
if (!person.id)
|
|
184
|
+
continue;
|
|
185
|
+
if (known.get(person.id))
|
|
186
|
+
summary.changed += 1;
|
|
187
|
+
else
|
|
188
|
+
summary.added += 1;
|
|
95
189
|
}
|
|
96
|
-
|
|
97
|
-
|
|
190
|
+
for (const chat of chats) {
|
|
191
|
+
putChat.run(chat.id, chat.title, chat.kind, chat.unreadCount, epoch(chat.lastMessageAt), chat.participantsCount, at);
|
|
192
|
+
}
|
|
193
|
+
upsertPeople(people, "login", at);
|
|
194
|
+
// Replaced per chat, never globally: MAX restates a chat's whole membership whenever it
|
|
195
|
+
// sends that chat, so a member missing from it has left — while a *person* missing from a
|
|
196
|
+
// delta is merely unchanged, which after the first login is every person we know.
|
|
197
|
+
for (const [chatId, personIds] of members) {
|
|
198
|
+
database.prepare("DELETE FROM chat_members WHERE chat_id = ?").run(chatId);
|
|
199
|
+
for (const personId of personIds) {
|
|
200
|
+
database
|
|
201
|
+
.prepare("INSERT OR IGNORE INTO chat_members (chat_id, person_id) VALUES (?, ?)")
|
|
202
|
+
.run(chatId, personId);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
refreshRecency();
|
|
206
|
+
database
|
|
207
|
+
.prepare("INSERT INTO sync_marker (id, marker) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET marker = excluded.marker")
|
|
208
|
+
.run(marker);
|
|
209
|
+
summary.known = Number(database.prepare("SELECT COUNT(*) AS n FROM people").get()?.n ?? 0);
|
|
210
|
+
});
|
|
211
|
+
return summary;
|
|
212
|
+
},
|
|
213
|
+
forgetSyncMarker: () => {
|
|
214
|
+
database.exec("DELETE FROM sync_marker");
|
|
98
215
|
},
|
|
99
216
|
messages: {
|
|
100
217
|
read: (chatId, limit, freshForMs) => {
|
|
@@ -129,6 +246,10 @@ export const openStore = ({ database, now = () => Date.now() }) => {
|
|
|
129
246
|
invalidate: (chatId) => {
|
|
130
247
|
database.prepare("DELETE FROM fetched WHERE kind = ?").run(`messages:${chatId}`);
|
|
131
248
|
},
|
|
249
|
+
timeOf: (id) => {
|
|
250
|
+
const row = database.prepare("SELECT time FROM messages WHERE id = ? LIMIT 1").get(id);
|
|
251
|
+
return row?.time === undefined ? undefined : Number(row.time);
|
|
252
|
+
},
|
|
132
253
|
},
|
|
133
254
|
claim: (chatId, anchor, holder, forMs) => {
|
|
134
255
|
const at = now();
|
|
@@ -143,7 +264,16 @@ export const openStore = ({ database, now = () => Date.now() }) => {
|
|
|
143
264
|
database.prepare("DELETE FROM fetch_lease WHERE chat_id = ? AND anchor = ?").run(chatId, anchor);
|
|
144
265
|
},
|
|
145
266
|
clear: () => {
|
|
146
|
-
for (const table of [
|
|
267
|
+
for (const table of [
|
|
268
|
+
"messages",
|
|
269
|
+
"chats",
|
|
270
|
+
"people",
|
|
271
|
+
"chat_members",
|
|
272
|
+
"sync_marker",
|
|
273
|
+
"ranges",
|
|
274
|
+
"fetched",
|
|
275
|
+
"fetch_lease",
|
|
276
|
+
]) {
|
|
147
277
|
database.exec(`DELETE FROM ${table}`);
|
|
148
278
|
}
|
|
149
279
|
},
|
package/dist/cache/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"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"}
|