@enspirit/emb 0.5.2 → 0.5.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.
Files changed (30) hide show
  1. package/README.md +61 -3
  2. package/dist/src/cli/commands/components/logs.js +4 -3
  3. package/dist/src/cli/commands/restart.d.ts +14 -0
  4. package/dist/src/cli/commands/restart.js +38 -0
  5. package/dist/src/cli/commands/stop.js +2 -2
  6. package/dist/src/cli/commands/tasks/index.js +1 -1
  7. package/dist/src/cli/commands/tasks/run.d.ts +1 -0
  8. package/dist/src/cli/commands/tasks/run.js +1 -0
  9. package/dist/src/config/schema.d.ts +6 -0
  10. package/dist/src/config/schema.json +7 -0
  11. package/dist/src/docker/compose/operations/ComposeExecOperation.d.ts +1 -0
  12. package/dist/src/docker/compose/operations/ComposeExecOperation.js +12 -1
  13. package/dist/src/docker/compose/operations/ComposeRestartOperation.d.ts +14 -0
  14. package/dist/src/docker/compose/operations/ComposeRestartOperation.js +46 -0
  15. package/dist/src/docker/compose/operations/index.d.ts +1 -0
  16. package/dist/src/docker/compose/operations/index.js +1 -0
  17. package/dist/src/docker/operations/containers/ContainerExecOperation.d.ts +0 -3
  18. package/dist/src/docker/operations/containers/ContainerExecOperation.js +3 -19
  19. package/dist/src/monorepo/config.js +1 -1
  20. package/dist/src/monorepo/operations/resources/BuildResourcesOperation.js +0 -1
  21. package/dist/src/monorepo/operations/shell/ExecuteLocalCommandOperation.js +1 -0
  22. package/dist/src/monorepo/operations/tasks/RunTasksOperation.d.ts +5 -1
  23. package/dist/src/monorepo/operations/tasks/RunTasksOperation.js +20 -4
  24. package/dist/src/monorepo/types.d.ts +6 -1
  25. package/dist/src/monorepo/utils/EMBCollection.d.ts +0 -3
  26. package/dist/src/monorepo/utils/EMBCollection.js +0 -14
  27. package/dist/src/monorepo/utils/index.d.ts +7 -4
  28. package/dist/src/monorepo/utils/index.js +14 -8
  29. package/oclif.manifest.json +83 -36
  30. package/package.json +1 -1
package/README.md CHANGED
@@ -14,7 +14,7 @@ $ npm install -g @enspirit/emb
14
14
  $ emb COMMAND
15
15
  running command...
16
16
  $ emb (--version)
17
- @enspirit/emb/0.5.2 darwin-x64 node-v22.18.0
17
+ @enspirit/emb/0.5.3 darwin-x64 node-v22.18.0
18
18
  $ emb --help [COMMAND]
19
19
  USAGE
20
20
  $ emb COMMAND
