libp2r2p 0.8.0 → 0.9.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.
- package/README.md +83 -2
- package/base16/index.js +6 -3
- package/base36/index.js +12 -8
- package/base62/index.js +15 -11
- package/base64/index.js +18 -2
- package/base93/index.js +19 -7
- package/content-key/event/index.js +40 -12
- package/double-dh/index.js +21 -6
- package/ecdh/index.js +12 -1
- package/error/index.js +32 -0
- package/event/helpers/serialize.js +31 -0
- package/event/index.js +116 -0
- package/i18n/index.js +15 -13
- package/idb/index.js +5 -3
- package/idb-queue/index.js +21 -20
- package/index.js +10 -0
- package/key/index.js +31 -18
- package/kind/index.js +244 -0
- package/nip04/index.js +47 -0
- package/nip05/index.js +61 -0
- package/nip19/index.js +176 -43
- package/nip44/helpers.js +95 -0
- package/nip44/index.js +14 -0
- package/nip44-v3/index.js +35 -16
- package/nip46/helpers/frame.js +6 -6
- package/nip46/helpers/url.js +8 -6
- package/nip46/services/bunker-signer.js +7 -6
- package/nip46/services/client.js +8 -7
- package/nip46/services/server-session.js +8 -7
- package/nip46/services/transport.js +9 -8
- package/nip96/index.js +285 -0
- package/nip98/index.js +56 -0
- package/nwt/index.js +241 -0
- package/package.json +22 -3
- package/private-channel/helpers/chunks.js +7 -6
- package/private-channel/helpers/event.js +5 -4
- package/private-channel/index.js +63 -61
- package/private-channel/services/received-chunks.js +4 -3
- package/private-message/index.js +32 -31
- package/private-messenger/index.js +23 -22
- package/private-messenger/recovery/index.js +15 -14
- package/private-messenger/services/channel-state.js +2 -1
- package/relay/helpers/hll.js +3 -1
- package/relay/services/query.js +3 -3
- package/relay/services/relay-connection.js +222 -121
- package/relay/services/relay-pool.js +13 -10
- package/temporary-storage/index.js +3 -1
- package/url/index.js +131 -0
- package/web-storage-queue/index.js +8 -6
package/event/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { schnorr, secp256k1 } from '@noble/curves/secp256k1.js'
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js'
|
|
3
|
+
|
|
4
|
+
import { bytesToBase16, base16ToBytes } from '../base16/index.js'
|
|
5
|
+
import { ValidationError } from '../error/index.js'
|
|
6
|
+
import {
|
|
7
|
+
isAddressableKind,
|
|
8
|
+
isEphemeralKind,
|
|
9
|
+
isRegularKind,
|
|
10
|
+
isReplaceableKind
|
|
11
|
+
} from '../kind/index.js'
|
|
12
|
+
import {
|
|
13
|
+
assertSerializableEvent,
|
|
14
|
+
isSerializableEvent,
|
|
15
|
+
serializeEvent,
|
|
16
|
+
serializableEventError
|
|
17
|
+
} from './helpers/serialize.js'
|
|
18
|
+
|
|
19
|
+
const HEX_32 = /^[0-9a-f]{64}$/
|
|
20
|
+
const HEX_64 = /^[0-9a-f]{128}$/
|
|
21
|
+
const textEncoder = new TextEncoder()
|
|
22
|
+
|
|
23
|
+
function copyTags (tags) {
|
|
24
|
+
return Array.isArray(tags) ? tags.map(tag => Array.isArray(tag) ? tag.slice() : tag) : tags
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { assertSerializableEvent, isSerializableEvent }
|
|
28
|
+
|
|
29
|
+
export function getEventHash (event) {
|
|
30
|
+
return bytesToBase16(sha256(textEncoder.encode(serializeEvent(event))))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function finalizeEvent (template, secretKey) {
|
|
34
|
+
if (!(secretKey instanceof Uint8Array) || secretKey.length !== 32 || !secp256k1.utils.isValidSecretKey(secretKey)) {
|
|
35
|
+
throw new ValidationError('INVALID_SECRET_KEY')
|
|
36
|
+
}
|
|
37
|
+
const pubkey = bytesToBase16(schnorr.getPublicKey(secretKey))
|
|
38
|
+
const event = { ...template, tags: copyTags(template?.tags), pubkey }
|
|
39
|
+
const id = getEventHash(event)
|
|
40
|
+
return { ...event, id, sig: bytesToBase16(schnorr.sign(base16ToBytes(id), secretKey)) }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function eventValidationError (event) {
|
|
44
|
+
const structureError = serializableEventError(event)
|
|
45
|
+
if (structureError) return structureError
|
|
46
|
+
if (typeof event.id !== 'string' || !HEX_32.test(event.id)) return 'INVALID_EVENT_ID'
|
|
47
|
+
if (typeof event.sig !== 'string' || !HEX_64.test(event.sig)) return 'INVALID_EVENT_SIGNATURE'
|
|
48
|
+
try {
|
|
49
|
+
const id = getEventHash(event)
|
|
50
|
+
if (id !== event.id) return 'EVENT_ID_MISMATCH'
|
|
51
|
+
if (!schnorr.verify(base16ToBytes(event.sig), base16ToBytes(id), base16ToBytes(event.pubkey))) {
|
|
52
|
+
return 'INVALID_EVENT_SIGNATURE'
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
return 'INVALID_EVENT_SIGNATURE'
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isValidEvent (event) {
|
|
61
|
+
return eventValidationError(event) === null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function assertValidEvent (event) {
|
|
65
|
+
const code = eventValidationError(event)
|
|
66
|
+
if (code) throw new ValidationError(code)
|
|
67
|
+
return event
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function hasTagDefinedClassification (event, classification) {
|
|
71
|
+
if (!event || typeof event !== 'object' || !Array.isArray(event.tags)) return false
|
|
72
|
+
|
|
73
|
+
if (classification === 'ephemeral') {
|
|
74
|
+
if (!Number.isSafeInteger(event.created_at) || event.created_at < 0) return false
|
|
75
|
+
const timestamp = String(event.created_at)
|
|
76
|
+
return event.tags.some(tag => Array.isArray(tag) && tag[0] === 'expiration' && tag[1] === timestamp)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const dTag = event.tags.find(tag => Array.isArray(tag) && tag[0] === 'd')
|
|
80
|
+
if (typeof dTag?.[1] !== 'string') return false
|
|
81
|
+
if (classification === 'replaceable') return dTag[1] === ''
|
|
82
|
+
if (classification === 'addressable') return dTag[1] !== ''
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function classifyEvent (event, { includeLegacyKindRanges = true } = {}) {
|
|
87
|
+
if (!event || typeof event !== 'object') return []
|
|
88
|
+
const options = { includeLegacyKindRanges }
|
|
89
|
+
return [
|
|
90
|
+
['regular', isRegularEvent],
|
|
91
|
+
['replaceable', isReplaceableEvent],
|
|
92
|
+
['ephemeral', isEphemeralEvent],
|
|
93
|
+
['addressable', isAddressableEvent]
|
|
94
|
+
].filter(([, predicate]) => predicate(event, options)).map(([classification]) => classification)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function isRegularEvent (event, options) {
|
|
98
|
+
if (!event || typeof event !== 'object') return false
|
|
99
|
+
return (options?.includeLegacyKindRanges !== false && isRegularKind(event.kind)) ||
|
|
100
|
+
(!isReplaceableEvent(event, options) && !isAddressableEvent(event, options))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isReplaceableEvent (event, options) {
|
|
104
|
+
return (options?.includeLegacyKindRanges !== false && isReplaceableKind(event?.kind)) ||
|
|
105
|
+
hasTagDefinedClassification(event, 'replaceable')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function isEphemeralEvent (event, options) {
|
|
109
|
+
return (options?.includeLegacyKindRanges !== false && isEphemeralKind(event?.kind)) ||
|
|
110
|
+
hasTagDefinedClassification(event, 'ephemeral')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function isAddressableEvent (event, options) {
|
|
114
|
+
return (options?.includeLegacyKindRanges !== false && isAddressableKind(event?.kind)) ||
|
|
115
|
+
hasTagDefinedClassification(event, 'addressable')
|
|
116
|
+
}
|
package/i18n/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { ValidationError } from '../error/index.js'
|
|
2
|
+
|
|
1
3
|
const DEFAULT_LOCALE = 'en'
|
|
2
4
|
const INTERPOLATION_RE = /{{\s*([A-Za-z0-9_.-]+)\s*}}/g
|
|
3
5
|
|
|
4
6
|
function assertLocales (locales) {
|
|
5
7
|
if (!locales || typeof locales !== 'object' || Array.isArray(locales)) {
|
|
6
|
-
throw new
|
|
8
|
+
throw new ValidationError('INVALID_LOCALES', { message: 'locales should be an object' })
|
|
7
9
|
}
|
|
8
10
|
}
|
|
9
11
|
|
|
@@ -17,21 +19,21 @@ function placeholderSignature (value) {
|
|
|
17
19
|
function validateTranslationValue (key, locale, value, expectedPlaceholders) {
|
|
18
20
|
if (typeof value === 'string') {
|
|
19
21
|
if (placeholderSignature(value) !== expectedPlaceholders) {
|
|
20
|
-
throw new
|
|
22
|
+
throw new ValidationError('I18N_PLACEHOLDER_MISMATCH', { message: `placeholder mismatch for "${key}" (${locale})` })
|
|
21
23
|
}
|
|
22
24
|
return
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.other !== 'string') {
|
|
26
|
-
throw new
|
|
28
|
+
throw new ValidationError('INVALID_TRANSLATION', { message: `translation for "${key}" (${locale}) should be a string or plural object with an other form` })
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
for (const form of Object.values(value)) {
|
|
30
32
|
if (typeof form !== 'string') {
|
|
31
|
-
throw new
|
|
33
|
+
throw new ValidationError('INVALID_PLURAL_TRANSLATION', { message: `plural forms for "${key}" (${locale}) should be strings` })
|
|
32
34
|
}
|
|
33
35
|
if (placeholderSignature(form) !== expectedPlaceholders) {
|
|
34
|
-
throw new
|
|
36
|
+
throw new ValidationError('I18N_PLACEHOLDER_MISMATCH', { message: `placeholder mismatch for "${key}" (${locale})` })
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -39,7 +41,7 @@ function validateTranslationValue (key, locale, value, expectedPlaceholders) {
|
|
|
39
41
|
export function validateLocales (locales, options = {}) {
|
|
40
42
|
assertLocales(locales)
|
|
41
43
|
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
42
|
-
throw new
|
|
44
|
+
throw new ValidationError('INVALID_I18N_VALIDATION_OPTIONS', { message: 'validation options should be an object' })
|
|
43
45
|
}
|
|
44
46
|
const {
|
|
45
47
|
requiredLocales = [],
|
|
@@ -47,20 +49,20 @@ export function validateLocales (locales, options = {}) {
|
|
|
47
49
|
requireReferenceKey = false
|
|
48
50
|
} = options
|
|
49
51
|
if (!Array.isArray(requiredLocales) || requiredLocales.some(locale => typeof locale !== 'string' || !locale)) {
|
|
50
|
-
throw new
|
|
52
|
+
throw new ValidationError('INVALID_REQUIRED_LOCALES', { message: 'requiredLocales should be an array of non-empty strings' })
|
|
51
53
|
}
|
|
52
54
|
if (typeof referenceLocale !== 'string' || !referenceLocale) {
|
|
53
|
-
throw new
|
|
55
|
+
throw new ValidationError('INVALID_REFERENCE_LOCALE', { message: 'referenceLocale should be a non-empty string' })
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
for (const [key, translations] of Object.entries(locales)) {
|
|
57
59
|
if (!translations || typeof translations !== 'object' || Array.isArray(translations)) {
|
|
58
|
-
throw new
|
|
60
|
+
throw new ValidationError('INVALID_TRANSLATIONS', { message: `translations for "${key}" should be an object` })
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
for (const locale of requiredLocales) {
|
|
62
64
|
if (!Object.prototype.hasOwnProperty.call(translations, locale)) {
|
|
63
|
-
throw new
|
|
65
|
+
throw new ValidationError('MISSING_TRANSLATION', { message: `missing translation for "${key}" (${locale})` })
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -71,12 +73,12 @@ export function validateLocales (locales, options = {}) {
|
|
|
71
73
|
|
|
72
74
|
if (requireReferenceKey) {
|
|
73
75
|
if (!Object.prototype.hasOwnProperty.call(translations, referenceLocale)) {
|
|
74
|
-
throw new
|
|
76
|
+
throw new ValidationError('MISSING_REFERENCE_TRANSLATION', { message: `missing reference translation for "${key}" (${referenceLocale})` })
|
|
75
77
|
}
|
|
76
78
|
const reference = translations[referenceLocale]
|
|
77
79
|
const referenceValue = typeof reference === 'string' ? reference : reference.other
|
|
78
80
|
if (referenceValue !== key) {
|
|
79
|
-
throw new
|
|
81
|
+
throw new ValidationError('REFERENCE_TRANSLATION_MISMATCH', { message: `reference translation should match key "${key}" (${referenceLocale})` })
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -227,7 +229,7 @@ export function getT (locales, {
|
|
|
227
229
|
const canonicalFallback = canonicalizeLocale(fallbackLocale) ?? DEFAULT_LOCALE
|
|
228
230
|
|
|
229
231
|
return function t (key, values) {
|
|
230
|
-
if (typeof key !== 'string') throw new
|
|
232
|
+
if (typeof key !== 'string') throw new ValidationError('INVALID_TRANSLATION_KEY', { message: 'translation key should be a string' })
|
|
231
233
|
const translations = Object.prototype.hasOwnProperty.call(locales, key) ? locales[key] : null
|
|
232
234
|
const template = selectTemplate(translations, requestedLocale, canonicalFallback, values) ?? key
|
|
233
235
|
return interpolate(template, values)
|
package/idb/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ValidationError } from '../error/index.js'
|
|
2
|
+
|
|
1
3
|
const READ_METHODS = new Set([
|
|
2
4
|
'count',
|
|
3
5
|
'get',
|
|
@@ -29,15 +31,15 @@ export async function run (method, args = [], storeName, indexName, {
|
|
|
29
31
|
storeOrIndex
|
|
30
32
|
} = {}) {
|
|
31
33
|
if (!tx) {
|
|
32
|
-
if (!db) throw new
|
|
33
|
-
if (!storeName) throw new
|
|
34
|
+
if (!db) throw new ValidationError('IDB_DATABASE_REQUIRED')
|
|
35
|
+
if (!storeName) throw new ValidationError('IDB_STORE_REQUIRED')
|
|
34
36
|
// Caller may pre-select it if it wants to use many different methods in a row
|
|
35
37
|
txMode ??= READ_METHODS.has(method) ? 'readonly' : 'readwrite'
|
|
36
38
|
tx = db.transaction([storeName], txMode)
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
if (!storeOrIndex) {
|
|
40
|
-
if (!storeName) throw new
|
|
42
|
+
if (!storeName) throw new ValidationError('IDB_STORE_REQUIRED')
|
|
41
43
|
const store = tx.objectStore(storeName)
|
|
42
44
|
storeOrIndex = indexName ? store.index(indexName) : store
|
|
43
45
|
}
|
package/idb-queue/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { run } from '../idb/index.js'
|
|
2
|
+
import { ValidationError } from '../error/index.js'
|
|
2
3
|
|
|
3
4
|
const encoder = new TextEncoder()
|
|
4
5
|
const ITEMS_STORE = 'items'
|
|
@@ -56,7 +57,7 @@ function normalizeEvictionPolicy (policy) {
|
|
|
56
57
|
if (policy === 'opposite-end' || policy === undefined || policy === null) return 'opposite-end'
|
|
57
58
|
if (policy === 'fifo' || policy === 'head') return 'head'
|
|
58
59
|
if (policy === 'lifo' || policy === 'tail') return 'tail'
|
|
59
|
-
throw new
|
|
60
|
+
throw new ValidationError('QUEUE_INVALID_EVICTION_POLICY')
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
function normalizeState (value) {
|
|
@@ -67,17 +68,17 @@ function normalizeState (value) {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
function normalizeIndexes (indexes = {}) {
|
|
70
|
-
if (!indexes || typeof indexes !== 'object' || Array.isArray(indexes)) throw new
|
|
71
|
+
if (!indexes || typeof indexes !== 'object' || Array.isArray(indexes)) throw new ValidationError('QUEUE_INDEXES_INVALID')
|
|
71
72
|
|
|
72
73
|
return Object.entries(indexes).map(([name, definition]) => {
|
|
73
74
|
const options = typeof definition === 'string' || Array.isArray(definition)
|
|
74
75
|
? { keyPath: definition }
|
|
75
76
|
: definition
|
|
76
77
|
const keyPath = options?.keyPath
|
|
77
|
-
if (!name || (!Array.isArray(keyPath) && typeof keyPath !== 'string')) throw new
|
|
78
|
-
if (Array.isArray(keyPath) && keyPath.some(path => typeof path !== 'string' || !path)) throw new
|
|
79
|
-
if (typeof keyPath === 'string' && !keyPath) throw new
|
|
80
|
-
if (options.multiEntry && Array.isArray(keyPath)) throw new
|
|
78
|
+
if (!name || (!Array.isArray(keyPath) && typeof keyPath !== 'string')) throw new ValidationError('QUEUE_INDEX_INVALID')
|
|
79
|
+
if (Array.isArray(keyPath) && keyPath.some(path => typeof path !== 'string' || !path)) throw new ValidationError('QUEUE_INDEX_INVALID')
|
|
80
|
+
if (typeof keyPath === 'string' && !keyPath) throw new ValidationError('QUEUE_INDEX_INVALID')
|
|
81
|
+
if (options.multiEntry && Array.isArray(keyPath)) throw new ValidationError('QUEUE_INDEX_MULTI_ENTRY_COMPOUND')
|
|
81
82
|
return {
|
|
82
83
|
name,
|
|
83
84
|
keyPath,
|
|
@@ -90,7 +91,7 @@ function normalizeIndexes (indexes = {}) {
|
|
|
90
91
|
})
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
function
|
|
94
|
+
function areKeyPathsEqual (a, b) {
|
|
94
95
|
return JSON.stringify(a) === JSON.stringify(b)
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -129,12 +130,12 @@ function ensureSchema (db, tx, indexDefinitions) {
|
|
|
129
130
|
items = db.createObjectStore(ITEMS_STORE, { keyPath: 'position' })
|
|
130
131
|
} else {
|
|
131
132
|
items = tx.objectStore(ITEMS_STORE)
|
|
132
|
-
if (!
|
|
133
|
+
if (!areKeyPathsEqual(items.keyPath, 'position')) throw new Error('QUEUE_SCHEMA_MISMATCH')
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
if (!db.objectStoreNames.contains(STATE_STORE)) {
|
|
136
137
|
db.createObjectStore(STATE_STORE, { keyPath: 'key' })
|
|
137
|
-
} else if (!
|
|
138
|
+
} else if (!areKeyPathsEqual(tx.objectStore(STATE_STORE).keyPath, 'key')) {
|
|
138
139
|
throw new Error('QUEUE_SCHEMA_MISMATCH')
|
|
139
140
|
}
|
|
140
141
|
|
|
@@ -148,7 +149,7 @@ function ensureSchema (db, tx, indexDefinitions) {
|
|
|
148
149
|
}
|
|
149
150
|
const existing = items.index(definition.name)
|
|
150
151
|
if (
|
|
151
|
-
!
|
|
152
|
+
!areKeyPathsEqual(existing.keyPath, definition.storedKeyPath) ||
|
|
152
153
|
existing.unique !== definition.unique ||
|
|
153
154
|
existing.multiEntry !== definition.multiEntry
|
|
154
155
|
) {
|
|
@@ -165,7 +166,7 @@ async function inspectSchema (db, indexDefinitions) {
|
|
|
165
166
|
const done = transactionDone(tx)
|
|
166
167
|
const items = tx.objectStore(ITEMS_STORE)
|
|
167
168
|
const state = tx.objectStore(STATE_STORE)
|
|
168
|
-
if (!
|
|
169
|
+
if (!areKeyPathsEqual(items.keyPath, 'position') || !areKeyPathsEqual(state.keyPath, 'key')) {
|
|
169
170
|
await done
|
|
170
171
|
return { missing: false, incompatible: true }
|
|
171
172
|
}
|
|
@@ -178,7 +179,7 @@ async function inspectSchema (db, indexDefinitions) {
|
|
|
178
179
|
}
|
|
179
180
|
const existing = items.index(definition.name)
|
|
180
181
|
if (
|
|
181
|
-
!
|
|
182
|
+
!areKeyPathsEqual(existing.keyPath, definition.storedKeyPath) ||
|
|
182
183
|
existing.unique !== definition.unique ||
|
|
183
184
|
existing.multiEntry !== definition.multiEntry
|
|
184
185
|
) {
|
|
@@ -241,13 +242,13 @@ function itemForStorage (position, item) {
|
|
|
241
242
|
|
|
242
243
|
function assertIndex (index, length, { allowEnd = false } = {}) {
|
|
243
244
|
const max = allowEnd ? length : length - 1
|
|
244
|
-
if (!Number.isSafeInteger(index) || index < 0 || index > max) throw new
|
|
245
|
+
if (!Number.isSafeInteger(index) || index < 0 || index > max) throw new ValidationError('QUEUE_INDEX_OUT_OF_RANGE')
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
function
|
|
248
|
+
function normalizeDirection (direction) {
|
|
248
249
|
if (direction === undefined || direction === 'next') return 'next'
|
|
249
250
|
if (direction === 'prev') return 'prev'
|
|
250
|
-
throw new
|
|
251
|
+
throw new ValidationError('QUEUE_INVALID_DIRECTION')
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
function isQuotaExceeded (err) {
|
|
@@ -265,7 +266,7 @@ export async function createQueue ({
|
|
|
265
266
|
evictionPolicy = 'opposite-end',
|
|
266
267
|
indexedDB = globalThis.indexedDB
|
|
267
268
|
} = {}) {
|
|
268
|
-
if (!prefix) throw new
|
|
269
|
+
if (!prefix) throw new ValidationError('QUEUE_PREFIX_REQUIRED')
|
|
269
270
|
|
|
270
271
|
const indexDefinitions = normalizeIndexes(indexes)
|
|
271
272
|
const db = await openQueueDatabase(indexedDB, prefix, indexDefinitions)
|
|
@@ -663,7 +664,7 @@ export async function createQueue ({
|
|
|
663
664
|
}
|
|
664
665
|
|
|
665
666
|
async function insertWhere (predicate, item, { appendIfMissing = false } = {}) {
|
|
666
|
-
if (typeof predicate !== 'function') throw new
|
|
667
|
+
if (typeof predicate !== 'function') throw new ValidationError('QUEUE_PREDICATE_REQUIRED')
|
|
667
668
|
const requiredBytes = itemForStorage(0, item).byteSize
|
|
668
669
|
return mutate(async (tx, state) => {
|
|
669
670
|
const records = await recordsForState(tx, state)
|
|
@@ -683,7 +684,7 @@ export async function createQueue ({
|
|
|
683
684
|
}
|
|
684
685
|
|
|
685
686
|
async function removeWhere (predicate) {
|
|
686
|
-
if (typeof predicate !== 'function') throw new
|
|
687
|
+
if (typeof predicate !== 'function') throw new ValidationError('QUEUE_PREDICATE_REQUIRED')
|
|
687
688
|
return mutate(async (tx, state) => {
|
|
688
689
|
const records = await recordsForState(tx, state)
|
|
689
690
|
const removed = new Set()
|
|
@@ -699,7 +700,7 @@ export async function createQueue ({
|
|
|
699
700
|
}
|
|
700
701
|
|
|
701
702
|
async function some (predicate) {
|
|
702
|
-
if (typeof predicate !== 'function') throw new
|
|
703
|
+
if (typeof predicate !== 'function') throw new ValidationError('QUEUE_PREDICATE_REQUIRED')
|
|
703
704
|
return snapshot(async tx => {
|
|
704
705
|
const state = await readState(tx)
|
|
705
706
|
const records = await recordsForState(tx, state)
|
|
@@ -797,7 +798,7 @@ export async function createQueue ({
|
|
|
797
798
|
}
|
|
798
799
|
|
|
799
800
|
async function * storedItemsBy (indexName, query, { direction = 'next' } = {}) {
|
|
800
|
-
direction =
|
|
801
|
+
direction = normalizeDirection(direction)
|
|
801
802
|
const records = await snapshot(async tx => {
|
|
802
803
|
const { result } = await run('getAll', [query], ITEMS_STORE, indexName, { tx })
|
|
803
804
|
return result
|
package/index.js
CHANGED
|
@@ -8,18 +8,28 @@ export * as contentKey from './content-key/index.js'
|
|
|
8
8
|
export * as contentKeyEvent from './content-key/event/index.js'
|
|
9
9
|
export * as doubleDh from './double-dh/index.js'
|
|
10
10
|
export * as ecdh from './ecdh/index.js'
|
|
11
|
+
export * as error from './error/index.js'
|
|
12
|
+
export * as event from './event/index.js'
|
|
11
13
|
export * as idb from './idb/index.js'
|
|
12
14
|
export * as idbQueue from './idb-queue/index.js'
|
|
13
15
|
export * as i18n from './i18n/index.js'
|
|
14
16
|
export * as key from './key/index.js'
|
|
17
|
+
export * as kind from './kind/index.js'
|
|
15
18
|
export * as network from './network/index.js'
|
|
19
|
+
export * as nip04 from './nip04/index.js'
|
|
20
|
+
export * as nip05 from './nip05/index.js'
|
|
16
21
|
export * as nip19 from './nip19/index.js'
|
|
22
|
+
export * as nip44 from './nip44/index.js'
|
|
17
23
|
export * as nip44v3 from './nip44-v3/index.js'
|
|
18
24
|
export * as nip46 from './nip46/index.js'
|
|
25
|
+
export * as nip96 from './nip96/index.js'
|
|
26
|
+
export * as nip98 from './nip98/index.js'
|
|
27
|
+
export * as nwt from './nwt/index.js'
|
|
19
28
|
export * as privateChannel from './private-channel/index.js'
|
|
20
29
|
export * as privateMessage from './private-message/index.js'
|
|
21
30
|
export * as privateMessenger from './private-messenger/index.js'
|
|
22
31
|
export * as privateMessengerRecovery from './private-messenger/recovery/index.js'
|
|
23
32
|
export * as relay from './relay/index.js'
|
|
24
33
|
export * as temporaryStorage from './temporary-storage/index.js'
|
|
34
|
+
export * as url from './url/index.js'
|
|
25
35
|
export * as webStorageQueue from './web-storage-queue/index.js'
|
package/key/index.js
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
generateSecretKey,
|
|
3
|
-
getPublicKey,
|
|
4
|
-
finalizeEvent,
|
|
5
|
-
nip19
|
|
6
|
-
} from 'nostr-tools'
|
|
1
|
+
import { schnorr, secp256k1 } from '@noble/curves/secp256k1.js'
|
|
7
2
|
|
|
8
3
|
import { bytesToHex, hexToBytes } from '../base16/index.js'
|
|
4
|
+
import { ValidationError } from '../error/index.js'
|
|
5
|
+
import { finalizeEvent } from '../event/index.js'
|
|
6
|
+
import { nsecDecode, nsecEncode, npubDecode, npubEncode } from '../nip19/index.js'
|
|
9
7
|
|
|
10
8
|
const HEX_SECKEY_REGEX = /^[0-9a-f]{64}$/i
|
|
11
9
|
|
|
10
|
+
export function generateSecretKey () {
|
|
11
|
+
return schnorr.utils.randomSecretKey()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getPublicKey (secretKey) {
|
|
15
|
+
if (!(secretKey instanceof Uint8Array) || secretKey.length !== 32 || !secp256k1.utils.isValidSecretKey(secretKey)) {
|
|
16
|
+
throw new ValidationError('INVALID_SECRET_KEY')
|
|
17
|
+
}
|
|
18
|
+
return bytesToHex(schnorr.getPublicKey(secretKey))
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
export function generateKeypair () {
|
|
13
22
|
const secretKey = generateSecretKey()
|
|
14
23
|
const pubkey = getPublicKey(secretKey)
|
|
@@ -16,8 +25,8 @@ export function generateKeypair () {
|
|
|
16
25
|
secretKey,
|
|
17
26
|
seckey: bytesToHex(secretKey),
|
|
18
27
|
pubkey,
|
|
19
|
-
nsec:
|
|
20
|
-
npub:
|
|
28
|
+
nsec: nsecEncode(bytesToHex(secretKey)),
|
|
29
|
+
npub: npubEncode(pubkey)
|
|
21
30
|
}
|
|
22
31
|
}
|
|
23
32
|
|
|
@@ -29,17 +38,19 @@ export function keypairFromSeckey (raw) {
|
|
|
29
38
|
if (HEX_SECKEY_REGEX.test(raw)) {
|
|
30
39
|
secretKey = hexToBytes(raw.toLowerCase())
|
|
31
40
|
} else {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
try {
|
|
42
|
+
secretKey = hexToBytes(nsecDecode(raw))
|
|
43
|
+
} catch (cause) {
|
|
44
|
+
throw new ValidationError('NOT_A_SECRET_KEY', { cause })
|
|
45
|
+
}
|
|
35
46
|
}
|
|
36
47
|
const pubkey = getPublicKey(secretKey)
|
|
37
48
|
return {
|
|
38
49
|
secretKey,
|
|
39
50
|
seckey: bytesToHex(secretKey),
|
|
40
51
|
pubkey,
|
|
41
|
-
nsec:
|
|
42
|
-
npub:
|
|
52
|
+
nsec: nsecEncode(bytesToHex(secretKey)),
|
|
53
|
+
npub: npubEncode(pubkey)
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
|
|
@@ -47,17 +58,19 @@ export function keypairFromSeckey (raw) {
|
|
|
47
58
|
// bech32 includes a checksum that protects against user-typo imports of a
|
|
48
59
|
// pubkey they cannot sign for.
|
|
49
60
|
export function pubkeyFromNpub (npub) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
try {
|
|
62
|
+
return npubDecode(npub)
|
|
63
|
+
} catch (cause) {
|
|
64
|
+
throw new ValidationError('NOT_AN_NPUB', { cause })
|
|
65
|
+
}
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
export function nsecFromHex (hex) {
|
|
56
|
-
return
|
|
69
|
+
return nsecEncode(hex)
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
export function npubFromPubkey (pubkey) {
|
|
60
|
-
return
|
|
73
|
+
return npubEncode(pubkey)
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
function cleanProfileValue (value) {
|