@xyo-network/archivist-abstract 5.3.22 → 5.3.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/archivist-abstract",
3
- "version": "5.3.22",
3
+ "version": "5.3.25",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -30,7 +30,6 @@
30
30
  "types": "dist/neutral/index.d.ts",
31
31
  "files": [
32
32
  "dist",
33
- "src",
34
33
  "!**/*.bench.*",
35
34
  "!**/*.spec.*",
36
35
  "!**/*.test.*",
@@ -38,41 +37,38 @@
38
37
  ],
39
38
  "dependencies": {
40
39
  "@opentelemetry/api": "^1.9.1",
41
- "@xyo-network/account-model": "~5.3.22",
42
- "@xyo-network/archivist-model": "~5.3.22",
43
- "@xyo-network/boundwitness-model": "~5.3.22",
44
- "@xyo-network/boundwitness-wrapper": "~5.3.22",
45
- "@xyo-network/module-abstract": "~5.3.22",
46
- "@xyo-network/module-model": "~5.3.22",
47
- "@xyo-network/payload-builder": "~5.3.22",
48
- "@xyo-network/payload-model": "~5.3.22",
49
- "lru-cache": "~11.2.7"
40
+ "lru-cache": "~11.2.7",
41
+ "@xyo-network/account-model": "~5.3.25",
42
+ "@xyo-network/archivist-model": "~5.3.25",
43
+ "@xyo-network/boundwitness-wrapper": "~5.3.25",
44
+ "@xyo-network/boundwitness-model": "~5.3.25",
45
+ "@xyo-network/module-abstract": "~5.3.25",
46
+ "@xyo-network/module-model": "~5.3.25",
47
+ "@xyo-network/payload-builder": "~5.3.25",
48
+ "@xyo-network/payload-model": "~5.3.25"
50
49
  },
51
50
  "devDependencies": {
52
51
  "@types/node": "^25.5.0",
53
- "@xylabs/sdk-js": "^5.0.91",
54
- "@xylabs/ts-scripts-common": "~7.6.8",
55
- "@xylabs/ts-scripts-yarn3": "~7.6.8",
56
- "@xylabs/tsconfig": "~7.6.8",
57
- "@xyo-network/account-model": "~5.3.22",
58
- "@xyo-network/archivist-model": "~5.3.22",
59
- "@xyo-network/boundwitness-model": "~5.3.22",
60
- "@xyo-network/boundwitness-wrapper": "~5.3.22",
61
- "@xyo-network/module-abstract": "~5.3.22",
62
- "@xyo-network/module-model": "~5.3.22",
63
- "@xyo-network/payload-builder": "~5.3.22",
64
- "@xyo-network/payload-model": "~5.3.22",
52
+ "@xylabs/sdk-js": "^5.0.93",
53
+ "@xylabs/ts-scripts-common": "~7.6.16",
54
+ "@xylabs/ts-scripts-pnpm": "~7.6.16",
55
+ "@xylabs/tsconfig": "~7.6.16",
65
56
  "acorn": "^8.16.0",
66
57
  "axios": "^1.14.0",
67
- "cosmiconfig": "^9.0.1",
68
- "esbuild": "^0.27.4",
69
- "eslint": "^10.1.0",
58
+ "esbuild": "^0.28.0",
70
59
  "ethers": "^6.16.0",
71
60
  "lru-cache": "^11.2.7",
72
- "rollup": "^4.60.1",
73
61
  "tslib": "^2.8.1",
74
62
  "typescript": "~5.9.3",
75
- "zod": "^4.3.6"
63
+ "zod": "^4.3.6",
64
+ "@xyo-network/account-model": "~5.3.25",
65
+ "@xyo-network/archivist-model": "~5.3.25",
66
+ "@xyo-network/boundwitness-model": "~5.3.25",
67
+ "@xyo-network/module-abstract": "~5.3.25",
68
+ "@xyo-network/boundwitness-wrapper": "~5.3.25",
69
+ "@xyo-network/module-model": "~5.3.25",
70
+ "@xyo-network/payload-builder": "~5.3.25",
71
+ "@xyo-network/payload-model": "~5.3.25"
76
72
  },
