@iebh/tera-fy 2.0.22 → 2.2.3

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/api.md +375 -298
  3. package/dist/lib/projectFile.d.ts +182 -0
  4. package/dist/lib/projectFile.js +157 -0
  5. package/dist/lib/projectFile.js.map +1 -0
  6. package/dist/lib/syncro/entities.d.ts +28 -0
  7. package/dist/lib/syncro/entities.js +203 -0
  8. package/dist/lib/syncro/entities.js.map +1 -0
  9. package/dist/lib/syncro/keyed.d.ts +95 -0
  10. package/dist/lib/syncro/keyed.js +286 -0
  11. package/dist/lib/syncro/keyed.js.map +1 -0
  12. package/dist/lib/syncro/syncro.d.ts +328 -0
  13. package/dist/lib/syncro/syncro.js +633 -0
  14. package/dist/lib/syncro/syncro.js.map +1 -0
  15. package/dist/lib/terafy.bootstrapper.d.ts +42 -0
  16. package/dist/lib/terafy.bootstrapper.js +130 -0
  17. package/dist/lib/terafy.bootstrapper.js.map +1 -0
  18. package/dist/lib/terafy.client.d.ts +552 -0
  19. package/dist/lib/terafy.client.js +1144 -0
  20. package/dist/lib/terafy.client.js.map +1 -0
  21. package/dist/lib/terafy.proxy.d.ts +66 -0
  22. package/dist/lib/terafy.proxy.js +123 -0
  23. package/dist/lib/terafy.proxy.js.map +1 -0
  24. package/dist/lib/terafy.server.d.ts +652 -0
  25. package/dist/lib/terafy.server.js +1988 -0
  26. package/dist/lib/terafy.server.js.map +1 -0
  27. package/dist/plugin.vue2.es2019.js +30 -13
  28. package/dist/plugins/base.d.ts +20 -0
  29. package/dist/plugins/base.js +21 -0
  30. package/dist/plugins/base.js.map +1 -0
  31. package/dist/plugins/firebase.d.ts +62 -0
  32. package/dist/plugins/firebase.js +111 -0
  33. package/dist/plugins/firebase.js.map +1 -0
  34. package/dist/plugins/vite.d.ts +12 -0
  35. package/dist/plugins/vite.js +22 -0
  36. package/dist/plugins/vite.js.map +1 -0
  37. package/dist/plugins/vue2.d.ts +68 -0
  38. package/dist/plugins/vue2.js +96 -0
  39. package/dist/plugins/vue2.js.map +1 -0
  40. package/dist/plugins/vue3.d.ts +64 -0
  41. package/dist/plugins/vue3.js +96 -0
  42. package/dist/plugins/vue3.js.map +1 -0
  43. package/dist/terafy.bootstrapper.es2019.js +2 -2
  44. package/dist/terafy.bootstrapper.js +2 -2
  45. package/dist/terafy.es2019.js +2 -2
  46. package/dist/terafy.js +2 -2
  47. package/dist/utils/mixin.d.ts +11 -0
  48. package/dist/utils/mixin.js +15 -0
  49. package/dist/utils/mixin.js.map +1 -0
  50. package/dist/utils/pDefer.d.ts +12 -0
  51. package/dist/utils/pDefer.js +14 -0
  52. package/dist/utils/pDefer.js.map +1 -0
  53. package/dist/utils/pathTools.d.ts +70 -0
  54. package/dist/utils/pathTools.js +120 -0
  55. package/dist/utils/pathTools.js.map +1 -0
  56. package/documentation.yml +3 -0
  57. package/eslint.config.js +44 -8
  58. package/lib/{projectFile.js → projectFile.ts} +83 -40
  59. package/lib/syncro/entities.ts +288 -0
  60. package/lib/syncro/{keyed.js → keyed.ts} +114 -57
  61. package/lib/syncro/{syncro.js → syncro.ts} +201 -168
  62. package/lib/{terafy.bootstrapper.js → terafy.bootstrapper.ts} +49 -31
  63. package/lib/{terafy.client.js → terafy.client.ts} +153 -86
  64. package/lib/{terafy.proxy.js → terafy.proxy.ts} +43 -16
  65. package/lib/{terafy.server.js → terafy.server.ts} +606 -223
  66. package/package.json +65 -26
  67. package/plugins/{base.js → base.ts} +3 -1
  68. package/plugins/{firebase.js → firebase.ts} +34 -16
  69. package/plugins/{vite.js → vite.ts} +3 -3
  70. package/plugins/{vue2.js → vue2.ts} +17 -10
  71. package/plugins/{vue3.js → vue3.ts} +11 -9
  72. package/tsconfig.json +30 -0
  73. package/utils/{mixin.js → mixin.ts} +1 -1
  74. package/utils/{pDefer.js → pDefer.ts} +10 -3
  75. package/utils/{pathTools.js → pathTools.ts} +11 -9
  76. package/lib/syncro/entities.js +0 -232
