@wishknish/knishio-client-ts 0.7.0

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 (141) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +425 -0
  3. package/dist/index.cjs +9961 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.iife.js +32241 -0
  6. package/dist/index.iife.js.map +1 -0
  7. package/dist/index.js +9853 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +113 -0
  10. package/src/AuthToken.ts +214 -0
  11. package/src/KnishIOClient.ts +2020 -0
  12. package/src/constants.ts +398 -0
  13. package/src/core/Atom.ts +646 -0
  14. package/src/core/AtomMeta.ts +278 -0
  15. package/src/core/Meta.ts +428 -0
  16. package/src/core/Molecule.ts +825 -0
  17. package/src/core/PolicyMeta.ts +130 -0
  18. package/src/core/TokenUnit.ts +148 -0
  19. package/src/core/Wallet.ts +467 -0
  20. package/src/exception/AtomIndexException.ts +97 -0
  21. package/src/exception/AtomsMissingException.ts +109 -0
  22. package/src/exception/AuthorizationRejectedException.ts +63 -0
  23. package/src/exception/BalanceInsufficientException.ts +58 -0
  24. package/src/exception/BaseException.ts +275 -0
  25. package/src/exception/BatchIdException.ts +58 -0
  26. package/src/exception/CodeException.ts +119 -0
  27. package/src/exception/DecryptionKeyException.ts +65 -0
  28. package/src/exception/InvalidResponseException.ts +112 -0
  29. package/src/exception/MetaMissingException.ts +58 -0
  30. package/src/exception/MolecularHashMismatchException.ts +115 -0
  31. package/src/exception/MolecularHashMissingException.ts +58 -0
  32. package/src/exception/NegativeAmountException.ts +58 -0
  33. package/src/exception/PolicyInvalidException.ts +58 -0
  34. package/src/exception/SignatureMalformedException.ts +58 -0
  35. package/src/exception/SignatureMismatchException.ts +98 -0
  36. package/src/exception/StackableUnitAmountException.ts +65 -0
  37. package/src/exception/StackableUnitDecimalsException.ts +65 -0
  38. package/src/exception/TransferBalanceException.ts +98 -0
  39. package/src/exception/TransferMalformedException.ts +58 -0
  40. package/src/exception/TransferMismatchedException.ts +58 -0
  41. package/src/exception/TransferRemainderException.ts +58 -0
  42. package/src/exception/TransferToSelfException.ts +58 -0
  43. package/src/exception/TransferUnbalancedException.ts +58 -0
  44. package/src/exception/UnauthenticatedException.ts +147 -0
  45. package/src/exception/WalletCredentialException.ts +108 -0
  46. package/src/exception/WalletShadowException.ts +65 -0
  47. package/src/exception/WrongTokenTypeException.ts +58 -0
  48. package/src/exception/index.ts +272 -0
  49. package/src/index.ts +512 -0
  50. package/src/instance/rules/Callback.ts +257 -0
  51. package/src/instance/rules/Condition.ts +96 -0
  52. package/src/instance/rules/Meta.ts +163 -0
  53. package/src/instance/rules/Rule.ts +29 -0
  54. package/src/instance/rules/exception/RuleArgumentException.ts +65 -0
  55. package/src/libraries/CheckMolecule.ts +581 -0
  56. package/src/libraries/Decimal.ts +94 -0
  57. package/src/libraries/Dot.ts +202 -0
  58. package/src/libraries/GraphQLClient.ts +276 -0
  59. package/src/libraries/Hex.ts +155 -0
  60. package/src/libraries/UrqlClientWrapper.ts +336 -0
  61. package/src/libraries/array.ts +91 -0
  62. package/src/libraries/crypto.ts +816 -0
  63. package/src/libraries/strings.ts +458 -0
  64. package/src/mutation/Mutation.ts +103 -0
  65. package/src/mutation/MutationActiveSession.ts +108 -0
  66. package/src/mutation/MutationClaimShadowWallet.ts +91 -0
  67. package/src/mutation/MutationCreateIdentifier.ts +86 -0
  68. package/src/mutation/MutationCreateMeta.ts +92 -0
  69. package/src/mutation/MutationCreateRule.ts +90 -0
  70. package/src/mutation/MutationCreateToken.ts +92 -0
  71. package/src/mutation/MutationCreateWallet.ts +78 -0
  72. package/src/mutation/MutationDepositBufferToken.ts +72 -0
  73. package/src/mutation/MutationLinkIdentifier.ts +87 -0
  74. package/src/mutation/MutationProposeMolecule.ts +147 -0
  75. package/src/mutation/MutationRequestAuthorization.ts +77 -0
  76. package/src/mutation/MutationRequestAuthorizationGuest.ts +85 -0
  77. package/src/mutation/MutationRequestTokens.ts +98 -0
  78. package/src/mutation/MutationTransferTokens.ts +87 -0
  79. package/src/mutation/MutationWithdrawBufferToken.ts +73 -0
  80. package/src/query/Query.ts +187 -0
  81. package/src/query/QueryActiveSession.ts +89 -0
  82. package/src/query/QueryAtom.ts +275 -0
  83. package/src/query/QueryBalance.ts +102 -0
  84. package/src/query/QueryBatch.ts +145 -0
  85. package/src/query/QueryBatchHistory.ts +86 -0
  86. package/src/query/QueryContinuId.ts +91 -0
  87. package/src/query/QueryMetaType.ts +177 -0
  88. package/src/query/QueryMetaTypeViaAtom.ts +199 -0
  89. package/src/query/QueryPolicy.ts +91 -0
  90. package/src/query/QueryToken.ts +90 -0
  91. package/src/query/QueryUserActivity.ts +153 -0
  92. package/src/query/QueryWalletBundle.ts +92 -0
  93. package/src/query/QueryWalletList.ts +106 -0
  94. package/src/response/EnhancedResponse.ts +345 -0
  95. package/src/response/Response.ts +254 -0
  96. package/src/response/ResponseActiveSession.ts +72 -0
  97. package/src/response/ResponseAtom.ts +132 -0
  98. package/src/response/ResponseAuthorizationGuest.ts +119 -0
  99. package/src/response/ResponseBalance.ts +153 -0
  100. package/src/response/ResponseClaimShadowWallet.ts +56 -0
  101. package/src/response/ResponseContinuId.ts +101 -0
  102. package/src/response/ResponseCreateIdentifier.ts +56 -0
  103. package/src/response/ResponseCreateMeta.ts +58 -0
  104. package/src/response/ResponseCreateRule.ts +56 -0
  105. package/src/response/ResponseCreateToken.ts +58 -0
  106. package/src/response/ResponseCreateWallet.ts +58 -0
  107. package/src/response/ResponseLinkIdentifier.ts +87 -0
  108. package/src/response/ResponseMetaBatch.ts +72 -0
  109. package/src/response/ResponseMetaType.ts +108 -0
  110. package/src/response/ResponseMetaTypeViaAtom.ts +108 -0
  111. package/src/response/ResponsePolicy.ts +90 -0
  112. package/src/response/ResponseProposeMolecule.ts +155 -0
  113. package/src/response/ResponseQueryActiveSession.ts +105 -0
  114. package/src/response/ResponseQueryUserActivity.ts +89 -0
  115. package/src/response/ResponseRequestAuthorization.ts +100 -0
  116. package/src/response/ResponseRequestAuthorizationGuest.ts +133 -0
  117. package/src/response/ResponseRequestTokens.ts +58 -0
  118. package/src/response/ResponseTransferTokens.ts +72 -0
  119. package/src/response/ResponseWalletBundle.ts +95 -0
  120. package/src/response/ResponseWalletList.ts +165 -0
  121. package/src/schemas/index.ts +457 -0
  122. package/src/subscribe/ActiveSessionSubscribe.ts +72 -0
  123. package/src/subscribe/ActiveWalletSubscribe.ts +99 -0
  124. package/src/subscribe/CreateMoleculeSubscribe.ts +106 -0
  125. package/src/subscribe/Subscribe.ts +182 -0
  126. package/src/subscribe/WalletStatusSubscribe.ts +70 -0
  127. package/src/subscribe/index.ts +59 -0
  128. package/src/types/assertions.ts +722 -0
  129. package/src/types/client.ts +567 -0
  130. package/src/types/crypto.ts +541 -0
  131. package/src/types/graphql.ts +630 -0
  132. package/src/types/guards.ts +659 -0
  133. package/src/types/index.ts +614 -0
  134. package/src/types/response.ts +133 -0
  135. package/src/types/template-literals.ts +382 -0
  136. package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +580 -0
  137. package/src/validation/ValidationService.ts +607 -0
  138. package/src/validation/schemas.ts +447 -0
  139. package/src/versions/HashAtom.ts +170 -0
  140. package/src/versions/Version4.ts +120 -0
  141. package/src/versions/index.ts +80 -0
