nappup 2.3.6 → 2.3.8
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 +2 -2
- package/src/errors.js +46 -2
- package/src/helpers/signer.js +8 -17
- package/src/index.js +10 -7
- package/src/services/blossom-upload.js +7 -0
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": "2.3.
|
|
9
|
+
"version": "2.3.8",
|
|
10
10
|
"description": "Nostr App Uploader",
|
|
11
11
|
"type": "module",
|
|
12
12
|
"scripts": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@noble/hashes": "^2.0.0",
|
|
23
23
|
"dotenv": "^17.2.0",
|
|
24
24
|
"file-type": "^21.0.0",
|
|
25
|
-
"libp2r2p": "^0.10.
|
|
25
|
+
"libp2r2p": "^0.10.6",
|
|
26
26
|
"mime-types": "^3.0.1",
|
|
27
27
|
"nmmr": "^2.0.0"
|
|
28
28
|
},
|
package/src/errors.js
CHANGED
|
@@ -12,6 +12,8 @@ export const NAPPUP_ERROR_CODES = Object.freeze({
|
|
|
12
12
|
BLOSSOM_UPLOAD_FAILED: 'NAPPUP_BLOSSOM_UPLOAD_FAILED',
|
|
13
13
|
IRFS_UPLOAD_FAILED: 'NAPPUP_IRFS_UPLOAD_FAILED',
|
|
14
14
|
MANIFEST_UPLOAD_FAILED: 'NAPPUP_MANIFEST_UPLOAD_FAILED',
|
|
15
|
+
SIGNER_LOCKED: 'NAPPUP_SIGNER_LOCKED',
|
|
16
|
+
SIGNER_DENIED: 'NAPPUP_SIGNER_DENIED',
|
|
15
17
|
UPLOAD_FAILED: 'NAPPUP_UPLOAD_FAILED'
|
|
16
18
|
})
|
|
17
19
|
|
|
@@ -60,9 +62,51 @@ export class NappupError extends Error {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
const SIGNER_LOCKED_PATTERNS = [
|
|
66
|
+
/^VAULT_LOCKED$/,
|
|
67
|
+
/vault (?:is )?locked/i,
|
|
68
|
+
/account (?:is )?locked/i,
|
|
69
|
+
/wallet (?:is )?locked/i
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
const SIGNER_DENIED_PATTERNS = [
|
|
73
|
+
/permission denied/i,
|
|
74
|
+
/user (?:rejected|denied)/i,
|
|
75
|
+
/sign(?:ing)? request (?:rejected|denied)/i
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
// Walks the cause chain looking for signer-level failures such as a locked
|
|
79
|
+
// vault or a rejected signing prompt. Returns a NAPPUP_* code or null.
|
|
80
|
+
export function classifySignerError (error) {
|
|
81
|
+
let current = error
|
|
82
|
+
for (let depth = 0; current && depth < 6; depth++) {
|
|
83
|
+
if (current.name === 'NotAllowedError' || current.code === 'DENIED_BY_USER') {
|
|
84
|
+
return NAPPUP_ERROR_CODES.SIGNER_DENIED
|
|
85
|
+
}
|
|
86
|
+
const message = typeof current.message === 'string' ? current.message : ''
|
|
87
|
+
if (SIGNER_LOCKED_PATTERNS.some(pattern => pattern.test(message))) {
|
|
88
|
+
return NAPPUP_ERROR_CODES.SIGNER_LOCKED
|
|
89
|
+
}
|
|
90
|
+
if (SIGNER_DENIED_PATTERNS.some(pattern => pattern.test(message))) {
|
|
91
|
+
return NAPPUP_ERROR_CODES.SIGNER_DENIED
|
|
92
|
+
}
|
|
93
|
+
current = current.cause
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
|
|
63
98
|
// Ensures every error crossing nappup's public API has a documented code.
|
|
64
99
|
export function normalizeNappupError (error) {
|
|
65
|
-
if (typeof error?.code === 'string' && error.code.startsWith('NAPPUP_'))
|
|
100
|
+
if (typeof error?.code === 'string' && error.code.startsWith('NAPPUP_')) {
|
|
101
|
+
const signerCode = classifySignerError(error)
|
|
102
|
+
if (signerCode && signerCode !== error.code) {
|
|
103
|
+
return new NappupError(signerCode, error.message, {
|
|
104
|
+
cause: error.cause,
|
|
105
|
+
details: error.details
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
return error
|
|
109
|
+
}
|
|
66
110
|
if (error?.name === 'AbortError' || error?.code === 'ABORT_ERR') {
|
|
67
111
|
return new NappupError(
|
|
68
112
|
NAPPUP_ERROR_CODES.UPLOAD_CANCELLED,
|
|
@@ -71,7 +115,7 @@ export function normalizeNappupError (error) {
|
|
|
71
115
|
)
|
|
72
116
|
}
|
|
73
117
|
return new NappupError(
|
|
74
|
-
NAPPUP_ERROR_CODES.UPLOAD_FAILED,
|
|
118
|
+
classifySignerError(error) ?? NAPPUP_ERROR_CODES.UPLOAD_FAILED,
|
|
75
119
|
error?.message || 'Upload failed',
|
|
76
120
|
{ cause: error }
|
|
77
121
|
)
|
package/src/helpers/signer.js
CHANGED
|
@@ -1,27 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { freeRelays, getRelaysByPubkey } from 'libp2r2p/relay'
|
|
2
2
|
|
|
3
|
-
export async function getRelays () {
|
|
3
|
+
export async function getRelays ({ _getRelaysByPubkey = getRelaysByPubkey } = {}) {
|
|
4
4
|
if (this.relays) return this.relays
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
6
|
+
const pubkey = await this.getPublicKey()
|
|
7
|
+
const relaysByPubkey = await _getRelaysByPubkey([pubkey])
|
|
8
|
+
const relays = relaysByPubkey[pubkey]
|
|
9
|
+
if (!relays.read.length && !relays.write.length) {
|
|
10
10
|
const defaults = freeRelays.slice(0, 2)
|
|
11
|
-
|
|
11
|
+
relays.read.push(...defaults)
|
|
12
|
+
relays.write.push(...defaults)
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
let keys
|
|
15
|
-
const keyAllowList = { read: true, write: true }
|
|
16
|
-
const relays = rTags.reduce((r, v) => {
|
|
17
|
-
keys = [v[2]].filter(v2 => keyAllowList[v2])
|
|
18
|
-
if (keys.length === 0) keys = ['read', 'write']
|
|
19
|
-
keys.forEach(k => r[k].push(v[1].trim().replace(/\/$/, '')))
|
|
20
|
-
return r
|
|
21
|
-
}, { read: [], write: [] })
|
|
22
14
|
for (const k in relays) {
|
|
23
15
|
if (relays[k].length === 0) relays[k].push(...freeRelays)
|
|
24
|
-
relays[k] = [...new Set(relays[k])]
|
|
25
16
|
}
|
|
26
17
|
return (this.relays = relays)
|
|
27
18
|
}
|
package/src/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { extractHtmlMetadata, findAppIcon, findIndexFile } from '#helpers/app-me
|
|
|
8
8
|
import { getBlossomServers, healthCheckServers, uploadFilesToBlossom } from '#services/blossom-upload.js'
|
|
9
9
|
import { uploadBinaryDataChunks } from '#services/irfs-upload.js'
|
|
10
10
|
import { uploadSiteManifest } from '#services/site-manifest.js'
|
|
11
|
-
import { NappupError, NAPPUP_ERROR_CODES, normalizeNappupError } from '#errors.js'
|
|
11
|
+
import { NappupError, NAPPUP_ERROR_CODES, normalizeNappupError, classifySignerError } from '#errors.js'
|
|
12
12
|
|
|
13
13
|
export { NappupError, NAPPUP_ERROR_CODES } from '#errors.js'
|
|
14
14
|
|
|
@@ -301,15 +301,18 @@ async function publishApp (fileList, nostrSigner, {
|
|
|
301
301
|
log
|
|
302
302
|
})
|
|
303
303
|
if (failedFiles.length) {
|
|
304
|
+
const signerError = failedFiles
|
|
305
|
+
.flatMap(failed => failed.errors ?? [])
|
|
306
|
+
.map(failed => failed.error)
|
|
307
|
+
.find(error => classifySignerError(error))
|
|
308
|
+
const details = {
|
|
309
|
+
failedFileCount: failedFiles.length,
|
|
310
|
+
filenames: failedFiles.map(failed => failed.filename).filter(Boolean)
|
|
311
|
+
}
|
|
304
312
|
throw new NappupError(
|
|
305
313
|
NAPPUP_ERROR_CODES.BLOSSOM_UPLOAD_FAILED,
|
|
306
314
|
`${failedFiles.length} file(s) failed to upload to Blossom`,
|
|
307
|
-
{
|
|
308
|
-
details: {
|
|
309
|
-
failedFileCount: failedFiles.length,
|
|
310
|
-
filenames: failedFiles.map(failed => failed.filename).filter(Boolean)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
315
|
+
signerError ? { cause: signerError, details } : { details }
|
|
313
316
|
)
|
|
314
317
|
}
|
|
315
318
|
|
|
@@ -2,6 +2,7 @@ import { sha256 } from '@noble/hashes/sha2.js'
|
|
|
2
2
|
import nostrRelays from '#services/nostr-relays.js'
|
|
3
3
|
import { bytesToBase16 } from '#helpers/base16.js'
|
|
4
4
|
import { normalizeBlossomServerUrl } from 'libp2r2p/url'
|
|
5
|
+
import { classifySignerError } from '#errors.js'
|
|
5
6
|
|
|
6
7
|
const DEFAULT_HEALTH_CHECK_TIMEOUT_MS = 5000
|
|
7
8
|
const DEFAULT_EXISTENCE_CHECK_TIMEOUT_MS = 5000
|
|
@@ -149,6 +150,12 @@ async function uploadFileToServer (serverUrl, signer, file, fileHash, mimeType,
|
|
|
149
150
|
const descriptor = await response.json()
|
|
150
151
|
return { success: true, descriptor }
|
|
151
152
|
} catch (err) {
|
|
153
|
+
if (classifySignerError(err)) {
|
|
154
|
+
// Signer-level failures (locked vault, rejected prompt) are not
|
|
155
|
+
// transient upload errors: retrying would just re-prompt or fail
|
|
156
|
+
// again, so surface them immediately.
|
|
157
|
+
return { success: false, error: err }
|
|
158
|
+
}
|
|
152
159
|
if (attempt === maxRetries) {
|
|
153
160
|
return { success: false, error: err }
|
|
154
161
|
}
|