@iebh/tera-fy 1.0.9 → 1.0.10

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 (36) hide show
  1. package/README.md +9 -4
  2. package/api.md +445 -431
  3. package/dist/terafy.js +2 -2
  4. package/dist/terafy.js.map +4 -4
  5. package/docs/assets/anchor.js +350 -0
  6. package/docs/assets/bass-addons.css +12 -0
  7. package/docs/assets/bass.css +544 -0
  8. package/docs/assets/fonts/EOT/SourceCodePro-Bold.eot +0 -0
  9. package/docs/assets/fonts/EOT/SourceCodePro-Regular.eot +0 -0
  10. package/docs/assets/fonts/LICENSE.txt +93 -0
  11. package/docs/assets/fonts/OTF/SourceCodePro-Bold.otf +0 -0
  12. package/docs/assets/fonts/OTF/SourceCodePro-Regular.otf +0 -0
  13. package/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf +0 -0
  14. package/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf +0 -0
  15. package/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff +0 -0
  16. package/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff +0 -0
  17. package/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff +0 -0
  18. package/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff +0 -0
  19. package/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 +0 -0
  20. package/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 +0 -0
  21. package/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 +0 -0
  22. package/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 +0 -0
  23. package/docs/assets/fonts/source-code-pro.css +23 -0
  24. package/docs/assets/github.css +123 -0
  25. package/docs/assets/site.js +168 -0
  26. package/docs/assets/split.css +15 -0
  27. package/docs/assets/split.js +782 -0
  28. package/docs/assets/style.css +147 -0
  29. package/docs/index.html +3636 -0
  30. package/{index.html → docs/playground.html} +48 -12
  31. package/documentation.yml +12 -0
  32. package/lib/terafy.client.js +227 -6
  33. package/lib/terafy.server.js +107 -5
  34. package/package.json +7 -6
  35. package/plugins/vue2.js +173 -0
  36. package/plugins/{vue.js → vue3.js} +20 -11
@@ -20,7 +20,7 @@
20
20
  </style>
21
21
 
22
22
  <script type="module">
23
- import TeraFy from './dist/terafy.js';
23
+ import TeraFy from 'https://esm.run/@iebh/tera-fy';
24
24
 
