@closerclick/closer-click-notifications 0.2.0 → 0.3.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 +4 -0
- package/package.json +1 -1
- package/src/index.d.ts +7 -0
- package/src/index.js +32 -0
package/README.md
CHANGED
|
@@ -153,6 +153,10 @@ receipts.start()
|
|
|
153
153
|
await receipts.report({ toPubkey: parsed.publickey, url: sharedUrl, name: parsed.name })
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
+
- **Acumular (referidos)**: pasá `onReceipt(env)` para que tu app sume por acuse
|
|
157
|
+
fresco (además de la notificación). Para enlaces de invitación con tu pubkey
|
|
158
|
+
embebida usá `packPubkey(pubkey)` → token base64url para `#i=<token>`, y
|
|
159
|
+
`unpackPubkey(token)` del lado que abre (matchea byte a byte el ruteo del proxy).
|
|
156
160
|
- **Identidad del que abre**: el sobre incluye **siempre** `from { pubkey, nick }`.
|
|
157
161
|
- **Anti-spam**: `report` aplica throttle por `(toPubkey|url)` (default 24h).
|
|
158
162
|
- **Propio**: `report` es no-op si el contenido es tuyo (`toPubkey === tu pubkey`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@closerclick/closer-click-notifications",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Notificaciones compartidas del ecosistema Closer Click: controlador data-agnostic (permiso del navegador + preferencias por categoría con scope por app + disparo), el Web Component <closer-click-notifications> (panel de ajustes), helper de Web Push ligado al vault + proxy y acuses de apertura de contenido compartido (createShareReceipts). Autohosteado, Shadow DOM, sin JS de terceros ni cookies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/index.d.ts
CHANGED
|
@@ -123,6 +123,8 @@ export interface ShareReceiptsConfig {
|
|
|
123
123
|
throttleMs?: number
|
|
124
124
|
/** Override del click (default: navegar a url). */
|
|
125
125
|
onOpen?: (url: string, env: ShareReceiptEnvelope) => void
|
|
126
|
+
/** Hook por acuse fresco entrante (además de notificar): para acumular (referidos, etc.). */
|
|
127
|
+
onReceipt?: (env: ShareReceiptEnvelope) => void
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
export interface ShareReceipts {
|
|
@@ -140,6 +142,11 @@ export interface ShareReceipts {
|
|
|
140
142
|
/** Motor común de acuses de apertura de contenido compartido. */
|
|
141
143
|
export function createShareReceipts(cfg: ShareReceiptsConfig): ShareReceipts
|
|
142
144
|
|
|
145
|
+
/** Empaqueta una pubkey (JWK string del vault) para un enlace de invitación/acuse (base64url exacto). */
|
|
146
|
+
export function packPubkey(pubkey: string): string
|
|
147
|
+
/** Desempaqueta la pubkey de un token de enlace. Devuelve null si es inválido. */
|
|
148
|
+
export function unpackPubkey(token: string): string | null
|
|
149
|
+
|
|
143
150
|
export class CloserClickNotifications extends HTMLElement {
|
|
144
151
|
controller: NotificationsController | null
|
|
145
152
|
}
|
package/src/index.js
CHANGED
|
@@ -314,6 +314,7 @@ export function createShareReceipts (cfg = {}) {
|
|
|
314
314
|
const render = typeof cfg.render === 'function' ? cfg.render : null
|
|
315
315
|
const lang = cfg.lang || 'auto'
|
|
316
316
|
const throttleMs = cfg.throttleMs != null ? cfg.throttleMs : 24 * 60 * 60 * 1000
|
|
317
|
+
const onReceipt = typeof cfg.onReceipt === 'function' ? cfg.onReceipt : null
|
|
317
318
|
const onOpen = typeof cfg.onOpen === 'function'
|
|
318
319
|
? cfg.onOpen
|
|
319
320
|
: (url) => { try { if (isBrowser && url) location.assign(url) } catch (_) {} }
|
|
@@ -392,6 +393,9 @@ export function createShareReceipts (cfg = {}) {
|
|
|
392
393
|
const key = `${(env.from && env.from.pubkey) || ''}|${env.url}|${env.ts || ''}`
|
|
393
394
|
if (_seen.has(key)) return
|
|
394
395
|
_seen.add(key)
|
|
396
|
+
// Hook para que la app ACUMULE (p. ej. referidos) además de notificar. Se
|
|
397
|
+
// llama una vez por acuse fresco; la app hace su propio dedup durable.
|
|
398
|
+
if (onReceipt) { try { onReceipt(env) } catch (_) {} }
|
|
395
399
|
const rendered = render ? render(env) : _defaultRender(env)
|
|
396
400
|
if (!rendered) return
|
|
397
401
|
const { title, body, ...rest } = rendered
|
|
@@ -427,6 +431,34 @@ export function createShareReceipts (cfg = {}) {
|
|
|
427
431
|
return { report, start, stop, category, RECEIPT_TAG, RECEIPT_VERSION }
|
|
428
432
|
}
|
|
429
433
|
|
|
434
|
+
/* ----------------------------------------------------------------------------
|
|
435
|
+
* Pubkey ↔ token compacto para enlaces de invitación/acuse.
|
|
436
|
+
*
|
|
437
|
+
* El acuse se enruta por la MISMA pubkey (JWK string) que el destinatario usó en
|
|
438
|
+
* `identify`. Para meterla en un enlace compartible (`#i=<token>`) la empaquetamos
|
|
439
|
+
* como base64url del string EXACTO (no comprimimos el punto P-256: así el ruteo
|
|
440
|
+
* por `sendByPubkey` matchea byte a byte sin reconstruir el JWK). Quien abre el
|
|
441
|
+
* enlace desempaqueta la pubkey del autor y le manda el acuse.
|
|
442
|
+
* --------------------------------------------------------------------------*/
|
|
443
|
+
|
|
444
|
+
function _b64urlEncode (str) {
|
|
445
|
+
const bytes = new TextEncoder().encode(str);
|
|
446
|
+
let bin = ''; for (const b of bytes) bin += String.fromCharCode(b);
|
|
447
|
+
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
448
|
+
}
|
|
449
|
+
function _b64urlDecode (s) {
|
|
450
|
+
const b64 = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
451
|
+
const bin = atob(b64);
|
|
452
|
+
const bytes = new Uint8Array(bin.length);
|
|
453
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
454
|
+
return new TextDecoder().decode(bytes);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** Empaqueta una pubkey (JWK string del vault) para un enlace: base64url exacto. */
|
|
458
|
+
export function packPubkey (pubkey) { return _b64urlEncode(String(pubkey)); }
|
|
459
|
+
/** Desempaqueta la pubkey de un token de enlace. Devuelve null si es inválido. */
|
|
460
|
+
export function unpackPubkey (token) { try { return _b64urlDecode(String(token)); } catch (_) { return null; } }
|
|
461
|
+
|
|
430
462
|
/* ============================================================================
|
|
431
463
|
* 4) Web Component ── <closer-click-notifications> (panel de ajustes)
|
|
432
464
|
* ==========================================================================*/
|