@xyo-network/xl1-protocol-sdk 1.7.8 → 1.7.9

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.
@@ -0,0 +1,70 @@
1
+ import { stepSize } from '@xyo-network/xl1-protocol'
2
+ import {
3
+ describe, expect, it,
4
+ } from 'vitest'
5
+
6
+ import { calculateFramesFromRange } from '../calculateFramesFromRange.ts'
7
+
8
+ describe('calculateFramesFromRange', () => {
9
+ it('handles range aligned with step size', () => {
10
+ const step = 1 // 105
11
+ const range: [number, number] = [0, stepSize(step) - 1]
12
+ const [fitted, remaining] = calculateFramesFromRange(range, step)
13
+
14
+ expect(fitted).toEqual([[0, 30]])
15
+ expect(remaining).toEqual([])
16
+ })
17
+
18
+ it('handles range starting at step boundary but incomplete', () => {
19
+ const step = 1
20
+ const range: [number, number] = [stepSize(step), stepSize(step) + 1]
21
+ const [fitted, remaining] = calculateFramesFromRange(range, step)
22
+
23
+ expect(fitted).toEqual([])
24
+ expect(remaining).toEqual([range])
25
+ })
26
+
27
+ it('handles range starting at step boundary (step 1)', () => {
28
+ const step = 1
29
+ const multiplier = 2
30
+ const additional = 1
31
+ const range: [number, number] = [stepSize(step), (stepSize(step) + additional) * multiplier]
32
+ const [fitted, remaining] = calculateFramesFromRange(range, step)
33
+
34
+ expect(fitted).toEqual([[31, 61]])
35
+ expect(remaining).toEqual([[62, 64]])
36
+ })
37
+
38
+ it('handles range starting at step boundary (step 5)', () => {
39
+ const step = 5
40
+ const multiplier = 2
41
+ const additional = 17
42
+ const range: [number, number] = [stepSize(step), (stepSize(step) + additional) * multiplier]
43
+ const [fitted, remaining] = calculateFramesFromRange(range, step)
44
+
45
+ expect(fitted).toEqual([[510_511, 1_021_021]])
46
+ expect(remaining).toEqual([[1_021_022, 1_021_056]])
47
+ })
48
+
49
+ it('handles range starting not at step boundary', () => {
50
+ const step = 1
51
+ const multiplier = 2
52
+ const additional = 1
53
+ const range: [number, number] = [stepSize(step) - additional, (stepSize(step) + additional) * multiplier]
54
+ const [fitted, remaining] = calculateFramesFromRange(range, step)
55
+
56
+ expect(fitted).toEqual([[31, 61]])
57
+ expect(remaining).toEqual([[range[0], 30], [62, range[1]]])
58
+ })
59
+
60
+ it('handles range starting not at step boundary (step 5)', () => {
61
+ const step = 5
62
+ const multiplier = 2
63
+ const additional = 17
64
+ const range: [number, number] = [stepSize(step) - additional, (stepSize(step) + additional) * multiplier]
65
+ const [fitted, remaining] = calculateFramesFromRange(range, step)
66
+
67
+ expect(fitted).toEqual([[510_511, 1_021_021]])
68
+ expect(remaining).toEqual([[range[0], 510_510], [1_021_022, range[1]]])
69
+ })
70
+ })
@@ -0,0 +1,75 @@
1
+ import {
2
+ describe, expect, it,
3
+ } from 'vitest'
4
+
5
+ import type { BlockNumberRange } from '../BlockNumberRange.ts'
6
+ import { deepCalculateFramesFromRange } from '../deepCalculateFramesFromRange.ts'
7
+
8
+ describe('deepCalculateFramesFromRange', () => {
9
+ it('calculates frames for a simple range with one step level', () => {
10
+ const range: BlockNumberRange = [0, 9]
11
+ const result = deepCalculateFramesFromRange(range)
12
+
13
+ // With step 0 (size 10), this should fit in one frame
14
+ expect(result).toEqual([[0, 6], [7, 7], [8, 8], [9, 9]])
15
+ })
16
+
17
+ it('calculates frames for a range spanning multiple step sizes', () => {
18
+ const range: BlockNumberRange = [0, 250]
19
+ const result = deepCalculateFramesFromRange(range)
20
+
21
+ expect(result).toContainEqual([0, 210])
22
+
23
+ expect(result).toMatchSnapshot(result)
24
+ expect(result.length).toEqual(11)
25
+ })
26
+
27
+ it('calculates frames for range with multiple step levels - [0, 1500]', () => {
28
+ const range: BlockNumberRange = [0, 1500]
29
+ const result = deepCalculateFramesFromRange(range)
30
+ expect(result).toMatchSnapshot(result)
31
+ expect(result.length).toEqual(19)
32
+ })
33
+
34
+ it('calculates frames for range with multiple step levels - [0, 3000]', () => {
35
+ const range: BlockNumberRange = [0, 3000]
36
+ const result = deepCalculateFramesFromRange(range)
37
+ expect(result).toMatchSnapshot(result)
38
+ expect(result.length).toEqual(31)
39
+ })
40
+
41
+ it('calculates frames for range with multiple step levels = [45, 1599]', () => {
42
+ const range: BlockNumberRange = [45, 1599]
43
+ const result = deepCalculateFramesFromRange(range)
44
+ expect(result).toMatchSnapshot(result)
45
+ expect(result.length).toEqual(43)
46
+ })
47
+
48
+ it('calculates frames for range with multiple step levels = [45, 3333]', () => {
49
+ const range: BlockNumberRange = [45, 3333]
50
+ const result = deepCalculateFramesFromRange(range)
51
+ expect(result).toMatchSnapshot(result)
52
+ expect(result.length).toEqual(85)
53
+ })
54
+
55
+ it('calculates frames for range with multiple step levels - [23_145, 230_102_040]', () => {
56
+ const range: BlockNumberRange = [23_145, 230_102_040]
57
+ const result = deepCalculateFramesFromRange(range)
58
+ expect(result).toMatchSnapshot(result)
59
+ expect(result.length).toEqual(742)
60
+ })
61
+
62
+ it('calculates frames for range with multiple step levels - [23_145, 34_548_554_394]', () => {
63
+ const range: BlockNumberRange = [23_145, 34_548_554_394]
64
+ const result = deepCalculateFramesFromRange(range)
65
+ expect(result).toMatchSnapshot(result)
66
+ expect(result.length).toEqual(1468)
67
+ })
68
+
69
+ it('calculates frames for range with multiple step levels - [23_145, 4_598_734_539_845]', () => {
70
+ const range: BlockNumberRange = [23_145, 4_598_734_539_845]
71
+ const result = deepCalculateFramesFromRange(range)
72
+ expect(result).toMatchSnapshot(result)
73
+ expect(result.length).toEqual(2445)
74
+ })
75
+ })
@@ -1 +1,2 @@
1
1
  export * from './Config.ts'
