@nan0web/release 1.0.1 → 1.0.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 (109) hide show
  1. package/README.md +24 -42
  2. package/bin/nan0release.js +27 -42
  3. package/package.json +48 -42
  4. package/project.yaml +79 -0
  5. package/src/README.md.js +90 -93
  6. package/src/Release/Document.js +13 -19
  7. package/src/Release/MarkdownToTest.js +35 -33
  8. package/src/Release/Person.js +3 -8
  9. package/src/Release/Scanner.js +179 -0
  10. package/src/Release.js +19 -12
  11. package/src/Team.js +9 -9
  12. package/src/architecture/ProjectManagementAsCode.js +8 -8
  13. package/src/cli/index.js +9 -0
  14. package/src/co/AppCommandOptions.js +4 -6
  15. package/src/commands/CloseCommand.js +67 -0
  16. package/src/commands/Command.js +48 -0
  17. package/src/commands/DepsCommand.js +143 -0
  18. package/src/commands/InitCommand.js +48 -56
  19. package/src/commands/PublishCommand.js +80 -119
  20. package/src/commands/SpecCommand.js +41 -0
  21. package/src/commands/StatusCommand.js +26 -0
  22. package/src/db/ReleaseDB.js +5 -5
  23. package/src/index.js +18 -0
  24. package/src/ui/cli/ReleaseCLi.js +52 -0
  25. package/src/utils/runSpawn.js +0 -0
  26. package/types/Release/Document.d.ts +2 -2
  27. package/types/Release/Person.d.ts +3 -3
  28. package/types/Release/Scanner.d.ts +90 -0
  29. package/types/Release.d.ts +37 -33
  30. package/types/ReleaseCLI.d.ts +29 -28
  31. package/types/Team.d.ts +3 -3
  32. package/types/architecture/ProjectManagementAsCode.d.ts +1 -1
  33. package/types/cli/index.d.ts +2 -0
  34. package/types/co/AppCommandMessage.d.ts +10 -11
  35. package/types/co/AppCommandOptions.d.ts +1 -2
  36. package/types/commands/BaseCommand.d.ts +23 -0
  37. package/types/commands/ChatCommand.d.ts +9 -9
  38. package/types/commands/ChatMessage.d.ts +36 -0
  39. package/types/commands/CloseCommand.d.ts +25 -0
  40. package/types/commands/Command.d.ts +28 -0
  41. package/types/commands/DepsCommand.d.ts +32 -0
  42. package/types/commands/DepsMessage.d.ts +28 -0
  43. package/types/commands/HostCommand.d.ts +7 -7
  44. package/types/commands/HostMessage.d.ts +44 -0
  45. package/types/commands/InitCommand.d.ts +24 -19
  46. package/types/commands/InitMessage.d.ts +24 -0
  47. package/types/commands/ListCommand.d.ts +8 -8
  48. package/types/commands/ListMessage.d.ts +30 -0
  49. package/types/commands/PublishCommand.d.ts +36 -47
  50. package/types/commands/PublishMessage.d.ts +39 -0
  51. package/types/commands/SealCommand.d.ts +7 -7
  52. package/types/commands/SealMessage.d.ts +28 -0
  53. package/types/commands/ServeCommand.d.ts +7 -7
  54. package/types/commands/ServeMessage.d.ts +30 -0
  55. package/types/commands/ShowCommand.d.ts +7 -7
  56. package/types/commands/SpecCommand.d.ts +25 -0
  57. package/types/commands/StatusCommand.d.ts +9 -0
  58. package/types/commands/SubCommand.d.ts +9 -7
  59. package/types/commands/ValidateCommand.d.ts +7 -7
  60. package/types/commands/ValidateMessage.d.ts +20 -0
  61. package/types/db/ReleaseDB.d.ts +2 -2
  62. package/types/index.d.ts +10 -0
  63. package/types/ui/cli/ReleaseCLi.d.ts +10 -0
  64. package/types/ui/cli/index.d.ts +0 -0
  65. package/types/utils/runSpawn.d.ts +0 -0
  66. package/.datasets/README.dataset.jsonl +0 -19
  67. package/.editorconfig +0 -20
  68. package/CHANGELOG.md +0 -6
  69. package/CONTRIBUTING.md +0 -42
  70. package/bin/commands/InitCommand.js +0 -10
  71. package/bin/lib/functions.js +0 -240
  72. package/bin/lib/functions.test.js +0 -138
  73. package/bin/release.js +0 -12
  74. package/bin/release.test.js +0 -73
  75. package/bin/temp/package.json +0 -1
  76. package/docs/system.uk.md +0 -413
  77. package/docs/uk/README.md +0 -284
  78. package/legacy/PrepareCommand.js +0 -154
  79. package/legacy/PrepareCommand.test.js +0 -189
  80. package/legacy/changelog.js +0 -446
  81. package/releases/1/0/v1.0.0/models/Person.js +0 -26
  82. package/releases/1/0/v1.0.0/models/Person.test.js +0 -44
  83. package/releases/1/0/v1.0.0/models/company.js +0 -11
  84. package/releases/1/0/v1.0.0/models/members.js +0 -14
  85. package/releases/1/0/v1.0.0/models/members.test.js +0 -21
  86. package/releases/1/0/v1.0.0/models/teams/DevTeam.js +0 -6
  87. package/releases/1/0/v1.0.0/models/teams/DevTeam.test.js +0 -15
  88. package/releases/1/0/v1.0.0/models/teams/index.js +0 -5
  89. package/releases/1/0/v1.0.0/release.js +0 -12
  90. package/releases/1/0/v1.0.0/release.md +0 -121
  91. package/releases/1/0/v1.0.0/release.test.js +0 -36
  92. package/releases/1/0/v1.0.0/test/YaRaSLove/git-native.test.js +0 -14
  93. package/releases/current.js +0 -3
  94. package/releases/index.js +0 -11
  95. package/src/Release/MarkdownToTest.test.js +0 -60
  96. package/src/ReleaseCLI.js +0 -122
  97. package/src/architecture/ProjectManagementAsCode.test.js +0 -128
  98. package/src/co/AppCommandMessage.js +0 -22
  99. package/src/commands/ChatCommand.js +0 -56
  100. package/src/commands/HostCommand.js +0 -63
  101. package/src/commands/ListCommand.js +0 -56
  102. package/src/commands/SealCommand.js +0 -51
  103. package/src/commands/ServeCommand.js +0 -53
  104. package/src/commands/ShowCommand.js +0 -58
  105. package/src/commands/SubCommand.js +0 -21
  106. package/src/commands/ValidateCommand.js +0 -66
  107. package/system.md +0 -571
  108. package/tsconfig.json +0 -23
  109. /package/{bin/lib/temp/pnpm-lock.yaml → src/ui/cli/index.js} +0 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * DepsCommand – Message‑based version of the old `DepsCommand`.