@@ -0,0 +1,278 @@
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ import Meta, { type NormalizedMeta } from './Meta'
50
+
51
+ // Configuration constants matching JS SDK
52
+ const USE_META_CONTEXT = false
53
+ const DEFAULT_META_CONTEXT = 'https://www.schema.org'
54
+
55
+ /**
56
+ * AtomMeta class - Manages metadata for atoms
57
+ * Provides methods for metadata manipulation and wallet metadata
58
+ */
59
+ export default class AtomMeta {
60
+ private meta: NormalizedMeta[]
61
+
62
+ /**
63
+ * Create new AtomMeta instance
64
+ * @param meta - Initial metadata (object or array)
65
+ */
66
+ constructor(meta: Record<string, any> | NormalizedMeta[] | null = null) {
67
+ this.meta = Meta.normalizeMeta(meta)
68
+ }
69
+
70
+ /**
71
+ * Merge additional metadata
72
+ * @param meta - Metadata to merge
73
+ * @return This instance for chaining
74
+ */
75
+ merge(meta: Record<string, any> | NormalizedMeta[]): AtomMeta {
76
+ const normalized = Meta.normalizeMeta(meta)
77
+
78
+ // Use Set to avoid duplicates based on key
79
+ const existingKeys = new Set(this.meta.map(m => m.key))
80
+
81
+ for (const item of normalized) {
82
+ if (existingKeys.has(item.key)) {
83
+ // Update existing key
84
+ const index = this.meta.findIndex(m => m.key === item.key)
85
+ if (index !== -1) {
86
+ this.meta[index] = item
87
+ }
88
+ } else {
89
+ // Add new key
90
+ this.meta.push(item)
91
+ existingKeys.add(item.key)
92
+ }
93
+ }
94
+
95
+ return this
96
+ }
97
+
98
+ /**
99
+ * Add context metadata if enabled
100
+ * @param context - Context URL or null for default
101
+ * @return This instance for chaining
102
+ */
103
+ addContext(context: string | null = null): AtomMeta {
104
+ // Add context key if it is enabled
105
+ if (USE_META_CONTEXT) {
106
+ this.merge({ context: context || DEFAULT_META_CONTEXT })
107
+ }
108
+
109
+ return this
110
+ }
111
+
112
+ /**
113
+ * Set atom wallet metadata
114
+ * @param wallet - Wallet to extract metadata from
115
+ * @return This instance for chaining
116
+ */
117
+ setAtomWallet(wallet: any): AtomMeta {
118
+ const walletMeta: Record<string, any> = {
119
+ pubkey: wallet.pubkey,
120
+ characters: wallet.characters
121
+ }
122
+
123
+ // Add token units meta key if present
124
+ if (wallet.tokenUnits && wallet.tokenUnits.length) {
125
+ walletMeta.tokenUnits = JSON.stringify(wallet.getTokenUnitsData ? wallet.getTokenUnitsData() : wallet.tokenUnits)
126
+ }
127
+
128
+ // Add trade rates meta key if present
129
+ if (wallet.tradeRates && Object.keys(wallet.tradeRates).length) {
130
+ walletMeta.tradeRates = JSON.stringify(wallet.tradeRates)
131
+ }
132
+
133
+ // Merge all wallet's metas
134
+ this.merge(walletMeta)
135
+ return this
136
+ }
137
+
138
+ /**
139
+ * Set full NEW wallet metadata
140
+ * Used for shadow wallet claim & wallet creation & token creation
141
+ * @param wallet - Wallet with full metadata
142
+ * @return This instance for chaining
143
+ */
144
+ setMetaWallet(wallet: any): AtomMeta {
145
+ this.merge({
146
+ walletTokenSlug: wallet.token,
147
+ walletBundleHash: wallet.bundle,
148
+ walletAddress: wallet.address,
149
+ walletPosition: wallet.position,
150
+ walletBatchId: wallet.batchId,
151
+ walletPubkey: wallet.pubkey,
152
+ walletCharacters: wallet.characters
153
+ })
154
+ return this
155
+ }
156
+
157
+ /**
158
+ * Set shadow wallet claim flag
159
+ * @param shadowWalletClaim - Claim flag (converted to number)
160
+ * @return This instance for chaining
161
+ */
162
+ setShadowWalletClaim(shadowWalletClaim: boolean | number): AtomMeta {
163
+ this.merge({ shadowWalletClaim: Number(shadowWalletClaim) })
164
+ return this
165
+ }
166
+
167
+ /**
168
+ * Set signing wallet metadata
169
+ * @param signingWallet - Wallet used for signing
170
+ * @return This instance for chaining
171
+ */
172
+ setSigningWallet(signingWallet: any): AtomMeta {
173
+ this.merge({
174
+ signingWallet: JSON.stringify({
175
+ tokenSlug: signingWallet.token,
176
+ bundleHash: signingWallet.bundle,
177
+ address: signingWallet.address,
178
+ position: signingWallet.position,
179
+ pubkey: signingWallet.pubkey,
180
+ characters: signingWallet.characters
181
+ })
182
+ })
183
+ return this
184
+ }
185
+
186
+ /**
187
+ * Add policy metadata
188
+ * @param policy - Policy object to add
189
+ * @return This instance for chaining
190
+ */
191
+ addPolicy(policy: Record<string, any>): AtomMeta {
192
+ // For now, just stringify the policy
193
+ // TODO: Implement PolicyMeta class if needed
194
+ this.merge({
195
+ policy: JSON.stringify(policy)
196
+ })
197
+
198
+ return this
199
+ }
200
+
201
+ /**
202
+ * Get the normalized metadata array
203
+ * @return Array of normalized metadata items
204
+ */
205
+ get(): NormalizedMeta[] {
206
+ return this.meta
207
+ }
208
+
209
+ /**
210
+ * Convert metadata to plain object
211
+ * @return Aggregated metadata object
212
+ */
213
+ toObject(): Record<string, any> {
214
+ return Meta.aggregateMeta(this.meta)
215
+ }
216
+
217
+ /**
218
+ * Convert metadata to JSON string
219
+ * @return JSON string representation
220
+ */
221
+ toJSON(): string {
222
+ return JSON.stringify(this.toObject())
223
+ }
224
+
225
+ /**
226
+ * Get metadata value by key
227
+ * @param key - Key to lookup
228
+ * @param defaultValue - Default if not found
229
+ * @return Value or default
230
+ */
231
+ getValue(key: string, defaultValue: any = null): any {
232
+ return Meta.getMetaValue(this.meta, key, defaultValue)
233
+ }
234
+
235
+ /**
236
+ * Check if metadata has a key
237
+ * @param key - Key to check
238
+ * @return True if key exists
239
+ */
240
+ hasKey(key: string): boolean {
241
+ return Meta.hasKey(this.meta, key)
242
+ }
243
+
244
+ /**
245
+ * Remove metadata by key
246
+ * @param key - Key to remove
247
+ * @return This instance for chaining
248
+ */
249
+ remove(key: string): AtomMeta {
250
+ this.meta = Meta.removeMeta(this.meta, key)
251
+ return this
252
+ }
253
+
254
+ /**
255
+ * Clear all metadata
256
+ * @return This instance for chaining
257
+ */
258
+ clear(): AtomMeta {
259
+ this.meta = []
260
+ return this
261
+ }
262
+
263
+ /**
264
+ * Get count of metadata items
265
+ * @return Number of items
266
+ */
267
+ count(): number {
268
+ return this.meta.length
269
+ }
270
+
271
+ /**
272
+ * Clone this AtomMeta instance
273
+ * @return New AtomMeta with same data
274
+ */
275
+ clone(): AtomMeta {
276
+ return new AtomMeta(Meta.clone(this.meta))
277
+ }
278
+ }
@@ -0,0 +1,428 @@
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ /**
50
+ * Meta class - Handles metadata normalization and aggregation
51
+ *
52
+ * Provides static methods for processing metadata across atoms and molecules
53
+ * Ensures consistent metadata handling across the SDK
54
+ */
55
+
56
+ import type { MetaData } from '@/types'
57
+
58
+ // =============================================================================
59
+ // META TYPES AND INTERFACES
60
+ // =============================================================================
61
+
62
+ export interface NormalizedMeta {
63
+ key: string
64
+ value: string | number | boolean | null
65
+ [additionalProps: string]: unknown
66
+ }
67
+
68
+ export interface MetaItem {
69
+ key: string
70
+ value: unknown
71
+ [props: string]: unknown
72
+ }
73
+
74
+ export interface AggregatedMetaResult {
75
+ [key: string]: unknown
76
+ }
77
+
78
+ // =============================================================================
79
+ // META CLASS IMPLEMENTATION
80
+ // =============================================================================
81
+
82
+ /**
83
+ * Meta class - Static methods for metadata processing
84
+ * Matches JavaScript SDK Meta class functionality exactly
85
+ */
86
+ export default class Meta {
87
+ // No instance properties - this class only provides static methods
88
+
89
+ /**
90
+ * Normalize metadata to consistent format
91
+ * Matches JavaScript SDK Meta.normalizeMeta exactly
92
+ */
93
+ static normalizeMeta(meta: MetaData | MetaItem[] | null): NormalizedMeta[] {
94
+ if (!meta) {
95
+ return []
96
+ }
97
+
98
+ // Handle array of meta items
99
+ if (Array.isArray(meta)) {
100
+ return meta.map(item => {
101
+ if (typeof item === 'object' && item !== null && 'key' in item) {
102
+ return {
103
+ ...item,
104
+ key: String(item.key),
105
+ value: item.value as string | number | boolean | null
106
+ }
107
+ }
108
+
109
+ // Fallback for invalid items
110
+ return {
111
+ key: 'unknown',
112
+ value: String(item) as string | number | boolean | null
113
+ }
114
+ })
115
+ }
116
+
117
+ // Handle object metadata - convert to array format
118
+ if (typeof meta === 'object' && meta !== null) {
119
+ return Object.entries(meta).map(([key, value]) => ({
120
+ key,
121
+ value: value as string | number | boolean | null
122
+ }))
123
+ }
124
+
125
+ // Handle primitive values
126
+ return [{
127
+ key: 'value',
128
+ value: meta as string | number | boolean | null
129
+ }]
130
+ }
131
+
132
+ /**
133
+ * Aggregate metadata into single object
134
+ * Matches JavaScript SDK Meta.aggregateMeta exactly
135
+ */
136
+ static aggregateMeta(metaItems: NormalizedMeta[] | MetaItem[] | null): AggregatedMetaResult {
137
+ if (!metaItems || !Array.isArray(metaItems)) {
138
+ return {}
139
+ }
140
+
141
+ const aggregated: AggregatedMetaResult = {}
142
+
143
+ for (const item of metaItems) {
144
+ if (item && typeof item === 'object' && 'key' in item) {
145
+ const key = String(item.key)
146
+
147
+ // Handle different value types appropriately
148
+ if ('value' in item) {
149
+ aggregated[key] = item.value
150
+ } else {
151
+ // If no explicit value property, use the entire item minus the key
152
+ const { key: itemKey, ...restOfItem } = item as any
153
+ aggregated[key] = Object.keys(restOfItem).length === 1 && 'value' in restOfItem
154
+ ? restOfItem.value
155
+ : restOfItem
156
+ }
157
+ }
158
+ }
159
+
160
+ return aggregated
161
+ }
162
+
163
+ /**
164
+ * Merge multiple metadata arrays
165
+ * Combines arrays while preserving order and handling duplicates
166
+ */
167
+ static mergeMeta(...metaArrays: (NormalizedMeta[] | MetaItem[] | null)[]): NormalizedMeta[] {
168
+ const merged: NormalizedMeta[] = []
169
+ const keysSeen = new Set<string>()
170
+
171
+ for (const metaArray of metaArrays) {
172
+ if (!metaArray || !Array.isArray(metaArray)) {
173
+ continue
174
+ }
175
+
176
+ const normalized = this.normalizeMeta(metaArray)
177
+
178
+ for (const item of normalized) {
179
+ // Handle duplicate keys by appending index
180
+ let finalKey = item.key
181
+ let counter = 1
182
+
183
+ while (keysSeen.has(finalKey)) {
184
+ finalKey = `${item.key}_${counter}`
185
+ counter++
186
+ }
187
+
188
+ keysSeen.add(finalKey)
189
+ merged.push({
190
+ ...item,
191
+ key: finalKey
192
+ })
193
+ }
194
+ }
195
+
196
+ return merged
197
+ }
198
+
199
+ /**
200
+ * Filter metadata by key pattern or predicate
201
+ */
202
+ static filterMeta(
203
+ metaItems: NormalizedMeta[] | MetaItem[] | null,
204
+ filter: string | RegExp | ((item: NormalizedMeta) => boolean)
205
+ ): NormalizedMeta[] {
206
+ if (!metaItems || !Array.isArray(metaItems)) {
207
+ return []
208
+ }
209
+
210
+ const normalized = this.normalizeMeta(metaItems)
211
+
212
+ if (typeof filter === 'string') {
213
+ return normalized.filter(item => item.key.includes(filter))
214
+ }
215
+
216
+ if (filter instanceof RegExp) {
217
+ return normalized.filter(item => filter.test(item.key))
218
+ }
219
+
220
+ if (typeof filter === 'function') {
221
+ return normalized.filter(filter)
222
+ }
223
+
224
+ return normalized
225
+ }
226
+
227
+ /**
228
+ * Find metadata item by key
229
+ */
230
+ static findMeta(
231
+ metaItems: NormalizedMeta[] | MetaItem[] | null,
232
+ key: string
233
+ ): NormalizedMeta | null {
234
+ if (!metaItems || !Array.isArray(metaItems)) {
235
+ return null
236
+ }
237
+
238
+ const normalized = this.normalizeMeta(metaItems)
239
+ return normalized.find(item => item.key === key) || null
240
+ }
241
+
242
+ /**
243
+ * Get metadata value by key
244
+ */
245
+ static getMetaValue(
246
+ metaItems: NormalizedMeta[] | MetaItem[] | null,
247
+ key: string,
248
+ defaultValue: unknown = null
249
+ ): unknown {
250
+ const item = this.findMeta(metaItems, key)
251
+ return item ? item.value : defaultValue
252
+ }
253
+
254
+ /**
255
+ * Set or update metadata item by key
256
+ */
257
+ static setMeta(
258
+ metaItems: NormalizedMeta[] | MetaItem[] | null,
259
+ key: string,
260
+ value: unknown
261
+ ): NormalizedMeta[] {
262
+ const normalized = this.normalizeMeta(metaItems)
263
+ const existingIndex = normalized.findIndex(item => item.key === key)
264
+
265
+ const newItem: NormalizedMeta = {
266
+ key,
267
+ value: value as string | number | boolean | null
268
+ }
269
+
270
+ if (existingIndex >= 0) {
271
+ // Update existing item
272
+ normalized[existingIndex] = newItem
273
+ } else {
274
+ // Add new item
275
+ normalized.push(newItem)
276
+ }
277
+
278
+ return normalized
279
+ }
280
+
281
+ /**
282
+ * Remove metadata item by key
283
+ */
284
+ static removeMeta(
285
+ metaItems: NormalizedMeta[] | MetaItem[] | null,
286
+ key: string
287
+ ): NormalizedMeta[] {
288
+ if (!metaItems || !Array.isArray(metaItems)) {
289
+ return []
290
+ }
291
+
292
+ const normalized = this.normalizeMeta(metaItems)
293
+ return normalized.filter(item => item.key !== key)
294
+ }
295
+
296
+ /**
297
+ * Convert metadata to JSON-serializable format
298
+ */
299
+ static toJSON(metaItems: NormalizedMeta[] | MetaItem[] | null): Record<string, unknown> {
300
+ return this.aggregateMeta(this.normalizeMeta(metaItems))
301
+ }
302
+
303
+ /**
304
+ * Create metadata from JSON object
305
+ */
306
+ static fromJSON(jsonData: Record<string, unknown>): NormalizedMeta[] {
307
+ if (!jsonData || typeof jsonData !== 'object') {
308
+ return []
309
+ }
310
+
311
+ return Object.entries(jsonData).map(([key, value]) => ({
312
+ key,
313
+ value: value as string | number | boolean | null
314
+ }))
315
+ }
316
+
317
+ /**
318
+ * Validate metadata structure
319
+ */
320
+ static validateMeta(metaItems: unknown): metaItems is NormalizedMeta[] {
321
+ if (!Array.isArray(metaItems)) {
322
+ return false
323
+ }
324
+
325
+ return metaItems.every(item =>
326
+ item &&
327
+ typeof item === 'object' &&
328
+ 'key' in item &&
329
+ typeof item.key === 'string'
330
+ )
331
+ }
332
+
333
+ /**
334
+ * Get all unique keys from metadata
335
+ */
336
+ static getKeys(metaItems: NormalizedMeta[] | MetaItem[] | null): string[] {
337
+ if (!metaItems || !Array.isArray(metaItems)) {
338
+ return []
339
+ }
340
+
341
+ const normalized = this.normalizeMeta(metaItems)
342
+ const keys = new Set<string>()
343
+
344
+ for (const item of normalized) {
345
+ keys.add(item.key)
346
+ }
347
+
348
+ return Array.from(keys)
349
+ }
350
+
351
+ /**
352
+ * Check if metadata contains specific key
353
+ */
354
+ static hasKey(metaItems: NormalizedMeta[] | MetaItem[] | null, key: string): boolean {
355
+ return this.getKeys(metaItems).includes(key)
356
+ }
357
+
358
+ /**
359
+ * Get metadata statistics
360
+ */
361
+ static getStats(metaItems: NormalizedMeta[] | MetaItem[] | null): {
362
+ totalItems: number
363
+ uniqueKeys: number
364
+ keys: string[]
365
+ valueTypes: Record<string, number>
366
+ } {
367
+ if (!metaItems || !Array.isArray(metaItems)) {
368
+ return {
369
+ totalItems: 0,
370
+ uniqueKeys: 0,
371
+ keys: [],
372
+ valueTypes: {}
373
+ }
374
+ }
375
+
376
+ const normalized = this.normalizeMeta(metaItems)
377
+ const keys = this.getKeys(normalized)
378
+ const valueTypes: Record<string, number> = {}
379
+
380
+ for (const item of normalized) {
381
+ const type = item.value === null ? 'null' : typeof item.value
382
+ valueTypes[type] = (valueTypes[type] || 0) + 1
383
+ }
384
+
385
+ return {
386
+ totalItems: normalized.length,
387
+ uniqueKeys: keys.length,
388
+ keys,
389
+ valueTypes
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Deep clone metadata array
395
+ */
396
+ static clone(metaItems: NormalizedMeta[] | MetaItem[] | null): NormalizedMeta[] {
397
+ if (!metaItems || !Array.isArray(metaItems)) {
398
+ return []
399
+ }
400
+
401
+ const normalized = this.normalizeMeta(metaItems)
402
+ return normalized.map(item => ({ ...item }))
403
+ }
404
+
405
+ /**
406
+ * Sort metadata by key
407
+ */
408
+ static sort(
409
+ metaItems: NormalizedMeta[] | MetaItem[] | null,
410
+ descending = false
411
+ ): NormalizedMeta[] {
412
+ if (!metaItems || !Array.isArray(metaItems)) {
413
+ return []
414
+ }
415
+
416
+ const normalized = this.normalizeMeta(metaItems)
417
+ return normalized.sort((a, b) => {
418
+ const comparison = a.key.localeCompare(b.key)
419
+ return descending ? -comparison : comparison
420
+ })
421
+ }
422
+ }
423
+
424
+ // =============================================================================
425
+ // TYPE EXPORTS
426
+ // =============================================================================
427
+
428
+ // Types are already exported at the interface declarations