@based/react 0.7.1 → 0.7.2

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/src/clients.ts ADDED
@@ -0,0 +1,160 @@
1
+ import React, {
2
+ createContext,
3
+ FunctionComponent,
4
+ ReactNode,
5
+ useContext,
6
+ useEffect,
7
+ useReducer,
8
+ } from 'react'
9
+
10
+ import based, { Based, BasedOpts } from '@based/client'
11
+
12
+ import { genOptsId } from './genOptsId'
13
+
14
+ export type CreateClient = (
15
+ selector: string | (BasedOpts & { key?: string })
16
+ ) => Based
17
+
18
+ const newClientListeners: Set<() => void> = new Set()
19
+
20
+ interface BasedContextType {
21
+ clients: { [key: string]: Based }
22
+ createClient?: CreateClient
23
+ removeClient: (
24
+ selector: string | (BasedOpts & { key?: string }) | Based
25
+ ) => void
26
+ }
27
+
28
+ export const BasedContext = createContext<BasedContextType>({
29
+ clients: {},
30
+ // eslint-disable-next-line
31
+ removeClient: (...args: any) => {},
32
+ })
33
+
34
+ export const defaultCreateClient: CreateClient = (selector) => {
35
+ if (typeof selector === 'object') {
36
+ if (
37
+ process.env.CLUSTER &&
38
+ process.env.CLUSTER.startsWith('local') &&
39
+ !selector.cluster
40
+ ) {
41
+ selector.cluster = process.env.CLUSTER
42
+ }
43
+ return based(selector)
44
+ } else {
45
+ // default
46
+ console.error('Cannot create client from ' + selector)
47
+ }
48
+ }
49
+
50
+ export const Provider: FunctionComponent<{
51
+ client?: Based
52
+ clients?: { [key: string]: Based }
53
+ children: ReactNode
54
+ createClient?: CreateClient
55
+ }> = ({ client, children, clients, createClient }) => {
56
+ if (!clients && client) {
57
+ clients = {
58
+ default: client,
59
+ }
60
+ } else if (clients && client) {
61
+ clients.default = client
62
+ }
63
+
64
+ const ctx = React.createElement(
65
+ BasedContext.Provider,
66
+ {
67
+ value: {
68
+ clients,
69
+ createClient: createClient || defaultCreateClient,
70
+ removeClient: (selector) => {
71
+ if (selector instanceof Based) {
72
+ for (const cl in clients) {
73
+ if (clients[cl] === selector) {
74
+ selector = cl
75
+ break
76
+ }
77
+ }
78
+ if (typeof selector !== 'string') {
79
+ console.error('Cannot find client to remove from ctx', selector)
80
+ return
81
+ }
82
+ } else if (typeof selector !== 'string') {
83
+ selector = genOptsId(selector)
84
+ }
85
+ // @ts-ignore
86
+ if (clients[selector]) {
87
+ // @ts-ignore
88
+ clients[selector].disconnect()
89
+ // @ts-ignore
90
+ delete clients[selector]
91
+ newClientListeners.forEach((fn) => fn())
92
+ }
93
+ },
94
+ },
95
+ },
96
+ children
97
+ )
98
+
99
+ return ctx
100
+ }
101
+
102
+ export const useBasedContext = () => {
103
+ return useContext(BasedContext)
104
+ }
105
+
106
+ function forceUpdate(state: number) {
107
+ return state + 1
108
+ }
109
+
110
+ export const useClients = (): Based[] => {
111
+ const ctx = useBasedContext()
112
+ const [, update] = useReducer(forceUpdate, 0)
113
+
114
+ useEffect(() => {
115
+ let timer
116
+ const fn = () => {
117
+ timer = setTimeout(update, 0)
118
+ }
119
+ newClientListeners.add(fn)
120
+ return () => {
121
+ newClientListeners.delete(fn)
122
+ clearTimeout(timer)
123
+ }
124
+ }, [])
125
+
126
+ return Object.values(ctx.clients)
127
+ }
128
+
129
+ export const useClient = (
130
+ selector: string | (BasedOpts & { key?: string }) = 'default'
131
+ ) => {
132
+ const basedCtx = useContext(BasedContext)
133
+
134
+ if (typeof selector === 'object') {
135
+ if (!(selector.env && selector.project && selector.org)) {
136
+ return
137
+ }
138
+ }
139
+
140
+ let key: string
141
+
142
+ if (typeof selector === 'string') {
143
+ key = selector
144
+ } else {
145
+ key = selector.key || genOptsId(selector)
146
+ }
147
+
148
+ let client: Based = basedCtx.clients[key]
149
+
150
+ if (!client && basedCtx.createClient) {
151
+ client = basedCtx.createClient(selector)
152
+
153
+ if (client) {
154
+ basedCtx.clients[key] = client
155
+ newClientListeners.forEach((fn) => fn())
156
+ }
157
+ }
158
+
159
+ return client
160
+ }
@@ -0,0 +1,13 @@
1
+ import { BasedOpts } from '@based/client'
2
+
3
+ export const genOptsId = (opts: BasedOpts & { key?: string }): string => {
4
+ if (!opts) {
5
+ return
6
+ }
7
+ if (opts.key) {
8
+ return opts.key
9
+ }
10
+ return `${opts.env}_${opts.project}_${opts.org}_${opts.cluster || ''}_${
11
+ opts.name || ''
12
+ }`
13
+ }
package/src/gql.ts ADDED
@@ -0,0 +1,210 @@
1
+ import { useEffect, useMemo, useReducer, useRef, useState } from 'react'
2
+ import {
3
+ addSubscriber,
4
+ removeSubscriber,
5
+ handleGraphqlVariables,
6
+ BasedGraphQL,
7
+ generateSubscriptionId,
8
+ BasedOpts,
9
+ } from '@based/client'
10
+ import { Loading, Data } from './types'
11
+ import { resultReducer } from './reducer'
12
+ import { useClient } from './clients'
13
+ import { genOptsId } from './genOptsId'
14
+ import { updateMeta } from './meta'
15
+ import { hashObjectIgnoreKeyOrder } from '@saulx/hash'
16
+
17
+ const schemaSubId = generateSubscriptionId({ $subscribe_schema: 'default' })
18
+
19
+ // step one make 1 useEffect
20
+ // - and the nessecary if
21
+
22
+ export function useQuery(
23
+ query?: string | BasedGraphQL,
24
+ variables: Record<string, any> = {},
25
+ clientSelector?: string | (BasedOpts & { key?: string })
26
+ ): {
27
+ data: Data
28
+ error?: Error
29
+ loading: Loading
30
+ } {
31
+ const [result, dispatch] = useReducer(resultReducer, {
32
+ loading: true,
33
+ data: {},
34
+ checksum: 0,
35
+ })
36
+
37
+ const r = useRef({ checksum: 0, fns: {} })
38
+ const selector = clientSelector || 'default'
39
+ const client = useClient(selector)
40
+
41
+ if (query) {
42
+ const [configState, updateConfigState] = useState(0)
43
+
44
+ useEffect(() => {
45
+ const [, subscriberId] = addSubscriber(
46
+ client.client,
47
+ // FIXME dont want too many updates plz this has to become 1 use effect
48
+ { $subscribe_schema: 'default' },
49
+ (d, checksum) => {
50
+ if (!client.client.configuration) {
51
+ client.client.configuration = { dbs: [], schema: {}, functions: {} }
52
+ }
53
+ client.client.configuration.schema.default = d
54
+ updateConfigState(checksum)
55
+ },
56
+ (err) => {
57
+ if (err) {
58
+ console.error(err)
59
+ }
60
+ },
61
+ (err) => {
62
+ console.error(err)
63
+ },
64
+ schemaSubId
65
+ )
66
+ return () => {
67
+ removeSubscriber(client.client, schemaSubId, subscriberId)
68
+ }
69
+ return () => {}
70
+ }, [])
71
+
72
+ if (configState) {
73
+ let op: BasedGraphQL
74
+ if (typeof query === 'string') {
75
+ op = client.gql(query)
76
+ } else {
77
+ op = query
78
+ }
79
+
80
+ op = handleGraphqlVariables(op, op, variables)
81
+
82
+ const fns: {
83
+ [key: string]: { name: string; payload: any; key: string }
84
+ } = {}
85
+ const queryObj: any = {}
86
+ for (const key in op.ops) {
87
+ if (op.ops[key].fnObserve) {
88
+ const { name, payload } = op.ops[key].fnObserve
89
+ fns[key] = { name: <string>name, payload, key }
90
+ continue
91
+ }
92
+ queryObj[key] = op.ops[key].get
93
+ }
94
+
95
+ const fnHash = useMemo(() => {
96
+ return hashObjectIgnoreKeyOrder(fns)
97
+ }, [fns])
98
+
99
+ const subId = useMemo(() => {
100
+ // fn?
101
+ return generateSubscriptionId(queryObj)
102
+ }, [queryObj])
103
+
104
+ const clientKey =
105
+ typeof selector === 'string' ? selector : genOptsId(selector)
106
+
107
+ if (client) {
108
+ const subKey = clientKey + subId
109
+
110
+ useEffect(() => {
111
+ const subs = []
112
+
113
+ for (const key in fns) {
114
+ subs.push(
115
+ addSubscriber(
116
+ client.client,
117
+ fns[key].payload,
118
+ (d, checksum) => {
119
+ updateMeta(subKey, false, false)
120
+ if (r.current.fns[key] !== checksum) {
121
+ r.current.fns[key] = checksum
122
+ dispatch({
123
+ merge: { [key]: d },
124
+ checksum: hashObjectIgnoreKeyOrder(r.current),
125
+ })
126
+ }
127
+ },
128
+ (err) => {
129
+ if (err) {
130
+ console.error(err)
131
+ dispatch({ error: err, loading: false })
132
+ }
133
+ },
134
+ (err) => {
135
+ console.error(err)
136
+ updateMeta(subKey, false, err)
137
+ dispatch({ error: err })
138
+ },
139
+ undefined,
140
+ fns[key].name
141
+ )
142
+ )
143
+ }
144
+
145
+ return () => {
146
+ for (const [subId, subscriberId] of subs) {
147
+ removeSubscriber(client.client, subId, subscriberId)
148
+ }
149
+ }
150
+ }, [fnHash])
151
+
152
+ useEffect(() => {
153
+ if (!configState) {
154
+ return
155
+ }
156
+ updateMeta(subKey, true, false)
157
+ const [, subscriberId] = addSubscriber(
158
+ client.client,
159
+ queryObj,
160
+ (d, checksum) => {
161
+ updateMeta(subKey, false, false)
162
+ if (r.current.checksum !== checksum) {
163
+ r.current.checksum = checksum
164
+ dispatch({
165
+ merge: d,
166
+ checksum: hashObjectIgnoreKeyOrder(r.current),
167
+ })
168
+ }
169
+ },
170
+ (err) => {
171
+ if (err) {
172
+ console.error(err)
173
+ updateMeta(subKey, false, err)
174
+ dispatch({ error: err, loading: false })
175
+ }
176
+ },
177
+ (err) => {
178
+ console.error(err)
179
+ updateMeta(subKey, false, err)
180
+ dispatch({ error: err })
181
+ },
182
+ subId
183
+ )
184
+ return () => {
185
+ updateMeta(subKey, false, false)
186
+ removeSubscriber(client.client, subId, subscriberId)
187
+ }
188
+ }, [subId, clientKey, configState])
189
+ } else {
190
+ useEffect(stubFn, [null, null])
191
+ }
192
+ } else {
193
+ useMemo(stubFn, [null])
194
+ useMemo(stubFn, [null])
195
+ useEffect(stubFn, [null])
196
+ useEffect(stubFn, [null, null, null])
197
+ }
198
+ } else {
199
+ useState(null)
200
+ useEffect(stubFn, [null])
201
+ useMemo(stubFn, [null])
202
+ useMemo(stubFn, [null])
203
+ useEffect(stubFn, [null])
204
+ useEffect(stubFn, [null, null, null])
205
+ }
206
+
207
+ return result
208
+ }
209
+
210
+ function stubFn() {}