@allstak/react-native 0.1.3 → 0.3.1

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.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Source-map upload pipeline for React Native (Metro / Hermes). Build-time only.
3
+ *
4
+ * Metro emits one bundle + one source map per build, so the React Native
5
+ * flow is simpler than the React-web flow (no `dist/` walk needed). The
6
+ * developer hands us the bundle + map paths produced by:
7
+ *
8
+ * npx react-native bundle \
9
+ * --platform ios --dev false --entry-file index.js \
10
+ * --bundle-output ios.bundle \
11
+ * --sourcemap-output ios.bundle.map
12
+ *
13
+ * If Hermes is enabled, the resulting `.hbc` bytecode replaces the JS
14
+ * bundle on the device, and the user must compose the Metro map with the
15
+ * Hermes map BEFORE uploading. We accept whatever map the user gives us
16
+ * and inject a debug-id into both the map and the bundle.
17
+ *
18
+ * Usage from a build script (`scripts/upload-sourcemaps.js`):
19
+ *
20
+ * const { uploadReactNativeSourcemap } = require('@allstak/react-native/sourcemaps');
21
+ *
22
+ * await uploadReactNativeSourcemap({
23
+ * bundle: 'ios.bundle',
24
+ * sourcemap: 'ios.bundle.map',
25
+ * release: 'mobile@1.2.3',
26
+ * dist: 'ios-hermes',
27
+ * token: process.env.ALLSTAK_UPLOAD_TOKEN,
28
+ * });
29
+ *
30
+ * Or `injectOnly: true` to add the debug-id without uploading (useful in
31
+ * CI dry-runs or when you don't have an upload token yet).
32
+ */
33
+ declare const DEFAULT_HOST = "https://api.allstak.sa";
34
+ interface UploadReactNativeSourcemapOptions {
35
+ /** Path to the JS/Hermes-bytecode bundle (Metro's `--bundle-output`). */
36
+ bundle: string;
37
+ /** Path to the matching `.map` (Metro's `--sourcemap-output`). */
38
+ sourcemap: string;
39
+ /** Release identifier (e.g. `mobile@1.2.3` — match `release` in AllStak.init). */
40
+ release: string;
41
+ /**
42
+ * Distribution tag — recommended values: `ios-hermes`, `android-hermes`,
43
+ * `ios-jsc`, `android-jsc`. Match the `dist` the SDK auto-detects at
44
+ * runtime (see `src/install.ts`). Required for the symbolicator to
45
+ * pick the right map per platform.
46
+ */
47
+ dist?: string;
48
+ /** Project upload token (`aspk_…`). Defaults to `ALLSTAK_UPLOAD_TOKEN`. */
49
+ token?: string;
50
+ /** Override ingest host. Defaults to `ALLSTAK_HOST` or production. */
51
+ host?: string;
52
+ /** Drop `sourcesContent` from the map before upload (smaller payload). */
53
+ stripSources?: boolean;
54
+ /** Also upload the JS/HBC bundle alongside the map. Off by default. */
55
+ uploadBundle?: boolean;
56
+ /** Inject debug-id but skip the upload (CI dry-run). */
57
+ injectOnly?: boolean;
58
+ /** Pre-existing debug-id to use instead of generating one. Optional. */
59
+ debugId?: string;
60
+ /** Suppress per-step console output. Default false. */
61
+ silent?: boolean;
62
+ }
63
+ interface UploadReactNativeSourcemapResult {
64
+ /** The debug-id injected into the bundle + map. */
65
+ debugId: string;
66
+ /** True if the bundle already had a debug-id; we reused it. */
67
+ reused: boolean;
68
+ /** True when upload(s) succeeded — undefined when `injectOnly: true`. */
69
+ uploaded?: boolean;
70
+ /** Per-artifact responses, in the order we sent them. */
71
+ steps?: Array<{
72
+ type: 'sourcemap' | 'bundle';
73
+ status: number;
74
+ sha8: string;
75
+ body?: string;
76
+ }>;
77
+ }
78
+ /**
79
+ * Inject a debug-id into the bundle + map (idempotent — reuses an
80
+ * existing id if present).
81
+ */
82
+ declare function injectReactNativeSourcemap(opts: Pick<UploadReactNativeSourcemapOptions, 'bundle' | 'sourcemap' | 'debugId'>): {
83
+ debugId: string;
84
+ reused: boolean;
85
+ };
86
+ /**
87
+ * Inject debug-id into the bundle + map and (optionally) upload.
88
+ *
89
+ * Returns the debug-id (so the caller can stash it for symbolicator
90
+ * lookups), whether it was reused, and per-step upload statuses.
91
+ */
92
+ declare function uploadReactNativeSourcemap(opts: UploadReactNativeSourcemapOptions): Promise<UploadReactNativeSourcemapResult>;
93
+
94
+ export { DEFAULT_HOST, type UploadReactNativeSourcemapOptions, type UploadReactNativeSourcemapResult, injectReactNativeSourcemap, uploadReactNativeSourcemap };
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/build/sourcemaps.ts
21
+ var sourcemaps_exports = {};
22
+ __export(sourcemaps_exports, {
23
+ DEFAULT_HOST: () => DEFAULT_HOST,
24
+ injectReactNativeSourcemap: () => injectReactNativeSourcemap,
25
+ uploadReactNativeSourcemap: () => uploadReactNativeSourcemap
26
+ });
27
+ module.exports = __toCommonJS(sourcemaps_exports);
28
+ var import_node_fs = require("fs");
29
+ var import_node_crypto = require("crypto");
30
+ var import_node_path = require("path");
31
+ var DEFAULT_HOST = "https://api.allstak.sa";
32
+ var DEBUG_ID_LINE_RE = /^\/\/# debugId=([0-9a-f-]{36})\s*$/m;
33
+ function sha8(buf) {
34
+ return (0, import_node_crypto.createHash)("sha256").update(buf).digest("hex").slice(0, 8);
35
+ }
36
+ function injectReactNativeSourcemap(opts) {
37
+ const bundleRaw = (0, import_node_fs.readFileSync)(opts.bundle, "utf8");
38
+ const mapRaw = (0, import_node_fs.readFileSync)(opts.sourcemap, "utf8");
39
+ const map = JSON.parse(mapRaw);
40
+ let debugId = opts.debugId ?? "";
41
+ if (!debugId && typeof map.debugId === "string") debugId = map.debugId;
42
+ const existing = DEBUG_ID_LINE_RE.exec(bundleRaw);
43
+ if (!debugId && existing && existing[1]) debugId = existing[1];
44
+ const reused = !!debugId;
45
+ if (!debugId) debugId = (0, import_node_crypto.randomUUID)();
46
+ map.debugId = debugId;
47
+ (0, import_node_fs.writeFileSync)(opts.sourcemap, JSON.stringify(map));
48
+ let bundleOut = bundleRaw.replace(DEBUG_ID_LINE_RE, "");
49
+ bundleOut = bundleOut.replace(/\s+$/, "");
50
+ bundleOut += `
51
+ //# debugId=${debugId}
52
+ `;
53
+ (0, import_node_fs.writeFileSync)(opts.bundle, bundleOut);
54
+ return { debugId, reused };
55
+ }
56
+ async function uploadOne(type, filePath, debugId, release, host, token, dist, stripSources) {
57
+ let buf = (0, import_node_fs.readFileSync)(filePath);
58
+ if (type === "sourcemap" && stripSources) {
59
+ const json = JSON.parse(buf.toString("utf8"));
60
+ if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;
61
+ buf = Buffer.from(JSON.stringify(json));
62
+ }
63
+ const form = new FormData();
64
+ form.append("debugId", debugId);
65
+ form.append("type", type);
66
+ form.append("release", release);
67
+ if (dist) form.append("dist", dist);
68
+ form.append(
69
+ "file",
70
+ new Blob([buf], {
71
+ type: type === "sourcemap" ? "application/json" : "application/javascript"
72
+ }),
73
+ (0, import_node_path.basename)(filePath)
74
+ );
75
+ const res = await fetch(host.replace(/\/$/, "") + "/api/v1/artifacts/upload", {
76
+ method: "POST",
77
+ headers: { "X-AllStak-Upload-Token": token },
78
+ body: form
79
+ });
80
+ return { status: res.status, body: await res.text(), ok: res.ok };
81
+ }
82
+ async function uploadReactNativeSourcemap(opts) {
83
+ const log = opts.silent ? () => void 0 : (m) => console.log(`[allstak/sourcemaps] ${m}`);
84
+ const inject = injectReactNativeSourcemap(opts);
85
+ log(`bundle: ${(0, import_node_path.basename)(opts.bundle)} debugId: ${inject.debugId} ${inject.reused ? "(reused)" : "(new)"}`);
86
+ const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;
87
+ if (opts.injectOnly || !token) {
88
+ if (!opts.injectOnly && !token) {
89
+ log("skipping upload \u2014 no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)");
90
+ }
91
+ return inject;
92
+ }
93
+ const host = opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST;
94
+ const stripSources = opts.stripSources ?? false;
95
+ const steps = [];
96
+ const mapResult = await uploadOne(
97
+ "sourcemap",
98
+ opts.sourcemap,
99
+ inject.debugId,
100
+ opts.release,
101
+ host,
102
+ token,
103
+ opts.dist,
104
+ stripSources
105
+ );
106
+ steps.push({
107
+ type: "sourcemap",
108
+ status: mapResult.status,
109
+ sha8: sha8((0, import_node_fs.readFileSync)(opts.sourcemap)),
110
+ body: mapResult.ok ? void 0 : mapResult.body
111
+ });
112
+ log(` sourcemap \u2192 ${mapResult.status}${mapResult.ok ? "" : " " + mapResult.body.slice(0, 120)}`);
113
+ let allOk = mapResult.ok;
114
+ if (opts.uploadBundle) {
115
+ const bundleResult = await uploadOne(
116
+ "bundle",
117
+ opts.bundle,
118
+ inject.debugId,
119
+ opts.release,
120
+ host,
121
+ token,
122
+ opts.dist,
123
+ false
124
+ );
125
+ steps.push({
126
+ type: "bundle",
127
+ status: bundleResult.status,
128
+ sha8: sha8((0, import_node_fs.readFileSync)(opts.bundle)),
129
+ body: bundleResult.ok ? void 0 : bundleResult.body
130
+ });
131
+ allOk = allOk && bundleResult.ok;
132
+ log(` bundle \u2192 ${bundleResult.status}${bundleResult.ok ? "" : " " + bundleResult.body.slice(0, 120)}`);
133
+ }
134
+ return { ...inject, uploaded: allOk, steps };
135
+ }
136
+ // Annotate the CommonJS export names for ESM import in node:
137
+ 0 && (module.exports = {
138
+ DEFAULT_HOST,
139
+ injectReactNativeSourcemap,
140
+ uploadReactNativeSourcemap
141
+ });
142
+ //# sourceMappingURL=sourcemaps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/build/sourcemaps.ts"],"sourcesContent":["/**\n * Source-map upload pipeline for React Native (Metro / Hermes). Build-time only.\n *\n * Metro emits one bundle + one source map per build, so the React Native\n * flow is simpler than the React-web flow (no `dist/` walk needed). The\n * developer hands us the bundle + map paths produced by:\n *\n * npx react-native bundle \\\n * --platform ios --dev false --entry-file index.js \\\n * --bundle-output ios.bundle \\\n * --sourcemap-output ios.bundle.map\n *\n * If Hermes is enabled, the resulting `.hbc` bytecode replaces the JS\n * bundle on the device, and the user must compose the Metro map with the\n * Hermes map BEFORE uploading. We accept whatever map the user gives us\n * and inject a debug-id into both the map and the bundle.\n *\n * Usage from a build script (`scripts/upload-sourcemaps.js`):\n *\n * const { uploadReactNativeSourcemap } = require('@allstak/react-native/sourcemaps');\n *\n * await uploadReactNativeSourcemap({\n * bundle: 'ios.bundle',\n * sourcemap: 'ios.bundle.map',\n * release: 'mobile@1.2.3',\n * dist: 'ios-hermes',\n * token: process.env.ALLSTAK_UPLOAD_TOKEN,\n * });\n *\n * Or `injectOnly: true` to add the debug-id without uploading (useful in\n * CI dry-runs or when you don't have an upload token yet).\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { randomUUID, createHash } from 'node:crypto';\nimport { basename } from 'node:path';\n\nexport const DEFAULT_HOST = 'https://api.allstak.sa';\n\nconst DEBUG_ID_LINE_RE = /^\\/\\/# debugId=([0-9a-f-]{36})\\s*$/m;\n\nexport interface UploadReactNativeSourcemapOptions {\n /** Path to the JS/Hermes-bytecode bundle (Metro's `--bundle-output`). */\n bundle: string;\n /** Path to the matching `.map` (Metro's `--sourcemap-output`). */\n sourcemap: string;\n /** Release identifier (e.g. `mobile@1.2.3` — match `release` in AllStak.init). */\n release: string;\n /**\n * Distribution tag — recommended values: `ios-hermes`, `android-hermes`,\n * `ios-jsc`, `android-jsc`. Match the `dist` the SDK auto-detects at\n * runtime (see `src/install.ts`). Required for the symbolicator to\n * pick the right map per platform.\n */\n dist?: string;\n /** Project upload token (`aspk_…`). Defaults to `ALLSTAK_UPLOAD_TOKEN`. */\n token?: string;\n /** Override ingest host. Defaults to `ALLSTAK_HOST` or production. */\n host?: string;\n /** Drop `sourcesContent` from the map before upload (smaller payload). */\n stripSources?: boolean;\n /** Also upload the JS/HBC bundle alongside the map. Off by default. */\n uploadBundle?: boolean;\n /** Inject debug-id but skip the upload (CI dry-run). */\n injectOnly?: boolean;\n /** Pre-existing debug-id to use instead of generating one. Optional. */\n debugId?: string;\n /** Suppress per-step console output. Default false. */\n silent?: boolean;\n}\n\nexport interface UploadReactNativeSourcemapResult {\n /** The debug-id injected into the bundle + map. */\n debugId: string;\n /** True if the bundle already had a debug-id; we reused it. */\n reused: boolean;\n /** True when upload(s) succeeded — undefined when `injectOnly: true`. */\n uploaded?: boolean;\n /** Per-artifact responses, in the order we sent them. */\n steps?: Array<{ type: 'sourcemap' | 'bundle'; status: number; sha8: string; body?: string }>;\n}\n\nfunction sha8(buf: Buffer): string {\n return createHash('sha256').update(buf).digest('hex').slice(0, 8);\n}\n\n/**\n * Inject a debug-id into the bundle + map (idempotent — reuses an\n * existing id if present).\n */\nexport function injectReactNativeSourcemap(\n opts: Pick<UploadReactNativeSourcemapOptions, 'bundle' | 'sourcemap' | 'debugId'>,\n): { debugId: string; reused: boolean } {\n const bundleRaw = readFileSync(opts.bundle, 'utf8');\n const mapRaw = readFileSync(opts.sourcemap, 'utf8');\n const map = JSON.parse(mapRaw) as { debugId?: unknown; [k: string]: unknown };\n\n let debugId = opts.debugId ?? '';\n if (!debugId && typeof map.debugId === 'string') debugId = map.debugId;\n const existing = DEBUG_ID_LINE_RE.exec(bundleRaw);\n if (!debugId && existing && existing[1]) debugId = existing[1];\n const reused = !!debugId;\n if (!debugId) debugId = randomUUID();\n\n // Re-stringify map with canonical debugId field.\n map.debugId = debugId;\n writeFileSync(opts.sourcemap, JSON.stringify(map));\n\n // Append `//# debugId=…` to the bundle (idempotent — strip prior line first).\n let bundleOut = bundleRaw.replace(DEBUG_ID_LINE_RE, '');\n bundleOut = bundleOut.replace(/\\s+$/, '');\n bundleOut += `\\n//# debugId=${debugId}\\n`;\n writeFileSync(opts.bundle, bundleOut);\n\n return { debugId, reused };\n}\n\nasync function uploadOne(\n type: 'sourcemap' | 'bundle',\n filePath: string,\n debugId: string,\n release: string,\n host: string,\n token: string,\n dist: string | undefined,\n stripSources: boolean,\n): Promise<{ status: number; body: string; ok: boolean }> {\n let buf = readFileSync(filePath);\n if (type === 'sourcemap' && stripSources) {\n const json = JSON.parse(buf.toString('utf8')) as { sourcesContent?: unknown };\n if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;\n buf = Buffer.from(JSON.stringify(json));\n }\n\n const form = new FormData();\n form.append('debugId', debugId);\n form.append('type', type);\n form.append('release', release);\n if (dist) form.append('dist', dist);\n form.append(\n 'file',\n new Blob([buf], {\n type: type === 'sourcemap' ? 'application/json' : 'application/javascript',\n }),\n basename(filePath),\n );\n\n const res = await fetch(host.replace(/\\/$/, '') + '/api/v1/artifacts/upload', {\n method: 'POST',\n headers: { 'X-AllStak-Upload-Token': token },\n body: form,\n });\n return { status: res.status, body: await res.text(), ok: res.ok };\n}\n\n/**\n * Inject debug-id into the bundle + map and (optionally) upload.\n *\n * Returns the debug-id (so the caller can stash it for symbolicator\n * lookups), whether it was reused, and per-step upload statuses.\n */\nexport async function uploadReactNativeSourcemap(\n opts: UploadReactNativeSourcemapOptions,\n): Promise<UploadReactNativeSourcemapResult> {\n const log = opts.silent ? () => undefined : (m: string) => console.log(`[allstak/sourcemaps] ${m}`);\n\n const inject = injectReactNativeSourcemap(opts);\n log(`bundle: ${basename(opts.bundle)} debugId: ${inject.debugId} ${inject.reused ? '(reused)' : '(new)'}`);\n\n const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;\n if (opts.injectOnly || !token) {\n if (!opts.injectOnly && !token) {\n log('skipping upload — no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)');\n }\n return inject;\n }\n\n const host = opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST;\n const stripSources = opts.stripSources ?? false;\n\n const steps: UploadReactNativeSourcemapResult['steps'] = [];\n const mapResult = await uploadOne(\n 'sourcemap', opts.sourcemap, inject.debugId, opts.release, host, token, opts.dist, stripSources,\n );\n steps.push({\n type: 'sourcemap',\n status: mapResult.status,\n sha8: sha8(readFileSync(opts.sourcemap)),\n body: mapResult.ok ? undefined : mapResult.body,\n });\n log(` sourcemap → ${mapResult.status}${mapResult.ok ? '' : ' ' + mapResult.body.slice(0, 120)}`);\n\n let allOk = mapResult.ok;\n if (opts.uploadBundle) {\n const bundleResult = await uploadOne(\n 'bundle', opts.bundle, inject.debugId, opts.release, host, token, opts.dist, false,\n );\n steps.push({\n type: 'bundle',\n status: bundleResult.status,\n sha8: sha8(readFileSync(opts.bundle)),\n body: bundleResult.ok ? undefined : bundleResult.body,\n });\n allOk = allOk && bundleResult.ok;\n log(` bundle → ${bundleResult.status}${bundleResult.ok ? '' : ' ' + bundleResult.body.slice(0, 120)}`);\n }\n\n return { ...inject, uploaded: allOk, steps };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,115 @@
1
+ // src/build/sourcemaps.ts
2
+ import { readFileSync, writeFileSync } from "fs";
3
+ import { randomUUID, createHash } from "crypto";
4
+ import { basename } from "path";
5
+ var DEFAULT_HOST = "https://api.allstak.sa";
6
+ var DEBUG_ID_LINE_RE = /^\/\/# debugId=([0-9a-f-]{36})\s*$/m;
7
+ function sha8(buf) {
8
+ return createHash("sha256").update(buf).digest("hex").slice(0, 8);
9
+ }
10
+ function injectReactNativeSourcemap(opts) {
11
+ const bundleRaw = readFileSync(opts.bundle, "utf8");
12
+ const mapRaw = readFileSync(opts.sourcemap, "utf8");
13
+ const map = JSON.parse(mapRaw);
14
+ let debugId = opts.debugId ?? "";
15
+ if (!debugId && typeof map.debugId === "string") debugId = map.debugId;
16
+ const existing = DEBUG_ID_LINE_RE.exec(bundleRaw);
17
+ if (!debugId && existing && existing[1]) debugId = existing[1];
18
+ const reused = !!debugId;
19
+ if (!debugId) debugId = randomUUID();
20
+ map.debugId = debugId;
21
+ writeFileSync(opts.sourcemap, JSON.stringify(map));
22
+ let bundleOut = bundleRaw.replace(DEBUG_ID_LINE_RE, "");
23
+ bundleOut = bundleOut.replace(/\s+$/, "");
24
+ bundleOut += `
25
+ //# debugId=${debugId}
26
+ `;
27
+ writeFileSync(opts.bundle, bundleOut);
28
+ return { debugId, reused };
29
+ }
30
+ async function uploadOne(type, filePath, debugId, release, host, token, dist, stripSources) {
31
+ let buf = readFileSync(filePath);
32
+ if (type === "sourcemap" && stripSources) {
33
+ const json = JSON.parse(buf.toString("utf8"));
34
+ if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;
35
+ buf = Buffer.from(JSON.stringify(json));
36
+ }
37
+ const form = new FormData();
38
+ form.append("debugId", debugId);
39
+ form.append("type", type);
40
+ form.append("release", release);
41
+ if (dist) form.append("dist", dist);
42
+ form.append(
43
+ "file",
44
+ new Blob([buf], {
45
+ type: type === "sourcemap" ? "application/json" : "application/javascript"
46
+ }),
47
+ basename(filePath)
48
+ );
49
+ const res = await fetch(host.replace(/\/$/, "") + "/api/v1/artifacts/upload", {
50
+ method: "POST",
51
+ headers: { "X-AllStak-Upload-Token": token },
52
+ body: form
53
+ });
54
+ return { status: res.status, body: await res.text(), ok: res.ok };
55
+ }
56
+ async function uploadReactNativeSourcemap(opts) {
57
+ const log = opts.silent ? () => void 0 : (m) => console.log(`[allstak/sourcemaps] ${m}`);
58
+ const inject = injectReactNativeSourcemap(opts);
59
+ log(`bundle: ${basename(opts.bundle)} debugId: ${inject.debugId} ${inject.reused ? "(reused)" : "(new)"}`);
60
+ const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;
61
+ if (opts.injectOnly || !token) {
62
+ if (!opts.injectOnly && !token) {
63
+ log("skipping upload \u2014 no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)");
64
+ }
65
+ return inject;
66
+ }
67
+ const host = opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST;
68
+ const stripSources = opts.stripSources ?? false;
69
+ const steps = [];
70
+ const mapResult = await uploadOne(
71
+ "sourcemap",
72
+ opts.sourcemap,
73
+ inject.debugId,
74
+ opts.release,
75
+ host,
76
+ token,
77
+ opts.dist,
78
+ stripSources
79
+ );
80
+ steps.push({
81
+ type: "sourcemap",
82
+ status: mapResult.status,
83
+ sha8: sha8(readFileSync(opts.sourcemap)),
84
+ body: mapResult.ok ? void 0 : mapResult.body
85
+ });
86
+ log(` sourcemap \u2192 ${mapResult.status}${mapResult.ok ? "" : " " + mapResult.body.slice(0, 120)}`);
87
+ let allOk = mapResult.ok;
88
+ if (opts.uploadBundle) {
89
+ const bundleResult = await uploadOne(
90
+ "bundle",
91
+ opts.bundle,
92
+ inject.debugId,
93
+ opts.release,
94
+ host,
95
+ token,
96
+ opts.dist,
97
+ false
98
+ );
99
+ steps.push({
100
+ type: "bundle",
101
+ status: bundleResult.status,
102
+ sha8: sha8(readFileSync(opts.bundle)),
103
+ body: bundleResult.ok ? void 0 : bundleResult.body
104
+ });
105
+ allOk = allOk && bundleResult.ok;
106
+ log(` bundle \u2192 ${bundleResult.status}${bundleResult.ok ? "" : " " + bundleResult.body.slice(0, 120)}`);
107
+ }
108
+ return { ...inject, uploaded: allOk, steps };
109
+ }
110
+ export {
111
+ DEFAULT_HOST,
112
+ injectReactNativeSourcemap,
113
+ uploadReactNativeSourcemap
114
+ };
115
+ //# sourceMappingURL=sourcemaps.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/build/sourcemaps.ts"],"sourcesContent":["/**\n * Source-map upload pipeline for React Native (Metro / Hermes). Build-time only.\n *\n * Metro emits one bundle + one source map per build, so the React Native\n * flow is simpler than the React-web flow (no `dist/` walk needed). The\n * developer hands us the bundle + map paths produced by:\n *\n * npx react-native bundle \\\n * --platform ios --dev false --entry-file index.js \\\n * --bundle-output ios.bundle \\\n * --sourcemap-output ios.bundle.map\n *\n * If Hermes is enabled, the resulting `.hbc` bytecode replaces the JS\n * bundle on the device, and the user must compose the Metro map with the\n * Hermes map BEFORE uploading. We accept whatever map the user gives us\n * and inject a debug-id into both the map and the bundle.\n *\n * Usage from a build script (`scripts/upload-sourcemaps.js`):\n *\n * const { uploadReactNativeSourcemap } = require('@allstak/react-native/sourcemaps');\n *\n * await uploadReactNativeSourcemap({\n * bundle: 'ios.bundle',\n * sourcemap: 'ios.bundle.map',\n * release: 'mobile@1.2.3',\n * dist: 'ios-hermes',\n * token: process.env.ALLSTAK_UPLOAD_TOKEN,\n * });\n *\n * Or `injectOnly: true` to add the debug-id without uploading (useful in\n * CI dry-runs or when you don't have an upload token yet).\n */\n\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { randomUUID, createHash } from 'node:crypto';\nimport { basename } from 'node:path';\n\nexport const DEFAULT_HOST = 'https://api.allstak.sa';\n\nconst DEBUG_ID_LINE_RE = /^\\/\\/# debugId=([0-9a-f-]{36})\\s*$/m;\n\nexport interface UploadReactNativeSourcemapOptions {\n /** Path to the JS/Hermes-bytecode bundle (Metro's `--bundle-output`). */\n bundle: string;\n /** Path to the matching `.map` (Metro's `--sourcemap-output`). */\n sourcemap: string;\n /** Release identifier (e.g. `mobile@1.2.3` — match `release` in AllStak.init). */\n release: string;\n /**\n * Distribution tag — recommended values: `ios-hermes`, `android-hermes`,\n * `ios-jsc`, `android-jsc`. Match the `dist` the SDK auto-detects at\n * runtime (see `src/install.ts`). Required for the symbolicator to\n * pick the right map per platform.\n */\n dist?: string;\n /** Project upload token (`aspk_…`). Defaults to `ALLSTAK_UPLOAD_TOKEN`. */\n token?: string;\n /** Override ingest host. Defaults to `ALLSTAK_HOST` or production. */\n host?: string;\n /** Drop `sourcesContent` from the map before upload (smaller payload). */\n stripSources?: boolean;\n /** Also upload the JS/HBC bundle alongside the map. Off by default. */\n uploadBundle?: boolean;\n /** Inject debug-id but skip the upload (CI dry-run). */\n injectOnly?: boolean;\n /** Pre-existing debug-id to use instead of generating one. Optional. */\n debugId?: string;\n /** Suppress per-step console output. Default false. */\n silent?: boolean;\n}\n\nexport interface UploadReactNativeSourcemapResult {\n /** The debug-id injected into the bundle + map. */\n debugId: string;\n /** True if the bundle already had a debug-id; we reused it. */\n reused: boolean;\n /** True when upload(s) succeeded — undefined when `injectOnly: true`. */\n uploaded?: boolean;\n /** Per-artifact responses, in the order we sent them. */\n steps?: Array<{ type: 'sourcemap' | 'bundle'; status: number; sha8: string; body?: string }>;\n}\n\nfunction sha8(buf: Buffer): string {\n return createHash('sha256').update(buf).digest('hex').slice(0, 8);\n}\n\n/**\n * Inject a debug-id into the bundle + map (idempotent — reuses an\n * existing id if present).\n */\nexport function injectReactNativeSourcemap(\n opts: Pick<UploadReactNativeSourcemapOptions, 'bundle' | 'sourcemap' | 'debugId'>,\n): { debugId: string; reused: boolean } {\n const bundleRaw = readFileSync(opts.bundle, 'utf8');\n const mapRaw = readFileSync(opts.sourcemap, 'utf8');\n const map = JSON.parse(mapRaw) as { debugId?: unknown; [k: string]: unknown };\n\n let debugId = opts.debugId ?? '';\n if (!debugId && typeof map.debugId === 'string') debugId = map.debugId;\n const existing = DEBUG_ID_LINE_RE.exec(bundleRaw);\n if (!debugId && existing && existing[1]) debugId = existing[1];\n const reused = !!debugId;\n if (!debugId) debugId = randomUUID();\n\n // Re-stringify map with canonical debugId field.\n map.debugId = debugId;\n writeFileSync(opts.sourcemap, JSON.stringify(map));\n\n // Append `//# debugId=…` to the bundle (idempotent — strip prior line first).\n let bundleOut = bundleRaw.replace(DEBUG_ID_LINE_RE, '');\n bundleOut = bundleOut.replace(/\\s+$/, '');\n bundleOut += `\\n//# debugId=${debugId}\\n`;\n writeFileSync(opts.bundle, bundleOut);\n\n return { debugId, reused };\n}\n\nasync function uploadOne(\n type: 'sourcemap' | 'bundle',\n filePath: string,\n debugId: string,\n release: string,\n host: string,\n token: string,\n dist: string | undefined,\n stripSources: boolean,\n): Promise<{ status: number; body: string; ok: boolean }> {\n let buf = readFileSync(filePath);\n if (type === 'sourcemap' && stripSources) {\n const json = JSON.parse(buf.toString('utf8')) as { sourcesContent?: unknown };\n if (Array.isArray(json.sourcesContent)) delete json.sourcesContent;\n buf = Buffer.from(JSON.stringify(json));\n }\n\n const form = new FormData();\n form.append('debugId', debugId);\n form.append('type', type);\n form.append('release', release);\n if (dist) form.append('dist', dist);\n form.append(\n 'file',\n new Blob([buf], {\n type: type === 'sourcemap' ? 'application/json' : 'application/javascript',\n }),\n basename(filePath),\n );\n\n const res = await fetch(host.replace(/\\/$/, '') + '/api/v1/artifacts/upload', {\n method: 'POST',\n headers: { 'X-AllStak-Upload-Token': token },\n body: form,\n });\n return { status: res.status, body: await res.text(), ok: res.ok };\n}\n\n/**\n * Inject debug-id into the bundle + map and (optionally) upload.\n *\n * Returns the debug-id (so the caller can stash it for symbolicator\n * lookups), whether it was reused, and per-step upload statuses.\n */\nexport async function uploadReactNativeSourcemap(\n opts: UploadReactNativeSourcemapOptions,\n): Promise<UploadReactNativeSourcemapResult> {\n const log = opts.silent ? () => undefined : (m: string) => console.log(`[allstak/sourcemaps] ${m}`);\n\n const inject = injectReactNativeSourcemap(opts);\n log(`bundle: ${basename(opts.bundle)} debugId: ${inject.debugId} ${inject.reused ? '(reused)' : '(new)'}`);\n\n const token = opts.token ?? process.env.ALLSTAK_UPLOAD_TOKEN;\n if (opts.injectOnly || !token) {\n if (!opts.injectOnly && !token) {\n log('skipping upload — no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)');\n }\n return inject;\n }\n\n const host = opts.host ?? process.env.ALLSTAK_HOST ?? DEFAULT_HOST;\n const stripSources = opts.stripSources ?? false;\n\n const steps: UploadReactNativeSourcemapResult['steps'] = [];\n const mapResult = await uploadOne(\n 'sourcemap', opts.sourcemap, inject.debugId, opts.release, host, token, opts.dist, stripSources,\n );\n steps.push({\n type: 'sourcemap',\n status: mapResult.status,\n sha8: sha8(readFileSync(opts.sourcemap)),\n body: mapResult.ok ? undefined : mapResult.body,\n });\n log(` sourcemap → ${mapResult.status}${mapResult.ok ? '' : ' ' + mapResult.body.slice(0, 120)}`);\n\n let allOk = mapResult.ok;\n if (opts.uploadBundle) {\n const bundleResult = await uploadOne(\n 'bundle', opts.bundle, inject.debugId, opts.release, host, token, opts.dist, false,\n );\n steps.push({\n type: 'bundle',\n status: bundleResult.status,\n sha8: sha8(readFileSync(opts.bundle)),\n body: bundleResult.ok ? undefined : bundleResult.body,\n });\n allOk = allOk && bundleResult.ok;\n log(` bundle → ${bundleResult.status}${bundleResult.ok ? '' : ' ' + bundleResult.body.slice(0, 120)}`);\n }\n\n return { ...inject, uploaded: allOk, steps };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,11 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
11
+ //# sourceMappingURL=chunk-BJTO5JO5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Expo config plugin.
3
+ *
4
+ * Apply via `app.json`:
5
+ *
6
+ * {
7
+ * "expo": {
8
+ * "plugins": [
9
+ * ["@allstak/react-native", { "release": "mobile@1.2.3", "environment": "production" }]
10
+ * ]
11
+ * }
12
+ * }
13
+ *
14
+ * The plugin runs at `expo prebuild` / EAS build time and:
15
+ *
16
+ * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).
17
+ * Expo's autolinking already picks this up — we just record metadata
18
+ * so `expo doctor` can verify the install is wired.
19
+ * 2. Stamps the chosen `release` into `expo-constants` extras so the JS
20
+ * layer can read it at runtime via `Constants.expoConfig.extra._allstak`
21
+ * without the host app needing to plumb it through env vars.
22
+ * 3. Records that `@allstak/react-native` was loaded as an Expo plugin
23
+ * for diagnostics.
24
+ *
25
+ * Pure config mutation — no native code is patched here. The native iOS
26
+ * + Android modules under ./native are linked by Expo's existing
27
+ * autolinking flow (which honors the `react-native.config.js` manifest).
28
+ */
29
+ interface AllStakExpoOptions {
30
+ /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */
31
+ release?: string;
32
+ /** Environment label — `production`, `staging`, etc. */
33
+ environment?: string;
34
+ /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */
35
+ dist?: string;
36
+ }
37
+ interface ExpoConfig {
38
+ name?: string;
39
+ extra?: Record<string, unknown>;
40
+ plugins?: any[];
41
+ [key: string]: any;
42
+ }
43
+ interface ExpoConfigContext {
44
+ modResults?: any;
45
+ modRawConfig?: ExpoConfig;
46
+ [key: string]: any;
47
+ }
48
+ /**
49
+ * The plugin function itself. Expo's plugin runner calls it as
50
+ * `(config, options) => modifiedConfig`. We avoid importing
51
+ * `@expo/config-plugins` so the package has zero hard dependencies on the
52
+ * Expo toolchain — the type checking happens at usage site instead.
53
+ */
54
+ declare function withAllStak(config: ExpoConfig & ExpoConfigContext, options?: AllStakExpoOptions): ExpoConfig;
55
+
56
+ export { type AllStakExpoOptions, withAllStak as default };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Expo config plugin.
3
+ *
4
+ * Apply via `app.json`:
5
+ *
6
+ * {
7
+ * "expo": {
8
+ * "plugins": [
9
+ * ["@allstak/react-native", { "release": "mobile@1.2.3", "environment": "production" }]
10
+ * ]
11
+ * }
12
+ * }
13
+ *
14
+ * The plugin runs at `expo prebuild` / EAS build time and:
15
+ *
16
+ * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).
17
+ * Expo's autolinking already picks this up — we just record metadata
18
+ * so `expo doctor` can verify the install is wired.
19
+ * 2. Stamps the chosen `release` into `expo-constants` extras so the JS
20
+ * layer can read it at runtime via `Constants.expoConfig.extra._allstak`
21
+ * without the host app needing to plumb it through env vars.
22
+ * 3. Records that `@allstak/react-native` was loaded as an Expo plugin
23
+ * for diagnostics.
24
+ *
25
+ * Pure config mutation — no native code is patched here. The native iOS
26
+ * + Android modules under ./native are linked by Expo's existing
27
+ * autolinking flow (which honors the `react-native.config.js` manifest).
28
+ */
29
+ interface AllStakExpoOptions {
30
+ /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */
31
+ release?: string;
32
+ /** Environment label — `production`, `staging`, etc. */
33
+ environment?: string;
34
+ /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */
35
+ dist?: string;
36
+ }
37
+ interface ExpoConfig {
38
+ name?: string;
39
+ extra?: Record<string, unknown>;
40
+ plugins?: any[];
41
+ [key: string]: any;
42
+ }
43
+ interface ExpoConfigContext {
44
+ modResults?: any;
45
+ modRawConfig?: ExpoConfig;
46
+ [key: string]: any;
47
+ }
48
+ /**
49
+ * The plugin function itself. Expo's plugin runner calls it as
50
+ * `(config, options) => modifiedConfig`. We avoid importing
51
+ * `@expo/config-plugins` so the package has zero hard dependencies on the
52
+ * Expo toolchain — the type checking happens at usage site instead.
53
+ */
54
+ declare function withAllStak(config: ExpoConfig & ExpoConfigContext, options?: AllStakExpoOptions): ExpoConfig;
55
+
56
+ export { type AllStakExpoOptions, withAllStak as default };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/expo-plugin.ts
21
+ var expo_plugin_exports = {};
22
+ __export(expo_plugin_exports, {
23
+ default: () => expo_plugin_default
24
+ });
25
+ module.exports = __toCommonJS(expo_plugin_exports);
26
+ function withAllStak(config, options = {}) {
27
+ const next = { ...config };
28
+ next.extra = { ...config.extra ?? {} };
29
+ const existing = next.extra._allstak ?? {};
30
+ next.extra._allstak = {
31
+ ...existing,
32
+ release: options.release ?? existing.release,
33
+ environment: options.environment ?? existing.environment,
34
+ dist: options.dist ?? existing.dist,
35
+ pluginVersion: "0.3.0"
36
+ };
37
+ return next;
38
+ }
39
+ var expo_plugin_default = withAllStak;
40
+ if (typeof module !== "undefined" && module.exports) {
41
+ module.exports = withAllStak;
42
+ module.exports.default = withAllStak;
43
+ }
44
+ //# sourceMappingURL=expo-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/expo-plugin.ts"],"sourcesContent":["/**\n * Expo config plugin.\n *\n * Apply via `app.json`:\n *\n * {\n * \"expo\": {\n * \"plugins\": [\n * [\"@allstak/react-native\", { \"release\": \"mobile@1.2.3\", \"environment\": \"production\" }]\n * ]\n * }\n * }\n *\n * The plugin runs at `expo prebuild` / EAS build time and:\n *\n * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).\n * Expo's autolinking already picks this up — we just record metadata\n * so `expo doctor` can verify the install is wired.\n * 2. Stamps the chosen `release` into `expo-constants` extras so the JS\n * layer can read it at runtime via `Constants.expoConfig.extra._allstak`\n * without the host app needing to plumb it through env vars.\n * 3. Records that `@allstak/react-native` was loaded as an Expo plugin\n * for diagnostics.\n *\n * Pure config mutation — no native code is patched here. The native iOS\n * + Android modules under ./native are linked by Expo's existing\n * autolinking flow (which honors the `react-native.config.js` manifest).\n */\n\nexport interface AllStakExpoOptions {\n /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */\n release?: string;\n /** Environment label — `production`, `staging`, etc. */\n environment?: string;\n /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */\n dist?: string;\n}\n\ninterface ExpoConfig {\n name?: string;\n extra?: Record<string, unknown>;\n plugins?: any[];\n [key: string]: any;\n}\n\ninterface ExpoConfigContext {\n modResults?: any;\n modRawConfig?: ExpoConfig;\n [key: string]: any;\n}\n\n/**\n * The plugin function itself. Expo's plugin runner calls it as\n * `(config, options) => modifiedConfig`. We avoid importing\n * `@expo/config-plugins` so the package has zero hard dependencies on the\n * Expo toolchain — the type checking happens at usage site instead.\n */\nfunction withAllStak(config: ExpoConfig & ExpoConfigContext, options: AllStakExpoOptions = {}): ExpoConfig {\n const next: ExpoConfig = { ...config };\n next.extra = { ...(config.extra ?? {}) };\n\n // Embed runtime-readable metadata under a namespaced key so we never\n // collide with the host app's other extras.\n const existing = (next.extra as any)._allstak ?? {};\n (next.extra as any)._allstak = {\n ...existing,\n release: options.release ?? existing.release,\n environment: options.environment ?? existing.environment,\n dist: options.dist ?? existing.dist,\n pluginVersion: '0.3.0',\n };\n\n return next;\n}\n\nexport default withAllStak;\n\n// Expose as a CommonJS function so `app.plugin.js`'s `require('./dist/expo-plugin.js')`\n// returns the plugin directly. The `declare const module` keeps the TS\n// type-checker happy without dragging in @types/node.\ndeclare const module: { exports: any };\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = withAllStak;\n module.exports.default = withAllStak;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDA,SAAS,YAAY,QAAwC,UAA8B,CAAC,GAAe;AACzG,QAAM,OAAmB,EAAE,GAAG,OAAO;AACrC,OAAK,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG;AAIvC,QAAM,WAAY,KAAK,MAAc,YAAY,CAAC;AAClD,EAAC,KAAK,MAAc,WAAW;AAAA,IAC7B,GAAG;AAAA,IACH,SAAS,QAAQ,WAAW,SAAS;AAAA,IACrC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,IAAO,sBAAQ;AAMf,IAAI,OAAO,WAAW,eAAe,OAAO,SAAS;AACnD,SAAO,UAAU;AACjB,SAAO,QAAQ,UAAU;AAC3B;","names":[]}
@@ -0,0 +1,25 @@
1
+ import "./chunk-BJTO5JO5.mjs";
2
+
3
+ // src/expo-plugin.ts
4
+ function withAllStak(config, options = {}) {
5
+ const next = { ...config };
6
+ next.extra = { ...config.extra ?? {} };
7
+ const existing = next.extra._allstak ?? {};
8
+ next.extra._allstak = {
9
+ ...existing,
10
+ release: options.release ?? existing.release,
11
+ environment: options.environment ?? existing.environment,
12
+ dist: options.dist ?? existing.dist,
13
+ pluginVersion: "0.3.0"
14
+ };
15
+ return next;
16
+ }
17
+ var expo_plugin_default = withAllStak;
18
+ if (typeof module !== "undefined" && module.exports) {
19
+ module.exports = withAllStak;
20
+ module.exports.default = withAllStak;
21
+ }
22
+ export {
23
+ expo_plugin_default as default
24
+ };
25
+ //# sourceMappingURL=expo-plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/expo-plugin.ts"],"sourcesContent":["/**\n * Expo config plugin.\n *\n * Apply via `app.json`:\n *\n * {\n * \"expo\": {\n * \"plugins\": [\n * [\"@allstak/react-native\", { \"release\": \"mobile@1.2.3\", \"environment\": \"production\" }]\n * ]\n * }\n * }\n *\n * The plugin runs at `expo prebuild` / EAS build time and:\n *\n * 1. Adds the AllStak iOS pod (declared in `react-native.config.js`).\n * Expo's autolinking already picks this up — we just record metadata\n * so `expo doctor` can verify the install is wired.\n * 2. Stamps the chosen `release` into `expo-constants` extras so the JS\n * layer can read it at runtime via `Constants.expoConfig.extra._allstak`\n * without the host app needing to plumb it through env vars.\n * 3. Records that `@allstak/react-native` was loaded as an Expo plugin\n * for diagnostics.\n *\n * Pure config mutation — no native code is patched here. The native iOS\n * + Android modules under ./native are linked by Expo's existing\n * autolinking flow (which honors the `react-native.config.js` manifest).\n */\n\nexport interface AllStakExpoOptions {\n /** Release identifier (e.g. `mobile@1.2.3`). Stamped into `Constants.expoConfig.extra._allstak.release`. */\n release?: string;\n /** Environment label — `production`, `staging`, etc. */\n environment?: string;\n /** Optional dist tag (e.g. `ios-hermes`). Auto-detected at runtime if unset. */\n dist?: string;\n}\n\ninterface ExpoConfig {\n name?: string;\n extra?: Record<string, unknown>;\n plugins?: any[];\n [key: string]: any;\n}\n\ninterface ExpoConfigContext {\n modResults?: any;\n modRawConfig?: ExpoConfig;\n [key: string]: any;\n}\n\n/**\n * The plugin function itself. Expo's plugin runner calls it as\n * `(config, options) => modifiedConfig`. We avoid importing\n * `@expo/config-plugins` so the package has zero hard dependencies on the\n * Expo toolchain — the type checking happens at usage site instead.\n */\nfunction withAllStak(config: ExpoConfig & ExpoConfigContext, options: AllStakExpoOptions = {}): ExpoConfig {\n const next: ExpoConfig = { ...config };\n next.extra = { ...(config.extra ?? {}) };\n\n // Embed runtime-readable metadata under a namespaced key so we never\n // collide with the host app's other extras.\n const existing = (next.extra as any)._allstak ?? {};\n (next.extra as any)._allstak = {\n ...existing,\n release: options.release ?? existing.release,\n environment: options.environment ?? existing.environment,\n dist: options.dist ?? existing.dist,\n pluginVersion: '0.3.0',\n };\n\n return next;\n}\n\nexport default withAllStak;\n\n// Expose as a CommonJS function so `app.plugin.js`'s `require('./dist/expo-plugin.js')`\n// returns the plugin directly. The `declare const module` keeps the TS\n// type-checker happy without dragging in @types/node.\ndeclare const module: { exports: any };\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = withAllStak;\n module.exports.default = withAllStak;\n}\n"],"mappings":";;;AAyDA,SAAS,YAAY,QAAwC,UAA8B,CAAC,GAAe;AACzG,QAAM,OAAmB,EAAE,GAAG,OAAO;AACrC,OAAK,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG;AAIvC,QAAM,WAAY,KAAK,MAAc,YAAY,CAAC;AAClD,EAAC,KAAK,MAAc,WAAW;AAAA,IAC7B,GAAG;AAAA,IACH,SAAS,QAAQ,WAAW,SAAS;AAAA,IACrC,aAAa,QAAQ,eAAe,SAAS;AAAA,IAC7C,MAAM,QAAQ,QAAQ,SAAS;AAAA,IAC/B,eAAe;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,IAAO,sBAAQ;AAMf,IAAI,OAAO,WAAW,eAAe,OAAO,SAAS;AACnD,SAAO,UAAU;AACjB,SAAO,QAAQ,UAAU;AAC3B;","names":[]}