@@ -40,6 +40,8 @@ USAGE
40
40
  * [`emb ps`](#emb-ps)
41
41
  * [`emb resources`](#emb-resources)
42
42
  * [`emb resources build [COMPONENT]`](#emb-resources-build-component)
43
+ * [`emb restart [COMPONENT]`](#emb-restart-component)
44
+ * [`emb run TASK`](#emb-run-task)
43
45
  * [`emb shell COMPONENT`](#emb-shell-component)
44
46
  * [`emb stop`](#emb-stop)
45
47
  * [`emb tasks`](#emb-tasks)
@@ -132,7 +134,7 @@ ARGUMENTS
132
134
  COMPONENT The component you want to see the logs of
133
135
 
134
136
  FLAGS
135
- -f, --follow Follow log output
137
+ -f, --[no-]follow Follow log output
136
138
 
137
139
  DESCRIPTION
138
140
  Get components logs.
@@ -348,7 +350,7 @@ ARGUMENTS
348
350
  COMPONENT The component you want to see the logs of
349
351
 
350
352
  FLAGS
351
- -f, --follow Follow log output
353
+ -f, --[no-]follow Follow log output
352
354
 
353
355
  DESCRIPTION
354
356
  Get components logs.
@@ -431,6 +433,59 @@ EXAMPLES
431
433
  $ emb resources build build --flavor development
432
434
  ```
433
435
 
436
+ ## `emb restart [COMPONENT]`
437
+
438
+ Restart the whole project.
439
+
440
+ ```
441
+ USAGE
442
+ $ emb restart [COMPONENT...] [--json] [-f]
443
+
444
+ ARGUMENTS
445
+ COMPONENT... The component(s) to restart
446
+
447
+ FLAGS
448
+ -f, --no-deps Don't restart depdendent components
449
+
450
+ GLOBAL FLAGS
451
+ --json Format output as json.
452
+
453
+ DESCRIPTION
454
+ Restart the whole project.
455
+
456
+ EXAMPLES
457
+ $ emb restart
458
+ ```
459
+
460
+ ## `emb run TASK`
461
+
462
+ Run tasks.
463
+
464
+ ```
465
+ USAGE
466
+ $ emb run TASK... [--json] [-x container|local] [-a]
467
+
468
+ ARGUMENTS
469
+ TASK... List of tasks to run. You can provide either ids or names (eg: component:task or task)
470
+
471
+ FLAGS
472
+ -a, --all-matching Run all tasks matching (when multiple matches)
473
+ -x, --executor=<option> Where to run the task. (experimental!)
474
+ <options: container|local>
475
+
476
+ GLOBAL FLAGS
477
+ --json Format output as json.
478
+
479
+ DESCRIPTION
480
+ Run tasks.
481
+
482
+ ALIASES
483
+ $ emb run
484
+
485
+ EXAMPLES
486
+ $ emb run
487
+ ```
488
+
434
489
  ## `emb shell COMPONENT`
435
490
 
436
491
  Get a shell on a running component.
@@ -516,6 +571,9 @@ GLOBAL FLAGS
516
571
  DESCRIPTION
517
572
  Run tasks.
518
573
 
574
+ ALIASES
575
+ $ emb run
576
+
519
577
  EXAMPLES
520
578
  $ emb tasks run
521
579
  ```
@@ -10,8 +10,9 @@ export default class ComponentsLogs extends BaseCommand {
10
10
  follow: Flags.boolean({
11
11
  name: 'follow',
12
12
  char: 'f',
13
+ allowNo: true,
13
14
  description: 'Follow log output',
14
- default: false,
15
+ default: true,
15
16
  }),
16
17
  };
17
18
  static args = {
@@ -26,11 +27,11 @@ export default class ComponentsLogs extends BaseCommand {
26
27
  const { monorepo, docker } = await getContext();
27
28
  const component = monorepo.component(args.component);
28
29
  const containers = await monorepo.run(new ListContainersOperation(), {
30
+ all: true,
29
31
  filters: {
30
32
  label: [
31
33
  `emb/project=${monorepo.name}`,
32
34
  `emb/component=${component.name}`,
33
- `emb/flavor=${monorepo.currentFlavor}`,
34
35
  ],
35
36
  },
36
37
  });
@@ -47,7 +48,7 @@ export default class ComponentsLogs extends BaseCommand {
47
48
  stderr: true,
48
49
  stdout: true,
49
50
  });
50
- stream.pipe(process.stdout);
51
+ docker.modem.demuxStream(stream, process.stdout, process.stderr);
51
52
  }
52
53
  else {
53
54
  const res = await container.logs({
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../index.js';
2
+ export default class RestartComand extends BaseCommand {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ 'no-deps': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ static args: {
10
+ component: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
11
+ };
12
+ static strict: boolean;
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,38 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { BaseCommand, getContext } from '../index.js';
3
+ import { ComposeRestartOperation } from '../../docker/index.js';
4
+ export default class RestartComand extends BaseCommand {
5
+ static description = 'Restart the whole project.';
6
+ static enableJsonFlag = true;
7
+ static examples = ['<%= config.bin %> <%= command.id %>'];
8
+ static flags = {
9
+ 'no-deps': Flags.boolean({
10
+ char: 'f',
11
+ default: false,
12
+ description: "Don't restart depdendent components",
13
+ name: 'no-deps',
14
+ }),
15
+ };
16
+ static args = {
17
+ component: Args.string({
18
+ name: 'component',
19
+ description: 'The component(s) to restart',
20
+ }),
21
+ };
22
+ static strict = false;
23
+ async run() {
24
+ const { flags, argv } = await this.parse(RestartComand);
25
+ const { monorepo } = getContext();
26
+ let components;
27
+ if (argv.length > 0) {
28
+ components =
29
+ argv.length > 0
30
+ ? argv.map((name) => monorepo.component(name))
31
+ : monorepo.components;
32
+ }
33
+ await monorepo.run(new ComposeRestartOperation(), {
34
+ noDeps: flags['no-deps'],
35
+ services: components?.map((c) => c.name),
36
+ });
37
+ }
38
+ }
@@ -1,6 +1,6 @@
1
1
  import { Listr } from 'listr2';
2
2
  import { FlavoredCommand, getContext } from '../index.js';
3
- import { ComposeDownOperation } from '../../docker/index.js';
3
+ import { ComposeStopOperation } from '../../docker/index.js';
4
4
  export default class StopCommand extends FlavoredCommand {
5
5
  static description = 'Stop the whole project.';
6
6
  static enableJsonFlag = true;
@@ -12,7 +12,7 @@ export default class StopCommand extends FlavoredCommand {
12
12
  {
13
13
  rendererOptions: { persistentOutput: true },
14
14
  async task(ctx, task) {
15
- return monorepo.run(new ComposeDownOperation(task.stdout()), {});
15
+ return monorepo.run(new ComposeStopOperation(task.stdout()), {});
16
16
  },
17
17
  title: 'Stopping project',
18
18
  },
@@ -20,7 +20,7 @@ export default class TasksIndex extends BaseCommand {
20
20
  return 1;
21
21
  }
22
22
  // Compare components (if both not null)
23
- if (Boolean(ac) && Boolean(bc)) {
23
+ if (ac && bc && Boolean(ac) && Boolean(bc)) {
24
24
  const cmp = ac.localeCompare(bc);
25
25
  if (cmp !== 0) {
26
26
  return cmp;
@@ -1,5 +1,6 @@
1
1
  import { BaseCommand } from '../../index.js';
2
2
  export default class RunTask extends BaseCommand {
3
+ static aliases: string[];
3
4
  static args: {
4
5
  task: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
6
  };
@@ -3,6 +3,7 @@ import { Args, Flags } from '@oclif/core';
3
3
  import { BaseCommand } from '../../index.js';
4
4
  import { ExecutorType, RunTasksOperation } from '../../../monorepo/index.js';
5
5
  export default class RunTask extends BaseCommand {
6
+ static aliases = ['run'];
6
7
  static args = {
7
8
  task: Args.string({
8
9
  description: 'List of tasks to run. You can provide either ids or names (eg: component:task or task)',
@@ -12,6 +12,12 @@ export type TaskConfig = TaskConfig1 & {
12
12
  [k: string]: unknown;
13
13
  };
14
14
  pre?: Identifier[];
15
+ /**
16
+ * Variables to pass onto the task
17
+ */
18
+ vars?: {
19
+ [k: string]: string;
20
+ };
15
21
  };
16
22
  export type TaskConfig1 = {
17
23
  [k: string]: unknown;
@@ -136,6 +136,13 @@
136
136
  "items": {
137
137
  "$ref": "#/definitions/Identifier"
138
138
  }
139
+ },
140
+ "vars": {
141
+ "type": "object",
142
+ "description": "Variables to pass onto the task",
143
+ "additionalProperties": {
144
+ "type": "string"
145
+ }
139
146
  }
140
147
  },
141
148
  "additionalProperties": false
@@ -3,6 +3,7 @@ import * as z from 'zod';
3
3
  import { AbstractOperation } from '../../../operations/index.js';
4
4
  declare const schema: z.ZodObject<{
5
5
  command: z.ZodString;
6
+ env: z.ZodOptional<z.ZodObject<{}, z.core.$catchall<z.ZodString>>>;
6
7
  service: z.ZodString;
7
8
  }, z.core.$strip>;
8
9
  export declare class ComposeExecOperation extends AbstractOperation<typeof schema, void> {
@@ -4,6 +4,11 @@ import { ComposeExecError } from '../../../errors.js';
4
4
  import { AbstractOperation } from '../../../operations/index.js';
5
5
  const schema = z.object({
6
6
  command: z.string().describe('The command to execute on the service'),
7
+ env: z
8
+ .object()
9
+ .catchall(z.string())
10
+ .describe('Environment variables to pass to the execution')
11
+ .optional(),
7
12
  service: z
8
13
  .string()
9
14
  .describe('The name of the compose service to exec a shell'),
@@ -17,7 +22,13 @@ export class ComposeExecOperation extends AbstractOperation {
17
22
  async _run(input) {
18
23
  const { monorepo } = this.context;
19
24
  const cmd = 'docker';
20
- const args = ['compose', 'exec', input.service, input.command];
25
+ const args = ['compose', 'exec'];
26
+ // Add any env vars
27
+ Object.entries(input.env || {}).forEach(([key, value]) => {
28
+ args.push('-e', `${key.trim()}=${value.trim()}`);
29
+ });
30
+ // add component and script
31
+ args.push(input.service, input.command);
21
32
  const child = spawn(cmd, args, {
22
33
  stdio: 'pipe',
23
34
  shell: true,
@@ -0,0 +1,14 @@
1
+ import * as z from 'zod';
2
+ import { AbstractOperation } from '../../../operations/index.js';
3
+ /**
4
+ * https://docs.docker.com/reference/cli/docker/compose/restart/
5
+ */
6
+ declare const schema: z.ZodOptional<z.ZodObject<{
7
+ services: z.ZodOptional<z.ZodArray<z.ZodString>>;
8
+ noDeps: z.ZodOptional<z.ZodBoolean>;
9
+ }, z.core.$strip>>;
10
+ export declare class ComposeRestartOperation extends AbstractOperation<typeof schema, void> {
11
+ constructor();
12
+ protected _run(input: z.input<typeof schema>): Promise<void>;
13
+ }
14
+ export {};
@@ -0,0 +1,46 @@
1
+ import { getContext } from '../../../index.js';
2
+ import * as z from 'zod';
3
+ import { ExecuteLocalCommandOperation, taskManagerFactory } from '../../../monorepo/index.js';
4
+ import { AbstractOperation } from '../../../operations/index.js';
5
+ /**
6
+ * https://docs.docker.com/reference/cli/docker/compose/restart/
7
+ */
8
+ const schema = z
9
+ .object({
10
+ services: z
11
+ .array(z.string())
12
+ .optional()
13
+ .describe('The list of service to restart'),
14
+ noDeps: z.boolean().optional().describe("Don't restart dependent services"),
15
+ })
16
+ .optional();
17
+ export class ComposeRestartOperation extends AbstractOperation {
18
+ constructor() {
19
+ super(schema);
20
+ }
21
+ async _run(input) {
22
+ const { monorepo } = getContext();
23
+ const manager = taskManagerFactory();
24
+ const command = ['docker', 'compose', 'restart'];
25
+ if (input?.services) {
26
+ command.push(...input.services);
27
+ }
28
+ if (input?.noDeps) {
29
+ command.push('--no-deps');
30
+ }
31
+ manager.add([
32
+ {
33
+ async task(ctx, task) {
34
+ return monorepo.run(new ExecuteLocalCommandOperation(task.stdout()), {
35
+ script: command.join(' '),
36
+ workingDir: monorepo.rootDir,
37
+ });
38
+ },
39
+ title: input?.services
40
+ ? `Restarting ${input.services.join(', ')}`
41
+ : 'Restarting project',
42
+ },
43
+ ]);
44
+ await manager.runAll();
45
+ }
46
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './ComposeDownOperation.js';
2
2
  export * from './ComposeExecOperation.js';
3
3
  export * from './ComposeExecShellOperation.js';
4
+ export * from './ComposeRestartOperation.js';
4
5
  export * from './ComposeStopOperation.js';
5
6
  export * from './ComposeUpOperation.js';
@@ -1,5 +1,6 @@
1
1
  export * from './ComposeDownOperation.js';
2
2
  export * from './ComposeExecOperation.js';
3
3
  export * from './ComposeExecShellOperation.js';
4
+ export * from './ComposeRestartOperation.js';
4
5
  export * from './ComposeStopOperation.js';
5
6
  export * from './ComposeUpOperation.js';
@@ -5,9 +5,6 @@ import { AbstractOperation } from '../../../operations/index.js';
5
5
  * https://docs.docker.com/reference/api/engine/version/v1.37/#tag/Exec/operation/ContainerExec
6
6
  */
7
7
  declare const schema: z.ZodObject<{
8
- attachStderr: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
9
- attachStdin: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
10
- attachStdout: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
11
8
  container: z.ZodString;
12
9
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
13
10
  script: z.ZodString;
@@ -4,21 +4,6 @@ import { AbstractOperation } from '../../../operations/index.js';
4
4
  * https://docs.docker.com/reference/api/engine/version/v1.37/#tag/Exec/operation/ContainerExec
5
5
  */
6
6
  const schema = z.object({
7
- attachStderr: z
8
- .boolean()
9
- .default(false)
10
- .optional()
11
- .describe('Attach to `stderr` of the exec command.'),
12
- attachStdin: z
13
- .boolean()
14
- .default(false)
15
- .optional()
16
- .describe('Attach to `stdin` of the exec command.'),
17
- attachStdout: z
18
- .boolean()
19
- .default(false)
20
- .optional()
21
- .describe('Attach to `stdout` of the exec command.'),
22
7
  container: z.string().describe('ID or name of the container'),
23
8
  env: z
24
9
  .record(z.string(), z.string())
@@ -43,9 +28,8 @@ export class ContainerExecOperation extends AbstractOperation {
43
28
  return [...arr, `${key}=${value}`];
44
29
  }, []);
45
30
  const options = {
46
- AttachStderr: input.attachStderr,
47
- AttachStdin: input.attachStdin,
48
- AttachStdout: input.attachStdout,
31
+ AttachStderr: true,
32
+ AttachStdout: true,
49
33
  Cmd: ['bash', '-eu', '-o', 'pipefail', '-c', input.script],
50
34
  Env: envVars,
51
35
  Tty: input.tty,
@@ -53,7 +37,7 @@ export class ContainerExecOperation extends AbstractOperation {
53
37
  };
54
38
  const exec = await container.exec(options);
55
39
  const stream = await exec.start({});
56
- container.modem.demuxStream(stream, this.out || process.stdout, this.out || process.stderr);
40
+ exec.modem.demuxStream(stream, this.out || process.stdout, this.out || process.stderr);
57
41
  await new Promise((resolve, reject) => {
58
42
  const onError = (err) => reject(err);
59
43
  const onEnd = async () => {
@@ -16,7 +16,7 @@ export class MonorepoConfig {
16
16
  this.flavors = config.flavors || {};
17
17
  this.env = config.env || {};
18
18
  this.plugins = config.plugins || [];
19
- this.tasks = toIdentifedHash(config.tasks || {}, 'global');
19
+ this.tasks = toIdentifedHash(config.tasks || {});
20
20
  this.components = config.components || {};
21
21
  }
22
22
  component(id) {
@@ -34,7 +34,6 @@ export class BuildResourcesOperation extends AbstractOperation {
34
34
  const collection = new EMBCollection(monorepo.resources, {
35
35
  idField: 'id',
36
36
  depField: 'dependencies',
37
- forbidIdNameCollision: true,
38
37
  });
39
38
  const ordered = findRunOrder(input.resources || [], collection);
40
39
  const tasks = ordered.map((resource) => {
@@ -26,6 +26,7 @@ export class ExecuteLocalCommandOperation extends AbstractOperation {
26
26
  all: true,
27
27
  cwd: input.workingDir,
28
28
  shell: true,
29
+ env: input.env,
29
30
  });
30
31
  if (this.out) {
31
32
  process.all.pipe(this.out);
@@ -13,8 +13,12 @@ export type RunTasksOperationParams = {
13
13
  export type TaskWithScript = TaskInfo & {
14
14
  script: string;
15
15
  };
16
+ export type TaskWithScriptAndComponent = TaskInfo & {
17
+ script: string;
18
+ component: string;
19
+ };
16
20
  export declare class RunTasksOperation implements IOperation<RunTasksOperationParams, Array<TaskInfo>> {
17
21
  run(params: RunTasksOperationParams): Promise<Array<TaskInfo>>;
18
- protected runDocker(task: TaskWithScript, out?: Writable): Promise<void>;
22
+ protected runDocker(task: TaskWithScriptAndComponent, out?: Writable): Promise<void>;
19
23
  protected runLocal(task: TaskWithScript, out: Writable): Promise<import("stream").Readable>;
20
24
  }
@@ -1,6 +1,6 @@
1
1
  import { getContext } from '../../../index.js';
2
2
  import { PassThrough } from 'node:stream';
3
- import { ComposeExecOperation } from '../../../docker/index.js';
3
+ import { ContainerExecOperation, ListContainersOperation } from '../../../docker/index.js';
4
4
  import { EMBCollection, findRunOrder, taskManagerFactory, } from '../../index.js';
5
5
  import { ExecuteLocalCommandOperation } from '../index.js';
6
6
  export var ExecutorType;
@@ -58,9 +58,24 @@ export class RunTasksOperation {
58
58
  }
59
59
  async runDocker(task, out) {
60
60
  const { monorepo } = getContext();
61
- return monorepo.run(new ComposeExecOperation(out), {
62
- service: task.component,
63
- command: task.script,
61
+ const containers = await monorepo.run(new ListContainersOperation(), {
62
+ filters: {
63
+ label: [
64
+ `emb/project=${monorepo.name}`,
65
+ `emb/component=${task.component}`,
66
+ ],
67
+ },
68
+ });
69
+ if (containers.length === 0) {
70
+ throw new Error(`No container found for component \`${task.component}\``);
71
+ }
72
+ if (containers.length > 1) {
73
+ throw new Error(`More than one container found for component \`${task.component}\``);
74
+ }
75
+ return monorepo.run(new ContainerExecOperation(out), {
76
+ container: containers[0].Id,
77
+ script: task.script,
78
+ env: await monorepo.expand(task.vars || {}),
64
79
  });
65
80
  }
66
81
  async runLocal(task, out) {
@@ -71,6 +86,7 @@ export class RunTasksOperation {
71
86
  return monorepo.run(new ExecuteLocalCommandOperation(out), {
72
87
  script: task.script,
73
88
  workingDir: cwd,
89
+ env: await monorepo.expand(task.vars || {}),
74
90
  });
75
91
  }
76
92
  }
@@ -1,4 +1,9 @@
1
1
  import { ComponentFlavorConfig, IResourceConfig, ProjectFlavorConfig, TaskConfig } from '../config/types.js';
2
+ export type MaybeComponentIdentifiable<T> = T & {
3
+ id: string;
4
+ name: string;
5
+ component?: string;
6
+ };
2
7
  export type ComponentIdentifiable<T> = T & {
3
8
  id: string;
4
9
  name: string;
@@ -17,7 +22,7 @@ export type ComponentFlavorInfo = ComponentIdentifiable<ComponentFlavorConfig>;
17
22
  export type ComponentFlavors = {
18
23
  [k: string]: ComponentFlavorInfo;
19
24
  };
20
- export type TaskInfo = ComponentIdentifiable<TaskConfig>;
25
+ export type TaskInfo = MaybeComponentIdentifiable<TaskConfig>;
21
26
  export type Tasks = {
22
27
  [k: string]: TaskInfo;
23
28
  };
@@ -2,8 +2,6 @@ import { DepList } from './types.js';
2
2
  export type CollectionConfig<IDK extends PropertyKey, DPK extends PropertyKey> = {
3
3
  idField: IDK;
4
4
  depField: DPK;
5
- /** If true, throw when an item's id equals some other item's name (or vice versa). */
6
- forbidIdNameCollision?: boolean;
7
5
  };
8
6
  export declare class EMBCollection<T extends Partial<Record<DPK, DepList>> & Record<IDK, string> & {
9
7
  name: string;
@@ -11,7 +9,6 @@ export declare class EMBCollection<T extends Partial<Record<DPK, DepList>> & Rec
11
9
  private items;
12
10
  readonly idField: IDK;
13
11
  readonly depField: DPK;
14
- readonly forbidIdNameCollision: boolean;
15
12
  private byId;
16
13
  private byName;
17
14
  constructor(items: Iterable<T>, cfg: CollectionConfig<IDK, DPK>);
@@ -3,7 +3,6 @@ export class EMBCollection {
3
3
  items;
4
4
  idField;
5
5
  depField;
6
- forbidIdNameCollision;
7
6
  //
8
7
  byId;
9
8
  byName;
@@ -11,7 +10,6 @@ export class EMBCollection {
11
10
  this.items = [];
12
11
  this.idField = cfg.idField;
13
12
  this.depField = cfg.depField;
14
- this.forbidIdNameCollision = cfg.forbidIdNameCollision ?? true;
15
13
  this.byId = new Map();
16
14
  this.byName = new Map();
17
15
  // single-pass validation state
@@ -32,18 +30,6 @@ export class EMBCollection {
32
30
  this.byId.set(id, t);
33
31
  seenIds.add(id);
34
32
  }
35
- // --- Optional validation: forbid id <-> name collisions ---
36
- if (this.forbidIdNameCollision) {
37
- if (seenNames.has(id)) {
38
- const nameOwners = this.byName.get(id) ?? [];
39
- const ownerIds = nameOwners.map((o) => o[this.idField]).join(', ');
40
- collisions.push(`value \`${id}\` is an id of \`${name}\` and also a name of item(s) with id(s): [${ownerIds}]`);
41
- }
42
- if (seenIds.has(name)) {
43
- const idOwner = this.byId.get(name);
44
- collisions.push(`value \`${name}\` is a name of \`${t.name}\` and also an id of \`${idOwner.name}\``);
45
- }
46
- }
47
33
  // byName index
48
34
  const list = this.byName.get(name);
49
35
  if (list) {
@@ -1,7 +1,10 @@
1
- import { ComponentIdentifiable } from '../../index.js';
1
+ import { ComponentIdentifiable, MaybeComponentIdentifiable } from '../../index.js';
2
2
  export * from './EMBCollection.js';
3
3
  export * from './graph.js';
4
4
  export * from './types.js';
5
- export declare const toIdentifedHash: <V, T extends {
6
- [k: string]: V;
7
- }>(hash: T, parentName: string) => Record<string, ComponentIdentifiable<V>>;
5
+ export declare function toIdentifedHash<V, T extends Record<string, V>>(hash: T, parentName: string): {
6
+ [K in keyof T]: ComponentIdentifiable<T[K]>;
7
+ };
8
+ export declare function toIdentifedHash<V, T extends Record<string, V>>(hash: T, parentName?: undefined): {
9
+ [K in keyof T]: MaybeComponentIdentifiable<T[K]>;
10
+ };
@@ -1,14 +1,20 @@
1
1
  export * from './EMBCollection.js';
2
2
  export * from './graph.js';
3
3
  export * from './types.js';
4
- export const toIdentifedHash = (hash, parentName) => {
5
- return Object.entries(hash).reduce((hash, [key, value]) => {
6
- hash[key] = {
4
+ // implementation
5
+ export function toIdentifedHash(hash, parentName) {
6
+ const out = {};
7
+ for (const key in hash) {
8
+ if (!Object.hasOwn(hash, key)) {
9
+ continue;
10
+ }
11
+ const value = hash[key];
12
+ out[key] = {
7
13
  ...value,
8
- id: `${parentName}:${key}`,
14
+ id: parentName ? `${parentName}:${key}` : key,
9
15
  name: key,
10
- component: parentName,
16
+ ...(parentName ? { component: parentName } : {}),
11
17
  };
12
- return hash;
13
- }, {});
14
- };
18
+ }
19
+ return out;
20
+ }
@@ -80,6 +80,51 @@
80
80
  "down.js"
81
81
  ]
82
82
  },
83
+ "restart": {
84
+ "aliases": [],
85
+ "args": {
86
+ "component": {
87
+ "description": "The component(s) to restart",
88
+ "name": "component"
89
+ }
90
+ },
91
+ "description": "Restart the whole project.",
92
+ "examples": [
93
+ "<%= config.bin %> <%= command.id %>"
94
+ ],
95
+ "flags": {
96
+ "json": {
97
+ "description": "Format output as json.",
98
+ "helpGroup": "GLOBAL",
99
+ "name": "json",
100
+ "allowNo": false,
101
+ "type": "boolean"
102
+ },
103
+ "no-deps": {
104
+ "char": "f",
105
+ "description": "Don't restart depdendent components",
106
+ "name": "no-deps",
107
+ "allowNo": false,
108
+ "type": "boolean"
109
+ }
110
+ },
111
+ "hasDynamicHelp": false,
112
+ "hiddenAliases": [],
113
+ "id": "restart",
114
+ "pluginAlias": "@enspirit/emb",
115
+ "pluginName": "@enspirit/emb",
116
+ "pluginType": "core",
117
+ "strict": false,
118
+ "enableJsonFlag": true,
119
+ "isESM": true,
120
+ "relativePath": [
121
+ "dist",
122
+ "src",
123
+ "cli",
124
+ "commands",
125
+ "restart.js"
126
+ ]
127
+ },
83
128
  "stop": {
84
129
  "aliases": [],
85
130
  "args": {},
@@ -234,7 +279,7 @@
234
279
  "char": "f",
235
280
  "description": "Follow log output",
236
281
  "name": "follow",
237
- "allowNo": false,
282
+ "allowNo": true,
238
283
  "type": "boolean"
239
284
  }
240
285
  },
@@ -300,12 +345,10 @@
300
345
  "shell.js"
301
346
  ]
302
347
  },
303
- "containers": {
304
- "aliases": [
305
- "ps"
306
- ],
348
+ "config:print": {
349
+ "aliases": [],
307
350
  "args": {},
308
- "description": "List docker containers.",
351
+ "description": "Print the current config.",
309
352
  "examples": [
310
353
  "<%= config.bin %> <%= command.id %>"
311
354
  ],
@@ -317,22 +360,21 @@
317
360
  "allowNo": false,
318
361
  "type": "boolean"
319
362
  },
320
- "all": {
321
- "char": "a",
322
- "description": "Retun all containers. By default, only running containers are shown",
323
- "name": "all",
363
+ "flavor": {
364
+ "description": "Specify the flavor to use.",
365
+ "name": "flavor",
324
366
  "required": false,
325
- "allowNo": false,
326
- "type": "boolean"
367
+ "hasDynamicHelp": false,
368
+ "multiple": false,
369
+ "type": "option"
327
370
  }
328
371
  },
329
372
  "hasDynamicHelp": false,
330
373
  "hiddenAliases": [],
331
- "id": "containers",
374
+ "id": "config:print",
332
375
  "pluginAlias": "@enspirit/emb",
333
376
  "pluginName": "@enspirit/emb",
334
377
  "pluginType": "core",
335
- "strict": true,
336
378
  "enableJsonFlag": true,
337
379
  "isESM": true,
338
380
  "relativePath": [
@@ -340,14 +382,16 @@
340
382
  "src",
341
383
  "cli",
342
384
  "commands",
343
- "containers",
344
- "index.js"
385
+ "config",
386
+ "print.js"
345
387
  ]
346
388
  },
347
- "containers:prune": {
348
- "aliases": [],
389
+ "containers": {
390
+ "aliases": [
391
+ "ps"
392
+ ],
349
393
  "args": {},
350
- "description": "Prune containers.",
394
+ "description": "List docker containers.",
351
395
  "examples": [
352
396
  "<%= config.bin %> <%= command.id %>"
353
397
  ],
@@ -358,11 +402,19 @@
358
402
  "name": "json",
359
403
  "allowNo": false,
360
404
  "type": "boolean"
405
+ },
406
+ "all": {
407
+ "char": "a",
408
+ "description": "Retun all containers. By default, only running containers are shown",
409
+ "name": "all",
410
+ "required": false,
411
+ "allowNo": false,
412
+ "type": "boolean"
361
413
  }
362
414
  },
363
415
  "hasDynamicHelp": false,
364
416
  "hiddenAliases": [],
365
- "id": "containers:prune",
417
+ "id": "containers",
366
418
  "pluginAlias": "@enspirit/emb",
367
419
  "pluginName": "@enspirit/emb",
368
420
  "pluginType": "core",
@@ -375,13 +427,13 @@
375
427
  "cli",
376
428
  "commands",
377
429
  "containers",
378
- "prune.js"
430
+ "index.js"
379
431
  ]
380
432
  },
381
- "config:print": {
433
+ "containers:prune": {
382
434
  "aliases": [],
383
435
  "args": {},
384
- "description": "Print the current config.",
436
+ "description": "Prune containers.",
385
437
  "examples": [
386
438
  "<%= config.bin %> <%= command.id %>"
387
439
  ],
@@ -392,22 +444,15 @@
392
444
  "name": "json",
393
445
  "allowNo": false,
394
446
  "type": "boolean"
395
- },
396
- "flavor": {
397
- "description": "Specify the flavor to use.",
398
- "name": "flavor",
399
- "required": false,
400
- "hasDynamicHelp": false,
401
- "multiple": false,
402
- "type": "option"
403
447
  }
404
448
  },
405
449
  "hasDynamicHelp": false,
406
450
  "hiddenAliases": [],
407
- "id": "config:print",
451
+ "id": "containers:prune",
408
452
  "pluginAlias": "@enspirit/emb",
409
453
  "pluginName": "@enspirit/emb",
410
454
  "pluginType": "core",
455
+ "strict": true,
411
456
  "enableJsonFlag": true,
412
457
  "isESM": true,
413
458
  "relativePath": [
@@ -415,8 +460,8 @@
415
460
  "src",
416
461
  "cli",
417
462
  "commands",
418
- "config",
419
- "print.js"
463
+ "containers",
464
+ "prune.js"
420
465
  ]
421
466
  },
422
467
  "images:delete": {
@@ -691,7 +736,9 @@
691
736
  ]
692
737
  },
693
738
  "tasks:run": {
694
- "aliases": [],
739
+ "aliases": [
740
+ "run"
741
+ ],
695
742
  "args": {
696
743
  "task": {
697
744
  "description": "List of tasks to run. You can provide either ids or names (eg: component:task or task)",
@@ -750,5 +797,5 @@
750
797
  ]
751
798
  }
752
799
  },
753
- "version": "0.5.2"
800
+ "version": "0.5.3"
754
801
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@enspirit/emb",
3
3
  "type": "module",
4
- "version": "0.5.2",
4
+ "version": "0.5.3",
5
5
  "keywords": [
6
6
  "monorepo",
7
7
  "docker",