@iebh/tera-fy 1.0.2 → 1.0.4

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/index.html CHANGED
@@ -225,10 +225,10 @@
225
225
  <div class="card-body">
226
226
  <div class="list-group">
227
227
  <a
228
- @click="run('getProjectStateSnapshot')"
228
+ @click="run('getProjectState')"
229
229
  class="list-group-item list-group-item-action"
230
230
  >
231
- terafy.getProjectStateSnapshot()
231
+ terafy.getProjectState()
232
232
  </a>
233
233
  <a
234
234
  @click="run('bindProjectState')"
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Base TeraFy plugin interface
3
+ * This is included as a documentation exanple only
4
+ *
5
+ * @class TeraFyPlugin
6
+ */
7
+ export default class TeraFyPlugin {
8
+
9
+ /**
10
+ * Optional function to be included when the main TeraFyClient is initalized
11
+ */
12
+ init() {
13
+ }
14
+
15
+
16
+ /**
17
+ * Instance constructor
18
+ *
19
+ * @param {TeraFy} terafy The TeraFy client this plugin is being initalized against
20
+ * @param {Object} [options] Additional options to mutate behaviour
21
+ */
22
+ constructor(terafy, options) {
23
+ }
24
+ }
@@ -0,0 +1,65 @@
1
+ import TeraFyPluginBase from './base.js';
2
+ import diff from 'just-diff';
3
+ import {reactive, watch} from 'vue';
4
+
5
+ /**
6
+ * Vue observables plugin
7
+ * Provides the `bindProjectState()` function for Vue based projects
8
+ *
9
+ * This function is expected to be included via the `terafy.use(MODULE, OPTIONS)` syntax rather than directly
10
+ *
11
+ * @class TeraFyPluginVue
12
+ */
13
+ export default class TeraFyPluginVue extends TeraFyPluginBase {
14
+
15
+ /**
16
+ * Return a Vue reactive object that can be read/written which whose changes will transparently be written back to the TERA server instance
17
+ *
18
+ * @param {Object} [options] Additional options to mutate behaviour
19
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
20
+ * @param {Boolean} [options.write=true] Allow local reactivity to writes - send these to the server
21
+ * @param {Array<String>} Paths to subscribe to e.g. ['/users/'],
22
+ *
23
+ * @returns {Promies<Reactive<Object>>} A reactive object representing the project state
24
+ */
25
+ bindProjectState(options) {
26
+ let settings = {
27
+ autoRequire: true,
28
+ write: true,
29
+ ...options,
30
+ };
31
+
32
+ return Promise.resolve()
33
+ .then(()=> settings.autoRequire && this.requireProject())
34
+ .then(()=> this.getProjectState({
35
+ autoRequire: false, // already handled this
36
+ paths: settings.paths,
37
+ }))
38
+ .then(snapshot => {
39
+ // Create initial reactive
40
+ let stateReactive = reactive(snapshot);
41
+
42
+ // Watch for remote changes and update
43
+ // FIXME: Not yet supported
44
+
45
+ // Watch for local writes and react
46
+ if (settings.write) {
47
+ watch(
48
+ stateReactive,
49
+ (newVal, oldVal) => {
50
+ let diff = diff(newVal, oldVal);
51
+ this.debug('APPLY DIFF', diff);
52
+ this.applyProjectStatePatch(diff);
53
+ },
54
+ {
55
+ deep: true,
56
+ },
57
+ );
58
+ }
59
+
60
+ // Return Vue Reactive
61
+ return stateReactive;
62
+ })
63
+ }
64
+
65
+ }
@@ -1,7 +1,5 @@
1
1
  import {cloneDeep} from 'lodash-es';
2
2
  import {nanoid} from 'nanoid';
3
- import {reactive, watch} from 'vue';
4
- import diff from 'just-diff';
5
3
 
6
4
  /* globals globalThis */
7
5
 