2
+ export { hasMongoConfig } from './storage/index.ts'
@@ -1,12 +1,31 @@
1
+ import { isDefined, isUndefined } from '@xylabs/typeof'
1
2
  import z from 'zod'
2
3
 
4
+ /**
5
+ * Checks if the provided MongoDB configuration contains all necessary fields
6
+ * for establishing a connection.
7
+ * @param config MongoDB configuration object
8
+ * @returns True if the configuration contains all necessary fields for
9
+ * establishing a connection
10
+ */
11
+ export const hasMongoConfig = (config?: MongoConfig): config is Required<MongoConfig> => {
12
+ if (isUndefined(config)) return false
13
+ return (
14
+ isDefined(config.connectionString)
15
+ && isDefined(config.database)
16
+ && isDefined(config.domain)
17
+ && isDefined(config.password)
18
+ && isDefined(config.username)
19
+ )
20
+ }
21
+
3
22
  export const MongoConfigSchema = z.object({
4
23
  // TODO: Create from other arguments
5
- connectionString: z.string().min(1).max(1024).optional().describe('MongoDB connection string'),
6
- database: z.string().min(1).max(128).optional().describe('MongoDB database name'),
7
- domain: z.string().min(1).max(128).optional().describe('MongoDB domain'),
8
- password: z.string().min(1).max(128).optional().describe('MongoDB password'),
9
- username: z.string().min(1).max(128).optional().describe('MongoDB username'),
24
+ connectionString: z.string().min(1).optional().describe('MongoDB connection string'),
25
+ database: z.string().min(1).optional().describe('MongoDB database name'),
26
+ domain: z.string().min(1).optional().describe('MongoDB domain'),
27
+ password: z.string().min(1).optional().describe('MongoDB password'),
28
+ username: z.string().min(1).optional().describe('MongoDB username'),
10
29
  })
