@cedarjs/realtime 0.0.4
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 +61 -0
- package/dist/graphql/index.d.ts +3 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/plugins/__fixtures__/common.d.ts +9 -0
- package/dist/graphql/plugins/__fixtures__/common.d.ts.map +1 -0
- package/dist/graphql/plugins/useRedwoodRealtime.d.ts +94 -0
- package/dist/graphql/plugins/useRedwoodRealtime.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +175 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Cedar
|
|
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,61 @@
|
|
|
1
|
+
# Realtime
|
|
2
|
+
|
|
3
|
+
The real-time solution for RedwoodJS is initially for GraphQL.
|
|
4
|
+
|
|
5
|
+
In GraphQL, there are two options for real-time updates: **live queries** and **subscriptions**. Subscriptions are part of the GraphQL specification, whereas live queries are not.
|
|
6
|
+
|
|
7
|
+
There are times where subscriptions are well-suited for a realtime problem — and in some cases live queries may be a better fit. Later we’ll explore the pros and cons of each approach and how best to decide that to use and when.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
RedwoodJS Realtime handles the hard parts of a GraphQL Realtime implementation by automatically:
|
|
12
|
+
|
|
13
|
+
- allowing GraphQL Subscription operations to be handled
|
|
14
|
+
- merging in your subscriptions types and mapping their handler functions (subscribe, and resolve) to your GraphQL schema letting you keep your subscription logic organized and apart from services (your subscription my use a service to respond to an event)
|
|
15
|
+
- authenticating subscription requests using the same `@requireAuth` directives already protecting other queries and mutations (or you can implement your own validator directive)
|
|
16
|
+
- adding in the `@live` query directive to your GraphQL schema and setting up the `useLiveQuery` envelop plugin to handle requests, invalidation, and managing the storage mechanism needed
|
|
17
|
+
- creating and configuring in-memory and persisted Redis stores uses by the PubSub transport for subscriptions and Live Queries (and letting you switch between them in development and production)
|
|
18
|
+
- placing the pubSub transport and stores into the GraphQL context so you can use them in services, subscription resolvers, or elsewhere (like a webhook, function, or job) to publish an event or invalidate data
|
|
19
|
+
- typing you subscription channel event payloads
|
|
20
|
+
|
|
21
|
+
It provides a first-class developer experience for real-time updates with GraphQL so you can easily
|
|
22
|
+
|
|
23
|
+
- respond to an event (e.g. NewPost, NewUserNotification)
|
|
24
|
+
- respond to a data change (e.g. Post 123's title updated)
|
|
25
|
+
|
|
26
|
+
and have the latest data reflected in your app.
|
|
27
|
+
|
|
28
|
+
Lastly, the Redwood CLI has commands to
|
|
29
|
+
|
|
30
|
+
- generate a boilerplate implementation and sample code needed to create your custom
|
|
31
|
+
- subscriptions
|
|
32
|
+
- live Queries
|
|
33
|
+
|
|
34
|
+
Regardless of the implementation chosen, **a stateful server and store are needed** to track changes, invalidation, or who wants to be informed about the change.
|
|
35
|
+
|
|
36
|
+
### useRedwoodRealtime
|
|
37
|
+
|
|
38
|
+
The `useRedwoodRealtime` plugin adds support for Redwood Realtime in GraphQL Yoga Server.
|
|
39
|
+
|
|
40
|
+
Note: Since a stateful server and store are needed, this plugin cannot be used this RedwoodJS applications deployed to serverless.
|
|
41
|
+
|
|
42
|
+
> **Warning**
|
|
43
|
+
>
|
|
44
|
+
> This is a new internal package. There are still changes we want to make, so we're marking it as experimental for now.
|
|
45
|
+
> **Don't depend on this directly in a Redwood project**.
|
|
46
|
+
|
|
47
|
+
<!-- ## Package size
|
|
48
|
+
|
|
49
|
+
| Version | Publish | Install |
|
|
50
|
+
| :--------------------------------------------------------------------------------- | :------ | :------ |
|
|
51
|
+
| [v5.2.1](https://packagephobia.com/result?p=%40redwoodjs%2Fproject-config%405.2.1) | 96.6 kB | 809 kB |
|
|
52
|
+
|
|
53
|
+
## Dependency graphs
|
|
54
|
+
|
|
55
|
+
### src
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
### dist
|
|
60
|
+
|
|
61
|
+
 -->
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { useRedwoodRealtime, createPubSub, liveDirectiveTypeDefs, InMemoryLiveQueryStore, RedisLiveQueryStore, liveQueryStore, pubSub, Repeater, } from './plugins/useRedwoodRealtime';
|
|
2
|
+
export type { LiveQueryStorageMechanism, PubSub, PublishClientType, SubscribeClientType, SubscriptionGlobImports, RedwoodRealtimeOptions, } from './plugins/useRedwoodRealtime';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/graphql/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,MAAM,EACN,QAAQ,GACT,MAAM,8BAA8B,CAAA;AAErC,YAAY,EACV,yBAAyB,EACzB,MAAM,EACN,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,8BAA8B,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const testSchema: import("graphql").GraphQLSchema;
|
|
2
|
+
export declare const testLiveSchema: import("graphql").GraphQLSchema;
|
|
3
|
+
export declare const testQuery = "\n query meQuery {\n me {\n id\n name\n }\n }\n";
|
|
4
|
+
export declare const testFilteredQuery = "\n query FilteredQuery {\n me {\n id\n name\n }\n }\n";
|
|
5
|
+
export declare const testErrorQuery = "\n query forbiddenUserQuery {\n forbiddenUser {\n id\n name\n }\n }\n";
|
|
6
|
+
export declare const testLiveQuery = "\n query meQuery @live {\n me {\n id\n name\n }\n }\n";
|
|
7
|
+
export declare const testParseErrorQuery = "\n query ParseErrorQuery {\n me {\n id\n name\n unknown_field\n }\n }\n";
|
|
8
|
+
export declare const testValidationErrorQuery = "\n query ValidationErrorQuery(id: Int!) {\n getUser(id: 'one') {\n id\n name\n }\n }\n";
|
|
9
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../../src/graphql/plugins/__fixtures__/common.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,iCAiCrB,CAAA;AAEF,eAAO,MAAM,cAAc,iCAgDzB,CAAA;AACF,eAAO,MAAM,SAAS,sEAOrB,CAAA;AAED,eAAO,MAAM,iBAAiB,4EAO7B,CAAA;AAED,eAAO,MAAM,cAAc,4FAO1B,CAAA;AAED,eAAO,MAAM,aAAa,4EAOzB,CAAA;AAED,eAAO,MAAM,mBAAmB,mGAQ/B,CAAA;AAED,eAAO,MAAM,wBAAwB,6GAOpC,CAAA"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Plugin } from '@envelop/core';
|
|
2
|
+
import type { CreateRedisEventTargetArgs } from '@graphql-yoga/redis-event-target';
|
|
3
|
+
import type { PubSub } from '@graphql-yoga/subscription';
|
|
4
|
+
import { createPubSub } from '@graphql-yoga/subscription';
|
|
5
|
+
import { InMemoryLiveQueryStore } from '@n1ru4l/in-memory-live-query-store';
|
|
6
|
+
import type { execute as defaultExecute } from 'graphql';
|
|
7
|
+
export { Repeater } from 'graphql-yoga';
|
|
8
|
+
/**
|
|
9
|
+
* We want SubscriptionsGlobs type to be an object with this shape:
|
|
10
|
+
*
|
|
11
|
+
* But not fully supported in TS
|
|
12
|
+
* {
|
|
13
|
+
* schema: DocumentNode // <-- required
|
|
14
|
+
* [string]: RedwoodSubscription
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* Note: This type is duplicated from packages/graphql-server/src/subscriptions/makeSubscriptions
|
|
18
|
+
* so there is no dependency on graphql-server from realtime and vice versa.
|
|
19
|
+
*/
|
|
20
|
+
export type SubscriptionGlobImports = Record<string, any>;
|
|
21
|
+
export type { PubSub };
|
|
22
|
+
export { createPubSub, InMemoryLiveQueryStore };
|
|
23
|
+
export declare const liveDirectiveTypeDefs: string;
|
|
24
|
+
export type LiveQueryStorageMechanism = RedisLiveQueryStore | InMemoryLiveQueryStore;
|
|
25
|
+
export type PublishClientType = CreateRedisEventTargetArgs['publishClient'];
|
|
26
|
+
export type SubscribeClientType = CreateRedisEventTargetArgs['subscribeClient'];
|
|
27
|
+
/**
|
|
28
|
+
* Configure RedwoodJS Realtime
|
|
29
|
+
*
|
|
30
|
+
* Realtime supports Live Queries and Subscriptions over GraphQL SSE.
|
|
31
|
+
*
|
|
32
|
+
* Live Queries are GraphQL queries that are automatically re-run when the data they depend on changes.
|
|
33
|
+
*
|
|
34
|
+
* Subscriptions are GraphQL queries that are run when a client subscribes to a channel.
|
|
35
|
+
*
|
|
36
|
+
* Redwood Realtime
|
|
37
|
+
* - uses a publish/subscribe model to broadcast data to clients.
|
|
38
|
+
* - uses a store to persist Live Query and Subscription data.
|
|
39
|
+
*
|
|
40
|
+
* Redwood Realtime supports in-memory and Redis stores:
|
|
41
|
+
* - In-memory stores are useful for development and testing.
|
|
42
|
+
* - Redis stores are useful for production.
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
export type RedwoodRealtimeOptions = {
|
|
46
|
+
enableDeferStream?: boolean;
|
|
47
|
+
liveQueries?: {
|
|
48
|
+
/**
|
|
49
|
+
* @description Redwood Realtime supports in-memory and Redis stores.
|
|
50
|
+
* @default 'in-memory'
|
|
51
|
+
*/
|
|
52
|
+
store: 'in-memory' | {
|
|
53
|
+
redis: {
|
|
54
|
+
/**
|
|
55
|
+
* @description The channel to publish invalidations to.
|
|
56
|
+
* @default 'live-query-invalidations'
|
|
57
|
+
*/
|
|
58
|
+
channel?: string;
|
|
59
|
+
publishClient: PublishClientType;
|
|
60
|
+
subscribeClient: SubscribeClientType;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
subscriptions?: {
|
|
65
|
+
/**
|
|
66
|
+
* @description Redwood Realtime supports in-memory and Redis stores.
|
|
67
|
+
* @default 'in-memory'
|
|
68
|
+
*/
|
|
69
|
+
store: 'in-memory' | {
|
|
70
|
+
redis: {
|
|
71
|
+
publishClient: PublishClientType;
|
|
72
|
+
subscribeClient: SubscribeClientType;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* @description Subscriptions passed from the glob import:
|
|
77
|
+
* import subscriptions from 'src/subscriptions/**\/*.{js,ts}'
|
|
78
|
+
*/
|
|
79
|
+
subscriptions: SubscriptionGlobImports;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export declare class RedisLiveQueryStore {
|
|
83
|
+
pub: PublishClientType;
|
|
84
|
+
sub: SubscribeClientType;
|
|
85
|
+
channel: string;
|
|
86
|
+
liveQueryStore: InMemoryLiveQueryStore;
|
|
87
|
+
constructor(pub: PublishClientType, sub: SubscribeClientType, channel: string, liveQueryStore: InMemoryLiveQueryStore);
|
|
88
|
+
invalidate(identifiers: string[] | string): Promise<void>;
|
|
89
|
+
makeExecute(execute: typeof defaultExecute): (args: import("graphql").ExecutionArgs) => AsyncIterableIterator<import("graphql").ExecutionResult<import("graphql/jsutils/ObjMap").ObjMap<unknown>, import("graphql/jsutils/ObjMap").ObjMap<unknown>> | import("@n1ru4l/graphql-live-query").LiveExecutionResult> | import("graphql").ExecutionResult<import("graphql/jsutils/ObjMap").ObjMap<unknown>, import("graphql/jsutils/ObjMap").ObjMap<unknown>> | Promise<AsyncIterableIterator<import("graphql").ExecutionResult<import("graphql/jsutils/ObjMap").ObjMap<unknown>, import("graphql/jsutils/ObjMap").ObjMap<unknown>> | import("@n1ru4l/graphql-live-query").LiveExecutionResult> | import("graphql").ExecutionResult<import("graphql/jsutils/ObjMap").ObjMap<unknown>, import("graphql/jsutils/ObjMap").ObjMap<unknown>>>;
|
|
90
|
+
}
|
|
91
|
+
export declare let liveQueryStore: LiveQueryStorageMechanism | undefined;
|
|
92
|
+
export declare let pubSub: ReturnType<typeof createPubSub> | undefined;
|
|
93
|
+
export declare const useRedwoodRealtime: (options: RedwoodRealtimeOptions) => Plugin;
|
|
94
|
+
//# sourceMappingURL=useRedwoodRealtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRedwoodRealtime.d.ts","sourceRoot":"","sources":["../../../src/graphql/plugins/useRedwoodRealtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAO3C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAA;AAClF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,SAAS,CAAA;AAGxD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAEzD,YAAY,EAAE,MAAM,EAAE,CAAA;AAEtB,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,CAAA;AAE/C,eAAO,MAAM,qBAAqB,QAEjC,CAAA;AAED,MAAM,MAAM,yBAAyB,GACjC,mBAAmB,GACnB,sBAAsB,CAAA;AAE1B,MAAM,MAAM,iBAAiB,GAAG,0BAA0B,CAAC,eAAe,CAAC,CAAA;AAC3E,MAAM,MAAM,mBAAmB,GAAG,0BAA0B,CAAC,iBAAiB,CAAC,CAAA;AAE/E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,WAAW,CAAC,EAAE;QACZ;;;WAGG;QACH,KAAK,EACD,WAAW,GACX;YACE,KAAK,EAAE;gBACL;;;mBAGG;gBACH,OAAO,CAAC,EAAE,MAAM,CAAA;gBAChB,aAAa,EAAE,iBAAiB,CAAA;gBAChC,eAAe,EAAE,mBAAmB,CAAA;aACrC,CAAA;SACF,CAAA;KACN,CAAA;IACD,aAAa,CAAC,EAAE;QACd;;;WAGG;QACH,KAAK,EACD,WAAW,GACX;YACE,KAAK,EAAE;gBACL,aAAa,EAAE,iBAAiB,CAAA;gBAChC,eAAe,EAAE,mBAAmB,CAAA;aACrC,CAAA;SACF,CAAA;QACL;;;WAGG;QACH,aAAa,EAAE,uBAAuB,CAAA;KACvC,CAAA;CACF,CAAA;AAED,qBAAa,mBAAmB;IAC9B,GAAG,EAAE,iBAAiB,CAAA;IACtB,GAAG,EAAE,mBAAmB,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,sBAAsB,CAAA;gBAGpC,GAAG,EAAE,iBAAiB,EACtB,GAAG,EAAE,mBAAmB,EACxB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,sBAAsB;IAoBlC,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM;IAS/C,WAAW,CAAC,OAAO,EAAE,OAAO,cAAc;CAG3C;AAGD,eAAO,IAAI,cAAc,EAAE,yBAAyB,GAAG,SAAqB,CAAA;AAC5E,eAAO,IAAI,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,GAAG,SAAqB,CAAA;AAE1E,eAAO,MAAM,kBAAkB,YAAa,sBAAsB,KAAG,MAiGpE,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { useRedwoodRealtime, createPubSub, liveDirectiveTypeDefs, InMemoryLiveQueryStore, RedisLiveQueryStore, liveQueryStore, pubSub, Repeater, } from './graphql';
|
|
2
|
+
export type { LiveQueryStorageMechanism, PubSub, PublishClientType, SubscribeClientType, SubscriptionGlobImports, RedwoodRealtimeOptions, } from './graphql';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EACd,MAAM,EACN,QAAQ,GACT,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,yBAAyB,EACzB,MAAM,EACN,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,WAAW,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
InMemoryLiveQueryStore: () => import_in_memory_live_query_store.InMemoryLiveQueryStore,
|
|
24
|
+
RedisLiveQueryStore: () => RedisLiveQueryStore,
|
|
25
|
+
Repeater: () => import_graphql_yoga.Repeater,
|
|
26
|
+
createPubSub: () => import_subscription.createPubSub,
|
|
27
|
+
liveDirectiveTypeDefs: () => liveDirectiveTypeDefs,
|
|
28
|
+
liveQueryStore: () => liveQueryStore,
|
|
29
|
+
pubSub: () => pubSub,
|
|
30
|
+
useRedwoodRealtime: () => useRedwoodRealtime
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
|
+
|
|
34
|
+
// src/graphql/plugins/useRedwoodRealtime.ts
|
|
35
|
+
var import_live_query = require("@envelop/live-query");
|
|
36
|
+
var import_schema = require("@graphql-tools/schema");
|
|
37
|
+
var import_utils = require("@graphql-tools/utils");
|
|
38
|
+
var import_plugin_defer_stream = require("@graphql-yoga/plugin-defer-stream");
|
|
39
|
+
var import_plugin_graphql_sse = require("@graphql-yoga/plugin-graphql-sse");
|
|
40
|
+
var import_redis_event_target = require("@graphql-yoga/redis-event-target");
|
|
41
|
+
var import_subscription = require("@graphql-yoga/subscription");
|
|
42
|
+
var import_graphql_live_query = require("@n1ru4l/graphql-live-query");
|
|
43
|
+
var import_in_memory_live_query_store = require("@n1ru4l/in-memory-live-query-store");
|
|
44
|
+
var import_graphql = require("graphql");
|
|
45
|
+
var import_graphql_yoga = require("graphql-yoga");
|
|
46
|
+
var liveDirectiveTypeDefs = (0, import_graphql.print)(
|
|
47
|
+
(0, import_utils.astFromDirective)(import_graphql_live_query.GraphQLLiveDirective)
|
|
48
|
+
);
|
|
49
|
+
var RedisLiveQueryStore = class {
|
|
50
|
+
pub;
|
|
51
|
+
sub;
|
|
52
|
+
channel;
|
|
53
|
+
liveQueryStore;
|
|
54
|
+
constructor(pub, sub, channel, liveQueryStore2) {
|
|
55
|
+
this.pub = pub;
|
|
56
|
+
this.sub = sub;
|
|
57
|
+
this.liveQueryStore = liveQueryStore2;
|
|
58
|
+
this.channel = channel;
|
|
59
|
+
this.sub.subscribe(this.channel, (err) => {
|
|
60
|
+
if (err) {
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
this.sub.on("message", (channel2, resourceIdentifier) => {
|
|
65
|
+
if (channel2 === this.channel && resourceIdentifier) {
|
|
66
|
+
this.liveQueryStore.invalidate(resourceIdentifier);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async invalidate(identifiers) {
|
|
71
|
+
if (typeof identifiers === "string") {
|
|
72
|
+
identifiers = [identifiers];
|
|
73
|
+
}
|
|
74
|
+
for (const identifier of identifiers) {
|
|
75
|
+
this.pub.publish(this.channel, identifier);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
makeExecute(execute) {
|
|
79
|
+
return this.liveQueryStore.makeExecute(execute);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var liveQueryStore = void 0;
|
|
83
|
+
var pubSub = void 0;
|
|
84
|
+
var useRedwoodRealtime = (options) => {
|
|
85
|
+
let liveQueriesEnabled = false;
|
|
86
|
+
let subscriptionsEnabled = false;
|
|
87
|
+
let liveQueryPlugin = {};
|
|
88
|
+
const inMemoryLiveQueryStore = new import_in_memory_live_query_store.InMemoryLiveQueryStore();
|
|
89
|
+
liveQueryStore = {};
|
|
90
|
+
pubSub = {};
|
|
91
|
+
const wasLiveQueryAdded = Symbol.for("useRedwoodRealtime.wasLiveQueryAdded");
|
|
92
|
+
if (options.liveQueries?.store) {
|
|
93
|
+
if (options.liveQueries.store === "in-memory") {
|
|
94
|
+
liveQueriesEnabled = true;
|
|
95
|
+
liveQueryStore = inMemoryLiveQueryStore;
|
|
96
|
+
liveQueryPlugin = (0, import_live_query.useLiveQuery)({
|
|
97
|
+
liveQueryStore
|
|
98
|
+
});
|
|
99
|
+
} else if (options.liveQueries.store.redis) {
|
|
100
|
+
liveQueriesEnabled = true;
|
|
101
|
+
liveQueryStore = new RedisLiveQueryStore(
|
|
102
|
+
options.liveQueries.store.redis.publishClient,
|
|
103
|
+
options.liveQueries.store.redis.subscribeClient,
|
|
104
|
+
options.liveQueries.store.redis.channel || "live-query-invalidations",
|
|
105
|
+
inMemoryLiveQueryStore
|
|
106
|
+
);
|
|
107
|
+
liveQueryPlugin = (0, import_live_query.useLiveQuery)({
|
|
108
|
+
liveQueryStore
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
throw new Error("Invalid live query store configuration.");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (options.subscriptions) {
|
|
115
|
+
if (options.subscriptions.store === "in-memory") {
|
|
116
|
+
subscriptionsEnabled = true;
|
|
117
|
+
pubSub = (0, import_subscription.createPubSub)();
|
|
118
|
+
} else if (options.subscriptions.store.redis) {
|
|
119
|
+
subscriptionsEnabled = true;
|
|
120
|
+
const eventTarget = (0, import_redis_event_target.createRedisEventTarget)({
|
|
121
|
+
publishClient: options.subscriptions.store.redis.publishClient,
|
|
122
|
+
subscribeClient: options.subscriptions.store.redis.subscribeClient
|
|
123
|
+
});
|
|
124
|
+
pubSub = (0, import_subscription.createPubSub)({ eventTarget });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
onSchemaChange({ replaceSchema, schema }) {
|
|
129
|
+
if (schema.extensions?.[wasLiveQueryAdded] === true) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (liveQueriesEnabled) {
|
|
133
|
+
const liveSchema = (0, import_schema.mergeSchemas)({
|
|
134
|
+
schemas: [schema],
|
|
135
|
+
typeDefs: [liveDirectiveTypeDefs]
|
|
136
|
+
});
|
|
137
|
+
liveSchema.extensions = {
|
|
138
|
+
...schema.extensions,
|
|
139
|
+
[wasLiveQueryAdded]: true
|
|
140
|
+
};
|
|
141
|
+
replaceSchema(liveSchema);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
onPluginInit({ addPlugin }) {
|
|
145
|
+
if (liveQueriesEnabled) {
|
|
146
|
+
addPlugin(liveQueryPlugin);
|
|
147
|
+
}
|
|
148
|
+
if (subscriptionsEnabled) {
|
|
149
|
+
addPlugin((0, import_plugin_graphql_sse.useGraphQLSSE)());
|
|
150
|
+
}
|
|
151
|
+
if (options.enableDeferStream) {
|
|
152
|
+
addPlugin((0, import_plugin_defer_stream.useDeferStream)());
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
onContextBuilding() {
|
|
156
|
+
return ({ extendContext }) => {
|
|
157
|
+
extendContext({
|
|
158
|
+
liveQueryStore: liveQueriesEnabled && liveQueryStore,
|
|
159
|
+
pubSub: subscriptionsEnabled && pubSub
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
166
|
+
0 && (module.exports = {
|
|
167
|
+
InMemoryLiveQueryStore,
|
|
168
|
+
RedisLiveQueryStore,
|
|
169
|
+
Repeater,
|
|
170
|
+
createPubSub,
|
|
171
|
+
liveDirectiveTypeDefs,
|
|
172
|
+
liveQueryStore,
|
|
173
|
+
pubSub,
|
|
174
|
+
useRedwoodRealtime
|
|
175
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cedarjs/realtime",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/cedarjs/cedar.git",
|
|
7
|
+
"directory": "packages/realtime"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"exports": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsx ./build.mts && run build:types",
|
|
17
|
+
"build:pack": "yarn pack -o cedar-realtime.tgz",
|
|
18
|
+
"build:types": "tsc --build --verbose",
|
|
19
|
+
"build:watch": "nodemon --watch src --ext \"js,ts,tsx\" --ignore dist --exec \"yarn build\"",
|
|
20
|
+
"prepublishOnly": "NODE_ENV=production yarn build",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest watch"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@envelop/live-query": "7.0.0",
|
|
26
|
+
"@graphql-tools/schema": "10.0.6",
|
|
27
|
+
"@graphql-tools/utils": "10.5.4",
|
|
28
|
+
"@graphql-yoga/plugin-defer-stream": "3.7.0",
|
|
29
|
+
"@graphql-yoga/plugin-graphql-sse": "3.7.0",
|
|
30
|
+
"@graphql-yoga/redis-event-target": "3.0.1",
|
|
31
|
+
"@graphql-yoga/subscription": "5.0.1",
|
|
32
|
+
"@n1ru4l/graphql-live-query": "0.10.0",
|
|
33
|
+
"@n1ru4l/in-memory-live-query-store": "0.10.0",
|
|
34
|
+
"graphql": "16.9.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@cedarjs/framework-tools": "0.0.4",
|
|
38
|
+
"@envelop/core": "5.0.2",
|
|
39
|
+
"@envelop/testing": "7.0.0",
|
|
40
|
+
"@envelop/types": "5.0.0",
|
|
41
|
+
"ioredis": "5.6.0",
|
|
42
|
+
"nodemon": "3.1.9",
|
|
43
|
+
"tsx": "4.19.3",
|
|
44
|
+
"typescript": "5.6.2",
|
|
45
|
+
"vitest": "2.1.9"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"ioredis": "^5.3.2"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"ioredis": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"gitHead": "5b4f77f985bd86ee31ee7338312627accf0cb85b"
|
|
59
|
+
}
|