@iebh/tera-fy 2.3.8 → 2.3.9
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/CHANGELOG.md +14 -0
- package/dist/lib/syncro/entities.d.ts +7 -1
- package/dist/lib/syncro/entities.js +79 -95
- package/dist/lib/syncro/entities.js.map +1 -1
- package/dist/lib/syncro/keyed.js +1 -0
- package/dist/lib/syncro/keyed.js.map +1 -1
- package/dist/lib/syncro/syncro.d.ts +7 -0
- package/dist/lib/syncro/syncro.js +6 -0
- package/dist/lib/syncro/syncro.js.map +1 -1
- package/dist/plugin.vue2.es2019.js +1 -1
- package/lib/syncro/entities.ts +94 -103
- package/lib/syncro/keyed.ts +1 -0
- package/lib/syncro/syncro.ts +9 -0
- package/package.json +1 -1
package/lib/syncro/entities.ts
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
1
|
+
/* eslint-disable jsdoc/reject-function-type, no-unused-vars */
|
|
2
2
|
// @ts-expect-error TODO: Remove when reflib gets declaration file
|
|
3
3
|
import Reflib from '@iebh/reflib';
|
|
4
4
|
import {v4 as uuid4} from 'uuid';
|
|
5
5
|
import {nanoid} from 'nanoid';
|
|
6
|
-
import {
|
|
6
|
+
import {BoundSupabaseyFunction} from '@iebh/supabasey';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// Minimal interface for a postgres-npm Sql instance (HYPERDRIVE is injected by the Cloudflare Worker runtime)
|
|
10
|
+
export interface PostgresSql {
|
|
11
|
+
(strings: TemplateStringsArray, ...values: any[]): Promise<any[]>;
|
|
12
|
+
json(value: any): any;
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
|
|
8
16
|
// DATABASE TABLE TYPE DEFINITIONS {{{
|
|
9
17
|
interface ProjectRow {
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
data: {
|
|
19
|
+
id: string;
|
|
12
20
|
// TODO: add other properties in project data
|
|
13
21
|
};
|
|
14
22
|
// TODO: Define other columns in project table
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
interface InstituteRow {
|
|
18
|
-
|
|
26
|
+
data: any;
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
interface UserRow {
|
|
@@ -36,20 +44,22 @@ interface NamespaceRow {
|
|
|
36
44
|
[key: string]: any;
|
|
37
45
|
// TODO: add other properties in namespace data
|
|
38
46
|
};
|
|
39
|
-
// TODO: Define other
|
|
47
|
+
// TODO: Define other columns in namespace table
|
|
40
48
|
}
|
|
41
49
|
// }}}
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
|
|
52
|
+
// Interface for each syncro entity config
|
|
44
53
|
interface SyncroEntityConfig {
|
|
45
54
|
singular: string;
|
|
46
55
|
initState: (args: {
|
|
56
|
+
HYPERDRIVE: PostgresSql;
|
|
47
57
|
supabasey: BoundSupabaseyFunction;
|
|
48
58
|
id: string; // Primary ID for the entity
|
|
49
59
|
relation?: string; // Optional relation identifier (for namespaces, libraries)
|
|
50
60
|
}) => Promise<any>;
|
|
51
|
-
|
|
52
61
|
flushState: (args: {
|
|
62
|
+
HYPERDRIVE?: PostgresSql;
|
|
53
63
|
supabasey: BoundSupabaseyFunction;
|
|
54
64
|
state: any; // The state object to flush
|
|
55
65
|
id?: string; // Primary ID (used in some lookups like namespaces)
|
|
@@ -58,49 +68,56 @@ interface SyncroEntityConfig {
|
|
|
58
68
|
}) => Promise<any>; // Return type signifies completion/result of flush
|
|
59
69
|
}
|
|
60
70
|
|
|
71
|
+
|
|
61
72
|
type SyncroConfig = Record<string, SyncroEntityConfig>;
|
|
62
73
|
|
|
74
|
+
|
|
63
75
|
/**
|
|
64
76
|
* Entities we support Syncro paths for, each should correspond directly with a Firebase/Firestore collection name
|
|
65
77
|
*
|
|
66
78
|
* @type {Object} An object lookup of entities
|
|
67
79
|
*
|
|
68
80
|
* @property {String} singular The singular noun for the item
|
|
69
|
-
* @property {Function} initState Function called to initialize state when Firestore has no existing document. Called as `({
|
|
81
|
+
* @property {Function} initState Function called to initialize state when Firestore has no existing document. Called as `({HYPERDRIVE:PostgresSql, supabasey:BoundSupabaseyFunction, id:String, relation?:string})` and expected to return the initial data object state
|
|
70
82
|
* @property {Function} flushState Function called to flush state from Firebase to Supabase. Called the same as `initState` + `{state:Object}`
|
|
71
83
|
*/
|
|
72
84
|
const syncroConfig: SyncroConfig = {
|
|
73
85
|
institutes: { // {{{
|
|
74
86
|
singular: 'institute',
|
|
75
|
-
async initState({
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (institute
|
|
87
|
+
async initState({HYPERDRIVE, id}: {HYPERDRIVE: PostgresSql, id: string}) {
|
|
88
|
+
let institute = await HYPERDRIVE`
|
|
89
|
+
SELECT data
|
|
90
|
+
FROM institutes
|
|
91
|
+
WHERE id = ${id}
|
|
92
|
+
LIMIT 1
|
|
93
|
+
`;
|
|
94
|
+
if (institute.length > 0) {
|
|
95
|
+
return institute[0].data; // institute is valid and already exists
|
|
96
|
+
} else {
|
|
97
|
+
throw new Error(`Syncro institute "${id}" not found`);
|
|
98
|
+
}
|
|
83
99
|
},
|
|
84
100
|
flushState({supabasey, state, id}) {
|
|
85
|
-
//
|
|
86
|
-
return supabasey.rpc('syncro_merge_data', {
|
|
101
|
+
// FIXME: Better to reuse `env.HYPERDRIVE` instead of supabasey here in future
|
|
102
|
+
return supabasey((supabase) => supabase.rpc('syncro_merge_data', {
|
|
87
103
|
table_name: 'institutes',
|
|
88
104
|
entity_id: id,
|
|
89
105
|
new_data: state,
|
|
90
|
-
});
|
|
106
|
+
}));
|
|
91
107
|
},
|
|
92
108
|
}, // }}}
|
|
93
109
|
projects: { // {{{
|
|
94
110
|
singular: 'project',
|
|
95
|
-
async initState({supabasey, id}) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
|
|
111
|
+
async initState({HYPERDRIVE, supabasey, id}: {HYPERDRIVE: PostgresSql, supabasey: BoundSupabaseyFunction, id: string}) {
|
|
112
|
+
let projects = await HYPERDRIVE`
|
|
113
|
+
SELECT data
|
|
114
|
+
FROM projects
|
|
115
|
+
WHERE id = ${id}
|
|
116
|
+
LIMIT 1
|
|
117
|
+
`;
|
|
118
|
+
if (projects.length == 0) throw new Error(`Syncro project "${id}" not found`);
|
|
119
|
+
|
|
120
|
+
const data = projects[0].data;
|
|
104
121
|
|
|
105
122
|
// MIGRATION - Move data.temp{} into Supabase files + add pointer to filename {{{
|
|
106
123
|
if (
|
|
@@ -113,7 +130,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
113
130
|
await Promise.all(
|
|
114
131
|
Object.entries(data.temp)
|
|
115
132
|
.filter(([, branch]) => typeof branch == 'object')
|
|
116
|
-
.map(([toolKey
|
|
133
|
+
.map(([toolKey]) => {
|
|
117
134
|
console.log(`[MIGRATION] Converting data.temp[${toolKey}]...`);
|
|
118
135
|
|
|
119
136
|
const toolName = toolKey.split('-')[0];
|
|
@@ -161,8 +178,8 @@ const syncroConfig: SyncroConfig = {
|
|
|
161
178
|
return data;
|
|
162
179
|
},
|
|
163
180
|
flushState({supabasey, state, fsId}) {
|
|
164
|
-
//
|
|
165
|
-
return supabasey(supabase => supabase.rpc('syncro_merge_data', {
|
|
181
|
+
// FIXME: Better to reuse `env.HYPERDRIVE` instead of supabasey here in future
|
|
182
|
+
return supabasey((supabase) => supabase.rpc('syncro_merge_data', {
|
|
166
183
|
table_name: 'projects',
|
|
167
184
|
entity_id: fsId,
|
|
168
185
|
new_data: state,
|
|
@@ -171,7 +188,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
171
188
|
}, // }}}
|
|
172
189
|
project_libraries: { // {{{
|
|
173
190
|
singular: 'project library',
|
|
174
|
-
async initState({supabasey, id, relation}) {
|
|
191
|
+
async initState({id, relation, supabasey}: {HYPERDRIVE: PostgresSql, id: string, relation?: string, supabasey: BoundSupabaseyFunction}) {
|
|
175
192
|
if (!relation || !/_\*$/.test(relation)) throw new Error('Project library relation missing, path should resemble "project_library::${PROJECT}::${LIBRARY_FILE_ID}_*"');
|
|
176
193
|
|
|
177
194
|
const fileId = relation.replace(/_\*$/, '');
|
|
@@ -209,63 +226,33 @@ const syncroConfig: SyncroConfig = {
|
|
|
209
226
|
throw new Error('Flushing project_libraries::* namespace is not yet supported');
|
|
210
227
|
},
|
|
211
228
|
}, // }}}
|
|
212
|
-
project_namespaces: { // {{{
|
|
229
|
+
project_namespaces: { // NOT YET SUPPORTED {{{
|
|
213
230
|
singular: 'project namespace',
|
|
214
|
-
async initState(
|
|
215
|
-
|
|
216
|
-
const rows = await supabasey((supabase) => supabase
|
|
217
|
-
.from('project_namespaces')
|
|
218
|
-
.select('data')
|
|
219
|
-
.eq('project', id)
|
|
220
|
-
.eq('name', relation)
|
|
221
|
-
.limit(1)
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
if (rows && rows.length == 1) {
|
|
225
|
-
return rows[0].data;
|
|
226
|
-
} else {
|
|
227
|
-
const newItem = await supabasey((supabase) => supabase
|
|
228
|
-
.from('project_namespaces') // Doesn't exist - create it
|
|
229
|
-
.insert<NamespaceRow>({
|
|
230
|
-
project: id,
|
|
231
|
-
name: relation,
|
|
232
|
-
data: {},
|
|
233
|
-
})
|
|
234
|
-
.select('data')
|
|
235
|
-
.single<NamespaceRow>() // Assuming insert returns the single inserted row
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
if (!newItem) throw new Error('Failed to create project namespace');
|
|
239
|
-
return newItem.data;
|
|
240
|
-
}
|
|
231
|
+
async initState() {
|
|
232
|
+
throw new Error('Updating project_namespaces is not yet supported');
|
|
241
233
|
},
|
|
242
|
-
flushState(
|
|
243
|
-
|
|
244
|
-
.from('project_namespaces')
|
|
245
|
-
.update({
|
|
246
|
-
edited_at: new Date().toISOString(),
|
|
247
|
-
data: state,
|
|
248
|
-
})
|
|
249
|
-
.eq('project', id)
|
|
250
|
-
.eq('name', relation)
|
|
251
|
-
);
|
|
234
|
+
async flushState() {
|
|
235
|
+
throw new Error('Updating project_namespaces is not yet supported');
|
|
252
236
|
},
|
|
253
237
|
}, // }}}
|
|
254
238
|
test: { // {{{
|
|
255
239
|
singular: 'test',
|
|
256
|
-
async initState({
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
240
|
+
async initState({HYPERDRIVE, id}: {HYPERDRIVE: PostgresSql, id: string}) {
|
|
241
|
+
let rows = await HYPERDRIVE`
|
|
242
|
+
SELECT data
|
|
243
|
+
FROM test
|
|
244
|
+
WHERE id = ${id}
|
|
245
|
+
LIMIT 1
|
|
246
|
+
`;
|
|
247
|
+
if (rows.length > 0) {
|
|
248
|
+
return rows[0].data; // User is valid and already exists
|
|
249
|
+
} else {
|
|
250
|
+
throw new Error(`Syncro test "${id}" not found`);
|
|
251
|
+
}
|
|
266
252
|
},
|
|
267
253
|
flushState({supabasey, state, fsId}) {
|
|
268
|
-
|
|
254
|
+
// FIXME: Better to reuse `env.HYPERDRIVE` instead of supabasey here in future
|
|
255
|
+
return supabasey((supabase) => supabase.rpc('syncro_merge_data', {
|
|
269
256
|
table_name: 'test',
|
|
270
257
|
entity_id: fsId,
|
|
271
258
|
new_data: state,
|
|
@@ -274,34 +261,38 @@ const syncroConfig: SyncroConfig = {
|
|
|
274
261
|
}, // }}}
|
|
275
262
|
users: { // {{{
|
|
276
263
|
singular: 'user',
|
|
277
|
-
async initState({
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (user) return user.data; // User is valid and already exists
|
|
264
|
+
async initState({HYPERDRIVE, id}: {HYPERDRIVE: PostgresSql, id: string}) {
|
|
265
|
+
let user = await HYPERDRIVE`
|
|
266
|
+
SELECT data
|
|
267
|
+
FROM users
|
|
268
|
+
WHERE id = ${id}
|
|
269
|
+
LIMIT 1
|
|
270
|
+
`;
|
|
271
|
+
if (user.length > 0) return user[0].data; // User is valid and already exists
|
|
285
272
|
|
|
286
|
-
// User row doesn't already exist -
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
273
|
+
// User row doesn't already exist - this shouldn't happen if the user has correctly gone through the onboarding process
|
|
274
|
+
// but... *shrugs*, who knows
|
|
275
|
+
let newUser = await HYPERDRIVE`
|
|
276
|
+
INSERT INTO users
|
|
277
|
+
(
|
|
290
278
|
id,
|
|
291
|
-
data
|
|
279
|
+
data
|
|
280
|
+
)
|
|
281
|
+
VALUES (
|
|
282
|
+
${id},
|
|
283
|
+
${HYPERDRIVE.json({
|
|
292
284
|
id,
|
|
293
285
|
credits: 1000,
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (!newUser) throw new Error('Failed to create user');
|
|
300
|
-
return newUser.data; // Return back the data that eventually got created - allowing for database triggers, default field values etc.
|
|
286
|
+
})}::JSONB
|
|
287
|
+
)
|
|
288
|
+
`;
|
|
289
|
+
if (!newUser?.length) throw new Error(`Failed to create new user "${id}"`);
|
|
290
|
+
return newUser[0].data; // Return back the data that eventually got created - allowing for database triggers, default field values etc.
|
|
301
291
|
|
|
302
292
|
},
|
|
303
293
|
flushState({supabasey, state, fsId}) {
|
|
304
|
-
|
|
294
|
+
// FIXME: Better to reuse `env.HYPERDRIVE` instead of supabasey here in future
|
|
295
|
+
return supabasey((supabase) => supabase.rpc('syncro_merge_data', {
|
|
305
296
|
table_name: 'users',
|
|
306
297
|
entity_id: fsId,
|
|
307
298
|
new_data: state,
|
package/lib/syncro/keyed.ts
CHANGED
package/lib/syncro/syncro.ts
CHANGED
|
@@ -25,6 +25,7 @@ import PromiseThrottle from 'p-throttle';
|
|
|
25
25
|
import PromiseRetry from 'p-retry';
|
|
26
26
|
import {FirebaseApp, FirebaseError} from 'firebase/app';
|
|
27
27
|
import { BoundSupabaseyFunction } from '@iebh/supabasey';
|
|
28
|
+
import type { PostgresSql } from './entities.js';
|
|
28
29
|
|
|
29
30
|
interface ThrottleOptions<T = any> {
|
|
30
31
|
limit: number,
|
|
@@ -81,6 +82,14 @@ export default class Syncro {
|
|
|
81
82
|
static supabasey: BoundSupabaseyFunction;
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Postgres SQL instance in use (injected by the Cloudflare Worker runtime via Hyperdrive)
|
|
87
|
+
*
|
|
88
|
+
* @type {PostgresSql}
|
|
89
|
+
*/
|
|
90
|
+
static db: PostgresSql;
|
|
91
|
+
|
|
92
|
+
|
|
84
93
|
/**
|
|
85
94
|
* The current user session, should be unique for the user + browser tab
|
|
86
95
|
* Used by the heartbeat system
|
package/package.json
CHANGED