@openclaw/buzz 2026.9.1 → 2026.9.3
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.
- package/dist/api.js +1 -1
- package/dist/{channel-B50upNWy.js → channel-C6rro-f7.js} +30 -13
- package/dist/channel-plugin-api.js +1 -1
- package/node_modules/nostr-tools/README.md +23 -0
- package/node_modules/nostr-tools/lib/cjs/index.js +25 -15
- package/node_modules/nostr-tools/lib/cjs/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip27.js +24 -14
- package/node_modules/nostr-tools/lib/cjs/nip27.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nipb7.js +1003 -98
- package/node_modules/nostr-tools/lib/cjs/nipb7.js.map +4 -4
- package/node_modules/nostr-tools/lib/cjs/relay.js +1 -1
- package/node_modules/nostr-tools/lib/cjs/relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/index.js +25 -15
- package/node_modules/nostr-tools/lib/esm/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip27.js +24 -14
- package/node_modules/nostr-tools/lib/esm/nip27.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nipb7.js +1009 -98
- package/node_modules/nostr-tools/lib/esm/nipb7.js.map +4 -4
- package/node_modules/nostr-tools/lib/esm/relay.js +1 -1
- package/node_modules/nostr-tools/lib/esm/relay.js.map +2 -2
- package/node_modules/nostr-tools/lib/nostr.bundle.js +25 -15
- package/node_modules/nostr-tools/lib/nostr.bundle.js.map +2 -2
- package/node_modules/nostr-tools/lib/types/nip27.d.ts +18 -0
- package/node_modules/nostr-tools/lib/types/nipb7.d.ts +112 -5
- package/node_modules/nostr-tools/lib/types/relay.d.ts +1 -1
- package/node_modules/nostr-tools/package.json +1 -7
- package/package.json +5 -5
- package/node_modules/nostr-tools/lib/cjs/nipb0.js +0 -1087
- package/node_modules/nostr-tools/lib/cjs/nipb0.js.map +0 -7
- package/node_modules/nostr-tools/lib/esm/nipb0.js +0 -1072
- package/node_modules/nostr-tools/lib/esm/nipb0.js.map +0 -7
- package/node_modules/nostr-tools/lib/types/nipb0.d.ts +0 -128
- package/node_modules/nostr-tools/lib/types/nipb0.test.d.ts +0 -1
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
// nipb7.ts
|
|
2
8
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
9
|
+
import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils.js";
|
|
3
10
|
|
|
4
11
|
// utils.ts
|
|
5
12
|
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
@@ -17,155 +24,1059 @@ function isHex32(input) {
|
|
|
17
24
|
return true;
|
|
18
25
|
}
|
|
19
26
|
|
|
27
|
+
// core.ts
|
|
28
|
+
var verifiedSymbol = Symbol("verified");
|
|
29
|
+
var isRecord = (obj) => obj instanceof Object;
|
|
30
|
+
function validateEvent(event) {
|
|
31
|
+
if (!isRecord(event))
|
|
32
|
+
return false;
|
|
33
|
+
if (typeof event.kind !== "number")
|
|
34
|
+
return false;
|
|
35
|
+
if (typeof event.content !== "string")
|
|
36
|
+
return false;
|
|
37
|
+
if (typeof event.created_at !== "number")
|
|
38
|
+
return false;
|
|
39
|
+
if (typeof event.pubkey !== "string")
|
|
40
|
+
return false;
|
|
41
|
+
if (!isHex32(event.pubkey))
|
|
42
|
+
return false;
|
|
43
|
+
if (!Array.isArray(event.tags))
|
|
44
|
+
return false;
|
|
45
|
+
for (let i = 0; i < event.tags.length; i++) {
|
|
46
|
+
let tag = event.tags[i];
|
|
47
|
+
if (!Array.isArray(tag))
|
|
48
|
+
return false;
|
|
49
|
+
for (let j = 0; j < tag.length; j++) {
|
|
50
|
+
if (typeof tag[j] !== "string")
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// kinds.ts
|
|
58
|
+
var kinds_exports = {};
|
|
59
|
+
__export(kinds_exports, {
|
|
60
|
+
AIEmbeddings: () => AIEmbeddings,
|
|
61
|
+
AppCurationSet: () => AppCurationSet,
|
|
62
|
+
Application: () => Application,
|
|
63
|
+
AuthoredPodcasts: () => AuthoredPodcasts,
|
|
64
|
+
BadgeAward: () => BadgeAward,
|
|
65
|
+
BadgeDefinition: () => BadgeDefinition,
|
|
66
|
+
Bid: () => Bid,
|
|
67
|
+
BidConfirmation: () => BidConfirmation,
|
|
68
|
+
BlobsAuth: () => BlobsAuth,
|
|
69
|
+
BlockedRelaysList: () => BlockedRelaysList,
|
|
70
|
+
BlossomServerList: () => BlossomServerList,
|
|
71
|
+
BookmarkList: () => BookmarkList,
|
|
72
|
+
Bookmarksets: () => Bookmarksets,
|
|
73
|
+
Calendar: () => Calendar,
|
|
74
|
+
CalendarEventRSVP: () => CalendarEventRSVP,
|
|
75
|
+
CashuMintAnnouncement: () => CashuMintAnnouncement,
|
|
76
|
+
CashuWalletEvent: () => CashuWalletEvent,
|
|
77
|
+
CashuWalletHistory: () => CashuWalletHistory,
|
|
78
|
+
CashuWalletTokens: () => CashuWalletTokens,
|
|
79
|
+
ChannelCreation: () => ChannelCreation,
|
|
80
|
+
ChannelHideMessage: () => ChannelHideMessage,
|
|
81
|
+
ChannelMessage: () => ChannelMessage,
|
|
82
|
+
ChannelMetadata: () => ChannelMetadata,
|
|
83
|
+
ChannelMuteUser: () => ChannelMuteUser,
|
|
84
|
+
ChatMessage: () => ChatMessage,
|
|
85
|
+
Chess: () => Chess,
|
|
86
|
+
ClassifiedListing: () => ClassifiedListing,
|
|
87
|
+
ClientAuth: () => ClientAuth,
|
|
88
|
+
CodeSnippet: () => CodeSnippet,
|
|
89
|
+
CoinjoinPool: () => CoinjoinPool,
|
|
90
|
+
Comment: () => Comment,
|
|
91
|
+
CommunitiesList: () => CommunitiesList,
|
|
92
|
+
CommunityDefinition: () => CommunityDefinition,
|
|
93
|
+
CommunityPostApproval: () => CommunityPostApproval,
|
|
94
|
+
ConferenceEvent: () => ConferenceEvent,
|
|
95
|
+
Contacts: () => Contacts,
|
|
96
|
+
CreateOrUpdateProduct: () => CreateOrUpdateProduct,
|
|
97
|
+
CreateOrUpdateStall: () => CreateOrUpdateStall,
|
|
98
|
+
CuratedVideoSets: () => CuratedVideoSets,
|
|
99
|
+
Curationsets: () => Curationsets,
|
|
100
|
+
Date: () => Date2,
|
|
101
|
+
DecoupledEncryptionKeyDistribution: () => DecoupledEncryptionKeyDistribution,
|
|
102
|
+
DecoupledKeyAnnouncement: () => DecoupledKeyAnnouncement,
|
|
103
|
+
DecoupledKeyClientAnnouncement: () => DecoupledKeyClientAnnouncement,
|
|
104
|
+
DirectMessageRelaysList: () => DirectMessageRelaysList,
|
|
105
|
+
DraftClassifiedListing: () => DraftClassifiedListing,
|
|
106
|
+
DraftEvent: () => DraftEvent,
|
|
107
|
+
DraftLong: () => DraftLong,
|
|
108
|
+
Emojisets: () => Emojisets,
|
|
109
|
+
EncryptedDirectMessage: () => EncryptedDirectMessage,
|
|
110
|
+
EventDeletion: () => EventDeletion,
|
|
111
|
+
FavoriteFollowSets: () => FavoriteFollowSets,
|
|
112
|
+
FavoritePodcasts: () => FavoritePodcasts,
|
|
113
|
+
FavoriteRelays: () => FavoriteRelays,
|
|
114
|
+
FedimintAnnouncement: () => FedimintAnnouncement,
|
|
115
|
+
Feed: () => Feed,
|
|
116
|
+
FileMessage: () => FileMessage,
|
|
117
|
+
FileMetadata: () => FileMetadata,
|
|
118
|
+
FileServerPreference: () => FileServerPreference,
|
|
119
|
+
Followsets: () => Followsets,
|
|
120
|
+
ForumThread: () => ForumThread,
|
|
121
|
+
GenericRepost: () => GenericRepost,
|
|
122
|
+
Genericlists: () => Genericlists,
|
|
123
|
+
GeocacheListing: () => GeocacheListing,
|
|
124
|
+
GeocacheLog: () => GeocacheLog,
|
|
125
|
+
GeocacheLogEntry: () => GeocacheLogEntry,
|
|
126
|
+
GeocacheProofOfFind: () => GeocacheProofOfFind,
|
|
127
|
+
GiftWrap: () => GiftWrap,
|
|
128
|
+
GitPullRequest: () => GitPullRequest,
|
|
129
|
+
GitPullRequestUpdate: () => GitPullRequestUpdate,
|
|
130
|
+
GoodWikiAuthorList: () => GoodWikiAuthorList,
|
|
131
|
+
GoodWikiRelayList: () => GoodWikiRelayList,
|
|
132
|
+
GroupMetadata: () => GroupMetadata,
|
|
133
|
+
HTTPAuth: () => HTTPAuth,
|
|
134
|
+
Handlerinformation: () => Handlerinformation,
|
|
135
|
+
Handlerrecommendation: () => Handlerrecommendation,
|
|
136
|
+
Highlights: () => Highlights,
|
|
137
|
+
InteractiveRoom: () => InteractiveRoom,
|
|
138
|
+
InterestsList: () => InterestsList,
|
|
139
|
+
Interestsets: () => Interestsets,
|
|
140
|
+
Issue: () => Issue,
|
|
141
|
+
JobFeedback: () => JobFeedback,
|
|
142
|
+
JobRequest: () => JobRequest,
|
|
143
|
+
JobResult: () => JobResult,
|
|
144
|
+
Label: () => Label,
|
|
145
|
+
LegacyNsiteFile: () => LegacyNsiteFile,
|
|
146
|
+
LightningPubRPC: () => LightningPubRPC,
|
|
147
|
+
LinkSet: () => LinkSet,
|
|
148
|
+
LiveChatMessage: () => LiveChatMessage,
|
|
149
|
+
LiveEvent: () => LiveEvent,
|
|
150
|
+
LongFormArticle: () => LongFormArticle,
|
|
151
|
+
MarketplaceUI: () => MarketplaceUI,
|
|
152
|
+
MediaFollows: () => MediaFollows,
|
|
153
|
+
MediaStarterPacks: () => MediaStarterPacks,
|
|
154
|
+
MergeRequests: () => MergeRequests,
|
|
155
|
+
Metadata: () => Metadata,
|
|
156
|
+
ModularArticleContent: () => ModularArticleContent,
|
|
157
|
+
ModularArticleHeader: () => ModularArticleHeader,
|
|
158
|
+
MuteSets: () => MuteSets,
|
|
159
|
+
Mutelist: () => Mutelist,
|
|
160
|
+
NWCWalletInfo: () => NWCWalletInfo,
|
|
161
|
+
NWCWalletRequest: () => NWCWalletRequest,
|
|
162
|
+
NWCWalletResponse: () => NWCWalletResponse,
|
|
163
|
+
NormalVideo: () => NormalVideo,
|
|
164
|
+
NostrConnect: () => NostrConnect,
|
|
165
|
+
NsiteNamed: () => NsiteNamed,
|
|
166
|
+
NsiteRoot: () => NsiteRoot,
|
|
167
|
+
NutZap: () => NutZap,
|
|
168
|
+
NutZapInfo: () => NutZapInfo,
|
|
169
|
+
OpenTimestamps: () => OpenTimestamps,
|
|
170
|
+
Patch: () => Patch,
|
|
171
|
+
PeerToPeerOrderEvents: () => PeerToPeerOrderEvents,
|
|
172
|
+
Photo: () => Photo,
|
|
173
|
+
Pinlist: () => Pinlist,
|
|
174
|
+
PodcastEpisode: () => PodcastEpisode,
|
|
175
|
+
PodcastMetadata: () => PodcastMetadata,
|
|
176
|
+
Poll: () => Poll,
|
|
177
|
+
PollResponse: () => PollResponse,
|
|
178
|
+
PrivateDirectMessage: () => PrivateDirectMessage,
|
|
179
|
+
PrivateEventRelayList: () => PrivateEventRelayList,
|
|
180
|
+
ProblemTracker: () => ProblemTracker,
|
|
181
|
+
ProductSoldAsAuction: () => ProductSoldAsAuction,
|
|
182
|
+
ProfileBadges: () => ProfileBadges,
|
|
183
|
+
ProxyAnnouncement: () => ProxyAnnouncement,
|
|
184
|
+
PublicChatsList: () => PublicChatsList,
|
|
185
|
+
PublicMessage: () => PublicMessage,
|
|
186
|
+
Reaction: () => Reaction,
|
|
187
|
+
ReactionToWebsite: () => ReactionToWebsite,
|
|
188
|
+
RecommendRelay: () => RecommendRelay,
|
|
189
|
+
Redirects: () => Redirects,
|
|
190
|
+
RelayDiscovery: () => RelayDiscovery,
|
|
191
|
+
RelayList: () => RelayList,
|
|
192
|
+
RelayMonitorAnnouncement: () => RelayMonitorAnnouncement,
|
|
193
|
+
RelayReview: () => RelayReview,
|
|
194
|
+
RelayReviews: () => RelayReviews,
|
|
195
|
+
Relaysets: () => Relaysets,
|
|
196
|
+
ReleaseArtifactSets: () => ReleaseArtifactSets,
|
|
197
|
+
Reply: () => Reply,
|
|
198
|
+
Report: () => Report,
|
|
199
|
+
Reporting: () => Reporting,
|
|
200
|
+
RepositoryAnnouncement: () => RepositoryAnnouncement,
|
|
201
|
+
RepositoryState: () => RepositoryState,
|
|
202
|
+
Repost: () => Repost,
|
|
203
|
+
ReservedCashuWalletTokens: () => ReservedCashuWalletTokens,
|
|
204
|
+
RoomPresence: () => RoomPresence,
|
|
205
|
+
Scroll: () => Scroll,
|
|
206
|
+
Seal: () => Seal,
|
|
207
|
+
SearchRelaysList: () => SearchRelaysList,
|
|
208
|
+
ShortTextNote: () => ShortTextNote,
|
|
209
|
+
ShortVideo: () => ShortVideo,
|
|
210
|
+
SimpleGroupAdmins: () => SimpleGroupAdmins,
|
|
211
|
+
SimpleGroupCreateGroup: () => SimpleGroupCreateGroup,
|
|
212
|
+
SimpleGroupCreateInvite: () => SimpleGroupCreateInvite,
|
|
213
|
+
SimpleGroupDeleteEvent: () => SimpleGroupDeleteEvent,
|
|
214
|
+
SimpleGroupDeleteGroup: () => SimpleGroupDeleteGroup,
|
|
215
|
+
SimpleGroupEditMetadata: () => SimpleGroupEditMetadata,
|
|
216
|
+
SimpleGroupJoinRequest: () => SimpleGroupJoinRequest,
|
|
217
|
+
SimpleGroupLeaveRequest: () => SimpleGroupLeaveRequest,
|
|
218
|
+
SimpleGroupList: () => SimpleGroupList,
|
|
219
|
+
SimpleGroupLiveKitParticipants: () => SimpleGroupLiveKitParticipants,
|
|
220
|
+
SimpleGroupMembers: () => SimpleGroupMembers,
|
|
221
|
+
SimpleGroupPutUser: () => SimpleGroupPutUser,
|
|
222
|
+
SimpleGroupRemoveUser: () => SimpleGroupRemoveUser,
|
|
223
|
+
SimpleGroupReply: () => SimpleGroupReply,
|
|
224
|
+
SimpleGroupRoles: () => SimpleGroupRoles,
|
|
225
|
+
SimpleGroupThreadedReply: () => SimpleGroupThreadedReply,
|
|
226
|
+
SlideSet: () => SlideSet,
|
|
227
|
+
SoftwareApplication: () => SoftwareApplication,
|
|
228
|
+
StarterPacks: () => StarterPacks,
|
|
229
|
+
StatusApplied: () => StatusApplied,
|
|
230
|
+
StatusClosed: () => StatusClosed,
|
|
231
|
+
StatusDraft: () => StatusDraft,
|
|
232
|
+
StatusOpen: () => StatusOpen,
|
|
233
|
+
TidalLogin: () => TidalLogin,
|
|
234
|
+
Time: () => Time,
|
|
235
|
+
Torrent: () => Torrent,
|
|
236
|
+
TorrentComment: () => TorrentComment,
|
|
237
|
+
TransportMethodAnnouncement: () => TransportMethodAnnouncement,
|
|
238
|
+
UserEmojiList: () => UserEmojiList,
|
|
239
|
+
UserGraspList: () => UserGraspList,
|
|
240
|
+
UserStatuses: () => UserStatuses,
|
|
241
|
+
VideoViewEvent: () => VideoViewEvent,
|
|
242
|
+
Voice: () => Voice,
|
|
243
|
+
VoiceComment: () => VoiceComment,
|
|
244
|
+
WebBookmarks: () => WebBookmarks,
|
|
245
|
+
WikiArticle: () => WikiArticle,
|
|
246
|
+
Zap: () => Zap,
|
|
247
|
+
ZapGoal: () => ZapGoal,
|
|
248
|
+
ZapRequest: () => ZapRequest,
|
|
249
|
+
classifyKind: () => classifyKind,
|
|
250
|
+
isAddressableKind: () => isAddressableKind,
|
|
251
|
+
isEphemeralKind: () => isEphemeralKind,
|
|
252
|
+
isKind: () => isKind,
|
|
253
|
+
isRegularKind: () => isRegularKind,
|
|
254
|
+
isReplaceableKind: () => isReplaceableKind
|
|
255
|
+
});
|
|
256
|
+
function isRegularKind(kind) {
|
|
257
|
+
return kind < 1e4 && kind !== 0 && kind !== 3;
|
|
258
|
+
}
|
|
259
|
+
function isReplaceableKind(kind) {
|
|
260
|
+
return kind === 0 || kind === 3 || 1e4 <= kind && kind < 2e4;
|
|
261
|
+
}
|
|
262
|
+
function isEphemeralKind(kind) {
|
|
263
|
+
return 2e4 <= kind && kind < 3e4;
|
|
264
|
+
}
|
|
265
|
+
function isAddressableKind(kind) {
|
|
266
|
+
return 3e4 <= kind && kind < 4e4;
|
|
267
|
+
}
|
|
268
|
+
function classifyKind(kind) {
|
|
269
|
+
if (isRegularKind(kind))
|
|
270
|
+
return "regular";
|
|
271
|
+
if (isReplaceableKind(kind))
|
|
272
|
+
return "replaceable";
|
|
273
|
+
if (isEphemeralKind(kind))
|
|
274
|
+
return "ephemeral";
|
|
275
|
+
if (isAddressableKind(kind))
|
|
276
|
+
return "parameterized";
|
|
277
|
+
return "unknown";
|
|
278
|
+
}
|
|
279
|
+
function isKind(event, kind) {
|
|
280
|
+
const kindAsArray = kind instanceof Array ? kind : [kind];
|
|
281
|
+
return validateEvent(event) && kindAsArray.includes(event.kind) || false;
|
|
282
|
+
}
|
|
283
|
+
var Metadata = 0;
|
|
284
|
+
var ShortTextNote = 1;
|
|
285
|
+
var RecommendRelay = 2;
|
|
286
|
+
var Contacts = 3;
|
|
287
|
+
var EncryptedDirectMessage = 4;
|
|
288
|
+
var EventDeletion = 5;
|
|
289
|
+
var Repost = 6;
|
|
290
|
+
var Reaction = 7;
|
|
291
|
+
var BadgeAward = 8;
|
|
292
|
+
var ChatMessage = 9;
|
|
293
|
+
var SimpleGroupThreadedReply = 10;
|
|
294
|
+
var ForumThread = 11;
|
|
295
|
+
var SimpleGroupReply = 12;
|
|
296
|
+
var Seal = 13;
|
|
297
|
+
var PrivateDirectMessage = 14;
|
|
298
|
+
var FileMessage = 15;
|
|
299
|
+
var GenericRepost = 16;
|
|
300
|
+
var ReactionToWebsite = 17;
|
|
301
|
+
var Photo = 20;
|
|
302
|
+
var NormalVideo = 21;
|
|
303
|
+
var ShortVideo = 22;
|
|
304
|
+
var PublicMessage = 24;
|
|
305
|
+
var ChannelCreation = 40;
|
|
306
|
+
var ChannelMetadata = 41;
|
|
307
|
+
var ChannelMessage = 42;
|
|
308
|
+
var ChannelHideMessage = 43;
|
|
309
|
+
var ChannelMuteUser = 44;
|
|
310
|
+
var PodcastEpisode = 54;
|
|
311
|
+
var Chess = 64;
|
|
312
|
+
var MergeRequests = 818;
|
|
313
|
+
var PollResponse = 1018;
|
|
314
|
+
var Bid = 1021;
|
|
315
|
+
var BidConfirmation = 1022;
|
|
316
|
+
var OpenTimestamps = 1040;
|
|
317
|
+
var GiftWrap = 1059;
|
|
318
|
+
var FileMetadata = 1063;
|
|
319
|
+
var Poll = 1068;
|
|
320
|
+
var Comment = 1111;
|
|
321
|
+
var Voice = 1222;
|
|
322
|
+
var Scroll = 1227;
|
|
323
|
+
var VoiceComment = 1244;
|
|
324
|
+
var LiveChatMessage = 1311;
|
|
325
|
+
var CodeSnippet = 1337;
|
|
326
|
+
var Patch = 1617;
|
|
327
|
+
var GitPullRequest = 1618;
|
|
328
|
+
var GitPullRequestUpdate = 1619;
|
|
329
|
+
var Issue = 1621;
|
|
330
|
+
var Reply = 1622;
|
|
331
|
+
var StatusOpen = 1630;
|
|
332
|
+
var StatusApplied = 1631;
|
|
333
|
+
var StatusClosed = 1632;
|
|
334
|
+
var StatusDraft = 1633;
|
|
335
|
+
var ProblemTracker = 1971;
|
|
336
|
+
var Report = 1984;
|
|
337
|
+
var Reporting = 1984;
|
|
338
|
+
var Label = 1985;
|
|
339
|
+
var RelayReviews = 1986;
|
|
340
|
+
var AIEmbeddings = 1987;
|
|
341
|
+
var Torrent = 2003;
|
|
342
|
+
var TorrentComment = 2004;
|
|
343
|
+
var CoinjoinPool = 2022;
|
|
344
|
+
var DecoupledKeyClientAnnouncement = 4454;
|
|
345
|
+
var DecoupledEncryptionKeyDistribution = 4455;
|
|
346
|
+
var CommunityPostApproval = 4550;
|
|
347
|
+
var JobRequest = 5999;
|
|
348
|
+
var JobResult = 6999;
|
|
349
|
+
var JobFeedback = 7e3;
|
|
350
|
+
var ReservedCashuWalletTokens = 7374;
|
|
351
|
+
var CashuWalletTokens = 7375;
|
|
352
|
+
var CashuWalletHistory = 7376;
|
|
353
|
+
var GeocacheLog = 7516;
|
|
354
|
+
var GeocacheProofOfFind = 7517;
|
|
355
|
+
var SimpleGroupPutUser = 9e3;
|
|
356
|
+
var SimpleGroupRemoveUser = 9001;
|
|
357
|
+
var SimpleGroupEditMetadata = 9002;
|
|
358
|
+
var SimpleGroupDeleteEvent = 9005;
|
|
359
|
+
var SimpleGroupCreateGroup = 9007;
|
|
360
|
+
var SimpleGroupDeleteGroup = 9008;
|
|
361
|
+
var SimpleGroupCreateInvite = 9009;
|
|
362
|
+
var SimpleGroupJoinRequest = 9021;
|
|
363
|
+
var SimpleGroupLeaveRequest = 9022;
|
|
364
|
+
var ZapGoal = 9041;
|
|
365
|
+
var NutZap = 9321;
|
|
366
|
+
var TidalLogin = 9467;
|
|
367
|
+
var ZapRequest = 9734;
|
|
368
|
+
var Zap = 9735;
|
|
369
|
+
var Highlights = 9802;
|
|
370
|
+
var Mutelist = 1e4;
|
|
371
|
+
var Pinlist = 10001;
|
|
372
|
+
var RelayList = 10002;
|
|
373
|
+
var BookmarkList = 10003;
|
|
374
|
+
var CommunitiesList = 10004;
|
|
375
|
+
var PublicChatsList = 10005;
|
|
376
|
+
var BlockedRelaysList = 10006;
|
|
377
|
+
var SearchRelaysList = 10007;
|
|
378
|
+
var SimpleGroupList = 10009;
|
|
379
|
+
var FavoriteRelays = 10012;
|
|
380
|
+
var PrivateEventRelayList = 10013;
|
|
381
|
+
var InterestsList = 10015;
|
|
382
|
+
var NutZapInfo = 10019;
|
|
383
|
+
var MediaFollows = 10020;
|
|
384
|
+
var FavoriteFollowSets = 10021;
|
|
385
|
+
var UserEmojiList = 10030;
|
|
386
|
+
var DecoupledKeyAnnouncement = 10044;
|
|
387
|
+
var DirectMessageRelaysList = 10050;
|
|
388
|
+
var FavoritePodcasts = 10054;
|
|
389
|
+
var BlossomServerList = 10063;
|
|
390
|
+
var FileServerPreference = 10096;
|
|
391
|
+
var GoodWikiAuthorList = 10101;
|
|
392
|
+
var GoodWikiRelayList = 10102;
|
|
393
|
+
var PodcastMetadata = 10154;
|
|
394
|
+
var AuthoredPodcasts = 10164;
|
|
395
|
+
var RelayMonitorAnnouncement = 10166;
|
|
396
|
+
var RoomPresence = 10312;
|
|
397
|
+
var UserGraspList = 10317;
|
|
398
|
+
var ProxyAnnouncement = 10377;
|
|
399
|
+
var TransportMethodAnnouncement = 11111;
|
|
400
|
+
var NWCWalletInfo = 13194;
|
|
401
|
+
var NsiteRoot = 15128;
|
|
402
|
+
var CashuWalletEvent = 17375;
|
|
403
|
+
var LightningPubRPC = 21e3;
|
|
404
|
+
var ClientAuth = 22242;
|
|
405
|
+
var NWCWalletRequest = 23194;
|
|
406
|
+
var NWCWalletResponse = 23195;
|
|
407
|
+
var NostrConnect = 24133;
|
|
408
|
+
var BlobsAuth = 24242;
|
|
409
|
+
var HTTPAuth = 27235;
|
|
410
|
+
var Followsets = 3e4;
|
|
411
|
+
var Genericlists = 30001;
|
|
412
|
+
var Relaysets = 30002;
|
|
413
|
+
var Bookmarksets = 30003;
|
|
414
|
+
var Curationsets = 30004;
|
|
415
|
+
var CuratedVideoSets = 30005;
|
|
416
|
+
var MuteSets = 30007;
|
|
417
|
+
var ProfileBadges = 30008;
|
|
418
|
+
var BadgeDefinition = 30009;
|
|
419
|
+
var Interestsets = 30015;
|
|
420
|
+
var CreateOrUpdateStall = 30017;
|
|
421
|
+
var CreateOrUpdateProduct = 30018;
|
|
422
|
+
var MarketplaceUI = 30019;
|
|
423
|
+
var ProductSoldAsAuction = 30020;
|
|
424
|
+
var LongFormArticle = 30023;
|
|
425
|
+
var DraftLong = 30024;
|
|
426
|
+
var Emojisets = 30030;
|
|
427
|
+
var ModularArticleHeader = 30040;
|
|
428
|
+
var ModularArticleContent = 30041;
|
|
429
|
+
var ReleaseArtifactSets = 30063;
|
|
430
|
+
var Application = 30078;
|
|
431
|
+
var RelayDiscovery = 30166;
|
|
432
|
+
var AppCurationSet = 30267;
|
|
433
|
+
var LiveEvent = 30311;
|
|
434
|
+
var InteractiveRoom = 30312;
|
|
435
|
+
var ConferenceEvent = 30313;
|
|
436
|
+
var UserStatuses = 30315;
|
|
437
|
+
var SlideSet = 30388;
|
|
438
|
+
var ClassifiedListing = 30402;
|
|
439
|
+
var DraftClassifiedListing = 30403;
|
|
440
|
+
var RepositoryAnnouncement = 30617;
|
|
441
|
+
var RepositoryState = 30618;
|
|
442
|
+
var WikiArticle = 30818;
|
|
443
|
+
var Redirects = 30819;
|
|
444
|
+
var DraftEvent = 31234;
|
|
445
|
+
var LinkSet = 31388;
|
|
446
|
+
var Feed = 31890;
|
|
447
|
+
var Date2 = 31922;
|
|
448
|
+
var Time = 31923;
|
|
449
|
+
var Calendar = 31924;
|
|
450
|
+
var CalendarEventRSVP = 31925;
|
|
451
|
+
var RelayReview = 31987;
|
|
452
|
+
var Handlerrecommendation = 31989;
|
|
453
|
+
var Handlerinformation = 31990;
|
|
454
|
+
var SoftwareApplication = 32267;
|
|
455
|
+
var LegacyNsiteFile = 34128;
|
|
456
|
+
var VideoViewEvent = 34237;
|
|
457
|
+
var CommunityDefinition = 34550;
|
|
458
|
+
var NsiteNamed = 35128;
|
|
459
|
+
var GeocacheListing = 37515;
|
|
460
|
+
var GeocacheLogEntry = 37516;
|
|
461
|
+
var CashuMintAnnouncement = 38172;
|
|
462
|
+
var FedimintAnnouncement = 38173;
|
|
463
|
+
var PeerToPeerOrderEvents = 38383;
|
|
464
|
+
var GroupMetadata = 39e3;
|
|
465
|
+
var SimpleGroupAdmins = 39001;
|
|
466
|
+
var SimpleGroupMembers = 39002;
|
|
467
|
+
var SimpleGroupRoles = 39003;
|
|
468
|
+
var SimpleGroupLiveKitParticipants = 39004;
|
|
469
|
+
var StarterPacks = 39089;
|
|
470
|
+
var MediaStarterPacks = 39092;
|
|
471
|
+
var WebBookmarks = 39701;
|
|
472
|
+
|
|
20
473
|
// nipb7.ts
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
474
|
+
function getBlobSize(blob) {
|
|
475
|
+
if (typeof File !== "undefined" && blob instanceof File || blob instanceof Blob) {
|
|
476
|
+
return blob.size;
|
|
477
|
+
}
|
|
478
|
+
return blob.length;
|
|
479
|
+
}
|
|
480
|
+
function getBlobType(blob) {
|
|
481
|
+
if (typeof File !== "undefined" && blob instanceof File || blob instanceof Blob) {
|
|
482
|
+
return blob.type || void 0;
|
|
483
|
+
}
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
486
|
+
async function computeBlobSha256(blob) {
|
|
487
|
+
let buffer;
|
|
488
|
+
if (typeof File !== "undefined" && blob instanceof File || blob instanceof Blob) {
|
|
489
|
+
buffer = await blob.arrayBuffer();
|
|
490
|
+
} else {
|
|
491
|
+
buffer = blob;
|
|
492
|
+
}
|
|
493
|
+
const hash = sha256.create().update(new Uint8Array(buffer)).digest();
|
|
494
|
+
return bytesToHex2(hash);
|
|
495
|
+
}
|
|
496
|
+
function encodeAuthorizationHeader(event) {
|
|
497
|
+
const json = JSON.stringify(event);
|
|
498
|
+
const bytes = new TextEncoder().encode(json);
|
|
499
|
+
let binary = "";
|
|
500
|
+
for (const byte of bytes)
|
|
501
|
+
binary += String.fromCharCode(byte);
|
|
502
|
+
return "Nostr " + btoa(binary);
|
|
503
|
+
}
|
|
504
|
+
function now() {
|
|
505
|
+
return Math.floor(Date.now() / 1e3);
|
|
506
|
+
}
|
|
507
|
+
function oneHour() {
|
|
508
|
+
return now() + 3600;
|
|
509
|
+
}
|
|
510
|
+
function getAuthTagValues(auth, tagName) {
|
|
511
|
+
return auth.tags.filter((tag) => tag[0] === tagName).map((tag) => tag[1]);
|
|
512
|
+
}
|
|
513
|
+
function getAuthExpiration(auth) {
|
|
514
|
+
const expiration = auth.tags.find((tag) => tag[0] === "expiration")?.[1];
|
|
515
|
+
if (!expiration)
|
|
516
|
+
return void 0;
|
|
517
|
+
const timestamp = Number(expiration);
|
|
518
|
+
if (!Number.isFinite(timestamp))
|
|
519
|
+
return void 0;
|
|
520
|
+
return timestamp;
|
|
521
|
+
}
|
|
522
|
+
function isAuthExpired(auth, timestamp = now()) {
|
|
523
|
+
const expiration = getAuthExpiration(auth);
|
|
524
|
+
return expiration !== void 0 && expiration <= timestamp;
|
|
525
|
+
}
|
|
526
|
+
function normalizeServerTag(server) {
|
|
527
|
+
if (server instanceof URL)
|
|
528
|
+
return server.hostname.toLowerCase();
|
|
529
|
+
if (URL.canParse(server))
|
|
530
|
+
return new URL(server).hostname.toLowerCase();
|
|
531
|
+
return server.toLowerCase();
|
|
532
|
+
}
|
|
533
|
+
function areServersEqual(a, b) {
|
|
534
|
+
return normalizeServerTag(a) === normalizeServerTag(b);
|
|
535
|
+
}
|
|
536
|
+
function normalizeServers(servers) {
|
|
537
|
+
const values = Array.isArray(servers) ? servers : [servers];
|
|
538
|
+
return [...new Set(values.map(normalizeServerTag))];
|
|
539
|
+
}
|
|
540
|
+
async function createAuthEvent(signer, type, options) {
|
|
541
|
+
const draft = {
|
|
542
|
+
created_at: now(),
|
|
543
|
+
kind: kinds_exports.BlobsAuth,
|
|
544
|
+
content: options?.message ?? "",
|
|
545
|
+
tags: [
|
|
546
|
+
["t", type],
|
|
547
|
+
["expiration", String(options?.expiration ?? oneHour())]
|
|
548
|
+
]
|
|
549
|
+
};
|
|
550
|
+
if (options?.blobs) {
|
|
551
|
+
const blobList = Array.isArray(options.blobs) ? options.blobs : [options.blobs];
|
|
552
|
+
const seen = /* @__PURE__ */ new Set();
|
|
553
|
+
for (const blob of blobList) {
|
|
554
|
+
const hash = typeof blob === "string" ? blob : await computeBlobSha256(blob);
|
|
555
|
+
if (!seen.has(hash)) {
|
|
556
|
+
draft.tags.push(["x", hash]);
|
|
557
|
+
seen.add(hash);
|
|
558
|
+
}
|
|
27
559
|
}
|
|
28
|
-
this.mediaserver = mediaserver.replace(/\/$/, "") + "/";
|
|
29
|
-
this.signer = signer;
|
|
30
560
|
}
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
headers["Content-Type"] = contentType;
|
|
561
|
+
if (options?.servers) {
|
|
562
|
+
for (const server of normalizeServers(options.servers)) {
|
|
563
|
+
draft.tags.push(["server", server]);
|
|
35
564
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
565
|
+
}
|
|
566
|
+
return signer(draft);
|
|
567
|
+
}
|
|
568
|
+
async function createUploadAuth(signer, blobs, options) {
|
|
569
|
+
return createAuthEvent(signer, options?.type ?? "upload", { message: "Upload Blob", ...options, blobs });
|
|
570
|
+
}
|
|
571
|
+
async function createDownloadAuth(signer, hash, options) {
|
|
572
|
+
return createAuthEvent(signer, "get", { message: "Download Blob", ...options, blobs: [hash] });
|
|
573
|
+
}
|
|
574
|
+
async function createMirrorAuth(signer, hash, options) {
|
|
575
|
+
return createAuthEvent(signer, "upload", { message: "Mirror Blob", ...options, blobs: [hash] });
|
|
576
|
+
}
|
|
577
|
+
async function createListAuth(signer, options) {
|
|
578
|
+
return createAuthEvent(signer, "list", { message: "List Blobs", ...options });
|
|
579
|
+
}
|
|
580
|
+
async function createDeleteAuth(signer, hash, options) {
|
|
581
|
+
return createAuthEvent(signer, "delete", { message: "Delete Blob", ...options, blobs: [hash] });
|
|
582
|
+
}
|
|
583
|
+
function parseBlossomURI(uri) {
|
|
584
|
+
if (!uri.startsWith("blossom:"))
|
|
585
|
+
throw new Error("Invalid blossom URI: missing blossom: scheme");
|
|
586
|
+
const body = uri.slice("blossom:".length);
|
|
587
|
+
const queryIndex = body.indexOf("?");
|
|
588
|
+
const path = queryIndex === -1 ? body : body.slice(0, queryIndex);
|
|
589
|
+
const query = queryIndex === -1 ? "" : body.slice(queryIndex + 1);
|
|
590
|
+
const dotIndex = path.indexOf(".");
|
|
591
|
+
if (dotIndex === -1)
|
|
592
|
+
throw new Error("Invalid blossom URI: missing file extension");
|
|
593
|
+
const sha2562 = path.slice(0, dotIndex);
|
|
594
|
+
const ext = path.slice(dotIndex + 1);
|
|
595
|
+
if (!isHex32(sha2562))
|
|
596
|
+
throw new Error("Invalid blossom URI: invalid sha256 hash");
|
|
597
|
+
if (!ext)
|
|
598
|
+
throw new Error("Invalid blossom URI: empty file extension");
|
|
599
|
+
const params = new URLSearchParams(query);
|
|
600
|
+
const servers = params.getAll("xs");
|
|
601
|
+
const authors = params.getAll("as");
|
|
602
|
+
const szValue = params.get("sz");
|
|
603
|
+
let size;
|
|
604
|
+
if (szValue !== null) {
|
|
605
|
+
size = Number(szValue);
|
|
606
|
+
if (!Number.isFinite(size) || size <= 0 || Math.floor(size) !== size) {
|
|
607
|
+
throw new Error("Invalid blossom URI: sz must be a positive integer");
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return { sha256: sha2562, ext, servers, authors, size };
|
|
611
|
+
}
|
|
612
|
+
function buildBlossomURI(options) {
|
|
613
|
+
const params = new URLSearchParams();
|
|
614
|
+
for (const server of options.servers)
|
|
615
|
+
params.append("xs", server);
|
|
616
|
+
for (const author of options.authors)
|
|
617
|
+
params.append("as", author);
|
|
618
|
+
if (options.size !== void 0)
|
|
619
|
+
params.append("sz", String(options.size));
|
|
620
|
+
const query = params.toString();
|
|
621
|
+
return `blossom:${options.sha256}.${options.ext}${query ? "?" + query : ""}`;
|
|
622
|
+
}
|
|
623
|
+
function blossomURIToURL(uri) {
|
|
624
|
+
const str = typeof uri === "string" ? uri : buildBlossomURI(uri);
|
|
625
|
+
return new URL(str);
|
|
626
|
+
}
|
|
627
|
+
function blossomURIFromURL(url) {
|
|
628
|
+
if (url.protocol !== "blossom:")
|
|
629
|
+
throw new Error("Invalid blossom URL: expected blossom: protocol");
|
|
630
|
+
const path = url.pathname;
|
|
631
|
+
const dotIndex = path.indexOf(".");
|
|
632
|
+
if (dotIndex === -1)
|
|
633
|
+
throw new Error("Invalid blossom URL: missing file extension");
|
|
634
|
+
const sha2562 = path.slice(0, dotIndex);
|
|
635
|
+
const ext = path.slice(dotIndex + 1);
|
|
636
|
+
if (!isHex32(sha2562))
|
|
637
|
+
throw new Error("Invalid blossom URL: invalid sha256 hash");
|
|
638
|
+
if (!ext)
|
|
639
|
+
throw new Error("Invalid blossom URL: empty file extension");
|
|
640
|
+
const servers = url.searchParams.getAll("xs");
|
|
641
|
+
const authors = url.searchParams.getAll("as");
|
|
642
|
+
const szValue = url.searchParams.get("sz");
|
|
643
|
+
let size;
|
|
644
|
+
if (szValue !== null) {
|
|
645
|
+
size = Number(szValue);
|
|
646
|
+
if (!Number.isFinite(size) || size <= 0 || Math.floor(size) !== size) {
|
|
647
|
+
throw new Error("Invalid blossom URL: sz must be a positive integer");
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return { sha256: sha2562, ext, servers, authors, size };
|
|
651
|
+
}
|
|
652
|
+
var commonMimeExtensions = {
|
|
653
|
+
"application/json": ".json",
|
|
654
|
+
"application/pdf": ".pdf",
|
|
655
|
+
"application/vnd.android.package-archive": ".apk",
|
|
656
|
+
"application/vnd.sqlite3": ".sqlite3",
|
|
657
|
+
"application/xml": ".xml",
|
|
658
|
+
"audio/aac": ".aac",
|
|
659
|
+
"audio/flac": ".flac",
|
|
660
|
+
"audio/midi": ".midi",
|
|
661
|
+
"audio/mp3": ".mp3",
|
|
662
|
+
"audio/mpeg": ".mp3",
|
|
663
|
+
"audio/mp4": ".m4a",
|
|
664
|
+
"audio/ogg": ".ogg",
|
|
665
|
+
"audio/wav": ".wav",
|
|
666
|
+
"audio/webm": ".weba",
|
|
667
|
+
"audio/x-aiff": ".aiff",
|
|
668
|
+
"audio/x-m4a": ".m4a",
|
|
669
|
+
"image/avif": ".avif",
|
|
670
|
+
"image/gif": ".gif",
|
|
671
|
+
"image/jpeg": ".jpg",
|
|
672
|
+
"image/png": ".png",
|
|
673
|
+
"image/svg+xml": ".svg",
|
|
674
|
+
"image/webp": ".webp",
|
|
675
|
+
"text/css": ".css",
|
|
676
|
+
"text/csv": ".csv",
|
|
677
|
+
"text/html": ".html",
|
|
678
|
+
"text/javascript": ".js",
|
|
679
|
+
"text/markdown": ".md",
|
|
680
|
+
"text/plain": ".txt",
|
|
681
|
+
"text/xml": ".xml",
|
|
682
|
+
"video/mp2t": ".ts",
|
|
683
|
+
"video/mp4": ".mp4",
|
|
684
|
+
"video/ogg": ".ogv",
|
|
685
|
+
"video/quicktime": ".mov",
|
|
686
|
+
"video/webm": ".webm",
|
|
687
|
+
"video/x-matroska": ".mkv"
|
|
688
|
+
};
|
|
689
|
+
var commonExtensionMimes = {
|
|
690
|
+
".aac": "audio/aac",
|
|
691
|
+
".aiff": "audio/x-aiff",
|
|
692
|
+
".apk": "application/vnd.android.package-archive",
|
|
693
|
+
".avif": "image/avif",
|
|
694
|
+
".css": "text/css; charset=utf-8",
|
|
695
|
+
".csv": "text/csv; charset=utf-8",
|
|
696
|
+
".flac": "audio/flac",
|
|
697
|
+
".gif": "image/gif",
|
|
698
|
+
".html": "text/html; charset=utf-8",
|
|
699
|
+
".jpeg": "image/jpeg",
|
|
700
|
+
".jpg": "image/jpeg",
|
|
701
|
+
".js": "text/javascript; charset=utf-8",
|
|
702
|
+
".json": "application/json",
|
|
703
|
+
".m4a": "audio/mp4",
|
|
704
|
+
".md": "text/markdown; charset=utf-8",
|
|
705
|
+
".midi": "audio/midi",
|
|
706
|
+
".mkv": "video/x-matroska",
|
|
707
|
+
".mov": "video/quicktime",
|
|
708
|
+
".mp3": "audio/mpeg",
|
|
709
|
+
".mp4": "video/mp4",
|
|
710
|
+
".oga": "audio/ogg",
|
|
711
|
+
".ogg": "audio/ogg",
|
|
712
|
+
".ogv": "video/ogg",
|
|
713
|
+
".pdf": "application/pdf",
|
|
714
|
+
".png": "image/png",
|
|
715
|
+
".sqlite3": "application/vnd.sqlite3",
|
|
716
|
+
".svg": "image/svg+xml",
|
|
717
|
+
".ts": "video/mp2t",
|
|
718
|
+
".txt": "text/plain; charset=utf-8",
|
|
719
|
+
".wav": "audio/wav",
|
|
720
|
+
".weba": "audio/webm",
|
|
721
|
+
".webm": "video/webm",
|
|
722
|
+
".webp": "image/webp",
|
|
723
|
+
".xml": "application/xml"
|
|
724
|
+
};
|
|
725
|
+
function normalizeMIMEType(mimetype) {
|
|
726
|
+
const idx = mimetype.indexOf(";");
|
|
727
|
+
return idx >= 0 ? mimetype.slice(0, idx).trim().toLowerCase() : mimetype.trim().toLowerCase();
|
|
728
|
+
}
|
|
729
|
+
function getExtension(mimetype) {
|
|
730
|
+
const normalized = normalizeMIMEType(mimetype);
|
|
731
|
+
if (!normalized)
|
|
732
|
+
return "";
|
|
733
|
+
return commonMimeExtensions[normalized] ?? "";
|
|
734
|
+
}
|
|
735
|
+
function getMIMEType(ext) {
|
|
736
|
+
if (!ext)
|
|
737
|
+
return "";
|
|
738
|
+
ext = ext.trim().toLowerCase();
|
|
739
|
+
if (ext[0] !== ".")
|
|
740
|
+
ext = "." + ext;
|
|
741
|
+
return commonExtensionMimes[ext] ?? "";
|
|
742
|
+
}
|
|
743
|
+
function getServersFromServerListEvent(event) {
|
|
744
|
+
const servers = [];
|
|
745
|
+
for (const tag of event.tags) {
|
|
746
|
+
if (tag[0] === "server" && tag[1]) {
|
|
747
|
+
try {
|
|
748
|
+
const url = new URL(tag[1]);
|
|
749
|
+
url.pathname = "/";
|
|
750
|
+
servers.push(url);
|
|
751
|
+
} catch {
|
|
40
752
|
}
|
|
41
753
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
754
|
+
}
|
|
755
|
+
return servers;
|
|
756
|
+
}
|
|
757
|
+
function getHashFromURL(url) {
|
|
758
|
+
const path = typeof url === "string" ? url.split("#", 1)[0].split("?", 1)[0] : url.pathname;
|
|
759
|
+
const lastSlash = path.lastIndexOf("/");
|
|
760
|
+
let segment = lastSlash === -1 ? path : path.slice(lastSlash + 1);
|
|
761
|
+
const dotIndex = segment.indexOf(".");
|
|
762
|
+
if (dotIndex !== -1 && dotIndex !== 64)
|
|
763
|
+
return null;
|
|
764
|
+
for (let i = 0; i < 64; i++) {
|
|
765
|
+
const c = segment.charCodeAt(i);
|
|
766
|
+
const isDigit = c >= 48 && c <= 57;
|
|
767
|
+
const isLowerHex = c >= 97 && c <= 102;
|
|
768
|
+
if (!isDigit && !isLowerHex)
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
return segment.slice(0, 64);
|
|
772
|
+
}
|
|
773
|
+
async function uploadBlob(server, blob, opts) {
|
|
774
|
+
const url = new URL("/upload", server).toString();
|
|
775
|
+
const sha2562 = await computeBlobSha256(blob);
|
|
776
|
+
const headers = {
|
|
777
|
+
"X-SHA-256": sha2562,
|
|
778
|
+
"Content-Type": getBlobType(blob) || "application/octet-stream"
|
|
779
|
+
};
|
|
780
|
+
if (opts?.auth) {
|
|
781
|
+
const authEvent = typeof opts.auth === "boolean" ? await opts.onAuth?.(server, sha2562) : opts.auth;
|
|
782
|
+
if (authEvent)
|
|
783
|
+
headers["Authorization"] = encodeAuthorizationHeader(authEvent);
|
|
784
|
+
}
|
|
785
|
+
const res = await fetch(url, {
|
|
786
|
+
method: "PUT",
|
|
787
|
+
body: blob,
|
|
788
|
+
headers,
|
|
789
|
+
signal: opts?.signal
|
|
790
|
+
});
|
|
791
|
+
if (res.status >= 300) {
|
|
792
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
793
|
+
throw new Error(`upload returned error (${res.status}): ${reason}`);
|
|
794
|
+
}
|
|
795
|
+
return res.json();
|
|
796
|
+
}
|
|
797
|
+
async function downloadBlob(server, hash, opts) {
|
|
798
|
+
const url = new URL("/" + hash, server).toString();
|
|
799
|
+
const headers = {};
|
|
800
|
+
if (opts?.auth) {
|
|
801
|
+
const authEvent = typeof opts.auth === "boolean" ? await opts.onAuth?.(server, hash) : opts.auth;
|
|
802
|
+
if (authEvent)
|
|
803
|
+
headers["Authorization"] = encodeAuthorizationHeader(authEvent);
|
|
804
|
+
}
|
|
805
|
+
const res = await fetch(url, { headers, signal: opts?.signal });
|
|
806
|
+
if (res.status >= 300) {
|
|
807
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
808
|
+
throw new Error(`${hash} download error (${res.status}): ${reason}`);
|
|
809
|
+
}
|
|
810
|
+
return res;
|
|
811
|
+
}
|
|
812
|
+
async function listBlobs(server, pubkey, opts) {
|
|
813
|
+
const url = new URL("/list/" + pubkey, server);
|
|
814
|
+
if (opts?.cursor)
|
|
815
|
+
url.searchParams.append("cursor", opts.cursor);
|
|
816
|
+
if (opts?.limit)
|
|
817
|
+
url.searchParams.append("limit", String(opts.limit));
|
|
818
|
+
if (opts?.since)
|
|
819
|
+
url.searchParams.append("since", String(opts.since));
|
|
820
|
+
if (opts?.until)
|
|
821
|
+
url.searchParams.append("until", String(opts.until));
|
|
822
|
+
const headers = {};
|
|
823
|
+
if (opts?.auth) {
|
|
824
|
+
const authEvent = typeof opts.auth === "boolean" ? await opts.onAuth?.(server) : opts.auth;
|
|
825
|
+
if (authEvent)
|
|
826
|
+
headers["Authorization"] = encodeAuthorizationHeader(authEvent);
|
|
827
|
+
}
|
|
828
|
+
const res = await fetch(url.toString(), { headers, signal: opts?.signal });
|
|
829
|
+
if (res.status >= 300) {
|
|
830
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
831
|
+
throw new Error(`list error (${res.status}): ${reason}`);
|
|
832
|
+
}
|
|
833
|
+
return res.json();
|
|
834
|
+
}
|
|
835
|
+
async function* iterateBlobs(server, pubkey, opts) {
|
|
836
|
+
let cursor = opts?.cursor;
|
|
837
|
+
while (true) {
|
|
838
|
+
const page = await listBlobs(server, pubkey, { ...opts, cursor });
|
|
839
|
+
if (page.length === 0)
|
|
840
|
+
return;
|
|
841
|
+
yield page;
|
|
842
|
+
if (opts?.limit && page.length < opts.limit)
|
|
843
|
+
return;
|
|
844
|
+
cursor = page[page.length - 1]?.sha256;
|
|
845
|
+
if (!cursor)
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
async function deleteBlob(server, hash, opts) {
|
|
850
|
+
const url = new URL("/" + hash, server).toString();
|
|
851
|
+
const headers = {};
|
|
852
|
+
if (opts?.auth) {
|
|
853
|
+
const authEvent = typeof opts.auth === "boolean" ? await opts.onAuth?.(server, hash) : opts.auth;
|
|
854
|
+
if (authEvent)
|
|
855
|
+
headers["Authorization"] = encodeAuthorizationHeader(authEvent);
|
|
856
|
+
}
|
|
857
|
+
const res = await fetch(url, { method: "DELETE", headers, signal: opts?.signal });
|
|
858
|
+
if (res.status >= 300) {
|
|
859
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
860
|
+
throw new Error(`delete error (${res.status}): ${reason}`);
|
|
861
|
+
}
|
|
862
|
+
return res.ok;
|
|
863
|
+
}
|
|
864
|
+
async function hasBlob(server, hash, opts) {
|
|
865
|
+
const url = new URL("/" + hash, server);
|
|
866
|
+
try {
|
|
867
|
+
const res = await fetch(url.toString(), { method: "HEAD", signal: opts?.signal });
|
|
868
|
+
return res.status !== 404;
|
|
869
|
+
} catch {
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
async function mirrorBlob(server, blob, opts) {
|
|
874
|
+
const url = new URL("/mirror", server).toString();
|
|
875
|
+
const headers = {
|
|
876
|
+
"Content-Type": "application/json",
|
|
877
|
+
"X-SHA-256": blob.sha256
|
|
878
|
+
};
|
|
879
|
+
if (opts?.auth) {
|
|
880
|
+
const authEvent = typeof opts.auth === "boolean" ? await opts.onAuth?.(server, blob.sha256) : opts.auth;
|
|
881
|
+
if (authEvent)
|
|
882
|
+
headers["Authorization"] = encodeAuthorizationHeader(authEvent);
|
|
883
|
+
}
|
|
884
|
+
const res = await fetch(url, {
|
|
885
|
+
method: "PUT",
|
|
886
|
+
body: JSON.stringify({ url: blob.url }),
|
|
887
|
+
headers,
|
|
888
|
+
signal: opts?.signal
|
|
889
|
+
});
|
|
890
|
+
if (res.status >= 300) {
|
|
891
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
892
|
+
throw new Error(`mirror error (${res.status}): ${reason}`);
|
|
893
|
+
}
|
|
894
|
+
return res.json();
|
|
895
|
+
}
|
|
896
|
+
async function reportBlobs(servers, report, opts) {
|
|
897
|
+
if (report.kind !== 1984 || !report.tags.some((tag) => tag[0] === "x" && !!tag[1])) {
|
|
898
|
+
throw new Error("Invalid blob report event: must be kind 1984 with x tag");
|
|
899
|
+
}
|
|
900
|
+
const results = /* @__PURE__ */ new Map();
|
|
901
|
+
const body = JSON.stringify(report);
|
|
902
|
+
for (const server of servers) {
|
|
903
|
+
try {
|
|
904
|
+
const res = await fetch(new URL("/report", server).toString(), {
|
|
905
|
+
method: "PUT",
|
|
906
|
+
headers: { "Content-Type": "application/json" },
|
|
907
|
+
body,
|
|
908
|
+
signal: opts?.signal
|
|
909
|
+
});
|
|
910
|
+
if (res.status >= 300) {
|
|
911
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
912
|
+
throw new Error(`report error (${res.status}): ${reason}`);
|
|
913
|
+
}
|
|
914
|
+
results.set(server, true);
|
|
915
|
+
} catch (error) {
|
|
916
|
+
if (opts?.onError && error instanceof Error)
|
|
917
|
+
opts.onError(server, error);
|
|
50
918
|
}
|
|
51
|
-
|
|
52
|
-
|
|
919
|
+
}
|
|
920
|
+
return results;
|
|
921
|
+
}
|
|
922
|
+
var BlossomClient = class {
|
|
923
|
+
constructor(mediaserver, signer) {
|
|
924
|
+
this.signer = signer;
|
|
925
|
+
if (!mediaserver.startsWith("http")) {
|
|
926
|
+
mediaserver = "https://" + mediaserver;
|
|
53
927
|
}
|
|
54
|
-
|
|
928
|
+
this.mediaserver = mediaserver.replace(/\/$/, "") + "/";
|
|
929
|
+
}
|
|
930
|
+
mediaserver;
|
|
931
|
+
getMediaServer() {
|
|
932
|
+
return this.mediaserver;
|
|
55
933
|
}
|
|
56
934
|
async authorizationHeader(modify) {
|
|
57
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
58
935
|
const event = {
|
|
59
|
-
created_at: now,
|
|
60
|
-
kind:
|
|
936
|
+
created_at: now(),
|
|
937
|
+
kind: kinds_exports.BlobsAuth,
|
|
61
938
|
content: "blossom stuff",
|
|
62
|
-
tags: [["expiration", String(now + 60)]]
|
|
939
|
+
tags: [["expiration", String(now() + 60)]]
|
|
63
940
|
};
|
|
64
|
-
|
|
65
|
-
modify(event);
|
|
66
|
-
}
|
|
941
|
+
modify?.(event);
|
|
67
942
|
try {
|
|
68
943
|
const signedEvent = await this.signer.signEvent(event);
|
|
69
|
-
const
|
|
70
|
-
return "Nostr " + btoa(
|
|
71
|
-
} catch
|
|
944
|
+
const json = JSON.stringify(signedEvent);
|
|
945
|
+
return "Nostr " + btoa(json);
|
|
946
|
+
} catch {
|
|
72
947
|
return "";
|
|
73
948
|
}
|
|
74
949
|
}
|
|
75
|
-
async
|
|
76
|
-
|
|
77
|
-
|
|
950
|
+
async httpCall(method, url, contentType, addAuthorization, body) {
|
|
951
|
+
const headers = {};
|
|
952
|
+
if (contentType)
|
|
953
|
+
headers["Content-Type"] = contentType;
|
|
954
|
+
if (addAuthorization) {
|
|
955
|
+
const auth = await addAuthorization();
|
|
956
|
+
if (auth)
|
|
957
|
+
headers["Authorization"] = auth;
|
|
78
958
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
throw new Error(
|
|
959
|
+
const res = await fetch(this.mediaserver + url, { method, headers, body });
|
|
960
|
+
if (res.status >= 300) {
|
|
961
|
+
const reason = res.headers.get("X-Reason") || res.statusText;
|
|
962
|
+
throw new Error(`${url} returned error (${res.status}): ${reason}`);
|
|
83
963
|
}
|
|
964
|
+
if (res.headers.get("content-type")?.includes("application/json")) {
|
|
965
|
+
return res.json();
|
|
966
|
+
}
|
|
967
|
+
return res;
|
|
84
968
|
}
|
|
85
969
|
async uploadBlob(file, contentType) {
|
|
86
|
-
const hash =
|
|
87
|
-
const actualContentType = contentType || file
|
|
88
|
-
|
|
970
|
+
const hash = bytesToHex2(sha256(new Uint8Array(await file.arrayBuffer())));
|
|
971
|
+
const actualContentType = contentType || getBlobType(file) || "application/octet-stream";
|
|
972
|
+
return this.httpCall(
|
|
89
973
|
"PUT",
|
|
90
974
|
"upload",
|
|
91
975
|
actualContentType,
|
|
92
976
|
() => this.authorizationHeader((evt) => {
|
|
93
|
-
evt.tags.push(["t", "upload"]);
|
|
94
|
-
evt.tags.push(["x", hash]);
|
|
977
|
+
evt.tags.push(["t", "upload"], ["x", hash]);
|
|
95
978
|
}),
|
|
96
|
-
file
|
|
97
|
-
{}
|
|
979
|
+
file
|
|
98
980
|
);
|
|
99
|
-
return bd;
|
|
100
981
|
}
|
|
101
982
|
async uploadFile(file) {
|
|
102
983
|
return this.uploadBlob(file, file.type);
|
|
103
984
|
}
|
|
104
985
|
async download(hash) {
|
|
105
|
-
if (!isHex32(hash))
|
|
986
|
+
if (!isHex32(hash))
|
|
106
987
|
throw new Error(`${hash} is not a valid 32-byte hex string`);
|
|
107
|
-
}
|
|
108
988
|
const authHeader = await this.authorizationHeader((evt) => {
|
|
109
|
-
evt.tags.push(["t", "get"]);
|
|
110
|
-
evt.tags.push(["x", hash]);
|
|
989
|
+
evt.tags.push(["t", "get"], ["x", hash]);
|
|
111
990
|
});
|
|
112
|
-
const
|
|
991
|
+
const res = await fetch(this.mediaserver + hash, {
|
|
113
992
|
method: "GET",
|
|
114
|
-
headers: {
|
|
115
|
-
Authorization: authHeader
|
|
116
|
-
}
|
|
993
|
+
headers: { Authorization: authHeader }
|
|
117
994
|
});
|
|
118
|
-
if (
|
|
119
|
-
throw new Error(`${hash}
|
|
995
|
+
if (res.status >= 300) {
|
|
996
|
+
throw new Error(`${hash} not present on ${this.mediaserver}: ${res.status}`);
|
|
120
997
|
}
|
|
121
|
-
return
|
|
998
|
+
return res.arrayBuffer();
|
|
122
999
|
}
|
|
123
1000
|
async downloadAsBlob(hash) {
|
|
124
|
-
|
|
125
|
-
return new Blob([arrayBuffer]);
|
|
1001
|
+
return new Blob([await this.download(hash)]);
|
|
126
1002
|
}
|
|
127
1003
|
async list() {
|
|
128
1004
|
const pubkey = await this.signer.getPublicKey();
|
|
129
1005
|
if (!isHex32(pubkey)) {
|
|
130
1006
|
throw new Error(`pubkey ${pubkey} is not valid`);
|
|
131
1007
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
void 0,
|
|
141
|
-
[]
|
|
142
|
-
);
|
|
143
|
-
return bds;
|
|
144
|
-
} catch (error) {
|
|
145
|
-
throw new Error(`failed to list blobs: ${error}`);
|
|
146
|
-
}
|
|
1008
|
+
return this.httpCall(
|
|
1009
|
+
"GET",
|
|
1010
|
+
`list/${pubkey}`,
|
|
1011
|
+
void 0,
|
|
1012
|
+
() => this.authorizationHeader((evt) => {
|
|
1013
|
+
evt.tags.push(["t", "list"]);
|
|
1014
|
+
})
|
|
1015
|
+
);
|
|
147
1016
|
}
|
|
148
1017
|
async delete(hash) {
|
|
149
|
-
if (!isHex32(hash))
|
|
1018
|
+
if (!isHex32(hash))
|
|
150
1019
|
throw new Error(`${hash} is not a valid 32-byte hex string`);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
1020
|
+
await this.httpCall(
|
|
1021
|
+
"DELETE",
|
|
1022
|
+
hash,
|
|
1023
|
+
void 0,
|
|
1024
|
+
() => this.authorizationHeader((evt) => {
|
|
1025
|
+
evt.tags.push(["t", "delete"], ["x", hash]);
|
|
1026
|
+
})
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
async check(hash) {
|
|
1030
|
+
if (!isHex32(hash))
|
|
1031
|
+
throw new Error(`${hash} is not valid 32-byte hex`);
|
|
1032
|
+
await this.httpCall("HEAD", hash);
|
|
1033
|
+
}
|
|
1034
|
+
async mirror(remoteBlobURL) {
|
|
1035
|
+
const hash = remoteBlobURL.split("/").pop()?.split(".")[0] || "";
|
|
1036
|
+
return this.httpCall(
|
|
1037
|
+
"PUT",
|
|
1038
|
+
"mirror",
|
|
1039
|
+
"application/json",
|
|
1040
|
+
() => this.authorizationHeader((evt) => {
|
|
1041
|
+
evt.tags.push(["t", "upload"], ["x", hash]);
|
|
1042
|
+
}),
|
|
1043
|
+
JSON.stringify({ url: remoteBlobURL })
|
|
1044
|
+
);
|
|
167
1045
|
}
|
|
168
1046
|
};
|
|
169
1047
|
export {
|
|
170
|
-
BlossomClient
|
|
1048
|
+
BlossomClient,
|
|
1049
|
+
areServersEqual,
|
|
1050
|
+
blossomURIFromURL,
|
|
1051
|
+
blossomURIToURL,
|
|
1052
|
+
buildBlossomURI,
|
|
1053
|
+
computeBlobSha256,
|
|
1054
|
+
createAuthEvent,
|
|
1055
|
+
createDeleteAuth,
|
|
1056
|
+
createDownloadAuth,
|
|
1057
|
+
createListAuth,
|
|
1058
|
+
createMirrorAuth,
|
|
1059
|
+
createUploadAuth,
|
|
1060
|
+
deleteBlob,
|
|
1061
|
+
downloadBlob,
|
|
1062
|
+
encodeAuthorizationHeader,
|
|
1063
|
+
getAuthExpiration,
|
|
1064
|
+
getAuthTagValues,
|
|
1065
|
+
getBlobSize,
|
|
1066
|
+
getBlobType,
|
|
1067
|
+
getExtension,
|
|
1068
|
+
getHashFromURL,
|
|
1069
|
+
getMIMEType,
|
|
1070
|
+
getServersFromServerListEvent,
|
|
1071
|
+
hasBlob,
|
|
1072
|
+
isAuthExpired,
|
|
1073
|
+
iterateBlobs,
|
|
1074
|
+
listBlobs,
|
|
1075
|
+
mirrorBlob,
|
|
1076
|
+
normalizeServerTag,
|
|
1077
|
+
now,
|
|
1078
|
+
oneHour,
|
|
1079
|
+
parseBlossomURI,
|
|
1080
|
+
reportBlobs,
|
|
1081
|
+
uploadBlob
|
|
171
1082
|
};
|