@xyo-network/image-thumbnail-plugin 2.71.11 → 2.71.13

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 (44) hide show
  1. package/dist/cjs/Diviner/Config.js +6 -0
  2. package/dist/cjs/Diviner/Config.js.map +1 -0
  3. package/dist/cjs/Diviner/Diviner.js +199 -0
  4. package/dist/cjs/Diviner/Diviner.js.map +1 -0
  5. package/dist/cjs/Diviner/Params.js +3 -0
  6. package/dist/cjs/Diviner/Params.js.map +1 -0
  7. package/dist/cjs/Diviner/index.js +7 -0
  8. package/dist/cjs/Diviner/index.js.map +1 -0
  9. package/dist/cjs/Plugin.js +6 -1
  10. package/dist/cjs/Plugin.js.map +1 -1
  11. package/dist/cjs/index.js +1 -0
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/docs.json +28852 -6911
  14. package/dist/esm/Diviner/Config.js +3 -0
  15. package/dist/esm/Diviner/Config.js.map +1 -0
  16. package/dist/esm/Diviner/Diviner.js +175 -0
  17. package/dist/esm/Diviner/Diviner.js.map +1 -0
  18. package/dist/esm/Diviner/Params.js +2 -0
  19. package/dist/esm/Diviner/Params.js.map +1 -0
  20. package/dist/esm/Diviner/index.js +4 -0
  21. package/dist/esm/Diviner/index.js.map +1 -0
  22. package/dist/esm/Plugin.js +7 -2
  23. package/dist/esm/Plugin.js.map +1 -1
  24. package/dist/esm/index.js +1 -0
  25. package/dist/esm/index.js.map +1 -1
  26. package/dist/types/Diviner/Config.d.ts +10 -0
  27. package/dist/types/Diviner/Config.d.ts.map +1 -0
  28. package/dist/types/Diviner/Diviner.d.ts +29 -0
  29. package/dist/types/Diviner/Diviner.d.ts.map +1 -0
  30. package/dist/types/Diviner/Params.d.ts +5 -0
  31. package/dist/types/Diviner/Params.d.ts.map +1 -0
  32. package/dist/types/Diviner/index.d.ts +4 -0
  33. package/dist/types/Diviner/index.d.ts.map +1 -0
  34. package/dist/types/Plugin.d.ts +49 -1
  35. package/dist/types/Plugin.d.ts.map +1 -1
  36. package/dist/types/index.d.ts +1 -0
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/package.json +16 -9
  39. package/src/Diviner/Config.ts +12 -0
  40. package/src/Diviner/Diviner.ts +196 -0
  41. package/src/Diviner/Params.ts +6 -0
  42. package/src/Diviner/index.ts +3 -0
  43. package/src/Plugin.ts +7 -2
  44. package/src/index.ts +1 -0
