spasm.js 1.0.2 → 2.0.0-alpha

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 (123) hide show
  1. package/README.md +454 -113
  2. package/lib.commonjs/convert/convertToEventForSpasmid.d.ts +4 -0
  3. package/lib.commonjs/convert/convertToEventForSpasmid.d.ts.map +1 -0
  4. package/lib.commonjs/convert/convertToEventForSpasmid.js +171 -0
  5. package/lib.commonjs/convert/convertToEventForSpasmid.js.map +1 -0
  6. package/lib.commonjs/convert/convertToSpasm.d.ts +17 -15
  7. package/lib.commonjs/convert/convertToSpasm.d.ts.map +1 -1
  8. package/lib.commonjs/convert/convertToSpasm.js +772 -265
  9. package/lib.commonjs/convert/convertToSpasm.js.map +1 -1
  10. package/lib.commonjs/convert/convertToSpasmEventDatabase.d.ts +4 -0
  11. package/lib.commonjs/convert/convertToSpasmEventDatabase.d.ts.map +1 -0
  12. package/lib.commonjs/convert/convertToSpasmEventDatabase.js +130 -0
  13. package/lib.commonjs/convert/convertToSpasmEventDatabase.js.map +1 -0
  14. package/lib.commonjs/convert/index.d.ts +2 -0
  15. package/lib.commonjs/convert/index.d.ts.map +1 -1
  16. package/lib.commonjs/convert/index.js +5 -1
  17. package/lib.commonjs/convert/index.js.map +1 -1
  18. package/lib.commonjs/id/getSpasmId.d.ts +4 -0
  19. package/lib.commonjs/id/getSpasmId.d.ts.map +1 -0
  20. package/lib.commonjs/id/getSpasmId.js +31 -0
  21. package/lib.commonjs/id/getSpasmId.js.map +1 -0
  22. package/lib.commonjs/id/index.d.ts +2 -0
  23. package/lib.commonjs/id/index.d.ts.map +1 -0
  24. package/lib.commonjs/id/index.js +6 -0
  25. package/lib.commonjs/id/index.js.map +1 -0
  26. package/lib.commonjs/identify/identifyEvent.d.ts.map +1 -1
  27. package/lib.commonjs/identify/identifyEvent.js +13 -5
  28. package/lib.commonjs/identify/identifyEvent.js.map +1 -1
  29. package/lib.commonjs/sort/index.d.ts +2 -0
  30. package/lib.commonjs/sort/index.d.ts.map +1 -0
  31. package/lib.commonjs/sort/index.js +6 -0
  32. package/lib.commonjs/sort/index.js.map +1 -0
  33. package/lib.commonjs/sort/sortEventForSpasmid.d.ts +3 -0
  34. package/lib.commonjs/sort/sortEventForSpasmid.d.ts.map +1 -0
  35. package/lib.commonjs/sort/sortEventForSpasmid.js +15 -0
  36. package/lib.commonjs/sort/sortEventForSpasmid.js.map +1 -0
  37. package/lib.commonjs/spasm.d.ts +5 -3
  38. package/lib.commonjs/spasm.d.ts.map +1 -1
  39. package/lib.commonjs/spasm.js +4 -5
  40. package/lib.commonjs/spasm.js.map +1 -1
  41. package/lib.commonjs/types/index.d.ts +1 -1
  42. package/lib.commonjs/types/index.d.ts.map +1 -1
  43. package/lib.commonjs/types/interfaces.d.ts +468 -90
  44. package/lib.commonjs/types/interfaces.d.ts.map +1 -1
  45. package/lib.commonjs/types/interfaces.js +2 -0
  46. package/lib.commonjs/types/interfaces.js.map +1 -1
  47. package/lib.commonjs/utils/nostrUtils.d.ts +10 -2
  48. package/lib.commonjs/utils/nostrUtils.d.ts.map +1 -1
  49. package/lib.commonjs/utils/nostrUtils.js +122 -22
  50. package/lib.commonjs/utils/nostrUtils.js.map +1 -1
  51. package/lib.commonjs/utils/utils.d.ts +26 -1
  52. package/lib.commonjs/utils/utils.d.ts.map +1 -1
  53. package/lib.commonjs/utils/utils.js +723 -3
  54. package/lib.commonjs/utils/utils.js.map +1 -1
  55. package/lib.esm/convert/convertToEventForSpasmid.d.ts +4 -0
  56. package/lib.esm/convert/convertToEventForSpasmid.d.ts.map +1 -0
  57. package/lib.esm/convert/convertToEventForSpasmid.js +168 -0
  58. package/lib.esm/convert/convertToEventForSpasmid.js.map +1 -0
  59. package/lib.esm/convert/convertToSpasm.d.ts +17 -15
  60. package/lib.esm/convert/convertToSpasm.d.ts.map +1 -1
  61. package/lib.esm/convert/convertToSpasm.js +759 -251
  62. package/lib.esm/convert/convertToSpasm.js.map +1 -1
  63. package/lib.esm/convert/convertToSpasmEventDatabase.d.ts +4 -0
  64. package/lib.esm/convert/convertToSpasmEventDatabase.d.ts.map +1 -0
  65. package/lib.esm/convert/convertToSpasmEventDatabase.js +137 -0
  66. package/lib.esm/convert/convertToSpasmEventDatabase.js.map +1 -0
  67. package/lib.esm/convert/index.d.ts +2 -0
  68. package/lib.esm/convert/index.d.ts.map +1 -1
  69. package/lib.esm/convert/index.js +2 -0
  70. package/lib.esm/convert/index.js.map +1 -1
  71. package/lib.esm/id/getSpasmId.d.ts +4 -0
  72. package/lib.esm/id/getSpasmId.d.ts.map +1 -0
  73. package/lib.esm/id/getSpasmId.js +31 -0
  74. package/lib.esm/id/getSpasmId.js.map +1 -0
  75. package/lib.esm/id/index.d.ts +2 -0
  76. package/lib.esm/id/index.d.ts.map +1 -0
  77. package/lib.esm/id/index.js +2 -0
  78. package/lib.esm/id/index.js.map +1 -0
  79. package/lib.esm/identify/identifyEvent.d.ts.map +1 -1
  80. package/lib.esm/identify/identifyEvent.js +13 -5
  81. package/lib.esm/identify/identifyEvent.js.map +1 -1
  82. package/lib.esm/sort/index.d.ts +2 -0
  83. package/lib.esm/sort/index.d.ts.map +1 -0
  84. package/lib.esm/sort/index.js +2 -0
  85. package/lib.esm/sort/index.js.map +1 -0
  86. package/lib.esm/sort/sortEventForSpasmid.d.ts +3 -0
  87. package/lib.esm/sort/sortEventForSpasmid.d.ts.map +1 -0
  88. package/lib.esm/sort/sortEventForSpasmid.js +17 -0
  89. package/lib.esm/sort/sortEventForSpasmid.js.map +1 -0
  90. package/lib.esm/spasm.d.ts +5 -3
  91. package/lib.esm/spasm.d.ts.map +1 -1
  92. package/lib.esm/spasm.js +4 -2
  93. package/lib.esm/spasm.js.map +1 -1
  94. package/lib.esm/types/index.d.ts +1 -1
  95. package/lib.esm/types/index.d.ts.map +1 -1
  96. package/lib.esm/types/interfaces.d.ts +468 -90
  97. package/lib.esm/types/interfaces.d.ts.map +1 -1
  98. package/lib.esm/types/interfaces.js +2 -0
  99. package/lib.esm/types/interfaces.js.map +1 -1
  100. package/lib.esm/utils/nostrUtils.d.ts +10 -2
  101. package/lib.esm/utils/nostrUtils.d.ts.map +1 -1
  102. package/lib.esm/utils/nostrUtils.js +119 -21
  103. package/lib.esm/utils/nostrUtils.js.map +1 -1
  104. package/lib.esm/utils/utils.d.ts +26 -1
  105. package/lib.esm/utils/utils.d.ts.map +1 -1
  106. package/lib.esm/utils/utils.js +701 -2
  107. package/lib.esm/utils/utils.js.map +1 -1
  108. package/package.json +9 -2
  109. package/src.ts/convert/convertToEventForSpasmid.ts +289 -0
  110. package/src.ts/convert/convertToSpasm.ts +962 -292
  111. package/src.ts/convert/convertToSpasmEventDatabase.ts +204 -0
  112. package/src.ts/convert/index.ts +2 -0
  113. package/src.ts/docs/architecture.md +413 -0
  114. package/src.ts/id/getSpasmId.ts +56 -0
  115. package/src.ts/id/index.ts +1 -0
  116. package/src.ts/identify/identifyEvent.ts +12 -5
  117. package/src.ts/sort/index.ts +1 -0
  118. package/src.ts/sort/sortEventForSpasmid.ts +30 -0
  119. package/src.ts/spasm.ts +5 -3
  120. package/src.ts/types/index.ts +1 -1
  121. package/src.ts/types/interfaces.ts +729 -125
  122. package/src.ts/utils/nostrUtils.ts +150 -22
  123. package/src.ts/utils/utils.ts +1005 -4
