Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / encrypted-search / lib / models / interfaces.ts
blobafcebd0cbc7b3a01e409fe9460a9e647136e372a
1 import type { DBSchema } from 'idb';
3 import type { ES_SYNC_ACTIONS, INDEXING_STATUS, TIMESTAMP_TYPE } from '../constants';
4 import type { ESSetResultsList } from './esFunctions';
6 /**
7  * Object to be stored locally to retry an API call
8  */
9 export interface RetryObject {
10     retryTime: number;
11     numberRetries: number;
14 /**
15  * Object stored in local storage during indexing to keep track
16  * of its status. Note that recoveryPoint can differ between
17  * metadata or content indexing
18  */
19 export interface ESProgress {
20     totalItems: number;
21     numPauses: number;
22     isRefreshed: boolean;
23     timestamps: {
24         type: TIMESTAMP_TYPE;
25         time: number;
26     }[];
27     originalEstimate: number;
28     recoveryPoint: any;
29     status: INDEXING_STATUS;
32 /**
33  * Collection of progress objects defined by the ESProgress interface
34  * "metadata" is always present, being it the default content type enabled by encrypted search
35  */
36 export interface ProgressObject {
37     metadata: ESProgress;
38     content?: ESProgress;
41 /**
42  * Collection of event IDs for all the components specified by the
43  * product (e.g. calendars in calendar and shares in drive)
44  */
45 export interface EventsObject {
46     [components: string]: string;
49 /**
50  * Object containing the ciphertext of items as stored in IDB
51  */
52 export interface AesGcmCiphertext {
53     iv: Uint8Array;
54     ciphertext: ArrayBuffer;
57 /**
58  * The type of keys in the temporal index of ESDB. The first number
59  * is supposed to be a time coordinate, while the second one a
60  * tie-breaker in case of equal time
61  */
62 export type ESTimepoint = [number, number];
64 /**
65  * Object representing the primary ID and the temporal coordinate
66  * of an item
67  */
68 export interface ESItemInfo {
69     ID: string;
70     timepoint: ESTimepoint;
73 /**
74  * Encrypted item, that can be either metadata or content, with
75  * extra information in plaintext
76  */
77 export interface EncryptedItemWithInfo extends ESItemInfo {
78     keepSize?: boolean;
79     aesGcmCiphertext: AesGcmCiphertext;
82 /**
83  * Encrypted item, that can be either metadata or content, with
84  * its ID in plaintext
85  */
86 export type EncryptedItemWithID = Omit<EncryptedItemWithInfo, 'timepoint' | 'keepSize'>;
88 /**
89  * Ciphertexts in the metadata table of IDB have out-of-line keys,
90  * therefore we need to specify the ID with which to index items externally
91  */
92 export type EncryptedMetadataItem = Omit<EncryptedItemWithInfo, 'ID'>;
94 /**
95  * List of possible key-value pairs types in the config object store
96  */
97 export interface ConfigValues {
98     indexKey: string;
99     size: number;
100     enabled: boolean;
101     limited: boolean;
102     retries?: string;
103     migrated?: any;
105 export type ConfigKeys = keyof ConfigValues;
108  * IndexedDB structure. Each sub-object corresponds to an object store
109  *   - config contains overall information, e.g. whether ES was enabled
110  *     or disabled, the index key and the estimated size of all items
111  *   - events contains the latest event IDs according to which items
112  *     had been updated, for all components of the product
113  *   - indexingProgress contains information about the status of indexing
114  *     for metadata and for any other content type specified by the product
115  *   - metadata contains all the actual items' metadata
116  *   - content contains the content of the items which are stored in the
117  *     metadata objectStore
118  */
119 export interface EncryptedSearchDB extends DBSchema {
120     config: {
121         value: ConfigValues[ConfigKeys];
122         key: ConfigKeys;
123     };
124     events: {
125         value: string;
126         key: string;
127     };
128     indexingProgress: {
129         value: ESProgress;
130         key: string;
131     };
132     metadata: {
133         value: EncryptedMetadataItem;
134         key: string;
135         indexes: { temporal: 'timepoint' };
136     };
137     content: {
138         value: AesGcmCiphertext;
139         key: string;
140     };
144  * Collection of fields to determine UI elements during indexing (e.g. progress bar, ...)
145  */
146 export interface ESIndexingState {
147     /**
148      * number of items indexed so far
149      */
150     esProgress: number;
151     /**
152      * estimated time (in minutes) expected for indexing to finish
153      */
154     estimatedMinutes: number;
155     /**
156      * Total items to index
157      */
158     totalIndexingItems: number;
159     /**
160      * progress value in percentage, i.e. number from 0 to 100
161      */
162     currentProgressValue: number;
165 export interface CachedItem<ESItemMetadata, ESItemContent> {
166     metadata: ESItemMetadata;
167     content?: ESItemContent;
171  * A decrypted copy of IDB kept in memory in plaintext form. The property
172  * esCache is a map of all indexed items. The property isCacheLimited refers
173  * to content only, as metadata is assumed to always fit cache
174  */
175 export interface ESCache<ESItemMetadata, ESItemContent> {
176     esCache: Map<string, CachedItem<ESItemMetadata, ESItemContent>>;
177     cacheSize: number;
178     isCacheLimited: boolean;
179     isCacheReady: boolean;
183  * Base type for metrics on encrypted search
184  */
185 interface ESMetrics {
186     indexSize: number;
187     // Note: the metrics dashboard expects a variable called "numMessagesIndexed" but
188     // it doesn't make too much sense in general to talk about "messages"
189     numMessagesIndexed: number;
193  * Type of the metrics report sent after each search
194  */
195 export interface ESSearchMetrics extends ESMetrics {
196     cacheSize: number;
197     isFirstSearch: boolean;
198     isCacheLimited: boolean;
199     searchTime: number;
203  * Type of the metrics report sent after indexing
204  */
205 export interface ESIndexMetrics extends ESMetrics {
206     numPauses: number;
207     originalEstimate: number;
208     numInterruptions: number;
209     isRefreshed: boolean;
210     indexTime: number;
214  * Required fields to correctly process events and keep IDB in sync. This object
215  * instructs the code to apply Action to the item specified by ID. ItemMetadata
216  * contains the metadata of the item being changed and can be omitted only in
217  * deletion events
218  */
219 export interface ESItemEvent<ESItemMetadata> {
220     ID: string;
221     Action: ES_SYNC_ACTIONS;
222     ItemMetadata: ESItemMetadata | undefined;
226  * Overall structure of an event
227  */
228 export interface ESEvent<ESItemMetadata> {
229     Refresh?: number;
230     Items?: ESItemEvent<ESItemMetadata>[];
231     attemptReDecryption?: boolean;
232     eventsToStore: EventsObject;
236  * Interface representing an ESItem, i.e. the combination of metadata plus
237  * content. This is the overall item that can be searched. Note that metadata
238  * must always be present, while content is optional, either because content
239  * search hasn't been activated, or because a product doesn't support content
240  * altogether
241  */
242 export type ESItem<ESItemMetadata, ESItemContent> = ESItemMetadata & Partial<ESItemContent>;
245  * Boolean variables of the ES status useful to display correct UI
246  * @var dbExists whether an instance of IndexedDB exists
247  * @var isEnablingContentSearch whether indexing of content is ongoing
248  * @var isDBLimited whether IndexedDB has fewer than the total amount of items
249  * @var esEnabled whether ES is enabled (in case a fallback to server-side search exists)
250  * @var esSupported whether the browser supports our search engine. It's true by default until indexing fails to initialise IndexedDB
251  * @var isRefreshing whether a refresh of IndexedDB (when correcting decryption errors) is ongoing
252  * @var isSearchPartial whether the current search only has partial results. It happens when IndexedDB does not fit in cache
253  * @var isSearching whether a search is ongoing
254  * @var isCacheLimited whether the cache is limited, i.e. it doesn't contain all items that are in IndexedDB
255  * @var isCacheReady whether in-memory cache load is filled
256  * @var isEnablingEncryptedSearch whether indexing of metadata is ongoing
257  * @var isContentIndexingPaused whether content indexing is paused
258  * @var isMetadataIndexingPaused whether metadata indexing is paused
259  * @var contentIndexingDone whether content indexing is finished
260  */
261 export interface ESStatusBooleans {
262     dbExists: boolean;
263     isDBLimited: boolean;
264     esEnabled: boolean;
265     esSupported: boolean;
266     isRefreshing: boolean;
267     isSearchPartial: boolean;
268     isSearching: boolean;
269     isFirstSearch: boolean;
270     isEnablingContentSearch: boolean;
271     isContentIndexingPaused: boolean;
272     isMetadataIndexingPaused: boolean;
273     isEnablingEncryptedSearch: boolean;
274     contentIndexingDone: boolean;
275     isConfigFromESDBLoaded: boolean;
279  * Internal variables on the status of ES
280  */
281 export interface ESStatus<ESItemMetadata, ESItemContent, ESSearchParameters> extends ESStatusBooleans {
282     permanentResults: ESItem<ESItemMetadata, ESItemContent>[];
283     setResultsList: ESSetResultsList<ESItemMetadata, ESItemContent>;
284     lastTimePoint: ESTimepoint | undefined;
285     previousESSearchParams: ESSearchParameters | undefined;
286     cachedIndexKey: CryptoKey | undefined;
287     getCacheStatus: () => { isCacheReady: boolean; isCacheLimited: boolean };