nappup 1.4.2 → 1.5.2

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/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "git+https://github.com/44billion/nappup.git"
7
7
  },
8
8
  "license": "MIT",
9
- "version": "1.4.2",
9
+ "version": "1.5.2",
10
10
  "description": "Nostr App Uploader",
11
11
  "type": "module",
12
12
  "scripts": {
package/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import NMMR from 'nmmr'
2
2
  import { appEncode } from '#helpers/nip19.js'
3
3
  import Base93Encoder from '#services/base93-encoder.js'
4
- import nostrRelays from '#services/nostr-relays.js'
4
+ import nostrRelays, { nappRelays } from '#services/nostr-relays.js'
5
5
  import NostrSigner from '#services/nostr-signer.js'
6
6
  import { streamToChunks, streamToText } from '#helpers/stream.js'
7
7
  import { isNostrAppDTagSafe, deriveNostrAppDTag } from '#helpers/app.js'
@@ -151,8 +151,8 @@ export async function toApp (fileList, nostrSigner, { log = () => {}, dTag, dTag
151
151
  hashtags: nappJson.hashtag
152
152
  })))
153
153
 
154
- log(`Uploading bundle #${dTag}`)
155
- const bundle = await uploadBundle({ dTag, channel, fileMetadata, signer: nostrSigner, pause })
154
+ log(`Uploading bundle ${dTag}`)
155
+ const bundle = await uploadBundle({ dTag, channel, fileMetadata, signer: nostrSigner, pause, shouldReupload, log })
156
156
 
157
157
  const appEntity = appEncode({
158
158
  dTag: bundle.tags.find(v => v[0] === 'd')[1],
@@ -188,8 +188,10 @@ async function uploadBinaryDataChunks ({ nmmr, signer, filename, chunkLength, lo
188
188
  }
189
189
 
190
190
  const event = await signer.signEvent(binaryDataChunk)
191
- log(`${filename}: Uploading file part ${++chunkIndex} of ${chunkLength} to ${writeRelays.length} relays`)
192
- ;({ pause } = (await throttledSendEvent(event, writeRelays, { pause, log, trailingPause: true })))
191
+ const relays = [...new Set([...writeRelays, ...nappRelays])]
192
+ const fallbackRelayCount = relays.length - writeRelays.length
193
+ log(`${filename}: Uploading file part ${++chunkIndex} of ${chunkLength} to ${writeRelays.length} relays${fallbackRelayCount > 0 ? ` (+${fallbackRelayCount} fallback)` : ''}`)
194
+ ;({ pause } = (await throttledSendEvent(event, relays, { pause, log, trailingPause: true })))
193
195
  }
194
196
  return { pause }
195
197
  }
@@ -245,7 +247,7 @@ async function getPreviousCtags (dTagValue, currentCtagValue, writeRelays, signe
245
247
  authors: [await signer.getPublicKey()],
246
248
  '#d': [dTagValue],
247
249
  limit: 1
248
- }, writeRelays)).result
250
+ }, [...new Set([...writeRelays, ...nappRelays])])).result
249
251
 
250
252
  let hasCurrentCtag = false
251
253
  const hasEvent = storedEvents.length > 0
@@ -278,23 +280,65 @@ async function getPreviousCtags (dTagValue, currentCtagValue, writeRelays, signe
278
280
  return { otherCtags, hasEvent, hasCurrentCtag }
279
281
  }
280
282
 
