@arkstack/realtime 0.19.1 → 0.19.3
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 +34 -40
- package/dist/{RealtimeClient-MI224djP.d.ts → RealtimeClient-C3_ppuiI.d.ts} +24 -4
- package/dist/importer-0BugD5WV.js +14 -0
- package/dist/index.d.ts +19 -5
- package/dist/index.js +37 -39
- package/dist/push-ucsxjX92.js +80 -0
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +3 -1
- package/dist/vue/index.d.ts +1 -1
- package/dist/vue/index.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ pnpm add firebase # Firebase transport
|
|
|
24
24
|
Create a client and subscribe to a user's channel. `subscribe`/`forUser` resolve to an unsubscribe function.
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
-
import { createRealtime } from '@arkstack/realtime'
|
|
27
|
+
import { createRealtime } from '@arkstack/realtime'
|
|
28
28
|
|
|
29
29
|
const realtime = createRealtime({
|
|
30
30
|
transport: 'pusher',
|
|
@@ -33,15 +33,15 @@ const realtime = createRealtime({
|
|
|
33
33
|
cluster: 'mt1',
|
|
34
34
|
authEndpoint: '/broadcasting/auth',
|
|
35
35
|
},
|
|
36
|
-
})
|
|
36
|
+
})
|
|
37
37
|
|
|
38
38
|
const unsubscribe = await realtime.forUser(user.id, (notification) => {
|
|
39
|
-
console.log(notification.title, notification.description)
|
|
40
|
-
})
|
|
39
|
+
console.log(notification.title, notification.description)
|
|
40
|
+
})
|
|
41
41
|
|
|
42
42
|
// later
|
|
43
|
-
unsubscribe()
|
|
44
|
-
await realtime.disconnect()
|
|
43
|
+
unsubscribe()
|
|
44
|
+
await realtime.disconnect()
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
### Client events
|
|
@@ -50,14 +50,12 @@ Pusher private and presence channels can send ephemeral client events (often
|
|
|
50
50
|
called whispers). Subscribe to the channel before sending an event:
|
|
51
51
|
|
|
52
52
|
```ts
|
|
53
|
-
const stopTyping = await realtime.listenForWhisper(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
({ userId }) => console.log(`${userId} is typing`),
|
|
57
|
-
);
|
|
53
|
+
const stopTyping = await realtime.listenForWhisper('private-room.7', 'typing', ({ userId }) =>
|
|
54
|
+
console.log(`${userId} is typing`),
|
|
55
|
+
)
|
|
58
56
|
|
|
59
|
-
await realtime.whisper('private-room.7', 'typing', { userId: user.id })
|
|
60
|
-
stopTyping()
|
|
57
|
+
await realtime.whisper('private-room.7', 'typing', { userId: user.id })
|
|
58
|
+
stopTyping()
|
|
61
59
|
```
|
|
62
60
|
|
|
63
61
|
The `client-` prefix is added automatically. Pusher sends client events over its
|
|
@@ -70,35 +68,31 @@ Each `notification` matches the payload broadcast by the server:
|
|
|
70
68
|
|
|
71
69
|
```ts
|
|
72
70
|
interface RealtimeNotification {
|
|
73
|
-
id: string
|
|
74
|
-
type: string | null
|
|
75
|
-
title: string
|
|
76
|
-
description: string
|
|
77
|
-
actionText?: string | null
|
|
78
|
-
actionLink?: string | null
|
|
79
|
-
meta?: Record<string, unknown> | null
|
|
80
|
-
read_at: string | null
|
|
81
|
-
created_at: string
|
|
71
|
+
id: string
|
|
72
|
+
type: string | null
|
|
73
|
+
title: string
|
|
74
|
+
description: string
|
|
75
|
+
actionText?: string | null
|
|
76
|
+
actionLink?: string | null
|
|
77
|
+
meta?: Record<string, unknown> | null
|
|
78
|
+
read_at: string | null
|
|
79
|
+
created_at: string
|
|
82
80
|
}
|
|
83
81
|
```
|
|
84
82
|
|
|
85
83
|
## React
|
|
86
84
|
|
|
87
85
|
```tsx
|
|
88
|
-
import { useNotifications } from '@arkstack/realtime/react'
|
|
86
|
+
import { useNotifications } from '@arkstack/realtime/react'
|
|
89
87
|
|
|
90
88
|
function Bell({ realtime, userId }) {
|
|
91
|
-
const { notifications, latest, clear } = useNotifications(
|
|
92
|
-
realtime,
|
|
93
|
-
realtime.channelFor(userId),
|
|
94
|
-
{ limit: 20 },
|
|
95
|
-
);
|
|
89
|
+
const { notifications, latest, clear } = useNotifications(realtime, realtime.channelFor(userId), { limit: 20 })
|
|
96
90
|
|
|
97
91
|
return (
|
|
98
92
|
<span onClick={clear}>
|
|
99
93
|
{notifications.length} · {latest?.title}
|
|
100
94
|
</span>
|
|
101
|
-
)
|
|
95
|
+
)
|
|
102
96
|
}
|
|
103
97
|
```
|
|
104
98
|
|
|
@@ -108,9 +102,9 @@ The hook accumulates notifications (newest first), caps them at `limit`, and uns
|
|
|
108
102
|
|
|
109
103
|
```vue
|
|
110
104
|
<script setup>
|
|
111
|
-
import { useNotifications } from
|
|
105
|
+
import { useNotifications } from "@arkstack/realtime/vue";
|
|
112
106
|
|
|
113
|
-
const props = defineProps([
|
|
107
|
+
const props = defineProps(["realtime", "userId"]);
|
|
114
108
|
const { notifications, latest, clear } = useNotifications(
|
|
115
109
|
props.realtime,
|
|
116
110
|
props.realtime.channelFor(props.userId),
|
|
@@ -139,7 +133,7 @@ const realtime = createRealtime({
|
|
|
139
133
|
messagingSenderId: import.meta.env.VITE_FIREBASE_SENDER_ID,
|
|
140
134
|
databaseURL: import.meta.env.VITE_FIREBASE_DATABASE_URL,
|
|
141
135
|
},
|
|
142
|
-
})
|
|
136
|
+
})
|
|
143
137
|
```
|
|
144
138
|
|
|
145
139
|
Firebase client events are written below `arkstack/client-events` in Realtime
|
|
@@ -152,22 +146,22 @@ the root with `firebase.clientEventsPath` when needed.
|
|
|
152
146
|
Provide `transportFactory` to bridge any backend (a raw WebSocket, SSE, a test double, …):
|
|
153
147
|
|
|
154
148
|
```ts
|
|
155
|
-
import { createRealtime, type RealtimeTransport } from '@arkstack/realtime'
|
|
149
|
+
import { createRealtime, type RealtimeTransport } from '@arkstack/realtime'
|
|
156
150
|
|
|
157
151
|
const transport: RealtimeTransport = {
|
|
158
152
|
subscribe(channel, event, handler) {
|
|
159
|
-
const socket = new WebSocket(`wss://example.test/${channel}`)
|
|
153
|
+
const socket = new WebSocket(`wss://example.test/${channel}`)
|
|
160
154
|
socket.addEventListener('message', (e) => {
|
|
161
|
-
const { event: name, payload } = JSON.parse(e.data)
|
|
162
|
-
if (name === event) handler(payload)
|
|
163
|
-
})
|
|
155
|
+
const { event: name, payload } = JSON.parse(e.data)
|
|
156
|
+
if (name === event) handler(payload)
|
|
157
|
+
})
|
|
164
158
|
|
|
165
|
-
return { channel, unsubscribe: () => socket.close() }
|
|
159
|
+
return { channel, unsubscribe: () => socket.close() }
|
|
166
160
|
},
|
|
167
161
|
disconnect() {},
|
|
168
|
-
}
|
|
162
|
+
}
|
|
169
163
|
|
|
170
|
-
const realtime = createRealtime({ transportFactory: () => transport })
|
|
164
|
+
const realtime = createRealtime({ transportFactory: () => transport })
|
|
171
165
|
```
|
|
172
166
|
|
|
173
167
|
## API
|
|
@@ -70,7 +70,7 @@ interface RealtimeTransport {
|
|
|
70
70
|
/**
|
|
71
71
|
* This device's push registration token, when the transport can issue one.
|
|
72
72
|
*/
|
|
73
|
-
deviceToken?(): Promise<string | null>;
|
|
73
|
+
deviceToken?(options?: DeviceTokenOptions): Promise<string | null>;
|
|
74
74
|
/**
|
|
75
75
|
* Set by transports that push nothing; clients should poll this often instead.
|
|
76
76
|
*/
|
|
@@ -117,6 +117,15 @@ interface FirebaseClientConfig {
|
|
|
117
117
|
/** Web push VAPID key used when requesting a messaging token. */
|
|
118
118
|
vapidKey?: string;
|
|
119
119
|
}
|
|
120
|
+
interface DeviceTokenOptions {
|
|
121
|
+
/**
|
|
122
|
+
* The service worker that receives background messages: a registration, or
|
|
123
|
+
* the URL of a script to register. A registered URL receives the Firebase
|
|
124
|
+
* web config as its `firebaseConfig` query parameter. Defaults to Firebase's
|
|
125
|
+
* own `/firebase-messaging-sw.js` lookup.
|
|
126
|
+
*/
|
|
127
|
+
serviceWorker?: ServiceWorkerRegistration | string;
|
|
128
|
+
}
|
|
120
129
|
interface FirebaseDatabaseClientConfig {
|
|
121
130
|
apiKey: string;
|
|
122
131
|
projectId: string;
|
|
@@ -140,6 +149,12 @@ interface PollingClientConfig {
|
|
|
140
149
|
/** How often, in milliseconds, {@link RealtimeClient.onPoll} handlers run (default 5000). */
|
|
141
150
|
interval?: number;
|
|
142
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Loads an SDK module (`pusher-js`, `firebase/app`, …) by package specifier.
|
|
154
|
+
* Supply one where bare imports cannot resolve, e.g. a plain `<script>` page
|
|
155
|
+
* that loads the SDKs from a CDN.
|
|
156
|
+
*/
|
|
157
|
+
type ModuleImporter = (specifier: string) => Promise<unknown>;
|
|
143
158
|
interface RealtimeConfig {
|
|
144
159
|
transport?: RealtimeTransportName;
|
|
145
160
|
/** Event name broadcasts are published under (default `notification`). */
|
|
@@ -150,6 +165,8 @@ interface RealtimeConfig {
|
|
|
150
165
|
firebase?: FirebaseClientConfig;
|
|
151
166
|
'firebase-database'?: FirebaseDatabaseClientConfig;
|
|
152
167
|
polling?: PollingClientConfig;
|
|
168
|
+
/** Loads the transport SDKs. Defaults to a dynamic `import()`. */
|
|
169
|
+
importModule?: ModuleImporter;
|
|
153
170
|
/** Inject a transport directly — bypasses the built-ins (tests, custom backends). */
|
|
154
171
|
transportFactory?: () => RealtimeTransport | Promise<RealtimeTransport>;
|
|
155
172
|
}
|
|
@@ -241,15 +258,18 @@ declare class RealtimeClient {
|
|
|
241
258
|
onPoll(handler: () => void, interval?: number): Promise<() => void>;
|
|
242
259
|
/**
|
|
243
260
|
* This device's push registration token, for registering with your API.
|
|
244
|
-
*
|
|
261
|
+
* Uses the transport when it can issue one, otherwise the `firebase` config
|
|
262
|
+
* (push over FCM while streaming over another transport). Null when neither
|
|
263
|
+
* can, or the browser cannot receive web push.
|
|
245
264
|
*
|
|
265
|
+
* @param options
|
|
246
266
|
* @returns
|
|
247
267
|
*/
|
|
248
|
-
deviceToken(): Promise<string | null>;
|
|
268
|
+
deviceToken(options?: DeviceTokenOptions): Promise<string | null>;
|
|
249
269
|
/** Tear down the underlying transport connection. */
|
|
250
270
|
disconnect(): Promise<void>;
|
|
251
271
|
}
|
|
252
272
|
/** Create a new {@link RealtimeClient}. */
|
|
253
273
|
declare const createRealtime: (config?: RealtimeConfig) => RealtimeClient;
|
|
254
274
|
//#endregion
|
|
255
|
-
export {
|
|
275
|
+
export { FirebaseDatabaseClientConfig as a, PollingClientConfig as c, RealtimeEventHandler as d, RealtimeNotification as f, RealtimeTransportName as h, FirebaseClientConfig as i, PusherClientConfig as l, RealtimeTransport as m, createRealtime as n, ModuleImporter as o, RealtimeSubscription as p, DeviceTokenOptions as r, NotificationHandler as s, RealtimeClient as t, RealtimeConfig as u };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/importer.ts
|
|
2
|
+
/**
|
|
3
|
+
* Load an optional SDK by its package specifier. Non-literal so bundlers leave
|
|
4
|
+
* SDKs the app does not use out of the bundle.
|
|
5
|
+
*
|
|
6
|
+
* @param specifier
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
const defaultImporter = (specifier) => import(
|
|
10
|
+
/* @vite-ignore */
|
|
11
|
+
specifier
|
|
12
|
+
);
|
|
13
|
+
//#endregion
|
|
14
|
+
export { defaultImporter as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as FirebaseDatabaseClientConfig, c as PollingClientConfig, d as RealtimeEventHandler, f as RealtimeNotification, h as RealtimeTransportName, i as FirebaseClientConfig, l as PusherClientConfig, m as RealtimeTransport, n as createRealtime, o as ModuleImporter, p as RealtimeSubscription, r as DeviceTokenOptions, s as NotificationHandler, t as RealtimeClient, u as RealtimeConfig } from "./RealtimeClient-C3_ppuiI.js";
|
|
2
2
|
//#region src/transports/pusher.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Realtime transport backed by [pusher-js](https://github.com/pusher/pusher-js).
|
|
@@ -6,9 +6,10 @@ import { a as NotificationHandler, c as RealtimeConfig, d as RealtimeSubscriptio
|
|
|
6
6
|
* it in when they use the Pusher transport.
|
|
7
7
|
*
|
|
8
8
|
* @param config
|
|
9
|
+
* @param importModule Loads `pusher-js`.
|
|
9
10
|
* @returns
|
|
10
11
|
*/
|
|
11
|
-
declare const createPusherTransport: (config: PusherClientConfig) => Promise<RealtimeTransport>;
|
|
12
|
+
declare const createPusherTransport: (config: PusherClientConfig, importModule?: ModuleImporter) => Promise<RealtimeTransport>;
|
|
12
13
|
//#endregion
|
|
13
14
|
//#region src/transports/firebase.d.ts
|
|
14
15
|
/**
|
|
@@ -18,7 +19,7 @@ declare const createPusherTransport: (config: PusherClientConfig) => Promise<Rea
|
|
|
18
19
|
* FCM delivers to the device (not per-channel), so the channel is informational;
|
|
19
20
|
* messages are matched by `event` and the JSON-encoded payload is parsed back.
|
|
20
21
|
*/
|
|
21
|
-
declare const createFirebaseTransport: (config: FirebaseClientConfig) => Promise<RealtimeTransport>;
|
|
22
|
+
declare const createFirebaseTransport: (config: FirebaseClientConfig, importModule?: ModuleImporter) => Promise<RealtimeTransport>;
|
|
22
23
|
//#endregion
|
|
23
24
|
//#region src/transports/firebase-database.d.ts
|
|
24
25
|
/**
|
|
@@ -30,9 +31,10 @@ declare const createFirebaseTransport: (config: FirebaseClientConfig) => Promise
|
|
|
30
31
|
* written after the transport connected are delivered.
|
|
31
32
|
*
|
|
32
33
|
* @param config
|
|
34
|
+
* @param importModule Loads the `firebase` SDK modules.
|
|
33
35
|
* @returns
|
|
34
36
|
*/
|
|
35
|
-
declare const createFirebaseDatabaseTransport: (config: FirebaseDatabaseClientConfig) => Promise<RealtimeTransport>;
|
|
37
|
+
declare const createFirebaseDatabaseTransport: (config: FirebaseDatabaseClientConfig, importModule?: ModuleImporter) => Promise<RealtimeTransport>;
|
|
36
38
|
//#endregion
|
|
37
39
|
//#region src/transports/polling.d.ts
|
|
38
40
|
/**
|
|
@@ -87,4 +89,16 @@ declare const createMemoryTransport: () => RealtimeTransport;
|
|
|
87
89
|
*/
|
|
88
90
|
declare const supersede: (list: RealtimeNotification[], incoming: RealtimeNotification, limit?: number) => RealtimeNotification[];
|
|
89
91
|
//#endregion
|
|
90
|
-
|
|
92
|
+
//#region src/push.d.ts
|
|
93
|
+
/**
|
|
94
|
+
* Issue a device token without a Firebase transport, e.g. when notifications
|
|
95
|
+
* stream over Pusher but are pushed to devices over FCM.
|
|
96
|
+
*
|
|
97
|
+
* @param config
|
|
98
|
+
* @param options
|
|
99
|
+
* @param importModule Loads the `firebase` SDK modules.
|
|
100
|
+
* @returns
|
|
101
|
+
*/
|
|
102
|
+
declare const requestDeviceToken: (config: FirebaseClientConfig, options?: DeviceTokenOptions, importModule?: ModuleImporter) => Promise<string | null>;
|
|
103
|
+
//#endregion
|
|
104
|
+
export { type DeviceTokenOptions, type FirebaseClientConfig, type FirebaseDatabaseClientConfig, type ModuleImporter, type NotificationHandler, type PollingClientConfig, type PusherClientConfig, RealtimeClient, type RealtimeConfig, type RealtimeEventHandler, type RealtimeNotification, type RealtimeSubscription, type RealtimeTransport, type RealtimeTransportName, createFirebaseDatabaseTransport, createFirebaseTransport, createMemoryTransport, createPollingTransport, createPusherTransport, createRealtime, emitMemoryEvent, requestDeviceToken, supersede };
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
+
import { i as __exportAll, r as requestDeviceToken, t as issueDeviceToken } from "./push-ucsxjX92.js";
|
|
2
|
+
import { t as defaultImporter } from "./importer-0BugD5WV.js";
|
|
1
3
|
import { t as supersede } from "./supersede-T1ch4wJF.js";
|
|
2
|
-
//#region \0rolldown/runtime.js
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __exportAll = (all, no_symbols) => {
|
|
5
|
-
let target = {};
|
|
6
|
-
for (var name in all) __defProp(target, name, {
|
|
7
|
-
get: all[name],
|
|
8
|
-
enumerable: true
|
|
9
|
-
});
|
|
10
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
11
|
-
return target;
|
|
12
|
-
};
|
|
13
|
-
//#endregion
|
|
14
4
|
//#region src/RealtimeClient.ts
|
|
15
5
|
/**
|
|
16
6
|
* Consumes Arkstack realtime notifications. Resolves a transport (Pusher,
|
|
@@ -29,9 +19,9 @@ var RealtimeClient = class {
|
|
|
29
19
|
}
|
|
30
20
|
/**
|
|
31
21
|
* The channel name a given user's notifications are broadcast on.
|
|
32
|
-
*
|
|
33
|
-
* @param userId
|
|
34
|
-
* @returns
|
|
22
|
+
*
|
|
23
|
+
* @param userId
|
|
24
|
+
* @returns
|
|
35
25
|
*/
|
|
36
26
|
channelFor(userId) {
|
|
37
27
|
return `${this.channelPrefix}${userId}`;
|
|
@@ -45,11 +35,11 @@ var RealtimeClient = class {
|
|
|
45
35
|
switch (this.transportName) {
|
|
46
36
|
case "firebase": {
|
|
47
37
|
const { createFirebaseTransport } = await Promise.resolve().then(() => firebase_exports);
|
|
48
|
-
return await createFirebaseTransport(this.required("firebase"));
|
|
38
|
+
return await createFirebaseTransport(this.required("firebase"), this.config.importModule);
|
|
49
39
|
}
|
|
50
40
|
case "firebase-database": {
|
|
51
41
|
const { createFirebaseDatabaseTransport } = await Promise.resolve().then(() => firebase_database_exports);
|
|
52
|
-
return await createFirebaseDatabaseTransport(this.required("firebase-database"));
|
|
42
|
+
return await createFirebaseDatabaseTransport(this.required("firebase-database"), this.config.importModule);
|
|
53
43
|
}
|
|
54
44
|
case "polling": {
|
|
55
45
|
const { createPollingTransport } = await Promise.resolve().then(() => polling_exports);
|
|
@@ -61,7 +51,7 @@ var RealtimeClient = class {
|
|
|
61
51
|
}
|
|
62
52
|
default: {
|
|
63
53
|
const { createPusherTransport } = await Promise.resolve().then(() => pusher_exports);
|
|
64
|
-
return await createPusherTransport(this.required("pusher"));
|
|
54
|
+
return await createPusherTransport(this.required("pusher"), this.config.importModule);
|
|
65
55
|
}
|
|
66
56
|
}
|
|
67
57
|
}
|
|
@@ -159,12 +149,19 @@ var RealtimeClient = class {
|
|
|
159
149
|
}
|
|
160
150
|
/**
|
|
161
151
|
* This device's push registration token, for registering with your API.
|
|
162
|
-
*
|
|
152
|
+
* Uses the transport when it can issue one, otherwise the `firebase` config
|
|
153
|
+
* (push over FCM while streaming over another transport). Null when neither
|
|
154
|
+
* can, or the browser cannot receive web push.
|
|
163
155
|
*
|
|
156
|
+
* @param options
|
|
164
157
|
* @returns
|
|
165
158
|
*/
|
|
166
|
-
async deviceToken() {
|
|
167
|
-
|
|
159
|
+
async deviceToken(options) {
|
|
160
|
+
const transport = await this.transport();
|
|
161
|
+
if (transport.deviceToken) return await transport.deviceToken(options);
|
|
162
|
+
if (!this.config.firebase) return null;
|
|
163
|
+
const { requestDeviceToken } = await import("./push-ucsxjX92.js").then((n) => n.n);
|
|
164
|
+
return await requestDeviceToken(this.config.firebase, options, this.config.importModule);
|
|
168
165
|
}
|
|
169
166
|
/** Tear down the underlying transport connection. */
|
|
170
167
|
async disconnect() {
|
|
@@ -181,13 +178,14 @@ var pusher_exports = /* @__PURE__ */ __exportAll({ createPusherTransport: () =>
|
|
|
181
178
|
/**
|
|
182
179
|
* Realtime transport backed by [pusher-js](https://github.com/pusher/pusher-js).
|
|
183
180
|
* The SDK is an optional peer dependency imported lazily, so consumers only pull
|
|
184
|
-
* it in when they use the Pusher transport.
|
|
185
|
-
*
|
|
186
|
-
* @param config
|
|
187
|
-
* @
|
|
181
|
+
* it in when they use the Pusher transport.
|
|
182
|
+
*
|
|
183
|
+
* @param config
|
|
184
|
+
* @param importModule Loads `pusher-js`.
|
|
185
|
+
* @returns
|
|
188
186
|
*/
|
|
189
|
-
const createPusherTransport = async (config) => {
|
|
190
|
-
const mod = await
|
|
187
|
+
const createPusherTransport = async (config, importModule = defaultImporter) => {
|
|
188
|
+
const mod = await importModule("pusher-js").catch(() => {
|
|
191
189
|
throw new Error("The \"pusher-js\" package is required for the Pusher transport. Install it with `npm i pusher-js`.");
|
|
192
190
|
});
|
|
193
191
|
const client = new (mod.default ?? mod)(config.key, {
|
|
@@ -250,11 +248,11 @@ const isFirebaseClientEvent = (value) => {
|
|
|
250
248
|
* FCM delivers to the device (not per-channel), so the channel is informational;
|
|
251
249
|
* messages are matched by `event` and the JSON-encoded payload is parsed back.
|
|
252
250
|
*/
|
|
253
|
-
const createFirebaseTransport = async (config) => {
|
|
251
|
+
const createFirebaseTransport = async (config, importModule = defaultImporter) => {
|
|
254
252
|
const [appMod, messagingMod, databaseMod] = await Promise.all([
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
importModule("firebase/app"),
|
|
254
|
+
importModule("firebase/messaging"),
|
|
255
|
+
importModule("firebase/database")
|
|
258
256
|
]).catch(() => {
|
|
259
257
|
throw new Error("The \"firebase\" package is required for the Firebase transport. Install it with `npm i firebase`.");
|
|
260
258
|
});
|
|
@@ -307,9 +305,8 @@ const createFirebaseTransport = async (config) => {
|
|
|
307
305
|
});
|
|
308
306
|
await databaseApi.remove(eventReference);
|
|
309
307
|
},
|
|
310
|
-
async deviceToken() {
|
|
311
|
-
|
|
312
|
-
return await getToken(messaging, config.vapidKey ? { vapidKey: config.vapidKey } : void 0) || null;
|
|
308
|
+
async deviceToken(options) {
|
|
309
|
+
return await issueDeviceToken(messagingMod, messaging, config, options);
|
|
313
310
|
},
|
|
314
311
|
disconnect() {
|
|
315
312
|
messageUnsubscribers.forEach((off) => off());
|
|
@@ -353,13 +350,14 @@ const tokenUid = (token) => {
|
|
|
353
350
|
* written after the transport connected are delivered.
|
|
354
351
|
*
|
|
355
352
|
* @param config
|
|
353
|
+
* @param importModule Loads the `firebase` SDK modules.
|
|
356
354
|
* @returns
|
|
357
355
|
*/
|
|
358
|
-
const createFirebaseDatabaseTransport = async (config) => {
|
|
356
|
+
const createFirebaseDatabaseTransport = async (config, importModule = defaultImporter) => {
|
|
359
357
|
const [appMod, authMod, databaseMod] = await Promise.all([
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
358
|
+
importModule("firebase/app"),
|
|
359
|
+
importModule("firebase/auth"),
|
|
360
|
+
importModule("firebase/database")
|
|
363
361
|
]).catch(() => {
|
|
364
362
|
throw new Error("The \"firebase\" package is required for the Firebase database transport. Install it with `npm i firebase`.");
|
|
365
363
|
});
|
|
@@ -508,4 +506,4 @@ const createMemoryTransport = () => {
|
|
|
508
506
|
};
|
|
509
507
|
};
|
|
510
508
|
//#endregion
|
|
511
|
-
export { RealtimeClient, createFirebaseDatabaseTransport, createFirebaseTransport, createMemoryTransport, createPollingTransport, createPusherTransport, createRealtime, emitMemoryEvent, supersede };
|
|
509
|
+
export { RealtimeClient, createFirebaseDatabaseTransport, createFirebaseTransport, createMemoryTransport, createPollingTransport, createPusherTransport, createRealtime, emitMemoryEvent, requestDeviceToken, supersede };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { t as defaultImporter } from "./importer-0BugD5WV.js";
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __exportAll = (all, no_symbols) => {
|
|
5
|
+
let target = {};
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
11
|
+
return target;
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/push.ts
|
|
15
|
+
var push_exports = /* @__PURE__ */ __exportAll({
|
|
16
|
+
issueDeviceToken: () => issueDeviceToken,
|
|
17
|
+
requestDeviceToken: () => requestDeviceToken
|
|
18
|
+
});
|
|
19
|
+
const APP_NAME = "arkstack-push";
|
|
20
|
+
/**
|
|
21
|
+
* The public settings a service worker needs to initialize Firebase.
|
|
22
|
+
*
|
|
23
|
+
* @param config
|
|
24
|
+
* @returns
|
|
25
|
+
*/
|
|
26
|
+
const webConfig = (config) => ({
|
|
27
|
+
apiKey: config.apiKey,
|
|
28
|
+
projectId: config.projectId,
|
|
29
|
+
appId: config.appId,
|
|
30
|
+
messagingSenderId: config.messagingSenderId
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Register `url` as the messaging service worker, handing it the Firebase web
|
|
34
|
+
* config as the `firebaseConfig` query parameter.
|
|
35
|
+
*
|
|
36
|
+
* @param url
|
|
37
|
+
* @param config
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
const registerServiceWorker = async (url, config) => {
|
|
41
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
42
|
+
const query = `firebaseConfig=${encodeURIComponent(JSON.stringify(webConfig(config)))}`;
|
|
43
|
+
return await navigator.serviceWorker.register(`${url}${separator}${query}`);
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Issue an FCM registration token for this browser, prompting for notification
|
|
47
|
+
* permission when needed. Null when the browser cannot receive web push.
|
|
48
|
+
*
|
|
49
|
+
* @param messagingMod
|
|
50
|
+
* @param messaging
|
|
51
|
+
* @param config
|
|
52
|
+
* @param options
|
|
53
|
+
* @returns
|
|
54
|
+
*/
|
|
55
|
+
const issueDeviceToken = async (messagingMod, messaging, config, options = {}) => {
|
|
56
|
+
if (!await messagingMod.isSupported().catch(() => false)) return null;
|
|
57
|
+
const serviceWorkerRegistration = typeof options.serviceWorker === "string" ? await registerServiceWorker(options.serviceWorker, config) : options.serviceWorker;
|
|
58
|
+
return await messagingMod.getToken(messaging, {
|
|
59
|
+
...config.vapidKey ? { vapidKey: config.vapidKey } : {},
|
|
60
|
+
...serviceWorkerRegistration ? { serviceWorkerRegistration } : {}
|
|
61
|
+
}) || null;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Issue a device token without a Firebase transport, e.g. when notifications
|
|
65
|
+
* stream over Pusher but are pushed to devices over FCM.
|
|
66
|
+
*
|
|
67
|
+
* @param config
|
|
68
|
+
* @param options
|
|
69
|
+
* @param importModule Loads the `firebase` SDK modules.
|
|
70
|
+
* @returns
|
|
71
|
+
*/
|
|
72
|
+
const requestDeviceToken = async (config, options = {}, importModule = defaultImporter) => {
|
|
73
|
+
const [appMod, messagingMod] = await Promise.all([importModule("firebase/app"), importModule("firebase/messaging")]).catch(() => {
|
|
74
|
+
throw new Error("The \"firebase\" package is required for device tokens. Install it with `npm i firebase`.");
|
|
75
|
+
});
|
|
76
|
+
const app = appMod.getApps().find((existing) => existing.name === APP_NAME) ?? appMod.initializeApp(webConfig(config), APP_NAME);
|
|
77
|
+
return await issueDeviceToken(messagingMod, messagingMod.getMessaging(app), config, options);
|
|
78
|
+
};
|
|
79
|
+
//#endregion
|
|
80
|
+
export { __exportAll as i, push_exports as n, requestDeviceToken as r, issueDeviceToken as t };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as RealtimeNotification, t as RealtimeClient } from "../RealtimeClient-C3_ppuiI.js";
|
|
2
2
|
//#region src/react/index.d.ts
|
|
3
3
|
interface UseNotificationsOptions {
|
|
4
4
|
/** Cap the number of retained notifications (newest kept). Default: unbounded. */
|
package/dist/react/index.js
CHANGED
|
@@ -24,7 +24,9 @@ function useNotifications(client, channel, options = {}) {
|
|
|
24
24
|
client.subscribe(channel, push).then((off) => {
|
|
25
25
|
if (cancelled) off();
|
|
26
26
|
else unsubscribe = off;
|
|
27
|
-
}).catch(() => {
|
|
27
|
+
}).catch(() => {
|
|
28
|
+
/** surfaced by the caller's own error handling */
|
|
29
|
+
});
|
|
28
30
|
return () => {
|
|
29
31
|
cancelled = true;
|
|
30
32
|
unsubscribe?.();
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as RealtimeNotification, t as RealtimeClient } from "../RealtimeClient-C3_ppuiI.js";
|
|
2
2
|
import { ComputedRef, Ref } from "vue";
|
|
3
3
|
//#region src/vue/index.d.ts
|
|
4
4
|
interface UseNotificationsOptions {
|
package/dist/vue/index.js
CHANGED
|
@@ -25,7 +25,9 @@ function useNotifications(client, channel, options = {}) {
|
|
|
25
25
|
client.subscribe(channel, push).then((off) => {
|
|
26
26
|
if (cancelled) off();
|
|
27
27
|
else unsubscribe = off;
|
|
28
|
-
}).catch(() => {
|
|
28
|
+
}).catch(() => {
|
|
29
|
+
/** surfaced by the caller's own error handling */
|
|
30
|
+
});
|
|
29
31
|
const stop = () => {
|
|
30
32
|
cancelled = true;
|
|
31
33
|
unsubscribe?.();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/realtime",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Client for consuming Arkstack realtime notifications (Pusher/Firebase), with framework-agnostic core plus React and Vue bindings.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net/guide/notifications",
|