@platforma-sdk/block-tools 2.11.2 → 2.11.4

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 (89) hide show
  1. package/bin/run.js +9 -5
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.js +13 -13
  4. package/dist/cli.js.map +1 -1
  5. package/dist/cli.mjs +845 -981
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/cmd/build-meta.d.ts +2 -9
  8. package/dist/cmd/build-model.d.ts +2 -10
  9. package/dist/cmd/list-overview-snapshots.d.ts +2 -9
  10. package/dist/cmd/mark-stable.d.ts +2 -13
  11. package/dist/cmd/pack.d.ts +2 -9
  12. package/dist/cmd/publish.d.ts +2 -12
  13. package/dist/cmd/refresh-registry.d.ts +2 -9
  14. package/dist/cmd/restore-overview-from-snapshot.d.ts +2 -10
  15. package/dist/cmd/structure/check.d.ts +2 -10
  16. package/dist/cmd/structure/init.d.ts +2 -18
  17. package/dist/cmd/structure/refresh.d.ts +10 -11
  18. package/dist/cmd/update-deps.d.ts +2 -6
  19. package/dist/cmd/upload-package-v1.d.ts +2 -14
  20. package/dist/config-BmJWc8sR.js +3 -0
  21. package/dist/config-BmJWc8sR.js.map +1 -0
  22. package/dist/{config-DpfTzcgG.mjs → config-CiC347sB.mjs} +453 -398
  23. package/dist/config-CiC347sB.mjs.map +1 -0
  24. package/dist/index.js +1 -1
  25. package/dist/index.mjs +15 -15
  26. package/dist/registry_v1/config_schema.d.ts +7 -7
  27. package/dist/registry_v1/flags.d.ts +2 -4
  28. package/dist/structure/cli/run-structure.d.ts +1 -1
  29. package/dist/structure/engine/discovery.d.ts +2 -3
  30. package/dist/structure/rules/block-package-json.d.ts +3 -3
  31. package/dist/structure/rules/shared/pascal-case.d.ts +1 -0
  32. package/dist/v2/index.d.ts +1 -0
  33. package/dist/v2/model/block_components.d.ts +11 -0
  34. package/dist/v2/model/block_meta.d.ts +7 -0
  35. package/dist/v2/resolve_to_registry.d.ts +37 -0
  36. package/dist/v2/source_package.d.ts +12 -0
  37. package/package.json +9 -24
  38. package/src/cli.test.ts +56 -0
  39. package/src/cli.ts +50 -0
  40. package/src/cmd/build-meta.ts +11 -22
  41. package/src/cmd/build-model.ts +16 -33
  42. package/src/cmd/list-overview-snapshots.ts +24 -28
  43. package/src/cmd/mark-stable.ts +35 -55
  44. package/src/cmd/pack.ts +11 -22
  45. package/src/cmd/publish.test.ts +35 -0
  46. package/src/cmd/publish.ts +55 -49
  47. package/src/cmd/refresh-registry.ts +22 -26
  48. package/src/cmd/restore-overview-from-snapshot.ts +24 -34
  49. package/src/cmd/structure/check.ts +19 -28
  50. package/src/cmd/structure/init.ts +40 -47
  51. package/src/cmd/structure/refresh.ts +45 -36
  52. package/src/cmd/update-deps.ts +22 -12
  53. package/src/cmd/upload-package-v1.ts +42 -60
  54. package/src/registry_v1/flags.ts +7 -7
  55. package/src/structure/__tests__/discovery-shortname.test.ts +3 -0
  56. package/src/structure/__tests__/facade-end-state.test.ts +195 -0
  57. package/src/structure/__tests__/model-package-json.snapshot.test.ts +1 -1
  58. package/src/structure/cli/run-structure.ts +2 -2
  59. package/src/structure/engine/__tests__/discovery.test.ts +22 -26
  60. package/src/structure/engine/discovery.ts +12 -30
  61. package/src/structure/rules/block-package-json.ts +146 -64
  62. package/src/structure/rules/block.ts +40 -5
  63. package/src/structure/rules/model-package-json.ts +6 -1
  64. package/src/structure/rules/shared/pascal-case.ts +16 -0
  65. package/src/structure/rules/software-package-json.ts +6 -1
  66. package/src/structure/rules/test-package-json.ts +5 -1
  67. package/src/structure/rules/ui-package-json.ts +5 -1
  68. package/src/structure/rules/workflow-package-json.ts +5 -1
  69. package/src/structure/templates/static/block/src/AGENTS.ts +5 -0
  70. package/src/structure/templates/static/block/src/agents-extra.ts +8 -0
  71. package/src/structure/templates/static/block/src/block-extra.ts +9 -0
  72. package/src/structure/templates/static/block/tsconfig.json +4 -0
  73. package/src/structure/templates/text/block/src/index.tpl.ts +55 -0
  74. package/src/v2/from_pack_v2.test.ts +142 -0
  75. package/src/v2/index.ts +1 -0
  76. package/src/v2/model/block_components.ts +33 -0
  77. package/src/v2/model/block_meta.ts +18 -0
  78. package/src/v2/model/content_conversion.ts +0 -1
  79. package/src/v2/resolve_to_registry.ts +84 -0
  80. package/src/v2/source_package.ts +34 -1
  81. package/bin/dev.cmd +0 -3
  82. package/bin/dev.js +0 -6
  83. package/dist/cmd/index.d.ts +0 -28
  84. package/dist/config-DpfTzcgG.mjs.map +0 -1
  85. package/dist/config-xxVy3IQR.js +0 -3
  86. package/dist/config-xxVy3IQR.js.map +0 -1
  87. package/src/cmd/index.ts +0 -31
  88. package/src/structure/templates/static/block/index.d.ts +0 -6
  89. package/src/structure/templates/static/block/index.js +0 -8
