@nan0web/release 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.datasets/README.dataset.jsonl +19 -0
  2. package/.editorconfig +20 -0
  3. package/CHANGELOG.md +6 -0
  4. package/CONTRIBUTING.md +42 -0
  5. package/LICENSE +15 -0
  6. package/README.md +284 -0
  7. package/bin/commands/InitCommand.js +10 -0
  8. package/bin/lib/functions.js +240 -0
  9. package/bin/lib/functions.test.js +138 -0
  10. package/bin/lib/temp/pnpm-lock.yaml +0 -0
  11. package/bin/nan0release.js +57 -0
  12. package/bin/release.js +12 -0
  13. package/bin/release.test.js +73 -0
  14. package/bin/temp/package.json +1 -0
  15. package/docs/system.uk.md +413 -0
  16. package/docs/uk/README.md +284 -0
  17. package/legacy/PrepareCommand.js +154 -0
  18. package/legacy/PrepareCommand.test.js +189 -0
  19. package/legacy/changelog.js +446 -0
  20. package/package.json +43 -0
  21. package/releases/1/0/v1.0.0/models/Person.js +26 -0
  22. package/releases/1/0/v1.0.0/models/Person.test.js +44 -0
  23. package/releases/1/0/v1.0.0/models/company.js +11 -0
  24. package/releases/1/0/v1.0.0/models/members.js +14 -0
  25. package/releases/1/0/v1.0.0/models/members.test.js +21 -0
  26. package/releases/1/0/v1.0.0/models/teams/DevTeam.js +6 -0
  27. package/releases/1/0/v1.0.0/models/teams/DevTeam.test.js +15 -0
  28. package/releases/1/0/v1.0.0/models/teams/index.js +5 -0
  29. package/releases/1/0/v1.0.0/release.js +12 -0
  30. package/releases/1/0/v1.0.0/release.md +121 -0
  31. package/releases/1/0/v1.0.0/release.test.js +36 -0
  32. package/releases/1/0/v1.0.0/test/YaRaSLove/git-native.test.js +14 -0
  33. package/releases/current.js +3 -0
  34. package/releases/index.js +11 -0
  35. package/src/README.md.js +468 -0
  36. package/src/Release/Document.js +110 -0
  37. package/src/Release/MarkdownToTest.js +137 -0
  38. package/src/Release/MarkdownToTest.test.js +60 -0
  39. package/src/Release/Person.js +29 -0
  40. package/src/Release.js +132 -0
  41. package/src/ReleaseCLI.js +122 -0
  42. package/src/Team.js +35 -0
  43. package/src/architecture/ProjectManagementAsCode.js +264 -0
  44. package/src/architecture/ProjectManagementAsCode.test.js +128 -0
  45. package/src/co/AppCommandMessage.js +22 -0
  46. package/src/co/AppCommandOptions.js +27 -0
  47. package/src/commands/ChatCommand.js +56 -0
  48. package/src/commands/HostCommand.js +63 -0
  49. package/src/commands/InitCommand.js +65 -0
  50. package/src/commands/ListCommand.js +56 -0
  51. package/src/commands/PublishCommand.js +144 -0
  52. package/src/commands/SealCommand.js +51 -0
  53. package/src/commands/ServeCommand.js +53 -0
  54. package/src/commands/ShowCommand.js +58 -0
  55. package/src/commands/SubCommand.js +21 -0
  56. package/src/commands/ValidateCommand.js +66 -0
  57. package/src/db/ReleaseDB.js +54 -0
  58. package/system.md +571 -0
  59. package/tsconfig.json +23 -0
  60. package/types/Release/Document.d.ts +31 -0
  61. package/types/Release/MarkdownToTest.d.ts +47 -0
  62. package/types/Release/Person.d.ts +17 -0
  63. package/types/Release.d.ts +89 -0
  64. package/types/ReleaseCLI.d.ts +33 -0
  65. package/types/Team.d.ts +27 -0
  66. package/types/architecture/ProjectManagementAsCode.d.ts +159 -0
  67. package/types/co/AppCommandMessage.d.ts +12 -0
  68. package/types/co/AppCommandOptions.d.ts +23 -0
  69. package/types/commands/ChatCommand.d.ts +20 -0
  70. package/types/commands/HostCommand.d.ts +16 -0
  71. package/types/commands/InitCommand.d.ts +20 -0
  72. package/types/commands/ListCommand.d.ts +21 -0
  73. package/types/commands/PublishCommand.d.ts +49 -0
  74. package/types/commands/SealCommand.d.ts +21 -0
  75. package/types/commands/ServeCommand.d.ts +17 -0
  76. package/types/commands/ShowCommand.d.ts +19 -0
  77. package/types/commands/SubCommand.d.ts +8 -0
  78. package/types/commands/ValidateCommand.d.ts +19 -0
  79. package/types/db/ReleaseDB.d.ts +30 -0
