underpost 3.1.1 → 3.1.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.
- package/.github/workflows/npmpkg.ci.yml +15 -1
- package/CHANGELOG.md +52 -1
- package/CLI-HELP.md +8 -2
- package/README.md +6 -3
- package/bin/deploy.js +29 -138
- package/conf.js +1 -8
- package/jsdoc.json +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +2 -2
- package/scripts/ports-ls.sh +2 -0
- package/src/cli/index.js +8 -0
- package/src/cli/repository.js +38 -49
- package/src/cli/run.js +99 -88
- package/src/client/components/core/Alert.js +2 -2
- package/src/client/components/core/Docs.js +9 -2
- package/src/client/components/core/RichText.js +1 -2
- package/src/client/ssr/body/404.js +15 -11
- package/src/client/ssr/body/500.js +15 -11
- package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
- package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
- package/src/client/ssr/pages/Test.js +11 -10
- package/src/index.js +1 -1
- package/src/runtime/express/Express.js +8 -8
- package/src/server/auth.js +6 -5
- package/src/server/client-build-docs.js +44 -110
- package/src/server/client-build.js +29 -38
- package/src/server/conf.js +19 -15
- package/src/server/start.js +15 -8
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
const swaggerDarkCss = css`
|
|
2
|
+
/* ── Toggle button ── */
|
|
3
|
+
.swagger-ui .topbar-wrapper {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
}
|
|
7
|
+
.swagger-theme-toggle-btn {
|
|
8
|
+
background: rgba(255, 255, 255, 0.12);
|
|
9
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
10
|
+
color: #fff;
|
|
11
|
+
padding: 5px 14px;
|
|
12
|
+
border-radius: 4px;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
font-size: 13px;
|
|
15
|
+
margin-left: auto;
|
|
16
|
+
margin-right: 16px;
|
|
17
|
+
white-space: nowrap;
|
|
18
|
+
transition: background 0.2s ease;
|
|
19
|
+
}
|
|
20
|
+
.swagger-theme-toggle-btn:hover {
|
|
21
|
+
background: rgba(255, 255, 255, 0.25);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ── Dark mode overrides — black/gray gradients ── */
|
|
25
|
+
body.swagger-dark {
|
|
26
|
+
background: #0f0f0f;
|
|
27
|
+
}
|
|
28
|
+
body.swagger-dark .swagger-ui {
|
|
29
|
+
background: #0f0f0f;
|
|
30
|
+
}
|
|
31
|
+
body.swagger-dark .swagger-ui .wrapper {
|
|
32
|
+
background: #0f0f0f;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Info block */
|
|
36
|
+
body.swagger-dark .swagger-ui .info .title,
|
|
37
|
+
body.swagger-dark .swagger-ui .info h1,
|
|
38
|
+
body.swagger-dark .swagger-ui .info h2,
|
|
39
|
+
body.swagger-dark .swagger-ui .info h3,
|
|
40
|
+
body.swagger-dark .swagger-ui .info p,
|
|
41
|
+
body.swagger-dark .swagger-ui .info li,
|
|
42
|
+
body.swagger-dark .swagger-ui .info a {
|
|
43
|
+
color: #e8e8e8;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Scheme / server selector bar */
|
|
47
|
+
body.swagger-dark .swagger-ui .scheme-container {
|
|
48
|
+
background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
|
|
49
|
+
box-shadow: 0 1px 0 #383838;
|
|
50
|
+
}
|
|
51
|
+
body.swagger-dark .swagger-ui select {
|
|
52
|
+
background: #2a2a2a;
|
|
53
|
+
color: #e8e8e8;
|
|
54
|
+
border-color: #383838;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Operation tags */
|
|
58
|
+
body.swagger-dark .swagger-ui .opblock-tag {
|
|
59
|
+
color: #e8e8e8;
|
|
60
|
+
border-color: #383838;
|
|
61
|
+
}
|
|
62
|
+
body.swagger-dark .swagger-ui .opblock-tag small {
|
|
63
|
+
color: #a8a8a8;
|
|
64
|
+
}
|
|
65
|
+
body.swagger-dark .swagger-ui .opblock-tag:hover {
|
|
66
|
+
background: rgba(255, 255, 255, 0.04);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Operation blocks */
|
|
70
|
+
body.swagger-dark .swagger-ui .opblock {
|
|
71
|
+
background: linear-gradient(180deg, #161616 0%, #1e1e1e 100%);
|
|
72
|
+
border-color: #383838;
|
|
73
|
+
}
|
|
74
|
+
body.swagger-dark .swagger-ui .opblock .opblock-summary {
|
|
75
|
+
border-color: #383838;
|
|
76
|
+
}
|
|
77
|
+
body.swagger-dark .swagger-ui .opblock .opblock-summary-description,
|
|
78
|
+
body.swagger-dark .swagger-ui .opblock .opblock-summary-operation-id {
|
|
79
|
+
color: #a8a8a8;
|
|
80
|
+
}
|
|
81
|
+
body.swagger-dark .swagger-ui .opblock-body {
|
|
82
|
+
background: #0f0f0f;
|
|
83
|
+
}
|
|
84
|
+
body.swagger-dark .swagger-ui .opblock-description-wrapper p,
|
|
85
|
+
body.swagger-dark .swagger-ui .opblock-external-docs-wrapper p {
|
|
86
|
+
color: #a8a8a8;
|
|
87
|
+
}
|
|
88
|
+
body.swagger-dark .swagger-ui .opblock-section-header {
|
|
89
|
+
background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
|
|
90
|
+
border-color: #383838;
|
|
91
|
+
}
|
|
92
|
+
body.swagger-dark .swagger-ui .opblock-section-header h4 {
|
|
93
|
+
color: #e8e8e8;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Models section */
|
|
97
|
+
body.swagger-dark .swagger-ui section.models {
|
|
98
|
+
background: linear-gradient(180deg, #161616 0%, #1e1e1e 100%);
|
|
99
|
+
border-color: #383838;
|
|
100
|
+
}
|
|
101
|
+
body.swagger-dark .swagger-ui section.models h4,
|
|
102
|
+
body.swagger-dark .swagger-ui section.models h5 {
|
|
103
|
+
color: #e8e8e8;
|
|
104
|
+
border-color: #383838;
|
|
105
|
+
}
|
|
106
|
+
body.swagger-dark .swagger-ui .model-container {
|
|
107
|
+
background: #0f0f0f;
|
|
108
|
+
border-color: #2a2a2a;
|
|
109
|
+
}
|
|
110
|
+
body.swagger-dark .swagger-ui .model-box {
|
|
111
|
+
background: #1a1a1a;
|
|
112
|
+
}
|
|
113
|
+
body.swagger-dark .swagger-ui .model,
|
|
114
|
+
body.swagger-dark .swagger-ui .model-title,
|
|
115
|
+
body.swagger-dark .swagger-ui .model span,
|
|
116
|
+
body.swagger-dark .swagger-ui .model .property {
|
|
117
|
+
color: #e8e8e8;
|
|
118
|
+
}
|
|
119
|
+
body.swagger-dark .swagger-ui .model .property.primitive {
|
|
120
|
+
color: #a8a8a8;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Tables */
|
|
124
|
+
body.swagger-dark .swagger-ui table thead tr th {
|
|
125
|
+
background: linear-gradient(180deg, #222222 0%, #2a2a2a 100%);
|
|
126
|
+
color: #e8e8e8;
|
|
127
|
+
border-color: #383838;
|
|
128
|
+
}
|
|
129
|
+
body.swagger-dark .swagger-ui table tbody tr td {
|
|
130
|
+
color: #a8a8a8;
|
|
131
|
+
border-color: #2a2a2a;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Parameters */
|
|
135
|
+
body.swagger-dark .swagger-ui .parameter__name,
|
|
136
|
+
body.swagger-dark .swagger-ui .parameter__type,
|
|
137
|
+
body.swagger-dark .swagger-ui .parameter__in,
|
|
138
|
+
body.swagger-dark .swagger-ui .parameter__extension,
|
|
139
|
+
body.swagger-dark .swagger-ui .parameter__empty_value_toggle {
|
|
140
|
+
color: #a8a8a8;
|
|
141
|
+
}
|
|
142
|
+
body.swagger-dark .swagger-ui .parameter__name.required:after {
|
|
143
|
+
color: #ff6b6b;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Inputs & textareas */
|
|
147
|
+
body.swagger-dark .swagger-ui input[type='text'],
|
|
148
|
+
body.swagger-dark .swagger-ui input[type='email'],
|
|
149
|
+
body.swagger-dark .swagger-ui input[type='password'],
|
|
150
|
+
body.swagger-dark .swagger-ui input[type='search'],
|
|
151
|
+
body.swagger-dark .swagger-ui input[type='number'],
|
|
152
|
+
body.swagger-dark .swagger-ui textarea {
|
|
153
|
+
background: #2a2a2a;
|
|
154
|
+
color: #e8e8e8;
|
|
155
|
+
border-color: #383838;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Buttons */
|
|
159
|
+
body.swagger-dark .swagger-ui .btn {
|
|
160
|
+
color: #e8e8e8;
|
|
161
|
+
border-color: #383838;
|
|
162
|
+
background: #222222;
|
|
163
|
+
}
|
|
164
|
+
body.swagger-dark .swagger-ui .btn:hover {
|
|
165
|
+
background: #2a2a2a;
|
|
166
|
+
}
|
|
167
|
+
body.swagger-dark .swagger-ui .btn.authorize {
|
|
168
|
+
color: #49cc90;
|
|
169
|
+
border-color: #49cc90;
|
|
170
|
+
background: transparent;
|
|
171
|
+
}
|
|
172
|
+
body.swagger-dark .swagger-ui .btn.execute {
|
|
173
|
+
background: linear-gradient(180deg, #333333 0%, #262626 100%);
|
|
174
|
+
border-color: #484848;
|
|
175
|
+
color: #e8e8e8;
|
|
176
|
+
}
|
|
177
|
+
body.swagger-dark .swagger-ui .btn.cancel {
|
|
178
|
+
color: #ff6b6b;
|
|
179
|
+
border-color: #ff6b6b;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Responses */
|
|
183
|
+
body.swagger-dark .swagger-ui .responses-inner h4,
|
|
184
|
+
body.swagger-dark .swagger-ui .responses-inner h5 {
|
|
185
|
+
color: #a8a8a8;
|
|
186
|
+
}
|
|
187
|
+
body.swagger-dark .swagger-ui .response-col_status {
|
|
188
|
+
color: #e8e8e8;
|
|
189
|
+
}
|
|
190
|
+
body.swagger-dark .swagger-ui .response-col_description__inner p {
|
|
191
|
+
color: #a8a8a8;
|
|
192
|
+
}
|
|
193
|
+
body.swagger-dark .swagger-ui .response {
|
|
194
|
+
border-color: #2a2a2a;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Code / highlight */
|
|
198
|
+
body.swagger-dark .swagger-ui .highlight-code,
|
|
199
|
+
body.swagger-dark .swagger-ui .microlight {
|
|
200
|
+
background: #1a1a1a;
|
|
201
|
+
border: 1px solid #2a2a2a;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Markdown */
|
|
205
|
+
body.swagger-dark .swagger-ui .markdown p,
|
|
206
|
+
body.swagger-dark .swagger-ui .markdown li,
|
|
207
|
+
body.swagger-dark .swagger-ui .markdown a {
|
|
208
|
+
color: #a8a8a8;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Tabs */
|
|
212
|
+
body.swagger-dark .swagger-ui .tab li {
|
|
213
|
+
color: #a8a8a8;
|
|
214
|
+
}
|
|
215
|
+
body.swagger-dark .swagger-ui .tab li.active {
|
|
216
|
+
color: #e8e8e8;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Auth / modal dialog */
|
|
220
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux {
|
|
221
|
+
background: linear-gradient(180deg, #1a1a1a 0%, #222222 100%);
|
|
222
|
+
border: 1px solid #383838;
|
|
223
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.7);
|
|
224
|
+
}
|
|
225
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-header {
|
|
226
|
+
border-bottom: 1px solid #383838;
|
|
227
|
+
background: linear-gradient(180deg, #222222 0%, #1a1a1a 100%);
|
|
228
|
+
}
|
|
229
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-header h3 {
|
|
230
|
+
color: #e8e8e8;
|
|
231
|
+
}
|
|
232
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content p,
|
|
233
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content label,
|
|
234
|
+
body.swagger-dark .swagger-ui .dialog-ux .modal-ux-content h4 {
|
|
235
|
+
color: #e8e8e8;
|
|
236
|
+
}
|
|
237
|
+
body.swagger-dark .swagger-ui .auth-container .errors {
|
|
238
|
+
color: #ff6b6b;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* Info / description */
|
|
242
|
+
body.swagger-dark .swagger-ui .info .base-url,
|
|
243
|
+
body.swagger-dark .swagger-ui .servers-title,
|
|
244
|
+
body.swagger-dark .swagger-ui .servers > label {
|
|
245
|
+
color: #a8a8a8;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Expand/collapse arrows */
|
|
249
|
+
body.swagger-dark .swagger-ui svg.arrow path {
|
|
250
|
+
fill: #a8a8a8;
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
|
|
254
|
+
const swaggerDarkJs = `(function () {
|
|
255
|
+
function injectThemeToggle() {
|
|
256
|
+
var topbarWrapper = document.querySelector('.swagger-ui .topbar-wrapper');
|
|
257
|
+
if (!topbarWrapper || document.getElementById('swagger-theme-toggle')) return;
|
|
258
|
+
|
|
259
|
+
var savedTheme = localStorage.getItem('swagger-theme') || 'light';
|
|
260
|
+
if (savedTheme === 'dark') document.body.classList.add('swagger-dark');
|
|
261
|
+
|
|
262
|
+
var btn = document.createElement('button');
|
|
263
|
+
btn.id = 'swagger-theme-toggle';
|
|
264
|
+
btn.className = 'swagger-theme-toggle-btn';
|
|
265
|
+
btn.setAttribute('title', 'Toggle dark / light mode');
|
|
266
|
+
btn.textContent = savedTheme === 'dark' ? '\u2600\uFE0F Light Mode' : '\uD83C\uDF19 Dark Mode';
|
|
267
|
+
|
|
268
|
+
btn.addEventListener('click', function () {
|
|
269
|
+
var isDark = document.body.classList.toggle('swagger-dark');
|
|
270
|
+
localStorage.setItem('swagger-theme', isDark ? 'dark' : 'light');
|
|
271
|
+
btn.textContent = isDark ? '\u2600\uFE0F Light Mode' : '\uD83C\uDF19 Dark Mode';
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
topbarWrapper.appendChild(btn);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
var poll = setInterval(function () {
|
|
278
|
+
if (document.querySelector('.swagger-ui .topbar-wrapper')) {
|
|
279
|
+
injectThemeToggle();
|
|
280
|
+
clearInterval(poll);
|
|
281
|
+
}
|
|
282
|
+
}, 100);
|
|
283
|
+
})();`;
|
|
284
|
+
|
|
285
|
+
SrrComponent = () => ({ css: swaggerDarkCss, js: swaggerDarkJs });
|
|
@@ -51,17 +51,18 @@ const main = () => {
|
|
|
51
51
|
<br />
|
|
52
52
|
<br />${Translate.Render('no-internet-connection')} <br />
|
|
53
53
|
<br />
|
|
54
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
54
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
55
55
|
</div>`,
|
|
56
56
|
);
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
SrrComponent = () =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
SrrComponent = () =>
|
|
60
|
+
html`<script>
|
|
61
|
+
{
|
|
62
|
+
const s = ${s};
|
|
63
|
+
const append = ${append};
|
|
64
|
+
const getLang = ${getLang};
|
|
65
|
+
const main = ${main};
|
|
66
|
+
window.onload = main;
|
|
67
|
+
}
|
|
68
|
+
</script>`;
|
|
@@ -182,17 +182,18 @@ const main = () => {
|
|
|
182
182
|
<span class="bold">Test Page</span>
|
|
183
183
|
<br />
|
|
184
184
|
<br />
|
|
185
|
-
<a href="${location.origin}">${Translate.Render('back')}</a>
|
|
185
|
+
<a target="_top" href="${location.origin}">${Translate.Render('back')}</a>
|
|
186
186
|
</div>`,
|
|
187
187
|
);
|
|
188
188
|
};
|
|
189
189
|
|
|
190
|
-
SrrComponent = () =>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
190
|
+
SrrComponent = () =>
|
|
191
|
+
html`<script>
|
|
192
|
+
{
|
|
193
|
+
const s = ${s};
|
|
194
|
+
const append = ${append};
|
|
195
|
+
const getLang = ${getLang};
|
|
196
|
+
const main = ${main};
|
|
197
|
+
window.onload = main;
|
|
198
|
+
}
|
|
199
|
+
</script>`;
|
package/src/index.js
CHANGED
|
@@ -165,14 +165,7 @@ class ExpressService {
|
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
//
|
|
169
|
-
if (fs.existsSync(swaggerJsonPath)) {
|
|
170
|
-
const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
|
|
171
|
-
const swaggerUiOptions = await buildSwaggerUiOptions();
|
|
172
|
-
app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDoc, swaggerUiOptions));
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Security and CORS
|
|
168
|
+
// Security and CORS — must run before swagger-ui so CSP headers are included in swagger responses
|
|
176
169
|
if (process.env.NODE_ENV === 'development' && useLocalSsl)
|
|
177
170
|
origins = origins.map((origin) => origin.replace('http', 'https'));
|
|
178
171
|
|
|
@@ -180,6 +173,13 @@ class ExpressService {
|
|
|
180
173
|
origin: origins,
|
|
181
174
|
});
|
|
182
175
|
|
|
176
|
+
// Swagger UI setup (after security middleware so responses carry correct CSP headers)
|
|
177
|
+
if (fs.existsSync(swaggerJsonPath)) {
|
|
178
|
+
const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
|
|
179
|
+
const swaggerUiOptions = await buildSwaggerUiOptions();
|
|
180
|
+
app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDoc, swaggerUiOptions));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
183
|
// Database and Valkey connections
|
|
184
184
|
if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
|
|
185
185
|
|
package/src/server/auth.js
CHANGED
|
@@ -613,13 +613,13 @@ function applySecurity(app, opts = {}) {
|
|
|
613
613
|
frameAncestors: frameAncestors,
|
|
614
614
|
imgSrc: ["'self'", 'data:', httpDirective, 'https:', 'blob:'],
|
|
615
615
|
objectSrc: ["'none'"],
|
|
616
|
-
// script-src and script-src-elem
|
|
617
|
-
|
|
616
|
+
// script-src and script-src-elem: use 'unsafe-inline' for swagger (no nonce, otherwise
|
|
617
|
+
// the nonce causes 'unsafe-inline' to be ignored per CSP3 spec), nonce for everything else.
|
|
618
|
+
scriptSrc: ["'self'", (req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : `'nonce-${res.locals.nonce}'`)],
|
|
619
|
+
scriptSrcElem: [
|
|
618
620
|
"'self'",
|
|
619
|
-
(req, res) => `'nonce-${res.locals.nonce}'
|
|
620
|
-
(req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : ''),
|
|
621
|
+
(req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : `'nonce-${res.locals.nonce}'`),
|
|
621
622
|
],
|
|
622
|
-
scriptSrcElem: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
|
|
623
623
|
// style-src: avoid 'unsafe-inline' when possible; if you must inline styles,
|
|
624
624
|
// use a nonce for them too (or hash).
|
|
625
625
|
styleSrc: [
|
|
@@ -627,6 +627,7 @@ function applySecurity(app, opts = {}) {
|
|
|
627
627
|
httpDirective,
|
|
628
628
|
(req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : `'nonce-${res.locals.nonce}'`),
|
|
629
629
|
],
|
|
630
|
+
styleSrcAttr: [(req, res) => (res.locals.isSwagger ? "'unsafe-inline'" : "'none'")],
|
|
630
631
|
// deny plugins
|
|
631
632
|
objectSrc: ["'none'"],
|
|
632
633
|
},
|
|
@@ -332,56 +332,17 @@ const buildApiDocs = async ({
|
|
|
332
332
|
* @param {string} options.path - The base path for the documentation
|
|
333
333
|
* @param {Object} options.metadata - Metadata for the documentation
|
|
334
334
|
* @param {string} options.publicClientId - Client ID used to resolve the tutorials/references directory
|
|
335
|
+
* @param {Object} options.docs - Documentation config from server conf
|
|
336
|
+
* @param {string} options.docs.jsJsonPath - Path to the JSDoc JSON config file
|
|
335
337
|
*/
|
|
336
|
-
const buildJsDocs = async ({ host, path, metadata = {}, publicClientId }) => {
|
|
338
|
+
const buildJsDocs = async ({ host, path, metadata = {}, publicClientId, docs }) => {
|
|
337
339
|
const logger = loggerFactory(import.meta);
|
|
338
340
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
for (const deployId of fs.readdirSync(privateConfBase)) {
|
|
344
|
-
const candidatePath = `./jsdoc.${deployId}.json`;
|
|
345
|
-
if (!fs.existsSync(candidatePath)) continue;
|
|
346
|
-
|
|
347
|
-
// Check if this deployId's server config contains the current host
|
|
348
|
-
const serverConfPath = `${privateConfBase}/${deployId}/conf.server.json`;
|
|
349
|
-
if (fs.existsSync(serverConfPath)) {
|
|
350
|
-
try {
|
|
351
|
-
const serverConf = JSON.parse(fs.readFileSync(serverConfPath, 'utf8'));
|
|
352
|
-
if (serverConf[host]) {
|
|
353
|
-
customJsDocPath = candidatePath;
|
|
354
|
-
logger.info('detected custom jsdoc config', { deployId, host, path: candidatePath });
|
|
355
|
-
break;
|
|
356
|
-
}
|
|
357
|
-
} catch (e) {
|
|
358
|
-
// skip invalid JSON
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Fallback: also check dev server configs
|
|
363
|
-
if (!customJsDocPath) {
|
|
364
|
-
const devConfFiles = fs
|
|
365
|
-
.readdirSync(`${privateConfBase}/${deployId}`)
|
|
366
|
-
.filter((f) => f.match(/^conf\.server\.dev\..*\.json$/));
|
|
367
|
-
for (const devFile of devConfFiles) {
|
|
368
|
-
try {
|
|
369
|
-
const devConf = JSON.parse(fs.readFileSync(`${privateConfBase}/${deployId}/${devFile}`, 'utf8'));
|
|
370
|
-
if (devConf[host]) {
|
|
371
|
-
customJsDocPath = candidatePath;
|
|
372
|
-
logger.info('detected custom jsdoc config (dev)', { deployId, host, path: candidatePath });
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
} catch (e) {
|
|
376
|
-
// skip invalid JSON
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
if (customJsDocPath) break;
|
|
381
|
-
}
|
|
341
|
+
const jsDocSourcePath = docs.jsJsonPath;
|
|
342
|
+
if (!fs.existsSync(jsDocSourcePath)) {
|
|
343
|
+
logger.warn('jsdoc config not found, skipping', jsDocSourcePath);
|
|
344
|
+
return;
|
|
382
345
|
}
|
|
383
|
-
|
|
384
|
-
const jsDocSourcePath = customJsDocPath || `./jsdoc.json`;
|
|
385
346
|
const jsDocsConfig = JSON.parse(fs.readFileSync(jsDocSourcePath, 'utf8'));
|
|
386
347
|
logger.info('using jsdoc config', jsDocSourcePath);
|
|
387
348
|
|
|
@@ -391,22 +352,14 @@ const buildJsDocs = async ({ host, path, metadata = {}, publicClientId }) => {
|
|
|
391
352
|
|
|
392
353
|
const tutorialsPath = `./src/client/public/${publicClientId}/docs/references`;
|
|
393
354
|
|
|
394
|
-
|
|
395
|
-
const includesHardhat =
|
|
396
|
-
jsDocsConfig.source &&
|
|
397
|
-
Array.isArray(jsDocsConfig.source.include) &&
|
|
398
|
-
jsDocsConfig.source.include.some((p) => p.includes('hardhat/'));
|
|
399
|
-
if (includesHardhat && fs.existsSync(`./hardhat`)) {
|
|
355
|
+
if (Array.isArray(docs.references) && docs.references.length > 0) {
|
|
400
356
|
fs.mkdirSync(tutorialsPath, { recursive: true });
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (fs.existsSync(hardhatWhitePaperPath)) {
|
|
408
|
-
fs.copySync(hardhatWhitePaperPath, `${tutorialsPath}/White Paper.md`);
|
|
409
|
-
logger.info('copied hardhat WHITE-PAPER.md to tutorials references');
|
|
357
|
+
for (const refPath of docs.references) {
|
|
358
|
+
if (fs.existsSync(refPath)) {
|
|
359
|
+
const fileName = refPath.split('/').pop();
|
|
360
|
+
fs.copySync(refPath, `${tutorialsPath}/${fileName}`);
|
|
361
|
+
logger.info('copied reference to tutorials', refPath);
|
|
362
|
+
}
|
|
410
363
|
}
|
|
411
364
|
}
|
|
412
365
|
|
|
@@ -420,10 +373,10 @@ const buildJsDocs = async ({ host, path, metadata = {}, publicClientId }) => {
|
|
|
420
373
|
delete jsDocsConfig.opts.tutorials;
|
|
421
374
|
}
|
|
422
375
|
|
|
423
|
-
fs.writeFileSync(
|
|
376
|
+
fs.writeFileSync(jsDocSourcePath, JSON.stringify(jsDocsConfig, null, 4), 'utf8');
|
|
424
377
|
logger.warn('build jsdoc view', jsDocsConfig.opts.destination);
|
|
425
378
|
|
|
426
|
-
shellExec(`
|
|
379
|
+
shellExec(`npx jsdoc -c ${jsDocSourcePath}`, { silent: true });
|
|
427
380
|
};
|
|
428
381
|
|
|
429
382
|
/**
|
|
@@ -433,58 +386,37 @@ const buildJsDocs = async ({ host, path, metadata = {}, publicClientId }) => {
|
|
|
433
386
|
* @param {Object} options - Coverage build options
|
|
434
387
|
* @param {string} options.host - The hostname for the coverage
|
|
435
388
|
* @param {string} options.path - The base path for the coverage
|
|
389
|
+
* @param {Object} options.docs - Documentation config from server conf
|
|
390
|
+
* @param {string} options.docs.coveragePath - Directory where to run npm run coverage
|
|
436
391
|
*/
|
|
437
|
-
const buildCoverage = async ({ host, path }) => {
|
|
392
|
+
const buildCoverage = async ({ host, path, docs }) => {
|
|
438
393
|
const logger = loggerFactory(import.meta);
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
shellExec(`npm test`);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const coverageBuildPath = `${jsDocsConfig.opts.destination}coverage`;
|
|
446
|
-
fs.mkdirSync(coverageBuildPath, { recursive: true });
|
|
447
|
-
fs.copySync(`./coverage`, coverageBuildPath);
|
|
448
|
-
|
|
449
|
-
logger.warn('build coverage', coverageBuildPath);
|
|
394
|
+
const jsDocSourcePath = docs.jsJsonPath;
|
|
395
|
+
const jsDocsConfig = JSON.parse(fs.readFileSync(jsDocSourcePath, 'utf8'));
|
|
396
|
+
const coveragePath = docs.coveragePath;
|
|
450
397
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (hardhatPkg.scripts && hardhatPkg.scripts.coverage) {
|
|
463
|
-
logger.info('generating hardhat coverage report');
|
|
464
|
-
shellExec(`cd ./hardhat && npx hardhat coverage`, { silent: true });
|
|
465
|
-
if (fs.existsSync(hardhatCoveragePath) && fs.readdirSync(hardhatCoveragePath).length > 0) {
|
|
466
|
-
const hardhatCoverageBuildPath = `${jsDocsConfig.opts.destination}hardhat-coverage`;
|
|
467
|
-
fs.mkdirSync(hardhatCoverageBuildPath, { recursive: true });
|
|
468
|
-
fs.copySync(hardhatCoveragePath, hardhatCoverageBuildPath);
|
|
469
|
-
logger.warn('build hardhat coverage (generated)', hardhatCoverageBuildPath);
|
|
470
|
-
}
|
|
398
|
+
const coverageOutputPath = `${coveragePath}/coverage`;
|
|
399
|
+
if (!fs.existsSync(coverageOutputPath)) {
|
|
400
|
+
const pkgPath = `${coveragePath}/package.json`;
|
|
401
|
+
if (fs.existsSync(pkgPath)) {
|
|
402
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
403
|
+
if (pkg.scripts && pkg.scripts.coverage) {
|
|
404
|
+
logger.info('generating coverage report', coveragePath);
|
|
405
|
+
shellExec(`cd ${coveragePath} && npm run coverage`, { silent: true });
|
|
406
|
+
} else if (pkg.scripts && pkg.scripts.test) {
|
|
407
|
+
logger.info('generating coverage via test', coveragePath);
|
|
408
|
+
shellExec(`cd ${coveragePath} && npm test`, { silent: true });
|
|
471
409
|
}
|
|
472
|
-
} catch (e) {
|
|
473
|
-
logger.warn('hardhat coverage generation skipped', e.message);
|
|
474
410
|
}
|
|
475
411
|
}
|
|
476
412
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
logger.
|
|
484
|
-
}
|
|
485
|
-
if (fs.existsSync(hardhatWhitePaperPath)) {
|
|
486
|
-
fs.copySync(hardhatWhitePaperPath, `${docsDestBase}hardhat-WHITE-PAPER.md`);
|
|
487
|
-
logger.info('copied hardhat WHITE-PAPER.md to docs');
|
|
413
|
+
if (fs.existsSync(coverageOutputPath) && fs.readdirSync(coverageOutputPath).length > 0) {
|
|
414
|
+
const coverageBuildPath = `${jsDocsConfig.opts.destination}coverage`;
|
|
415
|
+
fs.mkdirSync(coverageBuildPath, { recursive: true });
|
|
416
|
+
fs.copySync(coverageOutputPath, coverageBuildPath);
|
|
417
|
+
logger.warn('build coverage', coverageBuildPath);
|
|
418
|
+
} else {
|
|
419
|
+
logger.warn('no coverage output found, skipping', coverageOutputPath);
|
|
488
420
|
}
|
|
489
421
|
};
|
|
490
422
|
|
|
@@ -501,6 +433,7 @@ const buildCoverage = async ({ host, path }) => {
|
|
|
501
433
|
* @param {string} options.publicClientId - Client ID for the public documentation
|
|
502
434
|
* @param {string} options.rootClientPath - Root path for client files
|
|
503
435
|
* @param {Object} options.packageData - Package.json data
|
|
436
|
+
* @param {Object} options.docs - Documentation config from server conf
|
|
504
437
|
*/
|
|
505
438
|
const buildDocs = async ({
|
|
506
439
|
host,
|
|
@@ -511,9 +444,10 @@ const buildDocs = async ({
|
|
|
511
444
|
publicClientId,
|
|
512
445
|
rootClientPath,
|
|
513
446
|
packageData,
|
|
447
|
+
docs,
|
|
514
448
|
}) => {
|
|
515
|
-
await buildJsDocs({ host, path, metadata, publicClientId });
|
|
516
|
-
await buildCoverage({ host, path });
|
|
449
|
+
await buildJsDocs({ host, path, metadata, publicClientId, docs });
|
|
450
|
+
await buildCoverage({ host, path, docs });
|
|
517
451
|
await buildApiDocs({
|
|
518
452
|
host,
|
|
519
453
|
path,
|