@@ -1,46 +1,42 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command, Option } from "commander";
2
2
  import { BlockRegistryV2 } from "../v2/registry/registry";
3
3
  import { storageByUrl } from "../io/storage";
4
- import { OclifLoggerAdapter } from "@milaboratories/ts-helpers-oclif";
4
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
5
5
 
6
- export default class ListOverviewSnapshots extends Command {
7
- static description = "List all available global overview snapshots in the registry";
6
+ export function listOverviewSnapshotsCommand(): Command {
7
+ const cmd = new Command("list-overview-snapshots").description(
8
+ "List all available global overview snapshots in the registry",
9
+ );
8
10
 
9
- static flags = {
10
- registry: Flags.string({
11
- char: "r",
12
- summary: "full address of the registry",
13
- helpValue: "<address>",
14
- env: "PL_REGISTRY",
15
- required: true,
16
- }),
11
+ cmd.addOption(
12
+ new Option("-r, --registry <address>", "full address of the registry")
13
+ .env("PL_REGISTRY")
14
+ .makeOptionMandatory(),
15
+ );
16
+ cmd.option("--json", "output in JSON format", false);
17
17
 
18
- json: Flags.boolean({
19
- summary: "output in JSON format",
20
- default: false,
21
- }),
22
- };
23
-
24
- public async run(): Promise<void> {
25
- const { flags } = await this.parse(ListOverviewSnapshots);
18
+ cmd.action(async (flags) => {
19
+ const logger = new ConsoleLoggerAdapter();
26
20
  const storage = storageByUrl(flags.registry);
27
- const registry = new BlockRegistryV2(storage, new OclifLoggerAdapter(this));
21
+ const registry = new BlockRegistryV2(storage, logger);
28
22
 
29
23
  const snapshots = await registry.listGlobalOverviewSnapshots();
30
24
 
31
25
  if (flags.json) {
32
- this.log(JSON.stringify(snapshots, null, 2));
26
+ logger.info(JSON.stringify(snapshots, null, 2));
33
27
  } else {
34
28
  if (snapshots.length === 0) {
35
- this.log("No snapshots found.");
29
+ logger.info("No snapshots found.");
36
30
  } else {
37
- this.log(`Found ${snapshots.length} snapshot(s):\n`);
31
+ logger.info(`Found ${snapshots.length} snapshot(s):\n`);
38
32
  for (const snapshot of snapshots) {
39
- this.log(` ${snapshot.timestamp}`);
40
- this.log(` Path: ${snapshot.path}`);
41
- this.log("");
33
+ logger.info(` ${snapshot.timestamp}`);
34
+ logger.info(` Path: ${snapshot.path}`);
35
+ logger.info("");
42
36
  }
43
37
  }
44
38
  }
45
- }
39
+ });
40
+
41
+ return cmd;
46
42
  }
@@ -1,67 +1,47 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command, Option } from "commander";
2
2
  import { BlockRegistryV2, loadPackDescriptionRaw } from "../v2";
