Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2134ba58b8 | ||
|
|
c2bcc6c82c | ||
|
|
16550ec8bb |
+1
-1
@@ -1 +1 @@
|
||||
{ "singleQuote": false, "tabWidth": 4, "trailingComma": "all", "printWidth": 100 }
|
||||
{ "singleQuote": false, "tabWidth": 4, "trailingComma": "all", "printWidth": 100, "semi": true }
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# One-time bootstrap for the v0.9.1 tag, whose original tag workflow failed before publishing.
|
||||
when:
|
||||
- event: manual
|
||||
|
||||
steps:
|
||||
publish-v0-9-1:
|
||||
image: node:24-bookworm-slim
|
||||
environment:
|
||||
NPM_TOKEN:
|
||||
from_secret: gitea_package_token
|
||||
commands:
|
||||
- corepack enable
|
||||
- corepack prepare pnpm@11.0.0 --activate
|
||||
- apt-get update && apt-get install --yes --no-install-recommends ca-certificates git
|
||||
- pnpm install --frozen-lockfile
|
||||
- test "$(node -p \"require('./package.json').version\")" = "0.9.1"
|
||||
- git rev-parse --verify refs/tags/v0.9.1
|
||||
- pnpm check
|
||||
- pnpm test
|
||||
- pnpm build
|
||||
- npm config set @mifi:registry https://git.mifi.dev/api/packages/mifi/npm/
|
||||
- npm config set -- //git.mifi.dev/api/packages/mifi/npm/:_authToken "$NPM_TOKEN"
|
||||
- pnpm publish --no-git-checks
|
||||
|
||||
notify-bootstrap-failure:
|
||||
image: curlimages/curl:8.14.1
|
||||
depends_on: [publish-v0-9-1]
|
||||
environment:
|
||||
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||
from_secret: mattermost_bot_access_token
|
||||
MATTERMOST_CHANNEL_ID:
|
||||
from_secret: mattermost_pushes_channel_id
|
||||
MATTERMOST_POST_API_URL:
|
||||
from_secret: mattermost_post_api_url
|
||||
commands:
|
||||
- |
|
||||
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Package bootstrap failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||
curl --fail --show-error --silent --request POST --header 'Content-Type: application/json' --header "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" --data "$BODY" "$MATTERMOST_POST_API_URL"
|
||||
when:
|
||||
- status: [failure]
|
||||
|
||||
notify-bootstrap-success:
|
||||
image: curlimages/curl:8.14.1
|
||||
depends_on: [publish-v0-9-1]
|
||||
environment:
|
||||
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||
from_secret: mattermost_bot_access_token
|
||||
MATTERMOST_CHANNEL_ID:
|
||||
from_secret: mattermost_pushes_channel_id
|
||||
MATTERMOST_POST_API_URL:
|
||||
from_secret: mattermost_post_api_url
|
||||
commands:
|
||||
- |
|
||||
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Package bootstrap success 🎉"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||
curl --fail --show-error --silent --request POST --header 'Content-Type: application/json' --header "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" --data "$BODY" "$MATTERMOST_POST_API_URL"
|
||||
when:
|
||||
- status: [success]
|
||||
@@ -3,6 +3,9 @@ when:
|
||||
- event: push
|
||||
branch: main
|
||||
|
||||
labels:
|
||||
performance: low
|
||||
|
||||
steps:
|
||||
verify:
|
||||
image: node:24-bookworm-slim
|
||||
|
||||
@@ -6,6 +6,9 @@ when:
|
||||
depends_on:
|
||||
- ci
|
||||
|
||||
labels:
|
||||
performance: high
|
||||
|
||||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
|
||||
+10
-3
@@ -1,10 +1,17 @@
|
||||
## [1.0.1](https://git.mifi.dev/mifi/logger/compare/v1.0.0...v1.0.1) (2026-08-07)
|
||||
# [1.1.0](https://git.mifi.dev/mifi/logger/compare/v1.0.1...v1.1.0) (2026-08-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* now supports `async` functions for logging data ([c2bcc6c](https://git.mifi.dev/mifi/logger/commit/c2bcc6c82c675640b85e97f1d78dd7c6e627d8f0))
|
||||
* now supports `async` functions for logging data ([16550ec](https://git.mifi.dev/mifi/logger/commit/16550ec8bb7810ccc36c36cbaab5542e4ad66dae))
|
||||
|
||||
## [1.0.1](https://git.mifi.dev/mifi/logger/compare/v1.0.0...v1.0.1) (2026-08-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **sentry:** widen SentryLike so official SDKs assign without adapters ([9f6f6e4](https://git.mifi.dev/mifi/logger/commit/9f6f6e4df52449a84111656a939fda5f59816ea5))
|
||||
* **sentry:** widen SentryLike so official SDKs assign without adapters ([3c468db](https://git.mifi.dev/mifi/logger/commit/3c468dbf44caf119b4ff769cbc158a4c3d9f6c54))
|
||||
- **sentry:** widen SentryLike so official SDKs assign without adapters ([9f6f6e4](https://git.mifi.dev/mifi/logger/commit/9f6f6e4df52449a84111656a939fda5f59816ea5))
|
||||
- **sentry:** widen SentryLike so official SDKs assign without adapters ([3c468db](https://git.mifi.dev/mifi/logger/commit/3c468dbf44caf119b4ff769cbc158a4c3d9f6c54))
|
||||
|
||||
# [1.0.0](https://git.mifi.dev/mifi/logger/compare/v0.10.0...v1.0.0) (2026-08-07)
|
||||
|
||||
|
||||
@@ -196,8 +196,13 @@ Any function passed **after** the first argument is invoked only if the event is
|
||||
|
||||
```ts
|
||||
logger.debug("state", () => buildHugeSnapshot()); // skipped when debug is suppressed
|
||||
logger.debug("response", async () => ({ data: await res.json(), url }));
|
||||
```
|
||||
|
||||
Top-level thenables (including Promises returned from lazy factories, or a Promise passed directly as an argument) are settled before sinks run. The log call stays fire-and-forget (`void`). Settled events set `LogEvent.async`, and the console sink renders an `[async]` label suffix. Nested promises inside plain objects are not walked — compose them inside the async factory. Async emits may appear out of order relative to sync logs.
|
||||
|
||||
Rejection reasons replace rejected thenables in that argument slot; other arguments still emit.
|
||||
|
||||
## Releases
|
||||
|
||||
Woodpecker verifies pull requests and `main`, reports CI to Mattermost, then automatically releases from Conventional Commits merged to `main`. It updates `package.json` and `CHANGELOG.md`, creates a `vX.Y.Z` tag, and publishes to the private `@mifi` registry.
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mifi/logger",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"description": "Intentional, namespaced logging for browser and Node.js TypeScript applications.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -37,6 +37,7 @@
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"lint": "oxlint src test",
|
||||
"lint:fix": "oxlint src test --fix",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"pack:check": "pnpm pack --pack-destination .artifacts",
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Severity threshold for log filtering, ordered from most to least verbose.
|
||||
*
|
||||
* | Level | Meaning |
|
||||
* |-----------|----------------------------------------------|
|
||||
* | `trace` | Extremely detailed diagnostics |
|
||||
* | `debug` | Development diagnostics |
|
||||
* | `info` | Routine operational messages |
|
||||
* | `warn` | Unexpected but recoverable conditions |
|
||||
* | `error` | Failures that need attention |
|
||||
* | `silent` | Suppresses all console-bound output |
|
||||
*
|
||||
* A logger emits events at or above its configured threshold (e.g. `warn`
|
||||
* allows `warn` and `error`).
|
||||
*/
|
||||
export enum LogLevel {
|
||||
TRACE = "trace",
|
||||
DEBUG = "debug",
|
||||
INFO = "info",
|
||||
WARN = "warn",
|
||||
ERROR = "error",
|
||||
SILENT = "silent",
|
||||
}
|
||||
|
||||
/**
|
||||
* Deployment stage used to pick a default log level when none is configured
|
||||
* explicitly or via runtime overrides.
|
||||
*/
|
||||
export enum LoggerEnvironment {
|
||||
DEVELOPMENT = "development",
|
||||
STAGING = "staging",
|
||||
PRODUCTION = "production",
|
||||
}
|
||||
+51
-12
@@ -1,7 +1,6 @@
|
||||
import { createConsoleSink } from "./sinks/console";
|
||||
import type {
|
||||
LogCallOptions,
|
||||
LogData,
|
||||
LogEvent,
|
||||
Logger,
|
||||
LoggerEnvironment,
|
||||
@@ -178,16 +177,41 @@ function namespaceMatches(namespace: string | undefined, filters: readonly strin
|
||||
/**
|
||||
* Evaluates lazy log arguments: every argument after the first that is a
|
||||
* zero-arg function is invoked; earlier arguments and non-functions are kept.
|
||||
* Returned thenables are left as-is for {@link settleArguments}.
|
||||
*
|
||||
* @param arguments_ - Raw call arguments (message first, then optional data).
|
||||
* @returns Arguments with lazy factories resolved to their return values.
|
||||
* @returns Arguments with lazy factories invoked (may still contain thenables).
|
||||
*/
|
||||
function evaluate(arguments_: readonly unknown[]): readonly unknown[] {
|
||||
return arguments_.map((argument, index) =>
|
||||
index > 0 && typeof argument === "function" ? (argument as () => LogData)() : argument,
|
||||
index > 0 && typeof argument === "function" ? (argument as () => unknown)() : argument,
|
||||
);
|
||||
}
|
||||
|
||||
/** Duck-type check for Promise-like values. */
|
||||
function isThenable(value: unknown): value is PromiseLike<unknown> {
|
||||
return (
|
||||
value !== null &&
|
||||
(typeof value === "object" || typeof value === "function") &&
|
||||
typeof (value as { then?: unknown }).then === "function"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Settles top-level thenables in evaluated arguments.
|
||||
* Fulfilled values replace the thenable; rejection reasons replace rejected ones.
|
||||
*/
|
||||
async function settleArguments(arguments_: readonly unknown[]): Promise<readonly unknown[]> {
|
||||
const settled = await Promise.allSettled(
|
||||
arguments_.map((argument) => (isThenable(argument) ? argument : Promise.resolve(argument))),
|
||||
);
|
||||
return settled.map((result) => (result.status === "fulfilled" ? result.value : result.reason));
|
||||
}
|
||||
|
||||
function hasThenable(arguments_: readonly unknown[]): boolean {
|
||||
return arguments_.some(isThenable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves Sentry sink options from `options.sentry` or a Sentry sink in `sinks`.
|
||||
* Development always yields `undefined` (nothing is sent to Sentry).
|
||||
@@ -224,6 +248,8 @@ function resolveSentrySinkOptions(
|
||||
*
|
||||
* Functions passed after the first argument are evaluated lazily — only when
|
||||
* the event is emitted — so expensive snapshots stay cheap when suppressed.
|
||||
* Async factories and top-level Promises are settled before sinks run
|
||||
* (fire-and-forget); such events set {@link LogEvent.async}.
|
||||
*
|
||||
* @param options - Logger configuration. See {@link LoggerOptions}.
|
||||
* @returns A {@link Logger} instance.
|
||||
@@ -239,6 +265,7 @@ function resolveSentrySinkOptions(
|
||||
*
|
||||
* logger.error("Request failed", error);
|
||||
* logger.debug("skipped in production", () => hugeObject());
|
||||
* logger.debug("response", async () => ({ data: await res.json() }));
|
||||
* logger.warn("investigate", { id: 1 }, { sentry: true });
|
||||
* ```
|
||||
*/
|
||||
@@ -269,6 +296,9 @@ export function createLogger(options: LoggerOptions): Logger {
|
||||
levelRank(level) >= levelRank(threshold) &&
|
||||
(!runtimeOverride?.namespaces.length ||
|
||||
namespaceMatches(namespace, runtimeOverride.namespaces));
|
||||
const deliver = (event: LogEvent) => {
|
||||
sinks.forEach((sink) => sink.emit(event));
|
||||
};
|
||||
const emit = (level: Exclude<LogLevel, "silent">, arguments_: readonly unknown[]) => {
|
||||
const split = splitLogCallArguments(arguments_);
|
||||
const sendToConsole = enabled(level);
|
||||
@@ -281,17 +311,26 @@ export function createLogger(options: LoggerOptions): Logger {
|
||||
(Boolean(sentrySinkOptions?.logs) &&
|
||||
levelRank(level) >= levelRank(sentrySinkOptions.logLevel)));
|
||||
if (!sendToConsole && !sendToSentryIssue && !sendToSentryLogs) return;
|
||||
const event: LogEvent = {
|
||||
const timestamp = new Date();
|
||||
const evaluated = evaluate(split.arguments);
|
||||
const base = {
|
||||
level,
|
||||
namespace,
|
||||
arguments: evaluate(split.arguments),
|
||||
timestamp: new Date(),
|
||||
timestamp,
|
||||
environment: options.environment,
|
||||
sendToSentryLogs,
|
||||
sendToSentryIssue,
|
||||
sendToConsole,
|
||||
};
|
||||
sinks.forEach((sink) => sink.emit(event));
|
||||
if (!hasThenable(evaluated)) {
|
||||
deliver({ ...base, arguments: evaluated });
|
||||
return;
|
||||
}
|
||||
void settleArguments(evaluated)
|
||||
.then((settled) => deliver({ ...base, arguments: settled, async: true }))
|
||||
.catch(() => {
|
||||
/* allSettled absorbs rejections; defensive against unexpected throw */
|
||||
});
|
||||
};
|
||||
const consoleMethod = (
|
||||
method: keyof Console | "profile" | "profileEnd",
|
||||
@@ -301,11 +340,11 @@ export function createLogger(options: LoggerOptions): Logger {
|
||||
if (!enabled(level)) return;
|
||||
const console_ = globalThis.console as Console & Record<string, unknown>;
|
||||
const fn = console_[method];
|
||||
if (typeof fn === "function")
|
||||
(fn as (...items: unknown[]) => void).apply(
|
||||
console_,
|
||||
Array.from(prefix(namespace, evaluate(arguments_))),
|
||||
);
|
||||
if (typeof fn !== "function") return;
|
||||
(fn as (...items: unknown[]) => void).apply(
|
||||
console_,
|
||||
Array.from(prefix(namespace, evaluate(arguments_))),
|
||||
);
|
||||
};
|
||||
const api: Logger = {
|
||||
namespace,
|
||||
|
||||
@@ -33,7 +33,8 @@ function supportsAnsi(): boolean {
|
||||
*
|
||||
* - Skips events where `sendToConsole` is `false`.
|
||||
* - Maps `trace` to `console.debug`.
|
||||
* - Prefixes a styled namespace label (`[A][B]` or `[LOG]`).
|
||||
* - Prefixes a styled namespace label (`[A][B]` or `[LOG]`), plus `[async]`
|
||||
* when {@link LogEvent.async} is set.
|
||||
* - Uses CSS `%c` styling in browsers and ANSI colors on Node TTYs.
|
||||
* - Node `error` level uses `console.error` (stderr).
|
||||
*
|
||||
@@ -55,7 +56,10 @@ export function createConsoleSink(): LogSink {
|
||||
const console_ = globalThis.console as Console & Record<string, unknown>;
|
||||
const fn = console_[method];
|
||||
if (typeof fn !== "function") return;
|
||||
const label = event.namespace ? `[${event.namespace.split(":").join("][")}]` : "[LOG]";
|
||||
const labelBase = event.namespace
|
||||
? `[${event.namespace.split(":").join("][")}]`
|
||||
: "[LOG]";
|
||||
const label = event.async ? `${labelBase}[async]` : labelBase;
|
||||
const arguments_ = isBrowser()
|
||||
? [`%c${label}`, BROWSER_STYLE_BY_LEVEL[event.level], ...event.arguments]
|
||||
: supportsAnsi()
|
||||
|
||||
@@ -147,6 +147,7 @@ export function toSentryLogPayload(event: LogEvent): {
|
||||
event.arguments.filter((item) => typeof item === "string").join(" ") || "Log event";
|
||||
const attributes: Record<string, string | number | boolean> = {};
|
||||
if (event.namespace) attributes["logger.namespace"] = event.namespace;
|
||||
if (event.async) attributes.async = true;
|
||||
for (const item of event.arguments) {
|
||||
if (!item || typeof item !== "object" || item instanceof Error || Array.isArray(item))
|
||||
continue;
|
||||
|
||||
+18
-5
@@ -1,3 +1,5 @@
|
||||
import { LogLevel as LogLevelEnum, LoggerEnvironment as LoggerEnvironmentEnum } from "./constants";
|
||||
|
||||
/**
|
||||
* Severity threshold for log filtering, ordered from most to least verbose.
|
||||
*
|
||||
@@ -13,7 +15,7 @@
|
||||
* A logger emits events at or above its configured threshold (e.g. `warn`
|
||||
* allows `warn` and `error`).
|
||||
*/
|
||||
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "silent";
|
||||
export type LogLevel = `${LogLevelEnum}`;
|
||||
|
||||
/**
|
||||
* Deployment stage used to pick a default log level when none is configured
|
||||
@@ -24,20 +26,23 @@ export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "silent";
|
||||
* - `staging` → `warn`
|
||||
* - `production` → `error`
|
||||
*/
|
||||
export type LoggerEnvironment = "development" | "staging" | "production";
|
||||
export type LoggerEnvironment = `${LoggerEnvironmentEnum}`;
|
||||
|
||||
/**
|
||||
* A value passed as a log argument after the message.
|
||||
*
|
||||
* Prefer a zero-argument function for expensive payloads: it is only invoked
|
||||
* when the event is actually emitted, so suppressed logs avoid the work.
|
||||
* The factory may be `async` or otherwise return a `Promise`; top-level
|
||||
* thenables are settled before sinks see the event (fire-and-forget).
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* logger.debug("state", () => expensiveSnapshot());
|
||||
* logger.debug("response", async () => ({ data: await res.json() }));
|
||||
* ```
|
||||
*/
|
||||
export type LogData = unknown | (() => unknown);
|
||||
export type LogData = unknown | (() => unknown | Promise<unknown>);
|
||||
|
||||
/**
|
||||
* Per-call options for `trace` / `debug` / `log` / `info` / `warn` / `error`
|
||||
@@ -73,10 +78,13 @@ export interface LogEvent {
|
||||
namespace?: string;
|
||||
/**
|
||||
* Evaluated arguments for the event (call options already stripped).
|
||||
* Lazy `() => unknown` functions have already been invoked.
|
||||
* Lazy factories have been invoked and top-level thenables settled;
|
||||
* rejection reasons appear in place of rejected thenables.
|
||||
*/
|
||||
arguments: readonly unknown[];
|
||||
/** Wall-clock time when the event was created. */
|
||||
/**
|
||||
* Wall-clock time when the log call was made (before awaiting thenables).
|
||||
*/
|
||||
timestamp: Date;
|
||||
/** Deployment stage from {@link LoggerOptions.environment}. */
|
||||
environment: LoggerEnvironment;
|
||||
@@ -95,6 +103,11 @@ export interface LogEvent {
|
||||
* should be rendered by console-oriented sinks.
|
||||
*/
|
||||
sendToConsole: boolean;
|
||||
/**
|
||||
* `true` when emit was deferred to settle top-level thenables.
|
||||
* Omitted for fully synchronous emits. Console sinks render `[async]`.
|
||||
*/
|
||||
async?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { createConsoleSink } from "../src/index";
|
||||
import type { LogEvent } from "../src/index";
|
||||
|
||||
function event(overrides: Partial<LogEvent> = {}): LogEvent {
|
||||
return {
|
||||
level: "debug",
|
||||
namespace: "API",
|
||||
arguments: ["hello"],
|
||||
timestamp: new Date(),
|
||||
environment: "development",
|
||||
sendToSentryLogs: false,
|
||||
sendToSentryIssue: false,
|
||||
sendToConsole: true,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("createConsoleSink", () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("appends [async] to the namespace label when event.async is set", () => {
|
||||
const debug = vi.spyOn(console, "debug").mockImplementation(() => undefined);
|
||||
createConsoleSink().emit(event({ async: true }));
|
||||
expect(debug).toHaveBeenCalled();
|
||||
const first = debug.mock.calls[0]?.[0];
|
||||
expect(String(first)).toContain("[API][async]");
|
||||
});
|
||||
|
||||
it("omits [async] for synchronous events", () => {
|
||||
const debug = vi.spyOn(console, "debug").mockImplementation(() => undefined);
|
||||
createConsoleSink().emit(event());
|
||||
expect(debug).toHaveBeenCalled();
|
||||
const first = debug.mock.calls[0]?.[0];
|
||||
expect(String(first)).toContain("[API]");
|
||||
expect(String(first)).not.toContain("[async]");
|
||||
});
|
||||
});
|
||||
@@ -40,6 +40,43 @@ describe("createLogger", () => {
|
||||
expect(expensive).toHaveBeenCalledTimes(1);
|
||||
expect(destination.events).toHaveLength(1);
|
||||
expect(destination.events[0]?.arguments).toEqual(["kept", { huge: "snapshot" }]);
|
||||
expect(destination.events[0]?.async).toBeUndefined();
|
||||
});
|
||||
|
||||
it("never starts suppressed async lazy factories", () => {
|
||||
const destination = sink();
|
||||
const expensive = vi.fn(async () => ({ huge: "snapshot" }));
|
||||
const logger = createLogger({ environment: "production", sinks: [destination.sink] });
|
||||
logger.debug("ignored", expensive);
|
||||
expect(expensive).not.toHaveBeenCalled();
|
||||
expect(destination.events).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("settles async lazy factories before emitting and sets async", async () => {
|
||||
const destination = sink();
|
||||
const factory = vi.fn(async () => ({ data: 1 }));
|
||||
const logger = createLogger({ environment: "development", sinks: [destination.sink] });
|
||||
logger.debug("response", factory);
|
||||
expect(factory).toHaveBeenCalledTimes(1);
|
||||
expect(destination.events).toHaveLength(0);
|
||||
await vi.waitFor(() => expect(destination.events).toHaveLength(1));
|
||||
expect(destination.events[0]).toMatchObject({
|
||||
arguments: ["response", { data: 1 }],
|
||||
async: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("unwraps direct Promise arguments and keeps rejection reasons", async () => {
|
||||
const destination = sink();
|
||||
const reason = new Error("boom");
|
||||
const logger = createLogger({ environment: "development", sinks: [destination.sink] });
|
||||
logger.error("failed", { id: 1 }, Promise.reject(reason));
|
||||
expect(destination.events).toHaveLength(0);
|
||||
await vi.waitFor(() => expect(destination.events).toHaveLength(1));
|
||||
expect(destination.events[0]).toMatchObject({
|
||||
arguments: ["failed", { id: 1 }, reason],
|
||||
async: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("applies session namespace filtering to namespace descendants", () => {
|
||||
|
||||
@@ -66,6 +66,21 @@ describe("toSentryLogPayload", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("includes async when the event was deferred for thenables", () => {
|
||||
expect(
|
||||
toSentryLogPayload({
|
||||
level: "debug",
|
||||
arguments: ["response"],
|
||||
timestamp: new Date(),
|
||||
environment: "development",
|
||||
sendToSentryLogs: true,
|
||||
sendToSentryIssue: false,
|
||||
sendToConsole: true,
|
||||
async: true,
|
||||
}).attributes,
|
||||
).toEqual({ async: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("createSentrySink", () => {
|
||||
|
||||
+1
-2
@@ -6,8 +6,7 @@
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"skipLibCheck": true,
|
||||
"ignoreDeprecations": "6.0"
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src", "test", "tsup.config.ts", "vitest.config.ts"]
|
||||
}
|
||||
|
||||
+7
-1
@@ -3,7 +3,13 @@ import { defineConfig } from "tsup";
|
||||
export default defineConfig({
|
||||
entry: { index: "src/index.ts", sentry: "src/sentry.ts" },
|
||||
format: ["esm", "cjs"],
|
||||
dts: true,
|
||||
// tsup injects baseUrl for DTS; TS 6 treats that as an error until tsup stops
|
||||
// (https://github.com/egoist/tsup/issues/1388)
|
||||
dts: {
|
||||
compilerOptions: {
|
||||
ignoreDeprecations: "6.0",
|
||||
},
|
||||
},
|
||||
clean: true,
|
||||
target: "es2022",
|
||||
sourcemap: true,
|
||||
|
||||
Reference in New Issue
Block a user