@iebh/tera-fy 1.0.5 → 1.0.6

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/README.md CHANGED
@@ -1,6 +1,30 @@
1
1
  TERA-fy
2
2
  =======
3
- TERA website worker.
3
+ TERA website worker, intended to be embedded with TERA tools.
4
4
 
5
5
  * [API workbench](https://iebh.github.io/TERA-fy/)
6
6
  * [TERA-fy Class API](./api.md)
7
+
8
+
9
+ TERA-fy is a component intended to be dropped into a sub-site / tool used with in the main [TERA](https://tera-tools.com) project. It provides various functionality like data sync with the parent TERA instance.
10
+
11
+
12
+ Quick Start
13
+ -----------
14
+
15
+ ```javascript
16
+ import TeraFy from '@iebh/tera-fy';
17
+ import TerafyVue from '@iebh/tera-fy/plugins/vue';
18
+ let terafy = new TeraFy()
19
+ .set('devMode', true) // Set this option to see debugging messages
20
+ .use(TerafyVue); // Add the Vue plugin
21
+
22
+ // Initialize everything
23
+ await terafy.init();
24
+
25
+ // Require that the active session has a project selected
26
+ await terafy.requireProject();
27
+
28
+ // Go fetch the state of the active project
29
+ let projectState = await terafy.getProjectState(); //= Object representing the active project
30
+ ```
@@ -105,14 +105,19 @@ export default class TeraFy {
105
105
  * @param {Object} message Message object to send
106
106
  */
107
107
  sendRaw(message) {
108
- this.dom.iframe.contentWindow.postMessage(
109
- {
108
+ let payload;
109
+ try {
110
+ payload = {
110
111
  TERA: 1,
111
112
  id: message.id || nanoid(),
112
113
  ...cloneDeep(message), // Need to clone to resolve promise nasties
113
- },
114
- this.settings.restrictOrigin
115
- );
114
+ };
115
+ this.dom.iframe.contentWindow.postMessage(payload, this.settings.restrictOrigin);
116
+ } catch (e) {
117
+ this.debug('ERROR', 'Message compose client->server:', e);
118
+ this.debug('ERROR', 'Attempted to dispatch payload client->server', payload);
119
+ throw e;
120
+ }
116
121
  }
117
122
 
118
123
 
@@ -181,7 +186,7 @@ export default class TeraFy {
181
186
 
182
187
  // }}}
183
188
 
184
- // Init - constructor(), toggleDevMode(), init(), injectMain(), injectStylesheet(), injectMethods() {{{
189
+ // Init - constructor(), init(), inject*() {{{
185
190
 
186
191
  /**
187
192
  * Setup the TERA-fy client singleton
@@ -193,42 +198,34 @@ export default class TeraFy {
193
198
  }
194
199
 
195
200
 
196
- /**
197
- * Set or toggle devMode
198
- *
199
- * @param {String|Boolean} [devModeEnabled='toggle'] Optional boolean to force dev mode
200
- *
201
- * @returns {TeraFy} This chainable terafy instance
202
- */
203
- toggleDevMode(devModeEnabled = 'toggle') {
204
- this.settings.devMode = devModeEnabled === 'toggle'
205
- ? !this.settings.devMode
206
- : devModeEnabled;
207
-
208
- this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
209
- return this;
210
- }
211
-
212
-
213
201
  /**
214
202
  * Initalize the TERA client singleton
203
+ *
204
+ * @returns {Promise<TeraFy>} An eventual promise which will resovle with this terafy instance
215
205
  */
216
206
  init() {
217
207
  window.addEventListener('message', this.acceptMessage.bind(this));
218
208
 
219
- this.injectMain();
220
- this.injectStylesheet();
221
- this.injectMethods();
222
- return Promise.all(this.plugins
223
- .map(plugin => plugin.init())
224
- );
209
+ return Promise.all([
210
+ // Init core functions async
211
+ this.injectComms(),
212
+ this.injectStylesheet(),
213
+ this.injectMethods(),
214
+
215
+ // Init all plugins
216
+ ...this.plugins
217
+ .map(plugin => plugin.init()),
218
+ ])
219
+ .then(()=> this)
225
220
  }
226
221
 
227
222
 
228
223
  /**
229
224
  * Find an existing active TERA server OR initalize one
225
+ *
226
+ * @returns {Promise} A promise which will resolve when the loading has completed and we have found a parent TERA instance or initiallized a child
230
227
  */
231
- injectMain() {
228
+ injectComms() { return new Promise(resolve => {
232
229
  this.dom.el = document.createElement('div')
233
230
  this.dom.el.id = 'tera-fy';
234
231
  this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
@@ -240,12 +237,13 @@ export default class TeraFy {
240
237
  this.dom.iframe.setAttribute('sandbox', 'allow-downloads allow-scripts allow-same-origin');
241
238
  this.dom.iframe.addEventListener('load', ()=> {
242
239
  this.debug('TERA EMBED FRAME READY');
240
+ resolve();
243
241
  });
244
242
 
245
243
  // Start document load sequence + append to DOM
246
244
  this.dom.iframe.src = this.settings.siteUrl;
247
245
  this.dom.el.append(this.dom.iframe);
248
- }
246
+ })}
249
247
 
250
248
 
251
249
  /**
@@ -306,20 +304,33 @@ export default class TeraFy {
306
304
  this.methods.forEach(method =>
307
305
  this[method] = this.rpc.bind(this, method)
308
306
  );
307
+ this.debug('Remote methods installed:', this.methods);
309
308
  }
310
309
  // }}}
311
310
 
312
- // Utility - debug(), use(), mixin(), toggleFullscreen() {{{
311
+ // Utility - debug(), use(), mixin(), toggleDevMode(), toggleFocus() {{{
313
312
 
314
313
  /**
315
314
  * Debugging output function
316
315
  * This function will only act if `settings.devMode` is truthy
317
316
  *
317
+ * @param {'INFO'|'LOG'|'WARN'|'ERROR'} [status] Optional prefixing level to mark the message as. 'WARN' and 'ERROR' will always show reguardless of devMode being enabled
318
318
  * @param {String} [msg...] Output to show
319
319
  */
320
320
  debug(...msg) {
321
- if (!this.settings.devMode) return;
322
- console.log(
321
+ let method = 'log';
322
+ // Argument mangling for prefixing method {{{
323
+ if (typeof msg[0] == 'string' && ['INFO', 'LOG', 'WARN', 'ERROR'].includes(msg[0])) {
324
+ method = msg.shift().toLowerCase();
325
+ }
326
+ // }}}
327
+
328
+ if (
329
+ ['INFO', 'LOG'].includes(method)
330
+ && !this.settings.devMode
331
+ ) return;
332
+
333
+ console[method](
323
334
  '%c[TERA-FY CLIENT]',
324
335
  'font-weight: bold; color: #ff5722;',
325
336
  ...msg,
@@ -389,18 +400,34 @@ export default class TeraFy {
389
400
  }
390
401
 
391
402
 
403
+ /**
404
+ * Set or toggle devMode
405
+ *
406
+ * @param {String|Boolean} [devModeEnabled='toggle'] Optional boolean to force dev mode
407
+ *
408
+ * @returns {TeraFy} This chainable terafy instance
409
+ */
410
+ toggleDevMode(devModeEnabled = 'toggle') {
411
+ this.settings.devMode = devModeEnabled === 'toggle'
412
+ ? !this.settings.devMode
413
+ : devModeEnabled;
414
+
415
+ if (this.dom.el) // Have we actually set up yet?
416
+ this.dom.el.classList.toggle('dev-mode', this.settings.devMode);
417
+
418
+ return this;
419
+ }
420
+
421
+
392
422
  /**
393
423
  * Fit the nested TERA server to a full-screen
394
424
  * This is usually because the server component wants to perform some user activity like calling $prompt
395
425
  *
396
426
  * @param {String|Boolean} [isFocused='toggle'] Whether to fullscreen the embedded component
397
- *
398
- * @returns {TeraFy} This chainable terafy instance
399
427
  */
400
428
  toggleFocus(isFocused = 'toggle') {
401
429
  this.debug('Request focus', {isFocused});
402
430
  globalThis.document.body.classList.toggle('tera-fy-focus', isFocused === 'toggle' ? undefined : isFocused);
403
- return this;
404
431
  }
405
432
  // }}}
406
433
  }
@@ -31,18 +31,22 @@ export default class TeraFyServer {
31
31
  * @returns {Object} A context, which is this instance extended with additional properties
32
32
  */
33
33
  createContext(e) {
34
- // Rather ugly shallow-copy-of instance hack from https://stackoverflow.com/a/44782052/1295040
34
+ // Rather ugly shallow-copy-of instancr hack from https://stackoverflow.com/a/44782052/1295040
35
35
  return Object.assign(Object.create(Object.getPrototypeOf(this)), this, {
36
36
  messageEvent: e,
37
37
  sendRaw(message) { // Override sendRaw because we can't do this inline for security reasons
38
- this.debug('Send to message source', e.origin, {message});
39
- e.source.postMessage(
40
- {
38
+ let payload;
39
+ try {
40
+ payload = {
41
41
  TERA: 1,
42
42
  ...cloneDeep(message), // Need to clone to resolve promise nasties
43
- },
44
- this.settings.restrictOrigin
45
- );
43
+ };
44
+ e.source.postMessage(payload, this.settings.restrictOrigin);
45
+ } catch (e) {
46
+ this.debug('ERROR', 'Message compose/reply via server->cient:', e);
47
+ this.debug('ERROR', 'Attempted to dispatch payload server(via reply)->client', payload);
48
+ throw e;
49
+ }
46
50
  },
47
51
  });
48
52
  }
@@ -124,14 +128,18 @@ export default class TeraFyServer {
124
128
  * @param {Object} message Message object to send
125
129
  */
126
130
  sendRaw(message) {
127
- this.debug('SendRaw', {message});
128
- globalThis.parent.postMessage(
129
- {
131
+ let payload;
132
+ try {
133
+ payload = {
130
134
  TERA: 1,
131
135
  ...cloneDeep(message), // Need to clone to resolve promise nasties
132
- },
133
- this.settings.restrictOrigin
134
- );
136
+ };
137
+ globalThis.parent.postMessage(payload, this.settings.restrictOrigin);
138
+ } catch (e) {
139
+ this.debug('ERROR', 'Attempted to dispatch payload server->client', payload);
140
+ this.debug('ERROR', 'Message compose server->client:', e);
141
+ }
142
+
135
143
  }
136
144
 
137
145
 
@@ -497,7 +505,18 @@ export default class TeraFyServer {
497
505
  * @param {String} [msg...] Output to show
498
506
  */
499
507
  debug(...msg) {
500
- if (!this.settings.devMode) return;
508
+ let method = 'log';
509
+ // Argument mangling for prefixing method {{{
510
+ if (typeof msg[0] == 'string' && ['INFO', 'LOG', 'WARN', 'ERROR'].includes(msg[0])) {
511
+ method = msg.shift().toLowerCase();
512
+ }
513
+ // }}}
514
+
515
+ if (
516
+ ['INFO', 'LOG'].includes(method)
517
+ && !this.settings.devMode
518
+ ) return;
519
+
501
520
  console.log(
502
521
  '%c[TERA-FY SERVER]',
503
522
  'font-weight: bold; color: #4d659c;',
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "TERA website worker",
5
5
  "scripts": {
6
6
  "dev": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify --sourcemap --serve --servedir=.",
7
7
  "build": "concurrently 'npm:build:*'",
8
8
  "build:client": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify --sourcemap",
9
- "build:docs": "jsdoc2md --files lib/terafy.*.js lib/plugins/*.js >api.md",
9
+ "build:docs": "jsdoc2md --files lib/terafy.*.js plugins/*.js >api.md",
10
10
  "lint": "eslint ."
11
11
  },
12
12
  "type": "module",
package/plugins/vue.js CHANGED
@@ -67,7 +67,7 @@ export default class TeraFyPluginVue extends TeraFyPluginBase {
67
67
  * Provide a Vue@3 compatible plugin
68
68
  */
69
69
  vuePlugin() {
70
- let context = this;
70
+ let $tera = this;
71
71
 
72
72
  return {
73
73
 
@@ -90,9 +90,14 @@ export default class TeraFyPluginVue extends TeraFyPluginBase {
90
90
  ...options,
91
91
  };
92
92
 
93
- app.config.globalProperties[settings.globalName] = {
94
- // Create project binding
95
- // state: context.bindProjectState(settings.stateOptions),
93
+ // Make this module available globally
94
+ app.config.globalProperties[settings.globalName] = $tera;
95
+
96
+ // Bind $tera.state to the active project
97
+ // TODO: context.bindProjectState(settings.stateOptions),
98
+ $tera.state = {
99
+ id: 'TERAPROJ',
100
+ name: 'A fake project',
96
101
  };
97
102
  },
98
103