3
3
  import path from "node:path";
4
4
  import { overrideDescriptionVersion, StableChannel } from "@milaboratories/pl-model-middle-layer";
5
5
  import { storageByUrl } from "../io";
6
- import { OclifLoggerAdapter } from "@milaboratories/ts-helpers-oclif";
7
-
8
- export default class MarkStable extends Command {
9
- static description = "Mark target block stable";
10
-
11
- static flags = {
12
- modulePath: Flags.string({
13
- char: "i",
14
- summary: "input module path",
15
- helpValue: "<path>",
16
- default: ".",
17
- }),
18
-
19
- channel: Flags.string({
20
- char: "c",
21
- hidden: true,
22
- summary: "custom channel",
23
- helpValue: "<channel name>",
24
- default: StableChannel,
25
- }),
26
-
27
- "version-override": Flags.file({
28
- char: "v",
29
- summary: "override package version",
30
- }),
31
-
32
- registry: Flags.string({
33
- char: "r",
34
- summary: "full address of the registry",
35
- helpValue: "<address>",
36
- env: "PL_REGISTRY",
37
- required: true,
38
- }),
39
-
40
- refresh: Flags.boolean({
41
- summary: "refresh repository after adding the package",
42
- default: true,
43
- allowNo: true,
44
- env: "PL_REGISTRY_REFRESH",
45
- }),
46
-
47
- unmark: Flags.boolean({
48
- summary:
49
- 'reverses meaning of this command, flag can be used to remove "stable" flag from the package',
50
- default: false,
51
- }),
52
- };
53
-
54
- public async run(): Promise<void> {
55
- const { flags } = await this.parse(MarkStable);
6
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
7
+
8
+ export function markStableCommand(): Command {
9
+ const cmd = new Command("mark-stable").description("Mark target block stable");
10
+
11
+ cmd.option("-i, --modulePath <path>", "input module path", ".");
12
+ cmd.addOption(
13
+ new Option("-c, --channel <channel name>", "custom channel").default(StableChannel).hideHelp(),
14
+ );
15
+ cmd.option("-v, --version-override <path>", "override package version");
16
+ cmd.addOption(
17
+ new Option("-r, --registry <address>", "full address of the registry")
18
+ .env("PL_REGISTRY")
19
+ .makeOptionMandatory(),
20
+ );
21
+ cmd.addOption(
22
+ new Option("--refresh", "refresh repository after adding the package")
23
+ .default(true)
24
+ .env("PL_REGISTRY_REFRESH"),
25
+ );
26
+ cmd.option("--no-refresh", "do not refresh repository after adding the package");
27
+ cmd.option(
28
+ "--unmark",
29
+ 'reverses meaning of this command, flag can be used to remove "stable" flag from the package',
30
+ false,
31
+ );
32
+
33
+ cmd.action(async (flags) => {
56
34
  let description = await loadPackDescriptionRaw(path.resolve(flags.modulePath));
57
- if (flags["version-override"])
58
- description = overrideDescriptionVersion(description, flags["version-override"]);
35
+ if (flags.versionOverride)
36
+ description = overrideDescriptionVersion(description, flags.versionOverride);
59
37
  const storage = storageByUrl(flags.registry);
60
- const registry = new BlockRegistryV2(storage, new OclifLoggerAdapter(this));
38
+ const registry = new BlockRegistryV2(storage, new ConsoleLoggerAdapter());
61
39
 
62
40
  if (flags.unmark) await registry.removePackageFromChannel(description.id, flags.channel);
63
41
  else await registry.addPackageToChannel(description.id, flags.channel);
64
42
 
65
43
  if (flags.refresh) await registry.updateIfNeeded();
66
- }
44
+ });
45
+
46
+ return cmd;
67
47
  }
package/src/cmd/pack.ts CHANGED
@@ -1,32 +1,21 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command } from "commander";
2
2
  import { loadPackDescription } from "../v2/source_package";
