@zoho_apptics/apptics-js-sdk 2.0.0-beta.1 → 2.0.0-beta.2
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 +80 -16
- package/README.md +187 -100
- package/dist/apptics.esm.min.js +2 -2
- package/dist/apptics.esm.min.js.map +1 -1
- package/dist/apptics.min.js +2 -2
- package/dist/apptics.min.js.map +1 -1
- package/dist/chunks/lifecycle-C6m1W7jo.min.js +3 -0
- package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
- package/dist/chunks/send-CgUO82ne.min.js +3 -0
- package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +1 -2
- package/dist/cjs/core/consent.d.ts +13 -3
- package/dist/cjs/core/consent.js +24 -10
- package/dist/cjs/core/constants.d.ts +22 -17
- package/dist/cjs/core/constants.js +22 -19
- package/dist/cjs/core/db.js +1 -1
- package/dist/cjs/core/debug.js +1 -1
- package/dist/cjs/core/env.d.ts +0 -3
- package/dist/cjs/core/env.js +1 -9
- package/dist/cjs/core/framework-id.d.ts +1 -1
- package/dist/cjs/core/framework-id.js +3 -3
- package/dist/cjs/core/guard.js +1 -1
- package/dist/cjs/core/hooks.d.ts +5 -0
- package/dist/cjs/core/hooks.js +2 -1
- package/dist/cjs/core/locks.js +1 -1
- package/dist/cjs/core/logger.js +1 -1
- package/dist/cjs/core/state.d.ts +6 -4
- package/dist/cjs/core/state.js +2 -4
- package/dist/cjs/core/storage.js +1 -4
- package/dist/cjs/core/stylesheet.js +1 -1
- package/dist/cjs/core/transport.js +13 -16
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +1 -1
- package/dist/cjs/device/appversion.d.ts +10 -1
- package/dist/cjs/device/appversion.js +26 -3
- package/dist/cjs/device/index.d.ts +17 -2
- package/dist/cjs/device/index.js +195 -27
- package/dist/cjs/engagement/api-rules.js +1 -1
- package/dist/cjs/engagement/auto-api.js +1 -1
- package/dist/cjs/engagement/auto-screen.js +1 -1
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +7 -3
- package/dist/cjs/engagement/session.d.ts +17 -0
- package/dist/cjs/engagement/session.js +95 -4
- package/dist/cjs/engagement/track.d.ts +4 -4
- package/dist/cjs/engagement/track.js +15 -12
- package/dist/cjs/errors/send.js +2 -2
- package/dist/cjs/errors/track.d.ts +2 -1
- package/dist/cjs/errors/track.js +6 -4
- package/dist/cjs/feedback/controls.d.ts +6 -0
- package/dist/cjs/feedback/controls.js +74 -25
- package/dist/cjs/feedback/index.js +1 -1
- package/dist/cjs/feedback/logs.d.ts +3 -6
- package/dist/cjs/feedback/logs.js +8 -18
- package/dist/cjs/feedback/quartz.js +1 -1
- package/dist/cjs/feedback/send.d.ts +2 -2
- package/dist/cjs/feedback/send.js +4 -27
- package/dist/cjs/feedback/styles.js +2 -2
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +9 -1
- package/dist/cjs/feedback/ui.d.ts +7 -0
- package/dist/cjs/feedback/ui.js +160 -25
- package/dist/cjs/heatmap/send.d.ts +2 -0
- package/dist/cjs/heatmap/send.js +3 -5
- package/dist/cjs/heatmap/track.js +1 -1
- package/dist/cjs/index.d.ts +1 -2
- package/dist/cjs/index.js +54 -17
- package/dist/cjs/lifecycle.d.ts +13 -2
- package/dist/cjs/lifecycle.js +122 -54
- package/dist/cjs/remoteconfig/country.js +1 -1
- package/dist/cjs/remoteconfig/custom.js +1 -1
- package/dist/cjs/remoteconfig/evaluate.js +1 -1
- package/dist/cjs/remoteconfig/fetch.d.ts +17 -10
- package/dist/cjs/remoteconfig/fetch.js +126 -31
- package/dist/cjs/remoteconfig/index.d.ts +13 -4
- package/dist/cjs/remoteconfig/index.js +36 -20
- package/dist/cjs/remoteconfig/store.d.ts +15 -7
- package/dist/cjs/remoteconfig/store.js +68 -35
- package/dist/cjs/remoteconfig/tz-countries.js +1 -1
- package/dist/cjs/{core → remotelog}/callsite.d.ts +5 -4
- package/dist/cjs/{core → remotelog}/callsite.js +6 -5
- package/dist/cjs/remotelog/gate.js +1 -1
- package/dist/cjs/remotelog/logger.js +2 -2
- package/dist/cjs/remotelog/send.js +2 -2
- package/dist/cjs/remotelogger/index.js +1 -1
- package/dist/cjs/scheduler.d.ts +3 -1
- package/dist/cjs/scheduler.js +24 -21
- package/dist/cjs/types.d.ts +61 -16
- package/dist/cjs/user/index.js +6 -2
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +1 -2
- package/dist/esm/core/consent.d.ts +13 -3
- package/dist/esm/core/consent.js +24 -11
- package/dist/esm/core/constants.d.ts +22 -17
- package/dist/esm/core/constants.js +22 -19
- package/dist/esm/core/db.js +1 -1
- package/dist/esm/core/debug.js +1 -1
- package/dist/esm/core/env.d.ts +0 -3
- package/dist/esm/core/env.js +2 -9
- package/dist/esm/core/framework-id.d.ts +1 -1
- package/dist/esm/core/framework-id.js +3 -3
- package/dist/esm/core/guard.js +1 -1
- package/dist/esm/core/hooks.d.ts +5 -0
- package/dist/esm/core/hooks.js +2 -1
- package/dist/esm/core/locks.js +1 -1
- package/dist/esm/core/logger.js +1 -1
- package/dist/esm/core/state.d.ts +6 -4
- package/dist/esm/core/state.js +2 -4
- package/dist/esm/core/storage.js +1 -4
- package/dist/esm/core/stylesheet.js +1 -1
- package/dist/esm/core/transport.js +13 -16
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +1 -1
- package/dist/esm/device/appversion.d.ts +10 -1
- package/dist/esm/device/appversion.js +26 -4
- package/dist/esm/device/index.d.ts +17 -2
- package/dist/esm/device/index.js +195 -30
- package/dist/esm/engagement/api-rules.js +1 -1
- package/dist/esm/engagement/auto-api.js +1 -1
- package/dist/esm/engagement/auto-screen.js +1 -1
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +7 -3
- package/dist/esm/engagement/session.d.ts +17 -0
- package/dist/esm/engagement/session.js +93 -5
- package/dist/esm/engagement/track.d.ts +4 -4
- package/dist/esm/engagement/track.js +16 -13
- package/dist/esm/errors/send.js +2 -2
- package/dist/esm/errors/track.d.ts +2 -1
- package/dist/esm/errors/track.js +6 -4
- package/dist/esm/feedback/controls.d.ts +6 -0
- package/dist/esm/feedback/controls.js +74 -26
- package/dist/esm/feedback/index.js +1 -1
- package/dist/esm/feedback/logs.d.ts +3 -6
- package/dist/esm/feedback/logs.js +8 -18
- package/dist/esm/feedback/quartz.js +1 -1
- package/dist/esm/feedback/send.d.ts +2 -2
- package/dist/esm/feedback/send.js +6 -29
- package/dist/esm/feedback/styles.js +2 -2
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +9 -1
- package/dist/esm/feedback/ui.d.ts +7 -0
- package/dist/esm/feedback/ui.js +161 -26
- package/dist/esm/heatmap/send.d.ts +2 -0
- package/dist/esm/heatmap/send.js +3 -5
- package/dist/esm/heatmap/track.js +1 -1
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +57 -20
- package/dist/esm/lifecycle.d.ts +13 -2
- package/dist/esm/lifecycle.js +126 -60
- package/dist/esm/remoteconfig/country.js +1 -1
- package/dist/esm/remoteconfig/custom.js +1 -1
- package/dist/esm/remoteconfig/evaluate.js +1 -1
- package/dist/esm/remoteconfig/fetch.d.ts +17 -10
- package/dist/esm/remoteconfig/fetch.js +127 -32
- package/dist/esm/remoteconfig/index.d.ts +13 -4
- package/dist/esm/remoteconfig/index.js +36 -20
- package/dist/esm/remoteconfig/store.d.ts +15 -7
- package/dist/esm/remoteconfig/store.js +68 -36
- package/dist/esm/remoteconfig/tz-countries.js +1 -1
- package/dist/esm/{core → remotelog}/callsite.d.ts +5 -4
- package/dist/esm/{core → remotelog}/callsite.js +6 -5
- package/dist/esm/remotelog/gate.js +1 -1
- package/dist/esm/remotelog/logger.js +2 -2
- package/dist/esm/remotelog/send.js +2 -2
- package/dist/esm/remotelogger/index.js +1 -1
- package/dist/esm/scheduler.d.ts +3 -1
- package/dist/esm/scheduler.js +25 -23
- package/dist/esm/types.d.ts +61 -16
- package/dist/esm/user/index.js +7 -3
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/dist/remoteconfig.esm.min.js +3 -0
- package/dist/remoteconfig.esm.min.js.map +1 -0
- package/dist/remotelogger.esm.min.js +3 -0
- package/dist/remotelogger.esm.min.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunks/lifecycle-VYr43KcB.min.js +0 -3
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +0 -1
- package/dist/cjs/auto-init.d.ts +0 -8
- package/dist/cjs/auto-init.js +0 -32
- package/dist/esm/auto-init.d.ts +0 -8
- package/dist/esm/auto-init.js +0 -29
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Asks for the rule set
|
|
2
|
+
* Asks for the rule set: the whole of it with flagtime 0 on the first fetch of a page load, then
|
|
3
|
+
* only what changed since the last answered request, which is merged into what is stored.
|
|
3
4
|
*
|
|
4
|
-
* Resolves with the
|
|
5
|
-
*
|
|
6
|
-
* normal case and not a failure.
|
|
5
|
+
* Resolves with the answer, whether or not it carried a rule set — a later answer with no
|
|
6
|
+
* `remoteconfig` block means nothing has changed, which is the normal case and not a failure.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* With `whole`, a cold fetch: flagtime 0 whatever this page has fetched already, and the answer
|
|
9
|
+
* replaces the stored set as the first fetch of a page load does.
|
|
10
|
+
*
|
|
11
|
+
* A second caller joins the first rather than making another request. A cold fetch joins one
|
|
12
|
+
* only when it asked with flagtime 0 too, and otherwise asks once it has answered.
|
|
9
13
|
*/
|
|
10
|
-
export declare function fetchRemoteConfig(): Promise<Record<string, unknown>>;
|
|
14
|
+
export declare function fetchRemoteConfig(whole?: boolean): Promise<Record<string, unknown>>;
|
|
11
15
|
/**
|
|
12
16
|
* reset(): forgets the rule set, its values and what the server last reported, and disowns a
|
|
13
17
|
* request still in flight, whose answer would otherwise put the old rule set back.
|
|
14
18
|
*/
|
|
15
19
|
export declare function resetRemoteConfig(): void;
|
|
16
20
|
/**
|
|
17
|
-
* Makes sure the values are this page's own: from a fetch that
|
|
18
|
-
*
|
|
19
|
-
*
|
|
21
|
+
* Makes sure the values are this page's own: from a fetch on this page that succeeded, kept up to
|
|
22
|
+
* date by the polling after it. With `coldFetch`, a fetch with flagtime 0 now, whatever this page
|
|
23
|
+
* already has, whose answer replaces what is stored: at most COLD_LIMIT a minute, and one past
|
|
24
|
+
* that answers from what this page has. Resolves true when they are, false when the
|
|
25
|
+
* fetch failed or could not be made — the caller then decides whether stored values will do.
|
|
26
|
+
* Never rejects.
|
|
20
27
|
*
|
|
21
28
|
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
22
29
|
* when ready() resolves: getupdates is refused without the device ids.
|
|
23
30
|
*/
|
|
24
|
-
export declare function obtain(): Promise<boolean>;
|
|
31
|
+
export declare function obtain(coldFetch?: boolean): Promise<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { SDK_MODULE } from '../core/constants.js';
|
|
3
3
|
import { hooks } from '../core/hooks.js';
|
|
4
4
|
import { _ZAP } from '../core/state.js';
|
|
@@ -8,7 +8,7 @@ import { rejectIfOffline, rejectIfNoDeviceIds, postToApptics, apiHeaders } from
|
|
|
8
8
|
import { deviceReady } from '../device/index.js';
|
|
9
9
|
import { ready } from '../lifecycle.js';
|
|
10
10
|
import { clearCustomConditionValues } from './custom.js';
|
|
11
|
-
import { store, storeUnchanged, flagtime, reresolve, clearRemoteConfig } from './store.js';
|
|
11
|
+
import { store, merge, storeUnchanged, flagtime, reresolve, clearRemoteConfig } from './store.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The getupdates call.
|
|
@@ -28,15 +28,28 @@ const LABEL = 'AppticsRemoteConfig'; // no i18n
|
|
|
28
28
|
/** getupdatesobj caps moduleids at nine entries; this SDK asks about one. */
|
|
29
29
|
const MODULES = [Number(SDK_MODULE.REMOTE_CONFIG)];
|
|
30
30
|
var inFlight = null;
|
|
31
|
+
/** Whether the request in flight asked with flagtime 0, which a cold fetch can join. */
|
|
32
|
+
var inFlightWhole = false;
|
|
31
33
|
/** Bumped by reset(); an answer to a request made before it is not stored. */
|
|
32
34
|
var generation = 0;
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
+
* When getupdates is asked again, after the first of a page load: every POLL_MS. Coming back to
|
|
37
|
+
* the tab asks nothing; the poll's timer carries on. fetchValue() and fetchValues() never ask
|
|
38
|
+
* themselves once a fetch has succeeded: a value read on every render must not be a request on
|
|
39
|
+
* every render.
|
|
36
40
|
*/
|
|
37
|
-
const
|
|
38
|
-
/**
|
|
41
|
+
const POLL_MS = 3 * 60 * 1000;
|
|
42
|
+
/**
|
|
43
|
+
* coldFetch goes to the network every time, so it is limited: three in any minute, as on Android.
|
|
44
|
+
* One past that is answered from the values this page already has.
|
|
45
|
+
*/
|
|
46
|
+
const COLD_LIMIT = 3;
|
|
47
|
+
const COLD_WINDOW_MS = 60 * 1000;
|
|
48
|
+
/** When a fetch on this page last succeeded; 0 until one has, and the next one sends flagtime 0. */
|
|
39
49
|
var fetchedAt = 0;
|
|
50
|
+
/** When the coldFetch calls of the last minute went out. */
|
|
51
|
+
var coldCalls = [];
|
|
52
|
+
var pollTimer = null;
|
|
40
53
|
/**
|
|
41
54
|
* Whether remote config is still worth asking about.
|
|
42
55
|
*
|
|
@@ -56,11 +69,11 @@ function wanted() {
|
|
|
56
69
|
* id for "macOS 15", not the small int this template types. languagecode is sent when there is
|
|
57
70
|
* one, since safe_string cannot match an empty string.
|
|
58
71
|
*/
|
|
59
|
-
function body() {
|
|
72
|
+
function body(since) {
|
|
60
73
|
var payload = {
|
|
61
74
|
appversionid: String(_ZAP._device_info.appversionid || ''), // no i18n
|
|
62
75
|
moduleids: MODULES,
|
|
63
|
-
flagtime:
|
|
76
|
+
flagtime: since,
|
|
64
77
|
};
|
|
65
78
|
var language = String(navigator.language || '').slice(0, 10); // no i18n
|
|
66
79
|
if (language !== '') {
|
|
@@ -73,6 +86,9 @@ function body() {
|
|
|
73
86
|
*
|
|
74
87
|
* Only to stop asking: getsdkconfig decides whether the feature is on. A module reported off
|
|
75
88
|
* here is dropped from the next request, which is what the API asks for.
|
|
89
|
+
*
|
|
90
|
+
* A report that differs from the last one (on until one says otherwise) means the server's
|
|
91
|
+
* settings changed, so getsdkconfig, which decides, is asked now rather than at its next check.
|
|
76
92
|
*/
|
|
77
93
|
function applyReportedModules(data) {
|
|
78
94
|
var status = data.tracking_status;
|
|
@@ -81,20 +97,35 @@ function applyReportedModules(data) {
|
|
|
81
97
|
}
|
|
82
98
|
var entry = status[SDK_MODULE.REMOTE_CONFIG];
|
|
83
99
|
if (entry && typeof entry === 'object') {
|
|
84
|
-
|
|
100
|
+
var reported = entry.enabled !== false;
|
|
101
|
+
var changed = reported !== _ZAP._remoteConfigReported;
|
|
102
|
+
_ZAP._remoteConfigReported = reported;
|
|
103
|
+
if (changed) {
|
|
104
|
+
Console.log(LABEL + ': getupdates reported the module ' + (reported ? 'on' : 'off') + ', asking getsdkconfig'); // no i18n
|
|
105
|
+
hooks.askServerSettings();
|
|
106
|
+
}
|
|
85
107
|
}
|
|
86
108
|
}
|
|
87
109
|
/**
|
|
88
|
-
* Asks for the rule set
|
|
110
|
+
* Asks for the rule set: the whole of it with flagtime 0 on the first fetch of a page load, then
|
|
111
|
+
* only what changed since the last answered request, which is merged into what is stored.
|
|
112
|
+
*
|
|
113
|
+
* Resolves with the answer, whether or not it carried a rule set — a later answer with no
|
|
114
|
+
* `remoteconfig` block means nothing has changed, which is the normal case and not a failure.
|
|
89
115
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* normal case and not a failure.
|
|
116
|
+
* With `whole`, a cold fetch: flagtime 0 whatever this page has fetched already, and the answer
|
|
117
|
+
* replaces the stored set as the first fetch of a page load does.
|
|
93
118
|
*
|
|
94
|
-
* A second caller joins the first rather than making another request.
|
|
119
|
+
* A second caller joins the first rather than making another request. A cold fetch joins one
|
|
120
|
+
* only when it asked with flagtime 0 too, and otherwise asks once it has answered.
|
|
95
121
|
*/
|
|
96
|
-
function fetchRemoteConfig() {
|
|
122
|
+
function fetchRemoteConfig(whole) {
|
|
97
123
|
if (inFlight !== null) {
|
|
124
|
+
if (whole === true && !inFlightWhole) {
|
|
125
|
+
return inFlight.catch(function () { return {}; }).then(function () {
|
|
126
|
+
return fetchRemoteConfig(true);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
98
129
|
return inFlight;
|
|
99
130
|
}
|
|
100
131
|
if (!wanted()) {
|
|
@@ -106,7 +137,11 @@ function fetchRemoteConfig() {
|
|
|
106
137
|
return blocked;
|
|
107
138
|
}
|
|
108
139
|
var asked = generation;
|
|
109
|
-
|
|
140
|
+
// the first fetch of the page asks for everything; the stored set is a previous visit's
|
|
141
|
+
var askWhole = whole === true || fetchedAt === 0;
|
|
142
|
+
var since = askWhole ? 0 : flagtime();
|
|
143
|
+
var askedAt = nowMs();
|
|
144
|
+
var request = postToApptics('getupdates', body(since), apiHeaders(), LABEL).then(function (result) {
|
|
110
145
|
var data = (result.data || {});
|
|
111
146
|
if (asked !== generation) {
|
|
112
147
|
// asked before a reset(): the answer is about the device and app that were just forgotten
|
|
@@ -114,15 +149,18 @@ function fetchRemoteConfig() {
|
|
|
114
149
|
}
|
|
115
150
|
applyReportedModules(data);
|
|
116
151
|
var rules = data.remoteconfig;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
store(rules,
|
|
152
|
+
fetchedAt = nowMs();
|
|
153
|
+
if (askWhole) {
|
|
154
|
+
// with flagtime 0 the answer is the whole set, and no block means there is nothing in it
|
|
155
|
+
store(rules && typeof rules === 'object' ? rules : {}, askedAt);
|
|
156
|
+
}
|
|
157
|
+
else if (rules && typeof rules === 'object') {
|
|
158
|
+
merge(rules, askedAt);
|
|
121
159
|
}
|
|
122
160
|
else {
|
|
123
161
|
// nothing has changed since our flagtime, which is the answer most of the time
|
|
124
|
-
storeUnchanged(
|
|
125
|
-
Console.log(LABEL + ': no changes since ' +
|
|
162
|
+
storeUnchanged(askedAt);
|
|
163
|
+
Console.log(LABEL + ': no changes since ' + since); // no i18n
|
|
126
164
|
}
|
|
127
165
|
return data;
|
|
128
166
|
}).then(function (data) {
|
|
@@ -132,13 +170,19 @@ function fetchRemoteConfig() {
|
|
|
132
170
|
settle();
|
|
133
171
|
return Promise.reject(err);
|
|
134
172
|
});
|
|
135
|
-
// a request reset() has disowned must not clear the in-flight slot of one made after it
|
|
173
|
+
// a request reset() has disowned must not clear the in-flight slot of one made after it, nor
|
|
174
|
+
// start the polling again
|
|
136
175
|
function settle() {
|
|
137
176
|
if (inFlight === request) {
|
|
138
177
|
inFlight = null;
|
|
178
|
+
inFlightWhole = false;
|
|
179
|
+
}
|
|
180
|
+
if (asked === generation) {
|
|
181
|
+
schedulePoll();
|
|
139
182
|
}
|
|
140
183
|
}
|
|
141
184
|
inFlight = request;
|
|
185
|
+
inFlightWhole = askWhole;
|
|
142
186
|
return inFlight;
|
|
143
187
|
}
|
|
144
188
|
/**
|
|
@@ -148,30 +192,50 @@ function fetchRemoteConfig() {
|
|
|
148
192
|
function resetRemoteConfig() {
|
|
149
193
|
generation++;
|
|
150
194
|
inFlight = null;
|
|
195
|
+
inFlightWhole = false;
|
|
151
196
|
fetchedAt = 0;
|
|
197
|
+
coldCalls = [];
|
|
198
|
+
if (pollTimer !== null) {
|
|
199
|
+
clearTimeout(pollTimer);
|
|
200
|
+
pollTimer = null;
|
|
201
|
+
}
|
|
152
202
|
_ZAP._remoteConfigReported = true;
|
|
153
203
|
clearCustomConditionValues();
|
|
154
204
|
clearRemoteConfig();
|
|
155
205
|
}
|
|
156
206
|
/**
|
|
157
|
-
* Makes sure the values are this page's own: from a fetch that
|
|
158
|
-
*
|
|
159
|
-
*
|
|
207
|
+
* Makes sure the values are this page's own: from a fetch on this page that succeeded, kept up to
|
|
208
|
+
* date by the polling after it. With `coldFetch`, a fetch with flagtime 0 now, whatever this page
|
|
209
|
+
* already has, whose answer replaces what is stored: at most COLD_LIMIT a minute, and one past
|
|
210
|
+
* that answers from what this page has. Resolves true when they are, false when the
|
|
211
|
+
* fetch failed or could not be made — the caller then decides whether stored values will do.
|
|
212
|
+
* Never rejects.
|
|
160
213
|
*
|
|
161
214
|
* Waits for start-up and for the device to be registered, which a first visit is still doing
|
|
162
215
|
* when ready() resolves: getupdates is refused without the device ids.
|
|
163
216
|
*/
|
|
164
|
-
function obtain() {
|
|
217
|
+
function obtain(coldFetch) {
|
|
165
218
|
return ready().then(function (running) {
|
|
166
219
|
if (!running) {
|
|
167
220
|
return false;
|
|
168
221
|
}
|
|
169
|
-
var
|
|
170
|
-
if (
|
|
222
|
+
var cold = coldFetch === true;
|
|
223
|
+
if (cold) {
|
|
224
|
+
var now = nowMs();
|
|
225
|
+
coldCalls = coldCalls.filter(function (at) { return now - at < COLD_WINDOW_MS; });
|
|
226
|
+
if (coldCalls.length >= COLD_LIMIT) {
|
|
227
|
+
Console.warn(LABEL + ': coldFetch is limited to ' + COLD_LIMIT + ' a minute; this one is answered from the values this page has'); // no i18n
|
|
228
|
+
cold = false;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
coldCalls.push(now);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (fetchedAt > 0 && !cold) {
|
|
171
235
|
return true;
|
|
172
236
|
}
|
|
173
237
|
return deviceReady().then(function () {
|
|
174
|
-
return fetchRemoteConfig();
|
|
238
|
+
return fetchRemoteConfig(cold);
|
|
175
239
|
}).then(function () {
|
|
176
240
|
return true;
|
|
177
241
|
}, function (err) {
|
|
@@ -180,9 +244,40 @@ function obtain() {
|
|
|
180
244
|
});
|
|
181
245
|
});
|
|
182
246
|
}
|
|
183
|
-
/**
|
|
247
|
+
/** A fetch for the polling: failures are logged, and the next tick tries again. */
|
|
248
|
+
function refresh(reason) {
|
|
249
|
+
if (!_ZAP._ready) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
Console.log(LABEL + ': asking for changes (' + reason + ')'); // no i18n
|
|
253
|
+
deviceReady().then(function () {
|
|
254
|
+
return fetchRemoteConfig();
|
|
255
|
+
}).catch(function (err) {
|
|
256
|
+
Console.log(LABEL + ': fetch failed: ' + String(err)); // no i18n
|
|
257
|
+
}).then(keepPolling);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* A fetch that never went out (offline, the device not registered yet, the server switched it
|
|
261
|
+
* off) schedules nothing itself: the polling carries on regardless, so it picks up again.
|
|
262
|
+
*/
|
|
263
|
+
function keepPolling() {
|
|
264
|
+
if (_ZAP._ready && pollTimer === null && inFlight === null) {
|
|
265
|
+
schedulePoll();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/** The next poll, POLL_MS after the last fetch settled, answered or not. */
|
|
269
|
+
function schedulePoll() {
|
|
270
|
+
if (pollTimer !== null) {
|
|
271
|
+
clearTimeout(pollTimer);
|
|
272
|
+
}
|
|
273
|
+
pollTimer = setTimeout(function () {
|
|
274
|
+
pollTimer = null;
|
|
275
|
+
refresh('every ' + POLL_MS + ' ms'); // no i18n
|
|
276
|
+
}, POLL_MS);
|
|
277
|
+
}
|
|
278
|
+
/** Start-up's own fetch, so values are this session's without the app asking, then the polling. */
|
|
184
279
|
function fetchOnStart() {
|
|
185
|
-
obtain();
|
|
280
|
+
obtain().then(keepPolling);
|
|
186
281
|
}
|
|
187
282
|
hooks.resetRemoteConfig = resetRemoteConfig;
|
|
188
283
|
hooks.remoteConfigStarted = fetchOnStart;
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
|
|
2
2
|
export declare const AppticsRemoteConfig: {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/** One parameter's value, null when there is none. coldFetch (false by default) asks the server for the whole set now. */
|
|
4
|
+
fetchValue: {
|
|
5
|
+
(name: string, callback?: (value: string | null) => void, coldFetch?: boolean): Promise<string | null>;
|
|
6
|
+
(name: string, coldFetch: boolean): Promise<string | null>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The parameters named, or every parameter when none are: name -> value, null when there is
|
|
10
|
+
* none. coldFetch (false by default) asks the server for the whole set now.
|
|
11
|
+
*/
|
|
5
12
|
fetchValues: {
|
|
6
|
-
(names?: string[], callback?: (values: Record<string, string | null>) => void): Promise<Record<string, string | null>>;
|
|
7
|
-
(
|
|
13
|
+
(names?: string[], callback?: (values: Record<string, string | null>) => void, coldFetch?: boolean): Promise<Record<string, string | null>>;
|
|
14
|
+
(names: string[], coldFetch: boolean): Promise<Record<string, string | null>>;
|
|
15
|
+
(callback: (values: Record<string, string | null>) => void, coldFetch?: boolean): Promise<Record<string, string | null>>;
|
|
16
|
+
(coldFetch: boolean): Promise<Record<string, string | null>>;
|
|
8
17
|
};
|
|
9
18
|
/**
|
|
10
19
|
* What this device is for a custom condition, by the condition's name, as comma separated
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { forward, guard } from '../core/guard.js';
|
|
3
3
|
import { Console } from '../core/logger.js';
|
|
4
4
|
import { obtain } from './fetch.js';
|
|
@@ -13,9 +13,11 @@ import { all } from './store.js';
|
|
|
13
13
|
* parameter the console has. Each parameter's value is the console's: the value of the first
|
|
14
14
|
* condition, in the console's order, that this device matches, else the parameter's default value.
|
|
15
15
|
* setCustomConditionValue(conditionname, 'a,b') answers the custom conditions.
|
|
16
|
-
* The rule set is stored and resolved on the device, so a
|
|
17
|
-
*
|
|
18
|
-
* on start-up,
|
|
16
|
+
* The rule set is stored and resolved on the device, so a read answers without waiting on the
|
|
17
|
+
* network, and a failed fetch answers with the values fetched before. The SDK fetches the whole set
|
|
18
|
+
* on start-up, then what changed every 3 minutes; coldFetch (true as
|
|
19
|
+
* the last argument) asks for the whole set now. It re-applies the rules on its own when
|
|
20
|
+
* AppticsUser.setUser changes the user.
|
|
19
21
|
*
|
|
20
22
|
* A name the console does not have is null, never undefined: a config system whose failure mode
|
|
21
23
|
* is `undefined` moves the problem into the app's rendering code.
|
|
@@ -39,27 +41,37 @@ function answer(value, callback) {
|
|
|
39
41
|
/**
|
|
40
42
|
* One parameter, fetched for this page, as Android's fetchValue: resolves with its value, or null
|
|
41
43
|
* when the console has no such parameter. A failed fetch answers with the value fetched before.
|
|
44
|
+
*
|
|
45
|
+
* `coldFetch` (false by default) asks the server now with flagtime 0, for the whole set, and
|
|
46
|
+
* answers from that instead of what this page already has. It may come in place of the callback.
|
|
42
47
|
*/
|
|
43
|
-
function fetchValue(name, callback) {
|
|
44
|
-
|
|
48
|
+
function fetchValue(name, callback, coldFetch) {
|
|
49
|
+
if (typeof callback === 'boolean') { // no i18n
|
|
50
|
+
// fetchValue(name, coldFetch)
|
|
51
|
+
coldFetch = callback;
|
|
52
|
+
callback = undefined;
|
|
53
|
+
}
|
|
54
|
+
var done = typeof callback === 'function' ? callback : undefined; // no i18n
|
|
55
|
+
return obtain(coldFetch === true).then(function () {
|
|
45
56
|
var value = raw(name);
|
|
46
|
-
return answer(value === undefined ? null : value,
|
|
57
|
+
return answer(value === undefined ? null : value, done);
|
|
47
58
|
});
|
|
48
59
|
}
|
|
49
60
|
/**
|
|
50
61
|
* Several parameters at once: `names` lists the ones wanted, and the result has exactly those
|
|
51
62
|
* names, null for one the console does not have. With no names (or an empty list), every
|
|
52
|
-
* parameter the console has. A callback may come first when there are no names
|
|
63
|
+
* parameter the console has. A callback may come first when there are no names, and `coldFetch`
|
|
64
|
+
* (false by default, as for fetchValue) last, in place of either.
|
|
53
65
|
*/
|
|
54
|
-
function fetchValues(names, callback) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
var wanted = wantedNames(names);
|
|
61
|
-
var done =
|
|
62
|
-
return obtain().then(function () {
|
|
66
|
+
function fetchValues(names, callback, coldFetch) {
|
|
67
|
+
var args = [names, callback, coldFetch];
|
|
68
|
+
// the first boolean is coldFetch, wherever it comes: fetchValues(true), fetchValues(names, true),
|
|
69
|
+
// fetchValues(callback, true)
|
|
70
|
+
var flag = args.filter(function (one) { return typeof one === 'boolean'; })[0]; // no i18n
|
|
71
|
+
var fn = args.filter(function (one) { return typeof one === 'function'; })[0]; // no i18n
|
|
72
|
+
var wanted = wantedNames(typeof names === 'boolean' || typeof names === 'function' ? undefined : names); // no i18n
|
|
73
|
+
var done = fn;
|
|
74
|
+
return obtain(flag === true).then(function () {
|
|
63
75
|
var values = all();
|
|
64
76
|
var out = {};
|
|
65
77
|
(wanted === null ? Object.keys(values) : wanted).forEach(function (name) {
|
|
@@ -92,11 +104,15 @@ const fetchValuesGuarded = guard('AppticsRemoteConfig.fetchValues', fetchValues)
|
|
|
92
104
|
const setCustomConditionValueGuarded = guard('AppticsRemoteConfig.setCustomConditionValue', setCustomConditionValue); // no i18n
|
|
93
105
|
/** AppticsRemoteConfig: fetchValue, fetchValues and setCustomConditionValue, each with the parameter names the console shows. */
|
|
94
106
|
const AppticsRemoteConfig = {
|
|
95
|
-
|
|
107
|
+
/** One parameter's value, null when there is none. coldFetch (false by default) asks the server for the whole set now. */
|
|
108
|
+
fetchValue: function fetchValue(name, callback, coldFetch) {
|
|
96
109
|
return forward(fetchValueGuarded, arguments);
|
|
97
110
|
},
|
|
98
|
-
/**
|
|
99
|
-
|
|
111
|
+
/**
|
|
112
|
+
* The parameters named, or every parameter when none are: name -> value, null when there is
|
|
113
|
+
* none. coldFetch (false by default) asks the server for the whole set now.
|
|
114
|
+
*/
|
|
115
|
+
fetchValues: function fetchValues(names, callback, coldFetch) {
|
|
100
116
|
return forward(fetchValuesGuarded, arguments);
|
|
101
117
|
}, // no i18n
|
|
102
118
|
/**
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import type { Dict } from '../types.js';
|
|
2
|
-
/**
|
|
2
|
+
/** When the last answered getupdates went out; 0 when nothing is stored. */
|
|
3
3
|
export declare function flagtime(): number;
|
|
4
4
|
/** Re-resolves the stored rules against this device, without fetching. */
|
|
5
5
|
export declare function reresolve(): void;
|
|
6
|
-
/** Stores a rule set the server just sent, and resolves it. */
|
|
7
|
-
export declare function store(rules: Dict, at: number): void;
|
|
8
6
|
/**
|
|
9
|
-
*
|
|
7
|
+
* Stores a whole rule set, the answer to flagtime 0, in place of what was stored, and resolves
|
|
8
|
+
* it. `askedAt` is when the request went out: the next one asks for what changed since then.
|
|
9
|
+
*/
|
|
10
|
+
export declare function store(rules: Dict, askedAt: number): void;
|
|
11
|
+
/**
|
|
12
|
+
* Merges what a later getupdates sent, everything added or changed since its flagtime, into the
|
|
13
|
+
* stored rule set, and resolves the result. With nothing stored, it is the whole set.
|
|
14
|
+
*/
|
|
15
|
+
export declare function merge(changes: Dict, askedAt: number): void;
|
|
16
|
+
/**
|
|
17
|
+
* Records a later fetch that brought no changes.
|
|
10
18
|
*
|
|
11
|
-
* That is the normal answer
|
|
12
|
-
*
|
|
19
|
+
* That is the normal answer, and it is not a failure: what is stored stays, and the next request
|
|
20
|
+
* asks for what changed since this one went out.
|
|
13
21
|
*/
|
|
14
|
-
export declare function storeUnchanged(
|
|
22
|
+
export declare function storeUnchanged(askedAt: number): void;
|
|
15
23
|
/** Forgets the rule set and its values, so the next fetch asks with flagtime 0. */
|
|
16
24
|
export declare function clearRemoteConfig(): void;
|
|
17
25
|
/** Every parameter resolved for this device. Loads from storage on the first read. */
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { Console } from '../core/logger.js';
|
|
3
3
|
import { user_defaults } from '../core/storage.js';
|
|
4
|
+
import { nowMs } from '../core/utils.js';
|
|
4
5
|
import { resolveAll, resetEvaluationWarnings } from './evaluate.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* What remote config keeps between visits.
|
|
8
9
|
*
|
|
9
|
-
* The rule set is stored, not just the resolved values
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* flagtime 0 on every visit. And the resolved values depend on the device: an app version or a
|
|
13
|
-
* customer group that changed since the last visit has to re-resolve against the same rules.
|
|
10
|
+
* The rule set is stored, not just the resolved values: the values depend on the device, so an
|
|
11
|
+
* app version or a customer group that changed has to re-resolve against the same rules, and a
|
|
12
|
+
* visit that cannot reach the server still has the last visit's values to read.
|
|
14
13
|
*
|
|
15
|
-
* `flagtime` is the whole protocol here
|
|
16
|
-
*
|
|
14
|
+
* `flagtime` is the whole protocol here. The first getupdates of a page load sends 0 and gets the
|
|
15
|
+
* whole rule set, which replaces what is stored. Each later one sends the time the last answered
|
|
16
|
+
* request went out, and gets only what was added or changed since: those parameters and
|
|
17
|
+
* conditions are merged into the stored set, and one that did not change is kept as it was.
|
|
17
18
|
*/
|
|
18
19
|
const STORE_KEY = 'remoteconfig'; // no i18n
|
|
19
20
|
var values = {};
|
|
@@ -22,55 +23,86 @@ function read() {
|
|
|
22
23
|
var saved = user_defaults.item(STORE_KEY);
|
|
23
24
|
return saved && typeof saved === 'object' ? saved : null;
|
|
24
25
|
}
|
|
25
|
-
/**
|
|
26
|
+
/** When the last answered getupdates went out; 0 when nothing is stored. */
|
|
26
27
|
function flagtime() {
|
|
27
28
|
var saved = read();
|
|
28
29
|
return saved && typeof saved.flagtime === 'number' ? saved.flagtime : 0;
|
|
29
30
|
}
|
|
30
|
-
/**
|
|
31
|
-
* The newest `version[].date` in a rule set, which is what the next fetch sends as flagtime.
|
|
32
|
-
*
|
|
33
|
-
* The array is a publication history and is not promised to be in order, so it is the highest
|
|
34
|
-
* date that matters rather than the first or last entry.
|
|
35
|
-
*/
|
|
36
|
-
function publishedAt(rules) {
|
|
37
|
-
var versions = Array.isArray(rules.version) ? rules.version : [];
|
|
38
|
-
var newest = 0;
|
|
39
|
-
versions.forEach(function (one) {
|
|
40
|
-
var at = Number(one.date);
|
|
41
|
-
if (isFinite(at) && at > newest) {
|
|
42
|
-
newest = at;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
return newest;
|
|
46
|
-
}
|
|
47
31
|
/** Re-resolves the stored rules against this device, without fetching. */
|
|
48
32
|
function reresolve() {
|
|
49
33
|
var saved = read();
|
|
50
34
|
values = saved && saved.rules ? resolveAll(saved.rules) : {};
|
|
51
35
|
loaded = true;
|
|
52
36
|
}
|
|
53
|
-
/**
|
|
54
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Stores a whole rule set, the answer to flagtime 0, in place of what was stored, and resolves
|
|
39
|
+
* it. `askedAt` is when the request went out: the next one asks for what changed since then.
|
|
40
|
+
*/
|
|
41
|
+
function store(rules, askedAt) {
|
|
55
42
|
resetEvaluationWarnings();
|
|
56
|
-
var record = { flagtime:
|
|
43
|
+
var record = { flagtime: askedAt, rules: rules, fetchedat: nowMs() };
|
|
57
44
|
user_defaults.add(STORE_KEY, record);
|
|
58
45
|
values = resolveAll(rules);
|
|
59
46
|
loaded = true;
|
|
60
|
-
Console.log('AppticsRemoteConfig: ' + Object.keys(values).length + ' parameters,
|
|
47
|
+
Console.log('AppticsRemoteConfig: ' + Object.keys(values).length + ' parameters, as of ' + askedAt); // no i18n
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* `changed` laid over `stored`, matched by `key`: an entry the server resent replaces the one
|
|
51
|
+
* with the same key, in its place; a new one goes on the end; the rest stay as they were.
|
|
52
|
+
*/
|
|
53
|
+
function mergeList(stored, changed, key) {
|
|
54
|
+
var out = Array.isArray(stored) ? stored.slice() : [];
|
|
55
|
+
if (!Array.isArray(changed)) {
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
changed.forEach(function (entry) {
|
|
59
|
+
if (!entry || typeof entry !== 'object') {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
var id = key(entry);
|
|
63
|
+
var at = id === '' ? -1 : out.findIndex(function (one) { return key(one) === id; });
|
|
64
|
+
if (at >= 0) {
|
|
65
|
+
out[at] = entry;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
out.push(entry);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** An entry's id, or its name when it has none, as the key to match a resent entry by. */
|
|
74
|
+
function idOr(idKey, nameKey) {
|
|
75
|
+
return function (entry) {
|
|
76
|
+
var id = entry[idKey] !== undefined && entry[idKey] !== null ? entry[idKey] : entry[nameKey];
|
|
77
|
+
return id === undefined || id === null ? '' : String(id); // no i18n
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Merges what a later getupdates sent, everything added or changed since its flagtime, into the
|
|
82
|
+
* stored rule set, and resolves the result. With nothing stored, it is the whole set.
|
|
83
|
+
*/
|
|
84
|
+
function merge(changes, askedAt) {
|
|
85
|
+
var saved = read();
|
|
86
|
+
var base = saved && saved.rules && typeof saved.rules === 'object' ? saved.rules : {};
|
|
87
|
+
var rules = Object.assign({}, base, changes);
|
|
88
|
+
rules.all_parameter = mergeList(base.all_parameter, changes.all_parameter, idOr('paramid', 'paramname')); // no i18n
|
|
89
|
+
rules.all_condition = mergeList(base.all_condition, changes.all_condition, idOr('conditionid', 'conditionname')); // no i18n
|
|
90
|
+
rules.version = mergeList(base.version, changes.version, idOr('versionid', 'date')); // no i18n
|
|
91
|
+
store(rules, askedAt);
|
|
61
92
|
}
|
|
62
93
|
/**
|
|
63
|
-
* Records a fetch that brought no
|
|
94
|
+
* Records a later fetch that brought no changes.
|
|
64
95
|
*
|
|
65
|
-
* That is the normal answer
|
|
66
|
-
*
|
|
96
|
+
* That is the normal answer, and it is not a failure: what is stored stays, and the next request
|
|
97
|
+
* asks for what changed since this one went out.
|
|
67
98
|
*/
|
|
68
|
-
function storeUnchanged(
|
|
99
|
+
function storeUnchanged(askedAt) {
|
|
69
100
|
var saved = read();
|
|
70
101
|
if (saved === null) {
|
|
71
102
|
return;
|
|
72
103
|
}
|
|
73
|
-
saved.
|
|
104
|
+
saved.flagtime = askedAt;
|
|
105
|
+
saved.fetchedat = nowMs();
|
|
74
106
|
user_defaults.add(STORE_KEY, saved);
|
|
75
107
|
}
|
|
76
108
|
/** Forgets the rule set and its values, so the next fetch asks with flagtime 0. */
|
|
@@ -88,4 +120,4 @@ function all() {
|
|
|
88
120
|
return values;
|
|
89
121
|
}
|
|
90
122
|
|
|
91
|
-
export { all, clearRemoteConfig, flagtime, reresolve, store, storeUnchanged };
|
|
123
|
+
export { all, clearRemoteConfig, flagtime, merge, reresolve, store, storeUnchanged };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
/**
|
|
3
3
|
* The country of every IANA time zone, by its ISO 3166 code: generated by
|
|
4
4
|
* scripts/tz-countries.mjs from tzdata 2026d-rearguard (zone.tab, and the old names its links keep).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Where
|
|
2
|
+
* Where in the app an AppticsLogger line was written, for logSDK's `filename`, `linenumber` and
|
|
3
|
+
* `function`: the Remote Logger page shows each line with them.
|
|
3
4
|
*
|
|
4
5
|
* There is no caller introspection in a browser, so the only way to answer "which line was this"
|
|
5
|
-
* is to
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* is to make an Error and read its stack, skipping the SDK's own frames. That costs a little on
|
|
7
|
+
* every line the app logs, and nothing on any other call. Everything here is wrapped so a stack
|
|
8
|
+
* that cannot be parsed costs a line its call site and nothing more: the line is still logged.
|
|
8
9
|
*/
|
|
9
10
|
export interface CallSite {
|
|
10
11
|
/** The script's basename, not its url: logSDK caps filename at 100 characters. */
|