@xyo-network/image-thumbnail-plugin 2.75.5 → 2.75.6

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.
Files changed (40) hide show
  1. package/dist/docs.json +5065 -5089
  2. package/dist/node/Diviner/Config.d.cts +23 -0
  3. package/dist/node/Diviner/Config.d.cts.map +1 -1
  4. package/dist/node/Diviner/Config.d.mts +23 -0
  5. package/dist/node/Diviner/Config.d.mts.map +1 -1
  6. package/dist/node/Diviner/Config.d.ts +23 -0
  7. package/dist/node/Diviner/Config.d.ts.map +1 -1
  8. package/dist/node/Diviner/Config.js.map +1 -1
  9. package/dist/node/Diviner/Config.mjs.map +1 -1
  10. package/dist/node/Diviner/Diviner.d.cts +37 -20
  11. package/dist/node/Diviner/Diviner.d.cts.map +1 -1
  12. package/dist/node/Diviner/Diviner.d.mts +37 -20
  13. package/dist/node/Diviner/Diviner.d.mts.map +1 -1
  14. package/dist/node/Diviner/Diviner.d.ts +37 -20
  15. package/dist/node/Diviner/Diviner.d.ts.map +1 -1
  16. package/dist/node/Diviner/Diviner.js +158 -138
  17. package/dist/node/Diviner/Diviner.js.map +1 -1
  18. package/dist/node/Diviner/Diviner.mjs +156 -136
  19. package/dist/node/Diviner/Diviner.mjs.map +1 -1
  20. package/dist/node/Diviner/index.js +158 -138
  21. package/dist/node/Diviner/index.js.map +1 -1
  22. package/dist/node/Diviner/index.mjs +156 -136
  23. package/dist/node/Diviner/index.mjs.map +1 -1
  24. package/dist/node/Plugin.d.cts +3 -0
  25. package/dist/node/Plugin.d.cts.map +1 -1
  26. package/dist/node/Plugin.d.mts +3 -0
  27. package/dist/node/Plugin.d.mts.map +1 -1
  28. package/dist/node/Plugin.d.ts +3 -0
  29. package/dist/node/Plugin.d.ts.map +1 -1
  30. package/dist/node/Plugin.js +163 -143
  31. package/dist/node/Plugin.js.map +1 -1
  32. package/dist/node/Plugin.mjs +161 -141
  33. package/dist/node/Plugin.mjs.map +1 -1
  34. package/dist/node/index.js +163 -143
  35. package/dist/node/index.js.map +1 -1
  36. package/dist/node/index.mjs +161 -141
  37. package/dist/node/index.mjs.map +1 -1
  38. package/package.json +22 -16
  39. package/src/Diviner/Config.ts +24 -0
  40. package/src/Diviner/Diviner.ts +223 -157
@@ -4,10 +4,34 @@ import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugi
4
4
  export const ImageThumbnailDivinerConfigSchema = `${ImageThumbnailSchema}.diviner.config` as const
5
5
  export type ImageThumbnailDivinerConfigSchema = typeof ImageThumbnailDivinerConfigSchema
6
6
 
7
+ /**
8
+ * Describes an Archivist/Diviner combination
9
+ * that enables searching signed payloads
10
+ */
11
+ export interface SearchableStorage {
12
+ archivist: string
13
+ boundWitnessDiviner: string
14
+ payloadDiviner: string
15
+ }
16
+
7
17
  export type ImageThumbnailDivinerConfig = DivinerConfig<{
18
+ /** @deprecated Use appropriate Storage */
8
19
  archivist?: string
20
+ /**
21
+ * Where the diviner should store it's index
22
+ */
23
+ indexStore?: SearchableStorage
24
+ /** @deprecated Use appropriate Storage */
9
25
  payloadDiviner?: string
10
26
  payloadDivinerLimit?: number
11
27
  pollFrequency?: number
12
28
  schema: ImageThumbnailDivinerConfigSchema
29
+ /**
30
+ * Where the diviner should persist its internal state
31
+ */
32
+ stateStore?: SearchableStorage
33
+ /**
34
+ * Where the diviner should look for stored thumbnails
35
+ */
36
+ thumbnailStore?: SearchableStorage
13
37
  }>
@@ -1,159 +1,254 @@
1
1
  import { assertEx } from '@xylabs/assert'
