@ixo/topic-protocol 0.3.0 → 0.4.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 +8 -2
- package/dist/schemas/topic-index-entry.schema.json +34 -1
- package/dist/schemas/topic-index.schema.json +39 -0
- package/dist/src/contracts/schemas.d.ts +127 -1
- package/dist/src/contracts/schemas.js +2 -0
- package/dist/src/contracts/schemas.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/indexing/rank.d.ts +2 -0
- package/dist/src/indexing/rank.js +31 -0
- package/dist/src/indexing/rank.js.map +1 -0
- package/dist/src/indexing/reconcile.d.ts +3 -0
- package/dist/src/indexing/reconcile.js +58 -0
- package/dist/src/indexing/reconcile.js.map +1 -0
- package/dist/src/indexing/search.d.ts +2 -0
- package/dist/src/indexing/search.js +37 -0
- package/dist/src/indexing/search.js.map +1 -0
- package/dist/src/indexing/types.d.ts +75 -0
- package/dist/src/indexing/types.js +22 -0
- package/dist/src/indexing/types.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ identity of a discussion.
|
|
|
9
9
|
|
|
10
10
|
## Status
|
|
11
11
|
|
|
12
|
-
**Version:** 0.
|
|
12
|
+
**Version:** 0.4.0
|
|
13
13
|
|
|
14
14
|
**Maturity:** Draft implementation candidate
|
|
15
15
|
**Execution source of truth:** [Linear Topic Protocol project](https://linear.app/ixo-world/project/topic-protocol-4ea58bc7910d)
|
|
@@ -39,7 +39,8 @@ key-value adapter at its integration boundary.
|
|
|
39
39
|
|
|
40
40
|
The package includes:
|
|
41
41
|
|
|
42
|
-
- strict Draft 2020-12 validation for all
|
|
42
|
+
- strict Draft 2020-12 validation for all 17 contracts;
|
|
43
|
+
- deterministic Topic-index reconciliation, structured search, and personal ranking;
|
|
43
44
|
- deterministic Topic projection from a root and authorised operations;
|
|
44
45
|
- singular accepted Overview projection with deterministic conflict handling;
|
|
45
46
|
- relation-free Matrix root and canonical native `m.thread` event builders;
|
|
@@ -94,6 +95,11 @@ never binds a Topic to a temporary local-echo ID. Pending storage contains only
|
|
|
94
95
|
the room ID, Topic ID, root event ID, and creation timestamp; clients must back
|
|
95
96
|
it with durable local storage to make restart recovery effective.
|
|
96
97
|
|
|
98
|
+
Topic index snapshots are plaintext only while in trusted process memory. A
|
|
99
|
+
client persistence adapter must encrypt them at rest. Portal uses Matrix Secret
|
|
100
|
+
Storage, while follow, mute, pin, and mark-unread flags remain room account data
|
|
101
|
+
rather than shared Topic state.
|
|
102
|
+
|
|
97
103
|
Install development dependencies and run the complete quality gate:
|
|
98
104
|
|
|
99
105
|
```bash
|
|
@@ -8,10 +8,43 @@
|
|
|
8
8
|
"topicId": { "type": "string", "pattern": "^ixo:topic:[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" },
|
|
9
9
|
"roomId": { "type": "string", "minLength": 1 },
|
|
10
10
|
"rootEventId": { "type": "string", "pattern": "^\\$" },
|
|
11
|
+
"title": { "type": "string", "minLength": 1, "maxLength": 280 },
|
|
12
|
+
"currentState": { "type": "string", "minLength": 1, "maxLength": 280 },
|
|
11
13
|
"status": { "type": "string" },
|
|
14
|
+
"kind": { "type": "string", "enum": ["discussion", "question", "decision", "proposal", "task", "incident", "flow"] },
|
|
15
|
+
"domain": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["id"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"id": { "type": "string", "minLength": 1 },
|
|
20
|
+
"name": { "type": "string", "minLength": 1 }
|
|
21
|
+
},
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
},
|
|
24
|
+
"context": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["type", "id"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"type": { "type": "string", "minLength": 1 },
|
|
31
|
+
"id": { "type": "string", "minLength": 1 }
|
|
32
|
+
},
|
|
33
|
+
"additionalProperties": false
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"sourceKinds": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"uniqueItems": true,
|
|
39
|
+
"items": { "type": "string", "enum": ["message", "flow", "decision", "artifact", "notification", "other"] }
|
|
40
|
+
},
|
|
12
41
|
"latestActivityAt": { "type": "string", "format": "date-time" },
|
|
42
|
+
"latestEventId": { "type": "string", "pattern": "^\\$" },
|
|
43
|
+
"participated": { "type": "boolean" },
|
|
44
|
+
"unread": { "type": "string", "enum": ["unread", "read", "unknown"] },
|
|
13
45
|
"notificationMode": { "type": "string", "enum": ["follow", "default", "mute"] },
|
|
14
|
-
"completeness": { "type": "string", "enum": ["complete", "partial-history", "metadata-pending", "decryption-pending"] }
|
|
46
|
+
"completeness": { "type": "string", "enum": ["complete", "partial-history", "metadata-pending", "decryption-pending"] },
|
|
47
|
+
"indexedAt": { "type": "string", "format": "date-time" }
|
|
15
48
|
},
|
|
16
49
|
"additionalProperties": true
|
|
17
50
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://topic-protocol.ixo.world/schemas/topic-index.schema.json",
|
|
4
|
+
"title": "Encrypted Topic index plaintext snapshot",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["version", "updatedAt", "entries", "tombstones"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"version": { "const": 1 },
|
|
9
|
+
"updatedAt": { "type": "string", "format": "date-time" },
|
|
10
|
+
"entries": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": {
|
|
13
|
+
"allOf": [
|
|
14
|
+
{ "$ref": "https://topic-protocol.ixo.world/schemas/topic-index-entry.schema.json" },
|
|
15
|
+
{
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["participated", "unread", "indexedAt"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"status": { "type": "string", "enum": ["draft", "active", "waiting", "blocked", "resolved", "archived", "redirected"] }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"tombstones": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"items": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"required": ["roomId", "removedAt"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"roomId": { "type": "string", "minLength": 1 },
|
|
32
|
+
"removedAt": { "type": "string", "format": "date-time" }
|
|
33
|
+
},
|
|
34
|
+
"additionalProperties": false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
}
|
|
@@ -307,13 +307,79 @@ export declare const SCHEMA_REGISTRY: Readonly<{
|
|
|
307
307
|
type: string;
|
|
308
308
|
pattern: string;
|
|
309
309
|
};
|
|
310
|
+
title: {
|
|
311
|
+
type: string;
|
|
312
|
+
minLength: number;
|
|
313
|
+
maxLength: number;
|
|
314
|
+
};
|
|
315
|
+
currentState: {
|
|
316
|
+
type: string;
|
|
317
|
+
minLength: number;
|
|
318
|
+
maxLength: number;
|
|
319
|
+
};
|
|
310
320
|
status: {
|
|
311
321
|
type: string;
|
|
312
322
|
};
|
|
323
|
+
kind: {
|
|
324
|
+
type: string;
|
|
325
|
+
enum: string[];
|
|
326
|
+
};
|
|
327
|
+
domain: {
|
|
328
|
+
type: string;
|
|
329
|
+
required: string[];
|
|
330
|
+
properties: {
|
|
331
|
+
id: {
|
|
332
|
+
type: string;
|
|
333
|
+
minLength: number;
|
|
334
|
+
};
|
|
335
|
+
name: {
|
|
336
|
+
type: string;
|
|
337
|
+
minLength: number;
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
additionalProperties: boolean;
|
|
341
|
+
};
|
|
342
|
+
context: {
|
|
343
|
+
type: string;
|
|
344
|
+
items: {
|
|
345
|
+
type: string;
|
|
346
|
+
required: string[];
|
|
347
|
+
properties: {
|
|
348
|
+
type: {
|
|
349
|
+
type: string;
|
|
350
|
+
minLength: number;
|
|
351
|
+
};
|
|
352
|
+
id: {
|
|
353
|
+
type: string;
|
|
354
|
+
minLength: number;
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
additionalProperties: boolean;
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
sourceKinds: {
|
|
361
|
+
type: string;
|
|
362
|
+
uniqueItems: boolean;
|
|
363
|
+
items: {
|
|
364
|
+
type: string;
|
|
365
|
+
enum: string[];
|
|
366
|
+
};
|
|
367
|
+
};
|
|
313
368
|
latestActivityAt: {
|
|
314
369
|
type: string;
|
|
315
370
|
format: string;
|
|
316
371
|
};
|
|
372
|
+
latestEventId: {
|
|
373
|
+
type: string;
|
|
374
|
+
pattern: string;
|
|
375
|
+
};
|
|
376
|
+
participated: {
|
|
377
|
+
type: string;
|
|
378
|
+
};
|
|
379
|
+
unread: {
|
|
380
|
+
type: string;
|
|
381
|
+
enum: string[];
|
|
382
|
+
};
|
|
317
383
|
notificationMode: {
|
|
318
384
|
type: string;
|
|
319
385
|
enum: string[];
|
|
@@ -322,6 +388,66 @@ export declare const SCHEMA_REGISTRY: Readonly<{
|
|
|
322
388
|
type: string;
|
|
323
389
|
enum: string[];
|
|
324
390
|
};
|
|
391
|
+
indexedAt: {
|
|
392
|
+
type: string;
|
|
393
|
+
format: string;
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
additionalProperties: boolean;
|
|
397
|
+
};
|
|
398
|
+
"topic-index": {
|
|
399
|
+
$schema: string;
|
|
400
|
+
$id: string;
|
|
401
|
+
title: string;
|
|
402
|
+
type: string;
|
|
403
|
+
required: string[];
|
|
404
|
+
properties: {
|
|
405
|
+
version: {
|
|
406
|
+
const: number;
|
|
407
|
+
};
|
|
408
|
+
updatedAt: {
|
|
409
|
+
type: string;
|
|
410
|
+
format: string;
|
|
411
|
+
};
|
|
412
|
+
entries: {
|
|
413
|
+
type: string;
|
|
414
|
+
items: {
|
|
415
|
+
allOf: ({
|
|
416
|
+
$ref: string;
|
|
417
|
+
type?: never;
|
|
418
|
+
required?: never;
|
|
419
|
+
properties?: never;
|
|
420
|
+
} | {
|
|
421
|
+
type: string;
|
|
422
|
+
required: string[];
|
|
423
|
+
properties: {
|
|
424
|
+
status: {
|
|
425
|
+
type: string;
|
|
426
|
+
enum: string[];
|
|
427
|
+
};
|
|
428
|
+
};
|
|
429
|
+
$ref?: never;
|
|
430
|
+
})[];
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
tombstones: {
|
|
434
|
+
type: string;
|
|
435
|
+
items: {
|
|
436
|
+
type: string;
|
|
437
|
+
required: string[];
|
|
438
|
+
properties: {
|
|
439
|
+
roomId: {
|
|
440
|
+
type: string;
|
|
441
|
+
minLength: number;
|
|
442
|
+
};
|
|
443
|
+
removedAt: {
|
|
444
|
+
type: string;
|
|
445
|
+
format: string;
|
|
446
|
+
};
|
|
447
|
+
};
|
|
448
|
+
additionalProperties: boolean;
|
|
449
|
+
};
|
|
450
|
+
};
|
|
325
451
|
};
|
|
326
452
|
additionalProperties: boolean;
|
|
327
453
|
};
|
|
@@ -867,4 +993,4 @@ export declare const SCHEMA_REGISTRY: Readonly<{
|
|
|
867
993
|
};
|
|
868
994
|
}>;
|
|
869
995
|
export type ContractName = keyof typeof SCHEMA_REGISTRY;
|
|
870
|
-
export declare const CONTRACT_NAMES: readonly ("action-receipt" | "agent-run" | "external-session-binding" | "room-policy" | "topic-capsule" | "topic-file-attachment" | "topic-index-entry" | "topic-message" | "topic-operation" | "topic-preferences" | "topic-record" | "topic-record-message" | "topic-relation" | "topic-root" | "topic-state" | "topic-source-message")[];
|
|
996
|
+
export declare const CONTRACT_NAMES: readonly ("action-receipt" | "agent-run" | "external-session-binding" | "room-policy" | "topic-capsule" | "topic-file-attachment" | "topic-index-entry" | "topic-index" | "topic-message" | "topic-operation" | "topic-preferences" | "topic-record" | "topic-record-message" | "topic-relation" | "topic-root" | "topic-state" | "topic-source-message")[];
|
|
@@ -5,6 +5,7 @@ import roomPolicy from "../../schemas/room-policy.schema.json" with { type: "jso
|
|
|
5
5
|
import topicCapsule from "../../schemas/topic-capsule.schema.json" with { type: "json" };
|
|
6
6
|
import topicFileAttachment from "../../schemas/topic-file-attachment.schema.json" with { type: "json" };
|
|
7
7
|
import topicIndexEntry from "../../schemas/topic-index-entry.schema.json" with { type: "json" };
|
|
8
|
+
import topicIndex from "../../schemas/topic-index.schema.json" with { type: "json" };
|
|
8
9
|
import topicMessage from "../../schemas/topic-message.schema.json" with { type: "json" };
|
|
9
10
|
import topicOperation from "../../schemas/topic-operation.schema.json" with { type: "json" };
|
|
10
11
|
import topicPreferences from "../../schemas/topic-preferences.schema.json" with { type: "json" };
|
|
@@ -22,6 +23,7 @@ export const SCHEMA_REGISTRY = Object.freeze({
|
|
|
22
23
|
"topic-capsule": topicCapsule,
|
|
23
24
|
"topic-file-attachment": topicFileAttachment,
|
|
24
25
|
"topic-index-entry": topicIndexEntry,
|
|
26
|
+
"topic-index": topicIndex,
|
|
25
27
|
"topic-message": topicMessage,
|
|
26
28
|
"topic-operation": topicOperation,
|
|
27
29
|
"topic-preferences": topicPreferences,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../src/contracts/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,0CAA0C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC3F,OAAO,QAAQ,MAAM,qCAAqC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACjF,OAAO,sBAAsB,MAAM,oDAAoD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9G,OAAO,UAAU,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACrF,OAAO,YAAY,MAAM,yCAAyC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACzF,OAAO,mBAAmB,MAAM,iDAAiD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxG,OAAO,eAAe,MAAM,6CAA6C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG,OAAO,YAAY,MAAM,yCAAyC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACzF,OAAO,cAAc,MAAM,2CAA2C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC7F,OAAO,gBAAgB,MAAM,6CAA6C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACjG,OAAO,WAAW,MAAM,wCAAwC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACvF,OAAO,kBAAkB,MAAM,gDAAgD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACtG,OAAO,aAAa,MAAM,0CAA0C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC3F,OAAO,SAAS,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACnF,OAAO,UAAU,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACrF,OAAO,kBAAkB,MAAM,gDAAgD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEtG,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,gBAAgB,EAAE,aAAa;IAC/B,WAAW,EAAE,QAAQ;IACrB,0BAA0B,EAAE,sBAAsB;IAClD,aAAa,EAAE,UAAU;IACzB,eAAe,EAAE,YAAY;IAC7B,uBAAuB,EAAE,mBAAmB;IAC5C,mBAAmB,EAAE,eAAe;IACpC,eAAe,EAAE,YAAY;IAC7B,iBAAiB,EAAE,cAAc;IACjC,mBAAmB,EAAE,gBAAgB;IACrC,cAAc,EAAE,WAAW;IAC3B,sBAAsB,EAAE,kBAAkB;IAC1C,gBAAgB,EAAE,aAAa;IAC/B,YAAY,EAAE,SAAS;IACvB,aAAa,EAAE,UAAU;IACzB,sBAAsB,EAAE,kBAAkB;CAC3C,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CACzC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,EAAoB,CACtD,CAAC"}
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../src/contracts/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,0CAA0C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC3F,OAAO,QAAQ,MAAM,qCAAqC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACjF,OAAO,sBAAsB,MAAM,oDAAoD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9G,OAAO,UAAU,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACrF,OAAO,YAAY,MAAM,yCAAyC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACzF,OAAO,mBAAmB,MAAM,iDAAiD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxG,OAAO,eAAe,MAAM,6CAA6C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChG,OAAO,UAAU,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACrF,OAAO,YAAY,MAAM,yCAAyC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACzF,OAAO,cAAc,MAAM,2CAA2C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC7F,OAAO,gBAAgB,MAAM,6CAA6C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACjG,OAAO,WAAW,MAAM,wCAAwC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACvF,OAAO,kBAAkB,MAAM,gDAAgD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACtG,OAAO,aAAa,MAAM,0CAA0C,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC3F,OAAO,SAAS,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACnF,OAAO,UAAU,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACrF,OAAO,kBAAkB,MAAM,gDAAgD,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEtG,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,gBAAgB,EAAE,aAAa;IAC/B,WAAW,EAAE,QAAQ;IACrB,0BAA0B,EAAE,sBAAsB;IAClD,aAAa,EAAE,UAAU;IACzB,eAAe,EAAE,YAAY;IAC7B,uBAAuB,EAAE,mBAAmB;IAC5C,mBAAmB,EAAE,eAAe;IACpC,aAAa,EAAE,UAAU;IACzB,eAAe,EAAE,YAAY;IAC7B,iBAAiB,EAAE,cAAc;IACjC,mBAAmB,EAAE,gBAAgB;IACrC,cAAc,EAAE,WAAW;IAC3B,sBAAsB,EAAE,kBAAkB;IAC1C,gBAAgB,EAAE,aAAa;IAC/B,YAAY,EAAE,SAAS;IACvB,aAAa,EAAE,UAAU;IACzB,sBAAsB,EAAE,kBAAkB;CAC3C,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CACzC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,EAAoB,CACtD,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -11,3 +11,7 @@ export { TopicRuntime, type TopicRuntimeOptions } from "./matrix/runtime.js";
|
|
|
11
11
|
export { MATRIX_ROOM_MESSAGE, TOPIC_ANCHOR_KEY, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, type CanonicalTopicAnchor, type MatrixEventContent, type MatrixSendRequest, type MatrixTopicProjection, type MatrixTopicProjectionInput, type ParsedTopicRoot, type PendingTopicCreation, type PendingTopicStore, type RejectedTopicEvent, type RejectedTopicEventReason, type TopicAnchorConflict, type TopicCreationInput, type TopicCreationResult, type TopicFirstMessageFactory, type TopicMatrixEvent, type TopicMatrixTransport, type TopicRootContentOptions, } from "./matrix/types.js";
|
|
12
12
|
export { TOPIC_KINDS, TOPIC_CONTEXT_TYPES, TOPIC_STATUSES, TOPIC_VFS_FILE_PROVIDER, type IgnoredOperation, type IgnoredOperationReason, type MatrixAnchor, type ProjectedTopicRelation, type ProjectionInput, type TopicContext, type TopicContextType, type TopicFileAttachmentIdentity, type TopicDecision, type TopicKind, type TopicNextStep, type TopicOperation, type TopicOperationType, type TopicProjection, type ProjectedTopicAttachment, type ProjectedTopicOverview, type TopicOverview, type TopicProjectionConflict, type TopicRelationType, type TopicRoot, type TopicStatus, type TopicVfsFileReference, isCanonicalTopicContextType, } from "./projector/types.js";
|
|
13
13
|
export { TOPIC_RECORD_KINDS, type TopicRecord, type TopicRecordKind, } from "./records/types.js";
|
|
14
|
+
export { emptyTopicIndexSnapshot, reconcileTopicIndex } from "./indexing/reconcile.js";
|
|
15
|
+
export { searchTopicIndex } from "./indexing/search.js";
|
|
16
|
+
export { rankTopicIndex } from "./indexing/rank.js";
|
|
17
|
+
export { TOPIC_INDEX_COMPLETENESS, TOPIC_INDEX_RANK_REASONS, TOPIC_INDEX_STORAGE_STATES, TOPIC_INDEX_UNREAD_STATES, TOPIC_NOTIFICATION_MODES, type RankedTopicIndexEntry, type TopicAttentionSignalMap, type TopicAttentionSignals, type TopicIndexCompleteness, type TopicIndexDomain, type TopicIndexEntry, type TopicIndexObservation, type TopicIndexRankReason, type TopicIndexReconciliation, type TopicIndexSearchFilters, type TopicIndexSnapshot, type TopicIndexStorageState, type TopicIndexTombstone, type TopicIndexUnreadState, type TopicNotificationMode, type TopicPreference, type TopicPreferenceMap, } from "./indexing/types.js";
|
package/dist/src/index.js
CHANGED
|
@@ -11,4 +11,8 @@ export { TopicRuntime } from "./matrix/runtime.js";
|
|
|
11
11
|
export { MATRIX_ROOM_MESSAGE, TOPIC_ANCHOR_KEY, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, } from "./matrix/types.js";
|
|
12
12
|
export { TOPIC_KINDS, TOPIC_CONTEXT_TYPES, TOPIC_STATUSES, TOPIC_VFS_FILE_PROVIDER, isCanonicalTopicContextType, } from "./projector/types.js";
|
|
13
13
|
export { TOPIC_RECORD_KINDS, } from "./records/types.js";
|
|
14
|
+
export { emptyTopicIndexSnapshot, reconcileTopicIndex } from "./indexing/reconcile.js";
|
|
15
|
+
export { searchTopicIndex } from "./indexing/search.js";
|
|
16
|
+
export { rankTopicIndex } from "./indexing/rank.js";
|
|
17
|
+
export { TOPIC_INDEX_COMPLETENESS, TOPIC_INDEX_RANK_REASONS, TOPIC_INDEX_STORAGE_STATES, TOPIC_INDEX_UNREAD_STATES, TOPIC_NOTIFICATION_MODES, } from "./indexing/types.js";
|
|
14
18
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAEf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,gBAAgB,GAGjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,aAAa,EACb,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,+BAA+B,EAC/B,+BAA+B,EAC/B,8BAA8B,EAC9B,8BAA8B,GAS/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,GAkB3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EAuBvB,2BAA2B,GAC5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAEf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,gBAAgB,GAGjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,aAAa,EACb,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,+BAA+B,EAC/B,+BAA+B,EAC/B,8BAA8B,EAC9B,8BAA8B,GAS/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,GAkB3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EAuBvB,2BAA2B,GAC5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,GAkBzB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { RankedTopicIndexEntry, TopicAttentionSignalMap, TopicIndexEntry, TopicPreferenceMap } from "./types.js";
|
|
2
|
+
export declare function rankTopicIndex(entries: readonly TopicIndexEntry[], preferences?: TopicPreferenceMap, signals?: TopicAttentionSignalMap): RankedTopicIndexEntry[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { compareText } from "../shared/compare.js";
|
|
2
|
+
const REASON_RANK = {
|
|
3
|
+
pinned: 0,
|
|
4
|
+
"direct-attention": 1,
|
|
5
|
+
"followed-unread": 2,
|
|
6
|
+
participated: 3,
|
|
7
|
+
recent: 4,
|
|
8
|
+
};
|
|
9
|
+
function reasonFor(entry, preferences, signals) {
|
|
10
|
+
const preference = preferences[entry.topicId];
|
|
11
|
+
if (preference?.pinned)
|
|
12
|
+
return "pinned";
|
|
13
|
+
if (signals[entry.topicId]?.directAttention)
|
|
14
|
+
return "direct-attention";
|
|
15
|
+
if (preference?.notificationMode === "follow" && entry.unread === "unread")
|
|
16
|
+
return "followed-unread";
|
|
17
|
+
if (entry.participated)
|
|
18
|
+
return "participated";
|
|
19
|
+
return "recent";
|
|
20
|
+
}
|
|
21
|
+
function compareRanked(left, right) {
|
|
22
|
+
return REASON_RANK[left.reason] - REASON_RANK[right.reason]
|
|
23
|
+
|| compareText(right.entry.latestActivityAt, left.entry.latestActivityAt)
|
|
24
|
+
|| compareText(left.entry.topicId, right.entry.topicId);
|
|
25
|
+
}
|
|
26
|
+
export function rankTopicIndex(entries, preferences = {}, signals = {}) {
|
|
27
|
+
return entries
|
|
28
|
+
.map((entry) => ({ entry, reason: reasonFor(entry, preferences, signals) }))
|
|
29
|
+
.sort(compareRanked);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=rank.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../../src/indexing/rank.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AASnD,MAAM,WAAW,GAAyC;IACxD,MAAM,EAAE,CAAC;IACT,kBAAkB,EAAE,CAAC;IACrB,iBAAiB,EAAE,CAAC;IACpB,YAAY,EAAE,CAAC;IACf,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,SAAS,SAAS,CAAC,KAAsB,EAAE,WAA+B,EAAE,OAAgC;IAC1G,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,UAAU,EAAE,MAAM;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,eAAe;QAAE,OAAO,kBAAkB,CAAC;IACvE,IAAI,UAAU,EAAE,gBAAgB,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,iBAAiB,CAAC;IACrG,IAAI,KAAK,CAAC,YAAY;QAAE,OAAO,cAAc,CAAC;IAC9C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,IAA2B,EAAE,KAA4B;IAC9E,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC;WACtD,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC;WACtE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,OAAmC,EACnC,cAAkC,EAAE,EACpC,UAAmC,EAAE;IAErC,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;SAC3E,IAAI,CAAC,aAAa,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { TopicIndexReconciliation, TopicIndexSnapshot } from "./types.js";
|
|
2
|
+
export declare function emptyTopicIndexSnapshot(updatedAt: string): TopicIndexSnapshot;
|
|
3
|
+
export declare function reconcileTopicIndex(snapshot: TopicIndexSnapshot, input: TopicIndexReconciliation): TopicIndexSnapshot;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { compareText } from "../shared/compare.js";
|
|
2
|
+
const COMPLETENESS_RANK = {
|
|
3
|
+
complete: 4,
|
|
4
|
+
"partial-history": 3,
|
|
5
|
+
"metadata-pending": 2,
|
|
6
|
+
"decryption-pending": 1,
|
|
7
|
+
};
|
|
8
|
+
function compareEntries(left, right) {
|
|
9
|
+
return compareText(left.indexedAt, right.indexedAt)
|
|
10
|
+
|| COMPLETENESS_RANK[left.completeness] - COMPLETENESS_RANK[right.completeness]
|
|
11
|
+
|| compareText(right.roomId, left.roomId)
|
|
12
|
+
|| compareText(right.rootEventId, left.rootEventId);
|
|
13
|
+
}
|
|
14
|
+
function latestEntries(entries) {
|
|
15
|
+
const byTopic = new Map();
|
|
16
|
+
entries.forEach((entry) => {
|
|
17
|
+
const current = byTopic.get(entry.topicId);
|
|
18
|
+
if (current === undefined || compareEntries(current, entry) < 0)
|
|
19
|
+
byTopic.set(entry.topicId, entry);
|
|
20
|
+
});
|
|
21
|
+
return byTopic;
|
|
22
|
+
}
|
|
23
|
+
function latestTombstones(tombstones) {
|
|
24
|
+
const byRoom = new Map();
|
|
25
|
+
tombstones.forEach((tombstone) => {
|
|
26
|
+
const current = byRoom.get(tombstone.roomId);
|
|
27
|
+
if (current === undefined || compareText(current.removedAt, tombstone.removedAt) < 0)
|
|
28
|
+
byRoom.set(tombstone.roomId, tombstone);
|
|
29
|
+
});
|
|
30
|
+
return byRoom;
|
|
31
|
+
}
|
|
32
|
+
function isAfterTombstone(entry, tombstones) {
|
|
33
|
+
const tombstone = tombstones.get(entry.roomId);
|
|
34
|
+
return tombstone === undefined || compareText(tombstone.removedAt, entry.indexedAt) < 0;
|
|
35
|
+
}
|
|
36
|
+
export function emptyTopicIndexSnapshot(updatedAt) {
|
|
37
|
+
return { version: 1, updatedAt, entries: [], tombstones: [] };
|
|
38
|
+
}
|
|
39
|
+
export function reconcileTopicIndex(snapshot, input) {
|
|
40
|
+
const removed = new Set(input.leftRoomIds ?? []);
|
|
41
|
+
const tombstones = latestTombstones([
|
|
42
|
+
...snapshot.tombstones,
|
|
43
|
+
...[...removed].map((roomId) => ({ roomId, removedAt: input.reconciledAt })),
|
|
44
|
+
]);
|
|
45
|
+
const observed = input.observations
|
|
46
|
+
.filter(({ canonicalAnchor }) => canonicalAnchor)
|
|
47
|
+
.map(({ entry }) => entry);
|
|
48
|
+
const entries = [...latestEntries([...snapshot.entries, ...observed]).values()]
|
|
49
|
+
.filter((entry) => !removed.has(entry.roomId) && isAfterTombstone(entry, tombstones))
|
|
50
|
+
.sort((left, right) => compareText(left.topicId, right.topicId));
|
|
51
|
+
return {
|
|
52
|
+
version: 1,
|
|
53
|
+
updatedAt: input.reconciledAt,
|
|
54
|
+
entries,
|
|
55
|
+
tombstones: [...tombstones.values()].sort((left, right) => compareText(left.roomId, right.roomId)),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../src/indexing/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAQnD,MAAM,iBAAiB,GAAG;IACxB,QAAQ,EAAE,CAAC;IACX,iBAAiB,EAAE,CAAC;IACpB,kBAAkB,EAAE,CAAC;IACrB,oBAAoB,EAAE,CAAC;CACf,CAAC;AAEX,SAAS,cAAc,CAAC,IAAqB,EAAE,KAAsB;IACnE,OAAO,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC;WAC9C,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,YAAY,CAAC;WAC5E,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;WACtC,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,aAAa,CAAC,OAAmC;IACxD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IACnD,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACxB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,OAAO,KAAK,SAAS,IAAI,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,gBAAgB,CAAC,UAA0C;IAClE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;IACtD,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;QAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,OAAO,KAAK,SAAS,IAAI,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAChI,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAsB,EAAE,UAAoD;IACpG,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,QAA4B,EAC5B,KAA+B;IAE/B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,gBAAgB,CAAC;QAClC,GAAG,QAAQ,CAAC,UAAU;QACtB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;KAC7E,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY;SAChC,MAAM,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC,eAAe,CAAC;SAChD,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SAC5E,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;SACpF,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACnE,OAAO;QACL,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,KAAK,CAAC,YAAY;QAC7B,OAAO;QACP,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;KACnG,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { compareText } from "../shared/compare.js";
|
|
2
|
+
function includesAny(value, accepted) {
|
|
3
|
+
return accepted === undefined || accepted.length === 0 || (value !== undefined && accepted.includes(value));
|
|
4
|
+
}
|
|
5
|
+
function includesSource(entry, accepted) {
|
|
6
|
+
return accepted === undefined || accepted.length === 0 || accepted.some((kind) => entry.sourceKinds?.includes(kind));
|
|
7
|
+
}
|
|
8
|
+
function searchText(entry) {
|
|
9
|
+
return [
|
|
10
|
+
entry.title,
|
|
11
|
+
entry.currentState,
|
|
12
|
+
entry.domain?.name,
|
|
13
|
+
entry.domain?.id,
|
|
14
|
+
...(entry.context?.flatMap(({ type, id }) => [type, id]) ?? []),
|
|
15
|
+
].filter((value) => value !== undefined).join(" ").toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
function matchesQuery(entry, query) {
|
|
18
|
+
const tokens = query?.trim().toLowerCase().split(/\s+/).filter(Boolean) ?? [];
|
|
19
|
+
if (tokens.length === 0)
|
|
20
|
+
return true;
|
|
21
|
+
const haystack = searchText(entry);
|
|
22
|
+
return tokens.every((token) => haystack.includes(token));
|
|
23
|
+
}
|
|
24
|
+
function matchesFollowing(entry, following, preferences) {
|
|
25
|
+
if (following === undefined)
|
|
26
|
+
return true;
|
|
27
|
+
return (preferences[entry.topicId]?.notificationMode === "follow") === following;
|
|
28
|
+
}
|
|
29
|
+
export function searchTopicIndex(entries, filters = {}, preferences = {}) {
|
|
30
|
+
return entries.filter((entry) => matchesQuery(entry, filters.query)
|
|
31
|
+
&& includesAny(entry.status, filters.statuses)
|
|
32
|
+
&& includesAny(entry.domain?.id, filters.domainIds)
|
|
33
|
+
&& includesAny(entry.kind, filters.kinds)
|
|
34
|
+
&& includesSource(entry, filters.sourceKinds)
|
|
35
|
+
&& matchesFollowing(entry, filters.following, preferences)).sort((left, right) => compareText(left.title ?? "", right.title ?? "") || compareText(left.topicId, right.topicId));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/indexing/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { TopicSourceKind } from "../matrix/cards.js";
|
|
2
|
+
import type { TopicContext, TopicKind, TopicStatus } from "../projector/types.js";
|
|
3
|
+
export declare const TOPIC_INDEX_COMPLETENESS: readonly ["complete", "partial-history", "metadata-pending", "decryption-pending"];
|
|
4
|
+
export declare const TOPIC_INDEX_UNREAD_STATES: readonly ["unread", "read", "unknown"];
|
|
5
|
+
export declare const TOPIC_INDEX_STORAGE_STATES: readonly ["encrypted", "memory-only", "locked", "unavailable"];
|
|
6
|
+
export declare const TOPIC_NOTIFICATION_MODES: readonly ["follow", "default", "mute"];
|
|
7
|
+
export declare const TOPIC_INDEX_RANK_REASONS: readonly ["pinned", "direct-attention", "followed-unread", "participated", "recent"];
|
|
8
|
+
export type TopicIndexCompleteness = (typeof TOPIC_INDEX_COMPLETENESS)[number];
|
|
9
|
+
export type TopicIndexUnreadState = (typeof TOPIC_INDEX_UNREAD_STATES)[number];
|
|
10
|
+
export type TopicIndexStorageState = (typeof TOPIC_INDEX_STORAGE_STATES)[number];
|
|
11
|
+
export type TopicNotificationMode = (typeof TOPIC_NOTIFICATION_MODES)[number];
|
|
12
|
+
export type TopicIndexRankReason = (typeof TOPIC_INDEX_RANK_REASONS)[number];
|
|
13
|
+
export interface TopicIndexDomain {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly name?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TopicIndexEntry {
|
|
18
|
+
readonly topicId: string;
|
|
19
|
+
readonly roomId: string;
|
|
20
|
+
readonly rootEventId: string;
|
|
21
|
+
readonly title?: string;
|
|
22
|
+
readonly currentState?: string;
|
|
23
|
+
readonly status: TopicStatus;
|
|
24
|
+
readonly kind?: TopicKind;
|
|
25
|
+
readonly domain?: TopicIndexDomain;
|
|
26
|
+
readonly context?: readonly TopicContext[];
|
|
27
|
+
readonly sourceKinds?: readonly TopicSourceKind[];
|
|
28
|
+
readonly latestActivityAt: string;
|
|
29
|
+
readonly latestEventId?: string;
|
|
30
|
+
readonly participated: boolean;
|
|
31
|
+
readonly unread: TopicIndexUnreadState;
|
|
32
|
+
readonly completeness: TopicIndexCompleteness;
|
|
33
|
+
readonly indexedAt: string;
|
|
34
|
+
}
|
|
35
|
+
export interface TopicIndexTombstone {
|
|
36
|
+
readonly roomId: string;
|
|
37
|
+
readonly removedAt: string;
|
|
38
|
+
}
|
|
39
|
+
export interface TopicIndexSnapshot {
|
|
40
|
+
readonly version: 1;
|
|
41
|
+
readonly updatedAt: string;
|
|
42
|
+
readonly entries: readonly TopicIndexEntry[];
|
|
43
|
+
readonly tombstones: readonly TopicIndexTombstone[];
|
|
44
|
+
}
|
|
45
|
+
export interface TopicPreference {
|
|
46
|
+
readonly notificationMode: TopicNotificationMode;
|
|
47
|
+
readonly pinned: boolean;
|
|
48
|
+
readonly markedUnreadFrom?: string | null;
|
|
49
|
+
}
|
|
50
|
+
export type TopicPreferenceMap = Readonly<Record<string, TopicPreference>>;
|
|
51
|
+
export interface TopicIndexObservation {
|
|
52
|
+
readonly entry: TopicIndexEntry;
|
|
53
|
+
readonly canonicalAnchor: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface TopicIndexReconciliation {
|
|
56
|
+
readonly observations: readonly TopicIndexObservation[];
|
|
57
|
+
readonly leftRoomIds?: readonly string[];
|
|
58
|
+
readonly reconciledAt: string;
|
|
59
|
+
}
|
|
60
|
+
export interface TopicIndexSearchFilters {
|
|
61
|
+
readonly query?: string;
|
|
62
|
+
readonly statuses?: readonly TopicStatus[];
|
|
63
|
+
readonly domainIds?: readonly string[];
|
|
64
|
+
readonly following?: boolean;
|
|
65
|
+
readonly kinds?: readonly TopicKind[];
|
|
66
|
+
readonly sourceKinds?: readonly TopicSourceKind[];
|
|
67
|
+
}
|
|
68
|
+
export interface TopicAttentionSignals {
|
|
69
|
+
readonly directAttention?: boolean;
|
|
70
|
+
}
|
|
71
|
+
export type TopicAttentionSignalMap = Readonly<Record<string, TopicAttentionSignals>>;
|
|
72
|
+
export interface RankedTopicIndexEntry {
|
|
73
|
+
readonly entry: TopicIndexEntry;
|
|
74
|
+
readonly reason: TopicIndexRankReason;
|
|
75
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const TOPIC_INDEX_COMPLETENESS = [
|
|
2
|
+
"complete",
|
|
3
|
+
"partial-history",
|
|
4
|
+
"metadata-pending",
|
|
5
|
+
"decryption-pending",
|
|
6
|
+
];
|
|
7
|
+
export const TOPIC_INDEX_UNREAD_STATES = ["unread", "read", "unknown"];
|
|
8
|
+
export const TOPIC_INDEX_STORAGE_STATES = [
|
|
9
|
+
"encrypted",
|
|
10
|
+
"memory-only",
|
|
11
|
+
"locked",
|
|
12
|
+
"unavailable",
|
|
13
|
+
];
|
|
14
|
+
export const TOPIC_NOTIFICATION_MODES = ["follow", "default", "mute"];
|
|
15
|
+
export const TOPIC_INDEX_RANK_REASONS = [
|
|
16
|
+
"pinned",
|
|
17
|
+
"direct-attention",
|
|
18
|
+
"followed-unread",
|
|
19
|
+
"participated",
|
|
20
|
+
"recent",
|
|
21
|
+
];
|
|
22
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/indexing/types.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,UAAU;IACV,iBAAiB;IACjB,kBAAkB;IAClB,oBAAoB;CACZ,CAAC;AACX,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC;AAChF,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,WAAW;IACX,aAAa;IACb,QAAQ;IACR,aAAa;CACL,CAAC;AACX,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAU,CAAC;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,QAAQ;IACR,kBAAkB;IAClB,iBAAiB;IACjB,cAAc;IACd,QAAQ;CACA,CAAC"}
|