@logbrew/react-native 0.1.0 → 0.1.2

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.
Files changed (51) hide show
  1. package/README.md +431 -18
  2. package/apollo.cjs +301 -0
  3. package/apollo.d.cts +70 -0
  4. package/apollo.d.ts +70 -0
  5. package/apollo.js +294 -0
  6. package/examples/apollo-link-spans.mjs +149 -0
  7. package/examples/index.mjs +29 -1
  8. package/examples/instrumentation-kit.mjs +304 -0
  9. package/examples/lifecycle-spans.mjs +131 -0
  10. package/examples/native-bridge-scope.mjs +107 -0
  11. package/examples/navigation-resource-spans.mjs +156 -0
  12. package/examples/package.json +8 -1
  13. package/examples/real-user-smoke.mjs +106 -9
  14. package/examples/resource-fetch-spans.mjs +133 -0
  15. package/examples/trace-correlation.mjs +135 -0
  16. package/global-errors.cjs +366 -0
  17. package/global-errors.d.cts +63 -0
  18. package/global-errors.d.ts +63 -0
  19. package/global-errors.js +7 -0
  20. package/index.cjs +625 -111
  21. package/index.d.cts +236 -0
  22. package/index.d.ts +236 -0
  23. package/index.js +613 -95
  24. package/index.native.js +18 -0
  25. package/instrumentation.cjs +639 -0
  26. package/instrumentation.d.cts +84 -0
  27. package/instrumentation.d.ts +84 -0
  28. package/instrumentation.js +634 -0
  29. package/lifecycle.cjs +129 -0
  30. package/lifecycle.d.cts +50 -0
  31. package/lifecycle.d.ts +50 -0
  32. package/lifecycle.js +121 -0
  33. package/metadata.cjs +175 -0
  34. package/metadata.js +165 -0
  35. package/metro.cjs +310 -0
  36. package/metro.d.cts +37 -0
  37. package/metro.d.ts +37 -0
  38. package/metro.js +6 -0
  39. package/native-bridge.cjs +127 -0
  40. package/native-bridge.d.cts +60 -0
  41. package/native-bridge.d.ts +60 -0
  42. package/native-bridge.js +125 -0
  43. package/package.json +128 -4
  44. package/release-artifacts.cjs +344 -0
  45. package/release-artifacts.d.cts +55 -0
  46. package/release-artifacts.d.ts +53 -0
  47. package/release-artifacts.js +8 -0
  48. package/resource-fetch.cjs +469 -0
  49. package/resource-fetch.d.cts +60 -0
  50. package/resource-fetch.d.ts +60 -0
  51. package/resource-fetch.js +464 -0
