libp2r2p 0.10.13 → 0.10.15
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 +31 -0
- package/kind/index.js +2 -0
- package/nip27/helpers/compact-whitespace.js +33 -0
- package/nip27/index.js +1 -0
- package/nip44-v3/README.md +32 -0
- package/nip44-v3/index.js +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -320,6 +320,14 @@ NIP-44 v2 uses the interoperable `nip44-v2` salt by default. A custom UTF-8
|
|
|
320
320
|
salt of at most 32 bytes may be passed to `getConversationKey()`, but messages
|
|
321
321
|
derived with it are not interoperable with standard NIP-44 implementations.
|
|
322
322
|
|
|
323
|
+
NIP-44 v3 separates binary (`encryptBytes`/`decryptBytes`), UTF-8 text
|
|
324
|
+
(`encrypt`/`decrypt`), and standard-Base64 plaintext
|
|
325
|
+
(`encryptBase64`/`decryptBase64`) interfaces.
|
|
326
|
+
The suffix describes plaintext; ciphertext is always
|
|
327
|
+
standard Base64. NIP-46 uses the Base64 helpers, while browser NIP-07 APIs use
|
|
328
|
+
`ArrayBuffer` plaintext; see [the interface guide](nip44-v3/README.md) before
|
|
329
|
+
adapting a signer.
|
|
330
|
+
|
|
323
331
|
NIP-46 clients and bunker signers use a 30-second operation timeout by
|
|
324
332
|
default. Set `timeout` in the `Nip46Client`/`BunkerSigner` constructor to
|
|
325
333
|
choose another default, override it for an individual `connect()` or RPC, or
|
|
@@ -370,6 +378,29 @@ plus NIP-05 in its standard, root and custom compact spellings.
|
|
|
370
378
|
reads the file/media metadata carried in a URL fragment
|
|
371
379
|
(`#m=image/png&dim=640x480&...`).
|
|
372
380
|
|
|
381
|
+
`compactWhitespace(text, options?)` is also exported from `libp2r2p/nip27` as an
|
|
382
|
+
opt-in display helper. It removes carriage returns, collapses spaces and
|
|
383
|
+
tabs, removes spaces around line breaks, limits consecutive line breaks to
|
|
384
|
+
two, keeps the first eight line breaks (replacing subsequent runs with a
|
|
385
|
+
space), and trims the result. Empty strings are accepted; non-string inputs
|
|
386
|
+
throw `ValidationError` with code `INVALID_WHITESPACE_TEXT`. It is not
|
|
387
|
+
applied automatically by `extractMedia()`.
|
|
388
|
+
|
|
389
|
+
The option `maxLineBreaks` (default `8`) sets the total limit and accepts a
|
|
390
|
+
non-negative safe integer. Setting it to `0` replaces all line-break runs
|
|
391
|
+
with spaces. `consecutiveLineBreakThreshold` (default `3`) sets the minimum
|
|
392
|
+
run length that collapses to **two** line breaks; shorter runs are preserved.
|
|
393
|
+
It accepts safe integers of at least `3`. Either option accepts `Infinity`
|
|
394
|
+
to disable its rule. Runs are collapsed before applying the total limit,
|
|
395
|
+
and trimming happens last. Invalid options throw `ValidationError` with
|
|
396
|
+
code `INVALID_WHITESPACE_OPTIONS`.
|
|
397
|
+
|
|
398
|
+
```js
|
|
399
|
+
import { compactWhitespace } from 'libp2r2p/nip27'
|
|
400
|
+
|
|
401
|
+
compactWhitespace(text, { maxLineBreaks: 12, consecutiveLineBreakThreshold: 5 })
|
|
402
|
+
```
|
|
403
|
+
|
|
373
404
|
User references (`npub`, `nprofile`, hex pubkeys and every NIP-05 spelling)
|
|
374
405
|
are handled by `libp2r2p/nip27`: `decodeUserReference()` returns the decoded
|
|
375
406
|
form with its canonical compact spelling, `encodeUserReference()` returns
|
package/kind/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export const DELETION = 5
|
|
|
7
7
|
export const REPOST = 6
|
|
8
8
|
export const REACTION = 7
|
|
9
9
|
export const BADGE_AWARD = 8
|
|
10
|
+
export const CHAT_MESSAGE = 9
|
|
10
11
|
export const SEAL = 13
|
|
11
12
|
export const PRIVATE_DIRECT_MESSAGE = 14
|
|
12
13
|
export const GENERIC_REPOST = 16
|
|
@@ -146,6 +147,7 @@ export const eventKinds = /* @__PURE__ */ Object.freeze({
|
|
|
146
147
|
REPOST,
|
|
147
148
|
REACTION,
|
|
148
149
|
BADGE_AWARD,
|
|
150
|
+
CHAT_MESSAGE,
|
|
149
151
|
SEAL,
|
|
150
152
|
PRIVATE_DIRECT_MESSAGE,
|
|
151
153
|
GENERIC_REPOST,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ValidationError } from '../../error/index.js'
|
|
2
|
+
|
|
3
|
+
// Compacts display text, defaulting to collapsing runs of three or more line
|
|
4
|
+
// breaks to two and keeping eight overall. extractMedia preserves input.
|
|
5
|
+
export function compactWhitespace (text, options = {}) {
|
|
6
|
+
if (typeof text !== 'string') {
|
|
7
|
+
throw new ValidationError('INVALID_WHITESPACE_TEXT', { message: 'TEXT_SHOULD_BE_A_STRING' })
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
11
|
+
throw new ValidationError('INVALID_WHITESPACE_OPTIONS', { message: 'OPTIONS_SHOULD_BE_AN_OBJECT' })
|
|
12
|
+
}
|
|
13
|
+
const { maxLineBreaks = 8, consecutiveLineBreakThreshold = 3 } = options
|
|
14
|
+
if (maxLineBreaks !== Infinity && (!Number.isSafeInteger(maxLineBreaks) || maxLineBreaks < 0)) {
|
|
15
|
+
throw new ValidationError('INVALID_WHITESPACE_OPTIONS', { message: 'MAX_LINE_BREAKS_SHOULD_BE_A_NON_NEGATIVE_SAFE_INTEGER_OR_INFINITY' })
|
|
16
|
+
}
|
|
17
|
+
if (consecutiveLineBreakThreshold !== Infinity && (!Number.isSafeInteger(consecutiveLineBreakThreshold) || consecutiveLineBreakThreshold < 3)) {
|
|
18
|
+
throw new ValidationError('INVALID_WHITESPACE_OPTIONS', { message: 'CONSECUTIVE_LINE_BREAK_THRESHOLD_SHOULD_BE_A_SAFE_INTEGER_AT_LEAST_THREE_OR_INFINITY' })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let remainingLineBreaks = maxLineBreaks
|
|
22
|
+
return text
|
|
23
|
+
.replace(/\r/g, '')
|
|
24
|
+
.replace(/[\t ]+/g, ' ')
|
|
25
|
+
.replace(/ ?\n ?/g, '\n')
|
|
26
|
+
.replace(/\n+/g, lineBreaks => {
|
|
27
|
+
const consecutive = lineBreaks.length >= consecutiveLineBreakThreshold ? 2 : lineBreaks.length
|
|
28
|
+
const kept = Math.min(remainingLineBreaks, consecutive)
|
|
29
|
+
remainingLineBreaks -= kept
|
|
30
|
+
return lineBreaks.slice(0, kept) + (kept < consecutive ? ' ' : '')
|
|
31
|
+
})
|
|
32
|
+
.trim()
|
|
33
|
+
}
|
package/nip27/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from './helpers/user-reference.js'
|
|
15
15
|
|
|
16
16
|
export { decodeUserReference, encodeUserReference, tryDecodeUserReference }
|
|
17
|
+
export { compactWhitespace } from './helpers/compact-whitespace.js'
|
|
17
18
|
|
|
18
19
|
const BECH32_BODY = '[ac-hj-np-z02-9]'
|
|
19
20
|
const BOUNDARY_PREFIX = /(?<=^|[\s"«„「¡¿:{([])/.source
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# NIP-44 v3 interfaces
|
|
2
|
+
|
|
3
|
+
These public functions separate bytes, UTF-8 text and Base64 convenience.
|
|
4
|
+
Suffixes describe the plaintext input/output format; ciphertexts are standard
|
|
5
|
+
Base64 strings throughout.
|
|
6
|
+
|
|
7
|
+
| Functions | Plaintext input/output | Scope |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `encryptBytes` / `decryptBytes` | `Uint8Array` | UTF-8 bytes (`Uint8Array`) |
|
|
10
|
+
| `encryptWithConversationKeyBytes` / `decryptWithConversationKeyBytes` | `Uint8Array` | UTF-8 bytes (`Uint8Array`) |
|
|
11
|
+
| `encrypt` / `decrypt` | UTF-8 text (`string`) | UTF-8 text (`string`) |
|
|
12
|
+
| `encryptWithConversationKey` / `decryptWithConversationKey` | UTF-8 text (`string`) | UTF-8 text (`string`) |
|
|
13
|
+
| `encryptBase64` / `decryptBase64` | Standard Base64 (`string`) | UTF-8 text (`string`) |
|
|
14
|
+
|
|
15
|
+
The Base64 helpers decode plaintext before encryption and encode decrypted
|
|
16
|
+
bytes before returning them. Base64URL is not this
|
|
17
|
+
format, and arbitrary bytes must not pass through a UTF-8 text helper.
|
|
18
|
+
|
|
19
|
+
The [NIP-07 extension](https://github.com/nostr-land/nip44v3/blob/master/extensions/nip07.md)
|
|
20
|
+
defines `window.nostr.nip44v3.encrypt` with an `ArrayBuffer` plaintext and
|
|
21
|
+
`decrypt` with an `ArrayBuffer` result. Browser signer bridges should adapt
|
|
22
|
+
that boundary to their binary implementation or to the Base64
|
|
23
|
+
[NIP-46 transport](https://github.com/nostr-land/nip44v3/blob/master/extensions/nip46.md).
|
|
24
|
+
The library's text convenience functions are not implementations of that
|
|
25
|
+
browser interface. Do not guess whether a string is literal text or Base64.
|
|
26
|
+
|
|
27
|
+
The [implementation guide](https://github.com/nostr-land/nip44v3/blob/master/implementing.md)
|
|
28
|
+
requires binary support and explicit expected context. Callers choose kind and
|
|
29
|
+
scope from their protocol/event; decrypt verifies them. The
|
|
30
|
+
[NIP-17 extension](https://github.com/nostr-land/nip44v3/blob/master/extensions/nip17.md)
|
|
31
|
+
specifies kind 1059 for gift wraps and 13 for seals, each with empty scope;
|
|
32
|
+
these are protocol choices, not automatic defaults for other event types.
|
package/nip44-v3/index.js
CHANGED
|
@@ -113,6 +113,8 @@ export function encryptWithConversationKeyBytes (conversationKey, kind, scope, p
|
|
|
113
113
|
return bytesToBase64(concatBytes(new Uint8Array([VERSION]), nonce, mac, stuffing))
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// seckey/expectedScope: Uint8Array, pubkey: hex, ciphertext: standard Base64 string.
|
|
117
|
+
// Verifies the expected kind/scope and returns plaintext bytes as Uint8Array.
|
|
116
118
|
export function decryptBytes (seckey, pubkey, expectedKind, expectedScope, ciphertext) {
|
|
117
119
|
return decryptWithConversationKeyBytes(deriveSharedConversationKey(seckey, pubkey), expectedKind, expectedScope, ciphertext)
|
|
118
120
|
}
|
|
@@ -162,8 +164,7 @@ export function normalizeKind (kind) {
|
|
|
162
164
|
return n
|
|
163
165
|
}
|
|
164
166
|
|
|
165
|
-
//
|
|
166
|
-
// base64 on the NIP-07/46 wire so callers can encrypt arbitrary bytes.
|
|
167
|
+
// UTF-8 convenience helpers. Binary callers use encryptBytes/decryptBytes.
|
|
167
168
|
export function encrypt (seckey, pubkey, kind, scope, plaintext) {
|
|
168
169
|
return encryptBytes(seckey, pubkey, normalizeKind(kind), utf8ToBytes(scope || ''), utf8ToBytes(plaintext))
|
|
169
170
|
}
|
|
@@ -180,11 +181,14 @@ export function decryptWithConversationKey (conversationKey, kind, scope, cipher
|
|
|
180
181
|
return textDecoder.decode(decryptWithConversationKeyBytes(conversationKey, normalizeKind(kind), utf8ToBytes(scope || ''), ciphertext))
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
|
|
184
|
+
// Base64 convenience helpers: the suffix describes plaintext input/output.
|
|
185
|
+
// Ciphertext is always standard Base64. NIP-46 uses this plaintext format;
|
|
186
|
+
// NIP-07 instead exposes ArrayBuffer plaintext at the browser API boundary.
|
|
187
|
+
export function encryptBase64 (seckey, pubkey, kind, scope, plaintextB64) {
|
|
184
188
|
return encryptBytes(seckey, pubkey, normalizeKind(kind), utf8ToBytes(scope || ''), base64ToBytes(plaintextB64))
|
|
185
189
|
}
|
|
186
190
|
|
|
187
|
-
export function
|
|
191
|
+
export function decryptBase64 (seckey, pubkey, kind, scope, ciphertext) {
|
|
188
192
|
return bytesToBase64(decryptBytes(seckey, pubkey, normalizeKind(kind), utf8ToBytes(scope || ''), ciphertext))
|
|
189
193
|
}
|
|
190
194
|
|