@miso.ai/client-sdk-core 1.14.0-beta.3 → 1.14.0-beta.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/dist/cjs/api/base.js +12 -0
- package/dist/cjs/api/helpers.js +21 -0
- package/dist/cjs/classes.js +2 -1
- package/dist/cjs/client.js +2 -1
- package/dist/cjs/plugin/api-recovery.js +123 -0
- package/dist/cjs/plugin/index.js +7 -0
- package/dist/cjs/plugin/plugin-root.js +7 -1
- package/dist/cjs/root.js +1 -0
- package/dist/cjs/version.js +1 -1
- package/package.json +2 -2
- package/src/api/base.js +7 -0
- package/src/api/helpers.js +10 -0
- package/src/plugin/CLAUDE.md +18 -0
- package/src/plugin/api-recovery.js +124 -0
- package/src/plugin/index.js +1 -0
- package/src/plugin/plugin-root.js +8 -0
- package/src/root.js +1 -0
- package/src/version.js +1 -1
package/dist/cjs/api/base.js
CHANGED
|
@@ -16,6 +16,18 @@ class ApiBase extends _commons.Component {
|
|
|
16
16
|
this.context = api._client.context;
|
|
17
17
|
}
|
|
18
18
|
async _run(apiName, payload, options = {}) {
|
|
19
|
+
const apiGroup = this._apiPath;
|
|
20
|
+
return this.helpers.applyMiddlewares({
|
|
21
|
+
apiGroup,
|
|
22
|
+
apiName,
|
|
23
|
+
payload,
|
|
24
|
+
options
|
|
25
|
+
}, () => this._request(apiName, payload, options));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// one round of the request: the passes run per round, so a round run again
|
|
29
|
+
// by a middleware picks up the context as of then (e.g. a renewed token)
|
|
30
|
+
async _request(apiName, payload, options) {
|
|
19
31
|
const {
|
|
20
32
|
bulk
|
|
21
33
|
} = options;
|
package/dist/cjs/api/helpers.js
CHANGED
|
@@ -164,6 +164,27 @@ class ApiHelpers {
|
|
|
164
164
|
} = this._client.options;
|
|
165
165
|
return apiName === 'interactions' ? eventEndpoint : dataEndpoint;
|
|
166
166
|
}
|
|
167
|
+
|
|
168
|
+
// middlewares wrap the whole request — passes, send, and postprocess —
|
|
169
|
+
// in registration order, the first being the outermost. Each is called
|
|
170
|
+
// with the request info and a `next` function that runs the rest of the
|
|
171
|
+
// chain, which it may defer, catch the failure of, or call again
|
|
172
|
+
async applyMiddlewares({
|
|
173
|
+
apiGroup,
|
|
174
|
+
apiName,
|
|
175
|
+
payload,
|
|
176
|
+
options
|
|
177
|
+
}, run) {
|
|
178
|
+
const client = this._client;
|
|
179
|
+
const request = Object.freeze({
|
|
180
|
+
client,
|
|
181
|
+
apiGroup,
|
|
182
|
+
apiName,
|
|
183
|
+
payload,
|
|
184
|
+
options
|
|
185
|
+
});
|
|
186
|
+
return this._root._apiMiddlewares.reduceRight((next, middleware) => () => middleware(request, next), run)();
|
|
187
|
+
}
|
|
167
188
|
applyUrlPasses(component, {
|
|
168
189
|
apiGroup,
|
|
169
190
|
apiName,
|
package/dist/cjs/classes.js
CHANGED
|
@@ -7,7 +7,8 @@ exports.default = void 0;
|
|
|
7
7
|
var api = _interopRequireWildcard(require("./api/index.js"));
|
|
8
8
|
var _context = _interopRequireDefault(require("./context.js"));
|
|
9
9
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
-
|
|
10
|
+
var cache;
|
|
11
|
+
function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
|
|
11
12
|
var _default = exports.default = {
|
|
12
13
|
api,
|
|
13
14
|
context: {
|
package/dist/cjs/client.js
CHANGED
|
@@ -9,7 +9,8 @@ var _root = require("./root.js");
|
|
|
9
9
|
var _index = require("./api/index.js");
|
|
10
10
|
var _context = _interopRequireDefault(require("./context.js"));
|
|
11
11
|
var helpers = _interopRequireWildcard(require("./utils.js"));
|
|
12
|
-
|
|
12
|
+
var cache;
|
|
13
|
+
function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
|
|
13
14
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
15
|
const currentScript = _commons.isInBrowser ? document.currentScript : undefined;
|
|
15
16
|
class MisoClient extends _commons.Component {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _commons = require("@miso.ai/commons");
|
|
8
|
+
const _excluded = ["client"];
|
|
9
|
+
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
|
|
10
|
+
const PLUGIN_ID = 'std:api-recovery';
|
|
11
|
+
|
|
12
|
+
// a safety net against a callback that resends unconditionally
|
|
13
|
+
const MAX_RESENT = 10;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Lets the application intercept failed API requests, per client:
|
|
17
|
+
*
|
|
18
|
+
* ```js
|
|
19
|
+
* client.api.onError = async (error, { request, hold, resend }) => { ... };
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* The request settles with the outcome of the callback: the error it throws,
|
|
23
|
+
* the value it returns (taken as the API result), or — by returning
|
|
24
|
+
* `resend()` — the outcome of sending the request again.
|
|
25
|
+
*
|
|
26
|
+
* A resent request that fails is intercepted like any other, with
|
|
27
|
+
* `error.resent` telling how many times the request has been resent so far
|
|
28
|
+
* (0 for the original failure), so the callback can give up at some count.
|
|
29
|
+
* As a safety net, a request resent 10 times fails for good, its error
|
|
30
|
+
* thrown without reaching the callback.
|
|
31
|
+
*
|
|
32
|
+
* `hold()` holds the requests issued from then on (resent ones included)
|
|
33
|
+
* until the unhold function it returns is called. Requests already in flight
|
|
34
|
+
* are not affected, so several errors may reach the callback before a hold
|
|
35
|
+
* takes effect: managing them in one go (e.g. sharing a single token renewal)
|
|
36
|
+
* is up to the application.
|
|
37
|
+
*/
|
|
38
|
+
class ApiRecoveryPlugin {
|
|
39
|
+
static get id() {
|
|
40
|
+
return PLUGIN_ID;
|
|
41
|
+
}
|
|
42
|
+
constructor() {
|
|
43
|
+
this._recoveries = new WeakMap();
|
|
44
|
+
}
|
|
45
|
+
install(MisoClient, context) {
|
|
46
|
+
context.addApiMiddleware(this._middleware.bind(this));
|
|
47
|
+
MisoClient.on('create', this._injectClient.bind(this));
|
|
48
|
+
}
|
|
49
|
+
_injectClient(client) {
|
|
50
|
+
const recovery = new ApiRecovery();
|
|
51
|
+
this._recoveries.set(client, recovery);
|
|
52
|
+
(0, _commons.delegateProperties)(client.api, recovery, ['onError']);
|
|
53
|
+
}
|
|
54
|
+
async _middleware(_ref, next) {
|
|
55
|
+
let {
|
|
56
|
+
client
|
|
57
|
+
} = _ref,
|
|
58
|
+
request = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
59
|
+
const recovery = this._recoveries.get(client);
|
|
60
|
+
return recovery ? recovery.run(request, next) : next();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.default = ApiRecoveryPlugin;
|
|
64
|
+
class ApiRecovery {
|
|
65
|
+
constructor() {
|
|
66
|
+
this._callback = undefined;
|
|
67
|
+
this._holds = new Set();
|
|
68
|
+
this._gate = undefined;
|
|
69
|
+
}
|
|
70
|
+
get onError() {
|
|
71
|
+
return this._callback;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// a single callback: setting it replaces the current one, and setting it
|
|
75
|
+
// to undefined removes it
|
|
76
|
+
set onError(callback) {
|
|
77
|
+
if (callback != undefined && typeof callback !== 'function') {
|
|
78
|
+
throw new Error(`Expect onError to be a function: ${callback}`);
|
|
79
|
+
}
|
|
80
|
+
this._callback = callback || undefined;
|
|
81
|
+
}
|
|
82
|
+
async run(request, next, resent = 0) {
|
|
83
|
+
try {
|
|
84
|
+
return await this._send(next);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
const callback = this._callback;
|
|
87
|
+
if (!callback || resent >= MAX_RESENT) {
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
if (error && typeof error === 'object') {
|
|
91
|
+
error.resent = resent;
|
|
92
|
+
}
|
|
93
|
+
return callback(error, Object.freeze({
|
|
94
|
+
request,
|
|
95
|
+
hold: () => this._hold(),
|
|
96
|
+
resend: () => this.run(request, next, resent + 1)
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async _send(next) {
|
|
101
|
+
// loop: a new hold may be placed by the time the gate opens
|
|
102
|
+
while (this._gate) {
|
|
103
|
+
await this._gate.promise;
|
|
104
|
+
}
|
|
105
|
+
return next();
|
|
106
|
+
}
|
|
107
|
+
_hold() {
|
|
108
|
+
const hold = {};
|
|
109
|
+
this._holds.add(hold);
|
|
110
|
+
if (!this._gate) {
|
|
111
|
+
this._gate = new _commons.Resolution();
|
|
112
|
+
}
|
|
113
|
+
// holds overlap: the gate opens when the last one is released
|
|
114
|
+
return () => {
|
|
115
|
+
if (!this._holds.delete(hold) || this._holds.size) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const gate = this._gate;
|
|
119
|
+
this._gate = undefined;
|
|
120
|
+
gate.resolve();
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
package/dist/cjs/plugin/index.js
CHANGED
|
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "ApiPatchPlugin", {
|
|
|
15
15
|
return _apiPatch.default;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "ApiRecoveryPlugin", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _apiRecovery.default;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
Object.defineProperty(exports, "AutoEventsPlugin", {
|
|
19
25
|
enumerable: true,
|
|
20
26
|
get: function () {
|
|
@@ -57,6 +63,7 @@ var _autoEvents = _interopRequireDefault(require("./auto-events.js"));
|
|
|
57
63
|
var _interactions = _interopRequireDefault(require("./interactions.js"));
|
|
58
64
|
var _nativeFetch = _interopRequireDefault(require("./native-fetch.js"));
|
|
59
65
|
var _apiPatch = _interopRequireDefault(require("./api-patch.js"));
|
|
66
|
+
var _apiRecovery = _interopRequireDefault(require("./api-recovery.js"));
|
|
60
67
|
var _headerApiKey = _interopRequireDefault(require("./header-api-key.js"));
|
|
61
68
|
var _analytics = _interopRequireDefault(require("./analytics.js"));
|
|
62
69
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -106,6 +106,12 @@ class PluginRoot extends _commons.Registry {
|
|
|
106
106
|
}
|
|
107
107
|
this._root._urlPasses.push(pass);
|
|
108
108
|
}
|
|
109
|
+
addApiMiddleware(middleware) {
|
|
110
|
+
if (typeof middleware !== 'function') {
|
|
111
|
+
throw new Error(`Expect parameter to be a function: ${middleware}`);
|
|
112
|
+
}
|
|
113
|
+
this._root._apiMiddlewares.push(middleware);
|
|
114
|
+
}
|
|
109
115
|
setCustomFetch(fetch) {
|
|
110
116
|
if (typeof fetch !== 'function') {
|
|
111
117
|
throw new Error(`Expect parameter to be a function: ${fetch}`);
|
|
@@ -253,7 +259,7 @@ const PLUGIN_QUERY_METHODS = ['installed', 'registered', 'isInstalled', 'isRegis
|
|
|
253
259
|
*/
|
|
254
260
|
class PluginContext {
|
|
255
261
|
constructor(root, classes) {
|
|
256
|
-
(0, _commons.delegateGetters)(this, root, [...PLUGIN_QUERY_METHODS, 'getPluginClass', 'addSubtree', 'addExtensionPoints', 'addPayloadPass', 'addHeadersPass', 'addUrlPass', 'setCustomFetch', 'setCustomSendBeacon', 'setCustomApiIterator', 'onHubUpdate', 'onHubEmit']);
|
|
262
|
+
(0, _commons.delegateGetters)(this, root, [...PLUGIN_QUERY_METHODS, 'getPluginClass', 'addSubtree', 'addExtensionPoints', 'addPayloadPass', 'addHeadersPass', 'addUrlPass', 'addApiMiddleware', 'setCustomFetch', 'setCustomSendBeacon', 'setCustomApiIterator', 'onHubUpdate', 'onHubEmit']);
|
|
257
263
|
(0, _commons.defineValues)(this, {
|
|
258
264
|
classes
|
|
259
265
|
});
|
package/dist/cjs/root.js
CHANGED
package/dist/cjs/version.js
CHANGED
package/package.json
CHANGED
package/src/api/base.js
CHANGED
|
@@ -11,6 +11,13 @@ export default class ApiBase extends Component {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async _run(apiName, payload, options = {}) {
|
|
14
|
+
const apiGroup = this._apiPath;
|
|
15
|
+
return this.helpers.applyMiddlewares({ apiGroup, apiName, payload, options }, () => this._request(apiName, payload, options));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// one round of the request: the passes run per round, so a round run again
|
|
19
|
+
// by a middleware picks up the context as of then (e.g. a renewed token)
|
|
20
|
+
async _request(apiName, payload, options) {
|
|
14
21
|
const { bulk } = options;
|
|
15
22
|
const bulkInfo = bulk ? { bulk: this.helpers.bulkInfo } : undefined;
|
|
16
23
|
const apiGroup = this._apiPath;
|
package/src/api/helpers.js
CHANGED
|
@@ -128,6 +128,16 @@ export default class ApiHelpers {
|
|
|
128
128
|
return apiName === 'interactions' ? eventEndpoint : dataEndpoint;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// middlewares wrap the whole request — passes, send, and postprocess —
|
|
132
|
+
// in registration order, the first being the outermost. Each is called
|
|
133
|
+
// with the request info and a `next` function that runs the rest of the
|
|
134
|
+
// chain, which it may defer, catch the failure of, or call again
|
|
135
|
+
async applyMiddlewares({ apiGroup, apiName, payload, options }, run) {
|
|
136
|
+
const client = this._client;
|
|
137
|
+
const request = Object.freeze({ client, apiGroup, apiName, payload, options });
|
|
138
|
+
return this._root._apiMiddlewares.reduceRight((next, middleware) => () => middleware(request, next), run)();
|
|
139
|
+
}
|
|
140
|
+
|
|
131
141
|
applyUrlPasses(component, { apiGroup, apiName, url }) {
|
|
132
142
|
const client = this._client;
|
|
133
143
|
for (const pass of this._root._urlPasses) {
|
package/src/plugin/CLAUDE.md
CHANGED
|
@@ -20,6 +20,7 @@ Exposed to plugins during `install()`, provides hooks into the SDK:
|
|
|
20
20
|
context.addPayloadPass(fn) // Modify API request payloads
|
|
21
21
|
context.addHeadersPass(fn) // Modify request headers
|
|
22
22
|
context.addUrlPass(fn) // Modify request URLs
|
|
23
|
+
context.addApiMiddleware(fn) // Wrap API requests: defer, catch failures, resend
|
|
23
24
|
context.setCustomFetch(fn) // Override fetch implementation
|
|
24
25
|
context.setCustomSendBeacon(fn) // Override sendBeacon
|
|
25
26
|
context.addSubtree(component) // Register as component subtree for event propagation
|
|
@@ -77,6 +78,7 @@ export default class MyPlugin {
|
|
|
77
78
|
| `InteractionsPlugin` | `std:interactions` | Enriches interaction records with SDK version/UUID |
|
|
78
79
|
| `NativeFetchPlugin` | `std:native-fetch` | Uses native fetch, bypassing custom implementations |
|
|
79
80
|
| `ApiPatchPlugin` | `std:api-patch` | Applies patches to API responses |
|
|
81
|
+
| `ApiRecoveryPlugin` | `std:api-recovery` | `client.api.onError`: intercept failed requests, hold and resend |
|
|
80
82
|
| `HeaderApiKeyPlugin` | `std:header-api-key` | Sends API key in header instead of query |
|
|
81
83
|
| `AnalyticsPlugin` | `std:analytics` | User engagement timing and analytics |
|
|
82
84
|
|
|
@@ -98,6 +100,22 @@ function modifyHeaders({ client, headers }) {
|
|
|
98
100
|
}
|
|
99
101
|
```
|
|
100
102
|
|
|
103
|
+
## API Middlewares
|
|
104
|
+
|
|
105
|
+
A middleware wraps a whole API request (passes, send, postprocess). It may defer the request, catch its failure, or run it again — each round re-applies the passes, so it picks up the context as of then:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
// request: { client, apiGroup, apiName, payload, options }
|
|
109
|
+
async function middleware(request, next) {
|
|
110
|
+
try {
|
|
111
|
+
return await next();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
// ...
|
|
114
|
+
return next();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
101
119
|
## Lifecycle
|
|
102
120
|
|
|
103
121
|
1. `register()` - Plugin class stored by ID
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Resolution, delegateProperties } from '@miso.ai/commons';
|
|
2
|
+
|
|
3
|
+
const PLUGIN_ID = 'std:api-recovery';
|
|
4
|
+
|
|
5
|
+
// a safety net against a callback that resends unconditionally
|
|
6
|
+
const MAX_RESENT = 10;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Lets the application intercept failed API requests, per client:
|
|
10
|
+
*
|
|
11
|
+
* ```js
|
|
12
|
+
* client.api.onError = async (error, { request, hold, resend }) => { ... };
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* The request settles with the outcome of the callback: the error it throws,
|
|
16
|
+
* the value it returns (taken as the API result), or — by returning
|
|
17
|
+
* `resend()` — the outcome of sending the request again.
|
|
18
|
+
*
|
|
19
|
+
* A resent request that fails is intercepted like any other, with
|
|
20
|
+
* `error.resent` telling how many times the request has been resent so far
|
|
21
|
+
* (0 for the original failure), so the callback can give up at some count.
|
|
22
|
+
* As a safety net, a request resent 10 times fails for good, its error
|
|
23
|
+
* thrown without reaching the callback.
|
|
24
|
+
*
|
|
25
|
+
* `hold()` holds the requests issued from then on (resent ones included)
|
|
26
|
+
* until the unhold function it returns is called. Requests already in flight
|
|
27
|
+
* are not affected, so several errors may reach the callback before a hold
|
|
28
|
+
* takes effect: managing them in one go (e.g. sharing a single token renewal)
|
|
29
|
+
* is up to the application.
|
|
30
|
+
*/
|
|
31
|
+
export default class ApiRecoveryPlugin {
|
|
32
|
+
|
|
33
|
+
static get id() {
|
|
34
|
+
return PLUGIN_ID;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
this._recoveries = new WeakMap();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
install(MisoClient, context) {
|
|
42
|
+
context.addApiMiddleware(this._middleware.bind(this));
|
|
43
|
+
MisoClient.on('create', this._injectClient.bind(this));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_injectClient(client) {
|
|
47
|
+
const recovery = new ApiRecovery();
|
|
48
|
+
this._recoveries.set(client, recovery);
|
|
49
|
+
delegateProperties(client.api, recovery, ['onError']);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async _middleware({ client, ...request }, next) {
|
|
53
|
+
const recovery = this._recoveries.get(client);
|
|
54
|
+
return recovery ? recovery.run(request, next) : next();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class ApiRecovery {
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
this._callback = undefined;
|
|
63
|
+
this._holds = new Set();
|
|
64
|
+
this._gate = undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get onError() {
|
|
68
|
+
return this._callback;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// a single callback: setting it replaces the current one, and setting it
|
|
72
|
+
// to undefined removes it
|
|
73
|
+
set onError(callback) {
|
|
74
|
+
if (callback != undefined && typeof callback !== 'function') {
|
|
75
|
+
throw new Error(`Expect onError to be a function: ${callback}`);
|
|
76
|
+
}
|
|
77
|
+
this._callback = callback || undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async run(request, next, resent = 0) {
|
|
81
|
+
try {
|
|
82
|
+
return await this._send(next);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const callback = this._callback;
|
|
85
|
+
if (!callback || resent >= MAX_RESENT) {
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
if (error && typeof error === 'object') {
|
|
89
|
+
error.resent = resent;
|
|
90
|
+
}
|
|
91
|
+
return callback(error, Object.freeze({
|
|
92
|
+
request,
|
|
93
|
+
hold: () => this._hold(),
|
|
94
|
+
resend: () => this.run(request, next, resent + 1),
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async _send(next) {
|
|
100
|
+
// loop: a new hold may be placed by the time the gate opens
|
|
101
|
+
while (this._gate) {
|
|
102
|
+
await this._gate.promise;
|
|
103
|
+
}
|
|
104
|
+
return next();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_hold() {
|
|
108
|
+
const hold = {};
|
|
109
|
+
this._holds.add(hold);
|
|
110
|
+
if (!this._gate) {
|
|
111
|
+
this._gate = new Resolution();
|
|
112
|
+
}
|
|
113
|
+
// holds overlap: the gate opens when the last one is released
|
|
114
|
+
return () => {
|
|
115
|
+
if (!this._holds.delete(hold) || this._holds.size) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const gate = this._gate;
|
|
119
|
+
this._gate = undefined;
|
|
120
|
+
gate.resolve();
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
package/src/plugin/index.js
CHANGED
|
@@ -4,5 +4,6 @@ export { default as AutoEventsPlugin } from './auto-events.js';
|
|
|
4
4
|
export { default as InteractionsPlugin } from './interactions.js';
|
|
5
5
|
export { default as NativeFetchPlugin } from './native-fetch.js';
|
|
6
6
|
export { default as ApiPatchPlugin } from './api-patch.js';
|
|
7
|
+
export { default as ApiRecoveryPlugin } from './api-recovery.js';
|
|
7
8
|
export { default as HeaderApiKeyPlugin } from './header-api-key.js';
|
|
8
9
|
export { default as AnalyticsPlugin } from './analytics.js';
|
|
@@ -111,6 +111,13 @@ export default class PluginRoot extends Registry {
|
|
|
111
111
|
this._root._urlPasses.push(pass);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
addApiMiddleware(middleware) {
|
|
115
|
+
if (typeof middleware !== 'function') {
|
|
116
|
+
throw new Error(`Expect parameter to be a function: ${middleware}`);
|
|
117
|
+
}
|
|
118
|
+
this._root._apiMiddlewares.push(middleware);
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
setCustomFetch(fetch) {
|
|
115
122
|
if (typeof fetch !== 'function') {
|
|
116
123
|
throw new Error(`Expect parameter to be a function: ${fetch}`);
|
|
@@ -276,6 +283,7 @@ class PluginContext {
|
|
|
276
283
|
'addPayloadPass',
|
|
277
284
|
'addHeadersPass',
|
|
278
285
|
'addUrlPass',
|
|
286
|
+
'addApiMiddleware',
|
|
279
287
|
'setCustomFetch',
|
|
280
288
|
'setCustomSendBeacon',
|
|
281
289
|
'setCustomApiIterator',
|
package/src/root.js
CHANGED
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.14.0-beta.
|
|
1
|
+
export default '1.14.0-beta.4';
|