@@ -0,0 +1,125 @@
1
+ import { SdkError } from "@logbrew/sdk";
2
+ import {
3
+ getActiveLogBrewTrace,
4
+ getReactNativeTraceMetadata
5
+ } from "./index.js";
6
+
7
+ const DEFAULT_SCOPE_SOURCE = "react-native.native_bridge";
8
+ const RESERVED_TRACE_METADATA_KEYS = new Set([
9
+ "parentSpanId",
10
+ "spanId",
11
+ "traceFlags",
12
+ "traceId",
13
+ "traceSampled",
14
+ "traceparent"
15
+ ]);
16
+
17
+ export function createLogBrewNativeBridgeScope({
18
+ logger,
19
+ metadata = {},
20
+ screen,
21
+ sessionId,
22
+ source = DEFAULT_SCOPE_SOURCE,
23
+ trace = getActiveLogBrewTrace()
24
+ } = {}) {
25
+ const traceMetadata = getReactNativeTraceMetadata(trace);
26
+ return {
27
+ trace: Object.keys(traceMetadata).length === 0 ? undefined : {
28
+ parentSpanId: traceMetadata.parentSpanId,
29
+ spanId: traceMetadata.spanId,
30
+ traceFlags: traceMetadata.traceFlags,
31
+ traceId: traceMetadata.traceId,
32
+ traceSampled: traceMetadata.traceSampled
33
+ },
34
+ metadata: compactMetadata({
35
+ ...metadata,
36
+ logger,
37
+ screen,
38
+ sessionId,
39
+ source
40
+ })
41
+ };
42
+ }
43
+
44
+ export function syncLogBrewNativeBridgeScope(nativeBridge, options = {}) {
45
+ const payload = createLogBrewNativeBridgeScope(options);
46
+ bridgeSync(nativeBridge)(payload);
47
+ return payload;
48
+ }
49
+
50
+ export function clearLogBrewNativeBridgeScope(nativeBridge) {
51
+ bridgeClear(nativeBridge)();
52
+ }
53
+
54
+ export function withLogBrewNativeBridgeScope(nativeBridge, options, callback) {
55
+ const resolvedOptions = typeof options === "function" ? {} : options ?? {};
56
+ const resolvedCallback = typeof options === "function" ? options : callback;
57
+ if (typeof resolvedCallback !== "function") {
58
+ throw new SdkError("configuration_error", "withLogBrewNativeBridgeScope requires a callback");
59
+ }
60
+
61
+ const payload = syncLogBrewNativeBridgeScope(nativeBridge, resolvedOptions);
62
+ try {
63
+ const result = resolvedCallback(payload);
64
+ if (result && typeof result.then === "function") {
65
+ return Promise.resolve(result).finally(() => clearLogBrewNativeBridgeScope(nativeBridge));
66
+ }
67
+ clearLogBrewNativeBridgeScope(nativeBridge);
68
+ return result;
69
+ } catch (error) {
70
+ clearLogBrewNativeBridgeScope(nativeBridge);
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ function bridgeSync(nativeBridge) {
76
+ if (typeof nativeBridge === "function") {
77
+ return nativeBridge;
78
+ }
79
+ if (nativeBridge && typeof nativeBridge.setLogBrewScope === "function") {
80
+ return nativeBridge.setLogBrewScope.bind(nativeBridge);
81
+ }
82
+ if (nativeBridge && typeof nativeBridge.syncLogBrewScope === "function") {
83
+ return nativeBridge.syncLogBrewScope.bind(nativeBridge);
84
+ }
85
+ throw new SdkError(
86
+ "configuration_error",
87
+ "LogBrew native bridge scope sync requires a function, setLogBrewScope, or syncLogBrewScope"
88
+ );
89
+ }
90
+
91
+ function bridgeClear(nativeBridge) {
92
+ if (nativeBridge && typeof nativeBridge.clearLogBrewScope === "function") {
93
+ return nativeBridge.clearLogBrewScope.bind(nativeBridge);
94
+ }
95
+ if (nativeBridge && typeof nativeBridge.clearLogBrewTraceContext === "function") {
96
+ return nativeBridge.clearLogBrewTraceContext.bind(nativeBridge);
97
+ }
98
+ if (typeof nativeBridge === "function") {
99
+ return () => nativeBridge(undefined);
100
+ }
101
+ return () => {};
102
+ }
103
+
104
+ function compactMetadata(metadata) {
105
+ const compacted = {};
106
+ for (const [key, value] of Object.entries(metadata)) {
107
+ if (value === undefined) {
108
+ continue;
109
+ }
110
+ if (RESERVED_TRACE_METADATA_KEYS.has(key)) {
111
+ continue;
112
+ }
113
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
114
+ compacted[key] = value;
115
+ }
116
+ }
117
+ return compacted;
118
+ }
119
+
120
+ export default {
121
+ clearLogBrewNativeBridgeScope,
122
+ createLogBrewNativeBridgeScope,
123
+ syncLogBrewNativeBridgeScope,
124
+ withLogBrewNativeBridgeScope
125
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@logbrew/react-native",
3
- "version": "0.1.0",
4
- "description": "React Native helpers for the public LogBrew JavaScript SDK.",
3
+ "version": "0.1.2",
4
+ "description": "React Native screen, error, trace, action, and network timeline helpers for LogBrew.",
5
5
  "type": "module",
6
6
  "main": "./index.cjs",
7
7
  "module": "./index.js",
@@ -19,6 +19,94 @@
19
19
  "default": "./index.cjs"
20
20
  },
21
21
  "default": "./index.js"
22
+ },
23
+ "./lifecycle": {
24
+ "import": {
25
+ "types": "./lifecycle.d.ts",
26
+ "default": "./lifecycle.js"
27
+ },
28
+ "require": {
29
+ "types": "./lifecycle.d.cts",
30
+ "default": "./lifecycle.cjs"
31
+ },
32
+ "default": "./lifecycle.js"
33
+ },
34
+ "./global-errors": {
35
+ "import": {
36
+ "types": "./global-errors.d.ts",
37
+ "default": "./global-errors.js"
38
+ },
39
+ "require": {
40
+ "types": "./global-errors.d.cts",
41
+ "default": "./global-errors.cjs"
42
+ },
43
+ "default": "./global-errors.js"
44
+ },
45
+ "./instrumentation": {
46
+ "import": {
47
+ "types": "./instrumentation.d.ts",
48
+ "default": "./instrumentation.js"
49
+ },
50
+ "require": {
51
+ "types": "./instrumentation.d.cts",
52
+ "default": "./instrumentation.cjs"
53
+ },
54
+ "default": "./instrumentation.js"
55
+ },
56
+ "./native-bridge": {
57
+ "import": {
58
+ "types": "./native-bridge.d.ts",
59
+ "default": "./native-bridge.js"
60
+ },
61
+ "require": {
62
+ "types": "./native-bridge.d.cts",
63
+ "default": "./native-bridge.cjs"
64
+ },
65
+ "default": "./native-bridge.js"
66
+ },
67
+ "./apollo": {
68
+ "import": {
69
+ "types": "./apollo.d.ts",
70
+ "default": "./apollo.js"
71
+ },
72
+ "require": {
73
+ "types": "./apollo.d.cts",
74
+ "default": "./apollo.cjs"
75
+ },
76
+ "default": "./apollo.js"
77
+ },
78
+ "./resource-fetch": {
79
+ "import": {
80
+ "types": "./resource-fetch.d.ts",
81
+ "default": "./resource-fetch.js"
82
+ },
83
+ "require": {
84
+ "types": "./resource-fetch.d.cts",
85
+ "default": "./resource-fetch.cjs"
86
+ },
87
+ "default": "./resource-fetch.js"
88
+ },
89
+ "./release-artifacts": {
90
+ "import": {
91
+ "types": "./release-artifacts.d.ts",
92
+ "default": "./release-artifacts.js"
93
+ },
94
+ "require": {
95
+ "types": "./release-artifacts.d.cts",
96
+ "default": "./release-artifacts.cjs"
97
+ },
98
+ "default": "./release-artifacts.js"
99
+ },
100
+ "./metro": {
101
+ "import": {
102
+ "types": "./metro.d.ts",
103
+ "default": "./metro.js"
104
+ },
105
+ "require": {
106
+ "types": "./metro.d.cts",
107
+ "default": "./metro.cjs"
108
+ },
109
+ "default": "./metro.js"
22
110
  }