@@ -0,0 +1,110 @@
1
+ import Markdown, { MDElement, MDHeading1, MDHeading2, MDHeading3 } from "@nan0web/markdown"
2
+ import Person from "./Person.js"
3
+
4
+ class ReleaseDocument extends Markdown {
5
+ /** @type {Person[]} */
6
+ team
7
+ /** @type {Map<string, Person[]>} */
8
+ roles
9
+ /** @type {string} */
10
+ version
11
+ /** @type {Date | undefined} */
12
+ date
13
+
14
+ /**
15
+ * @param {Object} options
16
+ * @param {Array} [options.team] - Release team members
17
+ * @param {Map | Array} [options.roles] - Release roles map with team members
18
+ * @param {string} [options.version] - Release version
19
+ * @param {string} [options.date] - Release date
20
+ */
21
+ constructor(options = {}) {
22
+ super()
23
+ const {
24
+ team = [],
25
+ roles = new Map(),
26
+ version = "",
27
+ date
28
+ } = options
29
+ this.version = String(version)
30
+ this.date = date ? new Date(date) : undefined
31
+ this.team = team.map(t => Person.from(t))
32
+ this.roles = new Map(roles)
33
+ }
34
+
35
+ /**
36
+ * Parse release document content
37
+ * Extracts version and date from H1 heading
38
+ * @param {string} input - Markdown content
39
+ * @returns {MDElement[]}
40
+ */
41
+ parse(input) {
42
+ const result = super.parse(input)
43
+
44
+ const h1s = this.document.filter((el) => el instanceof MDHeading1)
45
+ if (h1s.length !== 1) {
46
+ throw new TypeError("Release document must have exactly one H1 heading with version and date")
47
+ }
48
+
49
+ const h1Content = h1s[0].content ?? ""
50
+ const [versionPart, ...dateParts] = h1Content.split(" - ")
51
+ this.version = versionPart.trim()
52
+ this.date = new Date(dateParts.join(" - ").trim())
53
+
54
+ const sections = []
55
+ /** @type {{title: string, tasks: Array} | null} */
56
+ let currentSection = null
57
+ for (const el of result) {
58
+ if (el instanceof MDHeading2) {
59
+ currentSection = {
60
+ title: el.content.trim(),
61
+ tasks: []
62
+ }
63
+ sections.push(currentSection)
64
+ }
65
+ else if (el instanceof MDHeading3 && currentSection) {
66
+ const content = el.content.trim()
67
+ const statusMatch = content.match(/^(\w+)\s*(.+)$/)
68
+ const slugMatch = content.match(/\[([^\]]+)\]$/)
69
+
70
+ let status = 'todo'
71
+ let taskContent = content
72
+ let slug = ''
73
+
74
+ if (statusMatch) {
75
+ status = statusMatch[1]
76
+ taskContent = statusMatch[2]
77
+ }
78
+
79
+ if (slugMatch) {
80
+ slug = slugMatch[1]
81
+ taskContent = taskContent.replace(/\[([^\]]+)\]$/, '').trim()
82
+ }
83
+
84
+ currentSection.tasks.push({
85
+ content: taskContent,
86
+ status: status,
87
+ slug: slug
88
+ })
89
+ }
90
+ }
91
+
92
+ return result
93
+ }
94
+
95
+ /**
96
+ * @param {*} input
97
+ * @returns {ReleaseDocument}
98
+ */
99
+ static from(input) {
100
+ if (input instanceof ReleaseDocument) return input
101
+ if (typeof input === "string") {
102
+ const doc = new ReleaseDocument()
103
+ doc.parse(input)
104
+ return doc
105
+ }
106
+ return new ReleaseDocument(input)
107
+ }
108
+ }
109
+
110
+ export default ReleaseDocument
@@ -0,0 +1,137 @@
1
+ /**
2
+ * MarkdownToTest - generates node:test code from markdown release notes
3
+ * Enables writing release notes first, then generating executable tests
4
+ */
5
+ class MarkdownToTest {
6
+ /**
7
+ * Convert markdown release notes to node:test code
8
+ * @param {string} markdown - Release notes markdown content
9
+ * @returns {string} Generated test code
10
+ */
11
+ generateTests(markdown) {
12
+ const lines = markdown.split('\n')
13
+ const versionMatch = markdown.match(/^#\s+(v[\d.]+)\s*-\s*(\d{4}-\d{2}-\d{2})/)
14
+
15
+ if (!versionMatch) {
16
+ throw new Error('Markdown must start with version header: # vX.Y.Z - YYYY-MM-DD')
17
+ }
18
+
19
+ const [, version, date] = versionMatch
20
+ let testCode = this.generateImports()
21
+ testCode += this.generateDescribeHeader(version, date)
22
+
23
+ const sections = this.extractSections(lines)
24
+ testCode += this.generateSectionTests(sections)
25
+
26
+ testCode += '})\n'
27
+ return testCode
28
+ }
29
+
30
+ /**
31
+ * Generate import statements for node:test
32
+ * @returns {string} Import code
33
+ */
34
+ generateImports() {
35
+ return `import { describe, it } from "node:test"
36
+ import { ok, strictEqual } from "node:assert"
37
+
38
+ `
39
+ }
40
+
41
+ /**
42
+ * Generate main describe block header
43
+ * @param {string} version - Release version
44
+ * @param {string} date - Release date
45
+ * @returns {string} Describe header code
46
+ */
47
+ generateDescribeHeader(version, date) {
48
+ return `describe("${version} - ${date}", () => {
49
+ `
50
+ }
51
+
52
+ /**
53
+ * Extract sections from markdown lines
54
+ * @param {string[]} lines - Markdown lines
55
+ * @returns {Array<{title: string, tasks: Array<{content: string, status: string, slug: string}>}>} Sections with tasks
56
+ */
57
+ extractSections(lines) {
58
+ /** @type {Array<{title: string, tasks: Array<{content: string, status: string, slug: string}>}>} */
59
+ const sections = []
60
+ /** @type {{title: string, tasks: Array<{content: string, status: string, slug: string}>} | null} */
61
+ let currentSection = null
62
+
63
+ for (let i = 0; i < lines.length; i++) {
64
+ const line = lines[i]
65
+
66
+ // H2 section
67
+ if (line.startsWith('## ')) {
68
+ currentSection = {
69
+ title: line.slice(3).trim(),
70
+ tasks: /** @type {Array<{content: string, status: string, slug: string}>} */([])
71
+ }
72
+ sections.push(currentSection)
73
+ continue
74
+ }
75
+
76
+ // H3 task
77
+ if (line.startsWith('###') && currentSection) {
78
+ const statusMatch = line.match(/^###( Draft| InProgress)? \*\*(.+)\*\* \[(.+)\]$/)
79
+ if (statusMatch) {
80
+ const [, status, content, slug] = statusMatch
81
+ currentSection.tasks.push({
82
+ content,
83
+ status: status ? status.trim() : 'Done',
84
+ slug
85
+ })
86
+ }
87
+ continue
88
+ }
89
+ }
90
+
91
+ return sections
92
+ }
93
+
94
+ /**
95
+ * Generate test code for sections and their tasks
96
+ * @param {Array<{title: string, tasks: Array}>} sections - Sections with tasks
97
+ * @returns {string} Generated test code
98
+ */
99
+ generateSectionTests(sections) {
100
+ let code = ''
101
+
102
+ sections.forEach(section => {
103
+ code += ` describe("${section.title}", () => {
104
+ `
105
+
106
+ section.tasks.forEach(/** @param {{status: string, content: string}} task */task => {
107
+ switch (task.status) {
108
+ case 'Draft':
109
+ code += ` it.todo("${task.content}", () => {
110
+ ok(false, "Draft task - not yet implemented")
111
+ })
112
+ `
113
+ break
114
+ case 'InProgress':
115
+ code += ` it.skip("${task.content}", () => {
116
+ ok(false, "In progress task - not yet completed")
117
+ })
118
+ `
119
+ break
120
+ default: // Done
121
+ code += ` it("${task.content}", () => {
122
+ ok(true, "Task completed successfully")
123
+ })
124
+ `
125
+ break
126
+ }
127
+ })
128
+
129
+ code += ` })
130
+ `
131
+ })
132
+
133
+ return code
134
+ }
135
+ }
136
+
137
+ export default MarkdownToTest
@@ -0,0 +1,60 @@
1
+ import { describe, it } from "node:test"
2
+ import { strictEqual } from "node:assert"
3
+ import MarkdownToTest from "./MarkdownToTest.js"
4
+
5
+ describe.skip("MarkdownToTest", () => {
6
+ it("should generate node:test code from markdown release notes", () => {
7
+ const parser = new MarkdownToTest()
8
+ const markdown = `# v1.1.0 - 2025-08-19
9
+
10
+ ## CLI application
11
+ ### **Treats project management like code: everything is versioned, auditable, reversible.** [treats-project-management-like-code-everything-is-versioned-auditable-reversible]
12
+ ### Draft **No external tools or databases needed — just Git.** [no-external-tools-or-databases-needed-just-git]
13
+ ### **Natural for developers; lowers barrier to entry for dev-centric teams.** [natural-for-developers-lowers-barrier-to-entry-for-dev-centric-teams]
14
+
15
+ ## UI application
16
+ ### **Drag-and-drop interface** [drag-and-drop-interface]
17
+ ### **Visual tree structure** [visual-tree-structure]`
18
+
19
+ const expected = `import { describe, it } from "node:test"
20
+ import { ok, strictEqual } from "node:assert"
21
+
22
+ describe("v1.1.0 - 2025-08-19", () => {
23
+ describe("CLI application", () => {
24
+ it("Treats project management like code: everything is versioned, auditable, reversible.", () => {
25
+ ok(true, "Task completed successfully")
26
+ })
27
+ it.skip("No external tools or databases needed — just Git.", () => {
28
+ ok(false, "Draft task - not yet implemented")
29
+ })
30
+ it("Natural for developers; lowers barrier to entry for dev-centric teams.", () => {
31
+ ok(true, "Task completed successfully")
32
+ })
33
+ })
34
+ describe("UI application", () => {
35
+ it("Drag-and-drop interface", () => {
36
+ ok(true, "Task completed successfully")
37
+ })
38
+ it("Visual tree structure", () => {
39
+ ok(true, "Task completed successfully")
40
+ })
41
+ })
42
+ })`
43
+
44
+ const result = parser.generateTests(markdown)
45
+ strictEqual(result.trim(), expected.trim())
46
+ })
47
+
48
+ it("should throw error if markdown doesn't have version header", () => {
49
+ const parser = new MarkdownToTest()
50
+ const markdown = `## CLI application
51
+ ### **Treats project management like code** [treats-project-management-like-code]`
52
+
53
+ try {
54
+ parser.generateTests(markdown)
55
+ throw new Error("Should have thrown an error")
56
+ } catch (error) {
57
+ strictEqual(error.message, 'Markdown must start with version header: # vX.Y.Z - YYYY-MM-DD')
58
+ }
59
+ })
60
+ })
@@ -0,0 +1,29 @@
1
+ import { HumanName, HumanContact, HumanGender } from "@nan0web/verse"
2
+
3
+ export default class Person {
4
+ /** @type {HumanName} */
5
+ name
6
+ /** @type {HumanGender} */
7
+ gender
8
+ /** @type {HumanContact[]} */
9
+ contacts
10
+ constructor(input = {}) {
11
+ const {
12
+ name = new HumanName([]),
13
+ gender = new HumanGender(-1),
14
+ contacts = []
15
+ } = input
16
+ this.name = HumanName.from(name)
17
+ this.gender = HumanGender.from(gender)
18
+ this.contacts = contacts.map(c => HumanContact.from(c))
19
+ }
20
+ /**
21
+ * @param {*} input
22
+ * @returns {Person}
23
+ */
24
+ static from(input) {
25
+ if (input instanceof Person) return input
26
+ return new Person(input)
27
+ }
28
+ }
29
+
package/src/Release.js ADDED
@@ -0,0 +1,132 @@
1
+ import ReleaseDocument from "./Release/Document.js"
2
+ import Logger from '@nan0web/log'
3
+
4
+ /**
5
+ * @typedef {Object} ReleaseConfig
6
+ * @property {string} [version] - Release version
7
+ * @property {string | number | Date | undefined} [createdAt] -
8
+ * @property {string | number | Date | undefined} [startAt] -
9
+ * @property {string | number | Date | undefined} [planAt] -
10
+ * @property {string | number | Date | undefined} [completeAt] -
11
+ * @property {string | ReleaseDocument} [document] -
12
+ * @property {Map<string, Function>} [tasks] - Task map with test functions
13
+ */
14
+
15
+ /**
16
+ * Release management class that coordinates task validation and release execution
17
+ */
18
+ class Release {
19
+ /** @type {string} */
20
+ version
21
+ /** @type {Date} */
22
+ createdAt
23
+ /** @type {Date?} */
24
+ startAt
25
+ /** @type {Date?} */
26
+ planAt
27
+ /** @type {Date?} */
28
+ completeAt
29
+ /** @type {Map<string, Function>} */
30
+ tasks
31
+ /** @type {ReleaseDocument} */
32
+ document
33
+ /** @type {Logger} */
34
+ logger
35
+
36
+ /**
37
+ * Creates a Release instance
38
+ * @param {ReleaseConfig} config - Release configuration
39
+ */
40
+ constructor(config = {}) {
41
+ const {
42
+ version = '0.0.0',
43
+ createdAt = new Date(),
44
+ startAt,
45
+ planAt,
46
+ completeAt,
47
+ document,
48
+ tasks = new Map()
49
+ } = config
50
+
51
+ this.version = String(version)
52
+ this.createdAt = new Date(createdAt)
53
+ this.startAt = startAt ? new Date(startAt) : null
54
+ this.planAt = planAt ? new Date(planAt) : null
55
+ this.completeAt = completeAt ? new Date(completeAt) : null
56
+ this.document = ReleaseDocument.from(document)
57
+ this.tasks = tasks
58
+ this.logger = new Logger()
59
+ }
60
+
61
+ get path() {
62
+ return [...this.version.replace("v", "").split(".").slice(0, 2), this.version].join("/")
63
+ }
64
+
65
+ /**
66
+ * Validate release by running all task tests
67
+ * @returns {Promise<Object>} Validation results
68
+ */
69
+ async validate() {
70
+ const results = {
71
+ passed: /** @type {Array} */([]),
72
+ failed: /** @type {Array<{taskId: any, error: string}>} */([]),
73
+ pending: /** @type {Array} */([])
74
+ }
75
+
76
+ for (const [taskId, testFn] of this.tasks.entries()) {
77
+ try {
78
+ await testFn()
79
+ results.passed.push(taskId)
80
+ } catch (error) {
81
+ results.failed.push({ taskId, error: /** @type {Error} */(error).message })
82
+ }
83
+ }
84
+
85
+ return results
86
+ }
87
+
88
+ /**
89
+ * Execute release process
90
+ * @param {Object} options - Release options
91
+ * @param {boolean} [options.ignoreFailTests=false] - Ignore failed tests
92
+ * @returns {Promise<boolean>} Release success status
93
+ */
94
+ async execute(options = {}) {
95
+ const { ignoreFailTests = false } = options
96
+ const validation = await this.validate()
97
+
98
+ if (!ignoreFailTests && validation.failed.length > 0) {
99
+ this.logger.error(`Release blocked: ${validation.failed.length} tasks failed`)
100
+ for (const failure of validation.failed) {
101
+ this.logger.error(` ${failure.taskId}: ${failure.error}`)
102
+ }
103
+ return false
104
+ }
105
+
106
+ this.logger.info(`Release ${this.version} executed successfully`)
107
+ return true
108
+ }
109
+
110
+ /**
111
+ * Get release progress statistics
112
+ * @returns {Promise<Object>} Progress statistics
113
+ */
114
+ async getProgress() {
115
+ const validation = await this.validate()
116
+ const totalTasks = this.tasks.size
117
+ const completedTasks = validation.passed.length
118
+ const failedTasks = validation.failed.length
119
+ const pendingTasks = validation.pending.length
120
+
121
+ return {
122
+ version: this.version,
123
+ total: totalTasks,
124
+ completed: completedTasks,
125
+ failed: failedTasks,
126
+ pending: pendingTasks,
127
+ progress: totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0
128
+ }
129
+ }
130
+ }
131
+
132
+ export default Release
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from '@nan0web/co'
4
+ import Logger from '@nan0web/log'
5
+ import FS from "@nan0web/db-fs"
6
+ import DB from "./db/ReleaseDB.js"
7
+ import Release from './Release.js'
8
+ import ChatCommand from './commands/ChatCommand.js'
9
+ import HostCommand from './commands/HostCommand.js'
10
+ import InitCommand from './commands/InitCommand.js'
11
+ import ListCommand from './commands/ListCommand.js'
12
+ import SealCommand from './commands/SealCommand.js'
13
+ import ServeCommand from './commands/ServeCommand.js'
14
+ import ShowCommand from './commands/ShowCommand.js'
15
+ import AppCommandMessage from './co/AppCommandMessage.js'
16
+
17
+ /**
18
+ * CLI application for release management
19
+ */
20
+ export default class ReleaseCLI extends Command {
21
+ static Message = AppCommandMessage
22
+ /** @type {Logger} */
23
+ logger
24
+ /** @type {DB?} */
25
+ db
26
+ /** @type {FS} */
27
+ fs
28
+ /** @type {Record<string, Release>} */
29
+ releases = {}
30
+ /** @type {Release?} */
31
+ current = null
32
+
33
+ constructor() {
34
+ super({
35
+ name: 'release',
36
+ help: 'Release management CLI tool'
37
+ })
38
+ this.logger = new Logger({ icons: true })
39
+ this.fs = new FS()
40
+ this.db = null
41
+ this.addOption('releaseDir', String, '.', 'Release dir')
42
+ this.addOption('version', String, '0.0.0', 'Release version')
43
+ }
44
+
45
+ /**
46
+ * @param {string[] | string} argv
47
+ * @returns {AppCommandMessage}
48
+ */
49
+ parse(argv = []) {
50
+ return AppCommandMessage.from(super.parse(argv))
51
+ }
52
+
53
+ /**
54
+ * Run CLI application
55
+ * @param {string[]} argv - Command line arguments
56
+ * @returns {Promise<void>}
57
+ */
58
+ async run(argv) {
59
+ const msg = this.parse(argv)
60
+ await this.fs.connect()
61
+
62
+ const rootDb = this.fs.extract(/** @type {string} */(msg.opts.releaseDir))
63
+
64
+ const indexStat = await rootDb.statDocument("index.js")
65
+ const currentStat = await rootDb.statDocument("current.js")
66
+ if (indexStat.exists && currentStat.exists) {
67
+ const indexJs = rootDb.absolute("index.js")
68
+ const currentJs = rootDb.absolute("current.js")
69
+ try {
70
+ this.releases = (await import(indexJs)).default
71
+ this.current = (await import(currentJs)).default
72
+ this.logger.info(Object.keys(this.releases).length, "release(s) found")
73
+ } catch (e) {
74
+ this.logger.error("Failed to load release database", e)
75
+ this.current = null
76
+ }
77
+ } else {
78
+ this.logger.info("No releases initialized. Use 'release init <version>' to create your first release.")
79
+ }
80
+
81
+ const globalOpts = { logger: this.logger }
82
+ this.addSubcommand(new ListCommand(globalOpts))
83
+ this.addSubcommand(new HostCommand(globalOpts))
84
+ this.addSubcommand(new ServeCommand(globalOpts))
85
+
86
+ if (this.current) {
87
+ this.logger.info("Current release", this.current.version)
88
+ this.db = DB.from(rootDb.extract(this.current.path))
89
+ await this.db.connect()
90
+ const document = await this.db.loadDocument("release.md")
91
+ const release = new Release({ document, version: this.current.version })
92
+ const currentOpts = { ...globalOpts, db: this.db }
93
+ this.addSubcommand(new InitCommand(currentOpts))
94
+ this.addSubcommand(new ShowCommand(currentOpts))
95
+ this.addSubcommand(new ChatCommand(currentOpts))
96
+ this.addSubcommand(new SealCommand(currentOpts))
97
+ }
98
+ else {
99
+ this.logger.info("Provide a release version")
100
+ this.logger.info(this.generateHelp())
101
+ }
102
+ return
103
+ }
104
+
105
+ async loadReleaseFiles() {
106
+ if (!this.db) return
107
+ const stream = this.db.findStream("")
108
+ const started = Date.now()
109
+ for await (const entry of stream) {
110
+ const spent = ((Date.now() - started) / 1000).toFixed(3)
111
+ this.logger.clearLine(this.logger.cursorUp())
112
+ this.logger.info("Loading ..", spent + "s", this.db.data.size, entry.file.path)
113
+ }
114
+ const spent = ((Date.now() - started) / 1000).toFixed(3)
115
+ this.logger.clearLine(this.logger.cursorUp(/** @type {DB} */(this.db).data.size & 1))
116
+ const info = [
117
+ ["Loaded in", spent + "s", /** @type {DB} */(this.db).data.size, "document(s)"],
118
+ ["", "", /** @type {DB} */(this.db).releases.length, "release(s)"],
119
+ ]
120
+ this.logger.table(info, [], { padding: 1 })
121
+ }
122
+ }
package/src/Team.js ADDED
@@ -0,0 +1,35 @@
1
+ import { HumanName, HumanGender, HumanContact } from "@nan0web/verse"
2
+
3
+ class Person {
4
+ /** @type {HumanName} */
5
+ name
6
+ /** @type {HumanGender} */
7
+ gender
8
+ /** @type {HumanContact[]} */
9
+ contacts
10
+
11
+ constructor(name) {
12
+ this.name = new HumanName(name)
13
+ this.gender = new HumanGender(-1)
14
+ this.contacts = []
15
+ }
16
+ }
17
+
18
+ class CLevelTeam {
19
+ static name = "C Level"
20
+ static ceo = new Person(["Yaroslav", "Wise"])
21
+ static cfo = new Person(["Chingis"])
22
+ }
23
+
24
+ class UXTeam {
25
+ static name = "UX"
26
+ static designer = new Person(["Taras", "Shevchenko"])
27
+ }
28
+
29
+ class Company {
30
+ static name = "NaN•Web"
31
+ static c = CLevelTeam
32
+ static ux = UXTeam
33
+ }
34
+
35
+ export default Company