@@ -1,3 +1,10 @@
1
+ /*
2
+ * Using sha256 from 'js-sha256' npm package, because
3
+ * built-in 'crypto' module works only in a server-side
4
+ * Node.js environment, not on the client-side (browser).
5
+ */
6
+ import { sha256 } from "js-sha256";
7
+ import { ethers } from "ethers";
1
8
  // Filter out undefined, null, 0, '', false, NaN, {}, []
2
9
  // Keep {a: null}, {b: undefined}
3
10
  // Examples:
@@ -78,12 +85,17 @@ export const extractSealedEvent = (unknownPostOrEvent) => {
78
85
  if (!isObjectWithValues(unknownPostOrEvent))
79
86
  return false;
80
87
  let signedObject = false;
81
- if ('signed_message' in unknownPostOrEvent &&
88
+ if (unknownPostOrEvent &&
89
+ typeof (unknownPostOrEvent) === "object" &&
90
+ 'signed_message' in unknownPostOrEvent &&
82
91
  unknownPostOrEvent['signed_message'] &&
83
92
  typeof (unknownPostOrEvent['signed_message'] === "string")) {
84
93
  signedObject = JSON.parse(unknownPostOrEvent['signed_message']);
85
94
  }
86
- else if ('signedString' in unknownPostOrEvent &&
95
+ else if (unknownPostOrEvent &&
96
+ typeof (unknownPostOrEvent) === "object" &&
97
+ 'signedString' in unknownPostOrEvent &&
98
+ unknownPostOrEvent['signedString'] &&
87
99
  typeof (unknownPostOrEvent['signedString'] === "string")) {
88
100
  signedObject = JSON.parse(unknownPostOrEvent['signedString']);
89
101
  }
@@ -96,6 +108,12 @@ export const toBeTimestamp = (time) => {
96
108
  if (Number.isNaN(timestamp)) {
97
109
  return undefined;
98
110
  }
111
+ // Optional
112
+ // Standardize the timestamp to 10 characters (seconds)
113
+ // by rounding down the timestamp to the nearest second.
114
+ // if (timestamp.toString().length > 10) {
115
+ // timestamp = Math.floor(timestamp / 1000) * 1000;
116
+ // }
99
117
  return timestamp;
100
118
  };
101
119
  export const getNostrSpasmVersion = (event) => {
@@ -112,4 +130,685 @@ export const getNostrSpasmVersion = (event) => {
112
130
  }
113
131
  return nostrSpasmVersion;
114
132
  };
133
+ // Example usage
134
+ // getSchemeFromUrl('https://example.com/news') // return 'https'
135
+ // getSchemeFromUrl('http://example.com') // return 'http'
136
+ // getSchemeFromUrl('ftp://example.com') // return 'ftp'
137
+ // getSchemeFromUrl('mailto://...') // return 'mailto'
138
+ // getSchemeFromUrl('ipfs://123abc') // return 'ipfs'
139
+ // export const getSchemeFromUrl = (url: any) => {
140
+ // if (!url || typeof(url) !== "string") return ""
141
+ // try {
142
+ // const urlObject = new URL(url);
143
+ // return urlObject.protocol.slice(0, -1); // Remove the trailing colon
144
+ // } catch (error) {
145
+ // console.log('Invalid URL:', url);
146
+ // return "";
147
+ // }
148
+ // }
149
+ export const isValidUrl = (value) => {
150
+ if (!value)
151
+ return false;
152
+ try {
153
+ // new URL() constructor is less vulnerable to ReDoS attacks
154
+ // because it's a built-it JS function that doesn't use regex
155
+ new URL(value);
156
+ return true;
157
+ }
158
+ catch (e) {
159
+ return false;
160
+ }
161
+ };
162
+ export const createLinkObjectFromUrl = (url, key) => {
163
+ if (!url || typeof (url) !== "string")
164
+ return null;
165
+ try {
166
+ const urlObject = new URL(url);
167
+ const linkObject = {
168
+ value: url,
169
+ // protocol: urlObject.protocol.slice(0, -1),
170
+ // host: urlObject.host,
171
+ // path: urlObject.pathname,
172
+ // search: urlObject.search,
173
+ };
174
+ if (urlObject.protocol) {
175
+ linkObject.protocol = urlObject.protocol.slice(0, -1);
176
+ }
177
+ if (urlObject.origin) {
178
+ linkObject.origin = urlObject.origin;
179
+ }
180
+ if (urlObject.host) {
181
+ linkObject.host = urlObject.host;
182
+ }
183
+ if (urlObject.pathname &&
184
+ typeof (urlObject.pathname) === "string" // &&
185
+ // urlObject.pathname.length > 1
186
+ ) {
187
+ linkObject.pathname = urlObject.pathname;
188
+ }
189
+ if (urlObject.search &&
190
+ typeof (urlObject.search) === "string" // &&
191
+ // urlObject.search.length > 1
192
+ ) {
193
+ linkObject.search = urlObject.search;
194
+ }
195
+ if (urlObject.port) {
196
+ linkObject.port = urlObject.port;
197
+ }
198
+ if (urlObject.hash) {
199
+ linkObject.hash = urlObject.hash;
200
+ }
201
+ if (key &&
202
+ (typeof (key) === "string" || typeof (key) === "number")) {
203
+ linkObject.originalProtocolKey = key;
204
+ }
205
+ return linkObject;
206
+ }
207
+ catch (error) {
208
+ console.log('Invalid URL:', url);
209
+ return null;
210
+ }
211
+ };
212
+ export const getFormatFromValue = (value) => {
213
+ let format = undefined;
214
+ if (!value)
215
+ return format;
216
+ if (typeof (value) !== "string" && typeof (value) !== "number") {
217
+ return format;
218
+ }
219
+ if (typeof (value) === "number") {
220
+ return format = { name: "number" };
221
+ }
222
+ if (value && typeof (value) === "string") {
223
+ // Spasm ID
224
+ if (value.length === 64 + 9 && value.startsWith("spasmid")) {
225
+ const version = value.slice(7, 9);
226
+ format = { name: "spasmid", version: version };
227
+ return format;
228
+ }
229
+ // Dmp ID (signature)
230
+ if (value.length === 132 && value.startsWith("0x")) {
231
+ format = { name: "ethereum-sig" };
232
+ return format;
233
+ }
234
+ // Nostr ID
235
+ if (value.length === 63 && value.startsWith("note")) {
236
+ format = { name: "nostr-note" };
237
+ return format;
238
+ }
239
+ if (value.length === 68 && value.startsWith("nevent")) {
240
+ format = { name: "nostr-nevent" };
241
+ return format;
242
+ }
243
+ // Spasm signer
244
+ // if (address.length === 64 + 9 && address.startsWith("spasmer")) {
245
+ // const version = address.slice(7,9)
246
+ // format = { name: "spasmer", version: version }
247
+ // return format
248
+ // }
249
+ // Ethereum signer
250
+ if (value.length === 42 && value.startsWith("0x")) {
251
+ format = { name: "ethereum-pubkey" };
252
+ return format;
253
+ }
254
+ // Nostr signer
255
+ if (value.length === 63 && value.startsWith("npub")) {
256
+ format = { name: "nostr-npub" };
257
+ return format;
258
+ }
259
+ // url
260
+ if (isValidUrl(value)) {
261
+ format = { name: "url" };
262
+ return format;
263
+ }
264
+ if (value.length === 64 &&
265
+ !value.startsWith("note") &&
266
+ !value.startsWith("nevent") &&
267
+ !value.startsWith("npub")) {
268
+ format = { name: "nostr-hex" };
269
+ return format;
270
+ }
271
+ }
272
+ if (typeof (value) === "string") {
273
+ return format = { name: "string" };
274
+ }
275
+ return format;
276
+ };
277
+ export const getFormatFromId = (id) => {
278
+ return getFormatFromValue(id);
279
+ };
280
+ export const getFormatFromAddress = (address) => {
281
+ return getFormatFromValue(address);
282
+ };
283
+ export const getFormatFromSignature = (address) => {
284
+ return getFormatFromValue(address);
285
+ };
286
+ export const getHashOfString = (string, algorithm = "sha256") => {
287
+ if (typeof (string) !== "string")
288
+ return "";
289
+ if (algorithm === "sha256") {
290
+ return sha256(string);
291
+ }
292
+ return "";
293
+ };
294
+ // Keep only specified keys in an object.
295
+ export const keepTheseKeysInObject = (obj, keys) => {
296
+ return keys.reduce((acc, key) => {
297
+ if (obj.hasOwnProperty(key)) {
298
+ acc[key] = obj[key];
299
+ }
300
+ return acc;
301
+ }, {});
302
+ };
303
+ // Keep only specified keys in each object of an array.
304
+ export const keepTheseKeysInObjectsInArray = (array, keys) => {
305
+ return array.map(obj => keepTheseKeysInObject(obj, keys));
306
+ };
307
+ // This function only sorts string and number values.
308
+ export const sortArrayOfStringsAndNumbers = (array) => {
309
+ // Separate values into valid and invalid categories.
310
+ const { validValues, invalidValues } = array.reduce((acc, value) => {
311
+ if (typeof value === 'string' ||
312
+ typeof value === 'number') {
313
+ acc.validValues.push(value);
314
+ }
315
+ else {
316
+ acc.invalidValues.push(value);
317
+ }
318
+ return acc;
319
+ }, { validValues: [], invalidValues: [] });
320
+ // Sort the valid values
321
+ const sortedValidValues = validValues.sort((a, b) => String(a).localeCompare(String(b)));
322
+ // Combine sorted valid values with invalid values
323
+ const result = [...sortedValidValues, ...invalidValues];
324
+ return result;
325
+ };
326
+ export const sortArrayOfObjects = (objects, sortBy = ["id"]) => {
327
+ if (!objects ||
328
+ !Array.isArray(objects) ||
329
+ !objects[0]) {
330
+ return [];
331
+ }
332
+ // Ensure sortBy is always treated as an array
333
+ const sortedBy = Array.isArray(sortBy) ? sortBy : [sortBy];
334
+ // Separate objects into valid and invalid categories based
335
+ // on the existence of the specified property(ies)
336
+ const { validObjects, invalidValues } = objects.reduce((acc, item) => {
337
+ let isValid = false;
338
+ // Only one prop should exist in item in order
339
+ // to make it a valid item.
340
+ sortedBy.forEach((key) => {
341
+ if (typeof (item) === 'object' && item &&
342
+ key in item && item[key] &&
343
+ (typeof (item[key]) === "string" ||
344
+ typeof (item[key]) === "number")) {
345
+ isValid = true;
346
+ }
347
+ });
348
+ if (isValid) {
349
+ acc.validObjects.push(item);
350
+ }
351
+ else {
352
+ acc.invalidValues.push(item);
353
+ }
354
+ return acc;
355
+ }, { validObjects: [], invalidValues: [] });
356
+ // Sort the valid objects by the specified property(ies)
357
+ const sortedValidObjects = validObjects.sort((a, b) => {
358
+ for (const key of sortedBy) {
359
+ const aValue = typeof a[key] === 'string' ? a[key] : String(a[key]);
360
+ const bValue = typeof b[key] === 'string' ? b[key] : String(b[key]);
361
+ const compareResult = aValue.localeCompare(bValue);
362
+ if (compareResult !== 0) {
363
+ return compareResult;
364
+ }
365
+ }
366
+ return 0; // Equal
367
+ });
368
+ const sortedInvalidValues = sortArrayOfStringsAndNumbers(invalidValues);
369
+ // Combine sorted valid objects with invalid objects
370
+ const result = [...sortedValidObjects, ...sortedInvalidValues];
371
+ return result;
372
+ };
373
+ export const sortAuthorsForSpasmEventV2 = (authors) => {
374
+ // Clean and sort addresses
375
+ authors.forEach(author => {
376
+ if (author && typeof (author) === "object" &&
377
+ 'addresses' in author && author.addresses &&
378
+ Array.isArray(author.addresses) &&
379
+ author.addresses[0]) {
380
+ // Clean addresses to keep only 'value' and 'format' keys
381
+ // and remove 'verified' and 'hosts' keys.
382
+ author.addresses = keepTheseKeysInObjectsInArray(author.addresses, ["value", "format"]);
383
+ // Sort addresses
384
+ author.addresses = sortArrayOfObjects(author.addresses, "value");
385
+ }
386
+ });
387
+ // Clean and sort usernames
388
+ authors.forEach(author => {
389
+ if (author && typeof (author) === "object" &&
390
+ 'usernames' in author && author.usernames &&
391
+ Array.isArray(author.usernames) &&
392
+ author.usernames[0]) {
393
+ // There is no need to clean usernames because all fields
394
+ // should be calculated for the Spasm ID 01.
395
+ // Sort usernames
396
+ author.usernames = sortArrayOfObjects(author.usernames, "value");
397
+ }
398
+ });
399
+ let authorsWithAddress = [];
400
+ // Authors without address are used temporary until we split
401
+ // them further depending on whether they have usernames.
402
+ let authorsWithoutAddress = [];
403
+ let authorsWithoutAddressWithUsername = [];
404
+ let authorsWithoutAddressWithoutUsername = [];
405
+ authors.forEach(author => {
406
+ if (author && typeof (author) === "object" &&
407
+ 'addresses' in author && author.addresses &&
408
+ Array.isArray(author.addresses) && author.addresses[0] &&
409
+ author.addresses[0].value &&
410
+ (typeof (author.addresses[0].value) === "string" ||
411
+ typeof (author.addresses[0].value) === "number")) {
412
+ authorsWithAddress.push(author);
413
+ }
414
+ else {
415
+ authorsWithoutAddress.push(author);
416
+ }
417
+ });
418
+ authorsWithoutAddress.forEach(author => {
419
+ if (author && typeof (author) === "object" &&
420
+ 'usernames' in author && author.usernames &&
421
+ Array.isArray(author.usernames) && author.usernames[0] &&
422
+ author.usernames[0].value &&
423
+ (typeof (author.usernames[0].value) === "string" ||
424
+ typeof (author.usernames[0].value) === "number")) {
425
+ authorsWithoutAddressWithUsername.push(author);
426
+ }
427
+ else {
428
+ authorsWithoutAddressWithoutUsername.push(author);
429
+ }
430
+ });
431
+ // Sort all 3 arrays
432
+ const sortedAuthorsWithAddress = sortArrayOfObjectsByKeyValue(authorsWithAddress, "addresses");
433
+ const sortedAuthorsWithoutAddressWithUsername = sortArrayOfObjectsByKeyValue(authorsWithoutAddressWithUsername, "usernames");
434
+ const sortedAuthorsWithoutAddressWithoutUsername = sortArrayOfObjects(authorsWithoutAddressWithoutUsername, ["id"]);
435
+ const result = [
436
+ ...sortedAuthorsWithAddress,
437
+ ...sortedAuthorsWithoutAddressWithUsername,
438
+ ...sortedAuthorsWithoutAddressWithoutUsername
439
+ ];
440
+ return result;
441
+ };
442
+ export const sortAuthorsForSpasmid01 = sortAuthorsForSpasmEventV2;
443
+ export const sortArrayOfObjectsByKeyValue = (objects, key) => {
444
+ const sortedObjects = objects.sort((a, b) => {
445
+ let aValue = "";
446
+ let bValue = "";
447
+ if (a[key] && a[key][0] &&
448
+ a[key][0].value) {
449
+ if (typeof (a[key][0].value) === 'string') {
450
+ aValue = a[key][0].value;
451
+ }
452
+ else if (typeof (a[key][0].value) === 'number') {
453
+ aValue = String(a[key][0].value);
454
+ }
455
+ }
456
+ if (b[key] && b[key][0] &&
457
+ b[key][0].value) {
458
+ if (typeof (b[key][0].value) === 'string') {
459
+ bValue = b[key][0].value;
460
+ }
461
+ else if (typeof (b[key][0].value) === 'number') {
462
+ bValue = String(b[key][0].value);
463
+ }
464
+ }
465
+ const compareResult = aValue.localeCompare(bValue);
466
+ if (compareResult !== 0) {
467
+ return compareResult;
468
+ }
469
+ return 0; // Equal
470
+ });
471
+ return sortedObjects;
472
+ };
473
+ export const sortHostsForSpasmEventV2 = (hosts) => {
474
+ if (!hosts ||
475
+ !Array.isArray(hosts) ||
476
+ !hosts[0]) {
477
+ return hosts;
478
+ }
479
+ const sortedHosts = sortArrayOfObjects(hosts, "value");
480
+ return sortedHosts;
481
+ };
482
+ export const sortHostsForSpasmid01 = sortHostsForSpasmEventV2;
483
+ export const sortLinksForSpasmEventV2 = sortHostsForSpasmEventV2;
484
+ export const sortLinksForSpasmid01 = sortLinksForSpasmEventV2;
485
+ export const sortMediasForSpasmid01 = (medias) => {
486
+ if (!medias || !Array.isArray(medias))
487
+ return [];
488
+ // Clean and sort IDs
489
+ medias.forEach(media => {
490
+ if (media && typeof (media) === "object" &&
491
+ 'ids' in media && media.ids &&
492
+ Array.isArray(media.ids) &&
493
+ media.ids[0]) {
494
+ // Clean ids to keep only 'value' key
495
+ media.ids = keepTheseKeysInObjectsInArray(media.ids, ["value"]);
496
+ // Sort ids
497
+ media.ids = sortArrayOfObjects(media.ids, "value");
498
+ }
499
+ });
500
+ // Clean and sort hashes
501
+ medias.forEach(media => {
502
+ if (media && typeof (media) === "object" &&
503
+ 'hashes' in media && media.hashes &&
504
+ Array.isArray(media.hashes) &&
505
+ media.hashes[0]) {
506
+ // Clean hashes to keep only 'value' key
507
+ media.hashes = keepTheseKeysInObjectsInArray(media.hashes, ["value"]);
508
+ // Sort hashes
509
+ media.hashes = sortArrayOfObjects(media.hashes, "value");
510
+ }
511
+ });
512
+ // Clean and sort links
513
+ medias.forEach(media => {
514
+ if (media && typeof (media) === "object" &&
515
+ 'links' in media && media.links &&
516
+ Array.isArray(media.links) &&
517
+ media.links[0]) {
518
+ // Clean links to keep only 'value' key
519
+ media.links = keepTheseKeysInObjectsInArray(media.links, ["value"]);
520
+ // Sort links
521
+ media.links = sortArrayOfObjects(media.links, "value");
522
+ }
523
+ });
524
+ // mediasWithIds might also have hashes and links
525
+ let mediasWithIds = [];
526
+ let mediasWithoutIds = [];
527
+ // mediasWithHashes might also have links, but no ids
528
+ let mediasWithHashes = [];
529
+ let mediasWithoutIdsHashes = [];
530
+ // mediasWithLinks only has links, but no ids and hashes
531
+ let mediasWithLinks = [];
532
+ let mediasWithoutIdsHashesLinks = [];
533
+ // Sort medias by ids
534
+ medias.forEach(media => {
535
+ if (media && typeof (media) === "object" &&
536
+ 'ids' in media && media.ids &&
537
+ Array.isArray(media.ids) && media.ids[0] &&
538
+ media.ids[0].value &&
539
+ (typeof (media.ids[0].value) === "string" ||
540
+ typeof (media.ids[0].value) === "number")) {
541
+ mediasWithIds.push(media);
542
+ }
543
+ else {
544
+ mediasWithoutIds.push(media);
545
+ }
546
+ });
547
+ // Sort medias by hashes
548
+ mediasWithoutIds.forEach(media => {
549
+ if (media && typeof (media) === "object" &&
550
+ 'hashes' in media && media.hashes &&
551
+ Array.isArray(media.hashes) && media.hashes[0] &&
552
+ media.hashes[0].value &&
553
+ (typeof (media.hashes[0].value) === "string" ||
554
+ typeof (media.hashes[0].value) === "number")) {
555
+ mediasWithHashes.push(media);
556
+ }
557
+ else {
558
+ mediasWithoutIdsHashes.push(media);
559
+ }
560
+ });
561
+ // Sort medias by links
562
+ mediasWithoutIdsHashes.forEach(media => {
563
+ if (media && typeof (media) === "object" &&
564
+ 'links' in media && media.links &&
565
+ Array.isArray(media.links) && media.links[0] &&
566
+ media.links[0].value &&
567
+ (typeof (media.links[0].value) === "string" ||
568
+ typeof (media.links[0].value) === "number")) {
569
+ mediasWithLinks.push(media);
570
+ }
571
+ else {
572
+ mediasWithoutIdsHashesLinks.push(media);
573
+ }
574
+ });
575
+ const mediasOther = mediasWithoutIdsHashesLinks;
576
+ // Sort all 3 arrays
577
+ const sortedMediasWithIds = sortArrayOfObjectsByKeyValue(mediasWithIds, "ids");
578
+ const sortedMediasWithHashes = sortArrayOfObjectsByKeyValue(mediasWithHashes, "hashes");
579
+ const sortedMediasWithLinks = sortArrayOfObjectsByKeyValue(mediasWithLinks, "links");
580
+ const sortedMediasOther = sortArrayOfObjects(mediasOther, ["id"]);
581
+ const result = [
582
+ ...sortedMediasWithIds,
583
+ ...sortedMediasWithHashes,
584
+ ...sortedMediasWithLinks,
585
+ ...sortedMediasOther
586
+ ];
587
+ return result;
588
+ };
589
+ // Deprecated sortMediasForSpasmEventV2 because we only keep
590
+ // a 'value' key to calculate Spasm ID 01.
591
+ // export const sortMediasForSpasmid01 = sortMediasforSpasmEventV2
592
+ export const sortReferencesForSpasmid01 = (references) => {
593
+ if (!references || !Array.isArray(references))
594
+ return [];
595
+ // Clean and sort IDs
596
+ references.forEach(reference => {
597
+ if (reference && typeof (reference) === "object" &&
598
+ 'ids' in reference && reference.ids &&
599
+ Array.isArray(reference.ids) &&
600
+ reference.ids[0]) {
601
+ // Clean ids to keep only 'value' key
602
+ reference.ids = keepTheseKeysInObjectsInArray(reference.ids, ["value"]);
603
+ // Sort ids
604
+ reference.ids = sortArrayOfObjects(reference.ids, "value");
605
+ }
606
+ });
607
+ // Sort references based on IDs
608
+ const sortedReferences = sortArrayOfObjectsByKeyValue(references, "ids");
609
+ return sortedReferences;
610
+ };
611
+ export const sortParentForSpasmid01 = (parent) => {
612
+ if (!parent || typeof (parent) !== "object")
613
+ return parent;
614
+ // Clean and sort IDs
615
+ if (parent && typeof (parent) === "object" &&
616
+ 'ids' in parent && parent.ids &&
617
+ Array.isArray(parent.ids) &&
618
+ parent.ids[0]) {
619
+ // Clean ids to keep only 'value' key
620
+ parent.ids = keepTheseKeysInObjectsInArray(parent.ids, ["value"]);
621
+ // Sort ids
622
+ parent.ids = sortArrayOfObjects(parent.ids, "value");
623
+ }
624
+ return parent;
625
+ };
626
+ export const sortTagsForSpasmid01 = (tags) => {
627
+ if (!tags || !Array.isArray(tags))
628
+ return [[]];
629
+ /**
630
+ * Tags are an array of arrays (e.g., Nostr tags).
631
+ * Each tag is an array with any number of elements.
632
+ * Some tags will have the same one-letter first element,
633
+ * so sorting by the first element is not a good approach.
634
+ * Instead, the current sorting logic for spasmid01 is
635
+ * to find the length of the longest tag array (e.g., 10),
636
+ * and start sorting tags by the 10th element, then
637
+ * by the 9th element, and continue until sorting is
638
+ * done by the first element.
639
+ *
640
+ * Each tag is an array of values. However, values inside
641
+ * each tag should not be sorted as it can affect the
642
+ * intention of the event. For example, the order of an
643
+ * element in a Nostr tag array has a meaning.
644
+ */
645
+ const sortTagsByElementNumber = (elementNumber = 0) => {
646
+ tags = tags.sort((a, b) => {
647
+ const key = elementNumber;
648
+ let aValue = "";
649
+ let bValue = "";
650
+ if (a[key]) {
651
+ if (typeof (a[key]) === 'string') {
652
+ aValue = a[key];
653
+ }
654
+ else if (typeof (a[key]) === 'number') {
655
+ aValue = String(a[key]);
656
+ }
657
+ }
658
+ if (b[key]) {
659
+ if (typeof (b[key]) === 'string') {
660
+ bValue = b[key];
661
+ }
662
+ else if (typeof (b[key]) === 'number') {
663
+ bValue = String(b[key]);
664
+ }
665
+ }
666
+ const compareResult = aValue.localeCompare(bValue);
667
+ if (compareResult !== 0) {
668
+ return compareResult;
669
+ }
670
+ return 0; // Equal
671
+ });
672
+ };
673
+ let longestTagArrayLength = 1;
674
+ // Find the longest array (tag) to be used for sorting.
675
+ tags.forEach(tag => {
676
+ if (tag && Array.isArray(tag) &&
677
+ tag.length > longestTagArrayLength) {
678
+ longestTagArrayLength = tag.length;
679
+ }
680
+ });
681
+ for (let i = longestTagArrayLength; i >= 0; i--) {
682
+ sortTagsByElementNumber(i);
683
+ }
684
+ return tags;
685
+ };
686
+ export const markSpasmEventAddressAsVerified = (spasmEvent, verifiedAddress, version = "2.0.0") => {
687
+ if (version === "2.0.0") {
688
+ if (spasmEvent.authors) {
689
+ spasmEvent.authors.forEach(author => {
690
+ if (author.addresses) {
691
+ author.addresses.forEach(address => {
692
+ if (address.value === verifiedAddress) {
693
+ address.verified = true;
694
+ }
695
+ });
696
+ }
697
+ });
698
+ }
699
+ }
700
+ };
701
+ export const verifyEthereumSignature = (messageString, signature, signerAddress) => {
702
+ try {
703
+ if (signature && typeof (signature) === 'string') {
704
+ const recoveredAddress = ethers.verifyMessage(messageString, signature);
705
+ return recoveredAddress.toLowerCase() ===
706
+ signerAddress.toLowerCase();
707
+ }
708
+ return false;
709
+ }
710
+ catch (error) {
711
+ return false;
712
+ }
713
+ };
714
+ export const utilsStatus = () => {
715
+ console.log("spasm.js utils status: success");
716
+ };
717
+ // var sha256pure = function sha256(ascii) {
718
+ // function rightRotate(value, amount) {
719
+ // return (value>>>amount) | (value<<(32 - amount));
720
+ // };
721
+ //
722
+ // var mathPow = Math.pow;
723
+ // var maxWord = mathPow(2, 32);
724
+ // var lengthProperty = 'length'
725
+ // var i, j; // Used as a counter across the whole file
726
+ // var result = ''
727
+ //
728
+ // var words = [];
729
+ // var asciiBitLength = ascii[lengthProperty]*8;
730
+ //
731
+ // /[> caching results is optional - remove/add slash from front of this line to toggle
732
+ // // Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes
733
+ // // (we actually calculate the first 64, but extra values are just ignored)
734
+ // var hash = sha256.h = sha256.h || [];
735
+ // // Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes
736
+ // var k = sha256.k = sha256.k || [];
737
+ // var primeCounter = k[lengthProperty];
738
+ // [>/
739
+ // var hash = [], k = [];
740
+ // var primeCounter = 0;
741
+ // /[>/
742
+ //
743
+ // var isComposite = {};
744
+ // for (var candidate = 2; primeCounter < 64; candidate++) {
745
+ // if (!isComposite[candidate]) {
746
+ // for (i = 0; i < 313; i += candidate) {
747
+ // isComposite[i] = candidate;
748
+ // }
749
+ // hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0;
750
+ // k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0;
751
+ // }
752
+ // }
753
+ //
754
+ // ascii += '\x80' // Append Ƈ' bit (plus zero padding)
755
+ // while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding
756
+ // for (i = 0; i < ascii[lengthProperty]; i++) {
757
+ // j = ascii.charCodeAt(i);
758
+ // if (j>>8) return; // ASCII check: only accept characters in range 0-255
759
+ // words[i>>2] |= j << ((3 - i)%4)*8;
760
+ // }
761
+ // words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0);
762
+ // words[words[lengthProperty]] = (asciiBitLength)
763
+ //
764
+ // // process each chunk
765
+ // for (j = 0; j < words[lengthProperty];) {
766
+ // var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration
767
+ // var oldHash = hash;
768
+ // // This is now the undefinedworking hash", often labelled as variables a...g
769
+ // // (we have to truncate as well, otherwise extra entries at the end accumulate
770
+ // hash = hash.slice(0, 8);
771
+ //
772
+ // for (i = 0; i < 64; i++) {
773
+ // // Typescript error: i2 value is never read
774
+ // var i2 = i + j;
775
+ // // Expand the message into 64 words
776
+ // // Used below if
777
+ // var w15 = w[i - 15], w2 = w[i - 2];
778
+ //
779
+ // // Iterate
780
+ // var a = hash[0], e = hash[4];
781
+ // var temp1 = hash[7]
782
+ // + (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1
783
+ // + ((e&hash[5])^((~e)&hash[6])) // ch
784
+ // + k[i]
785
+ // // Expand the message schedule if needed
786
+ // + (w[i] = (i < 16) ? w[i] : (
787
+ // w[i - 16]
788
+ // + (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0
789
+ // + w[i - 7]
790
+ // + (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1
791
+ // )|0
792
+ // );
793
+ // // This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
794
+ // var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0
795
+ // + ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj
796
+ //
797
+ // hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice()
798
+ // hash[4] = (hash[4] + temp1)|0;
799
+ // }
800
+ //
801
+ // for (i = 0; i < 8; i++) {
802
+ // hash[i] = (hash[i] + oldHash[i])|0;
803
+ // }
804
+ // }
805
+ //
806
+ // for (i = 0; i < 8; i++) {
807
+ // for (j = 3; j + 1; j--) {
808
+ // var b = (hash[i]>>(j*8))&255;
809
+ // result += ((b < 16) ? 0 : '') + b.toString(16);
810
+ // }
811
+ // }
812
+ // return result;
813
+ // };
115
814
  //# sourceMappingURL=utils.js.map