@sentry/nitro 11.0.0-alpha.2 → 11.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"type":"module","version":"11.0.0-alpha.2","sideEffects":false}
1
+ {"type":"module","version":"11.0.0-beta.1","sideEffects":false}
@@ -1,4 +1,5 @@
1
- import { getClient, captureException, flushIfServerless, parseUrl } from '@sentry/core';
1
+ import { getClient, captureException, parseUrl } from '@sentry/core';
2
+ import { flushIfServerless } from '@sentry/core/server';
2
3
  import { HTTPError } from 'h3';
3
4
 
4
5
  function extractErrorContext(errorContext) {
@@ -1 +1 @@
1
- {"version":3,"file":"captureErrorHook.js","sources":["../../../../src/runtime/hooks/captureErrorHook.ts"],"sourcesContent":["import { captureException, flushIfServerless, getClient, parseUrl } from '@sentry/core';\nimport { HTTPError } from 'h3';\nimport type { CapturedErrorContext } from 'nitro/types';\n\n/**\n * Extracts the relevant context information from the error context (HTTPEvent in Nitro Error)\n * and creates a structured context object.\n */\nfunction extractErrorContext(errorContext: CapturedErrorContext | undefined): Record<string, unknown> {\n const ctx: Record<string, unknown> = {};\n\n if (!errorContext) {\n return ctx;\n }\n\n if (errorContext.event) {\n ctx.method = errorContext.event.req.method;\n ctx.path = parseUrl(errorContext.event.req.url).path;\n }\n\n if (Array.isArray(errorContext.tags)) {\n ctx.tags = errorContext.tags;\n }\n\n return ctx;\n}\n\n/**\n * Hook that can be added in a Nitro plugin. It captures an error and sends it to Sentry.\n */\nexport async function captureErrorHook(error: Error, errorContext: CapturedErrorContext): Promise<void> {\n const sentryClient = getClient();\n const sentryClientOptions = sentryClient?.getOptions();\n\n if (\n sentryClientOptions &&\n 'enableNitroErrorHandler' in sentryClientOptions &&\n sentryClientOptions.enableNitroErrorHandler === false\n ) {\n return;\n }\n\n // Do not report HTTPErrors with 3xx or 4xx status codes\n if (HTTPError.isError(error) && error.status >= 300 && error.status < 500) {\n return;\n }\n\n const structuredContext = extractErrorContext(errorContext);\n\n captureException(error, {\n captureContext: { contexts: { nitro: structuredContext } },\n mechanism: { handled: false, type: 'auto.function.nitro.captureErrorHook' },\n });\n\n await flushIfServerless();\n}\n"],"names":[],"mappings":";;;AAQA,SAAS,oBAAoB,YAAA,EAAyE;AACpG,EAAA,MAAM,MAA+B,EAAC;AAEtC,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,aAAa,KAAA,EAAO;AACtB,IAAA,GAAA,CAAI,MAAA,GAAS,YAAA,CAAa,KAAA,CAAM,GAAA,CAAI,MAAA;AACpC,IAAA,GAAA,CAAI,OAAO,QAAA,CAAS,YAAA,CAAa,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA,CAAE,IAAA;AAAA,EAClD;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,YAAA,CAAa,IAAI,CAAA,EAAG;AACpC,IAAA,GAAA,CAAI,OAAO,YAAA,CAAa,IAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,GAAA;AACT;AAKA,eAAsB,gBAAA,CAAiB,OAAc,YAAA,EAAmD;AACtG,EAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,EAAA,MAAM,mBAAA,GAAsB,cAAc,UAAA,EAAW;AAErD,EAAA,IACE,mBAAA,IACA,yBAAA,IAA6B,mBAAA,IAC7B,mBAAA,CAAoB,4BAA4B,KAAA,EAChD;AACA,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,SAAA,CAAU,QAAQ,KAAK,CAAA,IAAK,MAAM,MAAA,IAAU,GAAA,IAAO,KAAA,CAAM,MAAA,GAAS,GAAA,EAAK;AACzE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,iBAAA,GAAoB,oBAAoB,YAAY,CAAA;AAE1D,EAAA,gBAAA,CAAiB,KAAA,EAAO;AAAA,IACtB,gBAAgB,EAAE,QAAA,EAAU,EAAE,KAAA,EAAO,mBAAkB,EAAE;AAAA,IACzD,SAAA,EAAW,EAAE,OAAA,EAAS,KAAA,EAAO,MAAM,sCAAA;AAAuC,GAC3E,CAAA;AAED,EAAA,MAAM,iBAAA,EAAkB;AAC1B;;;;"}
1
+ {"version":3,"file":"captureErrorHook.js","sources":["../../../../src/runtime/hooks/captureErrorHook.ts"],"sourcesContent":["import { captureException, getClient, parseUrl } from '@sentry/core';\nimport { flushIfServerless } from '@sentry/core/server';\nimport { HTTPError } from 'h3';\nimport type { CapturedErrorContext } from 'nitro/types';\n\n/**\n * Extracts the relevant context information from the error context (HTTPEvent in Nitro Error)\n * and creates a structured context object.\n */\nfunction extractErrorContext(errorContext: CapturedErrorContext | undefined): Record<string, unknown> {\n const ctx: Record<string, unknown> = {};\n\n if (!errorContext) {\n return ctx;\n }\n\n if (errorContext.event) {\n ctx.method = errorContext.event.req.method;\n ctx.path = parseUrl(errorContext.event.req.url).path;\n }\n\n if (Array.isArray(errorContext.tags)) {\n ctx.tags = errorContext.tags;\n }\n\n return ctx;\n}\n\n/**\n * Hook that can be added in a Nitro plugin. It captures an error and sends it to Sentry.\n */\nexport async function captureErrorHook(error: Error, errorContext: CapturedErrorContext): Promise<void> {\n const sentryClient = getClient();\n const sentryClientOptions = sentryClient?.getOptions();\n\n if (\n sentryClientOptions &&\n 'enableNitroErrorHandler' in sentryClientOptions &&\n sentryClientOptions.enableNitroErrorHandler === false\n ) {\n return;\n }\n\n // Do not report HTTPErrors with 3xx or 4xx status codes\n if (HTTPError.isError(error) && error.status >= 300 && error.status < 500) {\n return;\n }\n\n const structuredContext = extractErrorContext(errorContext);\n\n captureException(error, {\n captureContext: { contexts: { nitro: structuredContext } },\n mechanism: { handled: false, type: 'auto.function.nitro.captureErrorHook' },\n });\n\n await flushIfServerless();\n}\n"],"names":[],"mappings":";;;;AASA,SAAS,oBAAoB,YAAA,EAAyE;AACpG,EAAA,MAAM,MAA+B,EAAC;AAEtC,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,aAAa,KAAA,EAAO;AACtB,IAAA,GAAA,CAAI,MAAA,GAAS,YAAA,CAAa,KAAA,CAAM,GAAA,CAAI,MAAA;AACpC,IAAA,GAAA,CAAI,OAAO,QAAA,CAAS,YAAA,CAAa,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA,CAAE,IAAA;AAAA,EAClD;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,YAAA,CAAa,IAAI,CAAA,EAAG;AACpC,IAAA,GAAA,CAAI,OAAO,YAAA,CAAa,IAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,GAAA;AACT;AAKA,eAAsB,gBAAA,CAAiB,OAAc,YAAA,EAAmD;AACtG,EAAA,MAAM,eAAe,SAAA,EAAU;AAC/B,EAAA,MAAM,mBAAA,GAAsB,cAAc,UAAA,EAAW;AAErD,EAAA,IACE,mBAAA,IACA,yBAAA,IAA6B,mBAAA,IAC7B,mBAAA,CAAoB,4BAA4B,KAAA,EAChD;AACA,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,SAAA,CAAU,QAAQ,KAAK,CAAA,IAAK,MAAM,MAAA,IAAU,GAAA,IAAO,KAAA,CAAM,MAAA,GAAS,GAAA,EAAK;AACzE,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,iBAAA,GAAoB,oBAAoB,YAAY,CAAA;AAE1D,EAAA,gBAAA,CAAiB,KAAA,EAAO;AAAA,IACtB,gBAAgB,EAAE,QAAA,EAAU,EAAE,KAAA,EAAO,mBAAkB,EAAE;AAAA,IACzD,SAAA,EAAW,EAAE,OAAA,EAAS,KAAA,EAAO,MAAM,sCAAA;AAAuC,GAC3E,CAAA;AAED,EAAA,MAAM,iBAAA,EAAkB;AAC1B;;;;"}
@@ -1,7 +1,8 @@
1
1
  import * as dc from 'node:diagnostics_channel';
2
- import { SENTRY_OP } from '@sentry/conventions/attributes';
2
+ import { CACHE_OPERATION, SENTRY_OP } from '@sentry/conventions/attributes';
3
3
  import { CACHE_REMOVE, CACHE_PUT, CACHE_GET } from '@sentry/conventions/op';
4
- import { GLOBAL_OBJ, startInactiveSpan, SEMANTIC_ATTRIBUTE_CACHE_KEY, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_CACHE_HIT, flushIfServerless, isObjectLike } from '@sentry/core';
4
+ import { GLOBAL_OBJ, CACHE_OPERATION_NAMES, getClient, startInactiveSpan, SEMANTIC_ATTRIBUTE_CACHE_KEY, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, hasSpanStreamingEnabled, SEMANTIC_ATTRIBUTE_CACHE_HIT, isObjectLike } from '@sentry/core';
5
+ import { flushIfServerless } from '@sentry/core/server';
5
6
  import { bindTracingChannelToSpan } from '@sentry/server-utils';
6
7
 
7
8
  const ORIGIN = "auto.cache.nitro";
@@ -51,10 +52,14 @@ function setupStorageTracingChannel(operation) {
51
52
  dc.tracingChannel(`unstorage.${operation}`),
52
53
  (data) => {
53
54
  const cacheKeys = keys(data);
55
+ const cacheOperationName = CACHE_OPERATION_NAMES[OPERATION_SPAN_OPS[operation]];
56
+ const client = getClient();
54
57
  return startInactiveSpan({
55
- name: cacheKeys.join(", ") || operation,
58
+ // With span streaming, span names have to be low cardinality, so we can't fall back to the cache keys.
59
+ name: client && hasSpanStreamingEnabled(client) ? OPERATION_SPAN_OPS[operation] : cacheKeys.join(", ") || operation,
56
60
  attributes: {
57
61
  [SENTRY_OP]: OPERATION_SPAN_OPS[operation],
62
+ [CACHE_OPERATION]: cacheOperationName,
58
63
  [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,
59
64
  [SEMANTIC_ATTRIBUTE_CACHE_KEY]: cacheKeys.length > 1 ? cacheKeys : cacheKeys[0],
60
65
  "db.operation.name": operation,
@@ -1 +1 @@
1
- {"version":3,"file":"captureStorageEvents.js","sources":["../../../../src/runtime/hooks/captureStorageEvents.ts"],"sourcesContent":["import * as dc from 'node:diagnostics_channel';\nimport { SENTRY_OP } from '@sentry/conventions/attributes';\nimport { CACHE_GET, CACHE_PUT, CACHE_REMOVE } from '@sentry/conventions/op';\nimport {\n flushIfServerless,\n GLOBAL_OBJ,\n isObjectLike,\n SEMANTIC_ATTRIBUTE_CACHE_HIT,\n SEMANTIC_ATTRIBUTE_CACHE_KEY,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n} from '@sentry/core';\nimport { bindTracingChannelToSpan } from '@sentry/server-utils';\nimport type { TraceContext } from 'unstorage/tracing';\n\nconst ORIGIN = 'auto.cache.nitro';\n\nconst globalWithStorageChannels = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __SENTRY_NITRO_STORAGE_CHANNELS_INSTRUMENTED__: boolean;\n};\n\nconst TRACED_OPERATIONS = [\n 'hasItem',\n 'getItem',\n 'getItemRaw',\n 'getItems',\n 'setItem',\n 'setItemRaw',\n 'setItems',\n 'removeItem',\n 'getKeys',\n 'clear',\n] as const;\n\ntype TracedOperation = (typeof TRACED_OPERATIONS)[number];\n\nconst CACHE_HIT_OPERATIONS = new Set<TracedOperation>(['hasItem', 'getItem', 'getItemRaw', 'getItems']);\n\n/**\n * Maps each unstorage operation to a convention cache op. Reads (including existence and key\n * listing) are `cache.get`, writes are `cache.put`, and deletions are `cache.remove`.\n * The precise operation stays available on `db.operation.name`.\n */\nconst OPERATION_SPAN_OPS = {\n hasItem: CACHE_GET,\n getItem: CACHE_GET,\n getItemRaw: CACHE_GET,\n getItems: CACHE_GET,\n getKeys: CACHE_GET,\n setItem: CACHE_PUT,\n setItemRaw: CACHE_PUT,\n setItems: CACHE_PUT,\n removeItem: CACHE_REMOVE,\n clear: CACHE_REMOVE,\n} as const satisfies Record<TracedOperation, string>;\n\nconst CACHED_FN_HANDLERS_RE = /^nitro:(functions|handlers):/i;\n\n/**\n * Subscribes to unstorage tracing channels and creates Sentry spans for storage operations.\n */\nexport function captureStorageEvents(): void {\n if (globalWithStorageChannels.__SENTRY_NITRO_STORAGE_CHANNELS_INSTRUMENTED__) {\n return;\n }\n\n for (const operation of TRACED_OPERATIONS) {\n setupStorageTracingChannel(operation);\n }\n\n globalWithStorageChannels.__SENTRY_NITRO_STORAGE_CHANNELS_INSTRUMENTED__ = true;\n}\n\nfunction setupStorageTracingChannel(operation: TracedOperation): void {\n const keys = (data: TraceContext): string[] => data.keys ?? [];\n const mountBase = (data: TraceContext): string => (data.base ?? '').replace(/:$/, '');\n\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n bindTracingChannelToSpan(\n dc.tracingChannel<TraceContext>(`unstorage.${operation}`),\n data => {\n const cacheKeys = keys(data);\n\n return startInactiveSpan({\n name: cacheKeys.join(', ') || operation,\n attributes: {\n [SENTRY_OP]: OPERATION_SPAN_OPS[operation],\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_CACHE_KEY]: cacheKeys.length > 1 ? cacheKeys : cacheKeys[0],\n 'db.operation.name': operation,\n 'db.collection.name': mountBase(data),\n 'db.system.name': data.driver?.name ?? 'unknown',\n },\n });\n },\n {\n beforeSpanEnd(span, data) {\n // Error status is set by the binding; the error itself is captured at the request boundary,\n // not here (cache ops aren't an error boundary). Only enrich the success path.\n if (!('error' in data)) {\n const result = (data as { result?: unknown }).result;\n if (CACHE_HIT_OPERATIONS.has(operation)) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, resolveCacheHit(operation, data.keys?.[0], result));\n }\n }\n\n void flushIfServerless();\n },\n },\n );\n}\n\n/**\n * Resolves the `cache.hit` value for a read operation. `hasItem` returns a boolean directly,\n * `getItems` returns a `{ key, value }[]` where a hit means at least one entry has a value,\n * and single-key reads fall back to the value-based `isCacheHit` check.\n */\nfunction resolveCacheHit(operation: TracedOperation, key: unknown, result: unknown): boolean {\n if (operation === 'hasItem') {\n return Boolean(result);\n }\n\n if (operation === 'getItems') {\n return Array.isArray(result) && result.some(item => isObjectLike(item) && item.value != null);\n }\n\n return isCacheHit(key, result);\n}\n\ninterface CacheEntry<T = unknown> {\n value?: T;\n expires?: number;\n}\n\ninterface ResponseCacheEntry {\n status?: number;\n body?: unknown;\n headers?: Record<string, string | undefined>;\n}\n\nfunction isCacheHit(key: unknown, value: unknown): boolean {\n try {\n const isEmpty = value == null;\n if (isEmpty || typeof key !== 'string' || !CACHED_FN_HANDLERS_RE.test(key)) {\n return !isEmpty;\n }\n\n const entry = typeof value === 'string' ? (JSON.parse(value) as CacheEntry) : (value as CacheEntry);\n\n return validateCacheEntry(key, entry);\n } catch {\n return false;\n }\n}\n\nfunction validateCacheEntry(\n key: string,\n entry: CacheEntry | CacheEntry<ResponseCacheEntry & { status: number }>,\n): boolean {\n if (entry.value == null) {\n return false;\n }\n\n if (Date.now() > (entry.expires || 0)) {\n return false;\n }\n\n if (isResponseCacheEntry(key, entry)) {\n if ((entry.value.status ?? 0) >= 400) {\n return false;\n }\n\n if (entry.value.body === undefined) {\n return false;\n }\n\n if (entry.value.headers?.etag === 'undefined' || entry.value.headers?.['last-modified'] === 'undefined') {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isResponseCacheEntry(key: string, _: CacheEntry): _ is CacheEntry<ResponseCacheEntry & { status: number }> {\n return key.startsWith('nitro:handlers:');\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"captureStorageEvents.js","sources":["../../../../src/runtime/hooks/captureStorageEvents.ts"],"sourcesContent":["import * as dc from 'node:diagnostics_channel';\nimport { CACHE_OPERATION, SENTRY_OP } from '@sentry/conventions/attributes';\nimport { CACHE_GET, CACHE_PUT, CACHE_REMOVE } from '@sentry/conventions/op';\nimport {\n CACHE_OPERATION_NAMES,\n getClient,\n GLOBAL_OBJ,\n hasSpanStreamingEnabled,\n isObjectLike,\n SEMANTIC_ATTRIBUTE_CACHE_HIT,\n SEMANTIC_ATTRIBUTE_CACHE_KEY,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n startInactiveSpan,\n} from '@sentry/core';\nimport { flushIfServerless } from '@sentry/core/server';\nimport { bindTracingChannelToSpan } from '@sentry/server-utils';\nimport type { TraceContext } from 'unstorage/tracing';\n\nconst ORIGIN = 'auto.cache.nitro';\n\nconst globalWithStorageChannels = GLOBAL_OBJ as typeof GLOBAL_OBJ & {\n __SENTRY_NITRO_STORAGE_CHANNELS_INSTRUMENTED__: boolean;\n};\n\nconst TRACED_OPERATIONS = [\n 'hasItem',\n 'getItem',\n 'getItemRaw',\n 'getItems',\n 'setItem',\n 'setItemRaw',\n 'setItems',\n 'removeItem',\n 'getKeys',\n 'clear',\n] as const;\n\ntype TracedOperation = (typeof TRACED_OPERATIONS)[number];\n\nconst CACHE_HIT_OPERATIONS = new Set<TracedOperation>(['hasItem', 'getItem', 'getItemRaw', 'getItems']);\n\n/**\n * Maps each unstorage operation to a convention cache op. Reads (including existence and key\n * listing) are `cache.get`, writes are `cache.put`, and deletions are `cache.remove`.\n * The precise operation stays available on `db.operation.name`.\n */\nconst OPERATION_SPAN_OPS = {\n hasItem: CACHE_GET,\n getItem: CACHE_GET,\n getItemRaw: CACHE_GET,\n getItems: CACHE_GET,\n getKeys: CACHE_GET,\n setItem: CACHE_PUT,\n setItemRaw: CACHE_PUT,\n setItems: CACHE_PUT,\n removeItem: CACHE_REMOVE,\n clear: CACHE_REMOVE,\n} as const satisfies Record<TracedOperation, string>;\n\nconst CACHED_FN_HANDLERS_RE = /^nitro:(functions|handlers):/i;\n\n/**\n * Subscribes to unstorage tracing channels and creates Sentry spans for storage operations.\n */\nexport function captureStorageEvents(): void {\n if (globalWithStorageChannels.__SENTRY_NITRO_STORAGE_CHANNELS_INSTRUMENTED__) {\n return;\n }\n\n for (const operation of TRACED_OPERATIONS) {\n setupStorageTracingChannel(operation);\n }\n\n globalWithStorageChannels.__SENTRY_NITRO_STORAGE_CHANNELS_INSTRUMENTED__ = true;\n}\n\nfunction setupStorageTracingChannel(operation: TracedOperation): void {\n const keys = (data: TraceContext): string[] => data.keys ?? [];\n const mountBase = (data: TraceContext): string => (data.base ?? '').replace(/:$/, '');\n\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n bindTracingChannelToSpan(\n dc.tracingChannel<TraceContext>(`unstorage.${operation}`),\n data => {\n const cacheKeys = keys(data);\n const cacheOperationName = CACHE_OPERATION_NAMES[OPERATION_SPAN_OPS[operation]];\n const client = getClient();\n\n return startInactiveSpan({\n // With span streaming, span names have to be low cardinality, so we can't fall back to the cache keys.\n name:\n client && hasSpanStreamingEnabled(client) ? OPERATION_SPAN_OPS[operation] : cacheKeys.join(', ') || operation,\n attributes: {\n [SENTRY_OP]: OPERATION_SPAN_OPS[operation],\n [CACHE_OPERATION]: cacheOperationName,\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [SEMANTIC_ATTRIBUTE_CACHE_KEY]: cacheKeys.length > 1 ? cacheKeys : cacheKeys[0],\n 'db.operation.name': operation,\n 'db.collection.name': mountBase(data),\n 'db.system.name': data.driver?.name ?? 'unknown',\n },\n });\n },\n {\n beforeSpanEnd(span, data) {\n // Error status is set by the binding; the error itself is captured at the request boundary,\n // not here (cache ops aren't an error boundary). Only enrich the success path.\n if (!('error' in data)) {\n const result = (data as { result?: unknown }).result;\n if (CACHE_HIT_OPERATIONS.has(operation)) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, resolveCacheHit(operation, data.keys?.[0], result));\n }\n }\n\n void flushIfServerless();\n },\n },\n );\n}\n\n/**\n * Resolves the `cache.hit` value for a read operation. `hasItem` returns a boolean directly,\n * `getItems` returns a `{ key, value }[]` where a hit means at least one entry has a value,\n * and single-key reads fall back to the value-based `isCacheHit` check.\n */\nfunction resolveCacheHit(operation: TracedOperation, key: unknown, result: unknown): boolean {\n if (operation === 'hasItem') {\n return Boolean(result);\n }\n\n if (operation === 'getItems') {\n return Array.isArray(result) && result.some(item => isObjectLike(item) && item.value != null);\n }\n\n return isCacheHit(key, result);\n}\n\ninterface CacheEntry<T = unknown> {\n value?: T;\n expires?: number;\n}\n\ninterface ResponseCacheEntry {\n status?: number;\n body?: unknown;\n headers?: Record<string, string | undefined>;\n}\n\nfunction isCacheHit(key: unknown, value: unknown): boolean {\n try {\n const isEmpty = value == null;\n if (isEmpty || typeof key !== 'string' || !CACHED_FN_HANDLERS_RE.test(key)) {\n return !isEmpty;\n }\n\n const entry = typeof value === 'string' ? (JSON.parse(value) as CacheEntry) : (value as CacheEntry);\n\n return validateCacheEntry(key, entry);\n } catch {\n return false;\n }\n}\n\nfunction validateCacheEntry(\n key: string,\n entry: CacheEntry | CacheEntry<ResponseCacheEntry & { status: number }>,\n): boolean {\n if (entry.value == null) {\n return false;\n }\n\n if (Date.now() > (entry.expires || 0)) {\n return false;\n }\n\n if (isResponseCacheEntry(key, entry)) {\n if ((entry.value.status ?? 0) >= 400) {\n return false;\n }\n\n if (entry.value.body === undefined) {\n return false;\n }\n\n if (entry.value.headers?.etag === 'undefined' || entry.value.headers?.['last-modified'] === 'undefined') {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isResponseCacheEntry(key: string, _: CacheEntry): _ is CacheEntry<ResponseCacheEntry & { status: number }> {\n return key.startsWith('nitro:handlers:');\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"captureErrorHook.d.ts","sourceRoot":"","sources":["../../../../src/runtime/hooks/captureErrorHook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAyBxD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtG"}
1
+ {"version":3,"file":"captureErrorHook.d.ts","sourceRoot":"","sources":["../../../../src/runtime/hooks/captureErrorHook.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAyBxD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtG"}
@@ -1 +1 @@
1
- {"version":3,"file":"captureStorageEvents.d.ts","sourceRoot":"","sources":["../../../../src/runtime/hooks/captureStorageEvents.ts"],"names":[],"mappings":"AA0DA;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAU3C"}
1
+ {"version":3,"file":"captureStorageEvents.d.ts","sourceRoot":"","sources":["../../../../src/runtime/hooks/captureStorageEvents.ts"],"names":[],"mappings":"AA6DA;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAU3C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/nitro",
3
- "version": "11.0.0-alpha.2",
3
+ "version": "11.0.0-beta.1",
4
4
  "description": "Official Sentry SDK for Nitro",
5
5
  "repository": "git://github.com/getsentry/sentry-javascript.git",
6
6
  "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/nitro",
@@ -35,11 +35,12 @@
35
35
  "nitro": ">=3.0.0-0 <4.0.0 || 3.0.260311-beta || 3.0.260415-beta"
36
36
  },
37
37
  "dependencies": {
38
- "@sentry/bundler-plugins": "11.0.0-alpha.2",
38
+ "@sentry/bundler-plugins": "11.0.0-beta.1",
39
39
  "@sentry/conventions": "^0.20.0",
40
- "@sentry/core": "11.0.0-alpha.2",
41
- "@sentry/node": "11.0.0-alpha.2",
42
- "@sentry/server-utils": "11.0.0-alpha.2"
40
+ "@sentry/core": "11.0.0-beta.1",
41
+ "@sentry/node": "11.0.0-beta.1",
42
+ "@sentry/server-runtime-injection": "11.0.0-beta.1",
43
+ "@sentry/server-utils": "11.0.0-beta.1"
43
44
  },
44
45
  "devDependencies": {
45
46
  "nitro": "^3.0.260415-beta",