nappup 1.9.1 → 2.0.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.
@@ -1,112 +0,0 @@
1
- import { bytesToBase16, base16ToBytes } from '#helpers/base16.js'
2
- import { bytesToBase62, base62ToBytes, BASE62_ALPHABET } from '#helpers/base62.js'
3
- import { encode as bech32Encode, decode as bech32Decode, toWords, fromWords } from '#helpers/bech32.js'
4
- import { isNostrAppDTagSafe } from '#helpers/app.js'
5
-
6
- const MAX_SIZE = 5000
7
- export const NAPP_ENTITY_REGEX = new RegExp(`^\\+{1,3}[${BASE62_ALPHABET}]{48,${MAX_SIZE}}$`)
8
- const textEncoder = new TextEncoder()
9
- const textDecoder = new TextDecoder()
10
-
11
- const kindByChannel = {
12
- main: 35128,
13
- next: 35129,
14
- draft: 35130
15
- }
16
- const channelByKind = Object.fromEntries(
17
- Object.entries(kindByChannel).map(([k, v]) => [v, k])
18
- )
19
- const prefixByChannel = {
20
- main: '+',
21
- next: '++',
22
- draft: '+++'
23
- }
24
- const channelByPrefix = Object.fromEntries(
25
- Object.entries(prefixByChannel).map(([k, v]) => [v, k])
26
- )
27
- export function appEncode (ref) {
28
- if (!isNostrAppDTagSafe(ref.dTag)) { throw new Error('Invalid deduplication tag') }
29
- const channel = ref.channel ? (prefixByChannel[ref.channel] && ref.channel) : channelByKind[ref.kind]
30
- if (!channel) throw new Error('Wrong channel')
31
- const tlv = toTlv([
32
- [textEncoder.encode(ref.dTag)], // type 0 (the array index)
33
- (ref.relays || []).map(url => textEncoder.encode(url)), // type 1
34
- [base16ToBytes(ref.pubkey)] // type 2
35
- ])
36
- const base62 = bytesToBase62(tlv)
37
- const prefix = prefixByChannel[channel]
38
- return `${prefix}${base62}`
39
- }
40
-
41
- export function appDecode (entity) {
42
- const prefix = entity.match(/^\+*/)[0]
43
- const channel = channelByPrefix[prefix]
44
- if (!channel) throw new Error('Invalid channel')
45
- const base62 = entity.slice(prefix.length)
46
- const tlv = tlvToObj(base62ToBytes(base62))
47
- if (!tlv[0]?.[0]) throw new Error('Missing deduplication tag')
48
- if (!tlv[2]?.[0]) throw new Error('Missing author pubkey')
49
- if (tlv[2][0].length !== 32) throw new Error('Author pubkey should be 32 bytes')
50
- const dTag = textDecoder.decode(tlv[0][0])
51
- if (!isNostrAppDTagSafe(dTag)) { throw new Error('Invalid deduplication tag') }
52
-
53
- return {
54
- dTag,
55
- pubkey: bytesToBase16(tlv[2][0]),
56
- kind: kindByChannel[channel],
57
- channel,
58
- relays: tlv[1] ? tlv[1].map(url => textDecoder.decode(url)) : []
59
- }
60
- }
61
-
62
- export function nsecEncode (hex) {
63
- const bytes = base16ToBytes(hex)
64
- const words = toWords(bytes)
65
- return bech32Encode('nsec', words)
66
- }
67
-
68
- export function nsecDecode (nsec) {
69
- const decoded = bech32Decode(nsec, MAX_SIZE)
70
- if (!decoded) throw new Error('Invalid nsec')
71
- const { hrp, data } = decoded
72
- if (hrp !== 'nsec') throw new Error('Invalid nsec')
73
- const bytes = fromWords(data)
74
- if (bytes.length !== 32) throw new Error('Invalid nsec length')
75
- return bytesToBase16(new Uint8Array(bytes))
76
- }
77
-
78
- function toTlv (tlvConfig) {
79
- const arrays = []
80
- tlvConfig
81
- .map((v, i) => [i, v])
82
- .reverse() // if the first type is 0, entity always starts with the '0' char
83
- .forEach(([type, values]) => {
84
- // just non-empty values will be included
85
- values.forEach(v => {
86
- if (v.length > 255) throw new Error('Value is too big')
87
- const array = new Uint8Array(v.length + 2)
88
- array.set([type], 0) // t
89
- array.set([v.length], 1) // l
90
- array.set(v, 2) // v
91
- arrays.push(array)
92
- })
93
- })
94
- return new Uint8Array(arrays.reduce((r, v) => [...r, ...v]))
95
- }
96
-
97
- // { 0: [t0v0], 1: [t1v0, t1v2], 3: [t3v0] }
98
- function tlvToObj (tlv) {
99
- const ret = {}
100
- let rest = tlv
101
- let t, l, v
102
- while (rest.length > 0) {
103
- t = rest[0]
104
- l = rest[1]
105
- v = rest.slice(2, 2 + l)
106
- rest = rest.slice(2 + l)
107
- if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)
108
- ret[t] ??= []
109
- ret[t].push(v)
110
- }
111
- return ret
112
- }
@@ -1,9 +0,0 @@
1
- # src/services folder
2
-
3
- - Place services here. Services are multi-step functionality that make the code more
4
- readable than if placing every step together on a single function.
5
-
6
- ## General Instructions:
7
-
8
- - Use object-oriented programming paradigm.
9
- - Each file default exports one class or singleton.
@@ -1,107 +0,0 @@
1
- // https://github.com/ticlo/arrow-code/blob/master/src/base93.ts
2
- // https://github.com/ticlo/arrow-code/blob/master/LICENSE - Apache 2.0
3
-
4
- // JSON-safe (space included; " and \ excluded)
5
- const BASE93_ALPHABET =
6
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()*+,-./:;<=>?@[]^_`{|}~ "
7
-
8
- const DECODING_TABLE = (() => {
9
- const out = new Int16Array(128)
10
- out.fill(93) // sentinel = invalid
11
- for (let i = 0; i < 93; i++) out[BASE93_ALPHABET.charCodeAt(i)] = i
12
- return out
13
- })()
14
-
15
- /**
16
- * Decode Base93 string to Uint8Array
17
- * @param {string} str - The Base93 encoded string to decode
18
- * @param {number} [offset=0] - The starting position in the string
19
- * @param {number} [length=-1] - The number of characters to decode, or -1 for all remaining
20
- * @returns {Uint8Array} The decoded bytes
21
- */
22
- export function decode (str, offset = 0, length = -1) {
23
- let end = offset + length
24
- if (length < 0 || end > str.length) end = str.length
25
-
26
- // Over-allocate; we’ll trim at the end
27
- const out = new Uint8Array(Math.ceil((end - offset) * 7 / 8))
28
-
29
- let dbq = 0
30
- let dn = 0
31
- let dv = -1
32
- let pos = 0
33
-
34
- for (let i = offset; i < end; i++) {
35
- const code = str.charCodeAt(i)
36
- if (code > 126) continue // ignore non-ASCII
37
- const v = DECODING_TABLE[code]
38
- if (v === 93) continue // ignore invalids
39
- if (dv === -1) {
40
- dv = v
41
- } else {
42
- const t = dv + v * 93
43
- dv = -1
44
- dbq |= t << dn
45
- dn += ((t & 0x1fff) > 456 ? 13 : 14)
46
- while (dn > 7) {
47
- out[pos++] = dbq & 0xff
48
- dbq >>>= 8
49
- dn -= 8
50
- }
51
- }
52
- }
53
-
54
- if (dv !== -1) {
55
- out[pos++] = (dbq | (dv << dn)) & 0xff
56
- }
57
- return out.subarray(0, pos)
58
- }
59
-
60
- export default class Base93Decoder {
61
- constructor (source, { mimeType = '', preferTextStreamDecoding = false } = {}) {
62
- this.sourceIterator = source?.[Symbol.iterator]?.() || source?.[Symbol.asyncIterator]?.() || source()
63
- this.asTextStream = preferTextStreamDecoding && mimeType.startsWith('text/')
64
- if (this.asTextStream) this.textDecoder = new TextDecoder()
65
- }
66
-
67
- // decoder generator
68
- * [Symbol.iterator] (base93String) {
69
- if (this.asTextStream) {
70
- while (base93String) {
71
- // stream=true avoids cutting a multi-byte character
72
- base93String = yield this.textDecoder.decode(decode(base93String), { stream: true })
73
- }
74
- } else {
75
- while (base93String) {
76
- base93String = yield decode(base93String)
77
- }
78
- }
79
- }
80
-
81
- // Gets the decoded data.
82
- getDecoded () { return iteratorToStream(this, this.sourceIterator) }
83
- }
84
-
85
- function iteratorToStream (decoder, sourceIterator) {
86
- return new ReadableStream({
87
- decoderIterator: null,
88
- async start (controller) {
89
- const { value: chunk, done } = await sourceIterator.next()
90
- if (done) return controller.close()
91
-
92
- // Pass first chunk when instantiating the decoder generator
93
- this.decoderIterator = decoder[Symbol.iterator](chunk)
94
- const { value } = this.decoderIterator.next()
95
- if (value) controller.enqueue(value)
96
- },
97
- async pull (controller) {
98
- if (!this.decoderIterator) return
99
-
100
- const { value: chunk, done: sourceDone } = await sourceIterator.next()
101
- const { value, done } = this.decoderIterator.next(chunk)
102
-
103
- if (value) controller.enqueue(value)
104
- if (done || sourceDone) controller.close()
105
- }
106
- })
107
- }
@@ -1,96 +0,0 @@
1
- // https://github.com/ticlo/arrow-code/blob/master/src/base93.ts
2
- // https://github.com/ticlo/arrow-code/blob/master/LICENSE - Apache 2.0
3
-
4
- // JSON-safe (space included; " and \ excluded)
5
- const BASE93_ALPHABET =
6
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&'()*+,-./:;<=>?@[]^_`{|}~ "
7
- const ENCODING_TABLE = (() => {
8
- const out = new Uint16Array(93)
9
- for (let i = 0; i < 93; i++) out[i] = BASE93_ALPHABET.charCodeAt(i)
10
- return out
11
- })()
12
-
13
- function codesToString (codes, len) {
14
- const CHUNK = 16384 // 16k chars per slice
15
- let s = ''
16
- for (let i = 0; i < len; i += CHUNK) {
17
- const end = i + CHUNK < len ? i + CHUNK : len
18
- s += String.fromCharCode.apply(
19
- null,
20
- Array.prototype.slice.call(codes, i, end)
21
- )
22
- }
23
- return s
24
- }
25
-
26
- export default class Base93Encoder {
27
- constructor (prefix = '') {
28
- // bit reservoir
29
- this._ebq = 0 // queued bits
30
- this._en = 0 // number of bits in ebq
31
-
32
- // output parts
33
- this._parts = []
34
- this._finished = false
35
-
36
- if (prefix) this._parts.push(prefix)
37
- }
38
-
39
- // Stream bytes; keeps reservoir across calls.
40
- update (bytes) {
41
- if (this._finished) throw new Error('Encoder already finalized.')
42
- const src = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes)
43
-
44
- // Over-allocate for this update; we’ll trim to 'pos'
45
- const outCodes = new Uint16Array(Math.ceil(src.length * 8 / 6.5) + 4)
46
- let pos = 0
47
-
48
- let ebq = this._ebq
49
- let en = this._en
50
- let ev = 0
51
-
52
- for (let i = 0; i < src.length; i++) {
53
- ebq |= (src[i] & 0xff) << en
54
- en += 8
55
- if (en > 13) {
56
- ev = ebq & 0x1fff
57
- if (ev > 456) {
58
- ebq >>>= 13
59
- en -= 13
60
- } else {
61
- ev = ebq & 0x3fff
62
- ebq >>>= 14
63
- en -= 14
64
- }
65
- outCodes[pos++] = ENCODING_TABLE[ev % 93]
66
- outCodes[pos++] = ENCODING_TABLE[(ev / 93) | 0]
67
- }
68
- }
69
-
70
- // persist reservoir
71
- this._ebq = ebq
72
- this._en = en
73
-
74
- if (pos) this._parts.push(codesToString(outCodes, pos))
75
- return this
76
- }
77
-
78
- // Finalize on first call: flush trailing partial block, join, lock.
79
- getEncoded () {
80
- if (!this._finished) {
81
- if (this._en > 0) {
82
- const outCodes = new Uint16Array(2)
83
- let pos = 0
84
- outCodes[pos++] = ENCODING_TABLE[this._ebq % 93]
85
- if (this._en > 7 || this._ebq > 92) {
86
- outCodes[pos++] = ENCODING_TABLE[(this._ebq / 93) | 0]
87
- }
88
- this._parts.push(codesToString(outCodes, pos))
89
- }
90
- this._finished = true
91
- this._ebq = 0
92
- this._en = 0
93
- }
94
- return this._parts.join('')
95
- }
96
- }