@arkstack/realtime 0.18.6 → 0.18.8

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.
@@ -12,6 +12,14 @@ interface RealtimeNotification {
12
12
  actionText?: string | null;
13
13
  actionLink?: string | null;
14
14
  meta?: Record<string, unknown> | null;
15
+ /**
16
+ * Stable identity for what this notification is about. A later notification
17
+ * with the same tag supersedes this one rather than stacking beside it — see
18
+ * {@link supersede}, which the React and Vue bindings apply for you.
19
+ */
20
+ tag?: string | null;
21
+ /** Removes the notification carrying `tag` instead of displaying anything. */
22
+ retracted?: boolean;
15
23
  read_at: string | null;
16
24
  created_at: string;
17
25
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as PusherClientConfig, c as RealtimeNotification, d as RealtimeTransportName, i as NotificationHandler, l as RealtimeSubscription, n as createRealtime, o as RealtimeConfig, r as FirebaseClientConfig, s as RealtimeEventHandler, t as RealtimeClient, u as RealtimeTransport } from "./RealtimeClient-CpYmOGZa.js";
1
+ import { a as PusherClientConfig, c as RealtimeNotification, d as RealtimeTransportName, i as NotificationHandler, l as RealtimeSubscription, n as createRealtime, o as RealtimeConfig, r as FirebaseClientConfig, s as RealtimeEventHandler, t as RealtimeClient, u as RealtimeTransport } from "./RealtimeClient-CzrWnLtj.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).
@@ -20,4 +20,30 @@ declare const createPusherTransport: (config: PusherClientConfig) => Promise<Rea
20
20
  */
21
21
  declare const createFirebaseTransport: (config: FirebaseClientConfig) => Promise<RealtimeTransport>;
22
22
  //#endregion
23
- export { type FirebaseClientConfig, type NotificationHandler, type PusherClientConfig, RealtimeClient, type RealtimeConfig, type RealtimeEventHandler, type RealtimeNotification, type RealtimeSubscription, type RealtimeTransport, type RealtimeTransportName, createFirebaseTransport, createPusherTransport, createRealtime };
23
+ //#region src/supersede.d.ts
24
+ /**
25
+ * Fold an incoming notification into a list, honouring tag supersession.
26
+ *
27
+ * A notification carrying a `tag` names the thing it is about rather than the
28
+ * moment it was sent, so a later notification with the same tag is understood as
29
+ * a newer account of the same thing:
30
+ *
31
+ * - **Tagged, already present** — replaces the existing entry *in place*. The
32
+ * entry keeps its position so a list does not reshuffle under someone reading
33
+ * it; only its content changes.
34
+ * - **Tagged, not present** — prepended, like any other notification.
35
+ * - **Retracted** — removes the entry it names and contributes nothing itself. A
36
+ * retraction for something never seen is a no-op, which is the common case when
37
+ * a client connects after the fact.
38
+ * - **Untagged** — prepended. Untagged notifications never supersede anything.
39
+ *
40
+ * Pure and list-in/list-out, so it suits any state container; the React and Vue
41
+ * bindings are thin wrappers over it.
42
+ *
43
+ * @param list The notifications held so far, newest first.
44
+ * @param incoming The notification just received.
45
+ * @param limit Caps the retained list (newest kept) when growing it.
46
+ */
47
+ declare const supersede: (list: RealtimeNotification[], incoming: RealtimeNotification, limit?: number) => RealtimeNotification[];
48
+ //#endregion
49
+ export { type FirebaseClientConfig, type NotificationHandler, type PusherClientConfig, RealtimeClient, type RealtimeConfig, type RealtimeEventHandler, type RealtimeNotification, type RealtimeSubscription, type RealtimeTransport, type RealtimeTransportName, createFirebaseTransport, createPusherTransport, createRealtime, supersede };
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as supersede } from "./supersede-T1ch4wJF.js";
1
2
  //#region \0rolldown/runtime.js
2
3
  var __defProp = Object.defineProperty;
3
4
  var __exportAll = (all, no_symbols) => {
@@ -260,4 +261,4 @@ const createFirebaseTransport = async (config) => {
260
261
  };
261
262
  };
262
263
  //#endregion
