@iebh/tera-fy 2.3.0 → 2.3.2
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/.vscode/settings.json +5 -0
- package/CHANGELOG.md +22 -0
- package/api.md +35 -36
- package/dist/lib/projectFile.d.ts +3 -3
- package/dist/lib/projectFile.js +4 -3
- package/dist/lib/projectFile.js.map +1 -1
- package/dist/lib/syncro/entities.js +11 -10
- package/dist/lib/syncro/entities.js.map +1 -1
- package/dist/lib/syncro/keyed.d.ts +2 -2
- package/dist/lib/syncro/keyed.js +23 -23
- package/dist/lib/syncro/keyed.js.map +1 -1
- package/dist/lib/syncro/syncro.d.ts +15 -13
- package/dist/lib/syncro/syncro.js +84 -59
- package/dist/lib/syncro/syncro.js.map +1 -1
- package/dist/lib/terafy.bootstrapper.d.ts +2 -2
- package/dist/lib/terafy.bootstrapper.js +15 -16
- package/dist/lib/terafy.bootstrapper.js.map +1 -1
- package/dist/lib/terafy.client.d.ts +24 -25
- package/dist/lib/terafy.client.js +50 -48
- package/dist/lib/terafy.client.js.map +1 -1
- package/dist/lib/terafy.proxy.js +4 -2
- package/dist/lib/terafy.proxy.js.map +1 -1
- package/dist/lib/terafy.server.d.ts +22 -8
- package/dist/lib/terafy.server.js +54 -58
- package/dist/lib/terafy.server.js.map +1 -1
- package/dist/plugin.vue2.es2019.js +210 -1224
- package/dist/plugins/base.d.ts +2 -2
- package/dist/plugins/base.js +1 -0
- package/dist/plugins/base.js.map +1 -1
- package/dist/plugins/firebase.d.ts +4 -4
- package/dist/plugins/firebase.js +7 -7
- package/dist/plugins/firebase.js.map +1 -1
- package/dist/plugins/vue2.d.ts +1 -1
- package/dist/plugins/vue2.js +6 -5
- package/dist/plugins/vue2.js.map +1 -1
- package/dist/plugins/vue3.js +6 -5
- package/dist/plugins/vue3.js.map +1 -1
- package/dist/terafy.bootstrapper.es2019.js +2 -2
- package/dist/terafy.bootstrapper.js +2 -2
- package/dist/terafy.es2019.js +2 -2
- package/dist/terafy.js +2 -2
- package/dist/utils/mixin.js +1 -1
- package/dist/utils/mixin.js.map +1 -1
- package/dist/utils/pDefer.d.ts +5 -1
- package/dist/utils/pDefer.js +6 -1
- package/dist/utils/pDefer.js.map +1 -1
- package/dist/utils/pathTools.d.ts +1 -1
- package/dist/utils/pathTools.js +2 -2
- package/dist/utils/pathTools.js.map +1 -1
- package/eslint.config.js +21 -7
- package/lib/projectFile.ts +5 -4
- package/lib/syncro/entities.ts +11 -10
- package/lib/syncro/keyed.ts +24 -24
- package/lib/syncro/syncro.ts +97 -60
- package/lib/terafy.bootstrapper.ts +15 -16
- package/lib/terafy.client.ts +62 -62
- package/lib/terafy.proxy.ts +8 -5
- package/lib/terafy.server.ts +75 -64
- package/package.json +5 -3
- package/plugins/base.ts +3 -2
- package/plugins/firebase.ts +12 -11
- package/plugins/vue2.ts +7 -6
- package/plugins/vue3.ts +6 -5
- package/utils/mixin.ts +1 -1
- package/utils/pDefer.ts +7 -2
- package/utils/pathTools.ts +3 -3
package/lib/projectFile.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
1
2
|
import { filesize } from 'filesize';
|
|
2
3
|
import { pick, omit } from 'lodash-es';
|
|
3
4
|
import type TeraFy from './terafy.client.js';
|
|
@@ -82,7 +83,7 @@ export default class ProjectFile {
|
|
|
82
83
|
/**
|
|
83
84
|
* An object representing meta file parts of a file name
|
|
84
85
|
* @type {Object}
|
|
85
|
-
* @property {String} basename The filename +
|
|
86
|
+
* @property {String} basename The filename + extension (i.e. everything without directory name)
|
|
86
87
|
* @property {String} filename The file portion of the name (basename without the extension)
|
|
87
88
|
* @property {String} ext The extension portion of the name (always lower case)
|
|
88
89
|
* @property {String} dirName The directory path portion of the name
|
|
@@ -220,7 +221,7 @@ export default class ProjectFile {
|
|
|
220
221
|
if (this.isFolder) {
|
|
221
222
|
// Process all files in the folder
|
|
222
223
|
this.files = this.files?.map(file => {
|
|
223
|
-
|
|
224
|
+
const path = file.path.split(/\//).slice(3).join('/');
|
|
224
225
|
let url = this.url + '/' + file.name // Add file name to url
|
|
225
226
|
// Parse url to show library instead of download if reflib file
|
|
226
227
|
if (file.meta.reflib) {
|
|
@@ -253,7 +254,7 @@ export default class ProjectFile {
|
|
|
253
254
|
/**
|
|
254
255
|
* Fetch the raw file contents as a Blob
|
|
255
256
|
*
|
|
256
|
-
* @param {Object} [options]
|
|
257
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
257
258
|
*
|
|
258
259
|
* @returns {Promise<Blob>} The eventual raw file contents as a Blob
|
|
259
260
|
*
|
|
@@ -346,7 +347,7 @@ export default class ProjectFile {
|
|
|
346
347
|
* as it's not included in the serialized output.
|
|
347
348
|
*
|
|
348
349
|
* @param {Object} data An input object created via `ProjectFiles.serialize()` (MUST include a 'tera' property added manually)
|
|
349
|
-
* @returns {ProjectFile} A ProjectFile instance setup against the
|
|
350
|
+
* @returns {ProjectFile} A ProjectFile instance setup against the deserialized data
|
|
350
351
|
*/
|
|
351
352
|
|
|
352
353
|
static deserialize(data: any): ProjectFile {
|
package/lib/syncro/entities.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
// @ts-expect-error TODO: Remove when reflib gets declaration file
|
|
2
3
|
import Reflib from '@iebh/reflib';
|
|
3
4
|
import {v4 as uuid4} from 'uuid';
|
|
4
5
|
import {nanoid} from 'nanoid';
|
|
@@ -72,7 +73,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
72
73
|
institutes: { // {{{
|
|
73
74
|
singular: 'institute',
|
|
74
75
|
async initState({supabasey, id}: {supabasey: BoundSupabaseyFunction, id: string}) {
|
|
75
|
-
|
|
76
|
+
const institute = await supabasey((supabase) => supabase
|
|
76
77
|
.from('institutes')
|
|
77
78
|
.select('data')
|
|
78
79
|
.eq('id', id)
|
|
@@ -81,7 +82,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
81
82
|
if (institute) return institute.data; // institute is valid and already exists
|
|
82
83
|
},
|
|
83
84
|
flushState({supabasey, state, id}) {
|
|
84
|
-
// @ts-
|
|
85
|
+
// @ts-expect-error Typescript struggles to resolve supabasey import correctly
|
|
85
86
|
return supabasey.rpc('syncro_merge_data', {
|
|
86
87
|
table_name: 'institutes',
|
|
87
88
|
entity_id: id,
|
|
@@ -92,14 +93,14 @@ const syncroConfig: SyncroConfig = {
|
|
|
92
93
|
projects: { // {{{
|
|
93
94
|
singular: 'project',
|
|
94
95
|
async initState({supabasey, id}) {
|
|
95
|
-
|
|
96
|
+
const projectData = await supabasey((supabase) => supabase
|
|
96
97
|
.from('projects')
|
|
97
98
|
.select('data')
|
|
98
99
|
.eq('id', id)
|
|
99
100
|
.maybeSingle<ProjectRow>()
|
|
100
101
|
);
|
|
101
102
|
if (!projectData) throw new Error(`Syncro project "${id}" not found`);
|
|
102
|
-
|
|
103
|
+
const data = projectData.data;
|
|
103
104
|
|
|
104
105
|
// MIGRATION - Move data.temp{} into Supabase files + add pointer to filename {{{
|
|
105
106
|
if (
|
|
@@ -173,7 +174,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
173
174
|
async initState({supabasey, id, relation}) {
|
|
174
175
|
if (!relation || !/_\*$/.test(relation)) throw new Error('Project library relation missing, path should resemble "project_library::${PROJECT}::${LIBRARY_FILE_ID}_*"');
|
|
175
176
|
|
|
176
|
-
|
|
177
|
+
const fileId = relation.replace(/_\*$/, '');
|
|
177
178
|
|
|
178
179
|
const files = await supabasey((supabase) => supabase
|
|
179
180
|
.storage
|
|
@@ -212,7 +213,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
212
213
|
singular: 'project namespace',
|
|
213
214
|
async initState({supabasey, id, relation}) {
|
|
214
215
|
if (!relation) throw new Error('Project namespace relation missing, path should resemble "project_namespaces::${PROJECT}::${RELATION}"');
|
|
215
|
-
|
|
216
|
+
const rows = await supabasey((supabase) => supabase
|
|
216
217
|
.from('project_namespaces')
|
|
217
218
|
.select('data')
|
|
218
219
|
.eq('project', id)
|
|
@@ -253,7 +254,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
253
254
|
test: { // {{{
|
|
254
255
|
singular: 'test',
|
|
255
256
|
async initState({supabasey, id}: {supabasey: BoundSupabaseyFunction, id: string}) {
|
|
256
|
-
|
|
257
|
+
const rows = await supabasey((supabase) => supabase
|
|
257
258
|
.from('test')
|
|
258
259
|
.select('data')
|
|
259
260
|
.eq('id', id)
|
|
@@ -274,7 +275,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
274
275
|
users: { // {{{
|
|
275
276
|
singular: 'user',
|
|
276
277
|
async initState({supabasey, id}: {supabasey: BoundSupabaseyFunction, id: string}) {
|
|
277
|
-
|
|
278
|
+
const user = await supabasey((supabase) => supabase
|
|
278
279
|
.from('users')
|
|
279
280
|
.select('data')
|
|
280
281
|
.eq('id', id)
|
|
@@ -283,7 +284,7 @@ const syncroConfig: SyncroConfig = {
|
|
|
283
284
|
if (user) return user.data; // User is valid and already exists
|
|
284
285
|
|
|
285
286
|
// User row doesn't already exist - need to create stub
|
|
286
|
-
|
|
287
|
+
const newUser = await supabasey((supabase) => supabase
|
|
287
288
|
.from('users')
|
|
288
289
|
.insert<UserRow>({
|
|
289
290
|
id,
|
package/lib/syncro/keyed.ts
CHANGED
|
@@ -20,7 +20,7 @@ interface FlushOptions {
|
|
|
20
20
|
/**
|
|
21
21
|
* @class SyncroKeyed
|
|
22
22
|
* TERA Isomorphic SyncroKeyed class
|
|
23
|
-
* Collate a single (
|
|
23
|
+
* Collate a single (potentially very large) single Syncro object by splitting it across multiple Syncros
|
|
24
24
|
* This makes the assumption that the Syncro content is a large object collection of objects - a keyed map collection
|
|
25
25
|
* The original impetus is to allow TERA citation libraries to be held in a Syncro object and flushed back to Supabase when editing has completed
|
|
26
26
|
*/
|
|
@@ -66,7 +66,7 @@ export default class SyncroKeyed extends Syncro {
|
|
|
66
66
|
|
|
67
67
|
if (!/\*/.test(path)) throw new Error('SyncroKeyed paths must contain at least one asterisk as an object pagination indicator');
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
const {prefix, suffix} = /^(?<prefix>.+)\*(?<suffix>.*)$/.exec(path)!.groups!;
|
|
70
70
|
this.keyedPath.getKey = (path: string, index: number): string => `${prefix}${index}${suffix}`;
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -97,19 +97,19 @@ export default class SyncroKeyed extends Syncro {
|
|
|
97
97
|
*/
|
|
98
98
|
mount(): Promise<Syncro> {
|
|
99
99
|
// Cast the result to the expected interface
|
|
100
|
-
|
|
100
|
+
const {entity, id, relation, fsCollection, fsId} = Syncro.pathSplit(this.path, {allowAsterisk: true}) as PathSplitResult;
|
|
101
101
|
|
|
102
102
|
return Promise.resolve()
|
|
103
103
|
.then(()=> new Promise<void>(resolve => { // Mount all members by looking for similar keys
|
|
104
104
|
this.members = []; // Reset member list
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
const seekMember = async (index: number) => {
|
|
107
|
+
const memberId = fsId.replace('*', ''+index);
|
|
108
108
|
this.debug('Seek keyedMember', fsCollection, '#', memberId);
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
const docRef = FirestoreDocRef(Syncro.firestore, fsCollection, memberId);
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
const doc = await FirestoreGetDoc(docRef);
|
|
113
113
|
if (doc.exists()) { // Found a matching entry
|
|
114
114
|
// Expand member lookup with the new member by its numeric index
|
|
115
115
|
await this.keyedMembersExpand(index);
|
|
@@ -132,11 +132,11 @@ export default class SyncroKeyed extends Syncro {
|
|
|
132
132
|
this.debug('Populate initial SyncroKeyed state');
|
|
133
133
|
|
|
134
134
|
// Extract base data + add document and return new hook
|
|
135
|
-
const entityKey = entity
|
|
135
|
+
const entityKey = entity;
|
|
136
136
|
if (!SyncroEntities[entityKey]) throw new Error(`Unknown Sync entity "${entity}"`);
|
|
137
137
|
|
|
138
138
|
// Go fetch the initial state object
|
|
139
|
-
|
|
139
|
+
const state = await SyncroEntities[entityKey].initState({
|
|
140
140
|
supabasey: Syncro.supabasey,
|
|
141
141
|
id, relation,
|
|
142
142
|
});
|
|
@@ -144,7 +144,7 @@ export default class SyncroKeyed extends Syncro {
|
|
|
144
144
|
await this.keyedAssign(state);
|
|
145
145
|
})
|
|
146
146
|
.then(()=> { // Create the reactive
|
|
147
|
-
|
|
147
|
+
const reactive = this.getReactive(this.proxy());
|
|
148
148
|
// Assuming this.value should hold the reactive proxy
|
|
149
149
|
// If this.value is inherited and has a specific type, this might need adjustment
|
|
150
150
|
this.value = reactive.doc;
|
|
@@ -173,14 +173,14 @@ export default class SyncroKeyed extends Syncro {
|
|
|
173
173
|
|
|
174
174
|
// Scope through members until we get a hit on the key
|
|
175
175
|
get(target: SyncroKeyed, prop: string | symbol): any {
|
|
176
|
-
|
|
176
|
+
const targetMember = target.members.find(m => m.value && prop in m.value);
|
|
177
177
|
// Access value via targetMember.value if found
|
|
178
178
|
return targetMember ? targetMember.value[prop] : undefined;
|
|
179
179
|
},
|
|
180
180
|
|
|
181
181
|
// Set the member key if one already exists, otherwise overflow onto the next member
|
|
182
182
|
set(target: SyncroKeyed, prop: string | symbol, value: any): boolean {
|
|
183
|
-
|
|
183
|
+
const targetMember = target.members.find(m => m.value && prop in m.value);
|
|
184
184
|
if (targetMember && targetMember.value) {
|
|
185
185
|
targetMember.value[prop] = value;
|
|
186
186
|
} else {
|
|
@@ -192,7 +192,7 @@ export default class SyncroKeyed extends Syncro {
|
|
|
192
192
|
|
|
193
193
|
// Remove a key
|
|
194
194
|
deleteProperty(target: SyncroKeyed, prop: string | symbol): boolean {
|
|
195
|
-
|
|
195
|
+
const targetMember = target.members.find(m => m.value && prop in m.value);
|
|
196
196
|
if (targetMember && targetMember.value) {
|
|
197
197
|
delete targetMember.value[prop];
|
|
198
198
|
return true; // Indicate success
|
|
@@ -212,7 +212,7 @@ export default class SyncroKeyed extends Syncro {
|
|
|
212
212
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
213
213
|
*/
|
|
214
214
|
async flush(options?: FlushOptions): Promise<void> { // Match base class signature
|
|
215
|
-
|
|
215
|
+
const settings: FlushOptions = {
|
|
216
216
|
destroy: false,
|
|
217
217
|
...options,
|
|
218
218
|
};
|
|
@@ -240,7 +240,7 @@ export default class SyncroKeyed extends Syncro {
|
|
|
240
240
|
* @returns {Promise<*>} A promise which resolves when the operation has completed with the set value
|
|
241
241
|
*/
|
|
242
242
|
async keyedSet(key: string, value: any): Promise<any> {
|
|
243
|
-
|
|
243
|
+
const candidateMember = this.members.find(m => m.value && Object.keys(m.value).length < this.keyedConfig.maxKeys);
|
|
244
244
|
if (candidateMember?.value) {
|
|
245
245
|
candidateMember.value[key] = value;
|
|
246
246
|
return value;
|
|
@@ -249,8 +249,8 @@ export default class SyncroKeyed extends Syncro {
|
|
|
249
249
|
await this.keyedMembersExpand(); // Call without index to append
|
|
250
250
|
|
|
251
251
|
// Get the newly added member
|
|
252
|
-
//
|
|
253
|
-
|
|
252
|
+
// eslint-disable-next-line unicorn/prefer-at
|
|
253
|
+
const newMember = this.members[this.members.length - 1];
|
|
254
254
|
if (!newMember || !newMember.value) {
|
|
255
255
|
throw new Error('Failed to expand members or new member has no value object');
|
|
256
256
|
}
|
|
@@ -268,7 +268,7 @@ export default class SyncroKeyed extends Syncro {
|
|
|
268
268
|
|
|
269
269
|
/**
|
|
270
270
|
* Assign an entire in-memory object to members
|
|
271
|
-
* This can be thought of as the optimized
|
|
271
|
+
* This can be thought of as the optimized equivalent of Object.assign()
|
|
272
272
|
* Use this when merging large objects as it can make optimizations
|
|
273
273
|
*
|
|
274
274
|
* @param {Object} state The value to merge
|
|
@@ -276,10 +276,10 @@ export default class SyncroKeyed extends Syncro {
|
|
|
276
276
|
async keyedAssign(state: Record<string, any>): Promise<void> {
|
|
277
277
|
// Can we assume we have a blank state - this speeds up existing key checks significantly
|
|
278
278
|
// Ensure members[0] and its value exist
|
|
279
|
-
|
|
279
|
+
const isBlank = this.members.length === 1 && this.members[0]?.value && Object.keys(this.members[0].value).length === 0;
|
|
280
280
|
|
|
281
281
|
if (isBlank) {
|
|
282
|
-
|
|
282
|
+
const chunks = chunk(Object.entries(state), this.keyedConfig.maxKeys)
|
|
283
283
|
.map(chunk => Object.fromEntries(chunk));
|
|
284
284
|
|
|
285
285
|
await Promise.all(
|
|
@@ -327,20 +327,20 @@ export default class SyncroKeyed extends Syncro {
|
|
|
327
327
|
return; // Exit if member already exists
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
|
|
330
|
+
const syncroPath = this.keyedPath.getKey(this.path, index);
|
|
331
331
|
// Pass empty options object {} or specify allowAsterisk: false if needed
|
|
332
|
-
|
|
332
|
+
const {fsCollection, fsId} = Syncro.pathSplit(syncroPath, {}) as PathSplitResult;
|
|
333
333
|
this.debug('Expand SyncroKeyed size to index=', index);
|
|
334
334
|
|
|
335
335
|
// Create a new Syncro member, inheriteing some details from this parent item
|
|
336
|
-
|
|
336
|
+
const syncro = new Syncro(`${fsCollection}::${fsId}`, {
|
|
337
337
|
debug: this.debug,
|
|
338
338
|
getReactive: this.getReactive,
|
|
339
339
|
});
|
|
340
340
|
|
|
341
341
|
// Wait for mount to complete
|
|
342
342
|
await syncro.mount({
|
|
343
|
-
initialState: {}, // Force
|
|
343
|
+
initialState: {}, // Force initial state to empty object so we don't get stuck in a loop
|
|
344
344
|
});
|
|
345
345
|
|
|
346
346
|
// Insert at the correct index if specified, otherwise push
|