@data-client/core 0.15.0 → 0.15.3
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 +10 -0
- package/README.md +1 -1
- package/dist/mock.js +1242 -0
- package/package.json +4 -5
- package/data_client_logo_and_text.svg +0 -64
- package/typescript.svg +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @data-client/core
|
|
2
2
|
|
|
3
|
+
## 0.15.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#3691](https://github.com/reactive/data-client/pull/3691) [`bf3ac79`](https://github.com/reactive/data-client/commit/bf3ac7966dc615b1dc6cc6c6d600148fdca4e354) Thanks [@ntucker](https://github.com/ntucker)! - Fix CommonJS compatibility for `/mock` subpath exports.
|
|
8
|
+
|
|
9
|
+
Jest and other CommonJS consumers can now import from `@data-client/react/mock` and `@data-client/core/mock` without ESM parsing errors.
|
|
10
|
+
|
|
11
|
+
- [`ed4ec6d`](https://github.com/reactive/data-client/commit/ed4ec6dc516ac9e3977de7ec9018bff962626133) Thanks [@ntucker](https://github.com/ntucker)! - Fix image links in package README
|
|
12
|
+
|
|
3
13
|
## 0.15.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1>
|
|
2
2
|
<div align="center">
|
|
3
3
|
<a href="https://dataclient.io" target="_blank" rel="noopener">
|
|
4
|
-
<img alt="Reactive Data Client" src="
|
|
4
|
+
<img alt="Reactive Data Client" src="https://raw.githubusercontent.com/reactive/data-client/master/packages/core/data_client_logo_and_text.svg?sanitize=true">
|
|
5
5
|
</a>
|
|
6
6
|
</div>
|
|
7
7
|
</h1>
|
package/dist/mock.js
ADDED
|
@@ -0,0 +1,1242 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var normalizr = require('@data-client/normalizr');
|
|
4
|
+
|
|
5
|
+
function expireReducer(state, action) {
|
|
6
|
+
const meta = {
|
|
7
|
+
...state.meta
|
|
8
|
+
};
|
|
9
|
+
Object.keys(meta).forEach(key => {
|
|
10
|
+
if (action.testKey(key)) {
|
|
11
|
+
meta[key] = {
|
|
12
|
+
...meta[key],
|
|
13
|
+
// 1 instead of 0 so we can do 'falsy' checks to see if it is set
|
|
14
|
+
expiresAt: 1
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
...state,
|
|
20
|
+
meta
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createMeta(expiryLength, fetchedAt) {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
return {
|
|
27
|
+
fetchedAt: fetchedAt != null ? fetchedAt : now,
|
|
28
|
+
date: now,
|
|
29
|
+
expiresAt: now + expiryLength
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const FETCH = 'rdc/fetch';
|
|
34
|
+
const SET = 'rdc/set';
|
|
35
|
+
const SET_RESPONSE = 'rdc/setresponse';
|
|
36
|
+
const OPTIMISTIC = 'rdc/optimistic';
|
|
37
|
+
const RESET = 'rdc/reset';
|
|
38
|
+
const SUBSCRIBE = 'rdc/subscribe';
|
|
39
|
+
const UNSUBSCRIBE = 'rdc/unsubscribe';
|
|
40
|
+
const INVALIDATE = 'rdc/invalidate';
|
|
41
|
+
const INVALIDATEALL = 'rdc/invalidateall';
|
|
42
|
+
const EXPIREALL = 'rdc/expireall';
|
|
43
|
+
const GC = 'rdc/gc';
|
|
44
|
+
const FETCH_TYPE = FETCH;
|
|
45
|
+
|
|
46
|
+
function createOptimistic(endpoint, args, fetchedAt) {
|
|
47
|
+
var _endpoint$dataExpiryL, _endpoint$dataExpiryL2;
|
|
48
|
+
/* istanbul ignore next */
|
|
49
|
+
if (process.env.NODE_ENV === 'development' && ((_endpoint$dataExpiryL = endpoint.dataExpiryLength) != null ? _endpoint$dataExpiryL : 0) < 0) {
|
|
50
|
+
throw new Error('Negative expiry length are not allowed.');
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
type: OPTIMISTIC,
|
|
54
|
+
key: endpoint.key(...args),
|
|
55
|
+
args,
|
|
56
|
+
endpoint,
|
|
57
|
+
meta: createMeta((_endpoint$dataExpiryL2 = endpoint.dataExpiryLength) != null ? _endpoint$dataExpiryL2 : 60000, fetchedAt)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function fetchReducer(state, action) {
|
|
62
|
+
if (action.endpoint.getOptimisticResponse && action.endpoint.sideEffect) {
|
|
63
|
+
const setAction = createOptimistic(action.endpoint, action.args, action.meta.fetchedAt);
|
|
64
|
+
return {
|
|
65
|
+
...state,
|
|
66
|
+
optimistic: [...state.optimistic, setAction]
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return state;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function invalidateReducer(state, action) {
|
|
73
|
+
const endpoints = {
|
|
74
|
+
...state.endpoints
|
|
75
|
+
};
|
|
76
|
+
const meta = {
|
|
77
|
+
...state.meta
|
|
78
|
+
};
|
|
79
|
+
const invalidateKey = key => {
|
|
80
|
+
delete endpoints[key];
|
|
81
|
+
const itemMeta = {
|
|
82
|
+
...meta[key],
|
|
83
|
+
expiresAt: 0,
|
|
84
|
+
invalidated: true
|
|
85
|
+
};
|
|
86
|
+
delete itemMeta.error;
|
|
87
|
+
meta[key] = itemMeta;
|
|
88
|
+
};
|
|
89
|
+
if (action.type === INVALIDATE) {
|
|
90
|
+
invalidateKey(action.key);
|
|
91
|
+
} else {
|
|
92
|
+
Object.keys(endpoints).forEach(key => {
|
|
93
|
+
if (action.testKey(key)) {
|
|
94
|
+
invalidateKey(key);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
...state,
|
|
100
|
+
endpoints,
|
|
101
|
+
meta
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setReducer(state, action, controller) {
|
|
106
|
+
let value;
|
|
107
|
+
if (typeof action.value === 'function') {
|
|
108
|
+
const previousValue = controller.get(action.schema, ...action.args, state);
|
|
109
|
+
if (previousValue === undefined) return state;
|
|
110
|
+
value = action.value(previousValue);
|
|
111
|
+
} else {
|
|
112
|
+
value = action.value;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const {
|
|
116
|
+
entities,
|
|
117
|
+
indexes,
|
|
118
|
+
entitiesMeta
|
|
119
|
+
} = normalizr.normalize(action.schema, value, action.args, state, action.meta);
|
|
120
|
+
return {
|
|
121
|
+
entities,
|
|
122
|
+
endpoints: state.endpoints,
|
|
123
|
+
indexes,
|
|
124
|
+
meta: state.meta,
|
|
125
|
+
entitiesMeta,
|
|
126
|
+
optimistic: state.optimistic,
|
|
127
|
+
lastReset: state.lastReset
|
|
128
|
+
};
|
|
129
|
+
// reducer must update the state, so in case of processing errors we simply compute the endpoints inline
|
|
130
|
+
} catch (error) {
|
|
131
|
+
// this is not always bubbled up, so let's double sure this doesn't fail silently
|
|
132
|
+
/* istanbul ignore else */
|
|
133
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
134
|
+
console.error(error);
|
|
135
|
+
}
|
|
136
|
+
return state;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class AbortOptimistic extends Error {}
|
|
141
|
+
|
|
142
|
+
function setResponseReducer(state, action, controller) {
|
|
143
|
+
if (action.error) {
|
|
144
|
+
return reduceError(state, action, action.response);
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
var _state$meta$action$ke;
|
|
148
|
+
let response;
|
|
149
|
+
// for true set's response is contained in action
|
|
150
|
+
if (action.type === OPTIMISTIC) {
|
|
151
|
+
// this should never happen
|
|
152
|
+
/* istanbul ignore if */
|
|
153
|
+
if (!action.endpoint.getOptimisticResponse) return state;
|
|
154
|
+
try {
|
|
155
|
+
// compute optimistic response based on current state
|
|
156
|
+
response = action.endpoint.getOptimisticResponse.call(action.endpoint, controller.snapshot(state, action.meta.fetchedAt), ...action.args);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
// AbortOptimistic means 'do nothing', otherwise we count the exception as endpoint failure
|
|
159
|
+
if (e.constructor === AbortOptimistic) {
|
|
160
|
+
return state;
|
|
161
|
+
}
|
|
162
|
+
throw e;
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
response = action.response;
|
|
166
|
+
}
|
|
167
|
+
const {
|
|
168
|
+
result,
|
|
169
|
+
entities,
|
|
170
|
+
indexes,
|
|
171
|
+
entitiesMeta
|
|
172
|
+
} = normalizr.normalize(action.endpoint.schema, response, action.args, state, action.meta);
|
|
173
|
+
const endpoints = {
|
|
174
|
+
...state.endpoints,
|
|
175
|
+
[action.key]: result
|
|
176
|
+
};
|
|
177
|
+
try {
|
|
178
|
+
if (action.endpoint.update) {
|
|
179
|
+
const updaters = action.endpoint.update(result, ...action.args);
|
|
180
|
+
Object.keys(updaters).forEach(key => {
|
|
181
|
+
endpoints[key] = updaters[key](endpoints[key]);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// no reason to completely fail because of user-code error
|
|
185
|
+
// integrity of this state update is still guaranteed
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error(`The following error occured during Endpoint.update() for ${action.key}`);
|
|
188
|
+
console.error(error);
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
entities,
|
|
192
|
+
endpoints,
|
|
193
|
+
indexes,
|
|
194
|
+
meta: {
|
|
195
|
+
...state.meta,
|
|
196
|
+
[action.key]: {
|
|
197
|
+
date: action.meta.date,
|
|
198
|
+
fetchedAt: action.meta.fetchedAt,
|
|
199
|
+
expiresAt: action.meta.expiresAt,
|
|
200
|
+
prevExpiresAt: (_state$meta$action$ke = state.meta[action.key]) == null ? void 0 : _state$meta$action$ke.expiresAt
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
entitiesMeta,
|
|
204
|
+
optimistic: filterOptimistic(state, action),
|
|
205
|
+
lastReset: state.lastReset
|
|
206
|
+
};
|
|
207
|
+
// reducer must update the state, so in case of processing errors we simply compute the endpoints inline
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (typeof error === 'object') {
|
|
210
|
+
error.message = `Error processing ${action.key}\n\nFull Schema: ${JSON.stringify(action.endpoint.schema, undefined, 2)}\n\nError:\n${error.message}`;
|
|
211
|
+
if ('response' in action) error.response = action.response;
|
|
212
|
+
error.status = 400;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// this is not always bubbled up, so let's double sure this doesn't fail silently
|
|
216
|
+
/* istanbul ignore else */
|
|
217
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
218
|
+
console.error(error);
|
|
219
|
+
}
|
|
220
|
+
return reduceError(state, action, error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function reduceError(state, action, error) {
|
|
224
|
+
if (error.name === 'AbortError') {
|
|
225
|
+
// In case we abort simply undo the optimistic update and act like no fetch even occured
|
|
226
|
+
// We still want those watching promises from fetch directly to observed the abort, but we don't want to
|
|
227
|
+
// Trigger errors in this case. This means theoretically improperly built aborts useSuspense() could suspend forever.
|
|
228
|
+
return {
|
|
229
|
+
...state,
|
|
230
|
+
optimistic: filterOptimistic(state, action)
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
...state,
|
|
235
|
+
meta: {
|
|
236
|
+
...state.meta,
|
|
237
|
+
[action.key]: {
|
|
238
|
+
date: action.meta.date,
|
|
239
|
+
fetchedAt: action.meta.fetchedAt,
|
|
240
|
+
expiresAt: action.meta.expiresAt,
|
|
241
|
+
error,
|
|
242
|
+
errorPolicy: action.endpoint.errorPolicy == null ? void 0 : action.endpoint.errorPolicy(error)
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
optimistic: filterOptimistic(state, action)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/** Filter all requests with same serialization that did not start after the resolving request */
|
|
249
|
+
function filterOptimistic(state, resolvingAction) {
|
|
250
|
+
return state.optimistic.filter(optimisticAction => optimisticAction.key !== resolvingAction.key || (optimisticAction.type === OPTIMISTIC ? optimisticAction.meta.fetchedAt !== resolvingAction.meta.fetchedAt : optimisticAction.meta.date > resolvingAction.meta.date));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function createReducer(controller) {
|
|
254
|
+
return function reducer(state, action) {
|
|
255
|
+
if (!state) state = initialState$1;
|
|
256
|
+
switch (action.type) {
|
|
257
|
+
case GC:
|
|
258
|
+
// inline deletes are fine as these should have 0 refcounts
|
|
259
|
+
action.entities.forEach(({
|
|
260
|
+
key,
|
|
261
|
+
pk
|
|
262
|
+
}) => {
|
|
263
|
+
var _entities$key, _entitiesMeta$key;
|
|
264
|
+
(_entities$key = state.entities[key]) == null || delete _entities$key[pk];
|
|
265
|
+
(_entitiesMeta$key = state.entitiesMeta[key]) == null || delete _entitiesMeta$key[pk];
|
|
266
|
+
});
|
|
267
|
+
action.endpoints.forEach(fetchKey => {
|
|
268
|
+
delete state.endpoints[fetchKey];
|
|
269
|
+
delete state.meta[fetchKey];
|
|
270
|
+
});
|
|
271
|
+
return state;
|
|
272
|
+
case FETCH:
|
|
273
|
+
return fetchReducer(state, action);
|
|
274
|
+
case OPTIMISTIC:
|
|
275
|
+
// eslint-disable-next-line no-fallthrough
|
|
276
|
+
case SET_RESPONSE:
|
|
277
|
+
return setResponseReducer(state, action, controller);
|
|
278
|
+
case SET:
|
|
279
|
+
return setReducer(state, action, controller);
|
|
280
|
+
case INVALIDATEALL:
|
|
281
|
+
case INVALIDATE:
|
|
282
|
+
return invalidateReducer(state, action);
|
|
283
|
+
case EXPIREALL:
|
|
284
|
+
return expireReducer(state, action);
|
|
285
|
+
case RESET:
|
|
286
|
+
return {
|
|
287
|
+
...initialState$1,
|
|
288
|
+
lastReset: action.date
|
|
289
|
+
};
|
|
290
|
+
default:
|
|
291
|
+
// A reducer must always return a valid state.
|
|
292
|
+
// Alternatively you can throw an error if an invalid action is dispatched.
|
|
293
|
+
return state;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const initialState$1 = {
|
|
298
|
+
entities: {},
|
|
299
|
+
endpoints: {},
|
|
300
|
+
indexes: {},
|
|
301
|
+
meta: {},
|
|
302
|
+
entitiesMeta: {},
|
|
303
|
+
optimistic: [],
|
|
304
|
+
lastReset: 0
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
var __INTERNAL__ = /*#__PURE__*/Object.freeze({
|
|
308
|
+
__proto__: null,
|
|
309
|
+
INVALID: normalizr.INVALID,
|
|
310
|
+
MemoCache: normalizr.MemoCache,
|
|
311
|
+
initialState: initialState$1
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
function createSubscription(endpoint, {
|
|
315
|
+
args
|
|
316
|
+
}) {
|
|
317
|
+
return {
|
|
318
|
+
type: SUBSCRIBE,
|
|
319
|
+
key: endpoint.key(...args),
|
|
320
|
+
args,
|
|
321
|
+
endpoint
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function createUnsubscription(endpoint, {
|
|
325
|
+
args
|
|
326
|
+
}) {
|
|
327
|
+
return {
|
|
328
|
+
type: UNSUBSCRIBE,
|
|
329
|
+
key: endpoint.key(...args),
|
|
330
|
+
args,
|
|
331
|
+
endpoint
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const ensurePojo =
|
|
336
|
+
// FormData doesn't exist in node
|
|
337
|
+
/* istanbul ignore else we don't run coverage when we test node*/
|
|
338
|
+
typeof FormData !== 'undefined' ? body => body instanceof FormData ? Object.fromEntries(body.entries()) : body : /* istanbul ignore next */body => body;
|
|
339
|
+
|
|
340
|
+
function createSetResponse(endpoint, {
|
|
341
|
+
args,
|
|
342
|
+
fetchedAt,
|
|
343
|
+
response,
|
|
344
|
+
error = false
|
|
345
|
+
}) {
|
|
346
|
+
var _endpoint$errorExpiry, _endpoint$dataExpiryL;
|
|
347
|
+
const expiryLength = error ? (_endpoint$errorExpiry = endpoint.errorExpiryLength) != null ? _endpoint$errorExpiry : 1000 : (_endpoint$dataExpiryL = endpoint.dataExpiryLength) != null ? _endpoint$dataExpiryL : 60000;
|
|
348
|
+
/* istanbul ignore next */
|
|
349
|
+
if (process.env.NODE_ENV === 'development' && expiryLength < 0) {
|
|
350
|
+
throw new Error('Negative expiry length are not allowed.');
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
type: SET_RESPONSE,
|
|
354
|
+
key: endpoint.key(...args),
|
|
355
|
+
response,
|
|
356
|
+
args: args.map(ensurePojo),
|
|
357
|
+
endpoint,
|
|
358
|
+
meta: createMeta(expiryLength, fetchedAt),
|
|
359
|
+
error
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function createSet(schema, {
|
|
364
|
+
args,
|
|
365
|
+
fetchedAt,
|
|
366
|
+
value
|
|
367
|
+
}) {
|
|
368
|
+
return {
|
|
369
|
+
type: SET,
|
|
370
|
+
value,
|
|
371
|
+
args: args.map(ensurePojo),
|
|
372
|
+
schema,
|
|
373
|
+
meta: createMeta(60000, fetchedAt)
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function createReset() {
|
|
378
|
+
return {
|
|
379
|
+
type: RESET,
|
|
380
|
+
date: Date.now()
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function createInvalidateAll(testKey) {
|
|
385
|
+
return {
|
|
386
|
+
type: INVALIDATEALL,
|
|
387
|
+
testKey
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function createInvalidate(endpoint, {
|
|
392
|
+
args
|
|
393
|
+
}) {
|
|
394
|
+
return {
|
|
395
|
+
type: INVALIDATE,
|
|
396
|
+
key: endpoint.key(...args)
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Requesting a fetch to begin
|
|
402
|
+
*/
|
|
403
|
+
function createFetch(endpoint, {
|
|
404
|
+
args
|
|
405
|
+
}) {
|
|
406
|
+
let resolve = 0;
|
|
407
|
+
let reject = 0;
|
|
408
|
+
const promise = new Promise((a, b) => {
|
|
409
|
+
[resolve, reject] = [a, b];
|
|
410
|
+
});
|
|
411
|
+
const meta = {
|
|
412
|
+
fetchedAt: Date.now(),
|
|
413
|
+
resolve,
|
|
414
|
+
reject,
|
|
415
|
+
promise
|
|
416
|
+
};
|
|
417
|
+
return {
|
|
418
|
+
type: FETCH,
|
|
419
|
+
key: endpoint.key(...args),
|
|
420
|
+
args,
|
|
421
|
+
endpoint,
|
|
422
|
+
meta
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function createExpireAll(testKey) {
|
|
427
|
+
return {
|
|
428
|
+
type: EXPIREALL,
|
|
429
|
+
testKey
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
class ImmortalGCPolicy {
|
|
434
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
435
|
+
init() {}
|
|
436
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
437
|
+
cleanup() {}
|
|
438
|
+
createCountRef() {
|
|
439
|
+
return () => () => undefined;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function selectMeta(state, fetchKey) {
|
|
444
|
+
return state.meta[fetchKey];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const unsetDispatch = action => {
|
|
448
|
+
throw new Error(`Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.`);
|
|
449
|
+
};
|
|
450
|
+
const unsetState = () => {
|
|
451
|
+
// This is only the value until it is set by the DataProvider
|
|
452
|
+
/* istanbul ignore next */
|
|
453
|
+
return initialState$1;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Imperative control of Reactive Data Client store
|
|
458
|
+
* @see https://dataclient.io/docs/api/Controller
|
|
459
|
+
*/
|
|
460
|
+
class Controller {
|
|
461
|
+
/**
|
|
462
|
+
* Dispatches an action to Reactive Data Client reducer.
|
|
463
|
+
*
|
|
464
|
+
* @see https://dataclient.io/docs/api/Controller#dispatch
|
|
465
|
+
*/
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Gets the latest state snapshot that is fully committed.
|
|
469
|
+
*
|
|
470
|
+
* This can be useful for imperative use-cases like event handlers.
|
|
471
|
+
* This should *not* be used to render; instead useSuspense() or useCache()
|
|
472
|
+
* @see https://dataclient.io/docs/api/Controller#getState
|
|
473
|
+
*/
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Singleton to maintain referential equality between calls
|
|
477
|
+
*/
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Handles garbage collection
|
|
481
|
+
*/
|
|
482
|
+
|
|
483
|
+
constructor({
|
|
484
|
+
dispatch = unsetDispatch,
|
|
485
|
+
getState = unsetState,
|
|
486
|
+
memo = new normalizr.MemoCache(),
|
|
487
|
+
gcPolicy = new ImmortalGCPolicy()
|
|
488
|
+
} = {}) {
|
|
489
|
+
this._dispatch = dispatch;
|
|
490
|
+
this.getState = getState;
|
|
491
|
+
this.memo = memo;
|
|
492
|
+
this.gcPolicy = gcPolicy;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
496
|
+
set dispatch(dispatch) {
|
|
497
|
+
/* istanbul ignore next */
|
|
498
|
+
this._dispatch = dispatch;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
502
|
+
get dispatch() {
|
|
503
|
+
return this._dispatch;
|
|
504
|
+
}
|
|
505
|
+
bindMiddleware({
|
|
506
|
+
dispatch,
|
|
507
|
+
getState
|
|
508
|
+
}) {
|
|
509
|
+
this._dispatch = dispatch;
|
|
510
|
+
this.getState = getState;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/*************** Action Dispatchers ***************/
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Fetches the endpoint with given args, updating the Reactive Data Client cache with the response or error upon completion.
|
|
517
|
+
* @see https://dataclient.io/docs/api/Controller#fetch
|
|
518
|
+
*/
|
|
519
|
+
fetch = (endpoint, ...args) => {
|
|
520
|
+
const action = createFetch(endpoint, {
|
|
521
|
+
args
|
|
522
|
+
});
|
|
523
|
+
this.dispatch(action);
|
|
524
|
+
if (endpoint.schema) {
|
|
525
|
+
return action.meta.promise.then(input => normalizr.denormalize(endpoint.schema, input, {}, args));
|
|
526
|
+
}
|
|
527
|
+
return action.meta.promise;
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Fetches only if endpoint is considered 'stale'; otherwise returns undefined
|
|
532
|
+
* @see https://dataclient.io/docs/api/Controller#fetchIfStale
|
|
533
|
+
*/
|
|
534
|
+
fetchIfStale = (endpoint, ...args) => {
|
|
535
|
+
const {
|
|
536
|
+
data,
|
|
537
|
+
expiresAt,
|
|
538
|
+
expiryStatus
|
|
539
|
+
} = this.getResponseMeta(endpoint, ...args, this.getState());
|
|
540
|
+
if (expiryStatus !== normalizr.ExpiryStatus.Invalid && Date.now() <= expiresAt) return data;
|
|
541
|
+
return this.fetch(endpoint, ...args);
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Forces refetching and suspense on useSuspense with the same Endpoint and parameters.
|
|
546
|
+
* @see https://dataclient.io/docs/api/Controller#invalidate
|
|
547
|
+
*/
|
|
548
|
+
invalidate = (endpoint, ...args) => args[0] !== null ? this.dispatch(createInvalidate(endpoint, {
|
|
549
|
+
args: args
|
|
550
|
+
})) : Promise.resolve();
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Forces refetching and suspense on useSuspense on all matching endpoint result keys.
|
|
554
|
+
* @see https://dataclient.io/docs/api/Controller#invalidateAll
|
|
555
|
+
* @returns Promise that resolves when invalidation is commited.
|
|
556
|
+
*/
|
|
557
|
+
invalidateAll = options => this.dispatch(createInvalidateAll(key => options.testKey(key)));
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Sets all matching endpoint result keys to be STALE.
|
|
561
|
+
* @see https://dataclient.io/docs/api/Controller#expireAll
|
|
562
|
+
* @returns Promise that resolves when expiry is commited. *NOT* fetch promise
|
|
563
|
+
*/
|
|
564
|
+
expireAll = options => this.dispatch(createExpireAll(key => options.testKey(key)));
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Resets the entire Reactive Data Client cache. All inflight requests will not resolve.
|
|
568
|
+
* @see https://dataclient.io/docs/api/Controller#resetEntireStore
|
|
569
|
+
*/
|
|
570
|
+
resetEntireStore = () => this.dispatch(createReset());
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Sets value for the Queryable and args.
|
|
574
|
+
* @see https://dataclient.io/docs/api/Controller#set
|
|
575
|
+
*/
|
|
576
|
+
|
|
577
|
+
set(schema, ...rest) {
|
|
578
|
+
const value = rest[rest.length - 1];
|
|
579
|
+
const action = createSet(schema, {
|
|
580
|
+
args: rest.slice(0, rest.length - 1),
|
|
581
|
+
value
|
|
582
|
+
});
|
|
583
|
+
// TODO: reject with error if this fails in reducer
|
|
584
|
+
return this.dispatch(action);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Sets response for the Endpoint and args.
|
|
589
|
+
* @see https://dataclient.io/docs/api/Controller#setResponse
|
|
590
|
+
*/
|
|
591
|
+
setResponse = (endpoint, ...rest) => {
|
|
592
|
+
const response = rest[rest.length - 1];
|
|
593
|
+
const action = createSetResponse(endpoint, {
|
|
594
|
+
args: rest.slice(0, rest.length - 1),
|
|
595
|
+
response
|
|
596
|
+
});
|
|
597
|
+
return this.dispatch(action);
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Sets an error response for the Endpoint and args.
|
|
602
|
+
* @see https://dataclient.io/docs/api/Controller#setError
|
|
603
|
+
*/
|
|
604
|
+
setError = (endpoint, ...rest) => {
|
|
605
|
+
const response = rest[rest.length - 1];
|
|
606
|
+
const action = createSetResponse(endpoint, {
|
|
607
|
+
args: rest.slice(0, rest.length - 1),
|
|
608
|
+
response,
|
|
609
|
+
error: true
|
|
610
|
+
});
|
|
611
|
+
return this.dispatch(action);
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Resolves an inflight fetch.
|
|
616
|
+
* @see https://dataclient.io/docs/api/Controller#resolve
|
|
617
|
+
*/
|
|
618
|
+
resolve = (endpoint, meta) => {
|
|
619
|
+
return this.dispatch(createSetResponse(endpoint, meta));
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Marks a new subscription to a given Endpoint.
|
|
624
|
+
* @see https://dataclient.io/docs/api/Controller#subscribe
|
|
625
|
+
*/
|
|
626
|
+
subscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createSubscription(endpoint, {
|
|
627
|
+
args: args
|
|
628
|
+
})) : Promise.resolve();
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Marks completion of subscription to a given Endpoint.
|
|
632
|
+
* @see https://dataclient.io/docs/api/Controller#unsubscribe
|
|
633
|
+
*/
|
|
634
|
+
unsubscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createUnsubscription(endpoint, {
|
|
635
|
+
args: args
|
|
636
|
+
})) : Promise.resolve();
|
|
637
|
+
|
|
638
|
+
/*************** More ***************/
|
|
639
|
+
|
|
640
|
+
/* TODO:
|
|
641
|
+
abort = <E extends EndpointInterface>(
|
|
642
|
+
endpoint: E,
|
|
643
|
+
...args: readonly [...Parameters<E>]
|
|
644
|
+
): Promise<void>
|
|
645
|
+
*/
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Gets a snapshot (https://dataclient.io/docs/api/Snapshot)
|
|
649
|
+
* @see https://dataclient.io/docs/api/Controller#snapshot
|
|
650
|
+
*/
|
|
651
|
+
snapshot = (state, fetchedAt) => {
|
|
652
|
+
return new Snapshot(this, state, fetchedAt);
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Gets the error, if any, for a given endpoint. Returns undefined for no errors.
|
|
657
|
+
* @see https://dataclient.io/docs/api/Controller#getError
|
|
658
|
+
*/
|
|
659
|
+
|
|
660
|
+
getError(endpoint, ...rest) {
|
|
661
|
+
if (rest[0] === null) return;
|
|
662
|
+
const state = rest[rest.length - 1];
|
|
663
|
+
// this is typescript generics breaking
|
|
664
|
+
const args = rest.slice(0, rest.length - 1);
|
|
665
|
+
const key = endpoint.key(...args);
|
|
666
|
+
const meta = selectMeta(state, key);
|
|
667
|
+
const error = state.endpoints[key];
|
|
668
|
+
if (error !== undefined && (meta == null ? void 0 : meta.errorPolicy) === 'soft') return;
|
|
669
|
+
return meta == null ? void 0 : meta.error;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Gets the (globally referentially stable) response for a given endpoint/args pair from state given.
|
|
674
|
+
* @see https://dataclient.io/docs/api/Controller#getResponse
|
|
675
|
+
*/
|
|
676
|
+
|
|
677
|
+
getResponse(endpoint, ...rest) {
|
|
678
|
+
// TODO: breaking: only return data
|
|
679
|
+
return this.getResponseMeta(endpoint, ...rest);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Gets the (globally referentially stable) response for a given endpoint/args pair from state given.
|
|
684
|
+
* @see https://dataclient.io/docs/api/Controller#getResponseMeta
|
|
685
|
+
*/
|
|
686
|
+
|
|
687
|
+
getResponseMeta(endpoint, ...rest) {
|
|
688
|
+
const state = rest[rest.length - 1];
|
|
689
|
+
// this is typescript generics breaking
|
|
690
|
+
const args = rest.slice(0, rest.length - 1)
|
|
691
|
+
// handle FormData
|
|
692
|
+
.map(ensurePojo);
|
|
693
|
+
const isActive = args.length !== 1 || args[0] !== null;
|
|
694
|
+
const key = isActive ? endpoint.key(...args) : '';
|
|
695
|
+
const cacheEndpoints = isActive ? state.endpoints[key] : undefined;
|
|
696
|
+
const schema = endpoint.schema;
|
|
697
|
+
const meta = selectMeta(state, key);
|
|
698
|
+
let expiresAt = meta == null ? void 0 : meta.expiresAt;
|
|
699
|
+
// if we have no endpoint entry, and our endpoint has a schema - try querying the store
|
|
700
|
+
const shouldQuery = cacheEndpoints === undefined && schema !== undefined;
|
|
701
|
+
const input = shouldQuery ?
|
|
702
|
+
// nothing in endpoints cache, so try querying if we have a schema to do so
|
|
703
|
+
this.memo.buildQueryKey(schema, args, state, key) : cacheEndpoints;
|
|
704
|
+
if (!isActive) {
|
|
705
|
+
// when not active simply return the query input without denormalizing
|
|
706
|
+
return {
|
|
707
|
+
data: input,
|
|
708
|
+
expiryStatus: normalizr.ExpiryStatus.Valid,
|
|
709
|
+
expiresAt: Infinity,
|
|
710
|
+
countRef: () => () => undefined
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
let isInvalid = false;
|
|
714
|
+
if (shouldQuery) {
|
|
715
|
+
isInvalid = !normalizr.validateQueryKey(input);
|
|
716
|
+
// endpoint without entities
|
|
717
|
+
} else if (!schema || !schemaHasEntity(schema)) {
|
|
718
|
+
return {
|
|
719
|
+
data: cacheEndpoints,
|
|
720
|
+
expiryStatus: this.getExpiryStatus(!cacheEndpoints, !!endpoint.invalidIfStale, meta),
|
|
721
|
+
expiresAt: expiresAt || 0,
|
|
722
|
+
countRef: this.gcPolicy.createCountRef({
|
|
723
|
+
key
|
|
724
|
+
})
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const {
|
|
728
|
+
data,
|
|
729
|
+
paths
|
|
730
|
+
} = this.memo.denormalize(schema, input, state.entities, args);
|
|
731
|
+
if (!expiresAt) {
|
|
732
|
+
// note: isInvalid can only be true if shouldQuery is true
|
|
733
|
+
if (isInvalid) expiresAt = 1;
|
|
734
|
+
// fallback to entity expiry time
|
|
735
|
+
else expiresAt = entityExpiresAt(paths, state.entitiesMeta);
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
data,
|
|
739
|
+
expiryStatus: this.getExpiryStatus(typeof data === 'symbol', !!endpoint.invalidIfStale || isInvalid, meta),
|
|
740
|
+
expiresAt,
|
|
741
|
+
countRef: this.gcPolicy.createCountRef({
|
|
742
|
+
key,
|
|
743
|
+
paths
|
|
744
|
+
})
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Queries the store for a Querable schema
|
|
750
|
+
* @see https://dataclient.io/docs/api/Controller#get
|
|
751
|
+
*/
|
|
752
|
+
get(schema, ...rest) {
|
|
753
|
+
const state = rest[rest.length - 1];
|
|
754
|
+
// this is typescript generics breaking
|
|
755
|
+
const args = rest.slice(0, rest.length - 1).map(ensurePojo);
|
|
756
|
+
const {
|
|
757
|
+
data
|
|
758
|
+
} = this.memo.query(schema, args, state);
|
|
759
|
+
return typeof data === 'symbol' ? undefined : data;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Queries the store for a Querable schema; providing related metadata
|
|
764
|
+
* @see https://dataclient.io/docs/api/Controller#getQueryMeta
|
|
765
|
+
*/
|
|
766
|
+
getQueryMeta(schema, ...rest) {
|
|
767
|
+
const state = rest[rest.length - 1];
|
|
768
|
+
// this is typescript generics breaking
|
|
769
|
+
const args = rest.slice(0, rest.length - 1).map(ensurePojo);
|
|
770
|
+
const {
|
|
771
|
+
data,
|
|
772
|
+
paths
|
|
773
|
+
} = this.memo.query(schema, args, state);
|
|
774
|
+
return {
|
|
775
|
+
data: typeof data === 'symbol' ? undefined : data,
|
|
776
|
+
countRef: this.gcPolicy.createCountRef({
|
|
777
|
+
paths
|
|
778
|
+
})
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
getExpiryStatus(invalidData, invalidIfStale, meta = {}) {
|
|
782
|
+
// https://dataclient.io/docs/concepts/expiry-policy#expiry-status
|
|
783
|
+
// we don't track the difference between stale or fresh because that is tied to triggering
|
|
784
|
+
// conditions
|
|
785
|
+
return meta.invalidated || invalidData && !meta.error ? normalizr.ExpiryStatus.Invalid : invalidData || invalidIfStale ? normalizr.ExpiryStatus.InvalidIfStale : normalizr.ExpiryStatus.Valid;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// benchmark: https://www.measurethat.net/Benchmarks/Show/24691/0/min-reducer-vs-imperative-with-paths
|
|
790
|
+
// earliest expiry dictates age
|
|
791
|
+
function entityExpiresAt(paths, entitiesMeta) {
|
|
792
|
+
let expiresAt = Infinity;
|
|
793
|
+
for (const {
|
|
794
|
+
key,
|
|
795
|
+
pk
|
|
796
|
+
} of paths) {
|
|
797
|
+
var _entitiesMeta$key;
|
|
798
|
+
const entityExpiry = (_entitiesMeta$key = entitiesMeta[key]) == null || (_entitiesMeta$key = _entitiesMeta$key[pk]) == null ? void 0 : _entitiesMeta$key.expiresAt;
|
|
799
|
+
// expiresAt will always resolve to false with any comparison
|
|
800
|
+
if (entityExpiry < expiresAt) expiresAt = entityExpiry;
|
|
801
|
+
}
|
|
802
|
+
return expiresAt;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Determine whether the schema has any entities.
|
|
806
|
+
*
|
|
807
|
+
* Without entities, denormalization is not needed, and results should not be queried.
|
|
808
|
+
*/
|
|
809
|
+
function schemaHasEntity(schema) {
|
|
810
|
+
if (normalizr.isEntity(schema)) return true;
|
|
811
|
+
if (Array.isArray(schema)) return schema.length !== 0 && schemaHasEntity(schema[0]);
|
|
812
|
+
if (schema && (typeof schema === 'object' || typeof schema === 'function')) {
|
|
813
|
+
const nestedSchema = 'schema' in schema ? schema.schema : schema;
|
|
814
|
+
if (typeof nestedSchema === 'function') {
|
|
815
|
+
return schemaHasEntity(nestedSchema);
|
|
816
|
+
}
|
|
817
|
+
return Object.values(nestedSchema).some(x => schemaHasEntity(x));
|
|
818
|
+
}
|
|
819
|
+
return false;
|
|
820
|
+
}
|
|
821
|
+
class Snapshot {
|
|
822
|
+
static abort = new AbortOptimistic();
|
|
823
|
+
state;
|
|
824
|
+
controller;
|
|
825
|
+
fetchedAt;
|
|
826
|
+
abort = Snapshot.abort;
|
|
827
|
+
constructor(controller, state, fetchedAt = 0) {
|
|
828
|
+
this.state = state;
|
|
829
|
+
this.controller = controller;
|
|
830
|
+
this.fetchedAt = fetchedAt;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/*************** Data Access ***************/
|
|
834
|
+
/** @see https://dataclient.io/docs/api/Snapshot#getResponse */
|
|
835
|
+
|
|
836
|
+
getResponse(endpoint, ...args) {
|
|
837
|
+
return this.controller.getResponse(endpoint, ...args, this.state);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/** @see https://dataclient.io/docs/api/Snapshot#getResponseMeta */
|
|
841
|
+
|
|
842
|
+
getResponseMeta(endpoint, ...args) {
|
|
843
|
+
return this.controller.getResponseMeta(endpoint, ...args, this.state);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** @see https://dataclient.io/docs/api/Snapshot#getError */
|
|
847
|
+
|
|
848
|
+
getError(endpoint, ...args) {
|
|
849
|
+
return this.controller.getError(endpoint, ...args, this.state);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Retrieved memoized value for any Querable schema
|
|
854
|
+
* @see https://dataclient.io/docs/api/Snapshot#get
|
|
855
|
+
*/
|
|
856
|
+
get(schema, ...args) {
|
|
857
|
+
return this.controller.get(schema, ...args, this.state);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Queries the store for a Querable schema; providing related metadata
|
|
862
|
+
* @see https://dataclient.io/docs/api/Snapshot#getQueryMeta
|
|
863
|
+
*/
|
|
864
|
+
getQueryMeta(schema, ...args) {
|
|
865
|
+
return this.controller.getQueryMeta(schema, ...args, this.state);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
var _DevToolsManager;
|
|
870
|
+
let DEFAULT_CONFIG = {};
|
|
871
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
872
|
+
var _globalThis$document;
|
|
873
|
+
const extraEndpointKeys = ['dataExpiryLength', 'errorExpiryLength', 'errorPolicy', 'invalidIfStale', 'pollFrequency', 'getOptimisticResponse', 'update'];
|
|
874
|
+
function serializeEndpoint(endpoint) {
|
|
875
|
+
var _toJSON, _endpoint$schema;
|
|
876
|
+
const serial = {
|
|
877
|
+
name: endpoint.name,
|
|
878
|
+
schema: (_toJSON = (_endpoint$schema = endpoint.schema) == null || _endpoint$schema.toJSON == null ? void 0 : _endpoint$schema.toJSON()) != null ? _toJSON : endpoint.schema,
|
|
879
|
+
sideEffect: endpoint.sideEffect
|
|
880
|
+
};
|
|
881
|
+
extraEndpointKeys.forEach(key => {
|
|
882
|
+
if (key in endpoint) serial[key] = endpoint[key];
|
|
883
|
+
});
|
|
884
|
+
return serial;
|
|
885
|
+
}
|
|
886
|
+
const HASINTL = typeof Intl !== 'undefined';
|
|
887
|
+
DEFAULT_CONFIG = {
|
|
888
|
+
name: `Data Client: ${(_globalThis$document = globalThis.document) == null ? void 0 : _globalThis$document.title}`,
|
|
889
|
+
autoPause: true,
|
|
890
|
+
features: {
|
|
891
|
+
pause: true,
|
|
892
|
+
// start/pause recording of dispatched actions
|
|
893
|
+
lock: true,
|
|
894
|
+
// lock/unlock dispatching actions and side effects
|
|
895
|
+
persist: false,
|
|
896
|
+
// persist states on page reloading
|
|
897
|
+
export: true,
|
|
898
|
+
// export history of actions in a file
|
|
899
|
+
import: 'custom',
|
|
900
|
+
// import history of actions from a file
|
|
901
|
+
jump: true,
|
|
902
|
+
// jump back and forth (time travelling)
|
|
903
|
+
skip: true,
|
|
904
|
+
// skip (cancel) actions
|
|
905
|
+
reorder: true,
|
|
906
|
+
// drag and drop actions in the history list
|
|
907
|
+
dispatch: false,
|
|
908
|
+
// dispatch custom actions or action creators
|
|
909
|
+
test: false // generate tests for the selected actions
|
|
910
|
+
},
|
|
911
|
+
actionSanitizer: action => {
|
|
912
|
+
if (!('endpoint' in action)) return action;
|
|
913
|
+
return {
|
|
914
|
+
...action,
|
|
915
|
+
endpoint: serializeEndpoint(action.endpoint)
|
|
916
|
+
};
|
|
917
|
+
},
|
|
918
|
+
serialize: {
|
|
919
|
+
options: undefined,
|
|
920
|
+
/* istanbul ignore next */
|
|
921
|
+
replacer: HASINTL ? (key, value) => {
|
|
922
|
+
if (typeof value === 'number' && typeof key === 'string' && isFinite(value) && (key === 'date' || key.endsWith('At'))) {
|
|
923
|
+
return Intl.DateTimeFormat('en-US', {
|
|
924
|
+
hour: 'numeric',
|
|
925
|
+
minute: 'numeric',
|
|
926
|
+
second: 'numeric',
|
|
927
|
+
fractionalSecondDigits: 3
|
|
928
|
+
}).format(value);
|
|
929
|
+
}
|
|
930
|
+
return value;
|
|
931
|
+
} : undefined
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/** Integrates with https://github.com/reduxjs/redux-devtools
|
|
937
|
+
*
|
|
938
|
+
* Options: https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md
|
|
939
|
+
*
|
|
940
|
+
* @see https://dataclient.io/docs/api/DevToolsManager
|
|
941
|
+
*/
|
|
942
|
+
class DevToolsManager {
|
|
943
|
+
constructor(config, skipLogging) {
|
|
944
|
+
this.started = false;
|
|
945
|
+
this.actions = [];
|
|
946
|
+
this.maxBufferLength = 100;
|
|
947
|
+
/* istanbul ignore next */
|
|
948
|
+
this.devTools = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__.connect({
|
|
949
|
+
...DEFAULT_CONFIG,
|
|
950
|
+
...config
|
|
951
|
+
});
|
|
952
|
+
// we cut it in half so we should double so we don't lose
|
|
953
|
+
if (config != null && config.maxAge) this.maxBufferLength = config.maxAge * 2;
|
|
954
|
+
if (skipLogging) this.skipLogging = skipLogging;
|
|
955
|
+
}
|
|
956
|
+
handleAction(action, state) {
|
|
957
|
+
if (this.started) {
|
|
958
|
+
this.devTools.send(action, state);
|
|
959
|
+
} else {
|
|
960
|
+
// avoid this getting too big in case this is long running
|
|
961
|
+
// we cut in half so we aren't constantly reallocating
|
|
962
|
+
if (this.actions.length > this.maxBufferLength) this.actions = this.actions.slice(this.maxBufferLength / 2);
|
|
963
|
+
// queue actions
|
|
964
|
+
this.actions.push([action, state]);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** Called when initial state is ready */
|
|
969
|
+
init(state) {
|
|
970
|
+
if (process.env.NODE_ENV !== 'production' && this.devTools) {
|
|
971
|
+
this.devTools.init(state);
|
|
972
|
+
this.devTools.subscribe(msg => {
|
|
973
|
+
switch (msg.type) {
|
|
974
|
+
case 'START':
|
|
975
|
+
this.started = true;
|
|
976
|
+
if (this.actions.length) {
|
|
977
|
+
this.actions.forEach(([action, state]) => {
|
|
978
|
+
this.handleAction(action, state);
|
|
979
|
+
});
|
|
980
|
+
this.actions = [];
|
|
981
|
+
}
|
|
982
|
+
break;
|
|
983
|
+
case 'STOP':
|
|
984
|
+
this.started = false;
|
|
985
|
+
break;
|
|
986
|
+
case 'DISPATCH':
|
|
987
|
+
if (msg.payload.type === 'RESET') {
|
|
988
|
+
this.controller.resetEntireStore();
|
|
989
|
+
}
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/** Ensures all subscriptions are cleaned up. */
|
|
997
|
+
cleanup() {}
|
|
998
|
+
}
|
|
999
|
+
_DevToolsManager = DevToolsManager;
|
|
1000
|
+
(() => {
|
|
1001
|
+
/* istanbul ignore if */
|
|
1002
|
+
/* istanbul ignore next */
|
|
1003
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1004
|
+
_DevToolsManager.prototype.middleware = function (controller) {
|
|
1005
|
+
if (!this.devTools) return next => action => next(action);
|
|
1006
|
+
this.controller = controller;
|
|
1007
|
+
const reducer = createReducer(controller);
|
|
1008
|
+
let state = controller.getState();
|
|
1009
|
+
return next => action => {
|
|
1010
|
+
var _this$skipLogging;
|
|
1011
|
+
const shouldSkip = (_this$skipLogging = this.skipLogging) == null ? void 0 : _this$skipLogging.call(this, action);
|
|
1012
|
+
const ret = next(action);
|
|
1013
|
+
if (this.started) {
|
|
1014
|
+
// we track state changes here since getState() will only update after a batch commit
|
|
1015
|
+
state = reducer(state, action);
|
|
1016
|
+
} else {
|
|
1017
|
+
state = controller.getState();
|
|
1018
|
+
}
|
|
1019
|
+
ret.then(() => {
|
|
1020
|
+
if (shouldSkip) return;
|
|
1021
|
+
this.handleAction(action, state.optimistic.reduce(reducer, state));
|
|
1022
|
+
});
|
|
1023
|
+
return ret;
|
|
1024
|
+
};
|
|
1025
|
+
};
|
|
1026
|
+
} else {
|
|
1027
|
+
_DevToolsManager.prototype.middleware = () => next => action => next(action);
|
|
1028
|
+
}
|
|
1029
|
+
})();
|
|
1030
|
+
|
|
1031
|
+
async function collapseFixture(fixture, args, interceptorData) {
|
|
1032
|
+
let error = 'error' in fixture ? fixture.error : false;
|
|
1033
|
+
let response = fixture.response;
|
|
1034
|
+
if (typeof fixture.response === 'function') {
|
|
1035
|
+
try {
|
|
1036
|
+
response = await fixture.response.apply(interceptorData, args);
|
|
1037
|
+
// dispatch goes through user-code that can sometimes fail.
|
|
1038
|
+
// let's ensure we always handle errors
|
|
1039
|
+
} catch (e) {
|
|
1040
|
+
response = e;
|
|
1041
|
+
error = true;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
response,
|
|
1046
|
+
error
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function createFixtureMap(fixtures = []) {
|
|
1051
|
+
const map = new Map();
|
|
1052
|
+
const computed = [];
|
|
1053
|
+
for (const fixture of fixtures) {
|
|
1054
|
+
if ('args' in fixture) {
|
|
1055
|
+
if (typeof fixture.response !== 'function') {
|
|
1056
|
+
const key = fixture.endpoint.key(...fixture.args);
|
|
1057
|
+
map.set(key, fixture);
|
|
1058
|
+
} else {
|
|
1059
|
+
// this has to be a typo. probably needs to remove args
|
|
1060
|
+
console.warn(`Fixture found with function response, and explicit args. Interceptors should not specify args.
|
|
1061
|
+
${fixture.endpoint.name}: ${JSON.stringify(fixture.args)}
|
|
1062
|
+
|
|
1063
|
+
Treating as Interceptor`);
|
|
1064
|
+
computed.push(fixture);
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
computed.push(fixture);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return [map, computed];
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
function MockController(Base, {
|
|
1074
|
+
fixtures = [],
|
|
1075
|
+
getInitialInterceptorData = () => ({})
|
|
1076
|
+
}) {
|
|
1077
|
+
const [fixtureMap, interceptors] = createFixtureMap(fixtures);
|
|
1078
|
+
|
|
1079
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1080
|
+
// @ts-ignore
|
|
1081
|
+
return class MockedController extends Base {
|
|
1082
|
+
// legacy compatibility (re-declaration)
|
|
1083
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
1084
|
+
|
|
1085
|
+
fixtureMap = fixtureMap;
|
|
1086
|
+
interceptors = interceptors;
|
|
1087
|
+
interceptorData = getInitialInterceptorData();
|
|
1088
|
+
constructor(...args) {
|
|
1089
|
+
super(...args);
|
|
1090
|
+
|
|
1091
|
+
// legacy compatibility
|
|
1092
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
1093
|
+
if (!this._dispatch) {
|
|
1094
|
+
this._dispatch = args[0].dispatch;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// legacy compatibility - we need this to work with 0.14 and below as they do not have this setter
|
|
1099
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
1100
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1101
|
+
// @ts-ignore
|
|
1102
|
+
set dispatch(dispatch) {
|
|
1103
|
+
this._dispatch = dispatch;
|
|
1104
|
+
}
|
|
1105
|
+
get dispatch() {
|
|
1106
|
+
return action => {
|
|
1107
|
+
var _actionTypes$FETCH;
|
|
1108
|
+
// support legacy that has _TYPE suffix
|
|
1109
|
+
if (action.type === ((_actionTypes$FETCH = FETCH) != null ? _actionTypes$FETCH : FETCH_TYPE)) {
|
|
1110
|
+
// eslint-disable-next-line prefer-const
|
|
1111
|
+
let {
|
|
1112
|
+
key,
|
|
1113
|
+
args
|
|
1114
|
+
} = action;
|
|
1115
|
+
let fixture;
|
|
1116
|
+
if (this.fixtureMap.has(key)) {
|
|
1117
|
+
fixture = this.fixtureMap.get(key);
|
|
1118
|
+
if (!args) args = fixture.args;
|
|
1119
|
+
// exact matches take priority; now test ComputedFixture
|
|
1120
|
+
} else {
|
|
1121
|
+
for (const cfix of this.interceptors) {
|
|
1122
|
+
if (cfix.endpoint.testKey(key)) {
|
|
1123
|
+
fixture = cfix;
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
// we have a match
|
|
1129
|
+
if (fixture !== undefined) {
|
|
1130
|
+
var _fixture$delay;
|
|
1131
|
+
const replacedAction = {
|
|
1132
|
+
...action
|
|
1133
|
+
};
|
|
1134
|
+
const delayMs = typeof fixture.delay === 'function' ? fixture.delay(...args) : (_fixture$delay = fixture.delay) != null ? _fixture$delay : 0;
|
|
1135
|
+
if ('fetchResponse' in fixture) {
|
|
1136
|
+
const {
|
|
1137
|
+
fetchResponse
|
|
1138
|
+
} = fixture;
|
|
1139
|
+
fixture = {
|
|
1140
|
+
endpoint: fixture.endpoint,
|
|
1141
|
+
response(...args) {
|
|
1142
|
+
const endpoint = action.endpoint.extend({
|
|
1143
|
+
fetchResponse: (input, init) => {
|
|
1144
|
+
const ret = fetchResponse.call(this, input, init);
|
|
1145
|
+
return Promise.resolve(new Response(JSON.stringify(ret), {
|
|
1146
|
+
status: 200,
|
|
1147
|
+
headers: new Headers({
|
|
1148
|
+
'Content-Type': 'application/json'
|
|
1149
|
+
})
|
|
1150
|
+
}));
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
return endpoint(...args);
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
const fetch = async () => {
|
|
1158
|
+
if (!fixture) {
|
|
1159
|
+
throw new Error('No fixture found');
|
|
1160
|
+
}
|
|
1161
|
+
// delayCollapse determines when the fixture function is 'collapsed' (aka 'run')
|
|
1162
|
+
// collapsed: https://en.wikipedia.org/wiki/Copenhagen_interpretation
|
|
1163
|
+
if (fixture.delayCollapse) {
|
|
1164
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
1165
|
+
}
|
|
1166
|
+
const result = await collapseFixture(fixture, args, this.interceptorData);
|
|
1167
|
+
if (!fixture.delayCollapse && delayMs) {
|
|
1168
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
1169
|
+
}
|
|
1170
|
+
if (result.error) {
|
|
1171
|
+
throw result.response;
|
|
1172
|
+
}
|
|
1173
|
+
return result.response;
|
|
1174
|
+
};
|
|
1175
|
+
if (typeof replacedAction.endpoint.extend === 'function') {
|
|
1176
|
+
replacedAction.endpoint = replacedAction.endpoint.extend({
|
|
1177
|
+
fetch
|
|
1178
|
+
});
|
|
1179
|
+
} else {
|
|
1180
|
+
// TODO: full testing of this
|
|
1181
|
+
replacedAction.endpoint = fetch;
|
|
1182
|
+
replacedAction.endpoint.__proto__ = action.endpoint;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// TODO: make super.dispatch (once we drop support for destructuring)
|
|
1186
|
+
return this._dispatch(replacedAction);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
// TODO: make super.dispatch (once we drop support for destructuring)
|
|
1190
|
+
return this._dispatch(action);
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
const {
|
|
1197
|
+
initialState
|
|
1198
|
+
} = __INTERNAL__;
|
|
1199
|
+
function mockInitialState(fixtures = []) {
|
|
1200
|
+
const actions = [];
|
|
1201
|
+
const dispatch = action => {
|
|
1202
|
+
actions.push(action);
|
|
1203
|
+
return Promise.resolve();
|
|
1204
|
+
};
|
|
1205
|
+
const controller = new Controller({
|
|
1206
|
+
dispatch
|
|
1207
|
+
});
|
|
1208
|
+
const reducer = createReducer(controller);
|
|
1209
|
+
fixtures.forEach(fixture => {
|
|
1210
|
+
dispatchFixture(fixture, fixture.args, controller);
|
|
1211
|
+
});
|
|
1212
|
+
return actions.reduce(reducer, initialState);
|
|
1213
|
+
}
|
|
1214
|
+
function dispatchFixture(fixture, args, controller, fetchedAt) {
|
|
1215
|
+
// eslint-disable-next-line prefer-const
|
|
1216
|
+
let {
|
|
1217
|
+
endpoint
|
|
1218
|
+
} = fixture;
|
|
1219
|
+
const {
|
|
1220
|
+
response,
|
|
1221
|
+
error
|
|
1222
|
+
} = fixture;
|
|
1223
|
+
if (controller.resolve) {
|
|
1224
|
+
controller.resolve(endpoint, {
|
|
1225
|
+
args,
|
|
1226
|
+
response,
|
|
1227
|
+
error,
|
|
1228
|
+
fetchedAt: Date.now()
|
|
1229
|
+
});
|
|
1230
|
+
} else {
|
|
1231
|
+
if (error === true) {
|
|
1232
|
+
controller.setError(endpoint, ...args, response);
|
|
1233
|
+
} else {
|
|
1234
|
+
controller.setResponse(endpoint, ...args, response);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
exports.MockController = MockController;
|
|
1240
|
+
exports.collapseFixture = collapseFixture;
|
|
1241
|
+
exports.createFixtureMap = createFixtureMap;
|
|
1242
|
+
exports.mockInitialState = mockInitialState;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-client/core",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"./mock": {
|
|
58
58
|
"types": "./lib/mock/index.d.ts",
|
|
59
|
+
"require": "./dist/mock.js",
|
|
59
60
|
"default": "./lib/mock/index.js"
|
|
60
61
|
},
|
|
61
62
|
"./package.json": "./package.json"
|
|
@@ -72,9 +73,7 @@
|
|
|
72
73
|
"node.mjs",
|
|
73
74
|
"legacy",
|
|
74
75
|
"LICENSE",
|
|
75
|
-
"README.md"
|
|
76
|
-
"./typescript.svg",
|
|
77
|
-
"./data_client_logo_and_text.svg"
|
|
76
|
+
"README.md"
|
|
78
77
|
],
|
|
79
78
|
"scripts": {
|
|
80
79
|
"build:lib": "NODE_ENV=production BROWSERSLIST_ENV='2020' POLYFILL_TARGETS='chrome>88,safari>14' yarn g:babel --out-dir lib",
|
|
@@ -140,7 +139,7 @@
|
|
|
140
139
|
},
|
|
141
140
|
"devDependencies": {
|
|
142
141
|
"@anansi/browserslist-config": "^1.4.2",
|
|
143
|
-
"@data-client/endpoint": "0.15.
|
|
142
|
+
"@data-client/endpoint": "0.15.2",
|
|
144
143
|
"@types/jest": "30.0.0",
|
|
145
144
|
"@types/node": "^24.0.0",
|
|
146
145
|
"rollup-plugins": "1.0.0"
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
-
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
3
|
-
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
4
|
-
viewBox="69 220 1136 205" height="100" xml:space="preserve">
|
|
5
|
-
<style type="text/css">
|
|
6
|
-
.st0{fill:current;}
|
|
7
|
-
.st1{fill:#3E96DB;}
|
|
8
|
-
</style>
|
|
9
|
-
<g>
|
|
10
|
-
<g>
|
|
11
|
-
<g>
|
|
12
|
-
<path class="st0" d="M448.61,325.58l5.27,10.28h-11.52l-5.27-10.28l-5.51-10.78l-0.25-0.49c-1.81-2.96-5.02-4.94-8.72-4.94
|
|
13
|
-
h-10.28v26.49h-10.28v-57.59h36.37c4.28,0,8.15,1.73,10.94,4.53c2.8,2.88,4.53,6.75,4.53,11.02c0,6.25-3.7,11.68-9.05,14.15
|
|
14
|
-
c-1.4,0.66-2.88,1.07-4.44,1.23L448.61,325.58z M438.41,299.09c0.74,0,1.48-0.16,2.14-0.49c1.89-0.82,3.13-2.72,3.13-4.77
|
|
15
|
-
c0-1.89-0.99-3.21-1.56-3.78c-0.66-0.66-1.89-1.48-3.7-1.48h-26.08v10.61L438.41,299.09z"/>
|
|
16
|
-
<path class="st0" d="M518.13,293.74v5.35h37.02v10.28h-37.02v10.86c0,2.88,2.39,5.27,5.27,5.27h42.04v10.28H523.4
|
|
17
|
-
c-8.56,0-15.47-6.99-15.47-15.55v-26.49c0-8.56,6.91-15.55,15.47-15.55h42.04v10.28H523.4
|
|
18
|
-
C520.52,288.48,518.13,290.86,518.13,293.74z"/>
|
|
19
|
-
<path class="st0" d="M674.79,335.78h-11.52L658,325.5l-12.59-24.68l-12.59,24.68l-5.27,10.28h-11.52l5.27-10.28l24.11-47.31
|
|
20
|
-
l24.11,47.31L674.79,335.78z"/>
|
|
21
|
-
<path class="st0" d="M727.61,306.99c0,10.2,8.31,18.51,18.51,18.51h28.8v10.28h-28.8c-15.88,0-28.8-12.92-28.8-28.8
|
|
22
|
-
s12.92-28.8,28.8-28.8h28.8v10.28h-28.8C735.92,288.48,727.61,296.79,727.61,306.99z"/>
|
|
23
|
-
<path class="st0" d="M886.32,278.27v10.28h-23.61v47.31h-10.28v-47.31h-23.61v-10.28H886.32z"/>
|
|
24
|
-
<path class="st0" d="M940.3,335.78v-57.59h10.28v57.59H940.3z"/>
|
|
25
|
-
<path class="st0" d="M1059.85,278.19l-5.27,10.28l-24.11,47.31l-24.11-47.31l-5.27-10.28h11.52l5.27,10.28l12.59,24.68
|
|
26
|
-
l12.59-24.68l5.27-10.28H1059.85z"/>
|
|
27
|
-
<path class="st0" d="M1117.19,293.74v5.35h37.02v10.28h-37.02v10.86c0,2.88,2.39,5.27,5.27,5.27h42.04v10.28h-42.04
|
|
28
|
-
c-8.56,0-15.47-6.99-15.47-15.55v-26.49c0-8.56,6.91-15.55,15.47-15.55h42.04v10.28h-42.04
|
|
29
|
-
C1119.58,288.48,1117.19,290.86,1117.19,293.74z"/>
|
|
30
|
-
</g>
|
|
31
|
-
<g>
|
|
32
|
-
<path class="st0" d="M562.83,376.16h9.02c6.88,0,11.55,4.34,11.55,10.7c0,6.36-4.68,10.7-11.55,10.7h-9.02V376.16z M571.67,394.9
|
|
33
|
-
c5.29,0,8.68-3.24,8.68-8.04c0-4.8-3.39-8.04-8.68-8.04h-5.78v16.08H571.67z"/>
|
|
34
|
-
<path class="st0" d="M625.16,392.21h-11.37l-2.35,5.35h-3.15l9.69-21.4H621l9.72,21.4h-3.21L625.16,392.21z M624.09,389.76
|
|
35
|
-
l-4.62-10.48l-4.62,10.48H624.09z"/>
|
|
36
|
-
<path class="st0" d="M660.83,378.82h-7.34v-2.66h17.7v2.66h-7.34v18.74h-3.03V378.82z"/>
|
|
37
|
-
<path class="st0" d="M710.84,392.21h-11.37l-2.35,5.35h-3.15l9.69-21.4h3.03l9.72,21.4h-3.21L710.84,392.21z M709.77,389.76
|
|
38
|
-
l-4.62-10.48l-4.62,10.48H709.77z"/>
|
|
39
|
-
<path class="st0" d="M773.57,386.86c0-6.33,4.83-10.94,11.34-10.94c3.3,0,6.17,1.13,8.13,3.33l-1.99,1.93
|
|
40
|
-
c-1.65-1.74-3.67-2.54-6.02-2.54c-4.83,0-8.41,3.48-8.41,8.22c0,4.74,3.58,8.22,8.41,8.22c2.35,0,4.37-0.83,6.02-2.57l1.99,1.93
|
|
41
|
-
c-1.96,2.2-4.83,3.36-8.16,3.36C778.4,397.8,773.57,393.19,773.57,386.86z"/>
|
|
42
|
-
<path class="st0" d="M821.16,376.16h3.06v18.74h11.59v2.66h-14.64V376.16z"/>
|
|
43
|
-
<path class="st0" d="M863.07,376.16h3.06v21.4h-3.06V376.16z"/>
|
|
44
|
-
<path class="st0" d="M911.82,394.9v2.66h-15.53v-21.4h15.1v2.66h-12.04v6.57h10.73v2.6h-10.73v6.91H911.82z"/>
|
|
45
|
-
<path class="st0" d="M958.93,376.16v21.4h-2.51l-12.84-15.96v15.96h-3.06v-21.4h2.51l12.84,15.96v-15.96H958.93z"/>
|
|
46
|
-
<path class="st0" d="M993.35,378.82h-7.34v-2.66h17.7v2.66h-7.34v18.74h-3.03V378.82z"/>
|
|
47
|
-
</g>
|
|
48
|
-
</g>
|
|
49
|
-
<g>
|
|
50
|
-
<path class="st1" d="M275.11,347.41c-21.84,54.68-67.33,87.9-101.61,74.21c-1.86-0.74-3.65-1.61-5.36-2.59
|
|
51
|
-
c31.37,7.82,68.88-18.56,86.22-61.96c18.43-46.13,7.09-94.02-25.32-106.97c-19.2-7.67-41.49-1.36-60.3,14.65
|
|
52
|
-
c23.82-34.07,57.23-51.76,83.86-41.13C286.87,237.31,296.95,292.74,275.11,347.41z"/>
|
|
53
|
-
<path class="st1" d="M215.54,391.87c-67.76-21.72-114.76-64.06-104.98-94.57c0.53-1.66,1.22-3.24,2.06-4.76
|
|
54
|
-
c-3.46,28.09,34.11,62.97,87.9,80.22c57.17,18.33,111.01,9.8,120.25-19.05c5.48-17.09-5.92-37.48-28-55.07
|
|
55
|
-
c44.07,22.77,70.76,53.62,63.16,77.33C346.16,406.48,283.3,413.6,215.54,391.87z"/>
|
|
56
|
-
<path class="st1" d="M161.63,326.13c37.03-27.02,76.92-35.4,89.08-18.72c0.66,0.91,1.22,1.86,1.7,2.88
|
|
57
|
-
c-13.31-12.47-45.72-6.16-75.13,15.29c-31.25,22.79-47.25,54.05-35.75,69.82c6.81,9.34,21.75,11.15,39.29,6.34
|
|
58
|
-
c-28.58,14.29-54.78,16.46-64.23,3.51C104.44,388.57,124.6,353.15,161.63,326.13z"/>
|
|
59
|
-
<circle class="st1" cx="212.69" cy="340.87" r="11.83"/>
|
|
60
|
-
<circle class="st1" cx="143.77" cy="298.83" r="11.83"/>
|
|
61
|
-
<circle class="st1" cx="318.02" cy="274.77" r="11.83"/>
|
|
62
|
-
</g>
|
|
63
|
-
</g>
|
|
64
|
-
</svg>
|
package/typescript.svg
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg width="21px" height="21px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
|
3
|
-
<g>
|
|
4
|
-
<polygon fill="#007ACC" transform="translate(128.000000, 128.000000) scale(1, -1) translate(-128.000000, -128.000000) " points="0 128 0 0 128 0 256 0 256 128 256 256 128 256 0 256"></polygon>
|
|
5
|
-
<path d="M146.658132,223.436863 L146.739401,212.953054 L130.079084,212.953054 L113.418767,212.953054 L113.418767,165.613371 L113.418767,118.273689 L101.63464,118.273689 L89.8505126,118.273689 L89.8505126,165.613371 L89.8505126,212.953054 L73.1901951,212.953054 L56.5298776,212.953054 L56.5298776,223.233689 C56.5298776,228.922577 56.6517824,233.676863 56.8143221,233.798768 C56.9362269,233.961308 77.2130522,234.042577 101.797179,234.001943 L146.536227,233.880038 L146.658132,223.436863 Z" fill="#FFFFFF" transform="translate(101.634640, 176.142993) rotate(-180.000000) translate(-101.634640, -176.142993) "></path>
|
|
6
|
-
<path d="M206.566631,234.272145 C213.068219,232.646748 218.025679,229.761668 222.57679,225.048018 C224.933616,222.528653 228.428219,217.936907 228.712663,216.839764 C228.793933,216.514684 217.659965,209.037859 210.914568,204.852462 C210.670758,204.689922 209.69552,205.74643 208.598377,207.371827 C205.306949,212.166748 201.852981,214.239129 196.570441,214.604843 C188.809171,215.133097 183.811076,211.069605 183.851711,204.283573 C183.851711,202.292462 184.136155,201.114049 184.948854,199.488653 C186.65552,195.953414 189.825044,193.840399 199.7806,189.533097 C218.106949,181.649922 225.949489,176.448653 230.825679,169.053097 C236.270758,160.804208 237.489806,147.638494 233.792028,137.845478 C229.728536,127.199129 219.651076,119.966113 205.469489,117.568653 C201.080917,116.796589 190.678377,116.918494 185.964727,117.771827 C175.684092,119.600399 165.931711,124.679764 159.917743,131.343891 C157.560917,133.944526 152.969171,140.730557 153.253616,141.218176 C153.37552,141.380716 154.432028,142.030875 155.610441,142.721668 C156.748219,143.371827 161.05552,145.850557 165.119012,148.207383 L172.473933,152.474049 L174.01806,150.198494 C176.171711,146.907065 180.885362,142.396589 183.729806,140.893097 C191.897425,136.585795 203.112663,137.195319 208.639012,142.15278 C210.995838,144.30643 211.971076,146.541351 211.971076,149.83278 C211.971076,152.799129 211.605362,154.099446 210.061235,156.334367 C208.070123,159.178811 204.006631,161.576272 192.466314,166.574367 C179.259965,172.263256 173.571076,175.798494 168.369806,181.406113 C165.362822,184.656907 162.518377,189.858176 161.339965,194.206113 C160.364727,197.822621 160.120917,206.884208 160.892981,210.541351 C163.61552,223.300716 173.245996,232.199764 187.143139,234.841034 C191.653616,235.694367 202.137425,235.369287 206.566631,234.272145 Z" fill="#FFFFFF" transform="translate(194.578507, 176.190240) scale(1, -1) translate(-194.578507, -176.190240) "></path>
|
|
7
|
-
</g>
|
|
8
|
-
</svg>
|