dreaction-client-core 1.2.2 → 1.3.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.
Files changed (48) hide show
  1. package/lib/core.d.ts +30 -0
  2. package/lib/core.d.ts.map +1 -0
  3. package/lib/core.js +317 -0
  4. package/lib/index.d.ts +14 -195
  5. package/lib/index.d.ts.map +1 -1
  6. package/lib/index.js +20 -461
  7. package/lib/plugins/api-response.d.ts +1 -1
  8. package/lib/plugins/api-response.d.ts.map +1 -1
  9. package/lib/plugins/benchmark.d.ts +1 -1
  10. package/lib/plugins/benchmark.d.ts.map +1 -1
  11. package/lib/plugins/clear.d.ts +1 -1
  12. package/lib/plugins/clear.d.ts.map +1 -1
  13. package/lib/plugins/image.d.ts +1 -1
  14. package/lib/plugins/image.d.ts.map +1 -1
  15. package/lib/plugins/index.d.ts +53 -0
  16. package/lib/plugins/index.d.ts.map +1 -0
  17. package/lib/plugins/index.js +36 -0
  18. package/lib/plugins/issue.d.ts +19 -0
  19. package/lib/plugins/issue.d.ts.map +1 -0
  20. package/lib/plugins/issue.js +20 -0
  21. package/lib/plugins/logger.d.ts +20 -5
  22. package/lib/plugins/logger.d.ts.map +1 -1
  23. package/lib/plugins/logger.js +8 -26
  24. package/lib/plugins/repl.d.ts +1 -1
  25. package/lib/plugins/repl.d.ts.map +1 -1
  26. package/lib/plugins/state-responses.d.ts +17 -5
  27. package/lib/plugins/state-responses.d.ts.map +1 -1
  28. package/lib/plugins/state-responses.js +10 -19
  29. package/lib/types.d.ts +73 -0
  30. package/lib/types.d.ts.map +1 -0
  31. package/lib/types.js +3 -0
  32. package/lib/utils/plugin-guard.d.ts +12 -0
  33. package/lib/utils/plugin-guard.d.ts.map +1 -0
  34. package/lib/utils/plugin-guard.js +19 -0
  35. package/package.json +2 -2
  36. package/src/core.ts +428 -0
  37. package/src/index.ts +36 -720
  38. package/src/plugins/api-response.ts +1 -1
  39. package/src/plugins/benchmark.ts +1 -1
  40. package/src/plugins/clear.ts +1 -1
  41. package/src/plugins/image.ts +1 -1
  42. package/src/plugins/index.ts +26 -0
  43. package/src/plugins/issue.ts +25 -0
  44. package/src/plugins/logger.ts +17 -39
  45. package/src/plugins/state-responses.ts +19 -29
  46. package/src/types.ts +127 -0
  47. package/src/utils/plugin-guard.ts +31 -0
  48. package/src/plugins/repl.ts +0 -63
@@ -1,4 +1,4 @@
1
- import type { DReactionCore, Plugin } from '../';
1
+ import type { DReactionCore, Plugin } from '../types';
2
2
 