263
- export { RealtimeClient, createFirebaseTransport, createPusherTransport, createRealtime };
264
+ export { RealtimeClient, createFirebaseTransport, createPusherTransport, createRealtime, supersede };
@@ -1,4 +1,4 @@
1
- import { c as RealtimeNotification, t as RealtimeClient } from "../RealtimeClient-CpYmOGZa.js";
1
+ import { c as RealtimeNotification, t as RealtimeClient } from "../RealtimeClient-CzrWnLtj.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. */
@@ -14,6 +14,10 @@ interface UseNotificationsResult {
14
14
  * notifications (newest first) into state. Automatically unsubscribes on unmount
15
15
  * or when `client`/`channel` change.
16
16
  *
17
+ * Tagged notifications supersede rather than accumulate: one replaces an earlier
18
+ * notification with the same tag in place, and a retraction removes it. See
19
+ * {@link supersede}.
20
+ *
17
21
  * @param client A {@link RealtimeClient} (from `createRealtime`).
18
22
  * @param channel The channel to subscribe to, e.g. `client.channelFor(user.id)`.
19
23
  * @param options `limit` caps how many notifications are retained.
@@ -1,3 +1,4 @@
1
+ import { t as supersede } from "../supersede-T1ch4wJF.js";
1
2
  import { useCallback, useEffect, useState } from "react";
2
3
  //#region src/react/index.ts
3
4
  /**
@@ -5,6 +6,10 @@ import { useCallback, useEffect, useState } from "react";
5
6
  * notifications (newest first) into state. Automatically unsubscribes on unmount
6
7
  * or when `client`/`channel` change.
7
8
  *
9
+ * Tagged notifications supersede rather than accumulate: one replaces an earlier
10
+ * notification with the same tag in place, and a retraction removes it. See
11
+ * {@link supersede}.
12
+ *
8
13
  * @param client A {@link RealtimeClient} (from `createRealtime`).
9
14
  * @param channel The channel to subscribe to, e.g. `client.channelFor(user.id)`.
10
15
  * @param options `limit` caps how many notifications are retained.
@@ -15,10 +20,7 @@ function useNotifications(client, channel, options = {}) {
15
20
  useEffect(() => {
16
21
  let unsubscribe;
17
22
  let cancelled = false;
18
- const push = (notification) => setNotifications((prev) => {
19
- const next = [notification, ...prev];
20
- return limit ? next.slice(0, limit) : next;
21
- });
23
+ const push = (notification) => setNotifications((prev) => supersede(prev, notification, limit));
22
24
  client.subscribe(channel, push).then((off) => {
23
25
  if (cancelled) off();
24
26
  else unsubscribe = off;
@@ -0,0 +1,40 @@
1
+ //#region src/supersede.ts
2
+ /**
3
+ * Fold an incoming notification into a list, honouring tag supersession.
4
+ *
5
+ * A notification carrying a `tag` names the thing it is about rather than the
6
+ * moment it was sent, so a later notification with the same tag is understood as
7
+ * a newer account of the same thing:
8
+ *
9
+ * - **Tagged, already present** — replaces the existing entry *in place*. The
10
+ * entry keeps its position so a list does not reshuffle under someone reading
11
+ * it; only its content changes.
12
+ * - **Tagged, not present** — prepended, like any other notification.
13
+ * - **Retracted** — removes the entry it names and contributes nothing itself. A
14
+ * retraction for something never seen is a no-op, which is the common case when
15
+ * a client connects after the fact.
16
+ * - **Untagged** — prepended. Untagged notifications never supersede anything.
17
+ *
18
+ * Pure and list-in/list-out, so it suits any state container; the React and Vue
19
+ * bindings are thin wrappers over it.
20
+ *
21
+ * @param list The notifications held so far, newest first.
22
+ * @param incoming The notification just received.
23
+ * @param limit Caps the retained list (newest kept) when growing it.
24
+ */
25
+ const supersede = (list, incoming, limit) => {
26
+ const { tag, retracted } = incoming;
27
+ if (retracted) return tag ? list.filter((notification) => notification.tag !== tag) : list;
28
+ if (tag) {
29
+ const index = list.findIndex((notification) => notification.tag === tag);
30
+ if (index !== -1) {
31
+ const next = list.slice();
32
+ next[index] = incoming;
33
+ return next;
34
+ }
35
+ }
36
+ const next = [incoming, ...list];
37
+ return limit ? next.slice(0, limit) : next;
38
+ };
39
+ //#endregion
40
+ export { supersede as t };
@@ -1,4 +1,4 @@
1
- import { c as RealtimeNotification, t as RealtimeClient } from "../RealtimeClient-CpYmOGZa.js";
1
+ import { c as RealtimeNotification, t as RealtimeClient } from "../RealtimeClient-CzrWnLtj.js";
2
2
  import { ComputedRef, Ref } from "vue";
3
3
  //#region src/vue/index.d.ts
4
4
  interface UseNotificationsOptions {
@@ -16,6 +16,10 @@ interface UseNotificationsResult {
16
16
  * notifications (newest first) into a ref. Automatically unsubscribes when the
17
17
  * scope is disposed (component unmount).
18
18
  *
19
+ * Tagged notifications supersede rather than accumulate: one replaces an earlier
20
+ * notification with the same tag in place, and a retraction removes it. See
21
+ * {@link supersede}.
22
+ *
19
23
  * @param client A {@link RealtimeClient} (from `createRealtime`).
20
24
  * @param channel The channel to subscribe to, e.g. `client.channelFor(user.id)`.
21
25
  * @param options `limit` caps how many notifications are retained.
package/dist/vue/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as supersede } from "../supersede-T1ch4wJF.js";
1
2
  import { computed, onScopeDispose, ref } from "vue";
2
3
  //#region src/vue/index.ts
3
4
  /**
@@ -5,6 +6,10 @@ import { computed, onScopeDispose, ref } from "vue";
5
6
  * notifications (newest first) into a ref. Automatically unsubscribes when the
6
7
  * scope is disposed (component unmount).
7
8
  *
9
+ * Tagged notifications supersede rather than accumulate: one replaces an earlier
10
+ * notification with the same tag in place, and a retraction removes it. See
11
+ * {@link supersede}.
12
+ *
8
13
  * @param client A {@link RealtimeClient} (from `createRealtime`).
9
14
  * @param channel The channel to subscribe to, e.g. `client.channelFor(user.id)`.
10
15
  * @param options `limit` caps how many notifications are retained.
@@ -15,8 +20,7 @@ function useNotifications(client, channel, options = {}) {
15
20
  let unsubscribe;
16
21
  let cancelled = false;
17
22
  const push = (notification) => {
18
- const next = [notification, ...notifications.value];
19
- notifications.value = limit ? next.slice(0, limit) : next;
23
+ notifications.value = supersede(notifications.value, notification, limit);
20
24
  };
21
25
  client.subscribe(channel, push).then((off) => {
22
26
  if (cancelled) off();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/realtime",
3
- "version": "0.18.6",
3
+ "version": "0.18.8",
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",