@@ -31,21 +31,21 @@ export default class TeraFy {
31
31
  * @property {Array<String|Array<String>>} [debugPaths] List of paths (in either dotted or array notation) to enter debugging mode if a change is detected in dev mode e.g. `{debugPaths: ['foo.bar.baz']}`. This really slows down state writes so should only be used for debugging
32
32
  */
33
33
  settings = {
34
- session: null,
34
+ session: null as string | null,
35
35
  // client: 'tera-fy', // Reserved by terafy.bootstrapper.js
36
36
  // clientType: 'esm', // Reserved by terafy.bootstrapper.js
37
37
  devMode: false,
38
38
  verbosity: 1,
39
- mode: 'detect',
39
+ mode: 'detect' as 'detect' | 'parent' | 'child' | 'popup',
40
40
  modeTimeout: 300,
41
41
  modeFallback: 'child', // ENUM: 'child' (use iframes), 'popup' (use popup windows)
42
42
  modeOverrides: {
43
- child(config) { // When we're in child mode assume a local dev environment and use the dev.tera-tools.com site instead to work around CORS restrictions
43
+ child(config: any) { // When we're in child mode assume a local dev environment and use the dev.tera-tools.com site instead to work around CORS restrictions
44
44
  if (config.siteUrl == 'https://tera-tools.com/embed') { // Only if we're using the default URL...
45
45
  config.siteUrl = 'https://dev.tera-tools.com/embed'; // Repoint URL to dev site
46
46
  }
47
47
  },
48
- },
48
+ } as { [key: string]: (config: any) => void },
49
49
  siteUrl: 'https://tera-tools.com/embed',
50
50
  restrictOrigin: '*',
51
51
  frameSandbox: [
@@ -62,7 +62,7 @@ export default class TeraFy {
62
62
  ],
63
63
  handshakeInterval: 1_000, // ~1s
64
64
  handshakeTimeout: 10_000, // ~10s
65
- debugPaths: null, // Transformed into a Array<String> (in Lodash dotted notation) on init()
65
+ debugPaths: null as Array<string | Array<string>> | null, // Transformed into a Array<String> (in Lodash dotted notation) on init()
66
66
  };
67
67
 
68
68
 
@@ -70,6 +70,7 @@ export default class TeraFy {
70
70
  * Event emitter subscription endpoint
71
71
  * @type {Mitt}
72
72
  */
73
+ // @ts-ignore
73
74
  events = Mitt();
74
75
 
75
76
 
@@ -83,10 +84,10 @@ export default class TeraFy {
83
84
  * @property {DOMElement} stylesheet The corresponding stylesheet
84
85
  */
85
86
  dom = {
86
- el: null,
87
- iframe: null,
88
- popup: null,
89
- stylesheet: null,
87
+ el: null as HTMLDivElement | null,
88
+ iframe: null as HTMLIFrameElement | null,
89
+ popup: null as Window | null,
90
+ stylesheet: null as HTMLStyleElement | null,
90
91
  };
91
92
 
92
93
 
@@ -133,9 +134,14 @@ export default class TeraFy {
133
134
  // 'getProjectFile', - Handled below (requires return mapped to ProjectFile)
134
135
  'getProjectFileContents',
135
136
  // 'createProjectFile', - Handled below (requires return mapped to ProjectFile)
137
+ // 'moveProjectFile', - Handled below (requires return mapped to ProjectFile)
136
138
  'deleteProjectFile',
137
139
  'setProjectFileContents',
138
140
 
141
+ // Project folders
142
+ 'createProjectFolder',
143
+ 'deleteProjectFolder',
144
+
139
145
  // Project Libraries
140
146
  'selectProjectLibrary',
141
147
  'getProjectLibrary',
@@ -156,14 +162,14 @@ export default class TeraFy {
156
162
  'uiThrow',
157
163
  'uiSplat',
158
164
  'uiWindow',
159
- ];
165
+ ] as const;
160
166
 
161
167
 
162
168
  /**
163
169
  * Loaded plugins via Use()
164
170
  * @type {Array<TeraFyPlugin>}
165
171
  */
166
- plugins = [];
172
+ plugins: any[] = [];
167
173
 
168
174
 
169
175
  /**
@@ -173,7 +179,7 @@ export default class TeraFy {
173
179
  *
174
180
  * @type {Object<Object>}
175
181
  */
176
- namespaces = {};
182
+ namespaces: { [key: string]: any } = {};
177
183
 
178
184
 
179
185
  // Messages - send(), sendRaw(), rpc(), acceptMessage() {{{
@@ -184,7 +190,7 @@ export default class TeraFy {
184
190
  * @param {Object} message Message object to send
185
191
  * @returns {Promise<*>} A promise which resolves when the operation has completed with the remote reply
186
192
  */
187
- send(message) {
193
+ send(message: any) {
188
194
  let id = nanoid();
189
195
 
190
196
  this.acceptPostboxes[id] = {}; // Stub for the deferred promise
@@ -208,8 +214,8 @@ export default class TeraFy {
208
214
  *
209
215
  * @param {Object} message Message object to send
210
216
  */
211
- sendRaw(message) {
212
- let payload;
217
+ sendRaw(message: any) {
218
+ let payload: any;
213
219
  try {
214
220
  payload = {
215
221
  TERA: 1,
@@ -220,9 +226,9 @@ export default class TeraFy {
220
226
  if (this.settings.mode == 'parent') {
221
227
  window.parent.postMessage(payload, this.settings.restrictOrigin);
222
228
  } else if (this.settings.mode == 'child') {
223
- this.dom.iframe.contentWindow.postMessage(payload, this.settings.restrictOrigin);
229
+ this.dom.iframe!.contentWindow!.postMessage(payload, this.settings.restrictOrigin);
224
230
  } else if (this.settings.mode == 'popup') {
225
- this.dom.popup.postMessage(payload, this.settings.restrictOrigin);
231
+ this.dom.popup!.postMessage(payload, this.settings.restrictOrigin);
226
232
  } else if (this.settings.mode == 'detect') {
227
233
  throw new Error('Call init() or detectMode() before trying to send data to determine the mode');
228
234
  } else {
@@ -244,7 +250,7 @@ export default class TeraFy {
244
250
  *
245
251
  * @returns {Promise<*>} The resolved output of the server function
246
252
  */
247
- rpc(method, ...args) {
253
+ rpc(method: any, ...args: any[]) {
248
254
  return this.send({
249
255
  action: 'rpc',
250
256
  method,
@@ -260,7 +266,7 @@ export default class TeraFy {
260
266
  *
261
267
  * @returns {Promise} A promise which will resolve when the message has been processed
262
268
  */
263
- acceptMessage(rawMessage) {
269
+ acceptMessage(rawMessage: any) {
264
270
  if (rawMessage.origin == window.location.origin) return Promise.resolve(); // Message came from us
265
271
 
266
272
  let message = rawMessage.data;
@@ -276,7 +282,7 @@ export default class TeraFy {
276
282
  return Promise.resolve();
277
283
  } else if (message?.action == 'rpc') {
278
284
  return Promise.resolve()
279
- .then(()=> this[message.method].apply(this, message.args))
285
+ .then(()=> (this as any)[message.method].apply(this, message.args as any[]))
280
286
  .then(res => this.sendRaw({
281
287
  id: message.id,
282
288
  action: 'response',
@@ -293,7 +299,7 @@ export default class TeraFy {
293
299
  })
294
300
  } else if (message?.action == 'event') {
295
301
  return Promise.resolve()
296
- .then(()=> this.events.emit(message.event, ...message.payload))
302
+ .then(()=> this.events.emit(message.event, ...(message.payload as [])))
297
303
  .catch(e => {
298
304
  console.warn(`TERA-FY client threw while handling emitted event "${message.event}"`, {message});
299
305
  throw e;
@@ -311,7 +317,7 @@ export default class TeraFy {
311
317
  /**
312
318
  * Listening postboxes, these correspond to outgoing message IDs that expect a response
313
319
  */
314
- acceptPostboxes = {};
320
+ acceptPostboxes: { [key: string]: any } = {};
315
321
 
316
322
  // }}}
317
323
 
@@ -325,7 +331,7 @@ export default class TeraFy {
325
331
  *
326
332
  * @returns {Promise<Reactive>} A promise which resolves to the reactive object
327
333
  */
328
- mountNamespace(name) {
334
+ mountNamespace(name: any) {
329
335
  if (!/^[\w-]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
330
336
  if (this.namespaces[name]) return Promise.resolve(this.namespaces[name]); // Already mounted
331
337
 
@@ -343,7 +349,7 @@ export default class TeraFy {
343
349
  *
344
350
  * @returns {Promise} A promise which resolves when the mount operation has completed
345
351
  */
346
- _mountNamespace(name) { // eslint-disable-line no-unused-vars
352
+ _mountNamespace(name: any) { // eslint-disable-line no-unused-vars
347
353
  console.warn('teraFy._mountNamespace() has not been overriden by a TERA-fy plugin, load one to add this functionality for your preferred framework');
348
354
  throw new Error('teraFy._mountNamespace() is not supported');
349
355
  }
@@ -359,7 +365,7 @@ export default class TeraFy {
359
365
  *
360
366
  * @returns {Promise} A promise which resolves when the operation has completed
361
367
  */
362
- unmountNamespace(name) {
368
+ unmountNamespace(name: any) {
363
369
  if (!this.namespaces[name]) return Promise.resolve(); // Already unmounted
364
370
  return this._unmountNamespace(name);
365
371
  }
@@ -373,7 +379,7 @@ export default class TeraFy {
373
379
  *
374
380
  * @returns {Promise} A promise which resolves when the operation has completed
375
381
  */
376
- _unmountNamespace(name) { // eslint-disable-line no-unused-vars
382
+ _unmountNamespace(name: any) { // eslint-disable-line no-unused-vars
377
383
  console.warn('teraFy.unbindNamespace() has not been overriden by a TERA-fy plugin, load one to add this functionality for your preferred framework');
378
384
  }
379
385
  // }}}
@@ -385,10 +391,11 @@ export default class TeraFy {
385
391
  *
386
392
  * @param {Object} [options] Additional options to merge into `settings` via `set`
387
393
  */
388
- constructor(options) {
394
+ constructor(options?: any) {
389
395
  if (options) this.set(options);
390
396
  }
391
397
 
398
+ private initPromise: Promise<TeraFy> | null = null;
392
399
 
393
400
  /**
394
401
  * Initalize the TERA client singleton
@@ -397,26 +404,27 @@ export default class TeraFy {
397
404
  * @param {Object} [options] Additional options to merge into `settings` via `set`
398
405
  * @returns {Promise<TeraFy>} An eventual promise which will resovle with this terafy instance
399
406
  */
400
- init(options) {
407
+ init(options?: any): Promise<TeraFy> {
401
408
  if (options) this.set(options);
402
- if (this.init.promise) return this.init.promise; // Aleady been called - return init promise
409
+ if (this.initPromise) return this.initPromise; // Aleady been called - return init promise
403
410
 
404
411
  window.addEventListener('message', this.acceptMessage.bind(this));
405
412
 
406
413
  const context = this;
407
- return this.init.promise = Promise.resolve()
414
+ this.initPromise = Promise.resolve()
408
415
  .then(()=> this.settings.session ||= 'tfy-' + this.getEntropicString(16))
409
416
  .then(()=> this.debug('INFO', 4, '[0/6] Init', 'Session', this.settings.session, 'against', this.settings.siteUrl))
410
417
  .then(()=> { // Init various options for optimized access
411
418
  if (!this.settings.devMode) return; // Not in dev mode
412
- this.settings.debugPaths =
413
- !this.settings.debugPaths ? null // Falsy - disable
414
- : Array.isArray(this.settings.debugPaths) ? this.settings.debugPaths.map(path =>
419
+ if (this.settings.debugPaths) {
420
+ this.settings.debugPaths = (this.settings.debugPaths as any[]).map((path: any) =>
415
421
  Array.isArray(path) ? path.join('.') // Transform arrays into dotted notation
416
422
  : typeof path == 'string' ? path // Assume already in dotted notation
417
423
  : (()=> { throw new Error('Unknown path type - should be an array or string in dotted notation') })()
418
424
  )
419
- : (()=> { throw new Error('Unknown terafyClient.settings.debugPaths type') })()
425
+ } else {
426
+ this.settings.debugPaths = null;
427
+ }
420
428
 
421
429
  this.debug('INFO', 0, 'Watching state paths', this.settings.debugPaths);
422
430
  })
@@ -459,12 +467,16 @@ export default class TeraFy {
459
467
  plugin.init.call(context, this.settings)
460
468
  )
461
469
  ))
462
- .then(()=> this.debug('INFO', 4, '[6/6] Init complete'))
463
- .then(()=> context)
470
+ .then(()=> {
471
+ this.debug('INFO', 4, '[6/6] Init complete');
472
+ return context; // Resolve with the instance
473
+ })
464
474
  .catch(e => {
465
475
  this.debug('WARN', 0, 'Init process fault', e);
466
- return context;
467
- })
476
+ throw e; // Re-throw
477
+ });
478
+
479
+ return this.initPromise;
468
480
  }
469
481
 
470
482
 
@@ -475,23 +487,24 @@ export default class TeraFy {
475
487
  *
476
488
  * @returns {Promise<String>} A promise which will resolve with the detected mode to use
477
489
  */
478
- detectMode() {
490
+ detectMode(): Promise<'parent' | 'child' | 'popup'> {
479
491
  if (this.settings.mode != 'detect') { // Dev has specified a forced mode to use
480
492
  return Promise.resolve(this.settings.mode);
481
493
  } else if (window.self === window.parent) { // This frame is already at the top
482
- return Promise.resolve(this.settings.modeFallback);
494
+ return Promise.resolve(this.settings.modeFallback as 'child' | 'popup');
483
495
  } else { // No idea - try messaging
484
496
  return Promise.resolve()
485
497
  .then(()=> this.settings.mode = 'parent') // Switch to parent mode...
486
- .then(()=> new Promise((resolve, reject) => { // And try messaging with a timeout
498
+ .then(()=> new Promise<void>((resolve, reject) => { // And try messaging with a timeout
487
499
  let timeoutHandle = setTimeout(()=> reject('TIMEOUT'), this.settings.modeTimeout);
488
500
 
489
501
  this.rpc('handshake')
490
502
  .then(()=> clearTimeout(timeoutHandle))
491
- .then(()=> resolve())
503
+ .then(()=> resolve(undefined))
504
+ .catch(reject); // Propagate RPC errors
492
505
  }))
493
- .then(()=> 'parent')
494
- .catch(()=> this.settings.modeFallback)
506
+ .then(()=> 'parent' as 'parent')
507
+ .catch(()=> this.settings.modeFallback as 'child' | 'popup')
495
508
  }
496
509
  }
497
510
 
@@ -504,29 +517,29 @@ export default class TeraFy {
504
517
  injectComms() {
505
518
  switch (this.settings.mode) {
506
519
  case 'child': return Promise.resolve()
507
- .then(()=> new Promise(resolve => {
520
+ .then(()=> new Promise<void>(resolve => {
508
521
  this.debug('INFO', 2, 'Injecting TERA site as iFrame child');
509
522
 
510
523
  this.dom.el = document.createElement('div')
511
524
  this.dom.el.id = 'tera-fy';
512
- this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
513
- this.dom.el.classList.add('minimized');
514
- document.body.append(this.dom.el);
525
+ this.dom.el!.classList.toggle('dev-mode', this.settings.devMode);
526
+ this.dom.el!.classList.add('minimized');
527
+ document.body.append(this.dom.el!);
515
528
 
516
- this.dom.el.addEventListener('click', ()=> this.dom.el.classList.toggle('minimized'));
529
+ this.dom.el!.addEventListener('click', ()=> this.dom.el!.classList.toggle('minimized'));
517
530
 
518
531
  this.dom.iframe = document.createElement('iframe')
519
532
 
520
533
  // Queue up event chain when document loads
521
- this.dom.iframe.setAttribute('sandbox', this.settings.frameSandbox.join(' '));
522
- this.dom.iframe.addEventListener('load', ()=> {
534
+ this.dom.iframe!.setAttribute('sandbox', this.settings.frameSandbox.join(' '));
535
+ this.dom.iframe!.addEventListener('load', ()=> {
523
536
  this.debug('INFO', 3, 'Embeded iframe ready');
524
- resolve();
537
+ resolve(undefined);
525
538
  });
526
539
 
527
540
  // Start document load sequence + append to DOM
528
- this.dom.iframe.src = this.settings.siteUrl;
529
- this.dom.el.append(this.dom.iframe);
541
+ this.dom.iframe!.src = this.settings.siteUrl;
542
+ this.dom.el!.append(this.dom.iframe!);
530
543
  }))
531
544
  .then(()=> this.handshakeLoop())
532
545
 
@@ -556,7 +569,7 @@ export default class TeraFy {
556
569
  *
557
570
  * @returns {Promise} A promise which will either resolve when the handshake is successful OR fail with 'TIMEOUT'
558
571
  */
559
- handshakeLoop(options) {
572
+ handshakeLoop(options?: any) {
560
573
  let settings = {
561
574
  handshakeInterval: this.settings.handshakeInterval,
562
575
  handshakeTimeout: this.settings.handshakeTimeout,
@@ -564,9 +577,9 @@ export default class TeraFy {
564
577
  };
565
578
 
566
579
  // Loop until the window context returns a handshake
567
- return new Promise((resolve, reject) => {
580
+ return new Promise<void>((resolve, reject) => {
568
581
  let handshakeCount = 0;
569
- let handshakeTimer;
582
+ let handshakeTimer: any;
570
583
 
571
584
  let handshakeTimeout = setTimeout(()=> {
572
585
  clearTimeout(handshakeTimer);
@@ -584,8 +597,8 @@ export default class TeraFy {
584
597
  clearTimeout(handshakeTimeout);
585
598
  clearTimeout(handshakeTimer);
586
599
  })
587
- .then(()=> resolve())
588
- .catch(reject)
600
+ .then(()=> resolve(undefined))
601
+ .catch(reject) // Let RPC errors propagate
589
602
  };
590
603
  tryHandshake(); // Kick off initial handshake
591
604
  });
@@ -601,7 +614,7 @@ export default class TeraFy {
601
614
  switch (this.settings.mode) {
602
615
  case 'child':
603
616
  this.dom.stylesheet = document.createElement('style');
604
- this.dom.stylesheet.innerHTML = [
617
+ this.dom.stylesheet!.innerHTML = [
605
618
  ':root {',
606
619
  '--TERA-accent: #4d659c;',
607
620
  '}',
@@ -714,7 +727,7 @@ export default class TeraFy {
714
727
  '}',
715
728
  // }}}
716
729
  ].join('\n');
717
- document.head.appendChild(this.dom.stylesheet);
730
+ document.head.appendChild(this.dom.stylesheet!);
718
731
  break;
719
732
  case 'parent':
720
733
  case 'popup':
@@ -732,7 +745,7 @@ export default class TeraFy {
732
745
  */
733
746
  injectMethods() {
734
747
  this.methods.forEach(method =>
735
- this[method] = this.rpc.bind(this, method)
748
+ (this as any)[method] = this.rpc.bind(this, method)
736
749
  );
737
750
  }
738
751
  // }}}
@@ -748,7 +761,7 @@ export default class TeraFy {
748
761
  * @param {Number} [verboseLevel=1] The verbosity level to trigger at. If `settings.verbosity` is lower than this, the message is ignored
749
762
  * @param {...*} [msg] Output to show
750
763
  */
751
- debug(...msg) {
764
+ debug(...msg: any[]) {
752
765
  if (!this.settings.devMode || this.settings.verbosity < 1) return; // Debugging is disabled
753
766
  let method = 'log';
754
767
  let verboseLevel = 1;
@@ -765,7 +778,7 @@ export default class TeraFy {
765
778
 
766
779
  if (this.settings.verbosity < verboseLevel) return; // Called but this output is too verbose for our settings - skip
767
780
 
768
- console[method](
781
+ (console as any)[method](
769
782
  '%c[TERA-FY CLIENT]',
770
783
  'font-weight: bold; color: #ff5722;',
771
784
  ...msg,
@@ -786,7 +799,7 @@ export default class TeraFy {
786
799
  *
787
800
  * @returns {TeraFy} This chainable terafy instance
788
801
  */
789
- set(key, value, options) {
802
+ set(key: string | object, value?: any, options?: { ignoreNullish?: boolean }) {
790
803
  let settings = {
791
804
  ignoreNullish: true,
792
805
  ...options,
@@ -794,12 +807,12 @@ export default class TeraFy {
794
807
 
795
808
  if (typeof key == 'string') {
796
809
  if (!settings.ignoreNullish || (value !== null && value !== undefined))
797
- this.settings[key] = value;
810
+ (this.settings as any)[key] = value;
798
811
  } else {
799
812
  Object.assign(this.settings, key);
800
813
  }
801
814
 
802
- return this.toggleDevMode(this.settings.devMode);
815
+ return this.toggleDevMode(this.settings.devMode as boolean);
803
816
  }
804
817
 
805
818
 
@@ -813,7 +826,7 @@ export default class TeraFy {
813
826
  *
814
827
  * @returns {TeraFy} This chainable terafy instance
815
828
  */
816
- setIfDev(key, value, options) {
829
+ setIfDev(key: any, value: any, options?: any) {
817
830
  if (!this.settings.devMode || value === undefined) return this;
818
831
  return this.set(key, value, options);
819
832
  }
@@ -827,8 +840,8 @@ export default class TeraFy {
827
840
  *
828
841
  * @returns {TeraFy} This chainable terafy instance
829
842
  */
830
- use(source, options) {
831
- let mod =
843
+ use(source: any, options?: any) {
844
+ let mod: any =
832
845
  typeof source == 'function' ? new source(this, options)
833
846
  : typeof source == 'object' ? source
834
847
  : typeof source == 'string' ? (()=> { throw new Error('use(String) is not yet supported') })()
@@ -847,7 +860,7 @@ export default class TeraFy {
847
860
  * @param {Object} target Initalied class instance to extend
848
861
  * @param {Object} source Initalized source object to extend from
849
862
  */
850
- mixin(target, source) {
863
+ mixin(target: any, source: any) {
851
864
  // Iterate through the source object upwards extracting each prototype
852
865
  let prototypeStack = [];
853
866
  let node = source;
@@ -863,17 +876,17 @@ export default class TeraFy {
863
876
  && !prop.startsWith('__') // Ignore double underscore meta properties
864
877
  )
865
878
  .forEach(prop => {
866
- if (typeof source[prop] == 'function') { // Inheriting function - glue onto object as non-editable, non-enumerable property
879
+ if (typeof (source as any)[prop] == 'function') { // Inheriting function - glue onto object as non-editable, non-enumerable property
867
880
  Object.defineProperty(
868
881
  target,
869
882
  prop,
870
883
  {
871
884
  enumerable: false,
872
- value: source[prop].bind(target), // Rebind functions
885
+ value: (source as any)[prop].bind(target), // Rebind functions
873
886
  },
874
887
  );
875
888
  } else { // Everything else, just glue onto the object
876
- target[prop] = source[prop];
889
+ target[prop] = (source as any)[prop];
877
890
  }
878
891
  })
879
892
  )
@@ -891,7 +904,7 @@ export default class TeraFy {
891
904
  *
892
905
  * @returns {TeraFy} This chainable terafy instance
893
906
  */
894
- toggleDevMode(devModeEnabled = 'toggle') {
907
+ toggleDevMode(devModeEnabled: 'toggle' | 'proxy' | boolean = 'toggle') {
895
908
  if (devModeEnabled === 'toggle') {
896
909
  this.settings.devMode = !this.settings.devMode;
897
910
  } else if (devModeEnabled === 'proxy') {
@@ -908,7 +921,7 @@ export default class TeraFy {
908
921
  this.settings.restrictOrigin = '*'; // Allow all upstream iframes
909
922
 
910
923
  if (this.dom?.el) // Have we actually set up yet?
911
- this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
924
+ this.dom.el!.classList.toggle('dev-mode', this.settings.devMode);
912
925
 
913
926
  return this;
914
927
  }
@@ -920,7 +933,7 @@ export default class TeraFy {
920
933
  *
921
934
  * @param {String|Boolean} [isFocused='toggle'] Whether to fullscreen the embedded component
922
935
  */
923
- toggleFocus(isFocused = 'toggle') {
936
+ toggleFocus(isFocused: boolean | 'toggle' = 'toggle') {
924
937
  this.debug('INFO', 2, 'Request focus', {isFocused});
925
938
  globalThis.document.body.classList.toggle('tera-fy-focus', isFocused === 'toggle' ? undefined : isFocused);
926
939
  }
@@ -1187,9 +1200,9 @@ export default class TeraFy {
1187
1200
  *
1188
1201
  * @returns {Promise<ProjectFile>} The eventually selected file, if in save mode new files are created as stubs
1189
1202
  */
1190
- selectProjectFile(options) {
1203
+ selectProjectFile(options?: any) {
1191
1204
  return this.rpc('selectProjectFile', options)
1192
- .then(file => file
1205
+ .then((file: any) => file
1193
1206
  ? new ProjectFile({
1194
1207
  tera: this,
1195
1208
  ...file,
@@ -1210,9 +1223,9 @@ export default class TeraFy {
1210
1223
  *
1211
1224
  * @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
1212
1225
  */
1213
- getProjectFiles(options) {
1226
+ getProjectFiles(options?: any) {
1214
1227
  return this.rpc('getProjectFiles', options)
1215
- .then(files => files.map(file =>
1228
+ .then((files: any) => files.map((file: any) =>
1216
1229
  new ProjectFile({
1217
1230
  tera: this,
1218
1231
  ...file,
@@ -1245,9 +1258,9 @@ export default class TeraFy {
1245
1258
  *
1246
1259
  * @returns {Promise<ProjectFile>} The eventual fetched ProjectFile (or requested subkey)
1247
1260
  */
1248
- getProjectFile(id, options) {
1261
+ getProjectFile(id: any, options?: any) {
1249
1262
  return this.rpc('getProjectFile', id, options)
1250
- .then(file => file
1263
+ .then((file: any) => file
1251
1264
  ? new ProjectFile({
1252
1265
  tera: this,
1253
1266
  ...file,
@@ -1265,9 +1278,9 @@ export default class TeraFy {
1265
1278
  * @param {String} name The name + relative directory path component
1266
1279
  * @returns {Promise<ProjectFile>} The eventual ProjectFile created
1267
1280
  */
1268
- createProjectFile(name) {
1281
+ createProjectFile(name: any) {
1269
1282
  return this.rpc('createProjectFile', name)
1270
- .then(file => file
1283
+ .then((file: any) => file
1271
1284
  ? new ProjectFile({
1272
1285
  tera: this,
1273
1286
  ...file,
@@ -1276,6 +1289,36 @@ export default class TeraFy {
1276
1289
  )
1277
1290
  }
1278
1291
 
1292
+ /**
1293
+ * Moves a project file to a new name/path.
1294
+ * The file's unique ID (UUID) remains the same, but its 'name' (relative path) and associated properties will be updated.
1295
+ *
1296
+ * @function moveProjectFile
1297
+ * @param {String} sourceId The unique ID (UUID) of the file to move.
1298
+ * @param {String} newName The new relative name for the file (e.g., "documents/report-final.pdf" or "image.png").
1299
+ * This path is relative to the project's root file directory.
1300
+ *
1301
+ * @param {Object} [options] Additional options to mutate behaviour.
1302
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing.
1303
+ * @param {Boolean} [options.overwrite=true] If true (default), moving a file to a `newName` that already exists will overwrite the existing file.
1304
+ * This aligns with the default behavior of the underlying Supabase storage `move` operation.
1305
+ * If set to false, the function would ideally check and prevent overwrite, but current implementation relies on underlying storage behavior.
1306
+ *
1307
+ * @returns {Promise<ProjectFile | null>} A promise which resolves to the updated ProjectFile object for the moved file if found after the operation,
1308
+ * or null if the file could not be located post-move (e.g., if its ID changed unexpectedly or it was deleted).
1309
+ */
1310
+ moveProjectFile(sourceId: string, newName: string, options?: any): Promise<ProjectFile | null> {
1311
+ return this.rpc('moveProjectFile', sourceId, newName, options)
1312
+ .then((fileData: any) => { // fileData is the plain object from the server
1313
+ if (fileData) {
1314
+ return new ProjectFile({
1315
+ tera: this, // Pass the TeraFy client instance
1316
+ ...fileData,
1317
+ });
1318
+ }
1319
+ return null;
1320
+ });
1321
+ }
1279
1322
 
1280
1323
  /**
1281
1324
  * Remove a project file by its ID
@@ -1304,6 +1347,30 @@ export default class TeraFy {
1304
1347
  * @returns {Promise} A promise which will resolve when the write operation has completed
1305
1348
  */
1306
1349
 
1350
+ /**
1351
+ * Creates a new "folder" within the project's file storage.
1352
+ * Folders in Supabase storage are typically represented by creating a placeholder file (e.g., .emptyFolderPlaceholder) within the desired path.
1353
+ * This operation is idempotent: if the folder (via its placeholder) already exists, it will not error.
1354
+ *
1355
+ * @function createProjectFolder
1356
+ * @param {String} folderPath The relative path of the folder to create (e.g., "myDocuments/reports").
1357
+ * @param {Object} [options] Additional options.
1358
+ * @param {Boolean} [options.autoRequire=true] Automatically run `requireProject()` to ensure an active project context.
1359
+ * @returns {Promise<void>} A promise that resolves when the folder is created or ensured.
1360
+ * @throws {Error} If no project is active (and autoRequire is false), or if folderPath is invalid, or if the creation fails.
1361
+ */
1362
+
1363
+ /**
1364
+ * Deletes a "folder" and all its contents from the project's file storage.
1365
+ * This involves listing all files under the given folder path (prefix) and removing them.
1366
+ *
1367
+ * @function deleteProjectFolder
1368
+ * @param {String} folderPath The relative path of the folder to delete (e.g., "myDocuments/reports").
1369
+ * @param {Object} [options] Additional options.
1370
+ * @param {Boolean} [options.autoRequire=true] Automatically run `requireProject()` to ensure an active project context.
1371
+ * @returns {Promise<null>} A promise that resolves with null when the folder and its contents are deleted.
1372
+ * @throws {Error} If no project is active (and autoRequire is false), or if folderPath is invalid, or if deletion fails.
1373
+ */
1307
1374
 
1308
1375
  /**
1309
1376
  * Prompt the user to select a library to operate on and return a array of references in a given format
@@ -1498,4 +1565,4 @@ export default class TeraFy {
1498
1565
  */
1499
1566
 
1500
1567
  // }}}
1501
- }
1568
+ }