@pubber-subber/rxjs 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/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/index.cjs +66 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/index.ts +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sami Mishal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @pubber-subber/rxjs
|
|
2
|
+
|
|
3
|
+
Optional RxJS bridge for [`@pubber-subber/core`](https://www.npmjs.com/package/@pubber-subber/core). Turn any topic subscription into an `Observable`. Works with every adapter — the bridge is transport-agnostic.
|
|
4
|
+
|
|
5
|
+
The bridge is intentionally a separate package so the core stays dependency-free for apps that don't use RxJS.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @pubber-subber/core @pubber-subber/rxjs rxjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`rxjs` is a **peer dependency**. Supports RxJS 7 and 8.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { PubSub } from '@pubber-subber/core';
|
|
19
|
+
import { memory } from '@pubber-subber/memory';
|
|
20
|
+
import { fromTopic } from '@pubber-subber/rxjs';
|
|
21
|
+
|
|
22
|
+
const pubsub = new PubSub({ adapter: memory() });
|
|
23
|
+
|
|
24
|
+
const sub = fromTopic<{ id: number }>(pubsub, 'users.created')
|
|
25
|
+
.subscribe((user) => console.log(user.id));
|
|
26
|
+
|
|
27
|
+
await pubsub.publish('users.created', { id: 1 });
|
|
28
|
+
|
|
29
|
+
sub.unsubscribe(); // tears down the underlying pubsub subscription too
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## API
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
toObservable<T>(pubsub, topic, meta?): Observable<AdapterMessage<T>>
|
|
36
|
+
fromTopic<T>(pubsub, topic, meta?): Observable<T>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- **`toObservable`** emits the full `AdapterMessage` envelope — `topic`, `payload`, `raw`, `meta`, optional `ack`/`nack`. Use it when you need the envelope.
|
|
40
|
+
- **`fromTopic`** unwraps to just `payload` — the convenience choice when you don't care about the envelope.
|
|
41
|
+
|
|
42
|
+
Both lazy-create the underlying pubsub subscription on the first observer attach and tear it down on the last unsubscribe.
|
|
43
|
+
|
|
44
|
+
## Pipelines
|
|
45
|
+
|
|
46
|
+
The whole point of the bridge is composing with RxJS operators:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { fromTopic } from '@pubber-subber/rxjs';
|
|
50
|
+
import { bufferTime, filter, mergeMap } from 'rxjs';
|
|
51
|
+
|
|
52
|
+
interface PageView { user: string; path: string; ts: number; }
|
|
53
|
+
|
|
54
|
+
fromTopic<PageView>(pubsub, 'analytics.pageview')
|
|
55
|
+
.pipe(
|
|
56
|
+
filter((v) => !v.path.startsWith('/internal')),
|
|
57
|
+
bufferTime(250),
|
|
58
|
+
mergeMap((batch) => writeBatch(batch)),
|
|
59
|
+
)
|
|
60
|
+
.subscribe();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Notes
|
|
64
|
+
|
|
65
|
+
- **Cancellation race**: teardown is safe even if it races with the initial `subscribe()` call — the bridge tracks a cancellation flag and unsubscribes upstream as soon as it resolves. Subscribing and immediately unsubscribing never throws or leaks.
|
|
66
|
+
- **No `.observe()` on `PubSub`**: adding it would pull RxJS types into core. The function form keeps the coupling one-way; apps that don't use RxJS pay no cost.
|
|
67
|
+
- **Multi-subscriber**: each Observable subscription creates its own underlying pubsub subscription. For transports that fan out anyway (Redis pub/sub, in-memory) this is the expected behavior. For transports with shared subscriptions (GCP Pub/Sub, SQS) this means messages distribute across observers per the broker's semantics.
|
|
68
|
+
- **Errors**: errors from the underlying `pubsub.subscribe()` are emitted via `subscriber.error()`. Errors thrown from RxJS operators bubble up the pipeline as usual; they don't propagate back into the pubsub subscription.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var rxjs = require('rxjs');
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
function toObservable(pubsub, topic, meta) {
|
|
7
|
+
return new rxjs.Observable((subscriber) => {
|
|
8
|
+
let handle = null;
|
|
9
|
+
let cancelled = false;
|
|
10
|
+
pubsub.subscribe(
|
|
11
|
+
topic,
|
|
12
|
+
(msg) => {
|
|
13
|
+
subscriber.next(msg);
|
|
14
|
+
},
|
|
15
|
+
meta
|
|
16
|
+
).then((h) => {
|
|
17
|
+
if (cancelled) {
|
|
18
|
+
void h.unsubscribe();
|
|
19
|
+
} else {
|
|
20
|
+
handle = h;
|
|
21
|
+
}
|
|
22
|
+
}).catch((err) => {
|
|
23
|
+
if (!cancelled) subscriber.error(err);
|
|
24
|
+
});
|
|
25
|
+
return () => {
|
|
26
|
+
cancelled = true;
|
|
27
|
+
if (handle) {
|
|
28
|
+
void handle.unsubscribe();
|
|
29
|
+
handle = null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function fromTopic(pubsub, topic, meta) {
|
|
35
|
+
return new rxjs.Observable((subscriber) => {
|
|
36
|
+
let handle = null;
|
|
37
|
+
let cancelled = false;
|
|
38
|
+
pubsub.subscribe(
|
|
39
|
+
topic,
|
|
40
|
+
(msg) => {
|
|
41
|
+
subscriber.next(msg.payload);
|
|
42
|
+
},
|
|
43
|
+
meta
|
|
44
|
+
).then((h) => {
|
|
45
|
+
if (cancelled) {
|
|
46
|
+
void h.unsubscribe();
|
|
47
|
+
} else {
|
|
48
|
+
handle = h;
|
|
49
|
+
}
|
|
50
|
+
}).catch((err) => {
|
|
51
|
+
if (!cancelled) subscriber.error(err);
|
|
52
|
+
});
|
|
53
|
+
return () => {
|
|
54
|
+
cancelled = true;
|
|
55
|
+
if (handle) {
|
|
56
|
+
void handle.unsubscribe();
|
|
57
|
+
handle = null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
exports.fromTopic = fromTopic;
|
|
64
|
+
exports.toObservable = toObservable;
|
|
65
|
+
//# sourceMappingURL=index.cjs.map
|
|
66
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["Observable"],"mappings":";;;;;AAUO,SAAS,YAAA,CACd,MAAA,EACA,KAAA,EACA,IAAA,EAC+B;AAC/B,EAAA,OAAO,IAAIA,eAAA,CAA8B,CAAC,UAAA,KAAe;AACvD,IAAA,IAAI,MAAA,GAAoC,IAAA;AACxC,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,MAAA,CACG,SAAA;AAAA,MACC,KAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,UAAA,CAAW,KAAK,GAAG,CAAA;AAAA,MACrB,CAAA;AAAA,MACA;AAAA,KACF,CACC,IAAA,CAAK,CAAC,CAAA,KAAM;AACX,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,KAAK,EAAE,WAAA,EAAY;AAAA,MACrB,CAAA,MAAO;AACL,QAAA,MAAA,GAAS,CAAA;AAAA,MACX;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,IAAI,CAAC,SAAA,EAAW,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AAAA,IACtC,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,KAAK,OAAO,WAAA,EAAY;AACxB,QAAA,MAAA,GAAS,IAAA;AAAA,MACX;AAAA,IACF,CAAA;AAAA,EACF,CAAC,CAAA;AACH;AAOO,SAAS,SAAA,CACd,MAAA,EACA,KAAA,EACA,IAAA,EACe;AACf,EAAA,OAAO,IAAIA,eAAA,CAAc,CAAC,UAAA,KAAe;AACvC,IAAA,IAAI,MAAA,GAAoC,IAAA;AACxC,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,MAAA,CACG,SAAA;AAAA,MACC,KAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,UAAA,CAAW,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,MAC7B,CAAA;AAAA,MACA;AAAA,KACF,CACC,IAAA,CAAK,CAAC,CAAA,KAAM;AACX,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,KAAK,EAAE,WAAA,EAAY;AAAA,MACrB,CAAA,MAAO;AACL,QAAA,MAAA,GAAS,CAAA;AAAA,MACX;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,IAAI,CAAC,SAAA,EAAW,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AAAA,IACtC,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,KAAK,OAAO,WAAA,EAAY;AACxB,QAAA,MAAA,GAAS,IAAA;AAAA,MACX;AAAA,IACF,CAAA;AAAA,EACF,CAAC,CAAA;AACH","file":"index.cjs","sourcesContent":["import type { AdapterMessage, PubSub, SubscriptionHandle } from '@pubber-subber/core';\nimport { Observable } from 'rxjs';\n\n/**\n * Bridge a topic to an RxJS Observable that emits the full `AdapterMessage`.\n *\n * The subscription is created lazily when the first RxJS subscriber attaches,\n * and torn down on unsubscribe. Handles the case where teardown races the\n * underlying `pubsub.subscribe` resolution.\n */\nexport function toObservable<T = unknown>(\n pubsub: PubSub,\n topic: string,\n meta?: unknown,\n): Observable<AdapterMessage<T>> {\n return new Observable<AdapterMessage<T>>((subscriber) => {\n let handle: SubscriptionHandle | null = null;\n let cancelled = false;\n\n pubsub\n .subscribe<T>(\n topic,\n (msg) => {\n subscriber.next(msg);\n },\n meta as never,\n )\n .then((h) => {\n if (cancelled) {\n void h.unsubscribe();\n } else {\n handle = h;\n }\n })\n .catch((err) => {\n if (!cancelled) subscriber.error(err);\n });\n\n return () => {\n cancelled = true;\n if (handle) {\n void handle.unsubscribe();\n handle = null;\n }\n };\n });\n}\n\n/**\n * Same as `toObservable` but emits the unwrapped payload instead of the full\n * `AdapterMessage`. The convenience choice for app code that doesn't care\n * about topic / ack / raw envelope.\n */\nexport function fromTopic<T = unknown>(\n pubsub: PubSub,\n topic: string,\n meta?: unknown,\n): Observable<T> {\n return new Observable<T>((subscriber) => {\n let handle: SubscriptionHandle | null = null;\n let cancelled = false;\n\n pubsub\n .subscribe<T>(\n topic,\n (msg) => {\n subscriber.next(msg.payload);\n },\n meta as never,\n )\n .then((h) => {\n if (cancelled) {\n void h.unsubscribe();\n } else {\n handle = h;\n }\n })\n .catch((err) => {\n if (!cancelled) subscriber.error(err);\n });\n\n return () => {\n cancelled = true;\n if (handle) {\n void handle.unsubscribe();\n handle = null;\n }\n };\n });\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PubSub, AdapterMessage } from '@pubber-subber/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bridge a topic to an RxJS Observable that emits the full `AdapterMessage`.
|
|
6
|
+
*
|
|
7
|
+
* The subscription is created lazily when the first RxJS subscriber attaches,
|
|
8
|
+
* and torn down on unsubscribe. Handles the case where teardown races the
|
|
9
|
+
* underlying `pubsub.subscribe` resolution.
|
|
10
|
+
*/
|
|
11
|
+
declare function toObservable<T = unknown>(pubsub: PubSub, topic: string, meta?: unknown): Observable<AdapterMessage<T>>;
|
|
12
|
+
/**
|
|
13
|
+
* Same as `toObservable` but emits the unwrapped payload instead of the full
|
|
14
|
+
* `AdapterMessage`. The convenience choice for app code that doesn't care
|
|
15
|
+
* about topic / ack / raw envelope.
|
|
16
|
+
*/
|
|
17
|
+
declare function fromTopic<T = unknown>(pubsub: PubSub, topic: string, meta?: unknown): Observable<T>;
|
|
18
|
+
|
|
19
|
+
export { fromTopic, toObservable };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PubSub, AdapterMessage } from '@pubber-subber/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bridge a topic to an RxJS Observable that emits the full `AdapterMessage`.
|
|
6
|
+
*
|
|
7
|
+
* The subscription is created lazily when the first RxJS subscriber attaches,
|
|
8
|
+
* and torn down on unsubscribe. Handles the case where teardown races the
|
|
9
|
+
* underlying `pubsub.subscribe` resolution.
|
|
10
|
+
*/
|
|
11
|
+
declare function toObservable<T = unknown>(pubsub: PubSub, topic: string, meta?: unknown): Observable<AdapterMessage<T>>;
|
|
12
|
+
/**
|
|
13
|
+
* Same as `toObservable` but emits the unwrapped payload instead of the full
|
|
14
|
+
* `AdapterMessage`. The convenience choice for app code that doesn't care
|
|
15
|
+
* about topic / ack / raw envelope.
|
|
16
|
+
*/
|
|
17
|
+
declare function fromTopic<T = unknown>(pubsub: PubSub, topic: string, meta?: unknown): Observable<T>;
|
|
18
|
+
|
|
19
|
+
export { fromTopic, toObservable };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
function toObservable(pubsub, topic, meta) {
|
|
5
|
+
return new Observable((subscriber) => {
|
|
6
|
+
let handle = null;
|
|
7
|
+
let cancelled = false;
|
|
8
|
+
pubsub.subscribe(
|
|
9
|
+
topic,
|
|
10
|
+
(msg) => {
|
|
11
|
+
subscriber.next(msg);
|
|
12
|
+
},
|
|
13
|
+
meta
|
|
14
|
+
).then((h) => {
|
|
15
|
+
if (cancelled) {
|
|
16
|
+
void h.unsubscribe();
|
|
17
|
+
} else {
|
|
18
|
+
handle = h;
|
|
19
|
+
}
|
|
20
|
+
}).catch((err) => {
|
|
21
|
+
if (!cancelled) subscriber.error(err);
|
|
22
|
+
});
|
|
23
|
+
return () => {
|
|
24
|
+
cancelled = true;
|
|
25
|
+
if (handle) {
|
|
26
|
+
void handle.unsubscribe();
|
|
27
|
+
handle = null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function fromTopic(pubsub, topic, meta) {
|
|
33
|
+
return new Observable((subscriber) => {
|
|
34
|
+
let handle = null;
|
|
35
|
+
let cancelled = false;
|
|
36
|
+
pubsub.subscribe(
|
|
37
|
+
topic,
|
|
38
|
+
(msg) => {
|
|
39
|
+
subscriber.next(msg.payload);
|
|
40
|
+
},
|
|
41
|
+
meta
|
|
42
|
+
).then((h) => {
|
|
43
|
+
if (cancelled) {
|
|
44
|
+
void h.unsubscribe();
|
|
45
|
+
} else {
|
|
46
|
+
handle = h;
|
|
47
|
+
}
|
|
48
|
+
}).catch((err) => {
|
|
49
|
+
if (!cancelled) subscriber.error(err);
|
|
50
|
+
});
|
|
51
|
+
return () => {
|
|
52
|
+
cancelled = true;
|
|
53
|
+
if (handle) {
|
|
54
|
+
void handle.unsubscribe();
|
|
55
|
+
handle = null;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { fromTopic, toObservable };
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAUO,SAAS,YAAA,CACd,MAAA,EACA,KAAA,EACA,IAAA,EAC+B;AAC/B,EAAA,OAAO,IAAI,UAAA,CAA8B,CAAC,UAAA,KAAe;AACvD,IAAA,IAAI,MAAA,GAAoC,IAAA;AACxC,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,MAAA,CACG,SAAA;AAAA,MACC,KAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,UAAA,CAAW,KAAK,GAAG,CAAA;AAAA,MACrB,CAAA;AAAA,MACA;AAAA,KACF,CACC,IAAA,CAAK,CAAC,CAAA,KAAM;AACX,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,KAAK,EAAE,WAAA,EAAY;AAAA,MACrB,CAAA,MAAO;AACL,QAAA,MAAA,GAAS,CAAA;AAAA,MACX;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,IAAI,CAAC,SAAA,EAAW,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AAAA,IACtC,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,KAAK,OAAO,WAAA,EAAY;AACxB,QAAA,MAAA,GAAS,IAAA;AAAA,MACX;AAAA,IACF,CAAA;AAAA,EACF,CAAC,CAAA;AACH;AAOO,SAAS,SAAA,CACd,MAAA,EACA,KAAA,EACA,IAAA,EACe;AACf,EAAA,OAAO,IAAI,UAAA,CAAc,CAAC,UAAA,KAAe;AACvC,IAAA,IAAI,MAAA,GAAoC,IAAA;AACxC,IAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,IAAA,MAAA,CACG,SAAA;AAAA,MACC,KAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,UAAA,CAAW,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,MAC7B,CAAA;AAAA,MACA;AAAA,KACF,CACC,IAAA,CAAK,CAAC,CAAA,KAAM;AACX,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,KAAK,EAAE,WAAA,EAAY;AAAA,MACrB,CAAA,MAAO;AACL,QAAA,MAAA,GAAS,CAAA;AAAA,MACX;AAAA,IACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,IAAI,CAAC,SAAA,EAAW,UAAA,CAAW,KAAA,CAAM,GAAG,CAAA;AAAA,IACtC,CAAC,CAAA;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,KAAK,OAAO,WAAA,EAAY;AACxB,QAAA,MAAA,GAAS,IAAA;AAAA,MACX;AAAA,IACF,CAAA;AAAA,EACF,CAAC,CAAA;AACH","file":"index.js","sourcesContent":["import type { AdapterMessage, PubSub, SubscriptionHandle } from '@pubber-subber/core';\nimport { Observable } from 'rxjs';\n\n/**\n * Bridge a topic to an RxJS Observable that emits the full `AdapterMessage`.\n *\n * The subscription is created lazily when the first RxJS subscriber attaches,\n * and torn down on unsubscribe. Handles the case where teardown races the\n * underlying `pubsub.subscribe` resolution.\n */\nexport function toObservable<T = unknown>(\n pubsub: PubSub,\n topic: string,\n meta?: unknown,\n): Observable<AdapterMessage<T>> {\n return new Observable<AdapterMessage<T>>((subscriber) => {\n let handle: SubscriptionHandle | null = null;\n let cancelled = false;\n\n pubsub\n .subscribe<T>(\n topic,\n (msg) => {\n subscriber.next(msg);\n },\n meta as never,\n )\n .then((h) => {\n if (cancelled) {\n void h.unsubscribe();\n } else {\n handle = h;\n }\n })\n .catch((err) => {\n if (!cancelled) subscriber.error(err);\n });\n\n return () => {\n cancelled = true;\n if (handle) {\n void handle.unsubscribe();\n handle = null;\n }\n };\n });\n}\n\n/**\n * Same as `toObservable` but emits the unwrapped payload instead of the full\n * `AdapterMessage`. The convenience choice for app code that doesn't care\n * about topic / ack / raw envelope.\n */\nexport function fromTopic<T = unknown>(\n pubsub: PubSub,\n topic: string,\n meta?: unknown,\n): Observable<T> {\n return new Observable<T>((subscriber) => {\n let handle: SubscriptionHandle | null = null;\n let cancelled = false;\n\n pubsub\n .subscribe<T>(\n topic,\n (msg) => {\n subscriber.next(msg.payload);\n },\n meta as never,\n )\n .then((h) => {\n if (cancelled) {\n void h.unsubscribe();\n } else {\n handle = h;\n }\n })\n .catch((err) => {\n if (!cancelled) subscriber.error(err);\n });\n\n return () => {\n cancelled = true;\n if (handle) {\n void handle.unsubscribe();\n handle = null;\n }\n };\n });\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pubber-subber/rxjs",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "RxJS bridge for @pubber-subber. Turn any topic subscription into an Observable.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pubsub",
|
|
7
|
+
"pub-sub",
|
|
8
|
+
"messaging",
|
|
9
|
+
"events",
|
|
10
|
+
"rxjs",
|
|
11
|
+
"observable",
|
|
12
|
+
"reactive",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/samishal1998/pubber-subber/tree/main/packages/rxjs#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/samishal1998/pubber-subber.git",
|
|
19
|
+
"directory": "packages/rxjs"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/samishal1998/pubber-subber/issues"
|
|
23
|
+
},
|
|
24
|
+
"author": "Sami Mishal",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/index.d.cts",
|
|
36
|
+
"default": "./dist/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"main": "./dist/index.cjs",
|
|
42
|
+
"module": "./dist/index.js",
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"files": ["dist", "src", "README.md", "LICENSE"],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"prebuild": "node ../../scripts/swap-package-json.mjs build",
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"prepublishOnly": "node ../../scripts/swap-package-json.mjs publish",
|
|
52
|
+
"postpublish": "node ../../scripts/swap-package-json.mjs build"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@pubber-subber/core": "^0.0.1",
|
|
56
|
+
"rxjs": "^7.0.0 || ^8.0.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@pubber-subber/core": "workspace:*",
|
|
60
|
+
"@pubber-subber/memory": "workspace:*",
|
|
61
|
+
"rxjs": "^7.8.1"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { AdapterMessage, PubSub, SubscriptionHandle } from '@pubber-subber/core';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bridge a topic to an RxJS Observable that emits the full `AdapterMessage`.
|
|
6
|
+
*
|
|
7
|
+
* The subscription is created lazily when the first RxJS subscriber attaches,
|
|
8
|
+
* and torn down on unsubscribe. Handles the case where teardown races the
|
|
9
|
+
* underlying `pubsub.subscribe` resolution.
|
|
10
|
+
*/
|
|
11
|
+
export function toObservable<T = unknown>(
|
|
12
|
+
pubsub: PubSub,
|
|
13
|
+
topic: string,
|
|
14
|
+
meta?: unknown,
|
|
15
|
+
): Observable<AdapterMessage<T>> {
|
|
16
|
+
return new Observable<AdapterMessage<T>>((subscriber) => {
|
|
17
|
+
let handle: SubscriptionHandle | null = null;
|
|
18
|
+
let cancelled = false;
|
|
19
|
+
|
|
20
|
+
pubsub
|
|
21
|
+
.subscribe<T>(
|
|
22
|
+
topic,
|
|
23
|
+
(msg) => {
|
|
24
|
+
subscriber.next(msg);
|
|
25
|
+
},
|
|
26
|
+
meta as never,
|
|
27
|
+
)
|
|
28
|
+
.then((h) => {
|
|
29
|
+
if (cancelled) {
|
|
30
|
+
void h.unsubscribe();
|
|
31
|
+
} else {
|
|
32
|
+
handle = h;
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
.catch((err) => {
|
|
36
|
+
if (!cancelled) subscriber.error(err);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
cancelled = true;
|
|
41
|
+
if (handle) {
|
|
42
|
+
void handle.unsubscribe();
|
|
43
|
+
handle = null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Same as `toObservable` but emits the unwrapped payload instead of the full
|
|
51
|
+
* `AdapterMessage`. The convenience choice for app code that doesn't care
|
|
52
|
+
* about topic / ack / raw envelope.
|
|
53
|
+
*/
|
|
54
|
+
export function fromTopic<T = unknown>(
|
|
55
|
+
pubsub: PubSub,
|
|
56
|
+
topic: string,
|
|
57
|
+
meta?: unknown,
|
|
58
|
+
): Observable<T> {
|
|
59
|
+
return new Observable<T>((subscriber) => {
|
|
60
|
+
let handle: SubscriptionHandle | null = null;
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
|
|
63
|
+
pubsub
|
|
64
|
+
.subscribe<T>(
|
|
65
|
+
topic,
|
|
66
|
+
(msg) => {
|
|
67
|
+
subscriber.next(msg.payload);
|
|
68
|
+
},
|
|
69
|
+
meta as never,
|
|
70
|
+
)
|
|
71
|
+
.then((h) => {
|
|
72
|
+
if (cancelled) {
|
|
73
|
+
void h.unsubscribe();
|
|
74
|
+
} else {
|
|
75
|
+
handle = h;
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
.catch((err) => {
|
|
79
|
+
if (!cancelled) subscriber.error(err);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return () => {
|
|
83
|
+
cancelled = true;
|
|
84
|
+
if (handle) {
|
|
85
|
+
void handle.unsubscribe();
|
|
86
|
+
handle = null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|