@based/react 4.1.0 → 4.2.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 DELETED
@@ -1,69 +0,0 @@
1
- # @based/react
2
-
3
- React hooks for the based data platform
4
-
5
- > [Example react project here](https://github.com/atelier-saulx/based-react-sample)
6
-
7
- > Read more about [based](https://github.com/atelier-saulx/based/blob/main/packages/client/README.md)
8
-
9
- ---
10
-
11
- ```js
12
- import based from "@based/client"
13
- import { Provider, useData, useClient } from "@based/react"
14
- import React from "react"
15
- import ReactDom from "react-dom"
16
-
17
- const client = based({ env: "prod", project: "someproject", org: "a-org" })
18
-
19
- const Things = () => {
20
- const client = useClient()
21
-
22
- // automatically observes data when components get rendered
23
- const { data, loading, error } = useData({
24
- things: {
25
- id: true,
26
- name: true,
27
- $list: true,
28
- },
29
- })
30
-
31
- if (loading) {
32
- return <div>loading...</div>
33
- }
34
-
35
- return (
36
- <div>
37
- {data.things.map((thing) => (
38
- <div
39
- key={thing.id}
40
- onClick={(e) => {
41
- client.set({ $id: thing.id, name: "Some random name!" })
42
- }}
43
- >
44
- {thing.name}
45
- </div>
46
- ))}
47
- </div>
48
- )
49
- }
50
-
51
- const App = () => {
52
- return (
53
- <Provider client={client}>
54
- <Things />
55
- </Provider>
56
- )
57
- }
58
-
59
- ReactDom.render(<App />, document.body)
60
- ```
61
-
62
-
63
- ---
64
-
65
- ## License
66
-
67
- Licensed under the MIT License.
68
-
69
- See [LICENSE](./LICENSE) for more information.
package/build/index.js DELETED
@@ -1,32 +0,0 @@
1
- const { build } = require('esbuild')
2
- const { join } = require('path')
3
-
4
- const b = async () => {
5
- // make a seperate build for stream for both node and the index
6
-
7
- const makeAllPackagesExternalPlugin = {
8
- name: 'make-all-packages-external',
9
- setup(build) {
10
- // eslint-disable-next-line
11
- const filter = /(\.\/stream)|(^[^.\/]|^\.[^.\/]|^\.\.[^\/])/ // Must not start with "/" or "./" or "../"
12
- build.onResolve({ filter }, (args) => {
13
- return {
14
- path: args.path,
15
- external: true,
16
- }
17
- })
18
- },
19
- }
20
-
21
- build({
22
- outfile: join(__dirname, '../dist/index.js'),
23
- bundle: true,
24
- minify: true,
25
- target: 'es2017',
26
- platform: 'node',
27
- entryPoints: [join(__dirname, '../src/index.ts')],
28
- plugins: [makeAllPackagesExternalPlugin],
29
- })
30
- }
31
-
32
- b()
package/src/clients.ts DELETED
@@ -1,161 +0,0 @@
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
- typeof process !== 'undefined' &&
38
- process.env.CLUSTER &&
39
- process.env.CLUSTER.startsWith('local') &&
40
- !selector.cluster
41
- ) {
42
- selector.cluster = process.env.CLUSTER
43
- }
44
- return based(selector)
45
- } else {
46
- // default
47
- console.error('Cannot create client from ' + selector)
48
- }
49
- }
50
-
51
- export const Provider: FunctionComponent<{
52
- client?: Based
53
- clients?: { [key: string]: Based }
54
- children: ReactNode
55
- createClient?: CreateClient
56
- }> = ({ client, children, clients, createClient }) => {
57
- if (!clients && client) {
58
- clients = {
59
- default: client,
60
- }
61
- } else if (clients && client) {
62
- clients.default = client
63
- }
64
-
65
- const ctx = React.createElement(
66
- BasedContext.Provider,
67
- {
68
- value: {
69
- clients,
70
- createClient: createClient || defaultCreateClient,
71
- removeClient: (selector) => {
72
- if (selector instanceof Based) {
73
- for (const cl in clients) {
74
- if (clients[cl] === selector) {
75
- selector = cl
76
- break
77
- }
78
- }
79
- if (typeof selector !== 'string') {
80
- console.error('Cannot find client to remove from ctx', selector)
81
- return
82
- }
83
- } else if (typeof selector !== 'string') {
84
- selector = genOptsId(selector)
85
- }
86
- // @ts-ignore
87
- if (clients[selector]) {
88
- // @ts-ignore
89
- clients[selector].disconnect()
90
- // @ts-ignore
91
- delete clients[selector]
92
- newClientListeners.forEach((fn) => fn())
93
- }
94
- },
95
- },
96
- },
97
- children
98
- )
99
-
100
- return ctx
101
- }
102
-
103
- export const useBasedContext = () => {
104
- return useContext(BasedContext)
105
- }
106
-
107
- function forceUpdate(state: number) {
108
- return state + 1
109
- }
110
-
111
- export const useClients = (): Based[] => {
112
- const ctx = useBasedContext()
113
- const [, update] = useReducer(forceUpdate, 0)
114
-
115
- useEffect(() => {
116
- let timer
117
- const fn = () => {
118
- timer = setTimeout(update, 0)
119
- }
120
- newClientListeners.add(fn)
121
- return () => {
122
- newClientListeners.delete(fn)
123
- clearTimeout(timer)
124
- }
125
- }, [])
126
-
127
- return Object.values(ctx.clients)
128
- }
129
-
130
- export const useClient = (
131
- selector: string | (BasedOpts & { key?: string }) = 'default'
132
- ) => {
133
- const basedCtx = useContext(BasedContext)
134
-
135
- if (typeof selector === 'object') {
136
- if (!(selector.env && selector.project && selector.org)) {
137
- return
138
- }
139
- }
140
-
141
- let key: string
142
-
143
- if (typeof selector === 'string') {
144
- key = selector
145
- } else {
146
- key = selector.key || genOptsId(selector)
147
- }
148
-
149
- let client: Based = basedCtx.clients[key]
150
-
151
- if (!client && basedCtx.createClient) {
152
- client = basedCtx.createClient(selector)
153
-
154
- if (client) {
155
- basedCtx.clients[key] = client
156
- newClientListeners.forEach((fn) => fn())
157
- }
158
- }
159
-
160
- return client
161
- }
@@ -1,182 +0,0 @@
1
- import {
2
- addSubscriber,
3
- removeSubscriber,
4
- generateSubscriptionId,
5
- } from '@based/client'
6
- import React, { useRef, useState, useEffect } from 'react'
7
- import { FixedSizeList as List } from 'react-window'
8
- import { useData, useClient } from '../..'
9
-
10
- const addScrollSub = (client, subId, payload, offset, current, setChecksum) => {
11
- const [, subscriberId] = addSubscriber(
12
- client.client,
13
- payload,
14
- ({ items }, checksum) => {
15
- for (let i = 0; i < items.length; i++) {
16
- current.items[i + offset] = items[i]
17
- }
18
- setChecksum(`${offset}-${checksum}`)
19
- },
20
- (err) => err && console.error(err),
21
- console.error,
22
- subId
23
- )
24
- return subscriberId
25
- }
26
-
27
- export function InfiniteList({
28
- query,
29
- delay = 100,
30
- itemSize = 50,
31
- height = 400,
32
- limit = Math.ceil(height / itemSize),
33
- treshold = 0,
34
- target = 'root',
35
- ...props
36
- }) {
37
- const blockHeight = itemSize * limit
38
- const client = useClient()
39
- const [offset, setOffset] = useState(0)
40
- const [blocks, setBlocks] = useState(() => {
41
- let blocks = Math.ceil(height / blockHeight)
42
- if (treshold) {
43
- blocks += Math.ceil(
44
- (height / itemSize + treshold - blocks * limit) / limit
45
- )
46
- }
47
- return blocks
48
- })
49
-
50
- const [, setChecksum] = useState()
51
- const { current } = useRef({
52
- offset,
53
- blocks,
54
- scroll: 0,
55
- items: [],
56
- timer: null,
57
- subs: {},
58
- })
59
- const {
60
- data: { itemCount },
61
- } = useData({
62
- itemCount: {
63
- $aggregate: {
64
- $function: 'count',
65
- ...query().$list.$find,
66
- },
67
- },
68
- })
69
-
70
- useEffect(() => {
71
- if (client) {
72
- return () => {
73
- const { subs } = current
74
- current.subs = {}
75
- setTimeout(() => {
76
- for (const subId in subs) {
77
- const subscriberId = current.subs[subId]
78
- removeSubscriber(client.client, Number(subId), subscriberId)
79
- }
80
- })
81
- }
82
- }
83
- }, [client, current])
84
-
85
- useEffect(() => {
86
- if (client) {
87
- const subs = {}
88
- let i = blocks
89
-
90
- while (i--) {
91
- const start = offset + limit * i
92
- const payload = {
93
- $id: target,
94
- items: query(start, limit),
95
- }
96
- const subId = generateSubscriptionId(payload)
97
- subs[subId] =
98
- current.subs[subId] ||
99
- addScrollSub(client, subId, payload, start, current, setChecksum)
100
- }
101
-
102
- for (const subId in current.subs) {
103
- if (!(subId in subs)) {
104
- const subscriberId = current.subs[subId]
105
- removeSubscriber(client.client, Number(subId), subscriberId)
106
- }
107
- }
108
-
109
- current.subs = subs
110
- }
111
- }, [target, client, offset, blocks, query, current]) // dont include limit
112
-
113
- useEffect(update, [
114
- blockHeight,
115
- delay,
116
- height,
117
- itemSize,
118
- current,
119
- limit,
120
- treshold,
121
- ])
122
-
123
- if (!itemCount || !current.items.length) {
124
- return null
125
- }
126
-
127
- return (
128
- <List
129
- {...props}
130
- height={height}
131
- itemSize={itemSize}
132
- itemData={current.items}
133
- itemCount={itemCount}
134
- onScroll={({ scrollOffset }) => {
135
- current.scroll = scrollOffset
136
- update()
137
- }}
138
- />
139
- )
140
-
141
- function update() {
142
- const start = Math.max(0, current.scroll / itemSize - treshold)
143
- const end = (current.scroll + height) / itemSize
144
- const newOffset = start - (start % limit)
145
- let newBlocks = Math.ceil(
146
- height / blockHeight + (current.scroll % blockHeight) / blockHeight
147
- )
148
-
149
- if (treshold) {
150
- const newLength = newOffset + limit * newBlocks
151
- newBlocks += Math.max(0, Math.ceil((end + treshold - newLength) / limit))
152
- }
153
-
154
- if (newOffset !== current.offset || newBlocks !== current.blocks) {
155
- current.offset = newOffset
156
- current.blocks = newBlocks
157
-
158
- const set = () => {
159
- setOffset(current.offset)
160
- setBlocks(current.blocks)
161
- }
162
-
163
- if (current.timer) {
164
- clearTimeout(current.timer)
165
- } else {
166
- set()
167
- }
168
-
169
- current.timer = setTimeout(() => {
170
- current.timer = null
171
- set()
172
- }, delay)
173
- } else if (current.timer) {
174
- clearTimeout(current.timer)
175
- current.timer = setTimeout(() => {
176
- current.timer = null
177
- setOffset(current.offset)
178
- setBlocks(current.blocks)
179
- }, delay)
180
- }
181
- }
182
- }
package/src/genOptsId.ts DELETED
@@ -1,13 +0,0 @@
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 DELETED
@@ -1,214 +0,0 @@
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 = {
52
- dbs: [],
53
- schema: {},
54
- functions: {},
55
- } as any // TODO: FIX
56
- }
57
- client.client.configuration.schema.default = d
58
- updateConfigState(checksum)
59
- },
60
- (err) => {
61
- if (err) {
62
- console.error(err)
63
- }
64
- },
65
- (err) => {
66
- console.error(err)
67
- },
68
- schemaSubId
69
- )
70
- return () => {
71
- removeSubscriber(client.client, schemaSubId, subscriberId)
72
- }
73
- return () => {}
74
- }, [])
75
-
76
- if (configState) {
77
- let op: BasedGraphQL
78
- if (typeof query === 'string') {
79
- op = client.gql(query)
80
- } else {
81
- op = query
82
- }
83
-
84
- op = handleGraphqlVariables(op, op, variables)
85
-
86
- const fns: {
87
- [key: string]: { name: string; payload: any; key: string }
88
- } = {}
89
- const queryObj: any = {}
90
- for (const key in op.ops) {
91
- if (op.ops[key].fnObserve) {
92
- const { name, payload } = op.ops[key].fnObserve
93
- fns[key] = { name: <string>name, payload, key }
94
- continue
95
- }
96
- queryObj[key] = op.ops[key].get
97
- }
98
-
99
- const fnHash = useMemo(() => {
100
- return hashObjectIgnoreKeyOrder(fns)
101
- }, [fns])
102
-
103
- const subId = useMemo(() => {
104
- // fn?
105
- return generateSubscriptionId(queryObj)
106
- }, [queryObj])
107
-
108
- const clientKey =
109
- typeof selector === 'string' ? selector : genOptsId(selector)
110
-
111
- if (client) {
112
- const subKey = clientKey + subId
113
-
114
- useEffect(() => {
115
- const subs = []
116
-
117
- for (const key in fns) {
118
- subs.push(
119
- addSubscriber(
120
- client.client,
121
- fns[key].payload,
122
- (d, checksum) => {
123
- updateMeta(subKey, false, false)
124
- if (r.current.fns[key] !== checksum) {
125
- r.current.fns[key] = checksum
126
- dispatch({
127
- merge: { [key]: d },
128
- checksum: hashObjectIgnoreKeyOrder(r.current),
129
- })
130
- }
131
- },
132
- (err) => {
133
- if (err) {
134
- console.error(err)
135
- dispatch({ error: err, loading: false })
136
- }
137
- },
138
- (err) => {
139
- console.error(err)
140
- updateMeta(subKey, false, err)
141
- dispatch({ error: err })
142
- },
143
- undefined,
144
- fns[key].name
145
- )
146
- )
147
- }
148
-
149
- return () => {
150
- for (const [subId, subscriberId] of subs) {
151
- removeSubscriber(client.client, subId, subscriberId)
152
- }
153
- }
154
- }, [fnHash])
155
-
156
- useEffect(() => {
157
- if (!configState) {
158
- return
159
- }
160
- updateMeta(subKey, true, false)
161
- const [, subscriberId] = addSubscriber(
162
- client.client,
163
- queryObj,
164
- (d, checksum) => {
165
- updateMeta(subKey, false, false)
166
- if (r.current.checksum !== checksum) {
167
- r.current.checksum = checksum
168
- dispatch({
169
- merge: d,
170
- checksum: hashObjectIgnoreKeyOrder(r.current),
171
- })
172
- }
173
- },
174
- (err) => {
175
- if (err) {
176
- console.error(err)
177
- updateMeta(subKey, false, err)
178
- dispatch({ error: err, loading: false })
179
- }
180
- },
181
- (err) => {
182
- console.error(err)
183
- updateMeta(subKey, false, err)
184
- dispatch({ error: err })
185
- },
186
- subId
187
- )
188
- return () => {
189
- updateMeta(subKey, false, false)
190
- removeSubscriber(client.client, subId, subscriberId)
191
- }
192
- }, [subId, clientKey, configState])
193
- } else {
194
- useEffect(stubFn, [null, null])
195
- }
196
- } else {
197
- useMemo(stubFn, [null])
198
- useMemo(stubFn, [null])
199
- useEffect(stubFn, [null])
200
- useEffect(stubFn, [null, null, null])
201
- }
202
- } else {
203
- useState(null)
204
- useEffect(stubFn, [null])
205
- useMemo(stubFn, [null])
206
- useMemo(stubFn, [null])
207
- useEffect(stubFn, [null])
208
- useEffect(stubFn, [null, null, null])
209
- }
210
-
211
- return result
212
- }
213
-
214
- function stubFn() {}