3
3
  import path from "node:path";
4
4
  import { buildBlockPackDist } from "../v2/build_dist";
5
5
 
6
- export default class PackBlock extends Command {
7
- static description =
6
+ export function packCommand(): Command {
7
+ const cmd = new Command("pack").description(
8
8
  "Builds block pack and outputs a block pack manifest consolidating all " +
9
- "references assets into a single folder";
9
+ "references assets into a single folder",
10
+ );
10
11
 
11
- static flags = {
12
- modulePath: Flags.string({
13
- char: "i",
14
- summary: "input module path",
15
- helpValue: "<path>",
16
- default: ".",
17
- }),
12
+ cmd.option("-i, --modulePath <path>", "input module path", ".");
13
+ cmd.option("-o, --destinationPath <path>", "output folder", "./block-pack");
18
14
 
19
- destinationPath: Flags.string({
20
- char: "o",
21
- summary: "output folder",
22
- helpValue: "<path>",
23
- default: "./block-pack",
24
- }),
25
- };
26
-
27
- public async run(): Promise<void> {
28
- const { flags } = await this.parse(PackBlock);
15
+ cmd.action(async (flags) => {
29
16
  const description = await loadPackDescription(path.resolve(flags.modulePath));
30
17
  await buildBlockPackDist(description, path.resolve(flags.destinationPath));
31
- }
18
+ });
19
+
20
+ return cmd;
32
21
  }
@@ -0,0 +1,35 @@
1
+ import { test, expect } from "vitest";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import fsp from "node:fs/promises";
5
+ import { publishCommand } from "./publish";
6
+
7
+ function option(cmd: ReturnType<typeof publishCommand>, long: string) {
8
+ return cmd.options.find((o) => o.long === long);
9
+ }
10
+
11
+ test("publish keeps --registry required and adds a required --registry-serve-url", () => {
12
+ const cmd = publishCommand();
13
+ expect(option(cmd, "--registry")?.mandatory).toBe(true);
14
+ expect(option(cmd, "--registry-serve-url")?.mandatory).toBe(true);
15
+ });
16
+
17
+ test("publish errors when the serve URL is absent", async () => {
18
+ // A real manifest file so the action's read never runs — parsing must reject
19
+ // on the missing required --registry-serve-url before any registry I/O.
20
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), "publish-flags-"));
21
+ const manifestPath = path.join(dir, "manifest.json");
22
+ await fsp.writeFile(manifestPath, "{}");
23
+
24
+ // Clear the env binding so the mandatory check, not the environment, decides.
25
+ const prev = process.env.PL_REGISTRY_SERVE_URL;
26
+ delete process.env.PL_REGISTRY_SERVE_URL;
27
+ try {
28
+ const cmd = publishCommand().exitOverride();
29
+ await expect(
30
+ cmd.parseAsync(["-r", "s3://bucket", "-m", manifestPath], { from: "user" }),
31
+ ).rejects.toThrow(/registry-serve-url/i);
32
+ } finally {
33
+ if (prev !== undefined) process.env.PL_REGISTRY_SERVE_URL = prev;
34
+ }
35
+ });
@@ -1,6 +1,6 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command, Option } from "commander";
2
2
  import fs from "node:fs";
