Download superlog/apps/api/src/symbolication.test.ts from smlflg/Experimente: direct link, hf CLI and curl.
- Browser
- Download file 9.87 kB
-
https://huggingface.co/smlflg/Experimente/resolve/main/superlog/apps/api/src/symbolication.test.ts
- Command line
-
hf download hf://smlflg/Experimente/superlog/apps/api/src/symbolication.test.ts
-
curl -L -o symbolication.test.ts https://huggingface.co/smlflg/Experimente/resolve/main/superlog/apps/api/src/symbolication.test.ts
9.87 kB
| import assert from "node:assert/strict"; | |
| import { test } from "node:test"; | |
| import { gzipSync } from "node:zlib"; | |
| import type { DB, IssueSample } from "@superlog/db"; | |
| import type * as schema from "@superlog/db/schema"; | |
| import { | |
| findSourceMapArtifact, | |
| symbolicateIssueSample, | |
| symbolicateStacktraceWithArtifact, | |
| symbolicateTelemetrySample, | |
| symbolicationAttrsForSample, | |
| } from "./symbolication.js"; | |
| const sourceMap = JSON.stringify({ | |
| version: 3, | |
| file: "index.android.bundle", | |
| sources: ["app/index.tsx"], | |
| names: ["HomeScreen"], | |
| mappings: "AAAAA", | |
| }); | |
| const artifact = { | |
| id: "artifact-1", | |
| projectId: "project-1", | |
| platform: "android", | |
| release: "juno@1.2.3", | |
| dist: null, | |
| debugId: null, | |
| bundleFile: "index.android.bundle", | |
| mapFile: "index.android.bundle.map", | |
| sourceMapHash: "a".repeat(64), | |
| sourceMapBytes: Buffer.byteLength(sourceMap), | |
| storageBucket: "source-map-bucket", | |
| storageKey: "source-maps/project-1/android/hash.map.gz", | |
| contentEncoding: "gzip", | |
| uploadedByOrgApiKeyId: null, | |
| createdAt: new Date("2026-06-02T00:00:00.000Z"), | |
| updatedAt: new Date("2026-06-02T00:00:00.000Z"), | |
| } satisfies schema.SourceMapArtifact; | |
| test("symbolicateStacktraceWithArtifact rewrites generated stack frames", () => { | |
| const result = symbolicateStacktraceWithArtifact({ | |
| stacktrace: "TypeError: bad\n at useMemoCache (index.android.bundle:1:1)", | |
| sourceMap, | |
| artifact, | |
| }); | |
| assert.ok(result); | |
| assert.equal(result.stacktrace, "TypeError: bad\n at HomeScreen (app/index.tsx:1:1)"); | |
| assert.deepEqual(result.frames[0], { | |
| functionName: "HomeScreen", | |
| source: "app/index.tsx", | |
| line: 1, | |
| column: 1, | |
| generatedFile: "index.android.bundle", | |
| generatedLine: 1, | |
| generatedColumn: 1, | |
| }); | |
| }); | |
| test("symbolicateStacktraceWithArtifact infers original function names from source content", () => { | |
| const sourceMapWithoutNames = JSON.stringify({ | |
| version: 3, | |
| file: "entry.js", | |
| sources: ["/app/(tabs)/index.tsx"], | |
| sourcesContent: [ | |
| [ | |
| "async function emitError() {", | |
| " const superlog = await ensureSuperlog();", | |
| " superlog?.captureException(new Error('Expo SDK demo error'), { component: 'home' });", | |
| "}", | |
| ].join("\n"), | |
| ], | |
| names: [], | |
| mappings: "AAAA", | |
| }); | |
| const result = symbolicateStacktraceWithArtifact({ | |
| stacktrace: "Error: Expo SDK demo error\n at L (entry.js:1:1)", | |
| sourceMap: sourceMapWithoutNames, | |
| artifact: { ...artifact, platform: "web" }, | |
| }); | |
| assert.ok(result); | |
| assert.equal( | |
| result.stacktrace, | |
| "Error: Expo SDK demo error\n at emitError (/app/(tabs)/index.tsx:1:1)", | |
| ); | |
| assert.equal(result.frames[0]?.functionName, "emitError"); | |
| }); | |
| test("symbolicationAttrsForSample extracts release, platform, dist, and debug id", () => { | |
| const sample = { | |
| kind: "log", | |
| service: "juno", | |
| severity: "ERROR", | |
| message: "bad", | |
| body: "bad", | |
| exceptionType: "TypeError", | |
| topFrame: null, | |
| normalizedFrames: [], | |
| stacktrace: "TypeError: bad\n at index.android.bundle:1:1", | |
| seenAt: "2026-06-02T00:00:00.000Z", | |
| logAttrs: { | |
| "service.version": "juno@1.2.3", | |
| "device.platform": "Android", | |
| "expo.update_id": "update-1", | |
| "sourcemap.debug_id": "debug-1", | |
| }, | |
| resourceAttrs: null, | |
| } satisfies IssueSample; | |
| assert.deepEqual(symbolicationAttrsForSample(sample), { | |
| debugId: "debug-1", | |
| release: "juno@1.2.3", | |
| dist: "update-1", | |
| platform: "android", | |
| }); | |
| }); | |
| test("symbolicateIssueSample loads matching source map object and symbolicates", async () => { | |
| const sample = { | |
| kind: "log", | |
| service: "juno", | |
| severity: "ERROR", | |
| message: "bad", | |
| body: "bad", | |
| exceptionType: "TypeError", | |
| topFrame: null, | |
| normalizedFrames: [], | |
| stacktrace: "TypeError: bad\n at useMemoCache (index.android.bundle:1:1)", | |
| seenAt: "2026-06-02T00:00:00.000Z", | |
| logAttrs: { | |
| "service.version": "juno@1.2.3", | |
| "device.platform": "android", | |
| }, | |
| resourceAttrs: null, | |
| } satisfies IssueSample; | |
| const database = { | |
| query: { | |
| sourceMapArtifacts: { | |
| findFirst: async () => null, | |
| findMany: async () => [artifact], | |
| }, | |
| }, | |
| } as unknown as DB; | |
| const result = await symbolicateIssueSample({ | |
| database, | |
| objectReader: { | |
| async getSourceMapObject(input) { | |
| assert.equal(input.bucket, "source-map-bucket"); | |
| assert.equal(input.key, "source-maps/project-1/android/hash.map.gz"); | |
| return gzipSync(sourceMap); | |
| }, | |
| }, | |
| projectId: "project-1", | |
| sample, | |
| }); | |
| assert.ok(result); | |
| assert.equal(result.artifact.id, "artifact-1"); | |
| assert.equal(result.frames[0]?.source, "app/index.tsx"); | |
| }); | |
| test("symbolicateTelemetrySample symbolicates logs without issue rows", async () => { | |
| const database = { | |
| query: { | |
| sourceMapArtifacts: { | |
| findFirst: async () => null, | |
| findMany: async () => [artifact], | |
| }, | |
| }, | |
| } as unknown as DB; | |
| const result = await symbolicateTelemetrySample({ | |
| database, | |
| objectReader: { | |
| async getSourceMapObject(input) { | |
| assert.equal(input.bucket, "source-map-bucket"); | |
| assert.equal(input.key, "source-maps/project-1/android/hash.map.gz"); | |
| return gzipSync(sourceMap); | |
| }, | |
| }, | |
| projectId: "project-1", | |
| sample: { | |
| stacktrace: "TypeError: bad\n at useMemoCache (index.android.bundle:1:1)", | |
| logAttrs: { | |
| "service.version": "juno@1.2.3", | |
| "device.platform": "android", | |
| }, | |
| resourceAttrs: null, | |
| }, | |
| }); | |
| assert.ok(result); | |
| assert.equal(result.artifact.id, "artifact-1"); | |
| assert.equal(result.frames[0]?.source, "app/index.tsx"); | |
| }); | |
| test("symbolicateTelemetrySample returns null when a log has no stacktrace", async () => { | |
| const result = await symbolicateTelemetrySample({ | |
| database: {} as DB, | |
| objectReader: null, | |
| projectId: "project-1", | |
| sample: { | |
| stacktrace: null, | |
| logAttrs: null, | |
| resourceAttrs: null, | |
| }, | |
| }); | |
| assert.equal(result, null); | |
| }); | |
| test("findSourceMapArtifact prefers artifact matching generated stack frame file", async () => { | |
| const entryArtifact = { | |
| ...artifact, | |
| id: "entry-artifact", | |
| platform: "web", | |
| bundleFile: "dist/_expo/static/js/web/entry-abc123.js", | |
| mapFile: "dist/_expo/static/js/web/entry-abc123.js.map", | |
| createdAt: new Date("2026-06-02T00:00:00.000Z"), | |
| updatedAt: new Date("2026-06-02T00:00:00.000Z"), | |
| } satisfies schema.SourceMapArtifact; | |
| const indexArtifact = { | |
| ...artifact, | |
| id: "index-artifact", | |
| platform: "web", | |
| bundleFile: "dist/_expo/static/js/web/index-def456.js", | |
| mapFile: "dist/_expo/static/js/web/index-def456.js.map", | |
| createdAt: new Date("2026-06-02T00:01:00.000Z"), | |
| updatedAt: new Date("2026-06-02T00:01:00.000Z"), | |
| } satisfies schema.SourceMapArtifact; | |
| const database = { | |
| query: { | |
| sourceMapArtifacts: { | |
| findFirst: async () => null, | |
| findMany: async () => [indexArtifact, entryArtifact], | |
| }, | |
| }, | |
| } as unknown as DB; | |
| const result = await findSourceMapArtifact({ | |
| database, | |
| projectId: "project-1", | |
| attrs: { | |
| debugId: null, | |
| release: "juno@1.2.3", | |
| dist: null, | |
| platform: "web", | |
| }, | |
| stacktrace: | |
| "TypeError: bad\n at useMemoCache (https://app.example.com/_expo/static/js/web/entry-abc123.js:1:1)", | |
| }); | |
| assert.equal(result?.id, "entry-artifact"); | |
| }); | |
| test("findSourceMapArtifact falls back to generated stack frame file when release is missing", async () => { | |
| const entryArtifact = { | |
| ...artifact, | |
| id: "entry-artifact", | |
| platform: "web", | |
| release: "expo-test-2@local", | |
| bundleFile: "dist-superlog/_expo/static/js/web/entry-90d9514ed4f128f73d2feaf2e08ff315.js", | |
| mapFile: "dist-superlog/_expo/static/js/web/entry-90d9514ed4f128f73d2feaf2e08ff315.js.map", | |
| createdAt: new Date("2026-06-02T00:00:00.000Z"), | |
| updatedAt: new Date("2026-06-02T00:00:00.000Z"), | |
| } satisfies schema.SourceMapArtifact; | |
| const database = { | |
| query: { | |
| sourceMapArtifacts: { | |
| findFirst: async () => null, | |
| findMany: async () => [entryArtifact], | |
| }, | |
| }, | |
| } as unknown as DB; | |
| const result = await findSourceMapArtifact({ | |
| database, | |
| projectId: "project-1", | |
| attrs: { | |
| debugId: null, | |
| release: null, | |
| dist: null, | |
| platform: null, | |
| }, | |
| stacktrace: | |
| "Error: Expo SDK demo error\n at L (http://localhost:8093/_expo/static/js/web/entry-90d9514ed4f128f73d2feaf2e08ff315.js:1095:1077)", | |
| }); | |
| assert.equal(result?.id, "entry-artifact"); | |
| }); | |
| test("findSourceMapArtifact falls back to generated stack frame file when release attrs do not match upload release", async () => { | |
| const entryArtifact = { | |
| ...artifact, | |
| id: "entry-artifact", | |
| platform: "web", | |
| release: "expo-test-2@local", | |
| dist: null, | |
| bundleFile: "dist-superlog/_expo/static/js/web/entry-90d9514ed4f128f73d2feaf2e08ff315.js", | |
| mapFile: "dist-superlog/_expo/static/js/web/entry-90d9514ed4f128f73d2feaf2e08ff315.js.map", | |
| createdAt: new Date("2026-06-02T00:00:00.000Z"), | |
| updatedAt: new Date("2026-06-02T00:00:00.000Z"), | |
| } satisfies schema.SourceMapArtifact; | |
| const database = { | |
| query: { | |
| sourceMapArtifacts: { | |
| findFirst: async () => null, | |
| findMany: async (_query: unknown) => [entryArtifact], | |
| }, | |
| }, | |
| } as unknown as DB; | |
| const result = await findSourceMapArtifact({ | |
| database, | |
| projectId: "project-1", | |
| attrs: { | |
| debugId: null, | |
| release: "1.0.0", | |
| dist: "embedded", | |
| platform: "web", | |
| }, | |
| stacktrace: | |
| "Error: Expo SDK demo error\n at L (http://localhost:8093/_expo/static/js/web/entry-90d9514ed4f128f73d2feaf2e08ff315.js:1095:1077)", | |
| }); | |
| assert.equal(result?.id, "entry-artifact"); | |
| }); | |