3
+ *
4
+ * Checks and optionally fixes workspace / link dependencies.
5
+ *
6
+ * Body schema:
7
+ * fix – boolean – whether to apply fixes (default: false)
8
+ *
9
+ * @module release/commands/DepsCommand
10
+ */
11
+
12
+ import { Message, OutputMessage } from '@nan0web/co'
13
+
14
+ import Command from './Command.js'
15
+
16
+ class DepsBody {
17
+ /** @type {boolean} */
18
+ fix = false
19
+ static fix = {
20
+ help: 'Apply fixes automatically',
21
+ defaultValue: false,
22
+ }
23
+ /** @type {boolean} */
24
+ latest = false
25
+ static latest = {
26
+ help: 'Update @nan0web packages to the latests available versions',
27
+ defaultValue: false,
28
+ }
29
+ constructor(input = {}) {
30
+ const { fix = this.fix, latest = this.latest } = Message.parseBody(input, DepsBody)
31
+ this.fix = Boolean(fix)
32
+ this.latest = Boolean(latest)
33
+ }
34
+ }
35
+
36
+ export default class DepsCommand extends Command {
37
+ static name = 'deps'
38
+ static help = 'Dependencies manager'
39
+ static Body = DepsBody
40
+ /** @type {DepsBody} */
41
+ body
42
+
43
+ /** @param {Partial<DepsBody>} [input={}] */
44
+ constructor(input = {}) {
45
+ super({})
46
+ this.body = Command.parseBody(input, DepsBody)
47
+ }
48
+
49
+ /**
50
+ * @param {DepsCommand} [msg]
51
+ * @returns {AsyncGenerator<OutputMessage>}
52
+ */
53
+ async *run(msg = new DepsCommand()) {
54
+ const pkg = await this.fs.loadDocument('package.json', {})
55
+ yield new OutputMessage(['Loaded package.json', JSON.stringify(pkg).length + ' bytes'])
56
+ const deps = {
57
+ devDependencies: pkg?.devDependencies ?? {},
58
+ dependencies: pkg?.dependencies ?? {},
59
+ peerDependencies: pkg?.peerDependencies ?? {},
60
+ }
61
+ const fixes = new Map()
62
+ const fails = new Set()
63
+
64
+ const isLocal = (ver) =>
65
+ ['workspace:', 'link:'].some((p) => String(ver).toLowerCase().startsWith(p))
66
+
67
+ const changes = new Map()
68
+
69
+ for (const [group, map] of Object.entries(deps)) {
70
+ yield new OutputMessage(`${group} (${Object.keys(map).length} items)`)
71
+ for (const [dep, ver] of Object.entries(map ?? {})) {
72
+ if (isLocal(ver) || msg.body.latest) {
73
+ const result = await this._run(
74
+ 'npm',
75
+ ['info', dep, '--json'],
76
+ 'Cannot retrieve npm package info',
77
+ )
78
+ try {
79
+ const data = JSON.parse(result.text)
80
+ if (data.version) {
81
+ fixes.set(dep, data.version)
82
+ yield new OutputMessage({
83
+ content: [` ${dep}: ${ver} → ${data.version}`],
84
+ type: OutputMessage.TYPES.WARNING,
85
+ })
86
+ } else {
87
+ fails.add(dep)
88
+ yield new OutputMessage({
89
+ content: [` ${dep}: 404 — not found`],
90
+ type: OutputMessage.TYPES.ERROR,
91
+ })
92
+ }
93
+ } catch (/** @type {any} */ err) {
94
+ yield new OutputMessage(err)
95
+ }
96
+ } else {
97
+ yield new OutputMessage({
98
+ content: [` ${dep}: ${ver}`],
99
+ type: OutputMessage.TYPES.SUCCESS,
100
+ })
101
+ }
102
+ }
103
+ }
104
+
105
+ if (fails.size) {
106
+ yield new OutputMessage(
107
+ new Error(`Some dependencies are not yet published on npm: ${[...fails].join(', ')}`),
108
+ )
109
+ return
110
+ }
111
+
112
+ if ((msg.body.fix || msg.body.latest) && fixes.size) {
113
+ for (const [dep, ver] of fixes) {
114
+ // naive fix – replace in package.json
115
+ const args = ['install']
116
+ if (pkg.devDependencies?.[dep]) {
117
+ pkg.devDependencies[dep] = ver
118
+ args.push('--save-dev')
119
+ }
120
+ if (pkg.dependencies?.[dep]) pkg.dependencies[dep] = ver
121
+ args.push(dep + '@latest')
122
+ if (msg.body.latest) {
123
+ yield new OutputMessage([`Installing dependency`])
124
+ await this._run('npm', args)
125
+ }
126
+ changes.set(dep, `${dep}: ${ver}`)
127
+ yield new OutputMessage({
128
+ content: [`Fixed ${dep} → ${ver}`],
129
+ type: OutputMessage.TYPES.SUCCESS,
130
+ })
131
+ }
132
+ await this.fs.saveDocument('package.json', pkg)
133
+ yield new OutputMessage({
134
+ content: [`Fixes saved (${changes.size} changes)`],
135
+ type: OutputMessage.TYPES.SUCCESS,
136
+ })
137
+ yield new OutputMessage({ content: ['Dependencies fixed'] })
138
+ } else {
139
+ yield new OutputMessage(`Run with --fix to apply ${fixes.size} available fixes`)
140
+ yield new OutputMessage({ content: ['Dependencies check completed'] })
141
+ }
142
+ }
143
+ }
@@ -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';