@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
@@ -1,446 +0,0 @@
1
- import { applyFilter, findAllFiles, saveFile, findFilesIn } from "nanoweb-fs"
2
- import { readFileSync, statSync } from "node:fs"
3
- import { resolve, relative } from "node:path"
4
- import { execSync } from "node:child_process"
5
- import { packFiles } from "../functions/pack.js"
6
- import Request from "../../src/apps/core/Request.js"
7
- import Response from "../../src/apps/core/Response.js"
8
- import LLMApp from "../../src/apps/cgi/LLMApp.js"
9
- import TestRunner from "../functions/TestRunner.js"
10
-
11
- const binaryFileExtensions = [
12
- '.png', '.webp', '.avif', '.jpg', '.bmp', '.jpeg', '.gif',
13
- '.tiff', '.ico', '.svg', '.mp3', '.mp4', '.wav', '.ogg',
14
- '.flac', '.mkv', '.mov', '.avi', '.wmv', '.zip', '.rar',
15
- '.7z', '.tar', '.gz', '.pdf', '.exe', '.dll', '.bin',
16
- ]
17
-
18
- function trimQuotes(str) {
19
- return str.replace(/^"|"$/g, '')
20
- }
21
-
22
- /**
23
- * Reads a file if it passes the provided filter and is not a binary file.
24
- * @param {string} filePath - The path to the file.
25
- * @param {Object} filter - An optional filter to apply.
26
- * @returns {string|false} - The file content as a string or false if skipped.
27
- */
28
- function readFile(filePath, filter = {}) {
29
- // Check if the file is binary
30
- if (binaryFileExtensions.some(ext => filePath.endsWith(ext))) {
31
- return '@@ BINARY.FILE @@'
32
- }
33
-
34
- try {
35
- // Apply additional filter before reading
36
- if (applyFilter(filePath, filter)) {
37
- return readFileSync(filePath, 'utf-8')
38
- }
39
- } catch (error) {
40
- console.warn(`⚠️ Could not read file: ${filePath}`, error.message)
41
- }
42
-
43
- return false
44
- }
45
-
46
- /**
47
- * Changelog application class.
48
- * @extends LLMApp
49
- */
50
- class ChangelogApp extends LLMApp {
51
- static ROUTES = {
52
- ...LLMApp.ROUTES,
53
- write: "Generate changelog based on the latest changes",
54
- }
55
- static DEFAULT_ROUTE = "write"
56
- static OPTIONS = {
57
- ...LLMApp.OPTIONS,
58
- keepComments: {
59
- type: Boolean, prefix: "--", help: "Keep comments from source files for LLM prompt"
60
- },
61
- dry: {
62
- type: Boolean, prefix: "--", help: "Show all the options without making any requests"
63
- },
64
- recentCache: {
65
- type: Boolean, prefix: "--", help: "Use recent cache instead of making a request",
66
- },
67
- sourceDir: {
68
- type: String, prefix: "-", help: "Source directory to analyze for changes"
69
- },
70
- targetFile: {
71
- type: String, prefix: "-", help: "Target file to save prompt"
72
- },
73
- inputTokenLimit: { type: Number, prefix: "-", help: "Token count limit" },
74
- inputSizeLimit: { type: Number, prefix: "-", help: "Size limit in bytes" },
75
- ignoreTests: { type: Boolean, prefix: "--", help: "Ignore test files when generating changelog" }
76
- }
77
-
78
- detectVersion(version) {
79
- if (Array.isArray(version)) {
80
- const [v] = version.map(v => this.detectVersion(v)).filter(Boolean)
81
- return v ?? null
82
- }
83
- const arr = String(version).split('\n').map(v => v.trim()).filter(v => v.length > 0)
84
- for (let i = 0; i < arr.length; i++) {
85
- const v = arr[i]
86
- if (v.startsWith('## [')) {
87
- return v.split(']')[0].replace('## [', '').trim()
88
- }
89
- }
90
- return null
91
- }
92
-
93
- /**
94
- * Trims the end of a string.
95
- *
96
- * @param {string} text - The string to trim.
97
- * @returns {string} - The trimmed string.
98
- */
99
- trimEnd(text) {
100
- return text.replace(/\s+$/, '');
101
- }
102
-
103
- extractVersions(changelog) {
104
- const versions = []
105
- let intro = ""
106
- const versionsMap = new Map()
107
- let currentVersion = null
108
-
109
- if (!changelog) {
110
- return { intro, versions }
111
- }
112
-
113
- const rows = changelog.split('\n')
114
-
115
- for (const row of rows) {
116
- const trimmedRow = row.trim()
117
- if (trimmedRow.startsWith('## [')) {
118
- if (currentVersion !== null) {
119
- const text = this.trimEnd(currentVersion.join('\n'))
120
- versionsMap.set(this.detectVersion(currentVersion), text)
121
- versions.push(text)
122
- }
123
- currentVersion = [this.trimEnd(row)]
124
- } else if (currentVersion === null) {
125
- intro += row + "\n"
126
- } else {
127
- currentVersion.push(this.trimEnd(row))
128
- }
129
- }
130
-
131
- if (currentVersion !== null) {
132
- const text = this.trimEnd(currentVersion.join('\n'))
133
- versionsMap.set(this.detectVersion(currentVersion), text)
134
- versions.push(text)
135
- }
136
- return { intro, versions, versionsMap }
137
- }
138
-
139
- getChangeLogVersions(sourceDir) {
140
- const changelogFile = resolve(sourceDir, 'CHANGELOG.md')
141
- let changelog = ''
142
- try {
143
- changelog = readFileSync(changelogFile, 'utf-8')
144
- } catch (error) {
145
- console.warn(`⚠️ Could not read CHANGELOG.md: ${error.message}`)
146
- }
147
-
148
- const { intro, versions, versionsMap } = this.extractVersions(changelog)
149
- return { intro, versions, versionsMap, changelog }
150
- }
151
-
152
- /**
153
- * Reads the CHANGELOG.md file, extracts recent versions, and formats them for LLM input.
154
- * @param {string} sourceDir
155
- * @param {number} [messageSize=0]
156
- * @param {number} [maxSize=Infinity]
157
- * @returns {object} - Recent changelog entries and metadata.
158
- * @property {string} prompt - The formatted changelog prompt.
159
- * @property {Array<string>} intro - Introductory lines from the changelog.
160
- * @property {number} pass - Number of versions added to the prompt.
161
- * @property {number} recentCount - Number of recent versions included.
162
- * @property {Array<string>} recentVersions - Recent version entries.
163
- * @property {Array<string>} versions - All version entries from the changelog.
164
- */
165
- getRecentChangeLog(sourceDir, messageSize = 0, maxSize = Infinity) {
166
- const { intro, versions, changelog } = this.getChangeLogVersions(sourceDir)
167
-
168
- let prompt = ""
169
- let pass = 0
170
-
171
- if (!changelog) {
172
- return { prompt, intro, pass, versions }
173
- }
174
-
175
- const recentCount = 2
176
- const recentVersions = versions.slice(0, recentCount)
177
-
178
- for (const version of recentVersions) {
179
- if (prompt.length + version.length > 9_000 || messageSize + prompt.length > maxSize) {
180
- break
181
- }
182
- ++pass
183
- prompt += version.trim() + '\n'
184
- }
185
-
186
- return { prompt, intro, pass, recentCount, recentVersions, versions }
187
- }
188
-
189
- getChangedFiles(sourceDir = this.cwd, ignore = []) {
190
- const changes = execSync(`git status --porcelain`, { cwd: sourceDir }).toString().split("\n")
191
- const files = changes.map(line => line.trim()).filter(line => '' !== line).map(file => file.split(' '))
192
- const newDirs = files.filter(([status]) => '??' === status).map(([_, ...args]) => (args.join(' '))).map(trimQuotes).filter(f => !ignore.includes(f))
193
- const modifiedFiles = files.filter(([status]) => 'M' === status).map(([_, ...args]) => (args.join(' '))).map(trimQuotes).filter(f => !ignore.includes(f))
194
- const newFiles = findFilesIn(newDirs).map(f => relative(sourceDir, f))
195
- const allFiles = [...newFiles, ...modifiedFiles]
196
-
197
- if (allFiles.length === 0) {
198
- return { files: [], errors: [] }
199
- }
200
-
201
- const errors = []
202
- const diffs = {}
203
- newFiles.forEach(file => {
204
- try {
205
- const filePath = resolve(sourceDir, file)
206
- const stat = statSync(filePath)
207
- if (stat.isDirectory()) {
208
- const dirFiles = findAllFiles(filePath, {}, ignore)
209
- dirFiles.forEach(f => {
210
- const content = readFile(f, /\.+/)
211
- if (content) diffs[relative(sourceDir, f)] = content
212
- })
213
- } else {
214
- const content = readFile(filePath, /\.+/)
215
- if (content) diffs[file] = content
216
- }
217
- } catch (error) {
218
- errors.push({ message: `⚠️ Could not read new file: ${file}`, stack: error.stack })
219
- }
220
- })
221
-
222
- modifiedFiles.forEach(file => {
223
- try {
224
- const diff = execSync(`git diff -- ${file}`, { cwd: sourceDir }).toString().trim()
225
- if (diff) diffs[file] = diff
226
- } catch (error) {
227
- errors.push({ message: `⚠️ Could not fetch git diff for: ${file}`, stack: error.stack })
228
- }
229
- })
230
-
231
- const fileBlocks = Object.entries(diffs).map(([file, content]) => ({
232
- file,
233
- content
234
- }))
235
- return { files: fileBlocks, errors }
236
- }
237
-
238
- replaceVersion(versionContent, version, at) {
239
- const rows = versionContent.split('\n').filter(row => !row.trim().startsWith('## ['))
240
- rows.unshift(`## [${version}] - ${new Date(at).toISOString().split('T')[0]}`)
241
- return rows.join('\n')
242
- }
243
-
244
- mergeVersions(current, target, version = null, at = Date.now()) {
245
- const c = this.extractVersions(current)
246
- const t = this.extractVersions(target)
247
- const v = Array.from(t.versionsMap.keys()).filter(Boolean)
248
- const getContent = () => {
249
- const versionContent = t.versionsMap.get(version)
250
- if (!versionContent) {
251
- throw new Error(`Version ${version} not found in target changelog.`)
252
- }
253
- return versionContent
254
- }
255
- let result = []
256
- if (version) {
257
- if (v.includes(version)) {
258
- result.push(this.replaceVersion(getContent(), version, at).trim())
259
- } else {
260
- throw new Error(`Version ${version} not found in target changelog.`)
261
- }
262
- } else if (v.length > 0) {
263
- version = v[0]
264
- if (version.includes(".")) {
265
- result.push(this.replaceVersion(getContent(), version, at).trim())
266
- }
267
- }
268
- c.versions.forEach((versionContent) => {
269
- const ver = this.detectVersion(versionContent)
270
- if (ver !== version) {
271
- result.push(versionContent.trim())
272
- }
273
- })
274
- return (c.intro + result.join('\n\n')).trim()
275
- }
276
-
277
- async runTests(res) {
278
- // Example usage
279
- const testRunner = new TestRunner(this.cwd)
280
- const format = new Intl.NumberFormat("en-US").format
281
- const progress = (data, suffix = "") => {
282
- res.write(`Testing ✅ ${data.pass} ❗️ ${data.errs} | Files: ✅ ${data.filesPass} ❗️ ${data.filesErr} | ⏰ ${format(data.time)}ms${suffix}`)
283
- }
284
- testRunner.on("start", (data) => {
285
- res.write("Running tests... ")
286
- })
287
- testRunner.on("data", (data) => {
288
- progress(data)
289
- })
290
- testRunner.on("error", (error) => {
291
- if (["Debugger attached.", ""].includes(error.line)) {
292
- return
293
- }
294
- if (error.line) {
295
- return
296
- }
297
- res.error(`⚠️ Tests failed: ${error.message}`)
298
- res.debug(error.stack)
299
- })
300
- testRunner.on("end", (data) => {
301
- progress(data, data.errs ? "" : ` => ✅ ${data.pass} (${data.filesPass})`)
302
- if (data.errs > 0) {
303
- res.error(`⚠️ Tests completed with errors: ${data.errs} failed out of ${data.total}`)
304
- res.debug(data.stderr.filter(s => "" !== s).join("\n"))
305
- }
306
- })
307
- const ok = await testRunner.run()
308
- return ok
309
- }
310
-
311
- /**
312
- * Generates changelog by analyzing changed files and sending to LLM.
313
- * @param {Request} req - Input request.
314
- * @param {Response} res - Output response.
315
- * @returns {Promise<Response>} Response.
316
- */
317
- async routeWrite(req, res) {
318
- const { body, config } = req
319
- const isDry = body?.opts?.dry
320
- const sourceDir = body.sourceDir || this.cwd
321
- const targetFile = body.targetFile
322
-
323
- const { driver, model, config: chatConfig = {} } = this.llmDriverFor(["changelog", "learn", "$default"], config.chat)
324
- if (driver) {
325
- await driver.init()
326
- }
327
-
328
- const inputTokenLimit = body.inputTokenLimit || chatConfig?.maxInput || chatConfig?.maxTokens || model?.maxInput || model?.maxTokens || 32_000
329
- const inputSizeLimit = body.inputSizeLimit || model?.maxInputBytes || model?.maxBytes || (inputTokenLimit ? inputTokenLimit * 3 : 128_000)
330
- const ignore = body.i || chatConfig?.resolve?.ignore || ['node_modules', 'dist', 'build', '.min.', { endsWith: '.config.js' }, 'CHANGELOG.md']
331
- const ignoreTests = body.ignoreTests || chatConfig?.ignoreTests || false
332
-
333
- if (!ignoreTests) {
334
- const ok = await this.runTests(res)
335
- if (!ok) {
336
- res.error("⚠️ Tests failed, cannot proceed with changelog generation.")
337
- res.info("You can either fix the tests or run this command with --ignoreTests option to skip tests")
338
- res.info("You can also set ignoreTests to true in the config.")
339
- return res.send()
340
- }
341
- }
342
-
343
- const instructions = this.getInstructions(["coder", "changelog"])
344
- const id = this.getRequestID()
345
- let cache = this.loadCache(id)
346
-
347
- if (body.recentCache) {
348
- const arr = this.listCaches()
349
- cache = this.loadCache(arr.pop())
350
- res.info("Loaded cache", id)
351
- }
352
-
353
- if (false === cache) {
354
- const { files: fileBlocks, errors = [] } = this.getChangedFiles(sourceDir, ignore)
355
- errors.forEach(err => {
356
- res.error(err.message)
357
- res.debug(err.stack)
358
- })
359
- if (0 === fileBlocks.length) {
360
- res.info(`✅ No new or modified files detected.`)
361
- return res.send()
362
- }
363
- const expectedResponseRate = 0.1
364
- const { maxInput } = this.getMaxInput(model, chatConfig, body)
365
- const opts = {
366
- id, instructions, model, driver,
367
- maxInput,
368
- expectedResponseRate,
369
- resolve: {
370
- target: chatConfig?.resolve?.target || [this.cwd],
371
- accept: chatConfig?.resolve?.accept || null,
372
- ignore: body.i || chatConfig?.resolve?.ignore ||
373
- chatConfig?.resolve?.skip || ['node_modules', 'dist', 'build', '.min.', { endsWith: '.config.js' }]
374
- },
375
- concurrent: chatConfig?.concurrent || 1,
376
- files: fileBlocks
377
- }
378
- const { blocks, tokens, size, iCount } = await this.packRequest(opts)
379
-
380
- this.emit("pack.collected", { id, opts, blocks, iCount, tokens, size, model })
381
-
382
- const recentChangelog = this.getRecentChangeLog(sourceDir, 0, inputSizeLimit)
383
- const changelogPrompt = recentChangelog.prompt ? `## \n### Recent CHANGELOG.md entries\n#### \`./CHANGELOG.md\`\n\`\`\`md\n${recentChangelog.prompt}\n\`\`\`\n\n` : ''
384
- for (const block of blocks) {
385
- let tokens = await this.ensureTokensCount(changelogPrompt, driver)
386
- block.prompts.push({ prompt: changelogPrompt, size: changelogPrompt.length, tokens })
387
- }
388
-
389
- if (isDry) {
390
- res.info("Prompt with attached files and their changes:")
391
- blocks.forEach(block => {
392
- res.info(String(block.prompts.filter(block => !block.file).map(p => String(p.prompt)).join("\n\n")))
393
- res.info("## CONTEXT (only file names for dry mode)")
394
- res.info("- " + String(block.prompts.filter(block => block.file).map(p => String(p.file)).sort().join("\n- ")))
395
- })
396
- } else {
397
- if (!driver) {
398
- res.warn("Driver not found")
399
- if (targetFile) {
400
- const packed = packFiles(fileBlocks, targetFile, { keepComments: body.opts?.keepComments ?? false })
401
- const related = packed.map(p => ([relative(sourceDir, p[0]), p[1]]))
402
- res.info("Packed files into:")
403
- res.table(related, [], { padding: 3, prefix: "- " })
404
- } else {
405
- res.warn("Target file not specified, skipping packing")
406
- }
407
- return res.send()
408
- }
409
- const chunks = await this.completeChunks(blocks, { driver, model, id, config: chatConfig })
410
- cache = { id, chunks }
411
- }
412
- }
413
-
414
- if (cache && !isDry) {
415
- await this.unpackChunks({ ...cache, noSave: true })
416
- const changelogPath = resolve(sourceDir, 'CHANGELOG.md')
417
- const files = cache.chunks.reduce((acc, chunk) => ({ ...acc, ...this.extract(chunk.response) }), {})
418
- const newContent = files['./CHANGELOG.md'] || files['CHANGELOG.md']
419
- if (!newContent) {
420
- res.warn(`No CHANGELOG.md content found in LLM response`)
421
- return res.send()
422
- }
423
-
424
- // const newData = this.extractVersions(newContent)
425
- const current = this.getChangeLogVersions(sourceDir)
426
- // git tag -l
427
- const text = this.mergeVersions(current.changelog, newContent)
428
- saveFile(changelogPath, text)
429
- res.info(`Updated ./CHANGELOG.md with new content`)
430
-
431
- if (!ignoreTests) {
432
- const ok = await this.runTests(res)
433
- if (!ok) {
434
- res.error("⚠️ Tests failed after response")
435
- res.info("Use git command to revert changes")
436
- res.info("\n git checkout -- CHANGELOG.md\n")
437
- return res.send()
438
- }
439
- }
440
- }
441
-
442
- return res.send()
443
- }
444
- }
445
-
446
- export default ChangelogApp
@@ -1,26 +0,0 @@
1
- import { Contact } from "@nan0web/co"
2
-
3
- class Person {
4
- name
5
- contacts
6
- gpgKey
7
- revokedKey
8
- constructor(input = {}) {
9
- const {
10
- name = "",
11
- contacts = [],
12
- gpgKey = null,
13
- revokedKey = null
14
- } = input
15
- this.name = String(name)
16
- this.contacts = contacts.map(c => Contact.from(c))
17
- this.gpgKey = gpgKey
18
- this.revokedKey = revokedKey
19
- }
20
- static from(input) {
21
- if (input instanceof Person) return input
22
- return new Person(input)
23
- }
24
- }
25
-
26
- export default Person
@@ -1,44 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
-
4
- import Person from "./Person.js"
5
- import { Contact } from "@nan0web/contact"
6
-
7
- describe("Person Model", () => {
8
- it("constructs with default empty values", () => {
9
- const person = new Person()
10
- assert.equal(person.name, "")
11
- assert.deepEqual(person.contacts, [])
12
- assert.equal(person.gpgKey, null)
13
- assert.equal(person.revokedKey, null)
14
- })
15
-
16
- it("constructs with given input", () => {
17
- const input = {
18
- name: "Alice",
19
- contacts: ["https://example.com", "mailto:alice@example.com"],
20
- gpgKey: "KEY123",
21
- revokedKey: "OLDKEY456"
22
- }
23
- const person = new Person(input)
24
- assert.equal(person.name, "Alice")
25
- assert.equal(person.gpgKey, "KEY123")
26
- assert.equal(person.revokedKey, "OLDKEY456")
27
- assert.equal(person.contacts.length, 2)
28
- assert.ok(person.contacts[0] instanceof Contact)
29
- assert.ok(person.contacts[1] instanceof Contact)
30
- })
31
-
32
- it("can be created using static from method", () => {
33
- const input = { name: "Bob" }
34
- const person = Person.from(input)
35
- assert.ok(person instanceof Person)
36
- assert.equal(person.name, "Bob")
37
- })
38
-
39
- it("returns same instance when from receives Person", () => {
40
- const p1 = new Person({ name: "Charlie" })
41
- const p2 = Person.from(p1)
42
- assert.equal(p1, p2)
43
- })
44
- })
@@ -1,11 +0,0 @@
1
- import DevTeam from "./teams/DevTeam.js"
2
- import members from "./members.js"
3
-
4
- export default {
5
- name: "NaN•Web",
6
- teams: {
7
- DevTeam,
8
- },
9
- members,
10
- }
11
-
@@ -1,14 +0,0 @@
1
- import { Contact } from "@nan0web/co"
2
- import Person from "./Person.js"
3
-
4
- export default {
5
- YaRaSLove: new Person({
6
- name: "YaRaSLove",
7
- contacts: [
8
- new Contact("https://yaro.page"),
9
- new Contact("mailto:support@yaro.page"),
10
- new Contact("tel:00380673861050"),
11
- ],
12
- gpgKey: "?"
13
- }),
14
- }
@@ -1,21 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
-
4
- import members from "./members.js"
5
-
6
- describe("Members Model", () => {
7
- it("exports YaRaSLove as a member", () => {
8
- assert.ok(members.YaRaSLove)
9
- assert.equal(members.YaRaSLove.name, "YaRaSLove")
10
- })
11
-
12
- it("YaRaSLove has three contacts", () => {
13
- const yar = members.YaRaSLove
14
- assert.equal(yar.contacts.length, 3)
15
- })
16
-
17
- it("YaRaSLove includes a valid GPG key", () => {
18
- const yar = members.YaRaSLove
19
- assert.equal(yar.gpgKey, "ABC123456789")
20
- })
21
- })
@@ -1,6 +0,0 @@
1
- import members from "../members.js"
2
-
3
- export default {
4
- name: "Software development team",
5
- developer: members.YaRaSLove,
6
- }
@@ -1,15 +0,0 @@
1
- import { describe, it } from "node:test"
2
- import { strict as assert } from "node:assert"
3
-
4
- import DevTeam from "./DevTeam.js"
5
-
6
- describe("DevTeam Model", () => {
7
- it("has a static name defined", () => {
8
- assert.equal(DevTeam.name, "Software development team")
9
- })
10
-
11
- it("references developer from members", () => {
12
- assert.ok(DevTeam.developer)
13
- assert.equal(DevTeam.developer.name, "YaRaSLove")
14
- })
15
- })
@@ -1,5 +0,0 @@
1
- import DevTeam from "./DevTeam.js"
2
-
3
- export default {
4
- DevTeam
5
- }
@@ -1,12 +0,0 @@
1
- // import Release from "@nan0web/release"
2
- import Release from "../../../../src/Release.js"
3
- import company from "./models/company.js"
4
-
5
- export default new Release({
6
- version: "v1.0.0",
7
- date: "2025-08-18",
8
-
9
- company,
10
- teams: company.teams,
11
- members: company.members,
12
- })