@absolutejs/sync-bus-redis 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/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # @absolutejs/sync-bus-redis
2
+
3
+ Redis pub/sub `ClusterBus` for
4
+ [@absolutejs/sync](https://github.com/absolutejs/sync). Sibling to
5
+ [`@absolutejs/sync-bus-pg`](../bus-pg) — same `ClusterBus` contract,
6
+ different transport.
7
+
8
+ ## When to use Redis vs Postgres
9
+
10
+ | Concern | `sync-bus-redis` | `sync-bus-pg` |
11
+ |---|---|---|
12
+ | Payload size | No cap — JSON through | 8KB NOTIFY cap (spill-table fallback) |
13
+ | Fan-out latency at 10+ subscribers | In-memory, low | WAL-replicated, higher |
14
+ | Geo-replication | Native (Redis Cluster, ElastiCache, Memorystore, Upstash) | Heavy ops (PG logical replication) |
15
+ | Delivery semantics | At-most-once (no message retention) | At-most-once (NOTIFY) + spill rows for oversized |
16
+ | Already in your stack? | If yes → win-win | If yes → win-win |
17
+
18
+ **The headline tradeoff.** Redis is in-memory pub/sub — a subscriber
19
+ that's disconnected when a message fires misses it. For
20
+ cross-instance resume past shard reboot, pair with
21
+ `engine.exportChangeLog()` / `importChangeLog()` (sync 1.19.0+)
22
+ regardless of which bus you use.
23
+
24
+ ## Install
25
+
26
+ ```sh
27
+ bun add @absolutejs/sync @absolutejs/sync-bus-redis
28
+ # Plus a Redis client — bring your own:
29
+ bun add ioredis # OR
30
+ bun add redis # node-redis v4+
31
+ ```
32
+
33
+ The adapter has no hard runtime dep on a specific Redis client.
34
+
35
+ ## Usage with `ioredis`
36
+
37
+ ```ts
38
+ import { Redis } from 'ioredis';
39
+ import { createSyncEngine } from '@absolutejs/sync/engine';
40
+ import { createRedisClusterBus } from '@absolutejs/sync-bus-redis';
41
+
42
+ const publisher = new Redis(process.env.REDIS_URL!);
43
+ const subscriberClient = new Redis(process.env.REDIS_URL!);
44
+
45
+ // ioredis: a subscribed connection can't issue other commands.
46
+ // Bridge its EventEmitter API into our (channel, listener) shape:
47
+ const subscriber = {
48
+ subscribe: async (channel: string, listener: (msg: string) => void) => {
49
+ await subscriberClient.subscribe(channel);
50
+ const handler = (chan: string, msg: string) => {
51
+ if (chan === channel) listener(msg);
52
+ };
53
+ subscriberClient.on('message', handler);
54
+ return async () => {
55
+ subscriberClient.off('message', handler);
56
+ await subscriberClient.unsubscribe(channel);
57
+ };
58
+ },
59
+ };
60
+
61
+ const bus = createRedisClusterBus({ publisher, subscriber });
62
+ const engine = createSyncEngine({ instanceId: 'shard-A' });
63
+ await engine.connectCluster(bus);
64
+ ```
65
+
66
+ ## Usage with `node-redis` v4+
67
+
68
+ ```ts
69
+ import { createClient } from 'redis';
70
+ import { createRedisClusterBus } from '@absolutejs/sync-bus-redis';
71
+
72
+ const publisher = createClient({ url: process.env.REDIS_URL });
73
+ const subscriberClient = publisher.duplicate();
74
+ await Promise.all([publisher.connect(), subscriberClient.connect()]);
75
+
76
+ // node-redis: subscribe takes a callback directly.
77
+ const subscriber = {
78
+ subscribe: async (channel: string, listener: (msg: string) => void) => {
79
+ await subscriberClient.subscribe(channel, listener);
80
+ return async () => {
81
+ await subscriberClient.unsubscribe(channel);
82
+ };
83
+ },
84
+ };
85
+
86
+ const bus = createRedisClusterBus({ publisher, subscriber });
87
+ ```
88
+
89
+ ## API
90
+
91
+ ```ts
92
+ createRedisClusterBus({
93
+ publisher, // RedisPublisher — publish(channel, message)
94
+ subscriber, // RedisSubscriber — subscribe(channel, listener) → unsubscribe fn
95
+ channel?, // default 'absolutejs_sync_cluster'
96
+ onError?, // logger for parse / delivery failures
97
+ });
98
+ ```
99
+
100
+ Returns `ClusterBus & { metrics() }`. Pass to `engine.connectCluster(bus)`.
101
+
102
+ ### `bus.metrics()`
103
+
104
+ ```ts
105
+ {
106
+ published: number; // PUBLISH calls
107
+ received: number; // messages parsed + delivered to onMessage
108
+ publishErrors: number; // publisher.publish() rejections
109
+ subscribeErrors: number; // JSON.parse failures on incoming messages
110
+ totalSubscribersReached: number; // sum of counts Redis returned from PUBLISH
111
+ // — a drop to 0 when you expect peers signals
112
+ // subscriber disconnects (partition, restart)
113
+ }
114
+ ```
115
+
116
+ ## License
117
+
118
+ [Apache 2.0](../LICENSE). Tier B substrate-adjacent under the
119
+ AbsoluteJS licensing policy — rides `@absolutejs/sync` (BSL Tier A).
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @absolutejs/sync-bus-redis — Redis pub/sub `ClusterBus` for
3
+ * `@absolutejs/sync`. Cross-instance fan-out via PUBLISH/SUBSCRIBE.
4
+ *
5
+ * **Why Redis (vs the `sync-bus-pg` sibling).**
6
+ *
7
+ * - **No payload cap.** Postgres `NOTIFY` is capped at 8000 bytes,
8
+ * forcing `sync-bus-pg` into a spill-table dance for large batches.
9
+ * Redis PUBLISH has no equivalent limit — JSON straight through.
10
+ * - **Lower latency at fan-out.** Redis pub/sub is in-memory; PG
11
+ * `LISTEN/NOTIFY` rides the WAL replication path. For multi-shard
12
+ * fan-out (10+ subscribers), Redis is typically faster.
13
+ * - **Better geo-replication story.** Redis Cluster / Redis
14
+ * Enterprise / managed Redis (ElastiCache, Memorystore, Upstash)
15
+ * all geo-replicate natively. Postgres logical replication is
16
+ * possible but heavier ops.
17
+ *
18
+ * **Why NOT Redis (when to use `sync-bus-pg` instead).**
19
+ *
20
+ * - **At-most-once delivery.** Redis pub/sub does NOT durably store
21
+ * messages. A subscriber that's disconnected when a message fires
22
+ * misses it. If you need a durable cluster bus for replay across
23
+ * shard reboot, use `sync-bus-pg` (which has the spill table) and
24
+ * pair with `engine.exportChangeLog()` / `importChangeLog()` for
25
+ * post-reboot resume.
26
+ * - **Already have Postgres in your stack.** Adding Redis just for
27
+ * the cluster bus is a new dependency to operate.
28
+ *
29
+ * **Client compatibility.**
30
+ *
31
+ * The adapter takes a narrow interface (`publisher.publish(channel,
32
+ * message)` and `subscriber.subscribe(channel, listener)`) so it works
33
+ * with any 2026-era Redis client:
34
+ *
35
+ * - `ioredis`: `const redis = new Redis(url); const sub = redis.duplicate();`
36
+ * - `node-redis` v4+: `const client = createClient({ url }); const sub = client.duplicate();`
37
+ *
38
+ * Each pub/sub-using consumer needs TWO connections — one for
39
+ * publishing (your existing client), one for subscribing (a duplicate
40
+ * dedicated to the subscription, because Redis prohibits other
41
+ * commands on a subscribed connection). Pass both to
42
+ * `createRedisClusterBus({ publisher, subscriber })`.
43
+ *
44
+ * See `README.md` for `ioredis` and `node-redis` adapter snippets.
45
+ */
46
+ import type { ClusterBus } from '@absolutejs/sync/engine';
47
+ /**
48
+ * Minimal Redis publisher contract. Both `ioredis` and `node-redis` v4
49
+ * structurally satisfy this (`publish(channel, message)` is the
50
+ * canonical signature; both return a Promise that resolves to the
51
+ * number of subscribers that received the message).
52
+ */
53
+ export type RedisPublisher = {
54
+ publish: (channel: string, message: string) => Promise<number | unknown>;
55
+ };
56
+ /**
57
+ * Minimal Redis subscriber contract. The shape diverges between
58
+ * ioredis (EventEmitter-based) and node-redis (callback-based);
59
+ * `RedisSubscriber` is the lowest-common-denominator:
60
+ *
61
+ * - `subscribe(channel, listener)` registers `listener` for messages
62
+ * on `channel` AND returns an unsubscribe function. The listener
63
+ * receives the raw message string.
64
+ *
65
+ * The README shows how to wrap ioredis (where you'd call
66
+ * `subscriber.on('message', handler)` once and route by channel) and
67
+ * node-redis (where `subscribe(channel, listener)` is the SDK call
68
+ * directly).
69
+ */
70
+ export type RedisSubscriber = {
71
+ subscribe: (channel: string, listener: (message: string) => void) => Promise<() => Promise<void>>;
72
+ };
73
+ export type CreateRedisClusterBusOptions = {
74
+ /** The Redis client used for `publish()` calls. */
75
+ publisher: RedisPublisher;
76
+ /** The Redis subscriber. MUST be a dedicated connection (a
77
+ * `duplicate()` of your main client) — Redis forbids other
78
+ * commands on a subscribed connection. */
79
+ subscriber: RedisSubscriber;
80
+ /**
81
+ * Channel name. Defaults to `'absolutejs_sync_cluster'`. Two
82
+ * engines on the same Redis can scope themselves to different
83
+ * channels by overriding this.
84
+ */
85
+ channel?: string;
86
+ /**
87
+ * Called when message parsing / delivery fails. Defaults to
88
+ * `console.warn`. Note that Redis pub/sub doesn't surface delivery
89
+ * failures on the publisher side — `publish()` resolves to the
90
+ * count of subscribers that received the message (or 0); a 0 count
91
+ * does NOT fire onError (Redis treats "no subscribers" as success).
92
+ */
93
+ onError?: (error: unknown) => void;
94
+ };
95
+ /**
96
+ * Cumulative metrics since `createRedisClusterBus()`. Same shape
97
+ * style as `sync-bus-pg`'s — minus the spill-table fields (Redis
98
+ * has no equivalent).
99
+ */
100
+ export type RedisClusterBusMetrics = {
101
+ published: number;
102
+ received: number;
103
+ publishErrors: number;
104
+ subscribeErrors: number;
105
+ /**
106
+ * Cumulative count of subscribers reached on the publisher side.
107
+ * Returned by Redis's PUBLISH. Use it as a rough "is the cluster
108
+ * still wired up" signal — a drop to 0 when you expect peers means
109
+ * subscribers disconnected (replication lag, network partition).
110
+ */
111
+ totalSubscribersReached: number;
112
+ };
113
+ export type RedisClusterBus = ClusterBus & {
114
+ metrics: () => RedisClusterBusMetrics;
115
+ };
116
+ export declare const createRedisClusterBus: (options: CreateRedisClusterBusOptions) => RedisClusterBus;
117
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,yBAAyB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;CACzE,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,eAAe,GAAG;IAC7B,SAAS,EAAE,CACV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAC/B,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IAC1C,mDAAmD;IACnD,SAAS,EAAE,cAAc,CAAC;IAC1B;;+CAE2C;IAC3C,UAAU,EAAE,eAAe,CAAC;IAC5B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,uBAAuB,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG;IAC1C,OAAO,EAAE,MAAM,sBAAsB,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,qBAAqB,GACjC,SAAS,4BAA4B,KACnC,eA+CF,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ // @bun
2
+ // src/index.ts
3
+ var createRedisClusterBus = (options) => {
4
+ const channel = options.channel ?? "absolutejs_sync_cluster";
5
+ const onError = options.onError ?? ((error) => console.warn("[sync-bus-redis]", error));
6
+ const counters = {
7
+ publishErrors: 0,
8
+ published: 0,
9
+ received: 0,
10
+ subscribeErrors: 0,
11
+ totalSubscribersReached: 0
12
+ };
13
+ return {
14
+ metrics: () => ({ ...counters }),
15
+ publish: async (message) => {
16
+ try {
17
+ const serialized = JSON.stringify(message);
18
+ const result = await options.publisher.publish(channel, serialized);
19
+ if (typeof result === "number") {
20
+ counters.totalSubscribersReached += result;
21
+ }
22
+ counters.published += 1;
23
+ } catch (error) {
24
+ counters.publishErrors += 1;
25
+ throw error;
26
+ }
27
+ },
28
+ subscribe: async (onMessage) => {
29
+ const listener = (raw) => {
30
+ try {
31
+ const parsed = JSON.parse(raw);
32
+ counters.received += 1;
33
+ onMessage(parsed);
34
+ } catch (error) {
35
+ counters.subscribeErrors += 1;
36
+ onError(error);
37
+ }
38
+ };
39
+ return options.subscriber.subscribe(channel, listener);
40
+ }
41
+ };
42
+ };
43
+ export {
44
+ createRedisClusterBus
45
+ };
46
+
47
+ //# debugId=0C163B79C3CB22BF64756E2164756E21
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @absolutejs/sync-bus-redis — Redis pub/sub `ClusterBus` for\n * `@absolutejs/sync`. Cross-instance fan-out via PUBLISH/SUBSCRIBE.\n *\n * **Why Redis (vs the `sync-bus-pg` sibling).**\n *\n * - **No payload cap.** Postgres `NOTIFY` is capped at 8000 bytes,\n * forcing `sync-bus-pg` into a spill-table dance for large batches.\n * Redis PUBLISH has no equivalent limit — JSON straight through.\n * - **Lower latency at fan-out.** Redis pub/sub is in-memory; PG\n * `LISTEN/NOTIFY` rides the WAL replication path. For multi-shard\n * fan-out (10+ subscribers), Redis is typically faster.\n * - **Better geo-replication story.** Redis Cluster / Redis\n * Enterprise / managed Redis (ElastiCache, Memorystore, Upstash)\n * all geo-replicate natively. Postgres logical replication is\n * possible but heavier ops.\n *\n * **Why NOT Redis (when to use `sync-bus-pg` instead).**\n *\n * - **At-most-once delivery.** Redis pub/sub does NOT durably store\n * messages. A subscriber that's disconnected when a message fires\n * misses it. If you need a durable cluster bus for replay across\n * shard reboot, use `sync-bus-pg` (which has the spill table) and\n * pair with `engine.exportChangeLog()` / `importChangeLog()` for\n * post-reboot resume.\n * - **Already have Postgres in your stack.** Adding Redis just for\n * the cluster bus is a new dependency to operate.\n *\n * **Client compatibility.**\n *\n * The adapter takes a narrow interface (`publisher.publish(channel,\n * message)` and `subscriber.subscribe(channel, listener)`) so it works\n * with any 2026-era Redis client:\n *\n * - `ioredis`: `const redis = new Redis(url); const sub = redis.duplicate();`\n * - `node-redis` v4+: `const client = createClient({ url }); const sub = client.duplicate();`\n *\n * Each pub/sub-using consumer needs TWO connections — one for\n * publishing (your existing client), one for subscribing (a duplicate\n * dedicated to the subscription, because Redis prohibits other\n * commands on a subscribed connection). Pass both to\n * `createRedisClusterBus({ publisher, subscriber })`.\n *\n * See `README.md` for `ioredis` and `node-redis` adapter snippets.\n */\n\nimport type { ClusterBus, ClusterMessage } from '@absolutejs/sync/engine';\n\n/**\n * Minimal Redis publisher contract. Both `ioredis` and `node-redis` v4\n * structurally satisfy this (`publish(channel, message)` is the\n * canonical signature; both return a Promise that resolves to the\n * number of subscribers that received the message).\n */\nexport type RedisPublisher = {\n\tpublish: (channel: string, message: string) => Promise<number | unknown>;\n};\n\n/**\n * Minimal Redis subscriber contract. The shape diverges between\n * ioredis (EventEmitter-based) and node-redis (callback-based);\n * `RedisSubscriber` is the lowest-common-denominator:\n *\n * - `subscribe(channel, listener)` registers `listener` for messages\n * on `channel` AND returns an unsubscribe function. The listener\n * receives the raw message string.\n *\n * The README shows how to wrap ioredis (where you'd call\n * `subscriber.on('message', handler)` once and route by channel) and\n * node-redis (where `subscribe(channel, listener)` is the SDK call\n * directly).\n */\nexport type RedisSubscriber = {\n\tsubscribe: (\n\t\tchannel: string,\n\t\tlistener: (message: string) => void\n\t) => Promise<() => Promise<void>>;\n};\n\nexport type CreateRedisClusterBusOptions = {\n\t/** The Redis client used for `publish()` calls. */\n\tpublisher: RedisPublisher;\n\t/** The Redis subscriber. MUST be a dedicated connection (a\n\t * `duplicate()` of your main client) — Redis forbids other\n\t * commands on a subscribed connection. */\n\tsubscriber: RedisSubscriber;\n\t/**\n\t * Channel name. Defaults to `'absolutejs_sync_cluster'`. Two\n\t * engines on the same Redis can scope themselves to different\n\t * channels by overriding this.\n\t */\n\tchannel?: string;\n\t/**\n\t * Called when message parsing / delivery fails. Defaults to\n\t * `console.warn`. Note that Redis pub/sub doesn't surface delivery\n\t * failures on the publisher side — `publish()` resolves to the\n\t * count of subscribers that received the message (or 0); a 0 count\n\t * does NOT fire onError (Redis treats \"no subscribers\" as success).\n\t */\n\tonError?: (error: unknown) => void;\n};\n\n/**\n * Cumulative metrics since `createRedisClusterBus()`. Same shape\n * style as `sync-bus-pg`'s — minus the spill-table fields (Redis\n * has no equivalent).\n */\nexport type RedisClusterBusMetrics = {\n\tpublished: number;\n\treceived: number;\n\tpublishErrors: number;\n\tsubscribeErrors: number;\n\t/**\n\t * Cumulative count of subscribers reached on the publisher side.\n\t * Returned by Redis's PUBLISH. Use it as a rough \"is the cluster\n\t * still wired up\" signal — a drop to 0 when you expect peers means\n\t * subscribers disconnected (replication lag, network partition).\n\t */\n\ttotalSubscribersReached: number;\n};\n\nexport type RedisClusterBus = ClusterBus & {\n\tmetrics: () => RedisClusterBusMetrics;\n};\n\nexport const createRedisClusterBus = (\n\toptions: CreateRedisClusterBusOptions\n): RedisClusterBus => {\n\tconst channel = options.channel ?? 'absolutejs_sync_cluster';\n\tconst onError =\n\t\toptions.onError ?? ((error) => console.warn('[sync-bus-redis]', error));\n\n\tconst counters: RedisClusterBusMetrics = {\n\t\tpublishErrors: 0,\n\t\tpublished: 0,\n\t\treceived: 0,\n\t\tsubscribeErrors: 0,\n\t\ttotalSubscribersReached: 0\n\t};\n\n\treturn {\n\t\tmetrics: () => ({ ...counters }),\n\t\tpublish: async (message: ClusterMessage): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst serialized = JSON.stringify(message);\n\t\t\t\tconst result = await options.publisher.publish(\n\t\t\t\t\tchannel,\n\t\t\t\t\tserialized\n\t\t\t\t);\n\t\t\t\tif (typeof result === 'number') {\n\t\t\t\t\tcounters.totalSubscribersReached += result;\n\t\t\t\t}\n\t\t\t\tcounters.published += 1;\n\t\t\t} catch (error) {\n\t\t\t\tcounters.publishErrors += 1;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tsubscribe: async (\n\t\t\tonMessage: (message: ClusterMessage) => void\n\t\t): Promise<() => Promise<void>> => {\n\t\t\tconst listener = (raw: string): void => {\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(raw) as ClusterMessage;\n\t\t\t\t\tcounters.received += 1;\n\t\t\t\t\tonMessage(parsed);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcounters.subscribeErrors += 1;\n\t\t\t\t\tonError(error);\n\t\t\t\t}\n\t\t\t};\n\t\t\treturn options.subscriber.subscribe(channel, listener);\n\t\t}\n\t};\n};\n"
6
+ ],
7
+ "mappings": ";;AA6HO,IAAM,wBAAwB,CACpC,YACqB;AAAA,EACrB,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,UACL,QAAQ,YAAY,CAAC,UAAU,QAAQ,KAAK,oBAAoB,KAAK;AAAA,EAEtE,MAAM,WAAmC;AAAA,IACxC,eAAe;AAAA,IACf,WAAW;AAAA,IACX,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,yBAAyB;AAAA,EAC1B;AAAA,EAEA,OAAO;AAAA,IACN,SAAS,OAAO,KAAK,SAAS;AAAA,IAC9B,SAAS,OAAO,YAA2C;AAAA,MAC1D,IAAI;AAAA,QACH,MAAM,aAAa,KAAK,UAAU,OAAO;AAAA,QACzC,MAAM,SAAS,MAAM,QAAQ,UAAU,QACtC,SACA,UACD;AAAA,QACA,IAAI,OAAO,WAAW,UAAU;AAAA,UAC/B,SAAS,2BAA2B;AAAA,QACrC;AAAA,QACA,SAAS,aAAa;AAAA,QACrB,OAAO,OAAO;AAAA,QACf,SAAS,iBAAiB;AAAA,QAC1B,MAAM;AAAA;AAAA;AAAA,IAGR,WAAW,OACV,cACkC;AAAA,MAClC,MAAM,WAAW,CAAC,QAAsB;AAAA,QACvC,IAAI;AAAA,UACH,MAAM,SAAS,KAAK,MAAM,GAAG;AAAA,UAC7B,SAAS,YAAY;AAAA,UACrB,UAAU,MAAM;AAAA,UACf,OAAO,OAAO;AAAA,UACf,SAAS,mBAAmB;AAAA,UAC5B,QAAQ,KAAK;AAAA;AAAA;AAAA,MAGf,OAAO,QAAQ,WAAW,UAAU,SAAS,QAAQ;AAAA;AAAA,EAEvD;AAAA;",
8
+ "debugId": "0C163B79C3CB22BF64756E2164756E21",
9
+ "names": []
10
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@absolutejs/sync-bus-redis",
3
+ "version": "0.0.1",
4
+ "description": "Redis pub/sub ClusterBus for @absolutejs/sync — cross-instance fan-out via PUBLISH/SUBSCRIBE. Sibling to @absolutejs/sync-bus-pg; faster fanout, better geo-replication story. Works with any Redis client (ioredis, node-redis, etc.) via a narrow tag-template interface.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/absolutejs/sync-adapters.git",
8
+ "directory": "redis"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "type": "module",
14
+ "license": "Apache-2.0",
15
+ "author": "Alex Kahn",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "keywords": [
20
+ "absolutejs",
21
+ "sync",
22
+ "cluster",
23
+ "redis",
24
+ "pubsub",
25
+ "horizontal-scale",
26
+ "multi-region"
27
+ ],
28
+ "scripts": {
29
+ "build": "rm -rf dist && bun build src/index.ts --outdir dist --sourcemap --target=bun --external @absolutejs/sync && tsc --project tsconfig.build.json",
30
+ "test": "bun test",
31
+ "typecheck": "tsc --noEmit",
32
+ "format": "prettier --write \"./**/*.{ts,json,md}\"",
33
+ "release": "bun run format && bun run build && bun publish"
34
+ },
35
+ "peerDependencies": {
36
+ "@absolutejs/sync": ">= 1.17.0"
37
+ },
38
+ "devDependencies": {
39
+ "@absolutejs/sync": "^1.21.0",
40
+ "@types/bun": "1.3.14",
41
+ "prettier": "3.5.3",
42
+ "typescript": "5.8.3"
43
+ },
44
+ "exports": {
45
+ ".": {
46
+ "types": "./dist/index.d.ts",
47
+ "import": "./dist/index.js",
48
+ "default": "./dist/index.js"
49
+ }
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "README.md"
54
+ ]
55
+ }