@nan0web/release 1.0.0 → 1.0.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 (107) hide show
  1. package/README.md +24 -42
  2. package/bin/nan0release.js +27 -42
  3. package/package.json +21 -14
  4. package/src/Release/Document.js +13 -19
  5. package/src/Release/MarkdownToTest.js +35 -33
  6. package/src/Release/Person.js +3 -8
  7. package/src/Release/Scanner.js +179 -0
  8. package/src/Release.js +19 -12
  9. package/src/Team.js +9 -9
  10. package/src/architecture/ProjectManagementAsCode.js +8 -8
  11. package/src/cli/index.js +9 -0
  12. package/src/co/AppCommandOptions.js +4 -6
  13. package/src/commands/CloseCommand.js +67 -0
  14. package/src/commands/Command.js +48 -0
  15. package/src/commands/DepsCommand.js +143 -0
  16. package/src/commands/InitCommand.js +48 -56
  17. package/src/commands/PublishCommand.js +80 -119
  18. package/src/commands/SpecCommand.js +41 -0
  19. package/src/commands/StatusCommand.js +26 -0
  20. package/src/db/ReleaseDB.js +5 -5
  21. package/src/index.js +18 -0
  22. package/src/ui/cli/ReleaseCLi.js +52 -0
  23. package/src/utils/runSpawn.js +0 -0
  24. package/types/Release/Document.d.ts +2 -2
  25. package/types/Release/Person.d.ts +3 -3
  26. package/types/Release/Scanner.d.ts +90 -0
  27. package/types/Release.d.ts +37 -33
  28. package/types/ReleaseCLI.d.ts +29 -28
  29. package/types/Team.d.ts +3 -3
  30. package/types/cli/index.d.ts +2 -0
  31. package/types/co/AppCommandMessage.d.ts +10 -11
  32. package/types/co/AppCommandOptions.d.ts +1 -2
  33. package/types/commands/BaseCommand.d.ts +23 -0
  34. package/types/commands/ChatCommand.d.ts +9 -9
  35. package/types/commands/ChatMessage.d.ts +36 -0
  36. package/types/commands/CloseCommand.d.ts +25 -0
  37. package/types/commands/Command.d.ts +28 -0
  38. package/types/commands/DepsCommand.d.ts +32 -0
  39. package/types/commands/DepsMessage.d.ts +28 -0
  40. package/types/commands/HostCommand.d.ts +7 -7
  41. package/types/commands/HostMessage.d.ts +44 -0
  42. package/types/commands/InitCommand.d.ts +24 -19
  43. package/types/commands/InitMessage.d.ts +24 -0
  44. package/types/commands/ListCommand.d.ts +8 -8
  45. package/types/commands/ListMessage.d.ts +30 -0
  46. package/types/commands/PublishCommand.d.ts +36 -47
  47. package/types/commands/PublishMessage.d.ts +39 -0
  48. package/types/commands/SealCommand.d.ts +7 -7
  49. package/types/commands/SealMessage.d.ts +28 -0
  50. package/types/commands/ServeCommand.d.ts +7 -7
  51. package/types/commands/ServeMessage.d.ts +30 -0
  52. package/types/commands/ShowCommand.d.ts +7 -7
  53. package/types/commands/SpecCommand.d.ts +25 -0
  54. package/types/commands/StatusCommand.d.ts +9 -0
  55. package/types/commands/SubCommand.d.ts +9 -7
  56. package/types/commands/ValidateCommand.d.ts +7 -7
  57. package/types/commands/ValidateMessage.d.ts +20 -0
  58. package/types/db/ReleaseDB.d.ts +1 -1
  59. package/types/index.d.ts +10 -0
  60. package/types/ui/cli/ReleaseCLi.d.ts +10 -0
  61. package/types/ui/cli/index.d.ts +0 -0
  62. package/types/utils/runSpawn.d.ts +0 -0
  63. package/.datasets/README.dataset.jsonl +0 -19
  64. package/.editorconfig +0 -20
  65. package/CHANGELOG.md +0 -6
  66. package/CONTRIBUTING.md +0 -42
  67. package/bin/commands/InitCommand.js +0 -10
  68. package/bin/lib/functions.js +0 -240
  69. package/bin/lib/functions.test.js +0 -138
  70. package/bin/release.js +0 -12
  71. package/bin/release.test.js +0 -73
  72. package/bin/temp/package.json +0 -1
  73. package/docs/system.uk.md +0 -413
  74. package/docs/uk/README.md +0 -284
  75. package/legacy/PrepareCommand.js +0 -154
  76. package/legacy/PrepareCommand.test.js +0 -189
  77. package/legacy/changelog.js +0 -446
  78. package/releases/1/0/v1.0.0/models/Person.js +0 -26
  79. package/releases/1/0/v1.0.0/models/Person.test.js +0 -44
  80. package/releases/1/0/v1.0.0/models/company.js +0 -11
  81. package/releases/1/0/v1.0.0/models/members.js +0 -14
  82. package/releases/1/0/v1.0.0/models/members.test.js +0 -21
  83. package/releases/1/0/v1.0.0/models/teams/DevTeam.js +0 -6
  84. package/releases/1/0/v1.0.0/models/teams/DevTeam.test.js +0 -15
  85. package/releases/1/0/v1.0.0/models/teams/index.js +0 -5
  86. package/releases/1/0/v1.0.0/release.js +0 -12
  87. package/releases/1/0/v1.0.0/release.md +0 -121
  88. package/releases/1/0/v1.0.0/release.test.js +0 -36
  89. package/releases/1/0/v1.0.0/test/YaRaSLove/git-native.test.js +0 -14
  90. package/releases/current.js +0 -3
  91. package/releases/index.js +0 -11
  92. package/src/README.md.js +0 -468
  93. package/src/Release/MarkdownToTest.test.js +0 -60
  94. package/src/ReleaseCLI.js +0 -122
  95. package/src/architecture/ProjectManagementAsCode.test.js +0 -128
  96. package/src/co/AppCommandMessage.js +0 -22
  97. package/src/commands/ChatCommand.js +0 -56
  98. package/src/commands/HostCommand.js +0 -63
  99. package/src/commands/ListCommand.js +0 -56
  100. package/src/commands/SealCommand.js +0 -51
  101. package/src/commands/ServeCommand.js +0 -53
  102. package/src/commands/ShowCommand.js +0 -58
  103. package/src/commands/SubCommand.js +0 -21
  104. package/src/commands/ValidateCommand.js +0 -66
  105. package/system.md +0 -571
  106. package/tsconfig.json +0 -23
  107. /package/{bin/lib/temp/pnpm-lock.yaml → src/ui/cli/index.js} +0 -0
