spasm.js 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,812 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertToSpasm = exports.standartizePostWithNostrSpasmEventSignedOpened = exports.standartizePostWithDmpEventSignedClosed = exports.standartizeNostrSpasmEventSignedOpened = exports.standartizeDmpEventSignedClosed = exports.standartizePostOrEvent = exports.identifyEventInsidePost = exports.isPostWithNostrEvent = exports.isPostWithNostrSpasmEvent = exports.isPostWithNostrEventSignedOpened = exports.isPostWithNostrSpasmEventSignedOpened = exports.isPostWithDmpEventSignedClosed = exports.isPostWithDmpEventSignedOpened = exports.isPostWithDmpEvent = exports.isDmpEventSignedOpened = exports.isDmpEventSignedClosed = exports.isDmpEvent = exports.isNostrSpasmEventSignedOpened = exports.isNostrSpasmEvent = exports.isNostrEventSignedOpened = exports.isNostrEvent = exports.hasExtraSpasmFields = exports.identifyPrivateKey = exports.extractSealedEvent = exports.identifyLicenseInsideTags = exports.identifyLicense = exports.hasSignature = exports.identifyEvent = exports.isWeb3Post = exports.isWeb2Post = exports.identifyPostOrEvent = void 0;
4
+ const utils_1 = require("./utils");
5
+ // web2 post example
6
+ // webType: "web2",
7
+ // eventIsSealed: false,
8
+ // eventIsSealedUnderKeyName: false,
9
+ // eventInfo: false
10
+ // web3 post example
11
+ // webType: "web3",
12
+ // eventIsSealed: true,
13
+ // eventIsSealedUnderKeyName: "signed_message",
14
+ // eventInfo: {
15
+ // type: "DmpEventSignedClosed",
16
+ // hasSignature: true
17
+ // baseProtocol: "dmp",
18
+ // privateKey: "ethereum",
19
+ // isSpasmCompatible: true,
20
+ // hasExtraSpasmFields: false,
21
+ // }
22
+ // web3 event example
23
+ // webType: "web3",
24
+ // eventIsSealed: false,
25
+ // eventIsSealedUnderKeyName: false,
26
+ // eventInfo: {
27
+ // type: "NostrSpasmEventSignedOpened",
28
+ // hasSignature: true
29
+ // baseProtocol: "nostr",
30
+ // privateKey: "nostr",
31
+ // isSpasmCompatible: true,
32
+ // hasExtraSpasmFields: true,
33
+ // }
34
+ /**
35
+ There are usually 3 types of objects passed to this function:
36
+ - web3 post - an object is a post with a web3 event sealed inside
37
+ - web3 event - an object itself is a web3 event
38
+ - web2 post - an object is a post without a web3 event (e.g. RSS)
39
+ */
40
+ const identifyPostOrEvent = (unknownPostOrEvent) => {
41
+ const info = {
42
+ webType: false,
43
+ eventIsSealed: false,
44
+ eventIsSealedUnderKeyName: false,
45
+ eventInfo: {
46
+ type: false,
47
+ hasSignature: false,
48
+ baseProtocol: false,
49
+ privateKey: false,
50
+ isSpasmCompatible: false,
51
+ hasExtraSpasmFields: false,
52
+ license: false
53
+ }
54
+ };
55
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
56
+ return info;
57
+ let unknownEventOrWeb2Post;
58
+ // Option 1.
59
+ // If an object is a post with a sealed event (signed string),
60
+ // we need to extract the event by parsing the signed string.
61
+ // We then check if that extracted object is a valid web3 event.
62
+ if ((0, exports.isWeb3Post)(unknownPostOrEvent)) {
63
+ info.webType = "web3";
64
+ info.eventIsSealed = true;
65
+ if ('signed_message' in unknownPostOrEvent &&
66
+ typeof (unknownPostOrEvent.signed_message) === "string") {
67
+ const signedObject = JSON.parse(unknownPostOrEvent.signed_message);
68
+ info.eventIsSealedUnderKeyName = 'signed_message';
69
+ unknownEventOrWeb2Post = signedObject;
70
+ // Edge cases.
71
+ // Signed DMP event sealed in the Post under the key
72
+ // 'signed_message' doesn't have signature inside the signed
73
+ // string, so we cannot just parse the string to extract the
74
+ // object and then pass it into an identify function.
75
+ // Instead, we have to attach a signer and signature to the
76
+ // signed string, which is a type of DmpEventSignedClosed.
77
+ if ((0, exports.isDmpEvent)(unknownEventOrWeb2Post) &&
78
+ 'signer' in unknownPostOrEvent &&
79
+ typeof (unknownPostOrEvent.signer) === "string" &&
80
+ 'signature' in unknownPostOrEvent &&
81
+ typeof (unknownPostOrEvent.signature) === "string") {
82
+ // Recreating DmpEventSignedClosed
83
+ unknownEventOrWeb2Post = {
84
+ signer: unknownPostOrEvent.signer,
85
+ signedString: unknownPostOrEvent.signed_message,
86
+ signature: unknownPostOrEvent.signature
87
+ };
88
+ }
89
+ }
90
+ // Option 2.
91
+ // If an object doesn't have a sealed event (signed string), then
92
+ // - either the object itself is a web3 event,
93
+ // - or the object is a web2 post (e.g., from an RSS feed).
94
+ }
95
+ else {
96
+ info.webType = false;
97
+ info.eventIsSealed = false;
98
+ info.eventIsSealedUnderKeyName = false;
99
+ unknownEventOrWeb2Post = unknownPostOrEvent;
100
+ }
101
+ const eventInfo = (0, exports.identifyEvent)(unknownEventOrWeb2Post);
102
+ // An object has been identified as a web3 event.
103
+ if (eventInfo?.type && typeof (eventInfo.type) === "string") {
104
+ // web3 post and web3 event
105
+ info.webType = "web3";
106
+ info.eventInfo = eventInfo;
107
+ return info;
108
+ // An object has not been identified as a web3 event.
109
+ }
110
+ else {
111
+ // web2 post
112
+ info.webType = "web2";
113
+ info.eventInfo = false;
114
+ return info;
115
+ }
116
+ };
117
+ exports.identifyPostOrEvent = identifyPostOrEvent;
118
+ const isWeb2Post = (unknownPostOrEvent) => {
119
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
120
+ return false;
121
+ if (
122
+ // signatures
123
+ 'sig' in unknownPostOrEvent ||
124
+ 'signature' in unknownPostOrEvent ||
125
+ 'signed_message' in unknownPostOrEvent ||
126
+ 'signedObject' in unknownPostOrEvent ||
127
+ 'signedString' in unknownPostOrEvent) {
128
+ return false;
129
+ }
130
+ if ((0, exports.isNostrEvent)(unknownPostOrEvent))
131
+ return false;
132
+ if ((0, exports.isDmpEvent)(unknownPostOrEvent))
133
+ return false;
134
+ if ((0, exports.isDmpEventSignedClosed)(unknownPostOrEvent))
135
+ return false;
136
+ if ((0, exports.isDmpEventSignedOpened)(unknownPostOrEvent))
137
+ return false;
138
+ return false;
139
+ };
140
+ exports.isWeb2Post = isWeb2Post;
141
+ const isWeb3Post = (unknownPostOrEvent) => {
142
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
143
+ return false;
144
+ if ('signed_message' in unknownPostOrEvent &&
145
+ typeof (unknownPostOrEvent.signed_message) === "string") {
146
+ return true;
147
+ }
148
+ return false;
149
+ };
150
+ exports.isWeb3Post = isWeb3Post;
151
+ const identifyEvent = (unknownPostOrEvent) => {
152
+ console.log("identifyEvent called");
153
+ const eventInfo = {
154
+ type: "unknown",
155
+ hasSignature: false,
156
+ baseProtocol: false,
157
+ privateKey: false,
158
+ isSpasmCompatible: false,
159
+ hasExtraSpasmFields: false,
160
+ license: false
161
+ };
162
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
163
+ return eventInfo;
164
+ eventInfo.license = (0, exports.identifyLicense)(unknownPostOrEvent);
165
+ // TODO: refactor
166
+ // verifySignature()
167
+ // add key 'isSignatureValid' to EventInfo
168
+ // check privateKey after discovering eventInfo.type
169
+ if ((0, exports.hasSignature)(unknownPostOrEvent))
170
+ eventInfo.hasSignature = true;
171
+ // Another approach is to set a private key after identifying
172
+ // the event type (eventInfo.type).
173
+ if (eventInfo.hasSignature) {
174
+ eventInfo.privateKey = (0, exports.identifyPrivateKey)(unknownPostOrEvent);
175
+ }
176
+ if ((0, exports.hasExtraSpasmFields)(unknownPostOrEvent))
177
+ eventInfo.hasExtraSpasmFields = true;
178
+ // DMP
179
+ // Might be DMP or Nostr or web2 Post
180
+ if (!eventInfo.hasSignature && !eventInfo.hasExtraSpasmFields) {
181
+ if ((0, exports.isDmpEvent)(unknownPostOrEvent)) {
182
+ eventInfo.type = "DmpEvent";
183
+ eventInfo.baseProtocol = "dmp";
184
+ eventInfo.isSpasmCompatible = true;
185
+ return eventInfo;
186
+ }
187
+ // Might be DMP or Nostr with signature or Post with signature
188
+ }
189
+ else if (eventInfo.hasSignature && !eventInfo.hasExtraSpasmFields) {
190
+ if ((0, exports.isDmpEventSignedOpened)(unknownPostOrEvent)) {
191
+ eventInfo.type = "DmpEventSignedOpened";
192
+ eventInfo.baseProtocol = "dmp";
193
+ eventInfo.isSpasmCompatible = true;
194
+ return eventInfo;
195
+ }
196
+ else if ((0, exports.isDmpEventSignedClosed)(unknownPostOrEvent)) {
197
+ eventInfo.type = "DmpEventSignedClosed";
198
+ eventInfo.baseProtocol = "dmp";
199
+ eventInfo.isSpasmCompatible = true;
200
+ return eventInfo;
201
+ }
202
+ }
203
+ // Nostr
204
+ if (eventInfo.hasSignature && eventInfo.hasExtraSpasmFields) {
205
+ // Looks like Nostr event with signature and SPASM fields
206
+ if ((0, exports.isNostrSpasmEventSignedOpened)(unknownPostOrEvent)) {
207
+ eventInfo.type = "NostrSpasmEventSignedOpened";
208
+ eventInfo.baseProtocol = "nostr";
209
+ eventInfo.isSpasmCompatible = true;
210
+ return eventInfo;
211
+ }
212
+ }
213
+ else if (eventInfo.hasSignature && !eventInfo.hasExtraSpasmFields) {
214
+ // Looks like Nostr event with signature without SPASM fields
215
+ if ((0, exports.isNostrEventSignedOpened)(unknownPostOrEvent)) {
216
+ eventInfo.type = "NostrEventSignedOpened";
217
+ eventInfo.baseProtocol = "nostr";
218
+ eventInfo.isSpasmCompatible = false;
219
+ return eventInfo;
220
+ }
221
+ }
222
+ else if (!eventInfo.hasSignature && eventInfo.hasExtraSpasmFields) {
223
+ // Looks like Nostr event without signature, but with SPASM fields
224
+ if ((0, exports.isNostrSpasmEvent)(unknownPostOrEvent)) {
225
+ eventInfo.type = "NostrSpasmEvent";
226
+ eventInfo.baseProtocol = "nostr";
227
+ eventInfo.isSpasmCompatible = true;
228
+ return eventInfo;
229
+ }
230
+ }
231
+ else if (!eventInfo.hasSignature && !eventInfo.hasExtraSpasmFields) {
232
+ // Looks like Nostr event without signature and without SPASM fields
233
+ if ((0, exports.isNostrEvent)(unknownPostOrEvent)) {
234
+ eventInfo.type = "NostrEvent";
235
+ eventInfo.baseProtocol = "nostr";
236
+ eventInfo.isSpasmCompatible = false;
237
+ return eventInfo;
238
+ }
239
+ }
240
+ };
241
+ exports.identifyEvent = identifyEvent;
242
+ const hasSignature = (unknownPostOrEvent, signatureKey, signatureLength = 40) => {
243
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
244
+ return false;
245
+ let keys = signatureKey
246
+ ? [signatureKey] // signature key is provided
247
+ : ['signature', 'sig']; // check all signature keys
248
+ for (let key of keys) {
249
+ if (key in unknownPostOrEvent &&
250
+ typeof (unknownPostOrEvent[key]) === 'string' &&
251
+ unknownPostOrEvent[key].length > signatureLength) {
252
+ return true;
253
+ }
254
+ }
255
+ return false;
256
+ };
257
+ exports.hasSignature = hasSignature;
258
+ const identifyLicense = (unknownPostOrEvent) => {
259
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
260
+ return false;
261
+ let license = false;
262
+ // Option 1.
263
+ // A license can be inside a 'license' key
264
+ // or inside tags (e.g. SPASM tags in Nostr events).
265
+ if ('license' in unknownPostOrEvent &&
266
+ typeof (unknownPostOrEvent['license']) === 'string' &&
267
+ unknownPostOrEvent['license'].length > 0) {
268
+ license = unknownPostOrEvent['license'];
269
+ if (license)
270
+ return license;
271
+ }
272
+ license = (0, exports.identifyLicenseInsideTags)(unknownPostOrEvent);
273
+ if (license)
274
+ return license;
275
+ // Option 2.
276
+ // If no license was found, then we should try to extract
277
+ // an object (event) from a signed string if such a string
278
+ // exists, and then check that object for a license.
279
+ const signedObject = (0, exports.extractSealedEvent)(unknownPostOrEvent);
280
+ if (!signedObject)
281
+ return false;
282
+ if (!(0, utils_1.isObjectWithValues)(signedObject))
283
+ return false;
284
+ if ('license' in signedObject &&
285
+ typeof (signedObject['license']) === 'string' &&
286
+ signedObject['license'].length > 0) {
287
+ license = signedObject['license'];
288
+ if (license)
289
+ return license;
290
+ }
291
+ license = (0, exports.identifyLicenseInsideTags)(signedObject);
292
+ if (license)
293
+ return license;
294
+ return false;
295
+ };
296
+ exports.identifyLicense = identifyLicense;
297
+ const identifyLicenseInsideTags = (unknownPostOrEvent) => {
298
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
299
+ return false;
300
+ let license = false;
301
+ // A license can be placed inside SPASM tags
302
+ if ('tags' in unknownPostOrEvent &&
303
+ Array.isArray(unknownPostOrEvent.tags)) {
304
+ // Nostr events have tags of array type: NostrSpasmTag | AnyTag
305
+ // Post events have tags of string type: string
306
+ unknownPostOrEvent.tags.forEach(function (tag) {
307
+ if (Array.isArray(tag)) {
308
+ if (tag[0] === "license" &&
309
+ typeof (tag[1]) === "string") {
310
+ license = tag[1];
311
+ }
312
+ }
313
+ });
314
+ }
315
+ return license;
316
+ };
317
+ exports.identifyLicenseInsideTags = identifyLicenseInsideTags;
318
+ const extractSealedEvent = (unknownPostOrEvent) => {
319
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
320
+ return false;
321
+ let signedObject = false;
322
+ if ('signed_message' in unknownPostOrEvent &&
323
+ typeof (unknownPostOrEvent['signed_message'] === "string")) {
324
+ signedObject = JSON.parse(unknownPostOrEvent['signed_message']);
325
+ }
326
+ else if ('signedString' in unknownPostOrEvent &&
327
+ typeof (unknownPostOrEvent['signedString'] === "string")) {
328
+ signedObject = JSON.parse(unknownPostOrEvent['signedString']);
329
+ }
330
+ return signedObject;
331
+ };
332
+ exports.extractSealedEvent = extractSealedEvent;
333
+ const identifyPrivateKey = (unknownPostOrEvent) => {
334
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
335
+ return false;
336
+ /**
337
+ * If an object has 'sig' key, then it's most likely a Nostr event.
338
+ * Currently, all Nostr events can only be signed with a Nostr private
339
+ * key, so we can assume that a private key is "nostr".
340
+ * In the future there might be an option to sign Nostr events with
341
+ * different keys, so we will have to update the logic.
342
+ */
343
+ if ('sig' in unknownPostOrEvent &&
344
+ typeof (unknownPostOrEvent['sig']) === 'string' &&
345
+ unknownPostOrEvent['sig'].length > 40) {
346
+ return 'nostr';
347
+ }
348
+ /**
349
+ * A post with a sealed event (hidden inside the signed string)
350
+ * under the key like 'signed_message' or 'signedObject' will
351
+ * usually have a signature inside a 'signature' key.
352
+ *
353
+ * So if an object has a 'signature' key, then it can be:
354
+ * - a DMP event signed with the Ethereum private key,
355
+ * - has 'signature'
356
+ * - a Post with a sealed DMP event signed with an Ethereum private key,
357
+ * - has 'signature'
358
+ * - a Post with a sealed Nostr event signed with a Nostr private key.
359
+ * - has 'signature'
360
+ * - and also has 'sig' inside 'signed_message'
361
+ *
362
+ * In other words, a Post with a sealed Nostr event will have
363
+ * the same signature recorded twice in different places:
364
+ * 1. Inside a 'signature' key in the Post (envelope).
365
+ * 2. Inside a 'sig' key in the object extracted from a signed string.
366
+ * While, a Post with a sealed DMP event will only have the signature
367
+ * specified once inside the 'signature' key.
368
+ *
369
+ * Thus, we have to convert the signed string into an object,
370
+ * then check whether it has a 'sig' key (Nostr event),
371
+ * otherwise assume that it was signed with an Ethereum private key.
372
+ */
373
+ if ('signature' in unknownPostOrEvent &&
374
+ typeof (unknownPostOrEvent?.['signature']) === 'string' &&
375
+ unknownPostOrEvent?.['signature'].length > 40) {
376
+ if ('signed_message' in unknownPostOrEvent &&
377
+ typeof (unknownPostOrEvent?.['signed_message']) === 'string') {
378
+ const signedObject = JSON.parse(unknownPostOrEvent?.['signed_message']);
379
+ if (!(0, utils_1.isObjectWithValues)(signedObject))
380
+ return false;
381
+ if ('sig' in unknownPostOrEvent &&
382
+ typeof (unknownPostOrEvent['sig']) === 'string' &&
383
+ unknownPostOrEvent['sig'].length > 40) {
384
+ return 'nostr';
385
+ }
386
+ else {
387
+ return 'ethereum';
388
+ }
389
+ }
390
+ if ('signedString' in unknownPostOrEvent &&
391
+ typeof (unknownPostOrEvent?.['signedString']) === 'string') {
392
+ const signedObject = JSON.parse(unknownPostOrEvent?.['signedString']);
393
+ if (!(0, utils_1.isObjectWithValues)(signedObject))
394
+ return false;
395
+ if ((0, exports.isDmpEvent)(signedObject))
396
+ return 'ethereum';
397
+ }
398
+ }
399
+ };
400
+ exports.identifyPrivateKey = identifyPrivateKey;
401
+ const hasExtraSpasmFields = (unknownPostOrEvent) => {
402
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
403
+ return false;
404
+ if ('tags' in unknownPostOrEvent &&
405
+ Array.isArray(unknownPostOrEvent.tags)) {
406
+ // spasm_version is optional
407
+ let hasSpasmVersion = false;
408
+ // spasm_target is optional (e.g. new posts have no target)
409
+ let hasSpasmTarget = false;
410
+ // spasm_action is optional for Nostr (can be taken from 'kind' key)
411
+ let hasSpasmAction = false;
412
+ // spasm_title is optional (e.g. comments/reactions have no title)
413
+ let hasSpasmTitle = false;
414
+ // Nostr events have tags of array type: NostrSpasmTag | AnyTag
415
+ // Post events have tags of string type: string
416
+ unknownPostOrEvent.tags.forEach(function (tag) {
417
+ if (Array.isArray(tag)) {
418
+ if (tag[0] === "spasm_version") {
419
+ hasSpasmVersion = true;
420
+ }
421
+ if (tag[0] === "spasm_target") {
422
+ hasSpasmTarget = true;
423
+ }
424
+ if (tag[0] === "spasm_action") {
425
+ hasSpasmAction = true;
426
+ }
427
+ if (tag[0] === "spasm_title") {
428
+ hasSpasmTitle = true;
429
+ }
430
+ }
431
+ });
432
+ if (hasSpasmVersion || hasSpasmTarget || hasSpasmAction || hasSpasmTitle) {
433
+ return true;
434
+ }
435
+ }
436
+ return false;
437
+ };
438
+ exports.hasExtraSpasmFields = hasExtraSpasmFields;
439
+ const isNostrEvent = (unknownPostOrEvent) => {
440
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
441
+ return false;
442
+ // Unsigned Nostr event can be without 'id'
443
+ // if (!('id' in unknownPostOrEvent)) return false
444
+ // content
445
+ if (!('content' in unknownPostOrEvent))
446
+ return false;
447
+ if (!unknownPostOrEvent.content)
448
+ return false;
449
+ if (typeof (unknownPostOrEvent.content) !== "string")
450
+ return false;
451
+ // created_at
452
+ if (!('created_at' in unknownPostOrEvent))
453
+ return false;
454
+ // 0 is a valid created_at
455
+ if (!unknownPostOrEvent.created_at &&
456
+ unknownPostOrEvent.created_at !== 0)
457
+ return false;
458
+ if (typeof (unknownPostOrEvent.created_at) !== "number")
459
+ return false;
460
+ // kind
461
+ if (!('kind' in unknownPostOrEvent))
462
+ return false;
463
+ // 0 is a valid kind
464
+ if (!unknownPostOrEvent.kind &&
465
+ unknownPostOrEvent.kind !== 0)
466
+ return false;
467
+ if (typeof (unknownPostOrEvent.kind) !== "number")
468
+ return false;
469
+ // pubkey
470
+ if (!('pubkey' in unknownPostOrEvent))
471
+ return false;
472
+ if (!unknownPostOrEvent.pubkey)
473
+ return false;
474
+ if (typeof (unknownPostOrEvent.pubkey) !== "string")
475
+ return false;
476
+ // tags
477
+ // TODO: check if tags is a mandatory field
478
+ // if (!('tags' in unknownPostOrEvent)) return false
479
+ // sig
480
+ // Unsigned Nostr event can be without 'sig'
481
+ // if (!('sig' in unknownPostOrEvent)) return false
482
+ return true;
483
+ };
484
+ exports.isNostrEvent = isNostrEvent;
485
+ const isNostrEventSignedOpened = (unknownPostOrEvent) => {
486
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
487
+ return false;
488
+ if (!(0, exports.isNostrEvent)(unknownPostOrEvent))
489
+ return false;
490
+ // Signed Nostr event must have 'id'
491
+ if (!('id' in unknownPostOrEvent))
492
+ return false;
493
+ if (!(0, exports.hasSignature)(unknownPostOrEvent, 'sig'))
494
+ return false;
495
+ return true;
496
+ };
497
+ exports.isNostrEventSignedOpened = isNostrEventSignedOpened;
498
+ const isNostrSpasmEvent = (unknownPostOrEvent) => {
499
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
500
+ return false;
501
+ if (!(0, exports.isNostrEvent)(unknownPostOrEvent))
502
+ return false;
503
+ if (!(0, exports.hasExtraSpasmFields)(unknownPostOrEvent))
504
+ return false;
505
+ return true;
506
+ };
507
+ exports.isNostrSpasmEvent = isNostrSpasmEvent;
508
+ const isNostrSpasmEventSignedOpened = (unknownPostOrEvent) => {
509
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
510
+ return false;
511
+ if (!(0, exports.isNostrSpasmEvent)(unknownPostOrEvent))
512
+ return false;
513
+ if (!(0, exports.isNostrEventSignedOpened)(unknownPostOrEvent))
514
+ return false;
515
+ return true;
516
+ };
517
+ exports.isNostrSpasmEventSignedOpened = isNostrSpasmEventSignedOpened;
518
+ const isDmpEvent = (unknownPostOrEvent) => {
519
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
520
+ return false;
521
+ // TODO: think what if unknownPostOrEvent is Post with version, action, license
522
+ if (!('version' in unknownPostOrEvent))
523
+ return false;
524
+ if (!('action' in unknownPostOrEvent))
525
+ return false;
526
+ if (!('license' in unknownPostOrEvent))
527
+ return false;
528
+ // time is optional
529
+ // if (!('time' in unknownPostOrEvent)) return false
530
+ // target is optional (e.g. a new post has no target)
531
+ // if (!('target' in unknownPostOrEvent)) return false
532
+ // title is optional (e.g. a comment has no title)
533
+ // if (!('title' in unknownPostOrEvent)) return false
534
+ // text is optional (e.g. an event has only title)
535
+ // if (!('text' in unknownPostOrEvent)) return false
536
+ if (!unknownPostOrEvent?.version?.startsWith("dmp"))
537
+ return false;
538
+ return true;
539
+ };
540
+ exports.isDmpEvent = isDmpEvent;
541
+ const isDmpEventSignedClosed = (unknownPostOrEvent) => {
542
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
543
+ return false;
544
+ if (!('signedString' in unknownPostOrEvent))
545
+ return false;
546
+ if (!('signature' in unknownPostOrEvent))
547
+ return false;
548
+ // signer is optional
549
+ // if (!('signer' in unknownPostOrEvent)) return false
550
+ if (typeof (unknownPostOrEvent.signedString) !== "string")
551
+ return false;
552
+ const signedObject = JSON.parse(unknownPostOrEvent.signedString);
553
+ if (!(0, exports.isDmpEvent)(signedObject))
554
+ return false;
555
+ return true;
556
+ };
557
+ exports.isDmpEventSignedClosed = isDmpEventSignedClosed;
558
+ const isDmpEventSignedOpened = (unknownPostOrEvent) => {
559
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
560
+ return false;
561
+ if (!exports.isDmpEventSignedClosed)
562
+ return false;
563
+ if ('signedObject' in unknownPostOrEvent) {
564
+ if ((0, exports.isDmpEvent)(unknownPostOrEvent.signedObject))
565
+ return true;
566
+ }
567
+ return false;
568
+ };
569
+ exports.isDmpEventSignedOpened = isDmpEventSignedOpened;
570
+ /**
571
+ * 1. Post can contain DPM event without signature as a string inside
572
+ * signed_message, so signature is attached with 'signature' key.
573
+ *
574
+ * isPostWithDmpEventSignedOpened()
575
+ * - contains isDmpEventSignedOpened()
576
+ * isPostWithDmpEventSignedClosed()
577
+ * - contains isDmpEventSignedClosed()
578
+ * isPostWithDmpEvent()
579
+ * - contains isDmpEvent()
580
+ *
581
+ * 2. Post can contain Nostr event with signature as a string inside
582
+ * signed_message, and signature is also attached with 'signature' key.
583
+ * In other words, signature will be passed twice:
584
+ * - inside 'sig' key of parsed 'signed_message'
585
+ * - inside 'signature' key of the post object.
586
+ *
587
+ * isPostWithNostrSpasmEventSignedOpened()
588
+ * - contains isNostrSpasmEventSignedOpened()
589
+ * isPostWithNostrEventSignedOpened()
590
+ * - contains isNostrEventSignedOpened()
591
+ * isPostWithNostrSpasmEvent()
592
+ * - contains isNostrSpasmEvent()
593
+ * isPostWithNostrEvent
594
+ * - contains isNostrEvent()
595
+ */
596
+ const isPostWithDmpEvent = (unknownPostOrEvent) => {
597
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "DmpEvent");
598
+ };
599
+ exports.isPostWithDmpEvent = isPostWithDmpEvent;
600
+ const isPostWithDmpEventSignedOpened = (unknownPostOrEvent) => {
601
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "DmpEventSignedOpened");
602
+ };
603
+ exports.isPostWithDmpEventSignedOpened = isPostWithDmpEventSignedOpened;
604
+ const isPostWithDmpEventSignedClosed = (unknownPostOrEvent) => {
605
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "DmpEventSignedClosed");
606
+ };
607
+ exports.isPostWithDmpEventSignedClosed = isPostWithDmpEventSignedClosed;
608
+ const isPostWithNostrSpasmEventSignedOpened = (unknownPostOrEvent) => {
609
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "NostrSpasmEventSignedOpened");
610
+ };
611
+ exports.isPostWithNostrSpasmEventSignedOpened = isPostWithNostrSpasmEventSignedOpened;
612
+ const isPostWithNostrEventSignedOpened = (unknownPostOrEvent) => {
613
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "NostrEventSignedOpened");
614
+ };
615
+ exports.isPostWithNostrEventSignedOpened = isPostWithNostrEventSignedOpened;
616
+ const isPostWithNostrSpasmEvent = (unknownPostOrEvent) => {
617
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "NostrSpasmEvent");
618
+ };
619
+ exports.isPostWithNostrSpasmEvent = isPostWithNostrSpasmEvent;
620
+ const isPostWithNostrEvent = (unknownPostOrEvent) => {
621
+ return !!((0, exports.identifyEventInsidePost)(unknownPostOrEvent) === "NostrEvent");
622
+ };
623
+ exports.isPostWithNostrEvent = isPostWithNostrEvent;
624
+ const identifyEventInsidePost = (unknownPostOrEvent) => {
625
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
626
+ return false;
627
+ if (!('signed_message' in unknownPostOrEvent))
628
+ return false;
629
+ if (typeof (unknownPostOrEvent.signed_message) !== "string")
630
+ return false;
631
+ const signedObject = JSON.parse(unknownPostOrEvent.signed_message);
632
+ // isNostrEvent() will return true for NostrSpasmEvent,
633
+ // so the order of calling the functions is important.
634
+ // Sorted by popularity/frequency.
635
+ // TODO: Ideally, refactor and call identifyEvent(), which has
636
+ // if statements and calls isSomething... functions in the right
637
+ // order, depending on whether event has signature, etc.
638
+ // DMP
639
+ if ((0, exports.isDmpEventSignedOpened)(signedObject))
640
+ return "DmpEventSignedOpened";
641
+ if ((0, exports.isDmpEventSignedClosed)(signedObject))
642
+ return "DmpEventSignedClosed";
643
+ if ((0, exports.isDmpEvent)(signedObject))
644
+ return "DmpEvent";
645
+ // Nostr
646
+ if ((0, exports.isNostrSpasmEventSignedOpened)(signedObject))
647
+ return "NostrSpasmEventSignedOpened";
648
+ if ((0, exports.isNostrSpasmEvent)(signedObject))
649
+ return "NostrSpasmEvent";
650
+ if ((0, exports.isNostrEventSignedOpened)(signedObject))
651
+ return "NostrEventSignedOpened";
652
+ if ((0, exports.isNostrEvent)(signedObject))
653
+ return "NostrEvent";
654
+ };
655
+ exports.identifyEventInsidePost = identifyEventInsidePost;
656
+ const standartizePostOrEvent = (unknownPostOrEvent, info) => {
657
+ if (!(0, utils_1.isObjectWithValues)(unknownPostOrEvent))
658
+ return false;
659
+ // Info about post/event might be provided.
660
+ // If not, then we should identify an event.
661
+ if (!info) {
662
+ info = (0, exports.identifyPostOrEvent)(unknownPostOrEvent);
663
+ }
664
+ if (!info || !info.webType)
665
+ return false;
666
+ let standartizedEvent = {};
667
+ // DMP event submitted via UI
668
+ if (info.eventInfo &&
669
+ info.eventInfo.type === "DmpEventSignedClosed" &&
670
+ info.eventIsSealed === false) {
671
+ standartizedEvent = (0, exports.standartizeDmpEventSignedClosed)(unknownPostOrEvent);
672
+ }
673
+ // Nostr SPASM event submitted via UI
674
+ if (info.eventInfo &&
675
+ info.eventInfo.type === "NostrSpasmEventSignedOpened" &&
676
+ info.eventIsSealed === false) {
677
+ standartizedEvent = (0, exports.standartizeNostrSpasmEventSignedOpened)(unknownPostOrEvent);
678
+ }
679
+ // Post with sealed DMP event (received e.g. via SPASM module)
680
+ if (info.eventInfo &&
681
+ info.eventInfo.type === "DmpEventSignedClosed" &&
682
+ info.eventIsSealed === true) {
683
+ standartizedEvent = (0, exports.standartizePostWithDmpEventSignedClosed)(unknownPostOrEvent);
684
+ }
685
+ // Post with sealed Nostr event (received e.g. via SPASM module)
686
+ if (info.eventInfo &&
687
+ info.eventInfo.type === "NostrSpasmEventSignedOpened" &&
688
+ info.eventIsSealed === true) {
689
+ standartizedEvent = (0, exports.standartizePostWithNostrSpasmEventSignedOpened)(unknownPostOrEvent);
690
+ }
691
+ return standartizedEvent;
692
+ };
693
+ exports.standartizePostOrEvent = standartizePostOrEvent;
694
+ // standardizeDmpEventSignedClosed
695
+ const standartizeDmpEventSignedClosed = (event) => {
696
+ if (!(0, utils_1.isObjectWithValues)(event))
697
+ return null;
698
+ if (!(0, exports.isDmpEventSignedClosed)(event))
699
+ return null;
700
+ const signedString = event.signedString;
701
+ const signedObject = JSON.parse(signedString);
702
+ const signature = event.signature;
703
+ const signer = event.signer;
704
+ const target = signedObject.target;
705
+ const action = signedObject.action;
706
+ const title = signedObject.title;
707
+ const text = signedObject.text;
708
+ const signedDate = signedObject.time;
709
+ return {
710
+ signedString,
711
+ signature,
712
+ signer,
713
+ target,
714
+ action,
715
+ title,
716
+ text,
717
+ signedDate
718
+ };
719
+ };
720
+ exports.standartizeDmpEventSignedClosed = standartizeDmpEventSignedClosed;
721
+ // standardizeNostrSpasmEventSignedOpened
722
+ const standartizeNostrSpasmEventSignedOpened = (event) => {
723
+ if (!(0, utils_1.isObjectWithValues)(event))
724
+ return null;
725
+ if (!(0, exports.isNostrSpasmEventSignedOpened)(event))
726
+ return null;
727
+ const signedString = JSON.stringify(event);
728
+ // const signedObject: DmpEvent = JSON.parse(signedString)
729
+ const signature = event.sig;
730
+ const signer = (0, utils_1.convertHexToBech32)(event.pubkey);
731
+ const text = event.content;
732
+ // Convert the Unix timestamp to a JavaScript Date object
733
+ const date = new Date(event.created_at * 1000);
734
+ // Format the date in ISO format
735
+ const timestamptz = date.toISOString();
736
+ const signedDate = timestamptz;
737
+ let target;
738
+ let action;
739
+ let title;
740
+ if (event.tags &&
741
+ Array.isArray(event.tags)) {
742
+ event.tags.forEach(function (tag) {
743
+ if (Array.isArray(tag) && tag[0] === "spasm_target") {
744
+ target = tag[1];
745
+ }
746
+ if (Array.isArray(tag) && tag[0] === "spasm_action") {
747
+ action = tag[1];
748
+ }
749
+ if (Array.isArray(tag) && tag[0] === "spasm_title") {
750
+ title = tag[1];
751
+ }
752
+ });
753
+ }
754
+ return {
755
+ signedString,
756
+ signature,
757
+ signer,
758
+ target,
759
+ action,
760
+ title,
761
+ text,
762
+ signedDate
763
+ };
764
+ };
765
+ exports.standartizeNostrSpasmEventSignedOpened = standartizeNostrSpasmEventSignedOpened;
766
+ // standartizePostWithDmpEventSignedClosed
767
+ const standartizePostWithDmpEventSignedClosed = (post) => {
768
+ if (!(0, utils_1.isObjectWithValues)(post))
769
+ return null;
770
+ if (!('signed_message' in post) ||
771
+ typeof (post.signed_message) !== "string") {
772
+ return null;
773
+ }
774
+ const signedString = post.signed_message;
775
+ const signedObject = JSON.parse(signedString);
776
+ const signature = post.signature;
777
+ const signer = post.signer;
778
+ const target = signedObject.target;
779
+ const action = signedObject.action;
780
+ const title = signedObject.title;
781
+ const text = signedObject.text;
782
+ const signedDate = signedObject.time;
783
+ return {
784
+ signedString,
785
+ signature,
786
+ signer,
787
+ target,
788
+ action,
789
+ title,
790
+ text,
791
+ signedDate
792
+ };
793
+ };
794
+ exports.standartizePostWithDmpEventSignedClosed = standartizePostWithDmpEventSignedClosed;
795
+ // standardizePostWithNostrSpasmEventSignedOpened
796
+ const standartizePostWithNostrSpasmEventSignedOpened = (post) => {
797
+ if (!(0, utils_1.isObjectWithValues)(post))
798
+ return null;
799
+ if (!('signed_message' in post) ||
800
+ typeof (post.signed_message) !== "string") {
801
+ return null;
802
+ }
803
+ // Extract the event
804
+ const event = (0, exports.extractSealedEvent)(post);
805
+ return (0, exports.standartizeNostrSpasmEventSignedOpened)(event);
806
+ };
807
+ exports.standartizePostWithNostrSpasmEventSignedOpened = standartizePostWithNostrSpasmEventSignedOpened;
808
+ const convertToSpasm = (unknownPostOrEvent) => {
809
+ return (0, exports.standartizePostOrEvent)(unknownPostOrEvent);
810
+ };
811
+ exports.convertToSpasm = convertToSpasm;
812
+ //# sourceMappingURL=identifyEvent.js.map