@rpcbase/client 0.60.0 → 0.62.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/package.json CHANGED
@@ -1,20 +1,23 @@
1
1
  {
2
2
  "name": "@rpcbase/client",
3
- "version": "0.60.0",
3
+ "version": "0.62.0",
4
4
  "scripts": {
5
- "test": "echo \"Error: no test specified\" && exit 0"
5
+ "test": "jest"
6
6
  },
7
7
  "dependencies": {
8
8
  "axios": "1.4.0",
9
- "i18next": "23.2.11",
9
+ "i18next": "23.4.1",
10
10
  "i18next-chained-backend": "4.4.0",
11
11
  "i18next-resources-to-backend": "1.1.4",
12
12
  "lodash": "4.17.21",
13
+ "posthog-js": "1.75.3",
13
14
  "pouchdb-adapter-indexeddb": "8.0.1",
14
15
  "pouchdb-core": "8.0.1",
15
16
  "pouchdb-find": "8.0.1",
16
- "posthog-js": "1.70.0",
17
- "react-i18next": "13.0.1",
18
- "socket.io-client": "4.7.1"
17
+ "react-i18next": "13.0.3",
18
+ "socket.io-client": "4.7.2"
19
+ },
20
+ "devDependencies": {
21
+ "jest": "29.6.2"
19
22
  }
20
23
  }
package/rts/boot.js CHANGED
@@ -1,5 +1,3 @@
1
1
  /* @flow */
2
2
 
3
- console.log("RTS BOOT, TODO: connect")
4
-
5
3
  import "./store"
@@ -0,0 +1,23 @@
1
+ /* @flow */
2
+ import getUseQuery from "./getUseQuery"
3
+
4
+
5
+ const getUseDocument = (register_query) => (...args) => {
6
+
7
+ const useQuery = getUseQuery(register_query)
8
+
9
+ const res = useQuery(...args)
10
+
11
+ let data
12
+ // cache will always return [] when there are no matching documents, but we want null instead
13
+ if (Array.isArray(res.data) && res.data.length === 0 && res.source === "cache") {
14
+ data = null
15
+ } else if (Array.isArray(res.data) && res.data.length > 0) {
16
+ // TODO: should we throw if res.data.length > 1 ? ie: there are more than one matching document
17
+ data = res.data[0]
18
+ }
19
+
20
+ return {...res, data}
21
+ }
22
+
23
+ export default getUseDocument
@@ -11,7 +11,8 @@ import get_uid from "../auth/get_uid"
11
11
  import cacheStorage from "./cacheStorage"
12
12
 
13
13
 
14
- const log = debug("rb:useQuery")
14
+ const log = debug("rb:rts:useQuery")
15
+
15
16
 
