@logbrew/sdk 0.1.38 → 0.1.39
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/automatic-delivery.cjs +271 -0
- package/batch-sender.cjs +164 -0
- package/bounded-number.cjs +34 -0
- package/browser-breadcrumbs.cjs +346 -136
- package/capture-support.cjs +100 -0
- package/client-options.cjs +292 -0
- package/client-report.cjs +232 -0
- package/client.cjs +447 -0
- package/console-arguments.cjs +151 -0
- package/console-capture.cjs +194 -0
- package/core.cjs +30 -2810
- package/delivery-health.cjs +133 -0
- package/error-causes.cjs +130 -0
- package/error-details.cjs +124 -0
- package/error-issue.cjs +187 -0
- package/event-clone.cjs +55 -0
- package/event-metadata.cjs +40 -0
- package/event-pipeline.cjs +325 -0
- package/event-queue.cjs +306 -0
- package/event-store.cjs +237 -0
- package/event-validation.cjs +159 -0
- package/examples/agent-timeline.cjs +57 -46
- package/examples/agent-timeline.mjs +41 -35
- package/examples/index.mjs +122 -94
- package/examples/readme-example.cjs +36 -27
- package/examples/readme-example.mjs +21 -17
- package/examples/real-user-smoke.cjs +41 -32
- package/examples/real-user-smoke.mjs +26 -22
- package/exception-chain.cjs +205 -0
- package/index.d.ts +144 -1076
- package/index.js +1 -1
- package/issue-exception.cjs +13 -8
- package/metric-validation.cjs +95 -0
- package/network-milestone.cjs +155 -0
- package/null-value.cjs +4 -0
- package/opentelemetry-bridge.cjs +330 -0
- package/opentelemetry-context.cjs +245 -0
- package/opentelemetry-exceptions.cjs +69 -0
- package/opentelemetry-keys.cjs +183 -0
- package/opentelemetry-span.cjs +286 -0
- package/opentelemetry-summary.cjs +213 -0
- package/opentelemetry-time.cjs +131 -0
- package/opentelemetry.cjs +99 -1068
- package/package.json +55 -1
- package/pino-destination.cjs +108 -0
- package/pino-record.cjs +237 -0
- package/product-action.cjs +163 -0
- package/provided-breadcrumbs.cjs +117 -0
- package/random-hex.cjs +29 -0
- package/react-native.js +1 -1
- package/recording-transport.cjs +68 -0
- package/release-artifacts-build.cjs +263 -84
- package/release-artifacts-common.js +347 -66
- package/release-artifacts-debug-id.js +178 -0
- package/release-artifacts-inspect.js +213 -0
- package/release-artifacts-issue-event.js +99 -0
- package/release-artifacts-manifest-artifact.js +107 -0
- package/release-artifacts-manifest.js +77 -0
- package/release-artifacts-mappings.js +265 -0
- package/release-artifacts-prepare.js +297 -0
- package/release-artifacts-source-context.js +210 -0
- package/release-artifacts-symbolication.js +272 -421
- package/release-artifacts-upload-files.js +122 -0
- package/release-artifacts-upload-request.js +246 -0
- package/release-artifacts-upload.js +155 -300
- package/release-artifacts-urls.js +49 -0
- package/release-artifacts.js +189 -662
- package/retry-delay.cjs +32 -0
- package/span-validation.cjs +191 -0
- package/stored-events.cjs +243 -0
- package/types/client.d.ts +67 -0
- package/types/delivery.d.ts +125 -0
- package/types/issues.d.ts +249 -0
- package/types/logging.d.ts +175 -0
- package/types/opentelemetry.d.ts +205 -0
- package/types/product.d.ts +128 -0
- package/types/recording-transport.d.ts +15 -0
- package/types/sdk-error.d.ts +9 -0
- package/types/telemetry.d.ts +171 -0
- package/types/trace-context.d.ts +110 -0
- package/types/transport-error.d.ts +10 -0
- package/utf8.cjs +37 -0
- package/validation.cjs +49 -1
- package/vite-release-artifacts-config.cjs +120 -0
- package/vite-release-artifacts.cjs +199 -99
- package/vite-release-artifacts.d.cts +65 -40
- package/vite-release-artifacts.d.ts +65 -40
- package/vite-release-artifacts.js +2 -2
- package/winston-info.cjs +205 -0
- package/winston.cjs +189 -289
|
@@ -3,8 +3,25 @@
|
|
|
3
3
|
const { spawnSync } = require("node:child_process");
|
|
4
4
|
const net = require("node:net");
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {{
|
|
8
|
+
* readonly allowHostedUpload: boolean,
|
|
9
|
+
* readonly dryRun: boolean,
|
|
10
|
+
* readonly endpoint: string,
|
|
11
|
+
* readonly maxRetries: number | undefined,
|
|
12
|
+
* readonly retryDelay: number | undefined,
|
|
13
|
+
* readonly timeout: number | undefined,
|
|
14
|
+
* readonly tokenEnv: string | undefined
|
|
15
|
+
* }} ReleaseArtifactUploadOptions
|
|
16
|
+
* @typedef {{ readonly integer: boolean, readonly integration: string, readonly maximum: number, readonly minimum: number }} NumberBounds
|
|
17
|
+
* @typedef {{ readonly report: unknown, readonly stdout: string }} ReleaseArtifactCliResult
|
|
18
|
+
*/
|
|
19
|
+
|
|
6
20
|
const HOSTED_UPLOAD_ENDPOINT = "https://api.logbrew.co/api/release-artifacts";
|
|
7
21
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
22
|
+
const ENVIRONMENT_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/u;
|
|
23
|
+
const REPORT_STATUS_RE = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
24
|
+
const WHITESPACE_RUN_RE = /\s+/gu;
|
|
8
25
|
const UPLOAD_OPTION_NAMES = new Set([
|
|
9
26
|
"allowHostedUpload",
|
|
10
27
|
"dryRun",
|
|
@@ -12,24 +29,58 @@ const UPLOAD_OPTION_NAMES = new Set([
|
|
|
12
29
|
"maxRetries",
|
|
13
30
|
"retryDelay",
|
|
14
31
|
"timeout",
|
|
15
|
-
"tokenEnv"
|
|
32
|
+
"tokenEnv"
|
|
16
33
|
]);
|
|
34
|
+
const HTTP_PROTOCOLS = new Set(["http:", "https:"]);
|
|
35
|
+
const MAX_RETRIES = 10;
|
|
36
|
+
const MAX_RETRY_DELAY_SECONDS = 60;
|
|
37
|
+
const MAX_TIMEOUT_SECONDS = 300;
|
|
38
|
+
const MIN_UPLOAD_SETTING = 0;
|
|
39
|
+
const LAST_CONTROL_CHARACTER = 31;
|
|
40
|
+
const DELETE_CHARACTER = 127;
|
|
41
|
+
const MAX_DIAGNOSTIC_LENGTH = 200;
|
|
42
|
+
const MAX_DIAGNOSTICS = 3;
|
|
43
|
+
const START = 0;
|
|
44
|
+
const NOT_AN_IP = 0;
|
|
45
|
+
const ONE_ARTIFACT = 1;
|
|
46
|
+
const SUCCESS_EXIT_CODE = 0;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {unknown} value
|
|
50
|
+
* @param {string} key
|
|
51
|
+
* @returns {unknown}
|
|
52
|
+
*/
|
|
53
|
+
function field(value, key) {
|
|
54
|
+
return typeof value === "object" && value !== null ? Reflect.get(value, key) : undefined;
|
|
55
|
+
}
|
|
17
56
|
|
|
18
|
-
|
|
19
|
-
|
|
57
|
+
/**
|
|
58
|
+
* @param {unknown} options
|
|
59
|
+
* @param {string} name
|
|
60
|
+
* @param {string} integration
|
|
61
|
+
* @returns {boolean}
|
|
62
|
+
*/
|
|
63
|
+
function optionalBoolean(options, name, integration) {
|
|
64
|
+
const value = field(options, name);
|
|
20
65
|
if (value === undefined || value === null) {
|
|
21
|
-
return
|
|
66
|
+
return false;
|
|
22
67
|
}
|
|
23
|
-
if (typeof value
|
|
24
|
-
|
|
68
|
+
if (typeof value === "boolean") {
|
|
69
|
+
return value;
|
|
25
70
|
}
|
|
26
|
-
|
|
71
|
+
throw new Error(`LogBrew ${integration} release-artifact upload option ${name} must be a boolean`);
|
|
27
72
|
}
|
|
28
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @param {unknown} options
|
|
76
|
+
* @param {string} name
|
|
77
|
+
* @param {string} integration
|
|
78
|
+
* @returns {string | undefined}
|
|
79
|
+
*/
|
|
29
80
|
function optionalString(options, name, integration) {
|
|
30
|
-
const value = options
|
|
81
|
+
const value = field(options, name);
|
|
31
82
|
if (value === undefined || value === null) {
|
|
32
|
-
return
|
|
83
|
+
return undefined;
|
|
33
84
|
}
|
|
34
85
|
if (typeof value !== "string" || value.trim() === "") {
|
|
35
86
|
throw new Error(`LogBrew ${integration} release-artifact upload option ${name} must be a non-empty string`);
|
|
@@ -37,18 +88,30 @@ function optionalString(options, name, integration) {
|
|
|
37
88
|
return value.trim();
|
|
38
89
|
}
|
|
39
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @param {unknown} options
|
|
93
|
+
* @param {string} name
|
|
94
|
+
* @param {string} integration
|
|
95
|
+
* @returns {string}
|
|
96
|
+
*/
|
|
40
97
|
function requiredString(options, name, integration) {
|
|
41
98
|
const value = optionalString(options, name, integration);
|
|
42
|
-
if (
|
|
99
|
+
if (value === undefined) {
|
|
43
100
|
throw new Error(`LogBrew ${integration} release-artifact upload requires ${name}`);
|
|
44
101
|
}
|
|
45
102
|
return value;
|
|
46
103
|
}
|
|
47
104
|
|
|
48
|
-
|
|
49
|
-
|
|
105
|
+
/**
|
|
106
|
+
* @param {unknown} options
|
|
107
|
+
* @param {string} name
|
|
108
|
+
* @param {NumberBounds} bounds
|
|
109
|
+
* @returns {number | undefined}
|
|
110
|
+
*/
|
|
111
|
+
function optionalBoundedNumber(options, name, { integer, integration, maximum, minimum }) {
|
|
112
|
+
const value = field(options, name);
|
|
50
113
|
if (value === undefined || value === null) {
|
|
51
|
-
return
|
|
114
|
+
return undefined;
|
|
52
115
|
}
|
|
53
116
|
if (
|
|
54
117
|
typeof value !== "number" ||
|
|
@@ -59,15 +122,20 @@ function optionalBoundedNumber(options, name, integration, minimum, maximum, int
|
|
|
59
122
|
) {
|
|
60
123
|
const kind = integer ? "an integer" : "a number";
|
|
61
124
|
throw new Error(
|
|
62
|
-
`LogBrew ${integration} release-artifact upload option ${name} must be ${kind} from ${minimum} to ${maximum}
|
|
125
|
+
`LogBrew ${integration} release-artifact upload option ${name} must be ${kind} from ${String(minimum)} to ${String(maximum)}`
|
|
63
126
|
);
|
|
64
127
|
}
|
|
65
128
|
return value;
|
|
66
129
|
}
|
|
67
130
|
|
|
131
|
+
/**
|
|
132
|
+
* @param {unknown} value
|
|
133
|
+
* @param {string} integration
|
|
134
|
+
* @returns {string | undefined}
|
|
135
|
+
*/
|
|
68
136
|
function normalizeReleaseArtifactProjectId(value, integration) {
|
|
69
137
|
if (value === undefined || value === null) {
|
|
70
|
-
return
|
|
138
|
+
return undefined;
|
|
71
139
|
}
|
|
72
140
|
if (typeof value !== "string" || !UUID_RE.test(value.trim())) {
|
|
73
141
|
throw new Error(`LogBrew ${integration} release-artifact option projectId must be a UUID`);
|
|
@@ -75,142 +143,253 @@ function normalizeReleaseArtifactProjectId(value, integration) {
|
|
|
75
143
|
return value.trim().toLowerCase();
|
|
76
144
|
}
|
|
77
145
|
|
|
146
|
+
/**
|
|
147
|
+
* @param {string} endpoint
|
|
148
|
+
* @param {string} integration
|
|
149
|
+
* @returns {URL}
|
|
150
|
+
*/
|
|
78
151
|
function parseUploadEndpoint(endpoint, integration) {
|
|
79
|
-
let parsed;
|
|
80
152
|
try {
|
|
81
|
-
|
|
153
|
+
return new URL(endpoint);
|
|
82
154
|
} catch {
|
|
83
155
|
throw new Error(`LogBrew ${integration} release-artifact upload endpoint must be a valid URL`);
|
|
84
156
|
}
|
|
85
|
-
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} endpoint
|
|
161
|
+
* @param {string} integration
|
|
162
|
+
* @returns {string}
|
|
163
|
+
*/
|
|
164
|
+
function uploadEndpointHostname(endpoint, integration) {
|
|
165
|
+
const { hostname, protocol } = parseUploadEndpoint(endpoint, integration);
|
|
166
|
+
if (!HTTP_PROTOCOLS.has(protocol)) {
|
|
86
167
|
throw new Error(`LogBrew ${integration} release-artifact upload endpoint must use http or https`);
|
|
87
168
|
}
|
|
88
|
-
return
|
|
169
|
+
return hostname;
|
|
89
170
|
}
|
|
90
171
|
|
|
91
|
-
|
|
92
|
-
|
|
172
|
+
/**
|
|
173
|
+
* @param {string} endpointHostname
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isLoopbackEndpoint(endpointHostname) {
|
|
177
|
+
const hostname = endpointHostname.toLowerCase();
|
|
93
178
|
return (
|
|
94
179
|
hostname === "localhost" ||
|
|
95
180
|
hostname === "[::1]" ||
|
|
96
181
|
hostname === "::1" ||
|
|
97
|
-
(net.isIP(hostname) !==
|
|
182
|
+
(net.isIP(hostname) !== NOT_AN_IP && (hostname.startsWith("127.") || hostname === "::1"))
|
|
98
183
|
);
|
|
99
184
|
}
|
|
100
185
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
186
|
+
/**
|
|
187
|
+
* @param {string} endpoint
|
|
188
|
+
* @param {boolean} allowHostedUpload
|
|
189
|
+
* @param {Readonly<{ integration: string, projectId: string | null | undefined }>} context
|
|
190
|
+
* @returns {void}
|
|
191
|
+
*/
|
|
192
|
+
function requireHostedUpload(endpoint, allowHostedUpload, { integration, projectId }) {
|
|
193
|
+
if (!allowHostedUpload) {
|
|
194
|
+
throw new Error(`LogBrew ${integration} release-artifact hosted upload requires allowHostedUpload`);
|
|
104
195
|
}
|
|
105
|
-
if (
|
|
106
|
-
throw new Error(`LogBrew ${integration} release-artifact
|
|
196
|
+
if (endpoint !== HOSTED_UPLOAD_ENDPOINT) {
|
|
197
|
+
throw new Error(`LogBrew ${integration} hosted release-artifact uploads must use ${HOSTED_UPLOAD_ENDPOINT}`);
|
|
107
198
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
throw new Error(`LogBrew ${integration} release-artifact unknown upload option ${unknownOption}`);
|
|
199
|
+
if (projectId === undefined || projectId === null || projectId === "") {
|
|
200
|
+
throw new Error(`LogBrew ${integration} release-artifact hosted upload requires projectId`);
|
|
111
201
|
}
|
|
202
|
+
}
|
|
112
203
|
|
|
204
|
+
/**
|
|
205
|
+
* @param {unknown} upload
|
|
206
|
+
* @param {string} integration
|
|
207
|
+
* @param {string | null | undefined} projectId
|
|
208
|
+
* @returns {{ readonly allowHostedUpload: boolean, readonly endpoint: string }}
|
|
209
|
+
*/
|
|
210
|
+
function requireUploadTarget(upload, integration, projectId) {
|
|
113
211
|
const endpoint = requiredString(upload, "endpoint", integration);
|
|
114
|
-
const
|
|
212
|
+
const hostname = uploadEndpointHostname(endpoint, integration);
|
|
115
213
|
const allowHostedUpload = optionalBoolean(upload, "allowHostedUpload", integration);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
throw new Error(`LogBrew ${integration} release-artifact hosted upload requires allowHostedUpload`);
|
|
119
|
-
}
|
|
120
|
-
if (hosted && endpoint !== HOSTED_UPLOAD_ENDPOINT) {
|
|
121
|
-
throw new Error(`LogBrew ${integration} hosted release-artifact uploads must use ${HOSTED_UPLOAD_ENDPOINT}`);
|
|
122
|
-
}
|
|
123
|
-
if (hosted && !projectId) {
|
|
124
|
-
throw new Error(`LogBrew ${integration} release-artifact hosted upload requires projectId`);
|
|
214
|
+
if (!isLoopbackEndpoint(hostname)) {
|
|
215
|
+
requireHostedUpload(endpoint, allowHostedUpload, { integration, projectId });
|
|
125
216
|
}
|
|
217
|
+
return { allowHostedUpload, endpoint };
|
|
218
|
+
}
|
|
126
219
|
|
|
220
|
+
/**
|
|
221
|
+
* @param {unknown} upload
|
|
222
|
+
* @param {string} integration
|
|
223
|
+
* @returns {string | undefined}
|
|
224
|
+
*/
|
|
225
|
+
function optionalTokenEnv(upload, integration) {
|
|
127
226
|
const tokenEnv = optionalString(upload, "tokenEnv", integration);
|
|
128
|
-
if (tokenEnv &&
|
|
227
|
+
if (tokenEnv !== undefined && !ENVIRONMENT_VARIABLE_NAME_RE.test(tokenEnv)) {
|
|
129
228
|
throw new Error(`LogBrew ${integration} release-artifact upload option tokenEnv must be an environment variable name`);
|
|
130
229
|
}
|
|
230
|
+
return tokenEnv;
|
|
231
|
+
}
|
|
131
232
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
233
|
+
/**
|
|
234
|
+
* @param {unknown} upload
|
|
235
|
+
* @param {string} integration
|
|
236
|
+
* @returns {void}
|
|
237
|
+
*/
|
|
238
|
+
function requireKnownUploadOptions(upload, integration) {
|
|
239
|
+
const uploadObject = typeof upload === "object" && upload !== null && !Array.isArray(upload);
|
|
240
|
+
if (!uploadObject) {
|
|
241
|
+
throw new Error(`LogBrew ${integration} release-artifact option upload must be an object`);
|
|
242
|
+
}
|
|
243
|
+
const unknownOption = Object.keys(upload).find((name) => !UPLOAD_OPTION_NAMES.has(name));
|
|
244
|
+
if (unknownOption !== undefined && unknownOption !== "") {
|
|
245
|
+
throw new Error(`LogBrew ${integration} release-artifact unknown upload option ${unknownOption}`);
|
|
246
|
+
}
|
|
141
247
|
}
|
|
142
248
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
249
|
+
/**
|
|
250
|
+
* @param {unknown} upload
|
|
251
|
+
* @param {Readonly<{ integration: string, projectId: string | null | undefined }>} context
|
|
252
|
+
* @returns {ReleaseArtifactUploadOptions | undefined}
|
|
253
|
+
*/
|
|
254
|
+
function normalizeReleaseArtifactUploadOptions(upload, { integration, projectId }) {
|
|
255
|
+
if (upload === undefined || upload === null) {
|
|
256
|
+
return undefined;
|
|
146
257
|
}
|
|
258
|
+
requireKnownUploadOptions(upload, integration);
|
|
259
|
+
const { allowHostedUpload, endpoint } = requireUploadTarget(upload, integration, projectId);
|
|
260
|
+
const tokenEnv = optionalTokenEnv(upload, integration);
|
|
261
|
+
const dryRun = optionalBoolean(upload, "dryRun", integration);
|
|
262
|
+
const maxRetries = optionalBoundedNumber(upload, "maxRetries", { integer: true, integration, maximum: MAX_RETRIES, minimum: MIN_UPLOAD_SETTING });
|
|
263
|
+
const retryDelay = optionalBoundedNumber(upload, "retryDelay", { integer: false, integration, maximum: MAX_RETRY_DELAY_SECONDS, minimum: MIN_UPLOAD_SETTING });
|
|
264
|
+
const timeout = optionalBoundedNumber(upload, "timeout", { integer: false, integration, maximum: MAX_TIMEOUT_SECONDS, minimum: MIN_UPLOAD_SETTING });
|
|
265
|
+
return { allowHostedUpload, dryRun, endpoint, maxRetries, retryDelay, timeout, tokenEnv };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @param {string} name
|
|
270
|
+
* @param {number | string | undefined} value
|
|
271
|
+
* @returns {string[]}
|
|
272
|
+
*/
|
|
273
|
+
function optionArgs(name, value) {
|
|
274
|
+
return value === undefined ? [] : [name, String(value)];
|
|
147
275
|
}
|
|
148
276
|
|
|
277
|
+
/**
|
|
278
|
+
* @param {string} buildDir
|
|
279
|
+
* @param {string} manifestPath
|
|
280
|
+
* @param {ReleaseArtifactUploadOptions} upload
|
|
281
|
+
* @returns {string[]}
|
|
282
|
+
*/
|
|
149
283
|
function createReleaseArtifactUploadArgs(buildDir, manifestPath, upload) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
284
|
+
return [
|
|
285
|
+
"--build-dir",
|
|
286
|
+
buildDir,
|
|
287
|
+
"--manifest",
|
|
288
|
+
manifestPath,
|
|
289
|
+
"--endpoint",
|
|
290
|
+
upload.endpoint,
|
|
291
|
+
...optionArgs("--token-env", upload.tokenEnv),
|
|
292
|
+
...(upload.allowHostedUpload ? ["--allow-hosted"] : []),
|
|
293
|
+
...(upload.dryRun ? ["--dry-run"] : []),
|
|
294
|
+
...optionArgs("--max-retries", upload.maxRetries),
|
|
295
|
+
...optionArgs("--retry-delay", upload.retryDelay),
|
|
296
|
+
...optionArgs("--timeout", upload.timeout)
|
|
297
|
+
];
|
|
162
298
|
}
|
|
163
299
|
|
|
300
|
+
/**
|
|
301
|
+
* @param {string} stdout
|
|
302
|
+
* @param {string} command
|
|
303
|
+
* @returns {unknown}
|
|
304
|
+
*/
|
|
164
305
|
function parseJson(stdout, command) {
|
|
165
306
|
try {
|
|
166
|
-
|
|
307
|
+
/** @type {unknown} */
|
|
308
|
+
const report = JSON.parse(stdout);
|
|
309
|
+
return report;
|
|
167
310
|
} catch (error) {
|
|
168
311
|
throw new Error(`LogBrew release-artifact ${command} did not return JSON`, { cause: error });
|
|
169
312
|
}
|
|
170
313
|
}
|
|
171
314
|
|
|
315
|
+
/**
|
|
316
|
+
* @param {string} message
|
|
317
|
+
* @returns {string}
|
|
318
|
+
*/
|
|
172
319
|
function sanitizeValidationDiagnostic(message) {
|
|
173
320
|
const printable = Array.from(message, (character) => {
|
|
174
|
-
const codePoint = character.codePointAt(
|
|
175
|
-
return codePoint <=
|
|
321
|
+
const codePoint = character.codePointAt(START) ?? START;
|
|
322
|
+
return codePoint <= LAST_CONTROL_CHARACTER || codePoint === DELETE_CHARACTER ? " " : character;
|
|
176
323
|
}).join("");
|
|
177
|
-
return printable.
|
|
324
|
+
return printable.replaceAll(WHITESPACE_RUN_RE, " ").trim().slice(START, MAX_DIAGNOSTIC_LENGTH);
|
|
178
325
|
}
|
|
179
326
|
|
|
327
|
+
/**
|
|
328
|
+
* @param {unknown} report
|
|
329
|
+
* @returns {string[]}
|
|
330
|
+
*/
|
|
180
331
|
function validationErrors(report) {
|
|
181
|
-
|
|
332
|
+
const errors = field(field(report, "validation"), "errors");
|
|
333
|
+
if (!Array.isArray(errors)) {
|
|
182
334
|
return [];
|
|
183
335
|
}
|
|
184
|
-
|
|
336
|
+
/** @type {readonly unknown[]} */
|
|
337
|
+
const messages = errors;
|
|
338
|
+
return messages
|
|
185
339
|
.filter((message) => typeof message === "string")
|
|
186
|
-
.map(sanitizeValidationDiagnostic)
|
|
340
|
+
.map((message) => sanitizeValidationDiagnostic(message))
|
|
187
341
|
.filter((message) => message !== "")
|
|
188
|
-
.slice(
|
|
342
|
+
.slice(START, MAX_DIAGNOSTICS);
|
|
189
343
|
}
|
|
190
344
|
|
|
345
|
+
/**
|
|
346
|
+
* @param {unknown} report
|
|
347
|
+
* @returns {string | undefined}
|
|
348
|
+
*/
|
|
191
349
|
function safeReportStatus(report) {
|
|
192
|
-
const status = report
|
|
193
|
-
return typeof status === "string" &&
|
|
350
|
+
const status = field(report, "status");
|
|
351
|
+
return typeof status === "string" && REPORT_STATUS_RE.test(status) ? status : undefined;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @param {unknown} output
|
|
356
|
+
* @returns {string}
|
|
357
|
+
*/
|
|
358
|
+
function textOutput(output) {
|
|
359
|
+
return typeof output === "string" ? output : "";
|
|
194
360
|
}
|
|
195
361
|
|
|
362
|
+
/**
|
|
363
|
+
* @param {string} cliPath
|
|
364
|
+
* @param {string} command
|
|
365
|
+
* @param {readonly string[]} args
|
|
366
|
+
* @returns {ReleaseArtifactCliResult}
|
|
367
|
+
*/
|
|
196
368
|
function runReleaseArtifactCli(cliPath, command, args) {
|
|
197
369
|
const result = spawnSync(process.execPath, [cliPath, command, ...args], {
|
|
198
370
|
cwd: process.cwd(),
|
|
199
371
|
encoding: "utf8",
|
|
200
|
-
windowsHide: true
|
|
372
|
+
windowsHide: true
|
|
201
373
|
});
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
374
|
+
const stdout = textOutput(result.stdout);
|
|
375
|
+
const report = stdout === "" ? undefined : parseJson(stdout, command);
|
|
376
|
+
if (result.status !== SUCCESS_EXIT_CODE) {
|
|
377
|
+
const errors = validationErrors(report).join("; ");
|
|
378
|
+
const details = errors === "" ? safeReportStatus(report) ?? "execution_failed" : errors;
|
|
205
379
|
throw new Error(`LogBrew release-artifact ${command} failed: ${details}`);
|
|
206
380
|
}
|
|
207
|
-
return { report, stdout
|
|
381
|
+
return { report, stdout };
|
|
208
382
|
}
|
|
209
383
|
|
|
384
|
+
/**
|
|
385
|
+
* @param {unknown} report
|
|
386
|
+
* @returns {string}
|
|
387
|
+
*/
|
|
210
388
|
function formatReleaseArtifactUploadSummary(report) {
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
389
|
+
const count = field(report, "artifactCount");
|
|
390
|
+
const artifactCount = typeof count === "number" && Number.isSafeInteger(count) && count >= START ? count : START;
|
|
391
|
+
const artifactLabel = artifactCount === ONE_ARTIFACT ? "artifact" : "artifacts";
|
|
392
|
+
return `LogBrew release artifacts: ${safeReportStatus(report) ?? "unknown"} (${String(artifactCount)} ${artifactLabel})`;
|
|
214
393
|
}
|
|
215
394
|
|
|
216
395
|
module.exports = {
|
|
@@ -218,5 +397,5 @@ module.exports = {
|
|
|
218
397
|
formatReleaseArtifactUploadSummary,
|
|
219
398
|
normalizeReleaseArtifactProjectId,
|
|
220
399
|
normalizeReleaseArtifactUploadOptions,
|
|
221
|
-
runReleaseArtifactCli
|
|
400
|
+
runReleaseArtifactCli
|
|
222
401
|
};
|