23
111
  },
24
112
  "files": [
@@ -27,6 +115,40 @@
27
115
  "index.native.js",
28
116
  "index.d.ts",
29
117
  "index.d.cts",
118
+ "apollo.cjs",
119
+ "apollo.js",
120
+ "apollo.d.ts",
121
+ "apollo.d.cts",
122
+ "instrumentation.cjs",
123
+ "instrumentation.js",
124
+ "instrumentation.d.ts",
125
+ "instrumentation.d.cts",
126
+ "lifecycle.cjs",
127
+ "lifecycle.js",
128
+ "lifecycle.d.ts",
129
+ "lifecycle.d.cts",
130
+ "global-errors.cjs",
131
+ "global-errors.js",
132
+ "global-errors.d.ts",
133
+ "global-errors.d.cts",
134
+ "metadata.cjs",
135
+ "metadata.js",
136
+ "native-bridge.cjs",
137
+ "native-bridge.js",
138
+ "native-bridge.d.ts",
139
+ "native-bridge.d.cts",
140
+ "resource-fetch.cjs",
141
+ "resource-fetch.js",
142
+ "resource-fetch.d.ts",
143
+ "resource-fetch.d.cts",
144
+ "release-artifacts.cjs",
145
+ "release-artifacts.js",
146
+ "release-artifacts.d.ts",
147
+ "release-artifacts.d.cts",
148
+ "metro.cjs",
149
+ "metro.js",
150
+ "metro.d.ts",
151
+ "metro.d.cts",
30
152
  "README.md",
31
153
  "examples"
32
154
  ],