16
17
  const getUseQuery = (register_query) => (
17
18
  model_name,
@@ -44,10 +45,8 @@ const getUseQuery = (register_query) => (
44
45
  sort = {},
45
46
  } = options
46
47
 
47
-
48
48
  const storageKey = `${uid}.${key}.${model_name}.${JSON.stringify(query)}.${JSON.stringify(projection)}.${JSON.stringify(sort)}`
49
49
 
50
-
51
50
  useEffect(() => {
52
51
  if (options.debug) {
53
52
  console.log("use query", model_name, query, options)
@@ -57,9 +56,10 @@ const getUseQuery = (register_query) => (
57
56
  useEffect(() => {
58
57
  const load = async() => {
59
58
  const val = await cacheStorage.get(storageKey)
60
- // console.log("got val from storage", val)
59
+ // TODO: rm this
61
60
  // always initially apply when first load here
62
61
  if (val) {
62
+ console.log("Will set val from cache storage")
63
63
  setData(val)
64
64
  setLoading(false)
65
65
  }
@@ -94,6 +94,7 @@ const getUseQuery = (register_query) => (
94
94
  }
95
95
 
96
96
  const start = Date.now()
97
+ log("will register query", model_name, query)
97
98
  const unsubscribe = register_query(model_name, query, {...options, key: queryKey}, (err, queryResult, context) => {
98
99
  log("callback answer with context", context, queryResult?.length)
99
100
 
@@ -170,6 +171,11 @@ const getUseQuery = (register_query) => (
170
171
 
171
172
  const result = useMemo(() => ({data, source, error, loading}), [data, source, error, loading])
172
173
 
174
+ // TODO: investigate this one
175
+ // if (Array.isArray(result.data) && !result.source) {
176
+ // console.warn("RESULT HAS NO SOURCE", {data, error, loading, source})
177
+ // }
178
+
173
179
  return result
174
180
  }
175
181
 
package/rts/index.js CHANGED
@@ -9,6 +9,7 @@ import BASE_URL from "../base_url"
9
9
  import {get_tenant_id} from "../auth"
10
10
 
11
11
  import store from "./store"
12
+ import getUseDocument from "./getUseDocument"
12
13
  import getUseQuery from "./getUseQuery"
13
14
 
14
15
  const log = debug("rb:socket")
@@ -219,3 +220,5 @@ export const register_query = (model_name, query, _options, _callback) => {
219
220
  }
220
221
 
221
222
  export const useQuery = getUseQuery(register_query)
223
+
224
+ export const useDocument = getUseDocument(register_query)
@@ -2,7 +2,7 @@
2
2
  import debug from "debug"
3
3
  import PouchDB from "pouchdb-core"
4
4
 
5
- if (debug.enabled("rb:store")) {
5
+ if (debug.enabled("rb:rts:store")) {
6
6
 
7
7
  const log = debug("rb:store:pouch")
8
8
 
@@ -6,7 +6,7 @@ import FindPlugin from "pouchdb-find"
6
6
 
7
7
  import {TENANT_PREFIX, DATABASE_NAME} from "env"
8
8
 
9
- const log = debug("rb:store")
9
+ const log = debug("rb:rts:store")
10
10
 
11
11
  let prefix = `rb/${TENANT_PREFIX}/`
12
12
 
@@ -5,36 +5,15 @@ import "./debug"
5
5
 
6
6
  import get_collection from "./get_collection"
7
7
  import update_docs from "./update_docs"
8
+ import satisfies_projection from "./satisfies_projection"
9
+
10
+ const log = debug("rb:rts:store")
8
11
 
9
- const log = debug("rb:store")
10
12
 
11
- // import updateDocument from "./updateDocument"
12
- // import {DATABASE_NAME} from "env"
13
- // let prefix = "rb/"
14
- // if (DATABASE_NAME) prefix += `${DATABASE_NAME}/`
15
- //
16
- // PouchDB.prefix = prefix
17
- //
18
- // PouchDB.plugin(IndexedDBAdapter)
19
- // PouchDB.plugin(FindPlugin)
20
- //
21
- //
22
- // let db = new PouchDB(`db/items`, { adapter: "indexeddb" });
23
- //
24
- // // Create a new document
25
- // let doc = {
26
- // _id: "001",
27
- // message: "Hello, World!"
28
- // }
29
- // //
30
- //
31
- // const run = async() => {
32
13
  // // https://github.com/pouchdb/pouchdb/tree/master/packages/node_modules/pouchdb-find#dbcreateindexindex--callback
33
14
  // const res = await db.createIndex({
34
15
  // index: {fields: ["name"]}
35
16
  // })
36
- //
37
- //
38
17
  // // Listen for changes on the database
39
18
  // const fn = db.changes({
40
19
  // since: "now",
@@ -53,78 +32,18 @@ const log = debug("rb:store")
53
32
  // console.log("GOT FN", fn)
54
33
  //
55
34
  // console.log("got res", res)
56
- //
57
- // const doc = await db.find({
58
- // selector: {name: "Mario"},
59
- // sort: ["name"]
60
- // })
61
- //
62
- // console.log("GT doc", doc)
63
- //
64
- // const {docs} = await db.find({selector: {}})
65
- //
66
- // console.log("got all docs", docs)
67
- //
68
- // db.put({
69
- // _id: "001-" + Date.now(),
70
- // message: "Hello, World!"
71
- // })
72
- // }
73
- //
74
- //
75
- // run()
76
-
77
- // // Insert the document into the database
78
- // db.put(doc, function(err, response) {
79
- // if (err) {
80
- // return console.log(err);
81
- // } else {
82
- // console.log("Document created successfully");
83
- // }
35
+ // _cols_store[col_name].getIndexes().then(function (result) {
36
+ // console.log("got indexes", result)
84
37
  // })
85
- //
86
- //
87
- // // Insert the document into the database
88
- // db.put({
89
- // _id: "hello world",
90
- // fieldVal: 12,
91
- // }, {force: true}, async function(err, response) {
92
- // if (err) {
93
- // console.log("errrro", err);
94
- // console.log("Stt", JSON.stringify(err))
95
- // return
96
- // } else {
97
- // console.log("Document created successfully", response);
98
- // }
99
- //
100
- // console.log("will try to find:")
101
- // const doc = await db.find({
102
- // selector: {
103
- // fieldVal: 10
104
- // }
105
- // })
106
- // console.log("LEDOC", doc)
107
- //
108
- // });
109
- //
110
- // setInterval(() => {
111
- // // Fetch the document
112
- // db.get("001", function(err, doc) {
113
- // if (err) {
114
- // return console.log(err);
115
- // } else {
116
- // console.log(doc);
117
- // }
118
- // });
119
- // }, 2000)
120
- //
121
38
 
122
- // TODO: should the store be in a worker or the main thread ?
123
39
 
40
+ // TODO: implement store in a shared worker
41
+ // TODO: should we filter all docs by projection ? or just the ones where the projection isn't complete ?
124
42
  const run_query = async({model_name, query, query_key, options}, callback) => {
125
- // console.log("ALAAARM")
126
43
  // console.log("run_query", {model_name, query, query_key, options})
127
44
  // console.time("store run_query")
45
+
46
+ // TODO: we should prefix model_name with tenant_prefix + env_id
128
47
  const col = get_collection(model_name)
129
48
 
130
49
  // https://github.com/pouchdb/pouchdb/tree/master/packages/node_modules/pouchdb-find#dbcreateindexindex--callback
@@ -136,18 +55,22 @@ const run_query = async({model_name, query, query_key, options}, callback) => {
136
55
  // fields: [""]
137
56
  })
138
57
 
139
- const mapped_docs = docs.map(({_rev, ...doc}) => {
140
- // TODO: handle projections here
141
- const remapped_doc = Object.entries(doc).reduce((new_doc, [key, value]) => {
142
- let new_key = key.startsWith('$_') ? key.replace(/^\$_/, "") : key
143
- new_doc[new_key] = value
144
- return new_doc
145
- }, {})
58
+ let mapped_docs = docs
59
+ .map(({_rev, ...doc}) => {
60
+ // TODO: handle projections here
61
+ const remapped_doc = Object.entries(doc).reduce((new_doc, [key, value]) => {
62
+ let new_key = key.startsWith('$_') ? key.replace(/^\$_/, "") : key
63
+ new_doc[new_key] = value
64
+ return new_doc
65
+ }, {})
66
+
67
+ return remapped_doc
68
+ })
146
69
 
147
- return remapped_doc
148
- })
149
70
 
150
- // console.timeEnd("store run_query")
71
+ if (options.projection) {
72
+ mapped_docs = mapped_docs.filter((doc) => satisfies_projection(doc, options.projection))
73
+ }
151
74
 
152
75
  callback(null, mapped_docs, {source: "cache"})
153
76
  }
@@ -0,0 +1,32 @@
1
+ /* @flow */
2
+
3
+ const get_keys = (obj, parent_key = '') => {
4
+ return Object.keys(obj).reduce((acc, key) => {
5
+ const new_key = parent_key ? `${parent_key}.${key}` : key
6
+ if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
7
+ acc.push(...get_keys(obj[key], new_key))
8
+ } else {
9
+ acc.push(new_key)
10
+ }
11
+ return acc
12
+ }, [])
13
+ }
14
+
15
+ const set_contains_set = (set_a, set_b) => {
16
+ if (set_b.size > set_a.size) return false
17
+ for (const b of set_b) if (!set_a.has(b)) return false
18
+ return true
19
+ }
20
+
21
+ const satisfies_projection = (doc, projection) => {
22
+ const doc_keys = new Set(get_keys(doc))
23
+ const projection_keys = new Set(Object.keys(projection).filter(k => projection[k] === 1))
24
+
25
+ if (!projection_keys.has('_id')) {
26
+ doc_keys.delete('_id')
27
+ }
28
+
29
+ return set_contains_set(doc_keys, projection_keys)
30
+ }
31
+
32
+ module.exports = satisfies_projection
@@ -0,0 +1,42 @@
1
+ /* @flow */
2
+ const satisfies_projection = require("./satisfies_projection")
3
+
4
+
5
+ let doc = {
6
+ _id: '1',
7
+ field1: 'value1',
8
+ field2: {
9
+ subField1: 'value2',
10
+ subField2: 'value3'
11
+ }
12
+ }
13
+
14
+ let projection = {
15
+ field1: 1,
16
+ 'field2.subField1': 1,
17
+ 'field2.subField2': 1
18
+ }
19
+
20
+
21
+
22
+ test("simple", () => {
23
+ expect(satisfies_projection(doc, projection)).toBe(true)
24
+ })
25
+
26
+ test("missing field", () => {
27
+ expect(satisfies_projection(doc, {
28
+ missing_field: 1,
29
+ ...projection
30
+ })).toBe(false)
31
+ })
32
+
33
+ test("missing nested field", () => {
34
+ expect(satisfies_projection(doc, {
35
+ "missing_field.missing_nested": 1,
36
+ ...projection
37
+ })).toBe(false)
38
+ })
39
+
40
+ test("empty", () => {
41
+ expect(satisfies_projection(doc, {field1: 1})).toBe(true)
42
+ })