@basictech/react 0.2.0-beta.3 → 0.2.0-beta.5

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.
@@ -3,23 +3,53 @@
3
3
  import React, { createContext, useContext, useEffect, useState, useRef } from 'react'
4
4
  import { jwtDecode } from 'jwt-decode'
5
5
 
6
-
7
-
8
6
  import { BasicSync } from './sync'
9
7
  import { get, add, update, deleteRecord } from './db'
10
8
 
9
+ import { validator, log } from './config'
10
+
11
+ /*
12
+ schema todo:
13
+ field types
14
+ array types
15
+ relations
16
+ */
17
+
18
+
19
+ // const example = {
20
+ // project_id: '123',
21
+ // version: 0,
22
+ // tables: {
23
+ // example: {
24
+ // name: 'example',
25
+ // type: 'collection',
26
+ // fields: {
27
+ // id: {
28
+ // type: 'uuid',
29
+ // primary: true,
30
+ // },
31
+ // value: {
32
+ // type: 'string',
33
+ // indexed: true,
34
+ // },
35
+ // }
36
+ // }
37
+ // }
38
+ // }
39
+
40
+
11
41
  type BasicSyncType = {
12
42
  basic_schema: any;
13
43
  connect: (options: { access_token: string }) => void;
14
44
  debugeroo: () => void;
15
45
  collection: (name: string) => {
16
- ref: {
17
- toArray: () => Promise<any[]>;
18
- count: () => Promise<number>;
19
- };
46
+ ref: {
47
+ toArray: () => Promise<any[]>;
48
+ count: () => Promise<number>;
49
+ };
20
50
  };
21
51
  [key: string]: any; // For other potential methods and properties
22
- };
52
+ };
23
53
 
24
54
 
25
55
  enum DBStatus {
@@ -56,7 +86,7 @@ export const BasicContext = createContext<{
56
86
  signin: () => void,
57
87
  getToken: () => Promise<string>,
58
88
  getSignInLink: () => string,
59
- db: any,
89
+ db: any,
60
90
  dbStatus: DBStatus
61
91
  }>({
62
92
  unicorn: "🦄",
@@ -103,6 +133,12 @@ function getSyncStatus(statusCode: number): string {
103
133
  }
104
134
  }
105
135
 
136
+ type ErrorObject = {
137
+ code: string;
138
+ title: string;
139
+ message: string;
140
+ }
141
+
106
142
  export function BasicProvider({ children, project_id, schema }: { children: React.ReactNode, project_id: string, schema: any }) {
107
143
  const [isLoaded, setIsLoaded] = useState(false)
108
144
  const [isSignedIn, setIsSignedIn] = useState(false)
@@ -110,30 +146,53 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
110
146
  const [authCode, setAuthCode] = useState<string | null>(null)
111
147
  const [user, setUser] = useState<User>({})
112
148
 
113
- const [dbStatus, setDbStatus] = useState<DBStatus>(DBStatus.OFFLINE)
114
-
149
+ const [dbStatus, setDbStatus] = useState<DBStatus>(DBStatus.LOADING)
115
150
 
116
151
  const syncRef = useRef<BasicSync | null>(null);
117
152
 
153
+ const [error, setError] = useState<ErrorObject | null>(null)
154
+
118
155
  useEffect(() => {
119
- if (!syncRef.current) {
120
- syncRef.current = new BasicSync('basicdb', { schema: schema });
156
+ function initDb() {
157
+ if (!validator(schema)) {
158
+ console.error('Basic Schema is invalid!', validator.errors)
159
+ console.group('Schema Errors')
160
+ let errorMessage = ''
161
+ validator.errors.forEach((error, index) => {
162
+ console.log(`${index + 1}:`, error.message, ` - at ${error.instancePath}`)
163
+ errorMessage += `${index + 1}: ${error.message} - at ${error.instancePath}\n`
164
+ })
165
+ console.groupEnd('Schema Errors')
166
+ setError({
167
+ code: 'schema_invalid',
168
+ title: 'Basic Schema is invalid!',
169
+ message: errorMessage
170
+ })
171
+ return null
172
+ }
121
173
 
122
- // console.log('db is open', syncRef.current.isOpen())
123
- // syncRef.current.open()
124
- // .then(() => {
125
- // console.log("is open now:", syncRef.current.isOpen())
126
- // })
127
174
 
128
- syncRef.current.handleStatusChange((status: number, url: string) => {
129
- setDbStatus(getSyncStatus(status))
130
- })
175
+ if (!syncRef.current) {
176
+ syncRef.current = new BasicSync('basicdb', { schema: schema });
131
177
 
132
- syncRef.current.syncable.getStatus().then((status) => {
133
- console.log('sync status', getSyncStatus(status))
134
- })
178
+ // console.log('db is open', syncRef.current.isOpen())
179
+ // syncRef.current.open()
180
+ // .then(() => {
181
+ // console.log("is open now:", syncRef.current.isOpen())
182
+ // })
183
+
184
+ syncRef.current.handleStatusChange((status: number, url: string) => {
185
+ setDbStatus(getSyncStatus(status))
186
+ })
187
+
188
+ syncRef.current.syncable.getStatus().then((status) => {
189
+ console.log('sync status', getSyncStatus(status))
190
+ })
191
+ }
135
192
 
136
193
  }
194
+
195
+ initDb()
137
196
  }, []);
138
197
 
139
198
 
@@ -153,7 +212,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
153
212
  }
154
213
 
155
214
  useEffect(() => {
156
- if (token) {
215
+ if (token && syncRef.current) {
157
216
  connectToDb()
158
217
  }
159
218
  }, [token])
@@ -163,8 +222,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
163
222
 
164
223
  const randomState = Math.random().toString(36).substring(7);
165
224
 
166
- // let baseUrl = "https://api.basic.tech/auth/authorize"
167
- let baseUrl = "http://localhost:3003/auth/authorize"
225
+ let baseUrl = "https://api.basic.tech/auth/authorize"
168
226
  baseUrl += `?client_id=${project_id}`
169
227
  baseUrl += `&redirect_uri=${encodeURIComponent(window.location.href)}`
170
228
  baseUrl += `&response_type=code`
@@ -366,11 +424,39 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
366
424
  db: syncRef.current,
367
425
  dbStatus
368
426
  }}>
427
+ {error && <ErrorDisplay error={error} />}
369
428
  {syncRef.current ? children : null}
370
429
  </BasicContext.Provider>
371
430
  )
372
431
  }
