@zajno/common 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/coverage/clover.xml +429 -370
  2. package/coverage/coverage-final.json +39 -38
  3. package/coverage/lcov-report/NumberModel.ts.html +188 -0
  4. package/coverage/lcov-report/arrays.ts.html +602 -0
  5. package/coverage/lcov-report/calc.ts.html +278 -0
  6. package/coverage/lcov-report/enumHelper.ts.html +449 -0
  7. package/coverage/lcov-report/index.html +43 -43
  8. package/coverage/lcov-report/src/__tests__/helpers/index.html +1 -1
  9. package/coverage/lcov-report/src/__tests__/helpers/main.ts.html +2 -2
  10. package/coverage/lcov-report/src/async/arrays.ts.html +1 -1
  11. package/coverage/lcov-report/src/async/index.html +1 -1
  12. package/coverage/lcov-report/src/dates/calc.ts.html +1 -1
  13. package/coverage/lcov-report/src/dates/convert.ts.html +1 -1
  14. package/coverage/lcov-report/src/dates/datex.ts.html +1 -1
  15. package/coverage/lcov-report/src/dates/format.ts.html +1 -1
  16. package/coverage/lcov-report/src/dates/index.html +1 -1
  17. package/coverage/lcov-report/src/dates/index.ts.html +1 -1
  18. package/coverage/lcov-report/src/dates/parse.ts.html +1 -1
  19. package/coverage/lcov-report/src/dates/period.ts.html +1 -1
  20. package/coverage/lcov-report/src/dates/shift.ts.html +1 -1
  21. package/coverage/lcov-report/src/dates/types.ts.html +1 -1
  22. package/coverage/lcov-report/src/dates/yearDate.ts.html +1 -1
  23. package/coverage/lcov-report/src/enumHelper.ts.html +1 -1
  24. package/coverage/lcov-report/src/event.ts.html +3 -3
  25. package/coverage/lcov-report/src/index.html +21 -21
  26. package/coverage/lcov-report/src/lazy.light.ts.html +1 -1
  27. package/coverage/lcov-report/src/logger/batch.ts.html +107 -0
  28. package/coverage/lcov-report/src/logger/console.ts.html +10 -25
  29. package/coverage/lcov-report/src/logger/index.html +54 -39
  30. package/coverage/lcov-report/src/logger/index.ts.html +88 -61
  31. package/coverage/lcov-report/src/logger/named.ts.html +31 -31
  32. package/coverage/lcov-report/src/logger/proxy.ts.html +13 -13
  33. package/coverage/lcov-report/src/math/arrays.ts.html +269 -32
  34. package/coverage/lcov-report/src/math/calc.ts.html +11 -11
  35. package/coverage/lcov-report/src/math/distribution.ts.html +2 -2
  36. package/coverage/lcov-report/src/math/index.html +11 -11
  37. package/coverage/lcov-report/src/math/index.ts.html +6 -6
  38. package/coverage/lcov-report/src/transitionObserver.ts.html +5 -5
  39. package/coverage/lcov-report/src/types.ts.html +33 -18
  40. package/coverage/lcov-report/src/validation/ValidationErrors.ts.html +1 -1
  41. package/coverage/lcov-report/src/validation/creditCard.ts.html +1 -1
  42. package/coverage/lcov-report/src/validation/helpers.ts.html +1 -1
  43. package/coverage/lcov-report/src/validation/index.html +1 -1
  44. package/coverage/lcov-report/src/validation/index.ts.html +1 -1
  45. package/coverage/lcov-report/src/validation/types.ts.html +1 -1
  46. package/coverage/lcov-report/src/validation/validators.ts.html +1 -1
  47. package/coverage/lcov-report/src/validation/wrappers.ts.html +1 -1
  48. package/coverage/lcov-report/src/viewModels/FlagModel.ts.html +16 -13
  49. package/coverage/lcov-report/src/viewModels/LabeledFlagModel.ts.html +17 -26
  50. package/coverage/lcov-report/src/viewModels/MultiSelectModel.ts.html +34 -13
  51. package/coverage/lcov-report/src/viewModels/NumberModel.ts.html +19 -10
  52. package/coverage/lcov-report/src/viewModels/SelectModel.ts.html +19 -10
  53. package/coverage/lcov-report/src/viewModels/SelectViewModel.ts.html +17 -14
  54. package/coverage/lcov-report/src/viewModels/Validatable.ts.html +1 -1
  55. package/coverage/lcov-report/src/viewModels/index.html +42 -42
  56. package/coverage/lcov-report/src/viewModels/labeled.ts.html +128 -0
  57. package/coverage/lcov-report/src/viewModels/wrappers.ts.html +3 -15
  58. package/coverage/lcov-report/transitionObserver.ts.html +41 -77
  59. package/coverage/lcov.info +785 -654
  60. package/lib/event.d.ts.map +1 -1
  61. package/lib/event.js +1 -1
  62. package/lib/event.js.map +1 -1
  63. package/lib/logger/batch.d.ts +3 -0
  64. package/lib/logger/batch.d.ts.map +1 -0
  65. package/lib/logger/batch.js +12 -0
  66. package/lib/logger/batch.js.map +1 -0
  67. package/lib/logger/console.d.ts +0 -1
  68. package/lib/logger/console.d.ts.map +1 -1
  69. package/lib/logger/console.js +0 -3
  70. package/lib/logger/console.js.map +1 -1
  71. package/lib/logger/file.d.ts +15 -0
  72. package/lib/logger/file.d.ts.map +1 -0
  73. package/lib/logger/file.js +43 -0
  74. package/lib/logger/file.js.map +1 -0
  75. package/lib/logger/index.d.ts +7 -6
  76. package/lib/logger/index.d.ts.map +1 -1
  77. package/lib/logger/index.js +34 -21
  78. package/lib/logger/index.js.map +1 -1
  79. package/lib/logger/named.js +3 -3
  80. package/lib/logger/named.js.map +1 -1
  81. package/lib/logger/proxy.d.ts +1 -1
  82. package/lib/logger/proxy.d.ts.map +1 -1
  83. package/lib/logger/proxy.js +2 -2
  84. package/lib/logger/proxy.js.map +1 -1
  85. package/lib/math/arrays.d.ts +15 -0
  86. package/lib/math/arrays.d.ts.map +1 -1
  87. package/lib/math/arrays.js +69 -1
  88. package/lib/math/arrays.js.map +1 -1
  89. package/lib/transitionObserver.d.ts.map +1 -1
  90. package/lib/transitionObserver.js +2 -2
  91. package/lib/transitionObserver.js.map +1 -1
  92. package/lib/types.d.ts +4 -0
  93. package/lib/types.d.ts.map +1 -1
  94. package/lib/types.js +6 -2
  95. package/lib/types.js.map +1 -1
  96. package/lib/viewModels/FlagModel.d.ts +4 -4
  97. package/lib/viewModels/FlagModel.d.ts.map +1 -1
  98. package/lib/viewModels/FlagModel.js +1 -0
  99. package/lib/viewModels/FlagModel.js.map +1 -1
  100. package/lib/viewModels/MultiSelectModel.d.ts +6 -2
  101. package/lib/viewModels/MultiSelectModel.d.ts.map +1 -1
  102. package/lib/viewModels/MultiSelectModel.js +10 -1
  103. package/lib/viewModels/MultiSelectModel.js.map +1 -1
  104. package/lib/viewModels/NumberModel.d.ts +4 -2
  105. package/lib/viewModels/NumberModel.d.ts.map +1 -1
  106. package/lib/viewModels/NumberModel.js +1 -0
  107. package/lib/viewModels/NumberModel.js.map +1 -1
  108. package/lib/viewModels/SelectModel.d.ts +4 -2
  109. package/lib/viewModels/SelectModel.d.ts.map +1 -1
  110. package/lib/viewModels/SelectModel.js +1 -0
  111. package/lib/viewModels/SelectModel.js.map +1 -1
  112. package/lib/viewModels/TextModel.d.ts +4 -3
  113. package/lib/viewModels/TextModel.d.ts.map +1 -1
  114. package/lib/viewModels/TextModel.js +5 -5
  115. package/lib/viewModels/TextModel.js.map +1 -1
  116. package/lib/viewModels/ValuesCollector.d.ts +1 -3
  117. package/lib/viewModels/ValuesCollector.d.ts.map +1 -1
  118. package/lib/viewModels/ValuesCollector.js.map +1 -1
  119. package/lib/viewModels/index.d.ts +1 -0
  120. package/lib/viewModels/index.d.ts.map +1 -1
  121. package/lib/viewModels/index.js +1 -0
  122. package/lib/viewModels/index.js.map +1 -1
  123. package/lib/viewModels/types.d.ts +16 -0
  124. package/lib/viewModels/types.d.ts.map +1 -0
  125. package/lib/viewModels/types.js +3 -0
  126. package/lib/viewModels/types.js.map +1 -0
  127. package/lib/viewModels/wrappers.d.ts +1 -4
  128. package/lib/viewModels/wrappers.d.ts.map +1 -1
  129. package/lib/viewModels/wrappers.js.map +1 -1
  130. package/package.json +1 -1
  131. package/src/event.ts +2 -2
  132. package/src/logger/__tests__/{index.test.ts → logger.test.ts} +97 -41
  133. package/src/logger/batch.ts +9 -0
  134. package/src/logger/console.ts +0 -5
  135. package/src/logger/file.ts +46 -0
  136. package/src/logger/index.ts +37 -28
  137. package/src/logger/named.ts +3 -3
  138. package/src/logger/proxy.ts +2 -2
  139. package/src/math/__tests__/arrays.test.ts +53 -0
  140. package/src/math/arrays.ts +79 -0
  141. package/src/transitionObserver.ts +2 -2
  142. package/src/types.ts +6 -1
  143. package/src/viewModels/FlagModel.ts +5 -4
  144. package/src/viewModels/MultiSelectModel.ts +10 -3
  145. package/src/viewModels/NumberModel.ts +5 -2
  146. package/src/viewModels/SelectModel.ts +5 -2
  147. package/src/viewModels/TextModel.ts +5 -4
  148. package/src/viewModels/ValuesCollector.ts +1 -4
  149. package/src/viewModels/index.ts +1 -0
  150. package/src/viewModels/types.ts +19 -0
  151. package/src/viewModels/wrappers.ts +1 -5
  152. package/yarn-error.log +0 -3709
