@iebh/tera-fy 2.3.10 → 2.3.12
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 +32 -0
- package/api.md +8 -11
- package/dist/plugin.vue2.es2019.js +28 -27
- package/dist/terafy.bootstrapper.es2019.js +2 -2
- package/dist/terafy.bootstrapper.js +2 -2
- package/dist/terafy.es2019.js +5 -5
- package/dist/terafy.js +5 -5
- package/eslint.config.js +4 -66
- package/lib/{projectFile.ts → projectFile.js} +52 -76
- package/lib/syncro/{entities.ts → entities.js} +14 -83
- package/lib/syncro/{keyed.ts → keyed.js} +46 -68
- package/lib/syncro/{syncro.ts → syncro.js} +104 -136
- package/lib/{terafy.bootstrapper.ts → terafy.bootstrapper.js} +20 -30
- package/lib/{terafy.client.ts → terafy.client.js} +85 -84
- package/lib/{terafy.proxy.ts → terafy.proxy.js} +12 -26
- package/lib/{terafy.server.ts → terafy.server.js} +204 -229
- package/package.json +20 -39
- package/plugins/{base.ts → base.js} +3 -5
- package/plugins/{firebase.ts → firebase.js} +22 -21
- package/plugins/{vite.ts → vite.js} +3 -3
- package/plugins/{vue2.ts → vue2.js} +8 -10
- package/plugins/{vue3.ts → vue3.js} +7 -10
- package/utils/mixin.js +15 -0
- package/utils/{pathTools.ts → pathTools.js} +8 -9
- package/widgets/tera-file-select.vue +1 -1
- package/dist/lib/projectFile.d.ts +0 -190
- package/dist/lib/projectFile.js +0 -305
- package/dist/lib/projectFile.js.map +0 -1
- package/dist/lib/syncro/entities.d.ts +0 -34
- package/dist/lib/syncro/entities.js +0 -212
- package/dist/lib/syncro/entities.js.map +0 -1
- package/dist/lib/syncro/keyed (Rhino's conflicted copy 2026-05-10).js +0 -287
- package/dist/lib/syncro/keyed.d.ts +0 -95
- package/dist/lib/syncro/keyed.js +0 -288
- package/dist/lib/syncro/keyed.js (Rhino's conflicted copy 2026-05-10).map +0 -1
- package/dist/lib/syncro/keyed.js.map +0 -1
- package/dist/lib/syncro/syncro (Rhino's conflicted copy 2026-05-10).js +0 -765
- package/dist/lib/syncro/syncro.d (Rhino's conflicted copy 2026-05-10).ts +0 -336
- package/dist/lib/syncro/syncro.d.ts +0 -343
- package/dist/lib/syncro/syncro.js +0 -771
- package/dist/lib/syncro/syncro.js (Rhino's conflicted copy 2026-05-10).map +0 -1
- package/dist/lib/syncro/syncro.js.map +0 -1
- package/dist/lib/terafy.bootstrapper.d.ts +0 -42
- package/dist/lib/terafy.bootstrapper.js +0 -129
- package/dist/lib/terafy.bootstrapper.js.map +0 -1
- package/dist/lib/terafy.client.d.ts +0 -555
- package/dist/lib/terafy.client.js +0 -1153
- package/dist/lib/terafy.client.js.map +0 -1
- package/dist/lib/terafy.proxy.d.ts +0 -66
- package/dist/lib/terafy.proxy.js +0 -130
- package/dist/lib/terafy.proxy.js.map +0 -1
- package/dist/lib/terafy.server.d.ts +0 -697
- package/dist/lib/terafy.server.js +0 -2062
- package/dist/lib/terafy.server.js.map +0 -1
- package/dist/plugin.vue2.es2019 (Rhino's conflicted copy 2026-05-10).js +0 -1271
- package/dist/plugins/base.d.ts +0 -20
- package/dist/plugins/base.js +0 -22
- package/dist/plugins/base.js.map +0 -1
- package/dist/plugins/firebase.d.ts +0 -62
- package/dist/plugins/firebase.js +0 -123
- package/dist/plugins/firebase.js.map +0 -1
- package/dist/plugins/vite.d.ts +0 -12
- package/dist/plugins/vite.js +0 -22
- package/dist/plugins/vite.js.map +0 -1
- package/dist/plugins/vue2.d.ts +0 -67
- package/dist/plugins/vue2.js +0 -104
- package/dist/plugins/vue2.js.map +0 -1
- package/dist/plugins/vue3.d.ts +0 -63
- package/dist/plugins/vue3.js +0 -93
- package/dist/plugins/vue3.js.map +0 -1
- package/dist/utils/mixin.d.ts +0 -11
- package/dist/utils/mixin.js +0 -15
- package/dist/utils/mixin.js.map +0 -1
- package/dist/utils/pDefer.d.ts +0 -16
- package/dist/utils/pDefer.js +0 -19
- package/dist/utils/pDefer.js.map +0 -1
- package/dist/utils/pathTools.d.ts +0 -70
- package/dist/utils/pathTools.js +0 -120
- package/dist/utils/pathTools.js.map +0 -1
- package/tsconfig.json +0 -30
- package/utils/mixin.ts +0 -18
- package/utils/pDefer.ts +0 -27
|
@@ -1,45 +1,18 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
|
2
|
-
// Global 'app' declaration
|
|
3
|
-
declare let app: any;
|
|
4
|
-
|
|
5
|
-
// Global window augmentation
|
|
6
|
-
declare global {
|
|
7
|
-
interface Window {
|
|
8
|
-
// eslint-disable-next-line no-unused-vars
|
|
9
|
-
panic(text: any): void;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface UiWindowPermissions {
|
|
14
|
-
popup?: boolean;
|
|
15
|
-
location?: boolean;
|
|
16
|
-
menubar?: boolean;
|
|
17
|
-
status?: boolean;
|
|
18
|
-
scrollbars?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface UiWindowOptions {
|
|
22
|
-
width?: number;
|
|
23
|
-
height?: number;
|
|
24
|
-
center?: boolean;
|
|
25
|
-
permissions?: UiWindowPermissions;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
1
|
import {cloneDeep} from 'lodash-es';
|
|
29
|
-
import mixin from '#utils/mixin';
|
|
30
2
|
import {nanoid} from 'nanoid';
|
|
3
|
+
import {mixin} from '#utils/mixin';
|
|
31
4
|
import pathTools from '#utils/pathTools';
|
|
32
|
-
import promiseDefer from '#utils/pDefer';
|
|
33
|
-
// @ts-expect-error TODO: Remove when reflib gets declaration file
|
|
34
5
|
import Reflib from '@iebh/reflib';
|
|
35
6
|
import {reactive} from 'vue';
|
|
36
7
|
|
|
8
|
+
/* globals app */
|
|
9
|
+
|
|
10
|
+
|
|
37
11
|
/**
|
|
38
12
|
* Server-side functions available to the Tera-Fy client library
|
|
39
13
|
*
|
|
40
14
|
* @class TeraFyServer
|
|
41
15
|
*/
|
|
42
|
-
|
|
43
16
|
export default class TeraFyServer {
|
|
44
17
|
|
|
45
18
|
/**
|
|
@@ -55,8 +28,9 @@ export default class TeraFyServer {
|
|
|
55
28
|
* @property {String} siteUrl The main site absolute URL
|
|
56
29
|
* @property {String} sitePathLogin Either an absolute URL or the relative path (taken from `siteUrl`) when trying to log in the user
|
|
57
30
|
* @property {Boolean} embedWorkaround Try to use `getUserViaEmbedWorkaround()` to force a login via popup if the user is running in local mode (see function docs for more details). This is toggled to false after the first run
|
|
31
|
+
* @property {String} ioUrl entrypoint for the TERA-io worker (used to get/set various TERA entities such as files)
|
|
58
32
|
*/
|
|
59
|
-
settings
|
|
33
|
+
settings = {
|
|
60
34
|
devMode: false,
|
|
61
35
|
verbosity: 9,
|
|
62
36
|
restrictOrigin: '*',
|
|
@@ -66,6 +40,7 @@ export default class TeraFyServer {
|
|
|
66
40
|
siteUrl: window.location.href,
|
|
67
41
|
sitePathLogin: '/login',
|
|
68
42
|
embedWorkaround: true,
|
|
43
|
+
ioUrl: app.service('$infra').workerUrls.io,
|
|
69
44
|
};
|
|
70
45
|
|
|
71
46
|
static SERVERMODE_NONE = 0;
|
|
@@ -84,20 +59,19 @@ export default class TeraFyServer {
|
|
|
84
59
|
*
|
|
85
60
|
* @returns {Object} A context, which is this instance extended with additional properties
|
|
86
61
|
*/
|
|
87
|
-
createContext(e
|
|
62
|
+
createContext(e) {
|
|
88
63
|
// Construct wrapper for sendRaw for this client
|
|
89
64
|
return mixin(this, {
|
|
90
65
|
messageEvent: e,
|
|
91
|
-
sendRaw(message
|
|
66
|
+
sendRaw(message) {
|
|
92
67
|
let payload;
|
|
93
68
|
try {
|
|
94
69
|
payload = {
|
|
95
70
|
TERA: 1,
|
|
96
71
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
97
72
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
} catch (err: any) { // Changed variable name e -> err
|
|
73
|
+
e.source.postMessage(payload, this.settings.restrictOrigin);
|
|
74
|
+
} catch (err) {
|
|
101
75
|
this.debug('ERROR', 1, 'Attempted to dispatch payload server(via reply)->client', {payload, e: err});
|
|
102
76
|
throw err;
|
|
103
77
|
}
|
|
@@ -112,14 +86,14 @@ export default class TeraFyServer {
|
|
|
112
86
|
*
|
|
113
87
|
* @returns {Object} A context, which is this instance extended with additional properties
|
|
114
88
|
*/
|
|
115
|
-
getClientContext()
|
|
89
|
+
getClientContext() {
|
|
116
90
|
switch (this.settings.serverMode) {
|
|
117
91
|
case TeraFyServer.SERVERMODE_NONE:
|
|
118
92
|
throw new Error('Client has not yet initiated communication');
|
|
119
93
|
case TeraFyServer.SERVERMODE_EMBEDDED:
|
|
120
94
|
// Server is inside an iFrame so we need to send messages to the window parent
|
|
121
95
|
return mixin(this, {
|
|
122
|
-
sendRaw(message
|
|
96
|
+
sendRaw(message) {
|
|
123
97
|
let payload;
|
|
124
98
|
try {
|
|
125
99
|
payload = {
|
|
@@ -127,7 +101,7 @@ export default class TeraFyServer {
|
|
|
127
101
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
128
102
|
};
|
|
129
103
|
window.parent.postMessage(payload, this.settings.restrictOrigin);
|
|
130
|
-
} catch (e
|
|
104
|
+
} catch (e) {
|
|
131
105
|
this.debug('ERROR', 1, 'Attempted to dispatch payload server(iframe)->cient(top level window)', {payload, e});
|
|
132
106
|
throw e;
|
|
133
107
|
}
|
|
@@ -136,27 +110,26 @@ export default class TeraFyServer {
|
|
|
136
110
|
case TeraFyServer.SERVERMODE_TERA:
|
|
137
111
|
case TeraFyServer.SERVERMODE_FRAME: {
|
|
138
112
|
// Server is the top-level window so we need to send messages to an embedded iFrame
|
|
139
|
-
const iFrame = document.querySelector
|
|
113
|
+
const iFrame = document.querySelector('iframe#external');
|
|
140
114
|
if (!iFrame) {
|
|
141
115
|
this.debug('INFO', 2, 'Cannot locate TERA-FY top-level->iFrame#external - maybe there is none');
|
|
142
116
|
return mixin(this, {
|
|
143
|
-
sendRaw(message
|
|
117
|
+
sendRaw(message) {
|
|
144
118
|
this.debug('INFO', 2, 'Sending broadcast to zero listening clients', {message});
|
|
145
119
|
},
|
|
146
120
|
});
|
|
147
121
|
}
|
|
148
122
|
|
|
149
123
|
return mixin(this, {
|
|
150
|
-
sendRaw(message
|
|
124
|
+
sendRaw(message) {
|
|
151
125
|
let payload;
|
|
152
126
|
try {
|
|
153
127
|
payload = {
|
|
154
128
|
TERA: 1,
|
|
155
129
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
156
130
|
};
|
|
157
|
-
// Check if contentWindow exists before posting
|
|
158
131
|
iFrame.contentWindow?.postMessage(payload, this.settings.restrictOrigin);
|
|
159
|
-
} catch (e
|
|
132
|
+
} catch (e) {
|
|
160
133
|
this.debug('ERROR', 1, 'Attempted to dispatch payload server(top level window)->cient(iframe)', {payload, e});
|
|
161
134
|
throw e;
|
|
162
135
|
}
|
|
@@ -176,7 +149,7 @@ export default class TeraFyServer {
|
|
|
176
149
|
*
|
|
177
150
|
* @type {MessageEvent}
|
|
178
151
|
*/
|
|
179
|
-
messageEvent
|
|
152
|
+
messageEvent = null;
|
|
180
153
|
|
|
181
154
|
|
|
182
155
|
/**
|
|
@@ -188,7 +161,7 @@ export default class TeraFyServer {
|
|
|
188
161
|
*
|
|
189
162
|
* @returns {Promise<*>} The resolved output of the server function
|
|
190
163
|
*/
|
|
191
|
-
senderRpc(method
|
|
164
|
+
senderRpc(method, ...args) {
|
|
192
165
|
if (!this.messageEvent) throw new Error('senderRpc() can only be used if given a context from `createContext()`');
|
|
193
166
|
|
|
194
167
|
// Create a context specific to this event to use its sendRaw
|
|
@@ -209,7 +182,7 @@ export default class TeraFyServer {
|
|
|
209
182
|
* @returns {Promise<Object>} Basic promise result
|
|
210
183
|
* @property {Date} date Server date
|
|
211
184
|
*/
|
|
212
|
-
handshake()
|
|
185
|
+
handshake() {
|
|
213
186
|
return Promise.resolve({
|
|
214
187
|
date: new Date(),
|
|
215
188
|
});
|
|
@@ -224,7 +197,7 @@ export default class TeraFyServer {
|
|
|
224
197
|
* @param {Object} message Message object to send
|
|
225
198
|
* @returns {Promise<*>} A promise which resolves when the operation has completed with the remote reply
|
|
226
199
|
*/
|
|
227
|
-
send(message
|
|
200
|
+
send(message) {
|
|
228
201
|
if (!this.messageEvent?.source) throw new Error('send() requires a messageEvent with a source');
|
|
229
202
|
|
|
230
203
|
const id = nanoid();
|
|
@@ -252,7 +225,7 @@ export default class TeraFyServer {
|
|
|
252
225
|
* @param {Object} message Message object to send
|
|
253
226
|
* @param {Window} sendVia Window context to dispatch the message via if its not the same as the regular window
|
|
254
227
|
*/
|
|
255
|
-
sendRaw(message
|
|
228
|
+
sendRaw(message, sendVia) {
|
|
256
229
|
let payload;
|
|
257
230
|
try {
|
|
258
231
|
payload = {
|
|
@@ -267,7 +240,7 @@ export default class TeraFyServer {
|
|
|
267
240
|
} else {
|
|
268
241
|
this.debug('WARN', 1, 'Cannot sendRaw, no target window (sendVia or parent) found.');
|
|
269
242
|
}
|
|
270
|
-
} catch (e
|
|
243
|
+
} catch (e) {
|
|
271
244
|
this.debug('ERROR', 2, 'Attempted to dispatch response server->client', payload);
|
|
272
245
|
this.debug('ERROR', 2, 'Message compose server->client:', e);
|
|
273
246
|
}
|
|
@@ -279,7 +252,7 @@ export default class TeraFyServer {
|
|
|
279
252
|
*
|
|
280
253
|
* @param {String} mode The server mode to set to
|
|
281
254
|
*/
|
|
282
|
-
setServerMode(mode
|
|
255
|
+
setServerMode(mode) {
|
|
283
256
|
switch (mode) {
|
|
284
257
|
case 'embedded':
|
|
285
258
|
this.settings.serverMode = TeraFyServer.SERVERMODE_EMBEDDED;
|
|
@@ -301,7 +274,7 @@ export default class TeraFyServer {
|
|
|
301
274
|
*
|
|
302
275
|
* @param {MessageEvent} rawMessage Raw message event to process
|
|
303
276
|
*/
|
|
304
|
-
acceptMessage(rawMessage
|
|
277
|
+
acceptMessage(rawMessage) {
|
|
305
278
|
// Ignore messages from the same origin (potential loops)
|
|
306
279
|
if (typeof window !== 'undefined' && rawMessage.origin === window.location.origin) return;
|
|
307
280
|
|
|
@@ -320,21 +293,18 @@ export default class TeraFyServer {
|
|
|
320
293
|
}
|
|
321
294
|
delete this.acceptPostboxes[message.id]; // Clean up postbox
|
|
322
295
|
} else if (message.action == 'rpc' && typeof message.method === 'string') { // Relay RPC calls
|
|
323
|
-
const method = message.method
|
|
324
|
-
|
|
325
|
-
if (typeof (this as any)[method] === 'function') {
|
|
296
|
+
const method = message.method;
|
|
297
|
+
if (typeof this[method] === 'function') {
|
|
326
298
|
// Create context for this specific message event
|
|
327
299
|
const context = this.createContext(rawMessage);
|
|
328
300
|
// Store the event temporarily for potential use in send() called by the RPC method
|
|
329
301
|
context.messageEvent = rawMessage;
|
|
330
|
-
return
|
|
302
|
+
return this[method].apply(context, message.args || []);
|
|
331
303
|
} else {
|
|
332
304
|
throw new Error(`Unknown RPC method "${method}"`);
|
|
333
305
|
}
|
|
334
306
|
} else {
|
|
335
307
|
this.debug('ERROR', 2, 'Unexpected incoming TERA-FY SERVER message', {message});
|
|
336
|
-
// Don't throw, just ignore unknown formats silently? Or throw?
|
|
337
|
-
// throw new Error('Unknown message format');
|
|
338
308
|
}
|
|
339
309
|
})
|
|
340
310
|
.then(response => {
|
|
@@ -367,7 +337,7 @@ export default class TeraFyServer {
|
|
|
367
337
|
/**
|
|
368
338
|
* Listening postboxes, these correspond to outgoing message IDs that expect a response
|
|
369
339
|
*/
|
|
370
|
-
acceptPostboxes
|
|
340
|
+
acceptPostboxes = {};
|
|
371
341
|
|
|
372
342
|
|
|
373
343
|
/**
|
|
@@ -379,7 +349,7 @@ export default class TeraFyServer {
|
|
|
379
349
|
*
|
|
380
350
|
* @returns {Promise<*>} A promise which resolves with the resulting inner callback payload
|
|
381
351
|
*/
|
|
382
|
-
requestFocus(cb
|
|
352
|
+
requestFocus(cb) {
|
|
383
353
|
// Ensure messageEvent is set before calling senderRpc
|
|
384
354
|
if (!this.messageEvent && this.settings.serverMode != TeraFyServer.SERVERMODE_TERA) {
|
|
385
355
|
console.warn("requestFocus called without a messageEvent context. Cannot toggle focus.");
|
|
@@ -404,7 +374,7 @@ export default class TeraFyServer {
|
|
|
404
374
|
* @param {...*} [args] Optional event payload to send
|
|
405
375
|
* @returns {Promise} A promise which resolves when the transmission has completed
|
|
406
376
|
*/
|
|
407
|
-
emitClients(event
|
|
377
|
+
emitClients(event, ...args) {
|
|
408
378
|
// Use getClientContext to get the appropriate sendRaw method
|
|
409
379
|
const context = this.getClientContext();
|
|
410
380
|
return context.sendRaw({
|
|
@@ -421,7 +391,7 @@ export default class TeraFyServer {
|
|
|
421
391
|
*
|
|
422
392
|
* @param {Number} verbosity The desired server verbosity level
|
|
423
393
|
*/
|
|
424
|
-
setServerVerbosity(verbosity
|
|
394
|
+
setServerVerbosity(verbosity) {
|
|
425
395
|
this.settings.verbosity = +verbosity;
|
|
426
396
|
this.debug('INFO', 1, 'Server verbosity set to', this.settings.verbosity);
|
|
427
397
|
}
|
|
@@ -447,7 +417,7 @@ export default class TeraFyServer {
|
|
|
447
417
|
*
|
|
448
418
|
* @returns {Promise<User>} The current logged in user or null if none
|
|
449
419
|
*/
|
|
450
|
-
getUser(options
|
|
420
|
+
getUser(options) {
|
|
451
421
|
const settings = {
|
|
452
422
|
forceRetry: false,
|
|
453
423
|
waitPromises: true,
|
|
@@ -479,7 +449,7 @@ export default class TeraFyServer {
|
|
|
479
449
|
}
|
|
480
450
|
: null
|
|
481
451
|
)
|
|
482
|
-
.catch(
|
|
452
|
+
.catch(e => {
|
|
483
453
|
console.warn('getUser() catch', e);
|
|
484
454
|
return null; // Return null on error
|
|
485
455
|
})
|
|
@@ -493,9 +463,8 @@ export default class TeraFyServer {
|
|
|
493
463
|
*
|
|
494
464
|
* @returns {Promise<User>} A promise which will resolve if the there is a user and they are logged in
|
|
495
465
|
*/
|
|
496
|
-
requireUser()
|
|
497
|
-
|
|
498
|
-
let user: any; // Last getUser() response
|
|
466
|
+
requireUser() {
|
|
467
|
+
let user; // Last getUser() response
|
|
499
468
|
return Promise.resolve() // NOTE: This promise is upside down, it only continues down the chain if the user is NOT valid, otherwise it throws to exit
|
|
500
469
|
.then(()=> this.getUser())
|
|
501
470
|
.then(res => user = res)
|
|
@@ -554,31 +523,23 @@ export default class TeraFyServer {
|
|
|
554
523
|
/**
|
|
555
524
|
* Provide an object of credentials for 3rd party services like Firebase/Supabase
|
|
556
525
|
*
|
|
557
|
-
* @returns An object containing 3rd party service credentials
|
|
526
|
+
* @returns {Object} An object containing 3rd party service credentials
|
|
558
527
|
*/
|
|
559
|
-
getCredentials()
|
|
560
|
-
firebaseApiKey: string;
|
|
561
|
-
firebaseAppId: string;
|
|
562
|
-
firebaseAuthDomain: string;
|
|
563
|
-
firebaseProjectId: string;
|
|
564
|
-
kindeClientId: string;
|
|
565
|
-
kindeDomain: string;
|
|
566
|
-
logrocketProject: string;
|
|
567
|
-
supabaseKey: string;
|
|
568
|
-
supabaseUrl: string;
|
|
569
|
-
} {
|
|
528
|
+
getCredentials() {
|
|
570
529
|
return app.service('$auth').credentials;
|
|
571
530
|
}
|
|
572
531
|
|
|
532
|
+
|
|
573
533
|
/**
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
getKindeToken()
|
|
534
|
+
* Get auth credentials for kinde
|
|
535
|
+
* @returns {Promise<String>} The kinde auth token
|
|
536
|
+
*/
|
|
537
|
+
getKindeToken() {
|
|
578
538
|
const $auth = app.service('$auth');
|
|
579
539
|
return $auth.kinde.getToken();
|
|
580
540
|
}
|
|
581
541
|
|
|
542
|
+
|
|
582
543
|
/**
|
|
583
544
|
* In embed mode only - create a popup window and try to auth via that
|
|
584
545
|
*
|
|
@@ -595,10 +556,10 @@ export default class TeraFyServer {
|
|
|
595
556
|
*
|
|
596
557
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
597
558
|
*/
|
|
598
|
-
async getUserViaEmbedWorkaround()
|
|
559
|
+
async getUserViaEmbedWorkaround() {
|
|
599
560
|
this.debug('INFO', 4, 'Attempting to use getUserViaEmbedWorkaround()');
|
|
600
561
|
|
|
601
|
-
let lsState
|
|
562
|
+
let lsState = window.localStorage.getItem('tera.embedUser');
|
|
602
563
|
if (lsState) {
|
|
603
564
|
this.debug('INFO', 4, 'Using localStorage state');
|
|
604
565
|
try {
|
|
@@ -618,7 +579,7 @@ export default class TeraFyServer {
|
|
|
618
579
|
// Force refresh projects against the new user
|
|
619
580
|
await app.service('$projects').refresh();
|
|
620
581
|
return;
|
|
621
|
-
} catch (e
|
|
582
|
+
} catch (e) {
|
|
622
583
|
throw new Error(`Failed to decode local dev state - ${e.toString()}`, {cause: e});
|
|
623
584
|
}
|
|
624
585
|
}
|
|
@@ -635,10 +596,10 @@ export default class TeraFyServer {
|
|
|
635
596
|
);
|
|
636
597
|
|
|
637
598
|
// Create a deferred promise which will (eventually) resolve when the downstream window signals its ready
|
|
638
|
-
const waitOnWindowAuth =
|
|
599
|
+
const waitOnWindowAuth = Promise.withResolvers();
|
|
639
600
|
|
|
640
601
|
// Create a listener for the message from the downstream window to resolve the promise
|
|
641
|
-
const listenMessages = ({data}
|
|
602
|
+
const listenMessages = ({data}) => {
|
|
642
603
|
this.debug('INFO', 3, 'Recieved message from popup window', {data});
|
|
643
604
|
if (data.TERA && data.action == 'popupUserState' && data.user) { // Signal sent from landing page - we're logged in, yey!
|
|
644
605
|
const $auth = app.service('$auth');
|
|
@@ -652,7 +613,7 @@ export default class TeraFyServer {
|
|
|
652
613
|
this.debug('INFO', 3, 'Received user auth from popup window', {'$auth.user': $auth.user});
|
|
653
614
|
|
|
654
615
|
// Store local copy of user image - this only applies to dev mode (localhost connecting to embed) so we can ignore the security implications here
|
|
655
|
-
Promise.resolve()
|
|
616
|
+
Promise.resolve()
|
|
656
617
|
.then(()=> this.getUser({
|
|
657
618
|
forceRetry: false, // Avoid loops
|
|
658
619
|
waitPromises: false, // We have a partially resolved state so we don't care about outer promises resolving
|
|
@@ -705,7 +666,7 @@ export default class TeraFyServer {
|
|
|
705
666
|
*
|
|
706
667
|
* @returns {Promise<Project|null>} The currently active project, if any
|
|
707
668
|
*/
|
|
708
|
-
getProject()
|
|
669
|
+
getProject() {
|
|
709
670
|
const $projects = app.service('$projects');
|
|
710
671
|
|
|
711
672
|
return $projects.promise()
|
|
@@ -726,11 +687,11 @@ export default class TeraFyServer {
|
|
|
726
687
|
*
|
|
727
688
|
* @returns {Promise<Array<Project>>} Collection of projects the user has access to
|
|
728
689
|
*/
|
|
729
|
-
getProjects()
|
|
690
|
+
getProjects() {
|
|
730
691
|
const $projects = app.service('$projects');
|
|
731
692
|
|
|
732
693
|
return $projects.promise()
|
|
733
|
-
.then(()=> $projects.list.map(
|
|
694
|
+
.then(()=> $projects.list.map(project => ({
|
|
734
695
|
id: project.id,
|
|
735
696
|
name: project.name,
|
|
736
697
|
created: project.created,
|
|
@@ -745,7 +706,7 @@ export default class TeraFyServer {
|
|
|
745
706
|
* @param {Object|String} project The project to set as active - either the full Project object or its ID
|
|
746
707
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
747
708
|
*/
|
|
748
|
-
setActiveProject(project
|
|
709
|
+
setActiveProject(project) {
|
|
749
710
|
return app.service('$projects').setActive(project);
|
|
750
711
|
}
|
|
751
712
|
|
|
@@ -763,7 +724,7 @@ export default class TeraFyServer {
|
|
|
763
724
|
*
|
|
764
725
|
* @returns {Promise<Project>} The active project
|
|
765
726
|
*/
|
|
766
|
-
requireProject(options
|
|
727
|
+
requireProject(options) {
|
|
767
728
|
const settings = {
|
|
768
729
|
autoRequireUser: true,
|
|
769
730
|
autoSetActiveProject: true,
|
|
@@ -779,35 +740,36 @@ export default class TeraFyServer {
|
|
|
779
740
|
.then(active => {
|
|
780
741
|
if (active) return active; // Use active project
|
|
781
742
|
|
|
782
|
-
return
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
743
|
+
return Promise.resolve()
|
|
744
|
+
.then(()=> new Promise((resolve, reject) => {
|
|
745
|
+
const askProject = () => Promise.resolve()
|
|
746
|
+
.then(()=> this.selectProject({
|
|
747
|
+
allowCancel: false,
|
|
748
|
+
}))
|
|
749
|
+
.then(project => resolve(project))
|
|
750
|
+
.catch(e => {
|
|
751
|
+
if (e == 'cancel' || e === 'CANCEL') { // Handle string 'cancel' or rejected 'CANCEL'
|
|
752
|
+
return Promise.resolve()
|
|
753
|
+
.then(()=> this.requestFocus(()=>
|
|
754
|
+
app.service('$prompt').dialog({
|
|
755
|
+
title: settings.noSelectTitle,
|
|
756
|
+
body: settings.noSelectBody,
|
|
757
|
+
buttons: ['ok'],
|
|
758
|
+
})
|
|
759
|
+
))
|
|
760
|
+
.then(()=> askProject())
|
|
761
|
+
.catch(reject)
|
|
762
|
+
} else {
|
|
763
|
+
reject(e);
|
|
764
|
+
}
|
|
765
|
+
})
|
|
803
766
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
})
|
|
767
|
+
askProject(); // Kick off intial project loop
|
|
768
|
+
}))
|
|
769
|
+
.then(async project => {
|
|
770
|
+
if (settings.autoSetActiveProject) await this.setActiveProject(project);
|
|
771
|
+
return project;
|
|
772
|
+
})
|
|
811
773
|
})
|
|
812
774
|
}
|
|
813
775
|
|
|
@@ -822,7 +784,7 @@ export default class TeraFyServer {
|
|
|
822
784
|
*
|
|
823
785
|
* @returns {Promise<Project>} The active project
|
|
824
786
|
*/
|
|
825
|
-
selectProject(options
|
|
787
|
+
selectProject(options) {
|
|
826
788
|
const settings = {
|
|
827
789
|
title: 'Select a project to work with',
|
|
828
790
|
allowCancel: true,
|
|
@@ -838,7 +800,7 @@ export default class TeraFyServer {
|
|
|
838
800
|
buttons: settings.allowCancel ? ['cancel'] : [],
|
|
839
801
|
})
|
|
840
802
|
))
|
|
841
|
-
.then(
|
|
803
|
+
.then(project => settings.setActive
|
|
842
804
|
? this.setActiveProject(project)
|
|
843
805
|
.then(()=> project)
|
|
844
806
|
: project
|
|
@@ -857,7 +819,7 @@ export default class TeraFyServer {
|
|
|
857
819
|
*
|
|
858
820
|
* @returns {Promise<Object>} A promise which resolves to the namespace POJO state
|
|
859
821
|
*/
|
|
860
|
-
getNamespace(name
|
|
822
|
+
getNamespace(name) {
|
|
861
823
|
if (!/^[\w-]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
|
|
862
824
|
|
|
863
825
|
return app.service('$sync').getSnapshot(`project_namespaces::${app.service('$projects').active.id}::${name}`);
|
|
@@ -875,7 +837,7 @@ export default class TeraFyServer {
|
|
|
875
837
|
*
|
|
876
838
|
* @returns {Promise<Object>} A promise which resolves to the namespace POJO state
|
|
877
839
|
*/
|
|
878
|
-
setNamespace(name
|
|
840
|
+
setNamespace(name, state, options) {
|
|
879
841
|
if (!/^[\w-]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
|
|
880
842
|
if (typeof state != 'object') throw new Error('State must be an object');
|
|
881
843
|
|
|
@@ -891,7 +853,7 @@ export default class TeraFyServer {
|
|
|
891
853
|
* @returns {Promise<Array<Object>>} Collection of available namespaces for the current project
|
|
892
854
|
* @property {String} name The name of the namespace
|
|
893
855
|
*/
|
|
894
|
-
listNamespaces()
|
|
856
|
+
listNamespaces() {
|
|
895
857
|
return app.service('$projects').listNamespaces();
|
|
896
858
|
}
|
|
897
859
|
// }}}
|
|
@@ -907,7 +869,7 @@ export default class TeraFyServer {
|
|
|
907
869
|
*
|
|
908
870
|
* @returns {Promise<Object>} The current project state snapshot
|
|
909
871
|
*/
|
|
910
|
-
getProjectState(options
|
|
872
|
+
getProjectState(options) {
|
|
911
873
|
const settings = {
|
|
912
874
|
autoRequire: true,
|
|
913
875
|
paths: null,
|
|
@@ -940,7 +902,7 @@ export default class TeraFyServer {
|
|
|
940
902
|
*
|
|
941
903
|
* @returns {Promise<*>} A promise which resolves to `value` when the operation has been dispatched to the server and saved
|
|
942
904
|
*/
|
|
943
|
-
setProjectState(path
|
|
905
|
+
setProjectState(path, value, options) {
|
|
944
906
|
const settings = {
|
|
945
907
|
strategy: 'set',
|
|
946
908
|
...options,
|
|
@@ -980,12 +942,12 @@ export default class TeraFyServer {
|
|
|
980
942
|
*
|
|
981
943
|
* @returns {Promise<*>} A promise which resolves to the eventual input value after defaults have been applied
|
|
982
944
|
*/
|
|
983
|
-
setProjectStateDefaults(path
|
|
984
|
-
let settings = {
|
|
945
|
+
setProjectStateDefaults(path, value, options) {
|
|
946
|
+
let settings = {...options}; // Initialize settings from the third argument if present
|
|
985
947
|
if (!app.service('$projects').active) throw new Error('No active project');
|
|
986
948
|
|
|
987
949
|
const target = app.service('$projects').active;
|
|
988
|
-
let actualValue
|
|
950
|
+
let actualValue;
|
|
989
951
|
|
|
990
952
|
if (typeof path == 'string' || Array.isArray(path)) { // Called as (path, value, options?) Set sub-object
|
|
991
953
|
actualValue = value;
|
|
@@ -1015,7 +977,7 @@ export default class TeraFyServer {
|
|
|
1015
977
|
*
|
|
1016
978
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
1017
979
|
*/
|
|
1018
|
-
setProjectStateRefresh()
|
|
980
|
+
setProjectStateRefresh() {
|
|
1019
981
|
this.debug('INFO', 1, 'Force project state refresh!');
|
|
1020
982
|
if (!app.service('$projects').active) throw new Error('No active project');
|
|
1021
983
|
return app.service('$projects').active.$read({force: true})
|
|
@@ -1045,6 +1007,7 @@ export default class TeraFyServer {
|
|
|
1045
1007
|
* @property {String} mime The associated mime type for the file
|
|
1046
1008
|
*/
|
|
1047
1009
|
|
|
1010
|
+
|
|
1048
1011
|
/**
|
|
1049
1012
|
* Data structure for a file filter
|
|
1050
1013
|
* @class FileFilters
|
|
@@ -1055,6 +1018,7 @@ export default class TeraFyServer {
|
|
|
1055
1018
|
* @property {String} [ext] CSV of @momsfriendlydevco/match expressions to filter the file extension by
|
|
1056
1019
|
*/
|
|
1057
1020
|
|
|
1021
|
+
|
|
1058
1022
|
/**
|
|
1059
1023
|
* Prompt the user to select a library to operate on
|
|
1060
1024
|
*
|
|
@@ -1073,7 +1037,7 @@ export default class TeraFyServer {
|
|
|
1073
1037
|
*
|
|
1074
1038
|
* @returns {Promise<ProjectFile>} The eventually selected file, if in save mode new files are created as stubs
|
|
1075
1039
|
*/
|
|
1076
|
-
selectProjectFile(options
|
|
1040
|
+
selectProjectFile(options) {
|
|
1077
1041
|
const settings = {
|
|
1078
1042
|
title: 'Select a file',
|
|
1079
1043
|
hint: null,
|
|
@@ -1109,10 +1073,10 @@ export default class TeraFyServer {
|
|
|
1109
1073
|
showHiddenFilesProp: settings.showHiddenFiles,
|
|
1110
1074
|
},
|
|
1111
1075
|
componentEvents: {
|
|
1112
|
-
fileSave(file
|
|
1076
|
+
fileSave(file) {
|
|
1113
1077
|
app.service('$prompt').close(true, file);
|
|
1114
1078
|
},
|
|
1115
|
-
fileSelect(file
|
|
1079
|
+
fileSelect(file) {
|
|
1116
1080
|
app.service('$prompt').close(true, file);
|
|
1117
1081
|
},
|
|
1118
1082
|
},
|
|
@@ -1133,7 +1097,7 @@ export default class TeraFyServer {
|
|
|
1133
1097
|
*
|
|
1134
1098
|
* @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
|
|
1135
1099
|
*/
|
|
1136
|
-
getProjectFiles(options
|
|
1100
|
+
getProjectFiles(options) {
|
|
1137
1101
|
const settings = {
|
|
1138
1102
|
autoRequire: true,
|
|
1139
1103
|
lazy: true,
|
|
@@ -1144,7 +1108,7 @@ export default class TeraFyServer {
|
|
|
1144
1108
|
return Promise.resolve()
|
|
1145
1109
|
.then(()=> app.service('$projects').promise())
|
|
1146
1110
|
.then(()=> settings.autoRequire && this.requireProject())
|
|
1147
|
-
.then(()=>
|
|
1111
|
+
.then(()=> /* eslint-disable @stylistic/indent */
|
|
1148
1112
|
!app.service('$projects').activeFiles // List of project files not ready yet
|
|
1149
1113
|
|| app.service('$projects').activeFiles.length == 0 // OR if we have no files in the cache
|
|
1150
1114
|
|| !settings.lazy // OR lazy/cache use is disabled
|
|
@@ -1167,7 +1131,7 @@ export default class TeraFyServer {
|
|
|
1167
1131
|
*
|
|
1168
1132
|
* @returns {Promise<ProjectFile>} The eventual fetched ProjectFile (or requested subkey)
|
|
1169
1133
|
*/
|
|
1170
|
-
getProjectFile(name
|
|
1134
|
+
getProjectFile(name, options) {
|
|
1171
1135
|
const settings = {
|
|
1172
1136
|
subkey: null,
|
|
1173
1137
|
cache: true,
|
|
@@ -1175,7 +1139,7 @@ export default class TeraFyServer {
|
|
|
1175
1139
|
};
|
|
1176
1140
|
|
|
1177
1141
|
// This helper function performs a depth-first search on the file tree.
|
|
1178
|
-
const findFileRecursively = (items
|
|
1142
|
+
const findFileRecursively = (items, path) => {
|
|
1179
1143
|
// Iterate over each item at the current level (files or folders)
|
|
1180
1144
|
for (const item of items) {
|
|
1181
1145
|
console.log(item);
|
|
@@ -1194,7 +1158,7 @@ export default class TeraFyServer {
|
|
|
1194
1158
|
}
|
|
1195
1159
|
}
|
|
1196
1160
|
// If the loop completes, the file was not found at this level or any sub-levels.
|
|
1197
|
-
return
|
|
1161
|
+
return;
|
|
1198
1162
|
};
|
|
1199
1163
|
|
|
1200
1164
|
return Promise.resolve()
|
|
@@ -1207,13 +1171,14 @@ export default class TeraFyServer {
|
|
|
1207
1171
|
})
|
|
1208
1172
|
: app.service('$projects').activeFiles // Otherwise use file cache
|
|
1209
1173
|
)
|
|
1210
|
-
.then(
|
|
1174
|
+
.then(files => {
|
|
1211
1175
|
// Recursively search for the file to handle searching folders
|
|
1212
1176
|
return findFileRecursively(files, name);
|
|
1213
1177
|
})
|
|
1214
|
-
.then(
|
|
1178
|
+
.then(file => file && settings.subkey ? file[settings.subkey] : file)
|
|
1215
1179
|
}
|
|
1216
1180
|
|
|
1181
|
+
|
|
1217
1182
|
/**
|
|
1218
1183
|
* Fetch the raw contents of a file by its ID
|
|
1219
1184
|
*
|
|
@@ -1224,7 +1189,7 @@ export default class TeraFyServer {
|
|
|
1224
1189
|
*
|
|
1225
1190
|
* @returns {*} The file contents in the requested format
|
|
1226
1191
|
*/
|
|
1227
|
-
getProjectFileContents(id
|
|
1192
|
+
getProjectFileContents(id, options) {
|
|
1228
1193
|
const settings = {
|
|
1229
1194
|
format: 'blob',
|
|
1230
1195
|
...options,
|
|
@@ -1245,7 +1210,7 @@ export default class TeraFyServer {
|
|
|
1245
1210
|
* @param {String} name The name + relative directory path component
|
|
1246
1211
|
* @returns {Promise<ProjectFile>} The eventual ProjectFile created
|
|
1247
1212
|
*/
|
|
1248
|
-
createProjectFile(name
|
|
1213
|
+
createProjectFile(name) {
|
|
1249
1214
|
return Promise.resolve()
|
|
1250
1215
|
.then(()=> app.service('$supabase').fileUpload(app.service('$projects').convertRelativePath(name), {
|
|
1251
1216
|
file: new Blob([''], {type: 'text/plain'}),
|
|
@@ -1258,9 +1223,10 @@ export default class TeraFyServer {
|
|
|
1258
1223
|
.then(()=> this.getProjectFile(name, {
|
|
1259
1224
|
cache: false, // Force cache to update, as this is a new file
|
|
1260
1225
|
}))
|
|
1261
|
-
.then(
|
|
1226
|
+
.then(file => file || Promise.reject(`Could not create new file "${name}"`))
|
|
1262
1227
|
}
|
|
1263
1228
|
|
|
1229
|
+
|
|
1264
1230
|
/**
|
|
1265
1231
|
* Moves a project file to a new name/path.
|
|
1266
1232
|
* The file's unique ID (UUID) remains the same, but its 'name' (relative path) and associated properties will be updated.
|
|
@@ -1271,14 +1237,11 @@ export default class TeraFyServer {
|
|
|
1271
1237
|
*
|
|
1272
1238
|
* @param {Object} [options] Additional options to mutate behaviour.
|
|
1273
1239
|
* @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing.
|
|
1274
|
-
* @param {Boolean} [options.overwrite=true] If true (default), moving a file to a `newName` that already exists will overwrite the existing file.
|
|
1275
|
-
* This aligns with the default behavior of the underlying Supabase storage `move` operation.
|
|
1276
|
-
* If set to false, the function would ideally check and prevent overwrite, but current implementation relies on underlying storage behavior.
|
|
1240
|
+
* @param {Boolean} [options.overwrite=true] If true (default), moving a file to a `newName` that already exists will overwrite the existing file. This aligns with the default behavior of the underlying Supabase storage `move` operation. If set to false, the function would ideally check and prevent overwrite, but current implementation relies on underlying storage behavior.
|
|
1277
1241
|
*
|
|
1278
|
-
* @returns {Promise<ProjectFile
|
|
1279
|
-
* or null if the file could not be located post-move (e.g., if its ID changed unexpectedly or it was deleted).
|
|
1242
|
+
* @returns {Promise<ProjectFile|null>} A promise which resolves to the updated ProjectFile object for the moved file if found after the operation, or null if the file could not be located post-move (e.g., if its ID changed unexpectedly or it was deleted).
|
|
1280
1243
|
*/
|
|
1281
|
-
async moveProjectFile(sourceId
|
|
1244
|
+
async moveProjectFile(sourceId, newName, options) {
|
|
1282
1245
|
const settings = {
|
|
1283
1246
|
autoRequire: true,
|
|
1284
1247
|
overwrite: true, // Matches Supabase client's default behavior for move.
|
|
@@ -1320,7 +1283,7 @@ export default class TeraFyServer {
|
|
|
1320
1283
|
if (sourceStoragePath === targetStoragePath) {
|
|
1321
1284
|
this.debug('INFO', 2, `moveProjectFile: Source path "${sourceStoragePath}" and target path "${targetStoragePath}" are identical. No move operation needed.`);
|
|
1322
1285
|
// Attempt to find the file by ID, as its name should match newName if paths are identical.
|
|
1323
|
-
const currentFile = projectService.activeFiles?.find(
|
|
1286
|
+
const currentFile = projectService.activeFiles?.find(f => f.id === sourceId);
|
|
1324
1287
|
return currentFile || null;
|
|
1325
1288
|
}
|
|
1326
1289
|
|
|
@@ -1332,18 +1295,18 @@ export default class TeraFyServer {
|
|
|
1332
1295
|
// The standard Supabase `move` overwrites by default. If settings.overwrite is false,
|
|
1333
1296
|
// a more complex $supabase.fileMove or a pre-check here would be needed.
|
|
1334
1297
|
// For now, we pass the conceptual overwrite intent, but actual behavior depends on $supabase.fileMove.
|
|
1335
|
-
await supabaseService.fileMove(sourceStoragePath, targetStoragePath, {
|
|
1298
|
+
await supabaseService.fileMove(sourceStoragePath, targetStoragePath, {overwrite: settings.overwrite});
|
|
1336
1299
|
|
|
1337
1300
|
this.debug('INFO', 2, `Project file successfully moved from "${sourceStoragePath}" to "${targetStoragePath}".`);
|
|
1338
1301
|
|
|
1339
1302
|
// After a successful move, refresh the local cache of project files.
|
|
1340
|
-
await projectService.refreshFiles({
|
|
1303
|
+
await projectService.refreshFiles({lazy: false});
|
|
1341
1304
|
this.debug('INFO', 3, 'Project files list refreshed after move operation.');
|
|
1342
1305
|
|
|
1343
1306
|
// The file's unique ID (UUID) should remain the same.
|
|
1344
1307
|
// Find the updated ProjectFile entry in the refreshed cache using the original sourceId.
|
|
1345
|
-
const refreshedFiles = projectService.activeFiles
|
|
1346
|
-
const movedFileEntry = refreshedFiles?.find(
|
|
1308
|
+
const refreshedFiles = projectService.activeFiles;
|
|
1309
|
+
const movedFileEntry = refreshedFiles?.find(file => file.id === sourceId);
|
|
1347
1310
|
|
|
1348
1311
|
if (movedFileEntry) {
|
|
1349
1312
|
if (movedFileEntry.name !== newName) {
|
|
@@ -1356,13 +1319,14 @@ export default class TeraFyServer {
|
|
|
1356
1319
|
this.debug('ERROR', 1, `moveProjectFile: Failed to find file with original ID "${sourceId}" in the refreshed list after the move. The file might have been deleted or its ID changed unexpectedly.`);
|
|
1357
1320
|
return null;
|
|
1358
1321
|
}
|
|
1359
|
-
} catch (error
|
|
1322
|
+
} catch (error) {
|
|
1360
1323
|
this.debug('ERROR', 1, `Error during project file move from "${sourceStoragePath}" to "${targetStoragePath}":`, error);
|
|
1361
1324
|
// Re-throw a more specific error or the original error for the caller to handle.
|
|
1362
1325
|
throw new Error(`Failed to move project file "${sourceId}" to "${newName}": ${error.message || String(error)}`, {cause: error});
|
|
1363
1326
|
}
|
|
1364
1327
|
}
|
|
1365
1328
|
|
|
1329
|
+
|
|
1366
1330
|
/**
|
|
1367
1331
|
* Remove a project file by its ID
|
|
1368
1332
|
*
|
|
@@ -1370,7 +1334,7 @@ export default class TeraFyServer {
|
|
|
1370
1334
|
*
|
|
1371
1335
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
1372
1336
|
*/
|
|
1373
|
-
deleteProjectFile(id
|
|
1337
|
+
deleteProjectFile(id) {
|
|
1374
1338
|
return app.service('$supabase').fileRemove(app.service('$projects').decodeFilePath(id))
|
|
1375
1339
|
.then(()=> app.service('$projects').refreshFiles({ // Force a local file list update
|
|
1376
1340
|
lazy: false,
|
|
@@ -1394,26 +1358,26 @@ export default class TeraFyServer {
|
|
|
1394
1358
|
*
|
|
1395
1359
|
* @returns {Promise} A promise which will resolve when the write operation has completed
|
|
1396
1360
|
*/
|
|
1397
|
-
setProjectFileContents(id
|
|
1361
|
+
setProjectFileContents(id, contents, options) {
|
|
1398
1362
|
// Argument Mangling Logic (Simplified)
|
|
1399
|
-
let fileId
|
|
1400
|
-
let fileContents
|
|
1401
|
-
let mergedOptions
|
|
1363
|
+
let fileId = null; // eslint-disable-line no-useless-assignment
|
|
1364
|
+
let fileContents;
|
|
1365
|
+
let mergedOptions;
|
|
1402
1366
|
|
|
1403
1367
|
if (typeof id === 'string') {
|
|
1404
1368
|
fileId = id;
|
|
1405
1369
|
fileContents = contents;
|
|
1406
|
-
mergedOptions = {
|
|
1370
|
+
mergedOptions = {...options};
|
|
1407
1371
|
} else if (id !== null && typeof id === 'object' && !(id instanceof Blob) && !(id instanceof File) && !(id instanceof FormData) && !Array.isArray(id)) {
|
|
1408
1372
|
// Assuming called as (optionsObject)
|
|
1409
|
-
mergedOptions = {
|
|
1373
|
+
mergedOptions = {...id};
|
|
1410
1374
|
fileId = mergedOptions.id ?? null;
|
|
1411
1375
|
fileContents = mergedOptions.contents;
|
|
1412
1376
|
} else {
|
|
1413
1377
|
// Assuming called as (contents, options)
|
|
1414
1378
|
fileId = options?.id ?? null; // Check options for id if provided
|
|
1415
1379
|
fileContents = id; // First arg is contents
|
|
1416
|
-
mergedOptions = {
|
|
1380
|
+
mergedOptions = {...contents}; // Second arg is options
|
|
1417
1381
|
}
|
|
1418
1382
|
|
|
1419
1383
|
if (fileContents === undefined) throw new Error('setProjectFileContents requires contents to save.');
|
|
@@ -1434,7 +1398,7 @@ export default class TeraFyServer {
|
|
|
1434
1398
|
if (settings.autoRequire)
|
|
1435
1399
|
return this.requireProject();
|
|
1436
1400
|
})
|
|
1437
|
-
.then(()
|
|
1401
|
+
.then(()=> {
|
|
1438
1402
|
if (settings.id) {
|
|
1439
1403
|
// Validate the provided ID exists? Optional, but good practice.
|
|
1440
1404
|
// For now, just return it assuming it's valid.
|
|
@@ -1448,12 +1412,12 @@ export default class TeraFyServer {
|
|
|
1448
1412
|
saveFilename: settings.filename,
|
|
1449
1413
|
autoRequire: false, // Handled above anyway
|
|
1450
1414
|
})
|
|
1451
|
-
.then(
|
|
1415
|
+
.then(file => {
|
|
1452
1416
|
if (!file || !file.id) throw new Error('File selection cancelled or failed.');
|
|
1453
1417
|
return file.id; // Return the selected file ID
|
|
1454
1418
|
});
|
|
1455
1419
|
})
|
|
1456
|
-
.then(
|
|
1420
|
+
.then(resolvedFileId => {
|
|
1457
1421
|
settings.id = resolvedFileId; // Update settings.id with the resolved/validated ID
|
|
1458
1422
|
if (!settings.id) throw new Error("Could not determine file ID to save to."); // Final check
|
|
1459
1423
|
return app.service('$supabase').fileSet(app.service('$projects').decodeFilePath(settings.id), fileContents, {
|
|
@@ -1478,7 +1442,7 @@ export default class TeraFyServer {
|
|
|
1478
1442
|
* @returns {Promise<void>} A promise that resolves when the folder is created or ensured.
|
|
1479
1443
|
* @throws {Error} If no project is active (and autoRequire is false), or if folderPath is invalid, or if the creation fails.
|
|
1480
1444
|
*/
|
|
1481
|
-
async createProjectFolder(folderPath
|
|
1445
|
+
async createProjectFolder(folderPath, options) {
|
|
1482
1446
|
const settings = {
|
|
1483
1447
|
autoRequire: true,
|
|
1484
1448
|
...options,
|
|
@@ -1514,7 +1478,7 @@ export default class TeraFyServer {
|
|
|
1514
1478
|
|
|
1515
1479
|
try {
|
|
1516
1480
|
await app.service('$supabase').fileUpload(supabasePath, {
|
|
1517
|
-
file: new Blob([''], {
|
|
1481
|
+
file: new Blob([''], {type: 'text/plain'}), // Consistent with createProjectFile
|
|
1518
1482
|
mode: 'encoded',
|
|
1519
1483
|
overwrite: true, // Makes the operation idempotent
|
|
1520
1484
|
toast: false,
|
|
@@ -1522,14 +1486,15 @@ export default class TeraFyServer {
|
|
|
1522
1486
|
});
|
|
1523
1487
|
|
|
1524
1488
|
// Refresh the local file list cache to include the new placeholder/folder
|
|
1525
|
-
await app.service('$projects').refreshFiles({
|
|
1489
|
+
await app.service('$projects').refreshFiles({lazy: false});
|
|
1526
1490
|
this.debug('INFO', 2, `Project folder "${cleanFolderPath}" ensured/created via placeholder at "${relativePlaceholderPath}".`);
|
|
1527
|
-
} catch (error
|
|
1491
|
+
} catch (error) {
|
|
1528
1492
|
this.debug('ERROR', 1, `Failed to create/ensure project folder "${cleanFolderPath}" via placeholder "${relativePlaceholderPath}"`, error);
|
|
1529
1493
|
throw new Error(`Failed to create project folder "${cleanFolderPath}": ${error.message || String(error)}`, {cause: error});
|
|
1530
1494
|
}
|
|
1531
1495
|
}
|
|
1532
1496
|
|
|
1497
|
+
|
|
1533
1498
|
/**
|
|
1534
1499
|
* Deletes a "folder" and all its contents from the project's file storage.
|
|
1535
1500
|
* This involves listing all files under the given folder path (prefix) and removing them.
|
|
@@ -1540,7 +1505,7 @@ export default class TeraFyServer {
|
|
|
1540
1505
|
* @returns {Promise<null>} A promise that resolves with null when the folder and its contents are deleted.
|
|
1541
1506
|
* @throws {Error} If no project is active (and autoRequire is false), or if folderPath is invalid, or if deletion fails.
|
|
1542
1507
|
*/
|
|
1543
|
-
async deleteProjectFolder(folderPath
|
|
1508
|
+
async deleteProjectFolder(folderPath, options) {
|
|
1544
1509
|
const settings = {
|
|
1545
1510
|
autoRequire: true,
|
|
1546
1511
|
...options,
|
|
@@ -1571,7 +1536,11 @@ export default class TeraFyServer {
|
|
|
1571
1536
|
const pathPrefix = cleanFolderPath + '/'; // Files inside the folder will start with this prefix
|
|
1572
1537
|
|
|
1573
1538
|
// Fetch all files, ensuring the list is up-to-date
|
|
1574
|
-
const allFiles = await this.getProjectFiles({
|
|
1539
|
+
const allFiles = await this.getProjectFiles({
|
|
1540
|
+
autoRequire: false,
|
|
1541
|
+
lazy: false,
|
|
1542
|
+
meta: true,
|
|
1543
|
+
});
|
|
1575
1544
|
|
|
1576
1545
|
const filesToDelete = allFiles.filter(file => {
|
|
1577
1546
|
// file.name is the relative path like "documents/report.pdf" or "documents/archive/old.zip"
|
|
@@ -1583,7 +1552,7 @@ export default class TeraFyServer {
|
|
|
1583
1552
|
this.debug('INFO', 2, `No files found under project folder prefix "${pathPrefix}". The folder might be empty or not exist.`);
|
|
1584
1553
|
// It's possible the folder was already empty or never existed.
|
|
1585
1554
|
// Refresh files just in case, though `getProjectFiles` with `lazy: false` should be current.
|
|
1586
|
-
await app.service('$projects').refreshFiles({
|
|
1555
|
+
await app.service('$projects').refreshFiles({lazy: false});
|
|
1587
1556
|
return null;
|
|
1588
1557
|
}
|
|
1589
1558
|
|
|
@@ -1597,9 +1566,9 @@ export default class TeraFyServer {
|
|
|
1597
1566
|
await app.service('$supabase').fileRemove(supabasePathsToDelete);
|
|
1598
1567
|
|
|
1599
1568
|
// Refresh the local file list cache
|
|
1600
|
-
await app.service('$projects').refreshFiles({
|
|
1569
|
+
await app.service('$projects').refreshFiles({lazy: false});
|
|
1601
1570
|
this.debug('INFO', 2, `Project folder "${cleanFolderPath}" (prefix "${pathPrefix}") and its ${filesToDelete.length} contents deleted.`);
|
|
1602
|
-
} catch (error
|
|
1571
|
+
} catch (error) {
|
|
1603
1572
|
this.debug('ERROR', 1, `Failed to delete contents of project folder "${cleanFolderPath}" (prefix "${pathPrefix}")`, error);
|
|
1604
1573
|
throw new Error(`Failed to delete project folder "${cleanFolderPath}": ${error.message || String(error)}`, {cause: error});
|
|
1605
1574
|
}
|
|
@@ -1624,7 +1593,7 @@ export default class TeraFyServer {
|
|
|
1624
1593
|
*
|
|
1625
1594
|
* @returns {Promise<Array<Ref>>} A collection of references from the selected file
|
|
1626
1595
|
*/
|
|
1627
|
-
selectProjectLibrary(options
|
|
1596
|
+
selectProjectLibrary(options) {
|
|
1628
1597
|
const settings = {
|
|
1629
1598
|
title: 'Select a citation library',
|
|
1630
1599
|
hint: null,
|
|
@@ -1643,7 +1612,7 @@ export default class TeraFyServer {
|
|
|
1643
1612
|
|
|
1644
1613
|
return app.service('$projects').promise()
|
|
1645
1614
|
.then(()=> this.selectProjectFile(settings)) // Pass merged settings
|
|
1646
|
-
.then(
|
|
1615
|
+
.then(selectedFile => {
|
|
1647
1616
|
if (!selectedFile || !selectedFile.id) throw new Error('Library selection failed or was cancelled.');
|
|
1648
1617
|
// Pass relevant options down to getProjectLibrary
|
|
1649
1618
|
return this.getProjectLibrary(selectedFile.id, settings);
|
|
@@ -1664,17 +1633,17 @@ export default class TeraFyServer {
|
|
|
1664
1633
|
*
|
|
1665
1634
|
* @returns {Promise<Array<Ref>>|Promise<*>} A collection of references (default bevahiour) or a whatever format was requested
|
|
1666
1635
|
*/
|
|
1667
|
-
getProjectLibrary(id
|
|
1636
|
+
getProjectLibrary(id, options) {
|
|
1668
1637
|
const settings = {
|
|
1669
1638
|
format: 'pojo',
|
|
1670
1639
|
autoRequire: true,
|
|
1671
|
-
// eslint-disable-next-line no-unused-vars
|
|
1672
|
-
filter:
|
|
1673
|
-
find:
|
|
1640
|
+
// eslint-disable-next-line no-unused-vars
|
|
1641
|
+
filter: file => true, // Default filter
|
|
1642
|
+
find: files => files[0], // Default find
|
|
1674
1643
|
...options,
|
|
1675
1644
|
};
|
|
1676
1645
|
|
|
1677
|
-
const filePath
|
|
1646
|
+
const filePath = app.service('$projects').decodeFilePath(id);
|
|
1678
1647
|
|
|
1679
1648
|
return Promise.resolve()
|
|
1680
1649
|
.then(()=> settings.autoRequire && this.requireProject())
|
|
@@ -1710,11 +1679,11 @@ export default class TeraFyServer {
|
|
|
1710
1679
|
* Save back a citation library from some input
|
|
1711
1680
|
*
|
|
1712
1681
|
* @param {String} [id] File ID to save back to, if omitted a file will be prompted for
|
|
1713
|
-
* @param {Array<
|
|
1682
|
+
* @param {Array<Object>|Blob|File} [refs] Collection of references for the selected library or the raw Blob/File
|
|
1714
1683
|
*
|
|
1715
1684
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
1716
1685
|
* @param {String} [options.id] Alternate method to specify the file ID to save as, if omitted one will be prompted for
|
|
1717
|
-
* @param {Array<
|
|
1686
|
+
* @param {Array<Object>|Blob|File} [options.refs] Alternate method to specify the refs to save as an array or raw Blob/File
|
|
1718
1687
|
* @param {String} [options.format='auto'] Input format used. ENUM: 'auto' (try to figure it out from context), 'pojo' (JS array of RefLib references), 'blob' (raw JS Blob object), 'file' (named JS File object)
|
|
1719
1688
|
* @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
|
|
1720
1689
|
* @param {String|Array<String>} [options.hint] Hint(s) to store against the library. Generally corresponds to the current operation being performed - e.g. 'deduped'
|
|
@@ -1725,26 +1694,26 @@ export default class TeraFyServer {
|
|
|
1725
1694
|
*
|
|
1726
1695
|
* @returns {Promise} A promise which resolves when the save operation has completed
|
|
1727
1696
|
*/
|
|
1728
|
-
setProjectLibrary(id
|
|
1697
|
+
setProjectLibrary(id, refs, options) {
|
|
1729
1698
|
// Argument Mangling Logic (Simplified)
|
|
1730
|
-
let fileId
|
|
1731
|
-
let libraryRefs
|
|
1732
|
-
let mergedOptions
|
|
1699
|
+
let fileId = null; // eslint-disable-line no-useless-assignment
|
|
1700
|
+
let libraryRefs;
|
|
1701
|
+
let mergedOptions;
|
|
1733
1702
|
|
|
1734
1703
|
if (typeof id === 'string') {
|
|
1735
1704
|
fileId = id;
|
|
1736
1705
|
libraryRefs = refs;
|
|
1737
|
-
mergedOptions = {
|
|
1706
|
+
mergedOptions = {...options};
|
|
1738
1707
|
} else if (id !== null && typeof id === 'object' && !(id instanceof Blob) && !(id instanceof File) && !Array.isArray(id)) {
|
|
1739
1708
|
// Assuming called as (optionsObject)
|
|
1740
|
-
mergedOptions = {
|
|
1709
|
+
mergedOptions = {...id};
|
|
1741
1710
|
fileId = mergedOptions.id ?? null;
|
|
1742
1711
|
libraryRefs = mergedOptions.refs;
|
|
1743
1712
|
} else {
|
|
1744
1713
|
// Assuming called as (refs, options)
|
|
1745
1714
|
fileId = options?.id ?? null; // Check options for id if provided
|
|
1746
1715
|
libraryRefs = id; // First arg is refs
|
|
1747
|
-
mergedOptions = {
|
|
1716
|
+
mergedOptions = {...refs}; // Second arg is options
|
|
1748
1717
|
}
|
|
1749
1718
|
|
|
1750
1719
|
if (libraryRefs === undefined) throw new Error('setProjectLibrary requires refs to save.');
|
|
@@ -1762,10 +1731,10 @@ export default class TeraFyServer {
|
|
|
1762
1731
|
...mergedOptions // Apply options derived from mangling
|
|
1763
1732
|
};
|
|
1764
1733
|
|
|
1765
|
-
let filePath
|
|
1734
|
+
let filePath; // Eventual Supabase path to use
|
|
1766
1735
|
return Promise.resolve()
|
|
1767
1736
|
.then(()=> settings.autoRequire && this.requireProject())
|
|
1768
|
-
.then(()
|
|
1737
|
+
.then(()=> {
|
|
1769
1738
|
if (settings.id) {
|
|
1770
1739
|
// Optional: Validate settings.id exists?
|
|
1771
1740
|
return Promise.resolve(settings.id);
|
|
@@ -1781,12 +1750,12 @@ export default class TeraFyServer {
|
|
|
1781
1750
|
},
|
|
1782
1751
|
autoRequire: false, // Handled above anyway
|
|
1783
1752
|
})
|
|
1784
|
-
.then(
|
|
1753
|
+
.then(file => {
|
|
1785
1754
|
if (!file || !file.id) throw new Error('File selection cancelled or failed.');
|
|
1786
1755
|
return file.id; // Return selected file ID
|
|
1787
1756
|
});
|
|
1788
1757
|
})
|
|
1789
|
-
.then(
|
|
1758
|
+
.then(resolvedFileId => { // Compute filePath
|
|
1790
1759
|
settings.id = resolvedFileId; // Update settings.id
|
|
1791
1760
|
if (!settings.id) throw new Error("Could not determine file ID to save library to.");
|
|
1792
1761
|
filePath = app.service('$projects').decodeFilePath(settings.id);
|
|
@@ -1821,7 +1790,7 @@ export default class TeraFyServer {
|
|
|
1821
1790
|
throw new Error(`Unsupported library format "${settings.format}"`);
|
|
1822
1791
|
}
|
|
1823
1792
|
})
|
|
1824
|
-
.then(
|
|
1793
|
+
.then(fileBlob => app.service('$supabase').fileUpload(filePath, {
|
|
1825
1794
|
file: fileBlob,
|
|
1826
1795
|
overwrite: settings.overwrite,
|
|
1827
1796
|
mode: 'encoded',
|
|
@@ -1841,7 +1810,7 @@ export default class TeraFyServer {
|
|
|
1841
1810
|
* @param {Object} log The log entry to create
|
|
1842
1811
|
* @returns {Promise} A promise which resolves when the operation has completed
|
|
1843
1812
|
*/
|
|
1844
|
-
projectLog(log
|
|
1813
|
+
projectLog(log) {
|
|
1845
1814
|
return app.service('$projects').log(log);
|
|
1846
1815
|
}
|
|
1847
1816
|
// }}}
|
|
@@ -1855,7 +1824,7 @@ export default class TeraFyServer {
|
|
|
1855
1824
|
* @param {String} [options.path] The URL path segment to restore on next refresh
|
|
1856
1825
|
* @param {String} [options.title] The page title associated with the path
|
|
1857
1826
|
*/
|
|
1858
|
-
setPage(options
|
|
1827
|
+
setPage(options) {
|
|
1859
1828
|
app.service('$projects').setPage(options);
|
|
1860
1829
|
}
|
|
1861
1830
|
// }}}
|
|
@@ -1867,7 +1836,7 @@ export default class TeraFyServer {
|
|
|
1867
1836
|
*
|
|
1868
1837
|
* @param {Object} [options] Additional options to merge into `settings`
|
|
1869
1838
|
*/
|
|
1870
|
-
constructor(options
|
|
1839
|
+
constructor(options) {
|
|
1871
1840
|
Object.assign(this.settings, options);
|
|
1872
1841
|
}
|
|
1873
1842
|
|
|
@@ -1875,7 +1844,7 @@ export default class TeraFyServer {
|
|
|
1875
1844
|
/**
|
|
1876
1845
|
* Initialize the browser listener
|
|
1877
1846
|
*/
|
|
1878
|
-
init()
|
|
1847
|
+
init() {
|
|
1879
1848
|
// Ensure this only runs in a browser context
|
|
1880
1849
|
if (typeof window !== 'undefined' && typeof globalThis !== 'undefined') {
|
|
1881
1850
|
globalThis.addEventListener('message', this.acceptMessage.bind(this));
|
|
@@ -1898,7 +1867,7 @@ export default class TeraFyServer {
|
|
|
1898
1867
|
*
|
|
1899
1868
|
* @returns {Promise} A promise which resolves when the alert has been dismissed
|
|
1900
1869
|
*/
|
|
1901
|
-
uiAlert(text
|
|
1870
|
+
uiAlert(text, options) {
|
|
1902
1871
|
const settings = {
|
|
1903
1872
|
body: 'Alert!',
|
|
1904
1873
|
isHtml: false,
|
|
@@ -1938,7 +1907,7 @@ export default class TeraFyServer {
|
|
|
1938
1907
|
*
|
|
1939
1908
|
* @returns {Promise} A promise which resolves with `Promise.resolve('OK')` or rejects with `Promise.reject('CANCEL')`
|
|
1940
1909
|
*/
|
|
1941
|
-
uiConfirm(text
|
|
1910
|
+
uiConfirm(text, options) {
|
|
1942
1911
|
const settings = {
|
|
1943
1912
|
body: 'Confirm?',
|
|
1944
1913
|
isHtml: false,
|
|
@@ -1968,7 +1937,7 @@ export default class TeraFyServer {
|
|
|
1968
1937
|
},
|
|
1969
1938
|
],
|
|
1970
1939
|
})
|
|
1971
|
-
.then(()=> 'OK'
|
|
1940
|
+
.then(()=> 'OK') // Resolve with 'OK' if OK button clicked
|
|
1972
1941
|
.catch(()=> Promise.reject('CANCEL')) // Reject with 'CANCEL' if Cancel button clicked or closed
|
|
1973
1942
|
);
|
|
1974
1943
|
}
|
|
@@ -1988,7 +1957,7 @@ export default class TeraFyServer {
|
|
|
1988
1957
|
*
|
|
1989
1958
|
* @returns {Promise} A promise which resolves with `Promise.resolve('OK')`
|
|
1990
1959
|
*/
|
|
1991
|
-
uiJson(data
|
|
1960
|
+
uiJson(data, options) {
|
|
1992
1961
|
const settings = {
|
|
1993
1962
|
body: '',
|
|
1994
1963
|
isHtml: false,
|
|
@@ -2007,7 +1976,7 @@ export default class TeraFyServer {
|
|
|
2007
1976
|
data: data,
|
|
2008
1977
|
},
|
|
2009
1978
|
})
|
|
2010
|
-
.then(()=> 'OK'
|
|
1979
|
+
.then(()=> 'OK') // Resolve with 'OK' if OK button clicked
|
|
2011
1980
|
.catch(()=> Promise.reject('CANCEL')) // Reject with 'CANCEL' if Cancel button clicked or closed
|
|
2012
1981
|
);
|
|
2013
1982
|
}
|
|
@@ -2019,14 +1988,14 @@ export default class TeraFyServer {
|
|
|
2019
1988
|
* @function uiPanic
|
|
2020
1989
|
* @param {String} [text] Text to display
|
|
2021
1990
|
*/
|
|
2022
|
-
uiPanic(text
|
|
1991
|
+
uiPanic(text) {
|
|
2023
1992
|
// Ensure window context exists
|
|
2024
1993
|
if (typeof window !== 'undefined' && typeof window.panic === 'function') {
|
|
2025
1994
|
window.panic(text);
|
|
2026
1995
|
} else {
|
|
2027
1996
|
console.error("PANIC (window.panic not available):", text);
|
|
2028
1997
|
// Fallback behavior if window.panic doesn't exist
|
|
2029
|
-
alert(`PANIC: ${text}`);
|
|
1998
|
+
window.alert(`PANIC: ${text}`);
|
|
2030
1999
|
}
|
|
2031
2000
|
}
|
|
2032
2001
|
|
|
@@ -2045,7 +2014,7 @@ export default class TeraFyServer {
|
|
|
2045
2014
|
*
|
|
2046
2015
|
* @returns {Promise} A promise which resolves when the dialog has been updated
|
|
2047
2016
|
*/
|
|
2048
|
-
uiProgress(options
|
|
2017
|
+
uiProgress(options) {
|
|
2049
2018
|
const currentOptions = options === false ? {close: true} : options || {};
|
|
2050
2019
|
|
|
2051
2020
|
if (currentOptions.close) { // Asked to close the dialog
|
|
@@ -2089,7 +2058,15 @@ export default class TeraFyServer {
|
|
|
2089
2058
|
}
|
|
2090
2059
|
}
|
|
2091
2060
|
|
|
2092
|
-
|
|
2061
|
+
|
|
2062
|
+
/**
|
|
2063
|
+
* State storage for the uiProgress() function
|
|
2064
|
+
*
|
|
2065
|
+
* @type {Object}
|
|
2066
|
+
* @property {Object} options Options object used to setup the initial uiProgress() call
|
|
2067
|
+
* @property {Promise} promise The promise used to manage the uiProgress dialog
|
|
2068
|
+
*/
|
|
2069
|
+
_uiProgress = {
|
|
2093
2070
|
options: null,
|
|
2094
2071
|
promise: null,
|
|
2095
2072
|
};
|
|
@@ -2110,7 +2087,7 @@ export default class TeraFyServer {
|
|
|
2110
2087
|
*
|
|
2111
2088
|
* @returns {Promise<*>} Either the eventual user value or a throw with `Promise.reject('CANCEL')`
|
|
2112
2089
|
*/
|
|
2113
|
-
uiPrompt(text
|
|
2090
|
+
uiPrompt(text, options) {
|
|
2114
2091
|
const settings = {
|
|
2115
2092
|
body: '',
|
|
2116
2093
|
isHtml: false,
|
|
@@ -2141,19 +2118,19 @@ export default class TeraFyServer {
|
|
|
2141
2118
|
class: 'btn btn-success',
|
|
2142
2119
|
icon: 'fas fa-check',
|
|
2143
2120
|
title: 'Ok',
|
|
2144
|
-
click()
|
|
2121
|
+
click() {
|
|
2145
2122
|
// Assuming 'this' is the component instance with 'newValue' property
|
|
2146
2123
|
// And $prompt service is available globally via 'app'
|
|
2147
|
-
app.service('$prompt').close(true,
|
|
2124
|
+
app.service('$prompt').close(true, this.newValue); // Use app.$prompt.close
|
|
2148
2125
|
},
|
|
2149
2126
|
},
|
|
2150
2127
|
'cancel', // Standard cancel button that rejects
|
|
2151
2128
|
],
|
|
2152
2129
|
})
|
|
2153
2130
|
)
|
|
2154
|
-
.then(
|
|
2131
|
+
.then(answer => {
|
|
2155
2132
|
// Check if the answer is non-empty or if required is false
|
|
2156
|
-
if (answer || !settings.required) {
|
|
2133
|
+
if (answer || !settings.required) { // eslint-disable-line unicorn/prefer-ternary
|
|
2157
2134
|
return answer;
|
|
2158
2135
|
} else {
|
|
2159
2136
|
// If required and answer is empty/nullish, treat as cancel
|
|
@@ -2172,7 +2149,7 @@ export default class TeraFyServer {
|
|
|
2172
2149
|
*
|
|
2173
2150
|
* @returns {Void} This function is fatal
|
|
2174
2151
|
*/
|
|
2175
|
-
uiThrow(error
|
|
2152
|
+
uiThrow(error) {
|
|
2176
2153
|
return this.requestFocus(()=>
|
|
2177
2154
|
app.service('$errors').catch(error)
|
|
2178
2155
|
);
|
|
@@ -2192,7 +2169,7 @@ export default class TeraFyServer {
|
|
|
2192
2169
|
*
|
|
2193
2170
|
* @returns {WindowProxy} The opened window object (if `noopener` is not set in permissions)
|
|
2194
2171
|
*/
|
|
2195
|
-
uiWindow(url
|
|
2172
|
+
uiWindow(url, options) {
|
|
2196
2173
|
// Ensure this runs only in browser context
|
|
2197
2174
|
if (typeof window === 'undefined' || typeof screen === 'undefined') return null;
|
|
2198
2175
|
|
|
@@ -2237,7 +2214,7 @@ export default class TeraFyServer {
|
|
|
2237
2214
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
2238
2215
|
* @param {Boolean|String} [options.logo=false] Add a logo to the output, if boolean true the Tera-tools logo is used otherwise specify a path or URL
|
|
2239
2216
|
*/
|
|
2240
|
-
uiSplat(content
|
|
2217
|
+
uiSplat(content, options) {
|
|
2241
2218
|
// Ensure this runs only in browser context
|
|
2242
2219
|
if (typeof window === 'undefined' || typeof document === 'undefined') return;
|
|
2243
2220
|
|
|
@@ -2256,12 +2233,12 @@ export default class TeraFyServer {
|
|
|
2256
2233
|
return;
|
|
2257
2234
|
}
|
|
2258
2235
|
|
|
2259
|
-
let compiledContent
|
|
2236
|
+
let compiledContent;
|
|
2260
2237
|
if (typeof content == 'string') {
|
|
2261
2238
|
const el = document.createElement('div')
|
|
2262
2239
|
el.innerHTML = content;
|
|
2263
2240
|
// If the string contained multiple top-level elements, wrap them
|
|
2264
|
-
compiledContent = el.children.length === 1 ? el.firstElementChild
|
|
2241
|
+
compiledContent = el.children.length === 1 ? el.firstElementChild : el;
|
|
2265
2242
|
} else {
|
|
2266
2243
|
compiledContent = content;
|
|
2267
2244
|
}
|
|
@@ -2291,18 +2268,18 @@ export default class TeraFyServer {
|
|
|
2291
2268
|
* - The next argument can optionally be a numeric verbosity level. The message is ignored if `settings.verbosity` is lower than this level. Defaults to 1.
|
|
2292
2269
|
* - All subsequent arguments are passed to the console as the log message.
|
|
2293
2270
|
*/
|
|
2294
|
-
debug(...inputArgs
|
|
2271
|
+
debug(...inputArgs) {
|
|
2295
2272
|
// Ensure console exists
|
|
2296
2273
|
if (typeof console === 'undefined') return;
|
|
2297
2274
|
if (!this.settings.devMode || this.settings.verbosity < 1) return; // Debugging is disabled
|
|
2298
2275
|
|
|
2299
|
-
let method
|
|
2276
|
+
let method = 'log'; // Default method
|
|
2300
2277
|
let verboseLevel = 1;
|
|
2301
2278
|
const msgArgs = [...inputArgs]; // Copy args to modify
|
|
2302
2279
|
|
|
2303
2280
|
// Argument mangling for prefix method + verbosity level {{{
|
|
2304
2281
|
if (typeof msgArgs[0] == 'string' && ['INFO', 'LOG', 'WARN', 'ERROR'].includes(msgArgs[0].toUpperCase())) {
|
|
2305
|
-
const potentialMethod = msgArgs.shift().toLowerCase()
|
|
2282
|
+
const potentialMethod = msgArgs.shift().toLowerCase();
|
|
2306
2283
|
// Check if it's a valid console method
|
|
2307
2284
|
if (potentialMethod in console) {
|
|
2308
2285
|
method = potentialMethod;
|
|
@@ -2318,13 +2295,11 @@ export default class TeraFyServer {
|
|
|
2318
2295
|
|
|
2319
2296
|
if (this.settings.verbosity < verboseLevel) return; // Called but this output is too verbose for our settings - skip
|
|
2320
2297
|
|
|
2321
|
-
|
|
2322
|
-
(console as any)[method](
|
|
2298
|
+
console[method](
|
|
2323
2299
|
'%c[TERA-FY SERVER]',
|
|
2324
2300
|
'font-weight: bold; color: #4d659c;',
|
|
2325
2301
|
...msgArgs,
|
|
2326
2302
|
);
|
|
2327
2303
|
}
|
|
2328
|
-
/* eslint-enable */
|
|
2329
2304
|
// }}}
|
|
2330
2305
|
}
|