@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
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
byteSize,
|
|
3
|
+
errorMessage,
|
|
4
|
+
fileReference,
|
|
5
|
+
isJsonObject,
|
|
6
|
+
mapInOrder,
|
|
7
|
+
pathExists,
|
|
8
|
+
readText,
|
|
9
|
+
relativeTo,
|
|
10
|
+
safeResolve,
|
|
11
|
+
sourceMapDebugId
|
|
12
|
+
} from "./release-artifacts-common.js";
|
|
13
|
+
import { findDebugId, findSourceMappingUrl } from "./release-artifacts-debug-id.js";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { readdir } from "node:fs/promises";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @import { JsonObject } from "./release-artifacts-common.js"
|
|
19
|
+
* @typedef {{ readonly entryPath: string, readonly isDirectory: boolean, readonly isFile: boolean }} WalkEntry
|
|
20
|
+
* @typedef {{ readonly errors: readonly string[], readonly payload?: JsonObject }} SourceMapRead
|
|
21
|
+
* @typedef {{ readonly errors: readonly string[], readonly path?: string, readonly warnings: readonly string[] }} SourceMapLocation
|
|
22
|
+
* @typedef {{
|
|
23
|
+
* readonly errors: readonly string[],
|
|
24
|
+
* readonly mapDebugId?: string,
|
|
25
|
+
* readonly payload?: JsonObject,
|
|
26
|
+
* readonly relativePath?: string,
|
|
27
|
+
* readonly size?: number
|
|
28
|
+
* }} SourceMapInspection
|
|
29
|
+
* @typedef {{
|
|
30
|
+
* readonly errors: readonly string[],
|
|
31
|
+
* readonly jsDebugId: string | undefined,
|
|
32
|
+
* readonly jsSize: number,
|
|
33
|
+
* readonly jsSource: string,
|
|
34
|
+
* readonly mapDebugId: string | undefined,
|
|
35
|
+
* readonly relJs: string,
|
|
36
|
+
* readonly sourceMapPath: string | undefined,
|
|
37
|
+
* readonly sourceMapPayload: JsonObject | undefined,
|
|
38
|
+
* readonly sourceMapRel: string | undefined,
|
|
39
|
+
* readonly sourceMapSize: number | undefined,
|
|
40
|
+
* readonly sourceMappingUrl: string | undefined,
|
|
41
|
+
* readonly warnings: readonly string[]
|
|
42
|
+
* }} ArtifactInspection
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
const MINIFIED_SOURCE_SUFFIXES = [".js", ".mjs", ".bundle", ".jsbundle"];
|
|
46
|
+
const URL_WITH_SCHEME_RE = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//u;
|
|
47
|
+
const EMPTY_FILE_SIZE = 0;
|
|
48
|
+
const ROOT_PREFIX_LENGTH = 1;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} filePath
|
|
52
|
+
* @returns {Promise<SourceMapRead>}
|
|
53
|
+
*/
|
|
54
|
+
async function readSourceMap(filePath) {
|
|
55
|
+
try {
|
|
56
|
+
/** @type {unknown} */
|
|
57
|
+
const payload = JSON.parse(await readText(filePath));
|
|
58
|
+
return isJsonObject(payload) ? { errors: [], payload } : { errors: ["source map must be a JSON object"] };
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return { errors: [`source map is not valid JSON: ${errorMessage(error)}`] };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {string} jsPath
|
|
66
|
+
* @param {string} buildDir
|
|
67
|
+
* @param {string} reference
|
|
68
|
+
* @returns {Promise<SourceMapLocation>}
|
|
69
|
+
*/
|
|
70
|
+
async function resolveReferencedSourceMap(jsPath, buildDir, reference) {
|
|
71
|
+
const candidate = reference.startsWith("/")
|
|
72
|
+
? path.join(buildDir, reference.slice(ROOT_PREFIX_LENGTH))
|
|
73
|
+
: path.join(path.dirname(jsPath), reference);
|
|
74
|
+
const resolved = await safeResolve(candidate, buildDir);
|
|
75
|
+
if (resolved === undefined) {
|
|
76
|
+
return { errors: ["sourceMappingURL resolves outside the build directory"], warnings: [] };
|
|
77
|
+
}
|
|
78
|
+
return { errors: [], path: resolved, warnings: [] };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} jsPath
|
|
83
|
+
* @param {string} buildDir
|
|
84
|
+
* @param {string | undefined} sourceMappingUrl
|
|
85
|
+
* @returns {Promise<SourceMapLocation>}
|
|
86
|
+
*/
|
|
87
|
+
async function resolveSourceMapPath(jsPath, buildDir, sourceMappingUrl) {
|
|
88
|
+
if (sourceMappingUrl === undefined) {
|
|
89
|
+
const fallback = `${jsPath}.map`;
|
|
90
|
+
const warnings = ["sourceMappingURL comment missing; checked sibling .map fallback"];
|
|
91
|
+
return (await pathExists(fallback)) ? { errors: [], path: fallback, warnings } : { errors: [], warnings };
|
|
92
|
+
}
|
|
93
|
+
const reference = fileReference(sourceMappingUrl);
|
|
94
|
+
if (reference.startsWith("data:")) {
|
|
95
|
+
return { errors: ["inline source maps are not accepted for release artifact manifests"], warnings: [] };
|
|
96
|
+
}
|
|
97
|
+
if (URL_WITH_SCHEME_RE.test(reference)) {
|
|
98
|
+
return { errors: ["external sourceMappingURL cannot be validated from the local build directory"], warnings: [] };
|
|
99
|
+
}
|
|
100
|
+
return resolveReferencedSourceMap(jsPath, buildDir, reference);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {string} filePath
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
function isMinifiedSource(filePath) {
|
|
108
|
+
return !filePath.endsWith(".map") && MINIFIED_SOURCE_SUFFIXES.some((suffix) => filePath.endsWith(suffix));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {string} root
|
|
113
|
+
* @returns {Promise<string[]>}
|
|
114
|
+
*/
|
|
115
|
+
async function walkFiles(root) {
|
|
116
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
117
|
+
/** @type {WalkEntry[]} */
|
|
118
|
+
const descriptors = [];
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
descriptors.push({ entryPath: path.join(root, entry.name), isDirectory: entry.isDirectory(), isFile: entry.isFile() });
|
|
121
|
+
}
|
|
122
|
+
const nested = await mapInOrder(descriptors, async ({ entryPath, isDirectory, isFile }) => {
|
|
123
|
+
if (isDirectory) {
|
|
124
|
+
return walkFiles(entryPath);
|
|
125
|
+
}
|
|
126
|
+
return isFile ? [entryPath] : [];
|
|
127
|
+
});
|
|
128
|
+
const results = nested.flat();
|
|
129
|
+
results.sort();
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} buildDir
|
|
135
|
+
* @returns {Promise<string[]>}
|
|
136
|
+
*/
|
|
137
|
+
async function iterMinifiedSourceFiles(buildDir) {
|
|
138
|
+
const files = await walkFiles(buildDir);
|
|
139
|
+
return files.filter((filePath) => isMinifiedSource(filePath));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {string} sourceMapPath
|
|
144
|
+
* @param {string} relativePath
|
|
145
|
+
* @param {number} size
|
|
146
|
+
* @returns {Promise<SourceMapInspection>}
|
|
147
|
+
*/
|
|
148
|
+
async function inspectSourceMapPayload(sourceMapPath, relativePath, size) {
|
|
149
|
+
const { errors, payload } = await readSourceMap(sourceMapPath);
|
|
150
|
+
if (payload === undefined) {
|
|
151
|
+
return { errors, relativePath, size };
|
|
152
|
+
}
|
|
153
|
+
const mapDebugId = sourceMapDebugId(payload);
|
|
154
|
+
return mapDebugId === undefined
|
|
155
|
+
? { errors, payload, relativePath, size }
|
|
156
|
+
: { errors, mapDebugId, payload, relativePath, size };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string | undefined} sourceMapPath
|
|
161
|
+
* @param {string} buildDir
|
|
162
|
+
* @returns {Promise<SourceMapInspection>}
|
|
163
|
+
*/
|
|
164
|
+
async function inspectSourceMap(sourceMapPath, buildDir) {
|
|
165
|
+
if (sourceMapPath === undefined) {
|
|
166
|
+
return { errors: ["source map file is missing"] };
|
|
167
|
+
}
|
|
168
|
+
if (!(await pathExists(sourceMapPath))) {
|
|
169
|
+
return { errors: [`source map file is missing: ${relativeTo(buildDir, sourceMapPath)}`] };
|
|
170
|
+
}
|
|
171
|
+
const size = await byteSize(sourceMapPath);
|
|
172
|
+
if (size === EMPTY_FILE_SIZE) {
|
|
173
|
+
return { errors: [`source map file is empty: ${relativeTo(buildDir, sourceMapPath)}`] };
|
|
174
|
+
}
|
|
175
|
+
return inspectSourceMapPayload(sourceMapPath, relativeTo(buildDir, sourceMapPath), size);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {string} jsPath
|
|
180
|
+
* @param {string} buildDir
|
|
181
|
+
* @returns {Promise<ArtifactInspection>}
|
|
182
|
+
*/
|
|
183
|
+
async function inspectArtifactFiles(jsPath, buildDir) {
|
|
184
|
+
const jsSize = await byteSize(jsPath);
|
|
185
|
+
const jsSource = await readText(jsPath);
|
|
186
|
+
const jsDebugId = findDebugId(jsSource);
|
|
187
|
+
const sourceMappingUrl = findSourceMappingUrl(jsSource);
|
|
188
|
+
const location = await resolveSourceMapPath(jsPath, buildDir, sourceMappingUrl);
|
|
189
|
+
const sourceMap = await inspectSourceMap(location.path, buildDir);
|
|
190
|
+
const { mapDebugId } = sourceMap;
|
|
191
|
+
const mismatch = jsDebugId !== undefined && mapDebugId !== undefined && jsDebugId !== mapDebugId;
|
|
192
|
+
return {
|
|
193
|
+
errors: [
|
|
194
|
+
...(jsSize === EMPTY_FILE_SIZE ? ["minified source file is empty"] : []),
|
|
195
|
+
...location.errors,
|
|
196
|
+
...sourceMap.errors,
|
|
197
|
+
...(mismatch ? ["minified source debugId does not match source map debugId"] : [])
|
|
198
|
+
],
|
|
199
|
+
jsDebugId,
|
|
200
|
+
jsSize,
|
|
201
|
+
jsSource,
|
|
202
|
+
mapDebugId,
|
|
203
|
+
relJs: relativeTo(buildDir, jsPath),
|
|
204
|
+
sourceMapPath: location.path,
|
|
205
|
+
sourceMapPayload: sourceMap.payload,
|
|
206
|
+
sourceMapRel: sourceMap.relativePath,
|
|
207
|
+
sourceMapSize: sourceMap.size,
|
|
208
|
+
sourceMappingUrl,
|
|
209
|
+
warnings: location.warnings
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export { inspectArtifactFiles, iterMinifiedSourceFiles, readSourceMap };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { isJsonObject } from "./release-artifacts-common.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @import { JsonObject } from "./release-artifacts-common.js"
|
|
5
|
+
* @typedef {{ readonly issueId?: string, readonly metadataSource: string, readonly type: string }} IssueEventInput
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const FIRST_LINE = 1;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {unknown} value
|
|
12
|
+
* @returns {string | undefined}
|
|
13
|
+
*/
|
|
14
|
+
function trimmedString(value) {
|
|
15
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {JsonObject} issueEvent
|
|
20
|
+
* @returns {{ readonly input: IssueEventInput, readonly metadata: JsonObject }}
|
|
21
|
+
*/
|
|
22
|
+
function metadataFromIssueEvent(issueEvent) {
|
|
23
|
+
const { attributes, metadata } = issueEvent;
|
|
24
|
+
const attributesMetadata = isJsonObject(attributes) ? attributes["metadata"] : undefined;
|
|
25
|
+
if (isJsonObject(attributesMetadata)) {
|
|
26
|
+
const issueId = trimmedString(issueEvent["id"]);
|
|
27
|
+
return {
|
|
28
|
+
input: {
|
|
29
|
+
...(issueId === undefined ? {} : { issueId }),
|
|
30
|
+
metadataSource: "attributes.metadata",
|
|
31
|
+
type: "sdk_issue_event"
|
|
32
|
+
},
|
|
33
|
+
metadata: attributesMetadata
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (isJsonObject(metadata)) {
|
|
37
|
+
return { input: { metadataSource: "metadata", type: "issue_attributes" }, metadata };
|
|
38
|
+
}
|
|
39
|
+
throw new Error("issue event must contain attributes.metadata or metadata");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {JsonObject} metadata
|
|
44
|
+
* @param {string} name
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
function requireMetadataString(metadata, name) {
|
|
48
|
+
const value = trimmedString(metadata[name]);
|
|
49
|
+
if (value === undefined) {
|
|
50
|
+
throw new Error(`issue event metadata is missing ${name}`);
|
|
51
|
+
}
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {JsonObject} metadata
|
|
57
|
+
* @param {string} name
|
|
58
|
+
* @returns {number}
|
|
59
|
+
*/
|
|
60
|
+
function requireMetadataPositiveInteger(metadata, name) {
|
|
61
|
+
const value = metadata[name];
|
|
62
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < FIRST_LINE) {
|
|
63
|
+
throw new Error(`issue event metadata ${name} must be a one-based positive integer`);
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {JsonObject} metadata
|
|
70
|
+
* @param {JsonObject} manifest
|
|
71
|
+
* @returns {void}
|
|
72
|
+
*/
|
|
73
|
+
function requireMetadataMatchesManifest(metadata, manifest) {
|
|
74
|
+
for (const name of ["release", "environment", "service"]) {
|
|
75
|
+
if (manifest[name] !== requireMetadataString(metadata, name)) {
|
|
76
|
+
throw new Error(`issue event metadata ${name} does not match the manifest`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {JsonObject} metadata
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
function stackFrameFromIssueMetadata(metadata) {
|
|
86
|
+
const releaseArtifactType = requireMetadataString(metadata, "releaseArtifactType");
|
|
87
|
+
if (releaseArtifactType !== "sourcemap") {
|
|
88
|
+
throw new Error("issue event releaseArtifactType must be sourcemap");
|
|
89
|
+
}
|
|
90
|
+
const codeFile = trimmedString(metadata["releaseArtifactCodeFile"]) ?? trimmedString(metadata["errorFrameFile"]);
|
|
91
|
+
if (codeFile === undefined) {
|
|
92
|
+
throw new Error("issue event metadata is missing releaseArtifactCodeFile");
|
|
93
|
+
}
|
|
94
|
+
const line = requireMetadataPositiveInteger(metadata, "errorFrameLine");
|
|
95
|
+
const column = requireMetadataPositiveInteger(metadata, "errorFrameColumn");
|
|
96
|
+
return `${codeFile}:${String(line)}:${String(column)}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { metadataFromIssueEvent, requireMetadataMatchesManifest, requireMetadataString, stackFrameFromIssueMetadata };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { inspectArtifactFiles } from "./release-artifacts-inspect.js";
|
|
2
|
+
import { joinUrlOrPath } from "./release-artifacts-urls.js";
|
|
3
|
+
import { sha256File } from "./release-artifacts-common.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @import { JsonObject } from "./release-artifacts-common.js"
|
|
7
|
+
* @import { ArtifactInspection } from "./release-artifacts-inspect.js"
|
|
8
|
+
* @typedef {{ readonly errors: readonly string[], readonly status: "blocked" | "ready", readonly warnings: readonly string[] }} Validation
|
|
9
|
+
* @typedef {{
|
|
10
|
+
* readonly allowSourcesContent: boolean,
|
|
11
|
+
* readonly buildDir: string,
|
|
12
|
+
* readonly minifiedPathPrefix: string
|
|
13
|
+
* }} ManifestArtifactOptions
|
|
14
|
+
* @typedef {{
|
|
15
|
+
* readonly minifiedSource: JsonObject & { readonly path: string },
|
|
16
|
+
* readonly validation: Validation
|
|
17
|
+
* }} ManifestArtifact
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const NO_ITEMS = 0;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {JsonObject} payload
|
|
24
|
+
* @param {boolean} allowSourcesContent
|
|
25
|
+
* @returns {{ readonly errors: readonly string[], readonly warnings: readonly string[] }}
|
|
26
|
+
*/
|
|
27
|
+
function validateSourceMapPayload(payload, allowSourcesContent) {
|
|
28
|
+
const { mappings, sources, version } = payload;
|
|
29
|
+
const hasSourcesContent = "sourcesContent" in payload;
|
|
30
|
+
return {
|
|
31
|
+
errors: [
|
|
32
|
+
...(version === undefined ? ["source map version is required"] : []),
|
|
33
|
+
...(!Array.isArray(sources) || sources.length === NO_ITEMS ? ["source map sources must be a non-empty array"] : []),
|
|
34
|
+
...(typeof mappings !== "string" || mappings === "" ? ["source map mappings must be a non-empty string"] : []),
|
|
35
|
+
...(hasSourcesContent && !allowSourcesContent
|
|
36
|
+
? ["source map contains sourcesContent; rerun with --allow-sources-content only if policy permits it"]
|
|
37
|
+
: [])
|
|
38
|
+
],
|
|
39
|
+
warnings:
|
|
40
|
+
hasSourcesContent && allowSourcesContent
|
|
41
|
+
? ["source map contains sourcesContent; ensure app policy permits source upload"]
|
|
42
|
+
: []
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {ArtifactInspection} inspection
|
|
48
|
+
* @param {boolean} allowSourcesContent
|
|
49
|
+
* @returns {Promise<{ readonly entry: JsonObject | undefined, readonly errors: readonly string[], readonly warnings: readonly string[] }>}
|
|
50
|
+
*/
|
|
51
|
+
async function sourceMapEntry(inspection, allowSourcesContent) {
|
|
52
|
+
const { mapDebugId, sourceMapPath, sourceMapPayload, sourceMapRel, sourceMapSize } = inspection;
|
|
53
|
+
if (sourceMapPayload === undefined || sourceMapPath === undefined || sourceMapRel === undefined) {
|
|
54
|
+
return { entry: undefined, errors: [], warnings: [] };
|
|
55
|
+
}
|
|
56
|
+
const { errors, warnings } = validateSourceMapPayload(sourceMapPayload, allowSourcesContent);
|
|
57
|
+
const { sources } = sourceMapPayload;
|
|
58
|
+
return {
|
|
59
|
+
entry: {
|
|
60
|
+
artifactSha256: await sha256File(sourceMapPath),
|
|
61
|
+
byteSize: sourceMapSize,
|
|
62
|
+
...(mapDebugId === undefined ? {} : { debugId: mapDebugId }),
|
|
63
|
+
hasSourcesContent: "sourcesContent" in sourceMapPayload,
|
|
64
|
+
path: sourceMapRel,
|
|
65
|
+
sourceCount: Array.isArray(sources) ? sources.length : NO_ITEMS
|
|
66
|
+
},
|
|
67
|
+
errors,
|
|
68
|
+
warnings
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} jsPath
|
|
74
|
+
* @param {ManifestArtifactOptions} options
|
|
75
|
+
* @returns {Promise<ManifestArtifact & JsonObject>}
|
|
76
|
+
*/
|
|
77
|
+
async function buildManifestArtifact(jsPath, { allowSourcesContent, buildDir, minifiedPathPrefix }) {
|
|
78
|
+
const inspection = await inspectArtifactFiles(jsPath, buildDir);
|
|
79
|
+
const { jsDebugId, jsSize, mapDebugId, relJs, sourceMappingUrl } = inspection;
|
|
80
|
+
const sourceMap = await sourceMapEntry(inspection, allowSourcesContent);
|
|
81
|
+
const errors = [...inspection.errors, ...sourceMap.errors];
|
|
82
|
+
const missingDebugId = jsDebugId === undefined && mapDebugId === undefined;
|
|
83
|
+
const warnings = [
|
|
84
|
+
...inspection.warnings,
|
|
85
|
+
...sourceMap.warnings,
|
|
86
|
+
...(missingDebugId ? ["no debugId found; backend matching must rely on release/environment/service and minified path"] : [])
|
|
87
|
+
];
|
|
88
|
+
const debugId = jsDebugId ?? mapDebugId;
|
|
89
|
+
const minifiedUrl = joinUrlOrPath(minifiedPathPrefix, relJs);
|
|
90
|
+
const artifactSha256 = await sha256File(jsPath);
|
|
91
|
+
return {
|
|
92
|
+
artifactType: "javascript_source_map",
|
|
93
|
+
...(debugId === undefined ? {} : { debugId }),
|
|
94
|
+
minifiedSource: {
|
|
95
|
+
artifactSha256,
|
|
96
|
+
byteSize: jsSize,
|
|
97
|
+
...(jsDebugId === undefined ? {} : { debugId: jsDebugId }),
|
|
98
|
+
minifiedUrl,
|
|
99
|
+
path: relJs,
|
|
100
|
+
...(sourceMappingUrl === undefined ? {} : { sourceMappingUrl })
|
|
101
|
+
},
|
|
102
|
+
sourceMap: sourceMap.entry ?? null,
|
|
103
|
+
validation: { errors, status: errors.length > NO_ITEMS ? "blocked" : "ready", warnings }
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { buildManifestArtifact };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { buildManifestArtifact } from "./release-artifacts-manifest-artifact.js";
|
|
2
|
+
import { iterMinifiedSourceFiles } from "./release-artifacts-inspect.js";
|
|
3
|
+
import { mapInOrder } from "./release-artifacts-common.js";
|
|
4
|
+
import { normalizeUrlOrPath } from "./release-artifacts-urls.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @import { JsonObject } from "./release-artifacts-common.js"
|
|
8
|
+
* @import { ManifestArtifact, Validation } from "./release-artifacts-manifest-artifact.js"
|
|
9
|
+
* @typedef {{
|
|
10
|
+
* readonly allowSourcesContent: boolean,
|
|
11
|
+
* readonly buildDir: string,
|
|
12
|
+
* readonly commitSha: string | undefined,
|
|
13
|
+
* readonly environment: string,
|
|
14
|
+
* readonly minifiedPathPrefix: string,
|
|
15
|
+
* readonly projectId: string | undefined,
|
|
16
|
+
* readonly release: string,
|
|
17
|
+
* readonly repositoryUrl: string | undefined,
|
|
18
|
+
* readonly service: string
|
|
19
|
+
* }} ManifestOptions
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const MANIFEST_VERSION = 1;
|
|
23
|
+
const NO_ITEMS = 0;
|
|
24
|
+
const SCRIPT_VERSION = "0.1.0";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {readonly ManifestArtifact[]} artifacts
|
|
28
|
+
* @param {(artifact: ManifestArtifact) => readonly string[]} messages
|
|
29
|
+
* @returns {string[]}
|
|
30
|
+
*/
|
|
31
|
+
function prefixedMessages(artifacts, messages) {
|
|
32
|
+
return artifacts.flatMap((artifact) => messages(artifact).map((message) => `${artifact.minifiedSource.path}: ${message}`));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {string | undefined} repositoryUrl
|
|
37
|
+
* @param {string | undefined} commitSha
|
|
38
|
+
* @returns {JsonObject}
|
|
39
|
+
*/
|
|
40
|
+
function gitMetadata(repositoryUrl, commitSha) {
|
|
41
|
+
return {
|
|
42
|
+
...(commitSha === undefined || commitSha === "" ? {} : { commitSha: commitSha.trim() }),
|
|
43
|
+
...(repositoryUrl === undefined || repositoryUrl === "" ? {} : { repositoryUrl: repositoryUrl.trim() })
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {ManifestOptions} options
|
|
49
|
+
* @returns {Promise<JsonObject & { readonly validation: Validation }>}
|
|
50
|
+
*/
|
|
51
|
+
async function createManifest(options) {
|
|
52
|
+
const { allowSourcesContent, buildDir, environment, projectId, release, service } = options;
|
|
53
|
+
const minifiedPathPrefix = normalizeUrlOrPath(options.minifiedPathPrefix.trim());
|
|
54
|
+
const artifacts = await mapInOrder(await iterMinifiedSourceFiles(buildDir), async (filePath) =>
|
|
55
|
+
buildManifestArtifact(filePath, { allowSourcesContent, buildDir, minifiedPathPrefix }));
|
|
56
|
+
const errors = [
|
|
57
|
+
...(artifacts.length === NO_ITEMS ? ["no JavaScript release artifact files found in build directory"] : []),
|
|
58
|
+
...prefixedMessages(artifacts, (artifact) => artifact.validation.errors)
|
|
59
|
+
];
|
|
60
|
+
const warnings = prefixedMessages(artifacts, (artifact) => artifact.validation.warnings);
|
|
61
|
+
const git = gitMetadata(options.repositoryUrl, options.commitSha);
|
|
62
|
+
return {
|
|
63
|
+
artifactType: "javascript_source_map_manifest",
|
|
64
|
+
artifacts,
|
|
65
|
+
environment,
|
|
66
|
+
...(Object.keys(git).length > NO_ITEMS ? { git } : {}),
|
|
67
|
+
manifestVersion: MANIFEST_VERSION,
|
|
68
|
+
minifiedPathPrefix,
|
|
69
|
+
...(projectId === undefined ? {} : { projectId }),
|
|
70
|
+
release,
|
|
71
|
+
service,
|
|
72
|
+
uploader: { name: "logbrew-js-release-artifact-manifest", version: SCRIPT_VERSION },
|
|
73
|
+
validation: { errors, status: errors.length > NO_ITEMS ? "blocked" : "ready", warnings }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { createManifest };
|