@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
package/release-artifacts.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Buffer } from "node:buffer";
|
|
3
|
-
import crypto from "node:crypto";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
|
|
7
2
|
import {
|
|
8
|
-
|
|
9
|
-
fileReference,
|
|
3
|
+
errorMessage,
|
|
10
4
|
normalizeProjectId,
|
|
11
5
|
parseOptions,
|
|
6
|
+
pathExists,
|
|
12
7
|
printJson,
|
|
13
8
|
readJsonObject,
|
|
14
|
-
relativeTo,
|
|
15
|
-
requireOption,
|
|
16
9
|
requireBuildDir,
|
|
17
|
-
|
|
18
|
-
sha256File,
|
|
19
|
-
sourceMapDebugId,
|
|
20
|
-
SOURCE_MAP_DEBUG_ID_KEYS,
|
|
21
|
-
sortJson,
|
|
22
|
-
stableJson
|
|
10
|
+
requireOption
|
|
23
11
|
} from "./release-artifacts-common.js";
|
|
24
|
-
import { runUploadJs } from "./release-artifacts-upload.js";
|
|
25
12
|
import { verifyJavaScriptIssueSymbolication, verifyJavaScriptSymbolication } from "./release-artifacts-symbolication.js";
|
|
13
|
+
import { createDebugIdPlan } from "./release-artifacts-prepare.js";
|
|
14
|
+
import { createManifest } from "./release-artifacts-manifest.js";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { runUploadJs } from "./release-artifacts-upload.js";
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const HERMES_RUNTIME_DEBUG_ID_RE = /\/\*logbrew-runtime-debug-id\*\/\/\/# debugId=([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/iu;
|
|
32
|
-
const RUNTIME_DEBUG_ID_MARKER = "/*logbrew-runtime-debug-id*/";
|
|
33
|
-
const MINIFIED_SOURCE_SUFFIXES = [".js", ".mjs", ".bundle", ".jsbundle"];
|
|
34
|
-
const SCRIPT_VERSION = "0.1.0";
|
|
35
|
-
const SOURCE_MAPPING_COMMENT_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=[^\r\n]*/giu;
|
|
36
|
-
const SOURCE_MAPPING_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=([^\s*]+)/iu;
|
|
18
|
+
/**
|
|
19
|
+
* @import { CliOptions } from "./release-artifacts-common.js"
|
|
20
|
+
* @import { SourceContextOptions } from "./release-artifacts-source-context.js"
|
|
21
|
+
*/
|
|
37
22
|
|
|
23
|
+
const SCRIPT_VERSION = "0.1.0";
|
|
24
|
+
const DEFAULT_CONTEXT_LINES = 2;
|
|
25
|
+
const MAX_CONTEXT_LINES = 10;
|
|
26
|
+
const MIN_CONTEXT_LINES = 0;
|
|
27
|
+
const DECIMAL = 10;
|
|
28
|
+
const EXIT_OK = 0;
|
|
29
|
+
const EXIT_BLOCKED = 1;
|
|
30
|
+
const EXIT_USAGE = 2;
|
|
31
|
+
const ARGUMENT_OFFSET = 2;
|
|
32
|
+
const NON_NEGATIVE_INTEGER_RE = /^\d+$/u;
|
|
33
|
+
const VERIFIER = { name: "logbrew-js-release-artifact-symbolication-verifier", version: SCRIPT_VERSION };
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
38
|
function usage() {
|
|
39
39
|
return [
|
|
40
40
|
"Usage:",
|
|
@@ -48,691 +48,218 @@ function usage() {
|
|
|
48
48
|
].join("\n");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
let contextLines = 2;
|
|
61
|
-
if (hasContextLines) {
|
|
62
|
-
const value = options["context-lines"].trim();
|
|
63
|
-
if (!/^\d+$/u.test(value)) {
|
|
64
|
-
throw new Error("--context-lines must be an integer from 0 to 10");
|
|
65
|
-
}
|
|
66
|
-
contextLines = Number.parseInt(value, 10);
|
|
67
|
-
if (contextLines < 0 || contextLines > 10) {
|
|
68
|
-
throw new Error("--context-lines must be an integer from 0 to 10");
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
sourceRoot: path.resolve(requireOption(options, "source-root")),
|
|
73
|
-
contextLines
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function readText(filePath) {
|
|
78
|
-
return fs.readFileSync(filePath, "utf8");
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function writeText(filePath, value) {
|
|
82
|
-
fs.writeFileSync(filePath, value, "utf8");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function readSourceMap(filePath) {
|
|
86
|
-
let payload;
|
|
87
|
-
try {
|
|
88
|
-
payload = JSON.parse(readText(filePath));
|
|
89
|
-
} catch (error) {
|
|
90
|
-
return [null, [`source map is not valid JSON: ${error.message}`]];
|
|
91
|
-
}
|
|
92
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
93
|
-
return [null, ["source map must be a JSON object"]];
|
|
94
|
-
}
|
|
95
|
-
return [payload, []];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function findLastMatch(source, regex) {
|
|
99
|
-
let result = null;
|
|
100
|
-
regex.lastIndex = 0;
|
|
101
|
-
for (const match of source.matchAll(regex)) {
|
|
102
|
-
result = match[1]?.trim() ?? null;
|
|
103
|
-
}
|
|
104
|
-
return result;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function findDebugId(source) {
|
|
108
|
-
const runtimeDebugId = source.match(HERMES_RUNTIME_DEBUG_ID_RE)?.[1];
|
|
109
|
-
return runtimeDebugId ?? findLastMatch(source, DEBUG_ID_RE);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function findSourceMappingUrl(source) {
|
|
113
|
-
const match = source.match(SOURCE_MAPPING_RE);
|
|
114
|
-
return match?.[1]?.trim() ?? null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function resolveSourceMapPath(jsPath, buildDir, sourceMappingUrl) {
|
|
118
|
-
const warnings = [];
|
|
119
|
-
const errors = [];
|
|
120
|
-
if (!sourceMappingUrl) {
|
|
121
|
-
const fallback = `${jsPath}.map`;
|
|
122
|
-
warnings.push("sourceMappingURL comment missing; checked sibling .map fallback");
|
|
123
|
-
return [fs.existsSync(fallback) ? fallback : null, warnings, errors];
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const reference = fileReference(sourceMappingUrl);
|
|
127
|
-
if (reference.startsWith("data:")) {
|
|
128
|
-
errors.push("inline source maps are not accepted for release artifact manifests");
|
|
129
|
-
return [null, warnings, errors];
|
|
130
|
-
}
|
|
131
|
-
if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(reference)) {
|
|
132
|
-
errors.push("external sourceMappingURL cannot be validated from the local build directory");
|
|
133
|
-
return [null, warnings, errors];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const candidate = reference.startsWith("/")
|
|
137
|
-
? path.join(buildDir, reference.slice(1))
|
|
138
|
-
: path.join(path.dirname(jsPath), reference);
|
|
139
|
-
const resolved = safeResolve(candidate, buildDir);
|
|
140
|
-
if (!resolved) {
|
|
141
|
-
errors.push("sourceMappingURL resolves outside the build directory");
|
|
142
|
-
}
|
|
143
|
-
return [resolved, warnings, errors];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function isMinifiedSource(filePath) {
|
|
147
|
-
return !filePath.endsWith(".map") && MINIFIED_SOURCE_SUFFIXES.some((suffix) => filePath.endsWith(suffix));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function walkFiles(root) {
|
|
151
|
-
const results = [];
|
|
152
|
-
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
153
|
-
const entryPath = path.join(root, entry.name);
|
|
154
|
-
if (entry.isDirectory()) {
|
|
155
|
-
results.push(...walkFiles(entryPath));
|
|
156
|
-
} else if (entry.isFile()) {
|
|
157
|
-
results.push(entryPath);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return results.sort();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function iterMinifiedSourceFiles(buildDir) {
|
|
164
|
-
return walkFiles(buildDir).filter(isMinifiedSource);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function canonicalSourceWithoutDebugId(source) {
|
|
168
|
-
return source.replace(DEBUG_ID_RE, "");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function canonicalSourceMapWithoutDebugId(payload) {
|
|
172
|
-
const copy = { ...payload };
|
|
173
|
-
for (const key of SOURCE_MAP_DEBUG_ID_KEYS) {
|
|
174
|
-
delete copy[key];
|
|
175
|
-
}
|
|
176
|
-
return stableJson(copy);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function uuidBytes(value) {
|
|
180
|
-
return Buffer.from(value.replaceAll("-", ""), "hex");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function formatUuid(bytes) {
|
|
184
|
-
const hex = Buffer.from(bytes).toString("hex");
|
|
185
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function uuidV5(namespace, name) {
|
|
189
|
-
const bytes = crypto
|
|
190
|
-
.createHash("sha1")
|
|
191
|
-
.update(uuidBytes(namespace))
|
|
192
|
-
.update(Buffer.from(name, "utf8"))
|
|
193
|
-
.digest()
|
|
194
|
-
.subarray(0, 16);
|
|
195
|
-
bytes[6] = (bytes[6] & 0x0f) | 0x50;
|
|
196
|
-
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
197
|
-
return formatUuid(bytes);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function generateDebugId(relativeJsPath, jsSource, sourceMapPayload) {
|
|
201
|
-
const digest = crypto.createHash("sha256");
|
|
202
|
-
digest.update(relativeJsPath);
|
|
203
|
-
digest.update("\0");
|
|
204
|
-
digest.update(canonicalSourceWithoutDebugId(jsSource));
|
|
205
|
-
digest.update("\0");
|
|
206
|
-
digest.update(canonicalSourceMapWithoutDebugId(sourceMapPayload));
|
|
207
|
-
return uuidV5(DEBUG_ID_NAMESPACE, digest.digest("hex"));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function executableShebang(source) {
|
|
211
|
-
return source.startsWith("#!") ? source.match(/^#![^\r\n]*(?:\r?\n|$)/u)?.[0] ?? "" : "";
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function sourceStartsWithDirective(source, shebang) {
|
|
215
|
-
const body = source.slice(shebang.length).replace(/^(?:\s|\/\/[^\r\n]*(?:\r?\n|$)|\/\*[\s\S]*?\*\/)+/u, "");
|
|
216
|
-
return body.startsWith("\"") || body.startsWith("'");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function sourceWithDebugId(source, debugId, relativePath) {
|
|
220
|
-
const registryLine = `;try{${RUNTIME_DEBUG_ID_MARKER}(function(){var g=globalThis,s=${DEBUG_ID_REGISTRY_EXPRESSION},r=g[s];if(!r||typeof r!=="object")r=g[s]=Object.create(null);r[${JSON.stringify(relativePath)}]=${JSON.stringify(debugId)}})()}catch(_){}\n`;
|
|
221
|
-
const shebang = executableShebang(source);
|
|
222
|
-
const prepared = source.includes(RUNTIME_DEBUG_ID_MARKER)
|
|
223
|
-
? source
|
|
224
|
-
: `${shebang}${shebang === source && shebang ? "\n" : ""}${registryLine}${source.slice(shebang.length)}`;
|
|
225
|
-
if (findDebugId(prepared)) {
|
|
226
|
-
return prepared;
|
|
227
|
-
}
|
|
228
|
-
const debugLine = `//# debugId=${debugId}\n`;
|
|
229
|
-
const matches = [...prepared.matchAll(SOURCE_MAPPING_COMMENT_RE)];
|
|
230
|
-
if (matches.length === 0) {
|
|
231
|
-
return `${prepared}${prepared.endsWith("\n") ? "" : "\n"}${debugLine}`;
|
|
232
|
-
}
|
|
233
|
-
const last = matches.at(-1);
|
|
234
|
-
const prefix = prepared.slice(0, last.index);
|
|
235
|
-
const separator = prefix.endsWith("\n") || prefix.endsWith("\r") ? "" : "\n";
|
|
236
|
-
return `${prefix}${separator}${debugLine}${prepared.slice(last.index)}`;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function normalizeSourcePrefixes(values) {
|
|
240
|
-
const prefixes = [];
|
|
241
|
-
for (const value of values ?? []) {
|
|
242
|
-
for (const candidate of [path.resolve(value), fs.existsSync(value) ? fs.realpathSync(value) : path.resolve(value)]) {
|
|
243
|
-
const normalized = candidate.split(path.sep).join("/").replace(/\/+$/u, "");
|
|
244
|
-
if (normalized && !prefixes.includes(normalized)) {
|
|
245
|
-
prefixes.push(normalized);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return prefixes;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function sourceWithoutPrefix(source, prefixes) {
|
|
253
|
-
const normalized = source.replaceAll("\\", "/");
|
|
254
|
-
for (const prefix of prefixes) {
|
|
255
|
-
const marker = `${prefix}/`;
|
|
256
|
-
if (normalized === prefix) {
|
|
257
|
-
return path.posix.basename(prefix);
|
|
258
|
-
}
|
|
259
|
-
if (normalized.startsWith(marker)) {
|
|
260
|
-
return normalized.slice(marker.length);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return source;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function sourceMapSourcesWithoutPrefixes(payload, prefixes) {
|
|
267
|
-
if (prefixes.length === 0 || !Array.isArray(payload.sources)) {
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
const updated = payload.sources.map((source) => (typeof source === "string" ? sourceWithoutPrefix(source, prefixes) : source));
|
|
271
|
-
return JSON.stringify(updated) === JSON.stringify(payload.sources) ? null : updated;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function sourceMapPayloadForDebugId(payload, { stripSourcesContent, sourcePrefixes }) {
|
|
275
|
-
const updatedSources = sourceMapSourcesWithoutPrefixes(payload, sourcePrefixes);
|
|
276
|
-
if (!stripSourcesContent && !updatedSources) {
|
|
277
|
-
return payload;
|
|
278
|
-
}
|
|
279
|
-
const updated = updatedSources ? { ...payload, sources: updatedSources } : { ...payload };
|
|
280
|
-
if (stripSourcesContent) {
|
|
281
|
-
delete updated.sourcesContent;
|
|
282
|
-
}
|
|
283
|
-
return updated;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function sourceMapWithPrivacyUpdates(payload, debugId, options) {
|
|
287
|
-
const privacyUpdated = sourceMapPayloadForDebugId(payload, options);
|
|
288
|
-
const { shiftGeneratedLines } = options;
|
|
289
|
-
if (sourceMapDebugId(payload)
|
|
290
|
-
&& privacyUpdated === payload
|
|
291
|
-
&& !shiftGeneratedLines) {
|
|
292
|
-
return payload;
|
|
293
|
-
}
|
|
294
|
-
const updated = { ...privacyUpdated };
|
|
295
|
-
if (!sourceMapDebugId(updated)) {
|
|
296
|
-
updated.debug_id = debugId;
|
|
297
|
-
}
|
|
298
|
-
if (shiftGeneratedLines && typeof updated.mappings === "string") {
|
|
299
|
-
updated.mappings = `;${updated.mappings}`;
|
|
300
|
-
}
|
|
301
|
-
return updated;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function inspectArtifactFiles(jsPath, buildDir) {
|
|
305
|
-
const errors = [];
|
|
306
|
-
const warnings = [];
|
|
307
|
-
const relJs = relativeTo(buildDir, jsPath);
|
|
308
|
-
const jsSize = byteSize(jsPath);
|
|
309
|
-
|
|
310
|
-
if (jsSize === 0) {
|
|
311
|
-
errors.push("minified source file is empty");
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const jsSource = readText(jsPath);
|
|
315
|
-
const jsDebugId = findDebugId(jsSource);
|
|
316
|
-
const sourceMappingUrl = findSourceMappingUrl(jsSource);
|
|
317
|
-
const [sourceMapPath, mapWarnings, mapErrors] = resolveSourceMapPath(jsPath, buildDir, sourceMappingUrl);
|
|
318
|
-
warnings.push(...mapWarnings);
|
|
319
|
-
errors.push(...mapErrors);
|
|
320
|
-
|
|
321
|
-
let sourceMapPayload = null;
|
|
322
|
-
let mapDebugId = null;
|
|
323
|
-
let sourceMapRel = null;
|
|
324
|
-
let sourceMapSize = null;
|
|
325
|
-
if (!sourceMapPath) {
|
|
326
|
-
errors.push("source map file is missing");
|
|
327
|
-
} else if (!fs.existsSync(sourceMapPath)) {
|
|
328
|
-
errors.push(`source map file is missing: ${relativeTo(buildDir, sourceMapPath)}`);
|
|
329
|
-
} else if (byteSize(sourceMapPath) === 0) {
|
|
330
|
-
errors.push(`source map file is empty: ${relativeTo(buildDir, sourceMapPath)}`);
|
|
331
|
-
} else {
|
|
332
|
-
sourceMapRel = relativeTo(buildDir, sourceMapPath);
|
|
333
|
-
sourceMapSize = byteSize(sourceMapPath);
|
|
334
|
-
const [payload, sourceMapErrors] = readSourceMap(sourceMapPath);
|
|
335
|
-
errors.push(...sourceMapErrors);
|
|
336
|
-
if (payload) {
|
|
337
|
-
sourceMapPayload = payload;
|
|
338
|
-
mapDebugId = sourceMapDebugId(payload);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (jsDebugId && mapDebugId && jsDebugId !== mapDebugId) {
|
|
343
|
-
errors.push("minified source debugId does not match source map debugId");
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return {
|
|
347
|
-
errors,
|
|
348
|
-
warnings,
|
|
349
|
-
relJs,
|
|
350
|
-
jsSize,
|
|
351
|
-
jsSource,
|
|
352
|
-
jsDebugId,
|
|
353
|
-
sourceMappingUrl,
|
|
354
|
-
sourceMapPath,
|
|
355
|
-
sourceMapPayload,
|
|
356
|
-
sourceMapRel,
|
|
357
|
-
sourceMapSize,
|
|
358
|
-
mapDebugId
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function buildArtifactPlan(jsPath, buildDir, options) {
|
|
363
|
-
const {
|
|
364
|
-
errors,
|
|
365
|
-
warnings,
|
|
366
|
-
relJs,
|
|
367
|
-
jsSource,
|
|
368
|
-
jsDebugId,
|
|
369
|
-
sourceMapPayload,
|
|
370
|
-
sourceMapRel,
|
|
371
|
-
mapDebugId
|
|
372
|
-
} = inspectArtifactFiles(jsPath, buildDir);
|
|
373
|
-
const changes = [];
|
|
374
|
-
let debugId = jsDebugId || mapDebugId;
|
|
375
|
-
const needsRuntimeDebugId = !jsSource.includes(RUNTIME_DEBUG_ID_MARKER);
|
|
376
|
-
if (needsRuntimeDebugId && sourceStartsWithDirective(jsSource, executableShebang(jsSource))) {
|
|
377
|
-
errors.push("runtime Debug ID injection cannot precede a script directive");
|
|
378
|
-
}
|
|
379
|
-
if (needsRuntimeDebugId
|
|
380
|
-
&& sourceMapPayload
|
|
381
|
-
&& (typeof sourceMapPayload.mappings !== "string" || sourceMapPayload.mappings === "")) {
|
|
382
|
-
errors.push("source map mappings must be a non-empty string before runtime Debug ID injection");
|
|
383
|
-
}
|
|
384
|
-
if (errors.length === 0 && sourceMapPayload) {
|
|
385
|
-
if (needsRuntimeDebugId) {
|
|
386
|
-
changes.push("minifiedSource.runtimeDebugId");
|
|
387
|
-
}
|
|
388
|
-
if (!debugId) {
|
|
389
|
-
debugId = generateDebugId(
|
|
390
|
-
relJs,
|
|
391
|
-
jsSource,
|
|
392
|
-
sourceMapPayloadForDebugId(sourceMapPayload, options)
|
|
393
|
-
);
|
|
394
|
-
changes.push("minifiedSource.debugId", "sourceMap.debug_id");
|
|
395
|
-
} else {
|
|
396
|
-
if (!jsDebugId) {
|
|
397
|
-
changes.push("minifiedSource.debugId");
|
|
398
|
-
}
|
|
399
|
-
if (!mapDebugId) {
|
|
400
|
-
changes.push("sourceMap.debug_id");
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (options.stripSourcesContent && "sourcesContent" in sourceMapPayload) {
|
|
404
|
-
changes.push("sourceMap.sourcesContent");
|
|
405
|
-
}
|
|
406
|
-
if (sourceMapSourcesWithoutPrefixes(sourceMapPayload, options.sourcePrefixes)) {
|
|
407
|
-
changes.push("sourceMap.sources");
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return {
|
|
412
|
-
path: relJs,
|
|
413
|
-
...(sourceMapRel ? { sourceMapPath: sourceMapRel } : {}),
|
|
414
|
-
...(debugId ? { debugId } : {}),
|
|
415
|
-
changes,
|
|
416
|
-
validation: {
|
|
417
|
-
status: errors.length > 0 ? "blocked" : "ready",
|
|
418
|
-
errors,
|
|
419
|
-
warnings
|
|
420
|
-
}
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function applyArtifactPlan(artifact, buildDir, options) {
|
|
425
|
-
const debugId = artifact.debugId;
|
|
426
|
-
const jsPath = path.join(buildDir, artifact.path);
|
|
427
|
-
const sourceMapPath = path.join(buildDir, artifact.sourceMapPath);
|
|
428
|
-
const jsSource = readText(jsPath);
|
|
429
|
-
const shiftGeneratedLines = !jsSource.includes(RUNTIME_DEBUG_ID_MARKER);
|
|
430
|
-
const updatedSource = sourceWithDebugId(jsSource, debugId, artifact.path);
|
|
431
|
-
if (updatedSource !== jsSource) {
|
|
432
|
-
writeText(jsPath, updatedSource);
|
|
433
|
-
}
|
|
434
|
-
const [payload, errors] = readSourceMap(sourceMapPath);
|
|
435
|
-
if (!payload || errors.length > 0) {
|
|
436
|
-
throw new Error(`${artifact.path}: source map became unreadable before write`);
|
|
437
|
-
}
|
|
438
|
-
const updatedPayload = sourceMapWithPrivacyUpdates(payload, debugId, {
|
|
439
|
-
...options,
|
|
440
|
-
shiftGeneratedLines
|
|
441
|
-
});
|
|
442
|
-
if (stableJson(updatedPayload) !== stableJson(payload)) {
|
|
443
|
-
writeText(sourceMapPath, `${JSON.stringify(sortJson(updatedPayload), null, 2)}\n`);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function createDebugIdPlan({ buildDir, write, stripSourcesContent, stripSourcePrefixes }) {
|
|
448
|
-
const sourcePrefixes = normalizeSourcePrefixes(stripSourcePrefixes);
|
|
449
|
-
const artifactOptions = { stripSourcesContent, sourcePrefixes };
|
|
450
|
-
const artifacts = iterMinifiedSourceFiles(buildDir).map((filePath) => buildArtifactPlan(filePath, buildDir, artifactOptions));
|
|
451
|
-
const errors = artifacts.length === 0 ? ["no JavaScript release artifact files found in build directory"] : [];
|
|
452
|
-
const warnings = [];
|
|
453
|
-
for (const artifact of artifacts) {
|
|
454
|
-
errors.push(...artifact.validation.errors.map((message) => `${artifact.path}: ${message}`));
|
|
455
|
-
warnings.push(...artifact.validation.warnings.map((message) => `${artifact.path}: ${message}`));
|
|
456
|
-
}
|
|
457
|
-
const status = errors.length > 0 ? "blocked" : "ready";
|
|
458
|
-
if (write && status === "ready") {
|
|
459
|
-
for (const artifact of artifacts) {
|
|
460
|
-
applyArtifactPlan(artifact, buildDir, artifactOptions);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
return {
|
|
464
|
-
manifestVersion: 1,
|
|
465
|
-
tool: { name: "logbrew-js-release-artifact-debug-id-prep", version: SCRIPT_VERSION },
|
|
466
|
-
stripSourcesContent,
|
|
467
|
-
stripSourcePrefixCount: stripSourcePrefixes?.length ?? 0,
|
|
468
|
-
writeApplied: Boolean(write && status === "ready"),
|
|
469
|
-
artifacts,
|
|
470
|
-
validation: { status, errors, warnings }
|
|
471
|
-
};
|
|
51
|
+
/**
|
|
52
|
+
* @param {CliOptions} options
|
|
53
|
+
* @param {string} name
|
|
54
|
+
* @returns {string | undefined}
|
|
55
|
+
*/
|
|
56
|
+
function optionalOption(options, name) {
|
|
57
|
+
const value = options[name];
|
|
58
|
+
return typeof value === "string" ? value : undefined;
|
|
472
59
|
}
|
|
473
60
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
if (typeof payload.mappings !== "string" || payload.mappings === "") {
|
|
484
|
-
errors.push("source map mappings must be a non-empty string");
|
|
485
|
-
}
|
|
486
|
-
if ("sourcesContent" in payload) {
|
|
487
|
-
if (allowSourcesContent) {
|
|
488
|
-
warnings.push("source map contains sourcesContent; ensure app policy permits source upload");
|
|
489
|
-
} else {
|
|
490
|
-
errors.push("source map contains sourcesContent; rerun with --allow-sources-content only if policy permits it");
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return [errors, warnings];
|
|
61
|
+
/**
|
|
62
|
+
* @param {CliOptions} options
|
|
63
|
+
* @param {string} name
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
function hasOption(options, name) {
|
|
67
|
+
const value = options[name];
|
|
68
|
+
return typeof value === "string" && value.trim() !== "";
|
|
494
69
|
}
|
|
495
70
|
|
|
496
|
-
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} value
|
|
73
|
+
* @returns {number}
|
|
74
|
+
*/
|
|
75
|
+
function parseContextLines(value) {
|
|
497
76
|
const trimmed = value.trim();
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
const normalizedPath = path.posix.normalize(parsed.pathname || "/").replace(/\/+$/u, "") || "/";
|
|
501
|
-
return parsed.host
|
|
502
|
-
? `${parsed.protocol}//${parsed.host}${normalizedPath}`
|
|
503
|
-
: `${parsed.protocol}//${normalizedPath}`;
|
|
504
|
-
}
|
|
505
|
-
return trimmed.split("?", 1)[0].split("#", 1)[0].trim().replace(/\/+$/u, "");
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function joinUrlOrPath(prefix, relativePath) {
|
|
509
|
-
const normalizedPrefix = normalizeUrlOrPath(prefix);
|
|
510
|
-
const normalizedRelative = relativePath.replaceAll("\\", "/").replace(/^\/+/u, "");
|
|
511
|
-
if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(normalizedPrefix)) {
|
|
512
|
-
const parsed = new URL(normalizedPrefix);
|
|
513
|
-
const joinedPath = path.posix.join(parsed.pathname.replace(/\/+$/u, ""), normalizedRelative);
|
|
514
|
-
const finalPath = joinedPath.startsWith("/") ? joinedPath : `/${joinedPath}`;
|
|
515
|
-
return parsed.host
|
|
516
|
-
? `${parsed.protocol}//${parsed.host}${finalPath}`
|
|
517
|
-
: `${parsed.protocol}//${finalPath}`;
|
|
518
|
-
}
|
|
519
|
-
return normalizedPrefix ? `${normalizedPrefix.replace(/\/+$/u, "")}/${normalizedRelative}` : normalizedRelative;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function buildManifestArtifact(jsPath, buildDir, minifiedPathPrefix, allowSourcesContent) {
|
|
523
|
-
const {
|
|
524
|
-
errors,
|
|
525
|
-
warnings,
|
|
526
|
-
relJs,
|
|
527
|
-
jsSize,
|
|
528
|
-
jsDebugId,
|
|
529
|
-
sourceMappingUrl,
|
|
530
|
-
sourceMapPath,
|
|
531
|
-
sourceMapPayload,
|
|
532
|
-
sourceMapRel,
|
|
533
|
-
sourceMapSize,
|
|
534
|
-
mapDebugId
|
|
535
|
-
} = inspectArtifactFiles(jsPath, buildDir);
|
|
536
|
-
let sourceMapEntry = null;
|
|
537
|
-
if (sourceMapPayload && sourceMapPath && sourceMapRel) {
|
|
538
|
-
const [payloadErrors, payloadWarnings] = validateSourceMapPayload(sourceMapPayload, allowSourcesContent);
|
|
539
|
-
errors.push(...payloadErrors);
|
|
540
|
-
warnings.push(...payloadWarnings);
|
|
541
|
-
sourceMapEntry = {
|
|
542
|
-
path: sourceMapRel,
|
|
543
|
-
artifactSha256: sha256File(sourceMapPath),
|
|
544
|
-
byteSize: sourceMapSize,
|
|
545
|
-
sourceCount: Array.isArray(sourceMapPayload.sources) ? sourceMapPayload.sources.length : 0,
|
|
546
|
-
hasSourcesContent: "sourcesContent" in sourceMapPayload,
|
|
547
|
-
...(mapDebugId ? { debugId: mapDebugId } : {})
|
|
548
|
-
};
|
|
77
|
+
if (!NON_NEGATIVE_INTEGER_RE.test(trimmed)) {
|
|
78
|
+
throw new Error("--context-lines must be an integer from 0 to 10");
|
|
549
79
|
}
|
|
550
|
-
|
|
551
|
-
|
|
80
|
+
const contextLines = Number.parseInt(trimmed, DECIMAL);
|
|
81
|
+
if (contextLines < MIN_CONTEXT_LINES || contextLines > MAX_CONTEXT_LINES) {
|
|
82
|
+
throw new Error("--context-lines must be an integer from 0 to 10");
|
|
552
83
|
}
|
|
553
|
-
|
|
554
|
-
const debugId = jsDebugId || mapDebugId;
|
|
555
|
-
return {
|
|
556
|
-
artifactType: "javascript_source_map",
|
|
557
|
-
...(debugId ? { debugId } : {}),
|
|
558
|
-
minifiedSource: {
|
|
559
|
-
path: relJs,
|
|
560
|
-
minifiedUrl: joinUrlOrPath(minifiedPathPrefix, relJs),
|
|
561
|
-
artifactSha256: sha256File(jsPath),
|
|
562
|
-
byteSize: jsSize,
|
|
563
|
-
...(jsDebugId ? { debugId: jsDebugId } : {}),
|
|
564
|
-
...(sourceMappingUrl ? { sourceMappingUrl } : {})
|
|
565
|
-
},
|
|
566
|
-
sourceMap: sourceMapEntry,
|
|
567
|
-
validation: {
|
|
568
|
-
status: errors.length > 0 ? "blocked" : "ready",
|
|
569
|
-
errors,
|
|
570
|
-
warnings
|
|
571
|
-
}
|
|
572
|
-
};
|
|
84
|
+
return contextLines;
|
|
573
85
|
}
|
|
574
86
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
warnings.push(...artifact.validation.warnings.map((message) => `${relPath}: ${message}`));
|
|
586
|
-
}
|
|
587
|
-
const git = {};
|
|
588
|
-
if (repositoryUrl) {
|
|
589
|
-
git.repositoryUrl = repositoryUrl.trim();
|
|
87
|
+
/**
|
|
88
|
+
* @param {CliOptions} options
|
|
89
|
+
* @returns {SourceContextOptions | undefined}
|
|
90
|
+
*/
|
|
91
|
+
function optionalSourceContextOptions(options) {
|
|
92
|
+
const hasSourceRoot = hasOption(options, "source-root");
|
|
93
|
+
const contextLinesOption = optionalOption(options, "context-lines");
|
|
94
|
+
const hasContextLines = hasOption(options, "context-lines");
|
|
95
|
+
if (!hasSourceRoot && !hasContextLines) {
|
|
96
|
+
return undefined;
|
|
590
97
|
}
|
|
591
|
-
if (
|
|
592
|
-
|
|
98
|
+
if (!hasSourceRoot) {
|
|
99
|
+
throw new Error("--source-root is required when --context-lines is provided");
|
|
593
100
|
}
|
|
594
101
|
return {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
release,
|
|
598
|
-
environment,
|
|
599
|
-
service,
|
|
600
|
-
artifactType: "javascript_source_map_manifest",
|
|
601
|
-
minifiedPathPrefix: normalizedPrefix,
|
|
602
|
-
uploader: { name: "logbrew-js-release-artifact-manifest", version: SCRIPT_VERSION },
|
|
603
|
-
...(Object.keys(git).length > 0 ? { git } : {}),
|
|
604
|
-
artifacts,
|
|
605
|
-
validation: {
|
|
606
|
-
status: errors.length > 0 ? "blocked" : "ready",
|
|
607
|
-
errors,
|
|
608
|
-
warnings
|
|
609
|
-
}
|
|
102
|
+
contextLines: hasContextLines ? parseContextLines(contextLinesOption ?? "") : DEFAULT_CONTEXT_LINES,
|
|
103
|
+
sourceRoot: path.resolve(requireOption(options, "source-root"))
|
|
610
104
|
};
|
|
611
105
|
}
|
|
612
106
|
|
|
613
|
-
|
|
107
|
+
/**
|
|
108
|
+
* @param {readonly string[]} args
|
|
109
|
+
* @returns {Promise<number>}
|
|
110
|
+
*/
|
|
111
|
+
async function runPrepareJs(args) {
|
|
614
112
|
const options = parseOptions(args, {
|
|
615
113
|
"build-dir": "string",
|
|
616
|
-
|
|
114
|
+
"strip-source-prefix": "repeat",
|
|
617
115
|
"strip-sources-content": "boolean",
|
|
618
|
-
|
|
116
|
+
write: "boolean"
|
|
619
117
|
});
|
|
620
|
-
const buildDir = requireBuildDir(requireOption(options, "build-dir"));
|
|
621
|
-
const
|
|
118
|
+
const buildDir = await requireBuildDir(requireOption(options, "build-dir"));
|
|
119
|
+
const stripSourcePrefixes = options["strip-source-prefix"];
|
|
120
|
+
const plan = await createDebugIdPlan({
|
|
622
121
|
buildDir,
|
|
623
|
-
|
|
624
|
-
stripSourcesContent:
|
|
625
|
-
|
|
122
|
+
stripSourcePrefixes: Array.isArray(stripSourcePrefixes) ? stripSourcePrefixes : [],
|
|
123
|
+
stripSourcesContent: options["strip-sources-content"] === true,
|
|
124
|
+
write: options["write"] === true
|
|
626
125
|
});
|
|
627
126
|
printJson(plan);
|
|
628
|
-
return plan.validation.status === "blocked" ?
|
|
127
|
+
return plan.validation.status === "blocked" ? EXIT_BLOCKED : EXIT_OK;
|
|
629
128
|
}
|
|
630
129
|
|
|
631
|
-
|
|
130
|
+
/**
|
|
131
|
+
* @param {readonly string[]} args
|
|
132
|
+
* @returns {Promise<number>}
|
|
133
|
+
*/
|
|
134
|
+
async function runManifestJs(args) {
|
|
632
135
|
const options = parseOptions(args, {
|
|
136
|
+
"allow-sources-content": "boolean",
|
|
633
137
|
"build-dir": "string",
|
|
634
|
-
"
|
|
635
|
-
release: "string",
|
|
138
|
+
"commit-sha": "string",
|
|
636
139
|
environment: "string",
|
|
637
|
-
service: "string",
|
|
638
140
|
"minified-path-prefix": "string",
|
|
141
|
+
"project-id": "string",
|
|
142
|
+
release: "string",
|
|
639
143
|
"repository-url": "string",
|
|
640
|
-
|
|
641
|
-
"allow-sources-content": "boolean"
|
|
144
|
+
service: "string"
|
|
642
145
|
});
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
allowSourcesContent:
|
|
651
|
-
|
|
652
|
-
commitSha: options
|
|
146
|
+
const buildDir = await requireBuildDir(requireOption(options, "build-dir"));
|
|
147
|
+
const projectId = normalizeProjectId(options["project-id"], "--project-id must be a UUID");
|
|
148
|
+
const release = requireOption(options, "release");
|
|
149
|
+
const environment = requireOption(options, "environment");
|
|
150
|
+
const service = requireOption(options, "service");
|
|
151
|
+
const minifiedPathPrefix = requireOption(options, "minified-path-prefix");
|
|
152
|
+
const manifest = await createManifest({
|
|
153
|
+
allowSourcesContent: options["allow-sources-content"] === true,
|
|
154
|
+
buildDir,
|
|
155
|
+
commitSha: optionalOption(options, "commit-sha"),
|
|
156
|
+
environment,
|
|
157
|
+
minifiedPathPrefix,
|
|
158
|
+
projectId,
|
|
159
|
+
release,
|
|
160
|
+
repositoryUrl: optionalOption(options, "repository-url"),
|
|
161
|
+
service
|
|
653
162
|
});
|
|
654
163
|
printJson(manifest);
|
|
655
|
-
return manifest.validation.status === "blocked" ?
|
|
164
|
+
return manifest.validation.status === "blocked" ? EXIT_BLOCKED : EXIT_OK;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {CliOptions} options
|
|
169
|
+
* @param {string} name
|
|
170
|
+
* @param {string} label
|
|
171
|
+
* @returns {Promise<string>}
|
|
172
|
+
*/
|
|
173
|
+
async function requireExistingFile(options, name, label) {
|
|
174
|
+
const filePath = path.resolve(requireOption(options, name));
|
|
175
|
+
if (!(await pathExists(filePath))) {
|
|
176
|
+
throw new Error(`${label} file does not exist: ${String(options[name])}`);
|
|
177
|
+
}
|
|
178
|
+
return filePath;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {CliOptions} options
|
|
183
|
+
* @returns {Promise<number>}
|
|
184
|
+
*/
|
|
185
|
+
async function symbolicateFromOptions(options) {
|
|
186
|
+
const buildDir = await requireBuildDir(requireOption(options, "build-dir"));
|
|
187
|
+
const manifestPath = await requireExistingFile(options, "manifest", "manifest");
|
|
188
|
+
const issueEvent = hasOption(options, "issue-event");
|
|
189
|
+
if (hasOption(options, "stack-frame") === issueEvent) {
|
|
190
|
+
throw new Error("provide exactly one of --stack-frame or --issue-event");
|
|
191
|
+
}
|
|
192
|
+
const manifest = await readJsonObject(manifestPath, "manifest");
|
|
193
|
+
const sourceContext = optionalSourceContextOptions(options);
|
|
194
|
+
const report = issueEvent
|
|
195
|
+
? await verifyJavaScriptIssueSymbolication({
|
|
196
|
+
buildDir,
|
|
197
|
+
issueEvent: await readJsonObject(await requireExistingFile(options, "issue-event", "issue event"), "issue event"),
|
|
198
|
+
manifest,
|
|
199
|
+
sourceContext
|
|
200
|
+
})
|
|
201
|
+
: await verifyJavaScriptSymbolication({ buildDir, manifest, sourceContext, stackFrame: requireOption(options, "stack-frame") });
|
|
202
|
+
printJson(report);
|
|
203
|
+
return EXIT_OK;
|
|
656
204
|
}
|
|
657
205
|
|
|
658
|
-
|
|
206
|
+
/**
|
|
207
|
+
* @param {readonly string[]} args
|
|
208
|
+
* @returns {Promise<number>}
|
|
209
|
+
*/
|
|
210
|
+
async function runSymbolicateJs(args) {
|
|
659
211
|
const options = parseOptions(args, {
|
|
660
212
|
"build-dir": "string",
|
|
661
|
-
|
|
662
|
-
"stack-frame": "string",
|
|
213
|
+
"context-lines": "string",
|
|
663
214
|
"issue-event": "string",
|
|
215
|
+
manifest: "string",
|
|
664
216
|
"source-root": "string",
|
|
665
|
-
"
|
|
217
|
+
"stack-frame": "string"
|
|
666
218
|
});
|
|
667
219
|
try {
|
|
668
|
-
|
|
669
|
-
const manifestPath = path.resolve(requireOption(options, "manifest"));
|
|
670
|
-
if (!fs.existsSync(manifestPath)) {
|
|
671
|
-
throw new Error(`manifest file does not exist: ${options.manifest}`);
|
|
672
|
-
}
|
|
673
|
-
const stackFrame = typeof options["stack-frame"] === "string" && options["stack-frame"].trim() !== "";
|
|
674
|
-
const issueEvent = typeof options["issue-event"] === "string" && options["issue-event"].trim() !== "";
|
|
675
|
-
if (stackFrame === issueEvent) {
|
|
676
|
-
throw new Error("provide exactly one of --stack-frame or --issue-event");
|
|
677
|
-
}
|
|
678
|
-
const manifest = readJsonObject(manifestPath, "manifest");
|
|
679
|
-
const sourceContext = optionalSourceContextOptions(options);
|
|
680
|
-
if (issueEvent) {
|
|
681
|
-
const issueEventPath = path.resolve(requireOption(options, "issue-event"));
|
|
682
|
-
if (!fs.existsSync(issueEventPath)) {
|
|
683
|
-
throw new Error(`issue event file does not exist: ${options["issue-event"]}`);
|
|
684
|
-
}
|
|
685
|
-
const report = verifyJavaScriptIssueSymbolication({
|
|
686
|
-
buildDir,
|
|
687
|
-
manifest,
|
|
688
|
-
issueEvent: readJsonObject(issueEventPath, "issue event"),
|
|
689
|
-
sourceContext
|
|
690
|
-
});
|
|
691
|
-
printJson(report);
|
|
692
|
-
return 0;
|
|
693
|
-
}
|
|
694
|
-
const report = verifyJavaScriptSymbolication({
|
|
695
|
-
buildDir,
|
|
696
|
-
manifest,
|
|
697
|
-
stackFrame: requireOption(options, "stack-frame"),
|
|
698
|
-
sourceContext
|
|
699
|
-
});
|
|
700
|
-
printJson(report);
|
|
701
|
-
return 0;
|
|
220
|
+
return await symbolicateFromOptions(options);
|
|
702
221
|
} catch (error) {
|
|
703
|
-
printJson({
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
222
|
+
printJson({ status: "validation_failed", validation: { errors: [errorMessage(error)] }, verifier: VERIFIER });
|
|
223
|
+
return EXIT_BLOCKED;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const COMMANDS = new Map([
|
|
228
|
+
["manifest-js", runManifestJs],
|
|
229
|
+
["prepare-js", runPrepareJs],
|
|
230
|
+
["symbolicate-js", runSymbolicateJs],
|
|
231
|
+
["upload-js", runUploadJs]
|
|
232
|
+
]);
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @param {string} command
|
|
236
|
+
* @param {readonly string[]} args
|
|
237
|
+
* @returns {Promise<number>}
|
|
238
|
+
*/
|
|
239
|
+
async function runCommand(command, args) {
|
|
240
|
+
const run = COMMANDS.get(command);
|
|
241
|
+
if (run === undefined) {
|
|
242
|
+
throw new Error(`unknown command: ${command}`);
|
|
709
243
|
}
|
|
244
|
+
return run(args);
|
|
710
245
|
}
|
|
711
246
|
|
|
247
|
+
/**
|
|
248
|
+
* @param {readonly string[]} argv
|
|
249
|
+
* @returns {Promise<number>}
|
|
250
|
+
*/
|
|
712
251
|
async function main(argv) {
|
|
713
|
-
const [command, ...args] = argv;
|
|
714
|
-
if (
|
|
252
|
+
const [command = "", ...args] = argv;
|
|
253
|
+
if (command === "" || command === "--help" || command === "-h") {
|
|
715
254
|
process.stdout.write(`${usage()}\n`);
|
|
716
|
-
return command ?
|
|
255
|
+
return command === "" ? EXIT_BLOCKED : EXIT_OK;
|
|
717
256
|
}
|
|
718
257
|
try {
|
|
719
|
-
|
|
720
|
-
return runPrepareJs(args);
|
|
721
|
-
}
|
|
722
|
-
if (command === "manifest-js") {
|
|
723
|
-
return runManifestJs(args);
|
|
724
|
-
}
|
|
725
|
-
if (command === "symbolicate-js") {
|
|
726
|
-
return runSymbolicateJs(args);
|
|
727
|
-
}
|
|
728
|
-
if (command === "upload-js") {
|
|
729
|
-
return await runUploadJs(args);
|
|
730
|
-
}
|
|
731
|
-
throw new Error(`unknown command: ${command}`);
|
|
258
|
+
return await runCommand(command, args);
|
|
732
259
|
} catch (error) {
|
|
733
|
-
process.stderr.write(`${error
|
|
734
|
-
return
|
|
260
|
+
process.stderr.write(`${errorMessage(error)}\n\n${usage()}\n`);
|
|
261
|
+
return EXIT_USAGE;
|
|
735
262
|
}
|
|
736
263
|
}
|
|
737
264
|
|
|
738
|
-
process.exitCode = await main(process.argv.slice(
|
|
265
|
+
process.exitCode = await main(process.argv.slice(ARGUMENT_OFFSET));
|