@tsworldtech/flux-sse 0.0.1

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/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var b=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var x=(n,t)=>{for(var d in t)b(n,d,{get:t[d],enumerable:!0})},M=(n,t,d,c)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of C(t))!R.call(n,o)&&o!==d&&b(n,o,{get:()=>t[o],enumerable:!(c=E(t,o))||c.enumerable});return n};var I=n=>M(b({},"__esModule",{value:!0}),n);var D={};x(D,{createSseAdapter:()=>w});module.exports=I(D);function O(n){if(!n||typeof n!="object")return console.warn("[flux:sse] normalizeDeltaRow() \u2014 row is not an object, dropping:",n),null;let t=n.data!==void 0?n.data:n.row_data!==void 0?n.row_data:n;if(typeof t=="string")try{t=JSON.parse(t)}catch(c){console.error("[flux:sse] normalizeDeltaRow() \u2014 failed to JSON.parse data string:",t,c),t=null}let d=String(t?.id??t?._id??n.row_id??n.id??"");return{data:t,rowId:d}}function w(n){let t=new Map,d=[],c=[],o=0,l=!1,y=new Map,v,g={};function A(e){let a=typeof n.url=="function"?n.url(e):n.url,r=g[e]??0;try{let i=a.includes("?")?"&":"?";return`${a}${i}syncAnchor=${r}`}catch{return a}}function m(e,a,r){t.has(e)&&(t.get(e).close(),t.delete(e));let i=A(e),s=new EventSource(i,n.options);s.onopen=()=>{o++,v&&v(e),l||(l=!0,d.forEach(u=>u()))},s.onerror=()=>{s.readyState===EventSource.CLOSED&&(o=Math.max(0,o-1),o===0&&l&&(l=!1,c.forEach(u=>u())))};let f=u=>{let p;try{p=typeof u.data=="string"?JSON.parse(u.data):u.data}catch{console.warn(`[flux:sse] handler() \u2014 non-JSON payload on channel "${e}", forwarding as raw:`,u.data),r({raw:u.data,source:"sse",channel:e});return}let S=O(p);if(!S){console.warn(`[flux:sse] handler() \u2014 dropping event on channel "${e}" that failed to normalize:`,p);return}r({...p,data:S.data,id:S.rowId||p.id})};s.addEventListener(a,f),t.set(e,s)}let h={connect(){y.forEach(({event:e,cb:a},r)=>{t.has(r)||m(r,e,a)}),g={}},disconnect(){t.forEach(e=>e.close()),t.clear(),o=0,l=!1,c.forEach(e=>e())},notifyReconnect(e){g=e},subscribe(e,a,r){y.set(e,{event:a,cb:r}),l&&m(e,a,r)},unsubscribe(e,a=!1){let r=t.get(e);r&&(r.close(),t.delete(e)),y.delete(e),o=Math.max(0,o-1),!a&&o===0&&l&&(l=!1,c.forEach(i=>i()))},onConnect(e){d.push(e)},onDisconnect(e){c.push(e)},async sync(e,a){let r=t.get(e);if(r&&r.readyState!==EventSource.OPEN)return new Promise(i=>{let s=!1,f=()=>{s||(s=!0,r.removeEventListener("open",f),i())};r.addEventListener("open",f),setTimeout(()=>{s||(s=!0,r.removeEventListener("open",f),i())},6e4)})},onChannelSubscribed(e){v=e},destroy(){t.forEach(e=>e.close()),t.clear(),y.clear(),d.length=0,c.length=0,o=0,l=!1,g={},v=void 0}};return n.reconcileUrl&&(h.reconcile=async(e,a)=>{let r=typeof n.reconcileUrl=="function"?n.reconcileUrl(e):n.reconcileUrl;try{let i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({channel:e,localIds:a})});if(!i.ok)return{deletedIds:[]};let s=await i.json();return{deletedIds:Array.isArray(s?.deletedIds)?s.deletedIds:[]}}catch{return{deletedIds:[]}}}),n.deltaRetentionMs!==void 0&&(h.getDeltaRetentionWindow=()=>n.deltaRetentionMs),h}0&&(module.exports={createSseAdapter});
@@ -0,0 +1,27 @@
1
+ import { RealtimeAdapter } from '@tsworldtech/flux';
2
+
3
+ interface SseAdapterConfig {
4
+ url: string | ((channel: string) => string);
5
+ options?: EventSourceInit;
6
+ /**
7
+ * How far back in time (ms) this backend can reliably enumerate
8
+ * ALL changes, including deletions, via the ?syncAnchor=<ts> backfill
9
+ * query embedded in the EventSource URL (see buildChannelUrl below).
10
+ * Entirely a property of your own SSE endpoint's implementation. Leave
11
+ * unset if your endpoint's backfill query doesn't account for
12
+ * deletions — deletion catch-up is then best-effort with no hard
13
+ * staleness guarantee.
14
+ */
15
+ deltaRetentionMs?: number;
16
+ /**
17
+ * Endpoint for POSTing a reconciliation request. SSE has no
18
+ * client→server channel, so reconcile() can't reuse the EventSource
19
+ * connection the way sync() does. If omitted, reconcile() is not
20
+ * attached to the adapter at all — same degraded-mode behavior as any
21
+ * other adapter without reconcile support.
22
+ */
23
+ reconcileUrl?: string | ((channel: string) => string);
24
+ }
25
+ declare function createSseAdapter(config: SseAdapterConfig): RealtimeAdapter;
26
+
27
+ export { type SseAdapterConfig, createSseAdapter };
@@ -0,0 +1,27 @@
1
+ import { RealtimeAdapter } from '@tsworldtech/flux';
2
+
3
+ interface SseAdapterConfig {
4
+ url: string | ((channel: string) => string);
5
+ options?: EventSourceInit;
6
+ /**
7
+ * How far back in time (ms) this backend can reliably enumerate
8
+ * ALL changes, including deletions, via the ?syncAnchor=<ts> backfill
9
+ * query embedded in the EventSource URL (see buildChannelUrl below).
10
+ * Entirely a property of your own SSE endpoint's implementation. Leave
11
+ * unset if your endpoint's backfill query doesn't account for
12
+ * deletions — deletion catch-up is then best-effort with no hard
13
+ * staleness guarantee.
14
+ */
15
+ deltaRetentionMs?: number;
16
+ /**
17
+ * Endpoint for POSTing a reconciliation request. SSE has no
18
+ * client→server channel, so reconcile() can't reuse the EventSource
19
+ * connection the way sync() does. If omitted, reconcile() is not
20
+ * attached to the adapter at all — same degraded-mode behavior as any
21
+ * other adapter without reconcile support.
22
+ */
23
+ reconcileUrl?: string | ((channel: string) => string);
24
+ }
25
+ declare function createSseAdapter(config: SseAdapterConfig): RealtimeAdapter;
26
+
27
+ export { type SseAdapterConfig, createSseAdapter };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ function w(t){if(!t||typeof t!="object")return console.warn("[flux:sse] normalizeDeltaRow() \u2014 row is not an object, dropping:",t),null;let n=t.data!==void 0?t.data:t.row_data!==void 0?t.row_data:t;if(typeof n=="string")try{n=JSON.parse(n)}catch(l){console.error("[flux:sse] normalizeDeltaRow() \u2014 failed to JSON.parse data string:",n,l),n=null}let u=String(n?.id??n?._id??t.row_id??t.id??"");return{data:n,rowId:u}}function A(t){let n=new Map,u=[],l=[],i=0,d=!1,y=new Map,v,g={};function m(e){let o=typeof t.url=="function"?t.url(e):t.url,r=g[e]??0;try{let s=o.includes("?")?"&":"?";return`${o}${s}syncAnchor=${r}`}catch{return o}}function b(e,o,r){n.has(e)&&(n.get(e).close(),n.delete(e));let s=m(e),a=new EventSource(s,t.options);a.onopen=()=>{i++,v&&v(e),d||(d=!0,u.forEach(c=>c()))},a.onerror=()=>{a.readyState===EventSource.CLOSED&&(i=Math.max(0,i-1),i===0&&d&&(d=!1,l.forEach(c=>c())))};let f=c=>{let p;try{p=typeof c.data=="string"?JSON.parse(c.data):c.data}catch{console.warn(`[flux:sse] handler() \u2014 non-JSON payload on channel "${e}", forwarding as raw:`,c.data),r({raw:c.data,source:"sse",channel:e});return}let S=w(p);if(!S){console.warn(`[flux:sse] handler() \u2014 dropping event on channel "${e}" that failed to normalize:`,p);return}r({...p,data:S.data,id:S.rowId||p.id})};a.addEventListener(o,f),n.set(e,a)}let h={connect(){y.forEach(({event:e,cb:o},r)=>{n.has(r)||b(r,e,o)}),g={}},disconnect(){n.forEach(e=>e.close()),n.clear(),i=0,d=!1,l.forEach(e=>e())},notifyReconnect(e){g=e},subscribe(e,o,r){y.set(e,{event:o,cb:r}),d&&b(e,o,r)},unsubscribe(e,o=!1){let r=n.get(e);r&&(r.close(),n.delete(e)),y.delete(e),i=Math.max(0,i-1),!o&&i===0&&d&&(d=!1,l.forEach(s=>s()))},onConnect(e){u.push(e)},onDisconnect(e){l.push(e)},async sync(e,o){let r=n.get(e);if(r&&r.readyState!==EventSource.OPEN)return new Promise(s=>{let a=!1,f=()=>{a||(a=!0,r.removeEventListener("open",f),s())};r.addEventListener("open",f),setTimeout(()=>{a||(a=!0,r.removeEventListener("open",f),s())},6e4)})},onChannelSubscribed(e){v=e},destroy(){n.forEach(e=>e.close()),n.clear(),y.clear(),u.length=0,l.length=0,i=0,d=!1,g={},v=void 0}};return t.reconcileUrl&&(h.reconcile=async(e,o)=>{let r=typeof t.reconcileUrl=="function"?t.reconcileUrl(e):t.reconcileUrl;try{let s=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({channel:e,localIds:o})});if(!s.ok)return{deletedIds:[]};let a=await s.json();return{deletedIds:Array.isArray(a?.deletedIds)?a.deletedIds:[]}}catch{return{deletedIds:[]}}}),t.deltaRetentionMs!==void 0&&(h.getDeltaRetentionWindow=()=>t.deltaRetentionMs),h}export{A as createSseAdapter};
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@tsworldtech/flux-sse",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "description": "Server-Sent Events realtime adapter for @tsworldtech/flux",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js",
18
+ "require": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "peerDependencies": {
25
+ "@tsworldtech/flux": ">=0.0.1"
26
+ },
27
+ "devDependencies": {
28
+ "tsup": "^8.3.5",
29
+ "typescript": "5.9.3",
30
+ "@tsworldtech/flux": "0.0.1"
31
+ },
32
+ "scripts": {
33
+ "build": "tsup"
34
+ }
35
+ }