11
30
 
12
31
  export type MongoConfig = z.infer<typeof MongoConfigSchema>
@@ -1 +1,2 @@
1
+ export * from './driver/index.ts'
1
2
  export * from './Storage.ts'
@@ -0,0 +1,242 @@
1
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
2
+ import type { AnyPayload } from '@xyo-network/payload-model'
3
+ import type { HydratedTransaction, TransactionBoundWitness } from '@xyo-network/xl1-protocol'
4
+ import {
5
+ describe, expect, it,
6
+ } from 'vitest'
7
+
8
+ import { transactionBlockByteCount } from '../transactionBlockByteCount.ts'
9
+
10
+ const hydratedTransaction: HydratedTransaction<TransactionBoundWitness, AnyPayload> = [
11
+ {
12
+ schema: 'network.xyo.boundwitness',
13
+ addresses: [
14
+ '136c12e131f5e3eef708dbde954a2841dd72f9af',
15
+ ],
16
+ payload_hashes: [
17
+ '6d12a4aae2d7056b5b9b1a5e078c883e7ebf887a86d16a4e5e87b5940658873d',
18
+ '1f9b639358aefa6f3c2467a7d571cde3b68b8fd5d9c7af6a14d0a4023832286f',
19
+ ],
20
+ payload_schemas: [
21
+ 'network.xyo.hash',
22
+ 'network.xyo.crypto.asset',
23
+ ],
24
+ previous_hashes: [
25
+ null,
26
+ ],
27
+ $signatures: [
28
+ 'c6c4101d2854046974cebd14a941e61ff969f7cf523f677bec2907d262ad98235e00bf829be6344241b5f25cb313c3cadeb0b0abda618b8cfee359859d4246f4',
29
+ ],
30
+ nbf: 25_383,
31
+ exp: 26_383,
32
+ fees: {
33
+ base: '038d7ea4c68000',
34
+ gasLimit: '0de0b6b3a7640000',
35
+ gasPrice: '09184e72a000',
36
+ priority: '00',
37
+ },
38
+ chain: 'a82920051db4fcbb804463440dd45e03f72442fd',
39
+ from: '136c12e131f5e3eef708dbde954a2841dd72f9af',
40
+ script: [
41
+ 'elevate|6d12a4aae2d7056b5b9b1a5e078c883e7ebf887a86d16a4e5e87b5940658873d',
42
+ ],
43
+ _dataHash: '9c6c0ebf16761fc219d8235b398fa058db05f3b139a821f812683c5434e4d08b',
44
+ _hash: 'bd5909fa90603cba7c794ce8358140467a2aeee1845feaabcac3ad1d3e0dd48a',
45
+ _sequence: '0000000000000000000000003e0dd48a',
46
+ },
47
+ [
48
+ {
49
+ schema: 'network.xyo.hash',
50
+ hash: '1f9b639358aefa6f3c2467a7d571cde3b68b8fd5d9c7af6a14d0a4023832286f',
51
+ _dataHash: '6d12a4aae2d7056b5b9b1a5e078c883e7ebf887a86d16a4e5e87b5940658873d',
52
+ _hash: '6d12a4aae2d7056b5b9b1a5e078c883e7ebf887a86d16a4e5e87b5940658873d',
53
+ _sequence: '000001975b401332000000030658873d',
54
+ },
55
+ ],
56
+ ]
57
+
58
+ describe('transactionBlockByteCount', () => {
59
+ it('calculates correct byte count for a transaction with no payloads', () => {
60
+ // Expected byte count - transaction only (no payloads)
61
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(hydratedTransaction[0])
62
+ const expectedCleanPayloads = PayloadBuilder.omitStorageMeta(hydratedTransaction[1])
63
+ const expectedByteCount = JSON.stringify(expectedCleanTransaction).length
64
+ + expectedCleanPayloads.reduce((acc, payload) => acc + JSON.stringify(payload).length, 0)
65
+
66
+ // Act
67
+ const actualByteCount = transactionBlockByteCount(hydratedTransaction)
68
+
69
+ // Assert
70
+ expect(actualByteCount).toBe(expectedByteCount)
71
+ })
72
+
73
+ it('calculates correct byte count for a transaction with multiple payloads', () => {
74
+ // Expected byte count - transaction + payloads (without $meta)
75
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(hydratedTransaction[0])
76
+ const expectedCleanPayloads = PayloadBuilder.omitStorageMeta(hydratedTransaction[1])
77
+ const expectedByteCount
78
+ = JSON.stringify(expectedCleanTransaction).length
79
+ + expectedCleanPayloads.reduce((acc, payload) => acc + JSON.stringify(payload).length, 0)
80
+
81
+ // Act
82
+ const actualByteCount = transactionBlockByteCount(hydratedTransaction)
83
+
84
+ // Assert
85
+ expect(actualByteCount).toBe(expectedByteCount)
86
+ })
87
+
88
+ it('handles transaction with empty payload array', () => {
89
+ // Arrange
90
+ const emptyPayloadsTransaction: HydratedTransaction = [
91
+ { ...hydratedTransaction[0] },
92
+ [], // Empty payload array
93
+ ]
94
+
95
+ // Expected byte count - transaction only with no payloads
96
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(emptyPayloadsTransaction[0])
97
+ const expectedByteCount = JSON.stringify(expectedCleanTransaction).length
98
+
99
+ // Act
100
+ const actualByteCount = transactionBlockByteCount(emptyPayloadsTransaction)
101
+
102
+ // Assert
103
+ expect(actualByteCount).toBe(expectedByteCount)
104
+ })
105
+
106
+ it('correctly handles payloads with complex nested structures', async () => {
107
+ // Arrange
108
+ const complexPayload = await PayloadBuilder.addStorageMeta({
109
+ schema: 'network.xyo.complex',
110
+ data: {
111
+ nested: {
112
+ array: [1, 2, 3, 4, 5],
113
+ objects: [
114
+ { id: 1, name: 'item1' },
115
+ { id: 2, name: 'item2' },
116
+ ],
117
+ deepNesting: { level1: { level2: { level3: 'deep value' } } },
118
+ },
119
+ },
120
+ })
121
+
122
+ const complexTransaction: HydratedTransaction = [
123
+ { ...hydratedTransaction[0] },
124
+ [complexPayload],
125
+ ]
126
+
127
+ // Expected byte count
128
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(complexTransaction[0])
129
+ const expectedCleanPayloads = PayloadBuilder.omitStorageMeta([complexPayload])
130
+ const expectedByteCount
131
+ = JSON.stringify(expectedCleanTransaction).length
132
+ + JSON.stringify(expectedCleanPayloads[0]).length
133
+
134
+ // Act
135
+ const actualByteCount = transactionBlockByteCount(complexTransaction)
136
+
137
+ // Assert
138
+ expect(actualByteCount).toBe(expectedByteCount)
139
+ })
140
+
141
+ it('returns correct byte count for transaction with multiple large payloads', async () => {
142
+ // Arrange
143
+ const largePayloads = await PayloadBuilder.addStorageMeta(Array.from({ length: 5 }, (_, i) => ({
144
+ schema: `network.xyo.test.${i}`,
145
+ data: Array.from({ length: 100 }, (_, j) => `data-item-${j}`),
146
+ metadata: {
147
+ timestamp: Date.now(),
148
+ source: `test-source-${i}`,
149
+ tags: Array.from({ length: 10 }, (_, k) => `tag-${k}`),
150
+ },
151
+ _dataHash: `datahash${i}`,
152
+ _hash: `hash${i}`,
153
+ })))
154
+
155
+ const largeTransaction: HydratedTransaction = [
156
+ { ...hydratedTransaction[0] },
157
+ largePayloads,
158
+ ]
159
+
160
+ // Expected byte count
161
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(largeTransaction[0])
162
+ const expectedCleanPayloads = PayloadBuilder.omitStorageMeta(largePayloads)
163
+
164
+ let expectedByteCount = JSON.stringify(expectedCleanTransaction).length
165
+ for (const payload of expectedCleanPayloads) {
166
+ expectedByteCount += JSON.stringify(payload).length
167
+ }
168
+
169
+ // Act
170
+ const actualByteCount = transactionBlockByteCount(largeTransaction)
171
+
172
+ // Assert
173
+ expect(actualByteCount).toBe(expectedByteCount)
174
+ })
175
+
176
+ it('handles transactions with special characters in payloads', async () => {
177
+ // Arrange
178
+ const specialCharPayload = await PayloadBuilder.addStorageMeta({
179
+ schema: 'network.xyo.special',
180
+ text: 'Special characters: 你好, ñáéíóú, 🚀 💻 🔗, \u0000\u0001\u0002',
181
+ escapes: '\\n\\t\\r\\"\\\'\\\\',
182
+ _hash: 'special-hash',
183
+ })
184
+
185
+ const specialTransaction: HydratedTransaction = [
186
+ { ...hydratedTransaction[0] },
187
+ [specialCharPayload],
188
+ ]
189
+
190
+ // Expected byte count
191
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(specialTransaction[0])
192
+ const expectedCleanPayload = PayloadBuilder.omitStorageMeta([specialCharPayload])
193
+ const expectedByteCount
194
+ = JSON.stringify(expectedCleanTransaction).length
195
+ + JSON.stringify(expectedCleanPayload[0]).length
196
+
197
+ // Act
198
+ const actualByteCount = transactionBlockByteCount(specialTransaction)
199
+
200
+ // Assert
201
+ expect(actualByteCount).toBe(expectedByteCount)
202
+ })
203
+
204
+ it('correctly omits all storage metadata fields from byte count', async () => {
205
+ // Arrange
206
+ const txWithExtraMeta = await PayloadBuilder.addStorageMeta({
207
+ ...hydratedTransaction[0],
208
+ _extraMeta1: 'should be omitted',
209
+ _extraMeta2: 'should be omitted as well',
210
+ })
211
+
212
+ const payloadWithExtraMeta = await PayloadBuilder.addStorageMeta({
213
+ schema: 'network.xyo.test',
214
+ data: 'test data',
215
+ _extraMeta1: 'should be omitted',
216
+ _extraMeta2: 'should be omitted as well',
217
+ })
218
+
219
+ const transactionWithExtraMeta: HydratedTransaction = [
220
+ txWithExtraMeta,
221
+ [payloadWithExtraMeta],
222
+ ]
223
+
224
+ // Expected byte count - all meta fields should be omitted
225
+ const expectedCleanTransaction = PayloadBuilder.omitStorageMeta(txWithExtraMeta)
226
+ const expectedCleanPayload = PayloadBuilder.omitStorageMeta([payloadWithExtraMeta])
227
+ const expectedByteCount
228
+ = JSON.stringify(expectedCleanTransaction).length
229
+ + JSON.stringify(expectedCleanPayload[0]).length
230
+
231
+ // Act
232
+ const actualByteCount = transactionBlockByteCount(transactionWithExtraMeta)
233
+
234
+ // Assert
235
+ expect(actualByteCount).toBe(expectedByteCount)
236
+ // Verify meta fields are actually omitted
237
+ expect(expectedCleanTransaction).not.toHaveProperty('$meta')
238
+ expect(expectedCleanTransaction).not.toHaveProperty('_extraMeta1')
239
+ expect(expectedCleanPayload[0]).not.toHaveProperty('$meta')
240
+ expect(expectedCleanPayload[0]).not.toHaveProperty('_extraMeta1')
241
+ })
242
+ })
@@ -0,0 +1,20 @@
1
+ import {
2
+ describe, expect, it,
3
+ } from 'vitest'
4
+
5
+ import { isLocalhost } from '../isLocalhost.ts'
6
+
7
+ describe('is.ts type guards', () => {
8
+ describe('isLocalhost', () => {
9
+ it('correctly identifies localhost strings', () => {
10
+ expect(isLocalhost('localhost')).toBe(true)
11
+ expect(isLocalhost('127.0.0.1')).toBe(true)
12
+ expect(isLocalhost('::1')).toBe(true)
13
+ expect(isLocalhost('example.localhost')).toBe(true)
14
+
15
+ expect(isLocalhost('example.com')).toBe(false)
16
+ expect(isLocalhost('')).toBe(false)
17
+ expect(isLocalhost('local')).toBe(false)
18
+ })
19
+ })
20
+ })
package/xy.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { XyTsupConfig } from '@xylabs/ts-scripts-yarn3'
2
- const config: XyTsupConfig = {
3
- compile: {
4
- browser: {},
5
- node: {},
6
- neutral: { src: true },
7
- },
8
- }
9
-
10
- export default config