@@ -59,7 +57,7 @@ export default class TeraFy {
59
57
  'bindProject', 'getProject', 'getProjects', 'setActiveProject', 'requireProject', 'selectProject',
60
58
 
61
59
  // Project state
62
- 'getProjectStateSnapshot', 'applyProjectStatePatch',
60
+ 'getProjectState', 'applyProjectStatePatch',
63
61
  // bindProjectState() - See below
64
62
 
65
63
  // Project Libraries
@@ -67,6 +65,13 @@ export default class TeraFy {
67
65
  ];
68
66
 
69
67
 
68
+ /**
69
+ * Loaded plugins via Use()
70
+ * @type {Array<TeraFyPlugin>}
71
+ */
72
+ plugins = [];
73
+
74
+
70
75
  // Messages - send(), sendRaw(), rpc(), acceptMessage() {{{
71
76
 
72
77
  /**
@@ -214,6 +219,9 @@ export default class TeraFy {
214
219
  this.injectMain();
215
220
  this.injectStylesheet();
216
221
  this.injectMethods();
222
+ return Promise.all(this.plugins
223
+ .map(plugin => plugin.init())
224
+ );
217
225
  }
218
226
 
219
227
 
@@ -301,7 +309,7 @@ export default class TeraFy {
301
309
  }
302
310
  // }}}
303
311
 
304
- // Utility - debug(), bindProjectState(), toggleFullscreen() {{{
312
+ // Utility - debug(), use(), toggleFullscreen() {{{
305
313
 
306
314
  /**
307
315
  * Debugging output function
@@ -320,64 +328,54 @@ export default class TeraFy {
320
328
 
321
329
 
322
330
  /**
323
- * Return a Vue reactive object that can be read/written which whose changes will transparently be written back to the TERA server instance
331
+ * Set or merge settings
332
+ * This function also routes 'special' keys like `devMode` to their internal handlers
333
+ *
334
+ * @param {String|Object} key Either a single setting key to set or an object to merge
335
+ * @param {*} value The value to set if `key` is a string
324
336
  *
337
+ * @returns {TeraFy} This chainable terafy instance
338
+ */
339
+ set(key, value) {
340
+ if (typeof key == 'string') {
341
+ this.settings[key] = value;
342
+ } else {
343
+ Object.assign(this.settings, key);
344
+ }
345
+
346
+ return this.toggleDevMode(this.settings.devMode);
347
+ }
348
+
349
+
350
+ /**
351
+ * Include a TeraFy client plugin
352
+ *
353
+ * @param {Object} The module function to include. Invoked as `(teraClient:TeraFy, options:Object)`
325
354
  * @param {Object} [options] Additional options to mutate behaviour
326
- * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
327
- * @param {Boolean} [options.write=true] Allow local reactivity to writes - send these to the server
328
- * @param {Array<String>} Paths to subscribe to e.g. ['/users/'],
329
355
  *
330
- * @returns {Promies<Reactive<Object>>} A reactive object representing the project state
356
+ * @returns {TeraFy} This chainable terafy instance
331
357
  */
332
- bindProjectState(options) {
333
- let settings = {
334
- autoRequire: true,
335
- write: true,
336
- ...options,
337
- };
338
-
339
- return Promise.resolve()
340
- .then(()=> settings.autoRequire && this.requireProject())
341
- .then(()=> this.getProjectStateSnapshot({
342
- autoRequire: false, // already handled this
343
- paths: settings.paths,
344
- }))
345
- .then(snapshot => {
346
- // Create initial reactive
347
- let stateReactive = reactive(snapshot);
348
-
349
- // Watch for remote changes and update
350
- // FIXME: Not yet supported
351
-
352
- // Watch for local writes and react
353
- if (settings.write) {
354
- watch(
355
- stateReactive,
356
- (newVal, oldVal) => {
357
- let diff = diff(newVal, oldVal);
358
- this.debug('APPLY DIFF', diff);
359
- this.applyProjectStatePatch(diff);
360
- },
361
- {
362
- deep: true,
363
- },
364
- );
365
- }
366
-
367
- // Return Vue Reactive
368
- return stateReactive;
369
- })
358
+ use(mod, options) {
359
+ if (typeof mod != 'function') throw new Error('Expected use() call to be provided with a class initalizer');
360
+
361
+ let singleton = new mod(this, options);
362
+ this.plugins.push(singleton);
363
+ return this;
370
364
  }
371
365
 
372
366
 
373
367
  /**
374
368
  * Fit the nested TERA server to a full-screen
375
369
  * This is usually because the server component wants to perform some user activity like calling $prompt
370
+ *
376
371
  * @param {String|Boolean} [isFocused='toggle'] Whether to fullscreen the embedded component
372
+ *
373
+ * @returns {TeraFy} This chainable terafy instance
377
374
  */
378
375
  toggleFocus(isFocused = 'toggle') {
379
376
  this.debug('Request focus', {isFocused});
380
377
  globalThis.document.body.classList.toggle('tera-fy-focus', isFocused === 'toggle' ? undefined : isFocused);
378
+ return this;
381
379
  }
382
380
 
383
381
  // }}}
@@ -384,7 +384,7 @@ export default class TeraFyServer {
384
384
 
385
385
  // }}}
386
386
 
387
- // Project State - getProjectStateSnapshot(), applyProjectStatePatch() {{{
387
+ // Project State - getProjectState(), applyProjectStatePatch() {{{
388
388
 
389
389
  /**
390
390
  * Return the current, full snapshot state of the active project
@@ -395,7 +395,7 @@ export default class TeraFyServer {
395
395
  *
396
396
  * @returns {Promise<Object>} The current project state snapshot
397
397
  */
398
- getProjectStateSnapshot(options) {
398
+ getProjectState(options) {
399
399
  let settings = {
400
400
  autoRequire: true,
401
401
  paths: null,
@@ -404,6 +404,7 @@ export default class TeraFyServer {
404
404
 
405
405
  return Promise.resolve()
406
406
  .then(()=> settings.autoRequire && this.requireProject())
407
+ .then(()=> app.service('$projects').active)
407
408
  }
408
409
 
409
410
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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:client": "jsdoc2md --files lib/terafy.client.js >docs/terafy.client.md",
10
- "build:docs:server": "jsdoc2md --files lib/terafy.server.js >docs/terafy.server.md"
9
+ "build:docs": "jsdoc2md --files lib/terafy.*.js lib/plugins/*.js >api.md",
10
+ "lint": "eslint ./lib"
11
11
  },
12
12
  "type": "module",
13
13
  "imports": {
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "exports": {
17
17
  ".": "./lib/terafy.client.js",
18
- "./server": "./lib/terafy.server.js"
18
+ "./server": "./lib/terafy.server.js",
19
+ "./plugins/*": "./lib/plugins/*.js"
19
20
  },
20
21
  "repository": {
21
22
  "type": "git",
@@ -53,9 +54,11 @@
53
54
  "node": ">=18"
54
55
  },
55
56
  "peerDependencies": {
56
- "lodash-es": "^4.17.21",
57
57
  "just-diff": "^6.0.2",
58
- "nanoid": "^5.0.2",
58
+ "lodash-es": "^4.17.21",
59
+ "nanoid": "^5.0.2"
60
+ },
61
+ "optionalDependencies": {
59
62
  "vue": "^3.3.7"
60
63
  },
61
64
  "devDependencies": {
@@ -1,183 +0,0 @@
1
- ## Classes
2
-
3
- <dl>
4
- <dt><a href="#TeraFy">TeraFy</a></dt>
5
- <dd></dd>
6
- </dl>
7
-
8
- ## Functions
9
-
10
- <dl>
11
- <dt><a href="#send">send(message)</a> ⇒ <code>Promise.&lt;*&gt;</code></dt>
12
- <dd><p>Send a message + wait for a response object</p>
13
- </dd>
14
- <dt><a href="#sendRaw">sendRaw(message)</a></dt>
15
- <dd><p>Send raw message content to the server
16
- This function does not return or wait for a reply - use <code>send()</code> for that</p>
17
- </dd>
18
- <dt><a href="#rpc">rpc(method)</a> ⇒ <code>Promise.&lt;*&gt;</code></dt>
19
- <dd><p>Call an RPC function in the server instance</p>
20
- </dd>
21
- <dt><a href="#acceptMessage">acceptMessage(Raw)</a></dt>
22
- <dd><p>Accept an incoming message</p>
23
- </dd>
24
- <dt><a href="#toggleDevMode">toggleDevMode([devModeEnabled])</a> ⇒ <code><a href="#TeraFy">TeraFy</a></code></dt>
25
- <dd><p>Set or toggle devMode</p>
26
- </dd>
27
- <dt><a href="#init">init()</a></dt>
28
- <dd><p>Initalize the TERA client singleton</p>
29
- </dd>
30
- <dt><a href="#injectMain">injectMain()</a></dt>
31
- <dd><p>Find an existing active TERA server OR initalize one</p>
32
- </dd>
33
- <dt><a href="#injectStylesheet">injectStylesheet()</a></dt>
34
- <dd><p>Inject a local stylesheet to handle TERA server functionality</p>
35
- </dd>
36
- <dt><a href="#injectMethods">injectMethods()</a></dt>
37
- <dd><p>Inject all server methods defined in <code>methods</code> as local functions wrapped in the <code>rpc</code> function</p>
38
- </dd>
39
- <dt><a href="#debug">debug()</a></dt>
40
- <dd><p>Debugging output function
41
- This function will only act if <code>settings.devMode</code> is truthy</p>
42
- </dd>
43
- <dt><a href="#bindProjectState">bindProjectState([options], Paths)</a> ⇒ <code>Promies.&lt;Reactive.&lt;Object&gt;&gt;</code></dt>
44
- <dd><p>Return a Vue reactive object that can be read/written which whose changes will transparently be written back to the TERA server instance</p>
45
- </dd>
46
- <dt><a href="#toggleFocus">toggleFocus([isFocused])</a></dt>
47
- <dd><p>Fit the nested TERA server to a full-screen
48
- This is usually because the server component wants to perform some user activity like calling $prompt</p>
49
- </dd>
50
- </dl>
51
-
52
- <a name="TeraFy"></a>
53
-
54
- ## TeraFy
55
- **Kind**: global class
56
- <a name="new_TeraFy_new"></a>
57
-
58
- ### new TeraFy()
59
- Main Tera-Fy Client (class singleton) to be used in a frontend browser
60
-
61
- <a name="send"></a>
62
-
63
- ## send(message) ⇒ <code>Promise.&lt;\*&gt;</code>
64
- Send a message + wait for a response object
65
-
66
- **Kind**: global function
67
- **Returns**: <code>Promise.&lt;\*&gt;</code> - A promise which resolves when the operation has completed with the remote reply
68
-
69
- | Param | Type | Description |
70
- | --- | --- | --- |
71
- | message | <code>Object</code> | Message object to send |
72
-
73
- <a name="sendRaw"></a>
74
-
75
- ## sendRaw(message)
76
- Send raw message content to the server
77
- This function does not return or wait for a reply - use `send()` for that
78
-
79
- **Kind**: global function
80
-
81
- | Param | Type | Description |
82
- | --- | --- | --- |
83
- | message | <code>Object</code> | Message object to send |
84
-
85
- <a name="rpc"></a>
86
-
87
- ## rpc(method) ⇒ <code>Promise.&lt;\*&gt;</code>
88
- Call an RPC function in the server instance
89
-
90
- **Kind**: global function
91
- **Returns**: <code>Promise.&lt;\*&gt;</code> - The resolved output of the server function
92
-
93
- | Param | Type | Description |
94
- | --- | --- | --- |
95
- | method | <code>String</code> | The method name to call |
96
- | [...] | <code>\*</code> | Optional arguments to pass to the function |
97
-
98
- <a name="acceptMessage"></a>
99
-
100
- ## acceptMessage(Raw)
101
- Accept an incoming message
102
-
103
- **Kind**: global function
104
-
105
- | Param | Type | Description |
106
- | --- | --- | --- |
107
- | Raw | <code>MessageEvent</code> | message event to process |
108
-
109
- <a name="toggleDevMode"></a>
110
-
111
- ## toggleDevMode([devModeEnabled]) ⇒ [<code>TeraFy</code>](#TeraFy)
112
- Set or toggle devMode
113
-
114
- **Kind**: global function
115
- **Returns**: [<code>TeraFy</code>](#TeraFy) - This chainable terafy instance
116
-
117
- | Param | Type | Default | Description |
118
- | --- | --- | --- | --- |
119
- | [devModeEnabled] | <code>String</code> \| <code>Boolean</code> | <code>&#x27;toggle&#x27;</code> | Optional boolean to force dev mode |
120
-
121
- <a name="init"></a>
122
-
123
- ## init()
124
- Initalize the TERA client singleton
125
-
126
- **Kind**: global function
127
- <a name="injectMain"></a>
128
-
129
- ## injectMain()
130
- Find an existing active TERA server OR initalize one
131
-
132
- **Kind**: global function
133
- <a name="injectStylesheet"></a>
134
-
135
- ## injectStylesheet()
136
- Inject a local stylesheet to handle TERA server functionality
137
-
138
- **Kind**: global function
139
- <a name="injectMethods"></a>
140
-
141
- ## injectMethods()
142
- Inject all server methods defined in `methods` as local functions wrapped in the `rpc` function
143
-
144
- **Kind**: global function
145
- <a name="debug"></a>
146
-
147
- ## debug()
148
- Debugging output function
149
- This function will only act if `settings.devMode` is truthy
150
-
151
- **Kind**: global function
152
-
153
- | Param | Type | Description |
154
- | --- | --- | --- |
155
- | [msg...] | <code>String</code> | Output to show |
156
-
157
- <a name="bindProjectState"></a>
158
-
159
- ## bindProjectState([options], Paths) ⇒ <code>Promies.&lt;Reactive.&lt;Object&gt;&gt;</code>
160
- Return a Vue reactive object that can be read/written which whose changes will transparently be written back to the TERA server instance
161
-
162
- **Kind**: global function
163
- **Returns**: <code>Promies.&lt;Reactive.&lt;Object&gt;&gt;</code> - A reactive object representing the project state
164
-
165
- | Param | Type | Default | Description |
166
- | --- | --- | --- | --- |
167
- | [options] | <code>Object</code> | | Additional options to mutate behaviour |
168
- | [options.autoRequire] | <code>Boolean</code> | <code>true</code> | Run `requireProject()` automatically before continuing |
169
- | [options.write] | <code>Boolean</code> | <code>true</code> | Allow local reactivity to writes - send these to the server |
170
- | Paths | <code>Array.&lt;String&gt;</code> | | to subscribe to e.g. ['/users/'], |
171
-
172
- <a name="toggleFocus"></a>
173
-
174
- ## toggleFocus([isFocused])
175
- Fit the nested TERA server to a full-screen
176
- This is usually because the server component wants to perform some user activity like calling $prompt
177
-
178
- **Kind**: global function
179
-
180
- | Param | Type | Default | Description |
181
- | --- | --- | --- | --- |
182
- | [isFocused] | <code>String</code> \| <code>Boolean</code> | <code>&#x27;toggle&#x27;</code> | Whether to fullscreen the embedded component |
183
-