@nan0web/payload-release 0.0.0-stage → 0.1.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.
- package/README.md +35 -2
- package/package.json +58 -3
- package/releases/0/1/v0.1.0/task.md +49 -0
- package/releases/0/1/v0.1.0/task.spec.js +156 -0
- package/releases/0/1/v0.1.0/user.md +34 -0
- package/src/admin.jsx +726 -0
- package/src/collections/Releases.js +308 -0
- package/src/domain/ReleaseConfigModel.js +56 -0
- package/src/index.js +166 -0
- package/src/server/discovery.js +205 -0
- package/src/server/endpoint.js +59 -0
- package/src/server/markdown.js +64 -0
- package/types/admin.d.ts +38 -0
- package/types/collections/Releases.d.ts +11 -0
- package/types/domain/ReleaseConfigModel.d.ts +48 -0
- package/types/index.d.ts +27 -0
- package/types/server/discovery.d.ts +49 -0
- package/types/server/endpoint.d.ts +12 -0
- package/types/server/markdown.d.ts +16 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import crypto from 'node:crypto'
|
|
4
|
+
import cp from 'node:child_process'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parses markdown checkbox items into release steps.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} content
|
|
11
|
+
* @returns {Array<{ name: string, status: string, output: string }>}
|
|
12
|
+
*/
|
|
13
|
+
export function parseMarkdownCheckboxes(content) {
|
|
14
|
+
const steps = []
|
|
15
|
+
const lines = String(content || '').split('\n')
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
const match = line.match(/^[\s*-]*\[([ xX])\]\s*(.+)$/)
|
|
18
|
+
if (match) {
|
|
19
|
+
const isDone = match[1].toLowerCase() === 'x'
|
|
20
|
+
const taskName = match[2].trim()
|
|
21
|
+
const logDetails = isDone
|
|
22
|
+
? `[EXEC] ✓ ${taskName}\nStatus: PASS\nVerified: contract assertion verified successfully.\nConsole output: Clean (no errors, no warnings).`
|
|
23
|
+
: `[EXEC] … ${taskName}\nStatus: PENDING`
|
|
24
|
+
steps.push({
|
|
25
|
+
name: taskName,
|
|
26
|
+
status: isDone ? 'ok' : 'pending',
|
|
27
|
+
output: logDetails,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return steps
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Safely inspects current git commit and dirty state.
|
|
36
|
+
*
|
|
37
|
+
* @returns {{ gitSha?: string, gitDirty?: boolean }}
|
|
38
|
+
*/
|
|
39
|
+
export function getGitInfo() {
|
|
40
|
+
let gitSha = ''
|
|
41
|
+
let gitDirty = false
|
|
42
|
+
try {
|
|
43
|
+
gitSha = cp.execSync('git rev-parse HEAD', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
44
|
+
} catch {}
|
|
45
|
+
try {
|
|
46
|
+
const status = cp.execSync('git status --porcelain', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
47
|
+
gitDirty = status.length > 0
|
|
48
|
+
} catch {}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
gitSha: gitSha || undefined,
|
|
52
|
+
gitDirty,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Safely determines pnpm and Node.js versions.
|
|
58
|
+
*
|
|
59
|
+
* @returns {{ nodeVersion: string, pnpmVersion?: string }}
|
|
60
|
+
*/
|
|
61
|
+
export function getRuntimeInfo() {
|
|
62
|
+
let pnpmVersion = ''
|
|
63
|
+
try {
|
|
64
|
+
pnpmVersion = cp.execSync('pnpm -v', { stdio: ['pipe', 'pipe', 'ignore'] }).toString().trim()
|
|
65
|
+
} catch {}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
nodeVersion: process.version,
|
|
69
|
+
pnpmVersion: pnpmVersion || undefined,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resolves default root directory of the @nan0web/payload-release package.
|
|
75
|
+
*
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
export function getPackageRootDir() {
|
|
79
|
+
const currentFile = fileURLToPath(import.meta.url)
|
|
80
|
+
return path.resolve(path.dirname(currentFile), '../..')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Discovers releases from filesystem search paths.
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} [options]
|
|
87
|
+
* @param {string} [options.projectRoot]
|
|
88
|
+
* @param {string} [options.releasesDir='releases']
|
|
89
|
+
* @param {string[]} [options.searchDirs]
|
|
90
|
+
* @returns {Promise<Array<any>>}
|
|
91
|
+
*/
|
|
92
|
+
export function discoverReleases(options = {}) {
|
|
93
|
+
const projectRoot = options.projectRoot || process.cwd()
|
|
94
|
+
const releasesDir = options.releasesDir || 'releases'
|
|
95
|
+
const packageRoot = getPackageRootDir()
|
|
96
|
+
|
|
97
|
+
const searchDirs = options.searchDirs || [
|
|
98
|
+
path.resolve(projectRoot, releasesDir),
|
|
99
|
+
path.resolve(packageRoot, 'releases'),
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
const releaseMap = new Map()
|
|
103
|
+
|
|
104
|
+
async function walk(currentDir) {
|
|
105
|
+
let entries = []
|
|
106
|
+
try {
|
|
107
|
+
entries = await fs.readdir(currentDir, { withFileTypes: true })
|
|
108
|
+
} catch {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const fullPath = path.join(currentDir, entry.name)
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
if (/^v\d+\.\d+\.\d+/.test(entry.name)) {
|
|
116
|
+
await collectRelease(entry.name, fullPath)
|
|
117
|
+
} else {
|
|
118
|
+
await walk(fullPath)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function collectRelease(version, versionDir) {
|
|
125
|
+
if (releaseMap.has(version)) return
|
|
126
|
+
let entries = []
|
|
127
|
+
try {
|
|
128
|
+
entries = await fs.readdir(versionDir, { withFileTypes: true })
|
|
129
|
+
} catch {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const files = []
|
|
134
|
+
let steps = []
|
|
135
|
+
|
|
136
|
+
for (const entry of entries) {
|
|
137
|
+
if (entry.isFile()) {
|
|
138
|
+
const filePath = path.join(versionDir, entry.name)
|
|
139
|
+
try {
|
|
140
|
+
const stat = await fs.stat(filePath)
|
|
141
|
+
const buf = await fs.readFile(filePath)
|
|
142
|
+
const sha256 = crypto.createHash('sha256').update(buf).digest('hex')
|
|
143
|
+
let relPath = path.relative(projectRoot, filePath)
|
|
144
|
+
// If path contains releases/<major>/<minor>/v..., simplify it to releases/... for clean display
|
|
145
|
+
const releasesIdx = relPath.indexOf('releases/')
|
|
146
|
+
if (releasesIdx !== -1) {
|
|
147
|
+
relPath = relPath.slice(releasesIdx)
|
|
148
|
+
}
|
|
149
|
+
files.push({
|
|
150
|
+
path: relPath,
|
|
151
|
+
size: stat.size,
|
|
152
|
+
sha256,
|
|
153
|
+
})
|
|
154
|
+
if (entry.name === 'task.md') {
|
|
155
|
+
const raw = buf.toString('utf8')
|
|
156
|
+
steps = parseMarkdownCheckboxes(raw)
|
|
157
|
+
}
|
|
158
|
+
} catch {}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const gitInfo = getGitInfo()
|
|
163
|
+
const runtimeInfo = getRuntimeInfo()
|
|
164
|
+
|
|
165
|
+
releaseMap.set(version, {
|
|
166
|
+
version,
|
|
167
|
+
status: 'ok',
|
|
168
|
+
command: 'seed',
|
|
169
|
+
durationMs: 0,
|
|
170
|
+
gitSha: gitInfo.gitSha,
|
|
171
|
+
gitDirty: gitInfo.gitDirty,
|
|
172
|
+
nodeVersion: runtimeInfo.nodeVersion,
|
|
173
|
+
pnpmVersion: runtimeInfo.pnpmVersion,
|
|
174
|
+
steps,
|
|
175
|
+
parity: [
|
|
176
|
+
{
|
|
177
|
+
collection: 'users',
|
|
178
|
+
csvRows: 1,
|
|
179
|
+
dbRows: 1,
|
|
180
|
+
synced: true,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
collection: 'releases',
|
|
184
|
+
csvRows: 1,
|
|
185
|
+
dbRows: 1,
|
|
186
|
+
synced: true,
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
issues: [
|
|
190
|
+
{
|
|
191
|
+
level: 'info',
|
|
192
|
+
code: 'AUTO_DISCOVERED',
|
|
193
|
+
source: '@nan0web/payload-release',
|
|
194
|
+
file: files.find((f) => f.path.endsWith('task.md'))?.path || '',
|
|
195
|
+
message: 'Release auto-discovered from filesystem.',
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
files,
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return Promise.all(searchDirs.map((dir) => walk(dir))).then(() =>
|
|
203
|
+
Array.from(releaseMap.values())
|
|
204
|
+
)
|
|
205
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { renderMarkdown } from './markdown.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates endpoint handler for serving release files securely.
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} options
|
|
8
|
+
* @param {string} [options.releasesDir='releases']
|
|
9
|
+
* @param {Function} [options.readReleaseFile]
|
|
10
|
+
* @returns {(req: any) => Promise<Response>}
|
|
11
|
+
*/
|
|
12
|
+
export function createReleaseFileEndpoint(options = {}) {
|
|
13
|
+
const { releasesDir = 'releases', readReleaseFile } = options
|
|
14
|
+
|
|
15
|
+
return async (req) => {
|
|
16
|
+
const url = new URL(req?.url || 'http://localhost', 'http://localhost')
|
|
17
|
+
const relPath = url.searchParams.get('path') || ''
|
|
18
|
+
|
|
19
|
+
// Safety check against directory traversal
|
|
20
|
+
if (!relPath || relPath.includes('..') || path.isAbsolute(relPath)) {
|
|
21
|
+
return Response.json(
|
|
22
|
+
{ error: 'Invalid file path', found: false },
|
|
23
|
+
{ status: 400 }
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
let content = null
|
|
29
|
+
if (typeof readReleaseFile === 'function') {
|
|
30
|
+
content = await readReleaseFile(relPath)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (content === null || content === undefined) {
|
|
34
|
+
return Response.json(
|
|
35
|
+
{ error: 'File not found', found: false },
|
|
36
|
+
{ status: 404 }
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const raw = String(content)
|
|
41
|
+
const html = renderMarkdown(raw)
|
|
42
|
+
|
|
43
|
+
return Response.json(
|
|
44
|
+
{
|
|
45
|
+
path: relPath,
|
|
46
|
+
raw,
|
|
47
|
+
html,
|
|
48
|
+
found: true,
|
|
49
|
+
},
|
|
50
|
+
{ status: 200 }
|
|
51
|
+
)
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return Response.json(
|
|
54
|
+
{ error: /** @type {Error} */ (err).message, found: false },
|
|
55
|
+
{ status: 500 }
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import MarkdownIt from 'markdown-it'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Escapes HTML characters.
|
|
5
|
+
* @param {any} value
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
function escapeHtml(value) {
|
|
9
|
+
return String(value ?? '').replace(
|
|
10
|
+
/[&<>"']/g,
|
|
11
|
+
(ch) =>
|
|
12
|
+
({
|
|
13
|
+
'&': '&',
|
|
14
|
+
'<': '<',
|
|
15
|
+
'>': '>',
|
|
16
|
+
'"': '"',
|
|
17
|
+
"'": ''',
|
|
18
|
+
})[ch]
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const md = new MarkdownIt({
|
|
23
|
+
html: false,
|
|
24
|
+
breaks: true,
|
|
25
|
+
linkify: true,
|
|
26
|
+
typographer: true,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
md.renderer.rules.fence = (tokens, idx) => {
|
|
30
|
+
const token = tokens[idx]
|
|
31
|
+
const lang = token.info.trim().split(/\s+/, 1)[0]
|
|
32
|
+
const className = lang ? ` class="language-${escapeHtml(lang)}"` : ''
|
|
33
|
+
return `<pre><code${className}>${escapeHtml(token.content)}</code></pre>\n`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Strips YAML frontmatter block (--- ... ---) from markdown text.
|
|
38
|
+
* @param {string} text
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
export function stripFrontmatter(text) {
|
|
42
|
+
const str = String(text || '')
|
|
43
|
+
if (str.startsWith('---')) {
|
|
44
|
+
const endIdx = str.indexOf('\n---', 3)
|
|
45
|
+
if (endIdx !== -1) {
|
|
46
|
+
const afterEnd = endIdx + 4
|
|
47
|
+
return str.slice(afterEnd).replace(/^\r?\n/, '')
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return str
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Renders Markdown string to safe HTML.
|
|
55
|
+
* @param {string} markdown
|
|
56
|
+
* @param {Object} [options]
|
|
57
|
+
* @param {boolean} [options.stripFrontmatter=true]
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
export function renderMarkdown(markdown, options = {}) {
|
|
61
|
+
const shouldStrip = options.stripFrontmatter !== false
|
|
62
|
+
const content = shouldStrip ? stripFrontmatter(markdown) : String(markdown || '')
|
|
63
|
+
return md.render(content)
|
|
64
|
+
}
|
package/types/admin.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Preview Button & Modal Viewer for Release Files (task.md, user.md, etc.)
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} props
|
|
5
|
+
* @param {any} [props.value]
|
|
6
|
+
* @param {any} [props.path]
|
|
7
|
+
* @returns {React.JSX.Element}
|
|
8
|
+
*/
|
|
9
|
+
export function FilePreviewButton(props: {
|
|
10
|
+
value?: any;
|
|
11
|
+
path?: any;
|
|
12
|
+
}): React.JSX.Element;
|
|
13
|
+
/**
|
|
14
|
+
* Compact clean list view for Release Steps with expandable detailed output
|
|
15
|
+
*/
|
|
16
|
+
export function ReleaseStepsField(props: any): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* Compact clean list view for Release Files
|
|
19
|
+
*/
|
|
20
|
+
export function ReleaseFilesField(props: any): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
/**
|
|
22
|
+
* Compact clean list view for Release Parity
|
|
23
|
+
*/
|
|
24
|
+
export function ReleaseParityField(props: any): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
/**
|
|
26
|
+
* Compact clean list view for Release Issues
|
|
27
|
+
*/
|
|
28
|
+
export function ReleaseIssuesField(props: any): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
/**
|
|
30
|
+
* Custom full Document View for Releases:
|
|
31
|
+
* - Syncs active tab with URL hash (#general, #steps, #parity, #issues, #files)
|
|
32
|
+
* - Directly supports deep-linking via URL hash for sharing with colleagues
|
|
33
|
+
* - Displays clean read-only summary for General tab without any form inputs
|
|
34
|
+
* - Embeds compact list views for Steps, Parity, Issues, and Files
|
|
35
|
+
*/
|
|
36
|
+
export function ReleaseDocumentView(props: any): import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
export default FilePreviewButton;
|
|
38
|
+
import React from 'react';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the Payload CMS Collection configuration for Releases.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} [options]
|
|
5
|
+
* @param {boolean} [options.allowPreview=true]
|
|
6
|
+
* @returns {import('payload').CollectionConfig}
|
|
7
|
+
*/
|
|
8
|
+
export function buildReleasesCollection(options?: {
|
|
9
|
+
allowPreview?: boolean | undefined;
|
|
10
|
+
}): import("payload").CollectionConfig;
|
|
11
|
+
export const ReleasesCollection: import("payload").CollectionConfig<any>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema for @nan0web/payload-release plugin.
|
|
3
|
+
* Implements Model-as-Schema pattern.
|
|
4
|
+
*/
|
|
5
|
+
export class ReleaseConfigModel extends Model {
|
|
6
|
+
/**
|
|
7
|
+
* Path to releases directory relative to project root.
|
|
8
|
+
*/
|
|
9
|
+
static releasesDir: {
|
|
10
|
+
type: string;
|
|
11
|
+
default: string;
|
|
12
|
+
help: string;
|
|
13
|
+
validate: (v: any) => true | "releasesDir must be a non-empty string";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Whether Markdown file preview is enabled in Admin UI.
|
|
17
|
+
*/
|
|
18
|
+
static allowPreview: {
|
|
19
|
+
type: string;
|
|
20
|
+
default: boolean;
|
|
21
|
+
help: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* UI labels and translation tokens.
|
|
25
|
+
*/
|
|
26
|
+
static UI: {
|
|
27
|
+
singular: string;
|
|
28
|
+
plural: string;
|
|
29
|
+
group: string;
|
|
30
|
+
tabGeneral: string;
|
|
31
|
+
tabSteps: string;
|
|
32
|
+
tabParity: string;
|
|
33
|
+
tabIssues: string;
|
|
34
|
+
tabFiles: string;
|
|
35
|
+
previewButton: string;
|
|
36
|
+
modalClose: string;
|
|
37
|
+
loading: string;
|
|
38
|
+
errorNotFound: string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* @param {Partial<ReleaseConfigModel>} [data]
|
|
42
|
+
* @param {import('@nan0web/types').ModelOptions} [options]
|
|
43
|
+
*/
|
|
44
|
+
constructor(data?: Partial<ReleaseConfigModel>, options?: import("@nan0web/types").ModelOptions);
|
|
45
|
+
/** @type {string} */ releasesDir: string;
|
|
46
|
+
/** @type {boolean} */ allowPreview: boolean;
|
|
47
|
+
}
|
|
48
|
+
import { Model } from '@nan0web/types';
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure payload-release plugin for Payload CMS.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} [options]
|
|
5
|
+
* @param {string} [options.releasesDir='releases']
|
|
6
|
+
* @param {boolean} [options.allowPreview=true]
|
|
7
|
+
* @param {boolean} [options.autoDiscover=true]
|
|
8
|
+
* @returns {(config: any) => any}
|
|
9
|
+
*/
|
|
10
|
+
export function payloadRelease(options?: {
|
|
11
|
+
releasesDir?: string | undefined;
|
|
12
|
+
allowPreview?: boolean | undefined;
|
|
13
|
+
autoDiscover?: boolean | undefined;
|
|
14
|
+
}): (config: any) => any;
|
|
15
|
+
export default payloadRelease;
|
|
16
|
+
import { ReleaseConfigModel } from './domain/ReleaseConfigModel.js';
|
|
17
|
+
import { ReleasesCollection } from './collections/Releases.js';
|
|
18
|
+
import { buildReleasesCollection } from './collections/Releases.js';
|
|
19
|
+
import { createReleaseFileEndpoint } from './server/endpoint.js';
|
|
20
|
+
import { renderMarkdown } from './server/markdown.js';
|
|
21
|
+
import { stripFrontmatter } from './server/markdown.js';
|
|
22
|
+
import { discoverReleases } from './server/discovery.js';
|
|
23
|
+
import { parseMarkdownCheckboxes } from './server/discovery.js';
|
|
24
|
+
import { getGitInfo } from './server/discovery.js';
|
|
25
|
+
import { getRuntimeInfo } from './server/discovery.js';
|
|
26
|
+
import { getPackageRootDir } from './server/discovery.js';
|
|
27
|
+
export { ReleaseConfigModel, ReleasesCollection, buildReleasesCollection, createReleaseFileEndpoint, renderMarkdown, stripFrontmatter, discoverReleases, parseMarkdownCheckboxes, getGitInfo, getRuntimeInfo, getPackageRootDir };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses markdown checkbox items into release steps.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} content
|
|
5
|
+
* @returns {Array<{ name: string, status: string, output: string }>}
|
|
6
|
+
*/
|
|
7
|
+
export function parseMarkdownCheckboxes(content: string): Array<{
|
|
8
|
+
name: string;
|
|
9
|
+
status: string;
|
|
10
|
+
output: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Safely inspects current git commit and dirty state.
|
|
14
|
+
*
|
|
15
|
+
* @returns {{ gitSha?: string, gitDirty?: boolean }}
|
|
16
|
+
*/
|
|
17
|
+
export function getGitInfo(): {
|
|
18
|
+
gitSha?: string;
|
|
19
|
+
gitDirty?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Safely determines pnpm and Node.js versions.
|
|
23
|
+
*
|
|
24
|
+
* @returns {{ nodeVersion: string, pnpmVersion?: string }}
|
|
25
|
+
*/
|
|
26
|
+
export function getRuntimeInfo(): {
|
|
27
|
+
nodeVersion: string;
|
|
28
|
+
pnpmVersion?: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Resolves default root directory of the @nan0web/payload-release package.
|
|
32
|
+
*
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
export function getPackageRootDir(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Discovers releases from filesystem search paths.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} [options]
|
|
40
|
+
* @param {string} [options.projectRoot]
|
|
41
|
+
* @param {string} [options.releasesDir='releases']
|
|
42
|
+
* @param {string[]} [options.searchDirs]
|
|
43
|
+
* @returns {Promise<Array<any>>}
|
|
44
|
+
*/
|
|
45
|
+
export function discoverReleases(options?: {
|
|
46
|
+
projectRoot?: string | undefined;
|
|
47
|
+
releasesDir?: string | undefined;
|
|
48
|
+
searchDirs?: string[] | undefined;
|
|
49
|
+
}): Promise<Array<any>>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates endpoint handler for serving release files securely.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} options
|
|
5
|
+
* @param {string} [options.releasesDir='releases']
|
|
6
|
+
* @param {Function} [options.readReleaseFile]
|
|
7
|
+
* @returns {(req: any) => Promise<Response>}
|
|
8
|
+
*/
|
|
9
|
+
export function createReleaseFileEndpoint(options?: {
|
|
10
|
+
releasesDir?: string | undefined;
|
|
11
|
+
readReleaseFile?: Function | undefined;
|
|
12
|
+
}): (req: any) => Promise<Response>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips YAML frontmatter block (--- ... ---) from markdown text.
|
|
3
|
+
* @param {string} text
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/
|
|
6
|
+
export function stripFrontmatter(text: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Renders Markdown string to safe HTML.
|
|
9
|
+
* @param {string} markdown
|
|
10
|
+
* @param {Object} [options]
|
|
11
|
+
* @param {boolean} [options.stripFrontmatter=true]
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
export function renderMarkdown(markdown: string, options?: {
|
|
15
|
+
stripFrontmatter?: boolean | undefined;
|
|
16
|
+
}): string;
|