@allstak/react-native 0.3.3 → 0.3.4
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/build-hooks/upload-sourcemaps.js +15 -0
- package/dist/build/sourcemaps.js +18 -2
- package/dist/build/sourcemaps.js.map +1 -1
- package/dist/build/sourcemaps.mjs +20 -4
- package/dist/build/sourcemaps.mjs.map +1 -1
- package/dist/expo-plugin.js +1 -1
- package/dist/expo-plugin.js.map +1 -1
- package/dist/expo-plugin.mjs +1 -1
- package/dist/expo-plugin.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
const fs = require('node:fs');
|
|
37
37
|
const path = require('node:path');
|
|
38
38
|
|
|
39
|
+
loadDotEnv();
|
|
39
40
|
const args = parseArgs(process.argv.slice(2));
|
|
40
41
|
|
|
41
42
|
const RELEASE = process.env.ALLSTAK_RELEASE;
|
|
@@ -173,3 +174,17 @@ function guessDist(platform) {
|
|
|
173
174
|
if (platform === 'android') return 'android-hermes';
|
|
174
175
|
return platform;
|
|
175
176
|
}
|
|
177
|
+
|
|
178
|
+
function loadDotEnv() {
|
|
179
|
+
for (const file of ['.env.local', '.env']) {
|
|
180
|
+
const full = path.resolve(process.cwd(), file);
|
|
181
|
+
if (!fs.existsSync(full)) continue;
|
|
182
|
+
for (const line of fs.readFileSync(full, 'utf8').split(/\r?\n/)) {
|
|
183
|
+
const trimmed = line.trim();
|
|
184
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
185
|
+
const match = /^([A-Z0-9_]+)\s*=\s*(.*)$/.exec(trimmed);
|
|
186
|
+
if (!match || process.env[match[1]] !== undefined) continue;
|
|
187
|
+
process.env[match[1]] = match[2].replace(/^['"]|['"]$/g, '');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
package/dist/build/sourcemaps.js
CHANGED
|
@@ -83,14 +83,15 @@ async function uploadReactNativeSourcemap(opts) {
|
|
|
83
83
|
const log = opts.silent ? () => void 0 : (m) => console.log(`[allstak/sourcemaps] ${m}`);
|
|
84
84
|
const inject = injectReactNativeSourcemap(opts);
|
|
85
85
|
log(`bundle: ${(0, import_node_path.basename)(opts.bundle)} debugId: ${inject.debugId} ${inject.reused ? "(reused)" : "(new)"}`);
|
|
86
|
-
const
|
|
86
|
+
const env = loadAllStakEnv();
|
|
87
|
+
const token = opts.token ?? env.ALLSTAK_UPLOAD_TOKEN;
|
|
87
88
|
if (opts.injectOnly || !token) {
|
|
88
89
|
if (!opts.injectOnly && !token) {
|
|
89
90
|
log("skipping upload \u2014 no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)");
|
|
90
91
|
}
|
|
91
92
|
return inject;
|
|
92
93
|
}
|
|
93
|
-
const host = opts.host ??
|
|
94
|
+
const host = opts.host ?? env.ALLSTAK_HOST ?? DEFAULT_HOST;
|
|
94
95
|
const stripSources = opts.stripSources ?? false;
|
|
95
96
|
const steps = [];
|
|
96
97
|
const mapResult = await uploadOne(
|
|
@@ -133,6 +134,21 @@ async function uploadReactNativeSourcemap(opts) {
|
|
|
133
134
|
}
|
|
134
135
|
return { ...inject, uploaded: allOk, steps };
|
|
135
136
|
}
|
|
137
|
+
function loadAllStakEnv() {
|
|
138
|
+
const out = { ...process.env };
|
|
139
|
+
for (const file of [".env.local", ".env"]) {
|
|
140
|
+
const full = (0, import_node_path.resolve)(process.cwd(), file);
|
|
141
|
+
if (!(0, import_node_fs.existsSync)(full)) continue;
|
|
142
|
+
for (const line of (0, import_node_fs.readFileSync)(full, "utf8").split(/\r?\n/)) {
|
|
143
|
+
const trimmed = line.trim();
|
|
144
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
145
|
+
const match = /^([A-Z0-9_]+)\s*=\s*(.*)$/.exec(trimmed);
|
|
146
|
+
if (!match || out[match[1]] !== void 0) continue;
|
|
147
|
+
out[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
136
152
|
// Annotate the CommonJS export names for ESM import in node:
|
|
137
153
|
0 && (module.exports = {
|
|
138
154
|
DEFAULT_HOST,
|
|
@@ -1 +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":[]}
|
|
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 { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { randomUUID, createHash } from 'node:crypto';\nimport { basename, resolve } 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 env = loadAllStakEnv();\n const token = opts.token ?? 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 ?? 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\nfunction loadAllStakEnv(): Record<string, string | undefined> {\n const out: Record<string, string | undefined> = { ...process.env };\n for (const file of ['.env.local', '.env']) {\n const full = resolve(process.cwd(), file);\n if (!existsSync(full)) continue;\n for (const line of readFileSync(full, 'utf8').split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const match = /^([A-Z0-9_]+)\\s*=\\s*(.*)$/.exec(trimmed);\n if (!match || out[match[1]!] !== undefined) continue;\n out[match[1]!] = match[2]!.replace(/^['\"]|['\"]$/g, '');\n }\n }\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/build/sourcemaps.ts
|
|
2
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
3
3
|
import { randomUUID, createHash } from "crypto";
|
|
4
|
-
import { basename } from "path";
|
|
4
|
+
import { basename, resolve } from "path";
|
|
5
5
|
var DEFAULT_HOST = "https://api.allstak.sa";
|
|
6
6
|
var DEBUG_ID_LINE_RE = /^\/\/# debugId=([0-9a-f-]{36})\s*$/m;
|
|
7
7
|
function sha8(buf) {
|
|
@@ -57,14 +57,15 @@ async function uploadReactNativeSourcemap(opts) {
|
|
|
57
57
|
const log = opts.silent ? () => void 0 : (m) => console.log(`[allstak/sourcemaps] ${m}`);
|
|
58
58
|
const inject = injectReactNativeSourcemap(opts);
|
|
59
59
|
log(`bundle: ${basename(opts.bundle)} debugId: ${inject.debugId} ${inject.reused ? "(reused)" : "(new)"}`);
|
|
60
|
-
const
|
|
60
|
+
const env = loadAllStakEnv();
|
|
61
|
+
const token = opts.token ?? env.ALLSTAK_UPLOAD_TOKEN;
|
|
61
62
|
if (opts.injectOnly || !token) {
|
|
62
63
|
if (!opts.injectOnly && !token) {
|
|
63
64
|
log("skipping upload \u2014 no token (set ALLSTAK_UPLOAD_TOKEN or pass `token`)");
|
|
64
65
|
}
|
|
65
66
|
return inject;
|
|
66
67
|
}
|
|
67
|
-
const host = opts.host ??
|
|
68
|
+
const host = opts.host ?? env.ALLSTAK_HOST ?? DEFAULT_HOST;
|
|
68
69
|
const stripSources = opts.stripSources ?? false;
|
|
69
70
|
const steps = [];
|
|
70
71
|
const mapResult = await uploadOne(
|
|
@@ -107,6 +108,21 @@ async function uploadReactNativeSourcemap(opts) {
|
|
|
107
108
|
}
|
|
108
109
|
return { ...inject, uploaded: allOk, steps };
|
|
109
110
|
}
|
|
111
|
+
function loadAllStakEnv() {
|
|
112
|
+
const out = { ...process.env };
|
|
113
|
+
for (const file of [".env.local", ".env"]) {
|
|
114
|
+
const full = resolve(process.cwd(), file);
|
|
115
|
+
if (!existsSync(full)) continue;
|
|
116
|
+
for (const line of readFileSync(full, "utf8").split(/\r?\n/)) {
|
|
117
|
+
const trimmed = line.trim();
|
|
118
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
119
|
+
const match = /^([A-Z0-9_]+)\s*=\s*(.*)$/.exec(trimmed);
|
|
120
|
+
if (!match || out[match[1]] !== void 0) continue;
|
|
121
|
+
out[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
110
126
|
export {
|
|
111
127
|
DEFAULT_HOST,
|
|
112
128
|
injectReactNativeSourcemap,
|
|
@@ -1 +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":[]}
|
|
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 { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { randomUUID, createHash } from 'node:crypto';\nimport { basename, resolve } 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 env = loadAllStakEnv();\n const token = opts.token ?? 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 ?? 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\nfunction loadAllStakEnv(): Record<string, string | undefined> {\n const out: Record<string, string | undefined> = { ...process.env };\n for (const file of ['.env.local', '.env']) {\n const full = resolve(process.cwd(), file);\n if (!existsSync(full)) continue;\n for (const line of readFileSync(full, 'utf8').split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const match = /^([A-Z0-9_]+)\\s*=\\s*(.*)$/.exec(trimmed);\n if (!match || out[match[1]!] !== undefined) continue;\n out[match[1]!] = match[2]!.replace(/^['\"]|['\"]$/g, '');\n }\n }\n return out;\n}\n"],"mappings":";AAiCA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,YAAY,kBAAkB;AACvC,SAAS,UAAU,eAAe;AAE3B,IAAM,eAAe;AAE5B,IAAM,mBAAmB;AA2CzB,SAAS,KAAK,KAAqB;AACjC,SAAO,WAAW,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,CAAC;AAClE;AAMO,SAAS,2BACd,MACsC;AACtC,QAAM,YAAY,aAAa,KAAK,QAAQ,MAAM;AAClD,QAAM,SAAS,aAAa,KAAK,WAAW,MAAM;AAClD,QAAM,MAAM,KAAK,MAAM,MAAM;AAE7B,MAAI,UAAU,KAAK,WAAW;AAC9B,MAAI,CAAC,WAAW,OAAO,IAAI,YAAY,SAAU,WAAU,IAAI;AAC/D,QAAM,WAAW,iBAAiB,KAAK,SAAS;AAChD,MAAI,CAAC,WAAW,YAAY,SAAS,CAAC,EAAG,WAAU,SAAS,CAAC;AAC7D,QAAM,SAAS,CAAC,CAAC;AACjB,MAAI,CAAC,QAAS,WAAU,WAAW;AAGnC,MAAI,UAAU;AACd,gBAAc,KAAK,WAAW,KAAK,UAAU,GAAG,CAAC;AAGjD,MAAI,YAAY,UAAU,QAAQ,kBAAkB,EAAE;AACtD,cAAY,UAAU,QAAQ,QAAQ,EAAE;AACxC,eAAa;AAAA,cAAiB,OAAO;AAAA;AACrC,gBAAc,KAAK,QAAQ,SAAS;AAEpC,SAAO,EAAE,SAAS,OAAO;AAC3B;AAEA,eAAe,UACb,MACA,UACA,SACA,SACA,MACA,OACA,MACA,cACwD;AACxD,MAAI,MAAM,aAAa,QAAQ;AAC/B,MAAI,SAAS,eAAe,cAAc;AACxC,UAAM,OAAO,KAAK,MAAM,IAAI,SAAS,MAAM,CAAC;AAC5C,QAAI,MAAM,QAAQ,KAAK,cAAc,EAAG,QAAO,KAAK;AACpD,UAAM,OAAO,KAAK,KAAK,UAAU,IAAI,CAAC;AAAA,EACxC;AAEA,QAAM,OAAO,IAAI,SAAS;AAC1B,OAAK,OAAO,WAAW,OAAO;AAC9B,OAAK,OAAO,QAAQ,IAAI;AACxB,OAAK,OAAO,WAAW,OAAO;AAC9B,MAAI,KAAM,MAAK,OAAO,QAAQ,IAAI;AAClC,OAAK;AAAA,IACH;AAAA,IACA,IAAI,KAAK,CAAC,GAAG,GAAG;AAAA,MACd,MAAM,SAAS,cAAc,qBAAqB;AAAA,IACpD,CAAC;AAAA,IACD,SAAS,QAAQ;AAAA,EACnB;AAEA,QAAM,MAAM,MAAM,MAAM,KAAK,QAAQ,OAAO,EAAE,IAAI,4BAA4B;AAAA,IAC5E,QAAQ;AAAA,IACR,SAAS,EAAE,0BAA0B,MAAM;AAAA,IAC3C,MAAM;AAAA,EACR,CAAC;AACD,SAAO,EAAE,QAAQ,IAAI,QAAQ,MAAM,MAAM,IAAI,KAAK,GAAG,IAAI,IAAI,GAAG;AAClE;AAQA,eAAsB,2BACpB,MAC2C;AAC3C,QAAM,MAAM,KAAK,SAAS,MAAM,SAAY,CAAC,MAAc,QAAQ,IAAI,wBAAwB,CAAC,EAAE;AAElG,QAAM,SAAS,2BAA2B,IAAI;AAC9C,MAAI,WAAW,SAAS,KAAK,MAAM,CAAC,cAAc,OAAO,OAAO,IAAI,OAAO,SAAS,aAAa,OAAO,EAAE;AAE1G,QAAM,MAAM,eAAe;AAC3B,QAAM,QAAQ,KAAK,SAAS,IAAI;AAChC,MAAI,KAAK,cAAc,CAAC,OAAO;AAC7B,QAAI,CAAC,KAAK,cAAc,CAAC,OAAO;AAC9B,UAAI,4EAAuE;AAAA,IAC7E;AACA,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,KAAK,QAAQ,IAAI,gBAAgB;AAC9C,QAAM,eAAe,KAAK,gBAAgB;AAE1C,QAAM,QAAmD,CAAC;AAC1D,QAAM,YAAY,MAAM;AAAA,IACtB;AAAA,IAAa,KAAK;AAAA,IAAW,OAAO;AAAA,IAAS,KAAK;AAAA,IAAS;AAAA,IAAM;AAAA,IAAO,KAAK;AAAA,IAAM;AAAA,EACrF;AACA,QAAM,KAAK;AAAA,IACT,MAAM;AAAA,IACN,QAAQ,UAAU;AAAA,IAClB,MAAM,KAAK,aAAa,KAAK,SAAS,CAAC;AAAA,IACvC,MAAM,UAAU,KAAK,SAAY,UAAU;AAAA,EAC7C,CAAC;AACD,MAAI,sBAAiB,UAAU,MAAM,GAAG,UAAU,KAAK,KAAK,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAEhG,MAAI,QAAQ,UAAU;AACtB,MAAI,KAAK,cAAc;AACrB,UAAM,eAAe,MAAM;AAAA,MACzB;AAAA,MAAU,KAAK;AAAA,MAAQ,OAAO;AAAA,MAAS,KAAK;AAAA,MAAS;AAAA,MAAM;AAAA,MAAO,KAAK;AAAA,MAAM;AAAA,IAC/E;AACA,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,QAAQ,aAAa;AAAA,MACrB,MAAM,KAAK,aAAa,KAAK,MAAM,CAAC;AAAA,MACpC,MAAM,aAAa,KAAK,SAAY,aAAa;AAAA,IACnD,CAAC;AACD,YAAQ,SAAS,aAAa;AAC9B,QAAI,sBAAiB,aAAa,MAAM,GAAG,aAAa,KAAK,KAAK,MAAM,aAAa,KAAK,MAAM,GAAG,GAAG,CAAC,EAAE;AAAA,EAC3G;AAEA,SAAO,EAAE,GAAG,QAAQ,UAAU,OAAO,MAAM;AAC7C;AAEA,SAAS,iBAAqD;AAC5D,QAAM,MAA0C,EAAE,GAAG,QAAQ,IAAI;AACjE,aAAW,QAAQ,CAAC,cAAc,MAAM,GAAG;AACzC,UAAM,OAAO,QAAQ,QAAQ,IAAI,GAAG,IAAI;AACxC,QAAI,CAAC,WAAW,IAAI,EAAG;AACvB,eAAW,QAAQ,aAAa,MAAM,MAAM,EAAE,MAAM,OAAO,GAAG;AAC5D,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AACzC,YAAM,QAAQ,4BAA4B,KAAK,OAAO;AACtD,UAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAE,MAAM,OAAW;AAC5C,UAAI,MAAM,CAAC,CAAE,IAAI,MAAM,CAAC,EAAG,QAAQ,gBAAgB,EAAE;AAAA,IACvD;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
package/dist/expo-plugin.js
CHANGED
|
@@ -32,7 +32,7 @@ function withAllStak(config, options = {}) {
|
|
|
32
32
|
release: options.release ?? existing.release,
|
|
33
33
|
environment: options.environment ?? existing.environment,
|
|
34
34
|
dist: options.dist ?? existing.dist,
|
|
35
|
-
pluginVersion: "0.3.
|
|
35
|
+
pluginVersion: "0.3.4"
|
|
36
36
|
};
|
|
37
37
|
return next;
|
|
38
38
|
}
|
package/dist/expo-plugin.js.map
CHANGED
|
@@ -1 +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.
|
|
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.4',\n };\n\n return next;\n}\n\nexport default withAllStak;\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;","names":[]}
|
package/dist/expo-plugin.mjs
CHANGED
|
@@ -10,7 +10,7 @@ function withAllStak(config, options = {}) {
|
|
|
10
10
|
release: options.release ?? existing.release,
|
|
11
11
|
environment: options.environment ?? existing.environment,
|
|
12
12
|
dist: options.dist ?? existing.dist,
|
|
13
|
-
pluginVersion: "0.3.
|
|
13
|
+
pluginVersion: "0.3.4"
|
|
14
14
|
};
|
|
15
15
|
return next;
|
|
16
16
|
}
|
package/dist/expo-plugin.mjs.map
CHANGED
|
@@ -1 +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.
|
|
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.4',\n };\n\n return next;\n}\n\nexport default withAllStak;\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;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -440,7 +440,7 @@ declare function __resetConsoleInstrumentationFlagForTest(): void;
|
|
|
440
440
|
|
|
441
441
|
declare const INGEST_HOST = "https://api.allstak.sa";
|
|
442
442
|
declare const SDK_NAME = "allstak-react-native";
|
|
443
|
-
declare const SDK_VERSION = "0.3.
|
|
443
|
+
declare const SDK_VERSION = "0.3.4";
|
|
444
444
|
|
|
445
445
|
interface AllStakConfig {
|
|
446
446
|
/** Project API key (`ask_live_…`). Required. */
|
package/dist/index.d.ts
CHANGED
|
@@ -440,7 +440,7 @@ declare function __resetConsoleInstrumentationFlagForTest(): void;
|
|
|
440
440
|
|
|
441
441
|
declare const INGEST_HOST = "https://api.allstak.sa";
|
|
442
442
|
declare const SDK_NAME = "allstak-react-native";
|
|
443
|
-
declare const SDK_VERSION = "0.3.
|
|
443
|
+
declare const SDK_VERSION = "0.3.4";
|
|
444
444
|
|
|
445
445
|
interface AllStakConfig {
|
|
446
446
|
/** Project API key (`ask_live_…`). Required. */
|
package/dist/index.js
CHANGED
|
@@ -1446,7 +1446,7 @@ function unbindHttpInstrumentation() {
|
|
|
1446
1446
|
// src/client.ts
|
|
1447
1447
|
var INGEST_HOST = "https://api.allstak.sa";
|
|
1448
1448
|
var SDK_NAME = "allstak-react-native";
|
|
1449
|
-
var SDK_VERSION = "0.3.
|
|
1449
|
+
var SDK_VERSION = "0.3.4";
|
|
1450
1450
|
var ERRORS_PATH = "/ingest/v1/errors";
|
|
1451
1451
|
var LOGS_PATH = "/ingest/v1/logs";
|
|
1452
1452
|
var VALID_BREADCRUMB_TYPES = /* @__PURE__ */ new Set(["http", "log", "ui", "navigation", "query", "default"]);
|