@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
|
@@ -1,41 +1,145 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { access, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { realpath } from "node:fs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {"boolean" | "repeat" | "string"} OptionKind
|
|
9
|
+
* @typedef {string | true | readonly string[]} OptionValue
|
|
10
|
+
* @typedef {Readonly<Record<string, OptionValue>>} CliOptions
|
|
11
|
+
* @typedef {Readonly<Record<string, OptionKind>>} OptionSpec
|
|
12
|
+
* @typedef {Readonly<Record<string, unknown>>} JsonObject
|
|
13
|
+
* @typedef {{ readonly positional: string, readonly width: number }} PositionalToken
|
|
14
|
+
* @typedef {{ readonly kind: OptionKind, readonly name: string, readonly value: string | true, readonly width: number }} OptionToken
|
|
15
|
+
*/
|
|
4
16
|
|
|
5
17
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
6
|
-
|
|
18
|
+
const SOURCE_MAP_DEBUG_ID_KEYS = ["debug_id", "debugId", "debugID", "x_debug_id"];
|
|
19
|
+
const OPTION_PREFIX = "--";
|
|
20
|
+
const FLAG_WIDTH = 1;
|
|
21
|
+
const VALUE_OPTION_WIDTH = 2;
|
|
22
|
+
const NO_POSITIONALS = 0;
|
|
23
|
+
const FIRST_PART_ONLY = 1;
|
|
24
|
+
const JSON_INDENT = 2;
|
|
25
|
+
const realPathOf = promisify(realpath);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {unknown} value
|
|
29
|
+
* @returns {value is JsonObject}
|
|
30
|
+
*/
|
|
31
|
+
function isJsonObject(value) {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {unknown} value
|
|
37
|
+
* @returns {value is readonly unknown[]}
|
|
38
|
+
*/
|
|
39
|
+
function isJsonList(value) {
|
|
40
|
+
return Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {unknown} error
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
function errorMessage(error) {
|
|
48
|
+
return error instanceof Error ? error.message : String(error);
|
|
49
|
+
}
|
|
7
50
|
|
|
8
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @param {OptionSpec} spec
|
|
53
|
+
* @param {string} name
|
|
54
|
+
* @returns {OptionKind}
|
|
55
|
+
*/
|
|
56
|
+
function optionKind(spec, name) {
|
|
57
|
+
const kind = spec[name];
|
|
58
|
+
if (kind === undefined) {
|
|
59
|
+
throw new Error(`unknown option: --${name}`);
|
|
60
|
+
}
|
|
61
|
+
return kind;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {readonly string[]} window
|
|
66
|
+
* @param {OptionSpec} spec
|
|
67
|
+
* @returns {OptionToken | PositionalToken}
|
|
68
|
+
*/
|
|
69
|
+
function readToken(window, spec) {
|
|
70
|
+
const [arg = "", value] = window;
|
|
71
|
+
if (!arg.startsWith(OPTION_PREFIX)) {
|
|
72
|
+
return { positional: arg, width: FLAG_WIDTH };
|
|
73
|
+
}
|
|
74
|
+
const name = arg.slice(OPTION_PREFIX.length);
|
|
75
|
+
const kind = optionKind(spec, name);
|
|
76
|
+
if (kind === "boolean") {
|
|
77
|
+
return { kind, name, value: true, width: FLAG_WIDTH };
|
|
78
|
+
}
|
|
79
|
+
if (value === undefined || value.startsWith(OPTION_PREFIX)) {
|
|
80
|
+
throw new Error(`missing value for --${name}`);
|
|
81
|
+
}
|
|
82
|
+
return { kind, name, value, width: VALUE_OPTION_WIDTH };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {readonly string[]} args
|
|
87
|
+
* @param {OptionSpec} spec
|
|
88
|
+
* @returns {readonly (OptionToken | PositionalToken)[]}
|
|
89
|
+
*/
|
|
90
|
+
function readTokens(args, spec) {
|
|
91
|
+
/** @type {(OptionToken | PositionalToken)[]} */
|
|
92
|
+
const tokens = [];
|
|
93
|
+
let index = 0;
|
|
94
|
+
while (index < args.length) {
|
|
95
|
+
const token = readToken(args.slice(index, index + VALUE_OPTION_WIDTH), spec);
|
|
96
|
+
tokens.push(token);
|
|
97
|
+
index += token.width;
|
|
98
|
+
}
|
|
99
|
+
return tokens;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {OptionValue | undefined} previous
|
|
104
|
+
* @param {OptionToken} token
|
|
105
|
+
* @returns {OptionValue}
|
|
106
|
+
*/
|
|
107
|
+
function optionValue(previous, token) {
|
|
108
|
+
if (token.kind !== "repeat" || token.value === true) {
|
|
109
|
+
return token.value;
|
|
110
|
+
}
|
|
111
|
+
/** @type {readonly string[]} */
|
|
112
|
+
const earlier = typeof previous === "object" ? previous : [];
|
|
113
|
+
return [...earlier, token.value];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {readonly string[]} args
|
|
118
|
+
* @param {OptionSpec} spec
|
|
119
|
+
* @returns {CliOptions}
|
|
120
|
+
*/
|
|
121
|
+
function parseOptions(args, spec) {
|
|
122
|
+
const tokens = readTokens(args, spec);
|
|
123
|
+
const positionals = tokens.filter((token) => "positional" in token);
|
|
124
|
+
if (positionals.length > NO_POSITIONALS) {
|
|
125
|
+
throw new Error(`unexpected positional argument: ${String(positionals[NO_POSITIONALS]?.positional)}`);
|
|
126
|
+
}
|
|
127
|
+
/** @type {Record<string, OptionValue>} */
|
|
9
128
|
const options = {};
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (!arg.startsWith("--")) {
|
|
14
|
-
positionals.push(arg);
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
const name = arg.slice(2);
|
|
18
|
-
const kind = spec[name];
|
|
19
|
-
if (!kind) {
|
|
20
|
-
throw new Error(`unknown option: --${name}`);
|
|
21
|
-
}
|
|
22
|
-
if (kind === "boolean") {
|
|
23
|
-
options[name] = true;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
const value = args[++index];
|
|
27
|
-
if (value === undefined || value.startsWith("--")) {
|
|
28
|
-
throw new Error(`missing value for --${name}`);
|
|
129
|
+
for (const token of tokens) {
|
|
130
|
+
if ("name" in token) {
|
|
131
|
+
options[token.name] = optionValue(options[token.name], token);
|
|
29
132
|
}
|
|
30
|
-
options[name] = kind === "repeat" ? [...(options[name] ?? []), value] : value;
|
|
31
|
-
}
|
|
32
|
-
if (positionals.length > 0) {
|
|
33
|
-
throw new Error(`unexpected positional argument: ${positionals[0]}`);
|
|
34
133
|
}
|
|
35
134
|
return options;
|
|
36
135
|
}
|
|
37
136
|
|
|
38
|
-
|
|
137
|
+
/**
|
|
138
|
+
* @param {CliOptions} options
|
|
139
|
+
* @param {string} name
|
|
140
|
+
* @returns {string}
|
|
141
|
+
*/
|
|
142
|
+
function requireOption(options, name) {
|
|
39
143
|
const value = options[name];
|
|
40
144
|
if (typeof value !== "string" || value.trim() === "") {
|
|
41
145
|
throw new Error(`--${name} is required`);
|
|
@@ -43,97 +147,274 @@ export function requireOption(options, name) {
|
|
|
43
147
|
return value.trim();
|
|
44
148
|
}
|
|
45
149
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
150
|
+
/**
|
|
151
|
+
* @param {unknown} value
|
|
152
|
+
* @returns {unknown}
|
|
153
|
+
*/
|
|
154
|
+
function sortJson(value) {
|
|
155
|
+
if (isJsonList(value)) {
|
|
156
|
+
return value.map((item) => sortJson(item));
|
|
49
157
|
}
|
|
50
|
-
if (value
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.map((key) => [key, sortJson(value[key])])
|
|
55
|
-
);
|
|
158
|
+
if (isJsonObject(value)) {
|
|
159
|
+
const keys = Object.keys(value);
|
|
160
|
+
keys.sort();
|
|
161
|
+
return Object.fromEntries(keys.map((key) => [key, sortJson(value[key])]));
|
|
56
162
|
}
|
|
57
163
|
return value;
|
|
58
164
|
}
|
|
59
165
|
|
|
60
|
-
|
|
166
|
+
/**
|
|
167
|
+
* @param {unknown} value
|
|
168
|
+
* @returns {string}
|
|
169
|
+
*/
|
|
170
|
+
function stableJson(value) {
|
|
61
171
|
return JSON.stringify(sortJson(value));
|
|
62
172
|
}
|
|
63
173
|
|
|
64
|
-
|
|
65
|
-
|
|
174
|
+
/**
|
|
175
|
+
* @param {string} value
|
|
176
|
+
* @returns {string}
|
|
177
|
+
*/
|
|
178
|
+
function fileReference(value) {
|
|
179
|
+
const [withoutQuery = ""] = value.split("?", FIRST_PART_ONLY);
|
|
180
|
+
const [withoutFragment = ""] = withoutQuery.split("#", FIRST_PART_ONLY);
|
|
181
|
+
return withoutFragment;
|
|
66
182
|
}
|
|
67
183
|
|
|
68
|
-
|
|
184
|
+
/**
|
|
185
|
+
* @param {string} root
|
|
186
|
+
* @param {string} filePath
|
|
187
|
+
* @returns {string}
|
|
188
|
+
*/
|
|
189
|
+
function relativeTo(root, filePath) {
|
|
69
190
|
return path.relative(root, filePath).split(path.sep).join("/");
|
|
70
191
|
}
|
|
71
192
|
|
|
72
|
-
|
|
193
|
+
/**
|
|
194
|
+
* @param {JsonObject} payload
|
|
195
|
+
* @returns {string | undefined}
|
|
196
|
+
*/
|
|
197
|
+
function sourceMapDebugId(payload) {
|
|
73
198
|
for (const key of SOURCE_MAP_DEBUG_ID_KEYS) {
|
|
74
199
|
const value = payload[key];
|
|
75
200
|
if (typeof value === "string" && value.trim() !== "") {
|
|
76
201
|
return value.trim();
|
|
77
202
|
}
|
|
78
203
|
}
|
|
79
|
-
return
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @param {unknown} payload
|
|
209
|
+
* @returns {void}
|
|
210
|
+
*/
|
|
211
|
+
function printJson(payload) {
|
|
212
|
+
process.stdout.write(`${JSON.stringify(sortJson(payload), undefined, JSON_INDENT)}\n`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {string} filePath
|
|
217
|
+
* @returns {Promise<boolean>}
|
|
218
|
+
*/
|
|
219
|
+
async function pathExists(filePath) {
|
|
220
|
+
try {
|
|
221
|
+
await access(filePath);
|
|
222
|
+
return true;
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @param {string} filePath
|
|
230
|
+
* @returns {Promise<string>}
|
|
231
|
+
*/
|
|
232
|
+
async function realPath(filePath) {
|
|
233
|
+
return realPathOf(filePath);
|
|
80
234
|
}
|
|
81
235
|
|
|
82
|
-
|
|
83
|
-
|
|
236
|
+
/**
|
|
237
|
+
* @param {string} filePath
|
|
238
|
+
* @returns {Promise<string>}
|
|
239
|
+
*/
|
|
240
|
+
async function readText(filePath) {
|
|
241
|
+
return readFile(filePath, "utf8");
|
|
84
242
|
}
|
|
85
243
|
|
|
86
|
-
|
|
87
|
-
|
|
244
|
+
/**
|
|
245
|
+
* @param {string} candidate
|
|
246
|
+
* @param {string} root
|
|
247
|
+
* @returns {Promise<string | undefined>}
|
|
248
|
+
*/
|
|
249
|
+
async function safeResolve(candidate, root) {
|
|
250
|
+
const resolvedRoot = await realPath(root);
|
|
88
251
|
const resolved = path.resolve(candidate);
|
|
89
|
-
const comparable =
|
|
252
|
+
const comparable = (await pathExists(resolved)) ? await realPath(resolved) : resolved;
|
|
90
253
|
const relative = path.relative(resolvedRoot, comparable);
|
|
91
254
|
if (relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))) {
|
|
92
255
|
return comparable;
|
|
93
256
|
}
|
|
94
|
-
return
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @param {string} filePath
|
|
262
|
+
* @returns {Promise<number>}
|
|
263
|
+
*/
|
|
264
|
+
async function byteSize(filePath) {
|
|
265
|
+
const stats = await stat(filePath);
|
|
266
|
+
return stats.size;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {string} filePath
|
|
271
|
+
* @returns {Promise<boolean>}
|
|
272
|
+
*/
|
|
273
|
+
async function isFile(filePath) {
|
|
274
|
+
const stats = await stat(filePath);
|
|
275
|
+
return stats.isFile();
|
|
95
276
|
}
|
|
96
277
|
|
|
97
|
-
|
|
98
|
-
|
|
278
|
+
/**
|
|
279
|
+
* @param {string} filePath
|
|
280
|
+
* @returns {Promise<boolean>}
|
|
281
|
+
*/
|
|
282
|
+
async function isDirectory(filePath) {
|
|
283
|
+
const stats = await stat(filePath);
|
|
284
|
+
return stats.isDirectory();
|
|
99
285
|
}
|
|
100
286
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
287
|
+
/**
|
|
288
|
+
* @param {string} filePath
|
|
289
|
+
* @returns {Promise<string>}
|
|
290
|
+
*/
|
|
291
|
+
async function sha256File(filePath) {
|
|
292
|
+
const digest = createHash("sha256");
|
|
293
|
+
digest.update(await readFile(filePath));
|
|
104
294
|
return digest.digest("hex");
|
|
105
295
|
}
|
|
106
296
|
|
|
107
|
-
|
|
297
|
+
/**
|
|
298
|
+
* @param {string} value
|
|
299
|
+
* @returns {Promise<string>}
|
|
300
|
+
*/
|
|
301
|
+
async function requireBuildDir(value) {
|
|
108
302
|
const buildDir = path.resolve(value);
|
|
109
|
-
if (!
|
|
303
|
+
if (!(await pathExists(buildDir)) || !(await isDirectory(buildDir))) {
|
|
110
304
|
throw new Error(`build directory does not exist: ${value}`);
|
|
111
305
|
}
|
|
112
|
-
return
|
|
306
|
+
return realPath(buildDir);
|
|
113
307
|
}
|
|
114
308
|
|
|
115
|
-
|
|
116
|
-
|
|
309
|
+
/**
|
|
310
|
+
* @param {string} filePath
|
|
311
|
+
* @param {string} label
|
|
312
|
+
* @returns {Promise<unknown>}
|
|
313
|
+
*/
|
|
314
|
+
async function readJson(filePath, label) {
|
|
117
315
|
try {
|
|
118
|
-
|
|
316
|
+
/** @type {unknown} */
|
|
317
|
+
const payload = JSON.parse(await readText(filePath));
|
|
318
|
+
return payload;
|
|
119
319
|
} catch (error) {
|
|
120
|
-
throw new Error(`${label} is not valid JSON: ${error
|
|
320
|
+
throw new Error(`${label} is not valid JSON: ${errorMessage(error)}`, { cause: error });
|
|
121
321
|
}
|
|
122
|
-
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @param {string} filePath
|
|
326
|
+
* @param {string} label
|
|
327
|
+
* @returns {Promise<JsonObject>}
|
|
328
|
+
*/
|
|
329
|
+
async function readJsonObject(filePath, label) {
|
|
330
|
+
const payload = await readJson(filePath, label);
|
|
331
|
+
if (!isJsonObject(payload)) {
|
|
123
332
|
throw new Error(`${label} must be a JSON object`);
|
|
124
333
|
}
|
|
125
334
|
return payload;
|
|
126
335
|
}
|
|
127
336
|
|
|
128
|
-
|
|
337
|
+
/**
|
|
338
|
+
* @template Item, Result
|
|
339
|
+
* @param {readonly Item[]} items
|
|
340
|
+
* @param {(item: Item) => Promise<Result>} mapper
|
|
341
|
+
* @returns {Promise<Result[]>}
|
|
342
|
+
*/
|
|
343
|
+
async function mapInOrder(items, mapper) {
|
|
344
|
+
/** @type {Result[]} */
|
|
345
|
+
const results = [];
|
|
346
|
+
/**
|
|
347
|
+
* @param {readonly Item[]} remaining
|
|
348
|
+
* @returns {Promise<Result[]>}
|
|
349
|
+
*/
|
|
350
|
+
async function mapRemaining(remaining) {
|
|
351
|
+
const [first, ...rest] = remaining;
|
|
352
|
+
if (remaining.length === NO_POSITIONALS || first === undefined) {
|
|
353
|
+
return results;
|
|
354
|
+
}
|
|
355
|
+
results.push(await mapper(first));
|
|
356
|
+
return mapRemaining(rest);
|
|
357
|
+
}
|
|
358
|
+
return mapRemaining(items);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @template Item
|
|
363
|
+
* @param {readonly Item[]} items
|
|
364
|
+
* @param {(item: Item) => Promise<boolean>} predicate
|
|
365
|
+
* @returns {Promise<Item | undefined>}
|
|
366
|
+
*/
|
|
367
|
+
async function findInOrder(items, predicate) {
|
|
368
|
+
const [first, ...rest] = items;
|
|
369
|
+
if (items.length === NO_POSITIONALS || first === undefined) {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
return (await predicate(first)) ? first : findInOrder(rest, predicate);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @param {unknown} value
|
|
377
|
+
* @param {string} message
|
|
378
|
+
* @param {boolean} required
|
|
379
|
+
* @returns {string | undefined}
|
|
380
|
+
*/
|
|
381
|
+
function normalizeProjectId(value, message, required = false) {
|
|
129
382
|
if (value === undefined || value === null) {
|
|
130
383
|
if (required) {
|
|
131
|
-
throw new Error(
|
|
384
|
+
throw new Error(message);
|
|
132
385
|
}
|
|
133
386
|
return undefined;
|
|
134
387
|
}
|
|
135
|
-
if (typeof value !== "string" || UUID_RE.test(value.trim())
|
|
136
|
-
throw new Error(
|
|
388
|
+
if (typeof value !== "string" || !UUID_RE.test(value.trim())) {
|
|
389
|
+
throw new Error(message);
|
|
137
390
|
}
|
|
138
391
|
return value.trim().toLowerCase();
|
|
139
392
|
}
|
|
393
|
+
|
|
394
|
+
export {
|
|
395
|
+
SOURCE_MAP_DEBUG_ID_KEYS,
|
|
396
|
+
byteSize,
|
|
397
|
+
errorMessage,
|
|
398
|
+
fileReference,
|
|
399
|
+
findInOrder,
|
|
400
|
+
isDirectory,
|
|
401
|
+
isFile,
|
|
402
|
+
isJsonList,
|
|
403
|
+
isJsonObject,
|
|
404
|
+
mapInOrder,
|
|
405
|
+
normalizeProjectId,
|
|
406
|
+
parseOptions,
|
|
407
|
+
pathExists,
|
|
408
|
+
printJson,
|
|
409
|
+
readJsonObject,
|
|
410
|
+
readText,
|
|
411
|
+
realPath,
|
|
412
|
+
relativeTo,
|
|
413
|
+
requireBuildDir,
|
|
414
|
+
requireOption,
|
|
415
|
+
safeResolve,
|
|
416
|
+
sha256File,
|
|
417
|
+
sortJson,
|
|
418
|
+
sourceMapDebugId,
|
|
419
|
+
stableJson
|
|
420
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { SOURCE_MAP_DEBUG_ID_KEYS, stableJson } from "./release-artifacts-common.js";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
|
|
5
|
+
/** @import { JsonObject } from "./release-artifacts-common.js" */
|
|
6
|
+
|
|
7
|
+
const DEBUG_ID_NAMESPACE = "16f4a837-7e0b-4d7c-97d9-8a7af1fd2768";
|
|
8
|
+
const DEBUG_ID_RE = /(?:\/\/#|\/\*#)\s*debugId=(?<debugId>[A-Za-z0-9._:-]+)/giu;
|
|
9
|
+
const DEBUG_ID_REGISTRY_NAME = "logbrew.release-artifact.debug-ids";
|
|
10
|
+
const DEBUG_ID_REGISTRY_EXPRESSION = `Symbol.for(${JSON.stringify(DEBUG_ID_REGISTRY_NAME)})`;
|
|
11
|
+
const HERMES_RUNTIME_DEBUG_ID_RE =
|
|
12
|
+
/\/\*logbrew-runtime-debug-id\*\/\/\/# debugId=(?<debugId>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/iu;
|
|
13
|
+
const RUNTIME_DEBUG_ID_MARKER = "/*logbrew-runtime-debug-id*/";
|
|
14
|
+
const SOURCE_MAPPING_COMMENT_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=[^\r\n]*/giu;
|
|
15
|
+
const SOURCE_MAPPING_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=(?<url>[^\s*]+)/iu;
|
|
16
|
+
const SHEBANG_LINE_RE = /^#![^\r\n]*(?:\r?\n|$)/u;
|
|
17
|
+
const LEADING_TRIVIA_RE = /^(?:\s|\/\/[^\r\n]*(?:\r?\n|$)|\/\*[\s\S]*?\*\/)+/u;
|
|
18
|
+
const UUID_BYTE_LENGTH = 16;
|
|
19
|
+
const UUID_TIME_LOW_LENGTH = 8;
|
|
20
|
+
const UUID_SHORT_GROUP_LENGTH = 4;
|
|
21
|
+
const UUID_NODE_LENGTH = 12;
|
|
22
|
+
const UUID_GROUP_LENGTHS = [
|
|
23
|
+
UUID_TIME_LOW_LENGTH,
|
|
24
|
+
UUID_SHORT_GROUP_LENGTH,
|
|
25
|
+
UUID_SHORT_GROUP_LENGTH,
|
|
26
|
+
UUID_SHORT_GROUP_LENGTH,
|
|
27
|
+
UUID_NODE_LENGTH
|
|
28
|
+
];
|
|
29
|
+
const UUID_VERSION_BYTE = 6;
|
|
30
|
+
const UUID_VARIANT_BYTE = 8;
|
|
31
|
+
const UUID_VERSION_MODULUS = 16;
|
|
32
|
+
const UUID_VERSION_5_BITS = 80;
|
|
33
|
+
const UUID_VARIANT_MODULUS = 64;
|
|
34
|
+
const UUID_RFC_4122_VARIANT_BITS = 128;
|
|
35
|
+
const UUID_START = 0;
|
|
36
|
+
const LAST_INDEX = -1;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} source
|
|
40
|
+
* @returns {string | undefined}
|
|
41
|
+
*/
|
|
42
|
+
function findDebugId(source) {
|
|
43
|
+
const runtimeDebugId = HERMES_RUNTIME_DEBUG_ID_RE.exec(source)?.groups?.["debugId"];
|
|
44
|
+
if (runtimeDebugId !== undefined) {
|
|
45
|
+
return runtimeDebugId;
|
|
46
|
+
}
|
|
47
|
+
return [...source.matchAll(DEBUG_ID_RE)].at(LAST_INDEX)?.groups?.["debugId"]?.trim();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} source
|
|
52
|
+
* @returns {string | undefined}
|
|
53
|
+
*/
|
|
54
|
+
function findSourceMappingUrl(source) {
|
|
55
|
+
return SOURCE_MAPPING_RE.exec(source)?.groups?.["url"]?.trim();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {string} hex
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
function formatUuid(hex) {
|
|
63
|
+
/** @type {string[]} */
|
|
64
|
+
const groups = [];
|
|
65
|
+
let offset = UUID_START;
|
|
66
|
+
for (const length of UUID_GROUP_LENGTHS) {
|
|
67
|
+
groups.push(hex.slice(offset, offset + length));
|
|
68
|
+
offset += length;
|
|
69
|
+
}
|
|
70
|
+
return groups.join("-");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} namespace
|
|
75
|
+
* @param {string} name
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
function uuidV5(namespace, name) {
|
|
79
|
+
const bytes = createHash("sha1")
|
|
80
|
+
.update(Buffer.from(namespace.replaceAll("-", ""), "hex"))
|
|
81
|
+
.update(Buffer.from(name, "utf8"))
|
|
82
|
+
.digest()
|
|
83
|
+
.subarray(UUID_START, UUID_BYTE_LENGTH);
|
|
84
|
+
bytes.writeUInt8((bytes.readUInt8(UUID_VERSION_BYTE) % UUID_VERSION_MODULUS) + UUID_VERSION_5_BITS, UUID_VERSION_BYTE);
|
|
85
|
+
bytes.writeUInt8(
|
|
86
|
+
(bytes.readUInt8(UUID_VARIANT_BYTE) % UUID_VARIANT_MODULUS) + UUID_RFC_4122_VARIANT_BITS,
|
|
87
|
+
UUID_VARIANT_BYTE
|
|
88
|
+
);
|
|
89
|
+
return formatUuid(bytes.toString("hex"));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {JsonObject} payload
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
function canonicalSourceMapWithoutDebugId(payload) {
|
|
97
|
+
const keys = Object.keys(payload).filter((key) => !SOURCE_MAP_DEBUG_ID_KEYS.includes(key));
|
|
98
|
+
return stableJson(Object.fromEntries(keys.map((key) => [key, payload[key]])));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {string} relativeJsPath
|
|
103
|
+
* @param {string} jsSource
|
|
104
|
+
* @param {JsonObject} sourceMapPayload
|
|
105
|
+
* @returns {string}
|
|
106
|
+
*/
|
|
107
|
+
function generateDebugId(relativeJsPath, jsSource, sourceMapPayload) {
|
|
108
|
+
const digest = createHash("sha256");
|
|
109
|
+
digest.update(relativeJsPath);
|
|
110
|
+
digest.update("\0");
|
|
111
|
+
digest.update(jsSource.replaceAll(DEBUG_ID_RE, ""));
|
|
112
|
+
digest.update("\0");
|
|
113
|
+
digest.update(canonicalSourceMapWithoutDebugId(sourceMapPayload));
|
|
114
|
+
return uuidV5(DEBUG_ID_NAMESPACE, digest.digest("hex"));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {string} source
|
|
119
|
+
* @returns {string}
|
|
120
|
+
*/
|
|
121
|
+
function executableShebang(source) {
|
|
122
|
+
return source.startsWith("#!") ? (SHEBANG_LINE_RE.exec(source)?.[UUID_START] ?? "") : "";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {string} source
|
|
127
|
+
* @returns {boolean}
|
|
128
|
+
*/
|
|
129
|
+
function sourceStartsWithDirective(source) {
|
|
130
|
+
const body = source.slice(executableShebang(source).length).replace(LEADING_TRIVIA_RE, "");
|
|
131
|
+
return body.startsWith("\"") || body.startsWith("'");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {string} source
|
|
136
|
+
* @param {string} debugId
|
|
137
|
+
* @param {string} relativePath
|
|
138
|
+
* @returns {string}
|
|
139
|
+
*/
|
|
140
|
+
function sourceWithRuntimeDebugId(source, debugId, relativePath) {
|
|
141
|
+
if (source.includes(RUNTIME_DEBUG_ID_MARKER)) {
|
|
142
|
+
return source;
|
|
143
|
+
}
|
|
144
|
+
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`;
|
|
145
|
+
const shebang = executableShebang(source);
|
|
146
|
+
const separator = shebang === source && shebang !== "" ? "\n" : "";
|
|
147
|
+
return `${shebang}${separator}${registryLine}${source.slice(shebang.length)}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @param {string} source
|
|
152
|
+
* @param {string} debugId
|
|
153
|
+
* @param {string} relativePath
|
|
154
|
+
* @returns {string}
|
|
155
|
+
*/
|
|
156
|
+
function sourceWithDebugId(source, debugId, relativePath) {
|
|
157
|
+
const prepared = sourceWithRuntimeDebugId(source, debugId, relativePath);
|
|
158
|
+
if (findDebugId(prepared) !== undefined) {
|
|
159
|
+
return prepared;
|
|
160
|
+
}
|
|
161
|
+
const debugLine = `//# debugId=${debugId}\n`;
|
|
162
|
+
const last = [...prepared.matchAll(SOURCE_MAPPING_COMMENT_RE)].at(LAST_INDEX);
|
|
163
|
+
if (last === undefined) {
|
|
164
|
+
return `${prepared}${prepared.endsWith("\n") ? "" : "\n"}${debugLine}`;
|
|
165
|
+
}
|
|
166
|
+
const prefix = prepared.slice(UUID_START, last.index);
|
|
167
|
+
const separator = prefix.endsWith("\n") || prefix.endsWith("\r") ? "" : "\n";
|
|
168
|
+
return `${prefix}${separator}${debugLine}${prepared.slice(last.index)}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export {
|
|
172
|
+
RUNTIME_DEBUG_ID_MARKER,
|
|
173
|
+
findDebugId,
|
|
174
|
+
findSourceMappingUrl,
|
|
175
|
+
generateDebugId,
|
|
176
|
+
sourceStartsWithDirective,
|
|
177
|
+
sourceWithDebugId
|
|
178
|
+
};
|