@loggerjs/node 0.5.5 → 0.6.0

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
- {"version":3,"file":"node-destination.d.ts","sourceRoot":"","sources":["../src/node-destination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,KAAK,WAAW,EAAE,MAAM,IAAI,CAAC;AAEpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1E,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,KAAK,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,eAAe,CAyJjB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,eAAe,CAqE9F"}
1
+ {"version":3,"file":"node-destination.d.ts","sourceRoot":"","sources":["../src/node-destination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgE,KAAK,WAAW,EAAE,MAAM,IAAI,CAAC;AAEpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1E,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,KAAK,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,eAAe,CAyJjB;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,GAAG,eAAe,CAyE9F"}
@@ -5,6 +5,15 @@ const signalExitCodes = {
5
5
  SIGINT: 130,
6
6
  SIGTERM: 143
7
7
  };
8
+ function unhandledRejectionsMode() {
9
+ const flags = [...process.execArgv, ...(process.env.NODE_OPTIONS ?? "").split(/\s+/)];
10
+ let mode = "throw";
11
+ for (const flag of flags) {
12
+ const match = /^--unhandled-rejections=(.+)$/.exec(flag);
13
+ if (match?.[1]) mode = match[1];
14
+ }
15
+ return mode;
16
+ }
8
17
  function captureProcessIntegration(options = {}) {
9
18
  const uncaughtException = options.uncaughtException ?? true;
10
19
  const unhandledRejection = options.unhandledRejection ?? true;
@@ -15,6 +24,8 @@ function captureProcessIntegration(options = {}) {
15
24
  const signals = options.signals ?? ["SIGTERM"];
16
25
  const exitOnSignal = options.exitOnSignal ?? true;
17
26
  const exitOnUncaught = options.exitOnUncaught ?? true;
27
+ const rejectionsMode = unhandledRejectionsMode();
28
+ const exitOnUnhandledRejection = options.exitOnUnhandledRejection ?? (rejectionsMode === "throw" || rejectionsMode === "strict");
18
29
  const flushTimeoutMs = options.flushTimeoutMs ?? 250;
19
30
  const exitFn = options.exitFn ?? ((code) => process.exit(code));
20
31
  return {
@@ -60,7 +71,7 @@ function captureProcessIntegration(options = {}) {
60
71
  if (unhandledRejection) {
61
72
  const onUnhandledRejection = (reason) => {
62
73
  capture({
63
- level: "error",
74
+ level: exitOnUnhandledRejection ? "fatal" : "error",
64
75
  message: reason instanceof Error ? reason.message : "Unhandled promise rejection",
65
76
  error: reason,
66
77
  props: {
@@ -68,6 +79,14 @@ function captureProcessIntegration(options = {}) {
68
79
  reason: (0, _loggerjs_core.normalizeValue)(reason, { maxDepth: 5 })
69
80
  }
70
81
  });
82
+ if (exitOnUnhandledRejection) {
83
+ flushSync();
84
+ flushBounded().finally(() => exitFn(1));
85
+ return;
86
+ }
87
+ if (options.exitOnUnhandledRejection === void 0) {
88
+ if (rejectionsMode === "warn-with-error-code") process.exitCode = 1;
89
+ }
71
90
  flushBestEffort();
72
91
  };
73
92
  process.on("unhandledRejection", onUnhandledRejection);
@@ -1 +1 @@
1
- {"version":3,"file":"process-integration.cjs","names":[],"sources":["../src/process-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type CaptureInput,\n type Integration,\n type IntegrationSetupContext,\n} from \"@loggerjs/core\";\n\nexport type ProcessSignal = \"SIGHUP\" | \"SIGINT\" | \"SIGQUIT\" | \"SIGTERM\";\n\nexport interface CaptureProcessOptions {\n uncaughtException?: boolean;\n unhandledRejection?: boolean;\n warning?: boolean;\n beforeExitFlush?: boolean;\n exitFlush?: boolean;\n signalFlush?: boolean;\n signals?: ProcessSignal[];\n exitOnSignal?: boolean;\n exitOnUncaught?: boolean;\n flushTimeoutMs?: number;\n exitFn?: (code: number) => void;\n}\n\nconst signalExitCodes: Partial<Record<ProcessSignal, number>> = {\n SIGINT: 130,\n SIGTERM: 143,\n};\n\nexport function captureProcessIntegration(options: CaptureProcessOptions = {}): Integration {\n const uncaughtException = options.uncaughtException ?? true;\n const unhandledRejection = options.unhandledRejection ?? true;\n const warning = options.warning ?? true;\n const beforeExitFlush = options.beforeExitFlush ?? true;\n const exitFlush = options.exitFlush ?? true;\n const signalFlush = options.signalFlush ?? true;\n const signals = options.signals ?? ([\"SIGTERM\"] satisfies ProcessSignal[]);\n const exitOnSignal = options.exitOnSignal ?? true;\n const exitOnUncaught = options.exitOnUncaught ?? true;\n const flushTimeoutMs = options.flushTimeoutMs ?? 250;\n const exitFn = options.exitFn ?? ((code: number) => process.exit(code));\n\n return {\n name: \"capture-process\",\n setup(api: IntegrationSetupContext) {\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n const flushSync = () => {\n try {\n api.flushSync?.();\n } catch {\n // Logger.flushSync reports transport errors; this protects custom IntegrationAPI contexts.\n }\n };\n const flushBounded = async () => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n api.flush(),\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, flushTimeoutMs);\n }),\n ]);\n } catch {\n // Process handlers must not throw back into fatal runtime paths.\n } finally {\n if (timer) clearTimeout(timer);\n }\n };\n const flushBestEffort = () => {\n flushSync();\n void flushBounded();\n };\n\n if (uncaughtException) {\n const onUncaughtException = (error: Error) => {\n capture({\n level: \"fatal\",\n message: error.message,\n error,\n props: { process: { kind: \"uncaughtException\" } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnUncaught) void flushed.finally(() => exitFn(1));\n };\n process.on(\"uncaughtException\", onUncaughtException);\n disposers.push(() => process.off(\"uncaughtException\", onUncaughtException));\n }\n\n if (unhandledRejection) {\n const onUnhandledRejection = (reason: unknown) => {\n capture({\n level: \"error\",\n message: reason instanceof Error ? reason.message : \"Unhandled promise rejection\",\n error: reason,\n props: {\n process: { kind: \"unhandledRejection\" },\n reason: normalizeValue(reason, { maxDepth: 5 }),\n },\n });\n flushBestEffort();\n };\n process.on(\"unhandledRejection\", onUnhandledRejection);\n disposers.push(() => process.off(\"unhandledRejection\", onUnhandledRejection));\n }\n\n if (warning) {\n const onWarning = (warn: Error) => {\n capture({\n level: \"warn\",\n message: warn.message,\n error: warn,\n props: {\n process: {\n kind: \"warning\",\n name: warn.name,\n code: (warn as Error & { code?: string }).code,\n },\n },\n });\n };\n process.on(\"warning\", onWarning);\n disposers.push(() => process.off(\"warning\", onWarning));\n }\n\n if (beforeExitFlush) {\n const onBeforeExit = () => {\n void flushBounded();\n };\n process.on(\"beforeExit\", onBeforeExit);\n disposers.push(() => process.off(\"beforeExit\", onBeforeExit));\n }\n\n if (exitFlush) {\n const onExit = () => {\n flushSync();\n };\n process.on(\"exit\", onExit);\n disposers.push(() => process.off(\"exit\", onExit));\n }\n\n if (signalFlush) {\n for (const signal of signals) {\n const onSignal = () => {\n capture({\n level: \"fatal\",\n message: `Process signal ${signal}`,\n props: { process: { kind: \"signal\", signal } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnSignal) {\n void flushed.finally(() => exitFn(signalExitCodes[signal] ?? 1));\n }\n };\n process.on(signal, onSignal);\n disposers.push(() => process.off(signal, onSignal));\n }\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"process-integration.cjs","names":[],"sources":["../src/process-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type CaptureInput,\n type Integration,\n type IntegrationSetupContext,\n} from \"@loggerjs/core\";\n\nexport type ProcessSignal = \"SIGHUP\" | \"SIGINT\" | \"SIGQUIT\" | \"SIGTERM\";\n\nexport interface CaptureProcessOptions {\n uncaughtException?: boolean;\n unhandledRejection?: boolean;\n warning?: boolean;\n beforeExitFlush?: boolean;\n exitFlush?: boolean;\n signalFlush?: boolean;\n signals?: ProcessSignal[];\n exitOnSignal?: boolean;\n exitOnUncaught?: boolean;\n /**\n * Exit with code 1 after capturing an unhandled rejection. Registering an\n * `unhandledRejection` listener disables Node's default crash, so this\n * defaults to Node's own `--unhandled-rejections` behavior: exit unless the\n * mode is `warn`, `none`, or `warn-with-error-code`.\n */\n exitOnUnhandledRejection?: boolean;\n flushTimeoutMs?: number;\n exitFn?: (code: number) => void;\n}\n\nconst signalExitCodes: Partial<Record<ProcessSignal, number>> = {\n SIGINT: 130,\n SIGTERM: 143,\n};\n\ntype UnhandledRejectionsMode = \"throw\" | \"strict\" | \"warn\" | \"none\" | \"warn-with-error-code\";\n\nfunction unhandledRejectionsMode(): UnhandledRejectionsMode {\n const flags = [...process.execArgv, ...(process.env.NODE_OPTIONS ?? \"\").split(/\\s+/)];\n let mode: UnhandledRejectionsMode = \"throw\";\n for (const flag of flags) {\n const match = /^--unhandled-rejections=(.+)$/.exec(flag);\n if (match?.[1]) mode = match[1] as UnhandledRejectionsMode;\n }\n return mode;\n}\n\nexport function captureProcessIntegration(options: CaptureProcessOptions = {}): Integration {\n const uncaughtException = options.uncaughtException ?? true;\n const unhandledRejection = options.unhandledRejection ?? true;\n const warning = options.warning ?? true;\n const beforeExitFlush = options.beforeExitFlush ?? true;\n const exitFlush = options.exitFlush ?? true;\n const signalFlush = options.signalFlush ?? true;\n const signals = options.signals ?? ([\"SIGTERM\"] satisfies ProcessSignal[]);\n const exitOnSignal = options.exitOnSignal ?? true;\n const exitOnUncaught = options.exitOnUncaught ?? true;\n const rejectionsMode = unhandledRejectionsMode();\n const exitOnUnhandledRejection =\n options.exitOnUnhandledRejection ?? (rejectionsMode === \"throw\" || rejectionsMode === \"strict\");\n const flushTimeoutMs = options.flushTimeoutMs ?? 250;\n const exitFn = options.exitFn ?? ((code: number) => process.exit(code));\n\n return {\n name: \"capture-process\",\n setup(api: IntegrationSetupContext) {\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n const flushSync = () => {\n try {\n api.flushSync?.();\n } catch {\n // Logger.flushSync reports transport errors; this protects custom IntegrationAPI contexts.\n }\n };\n const flushBounded = async () => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n api.flush(),\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, flushTimeoutMs);\n }),\n ]);\n } catch {\n // Process handlers must not throw back into fatal runtime paths.\n } finally {\n if (timer) clearTimeout(timer);\n }\n };\n const flushBestEffort = () => {\n flushSync();\n void flushBounded();\n };\n\n if (uncaughtException) {\n const onUncaughtException = (error: Error) => {\n capture({\n level: \"fatal\",\n message: error.message,\n error,\n props: { process: { kind: \"uncaughtException\" } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnUncaught) void flushed.finally(() => exitFn(1));\n };\n process.on(\"uncaughtException\", onUncaughtException);\n disposers.push(() => process.off(\"uncaughtException\", onUncaughtException));\n }\n\n if (unhandledRejection) {\n const onUnhandledRejection = (reason: unknown) => {\n capture({\n level: exitOnUnhandledRejection ? \"fatal\" : \"error\",\n message: reason instanceof Error ? reason.message : \"Unhandled promise rejection\",\n error: reason,\n props: {\n process: { kind: \"unhandledRejection\" },\n reason: normalizeValue(reason, { maxDepth: 5 }),\n },\n });\n if (exitOnUnhandledRejection) {\n flushSync();\n void flushBounded().finally(() => exitFn(1));\n return;\n }\n if (options.exitOnUnhandledRejection === undefined) {\n if (rejectionsMode === \"warn-with-error-code\") process.exitCode = 1;\n }\n flushBestEffort();\n };\n process.on(\"unhandledRejection\", onUnhandledRejection);\n disposers.push(() => process.off(\"unhandledRejection\", onUnhandledRejection));\n }\n\n if (warning) {\n const onWarning = (warn: Error) => {\n capture({\n level: \"warn\",\n message: warn.message,\n error: warn,\n props: {\n process: {\n kind: \"warning\",\n name: warn.name,\n code: (warn as Error & { code?: string }).code,\n },\n },\n });\n };\n process.on(\"warning\", onWarning);\n disposers.push(() => process.off(\"warning\", onWarning));\n }\n\n if (beforeExitFlush) {\n const onBeforeExit = () => {\n void flushBounded();\n };\n process.on(\"beforeExit\", onBeforeExit);\n disposers.push(() => process.off(\"beforeExit\", onBeforeExit));\n }\n\n if (exitFlush) {\n const onExit = () => {\n flushSync();\n };\n process.on(\"exit\", onExit);\n disposers.push(() => process.off(\"exit\", onExit));\n }\n\n if (signalFlush) {\n for (const signal of signals) {\n const onSignal = () => {\n capture({\n level: \"fatal\",\n message: `Process signal ${signal}`,\n props: { process: { kind: \"signal\", signal } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnSignal) {\n void flushed.finally(() => exitFn(signalExitCodes[signal] ?? 1));\n }\n };\n process.on(signal, onSignal);\n disposers.push(() => process.off(signal, onSignal));\n }\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -10,6 +10,13 @@ export interface CaptureProcessOptions {
10
10
  signals?: ProcessSignal[];
11
11
  exitOnSignal?: boolean;
12
12
  exitOnUncaught?: boolean;
13
+ /**
14
+ * Exit with code 1 after capturing an unhandled rejection. Registering an
15
+ * `unhandledRejection` listener disables Node's default crash, so this
16
+ * defaults to Node's own `--unhandled-rejections` behavior: exit unless the
17
+ * mode is `warn`, `none`, or `warn-with-error-code`.
18
+ */
19
+ exitOnUnhandledRejection?: boolean;
13
20
  flushTimeoutMs?: number;
14
21
  exitFn?: (code: number) => void;
15
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"process-integration.d.ts","sourceRoot":"","sources":["../src/process-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAOD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,qBAA0B,GAAG,WAAW,CA6I1F"}
1
+ {"version":3,"file":"process-integration.d.ts","sourceRoot":"","sources":["../src/process-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAmBD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE,qBAA0B,GAAG,WAAW,CAwJ1F"}
@@ -4,6 +4,15 @@ const signalExitCodes = {
4
4
  SIGINT: 130,
5
5
  SIGTERM: 143
6
6
  };
7
+ function unhandledRejectionsMode() {
8
+ const flags = [...process.execArgv, ...(process.env.NODE_OPTIONS ?? "").split(/\s+/)];
9
+ let mode = "throw";
10
+ for (const flag of flags) {
11
+ const match = /^--unhandled-rejections=(.+)$/.exec(flag);
12
+ if (match?.[1]) mode = match[1];
13
+ }
14
+ return mode;
15
+ }
7
16
  function captureProcessIntegration(options = {}) {
8
17
  const uncaughtException = options.uncaughtException ?? true;
9
18
  const unhandledRejection = options.unhandledRejection ?? true;
@@ -14,6 +23,8 @@ function captureProcessIntegration(options = {}) {
14
23
  const signals = options.signals ?? ["SIGTERM"];
15
24
  const exitOnSignal = options.exitOnSignal ?? true;
16
25
  const exitOnUncaught = options.exitOnUncaught ?? true;
26
+ const rejectionsMode = unhandledRejectionsMode();
27
+ const exitOnUnhandledRejection = options.exitOnUnhandledRejection ?? (rejectionsMode === "throw" || rejectionsMode === "strict");
17
28
  const flushTimeoutMs = options.flushTimeoutMs ?? 250;
18
29
  const exitFn = options.exitFn ?? ((code) => process.exit(code));
19
30
  return {
@@ -59,7 +70,7 @@ function captureProcessIntegration(options = {}) {
59
70
  if (unhandledRejection) {
60
71
  const onUnhandledRejection = (reason) => {
61
72
  capture({
62
- level: "error",
73
+ level: exitOnUnhandledRejection ? "fatal" : "error",
63
74
  message: reason instanceof Error ? reason.message : "Unhandled promise rejection",
64
75
  error: reason,
65
76
  props: {
@@ -67,6 +78,14 @@ function captureProcessIntegration(options = {}) {
67
78
  reason: normalizeValue(reason, { maxDepth: 5 })
68
79
  }
69
80
  });
81
+ if (exitOnUnhandledRejection) {
82
+ flushSync();
83
+ flushBounded().finally(() => exitFn(1));
84
+ return;
85
+ }
86
+ if (options.exitOnUnhandledRejection === void 0) {
87
+ if (rejectionsMode === "warn-with-error-code") process.exitCode = 1;
88
+ }
70
89
  flushBestEffort();
71
90
  };
72
91
  process.on("unhandledRejection", onUnhandledRejection);
@@ -1 +1 @@
1
- {"version":3,"file":"process-integration.js","names":[],"sources":["../src/process-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type CaptureInput,\n type Integration,\n type IntegrationSetupContext,\n} from \"@loggerjs/core\";\n\nexport type ProcessSignal = \"SIGHUP\" | \"SIGINT\" | \"SIGQUIT\" | \"SIGTERM\";\n\nexport interface CaptureProcessOptions {\n uncaughtException?: boolean;\n unhandledRejection?: boolean;\n warning?: boolean;\n beforeExitFlush?: boolean;\n exitFlush?: boolean;\n signalFlush?: boolean;\n signals?: ProcessSignal[];\n exitOnSignal?: boolean;\n exitOnUncaught?: boolean;\n flushTimeoutMs?: number;\n exitFn?: (code: number) => void;\n}\n\nconst signalExitCodes: Partial<Record<ProcessSignal, number>> = {\n SIGINT: 130,\n SIGTERM: 143,\n};\n\nexport function captureProcessIntegration(options: CaptureProcessOptions = {}): Integration {\n const uncaughtException = options.uncaughtException ?? true;\n const unhandledRejection = options.unhandledRejection ?? true;\n const warning = options.warning ?? true;\n const beforeExitFlush = options.beforeExitFlush ?? true;\n const exitFlush = options.exitFlush ?? true;\n const signalFlush = options.signalFlush ?? true;\n const signals = options.signals ?? ([\"SIGTERM\"] satisfies ProcessSignal[]);\n const exitOnSignal = options.exitOnSignal ?? true;\n const exitOnUncaught = options.exitOnUncaught ?? true;\n const flushTimeoutMs = options.flushTimeoutMs ?? 250;\n const exitFn = options.exitFn ?? ((code: number) => process.exit(code));\n\n return {\n name: \"capture-process\",\n setup(api: IntegrationSetupContext) {\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n const flushSync = () => {\n try {\n api.flushSync?.();\n } catch {\n // Logger.flushSync reports transport errors; this protects custom IntegrationAPI contexts.\n }\n };\n const flushBounded = async () => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n api.flush(),\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, flushTimeoutMs);\n }),\n ]);\n } catch {\n // Process handlers must not throw back into fatal runtime paths.\n } finally {\n if (timer) clearTimeout(timer);\n }\n };\n const flushBestEffort = () => {\n flushSync();\n void flushBounded();\n };\n\n if (uncaughtException) {\n const onUncaughtException = (error: Error) => {\n capture({\n level: \"fatal\",\n message: error.message,\n error,\n props: { process: { kind: \"uncaughtException\" } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnUncaught) void flushed.finally(() => exitFn(1));\n };\n process.on(\"uncaughtException\", onUncaughtException);\n disposers.push(() => process.off(\"uncaughtException\", onUncaughtException));\n }\n\n if (unhandledRejection) {\n const onUnhandledRejection = (reason: unknown) => {\n capture({\n level: \"error\",\n message: reason instanceof Error ? reason.message : \"Unhandled promise rejection\",\n error: reason,\n props: {\n process: { kind: \"unhandledRejection\" },\n reason: normalizeValue(reason, { maxDepth: 5 }),\n },\n });\n flushBestEffort();\n };\n process.on(\"unhandledRejection\", onUnhandledRejection);\n disposers.push(() => process.off(\"unhandledRejection\", onUnhandledRejection));\n }\n\n if (warning) {\n const onWarning = (warn: Error) => {\n capture({\n level: \"warn\",\n message: warn.message,\n error: warn,\n props: {\n process: {\n kind: \"warning\",\n name: warn.name,\n code: (warn as Error & { code?: string }).code,\n },\n },\n });\n };\n process.on(\"warning\", onWarning);\n disposers.push(() => process.off(\"warning\", onWarning));\n }\n\n if (beforeExitFlush) {\n const onBeforeExit = () => {\n void flushBounded();\n };\n process.on(\"beforeExit\", onBeforeExit);\n disposers.push(() => process.off(\"beforeExit\", onBeforeExit));\n }\n\n if (exitFlush) {\n const onExit = () => {\n flushSync();\n };\n process.on(\"exit\", onExit);\n disposers.push(() => process.off(\"exit\", onExit));\n }\n\n if (signalFlush) {\n for (const signal of signals) {\n const onSignal = () => {\n capture({\n level: \"fatal\",\n message: `Process signal ${signal}`,\n props: { process: { kind: \"signal\", signal } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnSignal) {\n void flushed.finally(() => exitFn(signalExitCodes[signal] ?? 1));\n }\n };\n process.on(signal, onSignal);\n disposers.push(() => process.off(signal, onSignal));\n }\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"process-integration.js","names":[],"sources":["../src/process-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type CaptureInput,\n type Integration,\n type IntegrationSetupContext,\n} from \"@loggerjs/core\";\n\nexport type ProcessSignal = \"SIGHUP\" | \"SIGINT\" | \"SIGQUIT\" | \"SIGTERM\";\n\nexport interface CaptureProcessOptions {\n uncaughtException?: boolean;\n unhandledRejection?: boolean;\n warning?: boolean;\n beforeExitFlush?: boolean;\n exitFlush?: boolean;\n signalFlush?: boolean;\n signals?: ProcessSignal[];\n exitOnSignal?: boolean;\n exitOnUncaught?: boolean;\n /**\n * Exit with code 1 after capturing an unhandled rejection. Registering an\n * `unhandledRejection` listener disables Node's default crash, so this\n * defaults to Node's own `--unhandled-rejections` behavior: exit unless the\n * mode is `warn`, `none`, or `warn-with-error-code`.\n */\n exitOnUnhandledRejection?: boolean;\n flushTimeoutMs?: number;\n exitFn?: (code: number) => void;\n}\n\nconst signalExitCodes: Partial<Record<ProcessSignal, number>> = {\n SIGINT: 130,\n SIGTERM: 143,\n};\n\ntype UnhandledRejectionsMode = \"throw\" | \"strict\" | \"warn\" | \"none\" | \"warn-with-error-code\";\n\nfunction unhandledRejectionsMode(): UnhandledRejectionsMode {\n const flags = [...process.execArgv, ...(process.env.NODE_OPTIONS ?? \"\").split(/\\s+/)];\n let mode: UnhandledRejectionsMode = \"throw\";\n for (const flag of flags) {\n const match = /^--unhandled-rejections=(.+)$/.exec(flag);\n if (match?.[1]) mode = match[1] as UnhandledRejectionsMode;\n }\n return mode;\n}\n\nexport function captureProcessIntegration(options: CaptureProcessOptions = {}): Integration {\n const uncaughtException = options.uncaughtException ?? true;\n const unhandledRejection = options.unhandledRejection ?? true;\n const warning = options.warning ?? true;\n const beforeExitFlush = options.beforeExitFlush ?? true;\n const exitFlush = options.exitFlush ?? true;\n const signalFlush = options.signalFlush ?? true;\n const signals = options.signals ?? ([\"SIGTERM\"] satisfies ProcessSignal[]);\n const exitOnSignal = options.exitOnSignal ?? true;\n const exitOnUncaught = options.exitOnUncaught ?? true;\n const rejectionsMode = unhandledRejectionsMode();\n const exitOnUnhandledRejection =\n options.exitOnUnhandledRejection ?? (rejectionsMode === \"throw\" || rejectionsMode === \"strict\");\n const flushTimeoutMs = options.flushTimeoutMs ?? 250;\n const exitFn = options.exitFn ?? ((code: number) => process.exit(code));\n\n return {\n name: \"capture-process\",\n setup(api: IntegrationSetupContext) {\n const disposers: Array<() => void> = [];\n let disposed = false;\n\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n const flushSync = () => {\n try {\n api.flushSync?.();\n } catch {\n // Logger.flushSync reports transport errors; this protects custom IntegrationAPI contexts.\n }\n };\n const flushBounded = async () => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n api.flush(),\n new Promise<void>((resolve) => {\n timer = setTimeout(resolve, flushTimeoutMs);\n }),\n ]);\n } catch {\n // Process handlers must not throw back into fatal runtime paths.\n } finally {\n if (timer) clearTimeout(timer);\n }\n };\n const flushBestEffort = () => {\n flushSync();\n void flushBounded();\n };\n\n if (uncaughtException) {\n const onUncaughtException = (error: Error) => {\n capture({\n level: \"fatal\",\n message: error.message,\n error,\n props: { process: { kind: \"uncaughtException\" } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnUncaught) void flushed.finally(() => exitFn(1));\n };\n process.on(\"uncaughtException\", onUncaughtException);\n disposers.push(() => process.off(\"uncaughtException\", onUncaughtException));\n }\n\n if (unhandledRejection) {\n const onUnhandledRejection = (reason: unknown) => {\n capture({\n level: exitOnUnhandledRejection ? \"fatal\" : \"error\",\n message: reason instanceof Error ? reason.message : \"Unhandled promise rejection\",\n error: reason,\n props: {\n process: { kind: \"unhandledRejection\" },\n reason: normalizeValue(reason, { maxDepth: 5 }),\n },\n });\n if (exitOnUnhandledRejection) {\n flushSync();\n void flushBounded().finally(() => exitFn(1));\n return;\n }\n if (options.exitOnUnhandledRejection === undefined) {\n if (rejectionsMode === \"warn-with-error-code\") process.exitCode = 1;\n }\n flushBestEffort();\n };\n process.on(\"unhandledRejection\", onUnhandledRejection);\n disposers.push(() => process.off(\"unhandledRejection\", onUnhandledRejection));\n }\n\n if (warning) {\n const onWarning = (warn: Error) => {\n capture({\n level: \"warn\",\n message: warn.message,\n error: warn,\n props: {\n process: {\n kind: \"warning\",\n name: warn.name,\n code: (warn as Error & { code?: string }).code,\n },\n },\n });\n };\n process.on(\"warning\", onWarning);\n disposers.push(() => process.off(\"warning\", onWarning));\n }\n\n if (beforeExitFlush) {\n const onBeforeExit = () => {\n void flushBounded();\n };\n process.on(\"beforeExit\", onBeforeExit);\n disposers.push(() => process.off(\"beforeExit\", onBeforeExit));\n }\n\n if (exitFlush) {\n const onExit = () => {\n flushSync();\n };\n process.on(\"exit\", onExit);\n disposers.push(() => process.off(\"exit\", onExit));\n }\n\n if (signalFlush) {\n for (const signal of signals) {\n const onSignal = () => {\n capture({\n level: \"fatal\",\n message: `Process signal ${signal}`,\n props: { process: { kind: \"signal\", signal } },\n });\n flushSync();\n const flushed = flushBounded();\n if (exitOnSignal) {\n void flushed.finally(() => exitFn(signalExitCodes[signal] ?? 1));\n }\n };\n process.on(signal, onSignal);\n disposers.push(() => process.off(signal, onSignal));\n }\n }\n\n return () => {\n if (disposed) return;\n disposed = true;\n for (const dispose of disposers) dispose();\n };\n },\n };\n}\n"],"mappings":"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"}
@@ -165,8 +165,9 @@ function createNodeFileDestination(options) {
165
165
  const flags = options.flags ?? (options.append === false ? "w" : "a");
166
166
  if (options.mkdir) (0, fs.mkdirSync)((0, path.dirname)(options.path), { recursive: true });
167
167
  let fd;
168
+ const syncFlags = options.sync ? flags : "a";
168
169
  const getFd = () => {
169
- fd ??= (0, fs.openSync)(options.path, flags);
170
+ fd ??= (0, fs.openSync)(options.path, syncFlags);
170
171
  return fd;
171
172
  };
172
173
  const syncWrite = (payload) => {
@@ -1 +1 @@
1
- {"version":3,"file":"rotating-file-transport.cjs","names":[],"sources":["../src/node-destination.ts","../src/rotating-file-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n const getFd = () => {\n fd ??= openSync(options.path, flags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import { existsSync, renameSync, statSync, unlinkSync } from \"fs\";\nimport {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport { createNodeFileDestination } from \"./node-destination\";\n\nexport interface RotatingFileTransportOptions {\n path: string;\n name?: string;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n maxBytes?: number;\n maxFiles?: number;\n archivePath?: (path: string, index: number) => string;\n}\n\nexport interface RotatingFileTransport extends Transport {\n rotate: () => void;\n flushSync: () => void;\n currentBytes: () => number;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction payloadBytes(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? textEncoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return fallback;\n return Math.floor(value);\n}\n\nfunction normalizeNonNegativeInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value < 0) return fallback;\n return Math.floor(value);\n}\n\nfunction defaultArchivePath(path: string, index: number): string {\n return `${path}.${index}`;\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nexport function rotatingFileTransport(\n options: RotatingFileTransportOptions,\n): RotatingFileTransport {\n const codec = options.codec ?? ndjsonCodec();\n const maxBytes = normalizePositiveInteger(options.maxBytes, 10 * 1024 * 1024);\n const maxFiles = normalizeNonNegativeInteger(options.maxFiles, 5);\n const archivePath = options.archivePath ?? defaultArchivePath;\n const destination = createNodeFileDestination({\n name: options.name ?? \"rotating-file\",\n path: options.path,\n flags: options.flags,\n append: options.append,\n mkdir: options.mkdir,\n sync: true,\n });\n let bytes = fileSize(options.path);\n\n const rotate = () => {\n destination.releaseSync?.();\n\n if (maxFiles === 0) {\n if (existsSync(options.path)) unlinkSync(options.path);\n bytes = 0;\n return;\n }\n\n const oldest = archivePath(options.path, maxFiles);\n if (existsSync(oldest)) unlinkSync(oldest);\n\n for (let index = maxFiles - 1; index >= 1; index -= 1) {\n const source = archivePath(options.path, index);\n if (existsSync(source)) renameSync(source, archivePath(options.path, index + 1));\n }\n\n if (existsSync(options.path)) renameSync(options.path, archivePath(options.path, 1));\n bytes = 0;\n };\n\n const writePayload = (payload: string | Uint8Array) => {\n const size = payloadBytes(payload);\n if (bytes > 0 && bytes + size > maxBytes) rotate();\n destination.write(payload);\n bytes += size;\n };\n\n const transport: RotatingFileTransport = {\n name: options.name ?? \"rotating-file\",\n minLevel: options.minLevel,\n log(event: LogEvent) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n writePayload(codec.encode(event));\n },\n logBatch(events: LogEvent[]) {\n for (const event of events) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel))\n continue;\n writePayload(codec.encode(event));\n }\n },\n flush() {\n return destination.flush();\n },\n flushSync: destination.flushSync,\n close() {\n return destination.close();\n },\n rotate,\n currentBytes: () => bytes,\n };\n\n return transport;\n}\n"],"mappings":";;;;;AAuCA,MAAM,UAAU,IAAI,YAAY;AAEhC,MAAM,kBAAkB,UACtB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAE1D,SAAS,WAAW,SAAsC;CACxD,OAAO,OAAO,YAAY,WAAW,QAAQ,OAAO,OAAO,CAAC,CAAC,aAAa,QAAQ;AACpF;AAEA,SAAS,iBAAiB,UAA2D;CACnF,IAAI,SAAS,OAAO,YAAY,OAAO,YAAY,QAAQ,GACzD,OAAO,SAAS,KAAK,EAAE;CAEzB,MAAM,UAAU,SAAS,KAAK,YAC5B,OAAO,YAAY,WAAW,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,OAAO,CAC1E;CACA,OAAO,OAAO,OAAO,OAAO;AAC9B;AAEA,SAAS,QAAQ,OAAyB;CACxC,OACE,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAa,MAA6B,SAAS;AAE1F;AAEA,SAAS,mBAAmB,OAAmC;CAC7D,IAAI,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG,OAAO;CACzE,OAAO,KAAK,MAAM,KAAK;AACzB;AAEA,SAAgB,4BACd,SACiB;CACjB,MAAM,YAAY,mBAAmB,QAAQ,SAAS;CACtD,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,UAAyB,CAAC;CAChC,MAAM,SAAqC,CAAC;CAC5C,MAAM,kBAA8C,CAAC;CACrD,IAAI,gBAAgB;CACpB,IAAI,gBAAgB;CACpB,IAAI,gBAAgB;CACpB,IAAI;CACJ,IAAI;CACJ,IAAI,SAAS;CAEb,MAAM,uBAAuB,OAAgB,cAAsB;EACjE,IAAI,eAAe,QAAQ,KAAK,GAAG;EACnC,aAAa,oBAAoB,OAAO;GACtC,OAAO;GACP,WAAW,QAAQ;GACnB;EACF,CAAC;CACH;CAEA,MAAM,4BAA4B;EAChC,IAAI,gBAAgB,gBAAgB,GAAG;EACvC,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,QAAQ;EACd,YAAY,KAAA;EACZ,KAAK,MAAM,UAAU,QAAQ,OAAO,CAAC,GACnC,IAAI,OAAO,OAAO,OAAO,KAAK;OACzB,OAAO,QAAQ;CAExB;CAEA,MAAM,eAAe,OAAgB,cAAsB;EACzD,IAAI,eAAe,QAAQ,KAAK,GAAG;GACjC,SAAS;GACT;EACF;EACA,YAAY,eAAe,KAAK;EAChC,oBAAoB,WAAW,SAAS;CAC1C;CAEA,MAAM,iBAAiB,UAAiB;EACtC,YAAY,OAAO,cAAc;EACjC,oBAAoB;CACtB;CACA,MAAM,iBAAiB,QAAQ,OAAO,MAAM,SAAS,aAAa;CAClE,QAAQ,OAAO,KAAK,SAAS,aAAa;CAE1C,MAAM,qBAAqB;EACzB,IAAI,CAAC,QAAQ,OAAO,MAAM;EAC1B,iBAAiB;EACjB,QAAQ,OAAO,KAAK,eAAe;GACjC,iBAAiB;GACjB,oBAAoB;EACtB,CAAC;CACH;CAEA,MAAM,iBAAiB,YAAiC;EACtD,MAAM,QAAQ,gBAAgB,QAAQ,OAAO;EAC7C,IAAI,SAAS,GAAG,gBAAgB,OAAO,OAAO,CAAC;CACjD;CAEA,MAAM,YAAY,YAAiC;EACjD,IAAI,QAAQ;EACZ,gBAAgB,KAAK,OAAO;EAC5B,iBAAiB;EACjB,IAAI;GAOF,IANe,QAAQ,OAAO,MAAM,UAAU,UAAyB;IACrE,cAAc,OAAO;IACrB,iBAAiB;IACjB,IAAI,OAAO,YAAY,OAAO,QAAQ,mBAAmB,OAAO;IAChE,oBAAoB;GACtB,CACS,MAAM,OAAO,aAAa;EACrC,SAAS,OAAO;GACd,cAAc,OAAO;GACrB,iBAAiB;GACjB,YAAY,OAAO,QAAQ,mBAAmB,OAAO;GACrD,oBAAoB;EACtB;CACF;CAEA,MAAM,sBAAsB;EAC1B,IAAI,OAAO,WAAW,GAAG;EACzB,MAAM,UAAU,iBAAiB,OAAO,OAAO,CAAC,CAAC;EACjD,gBAAgB;EAChB,SAAS,OAAO;CAClB;CAEA,MAAM,cAA+B;EACnC,MAAM,SAAS,SAAS;GACtB,IAAI,QAAQ;GACZ,cAAc;GACd,IAAI,YAAY,GAAG;IACjB,OAAO,KAAK,OAAO;IACnB,iBAAiB,WAAW,OAAO;IACnC,IAAI,iBAAiB,WAAW,cAAc;IAC9C;GACF;GACA,SAAS,OAAO;EAClB;EACA,QAAQ;GACN,cAAc;GACd,IAAI,gBAAgB,kBAAkB,GAAG;IACvC,MAAM,QAAQ;IACd,YAAY,KAAA;IACZ,IAAI,OAAO,OAAO,QAAQ,OAAO,KAAK;IACtC,OAAO,QAAQ,QAAQ;GACzB;GACA,OAAO,IAAI,SAAe,SAAS,WAAW;IAC5C,QAAQ,KAAK;KAAE;KAAS;IAAO,CAAC;GAClC,CAAC;EACH;EACA,YAAY;GACV,IAAI,CAAC,QAAQ,WAAW;IACtB,cAAc;IACd;GACF;GACA,KAAK,MAAM,WAAW,OAAO,OAAO,CAAC,GAAG,QAAQ,UAAU,OAAO;GACjE,gBAAgB;GAChB,KAAK,MAAM,WAAW,gBAAgB,OAAO,CAAC,GAAG,QAAQ,UAAU,OAAO;EAC5E;EACA,QAAQ;GACN,OAAO,YAAY,MAAM,CAAC,CAAC,WAEvB,IAAI,SAAe,SAAS,WAAW;IACrC,IAAI,CAAC,QAAQ,OAAO,KAAK;KACvB,SAAS;KACT,SAAS;KACT,QAAQ;KACR;IACF;IACA,QAAQ,OAAO,KAAK,UAAyB;KAC3C,SAAS;KACT,SAAS;KACT,IAAI,SAAS,EAAE,eAAe,QAAQ,KAAK,IAAI,OAAO,KAAK;UACtD,QAAQ;IACf,CAAC;GACH,CAAC,IACF,UAAmB;IAClB,SAAS;IACT,OAAO,QAAQ,OAAO,KAAK;GAC7B,CACF;EACF;EACA,YAAY;GACV,YAAY,UAAU;GACtB,SAAS;GACT,SAAS;EACX;CACF;CACA,OAAO;AACT;AAEA,SAAgB,0BAA0B,SAAsD;CAC9F,MAAM,QAAQ,QAAQ,UAAU,QAAQ,WAAW,QAAQ,MAAM;CACjE,IAAI,QAAQ,OAAO,CAAA,GAAA,GAAA,UAAA,EAAA,GAAA,KAAA,QAAA,CAAkB,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAEvE,IAAI;CACJ,MAAM,cAAc;EAClB,QAAA,GAAA,GAAA,SAAA,CAAgB,QAAQ,MAAM,KAAK;EACnC,OAAO;CACT;CACA,MAAM,aAAa,YAAiC;EAClD,IAAI,OAAO,YAAY,UAAU,CAAA,GAAA,GAAA,UAAA,CAAU,MAAM,GAAG,OAAO;OACtD,CAAA,GAAA,GAAA,UAAA,CAAU,MAAM,GAAG,OAAO;CACjC;CAEA,IAAI,QAAQ,MAAM;EAChB,IAAI,SAAS;EACb,MAAM,oBAAoB;GACxB,IAAI,OAAO,KAAA,GAAW;IACpB,CAAA,GAAA,GAAA,UAAA,CAAU,EAAE;IACZ,KAAK,KAAA;GACP;EACF;EACA,OAAO;GACL,MAAM,SAAS;IACb,IAAI,QAAQ;IACZ,UAAU,OAAO;GACnB;GACA,QAAQ;IACN,OAAO,QAAQ,QAAQ;GACzB;GACA,YAAY,CAAC;GACb,QAAQ;IACN,YAAY;IACZ,SAAS;IACT,OAAO,QAAQ,QAAQ;GACzB;GACA,YAAY;IACV,YAAY;IACZ,SAAS;GACX;GACA;EACF;CACF;CAEA,MAAM,UAAA,GAAA,GAAA,kBAAA,CAA2B,QAAQ,MAAM,EAAE,MAAM,CAAC;CACxD,MAAM,cAAc,4BAA4B;EAC9C,MAAM,QAAQ;EACd;EACA,WAAW,QAAQ;EACnB;CACF,CAAC;CACD,MAAM,oBAAoB;EACxB,IAAI,OAAO,KAAA,GAAW;GACpB,CAAA,GAAA,GAAA,UAAA,CAAU,EAAE;GACZ,KAAK,KAAA;EACP;CACF;CACA,OAAO;EACL,GAAG;EACH;EACA,QAAQ;GACN,OAAO,YAAY,MAAM,CAAC,CAAC,QAAQ,WAAW;EAChD;EACA,YAAY;GACV,YAAY,UAAU;GACtB,YAAY;EACd;EACA;CACF;AACF;;;ACzQA,MAAM,cAAc,IAAI,YAAY;AAEpC,SAAS,aAAa,SAAsC;CAC1D,OAAO,OAAO,YAAY,WAAW,YAAY,OAAO,OAAO,CAAC,CAAC,aAAa,QAAQ;AACxF;AAEA,SAAS,yBAAyB,OAA2B,UAA0B;CACrF,IAAI,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG,OAAO;CACzE,OAAO,KAAK,MAAM,KAAK;AACzB;AAEA,SAAS,4BAA4B,OAA2B,UAA0B;CACxF,IAAI,UAAU,KAAA,KAAa,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG,OAAO;CACxE,OAAO,KAAK,MAAM,KAAK;AACzB;AAEA,SAAS,mBAAmB,MAAc,OAAuB;CAC/D,OAAO,GAAG,KAAK,GAAG;AACpB;AAEA,SAAS,SAAS,MAAsB;CACtC,IAAI;EACF,QAAA,GAAA,GAAA,SAAA,CAAgB,IAAI,CAAC,CAAC;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,sBACd,SACuB;CACvB,MAAM,QAAQ,QAAQ,UAAA,GAAA,eAAA,YAAA,CAAqB;CAC3C,MAAM,WAAW,yBAAyB,QAAQ,UAAU,KAAK,OAAO,IAAI;CAC5E,MAAM,WAAW,4BAA4B,QAAQ,UAAU,CAAC;CAChE,MAAM,cAAc,QAAQ,eAAe;CAC3C,MAAM,cAAc,0BAA0B;EAC5C,MAAM,QAAQ,QAAQ;EACtB,MAAM,QAAQ;EACd,OAAO,QAAQ;EACf,QAAQ,QAAQ;EAChB,OAAO,QAAQ;EACf,MAAM;CACR,CAAC;CACD,IAAI,QAAQ,SAAS,QAAQ,IAAI;CAEjC,MAAM,eAAe;EACnB,YAAY,cAAc;EAE1B,IAAI,aAAa,GAAG;GAClB,KAAA,GAAA,GAAA,WAAA,CAAe,QAAQ,IAAI,GAAG,CAAA,GAAA,GAAA,WAAA,CAAW,QAAQ,IAAI;GACrD,QAAQ;GACR;EACF;EAEA,MAAM,SAAS,YAAY,QAAQ,MAAM,QAAQ;EACjD,KAAA,GAAA,GAAA,WAAA,CAAe,MAAM,GAAG,CAAA,GAAA,GAAA,WAAA,CAAW,MAAM;EAEzC,KAAK,IAAI,QAAQ,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG;GACrD,MAAM,SAAS,YAAY,QAAQ,MAAM,KAAK;GAC9C,KAAA,GAAA,GAAA,WAAA,CAAe,MAAM,GAAG,CAAA,GAAA,GAAA,WAAA,CAAW,QAAQ,YAAY,QAAQ,MAAM,QAAQ,CAAC,CAAC;EACjF;EAEA,KAAA,GAAA,GAAA,WAAA,CAAe,QAAQ,IAAI,GAAG,CAAA,GAAA,GAAA,WAAA,CAAW,QAAQ,MAAM,YAAY,QAAQ,MAAM,CAAC,CAAC;EACnF,QAAQ;CACV;CAEA,MAAM,gBAAgB,YAAiC;EACrD,MAAM,OAAO,aAAa,OAAO;EACjC,IAAI,QAAQ,KAAK,QAAQ,OAAO,UAAU,OAAO;EACjD,YAAY,MAAM,OAAO;EACzB,SAAS;CACX;CA2BA,OAAO;EAxBL,MAAM,QAAQ,QAAQ;EACtB,UAAU,QAAQ;EAClB,IAAI,OAAiB;GACnB,IAAI,QAAQ,aAAa,KAAA,KAAa,MAAM,SAAA,GAAA,eAAA,aAAA,CAAqB,QAAQ,QAAQ,GAAG;GACpF,aAAa,MAAM,OAAO,KAAK,CAAC;EAClC;EACA,SAAS,QAAoB;GAC3B,KAAK,MAAM,SAAS,QAAQ;IAC1B,IAAI,QAAQ,aAAa,KAAA,KAAa,MAAM,SAAA,GAAA,eAAA,aAAA,CAAqB,QAAQ,QAAQ,GAC/E;IACF,aAAa,MAAM,OAAO,KAAK,CAAC;GAClC;EACF;EACA,QAAQ;GACN,OAAO,YAAY,MAAM;EAC3B;EACA,WAAW,YAAY;EACvB,QAAQ;GACN,OAAO,YAAY,MAAM;EAC3B;EACA;EACA,oBAAoB;CAGP;AACjB"}
1
+ {"version":3,"file":"rotating-file-transport.cjs","names":[],"sources":["../src/node-destination.ts","../src/rotating-file-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n // In sync mode this fd is the only writer, so it honors the configured flags.\n // In async stream mode the stream already opened (and, for \"w\", truncated)\n // the file; the crash-path fd must append or it would erase earlier logs.\n const syncFlags = options.sync ? flags : \"a\";\n const getFd = () => {\n fd ??= openSync(options.path, syncFlags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import { existsSync, renameSync, statSync, unlinkSync } from \"fs\";\nimport {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport { createNodeFileDestination } from \"./node-destination\";\n\nexport interface RotatingFileTransportOptions {\n path: string;\n name?: string;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n maxBytes?: number;\n maxFiles?: number;\n archivePath?: (path: string, index: number) => string;\n}\n\nexport interface RotatingFileTransport extends Transport {\n rotate: () => void;\n flushSync: () => void;\n currentBytes: () => number;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction payloadBytes(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? textEncoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return fallback;\n return Math.floor(value);\n}\n\nfunction normalizeNonNegativeInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value < 0) return fallback;\n return Math.floor(value);\n}\n\nfunction defaultArchivePath(path: string, index: number): string {\n return `${path}.${index}`;\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nexport function rotatingFileTransport(\n options: RotatingFileTransportOptions,\n): RotatingFileTransport {\n const codec = options.codec ?? ndjsonCodec();\n const maxBytes = normalizePositiveInteger(options.maxBytes, 10 * 1024 * 1024);\n const maxFiles = normalizeNonNegativeInteger(options.maxFiles, 5);\n const archivePath = options.archivePath ?? defaultArchivePath;\n const destination = createNodeFileDestination({\n name: options.name ?? \"rotating-file\",\n path: options.path,\n flags: options.flags,\n append: options.append,\n mkdir: options.mkdir,\n sync: true,\n });\n let bytes = fileSize(options.path);\n\n const rotate = () => {\n destination.releaseSync?.();\n\n if (maxFiles === 0) {\n if (existsSync(options.path)) unlinkSync(options.path);\n bytes = 0;\n return;\n }\n\n const oldest = archivePath(options.path, maxFiles);\n if (existsSync(oldest)) unlinkSync(oldest);\n\n for (let index = maxFiles - 1; index >= 1; index -= 1) {\n const source = archivePath(options.path, index);\n if (existsSync(source)) renameSync(source, archivePath(options.path, index + 1));\n }\n\n if (existsSync(options.path)) renameSync(options.path, archivePath(options.path, 1));\n bytes = 0;\n };\n\n const writePayload = (payload: string | Uint8Array) => {\n const size = payloadBytes(payload);\n if (bytes > 0 && bytes + size > maxBytes) rotate();\n destination.write(payload);\n bytes += size;\n };\n\n const transport: RotatingFileTransport = {\n name: options.name ?? \"rotating-file\",\n minLevel: options.minLevel,\n log(event: LogEvent) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n writePayload(codec.encode(event));\n },\n logBatch(events: LogEvent[]) {\n for (const event of events) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel))\n continue;\n writePayload(codec.encode(event));\n }\n },\n flush() {\n return destination.flush();\n },\n flushSync: destination.flushSync,\n close() {\n return destination.close();\n },\n rotate,\n currentBytes: () => bytes,\n };\n\n return transport;\n}\n"],"mappings":"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"}
@@ -164,8 +164,9 @@ function createNodeFileDestination(options) {
164
164
  const flags = options.flags ?? (options.append === false ? "w" : "a");
165
165
  if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });
166
166
  let fd;
167
+ const syncFlags = options.sync ? flags : "a";
167
168
  const getFd = () => {
168
- fd ??= openSync(options.path, flags);
169
+ fd ??= openSync(options.path, syncFlags);
169
170
  return fd;
170
171
  };
171
172
  const syncWrite = (payload) => {
@@ -1 +1 @@
1
- {"version":3,"file":"rotating-file-transport.js","names":[],"sources":["../src/node-destination.ts","../src/rotating-file-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n const getFd = () => {\n fd ??= openSync(options.path, flags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import { existsSync, renameSync, statSync, unlinkSync } from \"fs\";\nimport {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport { createNodeFileDestination } from \"./node-destination\";\n\nexport interface RotatingFileTransportOptions {\n path: string;\n name?: string;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n maxBytes?: number;\n maxFiles?: number;\n archivePath?: (path: string, index: number) => string;\n}\n\nexport interface RotatingFileTransport extends Transport {\n rotate: () => void;\n flushSync: () => void;\n currentBytes: () => number;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction payloadBytes(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? textEncoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return fallback;\n return Math.floor(value);\n}\n\nfunction normalizeNonNegativeInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value < 0) return fallback;\n return Math.floor(value);\n}\n\nfunction defaultArchivePath(path: string, index: number): string {\n return `${path}.${index}`;\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nexport function rotatingFileTransport(\n options: RotatingFileTransportOptions,\n): RotatingFileTransport {\n const codec = options.codec ?? ndjsonCodec();\n const maxBytes = normalizePositiveInteger(options.maxBytes, 10 * 1024 * 1024);\n const maxFiles = normalizeNonNegativeInteger(options.maxFiles, 5);\n const archivePath = options.archivePath ?? defaultArchivePath;\n const destination = createNodeFileDestination({\n name: options.name ?? \"rotating-file\",\n path: options.path,\n flags: options.flags,\n append: options.append,\n mkdir: options.mkdir,\n sync: true,\n });\n let bytes = fileSize(options.path);\n\n const rotate = () => {\n destination.releaseSync?.();\n\n if (maxFiles === 0) {\n if (existsSync(options.path)) unlinkSync(options.path);\n bytes = 0;\n return;\n }\n\n const oldest = archivePath(options.path, maxFiles);\n if (existsSync(oldest)) unlinkSync(oldest);\n\n for (let index = maxFiles - 1; index >= 1; index -= 1) {\n const source = archivePath(options.path, index);\n if (existsSync(source)) renameSync(source, archivePath(options.path, index + 1));\n }\n\n if (existsSync(options.path)) renameSync(options.path, archivePath(options.path, 1));\n bytes = 0;\n };\n\n const writePayload = (payload: string | Uint8Array) => {\n const size = payloadBytes(payload);\n if (bytes > 0 && bytes + size > maxBytes) rotate();\n destination.write(payload);\n bytes += size;\n };\n\n const transport: RotatingFileTransport = {\n name: options.name ?? \"rotating-file\",\n minLevel: options.minLevel,\n log(event: LogEvent) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n writePayload(codec.encode(event));\n },\n logBatch(events: LogEvent[]) {\n for (const event of events) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel))\n continue;\n writePayload(codec.encode(event));\n }\n },\n flush() {\n return destination.flush();\n },\n flushSync: destination.flushSync,\n close() {\n return destination.close();\n },\n rotate,\n currentBytes: () => bytes,\n };\n\n return transport;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rotating-file-transport.js","names":[],"sources":["../src/node-destination.ts","../src/rotating-file-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n // In sync mode this fd is the only writer, so it honors the configured flags.\n // In async stream mode the stream already opened (and, for \"w\", truncated)\n // the file; the crash-path fd must append or it would erase earlier logs.\n const syncFlags = options.sync ? flags : \"a\";\n const getFd = () => {\n fd ??= openSync(options.path, syncFlags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import { existsSync, renameSync, statSync, unlinkSync } from \"fs\";\nimport {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport { createNodeFileDestination } from \"./node-destination\";\n\nexport interface RotatingFileTransportOptions {\n path: string;\n name?: string;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n maxBytes?: number;\n maxFiles?: number;\n archivePath?: (path: string, index: number) => string;\n}\n\nexport interface RotatingFileTransport extends Transport {\n rotate: () => void;\n flushSync: () => void;\n currentBytes: () => number;\n}\n\nconst textEncoder = new TextEncoder();\n\nfunction payloadBytes(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? textEncoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction normalizePositiveInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return fallback;\n return Math.floor(value);\n}\n\nfunction normalizeNonNegativeInteger(value: number | undefined, fallback: number): number {\n if (value === undefined || !Number.isFinite(value) || value < 0) return fallback;\n return Math.floor(value);\n}\n\nfunction defaultArchivePath(path: string, index: number): string {\n return `${path}.${index}`;\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nexport function rotatingFileTransport(\n options: RotatingFileTransportOptions,\n): RotatingFileTransport {\n const codec = options.codec ?? ndjsonCodec();\n const maxBytes = normalizePositiveInteger(options.maxBytes, 10 * 1024 * 1024);\n const maxFiles = normalizeNonNegativeInteger(options.maxFiles, 5);\n const archivePath = options.archivePath ?? defaultArchivePath;\n const destination = createNodeFileDestination({\n name: options.name ?? \"rotating-file\",\n path: options.path,\n flags: options.flags,\n append: options.append,\n mkdir: options.mkdir,\n sync: true,\n });\n let bytes = fileSize(options.path);\n\n const rotate = () => {\n destination.releaseSync?.();\n\n if (maxFiles === 0) {\n if (existsSync(options.path)) unlinkSync(options.path);\n bytes = 0;\n return;\n }\n\n const oldest = archivePath(options.path, maxFiles);\n if (existsSync(oldest)) unlinkSync(oldest);\n\n for (let index = maxFiles - 1; index >= 1; index -= 1) {\n const source = archivePath(options.path, index);\n if (existsSync(source)) renameSync(source, archivePath(options.path, index + 1));\n }\n\n if (existsSync(options.path)) renameSync(options.path, archivePath(options.path, 1));\n bytes = 0;\n };\n\n const writePayload = (payload: string | Uint8Array) => {\n const size = payloadBytes(payload);\n if (bytes > 0 && bytes + size > maxBytes) rotate();\n destination.write(payload);\n bytes += size;\n };\n\n const transport: RotatingFileTransport = {\n name: options.name ?? \"rotating-file\",\n minLevel: options.minLevel,\n log(event: LogEvent) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n writePayload(codec.encode(event));\n },\n logBatch(events: LogEvent[]) {\n for (const event of events) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel))\n continue;\n writePayload(codec.encode(event));\n }\n },\n flush() {\n return destination.flush();\n },\n flushSync: destination.flushSync,\n close() {\n return destination.close();\n },\n rotate,\n currentBytes: () => bytes,\n };\n\n return transport;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stdout-transport.cjs","names":[],"sources":["../src/node-destination.ts","../src/stdout-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n const getFd = () => {\n fd ??= openSync(options.path, flags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\nimport { createNodeStreamDestination } from \"./node-destination\";\n\nexport interface StdoutTransportOptions {\n name?: string;\n stream?: WritableLike;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n minLength?: number;\n ignoreEpipe?: boolean;\n}\n\nexport function stdoutTransport(options: StdoutTransportOptions = {}): Transport {\n const codec = options.codec ?? ndjsonCodec();\n const stream = options.stream ?? process.stdout;\n const transportName = options.name ?? \"stdout\";\n const destination = createNodeStreamDestination({\n name: transportName,\n stream,\n minLength: options.minLength,\n ignoreEpipe: options.ignoreEpipe ?? true,\n });\n\n return {\n name: transportName,\n minLevel: options.minLevel,\n log(event: LogEvent, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n destination.write(codec.encode(event), context);\n },\n flush: destination.flush,\n flushSync: destination.flushSync,\n close: destination.close,\n };\n}\n\nexport function stderrTransport(options: Omit<StdoutTransportOptions, \"stream\"> = {}): Transport {\n return stdoutTransport({ ...options, name: options.name ?? \"stderr\", stream: process.stderr });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stdout-transport.cjs","names":[],"sources":["../src/node-destination.ts","../src/stdout-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n // In sync mode this fd is the only writer, so it honors the configured flags.\n // In async stream mode the stream already opened (and, for \"w\", truncated)\n // the file; the crash-path fd must append or it would erase earlier logs.\n const syncFlags = options.sync ? flags : \"a\";\n const getFd = () => {\n fd ??= openSync(options.path, syncFlags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\nimport { createNodeStreamDestination } from \"./node-destination\";\n\nexport interface StdoutTransportOptions {\n name?: string;\n stream?: WritableLike;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n minLength?: number;\n ignoreEpipe?: boolean;\n}\n\nexport function stdoutTransport(options: StdoutTransportOptions = {}): Transport {\n const codec = options.codec ?? ndjsonCodec();\n const stream = options.stream ?? process.stdout;\n const transportName = options.name ?? \"stdout\";\n const destination = createNodeStreamDestination({\n name: transportName,\n stream,\n minLength: options.minLength,\n ignoreEpipe: options.ignoreEpipe ?? true,\n });\n\n return {\n name: transportName,\n minLevel: options.minLevel,\n log(event: LogEvent, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n destination.write(codec.encode(event), context);\n },\n flush: destination.flush,\n flushSync: destination.flushSync,\n close: destination.close,\n };\n}\n\nexport function stderrTransport(options: Omit<StdoutTransportOptions, \"stream\"> = {}): Transport {\n return stdoutTransport({ ...options, name: options.name ?? \"stderr\", stream: process.stderr });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stdout-transport.js","names":[],"sources":["../src/node-destination.ts","../src/stdout-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n const getFd = () => {\n fd ??= openSync(options.path, flags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\nimport { createNodeStreamDestination } from \"./node-destination\";\n\nexport interface StdoutTransportOptions {\n name?: string;\n stream?: WritableLike;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n minLength?: number;\n ignoreEpipe?: boolean;\n}\n\nexport function stdoutTransport(options: StdoutTransportOptions = {}): Transport {\n const codec = options.codec ?? ndjsonCodec();\n const stream = options.stream ?? process.stdout;\n const transportName = options.name ?? \"stdout\";\n const destination = createNodeStreamDestination({\n name: transportName,\n stream,\n minLength: options.minLength,\n ignoreEpipe: options.ignoreEpipe ?? true,\n });\n\n return {\n name: transportName,\n minLevel: options.minLevel,\n log(event: LogEvent, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n destination.write(codec.encode(event), context);\n },\n flush: destination.flush,\n flushSync: destination.flushSync,\n close: destination.close,\n };\n}\n\nexport function stderrTransport(options: Omit<StdoutTransportOptions, \"stream\"> = {}): Transport {\n return stdoutTransport({ ...options, name: options.name ?? \"stderr\", stream: process.stderr });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stdout-transport.js","names":[],"sources":["../src/node-destination.ts","../src/stdout-transport.ts"],"sourcesContent":["import { closeSync, createWriteStream, mkdirSync, openSync, writeSync, type WriteStream } from \"fs\";\nimport { dirname } from \"path\";\nimport type { TransportContext } from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\n\nexport interface NodeDestination {\n stream?: WriteStream;\n write: (payload: string | Uint8Array, context?: TransportContext) => void;\n flush: () => Promise<void>;\n flushSync: () => void;\n close: () => Promise<void>;\n closeSync: () => void;\n releaseSync?: () => void;\n}\n\nexport interface NodeStreamDestinationOptions {\n name: string;\n stream: WritableLike;\n minLength?: number;\n ignoreEpipe?: boolean;\n reportOperation?: string;\n syncWrite?: (payload: string | Uint8Array) => void;\n}\n\nexport interface NodeFileDestinationOptions {\n name: string;\n path: string;\n flags?: string;\n append?: boolean;\n mkdir?: boolean;\n sync?: boolean;\n minLength?: number;\n}\n\ninterface FlushWaiter {\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\nconst encoder = new TextEncoder();\n\nconst normalizeError = (error: unknown): Error =>\n error instanceof Error ? error : new Error(String(error));\n\nfunction byteLength(payload: string | Uint8Array): number {\n return typeof payload === \"string\" ? encoder.encode(payload).byteLength : payload.byteLength;\n}\n\nfunction coalescePayloads(payloads: Array<string | Uint8Array>): string | Uint8Array {\n if (payloads.every((payload) => typeof payload === \"string\")) {\n return payloads.join(\"\");\n }\n const buffers = payloads.map((payload) =>\n typeof payload === \"string\" ? Buffer.from(payload) : Buffer.from(payload),\n );\n return Buffer.concat(buffers);\n}\n\nfunction isEpipe(error: unknown): boolean {\n return (\n Boolean(error) && typeof error === \"object\" && (error as { code?: unknown }).code === \"EPIPE\"\n );\n}\n\nfunction normalizeMinLength(value: number | undefined): number {\n if (value === undefined || !Number.isFinite(value) || value <= 0) return 0;\n return Math.floor(value);\n}\n\nexport function createNodeStreamDestination(\n options: NodeStreamDestinationOptions,\n): NodeDestination {\n const minLength = normalizeMinLength(options.minLength);\n const ignoreEpipe = options.ignoreEpipe ?? false;\n const waiters: FlushWaiter[] = [];\n const buffer: Array<string | Uint8Array> = [];\n const pendingPayloads: Array<string | Uint8Array> = [];\n let bufferedBytes = 0;\n let pendingWrites = 0;\n let pendingDrains = 0;\n let lastError: Error | undefined;\n let lastContext: TransportContext | undefined;\n let closed = false;\n\n const reportInternalError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) return;\n lastContext?.reportInternalError(error, {\n phase: \"transport\",\n transport: options.name,\n operation,\n });\n };\n\n const settleWaitersIfIdle = () => {\n if (pendingWrites + pendingDrains > 0) return;\n if (waiters.length === 0) return;\n const error = lastError;\n lastError = undefined;\n for (const waiter of waiters.splice(0)) {\n if (error) waiter.reject(error);\n else waiter.resolve();\n }\n };\n\n const recordError = (error: unknown, operation: string) => {\n if (ignoreEpipe && isEpipe(error)) {\n closed = true;\n return;\n }\n lastError = normalizeError(error);\n reportInternalError(lastError, operation);\n };\n\n const onStreamError = (error: Error) => {\n recordError(error, \"stream-error\");\n settleWaitersIfIdle();\n };\n const offError = () => options.stream.off?.(\"error\", onStreamError);\n options.stream.on?.(\"error\", onStreamError);\n\n const waitForDrain = () => {\n if (!options.stream.once) return;\n pendingDrains += 1;\n options.stream.once(\"drain\", () => {\n pendingDrains -= 1;\n settleWaitersIfIdle();\n });\n };\n\n const removePending = (payload: string | Uint8Array) => {\n const index = pendingPayloads.indexOf(payload);\n if (index >= 0) pendingPayloads.splice(index, 1);\n };\n\n const writeNow = (payload: string | Uint8Array) => {\n if (closed) return;\n pendingPayloads.push(payload);\n pendingWrites += 1;\n try {\n const result = options.stream.write(payload, (error?: Error | null) => {\n removePending(payload);\n pendingWrites -= 1;\n if (error) recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n });\n if (result === false) waitForDrain();\n } catch (error) {\n removePending(payload);\n pendingWrites -= 1;\n recordError(error, options.reportOperation ?? \"write\");\n settleWaitersIfIdle();\n }\n };\n\n const flushBuffered = () => {\n if (buffer.length === 0) return;\n const payload = coalescePayloads(buffer.splice(0));\n bufferedBytes = 0;\n writeNow(payload);\n };\n\n const destination: NodeDestination = {\n write(payload, context) {\n if (closed) return;\n lastContext = context;\n if (minLength > 0) {\n buffer.push(payload);\n bufferedBytes += byteLength(payload);\n if (bufferedBytes >= minLength) flushBuffered();\n return;\n }\n writeNow(payload);\n },\n flush() {\n flushBuffered();\n if (pendingWrites + pendingDrains === 0) {\n const error = lastError;\n lastError = undefined;\n if (error) return Promise.reject(error);\n return Promise.resolve();\n }\n return new Promise<void>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n },\n flushSync() {\n if (!options.syncWrite) {\n flushBuffered();\n return;\n }\n for (const payload of buffer.splice(0)) options.syncWrite(payload);\n bufferedBytes = 0;\n for (const payload of pendingPayloads.splice(0)) options.syncWrite(payload);\n },\n close() {\n return destination.flush().then(\n () =>\n new Promise<void>((resolve, reject) => {\n if (!options.stream.end) {\n closed = true;\n offError();\n resolve();\n return;\n }\n options.stream.end((error?: Error | null) => {\n closed = true;\n offError();\n if (error && !(ignoreEpipe && isEpipe(error))) reject(error);\n else resolve();\n });\n }),\n (error: unknown) => {\n offError();\n return Promise.reject(error);\n },\n );\n },\n closeSync() {\n destination.flushSync();\n closed = true;\n offError();\n },\n };\n return destination;\n}\n\nexport function createNodeFileDestination(options: NodeFileDestinationOptions): NodeDestination {\n const flags = options.flags ?? (options.append === false ? \"w\" : \"a\");\n if (options.mkdir) mkdirSync(dirname(options.path), { recursive: true });\n\n let fd: number | undefined;\n // In sync mode this fd is the only writer, so it honors the configured flags.\n // In async stream mode the stream already opened (and, for \"w\", truncated)\n // the file; the crash-path fd must append or it would erase earlier logs.\n const syncFlags = options.sync ? flags : \"a\";\n const getFd = () => {\n fd ??= openSync(options.path, syncFlags);\n return fd;\n };\n const syncWrite = (payload: string | Uint8Array) => {\n if (typeof payload === \"string\") writeSync(getFd(), payload);\n else writeSync(getFd(), payload);\n };\n\n if (options.sync) {\n let closed = false;\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n write(payload) {\n if (closed) return;\n syncWrite(payload);\n },\n flush() {\n return Promise.resolve();\n },\n flushSync() {},\n close() {\n releaseSync();\n closed = true;\n return Promise.resolve();\n },\n closeSync() {\n releaseSync();\n closed = true;\n },\n releaseSync,\n };\n }\n\n const stream = createWriteStream(options.path, { flags });\n const destination = createNodeStreamDestination({\n name: options.name,\n stream,\n minLength: options.minLength,\n syncWrite,\n });\n const releaseSync = () => {\n if (fd !== undefined) {\n closeSync(fd);\n fd = undefined;\n }\n };\n return {\n ...destination,\n stream,\n close() {\n return destination.close().finally(releaseSync);\n },\n closeSync() {\n destination.closeSync();\n releaseSync();\n },\n releaseSync,\n };\n}\n","import {\n ndjsonCodec,\n toLevelValue,\n type Codec,\n type LogEvent,\n type LoggerLevel,\n type Transport,\n} from \"@loggerjs/core\";\nimport type { WritableLike } from \"./internal-types\";\nimport { createNodeStreamDestination } from \"./node-destination\";\n\nexport interface StdoutTransportOptions {\n name?: string;\n stream?: WritableLike;\n codec?: Codec<string | Uint8Array>;\n minLevel?: LoggerLevel;\n minLength?: number;\n ignoreEpipe?: boolean;\n}\n\nexport function stdoutTransport(options: StdoutTransportOptions = {}): Transport {\n const codec = options.codec ?? ndjsonCodec();\n const stream = options.stream ?? process.stdout;\n const transportName = options.name ?? \"stdout\";\n const destination = createNodeStreamDestination({\n name: transportName,\n stream,\n minLength: options.minLength,\n ignoreEpipe: options.ignoreEpipe ?? true,\n });\n\n return {\n name: transportName,\n minLevel: options.minLevel,\n log(event: LogEvent, context) {\n if (options.minLevel !== undefined && event.level < toLevelValue(options.minLevel)) return;\n destination.write(codec.encode(event), context);\n },\n flush: destination.flush,\n flushSync: destination.flushSync,\n close: destination.close,\n };\n}\n\nexport function stderrTransport(options: Omit<StdoutTransportOptions, \"stream\"> = {}): Transport {\n return stdoutTransport({ ...options, name: options.name ?? \"stderr\", stream: process.stderr });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loggerjs/node",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "Node.js transports and integrations for loggerjs.",
5
5
  "homepage": "https://github.com/jskits/loggerjs/tree/main/packages/node#readme",
6
6
  "bugs": {
@@ -159,7 +159,7 @@
159
159
  "provenance": true
160
160
  },
161
161
  "dependencies": {
162
- "@loggerjs/core": "0.5.5"
162
+ "@loggerjs/core": "0.6.0"
163
163
  },
164
164
  "loggerjsSubpathEntries": {
165
165
  "http-transport": "src/http-transport.ts",