2
- import { delay } from '@xylabs/delay'
3
- import { compact } from '@xylabs/lodash'
2
+ import { exists } from '@xylabs/exists'
4
3
  import { AbstractDiviner } from '@xyo-network/abstract-diviner'
5
- import { ArchivistInstance, asArchivistInstance } from '@xyo-network/archivist-model'
4
+ import { asArchivistInstance, withArchivistModule } from '@xyo-network/archivist-model'
5
+ import { ArchivistWrapper } from '@xyo-network/archivist-wrapper'
6
+ import { isBoundWitness } from '@xyo-network/boundwitness-model'
6
7
  import { PayloadHasher } from '@xyo-network/core'
7
- import { asDivinerInstance, DivinerConfigSchema, DivinerInstance } from '@xyo-network/diviner-model'
8
+ import { BoundWitnessDivinerQueryPayload, BoundWitnessDivinerQuerySchema } from '@xyo-network/diviner-boundwitness-model'
9
+ import { asDivinerInstance, DivinerConfigSchema } from '@xyo-network/diviner-model'
8
10
  import { PayloadDivinerQueryPayload, PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
9
- import { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
10
- import { UrlPayload } from '@xyo-network/url-payload-plugin'
11
+ import { DivinerWrapper } from '@xyo-network/diviner-wrapper'
12
+ import { ImageThumbnailSchema, isImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'
13
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
14
+ import { isPayloadOfSchemaType, Payload } from '@xyo-network/payload-model'
15
+ import { isUrlPayload } from '@xyo-network/url-payload-plugin'
16
+ import { isTimestamp, TimestampSchema } from '@xyo-network/witness-timestamp'
11
17
 
12
- import { ImageThumbnailDivinerConfigSchema } from './Config'
18
+ import { ImageThumbnailDivinerConfig, ImageThumbnailDivinerConfigSchema } from './Config'
13
19
  import { ImageThumbnailDivinerParams } from './Params'
14
20
 
15
- export class ImageThumbnailDiviner<TParams extends ImageThumbnailDivinerParams = ImageThumbnailDivinerParams> extends AbstractDiviner<TParams> {
16
- static override configSchemas = [ImageThumbnailDivinerConfigSchema, DivinerConfigSchema]
21
+ /**
22
+ * TODO: Once the shape settles, make a generic payload so that it
23
+ * can be used for other modules
24
+ */
25
+ interface State<T> {
26
+ state: T
27
+ }
17
28
 
18
- private _archivistInstance: Promise<ArchivistInstance> | undefined
19
- private _initializeArchivistConnectionIfNeededPromise: Promise<void> | undefined
20
- private _map: Record<string, string> | undefined
21
- private _payloadDivinerInstance: Promise<DivinerInstance> | undefined
22
- private _pollId?: string | number | NodeJS.Timeout
29
+ interface ImageThumbnailDivinerState {
30
+ offset: number
31
+ }
23
32
 
24
- // static override get configSchema() {
25
- // return ImageThumbnailDivinerConfigSchema
26
- // }
33
+ const ModuleStateSchema = 'network.xyo.module.state' as const
34
+ type ModuleStateSchema = typeof ModuleStateSchema
27
35
 
28
- get archivist() {
29
- return this.config.archivist
30
- }
36
+ type ModuleState = Payload<State<ImageThumbnailDivinerState>, ModuleStateSchema>
31
37
 
32
- get payloadDiviner() {
33
- return this.config.payloadDiviner
34
- }
38
+ const isModuleState = isPayloadOfSchemaType<ModuleState>(ModuleStateSchema)
39
+
40
+ type ConfigStoreKey = 'indexStore' | 'stateStore' | 'thumbnailStore'
41
+
42
+ type ConfigStore = Extract<keyof ImageThumbnailDivinerConfig, ConfigStoreKey>
43
+
44
+ const ImageThumbnailResultIndexSchema = `${ImageThumbnailSchema}.index` as const
45
+ type ImageThumbnailResultIndexSchema = typeof ImageThumbnailResultIndexSchema
46
+
47
+ interface ImageThumbnailResultInfo {
48
+ sources: string[]
49
+ // TODO: Something richer than HTTP status code that allows for info about failure modes
50
+ status: number
51
+ timestamp: number
52
+ url: string
53
+ }
54
+
55
+ type ImageThumbnailResult = Payload<ImageThumbnailResultInfo, ImageThumbnailResultIndexSchema>
56
+
57
+ const isImageThumbnailResult = isPayloadOfSchemaType<ImageThumbnailResult>(ImageThumbnailResultIndexSchema)
58
+
59
+ /**
60
+ * The fields that will need to be indexed on in the underlying store
61
+ */
62
+ type QueryableImageThumbnailResultProperties = Extract<keyof ImageThumbnailResult, 'url' | 'timestamp' | 'status'>
63
+
64
+ /**
65
+ * The query that will be used to retrieve the results from the underlying store
66
+ */
67
+ type ImageThumbnailResultQuery = PayloadDivinerQueryPayload & { schemas: [ImageThumbnailSchema] } & Pick<
68
+ ImageThumbnailResult,
69
+ QueryableImageThumbnailResultProperties
70
+ >
71
+
72
+ const moduleName = 'ImageThumbnailDiviner'
73
+
74
+ export class ImageThumbnailDiviner<TParams extends ImageThumbnailDivinerParams = ImageThumbnailDivinerParams> extends AbstractDiviner<TParams> {
75
+ static override configSchemas = [ImageThumbnailDivinerConfigSchema, DivinerConfigSchema]
76
+
77
+ private _pollId?: string | number | NodeJS.Timeout
35
78
 
36
79
  get payloadDivinerLimit() {
37
- return this.config.payloadDivinerLimit ?? 10000
80
+ return this.config.payloadDivinerLimit ?? 1_0000
38
81
  }
39
82
 
40
83
  get pollFrequency() {
41
- return this.config.pollFrequency
84
+ return this.config.pollFrequency ?? 10_000
42
85
  }
43
86
 
44
- //using promise as mutex
45
- async getArchivistInstance(): Promise<ArchivistInstance> {
46
- //if previously checked, but not found, clear promise
47
- if (this._archivistInstance && !(await this._archivistInstance)) {
48
- this._archivistInstance = undefined
49
- }
50
- this._archivistInstance =
51
- this._archivistInstance ??
52
- (async () => {
53
- const module = this.archivist ? await this.resolve(this.archivist) : undefined
54
- return asArchivistInstance(module, 'Provided archivist address did not resolve to an Archivist')
55
- })()
56
- return this._archivistInstance
87
+ protected backgroundDivine = async (): Promise<void> => {
88
+ // Load last state
89
+ const lastState = (await this.retrieveState()) ?? { offset: 0 }
90
+ const { offset } = lastState
91
+ // Get next batch of results
92
+ const boundWitnessDiviner = await this.getBoundWitnessDivinerForStore('thumbnailStore')
93
+ const query = new PayloadBuilder<BoundWitnessDivinerQueryPayload>({ schema: BoundWitnessDivinerQuerySchema }).fields({
94
+ limit: this.payloadDivinerLimit,
95
+ offset,
96
+ order: 'asc',
97
+ payload_schemas: [ImageThumbnailSchema, TimestampSchema],
98
+ })
99
+ const batch = await boundWitnessDiviner.divine([query])
100
+ if (batch.length === 0) return
101
+ const imageThumbnailTimestampTuples = batch
102
+ .filter(isBoundWitness)
103
+ .map((bw) => {
104
+ const imageThumbnailIndex = bw.payload_schemas?.findIndex((schema) => schema === ImageThumbnailSchema)
105
+ const timestampIndex = bw.payload_schemas?.findIndex((schema) => schema === TimestampSchema)
106
+ if (!imageThumbnailIndex || !timestampIndex) return undefined
107
+ const imageThumbnail = bw.payload_hashes?.[imageThumbnailIndex]
108
+ const timestamp = bw.payload_hashes?.[timestampIndex]
109
+ return [imageThumbnail, timestamp] as const
110
+ })
111
+ .filter(exists)
112
+ const archivist = await this.getArchivistForStore('thumbnailStore')
113
+ const payloadTuples = (
114
+ await Promise.all(
115
+ imageThumbnailTimestampTuples.map(async ([imageThumbnailHash, timestampHash]) => {
116
+ const results = await archivist.get([imageThumbnailHash, timestampHash])
117
+ const imageThumbnailPayload = results.find(isImageThumbnail)
118
+ const timestampPayload = results.find(isTimestamp)
119
+ if (!imageThumbnailPayload || !timestampPayload) return undefined
120
+ const calculatedImageThumbnailHash = await PayloadHasher.hashAsync(imageThumbnailPayload)
121
+ const calculatedTimestampHash = await PayloadHasher.hashAsync(timestampPayload)
122
+ if (imageThumbnailHash !== calculatedImageThumbnailHash || timestampHash !== calculatedTimestampHash) return undefined
123
+ return [imageThumbnailHash, imageThumbnailPayload, timestampHash, timestampPayload] as const
124
+ }),
125
+ )
126
+ ).filter(exists)
127
+ // Build index results
128
+ const indexedResults = payloadTuples.map(([thumbnailHash, thumbnailPayload, timestampHash, timestampPayload]) => {
129
+ const { url } = thumbnailPayload
130
+ const { timestamp } = timestampPayload
131
+ const status = thumbnailPayload.http?.status ?? -1
132
+ const sources = [thumbnailHash, timestampHash]
133
+ const result = new PayloadBuilder<ImageThumbnailResult>({ schema: ImageThumbnailResultIndexSchema })
134
+ .fields({ sources, status, timestamp, url })
135
+ .build()
136
+ return result
137
+ })
138
+ // Insert index results
139
+ const indexArchivist = await this.getArchivistForStore('indexStore')
140
+ await indexArchivist.insert(indexedResults)
141
+ // Update state
142
+ const nextOffset = offset + batch.length + 1
143
+ const currentState = { ...lastState, offset: nextOffset }
144
+ await this.commitState(currentState)
57
145
  }
58
146
 
59
- //using promise as mutex
60
- async getPayloadDivinerInstance(): Promise<DivinerInstance | undefined> {
61
- const payloadDivinerAddress = this.payloadDiviner
62
- if (payloadDivinerAddress) {
63
- //if previously checked, but not found, clear promise
64
- if (this._payloadDivinerInstance && !(await this._payloadDivinerInstance)) {
65
- this._payloadDivinerInstance = undefined
66
- }
67
- this._payloadDivinerInstance =
68
- this._payloadDivinerInstance ??
69
- (async () => {
70
- const module = await this.resolve(payloadDivinerAddress)
71
- return asDivinerInstance(module, 'Provided payload diviner address did not resolve to a Diviner')
72
- })()
73
-
74
- return this._payloadDivinerInstance
75
- }
147
+ /**
148
+ * Commit the internal state of the Diviner process. This is similar
149
+ * to a transaction completion in a database and should only be called
150
+ * when results have been successfully persisted to the appropriate
151
+ * external stores.
152
+ */
153
+ protected async commitState(state: ImageThumbnailDivinerState) {
154
+ const stateStore = assertEx(this.config.stateStore?.archivist, `${moduleName}: No stateStore configured`)
155
+ const module = assertEx(await this.resolve(stateStore), `${moduleName}: Failed to resolve stateStore`)
156
+ await withArchivistModule(module, async (archivist) => {
157
+ const mod = ArchivistWrapper.wrap(archivist, this.account)
158
+ const payload = new PayloadBuilder<ModuleState>({ schema: ModuleStateSchema }).fields({ state }).build()
159
+ await mod.insert([payload])
160
+ })
76
161
  }
77
162
 
78
- protected override async divineHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {
79
- await this.initializeArchivistConnectionIfNeeded()
80
- const urls = payloads.map((urlPayload) => urlPayload.url)
81
- const map = await this.getSafeMap()
82
- const archivist = await this.getArchivistInstance()
83
- const hashes = compact(urls.map((url) => map?.[url]))
84
- return (await archivist.get(hashes)).filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema)
163
+ protected override async divineHandler(payloads: Payload[] = []): Promise<ImageThumbnailResult[]> {
164
+ const urls = payloads.filter(isUrlPayload).map((urlPayload) => urlPayload.url)
165
+ const diviner = await this.getPayloadDivinerForStore('indexStore')
166
+ const results = (
167
+ await Promise.all(
168
+ urls.map(async (url) => {
169
+ const query = new PayloadBuilder<ImageThumbnailResultQuery>({ schema: PayloadDivinerQuerySchema })
170
+ // TODO: Expose status, limit (and possibly offset) to caller. Currently only exposing URL
171
+ .fields({ limit: 1, offset: 0, order: 'desc', url })
172
+ .build()
173
+ return await diviner.divine([query])
174
+ }),
175
+ )
176
+ )
177
+ .flat()
178
+ .filter(isImageThumbnailResult)
179
+ return results
85
180
  }
86
181
 
87
- //using promise as mutex
88
- protected initializeArchivistConnectionIfNeeded() {
89
- this._initializeArchivistConnectionIfNeededPromise =
90
- this._initializeArchivistConnectionIfNeededPromise ??
91
- (async () => {
92
- if (!this._map) {
93
- await this.attachArchivistEvents()
94
- console.log('initializeArchivistConnectionIfNeeded: attachArchivistEvents done')
95
- await this.poll()
96
- console.log('initializeArchivistConnectionIfNeeded: poll done')
97
- }
98
- })()
99
- return this._initializeArchivistConnectionIfNeededPromise
182
+ protected async getArchivistForStore(store: ConfigStore, wrap?: boolean) {
183
+ const name = assertEx(this.config?.[store]?.boundWitnessDiviner, () => `${moduleName}: Config for ${store}.archivist not specified`)
184
+ const mod = assertEx(await this.resolve(name), () => `${moduleName}: Failed to resolve ${store}.archivist`)
185
+ return wrap
186
+ ? ArchivistWrapper.wrap(mod, this.account)
187
+ : asArchivistInstance(mod, () => `${moduleName}: ${store}.boundWitnessDiviner is not an Archivist`)
100
188
  }
101
189
 
102
- protected async loadMap() {
103
- if (this.payloadDiviner) {
104
- return await this.loadMapWithPayloadDiviner()
105
- } else {
106
- return await this.loadMapWithAll()
107
- }
190
+ protected async getBoundWitnessDivinerForStore(store: ConfigStore, wrap?: boolean) {
191
+ const name = assertEx(this.config?.[store]?.boundWitnessDiviner, () => `${moduleName}: Config for ${store}.boundWitnessDiviner not specified`)
192
+ const mod = assertEx(await this.resolve(name), () => `${moduleName}: Failed to resolve ${store}.boundWitnessDiviner`)
193
+ return wrap
194
+ ? DivinerWrapper.wrap(mod, this.account)
195
+ : asDivinerInstance(mod, () => `${moduleName}: ${store}.boundWitnessDiviner is not a Diviner`)
108
196
  }
109
197
 
110
- protected async loadMapWithAll() {
111
- if (await this.started()) {
112
- const archivist = await this.getArchivistInstance()
113
- assertEx(archivist.all, "Archivist does not support 'all'")
114
- const allPayloads = (await archivist.all?.()) ?? []
115
- const imagePayloadPairs = await Promise.all(
116
- allPayloads
117
- .filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema)
118
- .map<Promise<[string, ImageThumbnail]>>(async (payload) => [await PayloadHasher.hashAsync(payload), payload]),
119
- )
120
- this._map = imagePayloadPairs.reduce<Record<string, string>>((prev, [hash, payload]) => {
121
- prev[payload.sourceUrl] = hash
122
- return prev
123
- }, {})
124
- }
198
+ protected async getPayloadDivinerForStore(store: ConfigStore, wrap?: boolean) {
199
+ const name = assertEx(this.config?.[store]?.payloadDiviner, () => `${moduleName}: Config for ${store}.payloadDiviner not specified`)
200
+ const mod = assertEx(await this.resolve(name), () => `${moduleName}: Failed to resolve ${store}.payloadDiviner`)
201
+ return wrap ? DivinerWrapper.wrap(mod, this.account) : asDivinerInstance(mod, () => `${moduleName}: ${store}.payloadDiviner is not a Diviner`)
125
202
  }
126
203
 
127
- protected async loadMapWithPayloadDiviner() {
128
- console.log('loadMapWithPayloadDiviner: started')
129
- if (await this.started()) {
130
- const diviner = await this.getPayloadDivinerInstance()
131
- let offset: number | undefined = undefined
132
- let moreAvailable = true
133
- if (diviner) {
134
- const newMap: Record<string, string> = {}
135
- while (moreAvailable) {
136
- const payloadDivinerQuery: PayloadDivinerQueryPayload = {
137
- limit: this.payloadDivinerLimit,
138
- offset,
139
- schema: PayloadDivinerQuerySchema,
140
- schemas: [ImageThumbnailSchema],
141
- }
142
- const payloads = await diviner.divine([payloadDivinerQuery])
143
- offset = (offset ?? 0) + payloads.length
144
- moreAvailable = payloads.length > 0
145
- console.log(`loadMapWithPayloadDiviner.offset: ${offset}`)
146
- console.log(`loadMapWithPayloadDiviner.moreAvailable: ${moreAvailable}`)
147
- const imagePayloadPairs = await Promise.all(
148
- payloads
149
- .filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema)
150
- .map<Promise<[string, ImageThumbnail]>>(async (payload) => [await PayloadHasher.hashAsync(payload), payload]),
204
+ /**
205
+ * Retrieves the last state of the Diviner process. Used to recover state after
206
+ * preemptions, reboots, etc.
207
+ */
208
+ protected async retrieveState(): Promise<ImageThumbnailDivinerState | undefined> {
209
+ let hash: string = ''
210
+ const diviner = await this.getBoundWitnessDivinerForStore('stateStore')
211
+ const query = new PayloadBuilder<PayloadDivinerQueryPayload>({ schema: PayloadDivinerQuerySchema }).fields({
212
+ address: this.account.address,
213
+ limit: 1,
214
+ offset: 0,
215
+ order: 'desc',
216
+ schemas: [ImageThumbnailSchema],
217
+ })
218
+ const boundWitnesses = await diviner.divine([query])
219
+ if (boundWitnesses.length > 0) {
220
+ const boundWitness = boundWitnesses[0]
221
+ if (isBoundWitness(boundWitness)) {
222
+ // Find the index for this address in the BoundWitness that is a ModuleState
223
+ hash = boundWitness.addresses
224
+ .map((address, index) => ({ address, index }))
225
+ .filter(({ address }) => address === this.account.address)
226
+ .reduce(
227
+ (prev, curr) => (boundWitness.payload_schemas?.[curr?.index] === ModuleStateSchema ? boundWitness.payload_hashes[curr?.index] : prev),
228
+ '',
151
229
  )
152
- imagePayloadPairs.forEach(([hash, payload]) => (newMap[payload.sourceUrl] = hash))
153
- }
154
- this._map = newMap
155
230
  }
156
231
  }
232
+
233
+ // If we able to located the last state
234
+ if (hash) {
235
+ // Get last state
236
+ const stateStoreArchivist = assertEx(this.config.stateStore?.archivist, `${moduleName}: No stateStore archivist configured`)
237
+ await withArchivistModule(
238
+ assertEx(await this.resolve(stateStoreArchivist), `${moduleName}: Failed to resolve stateStore archivist`),
239
+ async (mod) => {
240
+ const archivist = ArchivistWrapper.wrap(mod, this.account)
241
+ const payloads = await archivist.get([hash])
242
+ if (payloads.length > 0) {
243
+ const payload = payloads[0]
244
+ if (isModuleState(payload)) {
245
+ return payload.state
246
+ }
247
+ }
248
+ },
249
+ )
250
+ }
251
+ return undefined
157
252
  }
158
253
 
159
254
  protected override async stopHandler(_timeout?: number | undefined): Promise<boolean> {
@@ -164,42 +259,13 @@ export class ImageThumbnailDiviner<TParams extends ImageThumbnailDivinerParams =
164
259
  return await super.stopHandler()
165
260
  }
166
261
 
167
- private async attachArchivistEvents() {
168
- const archivist = await this.getArchivistInstance()
169
- const mapPromise = this.getSafeMap()
170
- archivist.on('inserted', async ({ payloads }) => {
171
- const map = await mapPromise
172
- const thumbnails = compact(payloads.filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema))
173
- await Promise.all(thumbnails.map(async (payload) => (map[payload.sourceUrl] = await PayloadHasher.hashAsync(payload))))
174
- })
175
- }
176
-
177
- private async getSafeMap() {
178
- let mapRetry = 100 //10 seconds max
179
- let map = this._map
180
- while (!map) {
181
- await delay(100)
182
- mapRetry = mapRetry - 1
183
- if (mapRetry === 0) {
184
- throw Error('Map Not Loaded')
185
- }
186
- map = this._map
187
- }
188
- return map
189
- }
190
-
191
262
  private async poll() {
192
263
  if (await this.started()) {
193
- const pollFrequency = this.pollFrequency
194
- if (pollFrequency) {
195
- this._pollId = setTimeout(async () => {
196
- this._pollId = undefined
197
- await this.loadMap()
198
- await this.poll()
199
- }, pollFrequency)
200
- } else {
201
- await this.loadMap()
202
- }
264
+ this._pollId = setTimeout(async () => {
265
+ this._pollId = undefined
266
+ await this.backgroundDivine()
267
+ await this.poll()
268
+ }, this.pollFrequency)
203
269
  }
204
270
  }
205
271
  }