@supabase/realtime-js 1.3.6 → 1.4.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/dist/main/RealtimePresence.d.ts +92 -0
- package/dist/main/RealtimePresence.d.ts.map +1 -0
- package/dist/main/RealtimePresence.js +198 -0
- package/dist/main/RealtimePresence.js.map +1 -0
- package/dist/main/index.d.ts +2 -1
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js +3 -1
- package/dist/main/index.js.map +1 -1
- package/dist/main/lib/version.d.ts +1 -1
- package/dist/main/lib/version.js +1 -1
- package/dist/module/RealtimePresence.d.ts +92 -0
- package/dist/module/RealtimePresence.d.ts.map +1 -0
- package/dist/module/RealtimePresence.js +192 -0
- package/dist/module/RealtimePresence.js.map +1 -0
- package/dist/module/index.d.ts +2 -1
- package/dist/module/index.d.ts.map +1 -1
- package/dist/module/index.js +2 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/lib/version.d.ts +1 -1
- package/dist/module/lib/version.js +1 -1
- package/package.json +4 -1
- package/src/RealtimePresence.ts +319 -0
- package/src/index.ts +2 -0
- package/src/lib/version.ts +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { PresenceOpts, PresenceOnJoinCallback, PresenceOnLeaveCallback } from 'phoenix';
|
|
2
|
+
import RealtimeSubscription from './RealtimeSubscription';
|
|
3
|
+
declare type Presence = {
|
|
4
|
+
presence_id: string;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
declare type PresenceState = {
|
|
8
|
+
[key: string]: Presence[];
|
|
9
|
+
};
|
|
10
|
+
declare type PresenceDiff = {
|
|
11
|
+
joins: PresenceState;
|
|
12
|
+
leaves: PresenceState;
|
|
13
|
+
};
|
|
14
|
+
declare type RawPresenceState = {
|
|
15
|
+
[key: string]: Record<'metas', {
|
|
16
|
+
phx_ref?: string;
|
|
17
|
+
phx_ref_prev?: string;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}[]>;
|
|
20
|
+
};
|
|
21
|
+
declare type RawPresenceDiff = {
|
|
22
|
+
joins: RawPresenceState;
|
|
23
|
+
leaves: RawPresenceState;
|
|
24
|
+
};
|
|
25
|
+
declare type PresenceChooser<T> = (key: string, presences: any) => T;
|
|
26
|
+
export default class RealtimePresence {
|
|
27
|
+
channel: RealtimeSubscription;
|
|
28
|
+
state: PresenceState;
|
|
29
|
+
pendingDiffs: RawPresenceDiff[];
|
|
30
|
+
joinRef: string | null;
|
|
31
|
+
caller: {
|
|
32
|
+
onJoin: PresenceOnJoinCallback;
|
|
33
|
+
onLeave: PresenceOnLeaveCallback;
|
|
34
|
+
onSync: () => void;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Initializes the Presence
|
|
38
|
+
* @param channel - The RealtimeSubscription
|
|
39
|
+
* @param opts - The options,
|
|
40
|
+
* for example `{events: {state: 'state', diff: 'diff'}}`
|
|
41
|
+
*/
|
|
42
|
+
constructor(channel: RealtimeSubscription, opts?: PresenceOpts);
|
|
43
|
+
/**
|
|
44
|
+
* Used to sync the list of presences on the server
|
|
45
|
+
* with the client's state. An optional `onJoin` and `onLeave` callback can
|
|
46
|
+
* be provided to react to changes in the client's local presences across
|
|
47
|
+
* disconnects and reconnects with the server.
|
|
48
|
+
*/
|
|
49
|
+
static syncState(currentState: PresenceState, newState: RawPresenceState | PresenceState, onJoin: PresenceOnJoinCallback, onLeave: PresenceOnLeaveCallback): PresenceState;
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* Used to sync a diff of presence join and leave
|
|
53
|
+
* events from the server, as they happen. Like `syncState`, `syncDiff`
|
|
54
|
+
* accepts optional `onJoin` and `onLeave` callbacks to react to a user
|
|
55
|
+
* joining or leaving from a device.
|
|
56
|
+
*/
|
|
57
|
+
static syncDiff(state: PresenceState, diff: RawPresenceDiff | PresenceDiff, onJoin: PresenceOnJoinCallback, onLeave: PresenceOnLeaveCallback): PresenceState;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the array of presences, with selected metadata.
|
|
60
|
+
*/
|
|
61
|
+
static list<T = any>(presences: PresenceState, chooser: PresenceChooser<T> | undefined): T[];
|
|
62
|
+
private static map;
|
|
63
|
+
/**
|
|
64
|
+
* Remove 'metas' key
|
|
65
|
+
* Change 'phx_ref' to 'presence_id'
|
|
66
|
+
* Remove 'phx_ref' and 'phx_ref_prev'
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // returns {
|
|
70
|
+
* abc123: [
|
|
71
|
+
* { presence_id: '2', user_id: 1 },
|
|
72
|
+
* { presence_id: '3', user_id: 2 }
|
|
73
|
+
* ]
|
|
74
|
+
* }
|
|
75
|
+
* RealtimePresence.transformState({
|
|
76
|
+
* abc123: {
|
|
77
|
+
* metas: [
|
|
78
|
+
* { phx_ref: '2', phx_ref_prev: '1' user_id: 1 },
|
|
79
|
+
* { phx_ref: '3', user_id: 2 }
|
|
80
|
+
* ]
|
|
81
|
+
* }
|
|
82
|
+
* })
|
|
83
|
+
*/
|
|
84
|
+
private static transformState;
|
|
85
|
+
onJoin(callback: PresenceOnJoinCallback): void;
|
|
86
|
+
onLeave(callback: PresenceOnLeaveCallback): void;
|
|
87
|
+
onSync(callback: () => void): void;
|
|
88
|
+
list<T = any>(by?: PresenceChooser<T>): T[];
|
|
89
|
+
private inPendingSyncState;
|
|
90
|
+
}
|
|
91
|
+
export {};
|
|
92
|
+
//# sourceMappingURL=RealtimePresence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RealtimePresence.d.ts","sourceRoot":"","sources":["../../src/RealtimePresence.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,SAAS,CAAA;AAChB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;AAEzD,aAAK,QAAQ,GAAG;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,CAAA;AAED,aAAK,aAAa,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;CAAE,CAAA;AAElD,aAAK,YAAY,GAAG;IAClB,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,aAAa,CAAA;CACtB,CAAA;AAED,aAAK,gBAAgB,GAAG;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CACnB,OAAO,EACP;QACE,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KACnB,EAAE,CACJ,CAAA;CACF,CAAA;AAED,aAAK,eAAe,GAAG;IACrB,KAAK,EAAE,gBAAgB,CAAA;IACvB,MAAM,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,aAAK,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAA;AAE5D,MAAM,CAAC,OAAO,OAAO,gBAAgB;IAoBhB,OAAO,EAAE,oBAAoB;IAnBhD,KAAK,EAAE,aAAa,CAAK;IACzB,YAAY,EAAE,eAAe,EAAE,CAAK;IACpC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAO;IAC7B,MAAM,EAAE;QACN,MAAM,EAAE,sBAAsB,CAAA;QAC9B,OAAO,EAAE,uBAAuB,CAAA;QAChC,MAAM,EAAE,MAAM,IAAI,CAAA;KACnB,CAIA;IAED;;;;;OAKG;gBACgB,OAAO,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,YAAY;IAkDrE;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CACd,YAAY,EAAE,aAAa,EAC3B,QAAQ,EAAE,gBAAgB,GAAG,aAAa,EAC1C,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,aAAa;IA0ChB;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CACb,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,eAAe,GAAG,YAAY,EACpC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,aAAa;IAoDhB;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,EACjB,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,SAAS,GACtC,CAAC,EAAE;IAUN,OAAO,CAAC,MAAM,CAAC,GAAG;IAOlB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAyB7B,MAAM,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAI9C,OAAO,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI;IAIhD,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIlC,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;IAI3C,OAAO,CAAC,kBAAkB;CAG3B"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
This file draws heavily from https://github.com/phoenixframework/phoenix/blob/d344ec0a732ab4ee204215b31de69cf4be72e3bf/assets/js/phoenix/presence.js
|
|
4
|
+
License: https://github.com/phoenixframework/phoenix/blob/d344ec0a732ab4ee204215b31de69cf4be72e3bf/LICENSE.md
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
|
|
11
|
+
class RealtimePresence {
|
|
12
|
+
/**
|
|
13
|
+
* Initializes the Presence
|
|
14
|
+
* @param channel - The RealtimeSubscription
|
|
15
|
+
* @param opts - The options,
|
|
16
|
+
* for example `{events: {state: 'state', diff: 'diff'}}`
|
|
17
|
+
*/
|
|
18
|
+
constructor(channel, opts) {
|
|
19
|
+
this.channel = channel;
|
|
20
|
+
this.state = {};
|
|
21
|
+
this.pendingDiffs = [];
|
|
22
|
+
this.joinRef = null;
|
|
23
|
+
this.caller = {
|
|
24
|
+
onJoin: () => { },
|
|
25
|
+
onLeave: () => { },
|
|
26
|
+
onSync: () => { },
|
|
27
|
+
};
|
|
28
|
+
const events = (opts === null || opts === void 0 ? void 0 : opts.events) || {
|
|
29
|
+
state: 'presence_state',
|
|
30
|
+
diff: 'presence_diff',
|
|
31
|
+
};
|
|
32
|
+
this.channel.on(events.state, (newState) => {
|
|
33
|
+
const { onJoin, onLeave, onSync } = this.caller;
|
|
34
|
+
this.joinRef = this.channel.joinRef();
|
|
35
|
+
this.state = RealtimePresence.syncState(this.state, newState, onJoin, onLeave);
|
|
36
|
+
this.pendingDiffs.forEach((diff) => {
|
|
37
|
+
this.state = RealtimePresence.syncDiff(this.state, diff, onJoin, onLeave);
|
|
38
|
+
});
|
|
39
|
+
this.pendingDiffs = [];
|
|
40
|
+
onSync();
|
|
41
|
+
});
|
|
42
|
+
this.channel.on(events.diff, (diff) => {
|
|
43
|
+
const { onJoin, onLeave, onSync } = this.caller;
|
|
44
|
+
if (this.inPendingSyncState()) {
|
|
45
|
+
this.pendingDiffs.push(diff);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.state = RealtimePresence.syncDiff(this.state, diff, onJoin, onLeave);
|
|
49
|
+
onSync();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Used to sync the list of presences on the server
|
|
55
|
+
* with the client's state. An optional `onJoin` and `onLeave` callback can
|
|
56
|
+
* be provided to react to changes in the client's local presences across
|
|
57
|
+
* disconnects and reconnects with the server.
|
|
58
|
+
*/
|
|
59
|
+
static syncState(currentState, newState, onJoin, onLeave) {
|
|
60
|
+
const state = lodash_clonedeep_1.default(currentState);
|
|
61
|
+
const transformedState = this.transformState(newState);
|
|
62
|
+
const joins = {};
|
|
63
|
+
const leaves = {};
|
|
64
|
+
this.map(state, (key, presences) => {
|
|
65
|
+
if (!transformedState[key]) {
|
|
66
|
+
leaves[key] = presences;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
this.map(transformedState, (key, newPresences) => {
|
|
70
|
+
const currentPresences = state[key];
|
|
71
|
+
if (currentPresences) {
|
|
72
|
+
const newPresenceIds = newPresences.map((m) => m.presence_id);
|
|
73
|
+
const curPresenceIds = currentPresences.map((m) => m.presence_id);
|
|
74
|
+
const joinedPresences = newPresences.filter((m) => curPresenceIds.indexOf(m.presence_id) < 0);
|
|
75
|
+
const leftPresences = currentPresences.filter((m) => newPresenceIds.indexOf(m.presence_id) < 0);
|
|
76
|
+
if (joinedPresences.length > 0) {
|
|
77
|
+
joins[key] = joinedPresences;
|
|
78
|
+
}
|
|
79
|
+
if (leftPresences.length > 0) {
|
|
80
|
+
leaves[key] = leftPresences;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
joins[key] = newPresences;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return this.syncDiff(state, { joins, leaves }, onJoin, onLeave);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
*
|
|
91
|
+
* Used to sync a diff of presence join and leave
|
|
92
|
+
* events from the server, as they happen. Like `syncState`, `syncDiff`
|
|
93
|
+
* accepts optional `onJoin` and `onLeave` callbacks to react to a user
|
|
94
|
+
* joining or leaving from a device.
|
|
95
|
+
*/
|
|
96
|
+
static syncDiff(state, diff, onJoin, onLeave) {
|
|
97
|
+
const { joins, leaves } = {
|
|
98
|
+
joins: this.transformState(diff.joins),
|
|
99
|
+
leaves: this.transformState(diff.leaves),
|
|
100
|
+
};
|
|
101
|
+
if (!onJoin) {
|
|
102
|
+
onJoin = () => { };
|
|
103
|
+
}
|
|
104
|
+
if (!onLeave) {
|
|
105
|
+
onLeave = () => { };
|
|
106
|
+
}
|
|
107
|
+
this.map(joins, (key, newPresences) => {
|
|
108
|
+
const currentPresences = state[key];
|
|
109
|
+
state[key] = lodash_clonedeep_1.default(newPresences);
|
|
110
|
+
if (currentPresences) {
|
|
111
|
+
const joinedPresenceIds = state[key].map((m) => m.presence_id);
|
|
112
|
+
const curPresences = currentPresences.filter((m) => joinedPresenceIds.indexOf(m.presence_id) < 0);
|
|
113
|
+
state[key].unshift(...curPresences);
|
|
114
|
+
}
|
|
115
|
+
onJoin(key, currentPresences, newPresences);
|
|
116
|
+
});
|
|
117
|
+
this.map(leaves, (key, leftPresences) => {
|
|
118
|
+
let currentPresences = state[key];
|
|
119
|
+
if (!currentPresences)
|
|
120
|
+
return;
|
|
121
|
+
const presenceIdsToRemove = leftPresences.map((m) => m.presence_id);
|
|
122
|
+
currentPresences = currentPresences.filter((m) => presenceIdsToRemove.indexOf(m.presence_id) < 0);
|
|
123
|
+
state[key] = currentPresences;
|
|
124
|
+
onLeave(key, currentPresences, leftPresences);
|
|
125
|
+
if (currentPresences.length === 0)
|
|
126
|
+
delete state[key];
|
|
127
|
+
});
|
|
128
|
+
return state;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Returns the array of presences, with selected metadata.
|
|
132
|
+
*/
|
|
133
|
+
static list(presences, chooser) {
|
|
134
|
+
if (!chooser) {
|
|
135
|
+
chooser = (_key, pres) => pres;
|
|
136
|
+
}
|
|
137
|
+
return this.map(presences, (key, presences) => chooser(key, presences));
|
|
138
|
+
}
|
|
139
|
+
static map(obj, func) {
|
|
140
|
+
return Object.getOwnPropertyNames(obj).map((key) => func(key, obj[key]));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Remove 'metas' key
|
|
144
|
+
* Change 'phx_ref' to 'presence_id'
|
|
145
|
+
* Remove 'phx_ref' and 'phx_ref_prev'
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* // returns {
|
|
149
|
+
* abc123: [
|
|
150
|
+
* { presence_id: '2', user_id: 1 },
|
|
151
|
+
* { presence_id: '3', user_id: 2 }
|
|
152
|
+
* ]
|
|
153
|
+
* }
|
|
154
|
+
* RealtimePresence.transformState({
|
|
155
|
+
* abc123: {
|
|
156
|
+
* metas: [
|
|
157
|
+
* { phx_ref: '2', phx_ref_prev: '1' user_id: 1 },
|
|
158
|
+
* { phx_ref: '3', user_id: 2 }
|
|
159
|
+
* ]
|
|
160
|
+
* }
|
|
161
|
+
* })
|
|
162
|
+
*/
|
|
163
|
+
static transformState(state) {
|
|
164
|
+
state = lodash_clonedeep_1.default(state);
|
|
165
|
+
return Object.getOwnPropertyNames(state).reduce((newState, key) => {
|
|
166
|
+
const presences = state[key];
|
|
167
|
+
if ('metas' in presences) {
|
|
168
|
+
newState[key] = presences.metas.map((presence) => {
|
|
169
|
+
presence['presence_id'] = presence['phx_ref'];
|
|
170
|
+
delete presence['phx_ref'];
|
|
171
|
+
delete presence['phx_ref_prev'];
|
|
172
|
+
return presence;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
newState[key] = presences;
|
|
177
|
+
}
|
|
178
|
+
return newState;
|
|
179
|
+
}, {});
|
|
180
|
+
}
|
|
181
|
+
onJoin(callback) {
|
|
182
|
+
this.caller.onJoin = callback;
|
|
183
|
+
}
|
|
184
|
+
onLeave(callback) {
|
|
185
|
+
this.caller.onLeave = callback;
|
|
186
|
+
}
|
|
187
|
+
onSync(callback) {
|
|
188
|
+
this.caller.onSync = callback;
|
|
189
|
+
}
|
|
190
|
+
list(by) {
|
|
191
|
+
return RealtimePresence.list(this.state, by);
|
|
192
|
+
}
|
|
193
|
+
inPendingSyncState() {
|
|
194
|
+
return !this.joinRef || this.joinRef !== this.channel.joinRef();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.default = RealtimePresence;
|
|
198
|
+
//# sourceMappingURL=RealtimePresence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RealtimePresence.js","sourceRoot":"","sources":["../../src/RealtimePresence.ts"],"names":[],"mappings":"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"}
|
package/dist/main/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Transformers from './lib/transformers';
|
|
2
2
|
import RealtimeClient, { Options as RealtimeClientOptions } from './RealtimeClient';
|
|
3
3
|
import RealtimeSubscription from './RealtimeSubscription';
|
|
4
|
-
|
|
4
|
+
import RealtimePresence from './RealtimePresence';
|
|
5
|
+
export { RealtimeClient, RealtimeClientOptions, RealtimeSubscription, RealtimePresence, Transformers, };
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/main/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAClD,OAAO,cAAc,EAAE,EACrB,OAAO,IAAI,qBAAqB,EACjC,MAAM,kBAAkB,CAAA;AACzB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAClD,OAAO,cAAc,EAAE,EACrB,OAAO,IAAI,qBAAqB,EACjC,MAAM,kBAAkB,CAAA;AACzB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;AACzD,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,GACb,CAAA"}
|
package/dist/main/index.js
CHANGED
|
@@ -22,11 +22,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.Transformers = exports.RealtimeSubscription = exports.RealtimeClient = void 0;
|
|
25
|
+
exports.Transformers = exports.RealtimePresence = exports.RealtimeSubscription = exports.RealtimeClient = void 0;
|
|
26
26
|
const Transformers = __importStar(require("./lib/transformers"));
|
|
27
27
|
exports.Transformers = Transformers;
|
|
28
28
|
const RealtimeClient_1 = __importDefault(require("./RealtimeClient"));
|
|
29
29
|
exports.RealtimeClient = RealtimeClient_1.default;
|
|
30
30
|
const RealtimeSubscription_1 = __importDefault(require("./RealtimeSubscription"));
|
|
31
31
|
exports.RealtimeSubscription = RealtimeSubscription_1.default;
|
|
32
|
+
const RealtimePresence_1 = __importDefault(require("./RealtimePresence"));
|
|
33
|
+
exports.RealtimePresence = RealtimePresence_1.default;
|
|
32
34
|
//# sourceMappingURL=index.js.map
|
package/dist/main/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iEAAkD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iEAAkD;AAYhD,oCAAY;AAXd,sEAEyB;AAKvB,yBAPK,wBAAc,CAOL;AAJhB,kFAAyD;AAMvD,+BANK,8BAAoB,CAML;AALtB,0EAAiD;AAM/C,2BANK,0BAAgB,CAML"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "1.
|
|
1
|
+
export declare const version = "1.4.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/main/lib/version.js
CHANGED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { PresenceOpts, PresenceOnJoinCallback, PresenceOnLeaveCallback } from 'phoenix';
|
|
2
|
+
import RealtimeSubscription from './RealtimeSubscription';
|
|
3
|
+
declare type Presence = {
|
|
4
|
+
presence_id: string;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
};
|
|
7
|
+
declare type PresenceState = {
|
|
8
|
+
[key: string]: Presence[];
|
|
9
|
+
};
|
|
10
|
+
declare type PresenceDiff = {
|
|
11
|
+
joins: PresenceState;
|
|
12
|
+
leaves: PresenceState;
|
|
13
|
+
};
|
|
14
|
+
declare type RawPresenceState = {
|
|
15
|
+
[key: string]: Record<'metas', {
|
|
16
|
+
phx_ref?: string;
|
|
17
|
+
phx_ref_prev?: string;
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}[]>;
|
|
20
|
+
};
|
|
21
|
+
declare type RawPresenceDiff = {
|
|
22
|
+
joins: RawPresenceState;
|
|
23
|
+
leaves: RawPresenceState;
|
|
24
|
+
};
|
|
25
|
+
declare type PresenceChooser<T> = (key: string, presences: any) => T;
|
|
26
|
+
export default class RealtimePresence {
|
|
27
|
+
channel: RealtimeSubscription;
|
|
28
|
+
state: PresenceState;
|
|
29
|
+
pendingDiffs: RawPresenceDiff[];
|
|
30
|
+
joinRef: string | null;
|
|
31
|
+
caller: {
|
|
32
|
+
onJoin: PresenceOnJoinCallback;
|
|
33
|
+
onLeave: PresenceOnLeaveCallback;
|
|
34
|
+
onSync: () => void;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Initializes the Presence
|
|
38
|
+
* @param channel - The RealtimeSubscription
|
|
39
|
+
* @param opts - The options,
|
|
40
|
+
* for example `{events: {state: 'state', diff: 'diff'}}`
|
|
41
|
+
*/
|
|
42
|
+
constructor(channel: RealtimeSubscription, opts?: PresenceOpts);
|
|
43
|
+
/**
|
|
44
|
+
* Used to sync the list of presences on the server
|
|
45
|
+
* with the client's state. An optional `onJoin` and `onLeave` callback can
|
|
46
|
+
* be provided to react to changes in the client's local presences across
|
|
47
|
+
* disconnects and reconnects with the server.
|
|
48
|
+
*/
|
|
49
|
+
static syncState(currentState: PresenceState, newState: RawPresenceState | PresenceState, onJoin: PresenceOnJoinCallback, onLeave: PresenceOnLeaveCallback): PresenceState;
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* Used to sync a diff of presence join and leave
|
|
53
|
+
* events from the server, as they happen. Like `syncState`, `syncDiff`
|
|
54
|
+
* accepts optional `onJoin` and `onLeave` callbacks to react to a user
|
|
55
|
+
* joining or leaving from a device.
|
|
56
|
+
*/
|
|
57
|
+
static syncDiff(state: PresenceState, diff: RawPresenceDiff | PresenceDiff, onJoin: PresenceOnJoinCallback, onLeave: PresenceOnLeaveCallback): PresenceState;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the array of presences, with selected metadata.
|
|
60
|
+
*/
|
|
61
|
+
static list<T = any>(presences: PresenceState, chooser: PresenceChooser<T> | undefined): T[];
|
|
62
|
+
private static map;
|
|
63
|
+
/**
|
|
64
|
+
* Remove 'metas' key
|
|
65
|
+
* Change 'phx_ref' to 'presence_id'
|
|
66
|
+
* Remove 'phx_ref' and 'phx_ref_prev'
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // returns {
|
|
70
|
+
* abc123: [
|
|
71
|
+
* { presence_id: '2', user_id: 1 },
|
|
72
|
+
* { presence_id: '3', user_id: 2 }
|
|
73
|
+
* ]
|
|
74
|
+
* }
|
|
75
|
+
* RealtimePresence.transformState({
|
|
76
|
+
* abc123: {
|
|
77
|
+
* metas: [
|
|
78
|
+
* { phx_ref: '2', phx_ref_prev: '1' user_id: 1 },
|
|
79
|
+
* { phx_ref: '3', user_id: 2 }
|
|
80
|
+
* ]
|
|
81
|
+
* }
|
|
82
|
+
* })
|
|
83
|
+
*/
|
|
84
|
+
private static transformState;
|
|
85
|
+
onJoin(callback: PresenceOnJoinCallback): void;
|
|
86
|
+
onLeave(callback: PresenceOnLeaveCallback): void;
|
|
87
|
+
onSync(callback: () => void): void;
|
|
88
|
+
list<T = any>(by?: PresenceChooser<T>): T[];
|
|
89
|
+
private inPendingSyncState;
|
|
90
|
+
}
|
|
91
|
+
export {};
|
|
92
|
+
//# sourceMappingURL=RealtimePresence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RealtimePresence.d.ts","sourceRoot":"","sources":["../../src/RealtimePresence.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,SAAS,CAAA;AAChB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;AAEzD,aAAK,QAAQ,GAAG;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,CAAA;AAED,aAAK,aAAa,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAAA;CAAE,CAAA;AAElD,aAAK,YAAY,GAAG;IAClB,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,aAAa,CAAA;CACtB,CAAA;AAED,aAAK,gBAAgB,GAAG;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CACnB,OAAO,EACP;QACE,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KACnB,EAAE,CACJ,CAAA;CACF,CAAA;AAED,aAAK,eAAe,GAAG;IACrB,KAAK,EAAE,gBAAgB,CAAA;IACvB,MAAM,EAAE,gBAAgB,CAAA;CACzB,CAAA;AAED,aAAK,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAA;AAE5D,MAAM,CAAC,OAAO,OAAO,gBAAgB;IAoBhB,OAAO,EAAE,oBAAoB;IAnBhD,KAAK,EAAE,aAAa,CAAK;IACzB,YAAY,EAAE,eAAe,EAAE,CAAK;IACpC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAO;IAC7B,MAAM,EAAE;QACN,MAAM,EAAE,sBAAsB,CAAA;QAC9B,OAAO,EAAE,uBAAuB,CAAA;QAChC,MAAM,EAAE,MAAM,IAAI,CAAA;KACnB,CAIA;IAED;;;;;OAKG;gBACgB,OAAO,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,YAAY;IAkDrE;;;;;OAKG;IACH,MAAM,CAAC,SAAS,CACd,YAAY,EAAE,aAAa,EAC3B,QAAQ,EAAE,gBAAgB,GAAG,aAAa,EAC1C,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,aAAa;IA0ChB;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CACb,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,eAAe,GAAG,YAAY,EACpC,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,aAAa;IAoDhB;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,EACjB,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,SAAS,GACtC,CAAC,EAAE;IAUN,OAAO,CAAC,MAAM,CAAC,GAAG;IAOlB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAyB7B,MAAM,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAI9C,OAAO,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI;IAIhD,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIlC,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;IAI3C,OAAO,CAAC,kBAAkB;CAG3B"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file draws heavily from https://github.com/phoenixframework/phoenix/blob/d344ec0a732ab4ee204215b31de69cf4be72e3bf/assets/js/phoenix/presence.js
|
|
3
|
+
License: https://github.com/phoenixframework/phoenix/blob/d344ec0a732ab4ee204215b31de69cf4be72e3bf/LICENSE.md
|
|
4
|
+
*/
|
|
5
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
6
|
+
export default class RealtimePresence {
|
|
7
|
+
/**
|
|
8
|
+
* Initializes the Presence
|
|
9
|
+
* @param channel - The RealtimeSubscription
|
|
10
|
+
* @param opts - The options,
|
|
11
|
+
* for example `{events: {state: 'state', diff: 'diff'}}`
|
|
12
|
+
*/
|
|
13
|
+
constructor(channel, opts) {
|
|
14
|
+
this.channel = channel;
|
|
15
|
+
this.state = {};
|
|
16
|
+
this.pendingDiffs = [];
|
|
17
|
+
this.joinRef = null;
|
|
18
|
+
this.caller = {
|
|
19
|
+
onJoin: () => { },
|
|
20
|
+
onLeave: () => { },
|
|
21
|
+
onSync: () => { },
|
|
22
|
+
};
|
|
23
|
+
const events = (opts === null || opts === void 0 ? void 0 : opts.events) || {
|
|
24
|
+
state: 'presence_state',
|
|
25
|
+
diff: 'presence_diff',
|
|
26
|
+
};
|
|
27
|
+
this.channel.on(events.state, (newState) => {
|
|
28
|
+
const { onJoin, onLeave, onSync } = this.caller;
|
|
29
|
+
this.joinRef = this.channel.joinRef();
|
|
30
|
+
this.state = RealtimePresence.syncState(this.state, newState, onJoin, onLeave);
|
|
31
|
+
this.pendingDiffs.forEach((diff) => {
|
|
32
|
+
this.state = RealtimePresence.syncDiff(this.state, diff, onJoin, onLeave);
|
|
33
|
+
});
|
|
34
|
+
this.pendingDiffs = [];
|
|
35
|
+
onSync();
|
|
36
|
+
});
|
|
37
|
+
this.channel.on(events.diff, (diff) => {
|
|
38
|
+
const { onJoin, onLeave, onSync } = this.caller;
|
|
39
|
+
if (this.inPendingSyncState()) {
|
|
40
|
+
this.pendingDiffs.push(diff);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.state = RealtimePresence.syncDiff(this.state, diff, onJoin, onLeave);
|
|
44
|
+
onSync();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Used to sync the list of presences on the server
|
|
50
|
+
* with the client's state. An optional `onJoin` and `onLeave` callback can
|
|
51
|
+
* be provided to react to changes in the client's local presences across
|
|
52
|
+
* disconnects and reconnects with the server.
|
|
53
|
+
*/
|
|
54
|
+
static syncState(currentState, newState, onJoin, onLeave) {
|
|
55
|
+
const state = cloneDeep(currentState);
|
|
56
|
+
const transformedState = this.transformState(newState);
|
|
57
|
+
const joins = {};
|
|
58
|
+
const leaves = {};
|
|
59
|
+
this.map(state, (key, presences) => {
|
|
60
|
+
if (!transformedState[key]) {
|
|
61
|
+
leaves[key] = presences;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
this.map(transformedState, (key, newPresences) => {
|
|
65
|
+
const currentPresences = state[key];
|
|
66
|
+
if (currentPresences) {
|
|
67
|
+
const newPresenceIds = newPresences.map((m) => m.presence_id);
|
|
68
|
+
const curPresenceIds = currentPresences.map((m) => m.presence_id);
|
|
69
|
+
const joinedPresences = newPresences.filter((m) => curPresenceIds.indexOf(m.presence_id) < 0);
|
|
70
|
+
const leftPresences = currentPresences.filter((m) => newPresenceIds.indexOf(m.presence_id) < 0);
|
|
71
|
+
if (joinedPresences.length > 0) {
|
|
72
|
+
joins[key] = joinedPresences;
|
|
73
|
+
}
|
|
74
|
+
if (leftPresences.length > 0) {
|
|
75
|
+
leaves[key] = leftPresences;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
joins[key] = newPresences;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return this.syncDiff(state, { joins, leaves }, onJoin, onLeave);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* Used to sync a diff of presence join and leave
|
|
87
|
+
* events from the server, as they happen. Like `syncState`, `syncDiff`
|
|
88
|
+
* accepts optional `onJoin` and `onLeave` callbacks to react to a user
|
|
89
|
+
* joining or leaving from a device.
|
|
90
|
+
*/
|
|
91
|
+
static syncDiff(state, diff, onJoin, onLeave) {
|
|
92
|
+
const { joins, leaves } = {
|
|
93
|
+
joins: this.transformState(diff.joins),
|
|
94
|
+
leaves: this.transformState(diff.leaves),
|
|
95
|
+
};
|
|
96
|
+
if (!onJoin) {
|
|
97
|
+
onJoin = () => { };
|
|
98
|
+
}
|
|
99
|
+
if (!onLeave) {
|
|
100
|
+
onLeave = () => { };
|
|
101
|
+
}
|
|
102
|
+
this.map(joins, (key, newPresences) => {
|
|
103
|
+
const currentPresences = state[key];
|
|
104
|
+
state[key] = cloneDeep(newPresences);
|
|
105
|
+
if (currentPresences) {
|
|
106
|
+
const joinedPresenceIds = state[key].map((m) => m.presence_id);
|
|
107
|
+
const curPresences = currentPresences.filter((m) => joinedPresenceIds.indexOf(m.presence_id) < 0);
|
|
108
|
+
state[key].unshift(...curPresences);
|
|
109
|
+
}
|
|
110
|
+
onJoin(key, currentPresences, newPresences);
|
|
111
|
+
});
|
|
112
|
+
this.map(leaves, (key, leftPresences) => {
|
|
113
|
+
let currentPresences = state[key];
|
|
114
|
+
if (!currentPresences)
|
|
115
|
+
return;
|
|
116
|
+
const presenceIdsToRemove = leftPresences.map((m) => m.presence_id);
|
|
117
|
+
currentPresences = currentPresences.filter((m) => presenceIdsToRemove.indexOf(m.presence_id) < 0);
|
|
118
|
+
state[key] = currentPresences;
|
|
119
|
+
onLeave(key, currentPresences, leftPresences);
|
|
120
|
+
if (currentPresences.length === 0)
|
|
121
|
+
delete state[key];
|
|
122
|
+
});
|
|
123
|
+
return state;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Returns the array of presences, with selected metadata.
|
|
127
|
+
*/
|
|
128
|
+
static list(presences, chooser) {
|
|
129
|
+
if (!chooser) {
|
|
130
|
+
chooser = (_key, pres) => pres;
|
|
131
|
+
}
|
|
132
|
+
return this.map(presences, (key, presences) => chooser(key, presences));
|
|
133
|
+
}
|
|
134
|
+
static map(obj, func) {
|
|
135
|
+
return Object.getOwnPropertyNames(obj).map((key) => func(key, obj[key]));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Remove 'metas' key
|
|
139
|
+
* Change 'phx_ref' to 'presence_id'
|
|
140
|
+
* Remove 'phx_ref' and 'phx_ref_prev'
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // returns {
|
|
144
|
+
* abc123: [
|
|
145
|
+
* { presence_id: '2', user_id: 1 },
|
|
146
|
+
* { presence_id: '3', user_id: 2 }
|
|
147
|
+
* ]
|
|
148
|
+
* }
|
|
149
|
+
* RealtimePresence.transformState({
|
|
150
|
+
* abc123: {
|
|
151
|
+
* metas: [
|
|
152
|
+
* { phx_ref: '2', phx_ref_prev: '1' user_id: 1 },
|
|
153
|
+
* { phx_ref: '3', user_id: 2 }
|
|
154
|
+
* ]
|
|
155
|
+
* }
|
|
156
|
+
* })
|
|
157
|
+
*/
|
|
158
|
+
static transformState(state) {
|
|
159
|
+
state = cloneDeep(state);
|
|
160
|
+
return Object.getOwnPropertyNames(state).reduce((newState, key) => {
|
|
161
|
+
const presences = state[key];
|
|
162
|
+
if ('metas' in presences) {
|
|
163
|
+
newState[key] = presences.metas.map((presence) => {
|
|
164
|
+
presence['presence_id'] = presence['phx_ref'];
|
|
165
|
+
delete presence['phx_ref'];
|
|
166
|
+
delete presence['phx_ref_prev'];
|
|
167
|
+
return presence;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
newState[key] = presences;
|
|
172
|
+
}
|
|
173
|
+
return newState;
|
|
174
|
+
}, {});
|
|
175
|
+
}
|
|
176
|
+
onJoin(callback) {
|
|
177
|
+
this.caller.onJoin = callback;
|
|
178
|
+
}
|
|
179
|
+
onLeave(callback) {
|
|
180
|
+
this.caller.onLeave = callback;
|
|
181
|
+
}
|
|
182
|
+
onSync(callback) {
|
|
183
|
+
this.caller.onSync = callback;
|
|
184
|
+
}
|
|
185
|
+
list(by) {
|
|
186
|
+
return RealtimePresence.list(this.state, by);
|
|
187
|
+
}
|
|
188
|
+
inPendingSyncState() {
|
|
189
|
+
return !this.joinRef || this.joinRef !== this.channel.joinRef();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=RealtimePresence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RealtimePresence.js","sourceRoot":"","sources":["../../src/RealtimePresence.ts"],"names":[],"mappings":"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"}
|
package/dist/module/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Transformers from './lib/transformers';
|
|
2
2
|
import RealtimeClient, { Options as RealtimeClientOptions } from './RealtimeClient';
|
|
3
3
|
import RealtimeSubscription from './RealtimeSubscription';
|
|
4
|
-
|
|
4
|
+
import RealtimePresence from './RealtimePresence';
|
|
5
|
+
export { RealtimeClient, RealtimeClientOptions, RealtimeSubscription, RealtimePresence, Transformers, };
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAClD,OAAO,cAAc,EAAE,EACrB,OAAO,IAAI,qBAAqB,EACjC,MAAM,kBAAkB,CAAA;AACzB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAClD,OAAO,cAAc,EAAE,EACrB,OAAO,IAAI,qBAAqB,EACjC,MAAM,kBAAkB,CAAA;AACzB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;AACzD,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,GACb,CAAA"}
|
package/dist/module/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Transformers from './lib/transformers';
|
|
2
2
|
import RealtimeClient from './RealtimeClient';
|
|
3
3
|
import RealtimeSubscription from './RealtimeSubscription';
|
|
4
|
-
|
|
4
|
+
import RealtimePresence from './RealtimePresence';
|
|
5
|
+
export { RealtimeClient, RealtimeSubscription, RealtimePresence, Transformers, };
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAClD,OAAO,cAEN,MAAM,kBAAkB,CAAA;AACzB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAA;AAClD,OAAO,cAEN,MAAM,kBAAkB,CAAA;AACzB,OAAO,oBAAoB,MAAM,wBAAwB,CAAA;AACzD,OAAO,gBAAgB,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EACL,cAAc,EAEd,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,GACb,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "1.
|
|
1
|
+
export declare const version = "1.4.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supabase/realtime-js",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Listen to realtime updates to your PostgreSQL database",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"realtime",
|
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
"docs:json": "typedoc --json docs/spec.json --mode modules --includeDeclarations --excludeExternals"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@types/lodash.clonedeep": "^4.5.6",
|
|
38
|
+
"@types/phoenix": "^1.5.4",
|
|
37
39
|
"@types/websocket": "^1.0.3",
|
|
40
|
+
"lodash.clonedeep": "^4.5.0",
|
|
38
41
|
"websocket": "^1.0.34"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file draws heavily from https://github.com/phoenixframework/phoenix/blob/d344ec0a732ab4ee204215b31de69cf4be72e3bf/assets/js/phoenix/presence.js
|
|
3
|
+
License: https://github.com/phoenixframework/phoenix/blob/d344ec0a732ab4ee204215b31de69cf4be72e3bf/LICENSE.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import cloneDeep from 'lodash.clonedeep'
|
|
7
|
+
import {
|
|
8
|
+
PresenceOpts,
|
|
9
|
+
PresenceOnJoinCallback,
|
|
10
|
+
PresenceOnLeaveCallback,
|
|
11
|
+
} from 'phoenix'
|
|
12
|
+
import RealtimeSubscription from './RealtimeSubscription'
|
|
13
|
+
|
|
14
|
+
type Presence = {
|
|
15
|
+
presence_id: string
|
|
16
|
+
[key: string]: any
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type PresenceState = { [key: string]: Presence[] }
|
|
20
|
+
|
|
21
|
+
type PresenceDiff = {
|
|
22
|
+
joins: PresenceState
|
|
23
|
+
leaves: PresenceState
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type RawPresenceState = {
|
|
27
|
+
[key: string]: Record<
|
|
28
|
+
'metas',
|
|
29
|
+
{
|
|
30
|
+
phx_ref?: string
|
|
31
|
+
phx_ref_prev?: string
|
|
32
|
+
[key: string]: any
|
|
33
|
+
}[]
|
|
34
|
+
>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type RawPresenceDiff = {
|
|
38
|
+
joins: RawPresenceState
|
|
39
|
+
leaves: RawPresenceState
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type PresenceChooser<T> = (key: string, presences: any) => T
|
|
43
|
+
|
|
44
|
+
export default class RealtimePresence {
|
|
45
|
+
state: PresenceState = {}
|
|
46
|
+
pendingDiffs: RawPresenceDiff[] = []
|
|
47
|
+
joinRef: string | null = null
|
|
48
|
+
caller: {
|
|
49
|
+
onJoin: PresenceOnJoinCallback
|
|
50
|
+
onLeave: PresenceOnLeaveCallback
|
|
51
|
+
onSync: () => void
|
|
52
|
+
} = {
|
|
53
|
+
onJoin: () => {},
|
|
54
|
+
onLeave: () => {},
|
|
55
|
+
onSync: () => {},
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initializes the Presence
|
|
60
|
+
* @param channel - The RealtimeSubscription
|
|
61
|
+
* @param opts - The options,
|
|
62
|
+
* for example `{events: {state: 'state', diff: 'diff'}}`
|
|
63
|
+
*/
|
|
64
|
+
constructor(public channel: RealtimeSubscription, opts?: PresenceOpts) {
|
|
65
|
+
const events = opts?.events || {
|
|
66
|
+
state: 'presence_state',
|
|
67
|
+
diff: 'presence_diff',
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.channel.on(events.state, (newState: RawPresenceState) => {
|
|
71
|
+
const { onJoin, onLeave, onSync } = this.caller
|
|
72
|
+
|
|
73
|
+
this.joinRef = this.channel.joinRef()
|
|
74
|
+
|
|
75
|
+
this.state = RealtimePresence.syncState(
|
|
76
|
+
this.state,
|
|
77
|
+
newState,
|
|
78
|
+
onJoin,
|
|
79
|
+
onLeave
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
this.pendingDiffs.forEach((diff) => {
|
|
83
|
+
this.state = RealtimePresence.syncDiff(
|
|
84
|
+
this.state,
|
|
85
|
+
diff,
|
|
86
|
+
onJoin,
|
|
87
|
+
onLeave
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
this.pendingDiffs = []
|
|
92
|
+
|
|
93
|
+
onSync()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
this.channel.on(events.diff, (diff: RawPresenceDiff) => {
|
|
97
|
+
const { onJoin, onLeave, onSync } = this.caller
|
|
98
|
+
|
|
99
|
+
if (this.inPendingSyncState()) {
|
|
100
|
+
this.pendingDiffs.push(diff)
|
|
101
|
+
} else {
|
|
102
|
+
this.state = RealtimePresence.syncDiff(
|
|
103
|
+
this.state,
|
|
104
|
+
diff,
|
|
105
|
+
onJoin,
|
|
106
|
+
onLeave
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
onSync()
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Used to sync the list of presences on the server
|
|
116
|
+
* with the client's state. An optional `onJoin` and `onLeave` callback can
|
|
117
|
+
* be provided to react to changes in the client's local presences across
|
|
118
|
+
* disconnects and reconnects with the server.
|
|
119
|
+
*/
|
|
120
|
+
static syncState(
|
|
121
|
+
currentState: PresenceState,
|
|
122
|
+
newState: RawPresenceState | PresenceState,
|
|
123
|
+
onJoin: PresenceOnJoinCallback,
|
|
124
|
+
onLeave: PresenceOnLeaveCallback
|
|
125
|
+
): PresenceState {
|
|
126
|
+
const state = cloneDeep(currentState)
|
|
127
|
+
const transformedState = this.transformState(newState)
|
|
128
|
+
const joins: PresenceState = {}
|
|
129
|
+
const leaves: PresenceState = {}
|
|
130
|
+
|
|
131
|
+
this.map(state, (key: string, presences: Presence[]) => {
|
|
132
|
+
if (!transformedState[key]) {
|
|
133
|
+
leaves[key] = presences
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
this.map(transformedState, (key, newPresences: Presence[]) => {
|
|
138
|
+
const currentPresences: Presence[] = state[key]
|
|
139
|
+
|
|
140
|
+
if (currentPresences) {
|
|
141
|
+
const newPresenceIds = newPresences.map((m: Presence) => m.presence_id)
|
|
142
|
+
const curPresenceIds = currentPresences.map(
|
|
143
|
+
(m: Presence) => m.presence_id
|
|
144
|
+
)
|
|
145
|
+
const joinedPresences: Presence[] = newPresences.filter(
|
|
146
|
+
(m: Presence) => curPresenceIds.indexOf(m.presence_id) < 0
|
|
147
|
+
)
|
|
148
|
+
const leftPresences: Presence[] = currentPresences.filter(
|
|
149
|
+
(m: Presence) => newPresenceIds.indexOf(m.presence_id) < 0
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if (joinedPresences.length > 0) {
|
|
153
|
+
joins[key] = joinedPresences
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (leftPresences.length > 0) {
|
|
157
|
+
leaves[key] = leftPresences
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
joins[key] = newPresences
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return this.syncDiff(state, { joins, leaves }, onJoin, onLeave)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
*
|
|
169
|
+
* Used to sync a diff of presence join and leave
|
|
170
|
+
* events from the server, as they happen. Like `syncState`, `syncDiff`
|
|
171
|
+
* accepts optional `onJoin` and `onLeave` callbacks to react to a user
|
|
172
|
+
* joining or leaving from a device.
|
|
173
|
+
*/
|
|
174
|
+
static syncDiff(
|
|
175
|
+
state: PresenceState,
|
|
176
|
+
diff: RawPresenceDiff | PresenceDiff,
|
|
177
|
+
onJoin: PresenceOnJoinCallback,
|
|
178
|
+
onLeave: PresenceOnLeaveCallback
|
|
179
|
+
): PresenceState {
|
|
180
|
+
const { joins, leaves } = {
|
|
181
|
+
joins: this.transformState(diff.joins),
|
|
182
|
+
leaves: this.transformState(diff.leaves),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!onJoin) {
|
|
186
|
+
onJoin = () => {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!onLeave) {
|
|
190
|
+
onLeave = () => {}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.map(joins, (key, newPresences: Presence[]) => {
|
|
194
|
+
const currentPresences: Presence[] = state[key]
|
|
195
|
+
state[key] = cloneDeep(newPresences)
|
|
196
|
+
|
|
197
|
+
if (currentPresences) {
|
|
198
|
+
const joinedPresenceIds = state[key].map((m: Presence) => m.presence_id)
|
|
199
|
+
const curPresences: Presence[] = currentPresences.filter(
|
|
200
|
+
(m: Presence) => joinedPresenceIds.indexOf(m.presence_id) < 0
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
state[key].unshift(...curPresences)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
onJoin(key, currentPresences, newPresences)
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
this.map(leaves, (key, leftPresences: Presence[]) => {
|
|
210
|
+
let currentPresences: Presence[] = state[key]
|
|
211
|
+
|
|
212
|
+
if (!currentPresences) return
|
|
213
|
+
|
|
214
|
+
const presenceIdsToRemove = leftPresences.map(
|
|
215
|
+
(m: Presence) => m.presence_id
|
|
216
|
+
)
|
|
217
|
+
currentPresences = currentPresences.filter(
|
|
218
|
+
(m: Presence) => presenceIdsToRemove.indexOf(m.presence_id) < 0
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
state[key] = currentPresences
|
|
222
|
+
|
|
223
|
+
onLeave(key, currentPresences, leftPresences)
|
|
224
|
+
|
|
225
|
+
if (currentPresences.length === 0) delete state[key]
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
return state
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Returns the array of presences, with selected metadata.
|
|
233
|
+
*/
|
|
234
|
+
static list<T = any>(
|
|
235
|
+
presences: PresenceState,
|
|
236
|
+
chooser: PresenceChooser<T> | undefined
|
|
237
|
+
): T[] {
|
|
238
|
+
if (!chooser) {
|
|
239
|
+
chooser = (_key, pres) => pres
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return this.map(presences, (key, presences: Presence[]) =>
|
|
243
|
+
chooser!(key, presences)
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private static map<T = any>(
|
|
248
|
+
obj: PresenceState,
|
|
249
|
+
func: PresenceChooser<T>
|
|
250
|
+
): T[] {
|
|
251
|
+
return Object.getOwnPropertyNames(obj).map((key) => func(key, obj[key]))
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Remove 'metas' key
|
|
256
|
+
* Change 'phx_ref' to 'presence_id'
|
|
257
|
+
* Remove 'phx_ref' and 'phx_ref_prev'
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* // returns {
|
|
261
|
+
* abc123: [
|
|
262
|
+
* { presence_id: '2', user_id: 1 },
|
|
263
|
+
* { presence_id: '3', user_id: 2 }
|
|
264
|
+
* ]
|
|
265
|
+
* }
|
|
266
|
+
* RealtimePresence.transformState({
|
|
267
|
+
* abc123: {
|
|
268
|
+
* metas: [
|
|
269
|
+
* { phx_ref: '2', phx_ref_prev: '1' user_id: 1 },
|
|
270
|
+
* { phx_ref: '3', user_id: 2 }
|
|
271
|
+
* ]
|
|
272
|
+
* }
|
|
273
|
+
* })
|
|
274
|
+
*/
|
|
275
|
+
private static transformState(
|
|
276
|
+
state: RawPresenceState | PresenceState
|
|
277
|
+
): PresenceState {
|
|
278
|
+
state = cloneDeep(state)
|
|
279
|
+
|
|
280
|
+
return Object.getOwnPropertyNames(state).reduce((newState, key) => {
|
|
281
|
+
const presences = state[key]
|
|
282
|
+
|
|
283
|
+
if ('metas' in presences) {
|
|
284
|
+
newState[key] = presences.metas.map((presence) => {
|
|
285
|
+
presence['presence_id'] = presence['phx_ref']
|
|
286
|
+
|
|
287
|
+
delete presence['phx_ref']
|
|
288
|
+
delete presence['phx_ref_prev']
|
|
289
|
+
|
|
290
|
+
return presence
|
|
291
|
+
}) as Presence[]
|
|
292
|
+
} else {
|
|
293
|
+
newState[key] = presences
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return newState
|
|
297
|
+
}, {} as PresenceState)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
onJoin(callback: PresenceOnJoinCallback): void {
|
|
301
|
+
this.caller.onJoin = callback
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
onLeave(callback: PresenceOnLeaveCallback): void {
|
|
305
|
+
this.caller.onLeave = callback
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
onSync(callback: () => void): void {
|
|
309
|
+
this.caller.onSync = callback
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
list<T = any>(by?: PresenceChooser<T>): T[] {
|
|
313
|
+
return RealtimePresence.list<T>(this.state, by)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private inPendingSyncState(): boolean {
|
|
317
|
+
return !this.joinRef || this.joinRef !== this.channel.joinRef()
|
|
318
|
+
}
|
|
319
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,10 +3,12 @@ import RealtimeClient, {
|
|
|
3
3
|
Options as RealtimeClientOptions,
|
|
4
4
|
} from './RealtimeClient'
|
|
5
5
|
import RealtimeSubscription from './RealtimeSubscription'
|
|
6
|
+
import RealtimePresence from './RealtimePresence'
|
|
6
7
|
|
|
7
8
|
export {
|
|
8
9
|
RealtimeClient,
|
|
9
10
|
RealtimeClientOptions,
|
|
10
11
|
RealtimeSubscription,
|
|
12
|
+
RealtimePresence,
|
|
11
13
|
Transformers,
|
|
12
14
|
}
|
package/src/lib/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// generated by genversion
|
|
2
|
-
export const version = '1.
|
|
2
|
+
export const version = '1.4.0'
|