3
3
  /**
4
4
  * Sends API request/response information.
@@ -1,4 +1,4 @@
1
- import type { DReactionCore, Plugin } from '../';
1
+ import type { DReactionCore, Plugin } from '../types';
2
2
 
3
3
  /**
4
4
  * Runs small high-unscientific benchmarks for you.
@@ -1,4 +1,4 @@
1
- import type { DReactionCore, Plugin } from '../';
1
+ import type { DReactionCore, Plugin } from '../types';
2
2
 
3
3
  /**
4
4
  * Clears the dreaction server.
@@ -1,4 +1,4 @@
1
- import type { DReactionCore, Plugin } from '../';
1
+ import type { DReactionCore, Plugin } from '../types';
2
2
 
3
3
  export interface ImagePayload {
4
4
  uri: string;
@@ -0,0 +1,26 @@
1
+ import type { DReactionCore, PluginCreator } from '../types';
2
+ import logger from './logger';
3
+ import image from './image';
4
+ import benchmark from './benchmark';
5
+ import stateResponses from './state-responses';
6
+ import apiResponse from './api-response';
7
+ import clear from './clear';
8
+ import issue from './issue';
9
+
10
+ export { default as logger } from './logger';
11
+ export { default as image } from './image';
12
+ export { default as benchmark } from './benchmark';
13
+ export { default as stateResponses } from './state-responses';
14
+ export { default as apiResponse } from './api-response';
15
+ export { default as clear } from './clear';
16
+ export { default as issue } from './issue';
17
+
18
+ export const corePlugins = [
19
+ image(),
20
+ logger(),
21
+ benchmark(),
22
+ stateResponses(),
23
+ apiResponse(),
24
+ clear(),
25
+ issue(),
26
+ ] satisfies PluginCreator<DReactionCore>[];
@@ -0,0 +1,25 @@
1
+ import type { DReactionCore, Plugin, InferFeatures } from '../types';
2
+ import { createPluginGuard } from '../utils/plugin-guard';
3
+
4
+ /**
5
+ * Provides issue reporting feature
6
+ */
7
+ const issue = () => (dreaction: DReactionCore) => {
8
+ return {
9
+ features: {
10
+ reportIssue: (id: string, name?: string, description?: string) => {
11
+ dreaction.send('report.issue', { id, name, description }, true);
12
+ },
13
+ },
14
+ } satisfies Plugin<DReactionCore>;
15
+ };
16
+
17
+ export default issue;
18
+
19
+ export type IssuePlugin = ReturnType<typeof issue>;
20
+ export type IssueFeatures = InferFeatures<ReturnType<typeof issue>>;
21
+
22
+ const issueGuard = createPluginGuard<IssueFeatures>(['reportIssue'], 'issue');
23
+
24
+ export const hasIssuePlugin = issueGuard.has;
25
+ export const assertHasIssuePlugin = issueGuard.assert;
@@ -1,28 +1,29 @@
1
- import type { DReactionCore, Plugin, InferFeatures } from '../';
1
+ import type { DReactionCore, Plugin, InferFeatures } from '../types';
2
+ import { createPluginGuard } from '../utils/plugin-guard';
2
3
 
3
4
  /**
4
- * Provides 4 features for logging. log & debug are the same.
5
+ * Provides logging features: log, info, debug, warn, error
5
6
  */