@@ -1,65 +1,57 @@
1
- import SubCommand from "./SubCommand.js"
2
- import AppCommandMessage from "../co/AppCommandMessage.js"
3
- import { CommandError } from "@nan0web/co"
1
+ import { OutputMessage } from '@nan0web/co'
2
+ import Command from './Command.js'
3
+ import process from 'node:process'
4
4
 
5
- /**
6
- * InitCommand – scaffolds a new release directory with the minimal set of
7
- * files required for a functional release (release.js, release.md and a
8
- * placeholder task folder). The command is deliberately tiny – it only
9
- * creates the filesystem layout and writes a tiny starter release object.
10
- *
11
- * Usage:
12
- * release init v1.0.0
13
- */
14
- class InitCommand extends SubCommand {
15
- constructor(options = {}) {
16
- super(options)
17
- this.addArgument("*", String, "", "Version for the new release")
5
+ class InitBody {
6
+ static version = { help: 'Release version', defaultValue: '' }
7
+ /** @type {string} */
8
+ version = ''
9
+ constructor({ version = '' } = {}) {
10
+ this.version = String(version)
18
11
  }
12
+ }
19
13
 
20
- /**
21
- * Run the init process.
22
- *
23
- * @param {AppCommandMessage} msg – command context (contains parsed options)
24
- */
25
- async run(msg) {
26
- if (!this.db) {
27
- throw new CommandError("Database (working directory) not set")
28
- }
29
- const version = msg.args[0]
30
- if (!version) {
31
- throw new CommandError("Version not defined", msg)
32
- }
33
- const db = this.db.extractVersion(version)
14
+ export default class InitCommand extends Command {
15
+ static name = 'init'
16
+ static help = 'Initialize a new release structure'
17
+ static Body = InitBody
34
18
 
35
- const files = [
36
- ["release.js", [
37
- 'import Release from "../../../src/Release.js"',
38
- 'export default new Release({',
39
- ` version: "${version}",`,
40
- ' createdAt: new Date(),',
41
- ' tasks: new Map()',
42
- '})',
43
- ].join("\n")],
44
- ["release.md", [
45
- `# ${version} - ${new Date().toISOString().split("T")[0]}`,
46
- '## Overview',
47
- 'Initial release scaffold generated by `nan0release init`.',
48
- ].join("\n\n")]
49
- ]
19
+ /** @param {Partial<Command> & { body?: Partial<InitBody> }} [input={}] */
20
+ constructor(input = {}) {
21
+ super(input)
22
+ this.body = new InitBody(/** @type {*} */ (input))
23
+ }
50
24
 
51
- for (const [file, content] of files) {
52
- const jsExists = await db.loadDocument(file)
53
- if (jsExists) {
54
- this.logger.warn(file, "exists already")
55
- } else {
56
- await db.saveDocument(file, content)
57
- this.logger.success(file, "created")
58
- }
25
+ async *run() {
26
+ let v = this.body.version
27
+ if (!v) {
28
+ throw new Error('Version is required (e.g. --version=1.0.0 or --version=v1.0.0)')
59
29
  }
30
+ if (!v.startsWith('v')) v = 'v' + v
31
+
32
+ const parts = v.replace('v', '').split('.')
33
+ const major = parts[0] || '0'
34
+ const minor = parts[1] || '0'
60
35
 
61
- this.logger.success(`Release ${version} created`)
36
+ const { mkdirSync, writeFileSync } = await import('node:fs')
37
+ const { join } = await import('node:path')
38
+
39
+ const dir = join(process.cwd(), 'releases', major, minor, v)
40
+ mkdirSync(dir, { recursive: true })
41
+
42
+ const taskPath = join(dir, 'task.md')
43
+ writeFileSync(taskPath, `# Release ${v}\n\n- [ ] Task 1`, 'utf8')
44
+
45
+ const specPath = join(dir, 'test.spec.js')
46
+ writeFileSync(
47
+ specPath,
48
+ `import test from 'node:test'\nimport assert from 'node:assert'\n\ntest('Sample spec', () => {\n\tassert.ok(true)\n})\n`,
49
+ 'utf8',
50
+ )
51
+
52
+ yield new OutputMessage({
53
+ content: [`✅ Initialized release ${v} in ${dir}`],
54
+ type: OutputMessage.TYPES.SUCCESS,
55
+ })
62
56
  }
63
57
  }
64
-
65
- export default InitCommand
@@ -1,144 +1,105 @@
1
- import process from "node:process"
2
- import { Command, CommandMessage } from "@nan0web/co"
3
- import FS from "@nan0web/db-fs"
4
- import { runSpawn } from '@nan0web/test'
5
-
6
- export class PublishCommandOptions {
7
- /** @type {string} */
8
- tag
9
- constructor(input = {}) {
10
- const {
11
- tag = ""
12
- } = input
13
- this.tag = String(tag)
14
- }
15
- /**
16
- * @param {any} input
17
- * @returns {PublishCommandOptions}
18
- */
19
- static from(input) {
20
- if (input instanceof PublishCommandOptions) return input
21
- return new PublishCommandOptions(input)
22
- }
23
- }
24
-
25
1
  /**
26
- * @extends {CommandMessage}
2
+ * PublishCommand – Message‑based version of the old `PublishCommand`.
3
+ *
4
+ * Publishes the current package to NPM and creates a Git tag.
5
+ *
6
+ * Body schema:
7
+ * major | minor | patch – optional version bump flag.
8
+ *
9
+ * @module release/commands/PublishCommand
27
10
  */
28
- export class PublishCommandMessage extends CommandMessage {
29
- /** @returns {PublishCommandOptions} */
30
- get opts() {
31
- const opts = super.opts
32
- return PublishCommandOptions.from(opts)
33
- }
34
- set opts(value) {
35
- super.opts = value
36
- }
37
- constructor(input = {}) {
38
- super(input)
11
+
12
+ import { Message, OutputMessage } from '@nan0web/co'
13
+
14
+ import Command from './Command.js'
15
+
16
+ class PublishBody {
17
+ static major = { help: 'Bump major version', defaultValue: false }
18
+ static minor = { help: 'Bump minor version', defaultValue: false }
19
+ static patch = { help: 'Bump patch version', defaultValue: false }
20
+
21
+ /** @type {boolean} */
22
+ major = false
23
+ /** @type {boolean} */
24
+ minor = false
25
+ /** @type {boolean} */
26
+ patch = false
27
+
28
+ constructor({ major = false, minor = false, patch = false } = {}) {
29
+ this.major = Boolean(major)
30
+ this.minor = Boolean(minor)
31
+ this.patch = Boolean(patch)
39
32
  }
40
33
  }
41
34
 
42
- /**
43
- * @extends {Command}
44
- */
45
35
  export default class PublishCommand extends Command {
46
- static Message = PublishCommandMessage
36
+ static name = 'publish'
37
+ static help = 'Publish npm packages'
38
+ static Body = PublishBody
39
+
40
+ /** @type {PublishBody} */
41
+ body
47
42
 
43
+ /** @param {Partial<Command> & { body?: Partial<PublishBody> }} [input={}] */
48
44
  constructor(input = {}) {
49
- super({
50
- name: "publish",
51
- help: "Publish package to NPM and create Git tag",
52
- ...input
53
- })
54
- this.addArgument("*", String, "", "Category of new version: major, minor, patch, or empty.")
45
+ super(input)
46
+ const { body = new PublishBody() } = Message.parseBody(input, PublishBody)
47
+ this.body = new PublishBody(body)
55
48
  }
56
49
 
57
- /**
58
- * @docs
59
- * # `nan0release publish`
60
- *
61
- * Publishes current package to NPM and creates Git tags.
62
- *
63
- * Steps performed:
64
- * - Verifies no uncommitted changes
65
- * - Pulls latest changes
66
- * - Runs build and test scripts
67
- * - Creates Git tag if not exists
68
- * - Publishes to NPM registry
69
- * - Pushes changes and tags
70
- *
71
- * ```bash
72
- * nan0release publish
73
- * ```
74
- * @param {PublishCommandMessage} msg
75
- */
76
- async run(msg) {
77
- const fs = new FS()
78
- await fs.connect()
79
-
80
- const category = ["major", "minor", "patch"].find(c => msg.args.includes(c))
81
- if (category) {
82
- await this.#run('npm', ["version", category], "Unable to change version", fs)
50
+ /** @returns {AsyncGenerator<OutputMessage>} */
51
+ async *run() {
52
+ const bump = this.body.major
53
+ ? 'major'
54
+ : this.body.minor
55
+ ? 'minor'
56
+ : this.body.patch
57
+ ? 'patch'
58
+ : null
59
+ if (bump) {
60
+ await this._run('npm', ['version', bump], 'Failed to bump version')
83
61
  }
84
-
85
- const pkg = await fs.loadDocument('package.json', {})
62
+ const pkg = await this.fs.loadDocument('package.json', {})
86
63
  const tag = `v${pkg.version}`
87
64
 
88
- await this.#run('git', ["diff", "--quiet"], 'Uncommitted changes found. Commit or stash first.', fs)
65
+ yield new OutputMessage(`🛜 nan0release: publishing @${pkg.name}@${pkg.version}`)
66
+
67
+ await this._run('git', ['diff', '--quiet'], 'Uncommitted changes found')
89
68
 
90
- this.logger.info(`🛜 nan0release: publishing @${pkg.name}@${pkg.version}`)
69
+ await this._run('git', ['pull'], 'Failed to pull latest changes')
70
+ await this._run('npm', ['run', 'clean'], 'Clean failed')
71
+ await this._run('npm', ['run', 'build'], 'Build failed')
72
+ await this._run('npm', ['test'], 'Tests failed')
91
73
 
92
- await this.#run('git', ["pull"], 'Failed to pull latest changes', fs)
93
- await this.#run('npm', ["run", "clean"], 'Clean failed', fs)
94
- await this.#run('npm', ["run", "build"], 'Build failed', fs)
95
- await this.#run('npm', ["test"], 'Tests failed', fs)
74
+ return
96
75
 
97
- const tagsResult = await this.#run('git', ["tag"], "Failed to get tags", fs)
76
+ const tagsResult = await this._run('git', ['tag'], 'Failed to get tags')
98
77
  const tags = tagsResult.text.split('\n').filter(Boolean)
99
78
 
100
79
  if (!tags.includes(tag)) {
101
- await this.#run('git', ['tag', '-a', tag, '-m', `Release ${pkg.version}`], 'Tag creation failed', fs)
80
+ await this._run(
81
+ 'git',
82
+ ['tag', '-a', tag, '-m', `Release ${pkg.version}`],
83
+ 'Tag creation failed',
84
+ )
85
+ yield new OutputMessage({
86
+ content: [`Tag ${tag} created`],
87
+ type: OutputMessage.TYPES.SUCCESS,
88
+ })
102
89
  } else {
103
- this.logger.warn(`Tag ${tag} already exists`)
90
+ yield new OutputMessage({
91
+ content: [`Tag ${tag} already exists`],
92
+ type: OutputMessage.TYPES.WARNING,
93
+ })
104
94
  }
105
95
 
106
- await this.#run('npm', ['publish', '--access', 'public'], 'Publish to npm failed', fs)
96
+ await this._run('npm', ['publish', '--access', 'public'], 'Publish to npm failed')
97
+ await this._run('git', ['push', 'origin', 'main', '--no-verify'], 'Git push failed')
98
+ await this._run('git', ['push', 'origin', '--tags', '--no-verify'], 'Tag push failed')
107
99
 
108
- await this.#run('git', ['push', 'origin', 'main', '--no-verify'], 'Git push failed', fs)
109
- await this.#run('git', ['push', 'origin', '--tags', '--no-verify'], 'Tag push failed', fs)
110
-
111
- this.logger.success(`@${pkg.name}@${pkg.version} published. Zero becomes script of a life 🌱`)
112
- }
113
-
114
- /**
115
- * @param {string} cmd
116
- * @param {string[]} args
117
- * @param {string} errorMsg
118
- * @param {FS} fs
119
- * @param {number[]} [okCodes=[0]]
120
- */
121
- async #run(cmd, args, errorMsg, fs, okCodes = [0]) {
122
- let content = ""
123
- const onData = (chunk) => {
124
- content += String(chunk)
125
- const rows = content.split("\n").filter(Boolean)
126
- const recent = rows.slice(-1)
127
- if (recent.length) {
128
- this.logger.cursorUp(1, true)
129
- this.logger.info(recent[0])
130
- }
131
- }
132
-
133
- let cwd = fs.absolute()
134
- if ("/" === cwd && "." === fs.cwd) cwd = fs.cwd
135
- const response = await runSpawn(cmd, args, { onData, cwd })
136
-
137
- if (!okCodes.includes(response.code)) {
138
- this.logger.error(errorMsg)
139
- process.exit(1)
140
- }
141
-
142
- return response
100
+ yield new OutputMessage({
101
+ content: [`${pkg.name}@${pkg.version} published.`],
102
+ type: OutputMessage.TYPES.SUCCESS,
103
+ })
143
104
  }
144
105
  }
@@ -0,0 +1,41 @@
1
+ import { OutputMessage } from '@nan0web/co'
2
+ import Command from './Command.js'
3
+ import process from 'node:process'
4
+ import Scanner from '../Release/Scanner.js'
5
+
6
+ class SpecBody {
7
+ static version = { help: 'Release version filter', defaultValue: '' }
8
+ /** @type {string} */
9
+ version = ''
10
+ constructor({ version = '' } = {}) {
11
+ this.version = String(version)
12
+ }
13
+ }
14
+
15
+ export default class SpecCommand extends Command {
16
+ static name = 'spec'
17
+ static help = 'Run spec testing for the active release'
18
+ static Body = SpecBody
19
+
20
+ /** @param {Partial<Command> & { body?: Partial<SpecBody> }} [input={}] */
21
+ constructor(input = {}) {
22
+ super(input)
23
+ this.body = new SpecBody(/** @type {*} */ (input))
24
+ }
25
+
26
+ async *run() {
27
+ yield new OutputMessage(`🛜 nan0release spec ${this.body.version || 'all'}`)
28
+ const scanner = new Scanner(process.cwd())
29
+ const specs = scanner.findSpecs(this.body.version)
30
+ if (specs.length === 0) {
31
+ yield new OutputMessage(`No specs found.`)
32
+ return
33
+ }
34
+ const files = specs.map((s) => s.path)
35
+ await this._run('node', ['--test', ...files], 'Specs failed')
36
+ yield new OutputMessage({
37
+ content: [`✅ Specs passed`],
38
+ type: OutputMessage.TYPES.SUCCESS,
39
+ })
40
+ }
41
+ }
@@ -0,0 +1,26 @@
1
+ import { OutputMessage } from '@nan0web/co'
2
+ import Command from './Command.js'
3
+ import process from 'node:process'
4
+ import Scanner from '../Release/Scanner.js'
5
+
6
+ export default class StatusCommand extends Command {
7
+ static name = 'status'
8
+ static help = 'Show release status'
9
+
10
+ constructor(input = {}) {
11
+ super(input)
12
+ this.body = {}
13
+ }
14
+
15
+ async *run() {
16
+ const scanner = new Scanner(process.cwd())
17
+ const statuses = scanner.status()
18
+
19
+ yield new OutputMessage(`🛜 nan0release status`)
20
+ for (const st of statuses) {
21
+ yield new OutputMessage(
22
+ `${st.version.padEnd(10)} | ${st.state.padEnd(6)} | Specs: ${st.specs.length}, Tests: ${st.tests.length}`,
23
+ )
24
+ }
25
+ }
26
+ }
@@ -1,4 +1,4 @@
1
- import DBFS from "@nan0web/db-fs"
1
+ import DBFS from '@nan0web/db-fs'
2
2
 
3
3
  /**
4
4
  * ReleaseDB – thin wrapper around DBFS that adds a convenient `releases`
@@ -22,10 +22,10 @@ class ReleaseDB extends DBFS {
22
22
  extractVersion(version) {
23
23
  const found = version.match(/^v(\d+)\.(\d+)\.(\d+)$/)
24
24
  if (!found) {
25
- throw new Error("Incorrect version")
25
+ throw new Error('Incorrect version')
26
26
  }
27
- const arr = version.slice(1).split(".")
28
- const path = [arr[0], arr[1], version, ""].join("/")
27
+ const arr = version.slice(1).split('.')
28
+ const path = [arr[0], arr[1], version, ''].join('/')
29
29
  return ReleaseDB.from(this.extract(path))
30
30
  }
31
31
  /**
@@ -35,7 +35,7 @@ class ReleaseDB extends DBFS {
35
35
  const paths = new Set()
36
36
  for (const key of this.data.keys()) {
37
37
  // Expected structure: major/minor/vX.Y.Z/…
38
- const parts = key.split("/")
38
+ const parts = key.split('/')
39
39
  if (parts.length > 2 && /^\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) {
40
40
  paths.add(`${parts[0]}/${parts[1]}/${parts[2]}`)
41
41
  }
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ import Release from './Release.js'
2
+ import ReleaseDocument from './Release/Document.js'
3
+ import Person from './Release/Person.js'
4
+ import Scanner from './Release/Scanner.js'
5
+ import MarkdownToTest from './Release/MarkdownToTest.js'
6
+ import { ProjectManagement, ReleaseManager, TaskTestSuite, ChangelogTaskManager } from './architecture/ProjectManagementAsCode.js'
7
+
8
+ export {
9
+ Release,
10
+ ReleaseDocument,
11
+ Person,
12
+ Scanner,
13
+ MarkdownToTest,
14
+ ProjectManagement,
15
+ ReleaseManager,
16
+ TaskTestSuite,
17
+ ChangelogTaskManager
18
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ReleaseCLI – UI‑CLI driven entry point that registers the
3
+ * Message‑based release commands.
4
+ *
5
+ * Usage (from terminal):
6
+ *
7
+ * npx nan0release <subcommand> [options] [args]
8
+ *
9
+ * Subcommands:
10
+ * init, deps, publish, seal, host, serve, list, validate, chat
11
+ *
12
+ * Each subcommand is a Message class exporting a `run` async generator
13
+ * that yields {@link OutputMessage}s.
14
+ *
15
+ * @module release/cli/ReleaseCLI
16
+ */
17
+ import process from 'node:process'
18
+
19
+ import { CLI } from '@nan0web/ui-cli'
20
+ import Logger from '@nan0web/log'
21
+ import DBFS from '@nan0web/db-fs'
22
+ import DepsCommand from '../../commands/DepsCommand.js'
23
+ import PublishCommand from '../../commands/PublishCommand.js'
24
+ import InitCommand from '../../commands/InitCommand.js'
25
+ import SpecCommand from '../../commands/SpecCommand.js'
26
+ import CloseCommand from '../../commands/CloseCommand.js'
27
+ import StatusCommand from '../../commands/StatusCommand.js'
28
+
29
+ class ReleaseCLi extends CLI {
30
+ /** @type {DBFS} */
31
+ fs
32
+ constructor(input) {
33
+ super(input)
34
+ const { fs } = input
35
+ this.fs = fs ?? DBFS.from({})
36
+ }
37
+ }
38
+
39
+ export default function createReleaseCLI(
40
+ argv = process.argv.slice(2),
41
+ logger = new Logger(),
42
+ fs = {},
43
+ ) {
44
+ const cli = new ReleaseCLi({
45
+ argv,
46
+ commands: {}, // no map‑based commands
47
+ Messages: [InitCommand, DepsCommand, PublishCommand, SpecCommand, CloseCommand, StatusCommand],
48
+ logger,
49
+ fs: DBFS.from(fs),
50
+ })
51
+ return cli
52
+ }
File without changes
@@ -27,5 +27,5 @@ declare class ReleaseDocument extends Markdown {
27
27
  /** @type {Date | undefined} */
28
28
  date: Date | undefined;
29
29
  }
30
- import Markdown from "@nan0web/markdown";
31
- import Person from "./Person.js";
30
+ import Markdown from '@nan0web/markdown';
31
+ import Person from './Person.js';
@@ -12,6 +12,6 @@ export default class Person {
12
12
  /** @type {HumanContact[]} */
13
13
  contacts: HumanContact[];
14
14
  }
15
- import { HumanName } from "@nan0web/verse";
16
- import { HumanGender } from "@nan0web/verse";
17
- import { HumanContact } from "@nan0web/verse";
15
+ import { HumanName } from '@nan0web/verse';
16
+ import { HumanGender } from '@nan0web/verse';
17
+ import { HumanContact } from '@nan0web/verse';
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @typedef {Object} ContractEntry
3
+ * @property {string} version - Release version (e.g. "v1.0.0")
4
+ * @property {string} path - Absolute path to the contract file
5
+ * @property {string} type - "spec" or "test"
6
+ * @property {string} dir - Directory containing the contract
7
+ */
8
+ /**
9
+ * @typedef {Object} VersionStatus
10
+ * @property {string} version - Release version
11
+ * @property {'wip' | 'closed' | 'mixed'} state - Version state
12
+ * @property {string[]} specs - Paths to spec files
13
+ * @property {string[]} tests - Paths to test files
14
+ */
15
+ /**
16
+ * Рекурсивний сканер для пошуку release контрактів.
17
+ * Підтримує вкладену структуру: releases/{major}/{minor}/v{version}/
18
+ * та плоску: releases/v{version}/
19
+ */
20
+ export default class Scanner {
21
+ /**
22
+ * @param {string} root - Root directory of the project
23
+ */
24
+ constructor(root: string);
25
+ /** @type {string} */
26
+ root: string;
27
+ /**
28
+ * Find all .spec.js files (WIP contracts)
29
+ * @param {string} [version] - Optional version filter
30
+ * @returns {ContractEntry[]}
31
+ */
32
+ findSpecs(version?: string): ContractEntry[];
33
+ /**
34
+ * Find all .test.js files (Closed contracts)
35
+ * @param {string} [version] - Optional version filter
36
+ * @returns {ContractEntry[]}
37
+ */
38
+ findTests(version?: string): ContractEntry[];
39
+ /**
40
+ * Get status of all versions
41
+ * @returns {VersionStatus[]}
42
+ */
43
+ status(): VersionStatus[];
44
+ /**
45
+ * Close a release: rename all .spec.js → .test.js
46
+ * @param {string} [version] - Version to close. If omitted, closes all WIP.
47
+ * @returns {{ closed: number, files: string[] }}
48
+ */
49
+ close(version?: string): {
50
+ closed: number;
51
+ files: string[];
52
+ };
53
+ #private;
54
+ }
55
+ export type ContractEntry = {
56
+ /**
57
+ * - Release version (e.g. "v1.0.0")
58
+ */
59
+ version: string;
60
+ /**
61
+ * - Absolute path to the contract file
62
+ */
63
+ path: string;
64
+ /**
65
+ * - "spec" or "test"
66
+ */
67
+ type: string;
68
+ /**
69
+ * - Directory containing the contract
70
+ */
71
+ dir: string;
72
+ };
73
+ export type VersionStatus = {
74
+ /**
75
+ * - Release version
76
+ */
77
+ version: string;
78
+ /**
79
+ * - Version state
80
+ */
81
+ state: "wip" | "closed" | "mixed";
82
+ /**
83
+ * - Paths to spec files
84
+ */
85
+ specs: string[];
86
+ /**
87
+ * - Paths to test files
88
+ */
89
+ tests: string[];
90
+ };