getsentry / getsentry/sentry-javascript
Consolidate duplicated low-level helpers
- 主要语言
- TypeScript
- 星标
- 8.7k
- 派生
- 1.8k
- 平均合并
- 1 天 17 小时
- 30 天内合并 PR
- 523
描述
We re-implement the same small helpers (type guards, coercions, safe serializers) across many packages, often looser or subtly different from what already lives in other packages. This is a maintainability and correctness cleanup, not a bundle-size one as I think it wouldn't amount to much.
This came up after noticing we keep getting those `isRecord` utils, probably from agents liking it too much. Still it creates a bit of a situation where we have competing or duplicate utils that only differ in name or slight behavior mismatch.
I did a scan over what could be consolidated to our now three main util sources: `server-utils`, `browser-utils` and `core` depending on where the utils are duplicated and which runtimes they are relevant to.
### Stuff to consolidate in `core`
- `isRecord` (server-utils/src/vercel-ai/util.ts:19), `isObject` (browser/src/integrations/graphqlClient.ts:218), `isObject` (node-core/src/integrations/winston.ts:133). All identical `typeof === 'object' && x !== null`, and all intentionally accept arrays unlike core's existing `isPlainObject`.
- `arrayify` / `toArray` (bundler-plugins/src/core/utils.ts:17, plus aws vendored copies). Identical `Array.isArray(x) ? x : [x]`. Leave the vendored aws copies.
- `uniq` (inline): tanstackstart-react (routePatterns.ts:71, autoInstrumentMiddleware.ts:182), aws-serverless (MessageAttributes.ts:76), server-utils (orchestrion/config/index.ts:29) use `[...new Set()]`; vue (tracing.ts:65) uses `filter`+`indexOf` (O(n^2)). Same output, vue variant is the odd one.
- Nullish guards `isEmptyValue`: nitro (captureStorageEvents.ts:102) + nuxt (instrumentStorage.ts:215). Byte-identical `v === null || v === undefined`.
- `safeStringify` (server-utils/src/vercel-ai/util.ts:24) vs core-internal `getJsonString` (tracing/ai/utils.ts:198, NOT exported from the barrel). Identical except `safeStringify` adds a try/catch and returns `'[unserializable]'`, while `getJsonString` throws on circular refs.
- `addOriginToSpan`: cloudflare (utils/addOriginToSpan.ts:6), node-core (utils/addOriginToSpan.ts:6), opentelemetry (utils/addOriginToSpan.ts:6). Byte-identical including imports. cloudflare only depends on core (not opentelemetry), so core is the shared home. Sets `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN`, already core-exported.
- `lowQualityTransactionsFilter`: nuxt (server/sdk.ts:58) + solidstart (server/utils.ts:25) + react-router (integration variant) (+ hono patterns). Shared scaffolding (transaction-type guard, `Object.assign(fn, { id })`, log-then-return-null), but the match predicates are genuinely per-framework. Share a `makeTransactionFilter(id, predicate)`; keep predicates local.
- `parseEventHintOrCaptureContext`: node-core (utils/prepareEvent.ts:17) duplicates core's exported copy.
- `isStaticAssetRequest`: node-core (integrations/http/httpServerSpansIntegration.ts:292) duplicates core's exported copy.
- `classifyResponseStreaming`: deno `packages/deno/src/utils/streaming.ts` and cloudflare `packages/cloudflare/src/utils/streaming.ts`. Bun needs it too.
### Consolidate into @sentry/browser-utils
- `isElement`: already re-homed here (browser-utils/src/is.ts:6) and adds a try/catch core lacks. Core's copy is deprecated.
- `htmlTreeAsString`: browser-utils (htmlTreeAsString.ts:55) vs core (utils/browser.ts:20, exported). Divergent, should drop core's.
### Consolidate into @sentry/server-utils
- `isAsyncIterable`: server-utils tracing-channel/anthropic.ts:138 + openai.ts:116. Byte-identical intra-package duplicate. Hoist into one server-utils util (no API change).
- `getVercelEnv`: nextjs (common/getVercelEnv.ts:6) + vercel-edge (utils/vercel.ts:10). Byte-identical bodies (vercel-edge only adds a local `declare const process`).
- `flushSafelyWithTimeout`: nextjs (common/utils/responseEnd.ts:48) + nuxt (server/sdk.ts:123). Byte-identical (comment wording differs only). Core already has an internal `flushWithTimeout` (utils/flushIfServerless.ts:11) with the same shape - export it (or a `flushSafely`) and have both import it.
- `customRewriteFramesIntegration`: nextjs (server + edge - and these two already drifted) + sveltekit. Only the `defineIntegration` wrapper is shared; the iteratees are framework-specific. De-dup the nextjs server/edge pair first; sharing the thin wrapper is optional.
- `packages/vercel-edge/src/logs/exports.ts` is dead code. Imported nowhere, exported nowhere. Delete.
### Maybe in node-core?
- `markEventUnhandled`: aws-serverless (utils.ts:16) + google-cloud-serverless (utils.ts:46). Byte-identical. Both depend on node-core (not server-utils), so node-core is the home.
贡献指南
调研方向
Start by comparing the duplicate implementations in the listed package files and choose one consolidation target, such as the core helpers or a server-utils duplicate. Trace the relevant exports and imports before changing anything; done means the selected duplicate is removed, consumers use the shared helper, and the affected packages still pass their checks.
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- typescript
- 领域
- tooling
- Issue 类型
- 重构
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100