@@ -34,10 +156,12 @@
34
156
  "logbrew",
35
157
  "react-native",
36
158
  "mobile",
159
+ "metro",
37
160
  "observability",
38
161
  "logs",
39
162
  "traces",
40
- "events"
163
+ "events",
164
+ "source-maps"
41
165
  ],
42
166
  "sideEffects": false,
43
167
  "engines": {
@@ -54,6 +178,6 @@
54
178
  "react-native": ">=0.72"
55
179
  },
56
180
  "scripts": {
57
- "test": "node --check index.js && node --check index.cjs && node --check index.native.js && node --check examples/index.mjs && node --check examples/readme-example.mjs && node --check examples/real-user-smoke.mjs"
181
+ "test": "node --check index.js && node --check index.cjs && node --check index.native.js && node --check apollo.js && node --check apollo.cjs && node --check instrumentation.js && node --check instrumentation.cjs && node --check lifecycle.js && node --check lifecycle.cjs && node --check global-errors.js && node --check global-errors.cjs && node --check metadata.js && node --check metadata.cjs && node --check native-bridge.js && node --check native-bridge.cjs && node --check resource-fetch.js && node --check resource-fetch.cjs && node --check release-artifacts.js && node --check release-artifacts.cjs && node --check metro.js && node --check metro.cjs && node --check examples/index.mjs && node --check examples/apollo-link-spans.mjs && node --check examples/instrumentation-kit.mjs && node --check examples/lifecycle-spans.mjs && node --check examples/native-bridge-scope.mjs && node --check examples/navigation-resource-spans.mjs && node --check examples/readme-example.mjs && node --check examples/real-user-smoke.mjs && node --check examples/resource-fetch-spans.mjs && node --check examples/trace-correlation.mjs && node --test"
58
182
  }