@@ -0,0 +1,196 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { delay } from '@xylabs/delay'
3
+ import { AbstractDiviner } from '@xyo-network/abstract-diviner'
4
+ import { ArchivistInstance, asArchivistInstance } from '@xyo-network/archivist-model'
5
+ import { PayloadHasher } from '@xyo-network/core'
6
+ import { asDivinerInstance, DivinerConfigSchema, DivinerInstance } from '@xyo-network/diviner-model'
7
+ import { PayloadDivinerQueryPayload, PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
8
+ import { ImageThumbnail, ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
9
+ import { UrlPayload } from '@xyo-network/url-payload-plugin'
10
+ import compact from 'lodash/compact'
11
+
12
+ import { ImageThumbnailDivinerConfigSchema } from './Config'
13
+ import { ImageThumbnailDivinerParams } from './Params'
14
+
15
+ export class ImageThumbnailDiviner<TParams extends ImageThumbnailDivinerParams = ImageThumbnailDivinerParams> extends AbstractDiviner<TParams> {
16
+ static override configSchemas = [ImageThumbnailDivinerConfigSchema, DivinerConfigSchema]
17
+
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
23
+
24
+ get archivist() {
25
+ return this.config.archivist
26
+ }
27
+
28
+ get payloadDiviner() {
29
+ return this.config.payloadDiviner
30
+ }
31
+
32
+ get pollFrequency() {
33
+ return this.config.pollFrequency
34
+ }
35
+
36
+ //using promise as mutex
37
+ async getArchivistInstance(): Promise<ArchivistInstance> {
38
+ //if previously checked, but not found, clear promise
39
+ if (this._archivistInstance && !(await this._archivistInstance)) {
40
+ this._archivistInstance = undefined
41
+ }
42
+ this._archivistInstance =
43
+ this._archivistInstance ??
44
+ (async () => {
45
+ const module = await this.resolve(this.archivist)
46
+ return asArchivistInstance(module, 'Provided archivist address did not resolve to an Archivist')
47
+ })()
48
+ return this._archivistInstance
49
+ }
50
+
51
+ //using promise as mutex
52
+ async getPayloadDivinerInstance(): Promise<DivinerInstance | undefined> {
53
+ const payloadDivinerAddress = this.payloadDiviner
54
+ if (payloadDivinerAddress) {
55
+ //if previously checked, but not found, clear promise
56
+ if (this._payloadDivinerInstance && !(await this._payloadDivinerInstance)) {
57
+ this._payloadDivinerInstance = undefined
58
+ }
59
+ this._payloadDivinerInstance =
60
+ this._payloadDivinerInstance ??
61
+ (async () => {
62
+ const module = await this.resolve(payloadDivinerAddress)
63
+ return asDivinerInstance(module, 'Provided payload diviner address did not resolve to a Diviner')
64
+ })()
65
+
66
+ return this._payloadDivinerInstance
67
+ }
68
+ }
69
+
70
+ protected override async divineHandler(payloads: UrlPayload[] = []): Promise<ImageThumbnail[]> {
71
+ await this.initializeArchivistConnectionIfNeeded()
72
+ const urls = payloads.map((urlPayload) => urlPayload.url)
73
+ const map = await this.getSafeMap()
74
+ const archivist = await this.getArchivistInstance()
75
+ const hashes = compact(urls.map((url) => map?.[url]))
76
+ return (await archivist.get(hashes)).filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema)
77
+ }
78
+
79
+ //using promise as mutex
80
+ protected initializeArchivistConnectionIfNeeded() {
81
+ this._initializeArchivistConnectionIfNeededPromise =
82
+ this._initializeArchivistConnectionIfNeededPromise ??
83
+ (async () => {
84
+ if (!this._map) {
85
+ await this.attachArchivistEvents()
86
+ console.log('initializeArchivistConnectionIfNeeded: attachArchivistEvents done')
87
+ await this.poll()
88
+ console.log('initializeArchivistConnectionIfNeeded: poll done')
89
+ }
90
+ })()
91
+ return this._initializeArchivistConnectionIfNeededPromise
92
+ }
93
+
94
+ protected async loadMap() {
95
+ if (this.payloadDiviner) {
96
+ return await this.loadMapWithPayloadDiviner()
97
+ } else {
98
+ return await this.loadMapWithAll()
99
+ }
100
+ }
101
+
102
+ protected async loadMapWithAll() {
103
+ if (await this.started()) {
104
+ const archivist = await this.getArchivistInstance()
105
+ assertEx(archivist.all, "Archivist does not support 'all'")
106
+ const allPayloads = (await archivist.all?.()) ?? []
107
+ const imagePayloadPairs = await Promise.all(
108
+ allPayloads
109
+ .filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema)
110
+ .map<Promise<[string, ImageThumbnail]>>(async (payload) => [await PayloadHasher.hashAsync(payload), payload]),
111
+ )
112
+ this._map = imagePayloadPairs.reduce<Record<string, string>>((prev, [hash, payload]) => {
113
+ prev[payload.sourceUrl] = hash
114
+ return prev
115
+ }, {})
116
+ }
117
+ }
118
+
119
+ protected async loadMapWithPayloadDiviner() {
120
+ console.log('loadMapWithPayloadDiviner: started')
121
+ if (await this.started()) {
122
+ const diviner = await this.getPayloadDivinerInstance()
123
+ let offset: number | undefined = undefined
124
+ let moreAvailable = true
125
+ if (diviner) {
126
+ const newMap: Record<string, string> = {}
127
+ while (moreAvailable) {
128
+ const payloadDivinerQuery: PayloadDivinerQueryPayload = {
129
+ offset,
130
+ schema: PayloadDivinerQuerySchema,
131
+ schemas: [ImageThumbnailSchema],
132
+ }
133
+ const payloads = await diviner.divine([payloadDivinerQuery])
134
+ offset = (offset ?? 0) + payloads.length
135
+ moreAvailable = payloads.length > 0
136
+ console.log(`loadMapWithPayloadDiviner.offset: ${offset}`)
137
+ console.log(`loadMapWithPayloadDiviner.moreAvailable: ${moreAvailable}`)
138
+ const imagePayloadPairs = await Promise.all(
139
+ payloads
140
+ .filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema)
141
+ .map<Promise<[string, ImageThumbnail]>>(async (payload) => [await PayloadHasher.hashAsync(payload), payload]),
142
+ )
143
+ imagePayloadPairs.forEach(([hash, payload]) => (newMap[payload.sourceUrl] = hash))
144
+ }
145
+ this._map = newMap
146
+ }
147
+ }
148
+ }
149
+
150
+ protected override async stopHandler(_timeout?: number | undefined): Promise<boolean> {
151
+ if (this._pollId) {
152
+ clearTimeout(this._pollId)
153
+ this._pollId = undefined
154
+ }
155
+ return await super.stopHandler()
156
+ }
157
+
158
+ private async attachArchivistEvents() {
159
+ const archivist = await this.getArchivistInstance()
160
+ const mapPromise = this.getSafeMap()
161
+ archivist.on('inserted', async ({ payloads }) => {
162
+ const map = await mapPromise
163
+ const thumbnails = compact(payloads.filter((payload): payload is ImageThumbnail => payload.schema === ImageThumbnailSchema))
164
+ await Promise.all(thumbnails.map(async (payload) => (map[payload.sourceUrl] = await PayloadHasher.hashAsync(payload))))
165
+ })
166
+ }
167
+
168
+ private async getSafeMap() {
169
+ let mapRetry = 100 //10 seconds max
170
+ let map = this._map
171
+ while (!map) {
172
+ await delay(100)
173
+ mapRetry = mapRetry - 1
174
+ if (mapRetry === 0) {
175
+ throw Error('Map Not Loaded')
176
+ }
177
+ map = this._map
178
+ }
179
+ return map
180
+ }
181
+
182
+ private async poll() {
183
+ if (await this.started()) {
184
+ const pollFrequency = this.pollFrequency
185
+ if (pollFrequency) {
186
+ this._pollId = setTimeout(async () => {
187
+ this._pollId = undefined
188
+ await this.loadMap()
189
+ await this.poll()
190
+ }, pollFrequency)
191
+ } else {
192
+ await this.loadMap()
193
+ }
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,6 @@
1
+ import { DivinerParams } from '@xyo-network/diviner-model'
2
+ import { AnyConfigSchema } from '@xyo-network/module'
3
+
4
+ import { ImageThumbnailDivinerConfig } from './Config'
5
+
6
+ export type ImageThumbnailDivinerParams = DivinerParams<AnyConfigSchema<ImageThumbnailDivinerConfig>>
@@ -0,0 +1,3 @@
1
+ export * from './Config'
2
+ export * from './Diviner'
3
+ export * from './Params'
package/src/Plugin.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  import { ImageThumbnailSchema } from '@xyo-network/image-thumbnail-payload-plugin'
2
2
  import { PayloadSetSchema } from '@xyo-network/payload-model'
3
- import { createPayloadSetWitnessPlugin } from '@xyo-network/payloadset-plugin'
3
+ import { createPayloadSetDualPlugin } from '@xyo-network/payloadset-plugin'
4
4
 
5
+ import { ImageThumbnailDiviner } from './Diviner'
5
6
  import { ImageThumbnailWitness } from './Witness'
6
7
 
7
8
  export const ImageThumbnailPlugin = () =>
8
- createPayloadSetWitnessPlugin<ImageThumbnailWitness>(
9
+ createPayloadSetDualPlugin<ImageThumbnailWitness, ImageThumbnailDiviner>(
9
10
  { required: { [ImageThumbnailSchema]: 1 }, schema: PayloadSetSchema },
10
11
  {
12
+ diviner: async (params) => {
13
+ const result = await ImageThumbnailDiviner.create(params)
14
+ return result
15
+ },
11
16
  witness: async (params) => {
12
17
  const result = await ImageThumbnailWitness.create(params)
13
18
  return result
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ImageThumbnailPlugin } from './Plugin'
2
2
 
3
+ export * from './Diviner'
3
4
  export * from './Witness'
4
5
 
5
6
  export { ImageThumbnailPlugin }