@qrkit/bc-ur 2.0.0-beta.9-qrkit.1

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 (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +996 -0
  3. package/dist/commonjs/classes/FountainDecoder.d.ts +125 -0
  4. package/dist/commonjs/classes/FountainDecoder.js +453 -0
  5. package/dist/commonjs/classes/FountainDecoder.js.map +1 -0
  6. package/dist/commonjs/classes/FountainEncoder.d.ts +63 -0
  7. package/dist/commonjs/classes/FountainEncoder.js +168 -0
  8. package/dist/commonjs/classes/FountainEncoder.js.map +1 -0
  9. package/dist/commonjs/classes/RegistryItem.d.ts +104 -0
  10. package/dist/commonjs/classes/RegistryItem.js +172 -0
  11. package/dist/commonjs/classes/RegistryItem.js.map +1 -0
  12. package/dist/commonjs/classes/UR.d.ts +89 -0
  13. package/dist/commonjs/classes/UR.js +243 -0
  14. package/dist/commonjs/classes/UR.js.map +1 -0
  15. package/dist/commonjs/classes/UrFountainDecoder.d.ts +15 -0
  16. package/dist/commonjs/classes/UrFountainDecoder.js +127 -0
  17. package/dist/commonjs/classes/UrFountainDecoder.js.map +1 -0
  18. package/dist/commonjs/classes/UrFountainEncoder.d.ts +42 -0
  19. package/dist/commonjs/classes/UrFountainEncoder.js +92 -0
  20. package/dist/commonjs/classes/UrFountainEncoder.js.map +1 -0
  21. package/dist/commonjs/classes/key.helper.d.ts +27 -0
  22. package/dist/commonjs/classes/key.helper.js +70 -0
  23. package/dist/commonjs/classes/key.helper.js.map +1 -0
  24. package/dist/commonjs/encodingMethods/BytewordEncoding.d.ts +11 -0
  25. package/dist/commonjs/encodingMethods/BytewordEncoding.js +23 -0
  26. package/dist/commonjs/encodingMethods/BytewordEncoding.js.map +1 -0
  27. package/dist/commonjs/encodingMethods/CborEncoding.d.ts +44 -0
  28. package/dist/commonjs/encodingMethods/CborEncoding.js +151 -0
  29. package/dist/commonjs/encodingMethods/CborEncoding.js.map +1 -0
  30. package/dist/commonjs/encodingMethods/HexEncoding.d.ts +8 -0
  31. package/dist/commonjs/encodingMethods/HexEncoding.js +24 -0
  32. package/dist/commonjs/encodingMethods/HexEncoding.js.map +1 -0
  33. package/dist/commonjs/encodingMethods/UrEncoding.d.ts +10 -0
  34. package/dist/commonjs/encodingMethods/UrEncoding.js +19 -0
  35. package/dist/commonjs/encodingMethods/UrEncoding.js.map +1 -0
  36. package/dist/commonjs/encodingMethods/bytewords.d.ts +27 -0
  37. package/dist/commonjs/encodingMethods/bytewords.js +152 -0
  38. package/dist/commonjs/encodingMethods/bytewords.js.map +1 -0
  39. package/dist/commonjs/encodingMethods/index.d.ts +19 -0
  40. package/dist/commonjs/encodingMethods/index.js +27 -0
  41. package/dist/commonjs/encodingMethods/index.js.map +1 -0
  42. package/dist/commonjs/encodingMethods/pipeline.d.ts +19 -0
  43. package/dist/commonjs/encodingMethods/pipeline.js +80 -0
  44. package/dist/commonjs/encodingMethods/pipeline.js.map +1 -0
  45. package/dist/commonjs/enums/EncodingMethodName.d.ts +10 -0
  46. package/dist/commonjs/enums/EncodingMethodName.js +15 -0
  47. package/dist/commonjs/enums/EncodingMethodName.js.map +1 -0
  48. package/dist/commonjs/errors.d.ts +15 -0
  49. package/dist/commonjs/errors.js +39 -0
  50. package/dist/commonjs/errors.js.map +1 -0
  51. package/dist/commonjs/helpers/aliasSampling.d.ts +6 -0
  52. package/dist/commonjs/helpers/aliasSampling.js +73 -0
  53. package/dist/commonjs/helpers/aliasSampling.js.map +1 -0
  54. package/dist/commonjs/helpers/crc32.d.ts +1 -0
  55. package/dist/commonjs/helpers/crc32.js +19 -0
  56. package/dist/commonjs/helpers/crc32.js.map +1 -0
  57. package/dist/commonjs/helpers/fountainUtils.d.ts +40 -0
  58. package/dist/commonjs/helpers/fountainUtils.js +124 -0
  59. package/dist/commonjs/helpers/fountainUtils.js.map +1 -0
  60. package/dist/commonjs/helpers/sha256.d.ts +1 -0
  61. package/dist/commonjs/helpers/sha256.js +78 -0
  62. package/dist/commonjs/helpers/sha256.js.map +1 -0
  63. package/dist/commonjs/helpers/type.helper.d.ts +3 -0
  64. package/dist/commonjs/helpers/type.helper.js +3 -0
  65. package/dist/commonjs/helpers/type.helper.js.map +1 -0
  66. package/dist/commonjs/helpers/uintArrayHelper.d.ts +287 -0
  67. package/dist/commonjs/helpers/uintArrayHelper.js +545 -0
  68. package/dist/commonjs/helpers/uintArrayHelper.js.map +1 -0
  69. package/dist/commonjs/helpers/utils.d.ts +55 -0
  70. package/dist/commonjs/helpers/utils.js +123 -0
  71. package/dist/commonjs/helpers/utils.js.map +1 -0
  72. package/dist/commonjs/index-react-native.d.ts +9 -0
  73. package/dist/commonjs/index-react-native.js +15 -0
  74. package/dist/commonjs/index-react-native.js.map +1 -0
  75. package/dist/commonjs/index.d.ts +21 -0
  76. package/dist/commonjs/index.js +34 -0
  77. package/dist/commonjs/index.js.map +1 -0
  78. package/dist/commonjs/interfaces/IEncodingMethod.d.ts +9 -0
  79. package/dist/commonjs/interfaces/IEncodingMethod.js +3 -0
  80. package/dist/commonjs/interfaces/IEncodingMethod.js.map +1 -0
  81. package/dist/commonjs/package.json +3 -0
  82. package/dist/commonjs/registry.d.ts +26 -0
  83. package/dist/commonjs/registry.js +118 -0
  84. package/dist/commonjs/registry.js.map +1 -0
  85. package/dist/commonjs/test.utils.d.ts +31 -0
  86. package/dist/commonjs/test.utils.js +88 -0
  87. package/dist/commonjs/test.utils.js.map +1 -0
  88. package/dist/commonjs/wrappers/cbor2-cjs.cjs.map +1 -0
  89. package/dist/commonjs/wrappers/cbor2.d.ts +2 -0
  90. package/dist/commonjs/wrappers/cbor2.js +14 -0
  91. package/dist/commonjs/wrappers/cbor2Wrapper.d.ts +14 -0
  92. package/dist/commonjs/wrappers/cbor2Wrapper.js +49 -0
  93. package/dist/commonjs/wrappers/cbor2Wrapper.js.map +1 -0
  94. package/dist/commonjs/xoshiro.d.ts +12 -0
  95. package/dist/commonjs/xoshiro.js +52 -0
  96. package/dist/commonjs/xoshiro.js.map +1 -0
  97. package/dist/esm/classes/FountainDecoder.d.ts +125 -0
  98. package/dist/esm/classes/FountainDecoder.js +447 -0
  99. package/dist/esm/classes/FountainDecoder.js.map +1 -0
  100. package/dist/esm/classes/FountainEncoder.d.ts +63 -0
  101. package/dist/esm/classes/FountainEncoder.js +164 -0
  102. package/dist/esm/classes/FountainEncoder.js.map +1 -0
  103. package/dist/esm/classes/RegistryItem.d.ts +104 -0
  104. package/dist/esm/classes/RegistryItem.js +166 -0
  105. package/dist/esm/classes/RegistryItem.js.map +1 -0
  106. package/dist/esm/classes/UR.d.ts +89 -0
  107. package/dist/esm/classes/UR.js +239 -0
  108. package/dist/esm/classes/UR.js.map +1 -0
  109. package/dist/esm/classes/UrFountainDecoder.d.ts +15 -0
  110. package/dist/esm/classes/UrFountainDecoder.js +123 -0
  111. package/dist/esm/classes/UrFountainDecoder.js.map +1 -0
  112. package/dist/esm/classes/UrFountainEncoder.d.ts +42 -0
  113. package/dist/esm/classes/UrFountainEncoder.js +88 -0
  114. package/dist/esm/classes/UrFountainEncoder.js.map +1 -0
  115. package/dist/esm/classes/key.helper.d.ts +27 -0
  116. package/dist/esm/classes/key.helper.js +66 -0
  117. package/dist/esm/classes/key.helper.js.map +1 -0
  118. package/dist/esm/encodingMethods/BytewordEncoding.d.ts +11 -0
  119. package/dist/esm/encodingMethods/BytewordEncoding.js +19 -0
  120. package/dist/esm/encodingMethods/BytewordEncoding.js.map +1 -0
  121. package/dist/esm/encodingMethods/CborEncoding.d.ts +44 -0
  122. package/dist/esm/encodingMethods/CborEncoding.js +147 -0
  123. package/dist/esm/encodingMethods/CborEncoding.js.map +1 -0
  124. package/dist/esm/encodingMethods/HexEncoding.d.ts +8 -0
  125. package/dist/esm/encodingMethods/HexEncoding.js +20 -0
  126. package/dist/esm/encodingMethods/HexEncoding.js.map +1 -0
  127. package/dist/esm/encodingMethods/UrEncoding.d.ts +10 -0
  128. package/dist/esm/encodingMethods/UrEncoding.js +15 -0
  129. package/dist/esm/encodingMethods/UrEncoding.js.map +1 -0
  130. package/dist/esm/encodingMethods/bytewords.d.ts +27 -0
  131. package/dist/esm/encodingMethods/bytewords.js +147 -0
  132. package/dist/esm/encodingMethods/bytewords.js.map +1 -0
  133. package/dist/esm/encodingMethods/index.d.ts +19 -0
  134. package/dist/esm/encodingMethods/index.js +24 -0
  135. package/dist/esm/encodingMethods/index.js.map +1 -0
  136. package/dist/esm/encodingMethods/pipeline.d.ts +19 -0
  137. package/dist/esm/encodingMethods/pipeline.js +76 -0
  138. package/dist/esm/encodingMethods/pipeline.js.map +1 -0
  139. package/dist/esm/enums/EncodingMethodName.d.ts +10 -0
  140. package/dist/esm/enums/EncodingMethodName.js +12 -0
  141. package/dist/esm/enums/EncodingMethodName.js.map +1 -0
  142. package/dist/esm/errors.d.ts +15 -0
  143. package/dist/esm/errors.js +31 -0
  144. package/dist/esm/errors.js.map +1 -0
  145. package/dist/esm/helpers/aliasSampling.d.ts +6 -0
  146. package/dist/esm/helpers/aliasSampling.js +70 -0
  147. package/dist/esm/helpers/aliasSampling.js.map +1 -0
  148. package/dist/esm/helpers/crc32.d.ts +1 -0
  149. package/dist/esm/helpers/crc32.js +16 -0
  150. package/dist/esm/helpers/crc32.js.map +1 -0
  151. package/dist/esm/helpers/fountainUtils.d.ts +40 -0
  152. package/dist/esm/helpers/fountainUtils.js +114 -0
  153. package/dist/esm/helpers/fountainUtils.js.map +1 -0
  154. package/dist/esm/helpers/sha256.d.ts +1 -0
  155. package/dist/esm/helpers/sha256.js +75 -0
  156. package/dist/esm/helpers/sha256.js.map +1 -0
  157. package/dist/esm/helpers/type.helper.d.ts +3 -0
  158. package/dist/esm/helpers/type.helper.js +2 -0
  159. package/dist/esm/helpers/type.helper.js.map +1 -0
  160. package/dist/esm/helpers/uintArrayHelper.d.ts +287 -0
  161. package/dist/esm/helpers/uintArrayHelper.js +526 -0
  162. package/dist/esm/helpers/uintArrayHelper.js.map +1 -0
  163. package/dist/esm/helpers/utils.d.ts +55 -0
  164. package/dist/esm/helpers/utils.js +102 -0
  165. package/dist/esm/helpers/utils.js.map +1 -0
  166. package/dist/esm/index-react-native.d.ts +9 -0
  167. package/dist/esm/index-react-native.js +12 -0
  168. package/dist/esm/index-react-native.js.map +1 -0
  169. package/dist/esm/index.d.ts +21 -0
  170. package/dist/esm/index.js +16 -0
  171. package/dist/esm/index.js.map +1 -0
  172. package/dist/esm/interfaces/IEncodingMethod.d.ts +9 -0
  173. package/dist/esm/interfaces/IEncodingMethod.js +2 -0
  174. package/dist/esm/interfaces/IEncodingMethod.js.map +1 -0
  175. package/dist/esm/package.json +3 -0
  176. package/dist/esm/registry.d.ts +26 -0
  177. package/dist/esm/registry.js +114 -0
  178. package/dist/esm/registry.js.map +1 -0
  179. package/dist/esm/test.utils.d.ts +31 -0
  180. package/dist/esm/test.utils.js +83 -0
  181. package/dist/esm/test.utils.js.map +1 -0
  182. package/dist/esm/wrappers/cbor2-deno.d.mts +2 -0
  183. package/dist/esm/wrappers/cbor2-deno.mjs +5 -0
  184. package/dist/esm/wrappers/cbor2-deno.mjs.map +1 -0
  185. package/dist/esm/wrappers/cbor2.d.ts +2 -0
  186. package/dist/esm/wrappers/cbor2.js +5 -0
  187. package/dist/esm/wrappers/cbor2.js.map +1 -0
  188. package/dist/esm/wrappers/cbor2Wrapper.d.ts +5 -0
  189. package/dist/esm/wrappers/cbor2Wrapper.js +6 -0
  190. package/dist/esm/wrappers/cbor2Wrapper.js.map +1 -0
  191. package/dist/esm/xoshiro.d.ts +12 -0
  192. package/dist/esm/xoshiro.js +47 -0
  193. package/dist/esm/xoshiro.js.map +1 -0
  194. package/dist/web/bytewords.js +335 -0
  195. package/index.html +98 -0
  196. package/package.json +94 -0
  197. package/src/classes/FountainDecoder.ts +539 -0
  198. package/src/classes/FountainEncoder.ts +211 -0
  199. package/src/classes/RegistryItem.ts +240 -0
  200. package/src/classes/UR.ts +308 -0
  201. package/src/classes/UrFountainDecoder.ts +142 -0
  202. package/src/classes/UrFountainEncoder.ts +103 -0
  203. package/src/classes/key.helper.ts +85 -0
  204. package/src/encodingMethods/BytewordEncoding.ts +23 -0
  205. package/src/encodingMethods/CborEncoding.ts +196 -0
  206. package/src/encodingMethods/HexEncoding.ts +23 -0
  207. package/src/encodingMethods/UrEncoding.ts +19 -0
  208. package/src/encodingMethods/bytewords.ts +215 -0
  209. package/src/encodingMethods/index.ts +26 -0
  210. package/src/encodingMethods/pipeline.ts +103 -0
  211. package/src/enums/EncodingMethodName.ts +10 -0
  212. package/src/errors.ts +34 -0
  213. package/src/helpers/aliasSampling.ts +87 -0
  214. package/src/helpers/crc32.ts +19 -0
  215. package/src/helpers/fountainUtils.ts +157 -0
  216. package/src/helpers/sha256.ts +88 -0
  217. package/src/helpers/type.helper.ts +1 -0
  218. package/src/helpers/uintArrayHelper.ts +611 -0
  219. package/src/helpers/utils.ts +135 -0
  220. package/src/index-react-native.ts +12 -0
  221. package/src/index.ts +44 -0
  222. package/src/interfaces/IEncodingMethod.ts +10 -0
  223. package/src/registry.ts +146 -0
  224. package/src/test.utils.ts +105 -0
  225. package/src/wrappers/cbor2-cjs.cts +6 -0
  226. package/src/wrappers/cbor2-deno.mts +6 -0
  227. package/src/wrappers/cbor2.ts +7 -0
  228. package/src/wrappers/cbor2Wrapper.ts +14 -0
  229. package/src/xoshiro.ts +66 -0
@@ -0,0 +1,211 @@
1
+ import { chooseFragments, findNominalFragmentLength, mixFragments, partitionMessage } from "../helpers/fountainUtils.js";
2
+ import { toUint32, getCRC } from "../helpers/utils.js";
3
+ import { CborEncoding } from "../encodingMethods/CborEncoding.js";
4
+
5
+ const cborEncoder = new CborEncoding();
6
+
7
+ /** [seqNum, seqLength, messageLength, checksum, fragment] */
8
+ export type IMultipartUrPayload = [number, number, number, number, Uint8Array];
9
+
10
+ /**
11
+ * Encode data on the fly. This encoder uses an internal state to keep generating ur fragments of the payload.
12
+ */
13
+ export class FountainEncoder {
14
+ public fragmentLenghtFinder = findNominalFragmentLength;
15
+
16
+ /** Total data size of the input message */
17
+ public _messageLength: number;
18
+ /** Maximum data size in the fragment */
19
+ protected _maxFragmentLength: number;
20
+ /** Minumum data size in the fragment */
21
+ protected _minFragmentLength: number;
22
+ /** Calculated data size in the fragment */
23
+ public _nominalFragmentLength: number;
24
+ /** Array of pure fragments (without any fountain encoded) */
25
+ public _pureFragments: Uint8Array[];
26
+ /** Current index of the fragment start from 1 */
27
+ protected _seqNum: number;
28
+ /** Checksum of the original data */
29
+ protected _checksum: number;
30
+ /** Original Input data as UR */
31
+ protected _input: Uint8Array;
32
+
33
+ constructor(
34
+ input: Uint8Array,
35
+ maxFragmentLength: number = 100,
36
+ minFragmentLength: number = 10,
37
+ firstSeqNum: number = 0
38
+ ) {
39
+
40
+
41
+ // Validate the input
42
+ if (!input) {
43
+ throw new Error("input should be defined");
44
+ }
45
+
46
+ if(!(input instanceof Uint8Array)) {
47
+ throw new Error("input should be Uint8Array")
48
+ }
49
+
50
+ if (typeof maxFragmentLength !== "number") {
51
+ throw new Error("maxFragmentLength should be a number");
52
+ }
53
+
54
+ if (typeof minFragmentLength !== "number") {
55
+ throw new Error("minFragmentLength should be a number");
56
+ }
57
+
58
+ if (typeof firstSeqNum !== "number") {
59
+ throw new Error("firstSeqNum should be a number");
60
+ }
61
+
62
+ if (maxFragmentLength < 1) {
63
+ throw new Error("maxFragmentLength should be > 0");
64
+ }
65
+
66
+ if(maxFragmentLength < minFragmentLength) {
67
+ throw new Error("maxFragmentLength should be >= minFragmentLength");
68
+ }
69
+
70
+
71
+ this._input = input;
72
+ this._maxFragmentLength = maxFragmentLength;
73
+ this._minFragmentLength = minFragmentLength;
74
+ this._seqNum = toUint32(firstSeqNum);
75
+
76
+ // Get the length of the message
77
+ this._messageLength = input.length;
78
+
79
+ // Check if message is less than maxFragmentLength, then return the message as a single fragment
80
+ if (input.length <= maxFragmentLength) {
81
+ this._pureFragments = [input];
82
+ this._nominalFragmentLength = this._messageLength;
83
+ }
84
+ // Otherwise calciulate the nominal fragment length and neseccary fragments
85
+ else {
86
+ // Check for the nominal length of a fragment.
87
+ const fragmentLength = this.fragmentLenghtFinder(input.length, maxFragmentLength, minFragmentLength);
88
+ this._nominalFragmentLength = fragmentLength;
89
+ // Calculate the checksum of the message
90
+ this._checksum = getCRC(input);
91
+ // Split up the message buffer in an array of buffers, by the nominal length
92
+ this._pureFragments = partitionMessage(input, fragmentLength);
93
+ }
94
+ }
95
+
96
+ setFragmentLengthFinder(fn: typeof findNominalFragmentLength) {
97
+ this.fragmentLenghtFinder = fn;
98
+ }
99
+
100
+ /**
101
+ * Return all the fragments based on the fountain ratio at once as an array of Uint8Arrays.
102
+ * @param fountainRatio The ratio of the fountain fragments to the pure fragments. Default is 0.
103
+ * @returns
104
+ */
105
+ getAllParts(fountainRatio: number = 0): Uint8Array[] {
106
+ // First check if the input is a single fragment
107
+ if (this.isSinglePart()) {
108
+ return [this._input];
109
+ }
110
+ // Ceil to always get an integer
111
+ const numberofParts = Math.ceil(this.getPureFragmentCount() * (1 + fountainRatio));
112
+ // Save state
113
+ const oldSeqNum = this._seqNum;
114
+ // Reset state to start generating fragments from the beginning
115
+ this._seqNum = 0;
116
+ // Generate fragments
117
+ let fragments = []
118
+ for (let i = 0; i < numberofParts; i++) {
119
+ fragments.push(this.nextPart());
120
+ }
121
+ // Bring state back
122
+ this._seqNum = oldSeqNum;
123
+ return fragments;
124
+ }
125
+
126
+ // Encode fragment with correct CBOR structure
127
+ protected encodeFragment(seqNum:number, fragment:Uint8Array): Uint8Array {
128
+ // Shape for the CBOR payload
129
+ const payload: IMultipartUrPayload = [seqNum, this._pureFragments.length, this._messageLength, this._checksum, fragment];
130
+ const encodedFragment = cborEncoder.encode(payload);
131
+
132
+ return encodedFragment;
133
+ }
134
+
135
+ /**
136
+ * Reset the state of the encoder to start generating fragments from the beginning.
137
+ */
138
+ public reset() {
139
+ this._seqNum = 0;
140
+ }
141
+
142
+
143
+ /**
144
+ * Checks if all the pure fragments (full payload data) for this ur is generated.
145
+ * @returns boolean indicating if generated fragments have included all the data.
146
+ */
147
+ public isComplete(): boolean {
148
+ return this._seqNum >= this.getPureFragmentCount();
149
+ }
150
+
151
+ /**
152
+ * Checks if there is only one fragment generated for the ur.
153
+ * @returns boolean if the ur payload is contained in one fragment.
154
+ */
155
+ public isSinglePart(): boolean {
156
+ return this.getPureFragmentCount() === 1;
157
+ }
158
+
159
+ /**
160
+ * Gets the count of the "pure" fragments. These are fragments where the data is not mixed.
161
+ * @returns The count of the "pure" fragments.
162
+ */
163
+ public getPureFragmentCount(): number {
164
+ return this._pureFragments.length;
165
+ }
166
+
167
+ /**
168
+ * Get the pure fragments of the ur.
169
+ * @returns the pure fragments of the ur as an array of Uint8Arrays.
170
+ */
171
+ public getPureFragments(): Uint8Array[] {
172
+ return this._pureFragments;
173
+ }
174
+
175
+ /**
176
+ * Give the 'next' fragment for the ur for which the fountainEncoder was created.
177
+ * @returns the 'next' fragment, represented as a Ur multipart string.
178
+ */
179
+ public nextPart(): Uint8Array {
180
+ this._seqNum = toUint32(this._seqNum + 1);
181
+
182
+ // when the seqnum restarts because of a number bigger than Uint32, we need to make sure to skip 0 to prevent invalid Multipart URs.
183
+ if (this._seqNum === 0) {
184
+ this._seqNum = toUint32(this._seqNum + 1);
185
+ }
186
+
187
+ // If its single part, return the original input
188
+ if (this.isSinglePart()) {
189
+ return this._input;
190
+ }
191
+
192
+ // For the first seqNum of the pure fragments, return the pure fragment
193
+ if (!this.isComplete()) {
194
+ return this.encodeFragment(this._seqNum, this._pureFragments[this._seqNum - 1]);
195
+ }
196
+
197
+ // For the fountain fragments, mix the fragments
198
+ const indexes = chooseFragments(
199
+ this._seqNum,
200
+ this._pureFragments.length,
201
+ this._checksum
202
+ );
203
+ const mixed = mixFragments(
204
+ indexes,
205
+ this._pureFragments,
206
+ this._nominalFragmentLength
207
+ );
208
+
209
+ return this.encodeFragment(this._seqNum, mixed);
210
+ }
211
+ }
@@ -0,0 +1,240 @@
1
+ import { UR } from "./UR.js";
2
+ import { encodeKeys, decodeKeys, IKeyMap } from "./key.helper.js";
3
+
4
+ // Define the symbol
5
+ export const registryItemSymbol = Symbol.for("RegistryItemBase");
6
+
7
+ /**
8
+ * Static interface that RegistryItem classes should implement
9
+ */
10
+ export interface IRegistryType {
11
+ /**
12
+ * Cbor Tags
13
+ * Finalized versions defined in: https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
14
+ */
15
+ tag: number;
16
+ /**
17
+ * Uniform Resource ID
18
+ *
19
+ * Links:
20
+ * - https://developer.blockchaincommons.com/ur/
21
+ * - https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-006-urtypes.md
22
+ * - https://github.com/ngraveio/Research
23
+ * - https://github.com/KeystoneHQ/Keystone-developer-hub/tree/main/research
24
+ *
25
+ */
26
+ URType: string;
27
+ /** CDDL definition for CBOR encoding */
28
+ CDDL: string;
29
+ /** Key name to key in integer map for smaller encoded data size */
30
+ keyMap?: IKeyMap;
31
+ /** allow the keys that are not explicitely defined in the keyMap */
32
+ allowKeysNotInMap?: boolean;
33
+ }
34
+
35
+ export abstract class RegistryItemBase {
36
+ readonly type: IRegistryType;
37
+ /** If CDDL contains keys as numbers, map them to their respective values */
38
+ keyMap: IKeyMap;
39
+ /** Data that our item contains */
40
+ // TODO: should we force this to be a map? It is much safer that way for injection attacks
41
+ data: any;
42
+ static allowKeysNotInMap: any;
43
+
44
+ constructor(registryType: IRegistryType, data?: any, keyMap?: IKeyMap) {
45
+ this.type = registryType;
46
+ this.keyMap = keyMap;
47
+
48
+ // Verify input
49
+ const { valid, reasons } = this.verifyInput(data);
50
+ if (!valid) {
51
+ throw new Error(`Invalid input: ${reasons?.map((r) => r.message).join(", ")}`);
52
+ }
53
+ this.data = data;
54
+ (this as any)[registryItemSymbol] = true;
55
+ }
56
+
57
+ /**
58
+ * Verify the input data
59
+ *
60
+ * @param input
61
+ */
62
+ verifyInput(input: any): { valid: boolean; reasons?: Error[] } {
63
+ // This should be implemented by the child class
64
+ return {
65
+ valid: true,
66
+ reasons: undefined,
67
+ };
68
+ }
69
+
70
+ toString(): string {
71
+ return `${this.type.URType}[${this.type.tag}](${JSON.stringify(this.data)})`;
72
+ }
73
+
74
+ toJSON() {
75
+ // TODO: if there is any registry item in the data (could be nested or in array), we should call toJSON on them as well
76
+ return {
77
+ type: this.type.URType,
78
+ tag: this.type.tag,
79
+ ...this.data,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Preprocess the data before encoding into CBOR Tagged instance
85
+ *
86
+ * @param data, data before keymap conversion, if left empty, it will use the this.data property
87
+ */
88
+ preCBOR(data = this.data) {
89
+ // If key-map exists, convert keys to integers
90
+ if (this.keyMap) {
91
+ const allowKeysNotInMap = (this.constructor as typeof RegistryItemBase).allowKeysNotInMap;
92
+ return encodeKeys(data, this.keyMap, allowKeysNotInMap);
93
+ }
94
+ return data;
95
+ }
96
+
97
+ /**
98
+ * Called by the CBOR encoder for encoding the data
99
+ *
100
+ * [CBOR Docs](https://github.com/hildjj/cbor2?tab=readme-ov-file#tocbor-method)
101
+ *
102
+ * This is the easiest approach, if you can modify the class being encoded.
103
+ * Add a toCBOR() method to your class, which should return a two-element array containing the tag number and data item that encodes your class.
104
+ * If the tag number is NaN, no tag will be written. If you return undefined, nothing will be written.
105
+ * In this case you will likely write custom bytes to the Writer instance that is passed in,
106
+ * perhaps using the encoding options.
107
+ *
108
+ * @param encoder
109
+ * @returns
110
+ */
111
+ toCBOR(_writer, _options) {
112
+ const processed = this.preCBOR(this.data);
113
+ let tag = this.type.tag;
114
+ // TODO: find a better way to ignore top level tag on encoder
115
+ if (_options?.ignoreTopLevelTag) {
116
+ tag = NaN; // Do not tag the top level item
117
+ // Set it back to false for child items
118
+ _options.ignoreTopLevelTag = false;
119
+ }
120
+ return [tag, processed];
121
+ }
122
+
123
+ toUr() {
124
+ return new UR(this);
125
+ }
126
+
127
+ toHex() {
128
+ return this.toUr().getPayloadHex();
129
+ }
130
+
131
+ toBytes() {
132
+ return this.toUr().getPayloadCbor();
133
+ }
134
+
135
+ public encodeKeys = encodeKeys;
136
+ public decodeKeys = decodeKeys;
137
+
138
+ // Custom inspection method for Node.js
139
+ public [Symbol.for("nodejs.util.inspect.custom")](
140
+ _depth: number,
141
+ inspectOptions: object,
142
+ inspect: (val: unknown, opts: object) => unknown
143
+ ): string {
144
+ return `${this.type.URType}[${this.type.tag}](${inspect(this.data, inspectOptions)})`;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Factory function to create a new RegistryItem class
150
+ *
151
+ * It injects static properties to the class and does preprocessing when needed
152
+ *
153
+ * @param input
154
+ * @returns
155
+ */
156
+ export function registryItemFactory<T extends RegistryItemBase>(input: IRegistryType): RegistryItemClass<T> {
157
+ const { tag, URType, CDDL, keyMap, allowKeysNotInMap = true } = input;
158
+ const _keyMap = keyMap;
159
+
160
+ return class extends RegistryItemBase {
161
+ // Add static properties to the class
162
+ static tag: number = tag;
163
+ static URType: string = URType;
164
+ static CDDL: string = CDDL;
165
+ static keyMap: IKeyMap = _keyMap;
166
+ static allowKeysNotInMap: boolean = allowKeysNotInMap;
167
+
168
+ // Initiate base class with the values
169
+ constructor(data?: any, keyMap: IKeyMap = _keyMap) {
170
+ super(input, data, keyMap);
171
+ }
172
+
173
+ /**
174
+ * Post process the data after decoding CBOR
175
+ */
176
+ static postCBOR(val: any, allowKeysNotInMapOverwrite?: boolean) {
177
+ // If key-map exists, convert integer keys back to string keys
178
+ if (keyMap) {
179
+ return decodeKeys(val, keyMap, allowKeysNotInMapOverwrite ?? allowKeysNotInMap);
180
+ }
181
+ return val;
182
+ }
183
+
184
+ static fromUr(ur: UR | string): T {
185
+ const urObj = typeof ur === "string" ? UR.fromString(ur) : ur;
186
+ const decoded = urObj.decode() as unknown;
187
+
188
+ return decoded as T;
189
+ }
190
+
191
+ static fromHex(hex: string): T {
192
+ const ur = UR.fromHex({ type: URType, payload: hex });
193
+ const decoded = ur.decode() as unknown;
194
+
195
+ return decoded as T;
196
+ }
197
+
198
+ /**
199
+ * Static method to create an instance from CBOR DataItem data.
200
+ * It processes the raw CBOR data if needed and returns a new instance of the class.
201
+ */
202
+ static fromCBORData(val: any, allowKeysNotInMap?: boolean, tagged?: any) {
203
+ // Do some post processing data coming from the cbor decoder
204
+ const data = this.postCBOR(val, allowKeysNotInMap);
205
+
206
+ // Return an instance of the generated class
207
+ return new this(data);
208
+ }
209
+
210
+ } as RegistryItemClass<T>;
211
+ }
212
+
213
+ // Helper type to define the RegistryItem class with custom constructors and static properties
214
+ export type RegistryItemClass<T extends RegistryItemBase = RegistryItemBase> = {
215
+ new (...args: any[]): T;
216
+ tag: number;
217
+ URType: string;
218
+ CDDL: string;
219
+ keyMap?: IKeyMap;
220
+ allowKeysNotInMap: boolean;
221
+ postCBOR(val: any, allowKeysNotInMapOverwrite?: boolean): any;
222
+ fromCBORData(val: any, allowKeysNotInMap?: boolean, tagged?: any): T;
223
+ fromUr(ur: UR | string): T;
224
+ fromHex(hex: string): T;
225
+ };
226
+ export type RegistryItem = InstanceType<RegistryItemClass>;
227
+
228
+ /**
229
+ * Function to check if an object is an instance of RegistryItemBase or its subclasses
230
+ * @param obj - The object to check
231
+ * @returns true if the object is an instance of RegistryItemBase or its subclasses
232
+ */
233
+ export function isRegistryItem(obj: any): obj is RegistryItem {
234
+ return (
235
+ obj instanceof RegistryItemBase ||
236
+ (obj && obj[registryItemSymbol] === true) ||
237
+ //(obj && typeof obj.toCBOR === 'function' && typeof obj.toUr === 'function' && typeof obj.toHex === 'function' && typeof obj.toBytes === 'function' && obj.type !== undefined && obj.data !== undefined)
238
+ (obj && typeof obj.toCBOR === 'function' && typeof obj.toUr === 'function' && typeof obj.toHex === 'function' && obj.type !== undefined && obj.data !== undefined)
239
+ );
240
+ }