77
73
  "peerDependencies": {
78
74
  "@xylabs/sdk-js": "^5",
@@ -83,4 +79,4 @@
83
79
  "publishConfig": {
84
80
  "access": "public"
85
81
  }
86
- }
82
+ }
@@ -1,711 +0,0 @@
1
- /* eslint-disable max-lines */
2
- import type { Gauge, Meter } from '@opentelemetry/api'
3
- import type {
4
- Address, Hash, Promisable, PromisableArray,
5
- } from '@xylabs/sdk-js'
6
- import {
7
- assertEx,
8
- difference,
9
- exists, globallyUnique,
10
- isNull, isUndefined,
11
- } from '@xylabs/sdk-js'
12
- import type { AccountInstance } from '@xyo-network/account-model'
13
- import type {
14
- ArchivistAllQuery,
15
- ArchivistClearQuery,
16
- ArchivistCommitQuery,
17
- ArchivistDeleteQuery,
18
- ArchivistGetQuery,
19
- ArchivistInsertQuery,
20
- ArchivistInstance,
21
- ArchivistModuleEventData,
22
- ArchivistNextOptions,
23
- ArchivistNextQuery,
24
- ArchivistParams,
25
- ArchivistQueries,
26
- ArchivistSnapshotPayload,
27
- ArchivistSnapshotQuery,
28
- ArchivistStatsPayload,
29
- AttachableArchivistInstance,
30
- ReadArchivist,
31
- } from '@xyo-network/archivist-model'
32
- import {
33
- ArchivistAllQuerySchema,
34
- ArchivistClearQuerySchema,
35
- ArchivistCommitQuerySchema,
36
- ArchivistConfigSchema,
37
- ArchivistDeleteQuerySchema,
38
- ArchivistGetQuerySchema,
39
- ArchivistInsertQuerySchema,
40
- ArchivistNextQuerySchema,
41
- ArchivistSnapshotQuerySchema,
42
- ArchivistStatsPayloadSchema,
43
- asArchivistInstance,
44
- isArchivistInstance,
45
- } from '@xyo-network/archivist-model'
46
- import type { BoundWitness, QueryBoundWitness } from '@xyo-network/boundwitness-model'
47
- import { QueryBoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
48
- import { AbstractModuleInstance } from '@xyo-network/module-abstract'
49
- import type {
50
- ModuleConfig, ModuleIdentifier, ModuleQueryHandlerResult, ModuleQueryResult,
51
- } from '@xyo-network/module-model'
52
- import { creatableModule, duplicateModules } from '@xyo-network/module-model'
53
- import { PayloadBuilder } from '@xyo-network/payload-builder'
54
- import type {
55
- Payload, Schema, WithStorageMeta,
56
- } from '@xyo-network/payload-model'
57
- import { LRUCache } from 'lru-cache'
58
-
59
- import { StorageClassLabel } from './StorageClassLabel.ts'
60
-
61
- const NOT_IMPLEMENTED = 'Not implemented' as const
62
-
63
- export interface ActionConfig {
64
- emitEvents?: boolean
65
- }
66
-
67
- export interface InsertConfig extends ActionConfig {
68
- writeToParents?: boolean
69
- }
70
-
71
- interface ArchivistParentInstanceMap {
72
- commit?: Partial<Record<ModuleIdentifier, ArchivistInstance>>
73
- read?: Partial<Record<ModuleIdentifier, ArchivistInstance>>
74
- write?: Partial<Record<ModuleIdentifier, ArchivistInstance>>
75
- }
76
-
77
- creatableModule()
78
- export abstract class AbstractArchivist<
79
- TParams extends ArchivistParams = ArchivistParams,
80
- TEventData extends ArchivistModuleEventData = ArchivistModuleEventData,
81
- >
82
- extends AbstractModuleInstance<TParams, TEventData>
83
- implements AttachableArchivistInstance<TParams, TEventData, Payload> {
84
- static override readonly configSchemas: Schema[] = [...super.configSchemas, ArchivistConfigSchema]
85
- static override readonly defaultConfigSchema: Schema = ArchivistConfigSchema
86
- static override readonly labels = { ...super.labels, [StorageClassLabel]: 'unknown' }
87
- static override readonly uniqueName = globallyUnique('AbstractArchivist', AbstractArchivist, 'xyo')
88
-
89
- // override this if a specialized archivist should have a different default next limit
90
- protected static defaultNextLimitSetting = 100
91
-
92
- private _getCache?: LRUCache<Hash, WithStorageMeta<Payload>>
93
- private _parentArchivists?: ArchivistParentInstanceMap
94
- private _payloadCountGauge?: Gauge | null
95
- private _payloadCountMeter?: Meter | null
96
-
97
- // do not override this! It is meant to get the this.defaultNextLimitSetting and work if it is overridden
98
- static get defaultNextLimit() {
99
- return this.defaultNextLimitSetting
100
- }
101
-
102
- override get queries(): Schema[] {
103
- return [ArchivistGetQuerySchema, ...super.queries]
104
- }
105
-
106
- get requireAllParents() {
107
- return this.config.requireAllParents ?? false
108
- }
109
-
110
- protected get payloadCountGauge() {
111
- const meter = this.payloadCountMeter
112
- if (!isNull(meter)) {
113
- this._payloadCountGauge = meter?.createGauge('payloadCount', { description: 'Payloads in the archivist' })
114
- }
115
- return this._payloadCountGauge
116
- }
117
-
118
- protected get payloadCountMeter(): Meter | null {
119
- if (isUndefined(this._payloadCountMeter)) {
120
- this._payloadCountMeter = this.params?.meterProvider?.getMeter(this.id) ?? null
121
- }
122
- return this._payloadCountMeter ?? null
123
- }
124
-
125
- protected get storeParentReads() {
126
- return !!this.config?.storeParentReads
127
- }
128
-
129
- /** @deprecated use next or snapshot instead */
130
- async all(): Promise<WithStorageMeta<Payload>[]> {
131
- this._noOverride('all')
132
- this.isSupportedQuery(ArchivistAllQuerySchema, 'all')
133
- return await this.spanAsync(`${this.id}|all`, async () => {
134
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
135
- return []
136
- }
137
- try {
138
- await this.globalReentrancyMutex?.acquire()
139
- return await this.busy(async () => {
140
- await this.startedAsync('throw')
141
- return PayloadBuilder.omitPrivateStorageMeta(await this.allHandler()) as WithStorageMeta<Payload>[]
142
- })
143
- } finally {
144
- this.globalReentrancyMutex?.release()
145
- }
146
- }, { timeBudgetLimit: this.timeBudget })
147
- }
148
-
149
- /** deprecated use nextQuery or snapshotQuery instead */
150
- async allQuery(account: AccountInstance): Promise<ModuleQueryResult> {
151
- this._noOverride('allQuery')
152
- const queryPayload: ArchivistAllQuery = { schema: ArchivistAllQuerySchema }
153
- return await this.sendQueryRaw(queryPayload, undefined, account)
154
- }
155
-
156
- async clear(): Promise<void> {
157
- this._noOverride('clear')
158
- this.isSupportedQuery(ArchivistClearQuerySchema, 'clear')
159
- return await this.spanAsync(`${this.id}|clear`, async () => {
160
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
161
- return
162
- }
163
- try {
164
- await this.globalReentrancyMutex?.acquire()
165
- return await this.busy(async () => {
166
- await this.startedAsync('throw')
167
- await this.clearHandler()
168
- this.reportPayloadCount()
169
- await this.emit('cleared', { mod: this })
170
- })
171
- } finally {
172
- this.globalReentrancyMutex?.release()
173
- }
174
- }, { timeBudgetLimit: this.timeBudget })
175
- }
176
-
177
- async clearQuery(account: AccountInstance): Promise<ModuleQueryResult> {
178
- this._noOverride('clearQuery')
179
- const queryPayload: ArchivistClearQuery = { schema: ArchivistClearQuerySchema }
180
- return await this.sendQueryRaw(queryPayload, undefined, account)
181
- }
182
-
183
- async commit(): Promise<BoundWitness[]> {
184
- this._noOverride('commit')
185
- this.isSupportedQuery(ArchivistCommitQuerySchema, 'commit')
186
- return await this.spanAsync(`${this.id}commit`, async () => {
187
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
188
- return []
189
- }
190
- try {
191
- await this.globalReentrancyMutex?.acquire()
192
- return await this.busy(async () => {
193
- await this.startedAsync('throw')
194
- return await this.commitHandler()
195
- })
196
- } finally {
197
- this.globalReentrancyMutex?.release()
198
- }
199
- }, { timeBudgetLimit: this.timeBudget })
200
- }
201
-
202
- async commitQuery(account: AccountInstance): Promise<ModuleQueryResult> {
203
- this._noOverride('commitQuery')
204
- const queryPayload: ArchivistCommitQuery = { schema: ArchivistCommitQuerySchema }
205
- return await this.sendQueryRaw(queryPayload, undefined, account)
206
- }
207
-
208
- async delete(hashes: Hash[]): Promise<WithStorageMeta<Payload>[]> {
209
- this._noOverride('delete')
210
- this.isSupportedQuery(ArchivistDeleteQuerySchema, 'delete')
211
- return await this.spanAsync(`${this.id}|delete`, async () => {
212
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
213
- return []
214
- }
215
- try {
216
- await this.globalReentrancyMutex?.acquire()
217
- return await this.busy(async () => {
218
- await this.startedAsync('throw')
219
- return await this.deleteWithConfig(hashes)
220
- })
221
- } finally {
222
- this.globalReentrancyMutex?.release()
223
- }
224
- }, { timeBudgetLimit: this.timeBudget })
225
- }
226
-
227
- async deleteQuery(hashes: Hash[], account?: AccountInstance): Promise<ModuleQueryResult> {
228
- this._noOverride('deleteQuery')
229
- const queryPayload: ArchivistDeleteQuery = { hashes, schema: ArchivistDeleteQuerySchema }
230
- return await this.sendQueryRaw(queryPayload, undefined, account)
231
- }
232
-
233
- async get(hashes: Hash[]): Promise<WithStorageMeta<Payload>[]> {
234
- this._noOverride('get')
235
- this.isSupportedQuery(ArchivistGetQuerySchema, 'get')
236
- return await this.spanAsync(`${this.id}|get`, async () => {
237
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
238
- return []
239
- }
240
- try {
241
- await this.globalReentrancyMutex?.acquire()
242
- return await this.busy(async () => {
243
- await this.startedAsync('throw')
244
- return await this.getWithConfig(hashes)
245
- })
246
- } finally {
247
- this.globalReentrancyMutex?.release()
248
- }
249
- }, { timeBudgetLimit: this.timeBudget })
250
- }
251
-
252
- async getQuery(hashes: Hash[], account?: AccountInstance): Promise<ModuleQueryResult> {
253
- this._noOverride('getQuery')
254
- const queryPayload: ArchivistGetQuery = { hashes, schema: ArchivistGetQuerySchema }
255
- return await this.sendQueryRaw(queryPayload, undefined, account)
256
- }
257
-
258
- async insert(payloads: Payload[]): Promise<WithStorageMeta<Payload>[]> {
259
- this._noOverride('insert')
260
- this.isSupportedQuery(ArchivistInsertQuerySchema, 'insert')
261
- return await this.spanAsync(`${this.id}|insert`, async () => {
262
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
263
- return []
264
- }
265
- try {
266
- await this.globalReentrancyMutex?.acquire()
267
- return await this.busy(async () => {
268
- await this.startedAsync('throw')
269
- return await this.insertWithConfig(PayloadBuilder.omitStorageMeta(payloads))
270
- })
271
- } finally {
272
- this.globalReentrancyMutex?.release()
273
- }
274
- }, { timeBudgetLimit: this.timeBudget })
275
- }
276
-
277
- async insertQuery(payloads: Payload[], account?: AccountInstance): Promise<ModuleQueryResult> {
278
- this._noOverride('insertQuery')
279
- const queryPayload: ArchivistInsertQuery = { schema: ArchivistInsertQuerySchema }
280
- return await this.sendQueryRaw(queryPayload, payloads, account)
281
- }
282
-
283
- async next(options?: ArchivistNextOptions): Promise<WithStorageMeta<Payload>[]> {
284
- this._noOverride('next')
285
- this.isSupportedQuery(ArchivistNextQuerySchema, 'next')
286
- return await this.spanAsync(`${this.id}|next`, async () => {
287
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
288
- return []
289
- }
290
- try {
291
- await this.globalReentrancyMutex?.acquire()
292
- return await this.busy(async () => {
293
- await this.startedAsync('throw')
294
- const { limit = AbstractArchivist.defaultNextLimit, ...otherOptions } = options ?? {}
295
- return await this.nextWithConfig({ limit, ...otherOptions })
296
- })
297
- } finally {
298
- this.globalReentrancyMutex?.release()
299
- }
300
- }, { timeBudgetLimit: this.timeBudget })
301
- }
302
-
303
- async nextQuery(options?: ArchivistNextOptions, account?: AccountInstance): Promise<ModuleQueryResult> {
304
- this._noOverride('nextQuery')
305
- const queryPayload: ArchivistNextQuery = { schema: ArchivistNextQuerySchema, ...options }
306
- return await this.sendQueryRaw(queryPayload, undefined, account)
307
- }
308
-
309
- async snapshot(): Promise<ArchivistSnapshotPayload<WithStorageMeta<Payload>, Hash>[]> {
310
- this._noOverride('snapshot')
311
- this.isSupportedQuery(ArchivistSnapshotQuerySchema, 'snapshot')
312
- return await this.spanAsync(`${this.id}|snapshot`, async () => {
313
- if (this.reentrancy?.scope === 'global' && this.reentrancy.action === 'skip' && this.globalReentrancyMutex?.isLocked()) {
314
- throw new Error('Cannot take snapshot while in a global reentrancy lock')
315
- }
316
- try {
317
- await this.globalReentrancyMutex?.acquire()
318
- return await this.busy(async () => {
319
- await this.startedAsync('throw')
320
- return await this.snapshotHandler()
321
- })
322
- } finally {
323
- this.globalReentrancyMutex?.release()
324
- }
325
- }, { timeBudgetLimit: this.timeBudget })
326
- }
327
-
328
- async snapshotQuery(account?: AccountInstance): Promise<ModuleQueryResult> {
329
- this._noOverride('snapshotQuery')
330
- const queryPayload: ArchivistSnapshotQuery = { schema: ArchivistSnapshotQuerySchema }
331
- return await this.sendQueryRaw(queryPayload, undefined, account)
332
- }
333
-
334
- protected allHandler(): PromisableArray<WithStorageMeta<Payload>> {
335
- throw new Error(NOT_IMPLEMENTED)
336
- }
337
-
338
- protected clearHandler(): Promisable<void> {
339
- throw new Error(NOT_IMPLEMENTED)
340
- }
341
-
342
- protected commitHandler(): Promisable<BoundWitness[]> {
343
- throw new Error(NOT_IMPLEMENTED)
344
- }
345
-
346
- protected deleteHandler(_hashes: Hash[]): PromisableArray<WithStorageMeta<Payload>> {
347
- throw new Error(NOT_IMPLEMENTED)
348
- }
349
-
350
- protected async deleteWithConfig(hashes: Hash[], config?: ActionConfig): Promise<WithStorageMeta<Payload>[]> {
351
- const emitEvents = config?.emitEvents ?? true
352
-
353
- const payloads = await this.deleteHandler(hashes)
354
- const hashesDeleted = payloads.map(p => p._hash)
355
-
356
- if (emitEvents) {
357
- await this.emit('deleted', {
358
- hashes: hashesDeleted, payloads, mod: this,
359
- })
360
- }
361
- this.reportPayloadCount()
362
- return payloads
363
- }
364
-
365
- protected generateStats(): Promisable<ArchivistStatsPayload> {
366
- return {
367
- payloadCount: this.payloadCountHandler(),
368
- schema: ArchivistStatsPayloadSchema,
369
- }
370
- }
371
-
372
- protected async getFromParent(hashes: Hash[], archivist: ReadArchivist): Promise<[WithStorageMeta<Payload>[], Hash[]]> {
373
- const foundPairs = (await PayloadBuilder.dataHashPairs(await archivist.get(hashes))).filter(([, hash]) => {
374
- const askedFor = hashes.includes(hash)
375
- if (!askedFor) {
376
- console.warn(`Parent returned payload with hash not asked for: ${hash}`)
377
- // throw Error(`Parent returned payload with hash not asked for: ${hash}`)
378
- }
379
- return askedFor
380
- })
381
-
382
- const foundHashes = new Set(foundPairs.map(([, hash]) => hash))
383
- const foundPayloads = foundPairs.map(([payload]) => payload)
384
-
385
- const notfound = hashes.filter(hash => !foundHashes.has(hash))
386
- return [foundPayloads, notfound]
387
- }
388
-
389
- protected async getFromParents(hashes: Hash[]): Promise<[WithStorageMeta<Payload>[], Hash[]]> {
390
- const parents = Object.values((await this.parentArchivists())?.read ?? {})
391
- let remainingHashes = [...hashes]
392
- let parentIndex = 0
393
- let result: WithStorageMeta<Payload>[] = []
394
-
395
- // Intentionally doing this serially
396
- while (parentIndex < parents.length && remainingHashes.length > 0) {
397
- const parent = parents[parentIndex]
398
- if (parent) {
399
- const [found, notfound] = await this.getFromParent(remainingHashes, parent)
400
- result = [...result, ...found]
401
- remainingHashes = notfound
402
- }
403
- parentIndex++
404
- }
405
- return [result, remainingHashes]
406
- }
407
-
408
- protected getHandler(_hashes: Hash[]): Promisable<WithStorageMeta<Payload>[]> {
409
- throw new Error(NOT_IMPLEMENTED)
410
- }
411
-
412
- // eslint-disable-next-line max-statements
413
- protected async getWithConfig(hashes: Hash[], _config?: InsertConfig): Promise<WithStorageMeta<Payload>[]> {
414
- // Filter out duplicates
415
- const requestedHashes = new Set(hashes)
416
-
417
- // read from cache if we are caching
418
- const cache = this._getCache
419
- let fromCache: WithStorageMeta<Payload>[] = []
420
- let remainingHashes = [...requestedHashes]
421
- if (cache !== undefined) {
422
- fromCache = hashes.map(hash => cache.get(hash)).filter(exists)
423
- remainingHashes = hashes.filter(hash => !fromCache.some(payload => payload?._hash === hash || payload?._dataHash === hash))
424
- }
425
-
426
- // Attempt to find the payloads in the store
427
- const fromGet = await this.getHandler([...remainingHashes])
428
- const gotten = [...fromCache, ...fromGet].toSorted(PayloadBuilder.compareStorageMeta)
429
-
430
- // Do not just blindly return what the archivist told us but
431
- // ensure to only return requested payloads and keep track of
432
- // the ones it did not find so we can ask the parents.
433
- const foundPayloads: WithStorageMeta<Payload>[] = []
434
- const foundHashes = new Set<Hash>()
435
-
436
- // We are iterating over the returned result from the archivist
437
- // (not the array of hashes passed in) to preserve the natural order of the
438
- // hashes as returned by the archivist as that should loosely
439
- // correspond to the order when iterated and the symmetry will
440
- // be helpful for debugging
441
- for (const payload of gotten) {
442
- // trusting the hashes from the cache and parent
443
- const map: Record<Hash, WithStorageMeta<Payload>> = {
444
- [payload._hash]: payload,
445
- [payload._dataHash]: payload,
446
- }
447
- // Compute the hashes for this payload
448
- // const map = await PayloadBuilder.toAllHashMap([payload])
449
- for (const [key, payload] of Object.entries(map)) {
450
- let requestedPayloadFound = false
451
- const hash = key as Hash // Required cast as Object.entries always returns string keys
452
- // If this hash was requested
453
- if (
454
- requestedHashes.has(hash) // Indicate that we found it (but do not insert it yet). Since
455
- // one payload could satisfy two requested hashes (vit its dataHash
456
- // & rootHash) we only want to insert that payload once but we want
457
- // to keep track of all the hashes it satisfies so we can ask th
458
- // parents for the ones we did not find
459
- && !foundHashes.has(hash)
460
- ) {
461
- requestedPayloadFound = true
462
- // Add it to the list of found hashes
463
- foundHashes.add(hash)
464
- }
465
- if (requestedPayloadFound) foundPayloads.push(payload)
466
- }
467
- }
468
- // For all the hashes we did not find, ask the parents
469
- const notFoundHashes = [...difference(requestedHashes, foundHashes)]
470
- const [parentFoundPayloads] = await this.getFromParents(notFoundHashes)
471
-
472
- if (this.storeParentReads) {
473
- await this.insertWithConfig(parentFoundPayloads)
474
- }
475
- const result = this.omitClientMetaForDataHashes(
476
- hashes,
477
- (PayloadBuilder.omitPrivateStorageMeta([
478
- ...foundPayloads,
479
- ...parentFoundPayloads,
480
- ]) as WithStorageMeta<Payload>[]).toSorted(PayloadBuilder.compareStorageMeta),
481
- )
482
-
483
- // write to cache if we are caching
484
- if (cache !== undefined) {
485
- for (const payload of fromGet) {
486
- cache.set(payload._hash, payload)
487
- cache.set(payload._dataHash, payload)
488
- }
489
- for (const payload of parentFoundPayloads) {
490
- cache.set(payload._hash, payload)
491
- cache.set(payload._dataHash, payload)
492
- }
493
- }
494
-
495
- return result
496
- }
497
-
498
- protected insertHandler(_payloads: WithStorageMeta<Payload>[]): Promisable<WithStorageMeta<Payload>[]> {
499
- throw new Error(NOT_IMPLEMENTED)
500
- }
501
-
502
- protected async insertQueryHandler<T extends QueryBoundWitnessWrapper = QueryBoundWitnessWrapper>(query: T, payloads?: Payload[]) {
503
- assertEx(payloads, () => `Missing payloads: ${JSON.stringify(query.payload, null, 2)}`)
504
- const resolvedPayloads = await PayloadBuilder.filterIncludeByEitherHash(payloads, query.payloadHashes)
505
- assertEx(
506
- resolvedPayloads.length === query.payloadHashes.length,
507
- () => `Could not find some passed hashes [${resolvedPayloads.length} != ${query.payloadHashes.length}]`,
508
- )
509
- const queryPayload = await query.getQuery()
510
- const payloadsWithoutQuery = await PayloadBuilder.filterExclude(resolvedPayloads, await PayloadBuilder.dataHash(queryPayload))
511
- const result = await this.insertWithConfig(payloadsWithoutQuery)
512
- return result
513
- }
514
-
515
- protected async insertWithConfig(payloads: Payload[], config?: InsertConfig): Promise<WithStorageMeta<Payload>[]> {
516
- const emitEvents = config?.emitEvents ?? true
517
- const writeToParents = config?.writeToParents ?? true
518
-
519
- // remove the existing payloads
520
- const withStorageMeta = await PayloadBuilder.addStorageMeta(payloads)
521
- const hashes = withStorageMeta.map(p => p._hash)
522
- const existingPayloads = await this.getWithConfig(hashes)
523
- const existingHashes = new Set(existingPayloads.map(p => p._hash))
524
- const payloadsToInsert = withStorageMeta.filter(p => !existingHashes.has(p._hash))
525
-
526
- const insertedPayloads = await this.insertHandler(payloadsToInsert)
527
-
528
- if (writeToParents) {
529
- await this.writeToParents(insertedPayloads)
530
- }
531
- if (emitEvents) {
532
- await this.emit('inserted', {
533
- mod: this, payloads: insertedPayloads, outPayloads: insertedPayloads, inPayloads: payloads,
534
- })
535
- }
536
- this.reportPayloadCount()
537
- return PayloadBuilder.omitPrivateStorageMeta(insertedPayloads) as WithStorageMeta<Payload>[]
538
- }
539
-
540
- protected nextHandler(_options?: ArchivistNextOptions): Promisable<WithStorageMeta<Payload>[]> {
541
- throw new Error(NOT_IMPLEMENTED)
542
- }
543
-
544
- protected async nextWithConfig(options?: ArchivistNextOptions, _config?: InsertConfig): Promise<WithStorageMeta<Payload>[]> {
545
- const foundPayloads = await this.nextHandler(options)
546
- return PayloadBuilder.omitPrivateStorageMeta(foundPayloads) as WithStorageMeta<Payload>[]
547
- }
548
-
549
- protected async parentArchivists() {
550
- this._parentArchivists = this._parentArchivists ?? {
551
- commit: { ...await this.resolveArchivists(this.config?.parents?.commit, this.params.parents?.commit) },
552
- read: { ...await this.resolveArchivists(this.config?.parents?.read) },
553
- write: { ...await this.resolveArchivists(this.config?.parents?.write) },
554
- }
555
- return assertEx(this._parentArchivists)
556
- }
557
-
558
- // the number of payloads in the archivist, -1 if not implemented
559
- // the implementations of these must be fast, so they may not be promises and should read an auto updated value
560
- protected payloadCountHandler() {
561
- return -1
562
- }
563
-
564
- // eslint-disable-next-line max-statements
565
- protected override async queryHandler<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
566
- query: T,
567
- payloads: Payload[],
568
- queryConfig?: TConfig,
569
- ): Promise<ModuleQueryHandlerResult> {
570
- const sanitizedQuery = PayloadBuilder.omitStorageMeta(query) as T
571
- const sanitizedPayloads = PayloadBuilder.omitStorageMeta(payloads) as Payload[]
572
- const wrappedQuery = QueryBoundWitnessWrapper.parseQuery<ArchivistQueries>(sanitizedQuery, sanitizedPayloads)
573
- const queryPayload = await wrappedQuery.getQuery()
574
- assertEx(await this.queryable(sanitizedQuery, sanitizedPayloads, queryConfig))
575
- const resultPayloads: Payload[] = []
576
-
577
- switch (queryPayload.schema) {
578
- case ArchivistAllQuerySchema: {
579
- resultPayloads.push(...(await this.allHandler()))
580
- break
581
- }
582
- case ArchivistClearQuerySchema: {
583
- await this.clearHandler()
584
- break
585
- }
586
- case ArchivistCommitQuerySchema: {
587
- resultPayloads.push(...(await this.commitHandler()))
588
- break
589
- }
590
- case ArchivistDeleteQuerySchema: {
591
- const typedQueryPayload = queryPayload as ArchivistDeleteQuery
592
- resultPayloads.push(...(await this.deleteWithConfig(typedQueryPayload.hashes)))
593
- break
594
- }
595
- case ArchivistGetQuerySchema: {
596
- const typedQueryPayload = queryPayload as ArchivistGetQuery
597
- resultPayloads.push(...(await this.getWithConfig(typedQueryPayload.hashes ?? [])))
598
- break
599
- }
600
- case ArchivistInsertQuerySchema: {
601
- resultPayloads.push(...(await this.insertQueryHandler(wrappedQuery, sanitizedPayloads)))
602
- break
603
- }
604
- case ArchivistNextQuerySchema: {
605
- const typedQueryPayload = queryPayload as ArchivistNextQuery
606
- resultPayloads.push(...(await this.nextHandler(typedQueryPayload)))
607
- break
608
- }
609
- case ArchivistSnapshotQuerySchema: {
610
- resultPayloads.push(...(await this.snapshotHandler()))
611
- break
612
- }
613
- default: {
614
- const result = await super.queryHandler(sanitizedQuery, sanitizedPayloads)
615
- if (this.config.storeQueries) {
616
- await this.insertWithConfig([sanitizedQuery])
617
- }
618
- return PayloadBuilder.omitPrivateStorageMeta(result) as ModuleQueryHandlerResult
619
- }
620
- }
621
- if (this.config.storeQueries) {
622
- await this.insertWithConfig([sanitizedQuery])
623
- }
624
- return PayloadBuilder.omitPrivateStorageMeta(resultPayloads) as ModuleQueryHandlerResult
625
- }
626
-
627
- protected reportPayloadCount() {
628
- this._noOverride('reportPayloadCount')
629
- const gauge = this.payloadCountGauge
630
- if (gauge) {
631
- gauge.record(this.payloadCountHandler())
632
- }
633
- }
634
-
635
- protected snapshotHandler(): PromisableArray<ArchivistSnapshotPayload<WithStorageMeta<Payload>, Hash>> {
636
- throw new Error(NOT_IMPLEMENTED)
637
- }
638
-
639
- protected override async startHandler() {
640
- if (this.config.getCache?.enabled === true) {
641
- this._getCache = new LRUCache({
642
- max: this.config.getCache?.maxEntries ?? 10_000,
643
- allowStale: true,
644
- noDisposeOnSet: false,
645
- updateAgeOnGet: true,
646
- })
647
- }
648
- const result = await super.startHandler()
649
- this.reportPayloadCount()
650
- return result
651
- }
652
-
653
- protected override async stateHandler(): Promise<Payload[]> {
654
- return [...await super.stateHandler(), await this.generateStats()]
655
- }
656
-
657
- protected async writeToParent(parent: ArchivistInstance, payloads: Payload[]): Promise<Payload[]> {
658
- return await parent.insert(PayloadBuilder.omitStorageMeta(payloads) as Payload[])
659
- }
660
-
661
- protected async writeToParents(payloads: Payload[]): Promise<Payload[]> {
662
- const parents = await this.parentArchivists()
663
- return (
664
- await Promise.all(
665
- Object.values(parents.write ?? {}).map(async (parent) => {
666
- return parent ? await this.writeToParent(parent, payloads) : undefined
667
- }),
668
- )
669
- ).filter(exists).flat()
670
- }
671
-
672
- private omitClientMetaForDataHashes<T extends Payload>(hashes: Hash[], payloads: WithStorageMeta<T>[]): WithStorageMeta<T>[] {
673
- return payloads.map((payload) => {
674
- // if retrieved by dataHash and not hash (both could have been specified)
675
- if (hashes.includes(payload._dataHash) && !hashes.includes(payload._hash)) {
676
- // scrub client meta
677
- const result = PayloadBuilder.omitClientMeta(payload) as WithStorageMeta<T>
678
- // we also scrub the _hash
679
- result._hash = result._dataHash
680
- return result
681
- } else {
682
- return payload
683
- }
684
- })
685
- }
686
-
687
- private async resolveArchivists(archivists: ModuleIdentifier[] = [], archivistInstances?: ArchivistInstance[]) {
688
- const archivistModules = (await Promise.all(archivists.map(archivist => this.resolve(archivist)))).filter(exists).filter(duplicateModules)
689
-
690
- assertEx(
691
- !this.requireAllParents || (archivistModules.length === archivists.length),
692
- () =>
693
- `Failed to find some archivists for ${this.modName} (set allRequired to false if ok)]`,
694
- )
695
-
696
- const archivistInstancesMap: Record<Address, ArchivistInstance> = {}
697
- for (let archivistInstance of archivistInstances ?? []) {
698
- archivistInstancesMap[archivistInstance.address] = archivistInstance
699
- }
700
-
701
- // eslint-disable-next-line unicorn/no-array-reduce
702
- return archivistModules.reduce<Record<Address, ArchivistInstance>>((prev, mod) => {
703
- prev[mod.address] = asArchivistInstance(mod, () => {
704
- isArchivistInstance(mod, { log: console })
705
- return `Unable to cast resolved module to an archivist: [${mod.address}, ${mod.modName}, ${mod.config.schema})}]`
706
- }, { required: true })
707
-
708
- return prev
709
- }, archivistInstancesMap)
710
- }
711
- }
@@ -1,2 +0,0 @@
1
- export const StorageClassLabel = 'network.xyo.storage.class'
2
- export type StorageClassLabelValue = 'memory' | 'disk' | 'network' | 'proxy' | 'unknown'
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './AbstractArchivist.ts'
2
- export * from './StorageClassLabel.ts'