59
183
  }
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+
3
+ const { spawnSync } = require("node:child_process");
4
+ const fs = require("node:fs");
5
+ const net = require("node:net");
6
+ const path = require("node:path");
7
+
8
+ const PACKAGE_DIR = path.dirname(require.resolve("./package.json"));
9
+ const DEFAULT_MANIFEST_NAME = "logbrew-release-artifacts.json";
10
+ const SUPPORTED_PLATFORMS = new Set(["android", "ios"]);
11
+ const SOURCE_MAPPING_COMMENT_RE = /(?:\/\/#|\/\*#)\s*sourceMappingURL=[^\r\n]*/giu;
12
+
13
+ function requiredString(options, name) {
14
+ const value = options?.[name];
15
+ if (typeof value !== "string" || value.trim() === "") {
16
+ throw new Error(`LogBrew React Native release-artifact helper requires ${name}`);
17
+ }
18
+ return value.trim();
19
+ }
20
+
21
+ function optionalString(options, name) {
22
+ const value = options?.[name];
23
+ if (value === undefined || value === null) {
24
+ return null;
25
+ }
26
+ if (typeof value !== "string" || value.trim() === "") {
27
+ throw new Error(`LogBrew React Native release-artifact helper option ${name} must be a non-empty string`);
28
+ }
29
+ return value.trim();
30
+ }
31
+
32
+ function optionalUploadScalar(options, name) {
33
+ const value = options?.[name];
34
+ if (value === undefined || value === null) {
35
+ return null;
36
+ }
37
+ if (typeof value === "number") {
38
+ if (!Number.isFinite(value) || value < 0) {
39
+ throw new Error(`LogBrew React Native release-artifact helper option ${name} must be non-negative`);
40
+ }
41
+ return String(value);
42
+ }
43
+ if (typeof value !== "string" || value.trim() === "") {
44
+ throw new Error(`LogBrew React Native release-artifact helper option ${name} must be a non-empty string or number`);
45
+ }
46
+ return value.trim();
47
+ }
48
+
49
+ function normalizeStringArray(value, name) {
50
+ if (value === undefined || value === null) {
51
+ return [];
52
+ }
53
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim() === "")) {
54
+ throw new Error(`LogBrew React Native release-artifact helper option ${name} must be an array of non-empty strings`);
55
+ }
56
+ return value.map((item) => item.trim());
57
+ }
58
+
59
+ function normalizePlatform(options) {
60
+ const platform = requiredString(options, "platform").toLowerCase();
61
+ if (!SUPPORTED_PLATFORMS.has(platform)) {
62
+ throw new Error("LogBrew React Native release-artifact helper option platform must be android or ios");
63
+ }
64
+ return platform;
65
+ }
66
+
67
+ function parseJson(stdout, command) {
68
+ try {
69
+ return JSON.parse(stdout);
70
+ } catch (error) {
71
+ throw new Error(`LogBrew release-artifact ${command} did not return JSON`, { cause: error });
72
+ }
73
+ }
74
+
75
+ function validationErrors(report) {
76
+ if (report?.validation && Array.isArray(report.validation.errors)) {
77
+ return report.validation.errors.filter((message) => typeof message === "string" && message.trim() !== "");
78
+ }
79
+ return [];
80
+ }
81
+
82
+ function resolveLogBrewReleaseArtifactsCli() {
83
+ try {
84
+ const sdkEntry = require.resolve("@logbrew/sdk", { paths: [PACKAGE_DIR, process.cwd()] });
85
+ return path.join(path.dirname(sdkEntry), "release-artifacts.js");
86
+ } catch {
87
+ const sourceTreeCli = path.resolve(PACKAGE_DIR, "../logbrew-js/release-artifacts.js");
88
+ if (fs.existsSync(sourceTreeCli)) {
89
+ return sourceTreeCli;
90
+ }
91
+ throw new Error("LogBrew React Native release-artifact helper requires @logbrew/sdk to be installed");
92
+ }
93
+ }
94
+
95
+ function parseUploadEndpoint(endpoint) {
96
+ let parsed;
97
+ try {
98
+ parsed = new URL(endpoint);
99
+ } catch (error) {
100
+ throw new Error(`upload endpoint is not a valid URL: ${error.message}`, { cause: error });
101
+ }
102
+ if (!["http:", "https:"].includes(parsed.protocol)) {
103
+ throw new Error("release artifact upload proof endpoint must use http or https");
104
+ }
105
+ return parsed;
106
+ }
107
+
108
+ function isLoopbackUploadEndpoint(parsed) {
109
+ const hostname = parsed.hostname.toLowerCase();
110
+ return (
111
+ hostname === "localhost" ||
112
+ hostname === "[::1]" ||
113
+ hostname === "::1" ||
114
+ (net.isIP(hostname) !== 0 && (hostname.startsWith("127.") || hostname === "::1"))
115
+ );
116
+ }
117
+
118
+ function requireUploadEndpoint(endpoint, allowHosted) {
119
+ const parsed = parseUploadEndpoint(endpoint);
120
+ if (isLoopbackUploadEndpoint(parsed)) {
121
+ return;
122
+ }
123
+ if (!allowHosted) {
124
+ throw new Error("release artifact hosted upload requires explicit allowHostedUpload; use loopback endpoints for local proof");
125
+ }
126
+ if (parsed.protocol !== "https:") {
127
+ throw new Error("hosted release artifact upload endpoints must use https");
128
+ }
129
+ if (parsed.username || parsed.password) {
130
+ throw new Error("hosted release artifact upload endpoints must not include embedded auth values");
131
+ }
132
+ if (parsed.search || parsed.hash) {
133
+ throw new Error("hosted release artifact upload endpoints must not include query strings or fragments");
134
+ }
135
+ }
136
+
137
+ function runReleaseArtifactCli(command, args) {
138
+ const result = spawnSync(process.execPath, [resolveLogBrewReleaseArtifactsCli(), command, ...args], {
139
+ cwd: process.cwd(),
140
+ encoding: "utf8",
141
+ windowsHide: true,
142
+ });
143
+ const report = result.stdout ? parseJson(result.stdout, command) : null;
144
+ if (result.status !== 0) {
145
+ const details = validationErrors(report).join("; ") || result.stderr.trim() || `exit code ${result.status}`;
146
+ throw new Error(`LogBrew release-artifact ${command} failed: ${details}`);
147
+ }
148
+ return { report, stdout: result.stdout };
149
+ }
150
+
151
+ function resolvePathFromRoot(root, value) {
152
+ return path.isAbsolute(value) ? path.resolve(value) : path.resolve(root, value);
153
+ }
154
+
155
+ function requireExistingFile(label, filePath) {
156
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
157
+ throw new Error(`LogBrew React Native release-artifact helper ${label} does not exist: ${filePath}`);
158
+ }
159
+ }
160
+
161
+ function ensureInsideBuildDir(label, filePath, buildDir) {
162
+ const relative = path.relative(buildDir, filePath);
163
+ if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
164
+ throw new Error(`LogBrew React Native release-artifact helper ${label} must stay inside buildDir`);
165
+ }
166
+ return relative.split(path.sep).join("/");
167
+ }
168
+
169
+ function artifactForBundle(report, relativeBundlePath) {
170
+ return report?.artifacts?.find((artifact) => artifact?.path === relativeBundlePath) || null;
171
+ }
172
+
173
+ function sourceMapReferenceForBundle(bundlePath, sourcemapPath) {
174
+ return path.relative(path.dirname(bundlePath), sourcemapPath).split(path.sep).join("/");
175
+ }
176
+
177
+ function sourceWithSourceMappingUrl(source, reference) {
178
+ const comment = `//# sourceMappingURL=${reference}`;
179
+ const sourceWithoutMapComments = source.replace(SOURCE_MAPPING_COMMENT_RE, "").replace(/[ \t\r\n]*$/u, "");
180
+ return `${sourceWithoutMapComments}${sourceWithoutMapComments === "" ? "" : "\n"}${comment}\n`;
181
+ }
182
+
183
+ function applyExplicitSourceMapReference(bundlePath, sourcemapPath) {
184
+ const reference = sourceMapReferenceForBundle(bundlePath, sourcemapPath);
185
+ const source = fs.readFileSync(bundlePath, "utf8");
186
+ const updated = sourceWithSourceMappingUrl(source, reference);
187
+ if (updated !== source) {
188
+ fs.writeFileSync(bundlePath, updated, "utf8");
189
+ }
190
+ }
191
+
192
+ function defaultMinifiedPathPrefix(platform) {
193
+ return `app:///react-native/${platform}`;
194
+ }
195
+
196
+ function prepareLogBrewReactNativeReleaseArtifacts(options = {}) {
197
+ const release = requiredString(options, "release");
198
+ const environment = requiredString(options, "environment");
199
+ const service = requiredString(options, "service");
200
+ const projectId = optionalString(options, "projectId");
201
+ const platform = normalizePlatform(options);
202
+ const root = path.resolve(optionalString(options, "root") || process.cwd());
203
+ const bundlePath = resolvePathFromRoot(root, requiredString(options, "bundle"));
204
+ const sourcemapOption = optionalString(options, "sourcemap");
205
+ const sourcemapPath = sourcemapOption ? resolvePathFromRoot(root, sourcemapOption) : `${bundlePath}.map`;
206
+ const buildDirOption = optionalString(options, "buildDir");
207
+ const buildDir = buildDirOption ? resolvePathFromRoot(root, buildDirOption) : path.dirname(bundlePath);
208
+ const manifestPath = resolvePathFromRoot(
209
+ root,
210
+ optionalString(options, "manifestPath") || path.join(buildDir, DEFAULT_MANIFEST_NAME),
211
+ );
212
+ const minifiedPathPrefix = optionalString(options, "minifiedPathPrefix") || defaultMinifiedPathPrefix(platform);
213
+ const repositoryUrl = optionalString(options, "repositoryUrl");
214
+ const commitSha = optionalString(options, "commitSha");
215
+ const stripSourcesContent = options?.stripSourcesContent !== false;
216
+ const userSourcePrefixes = normalizeStringArray(options?.stripSourcePrefix, "stripSourcePrefix");
217
+ const sourcePrefixes = userSourcePrefixes.length > 0 ? userSourcePrefixes : [root];
218
+
219
+ requireExistingFile("bundle", bundlePath);
220
+ requireExistingFile("sourcemap", sourcemapPath);
221
+ const relativeBundlePath = ensureInsideBuildDir("bundle", bundlePath, buildDir);
222
+ const relativeSourceMapPath = ensureInsideBuildDir("sourcemap", sourcemapPath, buildDir);
223
+ applyExplicitSourceMapReference(bundlePath, sourcemapPath);
224
+
225
+ const prepareArgs = ["--build-dir", buildDir, "--write"];
226
+ if (stripSourcesContent) {
227
+ prepareArgs.push("--strip-sources-content");
228
+ }
229
+ for (const prefix of sourcePrefixes) {
230
+ prepareArgs.push("--strip-source-prefix", resolvePathFromRoot(root, prefix));
231
+ }
232
+
233
+ const { report: prepareReport } = runReleaseArtifactCli("prepare-js", prepareArgs);
234
+ const preparedArtifact = artifactForBundle(prepareReport, relativeBundlePath);
235
+ if (!preparedArtifact) {
236
+ throw new Error(`LogBrew React Native release-artifact helper did not find bundle: ${relativeBundlePath}`);
237
+ }
238
+ if (preparedArtifact.sourceMapPath !== relativeSourceMapPath) {
239
+ throw new Error(
240
+ `LogBrew React Native release-artifact helper expected source map ${relativeSourceMapPath} for ${relativeBundlePath}`,
241
+ );
242
+ }
243
+
244
+ const manifestArgs = [
245
+ "--build-dir",
246
+ buildDir,
247
+ "--release",
248
+ release,
249
+ "--environment",
250
+ environment,
251
+ "--service",
252
+ service,
253
+ "--minified-path-prefix",
254
+ minifiedPathPrefix,
255
+ ];
256
+ if (projectId) {
257
+ manifestArgs.push("--project-id", projectId);
258
+ }
259
+ if (repositoryUrl) {
260
+ manifestArgs.push("--repository-url", repositoryUrl);
261
+ }
262
+ if (commitSha) {
263
+ manifestArgs.push("--commit-sha", commitSha);
264
+ }
265
+
266
+ const { report: manifestReport, stdout } = runReleaseArtifactCli("manifest-js", manifestArgs);
267
+ const manifestArtifact = manifestReport?.artifacts?.find(
268
+ (artifact) => artifact?.minifiedSource?.path === relativeBundlePath,
269
+ );
270
+ if (!manifestArtifact) {
271
+ throw new Error(`LogBrew React Native release-artifact manifest did not include bundle: ${relativeBundlePath}`);
272
+ }
273
+
274
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
275
+ fs.writeFileSync(manifestPath, stdout, "utf8");
276
+
277
+ return {
278
+ buildDir,
279
+ bundlePath,
280
+ sourcemapPath,
281
+ manifestPath,
282
+ platform,
283
+ prepareReport,
284
+ manifestReport,
285
+ };
286
+ }
287
+
288
+ function uploadLogBrewReactNativeReleaseArtifacts(options = {}) {
289
+ const endpoint = requiredString(options, "endpoint");
290
+ const allowHostedUpload = options?.allowHostedUpload === true;
291
+ if (options?.allowHostedUpload !== undefined && typeof options.allowHostedUpload !== "boolean") {
292
+ throw new Error("LogBrew React Native release-artifact helper option allowHostedUpload must be a boolean");
293
+ }
294
+ requireUploadEndpoint(endpoint, allowHostedUpload);
295
+ const tokenEnv = optionalString(options, "tokenEnv");
296
+ const maxRetries = optionalUploadScalar(options, "maxRetries");
297
+ const retryDelay = optionalUploadScalar(options, "retryDelay");
298
+ const timeout = optionalUploadScalar(options, "timeout");
299
+ const dryRun = options?.dryRun;
300
+ if (dryRun !== undefined && typeof dryRun !== "boolean") {
301
+ throw new Error("LogBrew React Native release-artifact helper option dryRun must be a boolean");
302
+ }
303
+
304
+ const prepared = prepareLogBrewReactNativeReleaseArtifacts(options);
305
+ const uploadArgs = [
306
+ "--build-dir",
307
+ prepared.buildDir,
308
+ "--manifest",
309
+ prepared.manifestPath,
310
+ "--endpoint",
311
+ endpoint,
312
+ ];
313
+
314
+ if (tokenEnv) {
315
+ uploadArgs.push("--token-env", tokenEnv);
316
+ }
317
+ if (dryRun === true) {
318
+ uploadArgs.push("--dry-run");
319
+ }
320
+ if (allowHostedUpload) {
321
+ uploadArgs.push("--allow-hosted");
322
+ }
323
+ if (maxRetries) {
324
+ uploadArgs.push("--max-retries", maxRetries);
325
+ }
326
+ if (retryDelay) {
327
+ uploadArgs.push("--retry-delay", retryDelay);
328
+ }
329
+ if (timeout) {
330
+ uploadArgs.push("--timeout", timeout);
331
+ }
332
+
333
+ const { report: uploadReport } = runReleaseArtifactCli("upload-js", uploadArgs);
334
+ return {
335
+ ...prepared,
336
+ uploadReport,
337
+ };
338
+ }
339
+
340
+ module.exports = {
341
+ prepareLogBrewReactNativeReleaseArtifacts,
342
+ uploadLogBrewReactNativeReleaseArtifacts,
343
+ default: prepareLogBrewReactNativeReleaseArtifacts,
344
+ };
@@ -0,0 +1,55 @@
1
+ export type LogBrewReactNativeReleaseArtifactPlatform = "android" | "ios";
2
+
3
+ export type LogBrewReactNativeReleaseArtifactsOptions = {
4
+ bundle: string;
5
+ platform: LogBrewReactNativeReleaseArtifactPlatform;
6
+ release: string;
7
+ environment: string;
8
+ service: string;
9
+ projectId?: string;
10
+ sourcemap?: string;
11
+ root?: string;
12
+ buildDir?: string;
13
+ manifestPath?: string;
14
+ minifiedPathPrefix?: string;
15
+ repositoryUrl?: string;
16
+ commitSha?: string;
17
+ stripSourcesContent?: boolean;
18
+ stripSourcePrefix?: string[];
19
+ };
20
+
21
+ export type LogBrewReactNativeReleaseArtifactsResult = {
22
+ buildDir: string;
23
+ bundlePath: string;
24
+ sourcemapPath: string;
25
+ manifestPath: string;
26
+ platform: LogBrewReactNativeReleaseArtifactPlatform;
27
+ prepareReport: Record<string, unknown>;
28
+ manifestReport: Record<string, unknown>;
29
+ };
30
+
31
+ export type LogBrewReactNativeReleaseArtifactUploadOptions = LogBrewReactNativeReleaseArtifactsOptions & {
32
+ endpoint: string;
33
+ allowHostedUpload?: boolean;
34
+ tokenEnv?: string;
35
+ dryRun?: boolean;
36
+ maxRetries?: string | number;
37
+ retryDelay?: string | number;
38
+ timeout?: string | number;
39
+ };
40
+
41
+ export type LogBrewReactNativeReleaseArtifactUploadResult = LogBrewReactNativeReleaseArtifactsResult & {
42
+ uploadReport: Record<string, unknown>;
43
+ };
44
+
45
+ export declare function prepareLogBrewReactNativeReleaseArtifacts(
46
+ options: LogBrewReactNativeReleaseArtifactsOptions
47
+ ): LogBrewReactNativeReleaseArtifactsResult;
48
+
49
+ export declare function uploadLogBrewReactNativeReleaseArtifacts(
50
+ options: LogBrewReactNativeReleaseArtifactUploadOptions
51
+ ): LogBrewReactNativeReleaseArtifactUploadResult;
52
+
53
+ declare const defaultExport: typeof prepareLogBrewReactNativeReleaseArtifacts;
54
+
55
+ export default defaultExport;