@wirecat/cli-core 0.380.0 → 0.382.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/CHANGELOG.md +63 -1
- package/dist/cli/bot/watch.js +4 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/messenger/chat-answers.d.ts +2 -0
- package/dist/cli/messenger/chat-answers.js +2 -0
- package/dist/cli/messenger/chat-lists.d.ts +2 -0
- package/dist/cli/messenger/context.d.ts +2 -2
- package/dist/cli/messenger/context.js +2 -1
- package/dist/cli/messenger/conversation-answers.d.ts +1 -0
- package/dist/cli/messenger/message-answers.d.ts +1 -0
- package/dist/cli/messenger/message-items.js +1 -0
- package/dist/cli/messenger/message-writes.d.ts +1 -0
- package/dist/cli/messenger/serve-relay.d.ts +1 -0
- package/dist/cli/messenger/stored.d.ts +3 -1
- package/dist/cli/messenger/stored.js +24 -12
- package/dist/cli/messenger/watch-command.js +8 -0
- package/dist/domain/models.d.ts +52 -4
- package/dist/entities/chat.d.ts +4 -0
- package/dist/entities/chat.js +4 -0
- package/dist/entities/contact.d.ts +4 -0
- package/dist/entities/evidence.d.ts +8 -0
- package/dist/entities/index.d.ts +17 -0
- package/dist/entities/message.d.ts +2 -0
- package/dist/entities/message.js +2 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/meetings/answers.d.ts +1 -0
- package/dist/meetings/import.js +1 -0
- package/dist/meetings/index.d.ts +1 -1
- package/dist/meetings/index.js +1 -1
- package/dist/meetings/item.d.ts +10 -0
- package/dist/meetings/item.js +34 -18
- package/dist/meetings/store.d.ts +6 -0
- package/dist/ports/messenger.d.ts +10 -3
- package/dist/ports/traits.d.ts +6 -0
- package/dist/resolve.d.ts +6 -0
- package/dist/resolve.js +13 -4
- package/dist/search/garbled.d.ts +2 -1
- package/dist/search/garbled.js +6 -1
- package/dist/services/chats.js +16 -2
- package/dist/services/deps.d.ts +2 -0
- package/dist/services/deps.js +2 -0
- package/dist/services/inbox.d.ts +1 -0
- package/dist/services/inbox.js +10 -3
- package/dist/store/keys.d.ts +1 -1
- package/dist/store/messages.d.ts +22 -3
- package/dist/store/messages.js +22 -3
- package/dist/store/sqlite/accounts.js +2 -1
- package/dist/store/sqlite/chat-rekey.js +18 -2
- package/dist/store/sqlite/chats.d.ts +2 -2
- package/dist/store/sqlite/chats.js +39 -9
- package/dist/store/sqlite/manifest.js +3 -0
- package/dist/store/sqlite/meeting-hit-query.js +18 -3
- package/dist/store/sqlite/meetings.js +56 -1
- package/dist/store/sqlite/messages.d.ts +19 -4
- package/dist/store/sqlite/messages.js +103 -5
- package/dist/store/sqlite/migrations.generated.js +23 -0
- package/dist/store/sqlite/open-store.js +16 -5
- package/dist/store/sqlite/reads.d.ts +50 -0
- package/dist/store/sqlite/reads.js +3 -1
- package/dist/store/sqlite/schema.d.ts +251 -0
- package/dist/store/sqlite/schema.js +28 -0
- package/dist/store/sqlite/search.d.ts +33 -0
- package/dist/store/sqlite/values.d.ts +4 -0
- package/dist/store/sqlite/values.js +3 -0
- package/package.json +1 -1
package/dist/entities/index.d.ts
CHANGED
|
@@ -190,6 +190,7 @@ export declare const ENTITIES: {
|
|
|
190
190
|
}, undefined>, undefined>, undefined>;
|
|
191
191
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
192
192
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
193
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
193
194
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
194
195
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
195
196
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -370,6 +371,7 @@ export declare const ENTITIES: {
|
|
|
370
371
|
}, undefined>, undefined>, undefined>;
|
|
371
372
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
372
373
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
374
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
373
375
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
374
376
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
375
377
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -550,6 +552,7 @@ export declare const ENTITIES: {
|
|
|
550
552
|
}, undefined>, undefined>, undefined>;
|
|
551
553
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
552
554
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
555
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
553
556
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
554
557
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
555
558
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -1028,6 +1031,7 @@ export declare const ENTITIES: {
|
|
|
1028
1031
|
}, undefined>, undefined>, undefined>;
|
|
1029
1032
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1030
1033
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
1034
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
1031
1035
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
1032
1036
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
1033
1037
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -1208,6 +1212,7 @@ export declare const ENTITIES: {
|
|
|
1208
1212
|
}, undefined>, undefined>, undefined>;
|
|
1209
1213
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1210
1214
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
1215
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
1211
1216
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
1212
1217
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
1213
1218
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -1448,7 +1453,9 @@ export declare const ENTITIES: {
|
|
|
1448
1453
|
readonly last_message_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1449
1454
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
1450
1455
|
readonly muted: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1456
|
+
readonly muted_until: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1451
1457
|
readonly archived: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1458
|
+
readonly marked_unread: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1452
1459
|
readonly membership_state: v.OptionalSchema<v.PicklistSchema<["joined", "left", "public", "imported", "archived", "external"], undefined>, undefined>;
|
|
1453
1460
|
readonly parent_chat_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
1454
1461
|
readonly scope: v.OptionalSchema<v.PicklistSchema<["personal", "work"], undefined>, undefined>;
|
|
@@ -1527,7 +1534,9 @@ export declare const ENTITIES: {
|
|
|
1527
1534
|
readonly last_message_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1528
1535
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
1529
1536
|
readonly muted: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1537
|
+
readonly muted_until: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1530
1538
|
readonly archived: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1539
|
+
readonly marked_unread: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1531
1540
|
readonly membership_state: v.OptionalSchema<v.PicklistSchema<["joined", "left", "public", "imported", "archived", "external"], undefined>, undefined>;
|
|
1532
1541
|
readonly parent_chat_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
1533
1542
|
readonly scope: v.OptionalSchema<v.PicklistSchema<["personal", "work"], undefined>, undefined>;
|
|
@@ -1575,7 +1584,9 @@ export declare const ENTITIES: {
|
|
|
1575
1584
|
readonly last_message_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1576
1585
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
1577
1586
|
readonly muted: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1587
|
+
readonly muted_until: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
1578
1588
|
readonly archived: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1589
|
+
readonly marked_unread: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
1579
1590
|
readonly membership_state: v.OptionalSchema<v.PicklistSchema<["joined", "left", "public", "imported", "archived", "external"], undefined>, undefined>;
|
|
1580
1591
|
readonly parent_chat_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
1581
1592
|
readonly scope: v.OptionalSchema<v.PicklistSchema<["personal", "work"], undefined>, undefined>;
|
|
@@ -2032,6 +2043,7 @@ export declare const ENTITIES: {
|
|
|
2032
2043
|
readonly duration_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
2033
2044
|
readonly timezone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
2034
2045
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
2046
|
+
readonly issues: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["time_without_zone"], undefined>, undefined>, undefined>;
|
|
2035
2047
|
readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
2036
2048
|
readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
2037
2049
|
}, undefined>;
|
|
@@ -2049,6 +2061,7 @@ export declare const ENTITIES: {
|
|
|
2049
2061
|
readonly duration_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
2050
2062
|
readonly timezone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
2051
2063
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
2064
|
+
readonly issues: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["time_without_zone"], undefined>, undefined>, undefined>;
|
|
2052
2065
|
readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
2053
2066
|
readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
2054
2067
|
readonly event_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
@@ -3372,6 +3385,7 @@ export declare const ENTITIES: {
|
|
|
3372
3385
|
}, undefined>, undefined>, undefined>;
|
|
3373
3386
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
3374
3387
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
3388
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
3375
3389
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
3376
3390
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
3377
3391
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -3552,6 +3566,7 @@ export declare const ENTITIES: {
|
|
|
3552
3566
|
}, undefined>, undefined>, undefined>;
|
|
3553
3567
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
3554
3568
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
3569
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
3555
3570
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
3556
3571
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
3557
3572
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -3770,6 +3785,7 @@ export declare const ENTITIES: {
|
|
|
3770
3785
|
}, undefined>, undefined>, undefined>;
|
|
3771
3786
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
3772
3787
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
3788
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
3773
3789
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
3774
3790
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
3775
3791
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -3950,6 +3966,7 @@ export declare const ENTITIES: {
|
|
|
3950
3966
|
}, undefined>, undefined>, undefined>;
|
|
3951
3967
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
3952
3968
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
3969
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
3953
3970
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
3954
3971
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
3955
3972
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
|
@@ -151,6 +151,8 @@ export declare const message: v.StrictObjectSchema<{
|
|
|
151
151
|
readonly scheduled_for: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
152
152
|
/** The owner starred it; only where the messenger has stars, and only when set. */
|
|
153
153
|
readonly starred: v.OptionalSchema<v.LiteralSchema<true, undefined>, undefined>;
|
|
154
|
+
/** The owner's own message in a one-to-one chat: `delivered`, or `read` once opened; absent when no receipt came. */
|
|
155
|
+
readonly delivery: v.OptionalSchema<v.PicklistSchema<["delivered", "read"], undefined>, undefined>;
|
|
154
156
|
readonly email: v.OptionalSchema<v.StrictObjectSchema<{
|
|
155
157
|
readonly subject: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
156
158
|
readonly from: v.NullableSchema<v.StrictObjectSchema<{
|
package/dist/entities/message.js
CHANGED
|
@@ -64,6 +64,8 @@ export const message = v.strictObject({
|
|
|
64
64
|
scheduled_for: v.optional(time),
|
|
65
65
|
/** The owner starred it; only where the messenger has stars, and only when set. */
|
|
66
66
|
starred: v.optional(v.literal(true)),
|
|
67
|
+
/** The owner's own message in a one-to-one chat: `delivered`, or `read` once opened; absent when no receipt came. */
|
|
68
|
+
delivery: v.optional(v.picklist(["delivered", "read"])),
|
|
67
69
|
email: v.optional(v.strictObject({
|
|
68
70
|
subject: v.nullable(text),
|
|
69
71
|
from: v.nullable(mailPerson),
|
package/dist/errors/reasons.js
CHANGED
|
@@ -27,6 +27,7 @@ const OTHERS = [
|
|
|
27
27
|
validation("file.locked", "another process holds the file's lock"),
|
|
28
28
|
["profile.wrong-account", "authentication_error", "the signed-in source account does not match this profile"],
|
|
29
29
|
validation("store.refused", "the local store refused the document text"),
|
|
30
|
+
["store.not-writable", "permission_error", "the message store cannot be written from this process"],
|
|
30
31
|
validation("catalog.scope-limit", "the selected catalog exceeds its entry limit"),
|
|
31
32
|
validation("evidence.row-too-large", "one evidence row is over its size limit"),
|
|
32
33
|
validation("index.not-ready", "the search index is still being built"),
|
|
@@ -77,6 +77,7 @@ export declare const meetingDetails: v.StrictObjectSchema<{
|
|
|
77
77
|
readonly duration_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
78
78
|
readonly timezone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
79
79
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
80
|
+
readonly issues: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["time_without_zone"], undefined>, undefined>, undefined>;
|
|
80
81
|
readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
81
82
|
readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
82
83
|
/** The stored event and series it belongs to, as `meetings_list` takes them. */
|
package/dist/meetings/import.js
CHANGED
|
@@ -15,6 +15,7 @@ export const importFiles = async (files, store, now = Date.now()) => {
|
|
|
15
15
|
participants: file.participants,
|
|
16
16
|
transcripts: [await transcriptInput(lines, participants, file.source ?? "file", file.content, format)],
|
|
17
17
|
now,
|
|
18
|
+
fromFile: true,
|
|
18
19
|
});
|
|
19
20
|
await linkEvent(store, details, now);
|
|
20
21
|
report.meetings++;
|
package/dist/meetings/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { type FindingEvidence, type FindingOutcome, findingHash, MEETING_FINDING
|
|
|
8
8
|
export { type Finding, type FindingsInput, type FindingsReceipt, type FindingsRules, findingFingerprint, validateFindings, } from "./findings-rules.js";
|
|
9
9
|
export { MEETING_HIT_QUERY, meetingWordsQuery, parseMeetingQuery } from "./hit-query.js";
|
|
10
10
|
export { type ImportFile, importFiles } from "./import.js";
|
|
11
|
-
export { MEETING_QUERY, type MeetingItem, type MeetingOwner, meeting, meetingItem } from "./item.js";
|
|
11
|
+
export { MEETING_ISSUES, MEETING_QUERY, type MeetingIssue, type MeetingItem, type MeetingOwner, meeting, meetingIssuesOf, meetingItem, } from "./item.js";
|
|
12
12
|
export { createMeetingContext, MEETING_FACT_TYPES, type MeetingContextAction, type MeetingContextChange, type MeetingContextOperation, type MeetingContextPorts, type MeetingContextReceipt, validateMeetingContextChange, } from "./meeting-context.js";
|
|
13
13
|
export { createMeetingManagement, dispatchMeetingMutation, type ManagedMeeting, type ManagedMeetingChange, type MeetingManagement, type MeetingManagementPorts, type MeetingMutationOptions, type MeetingMutationProposal, type MeetingMutationReceipt, type MeetingOperationOptions, type MeetingProviderContext, validateMeetingChange, } from "./meeting-management.js";
|
|
14
14
|
export type { Attachment, ChatLine, Event, EventSeries, JsonValue, Meeting, MeetingSeries, Metadata, Occurrence, Participant, RecordTimes, SourceChatLine, SourceMeeting, Summary, SummaryContent, Transcript, TranscriptLine, TranscriptRow, } from "./model.js";
|
package/dist/meetings/index.js
CHANGED
|
@@ -6,7 +6,7 @@ export { findingHash, MEETING_FINDINGS_KIND, meetingFindingFingerprint, validate
|
|
|
6
6
|
export { findingFingerprint, validateFindings, } from "./findings-rules.js";
|
|
7
7
|
export { MEETING_HIT_QUERY, meetingWordsQuery, parseMeetingQuery } from "./hit-query.js";
|
|
8
8
|
export { importFiles } from "./import.js";
|
|
9
|
-
export { MEETING_QUERY, meeting, meetingItem } from "./item.js";
|
|
9
|
+
export { MEETING_ISSUES, MEETING_QUERY, meeting, meetingIssuesOf, meetingItem, } from "./item.js";
|
|
10
10
|
export { createMeetingContext, MEETING_FACT_TYPES, validateMeetingContextChange, } from "./meeting-context.js";
|
|
11
11
|
export { createMeetingManagement, dispatchMeetingMutation, validateMeetingChange, } from "./meeting-management.js";
|
|
12
12
|
export { normalizeMeetingParticipantQuery, validateMeetingParticipantSearch, } from "./participant-search.js";
|
package/dist/meetings/item.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
2
|
import type { Meeting } from "./model.js";
|
|
3
|
+
/**
|
|
4
|
+
* Why a stored meeting's facts may be off, kept so the data stays but a reader knows to doubt it.
|
|
5
|
+
* `time_without_zone`: the provider sent a time with no zone, and it was read in the computer's zone.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MEETING_ISSUES: readonly ["time_without_zone"];
|
|
8
|
+
export type MeetingIssue = (typeof MEETING_ISSUES)[number];
|
|
9
|
+
/** The issues a provider adapter put in `metadata.issues`, the known ones only, in their listed order. */
|
|
10
|
+
export declare const meetingIssuesOf: (metadata: Meeting["metadata"]) => MeetingIssue[];
|
|
3
11
|
/** A stored meeting as every command and tool answers it; `src/entities/` registers it as `meeting`. */
|
|
4
12
|
export declare const meeting: v.StrictObjectSchema<{
|
|
5
13
|
readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
|
|
@@ -16,6 +24,8 @@ export declare const meeting: v.StrictObjectSchema<{
|
|
|
16
24
|
readonly duration_ms: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
17
25
|
readonly timezone: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
18
26
|
readonly participants_count: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
27
|
+
/** Present only when something about this meeting is doubtful. */
|
|
28
|
+
readonly issues: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["time_without_zone"], undefined>, undefined>, undefined>;
|
|
19
29
|
readonly created_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
20
30
|
readonly updated_at: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
21
31
|
}, undefined>;
|
package/dist/meetings/item.js
CHANGED
|
@@ -4,6 +4,16 @@ import * as v from "valibot";
|
|
|
4
4
|
import { formatMeetingReference } from "./reference.js";
|
|
5
5
|
const text = v.string();
|
|
6
6
|
const count = v.pipe(v.number(), v.integer(), v.minValue(0));
|
|
7
|
+
/**
|
|
8
|
+
* Why a stored meeting's facts may be off, kept so the data stays but a reader knows to doubt it.
|
|
9
|
+
* `time_without_zone`: the provider sent a time with no zone, and it was read in the computer's zone.
|
|
10
|
+
*/
|
|
11
|
+
export const MEETING_ISSUES = ["time_without_zone"];
|
|
12
|
+
/** The issues a provider adapter put in `metadata.issues`, the known ones only, in their listed order. */
|
|
13
|
+
export const meetingIssuesOf = (metadata) => {
|
|
14
|
+
const listed = metadata?.issues;
|
|
15
|
+
return Array.isArray(listed) ? MEETING_ISSUES.filter((issue) => listed.includes(issue)) : [];
|
|
16
|
+
};
|
|
7
17
|
/** A stored meeting as every command and tool answers it; `src/entities/` registers it as `meeting`. */
|
|
8
18
|
export const meeting = v.strictObject({
|
|
9
19
|
ref: reference,
|
|
@@ -20,33 +30,39 @@ export const meeting = v.strictObject({
|
|
|
20
30
|
duration_ms: v.nullable(count),
|
|
21
31
|
timezone: v.optional(v.nullable(text)),
|
|
22
32
|
participants_count: v.nullable(count),
|
|
33
|
+
/** Present only when something about this meeting is doubtful. */
|
|
34
|
+
issues: v.optional(v.array(v.picklist(MEETING_ISSUES))),
|
|
23
35
|
created_at: isoTime,
|
|
24
36
|
updated_at: isoTime,
|
|
25
37
|
});
|
|
26
38
|
const iso = (ms) => (ms === null ? null : new Date(ms).toISOString());
|
|
27
|
-
export const meetingItem = (row, owner) =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
})
|
|
39
|
+
export const meetingItem = (row, owner) => {
|
|
40
|
+
const issues = meetingIssuesOf(row.metadata);
|
|
41
|
+
return {
|
|
42
|
+
ref: formatMeetingReference({ type: "meeting", accountId: row.accountId, meetingId: row.id }),
|
|
43
|
+
id: String(row.id),
|
|
44
|
+
provider: owner.provider,
|
|
45
|
+
account: owner.account,
|
|
46
|
+
title: row.title,
|
|
47
|
+
description: row.description,
|
|
48
|
+
location: row.location,
|
|
49
|
+
url: row.joinUrl,
|
|
50
|
+
started_at: iso(row.startedAt),
|
|
51
|
+
ended_at: iso(row.endedAt),
|
|
52
|
+
duration_ms: row.durationMs,
|
|
53
|
+
timezone: row.timezone,
|
|
54
|
+
participants_count: row.participantsCount,
|
|
55
|
+
...(issues.length ? { issues } : {}),
|
|
56
|
+
created_at: new Date(row.createdAt).toISOString(),
|
|
57
|
+
updated_at: new Date(row.updatedAt).toISOString(),
|
|
58
|
+
};
|
|
59
|
+
};
|
|
44
60
|
/** `meetings list` fields: what the meetings table holds of the meeting itself. */
|
|
45
61
|
export const MEETING_QUERY = {
|
|
46
62
|
entity: "meeting",
|
|
47
63
|
backend: "sql",
|
|
48
64
|
fields: queryFieldsOf(meeting, {
|
|
49
|
-
exclude: ["ref", "provider", "account", "description", "location", "url", "timezone"],
|
|
65
|
+
exclude: ["ref", "provider", "account", "description", "location", "url", "timezone", "issues"],
|
|
50
66
|
examples: {
|
|
51
67
|
id: "id:12",
|
|
52
68
|
title: "title:*standup*",
|
package/dist/meetings/store.d.ts
CHANGED
|
@@ -36,6 +36,12 @@ export interface MeetingSave {
|
|
|
36
36
|
summaries?: Omit<NewRecord<Summary>, "meetingId">[];
|
|
37
37
|
attachments?: Omit<NewRecord<Attachment>, "attachableType" | "attachableId">[];
|
|
38
38
|
now: number;
|
|
39
|
+
/**
|
|
40
|
+
* A copy of the occurrence read from a file, not from the provider. It joins the provider's record of the same
|
|
41
|
+
* occurrence — same series, start within 15 minutes — and only fills a missing transcript there; a provider
|
|
42
|
+
* record saved later takes over a record made from files. Transcripts from files have source `file`.
|
|
43
|
+
*/
|
|
44
|
+
fromFile?: boolean;
|
|
39
45
|
}
|
|
40
46
|
export interface MeetingDetails {
|
|
41
47
|
meeting: Meeting;
|
|
@@ -3,7 +3,7 @@ import type { CounterField, CounterObservations } from "../domain/counters.js";
|
|
|
3
3
|
import type { HtmlFormatting, MarkdownFormatting, TextSpan } from "../domain/formatting.js";
|
|
4
4
|
import type { Markup } from "../domain/markdown.js";
|
|
5
5
|
import type { MessagePermalink } from "../domain/message-link.js";
|
|
6
|
-
import type { Account, AccountSession, AdminRight, Button, CallRecord, Chat, ChatCard, ChatEvent, ChatEvents, ChatUpdate, Discussion, Folder, FolderChange, FolderRules, GroupCard, GroupChange, GroupMember, Id, InviteLink, InviteLinkChange, JoinRequest, LinkTarget, MediaKind, Member, Message, MessageEvent, MessageHit, OfficialChatStats, Page, PersonCard, PhoneBookEntry, Poll, PollVote, PrivacySettings, ProfileFacts, ProviderMetadata, SenderIdentity, Sticker, StickerSet, Topic, TopicChange, WindowedMessage } from "../domain/models.js";
|
|
6
|
+
import type { Account, AccountSession, AdminRight, Button, CallRecord, Chat, ChatCard, ChatEvent, ChatEvents, ChatUpdate, Discussion, Folder, FolderChange, FolderRules, GroupCard, GroupChange, GroupMember, Id, InviteLink, InviteLinkChange, JoinRequest, LinkTarget, MediaKind, Member, Message, MessageEvent, MessageHit, OfficialChatStats, Page, PersonCard, PersonReaction, PhoneBookEntry, Poll, PollVote, PrivacySettings, ProfileFacts, ProviderMetadata, SenderIdentity, Sticker, StickerSet, Topic, TopicChange, WindowedMessage } from "../domain/models.js";
|
|
7
7
|
/** One attachment's bytes, fetched over the adapter's connection when `bytes` is read — so read it before the command closes. */
|
|
8
8
|
export interface RemoteFile {
|
|
9
9
|
kind: string;
|
|
@@ -331,6 +331,12 @@ export interface HistoryVote {
|
|
|
331
331
|
messageId: Id;
|
|
332
332
|
vote: PollVote;
|
|
333
333
|
}
|
|
334
|
+
/** One person's reaction to message `messageId` of `chatId`, as `PersonReactionEvent` has it. */
|
|
335
|
+
export interface HistoryReaction {
|
|
336
|
+
chatId: Id;
|
|
337
|
+
messageId: Id;
|
|
338
|
+
reaction: PersonReaction;
|
|
339
|
+
}
|
|
334
340
|
/** One change to who is in chat `chatId`, or to its title, as `ChatEventNotice` has it. */
|
|
335
341
|
export interface HistoryChatEvent {
|
|
336
342
|
chatId: Id;
|
|
@@ -344,8 +350,8 @@ export interface HistoryStar {
|
|
|
344
350
|
}
|
|
345
351
|
/**
|
|
346
352
|
* What a messenger pushed in one go: any of chats, the people in them, past messages of any chats, votes in
|
|
347
|
-
* their polls, the owner's stars, and changes to who is in them. A vote or a star
|
|
348
|
-
* after them, so a message in the same batch is held before
|
|
353
|
+
* their polls, each person's reactions, the owner's stars, and changes to who is in them. A vote, a reaction or a star
|
|
354
|
+
* is saved with its chat's messages, after them, so a message in the same batch is held before them.
|
|
349
355
|
* `rekeys` are chats the messenger now names by another id; they apply first, so the batch's messages under the
|
|
350
356
|
* new id join the chat already stored under the old one.
|
|
351
357
|
*/
|
|
@@ -358,6 +364,7 @@ export interface HistoryBatch {
|
|
|
358
364
|
people?: Member[];
|
|
359
365
|
messages?: Message[];
|
|
360
366
|
votes?: HistoryVote[];
|
|
367
|
+
reactions?: HistoryReaction[];
|
|
361
368
|
stars?: HistoryStar[];
|
|
362
369
|
events?: HistoryChatEvent[];
|
|
363
370
|
}
|
package/dist/ports/traits.d.ts
CHANGED
|
@@ -72,6 +72,12 @@ export interface MessengerTraits {
|
|
|
72
72
|
* adapter's `history` — `messages sync <chat>`. `--all` stays refused: there is no chat list to walk. No when unset.
|
|
73
73
|
*/
|
|
74
74
|
historyOnDemand?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Who keeps a chat's unread count. `messenger` when unset: the store holds what the messenger last said. `store`: the
|
|
77
|
+
* messenger says it only now and then (a history push, a read on the phone), so each message from someone else that
|
|
78
|
+
* `watch` or `listen` saves for the first time, sent after the last count, adds one.
|
|
79
|
+
*/
|
|
80
|
+
unreadCounts?: "messenger" | "store";
|
|
75
81
|
/**
|
|
76
82
|
* Whether `search messages --backend` is offered: the adapter has `searchMessages`. `chat` when its server
|
|
77
83
|
* searches one chat at a time, so only a query that names one chat asks it. No when unset.
|
package/dist/resolve.d.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface PeopleLookup {
|
|
|
7
7
|
}
|
|
8
8
|
/** Shaped like a Telegram or MAX id. Another messenger's ids may be any string, so a miss here is not "not an id". */
|
|
9
9
|
export declare const isId: (reference: string) => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* A name as matching compares it. A copied emoji often carries a presentation mark (U+FE0F, U+FE0E) or a joiner
|
|
12
|
+
* (U+200D) the typed one lacks, and a letter may come composed or as letter plus accent; none of that is a different
|
|
13
|
+
* name.
|
|
14
|
+
*/
|
|
15
|
+
export declare const foldName: (name: string) => string;
|
|
10
16
|
/**
|
|
11
17
|
* A name matched as a fragment — and **an ambiguous one is an error, not a guess**: sending to the
|
|
12
18
|
* wrong conversation is not undoable, so the caller is shown the candidates and asked to be
|
package/dist/resolve.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { CliError, singleLine } from "@wirecat/cli-base";
|
|
2
2
|
/** Shaped like a Telegram or MAX id. Another messenger's ids may be any string, so a miss here is not "not an id". */
|
|
3
3
|
export const isId = (reference) => /^-?\d+$/.test(reference.trim());
|
|
4
|
+
/**
|
|
5
|
+
* A name as matching compares it. A copied emoji often carries a presentation mark (U+FE0F, U+FE0E) or a joiner
|
|
6
|
+
* (U+200D) the typed one lacks, and a letter may come composed or as letter plus accent; none of that is a different
|
|
7
|
+
* name.
|
|
8
|
+
*/
|
|
9
|
+
export const foldName = (name) => name
|
|
10
|
+
.normalize("NFC")
|
|
11
|
+
.replace(/[\uFE0E\uFE0F\u200D]/g, "")
|
|
12
|
+
.toLowerCase();
|
|
4
13
|
/**
|
|
5
14
|
* A name matched as a fragment — and **an ambiguous one is an error, not a guess**: sending to the
|
|
6
15
|
* wrong conversation is not undoable, so the caller is shown the candidates and asked to be
|
|
@@ -8,8 +17,8 @@ export const isId = (reference) => /^-?\d+$/.test(reference.trim());
|
|
|
8
17
|
* "Мама" must not quietly take a send meant for "Мама Иванова".
|
|
9
18
|
*/
|
|
10
19
|
export const pickChat = (reference, chats) => {
|
|
11
|
-
const wanted = reference.trim()
|
|
12
|
-
const matches = chats.filter((chat) => chat.title
|
|
20
|
+
const wanted = foldName(reference.trim());
|
|
21
|
+
const matches = chats.filter((chat) => chat.title !== null && foldName(chat.title).includes(wanted));
|
|
13
22
|
if (matches.length === 1 && matches[0])
|
|
14
23
|
return matches[0];
|
|
15
24
|
if (matches.length === 0)
|
|
@@ -27,9 +36,9 @@ export const pickPerson = (reference, people) => {
|
|
|
27
36
|
return known;
|
|
28
37
|
if (isId(trimmed))
|
|
29
38
|
throw new CliError("not_found", `no person ${trimmed} in what this account has seen`);
|
|
30
|
-
const wanted = trimmed.replace(/^@/, "")
|
|
39
|
+
const wanted = foldName(trimmed.replace(/^@/, ""));
|
|
31
40
|
const everyone = people.all().toSorted((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
32
|
-
const fields = (person) => [person.alias, person.name, person.username].filter(isPresent).map(
|
|
41
|
+
const fields = (person) => [person.alias, person.name, person.username].filter(isPresent).map(foldName);
|
|
33
42
|
const exact = everyone.filter((person) => fields(person).includes(wanted));
|
|
34
43
|
const matches = exact.length > 0 ? exact : everyone.filter((person) => fields(person).some((one) => one.includes(wanted)));
|
|
35
44
|
if (matches.length === 1 && matches[0])
|
package/dist/search/garbled.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ export declare const plainMatch: (probe: GarbledProbe, word: string, stems: stri
|
|
|
13
13
|
* stemmer cut differently: a stem one side extends counts only when the other side's word spells the extension
|
|
14
14
|
* out — «циана» (stem `циа`) reaches «цианом» (`циан`), «устав» (`уста`) does not reach «установить» (`установ`).
|
|
15
15
|
* `1` is a spelling recognition may have garbled, compared by stem: «рилторов» is three edits from «риелтор»
|
|
16
|
-
* as a word but one as a stem
|
|
16
|
+
* as a word but one as a stem; or a word recognition cut off, of four letters or more, that begins the query word
|
|
17
|
+
* («може» for «может»), as ztx matched.
|
|
17
18
|
*/
|
|
18
19
|
export declare const garbledDistance: (probe: GarbledProbe, word: string, stems: string[]) => number | null;
|
|
19
20
|
/**
|
package/dist/search/garbled.js
CHANGED
|
@@ -34,7 +34,8 @@ export const plainMatch = (probe, word, stems) => word.startsWith(probe.literal)
|
|
|
34
34
|
* stemmer cut differently: a stem one side extends counts only when the other side's word spells the extension
|
|
35
35
|
* out — «циана» (stem `циа`) reaches «цианом» (`циан`), «устав» (`уста`) does not reach «установить» (`установ`).
|
|
36
36
|
* `1` is a spelling recognition may have garbled, compared by stem: «рилторов» is three edits from «риелтор»
|
|
37
|
-
* as a word but one as a stem
|
|
37
|
+
* as a word but one as a stem; or a word recognition cut off, of four letters or more, that begins the query word
|
|
38
|
+
* («може» for «может»), as ztx matched.
|
|
38
39
|
*/
|
|
39
40
|
export const garbledDistance = (probe, word, stems) => {
|
|
40
41
|
if (probe.literal.length < SHORTEST_GARBLED || plainMatch(probe, word, stems))
|
|
@@ -59,6 +60,8 @@ export const garbledDistance = (probe, word, stems) => {
|
|
|
59
60
|
if (distance <= 1)
|
|
60
61
|
best = distance;
|
|
61
62
|
}
|
|
63
|
+
if (best === null && stored.length >= SHORTEST_GARBLED && literal.startsWith(stored))
|
|
64
|
+
return 1;
|
|
62
65
|
return best;
|
|
63
66
|
};
|
|
64
67
|
/** `editDistance` from `stem` to the nearest beginning of `word`, or `max + 1` once every beginning is further. */
|
|
@@ -88,6 +91,8 @@ const prefixDistance = (stem, word, max) => {
|
|
|
88
91
|
*/
|
|
89
92
|
const worthStemming = (probe, word) => {
|
|
90
93
|
const stored = plain(word);
|
|
94
|
+
if (stored.length >= SHORTEST_GARBLED && plain(probe.literal).startsWith(stored))
|
|
95
|
+
return true;
|
|
91
96
|
return probe.stems.some((raw) => {
|
|
92
97
|
const stem = plain(raw);
|
|
93
98
|
if (stem.length >= 3 && stored.startsWith(stem))
|
package/dist/services/chats.js
CHANGED
|
@@ -9,7 +9,7 @@ import { codeOf, guardedWrite } from "../sends/guarded.js";
|
|
|
9
9
|
import { newOperationId } from "../sends/send-id.js";
|
|
10
10
|
import { CHAT_COLUMNS, queryFilter, } from "../store/store.js";
|
|
11
11
|
import { chatStats } from "./chat-stats.js";
|
|
12
|
-
import { accountStore, accountStoreIfOpen, fromStore, nothingStored } from "./deps.js";
|
|
12
|
+
import { accountStore, accountStoreIfOpen, fromStore, nothingStored, unlessBusy } from "./deps.js";
|
|
13
13
|
import { memberEvents } from "./member-history.js";
|
|
14
14
|
import { AUDIT_BUDGET, AUDIT_MIN_SCORE, AUDIT_PAGE, AUDIT_PAUSE_MS, auditMembers, } from "./members-audit.js";
|
|
15
15
|
import { chatIdIn } from "./message-resolution.js";
|
|
@@ -249,7 +249,7 @@ export const chatsService = (deps) => ({
|
|
|
249
249
|
const markTopicRead = capability(connection, "markTopicRead", "mark a forum topic read");
|
|
250
250
|
act = (chatId) => markTopicRead(chatId, threadId, until);
|
|
251
251
|
}
|
|
252
|
-
const { id: chatId } = await connection.resolve(chat);
|
|
252
|
+
const { id: chatId, kind } = await connection.resolve(chat);
|
|
253
253
|
const operationId = newOperationId();
|
|
254
254
|
await guardedWrite(deps.guard, {
|
|
255
255
|
operationId,
|
|
@@ -258,9 +258,23 @@ export const chatsService = (deps) => ({
|
|
|
258
258
|
...(until === undefined ? {} : { messageId: until }),
|
|
259
259
|
...(threadId === undefined ? {} : { threadId }),
|
|
260
260
|
}, () => act(chatId));
|
|
261
|
+
if (deps.reads === "store" && threadId === undefined)
|
|
262
|
+
await readInStore(deps, chatId, kind, until);
|
|
261
263
|
return { operationId, chatId, until: until ?? null, ...(threadId === undefined ? {} : { threadId }) };
|
|
262
264
|
},
|
|
263
265
|
});
|
|
266
|
+
/**
|
|
267
|
+
* Where `inbox` reads the store, the store learns of a read the owner made here at once: the messenger may never say.
|
|
268
|
+
* A read up to an older message from someone else leaves the count, which the store cannot split.
|
|
269
|
+
*/
|
|
270
|
+
const readInStore = async (deps, chatId, kind, until) => {
|
|
271
|
+
const store = await accountStoreIfOpen(deps);
|
|
272
|
+
if (!store)
|
|
273
|
+
return;
|
|
274
|
+
const [newest] = (await store.messages.messages(chatId, { limit: 1, outgoing: false })).items;
|
|
275
|
+
if (newest !== undefined && (until === undefined || newest.id === until))
|
|
276
|
+
await unlessBusy(() => store.chats.saveChats([{ id: chatId, kind, unreadCount: 0, markedUnread: false }]));
|
|
277
|
+
};
|
|
264
278
|
/** Each sender's first stored message in the chat; `undefined` when the store holds none of it. */
|
|
265
279
|
const storedFacts = async (store, chatId) => {
|
|
266
280
|
const chat = (await store.chats.chats({})).items.find((one) => one.id === chatId);
|
package/dist/services/deps.d.ts
CHANGED
|
@@ -52,6 +52,8 @@ export declare const accountStore: (deps: Pick<ServiceDeps, "store" | "account">
|
|
|
52
52
|
* `reason` says which, so every such read is found by searching for this name.
|
|
53
53
|
*/
|
|
54
54
|
export declare const crossAccount: (deps: Pick<ServiceDeps, "store">, _reason: string) => Promise<MessageStore>;
|
|
55
|
+
/** A store write after the messenger already did the work: a store busy past its timeout leaves it unwritten. */
|
|
56
|
+
export declare const unlessBusy: <T>(work: () => Promise<T>) => Promise<T | undefined>;
|
|
55
57
|
/** `accountStore`, or nothing when there is no store to read or the account is not known yet. */
|
|
56
58
|
export declare const accountStoreIfOpen: (deps: ServiceDeps) => Promise<AccountStore | undefined>;
|
|
57
59
|
/**
|
package/dist/services/deps.js
CHANGED
|
@@ -33,6 +33,8 @@ const unless = async (work, expected) => {
|
|
|
33
33
|
throw error;
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
+
/** A store write after the messenger already did the work: a store busy past its timeout leaves it unwritten. */
|
|
37
|
+
export const unlessBusy = (work) => unless(work, storeAbsent);
|
|
36
38
|
/** `accountStore`, or nothing when there is no store to read or the account is not known yet. */
|
|
37
39
|
export const accountStoreIfOpen = async (deps) => {
|
|
38
40
|
const held = await storeIfOpen(deps);
|
package/dist/services/inbox.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export declare const capped: (chats: Chat[], most: number) => {
|
|
|
39
39
|
* Other people's unread messages, as the messenger counts them: for each chat with a count, its
|
|
40
40
|
* newest that many. Reading marks nothing read, so the same messages come back until they are read
|
|
41
41
|
* somewhere else — right for a person, wrong for a scheduled run, which is what `since` is for.
|
|
42
|
+
* A chat the owner marked unread is there with no count too, with its newest message from someone else.
|
|
42
43
|
*
|
|
43
44
|
* Muted and archived chats are left out unless `all`, or they mention the owner: on a busy account
|
|
44
45
|
* they are most of what is unread, and they would take the history reads a person's chat needs.
|
package/dist/services/inbox.js
CHANGED
|
@@ -15,8 +15,8 @@ export const REVIEW_DAYS = 3;
|
|
|
15
15
|
export const UNANSWERED_HOURS = 24;
|
|
16
16
|
export const reviewStart = (now = Date.now()) => now - REVIEW_DAYS * 86_400_000;
|
|
17
17
|
export const byRecency = (chats) => chats.toSorted((a, b) => Date.parse(b.lastMessageAt ?? "") - Date.parse(a.lastMessageAt ?? ""));
|
|
18
|
-
/** Muted or archived, and nothing in it mentions the owner or replies to them. */
|
|
19
|
-
const isQuiet = (chat) => (chat.muted === true || chat.archived === true) && (chat.unreadMentions ?? 0) === 0;
|
|
18
|
+
/** Muted or archived, and nothing in it mentions the owner or replies to them; one the owner marked unread never is. */
|
|
19
|
+
const isQuiet = (chat) => (chat.muted === true || chat.archived === true) && (chat.unreadMentions ?? 0) === 0 && chat.markedUnread !== true;
|
|
20
20
|
export const heard = (chats, all) => all
|
|
21
21
|
? { heard: chats, quiet: 0 }
|
|
22
22
|
: { heard: chats.filter((chat) => !isQuiet(chat)), quiet: chats.filter(isQuiet).length };
|
|
@@ -75,17 +75,24 @@ export const capped = (chats, most) => ({
|
|
|
75
75
|
* Other people's unread messages, as the messenger counts them: for each chat with a count, its
|
|
76
76
|
* newest that many. Reading marks nothing read, so the same messages come back until they are read
|
|
77
77
|
* somewhere else — right for a person, wrong for a scheduled run, which is what `since` is for.
|
|
78
|
+
* A chat the owner marked unread is there with no count too, with its newest message from someone else.
|
|
78
79
|
*
|
|
79
80
|
* Muted and archived chats are left out unless `all`, or they mention the owner: on a busy account
|
|
80
81
|
* they are most of what is unread, and they would take the history reads a person's chat needs.
|
|
81
82
|
*/
|
|
82
83
|
export const unreadIn = async (adapter, { limit, all = false, kinds }) => {
|
|
83
84
|
const page = await everyChat(adapter);
|
|
84
|
-
const { heard: waiting, quiet } = heard(byRecency(ofKinds(page.items, kinds).filter((chat) => (chat.unreadCount ?? 0) > 0)), all);
|
|
85
|
+
const { heard: waiting, quiet } = heard(byRecency(ofKinds(page.items, kinds).filter((chat) => (chat.unreadCount ?? 0) > 0 || chat.markedUnread === true)), all);
|
|
85
86
|
const { read, skipped } = capped(waiting, INBOX_CHATS);
|
|
86
87
|
const chats = [];
|
|
87
88
|
for (const { id, title, kind, unreadCount } of read) {
|
|
88
89
|
const count = unreadCount ?? 0;
|
|
90
|
+
if (count === 0) {
|
|
91
|
+
const newest = (await adapter.history(id, { limit })).items.findLast((message) => !message.outgoing);
|
|
92
|
+
if (newest)
|
|
93
|
+
chats.push({ id, title, kind, unreadCount, messages: [newest], more: false });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
89
96
|
const wanted = Math.min(count, limit);
|
|
90
97
|
const { items } = await adapter.history(id, { limit: wanted });
|
|
91
98
|
const theirs = items.slice(-wanted).filter((message) => !message.outgoing);
|
package/dist/store/keys.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare const keysOver: (context: StoreContext) => {
|
|
|
10
10
|
chatPkFor: (accountKey: number, chatId: Id) => number;
|
|
11
11
|
answeredChatPk: (accountKey: number, chatId: Id, typed: Id) => number;
|
|
12
12
|
identityPk: (accountKey: number, provider: Provider, nativeId: Id, name: string | null, facts?: Omit<PersonFacts, "id" | "name">) => number;
|
|
13
|
-
upsertMessage: (key: AccountKey, accountKey: number, chatKey: number, message: Message, via: string, seenAt?: number) =>
|
|
13
|
+
upsertMessage: (key: AccountKey, accountKey: number, chatKey: number, message: Message, via: string, seenAt?: number) => boolean;
|
|
14
14
|
writeMembers: (key: AccountKey, accountKey: number, chatId: Id, memberIds: Id[]) => void;
|
|
15
15
|
writeState: (accountKey: number, name: string, value: string) => void;
|
|
16
16
|
};
|