@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,308 @@
1
+ import { isRegistryItem, RegistryItem, RegistryItemBase } from "./RegistryItem.js";
2
+ import { EncodingMethodName } from "../enums/EncodingMethodName.js";
3
+ import { EncodingPipeline } from "../encodingMethods/pipeline.js";
4
+ import {
5
+ InvalidPathLengthError,
6
+ InvalidSchemeError,
7
+ InvalidTypeError,
8
+ } from "../errors.js";
9
+ import { dataPipeline } from "../encodingMethods/index.js";
10
+ import { ReplaceKeyType } from "../helpers/type.helper.js";
11
+
12
+ export interface IUR {
13
+ type: string; // bc-ur type defined in the registry
14
+ payload: string; // encoded data
15
+ seqNum?: number; // sequence number if multipart starting with index 1 - if single part, this is 0
16
+ seqLength?: number; // total number of parts in the multipart message - if single part, this is 0
17
+ isFragment?: boolean; // if the ur is a fragment of a multipart message
18
+ }
19
+
20
+ /**
21
+ * https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-005-ur.md
22
+ * Class that represents the structure of the data we encode/decode in this package.
23
+ * e.g. 'ur:bytes/lpamcmcfatrdcyzcpldpgwhdhtiaiaecgyktgsflguhshthfghjtjngrhsfegtiafegaktgugui'
24
+ *
25
+ *
26
+ * Single part = ur:<type>/<message(payload)>
27
+ * Multi part = ur:<type>/<seqNum-seqLength>/<fragment(payload)>
28
+ *
29
+ * CBOR encoding should not include tag on top level when UR has a tag
30
+ *
31
+ * I want to be able to create UR from encoding normal js types
32
+ * Or from any type in the pipeline
33
+ *
34
+ * Should i do it on the encoder or ur class itself?
35
+ */
36
+ export class UR {
37
+ public type: string;
38
+ public payload: string;
39
+ public seqNum?: number;
40
+ public seqLength?: number;
41
+ public isFragment: boolean;
42
+
43
+ static pipeline: EncodingPipeline<any, string> = dataPipeline;
44
+
45
+ // If type is unknown it should be cbor, because default encoding will take any and convert to cbor without tagging
46
+ constructor(input: IUR | RegistryItem) {
47
+ // Create from registry item
48
+ if (input instanceof RegistryItemBase || isRegistryItem(input)) {
49
+ const ur = UR.fromRegistryItem(input as RegistryItem);
50
+ this.type = ur.type;
51
+ this.payload = ur.payload;
52
+ this.seqNum = ur.seqNum;
53
+ this.seqLength = ur.seqLength;
54
+ this.isFragment = ur.isFragment;
55
+ }
56
+ // Create from raw data
57
+ else if (typeof input == "object" && 'type' in input && 'payload' in input) {
58
+ const { type, payload, seqNum = 0, seqLength = 0 } = input;
59
+ this.payload = payload;
60
+ this.type = type;
61
+ this.seqNum = seqNum;
62
+ this.seqLength = seqLength;
63
+ this.isFragment = seqLength > 0;
64
+ }
65
+ else {
66
+ throw new InvalidTypeError();
67
+ }
68
+ }
69
+
70
+ // Decode
71
+ decode(until?: EncodingMethodName) {
72
+ return UR.pipeline.decode(this.payload, {until, enforceType: this.isFragment ? undefined : this.type});
73
+ }
74
+
75
+ // Get string representation
76
+ toString() {
77
+ return UR.getUrString(this.type, this.payload, this.seqNum, this.seqLength);
78
+ }
79
+
80
+ // Get payload in bytewords
81
+ getPayloadBytewords() {
82
+ return this.payload;
83
+ }
84
+
85
+ // Get payload in hex
86
+ getPayloadHex() {
87
+ return UR.pipeline.decode<string>(this.payload, { until: EncodingMethodName.hex });
88
+ // TODO: add tag information
89
+ }
90
+
91
+ // Get Payload in cbor
92
+ getPayloadCbor() {
93
+ return UR.pipeline.decode<Uint8Array>(this.payload, {until: EncodingMethodName.cbor} );
94
+ // TODO: add tag information
95
+ }
96
+
97
+ toRegistryItem() {
98
+ if (this.isFragment) {
99
+ throw new Error(
100
+ "Cannot convert a multipart ur to registry item, it needs to be decoded first"
101
+ );
102
+ }
103
+ // Enforce type
104
+ // registrtqueryByURType
105
+ return UR.decode(this);
106
+ }
107
+
108
+ /// Static methods
109
+
110
+ static fromRegistryItem(item: RegistryItem) {
111
+ // First convert Registry item to bytewords
112
+ // Only on the top level, we should not include the tag
113
+ const bytewords = UR.pipeline.encode(item, {ignoreTopLevelTag: true});
114
+
115
+ // Now create new UR
116
+ return new UR({
117
+ type: item.type.URType,
118
+ payload: bytewords,
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Create UR from native javascript types by encoding them to bytewords
124
+ * @param input
125
+ * @returns
126
+ */
127
+ static fromData(input: ReplaceKeyType<IUR, 'payload', any>) {
128
+ const bytewords = UR.pipeline.encode(input.payload);
129
+
130
+ // Now create new UR
131
+ return new UR({
132
+ ...input,
133
+ payload: bytewords,
134
+ });
135
+ }
136
+
137
+ static fromCbor(input: ReplaceKeyType<IUR, 'payload', Uint8Array>) {
138
+ // This means we already have cbor Uint8Array
139
+ // So we will start from the next method
140
+ const bytewords = UR.pipeline.encode(input.payload, { from: EncodingMethodName.hex });
141
+
142
+ return new UR({
143
+ ...input,
144
+ payload: bytewords,
145
+ });
146
+ }
147
+
148
+ static fromHex(input: ReplaceKeyType<IUR, 'payload', string>) {
149
+ // This means we already have hex string
150
+ // So we will start from the next method
151
+ const bytewords = UR.pipeline.encode(input.payload, { from: EncodingMethodName.bytewords });
152
+
153
+ return new UR({
154
+ ...input,
155
+ payload: bytewords,
156
+ });
157
+ }
158
+
159
+ static fromBytewords(input: IUR) {
160
+ return new UR({
161
+ ...input,
162
+ });
163
+ }
164
+
165
+ static from(input: ReplaceKeyType<IUR, 'payload', any>, type: EncodingMethodName) {
166
+ switch (type) {
167
+ case EncodingMethodName.bytewords:
168
+ return UR.fromBytewords(input);
169
+ case EncodingMethodName.cbor:
170
+ return UR.fromCbor(input);
171
+ case EncodingMethodName.hex:
172
+ return UR.fromHex(input);
173
+ case EncodingMethodName.ur:
174
+ return UR.fromData(input);
175
+ default:
176
+ throw new Error("Invalid encoding method");
177
+ }
178
+ }
179
+
180
+ static fromString(ur: string) {
181
+ return new UR(UR.parseUr(ur));
182
+ }
183
+
184
+ static encode = this.fromRegistryItem;
185
+ static decode(ur: UR): RegistryItem {
186
+ // Force cbor type
187
+ return UR.pipeline.decode(ur.payload);
188
+ }
189
+
190
+ /**
191
+ * Check if the given string is a valid UR
192
+ * For single part ur, it should be in the form of "ur:<type>/<payload>" which is "ur:<lowercase letters, numbers or dashes>/<bytewords>"
193
+ * For multi part ur, it should be in the form of "ur:<type>/<seqNum-seqLength>/<fragment>" which is "ur:<lowercase letters, numbers or dashes>/<number-number>/<bytewords>"
194
+ * ur uses minimal bytewords encoding style which is a-z and has checksome bytes at the end
195
+ *
196
+ * @param input
197
+ * @returns
198
+ */
199
+ static validate(input: string): boolean {
200
+ // TODO: use bytewords encoding to validate the payload
201
+
202
+ const singlePartPattern = /^ur:[a-z0-9-]+\/[a-z]+$/;
203
+ const multiPartPattern = /^ur:[a-z0-9-]+\/[0-9]+-[0-9]+\/[a-z]+$/;
204
+
205
+ return singlePartPattern.test(input) || multiPartPattern.test(input);
206
+ }
207
+
208
+ /**
209
+ * Generates a UR string from the given type and payload.
210
+ * Single part = ur:<type>/<message(payload)> if seqNum and seqLength are 0
211
+ * Multi part = ur:<type>/<seqNum-seqLength>/<fragment(payload)>
212
+ */
213
+ static getUrString(
214
+ type: string,
215
+ payload: string,
216
+ seqNum = 0,
217
+ seqLen = 0
218
+ ): string {
219
+ // Single UR
220
+ if (seqNum === 0 && seqLen === 0) {
221
+ return joinUri("ur", [type, payload]);
222
+ }
223
+
224
+ // Multi part UR
225
+ const seq = `${seqNum}-${seqLen}`;
226
+ return joinUri("ur", [type, seq, payload]);
227
+ }
228
+
229
+ /**
230
+ * Parses a UR and performs basic validation
231
+ * @param message e.g. "UR:BYTES/6-23/LPAMCHCFATTTCYCLEHGSDPHDHGEHFGHKKKDL..."
232
+ */
233
+ static parseUr(message: string): IUR {
234
+ const lowercase = message.toLowerCase(); // e.g. "ur:bytes/6-23/lpamchcfatttcyclehgsdphdhgehfghkkkdl..."
235
+
236
+ // check if it is valid ur string
237
+ if (!UR.validate(lowercase)) {
238
+ throw new Error("Invalid UR string");
239
+ }
240
+
241
+ const components = lowercase.slice(3).split("/");
242
+ let isFragment = false;
243
+ let seqNum = 0;
244
+ let seqLength = 0;
245
+ let payload = "";
246
+
247
+ // Check for the number of components
248
+ switch (components.length) {
249
+ case 2:
250
+ // single part ur
251
+ isFragment = false;
252
+ break;
253
+ case 3:
254
+ // multi part ur
255
+ isFragment = true;
256
+ break;
257
+ default:
258
+ throw new InvalidPathLengthError();
259
+ }
260
+
261
+ const type = components[0]; //e.g. "bytes"
262
+ if (isFragment) {
263
+ const seq = components[1]; //e.g. "6-23"
264
+ const [num, length] = seq.split("-").map(Number);
265
+ seqNum = num;
266
+ seqLength = length;
267
+ payload = components[2];
268
+ } else {
269
+ payload = components[1];
270
+ }
271
+
272
+ return {
273
+ type,
274
+ payload,
275
+ seqNum,
276
+ seqLength,
277
+ isFragment,
278
+ };
279
+ }
280
+ }
281
+
282
+ /// Helper functions
283
+
284
+ /**
285
+ * Generates a uri. e.g. 'ur:bytes/6-22/lpamcmcfatrdcyzcpldpgwhdhtiaiaecgyktgsflguhshthfghjtjngrhsfegtiafegaktgugui'
286
+ * @param scheme scheme of the uri. e.g. "ur"
287
+ * @param pathComponents adds additional information to the uri in the form of a path (divided by "/"). e.g. "bytes/6-22/lpamcmcfatrdcyzcpldpgwhdhtiaiaecgyktgsflguhshthfghjtjngrhsfegtiafegaktgugui"
288
+ * @returns the complete uri.
289
+ */
290
+ function joinUri(scheme: string, pathComponents: string[]): string {
291
+ const path = pathComponents.join("/");
292
+ return [scheme, path].join(":");
293
+ }
294
+
295
+
296
+
297
+ /**
298
+ * [seqNum, fragments.length, totalPayloadLength, checksum, fragment]
299
+ *
300
+ * part = [
301
+ * uint32 seqNum,
302
+ * uint seqLen,
303
+ * uint messageLen,
304
+ * uint32 checksum,
305
+ * bytes data
306
+ * ]
307
+ */
308
+ export type IMultipartUrPayload = [number, number, number, number, Uint8Array];
@@ -0,0 +1,142 @@
1
+ import { RegistryItem } from "../classes/RegistryItem.js";
2
+ import { EncodingMethodName } from "../enums/EncodingMethodName.js";
3
+ import { FountainDecoder, MultipartPayload, validateDecodedMultipart } from "./FountainDecoder.js";
4
+ import { UR } from "./UR.js";
5
+
6
+ export class UrFountainDecoder extends FountainDecoder {
7
+ public expectedType: string;
8
+ public resultUr: UR;
9
+ public decodedData: RegistryItem | any;
10
+
11
+ constructor(parts: UR[] | string[] = []) {
12
+ super();
13
+ parts.forEach((part) => {
14
+ this.receivePartUr(part);
15
+ });
16
+ }
17
+
18
+ public reset(): void {
19
+ super.reset();
20
+ this.resultUr = undefined;
21
+ this.expectedType = undefined;
22
+ }
23
+
24
+ setExpectedValuesUr(part: UR, decodedPart: MultipartPayload): void {
25
+ this.expectedType = part.type;
26
+ this.expectedPartCount = part.seqLength;
27
+ super.setExpectedValues(decodedPart);
28
+ }
29
+
30
+ protected validateUr(part: UR, decodedPart: MultipartPayload): boolean {
31
+ // Check if UR is a fragment
32
+ if (!part.isFragment) {
33
+ return false;
34
+ }
35
+
36
+ // Check the type of the UR
37
+ if (part.type !== this.expectedType) {
38
+ return false;
39
+ }
40
+
41
+ // Expect metadata ur seqNum to be equal to the decoded part seqNum
42
+ if (part.seqNum !== decodedPart.seqNum) {
43
+ return false;
44
+ }
45
+
46
+ // Expect metadata ur seqLength to be equal to the decoded part seqLength
47
+ if (part.seqLength !== decodedPart.seqLength) {
48
+ return false;
49
+ }
50
+
51
+ // Validate decoded paylad
52
+ return super.validatePart(decodedPart);
53
+ }
54
+
55
+ receivePartUr(part: UR | string): boolean {
56
+ // If we already have a result, we're done
57
+ if (this.done) {
58
+ return false;
59
+ }
60
+
61
+ // Convert string into UR
62
+ if (typeof part === "string") {
63
+ part = UR.fromString(part);
64
+ }
65
+
66
+ // If what we received is not a multupart UR, then we're done
67
+ if (!part.isFragment) {
68
+ // If this is not a fragment and we have not received any fragments yet then its the whole UR
69
+ if (!this.started) {
70
+ this.resultUr = part;
71
+ this.started = true;
72
+ this.done = true;
73
+
74
+ try {
75
+ this.decodedData = this.resultUr.decode();
76
+ } catch (error) {
77
+ this.error = error;
78
+ }
79
+
80
+ // Finish here
81
+ return true;
82
+ }
83
+ // If we have received fragments before then this is an error
84
+ else {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ let parsed: MultipartPayload;
90
+ try {
91
+ const decoded = part.decode();
92
+ parsed = validateDecodedMultipart(decoded);
93
+ } catch (e) {
94
+ console.error(e);
95
+ return false;
96
+ }
97
+
98
+ // If this is the first part we've seen then set expected values
99
+ if (!this.started) this.setExpectedValuesUr(part, parsed);
100
+
101
+ // If this is a fragment validate UR
102
+ if (!this.validateUr(part, parsed)) {
103
+ return false;
104
+ }
105
+
106
+ return super.receivePart(parsed);
107
+ }
108
+
109
+ // Assemble data and generate single result UR
110
+ public finalize(): void {
111
+ super.finalize();
112
+
113
+ if (this.error) {
114
+ return;
115
+ }
116
+
117
+ if (this.resultRaw !== undefined) {
118
+ // Result data is already in CBOR
119
+ // Just convert it to bytewords instead of encoding it again
120
+ const payload = UR.pipeline.encode(this.resultRaw, { from: EncodingMethodName.hex });
121
+ this.resultUr = new UR({
122
+ type: this.expectedType,
123
+ payload: payload,
124
+ });
125
+
126
+ // Now Try to decode the result UR
127
+ try {
128
+ this.decodedData = this.resultUr.decode();
129
+ } catch (error) {
130
+ this.error = error;
131
+ }
132
+ }
133
+ }
134
+
135
+ getDecodedData(): RegistryItem | any {
136
+ if (!this.isSuccessful()) {
137
+ console.log("Fountain decoding was not successful");
138
+ return undefined;
139
+ }
140
+ return this.decodedData;
141
+ }
142
+ }
@@ -0,0 +1,103 @@
1
+ import { isRegistryItem, RegistryItem, RegistryItemBase } from "../classes/RegistryItem.js";
2
+ import { UR } from "./UR.js";
3
+ import { FountainEncoder } from "./FountainEncoder.js";
4
+ import { EncodingMethodName } from "../enums/EncodingMethodName.js";
5
+
6
+ /**
7
+ * UrFountainEncoder encodes data on the fly using an internal state to keep generating UR fragments of the payload.
8
+ * It extends the FountainEncoder class to provide additional functionality specific to UR encoding.
9
+ */
10
+ export class UrFountainEncoder extends FountainEncoder {
11
+ /** The UR type of the input data */
12
+ private _type: string;
13
+ /** The original input data as a UR object */
14
+ private _inputUr: UR;
15
+ /** Reset counter to 1 after hitting `fragmentCount * ratio`; if undefined or 0, it goes forever */
16
+ public repeatAfterRatio = 2;
17
+
18
+ /**
19
+ * Creates an instance of UrFountainEncoder.
20
+ * @param input - The input data to be encoded, either as a RegistryItem or UR.
21
+ * @param maxFragmentLength - The maximum length of each fragment.
22
+ * @param minFragmentLength - The minimum length of each fragment.
23
+ * @param firstSeqNum - The sequence number to start with.
24
+ * @param repeatAfterRatio - The ratio after which the sequence number resets. Default is 2. If 0 fountain goes forever.
25
+ */
26
+ constructor(
27
+ input: RegistryItem | UR,
28
+ maxFragmentLength: number = 100,
29
+ minFragmentLength: number = 10,
30
+ firstSeqNum: number = 0,
31
+ repeatAfterRatio = 2
32
+ ) {
33
+
34
+ let inputUr;
35
+ // If the input is a RegistryItem, convert it to a Ur
36
+ if (input instanceof RegistryItemBase || isRegistryItem(input)) {
37
+ inputUr = UR.fromRegistryItem(input);
38
+ }
39
+ else {
40
+ inputUr = input;
41
+ }
42
+
43
+ // Input in CBOR
44
+ let inputCBOR = inputUr.getPayloadCbor();
45
+
46
+ super(inputCBOR, maxFragmentLength, minFragmentLength, firstSeqNum);
47
+
48
+ this._inputUr = inputUr;
49
+ this._type = inputUr.type;
50
+ this.repeatAfterRatio = repeatAfterRatio;
51
+ }
52
+
53
+
54
+ /**
55
+ * Return all the fragments based on the fountain ratio at once as an array of Uint8Arrays.
56
+ * @param fountainRatio - The ratio of the fountain fragments to the pure fragments. Default is 0.
57
+ * @returns An array of UR objects representing the fragments.
58
+ */
59
+ getAllPartsUr(fountainRatio: number = 0): UR[] {
60
+ const allParts = super.getAllParts(fountainRatio);
61
+ return allParts.map((part, index) => this.fragment2Ur(index+1, part));
62
+ }
63
+
64
+ /**
65
+ * Give the 'next' fragment for the ur for which the fountainEncoder was created.
66
+ * @returns The 'next' fragment, represented as a Ur multipart string.
67
+ */
68
+ public nextPartUr(): UR {
69
+ // Reset the sequence number if we hit the ratio
70
+ if (this.repeatAfterRatio) {
71
+ if (this._seqNum > this._pureFragments.length * this.repeatAfterRatio) {
72
+ this._seqNum = 0;
73
+ }
74
+ }
75
+ const encodedFragment = super.nextPart();
76
+ return this.fragment2Ur(this._seqNum, encodedFragment);
77
+ }
78
+
79
+ /**
80
+ * Converts a fragment to a UR object.
81
+ * @param seqNum - The sequence number of the fragment.
82
+ * @param fragment - The fragment data as a Uint8Array.
83
+ * @returns A UR object representing the fragment.
84
+ */
85
+ private fragment2Ur(seqNum:number, fragment:Uint8Array) {
86
+ // Fragment is already encoded in CBOR
87
+ // Just convert it to bytewords instead of encoding it again
88
+ const payload = UR.pipeline.encode(fragment, {from: EncodingMethodName.hex});
89
+ if (this.isSinglePart()) {
90
+ return new UR({
91
+ type: this._type,
92
+ payload: payload,
93
+ });
94
+ }
95
+ return new UR({
96
+ type: this._type,
97
+ payload: payload,
98
+ seqNum: seqNum,
99
+ seqLength: this._pureFragments.length,
100
+ isFragment: true,
101
+ })
102
+ }
103
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * This file contains helper functions for encoding and decoding keys
3
+ */
4
+
5
+ /**
6
+ * Interface for the key to integer mapping
7
+ */
8
+ export type IKeyMap = Record<string, string | number>;
9
+
10
+ /**
11
+ * For some CDDL types we use integer as keys to keep the CBOR encoded data small
12
+ * This function converts the data keys to their respective integer values
13
+ *
14
+ * It needs to return MAP because cbor library only then encodes the keys as integers
15
+ *
16
+ * @param data Object that has key value pairs
17
+ * @param keyMap Map of the keys to integers values
18
+ * @returns Map with integer keys
19
+ */
20
+ export function encodeKeys(
21
+ data: object,
22
+ keyMap: IKeyMap,
23
+ allowKeysNotInMap: boolean
24
+ ): Map<string | number, any> {
25
+ const map = new Map();
26
+ // If we have a mapping, use it to map the data
27
+ // Check if our data is an object
28
+ if (typeof data !== "object") return undefined;
29
+
30
+ // Create a set from the keys of the data
31
+ const keys = new Set(Object.keys(data));
32
+
33
+ // Add the keys in the correct order to the map
34
+ for (const key in keyMap) {
35
+ // Check if the key exists in the data
36
+ if(data[key] !== undefined) map.set(keyMap[key], data[key]);
37
+ keys.delete(key);
38
+ }
39
+
40
+ if (allowKeysNotInMap) {
41
+ // Add other keys as string if they are not existent in the map
42
+ keys.forEach((key) => {
43
+ map.set(key, data[key]);
44
+ });
45
+ }
46
+
47
+ return map;
48
+ }
49
+
50
+ /**
51
+ * For some CDDL types we use integer as keys to keep the CBOR encoded data small
52
+ * This function converts the data keys back to their respective string values
53
+ *
54
+ * @param _data Object that has encoded key - value pairs
55
+ * @param keyMap Map of the keys to integer values
56
+ * @returns Object with string keys
57
+ */
58
+ export function decodeKeys(
59
+ data: Map<string | number, any> | Record<string | number, any>,
60
+ keyMap: IKeyMap,
61
+ allowKeysNotInMap: boolean
62
+ ): object {
63
+ // If we have a mapping, use it to map the data
64
+ const result = {};
65
+ // Convert the data to a map if it is not already
66
+ const _data = data instanceof Map ? data : new Map(Object.entries(data));
67
+
68
+ // Get all the keys in the data
69
+ const keys = new Set(_data.keys());
70
+
71
+ // Add the keys in the correct order
72
+ for (const key in keyMap) {
73
+ if (_data.has(keyMap[key])) result[key] = _data.get(keyMap[key]);
74
+ keys.delete(keyMap[key]);
75
+ }
76
+
77
+ if (allowKeysNotInMap) {
78
+ // Add other keys as string if they are not existent in the map
79
+ keys.forEach((key) => {
80
+ result[key] = _data.get(key);
81
+ });
82
+ }
83
+
84
+ return result;
85
+ }
@@ -0,0 +1,23 @@
1
+ import { STYLES, decode, encode } from "./bytewords.js";
2
+ import { EncodingMethodName } from "../enums/EncodingMethodName.js";
3
+ import { IEncodingMethod } from "../interfaces/IEncodingMethod.js";
4
+
5
+ export class BytewordEncoding implements IEncodingMethod<string, string> {
6
+ private _name: EncodingMethodName = EncodingMethodName.bytewords;
7
+
8
+ readonly _style: STYLES;
9
+
10
+ get name(): EncodingMethodName {
11
+ return this._name;
12
+ }
13
+
14
+ constructor(style: STYLES = STYLES.MINIMAL) {
15
+ this._style = style;
16
+ }
17
+ encode(payload: string): string {
18
+ return encode(payload, this._style);
19
+ }
20
+ decode(payload: string): string {
21
+ return decode(payload, this._style);
22
+ }
23
+ }