dreaction-client-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client-options.d.ts +77 -0
- package/lib/client-options.d.ts.map +1 -0
- package/lib/client-options.js +2 -0
- package/lib/index.d.ts +190 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +401 -0
- package/lib/plugins/api-response.d.ts +13 -0
- package/lib/plugins/api-response.d.ts.map +1 -0
- package/lib/plugins/api-response.js +23 -0
- package/lib/plugins/benchmark.d.ts +15 -0
- package/lib/plugins/benchmark.d.ts.map +1 -0
- package/lib/plugins/benchmark.js +31 -0
- package/lib/plugins/clear.d.ts +11 -0
- package/lib/plugins/clear.d.ts.map +1 -0
- package/lib/plugins/clear.js +13 -0
- package/lib/plugins/image.d.ts +19 -0
- package/lib/plugins/image.d.ts.map +1 -0
- package/lib/plugins/image.js +24 -0
- package/lib/plugins/logger.d.ts +18 -0
- package/lib/plugins/logger.d.ts.map +1 -0
- package/lib/plugins/logger.js +44 -0
- package/lib/plugins/repl.d.ts +10 -0
- package/lib/plugins/repl.d.ts.map +1 -0
- package/lib/plugins/repl.js +55 -0
- package/lib/plugins/state-responses.d.ts +20 -0
- package/lib/plugins/state-responses.d.ts.map +1 -0
- package/lib/plugins/state-responses.js +38 -0
- package/lib/reactotron-core-client.d.ts +191 -0
- package/lib/reactotron-core-client.d.ts.map +1 -0
- package/lib/reactotron-core-client.js +400 -0
- package/lib/serialize.d.ts +20 -0
- package/lib/serialize.d.ts.map +1 -0
- package/lib/serialize.js +112 -0
- package/lib/stopwatch.d.ts +6 -0
- package/lib/stopwatch.d.ts.map +1 -0
- package/lib/stopwatch.js +45 -0
- package/lib/validate.d.ts +9 -0
- package/lib/validate.d.ts.map +1 -0
- package/lib/validate.js +26 -0
- package/package.json +29 -0
- package/src/client-options.ts +95 -0
- package/src/index.ts +654 -0
- package/src/plugins/api-response.ts +32 -0
- package/src/plugins/benchmark.ts +35 -0
- package/src/plugins/clear.ts +14 -0
- package/src/plugins/image.ts +34 -0
- package/src/plugins/logger.ts +59 -0
- package/src/plugins/repl.ts +63 -0
- package/src/plugins/state-responses.ts +75 -0
- package/src/serialize.ts +125 -0
- package/src/stopwatch.ts +50 -0
- package/src/validate.ts +38 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DReactionCore, Plugin } from '../';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clears the reactotron server.
|
|
5
|
+
*/
|
|
6
|
+
const clear = () => (reactotron: DReactionCore) => {
|
|
7
|
+
return {
|
|
8
|
+
features: {
|
|
9
|
+
clear: () => reactotron.send('clear'),
|
|
10
|
+
},
|
|
11
|
+
} satisfies Plugin<DReactionCore>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default clear;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DReactionCore, Plugin } from '../';
|
|
2
|
+
|
|
3
|
+
export interface ImagePayload {
|
|
4
|
+
uri: string;
|
|
5
|
+
preview: string;
|
|
6
|
+
caption?: string;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
filename?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Provides an image.
|
|
14
|
+
*/
|
|
15
|
+
const image = () => (reactotron: DReactionCore) => {
|
|
16
|
+
return {
|
|
17
|
+
features: {
|
|
18
|
+
// expanded just to show the specs
|
|
19
|
+
image: (payload: ImagePayload) => {
|
|
20
|
+
const { uri, preview, filename, width, height, caption } = payload;
|
|
21
|
+
return reactotron.send('image', {
|
|
22
|
+
uri,
|
|
23
|
+
preview,
|
|
24
|
+
filename,
|
|
25
|
+
width,
|
|
26
|
+
height,
|
|
27
|
+
caption,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
} satisfies Plugin<DReactionCore>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default image;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DReactionCore, Plugin, InferFeatures } from '../';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provides 4 features for logging. log & debug are the same.
|
|
5
|
+
*/
|
|
6
|
+
const logger = () => (dreaction: DReactionCore) => {
|
|
7
|
+
return {
|
|
8
|
+
features: {
|
|
9
|
+
log: (...args) => {
|
|
10
|
+
const content = args && args.length === 1 ? args[0] : args;
|
|
11
|
+
dreaction.send('log', { level: 'debug', message: content }, false);
|
|
12
|
+
},
|
|
13
|
+
logImportant: (...args) => {
|
|
14
|
+
const content = args && args.length === 1 ? args[0] : args;
|
|
15
|
+
dreaction.send('log', { level: 'debug', message: content }, true);
|
|
16
|
+
},
|
|
17
|
+
debug: (message, important = false) =>
|
|
18
|
+
dreaction.send('log', { level: 'debug', message }, !!important),
|
|
19
|
+
warn: (message) =>
|
|
20
|
+
dreaction.send('log', { level: 'warn', message }, true),
|
|
21
|
+
error: (message, stack) =>
|
|
22
|
+
dreaction.send('log', { level: 'error', message, stack }, true),
|
|
23
|
+
},
|
|
24
|
+
} satisfies Plugin<DReactionCore>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default logger;
|
|
28
|
+
|
|
29
|
+
export type LoggerPlugin = ReturnType<typeof logger>;
|
|
30
|
+
|
|
31
|
+
export const hasLoggerPlugin = (
|
|
32
|
+
dreaction: DReactionCore
|
|
33
|
+
): dreaction is DReactionCore &
|
|
34
|
+
InferFeatures<DReactionCore, ReturnType<typeof logger>> => {
|
|
35
|
+
return (
|
|
36
|
+
dreaction &&
|
|
37
|
+
'log' in dreaction &&
|
|
38
|
+
typeof dreaction.log === 'function' &&
|
|
39
|
+
'logImportant' in dreaction &&
|
|
40
|
+
typeof dreaction.logImportant === 'function' &&
|
|
41
|
+
'debug' in dreaction &&
|
|
42
|
+
typeof dreaction.debug === 'function' &&
|
|
43
|
+
'warn' in dreaction &&
|
|
44
|
+
typeof dreaction.warn === 'function' &&
|
|
45
|
+
'error' in dreaction &&
|
|
46
|
+
typeof dreaction.error === 'function'
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const assertHasLoggerPlugin = (
|
|
51
|
+
dreaction: DReactionCore
|
|
52
|
+
): asserts dreaction is DReactionCore &
|
|
53
|
+
InferFeatures<DReactionCore, ReturnType<typeof logger>> => {
|
|
54
|
+
if (!hasLoggerPlugin(dreaction)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'This Reactotron client has not had the logger plugin applied to it. Make sure that you add `use(logger())` before adding this plugin.'
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { DReactionCore, Plugin } from '../';
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
4
|
+
export type AcceptableRepls = object | Function | string | number;
|
|
5
|
+
|
|
6
|
+
const repl = () => (reactotron: DReactionCore) => {
|
|
7
|
+
const myRepls: { [key: string]: AcceptableRepls } = {};
|
|
8
|
+
// let currentContext = null
|
|
9
|
+
return {
|
|
10
|
+
onCommand: ({ type, payload }) => {
|
|
11
|
+
if (type.substr(0, 5) !== 'repl.') return;
|
|
12
|
+
|
|
13
|
+
switch (type.substr(5)) {
|
|
14
|
+
case 'ls':
|
|
15
|
+
reactotron.send('repl.ls.response', Object.keys(myRepls));
|
|
16
|
+
break;
|
|
17
|
+
// case "cd":
|
|
18
|
+
// const changeTo = myRepls.find(r => r.name === payload)
|
|
19
|
+
// if (!changeTo) {
|
|
20
|
+
// reactotron.send("repl.cd.response", "That REPL does not exist")
|
|
21
|
+
// break
|
|
22
|
+
// }
|
|
23
|
+
// currentContext = payload
|
|
24
|
+
// reactotron.send("repl.cd.response", `Change REPL to "${payload}"`)
|
|
25
|
+
// break
|
|
26
|
+
case 'execute':
|
|
27
|
+
// if (!currentContext) {
|
|
28
|
+
// reactotron.send(
|
|
29
|
+
// "repl.execute.response",
|
|
30
|
+
// "You must first select the REPL to use. Try 'ls'"
|
|
31
|
+
// )
|
|
32
|
+
// break
|
|
33
|
+
// }
|
|
34
|
+
// const currentRepl = myRepls.find(r => r.name === currentContext)
|
|
35
|
+
// if (!currentRepl) {
|
|
36
|
+
// reactotron.send("repl.execute.response", "The selected REPL no longer exists.")
|
|
37
|
+
// break
|
|
38
|
+
// }
|
|
39
|
+
reactotron.send(
|
|
40
|
+
'repl.execute.response',
|
|
41
|
+
function () {
|
|
42
|
+
return eval(payload); // eslint-disable-line no-eval
|
|
43
|
+
}.call(myRepls)
|
|
44
|
+
);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
features: {
|
|
49
|
+
repl: (name: string, value: AcceptableRepls) => {
|
|
50
|
+
if (!name) {
|
|
51
|
+
throw new Error('You must provide a name for your REPL');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (myRepls[name]) {
|
|
55
|
+
throw new Error('You are already REPLing an item with that name');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
myRepls[name] = value;
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
} satisfies Plugin<DReactionCore>;
|
|
62
|
+
};
|
|
63
|
+
export default repl;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StateActionCompletePayload,
|
|
3
|
+
StateBackupResponsePayload,
|
|
4
|
+
StateKeysResponsePayload,
|
|
5
|
+
StateValuesChangePayload,
|
|
6
|
+
StateValuesResponsePayload,
|
|
7
|
+
} from 'dreaction-protocol';
|
|
8
|
+
import type { DReactionCore, Plugin, InferFeatures } from '../';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Provides helper functions for send state responses.
|
|
12
|
+
*/
|
|
13
|
+
const stateResponse = () => (reactotron: DReactionCore) => {
|
|
14
|
+
return {
|
|
15
|
+
features: {
|
|
16
|
+
stateActionComplete: (
|
|
17
|
+
name: StateActionCompletePayload['name'],
|
|
18
|
+
action: StateActionCompletePayload['action'],
|
|
19
|
+
important = false
|
|
20
|
+
) =>
|
|
21
|
+
reactotron.send('state.action.complete', { name, action }, !!important),
|
|
22
|
+
|
|
23
|
+
stateValuesResponse: (
|
|
24
|
+
path: StateValuesResponsePayload['path'],
|
|
25
|
+
value: StateValuesResponsePayload['value'],
|
|
26
|
+
valid: StateValuesResponsePayload['value'] = true
|
|
27
|
+
) => reactotron.send('state.values.response', { path, value, valid }),
|
|
28
|
+
|
|
29
|
+
stateKeysResponse: (
|
|
30
|
+
path: StateKeysResponsePayload['path'],
|
|
31
|
+
keys: StateKeysResponsePayload['keys'],
|
|
32
|
+
valid: StateKeysResponsePayload['valid'] = true
|
|
33
|
+
) => reactotron.send('state.keys.response', { path, keys, valid }),
|
|
34
|
+
|
|
35
|
+
stateValuesChange: (changes: StateValuesChangePayload['changes']) =>
|
|
36
|
+
changes.length > 0 &&
|
|
37
|
+
reactotron.send('state.values.change', { changes }),
|
|
38
|
+
|
|
39
|
+
/** sends the state backup over to the server */
|
|
40
|
+
stateBackupResponse: (state: StateBackupResponsePayload['state']) =>
|
|
41
|
+
reactotron.send('state.backup.response', { state }),
|
|
42
|
+
},
|
|
43
|
+
} satisfies Plugin<DReactionCore>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export type StateResponsePlugin = ReturnType<typeof stateResponse>;
|
|
47
|
+
|
|
48
|
+
export default stateResponse;
|
|
49
|
+
|
|
50
|
+
export const hasStateResponsePlugin = (
|
|
51
|
+
reactotron: DReactionCore
|
|
52
|
+
): reactotron is DReactionCore &
|
|
53
|
+
InferFeatures<DReactionCore, ReturnType<typeof stateResponse>> =>
|
|
54
|
+
reactotron &&
|
|
55
|
+
'stateActionComplete' in reactotron &&
|
|
56
|
+
typeof reactotron.stateActionComplete === 'function' &&
|
|
57
|
+
'stateValuesResponse' in reactotron &&
|
|
58
|
+
typeof reactotron.stateValuesResponse === 'function' &&
|
|
59
|
+
'stateKeysResponse' in reactotron &&
|
|
60
|
+
typeof reactotron.stateKeysResponse === 'function' &&
|
|
61
|
+
'stateValuesChange' in reactotron &&
|
|
62
|
+
typeof reactotron.stateValuesChange === 'function' &&
|
|
63
|
+
'stateBackupResponse' in reactotron &&
|
|
64
|
+
typeof reactotron.stateBackupResponse === 'function';
|
|
65
|
+
|
|
66
|
+
export const assertHasStateResponsePlugin = (
|
|
67
|
+
reactotron: DReactionCore
|
|
68
|
+
): asserts reactotron is DReactionCore &
|
|
69
|
+
InferFeatures<DReactionCore, ReturnType<typeof stateResponse>> => {
|
|
70
|
+
if (!hasStateResponsePlugin(reactotron)) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
'This Reactotron client has not had the state responses plugin applied to it. Make sure that you add `use(stateResponse())` before adding this plugin.'
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
};
|
package/src/serialize.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// JSON.stringify() doesn't support circular dependencies or keeping
|
|
2
|
+
// falsy values. This does.
|
|
3
|
+
//
|
|
4
|
+
// Mostly adapted from https://github.com/isaacs/json-stringify-safe
|
|
5
|
+
|
|
6
|
+
// replacement tokens
|
|
7
|
+
const UNDEFINED = '~~~ undefined ~~~';
|
|
8
|
+
const NULL = `~~~ null ~~~`;
|
|
9
|
+
const FALSE = `~~~ false ~~~`;
|
|
10
|
+
const ZERO = `~~~ zero ~~~`;
|
|
11
|
+
const EMPTY_STRING = `~~~ empty string ~~~`;
|
|
12
|
+
const CIRCULAR = '~~~ Circular Reference ~~~';
|
|
13
|
+
const ANONYMOUS = '~~~ anonymous function ~~~';
|
|
14
|
+
const INFINITY = '~~~ Infinity ~~~';
|
|
15
|
+
const NEGATIVE_INFINITY = '~~~ -Infinity ~~~';
|
|
16
|
+
// const NAN = '~~~ NaN ~~~'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fix BigInt serialization
|
|
20
|
+
* BigInts are not supported by JSON.stringify in Hermes android.
|
|
21
|
+
* This is a workaround.
|
|
22
|
+
* https://github.com/GoogleChromeLabs/jsbi/issues/30#issuecomment-953187833
|
|
23
|
+
* https://github.com/infinitered/reactotron/issues/1436
|
|
24
|
+
*/
|
|
25
|
+
declare global {
|
|
26
|
+
interface BigInt {
|
|
27
|
+
toJSON(): string;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (typeof BigInt !== 'undefined') {
|
|
31
|
+
// eslint-disable-next-line no-extend-native
|
|
32
|
+
BigInt.prototype.toJSON = function () {
|
|
33
|
+
return this.toString();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Attempts to give a name to a function.
|
|
39
|
+
*
|
|
40
|
+
* @param {Function} fn - The function to name.
|
|
41
|
+
*/
|
|
42
|
+
function getFunctionName(fn: any): string {
|
|
43
|
+
const n = fn.name;
|
|
44
|
+
if (n === null || n === undefined || n === '') {
|
|
45
|
+
return ANONYMOUS;
|
|
46
|
+
} else {
|
|
47
|
+
return `~~~ ${n}() ~~~`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Serializes an object to JSON.
|
|
53
|
+
*
|
|
54
|
+
* @param {any} source - The victim.
|
|
55
|
+
*/
|
|
56
|
+
function serialize(source: any, proxyHack = false) {
|
|
57
|
+
const stack = [] as any[];
|
|
58
|
+
const keys = [] as string[];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Replace this object node with something potentially custom.
|
|
62
|
+
*
|
|
63
|
+
* @param {*} key - The key currently visited.
|
|
64
|
+
* @param {*} value - The value to replace.
|
|
65
|
+
*/
|
|
66
|
+
function serializer(replacer: Function | null) {
|
|
67
|
+
return function (this: any, key: string, value: any) {
|
|
68
|
+
// slam dunks
|
|
69
|
+
if (value === true) return true;
|
|
70
|
+
|
|
71
|
+
// weird stuff
|
|
72
|
+
// if (Object.is(value, NaN)) return NAN // OK, apparently this is hard... leaving out for now
|
|
73
|
+
if (value === Infinity) return INFINITY;
|
|
74
|
+
if (value === -Infinity) return NEGATIVE_INFINITY;
|
|
75
|
+
if (value === 0) return ZERO;
|
|
76
|
+
|
|
77
|
+
// classic javascript
|
|
78
|
+
if (value === undefined) return UNDEFINED;
|
|
79
|
+
if (value === null) return NULL;
|
|
80
|
+
if (value === false) return FALSE;
|
|
81
|
+
|
|
82
|
+
// head shakers
|
|
83
|
+
if (value === -0) return ZERO; // eslint-disable-line
|
|
84
|
+
if (value === '') return EMPTY_STRING;
|
|
85
|
+
|
|
86
|
+
if (proxyHack && typeof value === 'object' && value.nativeEvent) {
|
|
87
|
+
return value.nativeEvent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// known types that have easy resolving
|
|
91
|
+
switch (typeof value) {
|
|
92
|
+
case 'string':
|
|
93
|
+
return value;
|
|
94
|
+
case 'number':
|
|
95
|
+
return value;
|
|
96
|
+
case 'bigint':
|
|
97
|
+
return value.toString();
|
|
98
|
+
case 'function':
|
|
99
|
+
return getFunctionName(value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Tough things to crack
|
|
103
|
+
// If we have an iterator but are not an array (because arrays are easily seralizeable already)...
|
|
104
|
+
if (value[Symbol.iterator] && !Array.isArray(value)) {
|
|
105
|
+
// Convert to an array!
|
|
106
|
+
return [...value];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (stack.length > 0) {
|
|
110
|
+
// check for prior existance
|
|
111
|
+
const thisPos = stack.indexOf(this);
|
|
112
|
+
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
|
|
113
|
+
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
|
|
114
|
+
if (~stack.indexOf(value)) value = CIRCULAR;
|
|
115
|
+
} else {
|
|
116
|
+
stack.push(value);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return replacer == null ? value : replacer.call(this, key, value);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return JSON.stringify(source, serializer(null));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default serialize;
|
package/src/stopwatch.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
declare const global: any;
|
|
4
|
+
|
|
5
|
+
const hasHirezNodeTimer =
|
|
6
|
+
false &&
|
|
7
|
+
typeof process === 'object' &&
|
|
8
|
+
process &&
|
|
9
|
+
process.hrtime &&
|
|
10
|
+
typeof process.hrtime === 'function';
|
|
11
|
+
|
|
12
|
+
// the default timer
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
14
|
+
const defaultPerformanceNow = (started?: number) => Date.now();
|
|
15
|
+
|
|
16
|
+
// try to find the browser-based performance timer
|
|
17
|
+
const nativePerformance =
|
|
18
|
+
typeof window !== 'undefined' &&
|
|
19
|
+
window &&
|
|
20
|
+
(window.performance ||
|
|
21
|
+
(window as any).msPerformance ||
|
|
22
|
+
(window as any).webkitPerformance);
|
|
23
|
+
|
|
24
|
+
// the function we're trying to assign
|
|
25
|
+
let performanceNow = defaultPerformanceNow;
|
|
26
|
+
|
|
27
|
+
// accepts an already started time and returns the number of milliseconds
|
|
28
|
+
let delta = (started: number) => performanceNow() - started;
|
|
29
|
+
|
|
30
|
+
if (hasHirezNodeTimer) {
|
|
31
|
+
performanceNow = process.hrtime as any;
|
|
32
|
+
// delta = (started) => performanceNow(started)[1] / 1000000;
|
|
33
|
+
delta = (started) => performanceNow(started) / 1000000;
|
|
34
|
+
} else if (global.nativePerformanceNow) {
|
|
35
|
+
// react native 47
|
|
36
|
+
performanceNow = global.nativePerformanceNow;
|
|
37
|
+
} else if (nativePerformance) {
|
|
38
|
+
// browsers + safely check for react native < 47
|
|
39
|
+
performanceNow = () => nativePerformance.now && nativePerformance.now();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Starts a lame, low-res timer. Returns a function which when invoked,
|
|
44
|
+
* gives you the number of milliseconds since passing. ish.
|
|
45
|
+
*/
|
|
46
|
+
export const start = () => {
|
|
47
|
+
// record the start time
|
|
48
|
+
const started = performanceNow();
|
|
49
|
+
return () => delta(started);
|
|
50
|
+
};
|
package/src/validate.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ClientOptions } from './client-options';
|
|
2
|
+
import type { DReactionCore } from './';
|
|
3
|
+
|
|
4
|
+
const isCreateSocketValid = (
|
|
5
|
+
createSocket: unknown
|
|
6
|
+
): createSocket is ClientOptions<DReactionCore>['createSocket'] =>
|
|
7
|
+
typeof createSocket !== 'undefined' && createSocket !== null;
|
|
8
|
+
const isHostValid = (host: string): boolean =>
|
|
9
|
+
(typeof host === 'string' && host && host !== '') as boolean;
|
|
10
|
+
const isPortValid = (port: number): boolean =>
|
|
11
|
+
typeof port === 'number' && port >= 1 && port <= 65535;
|
|
12
|
+
const onCommandValid = (fn: (cmd: string) => any) => typeof fn === 'function';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Ensures the options are sane to run this baby. Throw if not. These
|
|
16
|
+
* are basically sanity checks.
|
|
17
|
+
*/
|
|
18
|
+
const validate = (options: ClientOptions<DReactionCore>) => {
|
|
19
|
+
const { createSocket, host, port, onCommand } = options;
|
|
20
|
+
|
|
21
|
+
if (!isCreateSocketValid(createSocket)) {
|
|
22
|
+
throw new Error('invalid createSocket function');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!isHostValid(host!)) {
|
|
26
|
+
throw new Error('invalid host');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!isPortValid(port!)) {
|
|
30
|
+
throw new Error('invalid port');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!onCommandValid(onCommand!)) {
|
|
34
|
+
throw new Error('invalid onCommand handler');
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default validate;
|