@@ -1,14 +1,26 @@
1
1
  import faker from 'faker';
2
- import logger, { createLogger, getMode, LoggerFactory, setMode } from '../';
2
+ import logger, { batchLoggers, createLogger, getMode, ILogger, setMode } from '..';
3
3
  import fc from 'fast-check';
4
4
  import { toArbitrary } from '../../__tests__/helpers/main';
5
+ import { Getter } from '../../types';
5
6
 
6
7
  const CONSOLE = console;
7
8
 
8
9
  describe('#logger-tests', () => {
9
10
 
10
- const customLogger: LoggerFactory = () => ({ log: CONSOLE.log, warn: CONSOLE.warn, error: CONSOLE.error });
11
- const loggerMethods = ['log', 'warn', 'error'] as const;
11
+ const customLogger = {
12
+ log: jest.fn().mockImplementation(null),
13
+ warn: jest.fn().mockImplementation(null),
14
+ error: jest.fn().mockImplementation(null),
15
+ };
16
+ const customLoggerGetter = () => customLogger;
17
+
18
+ const consoleMocks: Record<keyof ILogger, jest.SpyInstance> = {
19
+ log: jest.spyOn(CONSOLE, 'log').mockImplementation(null),
20
+ warn: jest.spyOn(CONSOLE, 'warn').mockImplementation(null),
21
+ error: jest.spyOn(CONSOLE, 'error').mockImplementation(null),
22
+ } as const;
23
+ const loggerMethods = Object.keys(consoleMocks) as (keyof ILogger)[];
12
24
 
13
25
  it('use logger without mode (default logger)', () => {
14
26
  const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.internet.url());
@@ -19,10 +31,8 @@ describe('#logger-tests', () => {
19
31
  const methodName = loggerMethods[iteration];
20
32
  logger[methodName](textToLog);
21
33
 
22
- const spyCLog = jest.spyOn(CONSOLE, methodName);
23
-
24
- expect(getMode()).toBeFalsy();
25
- expect(spyCLog).not.toHaveBeenCalled();
34
+ expect(getMode()).toBe(false);
35
+ expect(consoleMocks[methodName]).not.toHaveBeenCalled();
26
36
 
27
37
  ++iteration;
28
38
  }), {
@@ -30,45 +40,45 @@ describe('#logger-tests', () => {
30
40
  });
31
41
  });
32
42
 
33
- it('use logger with runtime mode nulling', () => {
43
+ it('use logger with runtime mode disabling', () => {
34
44
  const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.random.word());
35
45
 
36
46
  let iteration = 0;
37
47
  fc.assert(
38
48
  fc.property(_textToLog, (textToLog) => {
39
- if (iteration % 2 === 0) { setMode('console'); setMode(customLogger); setMode(false); }
49
+ // set mode before or after creating a new logger
50
+
51
+ if (iteration % 2 === 0) { setMode('console'); setMode(customLoggerGetter); setMode(false); }
40
52
  const logger = createLogger(faker.random.word());
41
- if (iteration % 2 === 1) { setMode('console'); setMode(customLogger); setMode(false); }
53
+ if (iteration % 2 === 1) { setMode('console'); setMode(customLoggerGetter); setMode(false); }
42
54
 
43
55
  const methodName = loggerMethods[iteration % 3];
44
56
  logger[methodName](textToLog);
45
57
 
46
- const spyLogger = jest.spyOn(customLogger(), methodName);
47
-
48
- expect(getMode()).toBeFalsy();
49
- expect(spyLogger).not.toHaveBeenCalled();
58
+ expect(getMode()).toBe(false);
59
+ expect(consoleMocks[methodName]).not.toHaveBeenCalled();
50
60
  ++iteration;
51
61
  }), {
52
62
  numRuns: loggerMethods.length * 2,
53
63
  });
54
64
  });
55
65
 
56
- it('use logger with \'false\' mode', () => {
66
+ it('use logger with \'null\' mode', () => {
57
67
  const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.internet.url());
58
68
 
59
69
  let iteration = 0;
60
70
  fc.assert(
61
71
  fc.property(_textToLog, (textToLog) => {
62
- if (iteration % 2 === 0) setMode(false);
72
+ if (iteration % 2 === 0) setMode(null);
63
73
  const logger = createLogger(faker.random.word());
64
- if (iteration % 2 === 1) setMode(false);
74
+ if (iteration % 2 === 1) setMode(null);
65
75
 
66
76
  const methodName = loggerMethods[iteration % 3];
67
77
  logger[methodName](textToLog);
68
78
 
69
79
  const spyLogger = jest.spyOn(CONSOLE, methodName);
70
80
 
71
- expect(getMode()).toBeFalsy();
81
+ expect(getMode()).toBeNull();
72
82
  expect(spyLogger).not.toHaveBeenCalled();
73
83
  ++iteration;
74
84
  }), {
@@ -76,75 +86,121 @@ describe('#logger-tests', () => {
76
86
  });
77
87
  });
78
88
 
79
- it('use logger with \'null\' mode', () => {
80
- const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.internet.url());
89
+ it('use logger with \'console\' mode', () => {
90
+ const loggerName = `[${faker.random.word()}]`;
91
+ const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.random.word());
81
92
 
82
93
  let iteration = 0;
83
94
  fc.assert(
84
95
  fc.property(_textToLog, (textToLog) => {
85
- if (iteration % 2 === 0) setMode(null);
86
- const logger = createLogger(faker.random.word());
87
- if (iteration % 2 === 1) setMode(null);
88
96
 
97
+ if (iteration % 2 === 0) setMode('console');
98
+ const logger = createLogger(loggerName);
99
+ if (iteration % 2 === 1) setMode('console');
89
100
  const methodName = loggerMethods[iteration % 3];
90
101
  logger[methodName](textToLog);
91
102
 
92
- const spyLogger = jest.spyOn(CONSOLE, methodName);
103
+ expect(getMode()).toBe('console');
104
+ expect(consoleMocks[methodName]).toHaveBeenCalledWith(loggerName, textToLog);
105
+ consoleMocks[methodName].mockClear();
93
106
 
94
- expect(getMode()).toBeNull();
95
- expect(spyLogger).not.toHaveBeenCalled();
96
107
  ++iteration;
97
108
  }), {
98
109
  numRuns: loggerMethods.length * 2,
99
110
  });
100
111
  });
101
112
 
102
- it('use logger with \'console\' mode', () => {
113
+ it('use logger with custom mode', () => {
103
114
  const loggerName = `[${faker.random.word()}]`;
104
115
  const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.random.word());
105
116
 
106
117
  let iteration = 0;
107
118
  fc.assert(
108
119
  fc.property(_textToLog, (textToLog) => {
109
-
110
- if (iteration % 2 === 0) setMode('console');
120
+ if (iteration % 2 === 0) setMode(customLoggerGetter);
111
121
  const logger = createLogger(loggerName);
112
- if (iteration % 2 === 1) setMode('console');
122
+ if (iteration % 2 === 1) setMode(customLoggerGetter);
123
+
113
124
  const methodName = loggerMethods[iteration % 3];
114
125
  logger[methodName](textToLog);
115
126
 
116
- const spyLogger = jest.spyOn(CONSOLE, methodName);
127
+ const impl = customLogger[methodName];
117
128
 
118
- expect(getMode()).toBe('console');
119
- expect(spyLogger).toHaveBeenCalledWith(loggerName, textToLog);
129
+ expect(getMode()).toBe(customLoggerGetter);
130
+ expect(impl).toHaveBeenCalledWith(loggerName, textToLog);
131
+ impl.mockClear();
120
132
  ++iteration;
121
133
  }), {
122
134
  numRuns: loggerMethods.length * 2,
123
135
  });
124
136
  });
125
137
 
126
- it('use logger with custom mode', () => {
138
+ it('use logger with override mode #override', () => {
139
+
127
140
  const loggerName = `[${faker.random.word()}]`;
128
141
  const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.random.word());
129
142
 
143
+ expect(Getter.getValue(customLoggerGetter as Getter<ILogger>)).toBe(customLogger);
144
+
145
+ setMode('console');
146
+
130
147
  let iteration = 0;
131
148
  fc.assert(
132
149
  fc.property(_textToLog, (textToLog) => {
133
- if (iteration % 2 === 0) setMode(customLogger);
134
- const logger = createLogger(loggerName);
135
- if (iteration % 2 === 1) setMode(customLogger);
150
+ const loggerDisabled = createLogger(loggerName, false);
151
+ const loggerCustom = createLogger(loggerName, customLoggerGetter);
152
+
153
+ const methodName = loggerMethods[iteration % 3];
154
+ loggerDisabled[methodName](textToLog);
155
+ expect(consoleMocks[methodName]).not.toHaveBeenCalled();
156
+
157
+ loggerCustom[methodName](textToLog);
158
+
159
+ expect(consoleMocks[methodName]).not.toHaveBeenCalled();
160
+
161
+ const impl = customLogger[methodName];
162
+ expect(impl).toHaveBeenCalledWith(loggerName, textToLog);
163
+ impl.mockClear();
164
+
165
+ ++iteration;
166
+ }), {
167
+ numRuns: loggerMethods.length,
168
+ });
169
+
170
+ expect(getMode()).toBe('console');
171
+
172
+ });
173
+
174
+ it('batches loggers', () => {
175
+
176
+ const loggerName = `[${faker.random.word()}]`;
177
+ const _textToLog: fc.Arbitrary<string> = toArbitrary(() => faker.random.word());
178
+
179
+ setMode(false);
180
+
181
+ let iteration = 0;
182
+ fc.assert(
183
+ fc.property(_textToLog, (textToLog) => {
184
+ const logger = batchLoggers(
185
+ createLogger(loggerName, 'console'),
186
+ createLogger(loggerName, customLoggerGetter),
187
+ );
136
188
 
137
189
  const methodName = loggerMethods[iteration % 3];
138
190
  logger[methodName](textToLog);
139
191
 
140
- const spyLogger = jest.spyOn(CONSOLE, methodName);
192
+ expect(consoleMocks[methodName]).toHaveBeenCalledWith(loggerName, textToLog);
193
+ const impl = customLogger[methodName];
194
+ expect(impl).toHaveBeenCalledWith(loggerName, textToLog);
195
+ impl.mockClear();
141
196
 
142
- expect(getMode()).toBe(customLogger);
143
- expect(spyLogger).toHaveBeenCalledWith(loggerName, textToLog);
144
197
  ++iteration;
145
198
  }), {
146
- numRuns: loggerMethods.length * 2,
199
+ numRuns: loggerMethods.length,
147
200
  });
201
+
202
+ expect(getMode()).toBe(false);
203
+
148
204
  });
149
205
 
150
206
  });
@@ -0,0 +1,9 @@
1
+ import { ILogger } from './abstractions';
2
+
3
+ export function batchLoggers(...loggers: ILogger[]): ILogger {
4
+ return {
5
+ log: (...args) => loggers.forEach(l => l.log(...args)),
6
+ warn: (...args) => loggers.forEach(l => l.warn(...args)),
7
+ error: (...args) => loggers.forEach(l => l.error(...args)),
8
+ };
9
+ }
@@ -4,12 +4,7 @@ import { NamedLogger } from './named';
4
4
  export const CONSOLE: ILogger = console;
5
5
 
6
6
  export class ConsoleLogger extends NamedLogger {
7
-
8
7
  protected get implementation() { return CONSOLE; }
9
-
10
- constructor(name?: string, enabled = true) {
11
- super(name, enabled);
12
- }
13
8
  }
14
9
 
15
10
  export class BufferedConsoleLogger implements ILogger {
@@ -0,0 +1,46 @@
1
+ import * as FS from 'fs';
2
+ import * as Util from 'util';
3
+ import { ILogger } from './abstractions';
4
+ import { Getter } from '../types';
5
+
6
+ export class FileLoggerNode implements ILogger {
7
+ private readonly _buffer: string[];
8
+
9
+ constructor(readonly fileName: Getter<string>, bufferMode = false) {
10
+ this._buffer = bufferMode ? [] : null;
11
+ }
12
+
13
+ /** @example Path.resolve(__dirname, `../run.${new Date().toISOString()}.log`) */
14
+ private get logFilePath() { return Getter.getValue(this.fileName); }
15
+
16
+ log = (...args: any[]) => {
17
+ this.appendToFile(...args);
18
+ };
19
+
20
+ warn(...args: any[]) {
21
+ this.appendToFile(...args);
22
+ }
23
+
24
+ error(...args: any[]) {
25
+ this.appendToFile(...args);
26
+ }
27
+
28
+ public flush() {
29
+ if (!this._buffer) {
30
+ return;
31
+ }
32
+
33
+ FS.writeFileSync(this.logFilePath, this._buffer.join(''));
34
+ this._buffer.length = 0;
35
+ }
36
+
37
+ private appendToFile(..._args: any[]) {
38
+ // @ts-ignore
39
+ const str = Util.format.apply(null, arguments) + '\n';
40
+ if (this._buffer) {
41
+ this._buffer.push(str);
42
+ } else {
43
+ FS.appendFileSync(this.logFilePath, str);
44
+ }
45
+ }
46
+ }
@@ -1,44 +1,36 @@
1
+ import { Getter } from '../types';
1
2
  import { ILogger, LoggerFunction } from './abstractions';
2
3
  import { CONSOLE, ConsoleLogger } from './console';
3
- import { EMPTY_LOGGER, ProxyLogger } from './proxy';
4
+ import { ProxyLogger } from './proxy';
5
+ import { batchLoggers } from './batch';
6
+ import { removeItem } from '../math';
4
7
 
5
8
  export { NamedLogger } from './named';
6
- export { ILogger, LoggerFunction };
7
- export { ConsoleLogger };
9
+ export { ILogger, LoggerFunction, ConsoleLogger, batchLoggers };
8
10
 
9
11
  // TBD Introduce more logger types ?
10
12
  export type LoggerTypes = 'console';
11
- export type LoggerFactory = (() => ILogger);
12
13
 
13
- let Mode: LoggerTypes | false | LoggerFactory = process.env.COMMON_UTILS_LOGGER as LoggerTypes || false;
14
+ let Mode: LoggerTypes | false | Getter<ILogger> = process.env.COMMON_UTILS_LOGGER as LoggerTypes || false;
14
15
 
15
16
  const proxies: ProxyLogger[] = [];
16
17
 
17
- function _createImplementation(): ILogger {
18
- switch (Mode) {
19
- case 'console': {
20
- return CONSOLE;
21
- }
22
-
23
- case false: {
24
- return EMPTY_LOGGER;
25
- }
26
-
27
- default: {
28
- if (typeof Mode === 'function') {
29
- return Mode();
30
- }
18
+ export function createLogger(name: string, mode: typeof Mode = undefined): ILogger {
19
+ const result = _createImplementation(mode);
20
+ const proxy = new ProxyLogger(result, name);
21
+ proxies.push(proxy);
22
+ return proxy;
23
+ }
31
24
 
32
- return undefined;
25
+ export function detachLogger(instance: ILogger, terminate = false) {
26
+ const item = removeItem(proxies, instance as ProxyLogger);
27
+ if (item) {
28
+ if (terminate) {
29
+ item.disable();
33
30
  }
31
+ return true;
34
32
  }
35
- }
36
-
37
- export function createLogger(name: string, forceDisable = false): ILogger {
38
- const result = _createImplementation();
39
- const proxy = new ProxyLogger(result, name, !forceDisable);
40
- proxies.push(proxy);
41
- return proxy;
33
+ return false;
42
34
  }
43
35
 
44
36
  export function setMode(mode: typeof Mode) {
@@ -57,6 +49,23 @@ export function setMode(mode: typeof Mode) {
57
49
 
58
50
  export function getMode() { return Mode; }
59
51
 
60
- const logger: ILogger = createLogger('', false);
52
+ const logger: ILogger = createLogger(null, false);
61
53
 
62
54
  export default logger;
55
+
56
+ function _createImplementation(overrideMode: typeof Mode = undefined): ILogger {
57
+ const mode = overrideMode !== undefined ? overrideMode : Mode;
58
+ switch (mode) {
59
+ case 'console': {
60
+ return CONSOLE;
61
+ }
62
+
63
+ case false: {
64
+ return null;
65
+ }
66
+
67
+ default: {
68
+ return Getter.getValue(mode as Getter<ILogger>);
69
+ }
70
+ }
71
+ }
@@ -28,15 +28,15 @@ export abstract class NamedLogger implements ILogger, ILoggerSwitchable {
28
28
 
29
29
  this.log = this._name
30
30
  ? (...args) => this.implementation.log(this._name, ...args)
31
- : (...args) => this.implementation.log(...args);
31
+ : this.implementation.log;
32
32
 
33
33
  this.warn = this._name
34
34
  ? (...args) => this.implementation.warn(this._name, ...args)
35
- : (...args) => this.implementation.warn(...args);
35
+ : this.implementation.warn;
36
36
 
37
37
  this.error = this._name
38
38
  ? (...args) => this.implementation.error(this._name, ...args)
39
- : (...args) => this.implementation.error(...args);
39
+ : this.implementation.error;
40
40
  }
41
41
 
42
42
  disable() {
@@ -13,8 +13,8 @@ export class ProxyLogger extends NamedLogger {
13
13
 
14
14
  private _logger: ILogger = null;
15
15
 
16
- constructor(logger: ILogger, name?: string, enabled = true) {
17
- super(name, enabled);
16
+ constructor(logger: ILogger, name: string) {
17
+ super(name, logger != null);
18
18
  this._logger = logger;
19
19
  }
20
20
 
@@ -64,6 +64,45 @@ describe('math/arrays', () => {
64
64
  expect(arrays.arrayFirstResult([1, 2, 3], i => i == 2 ? 'pass' : false)).toBe('pass');
65
65
  });
66
66
 
67
+ it('arraysCompare', () => {
68
+ expect(arrays.arraysCompare(null, null)).toBeNull();
69
+ expect(arrays.arraysCompare([], null)).toBeNull();
70
+
71
+ const result = (missing, extra, diff) => ({ missing, extra, diff });
72
+
73
+ expect(arrays.arraysCompare([], [])).toStrictEqual(result([], [], 0));
74
+ expect(arrays.arraysCompare([1], [])).toStrictEqual(result([1], [], 1));
75
+ expect(arrays.arraysCompare([1, 2], [1])).toStrictEqual(result([2], [], 1));
76
+ expect(arrays.arraysCompare([1], [1])).toStrictEqual(result([], [], 0));
77
+ expect(arrays.arraysCompare([], [1])).toStrictEqual(result([], [1], 1));
78
+ expect(arrays.arraysCompare([1, 2, 3], [3, 2, 1])).toStrictEqual(result([1, 3], [], 2));
79
+ });
80
+
81
+ it('arraysCompareDistinct', () => {
82
+ expect(arrays.arraysCompareDistinct(null, null)).toBeNull();
83
+ expect(arrays.arraysCompareDistinct([], null)).toBeNull();
84
+
85
+ const result = (missing, extra, diff) => ({ missing, extra, diff });
86
+
87
+ expect(arrays.arraysCompareDistinct([], [])).toStrictEqual(result([], [], 0));
88
+ expect(arrays.arraysCompareDistinct([1], [])).toStrictEqual(result([1], [], 1));
89
+ expect(arrays.arraysCompareDistinct([1, 2], [1])).toStrictEqual(result([2], [], 1));
90
+ expect(arrays.arraysCompareDistinct([1], [1])).toStrictEqual(result([], [], 0));
91
+ expect(arrays.arraysCompareDistinct([], [1])).toStrictEqual(result([], [1], 1));
92
+ expect(arrays.arraysCompareDistinct([1, 2], [2, 1, 1])).toStrictEqual(result([], [], 0));
93
+ expect(arrays.arraysCompareDistinct([1, 2, 3], [3, 2, 1])).toStrictEqual(result([], [], 0));
94
+ });
95
+
96
+ it('arrayDistinct', () => {
97
+ expect(arrays.arrayDistinct(null)).toStrictEqual([]);
98
+ expect(arrays.arrayDistinct([])).toStrictEqual([]);
99
+ expect(arrays.arrayDistinct([1])).toStrictEqual([1]);
100
+ expect(arrays.arrayDistinct([1, 2, 3])).toStrictEqual([1, 2, 3]);
101
+ expect(arrays.arrayDistinct([1, '2', 3])).toStrictEqual([1, '2', 3]);
102
+ expect(arrays.arrayDistinct([1, 1, 1])).toStrictEqual([1]);
103
+ expect(arrays.arrayDistinct(['1', '1', '1'])).toStrictEqual(['1']);
104
+ });
105
+
67
106
  it('normalize', () => {
68
107
  expect(arrays.normalize([])).toHaveLength(0);
69
108
 
@@ -85,8 +124,12 @@ describe('math/arrays', () => {
85
124
  });
86
125
 
87
126
  it('shuffle', () => {
127
+ expect(arrays.shuffle(null)).toStrictEqual([]);
128
+ expect(arrays.shuffle(null, true)).toStrictEqual([]);
129
+
88
130
  const arr1 = [1, 2, 3], arr2 = arr1.slice();
89
131
  arrays.shuffle(arr2);
132
+ arrays.shuffle(arr2, true);
90
133
  expect(arr2).toHaveLength(arr1.length);
91
134
  arr2.forEach(i => {
92
135
  expect(arr1).toContain(i);
@@ -138,6 +181,16 @@ describe('math/arrays', () => {
138
181
  expect(arrays.findIndexLeast(2, [3, 2, 1])).toBe(0);
139
182
  expect(arrays.findIndexLeast(2, [3, 2, 1], true)).toBe(2);
140
183
  expect(arrays.findIndexLeast(2, [1, 2, 3])).toBe(2);
184
+ });
185
+
186
+ it('removeItem', () => {
187
+ const check = (input: any[], remove: any, output: any[]) => {
188
+ arrays.removeItem(input, remove);
189
+ expect(input).toStrictEqual(output);
190
+ };
141
191
 
192
+ check([1, 2, 3], 1, [2, 3]);
193
+ check([1, 2, 3], item => item === 1, [2, 3]);
194
+ check([1, 2, 3], '1', [1, 2, 3]);
142
195
  });
143
196
  });
@@ -1,3 +1,4 @@
1
+ import { Comparator, Predicate } from '../types';
1
2
  import { random } from './calc';
2
3
 
3
4
  export function arrayCompareG<T>(arr: ReadonlyArray<T>, cond: (current: T, previous: T) => boolean): T {
@@ -81,6 +82,71 @@ export function arrayFirstResult<T, V>(arr: ReadonlyArray<T>, mapper: (o: T) =>
81
82
  return false;
82
83
  }
83
84
 
85
+ export function arraysCompare<T>(base: readonly T[], target: readonly T[], comparator?: Comparator<T>) {
86
+ if (!base || !target) {
87
+ return null;
88
+ }
89
+
90
+ const compare = comparator || Comparator.Default;
91
+ const result = {
92
+ missing: [] as T[],
93
+ extra: [] as T[],
94
+ diff: 0,
95
+ };
96
+
97
+ for (let i = 0; i < base.length; ++i) {
98
+ const baseItem = base[i];
99
+ const targetItem = target[i];
100
+ if (compare(baseItem, targetItem)) {
101
+ continue;
102
+ }
103
+
104
+ result.missing.push(baseItem);
105
+ }
106
+
107
+ for (let i = base.length; i < target.length; ++i) {
108
+ result.extra.push(target[i]);
109
+ }
110
+
111
+ result.diff = result.missing.length + result.extra.length;
112
+ return result;
113
+ }
114
+
115
+ export function arraysCompareDistinct<T>(base: readonly T[], target: readonly T[]) {
116
+ if (!base || !target) {
117
+ return null;
118
+ }
119
+
120
+ const baseSet = new Set(base);
121
+ const targetSet = new Set(target);
122
+ const result = {
123
+ missing: [] as T[],
124
+ extra: [] as T[],
125
+ diff: 0,
126
+ };
127
+
128
+ for (const item of baseSet) {
129
+ if (!targetSet.has(item)) {
130
+ result.missing.push(item);
131
+ } else {
132
+ // reduce iterations for 'extra'
133
+ targetSet.delete(item);
134
+ }
135
+ }
136
+
137
+ for (const item of targetSet) {
138
+ // all items left in 'target' is missing in 'base'
139
+ result.extra.push(item);
140
+ }
141
+
142
+ result.diff = result.missing.length + result.extra.length;
143
+ return result;
144
+ }
145
+
146
+ export function arrayDistinct<T>(arr: readonly T[]) {
147
+ return Array.from(new Set(arr || []));
148
+ }
149
+
84
150
  export function normalize(arr: number[]): number[] {
85
151
  if (arr.length === 0) {
86
152
  return arr;
@@ -172,3 +238,16 @@ export function findIndexLeast(num: number, arr: number[], sort = false) {
172
238
 
173
239
  return arr.findIndex(i => i > num);
174
240
  }
241
+
242
+ type NonFunction<T> = T extends Function ? never : T;
243
+
244
+ export function removeItem<T>(array: T[], item: NonFunction<T> | Predicate<T>): T {
245
+ const index = typeof item === 'function'
246
+ ? array.findIndex(item as Predicate<T>)
247
+ : array.indexOf(item);
248
+ if (index < 0) {
249
+ return null;
250
+ }
251
+
252
+ return array.splice(index, 1)[0];
253
+ }
@@ -20,7 +20,7 @@ export class TransitionObserver<T> implements IDisposable {
20
20
  private _promise: Promise<T> = null;
21
21
  private _promiseReject: (err?: any) => any = null;
22
22
 
23
- private logger: ILogger = createLogger('', true);
23
+ private logger: ILogger = createLogger('', false);
24
24
 
25
25
  constructor(getter?: () => T) {
26
26
  if (getter) {
@@ -115,7 +115,7 @@ export class TransitionObserver<T> implements IDisposable {
115
115
  }
116
116
 
117
117
  enableLogging(name: string) {
118
- this.logger = createLogger(name, !name);
118
+ this.logger = createLogger(name, name ? undefined : false);
119
119
  return this;
120
120
  }
121
121
 
package/src/types.ts CHANGED
@@ -9,7 +9,7 @@ export type Getter<T> = (() => T) | T | null;
9
9
  export namespace Getter {
10
10
  export function getValue<T>(getter: Getter<T>): T {
11
11
  if (getter == null) {
12
- return null;
12
+ return undefined;
13
13
  }
14
14
  if (typeof getter === 'function') {
15
15
  return (getter as () => T)();
@@ -19,3 +19,8 @@ export namespace Getter {
19
19
  }
20
20
 
21
21
  export type Predicate<T> = (value: T) => boolean;
22
+ export type Comparator<T, C = boolean> = (v1: T, v2: T) => C;
23
+
24
+ export namespace Comparator {
25
+ export const Default: Comparator<any> = (a, b) => a === b;
26
+ }
@@ -1,10 +1,9 @@
1
1
  import { action, makeObservable, observable } from 'mobx';
2
- import { ILabel } from './wrappers';
3
- import { IValueModel } from './ValuesCollector';
2
+ import { IResetableModel } from 'viewModels';
3
+ import { ILabel, IValueModel } from './types';
4
4
 
5
- export interface IFlagModel extends IValueModel<boolean> {
5
+ export interface IFlagModel extends IValueModel<boolean>, IResetableModel {
6
6
  toggle(): void;
7
- reset(): void;
8
7
  }
9
8
 
10
9
  export type IFlagModelReadonly = {
@@ -31,6 +30,8 @@ export class FlagModel implements IFlagModel, IFlagModelReadonly {
31
30
  this._value = value;
32
31
  }
33
32
 
33
+ get isDefault() { return this._value === false; }
34
+
34
35
  @action
35
36
  toggle = () => {
36
37
  this._value = !this._value;