@logbrew/sdk 0.1.16 → 0.1.18

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/issue-stack.cjs CHANGED
@@ -5,6 +5,7 @@ const MAX_ISSUE_STACK_FUNCTION_LENGTH = 256;
5
5
  const MAX_ISSUE_STACK_MODULE_LENGTH = 512;
6
6
  const SAFE_DEBUG_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
7
7
  const LOCAL_ABSOLUTE_PATH_PATTERN = /(?:^|\s)(?:\/(?:Users|home|private|tmp|var|Volumes)\/|[A-Za-z]:[\\/])/u;
8
+ const DEBUG_ID_REGISTRY = Symbol.for("logbrew.release-artifact.debug-ids");
8
9
 
9
10
  function buildIssueStackHelpers({ SdkError }) {
10
11
  function javascriptStackEvidence(stack, debugIdMap) {
@@ -181,24 +182,52 @@ function sanitizeFrameFilename(value) {
181
182
  }
182
183
 
183
184
  function debugIdForFrame(filename, debugIdMap, SdkError) {
184
- if (debugIdMap === undefined || debugIdMap === null) {
185
- return null;
186
- }
187
- if (!debugIdMap || Array.isArray(debugIdMap) || typeof debugIdMap !== "object") {
185
+ if (debugIdMap !== undefined && debugIdMap !== null
186
+ && (!debugIdMap || Array.isArray(debugIdMap) || typeof debugIdMap !== "object")) {
188
187
  throw new SdkError("validation_error", "debugIdMap must be an object");
189
188
  }
189
+ return debugIdFromEntries(filename, debugIdMap ? Object.entries(debugIdMap) : [])
190
+ ?? debugIdFromEntries(filename, runtimeDebugIdEntries());
191
+ }
192
+
193
+ function debugIdFromEntries(filename, entries) {
190
194
  const normalizedFilename = sanitizeFrameFilename(filename);
191
- const aliases = new Set([normalizedFilename, basename(normalizedFilename)].filter(Boolean));
192
- for (const [candidate, debugId] of Object.entries(debugIdMap)) {
195
+ const frameBasename = basename(normalizedFilename);
196
+ let basenameMatch = null;
197
+ for (const [candidate, debugId] of entries) {
193
198
  if (typeof debugId !== "string" || !SAFE_DEBUG_ID_PATTERN.test(debugId.trim())) {
194
199
  continue;
195
200
  }
196
201
  const normalizedCandidate = sanitizeFrameFilename(candidate);
197
- if (aliases.has(normalizedCandidate) || aliases.has(basename(normalizedCandidate))) {
198
- return debugId.trim().toLowerCase();
202
+ const normalizedDebugId = debugId.trim().toLowerCase();
203
+ if (normalizedFilename === normalizedCandidate
204
+ || (normalizedCandidate.includes("/")
205
+ && normalizedFilename.endsWith(`/${normalizedCandidate.replace(/^\/+/, "")}`))) {
206
+ return normalizedDebugId;
207
+ }
208
+ if (frameBasename === basename(normalizedCandidate)) {
209
+ basenameMatch = basenameMatch === null || basenameMatch === normalizedDebugId
210
+ ? normalizedDebugId
211
+ : false;
212
+ }
213
+ }
214
+ return typeof basenameMatch === "string" ? basenameMatch : null;
215
+ }
216
+
217
+ function runtimeDebugIdEntries() {
218
+ try {
219
+ const registry = typeof globalThis === "object"
220
+ ? Object.getOwnPropertyDescriptor(globalThis, DEBUG_ID_REGISTRY)?.value
221
+ : null;
222
+ if (!registry || Array.isArray(registry) || typeof registry !== "object") {
223
+ return [];
199
224
  }
225
+ return Object.entries(Object.getOwnPropertyDescriptors(registry)).slice(0, 512).flatMap(
226
+ ([candidate, descriptor]) => "value" in descriptor ? [[candidate, descriptor.value]] : []
227
+ );
228
+ } catch {
229
+ return [];
200
230
  }
201
- return null;
202
231
  }
203
232
 
204
233
  function basename(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logbrew/sdk",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Public LogBrew JavaScript SDK for building, validating, and flushing event batches.",
5
5
  "type": "module",
6
6
  "main": "./index.cjs",
@@ -3,6 +3,45 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
 
5
5
  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
+ export const SOURCE_MAP_DEBUG_ID_KEYS = ["debug_id", "debugId", "debugID", "x_debug_id"];
7
+
8
+ export function parseOptions(args, spec) {
9
+ const options = {};
10
+ const positionals = [];
11
+ for (let index = 0; index < args.length; index += 1) {
12
+ const arg = args[index];
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}`);
29
+ }
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
+ }
35
+ return options;
36
+ }
37
+
38
+ export function requireOption(options, name) {
39
+ const value = options[name];
40
+ if (typeof value !== "string" || value.trim() === "") {
41
+ throw new Error(`--${name} is required`);
42
+ }
43
+ return value.trim();
44
+ }
6
45
 
7
46
  export function sortJson(value) {
8
47
  if (Array.isArray(value)) {
@@ -22,6 +61,24 @@ export function stableJson(value) {
22
61
  return JSON.stringify(sortJson(value));
23
62
  }
24
63
 
64
+ export function fileReference(value) {
65
+ return value.split("?", 1)[0].split("#", 1)[0];
66
+ }
67
+
68
+ export function relativeTo(root, filePath) {
69
+ return path.relative(root, filePath).split(path.sep).join("/");
70
+ }
71
+
72
+ export function sourceMapDebugId(payload) {
73
+ for (const key of SOURCE_MAP_DEBUG_ID_KEYS) {
74
+ const value = payload[key];
75
+ if (typeof value === "string" && value.trim() !== "") {
76
+ return value.trim();
77
+ }
78
+ }
79
+ return null;
80
+ }
81
+
25
82
  export function printJson(payload) {
26
83
  process.stdout.write(`${JSON.stringify(sortJson(payload), null, 2)}\n`);
27
84
  }
@@ -1,58 +1,19 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
 
4
+ import {
5
+ fileReference,
6
+ readJsonObject,
7
+ relativeTo,
8
+ safeResolve,
9
+ sourceMapDebugId
10
+ } from "./release-artifacts-common.js";
11
+
4
12
  const SCRIPT_VERSION = "0.1.0";
5
13
  const MAX_SOURCE_CONTEXT_FILE_BYTES = 1024 * 1024;
6
- const SOURCE_MAP_DEBUG_ID_KEYS = ["debug_id", "debugId", "debugID", "x_debug_id"];
7
14
  const VLQ_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
8
15
  const VLQ_VALUES = new Map([...VLQ_CHARS].map((char, index) => [char, index]));
9
16
 
10
- function fileReference(value) {
11
- return value.split("?", 1)[0].split("#", 1)[0];
12
- }
13
-
14
- function toPosix(value) {
15
- return value.split(path.sep).join("/");
16
- }
17
-
18
- function relativeTo(root, filePath) {
19
- return toPosix(path.relative(root, filePath));
20
- }
21
-
22
- function safeResolve(candidate, root) {
23
- const resolvedRoot = fs.realpathSync(root);
24
- const resolved = path.resolve(candidate);
25
- const comparable = fs.existsSync(resolved) ? fs.realpathSync(resolved) : resolved;
26
- const relative = path.relative(resolvedRoot, comparable);
27
- if (relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))) {
28
- return comparable;
29
- }
30
- return null;
31
- }
32
-
33
- function readJsonObject(filePath, label) {
34
- let payload;
35
- try {
36
- payload = JSON.parse(fs.readFileSync(filePath, "utf8"));
37
- } catch (error) {
38
- throw new Error(`${label} is not valid JSON: ${error.message}`, { cause: error });
39
- }
40
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
41
- throw new Error(`${label} must be a JSON object`);
42
- }
43
- return payload;
44
- }
45
-
46
- function sourceMapDebugId(payload) {
47
- for (const key of SOURCE_MAP_DEBUG_ID_KEYS) {
48
- const value = payload[key];
49
- if (typeof value === "string" && value.trim() !== "") {
50
- return value.trim();
51
- }
52
- }
53
- return null;
54
- }
55
-
56
17
  function parseStackFrame(stackFrame) {
57
18
  let line = stackFrame.trim();
58
19
  if (line.startsWith("at ")) {
@@ -122,7 +83,7 @@ function requireReadyArtifacts(manifest) {
122
83
  if (manifest.artifactType !== "javascript_source_map_manifest") {
123
84
  throw new Error("only javascript_source_map_manifest symbolication proof is supported");
124
85
  }
125
- if (!manifest.validation || manifest.validation.status !== "ready") {
86
+ if (manifest.validation?.status !== "ready") {
126
87
  throw new Error("manifest validation status must be ready");
127
88
  }
128
89
  if (!Array.isArray(manifest.artifacts) || manifest.artifacts.length === 0) {
@@ -132,7 +93,7 @@ function requireReadyArtifacts(manifest) {
132
93
  if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) {
133
94
  throw new Error("artifact entries must be JSON objects");
134
95
  }
135
- if (!artifact.validation || artifact.validation.status !== "ready") {
96
+ if (artifact.validation?.status !== "ready") {
136
97
  throw new Error("all artifact validation statuses must be ready");
137
98
  }
138
99
  }
@@ -9,8 +9,10 @@ import path from "node:path";
9
9
  import {
10
10
  byteSize,
11
11
  normalizeProjectId,
12
+ parseOptions,
12
13
  printJson,
13
14
  readJsonObject,
15
+ requireOption,
14
16
  requireBuildDir,
15
17
  safeResolve,
16
18
  sha256File,
@@ -23,56 +25,6 @@ const NON_RETRYABLE_UPLOAD_STATUSES = new Set([400, 401, 403, 413]);
23
25
  const RETRYABLE_UPLOAD_STATUSES = new Set([408, 429]);
24
26
  const SCRIPT_VERSION = "0.1.0";
25
27
 
26
- function parseOptions(args) {
27
- const spec = {
28
- "build-dir": "string",
29
- manifest: "string",
30
- endpoint: "string",
31
- "token-env": "string",
32
- "dry-run": "boolean",
33
- "allow-hosted": "boolean",
34
- "max-retries": "string",
35
- "retry-delay": "string",
36
- timeout: "string"
37
- };
38
- const options = {};
39
- const positionals = [];
40
- for (let index = 0; index < args.length; index += 1) {
41
- const arg = args[index];
42
- if (!arg.startsWith("--")) {
43
- positionals.push(arg);
44
- continue;
45
- }
46
- const name = arg.slice(2);
47
- const kind = spec[name];
48
- if (!kind) {
49
- throw new Error(`unknown option: --${name}`);
50
- }
51
- if (kind === "boolean") {
52
- options[name] = true;
53
- continue;
54
- }
55
- const value = args[index + 1];
56
- if (value === undefined || value.startsWith("--")) {
57
- throw new Error(`missing value for --${name}`);
58
- }
59
- options[name] = value;
60
- index += 1;
61
- }
62
- if (positionals.length > 0) {
63
- throw new Error(`unexpected positional argument: ${positionals[0]}`);
64
- }
65
- return options;
66
- }
67
-
68
- function requireOption(options, name) {
69
- const value = options[name];
70
- if (typeof value !== "string" || value.trim() === "") {
71
- throw new Error(`--${name} is required`);
72
- }
73
- return value.trim();
74
- }
75
-
76
28
  function parseNonNegativeInteger(value, label) {
77
29
  const trimmed = value.trim();
78
30
  if (!/^\d+$/u.test(trimmed)) {
@@ -238,7 +190,7 @@ function requireReadyJavaScriptManifest(manifest, requireProjectId) {
238
190
  if (manifest.artifactType !== "javascript_source_map_manifest") {
239
191
  throw new Error("only javascript_source_map_manifest uploads are supported by this verifier");
240
192
  }
241
- if (!manifest.validation || manifest.validation.status !== "ready") {
193
+ if (manifest.validation?.status !== "ready") {
242
194
  throw new Error("manifest validation status must be ready before upload");
243
195
  }
244
196
  if (!Array.isArray(manifest.artifacts) || manifest.artifacts.length === 0) {
@@ -320,7 +272,17 @@ function exitCodeForUploadStatus(status) {
320
272
  }
321
273
 
322
274
  export async function runUploadJs(args) {
323
- const options = parseOptions(args);
275
+ const options = parseOptions(args, {
276
+ "build-dir": "string",
277
+ manifest: "string",
278
+ endpoint: "string",
279
+ "token-env": "string",
280
+ "dry-run": "boolean",
281
+ "allow-hosted": "boolean",
282
+ "max-retries": "string",
283
+ "retry-delay": "string",
284
+ timeout: "string"
285
+ });
324
286
  try {
325
287
  const endpoint = requireOption(options, "endpoint");
326
288
  const parsedEndpoint = requireUploadEndpoint(endpoint, Boolean(options["allow-hosted"]));
@@ -6,12 +6,18 @@ import path from "node:path";
6
6
 
7
7
  import {
8
8
  byteSize,
9
+ fileReference,
9
10
  normalizeProjectId,
11
+ parseOptions,
10
12
  printJson,
11
13
  readJsonObject,
14
+ relativeTo,
15
+ requireOption,
12
16
  requireBuildDir,
13
17
  safeResolve,
14
18
  sha256File,
19
+ sourceMapDebugId,
20
+ SOURCE_MAP_DEBUG_ID_KEYS,
15
21
  sortJson,
16
22
  stableJson
17
23
  } from "./release-artifacts-common.js";
@@ -20,9 +26,11 @@ import { verifyJavaScriptIssueSymbolication, verifyJavaScriptSymbolication } fro
20
26
 
21
27
  const DEBUG_ID_NAMESPACE = "16f4a837-7e0b-4d7c-97d9-8a7af1fd2768";
22
28
  const DEBUG_ID_RE = /(?:\/\/#|\/\*#)\s*debugId=([A-Za-z0-9._:-]+)/giu;
29
+ const DEBUG_ID_REGISTRY_NAME = "logbrew.release-artifact.debug-ids";
30
+ const DEBUG_ID_REGISTRY_EXPRESSION = `Symbol.for(${JSON.stringify(DEBUG_ID_REGISTRY_NAME)})`;
31
+ const RUNTIME_DEBUG_ID_MARKER = "/*logbrew-runtime-debug-id*/";
23
32
  const MINIFIED_SOURCE_SUFFIXES = [".js", ".mjs", ".bundle", ".jsbundle"];
24
33
  const SCRIPT_VERSION = "0.1.0";
25
- const SOURCE_MAP_DEBUG_ID_KEYS = ["debug_id", "debugId", "debugID", "x_debug_id"];
26
34
  const SOURCE_MAPPING_COMMENT_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=[^\r\n]*/giu;
27
35
  const SOURCE_MAPPING_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=([^\s*]+)/iu;
28
36
 
@@ -39,49 +47,6 @@ function usage() {
39
47
  ].join("\n");
40
48
  }
41
49
 
42
- function parseOptions(args, spec) {
43
- const options = {};
44
- const positionals = [];
45
- for (let index = 0; index < args.length; index += 1) {
46
- const arg = args[index];
47
- if (!arg.startsWith("--")) {
48
- positionals.push(arg);
49
- continue;
50
- }
51
- const name = arg.slice(2);
52
- const kind = spec[name];
53
- if (!kind) {
54
- throw new Error(`unknown option: --${name}`);
55
- }
56
- if (kind === "boolean") {
57
- options[name] = true;
58
- continue;
59
- }
60
- const value = args[index + 1];
61
- if (value === undefined || value.startsWith("--")) {
62
- throw new Error(`missing value for --${name}`);
63
- }
64
- index += 1;
65
- if (kind === "repeat") {
66
- options[name] = [...(options[name] ?? []), value];
67
- } else {
68
- options[name] = value;
69
- }
70
- }
71
- if (positionals.length > 0) {
72
- throw new Error(`unexpected positional argument: ${positionals[0]}`);
73
- }
74
- return options;
75
- }
76
-
77
- function requireOption(options, name) {
78
- const value = options[name];
79
- if (typeof value !== "string" || value.trim() === "") {
80
- throw new Error(`--${name} is required`);
81
- }
82
- return value.trim();
83
- }
84
-
85
50
  function optionalSourceContextOptions(options) {
86
51
  const hasSourceRoot = typeof options["source-root"] === "string" && options["source-root"].trim() !== "";
87
52
  const hasContextLines = typeof options["context-lines"] === "string" && options["context-lines"].trim() !== "";
@@ -108,14 +73,6 @@ function optionalSourceContextOptions(options) {
108
73
  };
109
74
  }
110
75
 
111
- function toPosix(value) {
112
- return value.split(path.sep).join("/");
113
- }
114
-
115
- function relativeTo(root, filePath) {
116
- return toPosix(path.relative(root, filePath));
117
- }
118
-
119
76
  function readText(filePath) {
120
77
  return fs.readFileSync(filePath, "utf8");
121
78
  }
@@ -155,20 +112,6 @@ function findSourceMappingUrl(source) {
155
112
  return match?.[1]?.trim() ?? null;
156
113
  }
157
114
 
158
- function sourceMapDebugId(payload) {
159
- for (const key of SOURCE_MAP_DEBUG_ID_KEYS) {
160
- const value = payload[key];
161
- if (typeof value === "string" && value.trim() !== "") {
162
- return value.trim();
163
- }
164
- }
165
- return null;
166
- }
167
-
168
- function fileReference(value) {
169
- return value.split("?", 1)[0].split("#", 1)[0];
170
- }
171
-
172
115
  function resolveSourceMapPath(jsPath, buildDir, sourceMappingUrl) {
173
116
  const warnings = [];
174
117
  const errors = [];
@@ -262,26 +205,40 @@ function generateDebugId(relativeJsPath, jsSource, sourceMapPayload) {
262
205
  return uuidV5(DEBUG_ID_NAMESPACE, digest.digest("hex"));
263
206
  }
264
207
 
265
- function sourceWithDebugId(source, debugId) {
266
- if (findDebugId(source)) {
267
- return source;
208
+ function executableShebang(source) {
209
+ return source.startsWith("#!") ? source.match(/^#![^\r\n]*(?:\r?\n|$)/u)?.[0] ?? "" : "";
210
+ }
211
+
212
+ function sourceStartsWithDirective(source, shebang) {
213
+ const body = source.slice(shebang.length).replace(/^(?:\s|\/\/[^\r\n]*(?:\r?\n|$)|\/\*[\s\S]*?\*\/)+/u, "");
214
+ return body.startsWith("\"") || body.startsWith("'");
215
+ }
216
+
217
+ function sourceWithDebugId(source, debugId, relativePath) {
218
+ 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`;
219
+ const shebang = executableShebang(source);
220
+ const prepared = source.includes(RUNTIME_DEBUG_ID_MARKER)
221
+ ? source
222
+ : `${shebang}${shebang === source && shebang ? "\n" : ""}${registryLine}${source.slice(shebang.length)}`;
223
+ if (findDebugId(prepared)) {
224
+ return prepared;
268
225
  }
269
226
  const debugLine = `//# debugId=${debugId}\n`;
270
- const matches = [...source.matchAll(SOURCE_MAPPING_COMMENT_RE)];
227
+ const matches = [...prepared.matchAll(SOURCE_MAPPING_COMMENT_RE)];
271
228
  if (matches.length === 0) {
272
- return `${source}${source.endsWith("\n") ? "" : "\n"}${debugLine}`;
229
+ return `${prepared}${prepared.endsWith("\n") ? "" : "\n"}${debugLine}`;
273
230
  }
274
231
  const last = matches.at(-1);
275
- const prefix = source.slice(0, last.index);
232
+ const prefix = prepared.slice(0, last.index);
276
233
  const separator = prefix.endsWith("\n") || prefix.endsWith("\r") ? "" : "\n";
277
- return `${prefix}${separator}${debugLine}${source.slice(last.index)}`;
234
+ return `${prefix}${separator}${debugLine}${prepared.slice(last.index)}`;
278
235
  }
279
236
 
280
237
  function normalizeSourcePrefixes(values) {
281
238
  const prefixes = [];
282
239
  for (const value of values ?? []) {
283
240
  for (const candidate of [path.resolve(value), fs.existsSync(value) ? fs.realpathSync(value) : path.resolve(value)]) {
284
- const normalized = toPosix(candidate).replace(/\/+$/u, "");
241
+ const normalized = candidate.split(path.sep).join("/").replace(/\/+$/u, "");
285
242
  if (normalized && !prefixes.includes(normalized)) {
286
243
  prefixes.push(normalized);
287
244
  }
@@ -317,30 +274,27 @@ function sourceMapPayloadForDebugId(payload, { stripSourcesContent, sourcePrefix
317
274
  if (!stripSourcesContent && !updatedSources) {
318
275
  return payload;
319
276
  }
320
- const updated = { ...payload };
277
+ const updated = updatedSources ? { ...payload, sources: updatedSources } : { ...payload };
321
278
  if (stripSourcesContent) {
322
279
  delete updated.sourcesContent;
323
280
  }
324
- if (updatedSources) {
325
- updated.sources = updatedSources;
326
- }
327
281
  return updated;
328
282
  }
329
283
 
330
- function sourceMapWithPrivacyUpdates(payload, debugId, { stripSourcesContent, sourcePrefixes }) {
331
- const updatedSources = sourceMapSourcesWithoutPrefixes(payload, sourcePrefixes);
332
- if (sourceMapDebugId(payload) && (!stripSourcesContent || !("sourcesContent" in payload)) && !updatedSources) {
284
+ function sourceMapWithPrivacyUpdates(payload, debugId, options) {
285
+ const privacyUpdated = sourceMapPayloadForDebugId(payload, options);
286
+ const { shiftGeneratedLines } = options;
287
+ if (sourceMapDebugId(payload)
288
+ && privacyUpdated === payload
289
+ && !shiftGeneratedLines) {
333
290
  return payload;
334
291
  }
335
- const updated = { ...payload };
292
+ const updated = { ...privacyUpdated };
336
293
  if (!sourceMapDebugId(updated)) {
337
294
  updated.debug_id = debugId;
338
295
  }
339
- if (stripSourcesContent) {
340
- delete updated.sourcesContent;
341
- }
342
- if (updatedSources) {
343
- updated.sources = updatedSources;
296
+ if (shiftGeneratedLines && typeof updated.mappings === "string") {
297
+ updated.mappings = `;${updated.mappings}`;
344
298
  }
345
299
  return updated;
346
300
  }
@@ -416,7 +370,19 @@ function buildArtifactPlan(jsPath, buildDir, options) {
416
370
  } = inspectArtifactFiles(jsPath, buildDir);
417
371
  const changes = [];
418
372
  let debugId = jsDebugId || mapDebugId;
373
+ const needsRuntimeDebugId = !jsSource.includes(RUNTIME_DEBUG_ID_MARKER);
374
+ if (needsRuntimeDebugId && sourceStartsWithDirective(jsSource, executableShebang(jsSource))) {
375
+ errors.push("runtime Debug ID injection cannot precede a script directive");
376
+ }
377
+ if (needsRuntimeDebugId
378
+ && sourceMapPayload
379
+ && (typeof sourceMapPayload.mappings !== "string" || sourceMapPayload.mappings === "")) {
380
+ errors.push("source map mappings must be a non-empty string before runtime Debug ID injection");
381
+ }
419
382
  if (errors.length === 0 && sourceMapPayload) {
383
+ if (needsRuntimeDebugId) {
384
+ changes.push("minifiedSource.runtimeDebugId");
385
+ }
420
386
  if (!debugId) {
421
387
  debugId = generateDebugId(
422
388
  relJs,
@@ -458,7 +424,8 @@ function applyArtifactPlan(artifact, buildDir, options) {
458
424
  const jsPath = path.join(buildDir, artifact.path);
459
425
  const sourceMapPath = path.join(buildDir, artifact.sourceMapPath);
460
426
  const jsSource = readText(jsPath);
461
- const updatedSource = sourceWithDebugId(jsSource, debugId);
427
+ const shiftGeneratedLines = !jsSource.includes(RUNTIME_DEBUG_ID_MARKER);
428
+ const updatedSource = sourceWithDebugId(jsSource, debugId, artifact.path);
462
429
  if (updatedSource !== jsSource) {
463
430
  writeText(jsPath, updatedSource);
464
431
  }
@@ -466,7 +433,10 @@ function applyArtifactPlan(artifact, buildDir, options) {
466
433
  if (!payload || errors.length > 0) {
467
434
  throw new Error(`${artifact.path}: source map became unreadable before write`);
468
435
  }
469
- const updatedPayload = sourceMapWithPrivacyUpdates(payload, debugId, options);
436
+ const updatedPayload = sourceMapWithPrivacyUpdates(payload, debugId, {
437
+ ...options,
438
+ shiftGeneratedLines
439
+ });
470
440
  if (stableJson(updatedPayload) !== stableJson(payload)) {
471
441
  writeText(sourceMapPath, `${JSON.stringify(sortJson(updatedPayload), null, 2)}\n`);
472
442
  }
@@ -55,13 +55,6 @@ function resolveBuildDir(root, outDir, explicitBuildDir) {
55
55
  return resolvePathFromRoot(root, outDir || "dist");
56
56
  }
57
57
 
58
- function resolveManifestPath(root, buildDir, explicitManifestPath) {
59
- if (explicitManifestPath) {
60
- return resolvePathFromRoot(root, explicitManifestPath);
61
- }
62
- return path.join(buildDir, DEFAULT_MANIFEST_NAME);
63
- }
64
-
65
58
  function createLogBrewViteReleaseArtifactsPlugin(options) {
66
59
  const release = requiredString(options, "release");
67
60
  const environment = requiredString(options, "environment");
@@ -95,9 +88,11 @@ function createLogBrewViteReleaseArtifactsPlugin(options) {
95
88
  viteOutDir = config?.build?.outDir || "dist";
96
89
  viteLogger = config?.logger && typeof config.logger.info === "function" ? config.logger : null;
97
90
  },
98
- async closeBundle() {
91
+ async writeBundle() {
99
92
  const buildDir = resolveBuildDir(viteRoot, viteOutDir, explicitBuildDir);
100
- const manifestPath = resolveManifestPath(viteRoot, buildDir, explicitManifestPath);
93
+ const manifestPath = explicitManifestPath
94
+ ? resolvePathFromRoot(viteRoot, explicitManifestPath)
95
+ : path.join(buildDir, DEFAULT_MANIFEST_NAME);
101
96
  const sourcePrefixes = userSourcePrefixes.length > 0 ? userSourcePrefixes : [viteRoot];
102
97
  const prepareArgs = ["--build-dir", buildDir, "--write"];
103
98
  if (stripSourcesContent) {
@@ -34,7 +34,7 @@ export interface LogBrewViteReleaseArtifactsPlugin {
34
34
  build?: { outDir?: string };
35
35
  logger?: { info(message: string): void };
36
36
  }): void;
37
- closeBundle(): Promise<void>;
37
+ writeBundle(): Promise<void>;
38
38
  }
39
39
 
40
40
  export declare function createLogBrewViteReleaseArtifactsPlugin(
@@ -34,7 +34,7 @@ export interface LogBrewViteReleaseArtifactsPlugin {
34
34
  build?: { outDir?: string };
35
35
  logger?: { info(message: string): void };
36
36
  }): void;
37
- closeBundle(): Promise<void>;
37
+ writeBundle(): Promise<void>;
38
38
  }
39
39
 
40
40
  export declare function createLogBrewViteReleaseArtifactsPlugin(