@iebh/tera-fy 1.15.9 → 2.0.0
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 +26 -0
- package/api.md +621 -488
- package/dist/plugin.vue2.es2019.js +2294 -1
- package/dist/terafy.es2019.js +2 -2
- package/dist/terafy.js +2 -2
- package/lib/syncro.js +452 -0
- package/lib/terafy.client.js +152 -24
- package/lib/terafy.server.js +61 -21
- package/package.json +4 -1
- package/plugins/firebase.js +122 -0
- package/plugins/vue2.js +82 -94
- package/plugins/vue3.js +38 -142
package/lib/terafy.server.js
CHANGED
|
@@ -469,6 +469,16 @@ export default class TeraFyServer {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Provide an object of credentials for 3rd party services like Firebase/Supabase
|
|
474
|
+
*
|
|
475
|
+
* @returns {Object} An object containing 3rd party service credentials
|
|
476
|
+
*/
|
|
477
|
+
getCredentials() {
|
|
478
|
+
return app.service('$auth').credentials;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
|
|
472
482
|
/**
|
|
473
483
|
* In embed mode only - create a popup window and try to auth via that
|
|
474
484
|
*
|
|
@@ -736,6 +746,54 @@ export default class TeraFyServer {
|
|
|
736
746
|
|
|
737
747
|
// }}}
|
|
738
748
|
|
|
749
|
+
// Project namespaces - getNamespace(), setNamespace(), listNamespaces() {{{
|
|
750
|
+
/**
|
|
751
|
+
* Get a one-off snapshot of a namespace without mounting it
|
|
752
|
+
* This can be used for simpler apps which don't have their own reactive / observer equivelent
|
|
753
|
+
*
|
|
754
|
+
* @param {String} name The alias of the namespace, this should be alphanumeric + hyphens + underscores
|
|
755
|
+
*
|
|
756
|
+
* @returns {Promise<Object>} A promise which resolves to the namespace POJO state
|
|
757
|
+
*/
|
|
758
|
+
getNamespace(name) {
|
|
759
|
+
if (!/^[\w-]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
|
|
760
|
+
|
|
761
|
+
return this.$syncro.getSnapshot(`project_namespaces::${this.$projects.active.id}::${name}`);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Set (or merge by default) a one-off snapshot over an existing namespace
|
|
767
|
+
* This can be used for simpler apps which don't have their own reactive / observer equivelent and just want to quickly set something
|
|
768
|
+
*
|
|
769
|
+
* @param {String} name The name of the namespace
|
|
770
|
+
* @param {Object} state The state to merge
|
|
771
|
+
* @param {Object} [options] Additional options to mutate behaviour
|
|
772
|
+
* @param {'merge'|'set'} [options.method='merge'] How to handle the state. 'merge' (merge a partial state over the existing namespace state), 'set' (completely overwrite the existing namespace)
|
|
773
|
+
*
|
|
774
|
+
* @returns {Promise<Object>} A promise which resolves to the namespace POJO state
|
|
775
|
+
*/
|
|
776
|
+
setNamespace(name, state, options) {
|
|
777
|
+
if (!/^[\w--]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
|
|
778
|
+
if (typeof state != 'object') throw new Error('State must be an object');
|
|
779
|
+
|
|
780
|
+
return this.$syncro.setSnapshot(`project_namespaces::${this.$projects.active.id}}::${name}`, state, {
|
|
781
|
+
method: options.method,
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Return a list of namespaces available to the current project
|
|
788
|
+
*
|
|
789
|
+
* @returns {Promise<Array<Object>>} Collection of available namespaces for the current project
|
|
790
|
+
* @property {String} name The name of the namespace
|
|
791
|
+
*/
|
|
792
|
+
listNamespaces() {
|
|
793
|
+
return app.service('$projects').listNamespaces();
|
|
794
|
+
}
|
|
795
|
+
// }}}
|
|
796
|
+
|
|
739
797
|
// Project State - getProjectState(), setProjectState(), setProjectStateDefaults(), replaceProjectState(), applyProjectStatePatch() {{{
|
|
740
798
|
|
|
741
799
|
/**
|
|
@@ -819,13 +877,11 @@ export default class TeraFyServer {
|
|
|
819
877
|
* @param {String|Array<String>} [path] The sub-path within the project state to set, if unspecifed the entire target is used as a target and a save operation is forced
|
|
820
878
|
* @param {*} value The value to set as the default structure
|
|
821
879
|
* @param {Object} [options] Additional options to mutate behaviour, see setProjectState() for the full list of supported options
|
|
822
|
-
* @param {Boolean} [options.flush=true] Force a re-read from the server after setting, prevents race conditions with large objects. Calls `setProjectStateFlush()` after applying defaults
|
|
823
880
|
*
|
|
824
881
|
* @returns {Promise<*>} A promise which resolves to the eventual input value after defaults have been applied
|
|
825
882
|
*/
|
|
826
883
|
setProjectStateDefaults(path, value, options) {
|
|
827
884
|
let settings = {
|
|
828
|
-
flush: true,
|
|
829
885
|
...options,
|
|
830
886
|
};
|
|
831
887
|
if (!app.service('$projects').active) throw new Error('No active project');
|
|
@@ -841,7 +897,6 @@ export default class TeraFyServer {
|
|
|
841
897
|
...settings,
|
|
842
898
|
},
|
|
843
899
|
)
|
|
844
|
-
.then(()=> settings.flush && this.setProjectStateFlush())
|
|
845
900
|
.then(()=> pathTools.get(target, path));
|
|
846
901
|
} else { // Called as (value) - Populate entire project layout
|
|
847
902
|
pathTools.defaults(target, path);
|
|
@@ -850,26 +905,11 @@ export default class TeraFyServer {
|
|
|
850
905
|
newState: cloneDeep(target),
|
|
851
906
|
});
|
|
852
907
|
return this.saveProjectState()
|
|
853
|
-
.then(()=> settings.flush && this.setProjectStateFlush())
|
|
854
908
|
.then(()=> target);
|
|
855
909
|
}
|
|
856
910
|
}
|
|
857
911
|
|
|
858
912
|
|
|
859
|
-
/**
|
|
860
|
-
* Force copying local changes to the server
|
|
861
|
-
* This is only ever needed when saving large quantities of data that need to be immediately available
|
|
862
|
-
*
|
|
863
|
-
* @returns {Promise} A promise which resolves when the operation has completed
|
|
864
|
-
*/
|
|
865
|
-
setProjectStateFlush() {
|
|
866
|
-
this.debug('INFO', 1, 'Force project state flush!');
|
|
867
|
-
if (!app.service('$projects').active) throw new Error('No active project');
|
|
868
|
-
return app.service('$projects').active.$touchLocal()
|
|
869
|
-
.then(()=> null)
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
|
|
873
913
|
/**
|
|
874
914
|
* Force refetching the remote project state into local
|
|
875
915
|
*
|
|
@@ -1644,9 +1684,9 @@ export default class TeraFyServer {
|
|
|
1644
1684
|
*
|
|
1645
1685
|
* @param {Object} [options] Additional options to mutate behaviour
|
|
1646
1686
|
* @param {String} [options.body] Optional additional body text
|
|
1687
|
+
* @param {Boolean} [options.isHtml=false] If truthy, treat the body as HTML
|
|
1647
1688
|
* @param {String} [options.value] Current or default value to display pre-filled
|
|
1648
1689
|
* @param {String} [options.title='Input required'] The dialog title to display
|
|
1649
|
-
* @param {Boolean} [options.bodyHtml=false] If truthy, treat the body as HTML
|
|
1650
1690
|
* @param {String} [options.placeholder] Optional placeholder text
|
|
1651
1691
|
* @param {Boolean} [options.required=true] Treat nullish or empty inputs as a cancel operation
|
|
1652
1692
|
*
|
|
@@ -1655,7 +1695,7 @@ export default class TeraFyServer {
|
|
|
1655
1695
|
uiPrompt(text, options) {
|
|
1656
1696
|
let settings = {
|
|
1657
1697
|
body: '',
|
|
1658
|
-
|
|
1698
|
+
isHtml: false,
|
|
1659
1699
|
title: 'Input required',
|
|
1660
1700
|
value: '',
|
|
1661
1701
|
placeholder: '',
|
|
@@ -1674,7 +1714,7 @@ export default class TeraFyServer {
|
|
|
1674
1714
|
component: 'UiPrompt',
|
|
1675
1715
|
componentProps: {
|
|
1676
1716
|
body: settings.body,
|
|
1677
|
-
|
|
1717
|
+
isHtml: settings.isHtml,
|
|
1678
1718
|
placeholder: settings.placeholder,
|
|
1679
1719
|
value: settings.value,
|
|
1680
1720
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iebh/tera-fy",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
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 --serve --servedir=.",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"node": ">=18"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
+
"@momsfriendlydevco/marshal": "^2.1.1",
|
|
75
76
|
"detect-port": "^1.6.1",
|
|
76
77
|
"filesize": "^10.1.4",
|
|
77
78
|
"http-proxy": "^1.18.1",
|
|
@@ -91,6 +92,8 @@
|
|
|
91
92
|
"nodemon": "^3.1.7"
|
|
92
93
|
},
|
|
93
94
|
"peerDependencies": {
|
|
95
|
+
"@supabase/supabase-js": "^2.48.1",
|
|
96
|
+
"firebase": "^11.3.1",
|
|
94
97
|
"vue": "^3.0.0"
|
|
95
98
|
},
|
|
96
99
|
"optionalDependencies": {
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {initializeApp as Firebase} from 'firebase/app';
|
|
2
|
+
import {getFirestore as Firestore} from 'firebase/firestore';
|
|
3
|
+
import {createClient as Supabase} from '@supabase/supabase-js'
|
|
4
|
+
import Syncro from '../lib/syncro.js';
|
|
5
|
+
import TeraFyPluginBase from './base.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Plugin which adds Firebase / Firestore support for namespace mounts
|
|
9
|
+
*
|
|
10
|
+
* @class TeraFyPluginFirebase
|
|
11
|
+
*/
|
|
12
|
+
export default class TeraFyPluginFirebase extends TeraFyPluginBase {
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Lookup object of mounted Syncro objects by path
|
|
16
|
+
*
|
|
17
|
+
* @type {Object<Syncro>}
|
|
18
|
+
*/
|
|
19
|
+
syncros = {};
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @interface
|
|
24
|
+
* The Syncro#reactive option to use when creating new Syncro instances
|
|
25
|
+
* This is expected to be overriden by other plugins
|
|
26
|
+
* If falsy the Syncro module will fall back to its internal (POJO only) getReactive() function
|
|
27
|
+
*
|
|
28
|
+
* @name getReactive
|
|
29
|
+
* @type {Function} A reactive function as defined in Syncro
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Setup Firebase + Firestore + Supabase
|
|
35
|
+
* Default credentials (Firebase + Supabase) will be retrieved from `getCredentials()` unless overriden here
|
|
36
|
+
*
|
|
37
|
+
* @param {Object} options Additional options to mutate behaviour (defaults to the main teraFy settings)
|
|
38
|
+
* @param {String} [options.firebaseApiKey] Firebase API key
|
|
39
|
+
* @param {String} [options.firebaseAuthDomain] Firebase authorized domain
|
|
40
|
+
* @param {String} [options.firebaseProjectId] Firebase project ID
|
|
41
|
+
* @param {String} [options.firebaseAppId] Firebase App ID
|
|
42
|
+
* @param {String} [options.supabaseUrl] Supabase URL
|
|
43
|
+
* @param {String} [options.supabaseKey] Supabase client key
|
|
44
|
+
*
|
|
45
|
+
* @returns {Promise} A Promise which will resolve when the init process has completed
|
|
46
|
+
*/
|
|
47
|
+
async init(options) {
|
|
48
|
+
let settings = {
|
|
49
|
+
firebaseApiKey: null,
|
|
50
|
+
firebaseAuthDomain: null,
|
|
51
|
+
firebaseProjectId: null,
|
|
52
|
+
firebaseAppId: null,
|
|
53
|
+
supabaseUrl: null,
|
|
54
|
+
supabaseKey: null,
|
|
55
|
+
...await this.getCredentials(),
|
|
56
|
+
...options,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
let emptyValues = Object.keys(settings).filter(k => k === null);
|
|
60
|
+
if (emptyValues.length > 0)
|
|
61
|
+
throw new Error('Firebase plugin requires mandatory options: ' + emptyValues.join(', '));
|
|
62
|
+
|
|
63
|
+
Syncro.firebase = Firebase({
|
|
64
|
+
apiKey: settings.firebaseApiKey,
|
|
65
|
+
authDomain: settings.firebaseAuthDomain,
|
|
66
|
+
projectId: settings.firebaseProjectId,
|
|
67
|
+
appId: settings.firebaseAppId,
|
|
68
|
+
});
|
|
69
|
+
Syncro.firestore = Firestore(this.firebase);
|
|
70
|
+
|
|
71
|
+
Syncro.supabase = Supabase(settings.supabaseUrl, settings.supabaseKey);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Mount the given namespace against `namespaces[name]`
|
|
77
|
+
*
|
|
78
|
+
* @param {'_PROJECT'|String} name The name/Syncro path of the namespace to mount (or '_PROJECT' for the project mountpoint)
|
|
79
|
+
*
|
|
80
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
81
|
+
*/
|
|
82
|
+
_mountNamespace(name) {
|
|
83
|
+
let syncro; // The eventually bootstrapped Syncro object
|
|
84
|
+
|
|
85
|
+
return Promise.resolve()
|
|
86
|
+
.then(()=> this.requireProject())
|
|
87
|
+
.then(project => {
|
|
88
|
+
let path = name == '_PROJECT'
|
|
89
|
+
? `projects::${project.id}`
|
|
90
|
+
: `project_namespaces::${project.id}::${name}`;
|
|
91
|
+
|
|
92
|
+
syncro = this.syncros[name] = new Syncro(path, {
|
|
93
|
+
debug: (...msg) => this.debug(`SYNCRO://${path}`, ...msg),
|
|
94
|
+
getReactive: this.getReactive, // Try to inherit this instances getReactive prop, otherwise Syncro will fall back to its default
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Perform the mount action
|
|
98
|
+
return syncro.mount();
|
|
99
|
+
})
|
|
100
|
+
.then(()=> { // Assign local state
|
|
101
|
+
this.namespaces[name] = syncro.value;
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Unmount the given namespace from `namespaces[name]`
|
|
108
|
+
*
|
|
109
|
+
* @param {String} name The name/Syncro path of the namespace to unmount
|
|
110
|
+
*
|
|
111
|
+
* @returns {Promise} A promise which resolves when the operation has completed
|
|
112
|
+
*/
|
|
113
|
+
_unmountNamespace(name) {
|
|
114
|
+
let syncro = this.syncros[name]; // Create local alias for Syncro before we detach it
|
|
115
|
+
|
|
116
|
+
// Detach local state
|
|
117
|
+
delete this.namespaces[name];
|
|
118
|
+
delete this.syncros[name];
|
|
119
|
+
|
|
120
|
+
return syncro.destroy(); // Trigger Syncro distruction
|
|
121
|
+
}
|
|
122
|
+
}
|
package/plugins/vue2.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {cloneDeep
|
|
2
|
-
import
|
|
1
|
+
import {cloneDeep} from 'lodash-es';
|
|
2
|
+
import TeraFyPluginFirebase from './firebase.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* Provides the `bindProjectState()` function for Vue based projects
|
|
5
|
+
* Vue@2 observables plugin
|
|
7
6
|
*
|
|
8
7
|
* This function is expected to be included via the `terafy.use(MODULE, OPTIONS)` syntax rather than directly
|
|
9
8
|
*
|
|
10
|
-
* @class
|
|
11
|
-
* @param {Object} options Options when initalizing
|
|
12
|
-
* @param {Vue} options.Vue Vue instance to bind against
|
|
9
|
+
* @class TeraFyPluginVue2
|
|
13
10
|
*
|
|
14
|
-
* @example Implementation within a
|
|
11
|
+
* @example Implementation within a Vue@2 project `src/main.js`:
|
|
15
12
|
* // Include the main Tera-Fy core
|
|
16
13
|
* import TeraFy from '@iebh/tera-fy';
|
|
17
14
|
* import TerafyVue from '@iebh/tera-fy/plugins/vue2';
|
|
@@ -27,7 +24,7 @@ import TeraFyPluginBase from './base.js';
|
|
|
27
24
|
* app.$mount("#app");
|
|
28
25
|
* await terafy.init({app});
|
|
29
26
|
*/
|
|
30
|
-
export default class TeraFyPluginVue2 extends
|
|
27
|
+
export default class TeraFyPluginVue2 extends TeraFyPluginFirebase {
|
|
31
28
|
|
|
32
29
|
/**
|
|
33
30
|
* Local Vue@2 library to use, set during constuctor
|
|
@@ -37,6 +34,82 @@ export default class TeraFyPluginVue2 extends TeraFyPluginBase {
|
|
|
37
34
|
Vue;
|
|
38
35
|
|
|
39
36
|
|
|
37
|
+
/**
|
|
38
|
+
* The bound, reactive state of the active TERA project
|
|
39
|
+
*
|
|
40
|
+
* @type {Object}
|
|
41
|
+
*/
|
|
42
|
+
project = null;
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Simple incrementor to ensure unique IDs for $watch expressions
|
|
47
|
+
*
|
|
48
|
+
* @type {Number{
|
|
49
|
+
*/
|
|
50
|
+
reactiveId = 1001;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Install into Vue@2
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} options Additional options to mutate behaviour, see TeraFyPluginFirebase
|
|
57
|
+
* @param {Object} options.app Root level Vue app to bind against
|
|
58
|
+
* @param {Vue} options.Vue Vue@2 instance to bind against
|
|
59
|
+
* @param {String} [options.globalName='$tera'] Global property to allocate this service as within Vue2
|
|
60
|
+
* @param {*...} [options...] see TeraFyPluginFirebase
|
|
61
|
+
*
|
|
62
|
+
* @returns {Promise} A Promise which will resolve when the init process has completed
|
|
63
|
+
*/
|
|
64
|
+
async init(options) {
|
|
65
|
+
let settings = {
|
|
66
|
+
app: null,
|
|
67
|
+
Vue: null,
|
|
68
|
+
globalName: '$tera',
|
|
69
|
+
...options,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (!settings.Vue) throw new Error('Vue instance to use must be specified in init options as `Vue`');
|
|
73
|
+
this.Vue = settings.Vue;
|
|
74
|
+
|
|
75
|
+
if (!settings.app) throw new Error('Vue Root / App instance to use must be specified in init options as `app`');
|
|
76
|
+
this.app = settings.app;
|
|
77
|
+
|
|
78
|
+
// Make this module available globally
|
|
79
|
+
if (settings.globalName)
|
|
80
|
+
this.Vue.prototype[settings.globalName] = this;
|
|
81
|
+
|
|
82
|
+
await super.init(settings); // Initalize parent class Firebase functionality
|
|
83
|
+
|
|
84
|
+
this.project = await this.mountNamespace('_PROJECT');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/** @override */
|
|
89
|
+
getReactive(value) {
|
|
90
|
+
let doc = this.Vue.observable(value);
|
|
91
|
+
|
|
92
|
+
let watcherPath = `_teraFy_${this.reactiveId++}`;
|
|
93
|
+
this.app[watcherPath] = doc; // Attach onto app so we can use $watch later on
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
doc,
|
|
97
|
+
setState(state) {
|
|
98
|
+
// Shallow copy all sub-keys into existing object (keeping the object pointer)
|
|
99
|
+
Object.entries(state || {})
|
|
100
|
+
.forEach(([k, v]) => doc[k] = v)
|
|
101
|
+
},
|
|
102
|
+
getState() {
|
|
103
|
+
return cloneDeep(doc);
|
|
104
|
+
},
|
|
105
|
+
watch: cb => {
|
|
106
|
+
this.app.$watch(watcherPath, cb, {deep: true});
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
40
113
|
/**
|
|
41
114
|
* Return a Vue Observable object that can be read/written which whose changes will transparently be written back to the TERA server instance
|
|
42
115
|
*
|
|
@@ -175,89 +248,4 @@ export default class TeraFyPluginVue2 extends TeraFyPluginBase {
|
|
|
175
248
|
return target;
|
|
176
249
|
}
|
|
177
250
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* List of available projects for the current session
|
|
182
|
-
* Initalized during constructor
|
|
183
|
-
*
|
|
184
|
-
* @type {VueReactive<Array<Object>>}
|
|
185
|
-
*/
|
|
186
|
-
projects;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* The bound, reactive state of a Vue project
|
|
191
|
-
* When loaded this represents the state of a project as an object
|
|
192
|
-
*
|
|
193
|
-
* @type {Object}
|
|
194
|
-
*/
|
|
195
|
-
state = null;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Install into Vue@2
|
|
200
|
-
*
|
|
201
|
-
* @param {Object} options Additional options to mutate behaviour (defaults to the main teraFy settings)
|
|
202
|
-
* @param {Object} options.app Root level Vue app to bind against
|
|
203
|
-
* @param {Vue} options.Vue Vue@2 instance to bind against
|
|
204
|
-
* @param {String} [options.globalName='$tera'] Global property to allocate this service as within Vue2
|
|
205
|
-
* @param {Boolean} [options.requireProject=true] Automatically call requireProject() prior to any operation
|
|
206
|
-
* @param {Boolean} [options.subscribeState=true] Setup `vm.$tera.state` as a live binding on init
|
|
207
|
-
* @param {Boolean} [options.subscribeList=true] Setup `vm.$tera.projects` as a list of accesible projects on init
|
|
208
|
-
* @param {Objecct} [options.stateOptions] Options passed to `bindProjectState()` when setting up the main state
|
|
209
|
-
*
|
|
210
|
-
* @returns {Promise} A Promise which will resolve when the init process has completed
|
|
211
|
-
*/
|
|
212
|
-
init(options) {
|
|
213
|
-
let settings = {
|
|
214
|
-
app: null,
|
|
215
|
-
Vue: null,
|
|
216
|
-
globalName: '$tera',
|
|
217
|
-
requireProject: true,
|
|
218
|
-
subscribeState: true,
|
|
219
|
-
subscribeProjects: true,
|
|
220
|
-
stateOptions: {
|
|
221
|
-
read: true,
|
|
222
|
-
write: true,
|
|
223
|
-
},
|
|
224
|
-
...options,
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
if (!settings.Vue) throw new Error('Vue instance to use must be specified in init options as `Vue`');
|
|
228
|
-
this.Vue = options.Vue;
|
|
229
|
-
|
|
230
|
-
if (!this.settings.app) throw new Error('Need to specify the root level Vue2 app during init');
|
|
231
|
-
settings.stateOptions.app = this.settings.app;
|
|
232
|
-
|
|
233
|
-
// Create observable binding for projects
|
|
234
|
-
this.projects = this.Vue.observable([])
|
|
235
|
-
|
|
236
|
-
// Make this module available globally
|
|
237
|
-
if (settings.globalName)
|
|
238
|
-
this.Vue.prototype[settings.globalName] = this;
|
|
239
|
-
|
|
240
|
-
// Bind `state` to the active project
|
|
241
|
-
// Initialize state to null
|
|
242
|
-
this.state = null;
|
|
243
|
-
|
|
244
|
-
// this.statePromisable becomes the promise we are waiting on to resolve
|
|
245
|
-
return Promise.resolve()
|
|
246
|
-
.then(()=> settings.requireProject && this.requireProject())
|
|
247
|
-
.then(()=> Promise.all([
|
|
248
|
-
// Bind available project and wait on it
|
|
249
|
-
settings.subscribeState && this.bindProjectState({
|
|
250
|
-
...settings.stateOptions,
|
|
251
|
-
component: this.settings.app.$root,
|
|
252
|
-
})
|
|
253
|
-
.then(state => this.state = state)
|
|
254
|
-
.then(()=> this.debug('INFO', 1, 'Loaded initial project state', this.state)),
|
|
255
|
-
|
|
256
|
-
// Fetch available projects
|
|
257
|
-
settings.subscribeProjects && this.getProjects()
|
|
258
|
-
.then(projects => this.projects = this.Vue.observable(projects))
|
|
259
|
-
.then(()=> this.debug('INFO', 2, 'Loaded project list', this.projects)),
|
|
260
|
-
]))
|
|
261
|
-
.then(()=> this.debug('INFO', 1, 'Ready'))
|
|
262
|
-
}
|
|
263
251
|
}
|