@kubun/with-relay 0.17.0 → 0.18.0

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/lib/index.d.ts CHANGED
@@ -1,10 +1,37 @@
1
+ import { type SubscriptionRecoveryOptions } from '@kubun/client';
1
2
  import type { GraphQLSource } from '@kubun/protocol';
2
- import { Environment, type INetwork, Store } from 'relay-runtime';
3
- export type NetworkParams = {
3
+ import { type CacheConfig, Environment, type INetwork, Store } from 'relay-runtime';
4
+ export type NetworkParams = SubscriptionRecoveryOptions & {
4
5
  source: GraphQLSource;
6
+ /**
7
+ * Makes the store consistent again after a subscription recovered: events sent while it
8
+ * was down are lost. Without it a subscription does not recover, and its interruption
9
+ * reaches the subscriber as an error.
10
+ */
11
+ rebaseline?: () => Promise<unknown>;
5
12
  };
6
- export type EnvironmentParams = NetworkParams & {
13
+ export type EnvironmentParams = Omit<NetworkParams, 'rebaseline'> & {
7
14
  store?: Store;
15
+ /**
16
+ * Makes the store consistent again after a subscription recovered. Defaults to
17
+ * invalidating the store, so each query is fetched again the next time it is read.
18
+ */
19
+ rebaseline?: (environment: Environment) => Promise<unknown>;
8
20
  };
21
+ /**
22
+ * The connector operation-envelope ids a mutation carries (see
23
+ * `GraphQLOperationParams.operationID`): required on a connector action
24
+ * mutation, the same pair on every retry of one user intent, a new pair for a
25
+ * new intent.
26
+ */
27
+ export type OperationEnvelope = {
28
+ operationID: string;
29
+ deterministicEventID: string;
30
+ };
31
+ /**
32
+ * The Relay `cacheConfig` that carries an operation envelope to the network
33
+ * layer, for a mutation commit (`commitMutation` / `useMutation`).
34
+ */
35
+ export declare function operationEnvelopeCacheConfig(envelope: OperationEnvelope): CacheConfig;
9
36
  export declare function createNetwork(params: NetworkParams): INetwork;
10
37
  export declare function createEnvironment(params: EnvironmentParams): Environment;
package/lib/index.js CHANGED
@@ -1,7 +1,34 @@
1
- import { Environment, Network, Observable, RecordSource, Store } from 'relay-runtime';
1
+ import { runRecoveringSubscription } from '@kubun/client';
2
+ import { commitLocalUpdate, Environment, Network, Observable, RecordSource, Store } from 'relay-runtime';
3
+ /**
4
+ * The Relay `cacheConfig` that carries an operation envelope to the network
5
+ * layer, for a mutation commit (`commitMutation` / `useMutation`).
6
+ */ export function operationEnvelopeCacheConfig(envelope) {
7
+ return {
8
+ metadata: {
9
+ operationID: envelope.operationID,
10
+ deterministicEventID: envelope.deterministicEventID
11
+ }
12
+ };
13
+ }
14
+ // The operation envelope a mutation's `cacheConfig` carries, each id only
15
+ // when it is a string.
16
+ function operationEnvelopeFrom(cacheConfig) {
17
+ const metadata = cacheConfig?.metadata;
18
+ const operationID = metadata?.operationID;
19
+ const deterministicEventID = metadata?.deterministicEventID;
20
+ return {
21
+ ...typeof operationID === 'string' ? {
22
+ operationID
23
+ } : {},
24
+ ...typeof deterministicEventID === 'string' ? {
25
+ deterministicEventID
26
+ } : {}
27
+ };
28
+ }
2
29
  export function createNetwork(params) {
3
- const { source } = params;
4
- return Network.create((request, variables)=>{
30
+ const { source, rebaseline, ...recovery } = params;
31
+ return Network.create((request, variables, cacheConfig)=>{
5
32
  const text = request.text;
6
33
  if (text == null) {
7
34
  return Observable.create((sink)=>{
@@ -12,7 +39,10 @@ export function createNetwork(params) {
12
39
  text,
13
40
  variables: variables
14
41
  };
15
- const promise = request.operationKind === 'mutation' ? source.mutate(params) : source.query(params);
42
+ const promise = request.operationKind === 'mutation' ? source.mutate({
43
+ ...params,
44
+ ...operationEnvelopeFrom(cacheConfig)
45
+ }) : source.query(params);
16
46
  return Observable.from(promise);
17
47
  }, (request, variables)=>{
18
48
  const text = request.text;
@@ -21,58 +51,35 @@ export function createNetwork(params) {
21
51
  sink.error(new Error('Relay request is missing query text'));
22
52
  });
23
53
  }
24
- return Observable.create((sink)=>{
25
- let disposed = false;
26
- let generator;
27
- source.subscribe({
28
- text,
29
- variables: variables
30
- }).then((result)=>{
31
- if (Symbol.asyncIterator in result) {
32
- generator = result;
33
- // Dispose may win the race against this resolution: close the
34
- // server subscription immediately rather than draining it forever.
35
- if (disposed) {
36
- void generator.return?.(undefined);
37
- return;
38
- }
39
- ;
40
- (async ()=>{
41
- try {
42
- for await (const value of generator){
43
- if (disposed) break;
44
- sink.next(value);
45
- }
46
- if (!disposed) {
47
- sink.complete();
48
- }
49
- } catch (error) {
50
- if (!disposed) {
51
- sink.error(error instanceof Error ? error : new Error(String(error)));
52
- }
53
- }
54
- })();
55
- return;
56
- }
57
- sink.next(result);
58
- sink.complete();
59
- }, (error)=>{
60
- sink.error(error instanceof Error ? error : new Error(String(error)));
61
- });
62
- // Closing the consumer generator propagates through the client and
63
- // transport to tear down the server-side subscription. The `disposed`
64
- // flag still guards sink.next/complete/error after teardown.
65
- return ()=>{
66
- disposed = true;
67
- void generator?.return?.(undefined);
68
- };
69
- });
54
+ const subscriptionParams = {
55
+ text,
56
+ variables: variables
57
+ };
58
+ // The returned close also wins a race against the subscribe resolving, and closing
59
+ // the consumer generator propagates through the client and transport to tear down
60
+ // the server-side subscription.
61
+ return Observable.create((sink)=>runRecoveringSubscription({
62
+ ...recovery,
63
+ subscribe: ()=>source.subscribe(subscriptionParams),
64
+ rebaseline,
65
+ next: (value)=>sink.next(value),
66
+ error: (error)=>sink.error(error),
67
+ complete: ()=>sink.complete()
68
+ }));
70
69
  });
71
70
  }
71
+ function invalidateStore(environment) {
72
+ commitLocalUpdate(environment, (store)=>store.invalidateStore());
73
+ return Promise.resolve();
74
+ }
72
75
  export function createEnvironment(params) {
73
- const { store, ...networkParams } = params;
74
- return new Environment({
75
- network: createNetwork(networkParams),
76
+ const { store, rebaseline = invalidateStore, ...networkParams } = params;
77
+ const environment = new Environment({
78
+ network: createNetwork({
79
+ ...networkParams,
80
+ rebaseline: ()=>rebaseline(environment)
81
+ }),
76
82
  store: store ?? new Store(new RecordSource())
77
83
  });
84
+ return environment;
78
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/with-relay",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,18 +15,23 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
+ "@kubun/client": "^0.18.0",
18
19
  "relay-runtime": "^21.0.1"
19
20
  },
20
21
  "devDependencies": {
21
- "@enkaku/transport": "^0.21.0",
22
- "@kubun/client": "^0.17.0",
23
- "@kubun/engine": "^0.17.0",
24
- "@kubun/models": "^0.17.0",
25
- "@kubun/plugin-service-server": "^0.17.0",
26
- "@kubun/protocol": "^0.17.0",
27
- "@kubun/service-graph-api": "^0.17.0",
22
+ "@enkaku/transport": "^0.21.5",
23
+ "@kubun/engine": "^0.18.0",
24
+ "@kubun/models-calendar": "^0.18.0",
25
+ "@kubun/models-contacts": "^0.18.0",
26
+ "@kubun/models-content": "^0.18.0",
27
+ "@kubun/models-core": "^0.18.0",
28
+ "@kubun/models-messaging": "^0.18.0",
29
+ "@kubun/models-tasks": "^0.18.0",
30
+ "@kubun/plugin-service-server": "^0.18.0",
31
+ "@kubun/protocol": "^0.18.0",
32
+ "@kubun/service-graph-api": "^0.18.0",
28
33
  "@kubun/test-utils": "^0.0.0",
29
- "graphql": "^16.14.2"
34
+ "graphql": "^17.0.2"
30
35
  },
31
36
  "publishConfig": {
32
37
  "access": "public"