25
25
  Vue.createApp({
26
26
  data() { return {
@@ -97,6 +97,32 @@
97
97
  </head>
98
98
  <body>
99
99
  <div id="app">
100
+ <!-- Nav header {{{ -->
101
+ <nav class="navbar navbar-expand-lg navbar-light bg-light px-4">
102
+ <a href="https://github.com/IEBH/TERA-fy" class="navbar-brand">TERA-fy</a>
103
+ <button type="button" data-toggle="collapse" data-target="#navbarAreas" class="navbar-toggler">
104
+ <span class="navbar-toggler-icon"></span>
105
+ </button>
106
+ <div id="navbarAreas" class="collapse navbar-collapse">
107
+ <ul class="navbar-nav">
108
+ <li class="navbar-item me-1">
109
+ <a href="https://iebh.github.io/TERA-fy" class="btn btn-light">API docs</a>
110
+ </li>
111
+ <li class="navbar-item mr-1">
112
+ <a href="https://iebh.github.io/TERA-fy/playground.html" class="btn btn-primary text-white">Playground</a>
113
+ </li>
114
+ </ul>
115
+ </div>
116
+ <ul class="navbar-nav">
117
+ <li class="navbar-item">
118
+ <a href="https://github.com/IEBH/TERA-fy" class="btn btn-light">
119
+ GitHub
120
+ </a>
121
+ </li>
122
+ </ul>
123
+ </nav>
124
+ <!-- }}} -->
125
+
100
126
  <div class="container pt-4">
101
127
  <div class="row">
102
128
  <div class="col-sm-12 col-md-6">
@@ -161,12 +187,6 @@
161
187
  <div class="card-header">Projects</div>
162
188
  <div class="card-body">
163
189
  <div class="list-group">
164
- <a
165
- @click="run('bindProject')"
166
- class="list-group-item list-group-item-action disabled"
167
- >
168
- terafy.bindProject()
169
- </a>
170
190
  <a
171
191
  @click="run('getProject')"
172
192
  class="list-group-item list-group-item-action"
@@ -186,7 +206,7 @@
186
206
  >
187
207
  <div>terafy.setActiveProject(</div>
188
208
  <select
189
- v-if="projects"
209
+ v-if="projects && projects.length > 0"
190
210
  v-model="project"
191
211
  class="form-control"
192
212
  placeholder="Select project..."
@@ -231,16 +251,32 @@
231
251
  terafy.getProjectState()
232
252
  </a>
233
253
  <a
234
- @click="run('bindProjectState')"
254
+ @click="run('applyProjectStatePatch')"
235
255
  class="list-group-item list-group-item-action disabled"
236
256
  >
237
- terafy.bindProjectState()
257
+ terafy.applyProjectStatePatch()
238
258
  </a>
239
259
  <a
240
- @click="run('applyProjectStatePatch')"
260
+ @click="run('subscribeProjectState')"
241
261
  class="list-group-item list-group-item-action disabled"
242
262
  >
243
- terafy.applyProjectStatePatch()
263
+ terafy.subscribeProjectState()
264
+ </a>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ <!-- }}} -->
269
+
270
+ <!-- Project Files {{{ -->
271
+ <div class="card mb-2">
272
+ <div class="card-header">Project Files</div>
273
+ <div class="card-body">
274
+ <div class="list-group">
275
+ <a
276
+ @click="run('getProjectFiles')"
277
+ class="list-group-item list-group-item-action"
278
+ >
279
+ terafy.getProjectFiles()
244
280
  </a>
245
281
  </div>
246
282
  </div>
@@ -0,0 +1,12 @@
1
+ toc:
2
+ - name: TeraFy Playground
3
+ description: |
4
+ See [the TeraFy Playground](./playground.html) to expriment with the various TeraFy API calls.
5
+ - name: Data entities
6
+ children:
7
+ - User
8
+ - Project
9
+ - ProjectFile
10
+ - name: TeraFy
11
+ description: |
12
+ API reference for all methods exposed by the TeraFy client library.
@@ -1,3 +1,4 @@
1
+ import {diff, jsonPatchPathConverter as jsPatchConverter} from 'just-diff';
1
2
  import {cloneDeep} from 'lodash-es';
2
3
  import {nanoid} from 'nanoid';
3
4
 
@@ -61,7 +62,7 @@ export default class TeraFy {
61
62
  'bindProject', 'getProject', 'getProjects', 'setActiveProject', 'requireProject', 'selectProject',
62
63
 
63
64
  // Project state
64
- 'getProjectState', 'applyProjectStatePatch',
65
+ 'getProjectState', 'applyProjectStatePatch', 'subscribeProjectState',
65
66
  // bindProjectState() - See below
66
67
 
67
68
  // Project files
@@ -204,6 +205,39 @@ export default class TeraFy {
204
205
 
205
206
  // }}}
206
207
 
208
+ // Project state - createProjectStatePatch(), applyProjectStatePatchLocal() {{{
209
+ /**
210
+ * Create + transmit a new project state patch base on the current and previous states
211
+ * The transmitted patch follows the [JSPatch](http://jsonpatch.com) standard
212
+ * This function accepts an entire projectState instance, computes the delta and transmits that to the server for merging
213
+ *
214
+ * @param {Object} newState The local projectState to accept
215
+ * @param {Object} oldState The previous projectState to examine against
216
+ *
217
+ * @returns {Promise} A promise which will resolve when the operation has completed
218
+ */
219
+ createProjectStatePatch(newState, oldState) {
220
+ let patch = diff(oldState, newState, jsPatchConverter);
221
+ this.debug('INFO', 'Created project patch', {newState, oldState});
222
+ return this.applyProjectStatePatch(patch);
223
+ }
224
+
225
+
226
+ /**
227
+ * Client function which accepts a patch from the server and applies it to local project state
228
+ * The patch should follow the [JSPatch](http://jsonpatch.com) standard
229
+ * This function is expected to be sub-classed by a plugin
230
+ *
231
+ * @param {Array} patch A JSPatch patch to apply
232
+ *
233
+ * @returns {Promise} A promise which will resolve when the operation has completed
234
+ */
235
+ applyProjectStatePatchLocal(patch) {
236
+ this.debug('WARN', 'Accepted project patch', patch, 'but applyProjectStatePatchLocal() has not been sub-classed');
237
+ return Promise.resolve();
238
+ }
239
+ // }}}
240
+
207
241
  // Init - constructor(), init(), inject*() {{{
208
242
 
209
243
  /**
@@ -220,13 +254,16 @@ export default class TeraFy {
220
254
  * Initalize the TERA client singleton
221
255
  * This function can only be called once and will return the existing init() worker Promise if its called againt
222
256
  *
257
+ * @param {Object} [options] Additional options to merge into `settings` via `set`
223
258
  * @returns {Promise<TeraFy>} An eventual promise which will resovle with this terafy instance
224
259
  */
225
- init() {
260
+ init(options) {
261
+ if (options) this.set(options);
226
262
  if (this.init.promise) return this.init.promise; // Aleady been called - return init promise
227
263
 
228
264
  window.addEventListener('message', this.acceptMessage.bind(this));
229
265
 
266
+ const context = this;
230
267
  return this.init.promise = Promise.resolve()
231
268
  .then(()=> this.detectMode())
232
269
  .then(mode => this.settings.mode = mode)
@@ -235,11 +272,12 @@ export default class TeraFy {
235
272
  this.injectComms(),
236
273
  this.injectStylesheet(),
237
274
  this.injectMethods(),
238
-
239
- // Init all plugins
240
- ...this.plugins
241
- .map(plugin => plugin.init()),
242
275
  ]))
276
+ .then(()=> Promise.all( // Init all plugins (with this outer module as the context)
277
+ this.plugins.map(plugin =>
278
+ plugin.init.call(context)
279
+ )
280
+ ))
243
281
  }
244
282
 
245
283
 
@@ -453,6 +491,7 @@ export default class TeraFy {
453
491
  mixin(target, source) {
454
492
  Object.getOwnPropertyNames(Object.getPrototypeOf(source))
455
493
  .filter(prop => !['constructor', 'prototype', 'name'].includes(prop))
494
+ .filter(prop => prop != 'init') // Don't allow plugin init() to override our version - these all get called during init() anyway
456
495
  .forEach((prop) => {
457
496
  Object.defineProperty(
458
497
  target,
@@ -496,4 +535,186 @@ export default class TeraFy {
496
535
  globalThis.document.body.classList.toggle('tera-fy-focus', isFocused === 'toggle' ? undefined : isFocused);
497
536
  }
498
537
  // }}}
538
+
539
+ // Stub documentation carried over from ./terafy.server.js {{{
540
+ /**
541
+ * Return basic server information as a form of validation
542
+ *
543
+ * @function handshake
544
+ * @returns {Promise<Object>} Basic promise result
545
+ * @property {Date} date Server date
546
+ */
547
+
548
+
549
+ /**
550
+ * User / active session within TERA
551
+ *
552
+ * @name User
553
+ * @property {String} id Unique identifier of the user
554
+ * @property {String} email The email address of the current user
555
+ * @property {String} name The provided full name of the user
556
+ * @property {Boolean} isSubscribed Whether the active user has a TERA subscription
557
+ */
558
+
559
+
560
+ /**
561
+ * Fetch the current session user
562
+ *
563
+ * @function getUser
564
+ * @returns {Promise<User>} The current logged in user or null if none
565
+ */
566
+
567
+
568
+ /**
569
+ * Project entry within TERA
570
+ *
571
+ * @name Project
572
+ * @url https://tera-tools.com/help/api/schema
573
+ */
574
+
575
+
576
+ /**
577
+ * Get the currently active project, if any
578
+ *
579
+ * @function getProject
580
+ * @returns {Promise<Project|null>} The currently active project, if any
581
+ */
582
+
583
+
584
+ /**
585
+ * Get a list of projects the current session user has access to
586
+ *
587
+ * @function getProjects
588
+ * @returns {Promise<Array<Project>>} Collection of projects the user has access to
589
+ */
590
+
591
+
592
+ /**
593
+ * Set the currently active project within TERA
594
+ *
595
+ * @function setActiveProject
596
+ * @param {Object|String} project The project to set as active - either the full Project object or its ID
597
+ */
598
+
599
+
600
+ /**
601
+ * Ask the user to select a project from those available - if one isn't already active
602
+ * Note that this function will percist in asking the uesr even if they try to cancel
603
+ *
604
+ * @function requireProject
605
+ * @param {Object} [options] Additional options to mutate behaviour
606
+ * @param {Boolean} [options.autoSetActiveProject=true] After selecting a project set that project as active in TERA
607
+ * @param {String} [options.title="Select a project to work with"] The title of the dialog to display
608
+ * @param {String} [options.noSelectTitle='Select project'] Dialog title when warning the user they need to select something
609
+ * @param {String} [options.noSelectBody='A project needs to be selected to continue'] Dialog body when warning the user they need to select something
610
+ *
611
+ * @returns {Promise<Project>} The active project
612
+ */
613
+
614
+
615
+ /**
616
+ * Prompt the user to select a project from those available
617
+ *
618
+ * @function selectProject
619
+ * @param {Object} [options] Additional options to mutate behaviour
620
+ * @param {String} [options.title="Select a project to work with"] The title of the dialog to display
621
+ * @param {Boolean} [options.allowCancel=true] Advertise cancelling the operation, the dialog can still be cancelled by closing it
622
+ * @param {Boolean} [options.setActive=false] Also set the project as active when selected
623
+ *
624
+ * @returns {Promise<Project>} The active project
625
+ */
626
+
627
+
628
+ /**
629
+ * Return the current, full snapshot state of the active project
630
+ *
631
+ * @function getProjectState
632
+ * @param {Object} [options] Additional options to mutate behaviour
633
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
634
+ * @param {Array<String>} Paths to subscribe to e.g. ['/users/'],
635
+ *
636
+ * @returns {Promise<Object>} The current project state snapshot
637
+ */
638
+
639
+
640
+ /**
641
+ * Apply a computed `just-diff` patch to the current project state
642
+ *
643
+ * @function applyProjectStatePatch
644
+ * @param {Object} Patch to apply
645
+ * @returns {Promise} A promise which resolves when the operation has completed
646
+ */
647
+
648
+
649
+ /**
650
+ * Subscribe to project state changes
651
+ * This will dispatch an RPC call to the source object `applyProjectStatePatchLocal()` function with the patch
652
+ * If the above call fails the subscriber is assumed as dead and unsubscribed from the polling list
653
+ *
654
+ * @function subscribeProjectState
655
+ * @returns {Promise<Function>} A promise which resolves when a subscription has been created, call the resulting function to unsubscribe
656
+ */
657
+
658
+
659
+ /**
660
+ * Data structure for a project file
661
+ *
662
+ * @name ProjectFile
663
+ *
664
+ * @property {String} id A UUID string representing the unique ID of the file
665
+ * @property {String} name Relative name path (can contain prefix directories) for the human readable file name
666
+ * @property {Object} parsedName An object representing meta file parts of a file name
667
+ * @property {String} parsedName.basename The filename + extention (i.e. everything without directory name)
668
+ * @property {String} parsedName.filename The file portion of the name (basename without the extension)
669
+ * @property {String} parsedName.ext The extension portion of the name (always lower case)
670
+ * @property {String} parsedName.dirName The directory path portion of the name
671
+ * @property {Date} created A date representing when the file was created
672
+ * @property {Date} modified A date representing when the file was created
673
+ * @property {Date} accessed A date representing when the file was last accessed
674
+ * @property {Number} size Size, in bytes, of the file
675
+ * @property {String} mime The associated mime type for the file
676
+ */
677
+
678
+
679
+ /**
680
+ * Fetch the files associated with a given project
681
+ *
682
+ * @function getProjectFiles
683
+ * @param {Object} options Options which mutate behaviour
684
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
685
+ * @param {Boolean} [options.meta=true] Pull meta information for each file entity
686
+ *
687
+ * @returns {Promise<ProjectFile>} A collection of project files for the given project
688
+ */
689
+
690
+
691
+ /**
692
+ * Fetch the active projects citation library
693
+ *
694
+ * @function getProjectLibrary
695
+ * @param {String} [path] Optional file path to use, if omitted the contents of `options` are used to guess at a suitable file
696
+ * @param {Object} [options] Additional options to mutate behaviour
697
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
698
+ * @param {Boolean} [options.multiple=false] Allow selection of multiple libraries
699
+ * @param {Function} [options.filter] Optional async file filter, called each time as `(File:ProjectFile)`
700
+ * @param {Function} [options.find] Optional async final stage file filter to reduce all candidates down to one subject file
701
+ * @param {String|Array<String>} [options.hint] Hints to identify the library to select in array order of preference. Generally corresponds to the previous stage - e.g. 'deduped', 'review1', 'review2', 'dedisputed'
702
+ *
703
+ * @returns {Promise<Array<ProjectFile>>} Collection of references for the selected library matching the given hint + filter, this could be a zero length array
704
+ */
705
+
706
+
707
+ /**
708
+ * Save back a projects citation library
709
+ *
710
+ * @function setProjectLibrary
711
+ * @param {Array<RefLibRef>} Collection of references for the selected library
712
+ *
713
+ * @param {Object} [options] Additional options to mutate behaviour
714
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
715
+ * @param {String} [options.hint] Hint to store against the library. Generally corresponds to the current operation being performed - e.g. 'deduped'
716
+ *
717
+ * @returns {Promise} A promise which resolves when the save operation has completed
718
+ */
719
+ // }}}
499
720
  }
@@ -1,4 +1,5 @@
1
- import {cloneDeep} from 'lodash-es';
1
+ import {cloneDeep, set as pathSet} from 'lodash-es';
2
+ import {diffApply, jsonPatchPathConverter as jsPatchConverter} from 'just-diff-apply';
2
3
  import {nanoid} from 'nanoid';
3
4
 
4
5
  /**
@@ -15,10 +16,15 @@ export default class TeraFyServer {
15
16
  *
16
17
  * @type {Object}
17
18
  * @property {Boolean} devMode Operate in devMode - i.e. force outer refresh when encountering an existing TeraFy instance
19
+ * @property {Number} subscribeTimeout Acceptable timeout period for subscribers to acklowledge a project change event, failing to respond will result in the subscriber being removed from the available subscriber list
18
20
  * @property {String} restrictOrigin URL to restrict communications to
21
+ * @property {String} projectId The project to use as the default reference when calling various APIs
19
22
  */
20
23
  settings = {
24
+ devMode: false,
21
25
  restrictOrigin: '*',
26
+ subscribeTimeout: 2000,
27
+ projectId: null,
22
28
  };
23
29
 
24
30
  // Contexts - createContext(), messageEvent, senderRpc() {{{
@@ -237,7 +243,7 @@ export default class TeraFyServer {
237
243
  $auth.promise(),
238
244
  $subscriptions.promise(),
239
245
  ])
240
- .then(()=> ({
246
+ .then(()=> $auth.user.id ? {
241
247
  id: $auth.user.id,
242
248
  email: $auth.user.email,
243
249
  name: [
@@ -245,7 +251,7 @@ export default class TeraFyServer {
245
251
  $auth.user.family_name,
246
252
  ].filter(Boolean).join(' '),
247
253
  isSubscribed: $subscriptions.isSubscribed,
248
- }))
254
+ } : null)
249
255
  }
250
256
 
251
257
  // }}}
@@ -403,7 +409,7 @@ export default class TeraFyServer {
403
409
 
404
410
  // }}}
405
411
 
406
- // Project State - getProjectState(), applyProjectStatePatch() {{{
412
+ // Project State - getProjectState(), setProjectState(), saveProjectState(), applyProjectStatePatch(), subscribeProjectState() {{{
407
413
 
408
414
  /**
409
415
  * Return the current, full snapshot state of the active project
@@ -427,14 +433,110 @@ export default class TeraFyServer {
427
433
  }
428
434
 
429
435
 
436
+ /**
437
+ * Set a nested value within the project state
438
+ * Paths can be any valid Lodash.set() value such as:
439
+ *
440
+ * - Dotted notation - e.g. `foo.bar.1.baz`
441
+ * - Array path segments e.g. `['foo', 'bar', 1, 'baz']`
442
+ *
443
+ *
444
+ * @param {String|Array<String>} path The sub-path within the project state to set
445
+ * @param {*} value The value to set
446
+ *
447
+ * @param {Object} [options] Additional options to mutate behaviour
448
+ * @param {Boolean} [options.save=true] Save the changes to the server immediately, disable to queue up multiple writes
449
+ * @param {Boolean} [options.sync=false] Wait for the server to acknowledge the write, you almost never need to do this
450
+ *
451
+ * @returns {Promise} A promise which resolves when the operation has synced with the server
452
+ */
453
+ setProjectState(path, value, options) {
454
+ let settings = {
455
+ save: true,
456
+ sync: false,
457
+ ...options,
458
+ };
459
+
460
+ if (!app.service('$projects').active) throw new Error('No active project');
461
+
462
+ pathSet(app.service('$projects').active, path, value)
463
+
464
+ return (
465
+ this.save && this.sync ? this.saveProjectState()
466
+ : this.save ? void this.saveProjectState()
467
+ : (()=> { throw new Error('setProjectState({sync: true, save: false}) makes no sense') })()
468
+ );
469
+ }
470
+
471
+
472
+ /**
473
+ * Force-Save the currently active project state
474
+ *
475
+ * @returns {Promise} A promise which resolves when the operation has completed
476
+ */
477
+ saveProjectState() {
478
+ if (!app.service('$projects').active) throw new Error('No active project');
479
+
480
+ // TODO: Would be nice if we compared against a sanity hash or something before just clobbering
481
+ this.debug('FIXME: Force saving projects is not yet supported - this should occur in realtime anyway');
482
+ return Promise.resolve();
483
+ }
484
+
485
+
430
486
  /**
431
487
  * Apply a computed `just-diff` patch to the current project state
488
+ *
489
+ * @param {Object} Patch to apply
490
+ * @returns {Promise} A promise which resolves when the operation has completed
432
491
  */
433
492
  applyProjectStatePatch(patch) {
434
- this.debug('Applying sever state patch', {patch});
493
+ if (!app.service('$projects').active) throw new Error('No active project to patch');
494
+ this.debug('Applying', patch.length, 'project state patches', {patch});
495
+ diffApply(app.service('$projects').active, patch, jsPatchConverter);
496
+
497
+ return Promise.resolve();
435
498
  }
436
499
 
437
500
 
501
+ /**
502
+ * Subscribe to project state changes
503
+ * This will dispatch an RPC call to the source object `applyProjectStatePatchLocal()` function with the patch
504
+ * If the above call fails the subscriber is assumed as dead and unsubscribed from the polling list
505
+ *
506
+ * @returns {Promise<Function>} A promise which resolves when a subscription has been created, call the resulting function to unsubscribe
507
+ */
508
+ subscribeProjectState() {
509
+ if (!this.messageEvent) throw new Error('senderRpc() can only be used if given a context from `createContext()`');
510
+
511
+ let subscriber = {
512
+ id: nanoid(),
513
+ origin: this.messageEvent.origin,
514
+ sendPatch: patch => new Promise((resolve, reject) => {
515
+ let senderTimeout = setTimeout(()=> {
516
+ reject(`Timed out sending to project-state subscriber "${subscriber.origin}"`);
517
+ }, this.subscribeTimeout);
518
+
519
+ return this.senderRpc.call(this, 'applyProjectStatePatchLocal', patch)
520
+ .then(()=> {
521
+ clearTimeout(senderTimeout);
522
+ resolve()
523
+ })
524
+ .catch(e => {
525
+ subscriber.unsubscribe();
526
+ reject(`Rejected calling RPC:applyProjectStatePatchLocal() with project-state subscriber "${subscriber.origin}" -`, e)
527
+
528
+ })
529
+ }),
530
+ unsubscribe: ()=> {
531
+ this.debug('Unsubscribing project-state subscriber', subscriber.origin);
532
+ this.projectStateSubscribers = this.projectStateSubscribers.filter(ps => ps.id != subscriber.id);
533
+ },
534
+ };
535
+
536
+ // Append to subscriber list
537
+ this.projectStateSubscribers.push(subscriber)
538
+ }
539
+
438
540
 
439
541
  /**
440
542
  * Subscribers to server project state changes
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@iebh/tera-fy",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
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 plugins/*.js >api.md",
9
+ "build:docs:api": "documentation build lib/terafy.client.js --format html --config documentation.yml --output docs/",
10
+ "build:docs:markdown": "documentation build lib/terafy.client.js --format md --markdown-toc --output api.md",
10
11
  "lint": "eslint ."
11
12
  },
12
13
  "type": "module",
@@ -59,19 +60,19 @@
59
60
  "node": ">=18"
60
61
  },
61
62
  "peerDependencies": {
62
- "just-diff": "^6.0.2",
63
63
  "lodash-es": "^4.17.21",
64
64
  "nanoid": "^5.0.2"
65
65
  },
66
66
  "optionalDependencies": {
67
+ "just-diff": "^6.0.2",
68
+ "just-diff-apply": "^5.5.0",
67
69
  "vue": "^3.3.7"
68
70
  },
69
71
  "devDependencies": {
70
72
  "@momsfriendlydevco/eslint-config": "^1.0.7",
71
73
  "concurrently": "^8.2.2",
72
- "esbuild": "^0.19.5",
73
- "eslint": "^8.47.0",
74
- "jsdoc-to-markdown": "^8.0.0"
74
+ "documentation": "^14.0.2",
75
+ "esbuild": "^0.19.5"
75
76
  },
76
77
  "eslintConfig": {
77
78
  "extends": "@momsfriendlydevco",