6
7
  const logger = () => (dreaction: DReactionCore) => {
7
8
  return {
8
9
  features: {
9
- log: (...args) => {
10
- const content = args && args.length === 1 ? args[0] : args;
10
+ log: (...args: any[]) => {
11
+ const content = args.length === 1 ? args[0] : args;
11
12
  dreaction.send('log', { level: 'debug', message: content }, false);
12
13
  },
13
- info: (...args) => {
14
- const content = args && args.length === 1 ? args[0] : args;
14
+ info: (...args: any[]) => {
15
+ const content = args.length === 1 ? args[0] : args;
15
16
  dreaction.send('log', { level: 'debug', message: content }, false);
16
17
  },
17
- logImportant: (...args) => {
18
- const content = args && args.length === 1 ? args[0] : args;
18
+ logImportant: (...args: any[]) => {
19
+ const content = args.length === 1 ? args[0] : args;
19
20
  dreaction.send('log', { level: 'debug', message: content }, true);
20
21
  },
21
22
  debug: (message, important = false) =>
22
23
  dreaction.send('log', { level: 'debug', message }, !!important),
23
24
  warn: (message) =>
24
25
  dreaction.send('log', { level: 'warn', message }, true),
25
- error: (message, stack) =>
26
+ error: (message, stack?) =>
26
27
  dreaction.send('log', { level: 'error', message, stack }, true),
27
28
  },
28
29
  } satisfies Plugin<DReactionCore>;
@@ -31,35 +32,12 @@ const logger = () => (dreaction: DReactionCore) => {
31
32
  export default logger;
32
33
 
33
34
  export type LoggerPlugin = ReturnType<typeof logger>;
35
+ export type LoggerFeatures = InferFeatures<ReturnType<typeof logger>>;
34
36
 
35
- export const hasLoggerPlugin = (
36
- dreaction: DReactionCore
37
- ): dreaction is DReactionCore &
38
- InferFeatures<DReactionCore, ReturnType<typeof logger>> => {
39
- return (
40
- dreaction &&
41
- 'log' in dreaction &&
42
- typeof dreaction.log === 'function' &&
43
- 'info' in dreaction &&
44
- typeof dreaction.info === 'function' &&
45
- 'logImportant' in dreaction &&
46
- typeof dreaction.logImportant === 'function' &&
47
- 'debug' in dreaction &&
48
- typeof dreaction.debug === 'function' &&
49
- 'warn' in dreaction &&
50
- typeof dreaction.warn === 'function' &&
51
- 'error' in dreaction &&
52
- typeof dreaction.error === 'function'
53
- );
54
- };
37
+ const loggerGuard = createPluginGuard<LoggerFeatures>(
38
+ ['log', 'info', 'logImportant', 'debug', 'warn', 'error'],
39
+ 'logger'
40
+ );
55
41
 
56
- export const assertHasLoggerPlugin = (
57
- dreaction: DReactionCore
58
- ): asserts dreaction is DReactionCore &
59
- InferFeatures<DReactionCore, ReturnType<typeof logger>> => {
60
- if (!hasLoggerPlugin(dreaction)) {
61
- throw new Error(
62
- 'This DReaction client has not had the logger plugin applied to it. Make sure that you add `use(logger())` before adding this plugin.'
63
- );
64
- }
65
- };
42
+ export const hasLoggerPlugin = loggerGuard.has;
43
+ export const assertHasLoggerPlugin = loggerGuard.assert;
@@ -5,7 +5,8 @@ import type {
5
5
  StateValuesChangePayload,
6
6
  StateValuesResponsePayload,
7
7
  } from 'dreaction-protocol';
8
- import type { DReactionCore, Plugin, InferFeatures } from '../';
8
+ import type { DReactionCore, Plugin, InferFeatures } from '../types';
9
+ import { createPluginGuard } from '../utils/plugin-guard';
9
10
 
10
11
  /**
11
12
  * Provides helper functions for send state responses.
@@ -36,40 +37,29 @@ const stateResponse = () => (dreaction: DReactionCore) => {
36
37
  changes.length > 0 &&
37
38
  dreaction.send('state.values.change', { changes }),
38
39
 
39
- /** sends the state backup over to the server */
40
40
  stateBackupResponse: (state: StateBackupResponsePayload['state']) =>
41
41
  dreaction.send('state.backup.response', { state }),
42
42
  },
43
43
  } satisfies Plugin<DReactionCore>;
44
44
  };
45
45
 
46
- export type StateResponsePlugin = ReturnType<typeof stateResponse>;
47
-
48
46
  export default stateResponse;
49
47
 
50
- export const hasStateResponsePlugin = (
51
- dreaction: DReactionCore
52
- ): dreaction is DReactionCore &
53
- InferFeatures<DReactionCore, ReturnType<typeof stateResponse>> =>
54
- dreaction &&
55
- 'stateActionComplete' in dreaction &&
56
- typeof dreaction.stateActionComplete === 'function' &&
57
- 'stateValuesResponse' in dreaction &&
58
- typeof dreaction.stateValuesResponse === 'function' &&
59
- 'stateKeysResponse' in dreaction &&
60
- typeof dreaction.stateKeysResponse === 'function' &&
61
- 'stateValuesChange' in dreaction &&
62
- typeof dreaction.stateValuesChange === 'function' &&
63
- 'stateBackupResponse' in dreaction &&
64
- typeof dreaction.stateBackupResponse === 'function';
48
+ export type StateResponsePlugin = ReturnType<typeof stateResponse>;
49
+ export type StateResponseFeatures = InferFeatures<
50
+ ReturnType<typeof stateResponse>
51
+ >;
65
52
 
66
- export const assertHasStateResponsePlugin = (
67
- dreaction: DReactionCore
68
- ): asserts dreaction is DReactionCore &
69
- InferFeatures<DReactionCore, ReturnType<typeof stateResponse>> => {
70
- if (!hasStateResponsePlugin(dreaction)) {
71
- throw new Error(
72
- 'This DReaction 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
- };
53
+ const stateResponseGuard = createPluginGuard<StateResponseFeatures>(
54
+ [
55
+ 'stateActionComplete',
56
+ 'stateValuesResponse',
57
+ 'stateKeysResponse',
58
+ 'stateValuesChange',
59
+ 'stateBackupResponse',
60
+ ],
61
+ 'state responses'
62
+ );
63
+
64
+ export const hasStateResponsePlugin = stateResponseGuard.has;
65
+ export const assertHasStateResponsePlugin = stateResponseGuard.assert;
package/src/types.ts ADDED
@@ -0,0 +1,127 @@
1
+ import type {
2
+ Command,
3
+ CommandMap,
4
+ CustomCommandArg,
5
+ CustomCommandRegisterPayload,
6
+ } from 'dreaction-protocol';
7
+ import type { ClientOptions } from './client-options';
8
+
9
+ // #region Basic Types
10
+ type AnyFunction = (...args: any[]) => any;
11
+
12
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
13
+ k: infer I
14
+ ) => void
15
+ ? I
16
+ : never;
17
+
18
+ export interface DisplayConfig {
19
+ name: string;
20
+ value?: object | string | number | boolean | null | undefined;
21
+ preview?: string;
22
+ image?: string | { uri: string };
23
+ important?: boolean;
24
+ }
25
+
26
+ interface ArgTypeMap {
27
+ string: string;
28
+ }
29
+
30
+ export type CustomCommandArgs<Args extends CustomCommandArg[]> =
31
+ UnionToIntersection<
32
+ Args extends Array<infer U>
33
+ ? U extends CustomCommandArg
34
+ ? { [K in U as U['name']]: ArgTypeMap[U['type']] }
35
+ : never
36
+ : never
37
+ >;
38
+
39
+ export interface CustomCommand<
40
+ Args extends CustomCommandArg[] = CustomCommandArg[]
41
+ > extends Omit<CustomCommandRegisterPayload, 'id' | 'args'> {
42
+ id?: number;
43
+ handler: (args: CustomCommandArgs<Args>) => any | Promise<any>;
44
+ args?: Args;
45
+ }
46
+ // #endregion
47
+
48
+ // #region Plugin Types
49
+ export interface LifeCycleMethods {
50
+ onCommand?: (command: Command) => void;
51
+ onConnect?: () => void;
52
+ onDisconnect?: () => void;
53
+ }
54
+
55
+ export interface Plugin<Client> extends LifeCycleMethods {
56
+ features?: {
57
+ [key: string]: AnyFunction;
58
+ };
59
+ onPlugin?: (client: Client) => void;
60
+ }
61
+
62
+ export type PluginCreator<Client> = (client: Client) => Plugin<Client>;
63
+
64
+ /**
65
+ * Extract features from a single PluginCreator
66
+ */
67
+ type ExtractPluginFeatures<P> = P extends PluginCreator<any>
68
+ ? ReturnType<P> extends { features: infer F }
69
+ ? F
70
+ : {}
71
+ : {};
72
+
73
+ /**
74
+ * Extract features type from a plugin or array of plugins.
75
+ * Supports:
76
+ * - Single PluginCreator: InferFeatures<typeof myPlugin>
77
+ * - Array of PluginCreators: InferFeatures<typeof plugins>
78
+ * - Legacy format: InferFeatures<Client, PluginCreator> (second param is the plugin)
79
+ */
80
+ export type InferFeatures<ClientOrPlugin, LegacyPluginCreator = never> = [
81
+ LegacyPluginCreator
82
+ ] extends [never]
83
+ ? ClientOrPlugin extends PluginCreator<any>[]
84
+ ? UnionToIntersection<ExtractPluginFeatures<ClientOrPlugin[number]>>
85
+ : ExtractPluginFeatures<ClientOrPlugin>
86
+ : ExtractPluginFeatures<LegacyPluginCreator>;
87
+
88
+ /**
89
+ * @deprecated Use InferFeatures instead
90
+ */
91
+ export type InferFeaturesFromPlugins<
92
+ _Client,
93
+ Plugins extends PluginCreator<any>[]
94
+ > = UnionToIntersection<ExtractPluginFeatures<Plugins[number]>>;
95
+ // #endregion
96
+
97
+ // #region Core Interface
98
+ export interface DReactionCore {
99
+ connected: boolean;
100
+ isReady: boolean;
101
+ options: ClientOptions<this>;
102
+ plugins: Plugin<this>[];
103
+ startTimer: () => () => number;
104
+ close: () => this;
105
+ send: <Type extends keyof CommandMap>(
106
+ type: Type,
107
+ payload?: CommandMap[Type],
108
+ important?: boolean
109
+ ) => void;
110
+ display: (config: DisplayConfig) => void;
111
+ registerCustomCommand: <
112
+ Args extends CustomCommandArg[] = Exclude<CustomCommand['args'], undefined>
113
+ >(
114
+ config: CustomCommand<Args>
115
+ ) => () => void | ((config: string, optHandler?: () => void) => () => void);
116
+ configure: (
117
+ options?: ClientOptions<this>
118
+ ) => ClientOptions<this>['plugins'] extends PluginCreator<this>[]
119
+ ? this & InferFeatures<ClientOptions<this>['plugins']>
120
+ : this;
121
+ use: <P extends PluginCreator<this>>(
122
+ pluginCreator: P
123
+ ) => this & InferFeatures<P>;
124
+ connect: () => this;
125
+ waitForConnect: () => Promise<void>;
126
+ }
127
+ // #endregion
@@ -0,0 +1,31 @@
1
+ import type { DReactionCore } from '../types';
2
+
3
+ type AnyFunction = (...args: any[]) => any;
4
+
5
+ /**
6
+ * Create type guard functions for plugin features.
7
+ * Reduces boilerplate for hasXxxPlugin and assertHasXxxPlugin patterns.
8
+ */
9
+ export function createPluginGuard<T extends Record<string, AnyFunction>>(
10
+ featureNames: (keyof T)[],
11
+ pluginName: string
12
+ ) {
13
+ const has = (client: DReactionCore): client is DReactionCore & T => {
14
+ return featureNames.every(
15
+ (name) => name in client && typeof (client as any)[name] === 'function'
16
+ );
17
+ };
18
+
19
+ const assert = (
20
+ client: DReactionCore
21
+ ): asserts client is DReactionCore & T => {
22
+ if (!has(client)) {
23
+ throw new Error(
24
+ `This DReaction client has not had the ${pluginName} plugin applied to it. ` +
25
+ `Make sure that you add the plugin before using these features.`
26
+ );
27
+ }
28
+ };
29
+
30
+ return { has, assert };
31
+ }
@@ -1,63 +0,0 @@
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 = () => (dreaction: 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
- dreaction.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
- // dreaction.send("repl.cd.response", "That REPL does not exist")
21
- // break
22
- // }
23
- // currentContext = payload
24
- // dreaction.send("repl.cd.response", `Change REPL to "${payload}"`)
25
- // break
26
- case 'execute':
27
- // if (!currentContext) {
28
- // dreaction.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
- // dreaction.send("repl.execute.response", "The selected REPL no longer exists.")
37
- // break
38
- // }
39
- dreaction.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;