@xyo-network/payload-builder 2.88.3 → 2.89.0-rc.2

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/src/Builder.ts CHANGED
@@ -1,26 +1,24 @@
1
1
  import { assertEx } from '@xylabs/assert'
2
- import { AnyObject } from '@xylabs/object'
3
- import { deepOmitUnderscoreFields, PayloadHasher, removeEmptyFields } from '@xyo-network/hash'
4
- import { Payload } from '@xyo-network/payload-model'
2
+ import { Hash } from '@xylabs/hex'
3
+ import { JsonObject } from '@xylabs/object'
4
+ import { deepOmitPrefixedFields, PayloadHasher, removeEmptyFields } from '@xyo-network/hash'
5
+ import { Payload, PayloadWithMeta, WithMeta } from '@xyo-network/payload-model'
5
6
 
6
- export interface PayloadBuilderOptions {
7
+ export interface PayloadBuilderOptions<T> {
8
+ fields?: Partial<T>
9
+ meta?: JsonObject
7
10
  schema: string
8
11
  }
9
12
 
10
- export class PayloadBuilder<T extends Payload = Payload<AnyObject>> {
11
- private _$meta?: AnyObject
12
- private _client = 'js'
13
+ export class PayloadBuilder<T extends Payload = Payload> {
14
+ private _$meta?: JsonObject
13
15
  private _fields: Partial<T> = {}
14
16
  private _schema: string
15
- private _timestamp = Date.now()
16
17
 
17
- constructor({ schema }: PayloadBuilderOptions) {
18
+ constructor({ schema, fields, meta }: PayloadBuilderOptions<T>) {
18
19
  this._schema = schema
19
- }
20
-
21
- get externalMeta() {
22
- const _hash = PayloadHasher.hashAsync(this.hashableFields)
23
- return { _client: this._client, _hash, _timestamp: this._timestamp }
20
+ this._fields = fields ?? {}
21
+ this._$meta = meta
24
22
  }
25
23
 
26
24
  get schema() {
@@ -28,31 +26,125 @@ export class PayloadBuilder<T extends Payload = Payload<AnyObject>> {
28
26
  return this._schema
29
27
  }
30
28
 
31
- $meta(fields?: AnyObject) {
32
- this._$meta = fields
33
- return this
29
+ static async build<T extends Payload>(payload: T) {
30
+ const builder = new PayloadBuilder<T>({ schema: payload.schema })
31
+ builder.fields(payload)
32
+ return await builder.build()
33
+ }
34
+
35
+ static async dataHash<T extends Payload>(payload: T): Promise<Hash> {
36
+ return (await this.build(payload)).$hash
37
+ }
38
+
39
+ static async dataHashPairs<T extends Payload>(payloads: T[]): Promise<[WithMeta<T>, Hash][]> {
40
+ return await Promise.all(
41
+ payloads.map(async (payload) => {
42
+ const built = await PayloadBuilder.build(payload)
43
+ return [built, built.$hash]
44
+ }),
45
+ )
46
+ }
47
+
48
+ static async dataHashes<T extends Payload>(payloads?: T[]): Promise<undefined | Hash[]>
49
+ static async dataHashes<T extends Payload>(payloads: T[]): Promise<Hash[]>
50
+ static async dataHashes<T extends Payload>(payloads?: T[]): Promise<Hash[] | undefined> {
51
+ return payloads
52
+ ? await Promise.all(
53
+ payloads.map(async (payload) => {
54
+ const built = await PayloadBuilder.build(payload)
55
+ return built.$hash
56
+ }),
57
+ )
58
+ : undefined
59
+ }
60
+
61
+ static async filterExclude<T extends Payload>(payloads: T[] = [], hash: Hash[] | Hash): Promise<T[]> {
62
+ const hashes = Array.isArray(hash) ? hash : [hash]
63
+ return (await this.hashPairs(payloads)).filter(([_, objHash]) => !hashes.includes(objHash))?.map((pair) => pair[0])
64
+ }
65
+
66
+ static async filterInclude<T extends Payload>(payloads: T[] = [], hash: Hash[] | Hash): Promise<T[]> {
67
+ const hashes = Array.isArray(hash) ? hash : [hash]
68
+ return (await this.hashPairs(payloads)).filter(([_, objHash]) => hashes.includes(objHash))?.map((pair) => pair[0])
69
+ }
70
+
71
+ static async find<T extends Payload>(payloads: T[] = [], hash: Hash): Promise<T | undefined> {
72
+ return (await this.hashPairs(payloads)).find(([_, objHash]) => objHash === hash)?.[0]
73
+ }
74
+
75
+ static async hash<T extends Payload>(payload: T): Promise<Hash> {
76
+ return await PayloadHasher.hash(payload)
77
+ }
78
+
79
+ /**
80
+ * Creates an array of payload/hash tuples based on the payloads passed in
81
+ * @param objs Any array of payloads
82
+ * @returns An array of payload/hash tuples
83
+ */
84
+ static async hashPairs<T extends Payload>(payloads: T[]): Promise<[WithMeta<T>, Hash][]> {
85
+ return await Promise.all(
86
+ payloads.map<Promise<[WithMeta<T>, Hash]>>(async (payload) => {
87
+ const built = await PayloadBuilder.build(payload)
88
+ return [built, await PayloadBuilder.hash(built)]
89
+ }),
90
+ )
34
91
  }
35
92
 
36
- async build(withExternalMeta = false) {
37
- let hashableFields = this.hashableFields()
38
- if (this._$meta) {
39
- const $hash = await PayloadHasher.hashAsync(hashableFields)
40
- hashableFields = { ...hashableFields, $hash, $meta: this._$meta }
93
+ static async hashes<T extends Payload>(payloads?: T[]): Promise<undefined | Hash[]>
94
+ static async hashes<T extends Payload>(payloads: T[]): Promise<Hash[]>
95
+ static async hashes<T extends Payload>(payloads?: T[]): Promise<Hash[] | undefined> {
96
+ return await PayloadHasher.hashes(payloads)
97
+ }
98
+
99
+ static async toDataHashMap<T extends Payload>(objs: T[]): Promise<Record<Hash, T>> {
100
+ const result: Record<Hash, T> = {}
101
+ for (const pair of await this.dataHashPairs(objs)) {
102
+ result[pair[1]] = pair[0]
41
103
  }
42
- return withExternalMeta ? { ...hashableFields, ...this.externalMeta } : hashableFields
104
+ return result
43
105
  }
44
106
 
45
- fields(fields?: Partial<T>) {
46
- if (fields) {
47
- this._fields = { ...this._fields, ...removeEmptyFields(fields) }
107
+ /**
108
+ * Creates an object map of payload hashes to payloads based on the payloads passed in
109
+ * @param objs Any array of payloads
110
+ * @returns A map of hashes to payloads
111
+ */
112
+ static async toHashMap<T extends Payload>(objs: T[]): Promise<Record<Hash, T>> {
113
+ const result: Record<Hash, T> = {}
114
+ for (const pair of await this.hashPairs(objs)) {
115
+ result[pair[1]] = pair[0]
48
116
  }
117
+ return result
118
+ }
119
+
120
+ $meta(fields?: JsonObject) {
121
+ this._$meta = fields
49
122
  return this
50
123
  }
51
124
 
52
- hashableFields() {
125
+ async build(): Promise<WithMeta<T>> {
126
+ const dataHashableFields = this.dataHashableFields()
127
+ const $hash = await PayloadBuilder.hash(dataHashableFields)
128
+ const hashableFields: PayloadWithMeta = { ...dataHashableFields, $hash }
129
+
130
+ //only add $meta if it exists and has at least one field
131
+ if (this._$meta && Object.keys(this._$meta).length > 0) {
132
+ hashableFields['$meta'] = this._$meta
133
+ }
134
+ return hashableFields as WithMeta<T>
135
+ }
136
+
137
+ dataHashableFields() {
53
138
  return {
54
- ...removeEmptyFields(deepOmitUnderscoreFields(this._fields)),
139
+ ...removeEmptyFields(deepOmitPrefixedFields(deepOmitPrefixedFields(this._fields, '$'), '_')),
55
140
  schema: assertEx(this.schema, 'Payload: Missing Schema'),
56
141
  } as T
57
142
  }
143
+
144
+ fields(fields?: Partial<T>) {
145
+ if (fields) {
146
+ this._fields = { ...this._fields, ...removeEmptyFields(fields) }
147
+ }
148
+ return this
149
+ }
58
150
  }