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 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
- // String-oriented helpers for app-facing methods. Plaintext travels as
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
- export function nip07Encrypt (seckey, pubkey, kind, scope, plaintextB64) {
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 nip07Decrypt (seckey, pubkey, kind, scope, ciphertext) {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "libp2r2p",
3
- "version": "0.10.13",
3
+ "version": "0.10.15",
4
4
  "description": "Peer-to-relay-to-peer",
5
5
  "keywords": [
6
6
  "p2r2p",