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

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,61 @@
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
+ example2: {
38
+ name: 'example2',
39
+ type: 'collection',
40
+ fields: {
41
+ id: { type: 'string', primary: true },
42
+ id: { type: 'string', primary: true },
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+
11
49
  type BasicSyncType = {
12
50
  basic_schema: any;
13
51
  connect: (options: { access_token: string }) => void;
14
52
  debugeroo: () => void;
15
53
  collection: (name: string) => {
16
- ref: {
17
- toArray: () => Promise<any[]>;
18
- count: () => Promise<number>;
19
- };
54
+ ref: {
55
+ toArray: () => Promise<any[]>;
56
+ count: () => Promise<number>;
57
+ };
20
58
  };
21
59
  [key: string]: any; // For other potential methods and properties
22
- };
60
+ };
23
61
 
24
62
 
25
63
  enum DBStatus {
@@ -56,7 +94,7 @@ export const BasicContext = createContext<{
56
94
  signin: () => void,
57
95
  getToken: () => Promise<string>,
58
96
  getSignInLink: () => string,
59
- db: any,
97
+ db: any,
60
98
  dbStatus: DBStatus
61
99
  }>({
62
100
  unicorn: "🦄",
@@ -103,6 +141,12 @@ function getSyncStatus(statusCode: number): string {
103
141
  }
104
142
  }
105
143
 
144
+ type ErrorObject = {
145
+ code: string;
146
+ title: string;
147
+ message: string;
148
+ }
149
+
106
150
  export function BasicProvider({ children, project_id, schema }: { children: React.ReactNode, project_id: string, schema: any }) {
107
151
  const [isLoaded, setIsLoaded] = useState(false)
108
152
  const [isSignedIn, setIsSignedIn] = useState(false)
@@ -110,30 +154,55 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
110
154
  const [authCode, setAuthCode] = useState<string | null>(null)
111
155
  const [user, setUser] = useState<User>({})
112
156
 
113
- const [dbStatus, setDbStatus] = useState<DBStatus>(DBStatus.OFFLINE)
114
-
157
+ const [dbStatus, setDbStatus] = useState<DBStatus>(DBStatus.LOADING)
115
158
 
116
159
  const syncRef = useRef<BasicSync | null>(null);
117
160
 
161
+ const [error, setError] = useState<ErrorObject | null>(null)
162
+
118
163
  useEffect(() => {
119
- if (!syncRef.current) {
120
- syncRef.current = new BasicSync('basicdb', { schema: schema });
164
+ function initDb() {
165
+ // console.log('S', validator(example))
166
+ if (!validator(example)) {
167
+ console.error('Basic Schema is invalid!', validator.errors)
168
+ console.group('Schema Errors')
169
+ let errorMessage = ''
170
+ validator.errors.forEach((error, index) => {
171
+ console.log('error', error)
172
+ console.log(`${index + 1}:`, error.message, ` - at ${error.instancePath}`)
173
+ errorMessage += `${index + 1}: ${error.message} - at ${error.instancePath}\n`
174
+ })
175
+ console.groupEnd('Schema Errors')
176
+ setError({
177
+ code: 'schema_invalid',
178
+ title: 'Basic Schema is invalid!',
179
+ message: errorMessage
180
+ })
181
+ return null
182
+ }
121
183
 
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
184
 
128
- syncRef.current.handleStatusChange((status: number, url: string) => {
129
- setDbStatus(getSyncStatus(status))
130
- })
185
+ if (!syncRef.current) {
186
+ syncRef.current = new BasicSync('basicdb', { schema: schema });
131
187
 
132
- syncRef.current.syncable.getStatus().then((status) => {
133
- console.log('sync status', getSyncStatus(status))
134
- })
188
+ // console.log('db is open', syncRef.current.isOpen())
189
+ // syncRef.current.open()
190
+ // .then(() => {
191
+ // console.log("is open now:", syncRef.current.isOpen())
192
+ // })
193
+
194
+ syncRef.current.handleStatusChange((status: number, url: string) => {
195
+ setDbStatus(getSyncStatus(status))
196
+ })
197
+
198
+ syncRef.current.syncable.getStatus().then((status) => {
199
+ console.log('sync status', getSyncStatus(status))
200
+ })
201
+ }
135
202
 
136
203
  }
204
+
205
+ initDb()
137
206
  }, []);
138
207
 
139
208
 
@@ -153,7 +222,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
153
222
  }
154
223
 
155
224
  useEffect(() => {
156
- if (token) {
225
+ if (token && syncRef.current) {
157
226
  connectToDb()
158
227
  }
159
228
  }, [token])
@@ -163,8 +232,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
163
232
 
164
233
  const randomState = Math.random().toString(36).substring(7);
165
234
 
166
- // let baseUrl = "https://api.basic.tech/auth/authorize"
167
- let baseUrl = "http://localhost:3003/auth/authorize"
235
+ let baseUrl = "https://api.basic.tech/auth/authorize"
168
236
  baseUrl += `?client_id=${project_id}`
169
237
  baseUrl += `&redirect_uri=${encodeURIComponent(window.location.href)}`
170
238
  baseUrl += `&response_type=code`
@@ -366,11 +434,39 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
366
434
  db: syncRef.current,
367
435
  dbStatus
368
436
  }}>
437
+ {error && <ErrorDisplay error={error} />}
369
438
  {syncRef.current ? children : null}
370
439
  </BasicContext.Provider>
371
440
  )
372
441
  }
373
442
 
443
+ function ErrorDisplay({ error }: { error: ErrorObject }) {
444
+ return <div style={{
445
+ position: 'absolute',
446
+ top: 20,
447
+ left: 20,
448
+ color: 'black',
449
+ backgroundColor: '#f8d7da',
450
+ border: '1px solid #f5c6cb',
451
+ borderRadius: '4px',
452
+ padding: '20px',
453
+ maxWidth: '400px',
454
+ margin: '20px auto',
455
+ boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
456
+ fontFamily: 'monospace',
457
+ }}>
458
+ <h3 style={{fontSize: '0.8rem', opacity: 0.8}}>code: {error.code}</h3>
459
+ <h1 style={{fontSize: '1.2rem', lineHeight: '1.5'}}>{error.title}</h1>
460
+ <p>{error.message}</p>
461
+ </div>
462
+ }
463
+
464
+ /*
465
+ possible errors:
466
+ - projectid missing / invalid
467
+ - schema missing / invalid
468
+ */
469
+
374
470
  export function useBasic() {
375
471
  return useContext(BasicContext);
376
472
  }
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