3
- import { OclifLoggerAdapter } from "@milaboratories/ts-helpers-oclif";
3
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
4
4
  import { ManifestFileName } from "../v2/registry/schema_public";
5
5
  import {
6
6
  BlockPackManifest,
@@ -9,6 +9,7 @@ import {
9
9
  } from "@milaboratories/pl-model-middle-layer";
10
10
  import { storageByUrl } from "../io/storage";
11
11
  import { BlockRegistryV2 } from "../v2/registry/registry";
12
+ import { buildPublishedCoords, writePublishedCoords } from "../v2/resolve_to_registry";
12
13
  import path from "node:path";
13
14
 
14
15
  function simpleDeepMerge<T extends Record<string, unknown>>(
@@ -35,47 +36,40 @@ function simpleDeepMerge<T extends Record<string, unknown>>(
35
36
  return result as T;
36
37
  }
37
38
 
38
- export default class Publish extends Command {
39
- static description =
40
- "Publishes the block package and refreshes the registry (for v2 block-pack schema)";
41
-
42
- static flags = {
43
- registry: Flags.string({
44
- char: "r",
45
- summary: "full address of the registry",
46
- helpValue: "<address>",
47
- env: "PL_REGISTRY",
48
- required: true,
49
- }),
50
-
51
- manifest: Flags.file({
52
- char: "m",
53
- summary: "manifest file path",
54
- exists: true,
55
- default: `./block-pack/${ManifestFileName}`,
56
- }),
57
-
58
- "version-override": Flags.file({
59
- char: "v",
60
- summary: "override package version",
61
- }),
62
-
63
- refresh: Flags.boolean({
64
- summary: "refresh repository after adding the package",
65
- default: true,
66
- allowNo: true,
67
- env: "PL_REGISTRY_REFRESH",
68
- }),
69
-
70
- unstable: Flags.boolean({
71
- summary: "do not add the published package to stable channel",
72
- default: false,
73
- env: "PL_PUBLISH_UNSTABLE",
74
- }),
75
- };
76
-
77
- public async run(): Promise<void> {
78
- const { flags } = await this.parse(Publish);
39
+ export function publishCommand(): Command {
40
+ const cmd = new Command("publish").description(
41
+ "Publishes the block package and refreshes the registry (for v2 block-pack schema)",
42
+ );
43
+
44
+ cmd.addOption(
45
+ new Option("-r, --registry <address>", "full address of the registry")
46
+ .env("PL_REGISTRY")
47
+ .makeOptionMandatory(),
48
+ );
49
+ cmd.addOption(
50
+ new Option(
51
+ "--registry-serve-url <url>",
52
+ "CDN serve URL written into block-pack/published.json (npm-consumed blocks)",
53
+ )
54
+ .env("PL_REGISTRY_SERVE_URL")
55
+ .makeOptionMandatory(),
56
+ );
57
+ cmd.option("-m, --manifest <path>", "manifest file path", `./block-pack/${ManifestFileName}`);
58
+ cmd.option("-v, --version-override <path>", "override package version");
59
+ cmd.addOption(
60
+ new Option("--refresh", "refresh repository after adding the package")
61
+ .default(true)
62
+ .env("PL_REGISTRY_REFRESH"),
63
+ );
64
+ cmd.option("--no-refresh", "do not refresh repository after adding the package");
65
+ cmd.addOption(
66
+ new Option("--unstable", "do not add the published package to stable channel")
67
+ .default(false)
68
+ .env("PL_PUBLISH_UNSTABLE"),
69
+ );
70
+
71
+ cmd.action(async (flags) => {
72
+ const logger = new ConsoleLoggerAdapter();
79
73
 
80
74
  const manifestPath = path.resolve(flags.manifest);
81
75
  const rawManifest = JSON.parse(
@@ -89,23 +83,35 @@ export default class Publish extends Command {
89
83
  );
90
84
  const manifestRoot = path.dirname(manifestPath);
91
85
 
92
- this.log(`Manifest root = ${manifestRoot}`);
86
+ logger.info(`Manifest root = ${manifestRoot}`);
93
87
 
94
- if (flags["version-override"])
95
- manifest = overrideManifestVersion(manifest, flags["version-override"]);
88
+ if (flags.versionOverride) manifest = overrideManifestVersion(manifest, flags.versionOverride);
96
89
 
97
90
  const storage = storageByUrl(flags.registry);
98
- const registry = new BlockRegistryV2(storage, new OclifLoggerAdapter(this));
91
+ const registry = new BlockRegistryV2(storage, logger);
99
92
 
100
93
  await registry.publishPackage(manifest, async (file) =>
101
94
  Buffer.from(await fs.promises.readFile(path.resolve(manifestRoot, file))),
102
95
  );
103
96
 
104
97
  if (!flags.unstable) {
105
- this.log(`Adding package to ${StableChannel} channel...`);
98
+ logger.info(`Adding package to ${StableChannel} channel...`);
106
99
  await registry.addPackageToChannel(manifest.description.id, StableChannel);
107
100
  }
108
101
 
102
+ // Write registry coordinates next to the manifest so npm consumers can
103
+ // resolve the published block via `resolveToRegistry`. Written before
104
+ // returning so `npm publish`'s tarball generation (after prepublishOnly)
105
+ // picks it up.
106
+ const coords = buildPublishedCoords({
107
+ registryUrl: flags.registryServeUrl,
108
+ id: manifest.description.id,
109
+ });
110
+ await writePublishedCoords(manifestRoot, coords);
111
+ logger.info(`Wrote published coordinates to ${path.resolve(manifestRoot, "published.json")}`);
112
+
109
113
  if (flags.refresh) await registry.updateIfNeeded();
110
- }
114
+ });
115
+
116
+ return cmd;
111
117
  }
@@ -1,34 +1,30 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command, Option } from "commander";
2
2
  import { BlockRegistryV2 } from "../v2";
3
3
  import { storageByUrl } from "../io";
4
- import { OclifLoggerAdapter } from "@milaboratories/ts-helpers-oclif";
4
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
5
5
 
6
- export default class RefreshRegistry extends Command {
7
- static description = "Refresh overview files based on published but not proecessed artefacts";
6
+ export function refreshRegistryCommand(): Command {
7
+ const cmd = new Command("refresh-registry").description(
8
+ "Refresh overview files based on published but not proecessed artefacts",
9
+ );
8
10
 
9
- static flags = {
10
- registry: Flags.string({
11
- char: "r",
12
- summary: "full address of the registry",
13
- helpValue: "<address>",
14
- env: "PL_REGISTRY",
15
- required: true,
16
- }),
11
+ cmd.addOption(
12
+ new Option("-r, --registry <address>", "full address of the registry")
13
+ .env("PL_REGISTRY")
14
+ .makeOptionMandatory(),
15
+ );
16
+ cmd.addOption(
17
+ new Option("-m, --mode <mode>", 'refresh mode (allowed valiues: "force", "normal", "dry-run")')
18
+ .choices(["force", "normal", "dry-run"])
19
+ .env("PL_REGISTRY_REFRESH_DRY_RUN")
20
+ .default("normal"),
21
+ );
17
22
 
18
- mode: Flags.string({
19
- char: "m",
20
- summary: 'refresh mode (allowed valiues: "force", "normal", "dry-run")',
21
- helpValue: "<mode>",
22
- options: ["force", "normal", "dry-run"],
23
- env: "PL_REGISTRY_REFRESH_DRY_RUN",
24
- default: "normal",
25
- }),
26
- };
27
-
28
- public async run(): Promise<void> {
29
- const { flags } = await this.parse(RefreshRegistry);
23
+ cmd.action(async (flags) => {
30
24
  const storage = storageByUrl(flags.registry);
31
- const registry = new BlockRegistryV2(storage, new OclifLoggerAdapter(this));
25
+ const registry = new BlockRegistryV2(storage, new ConsoleLoggerAdapter());
32
26
  await registry.updateIfNeeded(flags.mode as "force" | "normal" | "dry-run");
33
- }
27
+ });
28
+
29
+ return cmd;
34
30
  }
@@ -1,44 +1,32 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command, Option } from "commander";
2
2
  import { BlockRegistryV2 } from "../v2/registry/registry";
3
3
  import { storageByUrl } from "../io/storage";
4
- import { OclifLoggerAdapter } from "@milaboratories/ts-helpers-oclif";
4
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
5
5
 
6
- export default class RestoreOverviewFromSnapshot extends Command {
7
- static description = "Restore global overview from a snapshot";
6
+ export function restoreOverviewFromSnapshotCommand(): Command {
7
+ const cmd = new Command("restore-overview-from-snapshot").description(
8
+ "Restore global overview from a snapshot",
9
+ );
8
10
 
9
- static flags = {
10
- registry: Flags.string({
11
- char: "r",
12
- summary: "full address of the registry",
13
- helpValue: "<address>",
14
- env: "PL_REGISTRY",
15
- required: true,
16
- }),
11
+ cmd.addOption(
12
+ new Option("-r, --registry <address>", "full address of the registry")
13
+ .env("PL_REGISTRY")
14
+ .makeOptionMandatory(),
15
+ );
16
+ cmd.requiredOption("-s, --snapshot <timestamp>", "snapshot timestamp ID to restore from");
17
+ cmd.option("--skip-confirmation", "skip confirmation prompt (use with caution)", false);
17
18
 
18
- snapshot: Flags.string({
19
- char: "s",
20
- summary: "snapshot timestamp ID to restore from",
21
- helpValue: "<timestamp>",
22
- required: true,
23
- }),
24
-
25
- "skip-confirmation": Flags.boolean({
26
- summary: "skip confirmation prompt (use with caution)",
27
- default: false,
28
- }),
29
- };
30
-
31
- public async run(): Promise<void> {
32
- const { flags } = await this.parse(RestoreOverviewFromSnapshot);
19
+ cmd.action(async (flags) => {
20
+ const logger = new ConsoleLoggerAdapter();
33
21
  const storage = storageByUrl(flags.registry);
34
- const registry = new BlockRegistryV2(storage, new OclifLoggerAdapter(this));
22
+ const registry = new BlockRegistryV2(storage, logger);
35
23
 
36
24
  // Check if snapshot exists
37
25
  const snapshots = await registry.listGlobalOverviewSnapshots();
38
26
  const targetSnapshot = snapshots.find((s) => s.timestamp === flags.snapshot);
39
27
 
40
28
  if (!targetSnapshot) {
41
- this.error(
29
+ throw new Error(
42
30
  `Snapshot '${flags.snapshot}' not found. Available snapshots:\n${
43
31
  snapshots.map((s) => ` - ${s.timestamp}`).join("\n") || " (none)"
44
32
  }`,
@@ -46,7 +34,7 @@ export default class RestoreOverviewFromSnapshot extends Command {
46
34
  }
47
35
 
48
36
  // Confirmation prompt (unless skipped)
49
- if (!flags["skip-confirmation"]) {
37
+ if (!flags.skipConfirmation) {
50
38
  const readline = await import("node:readline");
51
39
  const rl = readline.createInterface({
52
40
  input: process.stdin,
@@ -64,7 +52,7 @@ export default class RestoreOverviewFromSnapshot extends Command {
64
52
  rl.close();
65
53
 
66
54
  if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
67
- this.log("Restore cancelled.");
55
+ logger.info("Restore cancelled.");
68
56
  return;
69
57
  }
70
58
  }
@@ -72,9 +60,11 @@ export default class RestoreOverviewFromSnapshot extends Command {
72
60
  // Perform restore
73
61
  try {
74
62
  await registry.restoreGlobalOverviewFromSnapshot(flags.snapshot);
75
- this.log(`✅ Successfully restored global overview from snapshot '${flags.snapshot}'`);
63
+ logger.info(`✅ Successfully restored global overview from snapshot '${flags.snapshot}'`);
76
64
  } catch (error) {
77
- this.error(`Failed to restore from snapshot: ${String(error)}`);
65
+ throw new Error(`Failed to restore from snapshot: ${String(error)}`);
78
66
  }
79
- }
67
+ });
68
+
69
+ return cmd;
80
70
  }
@@ -1,51 +1,42 @@
1
- import { Command, Flags } from "@oclif/core";
1
+ import { Command } from "commander";
2
2
  import path from "node:path";
3
+ import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
3
4
  import { runStructureForPath, formatChanges } from "../../structure/cli/run-structure";
4
5
 
5
- export default class StructureCheck extends Command {
6
- static override description =
7
- "Check block(s) against the canonical structure (read-only). Exits non-zero if any diff would land — used as the CI gate.";
8
-
9
- static override examples = [
10
- "<%= config.bin %> <%= command.id %> ./my-block",
11
- "<%= config.bin %> <%= command.id %> --sdk-internal etc/blocks/*",
12
- ];
6
+ export function structureCheckCommand(packageRoot: string): Command {
7
+ const cmd = new Command("check").description(
8
+ "Check block(s) against the canonical structure (read-only). Exits non-zero if any diff would land — used as the CI gate.",
9
+ );
13
10
 
14
11
  // Variadic positional block paths (shell-expanded globs).
15
- static override strict = false;
16
-
17
- static override flags = {
18
- "sdk-internal": Flags.boolean({
19
- description: "block(s) live inside the SDK monorepo; skip root-scope rules",
20
- }),
21
- };
12
+ cmd.argument("[paths...]", "block path(s); defaults to the current directory");
13
+ cmd.option("--sdk-internal", "block(s) live inside the SDK monorepo; skip root-scope rules");
22
14
 
23
- public async run(): Promise<void> {
24
- const { argv, flags } = await this.parse(StructureCheck);
25
- const paths = (argv as string[]).length > 0 ? (argv as string[]) : ["."];
26
- const templatesRoot = path.join(this.config.root, "src", "structure", "templates");
15
+ cmd.action(async (argv: string[], flags) => {
16
+ const logger = new ConsoleLoggerAdapter();
17
+ const paths = argv.length > 0 ? argv : ["."];
18
+ const templatesRoot = path.join(packageRoot, "src", "structure", "templates");
27
19
 
28
20
  let anyChanged = false;
29
21
  for (const p of paths) {
30
22
  const res = await runStructureForPath({
31
23
  blockPath: p,
32
- isSdkInternal: flags["sdk-internal"],
24
+ isSdkInternal: Boolean(flags.sdkInternal),
33
25
  updateDepsOnly: false,
34
26
  mode: "check",
35
27
  templatesRoot,
36
- log: (m) => this.log(m),
28
+ log: (m) => logger.info(m),
37
29
  });
38
- this.log(formatChanges(p, res.changes));
30
+ logger.info(formatChanges(p, res.changes));
39
31
  if (res.changes.length > 0) anyChanged = true;
40
32
  }
41
33
 
42
34
  if (anyChanged) {
43
- this.error(
35
+ throw new Error(
44
36
  "Structure check failed: one or more blocks are out of date. Run `structure refresh`.",
45
- {
46
- exit: 1,
47
- },
48
37
  );
49
38
  }
50
- }
39
+ });
40
+
41
+ return cmd;
51
42
  }