@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,308 @@
|
|
|
1
|
+
import { ReleaseConfigModel } from '../domain/ReleaseConfigModel.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates dynamic tab label with count badge
|
|
5
|
+
* @param {string} baseLabel
|
|
6
|
+
* @param {string} arrayFieldName
|
|
7
|
+
* @returns {import('payload').LabelFunction}
|
|
8
|
+
*/
|
|
9
|
+
function createTabBadgeLabel(baseLabel, arrayFieldName) {
|
|
10
|
+
return ({ data }) => {
|
|
11
|
+
const arr = data?.[arrayFieldName]
|
|
12
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
13
|
+
return baseLabel.replace('{count}', String(arr.length))
|
|
14
|
+
}
|
|
15
|
+
// Return clean label without ' (0)' placeholder if no items or data not loaded yet
|
|
16
|
+
return baseLabel.replace(' ({count})', '').replace('{count}', '')
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builds the Payload CMS Collection configuration for Releases.
|
|
22
|
+
*
|
|
23
|
+
* @param {Object} [options]
|
|
24
|
+
* @param {boolean} [options.allowPreview=true]
|
|
25
|
+
* @returns {import('payload').CollectionConfig}
|
|
26
|
+
*/
|
|
27
|
+
export function buildReleasesCollection(options = {}) {
|
|
28
|
+
const allowPreview = options.allowPreview !== false
|
|
29
|
+
const fileEndpointHandler = options.fileEndpointHandler
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
slug: 'releases',
|
|
33
|
+
labels: {
|
|
34
|
+
singular: ReleaseConfigModel.UI.singular,
|
|
35
|
+
plural: ReleaseConfigModel.UI.plural,
|
|
36
|
+
},
|
|
37
|
+
endpoints: fileEndpointHandler
|
|
38
|
+
? [
|
|
39
|
+
{
|
|
40
|
+
path: '/:id/file',
|
|
41
|
+
method: 'get',
|
|
42
|
+
handler: fileEndpointHandler,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: '/view/file',
|
|
46
|
+
method: 'get',
|
|
47
|
+
handler: fileEndpointHandler,
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
: [],
|
|
51
|
+
access: {
|
|
52
|
+
read: () => true,
|
|
53
|
+
create: () => false,
|
|
54
|
+
update: () => false,
|
|
55
|
+
delete: () => false,
|
|
56
|
+
},
|
|
57
|
+
admin: {
|
|
58
|
+
useAsTitle: 'version',
|
|
59
|
+
defaultColumns: ['version', 'command', 'status', 'durationMs', 'createdAt'],
|
|
60
|
+
group: ReleaseConfigModel.UI.group,
|
|
61
|
+
hideAPIURL: false,
|
|
62
|
+
components: {
|
|
63
|
+
beforeList: [],
|
|
64
|
+
views: {
|
|
65
|
+
edit: {
|
|
66
|
+
default: {
|
|
67
|
+
Component: '@nan0web/payload-release/admin#ReleaseDocumentView',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
fields: [
|
|
74
|
+
{
|
|
75
|
+
type: 'tabs',
|
|
76
|
+
tabs: [
|
|
77
|
+
{
|
|
78
|
+
label: ReleaseConfigModel.UI.tabGeneral,
|
|
79
|
+
fields: [
|
|
80
|
+
{
|
|
81
|
+
type: 'row',
|
|
82
|
+
fields: [
|
|
83
|
+
{
|
|
84
|
+
name: 'version',
|
|
85
|
+
type: 'text',
|
|
86
|
+
required: true,
|
|
87
|
+
unique: true,
|
|
88
|
+
index: true,
|
|
89
|
+
admin: { width: '50%' },
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'status',
|
|
93
|
+
type: 'select',
|
|
94
|
+
defaultValue: 'ok',
|
|
95
|
+
options: [
|
|
96
|
+
{ label: 'OK', value: 'ok' },
|
|
97
|
+
{ label: 'Failed', value: 'failed' },
|
|
98
|
+
],
|
|
99
|
+
admin: { width: '50%' },
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: 'row',
|
|
105
|
+
fields: [
|
|
106
|
+
{
|
|
107
|
+
name: 'command',
|
|
108
|
+
type: 'select',
|
|
109
|
+
options: [
|
|
110
|
+
{ label: 'Seed', value: 'seed' },
|
|
111
|
+
{ label: 'Fresh', value: 'fresh' },
|
|
112
|
+
{ label: 'Migrate', value: 'migrate' },
|
|
113
|
+
],
|
|
114
|
+
admin: { width: '50%' },
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'durationMs',
|
|
118
|
+
type: 'number',
|
|
119
|
+
admin: { width: '50%' },
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
type: 'row',
|
|
125
|
+
fields: [
|
|
126
|
+
{
|
|
127
|
+
name: 'gitSha',
|
|
128
|
+
type: 'text',
|
|
129
|
+
admin: { width: '50%' },
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'gitDirty',
|
|
133
|
+
type: 'checkbox',
|
|
134
|
+
admin: { width: '50%' },
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'row',
|
|
140
|
+
fields: [
|
|
141
|
+
{
|
|
142
|
+
name: 'nodeVersion',
|
|
143
|
+
type: 'text',
|
|
144
|
+
admin: { width: '50%' },
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'pnpmVersion',
|
|
148
|
+
type: 'text',
|
|
149
|
+
admin: { width: '50%' },
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
label: createTabBadgeLabel(ReleaseConfigModel.UI.tabSteps, 'steps'),
|
|
157
|
+
fields: [
|
|
158
|
+
{
|
|
159
|
+
name: 'steps',
|
|
160
|
+
type: 'array',
|
|
161
|
+
admin: {
|
|
162
|
+
components: {
|
|
163
|
+
Field: '@nan0web/payload-release/admin#ReleaseStepsField',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
fields: [
|
|
167
|
+
{
|
|
168
|
+
type: 'row',
|
|
169
|
+
fields: [
|
|
170
|
+
{ name: 'name', type: 'text', required: true, admin: { width: '40%' } },
|
|
171
|
+
{
|
|
172
|
+
name: 'status',
|
|
173
|
+
type: 'select',
|
|
174
|
+
defaultValue: 'ok',
|
|
175
|
+
options: [
|
|
176
|
+
{ label: 'OK', value: 'ok' },
|
|
177
|
+
{ label: 'Error', value: 'error' },
|
|
178
|
+
],
|
|
179
|
+
admin: { width: '20%' },
|
|
180
|
+
},
|
|
181
|
+
{ name: 'startedAt', type: 'date', admin: { width: '20%' } },
|
|
182
|
+
{ name: 'durationMs', type: 'number', admin: { width: '20%' } },
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'output',
|
|
187
|
+
type: 'textarea',
|
|
188
|
+
admin: { rows: 3 },
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
label: createTabBadgeLabel(ReleaseConfigModel.UI.tabParity, 'parity'),
|
|
196
|
+
fields: [
|
|
197
|
+
{
|
|
198
|
+
name: 'parity',
|
|
199
|
+
type: 'array',
|
|
200
|
+
admin: {
|
|
201
|
+
components: {
|
|
202
|
+
Field: '@nan0web/payload-release/admin#ReleaseParityField',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
fields: [
|
|
206
|
+
{
|
|
207
|
+
type: 'row',
|
|
208
|
+
fields: [
|
|
209
|
+
{ name: 'collection', type: 'text', required: true, admin: { width: '30%' } },
|
|
210
|
+
{ name: 'csvRows', type: 'number', admin: { width: '25%' } },
|
|
211
|
+
{ name: 'dbRows', type: 'number', admin: { width: '25%' } },
|
|
212
|
+
{
|
|
213
|
+
name: 'synced',
|
|
214
|
+
type: 'checkbox',
|
|
215
|
+
defaultValue: true,
|
|
216
|
+
admin: { width: '20%' },
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
label: createTabBadgeLabel(ReleaseConfigModel.UI.tabIssues, 'issues'),
|
|
226
|
+
fields: [
|
|
227
|
+
{
|
|
228
|
+
name: 'issues',
|
|
229
|
+
type: 'array',
|
|
230
|
+
admin: {
|
|
231
|
+
components: {
|
|
232
|
+
Field: '@nan0web/payload-release/admin#ReleaseIssuesField',
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
fields: [
|
|
236
|
+
{
|
|
237
|
+
type: 'row',
|
|
238
|
+
fields: [
|
|
239
|
+
{
|
|
240
|
+
name: 'level',
|
|
241
|
+
type: 'select',
|
|
242
|
+
defaultValue: 'warning',
|
|
243
|
+
options: [
|
|
244
|
+
{ label: 'Info', value: 'info' },
|
|
245
|
+
{ label: 'Warning', value: 'warning' },
|
|
246
|
+
{ label: 'Error', value: 'error' },
|
|
247
|
+
],
|
|
248
|
+
admin: { width: '25%' },
|
|
249
|
+
},
|
|
250
|
+
{ name: 'code', type: 'text', admin: { width: '25%' } },
|
|
251
|
+
{ name: 'source', type: 'text', admin: { width: '25%' } },
|
|
252
|
+
{ name: 'file', type: 'text', admin: { width: '25%' } },
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: 'message',
|
|
257
|
+
type: 'textarea',
|
|
258
|
+
required: true,
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
label: createTabBadgeLabel(ReleaseConfigModel.UI.tabFiles, 'files'),
|
|
266
|
+
fields: [
|
|
267
|
+
{
|
|
268
|
+
name: 'files',
|
|
269
|
+
type: 'array',
|
|
270
|
+
admin: {
|
|
271
|
+
components: {
|
|
272
|
+
Field: '@nan0web/payload-release/admin#ReleaseFilesField',
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
fields: [
|
|
276
|
+
{
|
|
277
|
+
type: 'row',
|
|
278
|
+
fields: [
|
|
279
|
+
{ name: 'path', type: 'text', required: true, admin: { width: '40%' } },
|
|
280
|
+
{ name: 'size', type: 'number', admin: { width: '20%' } },
|
|
281
|
+
{ name: 'sha256', type: 'text', admin: { width: '30%' } },
|
|
282
|
+
...(allowPreview
|
|
283
|
+
? [
|
|
284
|
+
{
|
|
285
|
+
name: 'preview',
|
|
286
|
+
type: 'ui',
|
|
287
|
+
admin: {
|
|
288
|
+
width: '10%',
|
|
289
|
+
components: {
|
|
290
|
+
Field: '@nan0web/payload-release/admin#FilePreviewButton',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
]
|
|
295
|
+
: []),
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export const ReleasesCollection = buildReleasesCollection()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Model } from '@nan0web/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration schema for @nan0web/payload-release plugin.
|
|
5
|
+
* Implements Model-as-Schema pattern.
|
|
6
|
+
*/
|
|
7
|
+
export class ReleaseConfigModel extends Model {
|
|
8
|
+
/**
|
|
9
|
+
* Path to releases directory relative to project root.
|
|
10
|
+
*/
|
|
11
|
+
static releasesDir = {
|
|
12
|
+
type: 'string',
|
|
13
|
+
default: 'releases',
|
|
14
|
+
help: 'Path to releases directory relative to project root',
|
|
15
|
+
validate: (v) =>
|
|
16
|
+
(typeof v === 'string' && v.trim().length > 0) ||
|
|
17
|
+
'releasesDir must be a non-empty string',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether Markdown file preview is enabled in Admin UI.
|
|
22
|
+
*/
|
|
23
|
+
static allowPreview = {
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
default: true,
|
|
26
|
+
help: 'Allow modal preview of release Markdown documents',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* UI labels and translation tokens.
|
|
31
|
+
*/
|
|
32
|
+
static UI = {
|
|
33
|
+
singular: 'Release',
|
|
34
|
+
plural: 'Releases',
|
|
35
|
+
group: 'System',
|
|
36
|
+
tabGeneral: '📋 General',
|
|
37
|
+
tabSteps: '⚡ Steps ({count})',
|
|
38
|
+
tabParity: '⚖️ Parity ({count})',
|
|
39
|
+
tabIssues: '⚠️ Issues ({count})',
|
|
40
|
+
tabFiles: '📦 Files ({count})',
|
|
41
|
+
previewButton: 'Preview',
|
|
42
|
+
modalClose: 'Close',
|
|
43
|
+
loading: 'Loading content...',
|
|
44
|
+
errorNotFound: 'Document not found or inaccessible',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {Partial<ReleaseConfigModel>} [data]
|
|
49
|
+
* @param {import('@nan0web/types').ModelOptions} [options]
|
|
50
|
+
*/
|
|
51
|
+
constructor(data = {}, options = {}) {
|
|
52
|
+
super(data, options)
|
|
53
|
+
/** @type {string} */ this.releasesDir
|
|
54
|
+
/** @type {boolean} */ this.allowPreview
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { ReleaseConfigModel } from './domain/ReleaseConfigModel.js'
|
|
4
|
+
import { buildReleasesCollection, ReleasesCollection } from './collections/Releases.js'
|
|
5
|
+
import { createReleaseFileEndpoint } from './server/endpoint.js'
|
|
6
|
+
import { renderMarkdown, stripFrontmatter } from './server/markdown.js'
|
|
7
|
+
import {
|
|
8
|
+
discoverReleases,
|
|
9
|
+
parseMarkdownCheckboxes,
|
|
10
|
+
getGitInfo,
|
|
11
|
+
getRuntimeInfo,
|
|
12
|
+
getPackageRootDir,
|
|
13
|
+
} from './server/discovery.js'
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
ReleaseConfigModel,
|
|
17
|
+
ReleasesCollection,
|
|
18
|
+
buildReleasesCollection,
|
|
19
|
+
createReleaseFileEndpoint,
|
|
20
|
+
renderMarkdown,
|
|
21
|
+
stripFrontmatter,
|
|
22
|
+
discoverReleases,
|
|
23
|
+
parseMarkdownCheckboxes,
|
|
24
|
+
getGitInfo,
|
|
25
|
+
getRuntimeInfo,
|
|
26
|
+
getPackageRootDir,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configure payload-release plugin for Payload CMS.
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} [options]
|
|
33
|
+
* @param {string} [options.releasesDir='releases']
|
|
34
|
+
* @param {boolean} [options.allowPreview=true]
|
|
35
|
+
* @param {boolean} [options.autoDiscover=true]
|
|
36
|
+
* @returns {(config: any) => any}
|
|
37
|
+
*/
|
|
38
|
+
export function payloadRelease(options = {}) {
|
|
39
|
+
const model = new ReleaseConfigModel(options)
|
|
40
|
+
const { releasesDir, allowPreview } = model
|
|
41
|
+
const autoDiscover = options.autoDiscover !== false
|
|
42
|
+
|
|
43
|
+
return (config) => {
|
|
44
|
+
const packageRoot = getPackageRootDir()
|
|
45
|
+
|
|
46
|
+
// Safe DB-FS or filesystem file reader
|
|
47
|
+
const readReleaseFile = async (relPath) => {
|
|
48
|
+
const projectRoot = process.cwd()
|
|
49
|
+
const cleanPath = String(relPath || '')
|
|
50
|
+
.replace(/^@releases\/?/, '')
|
|
51
|
+
.replace(/^\.\//, '')
|
|
52
|
+
|
|
53
|
+
const candidatePaths = [
|
|
54
|
+
path.resolve(projectRoot, cleanPath),
|
|
55
|
+
path.resolve(projectRoot, releasesDir, cleanPath),
|
|
56
|
+
path.resolve(packageRoot, cleanPath),
|
|
57
|
+
path.resolve(packageRoot, 'releases', cleanPath),
|
|
58
|
+
path.resolve(packageRoot, cleanPath.replace(/^releases\/?/, 'releases/')),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
const allowedBases = [
|
|
62
|
+
path.resolve(projectRoot),
|
|
63
|
+
path.resolve(packageRoot),
|
|
64
|
+
path.resolve(packageRoot, 'releases'),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
for (const fullPath of candidatePaths) {
|
|
68
|
+
const isUnderAllowedBase = allowedBases.some(
|
|
69
|
+
(base) => fullPath === base || fullPath.startsWith(`${base}${path.sep}`)
|
|
70
|
+
)
|
|
71
|
+
if (!isUnderAllowedBase) continue
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
return await fs.readFile(fullPath, 'utf8')
|
|
75
|
+
} catch {}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const fileEndpointHandler = createReleaseFileEndpoint({
|
|
82
|
+
releasesDir,
|
|
83
|
+
readReleaseFile,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const collection = buildReleasesCollection({ allowPreview, fileEndpointHandler })
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
const endpoints = [
|
|
90
|
+
...(Array.isArray(config.endpoints) ? config.endpoints : []),
|
|
91
|
+
{
|
|
92
|
+
path: '/releases/:id/file',
|
|
93
|
+
method: 'get',
|
|
94
|
+
handler: fileEndpointHandler,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
path: '/releases/view/file',
|
|
98
|
+
method: 'get',
|
|
99
|
+
handler: fileEndpointHandler,
|
|
100
|
+
},
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
const existingOnInit = config.onInit
|
|
104
|
+
const wrappedOnInit = async (payload) => {
|
|
105
|
+
if (existingOnInit) {
|
|
106
|
+
await existingOnInit(payload)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (autoDiscover && payload?.find && payload?.create) {
|
|
110
|
+
try {
|
|
111
|
+
const discovered = await discoverReleases({
|
|
112
|
+
projectRoot: process.cwd(),
|
|
113
|
+
releasesDir,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
for (const release of discovered) {
|
|
117
|
+
const existing = await payload.find({
|
|
118
|
+
collection: 'releases',
|
|
119
|
+
where: {
|
|
120
|
+
version: { equals: release.version },
|
|
121
|
+
},
|
|
122
|
+
limit: 1,
|
|
123
|
+
depth: 0,
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (!existing?.docs?.length) {
|
|
127
|
+
await payload.create({
|
|
128
|
+
collection: 'releases',
|
|
129
|
+
data: release,
|
|
130
|
+
})
|
|
131
|
+
} else {
|
|
132
|
+
const doc = existing.docs[0]
|
|
133
|
+
// If DB record lacks parity data or has fewer steps, sync it
|
|
134
|
+
if (!Array.isArray(doc.parity) || doc.parity.length === 0) {
|
|
135
|
+
await payload.update({
|
|
136
|
+
collection: 'releases',
|
|
137
|
+
id: doc.id,
|
|
138
|
+
data: {
|
|
139
|
+
parity: release.parity,
|
|
140
|
+
steps: release.steps,
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
payload?.logger?.warn?.(
|
|
148
|
+
`[payload-release] Auto-discovery note: ${/** @type {Error} */ (err).message}`
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
...config,
|
|
156
|
+
collections: [
|
|
157
|
+
...(Array.isArray(config.collections) ? config.collections : []),
|
|
158
|
+
collection,
|
|
159
|
+
],
|
|
160
|
+
endpoints,
|
|
161
|
+
onInit: wrappedOnInit,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export default payloadRelease
|