@goliapkg/sentori-cli 0.5.2 → 0.5.3

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.
@@ -1,148 +0,0 @@
1
- // `sentori-cli upload dsym` (iOS) + `sentori-cli upload mapping` (Android).
2
- // Both endpoints take the raw artifact bytes (NOT multipart) with a few
3
- // headers / query params.
4
- import { spawnSync } from 'node:child_process';
5
- import { readFile } from 'node:fs/promises';
6
- import { basename, extname, join } from 'node:path';
7
- import { statSync, readdirSync } from 'node:fs';
8
- async function postBytes(url, body, token, headers = {}) {
9
- const resp = await fetch(url, {
10
- body,
11
- headers: {
12
- Authorization: `Bearer ${token}`,
13
- 'Content-Type': 'application/octet-stream',
14
- ...headers,
15
- },
16
- method: 'POST',
17
- });
18
- if (!resp.ok) {
19
- let detail = '';
20
- try {
21
- detail = await resp.text();
22
- }
23
- catch {
24
- // ignore
25
- }
26
- throw new Error(`${resp.status} ${resp.statusText}${detail ? ` — ${detail.slice(0, 300)}` : ''}`);
27
- }
28
- const txt = await resp.text();
29
- return txt ? JSON.parse(txt) : null;
30
- }
31
- const ARCHES = new Set([
32
- 'arm64',
33
- 'arm64_32',
34
- 'arm64e',
35
- 'armv7',
36
- 'armv7k',
37
- 'armv7s',
38
- 'i386',
39
- 'x86_64',
40
- 'x86_64h',
41
- ]);
42
- /**
43
- * Use `dwarfdump --uuid <path>` to enumerate `(arch, debug_id, file)`
44
- * for each Mach-O slice. Returns [] if dwarfdump isn't installed or the
45
- * output couldn't be parsed; callers should fall back to explicit
46
- * `--debug-id` / `--arch` flags in that case.
47
- */
48
- export function dsymSlicesFromDwarfdump(path) {
49
- const r = spawnSync('dwarfdump', ['--uuid', path]);
50
- if (r.status !== 0 || !r.stdout)
51
- return [];
52
- const out = [];
53
- // Output lines look like:
54
- // UUID: 1234ABCD-... (arm64) /path/to/Foo.dSYM/Contents/Resources/DWARF/Foo
55
- const re = /^UUID:\s+([0-9A-Fa-f-]{32,36})\s+\(([^)]+)\)\s+(.+)\s*$/m;
56
- for (const line of r.stdout.toString().split('\n')) {
57
- const m = re.exec(line);
58
- if (!m)
59
- continue;
60
- const [, debugId, arch, file] = m;
61
- if (!ARCHES.has(arch))
62
- continue;
63
- out.push({ arch, debugId: debugId.toUpperCase(), file: file.trim() });
64
- }
65
- return out;
66
- }
67
- /** Walk a `.dSYM` bundle and return the DWARF binary files inside
68
- * `Contents/Resources/DWARF/`. If `path` already points at a binary
69
- * (not a bundle), returns `[path]`. */
70
- export function dwarfBinariesIn(path) {
71
- let st;
72
- try {
73
- st = statSync(path);
74
- }
75
- catch {
76
- return [];
77
- }
78
- if (st.isFile())
79
- return [path];
80
- const dwarfDir = join(path, 'Contents/Resources/DWARF');
81
- try {
82
- return readdirSync(dwarfDir)
83
- .filter((n) => !n.startsWith('.'))
84
- .map((n) => join(dwarfDir, n));
85
- }
86
- catch {
87
- // not a .dSYM bundle layout — try the top-level path
88
- return [path];
89
- }
90
- }
91
- export async function uploadDsym(opts) {
92
- const slices = [];
93
- if (opts.debugId && opts.arch) {
94
- // Explicit single-slice upload — no parsing needed.
95
- const binaries = dwarfBinariesIn(opts.path);
96
- if (binaries.length === 0)
97
- throw new Error(`no DWARF binary at: ${opts.path}`);
98
- slices.push({ arch: opts.arch, debugId: opts.debugId.toUpperCase(), file: binaries[0] });
99
- }
100
- else {
101
- // Auto-discover via dwarfdump.
102
- const found = dsymSlicesFromDwarfdump(opts.path);
103
- if (found.length === 0) {
104
- throw new Error('couldn’t enumerate dSYM slices — install Xcode command-line tools ' +
105
- '(for `dwarfdump`), or pass --debug-id and --arch explicitly');
106
- }
107
- slices.push(...found);
108
- }
109
- const base = opts.apiUrl.replace(/\/+$/, '');
110
- const q = new URLSearchParams();
111
- if (opts.release)
112
- q.set('release', opts.release);
113
- if (opts.objectName ?? basename(opts.path).replace(/\.dSYM$/, ''))
114
- q.set('objectName', opts.objectName ?? basename(opts.path).replace(/\.dSYM$/, ''));
115
- const qs = q.toString();
116
- const url = `${base}/admin/api/projects/${encodeURIComponent(opts.projectId)}/dsyms${qs ? '?' + qs : ''}`;
117
- const uploaded = [];
118
- for (const s of slices) {
119
- const body = await readFile(s.file);
120
- await postBytes(url, body, opts.token, {
121
- 'x-sentori-arch': s.arch,
122
- 'x-sentori-debug-id': s.debugId,
123
- });
124
- uploaded.push({ arch: s.arch, debugId: s.debugId });
125
- }
126
- return { slices: uploaded };
127
- }
128
- export async function uploadMapping(opts) {
129
- const ext = extname(opts.path).toLowerCase();
130
- if (ext && ext !== '.txt' && ext !== '.map') {
131
- // R8 emits `mapping.txt` by default; accept anything but warn.
132
- console.warn(`[sentori-cli] upload mapping: unexpected extension ${ext} — uploading anyway`);
133
- }
134
- const body = await readFile(opts.path);
135
- if (body.length === 0)
136
- throw new Error(`empty mapping file: ${opts.path}`);
137
- const base = opts.apiUrl.replace(/\/+$/, '');
138
- const q = new URLSearchParams();
139
- if (opts.release)
140
- q.set('release', opts.release);
141
- const qs = q.toString();
142
- const url = `${base}/admin/api/projects/${encodeURIComponent(opts.projectId)}/mappings${qs ? '?' + qs : ''}`;
143
- const headers = {};
144
- if (opts.debugId)
145
- headers['x-sentori-debug-id'] = opts.debugId;
146
- await postBytes(url, body, opts.token, headers);
147
- }
148
- //# sourceMappingURL=native-artifacts.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"native-artifacts.js","sourceRoot":"","sources":["../src/native-artifacts.ts"],"names":[],"mappings":"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"}
@@ -1,25 +0,0 @@
1
- /** Resolve `react-native/scripts/compose-source-maps.js` from the
2
- * current project's node_modules. Returns null if react-native isn't
3
- * installed or the version doesn't ship that script. */
4
- export declare function resolveComposeScript(fromDir?: string): null | string;
5
- /** Compose a Metro packager source map + a Hermes source map into a
6
- * single map (a temp file the caller is responsible for deleting).
7
- * Throws if react-native's compose script can't be found or fails. */
8
- export declare function composeSourceMaps(metroMap: string, hermesMap: string): string;
9
- export type RnUploadOptions = {
10
- apiUrl: string;
11
- /** Optional bundle file (.jsbundle / .bundle) to upload alongside the map. */
12
- bundle?: string;
13
- dryRun?: boolean;
14
- hermesMap: string;
15
- metroMap: string;
16
- release: string;
17
- token: string;
18
- };
19
- /** Compose the Metro + Hermes maps, then upload the result (and the
20
- * bundle, if given). Cleans up the temp composed map. */
21
- export declare function reactNativeUpload(opts: RnUploadOptions): Promise<{
22
- files: string[];
23
- uploaded?: number;
24
- }>;
25
- //# sourceMappingURL=react-native.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-native.d.ts","sourceRoot":"","sources":["../src/react-native.ts"],"names":[],"mappings":"AAQA;;yDAEyD;AACzD,wBAAgB,oBAAoB,CAAC,OAAO,SAAgB,GAAG,IAAI,GAAG,MAAM,CAc3E;AAED;;uEAEuE;AACvE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAkB7E;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;0DAC0D;AAC1D,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;IACtE,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAC,CAoBD"}
@@ -1,74 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { existsSync, mkdtempSync, rmSync } from 'node:fs';
3
- import { createRequire } from 'node:module';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { uploadSourcemaps } from './upload.js';
7
- /** Resolve `react-native/scripts/compose-source-maps.js` from the
8
- * current project's node_modules. Returns null if react-native isn't
9
- * installed or the version doesn't ship that script. */
10
- export function resolveComposeScript(fromDir = process.cwd()) {
11
- const req = createRequire(join(fromDir, 'noop.js'));
12
- for (const id of [
13
- 'react-native/scripts/compose-source-maps.js',
14
- '@react-native/community-cli-plugin/dist/utils/composeSourceMaps.js',
15
- ]) {
16
- try {
17
- const p = req.resolve(id);
18
- if (existsSync(p))
19
- return p;
20
- }
21
- catch {
22
- // try next
23
- }
24
- }
25
- return null;
26
- }
27
- /** Compose a Metro packager source map + a Hermes source map into a
28
- * single map (a temp file the caller is responsible for deleting).
29
- * Throws if react-native's compose script can't be found or fails. */
30
- export function composeSourceMaps(metroMap, hermesMap) {
31
- for (const p of [metroMap, hermesMap]) {
32
- if (!existsSync(p))
33
- throw new Error(`no such file: ${p}`);
34
- }
35
- const script = resolveComposeScript();
36
- if (!script) {
37
- throw new Error("couldn't find react-native's compose-source-maps.js — install react-native, or " +
38
- 'compose the maps yourself and use `sentori-cli upload sourcemap <composed.map>`');
39
- }
40
- const out = join(mkdtempSync(join(tmpdir(), 'sentori-rn-')), 'composed.map');
41
- const r = spawnSync('node', [script, metroMap, hermesMap, '-o', out], { stdio: 'inherit' });
42
- if (r.status !== 0) {
43
- throw new Error(`compose-source-maps.js exited with ${r.status ?? 'signal'}`);
44
- }
45
- if (!existsSync(out))
46
- throw new Error('compose-source-maps.js produced no output');
47
- return out;
48
- }
49
- /** Compose the Metro + Hermes maps, then upload the result (and the
50
- * bundle, if given). Cleans up the temp composed map. */
51
- export async function reactNativeUpload(opts) {
52
- const composed = composeSourceMaps(opts.metroMap, opts.hermesMap);
53
- try {
54
- const paths = [composed, ...(opts.bundle ? [opts.bundle] : [])];
55
- const r = await uploadSourcemaps({
56
- apiUrl: opts.apiUrl,
57
- dryRun: opts.dryRun,
58
- paths,
59
- release: opts.release,
60
- token: opts.token,
61
- });
62
- return { files: r.files, uploaded: r.uploaded };
63
- }
64
- finally {
65
- try {
66
- rmSync(composed, { force: true });
67
- rmSync(join(composed, '..'), { force: true, recursive: true });
68
- }
69
- catch {
70
- // best-effort cleanup
71
- }
72
- }
73
- }
74
- //# sourceMappingURL=react-native.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-native.js","sourceRoot":"","sources":["../src/react-native.ts"],"names":[],"mappings":"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"}
package/lib/upload.d.ts DELETED
@@ -1,24 +0,0 @@
1
- export type UploadOptions = {
2
- release: string;
3
- token: string;
4
- /** Sentori API base, e.g. https://api.sentori.golia.jp or your host. */
5
- apiUrl: string;
6
- /** Files or directories. Directories are scanned one level deep. */
7
- paths: string[];
8
- dryRun?: boolean;
9
- };
10
- export type UploadResult = {
11
- files: string[];
12
- uploaded?: number;
13
- artifacts?: {
14
- kind: string;
15
- name: string;
16
- }[];
17
- };
18
- /** Resolve `paths` (files or dirs) to a deduped list of files to upload.
19
- * A directory contributes its top-level `.map` / `.js` / `.bundle` /
20
- * `.hbc` files; a file given explicitly is taken as-is (even if its
21
- * extension isn't in the list — the caller asked for it). */
22
- export declare function collectFiles(paths: string[]): Promise<string[]>;
23
- export declare function uploadSourcemaps(opts: UploadOptions): Promise<UploadResult>;
24
- //# sourceMappingURL=upload.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAA;IACd,oEAAoE;IACpE,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC7C,CAAA;AAED;;;8DAG8D;AAC9D,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoBrE;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAiCjF"}
package/lib/upload.js DELETED
@@ -1,65 +0,0 @@
1
- import { readFile, readdir, stat } from 'node:fs/promises';
2
- import { basename, join } from 'node:path';
3
- // Files worth uploading: source maps, and the bundle they map (so the
4
- // dashboard can show the minified line too if a frame falls outside
5
- // the map). `.jsbundle` (iOS) and `.bundle` (Android) are RN's bundle
6
- // names; `.hbc` is the Hermes bytecode bundle.
7
- const UPLOADABLE = /\.(map|js|jsbundle|bundle|hbc)$/i;
8
- /** Resolve `paths` (files or dirs) to a deduped list of files to upload.
9
- * A directory contributes its top-level `.map` / `.js` / `.bundle` /
10
- * `.hbc` files; a file given explicitly is taken as-is (even if its
11
- * extension isn't in the list — the caller asked for it). */
12
- export async function collectFiles(paths) {
13
- const out = [];
14
- for (const p of paths) {
15
- const s = await stat(p).catch(() => null);
16
- if (!s)
17
- throw new Error(`no such file or directory: ${p}`);
18
- if (s.isDirectory()) {
19
- for (const entry of await readdir(p)) {
20
- const full = join(p, entry);
21
- const es = await stat(full).catch(() => null);
22
- if (es?.isFile() && UPLOADABLE.test(entry))
23
- out.push(full);
24
- }
25
- }
26
- else {
27
- out.push(p);
28
- }
29
- }
30
- const deduped = [...new Set(out)];
31
- if (deduped.length === 0) {
32
- throw new Error('no .map / .js / .bundle files found in the given path(s)');
33
- }
34
- return deduped;
35
- }
36
- export async function uploadSourcemaps(opts) {
37
- const files = await collectFiles(opts.paths);
38
- if (opts.dryRun)
39
- return { files };
40
- const form = new FormData();
41
- for (const f of files) {
42
- const buf = await readFile(f);
43
- form.append('file', new Blob([buf]), basename(f));
44
- }
45
- const base = opts.apiUrl.replace(/\/+$/, '');
46
- const url = `${base}/admin/api/releases/${encodeURIComponent(opts.release)}/sourcemaps`;
47
- const resp = await fetch(url, {
48
- body: form,
49
- headers: { Authorization: `Bearer ${opts.token}` },
50
- method: 'POST',
51
- });
52
- if (!resp.ok) {
53
- let detail = '';
54
- try {
55
- detail = await resp.text();
56
- }
57
- catch {
58
- // ignore
59
- }
60
- throw new Error(`${resp.status} ${resp.statusText}${detail ? ` — ${detail.slice(0, 300)}` : ''}`);
61
- }
62
- const body = (await resp.json().catch(() => ({})));
63
- return { artifacts: body.artifacts, files, uploaded: body.uploaded ?? files.length };
64
- }
65
- //# sourceMappingURL=upload.js.map
package/lib/upload.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"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"}
@@ -1,95 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
2
-
3
- import { formatIssueLine, issueList, issuePatch } from '../issue.js'
4
-
5
- const cfg = {
6
- apiUrl: 'https://api.example.com/',
7
- projectId: 'proj-1',
8
- token: 'sk_test',
9
- }
10
-
11
- const sample = {
12
- errorType: 'TypeError',
13
- eventCount: 17,
14
- id: 'issue-1',
15
- lastSeen: '2026-05-13T00:00:00Z',
16
- messageSample: 'undefined is not an object',
17
- status: 'active' as const,
18
- }
19
-
20
- const origFetch = globalThis.fetch
21
- let calls: { body: string | undefined; headers: Headers; method: string; url: string }[]
22
- beforeEach(() => {
23
- calls = []
24
- })
25
- afterEach(() => {
26
- globalThis.fetch = origFetch
27
- })
28
-
29
- function mockFetch(respBody: string, init: ResponseInit = { status: 200 }): void {
30
- globalThis.fetch = (async (url: Request | string | URL, opts?: RequestInit) => {
31
- calls.push({
32
- body: typeof opts?.body === 'string' ? opts.body : undefined,
33
- headers: new Headers(opts?.headers),
34
- method: opts?.method ?? 'GET',
35
- url: String(url),
36
- })
37
- return new Response(respBody, {
38
- headers: { 'content-type': 'application/json' },
39
- ...init,
40
- })
41
- }) as typeof fetch
42
- }
43
-
44
- describe('issueList', () => {
45
- test('GETs /admin/api/projects/<id>/issues with query params + Bearer', async () => {
46
- mockFetch(JSON.stringify([sample]))
47
- const rows = await issueList({ config: cfg, limit: 20, status: 'active' })
48
- expect(calls).toHaveLength(1)
49
- expect(calls[0]?.method).toBe('GET')
50
- expect(calls[0]?.url).toBe(
51
- 'https://api.example.com/admin/api/projects/proj-1/issues?status=active&limit=20',
52
- )
53
- expect(calls[0]?.headers.get('authorization')).toBe('Bearer sk_test')
54
- expect(rows).toHaveLength(1)
55
- expect(rows[0]?.id).toBe('issue-1')
56
- })
57
-
58
- test('omits empty query params', async () => {
59
- mockFetch(JSON.stringify([]))
60
- await issueList({ config: cfg })
61
- expect(calls[0]?.url).toBe('https://api.example.com/admin/api/projects/proj-1/issues')
62
- })
63
-
64
- test('throws with server detail on non-2xx', async () => {
65
- mockFetch('forbidden', { status: 403, statusText: 'Forbidden' })
66
- await expect(issueList({ config: cfg })).rejects.toThrow('403 Forbidden — forbidden')
67
- })
68
- })
69
-
70
- describe('issuePatch', () => {
71
- test('PATCHes /issues/<id> with the body', async () => {
72
- mockFetch(JSON.stringify({ ...sample, status: 'resolved' }))
73
- const updated = await issuePatch(cfg, 'issue-1', {
74
- resolvedInRelease: 'app@1.2.4+457',
75
- status: 'resolved',
76
- })
77
- expect(calls[0]?.method).toBe('PATCH')
78
- expect(calls[0]?.url).toBe('https://api.example.com/admin/api/projects/proj-1/issues/issue-1')
79
- expect(JSON.parse(calls[0]?.body ?? '{}')).toEqual({
80
- resolvedInRelease: 'app@1.2.4+457',
81
- status: 'resolved',
82
- })
83
- expect(updated.status).toBe('resolved')
84
- })
85
- })
86
-
87
- describe('formatIssueLine', () => {
88
- test('renders id + status + title + event count on one line', () => {
89
- const line = formatIssueLine(sample)
90
- expect(line).toContain('issue-1')
91
- expect(line).toContain('active')
92
- expect(line).toContain('TypeError: undefined is not an object')
93
- expect(line).toContain('17×')
94
- })
95
- })
@@ -1,132 +0,0 @@
1
- import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
-
5
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
6
-
7
- import {
8
- dsymSlicesFromDwarfdump,
9
- dwarfBinariesIn,
10
- uploadDsym,
11
- uploadMapping,
12
- } from '../native-artifacts.js'
13
-
14
- const ADMIN = {
15
- apiUrl: 'https://api.example.com/',
16
- projectId: 'proj-1',
17
- token: 'sk_test',
18
- }
19
-
20
- let dir = ''
21
- const origFetch = globalThis.fetch
22
- let calls: { body: Buffer | null; headers: Headers; method: string; url: string }[]
23
- beforeEach(async () => {
24
- dir = await mkdtemp(join(tmpdir(), 'sentori-cli-native-'))
25
- calls = []
26
- })
27
- afterEach(async () => {
28
- globalThis.fetch = origFetch
29
- await rm(dir, { force: true, recursive: true })
30
- })
31
-
32
- function mockFetch(status = 200, respBody = ''): void {
33
- globalThis.fetch = (async (url: Request | string | URL, init?: RequestInit) => {
34
- const body = init?.body
35
- calls.push({
36
- body: body instanceof Uint8Array ? Buffer.from(body) : null,
37
- headers: new Headers(init?.headers),
38
- method: init?.method ?? 'GET',
39
- url: String(url),
40
- })
41
- return new Response(respBody, { status })
42
- }) as typeof fetch
43
- }
44
-
45
- describe('uploadMapping', () => {
46
- test('POSTs raw bytes to /mappings with Bearer + release query', async () => {
47
- mockFetch()
48
- const path = join(dir, 'mapping.txt')
49
- await writeFile(path, '# pg_map_id: abc\nfoo.bar -> a.b:\n')
50
- await uploadMapping({ ...ADMIN, path, release: 'app@1.0.0+1' })
51
- expect(calls).toHaveLength(1)
52
- expect(calls[0]?.method).toBe('POST')
53
- expect(calls[0]?.url).toBe(
54
- 'https://api.example.com/admin/api/projects/proj-1/mappings?release=app%401.0.0%2B1',
55
- )
56
- expect(calls[0]?.headers.get('authorization')).toBe('Bearer sk_test')
57
- expect(calls[0]?.headers.get('content-type')).toBe('application/octet-stream')
58
- expect(calls[0]?.body?.toString()).toContain('pg_map_id')
59
- })
60
-
61
- test('sets x-sentori-debug-id when given', async () => {
62
- mockFetch()
63
- const path = join(dir, 'mapping.txt')
64
- await writeFile(path, 'x')
65
- await uploadMapping({ ...ADMIN, debugId: '1234-uuid', path })
66
- expect(calls[0]?.headers.get('x-sentori-debug-id')).toBe('1234-uuid')
67
- })
68
-
69
- test('throws on empty file', async () => {
70
- const path = join(dir, 'empty.txt')
71
- await writeFile(path, '')
72
- await expect(uploadMapping({ ...ADMIN, path })).rejects.toThrow('empty mapping file')
73
- })
74
-
75
- test('throws on non-2xx with server detail', async () => {
76
- mockFetch(403, 'forbidden')
77
- const path = join(dir, 'mapping.txt')
78
- await writeFile(path, 'x')
79
- await expect(uploadMapping({ ...ADMIN, path })).rejects.toThrow('403')
80
- })
81
- })
82
-
83
- describe('uploadDsym (explicit single-slice)', () => {
84
- test('POSTs to /dsyms with x-sentori-debug-id, x-sentori-arch, release + objectName', async () => {
85
- mockFetch()
86
- const path = join(dir, 'Foo.dSYM/Contents/Resources/DWARF/Foo')
87
- await mkdir(join(dir, 'Foo.dSYM/Contents/Resources/DWARF'), { recursive: true })
88
- await writeFile(path, 'macho-bytes')
89
- const r = await uploadDsym({
90
- ...ADMIN,
91
- arch: 'arm64',
92
- debugId: '1234abcd-1234-1234-1234-1234567890ab',
93
- objectName: 'Foo',
94
- path,
95
- release: 'app@1.0.0+1',
96
- })
97
- expect(calls).toHaveLength(1)
98
- expect(calls[0]?.method).toBe('POST')
99
- expect(calls[0]?.url).toContain('/admin/api/projects/proj-1/dsyms')
100
- expect(calls[0]?.url).toContain('release=app%401.0.0%2B1')
101
- expect(calls[0]?.url).toContain('objectName=Foo')
102
- expect(calls[0]?.headers.get('x-sentori-debug-id')).toBe('1234ABCD-1234-1234-1234-1234567890AB')
103
- expect(calls[0]?.headers.get('x-sentori-arch')).toBe('arm64')
104
- expect(r.slices).toEqual([{ arch: 'arm64', debugId: '1234ABCD-1234-1234-1234-1234567890AB' }])
105
- })
106
- })
107
-
108
- describe('helpers', () => {
109
- test('dwarfBinariesIn walks a .dSYM bundle', async () => {
110
- const bundle = join(dir, 'Foo.dSYM')
111
- const dwarfDir = join(bundle, 'Contents/Resources/DWARF')
112
- await mkdir(dwarfDir, { recursive: true })
113
- await writeFile(join(dwarfDir, 'Foo'), 'x')
114
- await writeFile(join(dwarfDir, '.DS_Store'), 'noise')
115
- const found = dwarfBinariesIn(bundle)
116
- expect(found.map((p) => p.replace(bundle + '/', ''))).toEqual([
117
- 'Contents/Resources/DWARF/Foo',
118
- ])
119
- })
120
-
121
- test('dwarfBinariesIn on a plain file returns just that file', async () => {
122
- const f = join(dir, 'binary')
123
- await writeFile(f, 'x')
124
- expect(dwarfBinariesIn(f)).toEqual([f])
125
- })
126
-
127
- test('dsymSlicesFromDwarfdump returns [] when dwarfdump fails (or is absent)', () => {
128
- // pass a path that doesn't exist; dwarfdump exits non-zero (or isn't installed)
129
- const slices = dsymSlicesFromDwarfdump(join(dir, 'nope.dSYM'))
130
- expect(slices).toEqual([])
131
- })
132
- })
@@ -1,37 +0,0 @@
1
- import { mkdtemp, rm, writeFile } from 'node:fs/promises'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
-
5
- import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
6
-
7
- import { composeSourceMaps, resolveComposeScript } from '../react-native.js'
8
-
9
- let dir = ''
10
- beforeEach(async () => {
11
- dir = await mkdtemp(join(tmpdir(), 'sentori-cli-rn-'))
12
- })
13
- afterEach(async () => {
14
- await rm(dir, { force: true, recursive: true })
15
- })
16
-
17
- describe('react-native helpers', () => {
18
- test('resolveComposeScript returns null when react-native is not installed', () => {
19
- // This package doesn't depend on react-native, so from sdk/cli's
20
- // own node_modules there's nothing to find.
21
- expect(resolveComposeScript()).toBeNull()
22
- })
23
-
24
- test('composeSourceMaps throws "no such file" for a missing input', () => {
25
- expect(() => composeSourceMaps(join(dir, 'nope.map'), join(dir, 'nope2.map'))).toThrow(
26
- 'no such file',
27
- )
28
- })
29
-
30
- test('composeSourceMaps throws a helpful message when react-native is absent', async () => {
31
- await writeFile(join(dir, 'a.packager.map'), '{"version":3}')
32
- await writeFile(join(dir, 'a.hbc.map'), '{"version":3}')
33
- expect(() =>
34
- composeSourceMaps(join(dir, 'a.packager.map'), join(dir, 'a.hbc.map')),
35
- ).toThrow(/compose-source-maps\.js/)
36
- })
37
- })