goblin-laboratory 4.8.0 → 4.10.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/termux.js CHANGED
@@ -297,9 +297,9 @@ class Termux extends Elf.Alone {
297
297
 
298
298
  async askForCompletion(input) {
299
299
  const prompt = getPrompt(this.user);
300
- const tools = Object.keys(this._tools).filter((tool) =>
301
- tool.startsWith(input)
302
- );
300
+ const tools = Object.keys(this._tools)
301
+ .filter((tool) => tool.startsWith(input))
302
+ .sort();
303
303
  this.logic.askForCompletion(prompt, input, tools);
304
304
 
305
305
  const {completion} = this.state;
@@ -330,6 +330,9 @@ class Termux extends Elf.Alone {
330
330
  for (const item of items) {
331
331
  if (item in desc) {
332
332
  desc = desc[item];
333
+ if (!desc) {
334
+ continue;
335
+ }
333
336
  if (!Array.isArray(desc)) {
334
337
  cmds = Object.keys(desc);
335
338
  continue;
@@ -451,6 +454,58 @@ class Termux extends Elf.Alone {
451
454
  return '';
452
455
  }
453
456
 
457
+ async metrics$tool(horde, output) {
458
+ const {topology} = require('xcraft-core-etc')().load('xcraft-core-horde');
459
+ const _xcraftRPC = topology?.[horde]?.passive;
460
+
461
+ /* FIXME: must be moved on the server side */
462
+ if (_xcraftRPC && this.user.rank !== 'admin') {
463
+ throw new Error('Forbidden');
464
+ }
465
+
466
+ let metrics = await this.quest.cmd(`bus.${horde}.xcraftMetrics`, {
467
+ from: this.id,
468
+ _xcraftRPC,
469
+ });
470
+ metrics = JSON.stringify(metrics, null, 2);
471
+
472
+ if (!output) {
473
+ return metrics;
474
+ }
475
+
476
+ const fse = require('fs-extra');
477
+ await fse.writeFile(output, metrics);
478
+ return 'Metrics saved to: ' + output;
479
+ }
480
+
481
+ async heapdump$tool(horde) {
482
+ const {topology} = require('xcraft-core-etc')().load('xcraft-core-horde');
483
+ const _xcraftRPC = topology?.[horde]?.passive;
484
+
485
+ /* FIXME: must be moved on the server side */
486
+ if (_xcraftRPC && this.user.rank !== 'admin') {
487
+ throw new Error('Forbidden');
488
+ }
489
+
490
+ const output = await this.quest.cmd(`bus.${horde}.heapdump`, {_xcraftRPC});
491
+ return output;
492
+ }
493
+
494
+ async malloctrim$tool(horde) {
495
+ const {topology} = require('xcraft-core-etc')().load('xcraft-core-horde');
496
+ const _xcraftRPC = topology?.[horde]?.passive;
497
+
498
+ /* FIXME: must be moved on the server side */
499
+ if (_xcraftRPC && this.user.rank !== 'admin') {
500
+ throw new Error('Forbidden');
501
+ }
502
+
503
+ const output = await this.quest.cmd(`bus.${horde}.malloctrim`, {
504
+ _xcraftRPC,
505
+ });
506
+ return output;
507
+ }
508
+
454
509
  async $tool(tool) {
455
510
  if (tool === 'man') {
456
511
  return {
@@ -475,6 +530,36 @@ class Termux extends Elf.Alone {
475
530
  return autocomp;
476
531
  }, {});
477
532
  }
533
+ if (tool === 'metrics') {
534
+ const {resp} = this.quest;
535
+ const registry = resp.getCommandsRegistry();
536
+ return Object.keys(registry)
537
+ .filter((cmd) => /^bus[.][^.]+[.]xcraftMetrics$/.test(cmd))
538
+ .reduce((autocomp, cmd) => {
539
+ autocomp[cmd.split('.')[1]] = null;
540
+ return autocomp;
541
+ }, {});
542
+ }
543
+ if (tool === 'heapdump') {
544
+ const {resp} = this.quest;
545
+ const registry = resp.getCommandsRegistry();
546
+ return Object.keys(registry)
547
+ .filter((cmd) => /^bus[.][^.]+[.]heapdump$/.test(cmd))
548
+ .reduce((autocomp, cmd) => {
549
+ autocomp[cmd.split('.')[1]] = null;
550
+ return autocomp;
551
+ }, {});
552
+ }
553
+ if (tool === 'malloctrim') {
554
+ const {resp} = this.quest;
555
+ const registry = resp.getCommandsRegistry();
556
+ return Object.keys(registry)
557
+ .filter((cmd) => /^bus[.][^.]+[.]malloctrim$/.test(cmd))
558
+ .reduce((autocomp, cmd) => {
559
+ autocomp[cmd.split('.')[1]] = null;
560
+ return autocomp;
561
+ }, {});
562
+ }
478
563
  return {};
479
564
  }
480
565
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goblin-laboratory",
3
- "version": "4.8.0",
3
+ "version": "4.10.0",
4
4
  "description": "Laboratory",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -26,6 +26,7 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
+ "fs-extra": "^11.3.3",
29
30
  "goblin-theme": "^2.0.0",
30
31
  "xcraft-core-goblin": "^5.0.0",
31
32
  "xcraft-core-log": "^2.2.0",
@@ -57,4 +58,4 @@
57
58
  "xcraft-traverse": "^0.7.0"
58
59
  },
59
60
  "prettier": "xcraft-dev-prettier"
60
- }
61
+ }
@@ -77,7 +77,7 @@ export default function withC(Component, dispatchProps = {}, {modelProp} = {}) {
77
77
  // It applies "inFunc" to the connected props and
78
78
  // prevents giving internal props (starting with "_") to the underlying component
79
79
  const ConnectedPropsMapper = (props) => {
80
- let {_connectedProps, _connectedProp, ...otherProps} = props;
80
+ let {_connectedProps, _connectedProp, _model, ...otherProps} = props;
81
81
  const newProps = {};
82
82
  for (const prop of _connectedProps) {
83
83
  const inFunc = prop.inFunc;
@@ -158,7 +158,7 @@ export default function withC(Component, dispatchProps = {}, {modelProp} = {}) {
158
158
  if (path === null || path === undefined) {
159
159
  return null;
160
160
  }
161
- const model = this.props.model || this.context.model;
161
+ const model = this.props._model || this.context.model;
162
162
  return joinModels(model, path);
163
163
  }
164
164
 
@@ -246,7 +246,8 @@ export default function withC(Component, dispatchProps = {}, {modelProp} = {}) {
246
246
 
247
247
  // Render function used when there is no connected prop
248
248
  renderNotConnected() {
249
- return <Component {...this.props} />;
249
+ const {_model, ...props} = this.props;
250
+ return <Component {...props} />;
250
251
  }
251
252
 
252
253
  render() {
@@ -276,7 +277,7 @@ export default function withC(Component, dispatchProps = {}, {modelProp} = {}) {
276
277
 
277
278
  return (props) => (
278
279
  <ModelContext.Consumer>
279
- {(model) => <WithC model={model} {...props} />}
280
+ {(model) => <WithC _model={model} {...props} />}
280
281
  </ModelContext.Consumer>
281
282
  );
282
283
  }
@@ -48,7 +48,7 @@ class Renderer {
48
48
  }
49
49
 
50
50
  newBackendState(transitState) {
51
- setTimeout(() =>
51
+ queueMicrotask(() =>
52
52
  this.store.dispatch({
53
53
  type: 'NEW_BACKEND_STATE',
54
54
  data: transitState,
@@ -36,5 +36,18 @@ export default function configureStore(initialState, history, send) {
36
36
  store.replaceReducer(nextRootReducer);
37
37
  });
38
38
  }
39
+
40
+ const subscribe = store.subscribe.bind(store);
41
+ store.subscribe = (listener) => {
42
+ let previousState = undefined;
43
+ return subscribe(() => {
44
+ const state = store.getState();
45
+ if (state !== previousState) {
46
+ previousState = state;
47
+ listener();
48
+ }
49
+ });
50
+ };
51
+
39
52
  return store;
40
53
  }
@@ -5,15 +5,28 @@ import Shredder from 'xcraft-core-shredder';
5
5
  import shallowEqualShredder from './shallowEqualShredder';
6
6
  import wrapMapStateToProps from './wrapMapStateToProps';
7
7
 
8
+ let lastState = null;
9
+ let lastShredder = null;
10
+
11
+ function stateToShredder(state) {
12
+ if (state === lastState) {
13
+ return lastShredder;
14
+ }
15
+ const s = new Shredder({
16
+ backend: state.backend,
17
+ widgets: state.widgets,
18
+ network: state.network,
19
+ });
20
+ lastState = state;
21
+ lastShredder = s;
22
+ return s;
23
+ }
24
+
8
25
  function withShredder(mapStateToProps) {
9
26
  mapStateToProps = wrapMapStateToProps(mapStateToProps);
10
27
 
11
28
  const mapStateToPropsWithOwnProps = (state, ownProps) => {
12
- const s = new Shredder({
13
- backend: state.backend,
14
- widgets: state.widgets,
15
- network: state.network,
16
- });
29
+ const s = stateToShredder(state);
17
30
  return mapStateToProps(s, ownProps);
18
31
  };
19
32