373
432
 
433
+ function ErrorDisplay({ error }: { error: ErrorObject }) {
434
+ return <div style={{
435
+ position: 'absolute',
436
+ top: 20,
437
+ left: 20,
438
+ color: 'black',
439
+ backgroundColor: '#f8d7da',
440
+ border: '1px solid #f5c6cb',
441
+ borderRadius: '4px',
442
+ padding: '20px',
443
+ maxWidth: '400px',
444
+ margin: '20px auto',
445
+ boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
446
+ fontFamily: 'monospace',
447
+ }}>
448
+ <h3 style={{fontSize: '0.8rem', opacity: 0.8}}>code: {error.code}</h3>
449
+ <h1 style={{fontSize: '1.2rem', lineHeight: '1.5'}}>{error.title}</h1>
450
+ <p>{error.message}</p>
451
+ </div>
452
+ }
453
+
454
+ /*
455
+ possible errors:
456
+ - projectid missing / invalid
457
+ - schema missing / invalid
458
+ */
459
+
374
460
  export function useBasic() {
375
461
  return useContext(BasicContext);
376
462
  }
package/src/config.ts ADDED
@@ -0,0 +1,73 @@
1
+ import Ajv from 'ajv'
2
+
3
+ export const SERVER_URL = "https://api.basic.tech"
4
+ // export const WS_URL = `${SERVER_URL}/ws`
5
+
6
+ export const log = (...args: any[]) => {
7
+ if (process.env.NODE_ENV === 'development') {
8
+ console.log(...args)
9
+ }
10
+ }
11
+
12
+
13
+ const basicJsonSchema = {
14
+ "$schema": "http://json-schema.org/draft-07/schema#",
15
+ "type": "object",
16
+ "properties": {
17
+ "project_id": {
18
+ "type": "string"
19
+ },
20
+ "namespace": {
21
+ "type": "string",
22
+ },
23
+ "version": {
24
+ "type": "integer",
25
+ "minimum": 0
26
+ },
27
+ "tables": {
28
+ "type": "object",
29
+ "patternProperties": {
30
+ "^[a-zA-Z0-9_]+$": {
31
+ "type": "object",
32
+ "properties": {
33
+ "name": {
34
+ "type": "string"
35
+ },
36
+ "type": {
37
+ "type": "string",
38
+ "enum": ["collection"]
39
+ },
40
+ "fields": {
41
+ "type": "object",
42
+ "patternProperties": {
43
+ "^[a-zA-Z0-9_]+$": {
44
+ "type": "object",
45
+ "properties": {
46
+ "type": {
47
+ "type": "string"
48
+ },
49
+ "primary": {
50
+ "type": "boolean"
51
+ },
52
+ "indexed": {
53
+ "type": "boolean"
54
+ }
55
+ },
56
+ "required": ["type"]
57
+ }
58
+ },
59
+ "additionalProperties": true
60
+ }
61
+ },
62
+ "required": ["fields"]
63
+ }
64
+ },
65
+ "additionalProperties": true
66
+ }
67
+ },
68
+ "required": ["project_id", "version", "tables"]
69
+ }
70
+
71
+
72
+ const ajv = new Ajv()
73
+ export const validator = ajv.compile(basicJsonSchema)
package/src/sync/index.ts CHANGED
@@ -1,21 +1,27 @@
1
+ "use client"
2
+
1
3
  import { v7 as uuidv7 } from 'uuid';
2
4
  import { Dexie, PromiseExtended } from 'dexie';
3
- import 'dexie-observable';
5
+ // if (typeof window !== 'undefined') {
6
+ // import('dexie-observable');
7
+ // }
4
8
  import 'dexie-syncable';
9
+ import 'dexie-observable';
5
10
 
6
11
  import { syncProtocol } from './syncProtocol'
7
-
12
+ import { SERVER_URL } from '../config'
8
13
  syncProtocol()
9
14
 
10
15
 
11
- const DexieSyncStatus = {
12
- "-1": "ERROR",
13
- "0": "OFFLINE",
14
- "1": "CONNECTING",
15
- "2": "ONLINE",
16
- "3": "SYNCING",
17
- "4": "ERROR_WILL_RETRY"
18
- }
16
+ // const DexieSyncStatus = {
17
+ // "-1": "ERROR",
18
+ // "0": "OFFLINE",
19
+ // "1": "CONNECTING",
20
+ // "2": "ONLINE",
21
+ // "3": "SYNCING",
22
+ // "4": "ERROR_WILL_RETRY"
23
+ // }
24
+
19
25
 
20
26
 
21
27
 
@@ -52,7 +58,8 @@ export class BasicSync extends Dexie {
52
58
  }
53
59
 
54
60
  async connect({ access_token }: { access_token: string }) {
55
- const WS_URL = "ws://localhost:3003/ws"
61
+ // const WS_URL = "ws://localhost:3003/ws"
62
+ const WS_URL = `${SERVER_URL}/ws`
56
63
 
57
64
 
58
65
  // Update sync nodes