281
- async function uploadBundle ({ dTag, channel, fileMetadata, signer, pause = 0 }) {
283
+ async function uploadBundle ({ dTag, channel, fileMetadata, signer, pause = 0, shouldReupload = false, log = () => {} }) {
282
284
  const kind = {
283
285
  main: 37448, // stable
284
286
  next: 37449, // insider
285
287
  draft: 37450 // vibe coded preview
286
288
  }[channel] ?? 37448
289
+
290
+ const fileTags = fileMetadata.map(v => ['file', v.rootHash, v.filename, v.mimeType])
291
+ const tags = [
292
+ ['d', dTag],
293
+ ...fileTags
294
+ ]
295
+
296
+ const writeRelays = [...new Set([...(await signer.getRelays()).write, ...nappRelays])]
297
+
298
+ if (!shouldReupload) {
299
+ const events = (await nostrRelays.getEvents({
300
+ kinds: [kind],
301
+ authors: [await signer.getPublicKey()],
302
+ '#d': [dTag]
303
+ }, writeRelays)).result
304
+
305
+ if (events.length > 0) {
306
+ events.sort((a, b) => {
307
+ if (b.created_at !== a.created_at) return b.created_at - a.created_at
308
+ if (a.id < b.id) return -1
309
+ if (a.id > b.id) return 1
310
+ return 0
311
+ })
312
+
313
+ const mostRecentEvent = events[0]
314
+ const recentFileTags = mostRecentEvent.tags.filter(t => t[0] === 'file')
315
+
316
+ const isSame = fileTags.length === recentFileTags.length && fileTags.every((t, i) => {
317
+ const rt = recentFileTags[i]
318
+ return rt.length >= 4 && rt[1] === t[1] && rt[2] === t[2] && rt[3] === t[3]
319
+ })
320
+
321
+ if (isSame) {
322
+ log(`Bundle based on ${fileTags.length} files is up to date (id: ${mostRecentEvent.id} - created_at: ${new Date(mostRecentEvent.created_at * 1000).toISOString()})`)
323
+ if (events.length === writeRelays.length && events.every(e => e.id === mostRecentEvent.id)) return mostRecentEvent
324
+
325
+ // nostrRelays.getEvents currently doesn't tell us which event came from which relay,
326
+ // so we re-upload to all relays to ensure consistency
327
+ log(`Re-uploading existing bundle event to all ${writeRelays.length} relays`)
328
+ await throttledSendEvent(mostRecentEvent, writeRelays, { pause, trailingPause: true, log })
329
+ return mostRecentEvent
330
+ }
331
+ }
332
+ }
333
+
287
334
  const appBundle = {
288
335
  kind,
289
- tags: [
290
- ['d', dTag],
291
- ...fileMetadata.map(v => ['file', v.rootHash, v.filename, v.mimeType])
292
- ],
336
+ tags,
293
337
  content: '',
294
338
  created_at: Math.floor(Date.now() / 1000)
295
339
  }
296
340
  const event = await signer.signEvent(appBundle)
297
- await throttledSendEvent(event, (await signer.getRelays()).write, { pause, trailingPause: true })
341
+ await throttledSendEvent(event, writeRelays, { pause, trailingPause: true, log })
298
342
  return event
299
343
  }
300
344
 
@@ -330,8 +374,7 @@ async function maybeUploadStall ({
330
374
 
331
375
  const publishStall = async (event) => {
332
376
  const signedEvent = await signer.signEvent(event)
333
- // App stores are fetching stall events just from 44b relay for now
334
- const relays = [...new Set([...writeRelays, 'wss://relay.44billion.net'])]
377
+ const relays = [...new Set([...writeRelays, ...nappRelays])]
335
378
  return await throttledSendEvent(signedEvent, relays, { pause, log, trailingPause: true })
336
379
  }
337
380
 
@@ -579,7 +622,7 @@ async function getPreviousStall (dTagValue, writeRelays, signer, channel) {
579
622
  authors: [await signer.getPublicKey()],
580
623
  '#d': [dTagValue],
581
624
  limit: 1
582
- }, writeRelays)).result
625
+ }, [...new Set([...writeRelays, ...nappRelays])])).result
583
626
 
584
627
  if (storedEvents.length === 0) return null
585
628
  return storedEvents.sort((a, b) => b.created_at - a.created_at)[0]
@@ -15,6 +15,9 @@ export const freeRelays = [
15
15
  'wss://relay.damus.io',
16
16
  'wss://relay.nostr.band'
17
17
  ]
18
+ export const nappRelays = [
19
+ 'wss://relay.44billion.net'
20
+ ]
18
21
 
19
22
  // Interacts with Nostr relays
20
23
  export class NostrRelays {