@logtape/logtape 2.3.0-dev.795 → 2.3.0-dev.805

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.
package/dist/config.cjs CHANGED
@@ -186,7 +186,7 @@ async function withConfig(config, callback) {
186
186
  callbackError = error;
187
187
  }
188
188
  try {
189
- await require_scoped_config.disposeScopedConfig(scopedConfig);
189
+ await require_scoped_config.disposeScopedConfig(scopedConfig, getGlobalDisposables());
190
190
  } catch (disposeError) {
191
191
  if (callbackFailed) require_scoped_config.throwCombinedErrors(callbackError, disposeError);
192
192
  throw disposeError;
@@ -226,7 +226,7 @@ function withConfigSync(config, callback) {
226
226
  callbackError = error;
227
227
  }
228
228
  try {
229
- require_scoped_config.disposeScopedConfigSync(scopedConfig);
229
+ require_scoped_config.disposeScopedConfigSync(scopedConfig, getGlobalDisposables());
230
230
  } catch (disposeError) {
231
231
  if (callbackFailed) require_scoped_config.throwCombinedErrors(callbackError, disposeError);
232
232
  throw disposeError;
@@ -239,6 +239,14 @@ function withConfigSync(config, callback) {
239
239
  function isThenable(value) {
240
240
  return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
241
241
  }
242
+ function getGlobalDisposables() {
243
+ return new Set([
244
+ ...filterDisposables,
245
+ ...asyncFilterDisposables,
246
+ ...sinkDisposables,
247
+ ...asyncSinkDisposables
248
+ ]);
249
+ }
242
250
  function configureInternal(config, allowAsync) {
243
251
  currentConfig = config;
244
252
  let metaConfigured = false;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAiBA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAkB3C;EAAwB,OAAA,EAxBb,YAwBa,CAxBA,OAwBA,EAxBS,SAwBT,CAAA,EAAA;EAAA;;;AACN;EAEb,mBAAA,CAAA,EArBmB,mBAqBD,CArBqB,MAqBrB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAAuC,KAC1D,CAAA,EAAA,OAAA;AAAO;AAKX;;;;;AAoCwB;AA0HxB;;;;AAGU,KA1KE,YA0KF,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,GAzKR,gBAyKQ,CAzKS,OAyKT,EAzKkB,SAyKlB,CAAA;KAvKL,kBAuKkC,CAAA,OAAA,CAAA,GAvKJ,OAuKI,SAvKY,WAuKZ,CAAA,OAAA,CAAA,GAAA,KAAA,GAtKnC,OAsKmC;AAAO;AAsD9C;;AACiB,UAxNA,YAwNA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;AAyChB;EAAgC,QAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAA;;;EAKV,KACJ,CAAA,EA1PR,OA0PQ,EAAA;EAAO;;;AACf;AAiDV;;;;;EAKsB,WACe,CAAA,EAAA,SAAA,GAAA,UAAA;EAAO;;;EACvB,OAAA,CAAA,EAnST,SAmSS,EAAA;EAyJL;AAOhB;AAYA;AAkBA;AAkCA;EAoKa,WAAA,CAAA,EAhqBG,QAgqBS,GAAQ,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAtiBX,oEAGZ,OAAO,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDvB,wEACN,OAAO,SAAS;;;;;;;;;;;;;;iBAyCJ,8EAKZ,aAAa,SAAS,4BACd,UACf,QAAQ,QAAQ;;;;;;;;;;;;iBAiDH,kFAKN,aAAa,SAAS,4BACd,mBAAmB,WAClC,mBAAmB;;;;;iBAyJN,SAAA,CAAA,GAAa;;;;iBAOP,KAAA,CAAA,GAAS;;;;;;iBAYf,SAAA,CAAA;;;;iBAkBM,OAAA,CAAA,GAAW;;;;;;iBAkCjB,WAAA,CAAA;;;;cAoKH,WAAA,SAAoB,KAAK"}
1
+ {"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAiBA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAkB3C;EAAwB,OAAA,EAxBb,YAwBa,CAxBA,OAwBA,EAxBS,SAwBT,CAAA,EAAA;EAAA;;;AACN;EAEb,mBAAA,CAAA,EArBmB,mBAqBD,CArBqB,MAqBrB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAAuC,KAC1D,CAAA,EAAA,OAAA;AAAO;AAKX;;;;;AAoCwB;AA0HxB;;;;AAGU,KA1KE,YA0KF,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,GAzKR,gBAyKQ,CAzKS,OAyKT,EAzKkB,SAyKlB,CAAA;KAvKL,kBAuKkC,CAAA,OAAA,CAAA,GAvKJ,OAuKI,SAvKY,WAuKZ,CAAA,OAAA,CAAA,GAAA,KAAA,GAtKnC,OAsKmC;AAAO;AAsD9C;;AACiB,UAxNA,YAwNA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;AAyChB;EAAgC,QAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAA;;;EAKV,KACJ,CAAA,EA1PR,OA0PQ,EAAA;EAAO;;;AACf;AAiDV;;;;;EAKsB,WACe,CAAA,EAAA,SAAA,GAAA,UAAA;EAAO;;;EACvB,OAAA,CAAA,EAnST,SAmSS,EAAA;EAkKL;AAOhB;AAYA;AAkBA;AAkCA;EAoKa,WAAA,CAAA,EAzqBG,QAyqBS,GAAQ,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA/iBX,oEAGZ,OAAO,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDvB,wEACN,OAAO,SAAS;;;;;;;;;;;;;;iBAyCJ,8EAKZ,aAAa,SAAS,4BACd,UACf,QAAQ,QAAQ;;;;;;;;;;;;iBAiDH,kFAKN,aAAa,SAAS,4BACd,mBAAmB,WAClC,mBAAmB;;;;;iBAkKN,SAAA,CAAA,GAAa;;;;iBAOP,KAAA,CAAA,GAAS;;;;;;iBAYf,SAAA,CAAA;;;;iBAkBM,OAAA,CAAA,GAAW;;;;;;iBAkCjB,WAAA,CAAA;;;;cAoKH,WAAA,SAAoB,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAiBA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAkB3C;EAAwB,OAAA,EAxBb,YAwBa,CAxBA,OAwBA,EAxBS,SAwBT,CAAA,EAAA;EAAA;;;AACN;EAEb,mBAAA,CAAA,EArBmB,mBAqBD,CArBqB,MAqBrB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAAuC,KAC1D,CAAA,EAAA,OAAA;AAAO;AAKX;;;;;AAoCwB;AA0HxB;;;;AAGU,KA1KE,YA0KF,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,GAzKR,gBAyKQ,CAzKS,OAyKT,EAzKkB,SAyKlB,CAAA;KAvKL,kBAuKkC,CAAA,OAAA,CAAA,GAvKJ,OAuKI,SAvKY,WAuKZ,CAAA,OAAA,CAAA,GAAA,KAAA,GAtKnC,OAsKmC;AAAO;AAsD9C;;AACiB,UAxNA,YAwNA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;AAyChB;EAAgC,QAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAA;;;EAKV,KACJ,CAAA,EA1PR,OA0PQ,EAAA;EAAO;;;AACf;AAiDV;;;;;EAKsB,WACe,CAAA,EAAA,SAAA,GAAA,UAAA;EAAO;;;EACvB,OAAA,CAAA,EAnST,SAmSS,EAAA;EAyJL;AAOhB;AAYA;AAkBA;AAkCA;EAoKa,WAAA,CAAA,EAhqBG,QAgqBS,GAAQ,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAtiBX,oEAGZ,OAAO,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDvB,wEACN,OAAO,SAAS;;;;;;;;;;;;;;iBAyCJ,8EAKZ,aAAa,SAAS,4BACd,UACf,QAAQ,QAAQ;;;;;;;;;;;;iBAiDH,kFAKN,aAAa,SAAS,4BACd,mBAAmB,WAClC,mBAAmB;;;;;iBAyJN,SAAA,CAAA,GAAa;;;;iBAOP,KAAA,CAAA,GAAS;;;;;;iBAYf,SAAA,CAAA;;;;iBAkBM,OAAA,CAAA,GAAW;;;;;;iBAkCjB,WAAA,CAAA;;;;cAoKH,WAAA,SAAoB,KAAK"}
1
+ {"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAiBA;AAAuB,UAAN,MAAM,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAA;;;;EAUK,KAAE,EALrB,MAKqB,CALd,OAKc,EALL,IAKK,CAAA;EAAU;;;;EAKjB,OAMqB,CAAA,EAXhC,MAWgC,CAXzB,SAWyB,EAXd,UAWc,CAAA;EAAM;AAAP;AAkB3C;EAAwB,OAAA,EAxBb,YAwBa,CAxBA,OAwBA,EAxBS,SAwBT,CAAA,EAAA;EAAA;;;AACN;EAEb,mBAAA,CAAA,EArBmB,mBAqBD,CArBqB,MAqBrB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EAAA;;;EAAuC,KAC1D,CAAA,EAAA,OAAA;AAAO;AAKX;;;;;AAoCwB;AA0HxB;;;;AAGU,KA1KE,YA0KF,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,GAzKR,gBAyKQ,CAzKS,OAyKT,EAzKkB,SAyKlB,CAAA;KAvKL,kBAuKkC,CAAA,OAAA,CAAA,GAvKJ,OAuKI,SAvKY,WAuKZ,CAAA,OAAA,CAAA,GAAA,KAAA,GAtKnC,OAsKmC;AAAO;AAsD9C;;AACiB,UAxNA,YAwNA,CAAA,gBAAA,MAAA,EAAA,kBAAA,MAAA,CAAA,CAAA;EAAO;;AAAR;AAyChB;EAAgC,QAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAA;;;EAKV,KACJ,CAAA,EA1PR,OA0PQ,EAAA;EAAO;;;AACf;AAiDV;;;;;EAKsB,WACe,CAAA,EAAA,SAAA,GAAA,UAAA;EAAO;;;EACvB,OAAA,CAAA,EAnST,SAmSS,EAAA;EAkKL;AAOhB;AAYA;AAkBA;AAkCA;EAoKa,WAAA,CAAA,EAzqBG,QAyqBS,GAAQ,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA/iBX,oEAGZ,OAAO,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsDvB,wEACN,OAAO,SAAS;;;;;;;;;;;;;;iBAyCJ,8EAKZ,aAAa,SAAS,4BACd,UACf,QAAQ,QAAQ;;;;;;;;;;;;iBAiDH,kFAKN,aAAa,SAAS,4BACd,mBAAmB,WAClC,mBAAmB;;;;;iBAkKN,SAAA,CAAA,GAAa;;;;iBAOP,KAAA,CAAA,GAAS;;;;;;iBAYf,SAAA,CAAA;;;;iBAkBM,OAAA,CAAA,GAAW;;;;;;iBAkCjB,WAAA,CAAA;;;;cAoKH,WAAA,SAAoB,KAAK"}
package/dist/config.js CHANGED
@@ -186,7 +186,7 @@ async function withConfig(config, callback) {
186
186
  callbackError = error;
187
187
  }
188
188
  try {
189
- await disposeScopedConfig(scopedConfig);
189
+ await disposeScopedConfig(scopedConfig, getGlobalDisposables());
190
190
  } catch (disposeError) {
191
191
  if (callbackFailed) throwCombinedErrors(callbackError, disposeError);
192
192
  throw disposeError;
@@ -226,7 +226,7 @@ function withConfigSync(config, callback) {
226
226
  callbackError = error;
227
227
  }
228
228
  try {
229
- disposeScopedConfigSync(scopedConfig);
229
+ disposeScopedConfigSync(scopedConfig, getGlobalDisposables());
230
230
  } catch (disposeError) {
231
231
  if (callbackFailed) throwCombinedErrors(callbackError, disposeError);
232
232
  throw disposeError;
@@ -239,6 +239,14 @@ function withConfigSync(config, callback) {
239
239
  function isThenable(value) {
240
240
  return value != null && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
241
241
  }
242
+ function getGlobalDisposables() {
243
+ return new Set([
244
+ ...filterDisposables,
245
+ ...asyncFilterDisposables,
246
+ ...sinkDisposables,
247
+ ...asyncSinkDisposables
248
+ ]);
249
+ }
242
250
  function configureInternal(config, allowAsync) {
243
251
  currentConfig = config;
244
252
  let metaConfigured = false;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":["currentConfig: Config<string, string> | null","strongRefs: Set<LoggerImpl>","filterDisposables: Set<Disposable>","sinkDisposables: Set<Disposable>","asyncFilterDisposables: Set<AsyncDisposable>","asyncSinkDisposables: Set<AsyncDisposable>","cfg: LoggerConfig<TSinkId, TFilterId>","allowAsync: boolean","config: Config<TSinkId, TFilterId>","config: ScopedConfig<TSinkId, TFilterId>","callback: () => TResult","result: Awaited<TResult>","callbackError: unknown","callback: () => SyncCallbackResult<TResult>","result: SyncCallbackResult<TResult>","value: unknown","errors: unknown[]","functionName: string","callback: () => Promise<T>","callback: () => T","disposables: Set<Disposable>","disposables: Set<AsyncDisposable>","promises: PromiseLike<void>[]","promises: readonly PromiseLike<void>[]","errors: readonly unknown[]","message: string"],"sources":["../src/config.ts"],"sourcesContent":["import type { ContextLocalStorage } from \"./context.ts\";\nimport { type FilterLike, toFilter } from \"./filter.ts\";\nimport type { LogLevel } from \"./level.ts\";\nimport { LoggerImpl } from \"./logger.ts\";\nimport {\n compileScopedConfig,\n disposeScopedConfig,\n disposeScopedConfigSync,\n runWithScopedConfig,\n type ScopedConfigLike,\n throwCombinedErrors,\n} from \"./scoped-config.ts\";\nimport { getConsoleSink, type Sink } from \"./sink.ts\";\n\n/**\n * A configuration for the loggers.\n */\nexport interface Config<TSinkId extends string, TFilterId extends string> {\n /**\n * The sinks to use. The keys are the sink identifiers, and the values are\n * {@link Sink}s.\n */\n sinks: Record<TSinkId, Sink>;\n /**\n * The filters to use. The keys are the filter identifiers, and the values\n * are either {@link Filter}s or {@link LogLevel}s.\n */\n filters?: Record<TFilterId, FilterLike>;\n\n /**\n * The loggers to configure.\n */\n loggers: LoggerConfig<TSinkId, TFilterId>[];\n\n /**\n * The context-local storage to use for implicit contexts.\n * @since 0.7.0\n */\n contextLocalStorage?: ContextLocalStorage<Record<string, unknown>>;\n\n /**\n * Whether to reset the configuration before applying this one.\n */\n reset?: boolean;\n}\n\n/**\n * A scoped configuration for the current execution context.\n *\n * Unlike {@link Config}, this type does not include `reset` or\n * `contextLocalStorage`. Scoped configuration changes only the logging policy\n * for a callback; the context-local storage must already be initialized by the\n * process-global configuration.\n *\n * @since 2.3.0\n */\nexport type ScopedConfig<TSinkId extends string, TFilterId extends string> =\n ScopedConfigLike<TSinkId, TFilterId>;\n\ntype SyncCallbackResult<TResult> = TResult extends PromiseLike<unknown> ? never\n : TResult;\n\n/**\n * A logger configuration.\n */\nexport interface LoggerConfig<\n TSinkId extends string,\n TFilterId extends string,\n> {\n /**\n * The category of the logger. If a string, it is equivalent to an array\n * with one element.\n */\n category: string | string[];\n\n /**\n * The sink identifiers to use.\n */\n sinks?: TSinkId[];\n\n /**\n * Whether to inherit the parent's sinks. If `inherit`, the parent's sinks\n * are used along with the specified sinks. If `override`, the parent's\n * sinks are not used, and only the specified sinks are used.\n *\n * The default is `inherit`.\n * @default `\"inherit\"\n * @since 0.6.0\n */\n parentSinks?: \"inherit\" | \"override\";\n\n /**\n * The filter identifiers to use.\n */\n filters?: TFilterId[];\n\n /**\n * The lowest log level to accept. If `null`, the logger will reject all\n * records.\n * @since 0.8.0\n */\n lowestLevel?: LogLevel | null;\n}\n\n/**\n * The current configuration, if any. Otherwise, `null`.\n */\nlet currentConfig: Config<string, string> | null = null;\nlet activeScopedConfigCount = 0;\nlet globalConfigMutationInProgress = false;\n\n/**\n * Strong references to the loggers.\n * This is to prevent the loggers from being garbage collected so that their\n * sinks and filters are not removed.\n */\nconst strongRefs: Set<LoggerImpl> = new Set();\n\n/**\n * Sync filter disposables to dispose when resetting the configuration.\n */\nconst filterDisposables: Set<Disposable> = new Set();\n\n/**\n * Sync sink disposables to dispose when resetting the configuration.\n */\nconst sinkDisposables: Set<Disposable> = new Set();\n\n/**\n * Async filter disposables to dispose when resetting the configuration.\n */\nconst asyncFilterDisposables: Set<AsyncDisposable> = new Set();\n\n/**\n * Async sink disposables to dispose when resetting the configuration.\n */\nconst asyncSinkDisposables: Set<AsyncDisposable> = new Set();\n\n/**\n * Check if a config is for the meta logger.\n */\nfunction isLoggerConfigMeta<TSinkId extends string, TFilterId extends string>(\n cfg: LoggerConfig<TSinkId, TFilterId>,\n): boolean {\n const category = Array.isArray(cfg.category) ? cfg.category : [cfg.category];\n return category.length === 0 ||\n (category.length === 1 && category[0] === \"logtape\") ||\n (category.length === 2 &&\n category[0] === \"logtape\" &&\n category[1] === \"meta\");\n}\n\nfunction registerDisposeHook(allowAsync: boolean): void {\n const handler = allowAsync ? disposeInternal : disposeSyncInternal;\n\n if (\n // deno-lint-ignore no-explicit-any\n typeof (globalThis as any).EdgeRuntime !== \"string\" &&\n \"process\" in globalThis &&\n !(\"Deno\" in globalThis)\n ) {\n // deno-lint-ignore no-explicit-any\n const proc = (globalThis as any).process;\n // Use bracket notation to avoid static analysis detection in Edge Runtime.\n const onMethod = proc?.[\"on\"];\n if (typeof onMethod === \"function\") {\n onMethod.call(proc, \"exit\", handler);\n return;\n }\n }\n\n // Some edge runtimes expose neither process.on() nor addEventListener().\n // In those environments users can still call dispose()/disposeSync() manually.\n // deno-lint-ignore no-explicit-any\n const addEventListenerMethod = (globalThis as any).addEventListener;\n if (typeof addEventListenerMethod !== \"function\") return;\n\n if (\"Deno\" in globalThis) {\n addEventListenerMethod.call(globalThis, \"unload\", handler);\n } else {\n addEventListenerMethod.call(globalThis, \"pagehide\", handler);\n }\n}\n\n/**\n * Configure the loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * @example\n * ```typescript\n * await configure({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * filters: {\n * slow: (log) =>\n * \"duration\" in log.properties &&\n * log.properties.duration as number > 1000,\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: [\"my-app\", \"sql\"],\n * filters: [\"slow\"],\n * lowestLevel: \"debug\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n */\nexport async function configure<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>): Promise<void> {\n await runGlobalConfigMutation(\"configure()\", async () => {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n await disposeInternal();\n resetInternal();\n try {\n configureInternal(config, true);\n } catch (e) {\n if (e instanceof ConfigError) {\n await disposeInternal();\n resetInternal();\n }\n throw e;\n }\n });\n}\n\n/**\n * Configure sync loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * Also note that passing async sinks or filters will throw. If\n * necessary use {@link resetSync} or {@link disposeSync}.\n *\n * @example\n * ```typescript\n * configureSync({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n * @since 0.9.0\n */\nexport function configureSync<TSinkId extends string, TFilterId extends string>(\n config: Config<TSinkId, TFilterId>,\n): void {\n runGlobalConfigMutationSync(\"configureSync()\", () => {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n if (asyncFilterDisposables.size > 0 || asyncSinkDisposables.size > 0) {\n throw new ConfigError(\n \"Previously configured async disposables are still active. \" +\n \"Use configure() instead or explicitly dispose them using dispose().\",\n );\n }\n disposeSyncInternal();\n resetInternal();\n try {\n configureInternal(config, false);\n } catch (e) {\n if (e instanceof ConfigError) {\n disposeSyncInternal();\n resetInternal();\n }\n throw e;\n }\n });\n}\n\n/**\n * Runs a callback with a LogTape configuration scoped to the current execution\n * context.\n *\n * The process-global configuration must already provide\n * `contextLocalStorage`. The scoped configuration fully overrides global\n * logger routing while the callback and its async work run.\n *\n * @param config The scoped configuration.\n * @param callback The callback to run.\n * @returns The callback's resolved return value.\n * @since 2.3.0\n */\nexport async function withConfig<\n TSinkId extends string,\n TFilterId extends string,\n TResult,\n>(\n config: ScopedConfig<TSinkId, TFilterId>,\n callback: () => TResult,\n): Promise<Awaited<TResult>> {\n const contextLocalStorage = getConfiguredContextLocalStorage(\"withConfig()\");\n const scopedConfig = compileScopedConfig(\n config,\n true,\n (message) => new ConfigError(message),\n );\n\n let result: Awaited<TResult>;\n let callbackError: unknown;\n let callbackFailed = false;\n activeScopedConfigCount++;\n try {\n result = await runWithScopedConfig(\n contextLocalStorage,\n scopedConfig,\n callback,\n );\n } catch (error) {\n callbackFailed = true;\n callbackError = error;\n }\n\n try {\n await disposeScopedConfig(scopedConfig);\n } catch (disposeError) {\n if (callbackFailed) {\n throwCombinedErrors(callbackError, disposeError);\n }\n throw disposeError;\n } finally {\n activeScopedConfigCount--;\n }\n\n if (callbackFailed) throw callbackError;\n return result!;\n}\n\n/**\n * Runs a synchronous callback with a LogTape configuration scoped to the\n * current execution context.\n *\n * The scoped configuration must not contain async disposable sinks or filters.\n *\n * @param config The scoped configuration.\n * @param callback The callback to run.\n * @returns The callback's return value.\n * @since 2.3.0\n */\nexport function withConfigSync<\n TSinkId extends string,\n TFilterId extends string,\n TResult,\n>(\n config: ScopedConfig<TSinkId, TFilterId>,\n callback: () => SyncCallbackResult<TResult>,\n): SyncCallbackResult<TResult> {\n const contextLocalStorage = getConfiguredContextLocalStorage(\n \"withConfigSync()\",\n );\n const scopedConfig = compileScopedConfig(\n config,\n false,\n (message) => new ConfigError(message),\n );\n\n let result: SyncCallbackResult<TResult>;\n let callbackError: unknown;\n let callbackFailed = false;\n activeScopedConfigCount++;\n try {\n result = runWithScopedConfig(contextLocalStorage, scopedConfig, callback);\n if (isThenable(result)) {\n void Promise.resolve(result).catch(() => {});\n callbackFailed = true;\n callbackError = new ConfigError(\n \"withConfigSync() callback must not return a promise. \" +\n \"Use withConfig() for async callbacks.\",\n );\n }\n } catch (error) {\n callbackFailed = true;\n callbackError = error;\n }\n\n try {\n disposeScopedConfigSync(scopedConfig);\n } catch (disposeError) {\n if (callbackFailed) {\n throwCombinedErrors(callbackError, disposeError);\n }\n throw disposeError;\n } finally {\n activeScopedConfigCount--;\n }\n\n if (callbackFailed) throw callbackError;\n return result!;\n}\n\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n return value != null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n \"then\" in value &&\n typeof value.then === \"function\";\n}\n\nfunction configureInternal<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>, allowAsync: boolean): void {\n currentConfig = config;\n\n let metaConfigured = false;\n const configuredCategories = new Set<string>();\n\n for (const cfg of config.loggers) {\n if (isLoggerConfigMeta(cfg)) {\n metaConfigured = true;\n }\n\n // Check for duplicate logger categories\n const categoryKey = Array.isArray(cfg.category)\n ? JSON.stringify(cfg.category)\n : JSON.stringify([cfg.category]);\n if (configuredCategories.has(categoryKey)) {\n throw new ConfigError(\n `Duplicate logger configuration for category: ${categoryKey}. ` +\n `Each category can only be configured once.`,\n );\n }\n configuredCategories.add(categoryKey);\n\n const logger = LoggerImpl.getLogger(cfg.category);\n for (const sinkId of cfg.sinks ?? []) {\n const sink = config.sinks[sinkId];\n if (!sink) {\n throw new ConfigError(`Sink not found: ${sinkId}.`);\n }\n logger.sinks.push(sink);\n }\n logger.parentSinks = cfg.parentSinks ?? \"inherit\";\n if (cfg.lowestLevel !== undefined) {\n logger.lowestLevel = cfg.lowestLevel;\n }\n for (const filterId of cfg.filters ?? []) {\n const filter = config.filters?.[filterId];\n if (filter === undefined) {\n throw new ConfigError(`Filter not found: ${filterId}.`);\n }\n logger.filters.push(toFilter(filter));\n }\n strongRefs.add(logger);\n }\n\n LoggerImpl.getLogger().contextLocalStorage = config.contextLocalStorage;\n\n for (const sink of Object.values<Sink>(config.sinks)) {\n if (Symbol.asyncDispose in sink) {\n if (allowAsync) asyncSinkDisposables.add(sink as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n }\n if (Symbol.dispose in sink) sinkDisposables.add(sink as Disposable);\n }\n\n for (const filter of Object.values<FilterLike>(config.filters ?? {})) {\n if (filter == null || typeof filter === \"string\") continue;\n if (Symbol.asyncDispose in filter) {\n if (allowAsync) asyncFilterDisposables.add(filter as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n asyncSinkDisposables.delete(filter as AsyncDisposable);\n }\n if (Symbol.dispose in filter) {\n filterDisposables.add(filter as Disposable);\n sinkDisposables.delete(filter as Disposable);\n }\n }\n\n registerDisposeHook(allowAsync);\n const meta = LoggerImpl.getLogger([\"logtape\", \"meta\"]);\n if (!metaConfigured) {\n meta.sinks.push(getConsoleSink());\n }\n\n meta.info(\n \"LogTape loggers are configured. Note that LogTape itself uses the meta \" +\n \"logger, which has category {metaLoggerCategory}. The meta logger is \" +\n \"used to log internal diagnostics such as sink exceptions. \" +\n \"It's recommended to configure the meta logger with a separate sink \" +\n \"so that you can easily notice if logging itself fails or is \" +\n \"misconfigured. To turn off this message, configure the meta logger \" +\n \"with higher log levels than {dismissLevel}. See also \" +\n \"<https://logtape.org/manual/categories#meta-logger>.\",\n { metaLoggerCategory: [\"logtape\", \"meta\"], dismissLevel: \"info\" },\n );\n}\n\n/**\n * Get the current configuration, if any. Otherwise, `null`.\n * @returns The current configuration, if any. Otherwise, `null`.\n */\nexport function getConfig(): Config<string, string> | null {\n return currentConfig;\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes.\n */\nexport async function reset(): Promise<void> {\n await runGlobalConfigMutation(\"reset()\", async () => {\n await disposeInternal();\n resetInternal();\n });\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes. Will not clear async\n * sinks, only use with sync sinks. Use {@link reset} if you have async sinks.\n * @since 0.9.0\n */\nexport function resetSync(): void {\n runGlobalConfigMutationSync(\"resetSync()\", () => {\n disposeSyncInternal();\n resetInternal();\n });\n}\n\nfunction resetInternal(): void {\n const rootLogger = LoggerImpl.getLogger([]);\n rootLogger.resetDescendants();\n delete rootLogger.contextLocalStorage;\n strongRefs.clear();\n currentConfig = null;\n}\n\n/**\n * Dispose of the disposables.\n */\nexport async function dispose(): Promise<void> {\n await runGlobalConfigMutation(\"dispose()\", disposeInternal);\n}\n\nasync function disposeInternal(): Promise<void> {\n const errors: unknown[] = [];\n try {\n disposeSyncFilters();\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncFilters();\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncSinks();\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncSinks();\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\n/**\n * Dispose of the sync disposables. Async disposables will be untouched,\n * use {@link dispose} if you have async sinks.\n * @since 0.9.0\n */\nexport function disposeSync(): void {\n runGlobalConfigMutationSync(\"disposeSync()\", disposeSyncInternal);\n}\n\nfunction disposeSyncInternal(): void {\n const errors: unknown[] = [];\n try {\n disposeSyncFilters();\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncSinks();\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\nfunction getConfiguredContextLocalStorage(\n functionName: string,\n): ContextLocalStorage<Record<string, unknown>> {\n if (globalConfigMutationInProgress) {\n throw new ConfigError(\n `${functionName} cannot be called while LogTape is being reconfigured.`,\n );\n }\n if (currentConfig == null) {\n throw new ConfigError(\n `${functionName} requires LogTape to be configured first.`,\n );\n }\n const contextLocalStorage = LoggerImpl.getLogger().contextLocalStorage;\n if (contextLocalStorage == null) {\n throw new ConfigError(\n `${functionName} requires Config.contextLocalStorage to be configured.`,\n );\n }\n return contextLocalStorage;\n}\n\nasync function runGlobalConfigMutation<T>(\n functionName: string,\n callback: () => Promise<T>,\n): Promise<T> {\n assertCanMutateGlobalConfig(functionName);\n globalConfigMutationInProgress = true;\n try {\n return await callback();\n } finally {\n globalConfigMutationInProgress = false;\n }\n}\n\nfunction runGlobalConfigMutationSync<T>(\n functionName: string,\n callback: () => T,\n): T {\n assertCanMutateGlobalConfig(functionName);\n globalConfigMutationInProgress = true;\n try {\n return callback();\n } finally {\n globalConfigMutationInProgress = false;\n }\n}\n\nfunction assertCanMutateGlobalConfig(functionName: string): void {\n if (globalConfigMutationInProgress) {\n throw new ConfigError(\n `${functionName} cannot be called while LogTape is being reconfigured.`,\n );\n }\n assertNoScopedConfig(functionName);\n}\n\nfunction assertNoScopedConfig(functionName: string): void {\n if (activeScopedConfigCount > 0) {\n throw new ConfigError(\n `${functionName} cannot be called while a scoped configuration is ` +\n \"active. Use nested withConfig() instead.\",\n );\n }\n}\n\nfunction disposeSyncFilters(): void {\n disposeSyncDisposables(filterDisposables);\n}\n\nfunction disposeSyncSinks(): void {\n disposeSyncDisposables(sinkDisposables);\n}\n\nfunction disposeSyncDisposables(disposables: Set<Disposable>): void {\n const errors: unknown[] = [];\n try {\n for (const disposable of disposables) {\n try {\n disposable[Symbol.dispose]();\n } catch (error) {\n errors.push(error);\n } finally {\n disposables.delete(disposable);\n }\n }\n } finally {\n disposables.clear();\n }\n throwDisposeErrors(errors);\n}\n\nasync function disposeAsyncFilters(): Promise<void> {\n await disposeAsyncDisposables(asyncFilterDisposables);\n}\n\nasync function disposeAsyncSinks(): Promise<void> {\n await disposeAsyncDisposables(asyncSinkDisposables);\n}\n\nasync function disposeAsyncDisposables(\n disposables: Set<AsyncDisposable>,\n): Promise<void> {\n const promises: PromiseLike<void>[] = [];\n try {\n for (const disposable of disposables) {\n try {\n promises.push(Promise.resolve(disposable[Symbol.asyncDispose]()));\n } catch (error) {\n promises.push(Promise.reject(error));\n } finally {\n disposables.delete(disposable);\n }\n }\n } finally {\n disposables.clear();\n }\n await settleDisposePromises(promises);\n}\n\nasync function settleDisposePromises(\n promises: readonly PromiseLike<void>[],\n): Promise<void> {\n const results = await Promise.allSettled(promises);\n throwDisposeErrors(\n results\n .filter((result): result is PromiseRejectedResult =>\n result.status === \"rejected\"\n )\n .map((result) => result.reason),\n );\n}\n\nfunction throwDisposeErrors(errors: readonly unknown[]): void {\n if (errors.length < 1) return;\n if (errors.length === 1) throw errors[0];\n throw new AggregateError(\n errors,\n \"Multiple errors occurred while disposing LogTape resources.\",\n );\n}\n\n/**\n * A configuration error.\n */\nexport class ConfigError extends Error {\n /**\n * Constructs a new configuration error.\n * @param message The error message.\n */\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":["currentConfig: Config<string, string> | null","strongRefs: Set<LoggerImpl>","filterDisposables: Set<Disposable>","sinkDisposables: Set<Disposable>","asyncFilterDisposables: Set<AsyncDisposable>","asyncSinkDisposables: Set<AsyncDisposable>","cfg: LoggerConfig<TSinkId, TFilterId>","allowAsync: boolean","config: Config<TSinkId, TFilterId>","config: ScopedConfig<TSinkId, TFilterId>","callback: () => TResult","result: Awaited<TResult>","callbackError: unknown","callback: () => SyncCallbackResult<TResult>","result: SyncCallbackResult<TResult>","value: unknown","errors: unknown[]","functionName: string","callback: () => Promise<T>","callback: () => T","disposables: Set<Disposable>","disposables: Set<AsyncDisposable>","promises: PromiseLike<void>[]","promises: readonly PromiseLike<void>[]","errors: readonly unknown[]","message: string"],"sources":["../src/config.ts"],"sourcesContent":["import type { ContextLocalStorage } from \"./context.ts\";\nimport { type FilterLike, toFilter } from \"./filter.ts\";\nimport type { LogLevel } from \"./level.ts\";\nimport { LoggerImpl } from \"./logger.ts\";\nimport {\n compileScopedConfig,\n disposeScopedConfig,\n disposeScopedConfigSync,\n runWithScopedConfig,\n type ScopedConfigLike,\n throwCombinedErrors,\n} from \"./scoped-config.ts\";\nimport { getConsoleSink, type Sink } from \"./sink.ts\";\n\n/**\n * A configuration for the loggers.\n */\nexport interface Config<TSinkId extends string, TFilterId extends string> {\n /**\n * The sinks to use. The keys are the sink identifiers, and the values are\n * {@link Sink}s.\n */\n sinks: Record<TSinkId, Sink>;\n /**\n * The filters to use. The keys are the filter identifiers, and the values\n * are either {@link Filter}s or {@link LogLevel}s.\n */\n filters?: Record<TFilterId, FilterLike>;\n\n /**\n * The loggers to configure.\n */\n loggers: LoggerConfig<TSinkId, TFilterId>[];\n\n /**\n * The context-local storage to use for implicit contexts.\n * @since 0.7.0\n */\n contextLocalStorage?: ContextLocalStorage<Record<string, unknown>>;\n\n /**\n * Whether to reset the configuration before applying this one.\n */\n reset?: boolean;\n}\n\n/**\n * A scoped configuration for the current execution context.\n *\n * Unlike {@link Config}, this type does not include `reset` or\n * `contextLocalStorage`. Scoped configuration changes only the logging policy\n * for a callback; the context-local storage must already be initialized by the\n * process-global configuration.\n *\n * @since 2.3.0\n */\nexport type ScopedConfig<TSinkId extends string, TFilterId extends string> =\n ScopedConfigLike<TSinkId, TFilterId>;\n\ntype SyncCallbackResult<TResult> = TResult extends PromiseLike<unknown> ? never\n : TResult;\n\n/**\n * A logger configuration.\n */\nexport interface LoggerConfig<\n TSinkId extends string,\n TFilterId extends string,\n> {\n /**\n * The category of the logger. If a string, it is equivalent to an array\n * with one element.\n */\n category: string | string[];\n\n /**\n * The sink identifiers to use.\n */\n sinks?: TSinkId[];\n\n /**\n * Whether to inherit the parent's sinks. If `inherit`, the parent's sinks\n * are used along with the specified sinks. If `override`, the parent's\n * sinks are not used, and only the specified sinks are used.\n *\n * The default is `inherit`.\n * @default `\"inherit\"\n * @since 0.6.0\n */\n parentSinks?: \"inherit\" | \"override\";\n\n /**\n * The filter identifiers to use.\n */\n filters?: TFilterId[];\n\n /**\n * The lowest log level to accept. If `null`, the logger will reject all\n * records.\n * @since 0.8.0\n */\n lowestLevel?: LogLevel | null;\n}\n\n/**\n * The current configuration, if any. Otherwise, `null`.\n */\nlet currentConfig: Config<string, string> | null = null;\nlet activeScopedConfigCount = 0;\nlet globalConfigMutationInProgress = false;\n\n/**\n * Strong references to the loggers.\n * This is to prevent the loggers from being garbage collected so that their\n * sinks and filters are not removed.\n */\nconst strongRefs: Set<LoggerImpl> = new Set();\n\n/**\n * Sync filter disposables to dispose when resetting the configuration.\n */\nconst filterDisposables: Set<Disposable> = new Set();\n\n/**\n * Sync sink disposables to dispose when resetting the configuration.\n */\nconst sinkDisposables: Set<Disposable> = new Set();\n\n/**\n * Async filter disposables to dispose when resetting the configuration.\n */\nconst asyncFilterDisposables: Set<AsyncDisposable> = new Set();\n\n/**\n * Async sink disposables to dispose when resetting the configuration.\n */\nconst asyncSinkDisposables: Set<AsyncDisposable> = new Set();\n\n/**\n * Check if a config is for the meta logger.\n */\nfunction isLoggerConfigMeta<TSinkId extends string, TFilterId extends string>(\n cfg: LoggerConfig<TSinkId, TFilterId>,\n): boolean {\n const category = Array.isArray(cfg.category) ? cfg.category : [cfg.category];\n return category.length === 0 ||\n (category.length === 1 && category[0] === \"logtape\") ||\n (category.length === 2 &&\n category[0] === \"logtape\" &&\n category[1] === \"meta\");\n}\n\nfunction registerDisposeHook(allowAsync: boolean): void {\n const handler = allowAsync ? disposeInternal : disposeSyncInternal;\n\n if (\n // deno-lint-ignore no-explicit-any\n typeof (globalThis as any).EdgeRuntime !== \"string\" &&\n \"process\" in globalThis &&\n !(\"Deno\" in globalThis)\n ) {\n // deno-lint-ignore no-explicit-any\n const proc = (globalThis as any).process;\n // Use bracket notation to avoid static analysis detection in Edge Runtime.\n const onMethod = proc?.[\"on\"];\n if (typeof onMethod === \"function\") {\n onMethod.call(proc, \"exit\", handler);\n return;\n }\n }\n\n // Some edge runtimes expose neither process.on() nor addEventListener().\n // In those environments users can still call dispose()/disposeSync() manually.\n // deno-lint-ignore no-explicit-any\n const addEventListenerMethod = (globalThis as any).addEventListener;\n if (typeof addEventListenerMethod !== \"function\") return;\n\n if (\"Deno\" in globalThis) {\n addEventListenerMethod.call(globalThis, \"unload\", handler);\n } else {\n addEventListenerMethod.call(globalThis, \"pagehide\", handler);\n }\n}\n\n/**\n * Configure the loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * @example\n * ```typescript\n * await configure({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * filters: {\n * slow: (log) =>\n * \"duration\" in log.properties &&\n * log.properties.duration as number > 1000,\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: [\"my-app\", \"sql\"],\n * filters: [\"slow\"],\n * lowestLevel: \"debug\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n */\nexport async function configure<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>): Promise<void> {\n await runGlobalConfigMutation(\"configure()\", async () => {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n await disposeInternal();\n resetInternal();\n try {\n configureInternal(config, true);\n } catch (e) {\n if (e instanceof ConfigError) {\n await disposeInternal();\n resetInternal();\n }\n throw e;\n }\n });\n}\n\n/**\n * Configure sync loggers with the specified configuration.\n *\n * Note that if the given sinks or filters are disposable, they will be\n * disposed when the configuration is reset, or when the process exits.\n *\n * Also note that passing async sinks or filters will throw. If\n * necessary use {@link resetSync} or {@link disposeSync}.\n *\n * @example\n * ```typescript\n * configureSync({\n * sinks: {\n * console: getConsoleSink(),\n * },\n * loggers: [\n * {\n * category: \"my-app\",\n * sinks: [\"console\"],\n * lowestLevel: \"info\",\n * },\n * {\n * category: \"logtape\",\n * sinks: [\"console\"],\n * lowestLevel: \"error\",\n * },\n * ],\n * });\n * ```\n *\n * @param config The configuration.\n * @since 0.9.0\n */\nexport function configureSync<TSinkId extends string, TFilterId extends string>(\n config: Config<TSinkId, TFilterId>,\n): void {\n runGlobalConfigMutationSync(\"configureSync()\", () => {\n if (currentConfig != null && !config.reset) {\n throw new ConfigError(\n \"Already configured; if you want to reset, turn on the reset flag.\",\n );\n }\n if (asyncFilterDisposables.size > 0 || asyncSinkDisposables.size > 0) {\n throw new ConfigError(\n \"Previously configured async disposables are still active. \" +\n \"Use configure() instead or explicitly dispose them using dispose().\",\n );\n }\n disposeSyncInternal();\n resetInternal();\n try {\n configureInternal(config, false);\n } catch (e) {\n if (e instanceof ConfigError) {\n disposeSyncInternal();\n resetInternal();\n }\n throw e;\n }\n });\n}\n\n/**\n * Runs a callback with a LogTape configuration scoped to the current execution\n * context.\n *\n * The process-global configuration must already provide\n * `contextLocalStorage`. The scoped configuration fully overrides global\n * logger routing while the callback and its async work run.\n *\n * @param config The scoped configuration.\n * @param callback The callback to run.\n * @returns The callback's resolved return value.\n * @since 2.3.0\n */\nexport async function withConfig<\n TSinkId extends string,\n TFilterId extends string,\n TResult,\n>(\n config: ScopedConfig<TSinkId, TFilterId>,\n callback: () => TResult,\n): Promise<Awaited<TResult>> {\n const contextLocalStorage = getConfiguredContextLocalStorage(\"withConfig()\");\n const scopedConfig = compileScopedConfig(\n config,\n true,\n (message) => new ConfigError(message),\n );\n\n let result: Awaited<TResult>;\n let callbackError: unknown;\n let callbackFailed = false;\n activeScopedConfigCount++;\n try {\n result = await runWithScopedConfig(\n contextLocalStorage,\n scopedConfig,\n callback,\n );\n } catch (error) {\n callbackFailed = true;\n callbackError = error;\n }\n\n try {\n await disposeScopedConfig(scopedConfig, getGlobalDisposables());\n } catch (disposeError) {\n if (callbackFailed) {\n throwCombinedErrors(callbackError, disposeError);\n }\n throw disposeError;\n } finally {\n activeScopedConfigCount--;\n }\n\n if (callbackFailed) throw callbackError;\n return result!;\n}\n\n/**\n * Runs a synchronous callback with a LogTape configuration scoped to the\n * current execution context.\n *\n * The scoped configuration must not contain async disposable sinks or filters.\n *\n * @param config The scoped configuration.\n * @param callback The callback to run.\n * @returns The callback's return value.\n * @since 2.3.0\n */\nexport function withConfigSync<\n TSinkId extends string,\n TFilterId extends string,\n TResult,\n>(\n config: ScopedConfig<TSinkId, TFilterId>,\n callback: () => SyncCallbackResult<TResult>,\n): SyncCallbackResult<TResult> {\n const contextLocalStorage = getConfiguredContextLocalStorage(\n \"withConfigSync()\",\n );\n const scopedConfig = compileScopedConfig(\n config,\n false,\n (message) => new ConfigError(message),\n );\n\n let result: SyncCallbackResult<TResult>;\n let callbackError: unknown;\n let callbackFailed = false;\n activeScopedConfigCount++;\n try {\n result = runWithScopedConfig(contextLocalStorage, scopedConfig, callback);\n if (isThenable(result)) {\n void Promise.resolve(result).catch(() => {});\n callbackFailed = true;\n callbackError = new ConfigError(\n \"withConfigSync() callback must not return a promise. \" +\n \"Use withConfig() for async callbacks.\",\n );\n }\n } catch (error) {\n callbackFailed = true;\n callbackError = error;\n }\n\n try {\n disposeScopedConfigSync(scopedConfig, getGlobalDisposables());\n } catch (disposeError) {\n if (callbackFailed) {\n throwCombinedErrors(callbackError, disposeError);\n }\n throw disposeError;\n } finally {\n activeScopedConfigCount--;\n }\n\n if (callbackFailed) throw callbackError;\n return result!;\n}\n\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n return value != null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n \"then\" in value &&\n typeof value.then === \"function\";\n}\n\nfunction getGlobalDisposables(): ReadonlySet<Disposable | AsyncDisposable> {\n return new Set<Disposable | AsyncDisposable>([\n ...filterDisposables,\n ...asyncFilterDisposables,\n ...sinkDisposables,\n ...asyncSinkDisposables,\n ]);\n}\n\nfunction configureInternal<\n TSinkId extends string,\n TFilterId extends string,\n>(config: Config<TSinkId, TFilterId>, allowAsync: boolean): void {\n currentConfig = config;\n\n let metaConfigured = false;\n const configuredCategories = new Set<string>();\n\n for (const cfg of config.loggers) {\n if (isLoggerConfigMeta(cfg)) {\n metaConfigured = true;\n }\n\n // Check for duplicate logger categories\n const categoryKey = Array.isArray(cfg.category)\n ? JSON.stringify(cfg.category)\n : JSON.stringify([cfg.category]);\n if (configuredCategories.has(categoryKey)) {\n throw new ConfigError(\n `Duplicate logger configuration for category: ${categoryKey}. ` +\n `Each category can only be configured once.`,\n );\n }\n configuredCategories.add(categoryKey);\n\n const logger = LoggerImpl.getLogger(cfg.category);\n for (const sinkId of cfg.sinks ?? []) {\n const sink = config.sinks[sinkId];\n if (!sink) {\n throw new ConfigError(`Sink not found: ${sinkId}.`);\n }\n logger.sinks.push(sink);\n }\n logger.parentSinks = cfg.parentSinks ?? \"inherit\";\n if (cfg.lowestLevel !== undefined) {\n logger.lowestLevel = cfg.lowestLevel;\n }\n for (const filterId of cfg.filters ?? []) {\n const filter = config.filters?.[filterId];\n if (filter === undefined) {\n throw new ConfigError(`Filter not found: ${filterId}.`);\n }\n logger.filters.push(toFilter(filter));\n }\n strongRefs.add(logger);\n }\n\n LoggerImpl.getLogger().contextLocalStorage = config.contextLocalStorage;\n\n for (const sink of Object.values<Sink>(config.sinks)) {\n if (Symbol.asyncDispose in sink) {\n if (allowAsync) asyncSinkDisposables.add(sink as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n }\n if (Symbol.dispose in sink) sinkDisposables.add(sink as Disposable);\n }\n\n for (const filter of Object.values<FilterLike>(config.filters ?? {})) {\n if (filter == null || typeof filter === \"string\") continue;\n if (Symbol.asyncDispose in filter) {\n if (allowAsync) asyncFilterDisposables.add(filter as AsyncDisposable);\n else {\n throw new ConfigError(\n \"Async disposables cannot be used with configureSync().\",\n );\n }\n asyncSinkDisposables.delete(filter as AsyncDisposable);\n }\n if (Symbol.dispose in filter) {\n filterDisposables.add(filter as Disposable);\n sinkDisposables.delete(filter as Disposable);\n }\n }\n\n registerDisposeHook(allowAsync);\n const meta = LoggerImpl.getLogger([\"logtape\", \"meta\"]);\n if (!metaConfigured) {\n meta.sinks.push(getConsoleSink());\n }\n\n meta.info(\n \"LogTape loggers are configured. Note that LogTape itself uses the meta \" +\n \"logger, which has category {metaLoggerCategory}. The meta logger is \" +\n \"used to log internal diagnostics such as sink exceptions. \" +\n \"It's recommended to configure the meta logger with a separate sink \" +\n \"so that you can easily notice if logging itself fails or is \" +\n \"misconfigured. To turn off this message, configure the meta logger \" +\n \"with higher log levels than {dismissLevel}. See also \" +\n \"<https://logtape.org/manual/categories#meta-logger>.\",\n { metaLoggerCategory: [\"logtape\", \"meta\"], dismissLevel: \"info\" },\n );\n}\n\n/**\n * Get the current configuration, if any. Otherwise, `null`.\n * @returns The current configuration, if any. Otherwise, `null`.\n */\nexport function getConfig(): Config<string, string> | null {\n return currentConfig;\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes.\n */\nexport async function reset(): Promise<void> {\n await runGlobalConfigMutation(\"reset()\", async () => {\n await disposeInternal();\n resetInternal();\n });\n}\n\n/**\n * Reset the configuration. Mostly for testing purposes. Will not clear async\n * sinks, only use with sync sinks. Use {@link reset} if you have async sinks.\n * @since 0.9.0\n */\nexport function resetSync(): void {\n runGlobalConfigMutationSync(\"resetSync()\", () => {\n disposeSyncInternal();\n resetInternal();\n });\n}\n\nfunction resetInternal(): void {\n const rootLogger = LoggerImpl.getLogger([]);\n rootLogger.resetDescendants();\n delete rootLogger.contextLocalStorage;\n strongRefs.clear();\n currentConfig = null;\n}\n\n/**\n * Dispose of the disposables.\n */\nexport async function dispose(): Promise<void> {\n await runGlobalConfigMutation(\"dispose()\", disposeInternal);\n}\n\nasync function disposeInternal(): Promise<void> {\n const errors: unknown[] = [];\n try {\n disposeSyncFilters();\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncFilters();\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncSinks();\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncSinks();\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\n/**\n * Dispose of the sync disposables. Async disposables will be untouched,\n * use {@link dispose} if you have async sinks.\n * @since 0.9.0\n */\nexport function disposeSync(): void {\n runGlobalConfigMutationSync(\"disposeSync()\", disposeSyncInternal);\n}\n\nfunction disposeSyncInternal(): void {\n const errors: unknown[] = [];\n try {\n disposeSyncFilters();\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncSinks();\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\nfunction getConfiguredContextLocalStorage(\n functionName: string,\n): ContextLocalStorage<Record<string, unknown>> {\n if (globalConfigMutationInProgress) {\n throw new ConfigError(\n `${functionName} cannot be called while LogTape is being reconfigured.`,\n );\n }\n if (currentConfig == null) {\n throw new ConfigError(\n `${functionName} requires LogTape to be configured first.`,\n );\n }\n const contextLocalStorage = LoggerImpl.getLogger().contextLocalStorage;\n if (contextLocalStorage == null) {\n throw new ConfigError(\n `${functionName} requires Config.contextLocalStorage to be configured.`,\n );\n }\n return contextLocalStorage;\n}\n\nasync function runGlobalConfigMutation<T>(\n functionName: string,\n callback: () => Promise<T>,\n): Promise<T> {\n assertCanMutateGlobalConfig(functionName);\n globalConfigMutationInProgress = true;\n try {\n return await callback();\n } finally {\n globalConfigMutationInProgress = false;\n }\n}\n\nfunction runGlobalConfigMutationSync<T>(\n functionName: string,\n callback: () => T,\n): T {\n assertCanMutateGlobalConfig(functionName);\n globalConfigMutationInProgress = true;\n try {\n return callback();\n } finally {\n globalConfigMutationInProgress = false;\n }\n}\n\nfunction assertCanMutateGlobalConfig(functionName: string): void {\n if (globalConfigMutationInProgress) {\n throw new ConfigError(\n `${functionName} cannot be called while LogTape is being reconfigured.`,\n );\n }\n assertNoScopedConfig(functionName);\n}\n\nfunction assertNoScopedConfig(functionName: string): void {\n if (activeScopedConfigCount > 0) {\n throw new ConfigError(\n `${functionName} cannot be called while a scoped configuration is ` +\n \"active. Use nested withConfig() instead.\",\n );\n }\n}\n\nfunction disposeSyncFilters(): void {\n disposeSyncDisposables(filterDisposables);\n}\n\nfunction disposeSyncSinks(): void {\n disposeSyncDisposables(sinkDisposables);\n}\n\nfunction disposeSyncDisposables(disposables: Set<Disposable>): void {\n const errors: unknown[] = [];\n try {\n for (const disposable of disposables) {\n try {\n disposable[Symbol.dispose]();\n } catch (error) {\n errors.push(error);\n } finally {\n disposables.delete(disposable);\n }\n }\n } finally {\n disposables.clear();\n }\n throwDisposeErrors(errors);\n}\n\nasync function disposeAsyncFilters(): Promise<void> {\n await disposeAsyncDisposables(asyncFilterDisposables);\n}\n\nasync function disposeAsyncSinks(): Promise<void> {\n await disposeAsyncDisposables(asyncSinkDisposables);\n}\n\nasync function disposeAsyncDisposables(\n disposables: Set<AsyncDisposable>,\n): Promise<void> {\n const promises: PromiseLike<void>[] = [];\n try {\n for (const disposable of disposables) {\n try {\n promises.push(Promise.resolve(disposable[Symbol.asyncDispose]()));\n } catch (error) {\n promises.push(Promise.reject(error));\n } finally {\n disposables.delete(disposable);\n }\n }\n } finally {\n disposables.clear();\n }\n await settleDisposePromises(promises);\n}\n\nasync function settleDisposePromises(\n promises: readonly PromiseLike<void>[],\n): Promise<void> {\n const results = await Promise.allSettled(promises);\n throwDisposeErrors(\n results\n .filter((result): result is PromiseRejectedResult =>\n result.status === \"rejected\"\n )\n .map((result) => result.reason),\n );\n}\n\nfunction throwDisposeErrors(errors: readonly unknown[]): void {\n if (errors.length < 1) return;\n if (errors.length === 1) throw errors[0];\n throw new AggregateError(\n errors,\n \"Multiple errors occurred while disposing LogTape resources.\",\n );\n}\n\n/**\n * A configuration error.\n */\nexport class ConfigError extends Error {\n /**\n * Constructs a new configuration error.\n * @param message The error message.\n */\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n"],"mappings":"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"}
@@ -10,6 +10,9 @@ const defaultScopedLogger = {
10
10
  sinks: []
11
11
  };
12
12
  function compileScopedConfig(config, allowAsync, createError) {
13
+ if (!isObjectLike(config)) throw createError("Configuration must be an object.");
14
+ if (!isObjectLike(config.sinks)) throw createError("Configuration must include a sinks object.");
15
+ if (!Array.isArray(config.loggers)) throw createError("Configuration must include a loggers array.");
13
16
  const nodes = /* @__PURE__ */ new Map();
14
17
  const configuredCategories = /* @__PURE__ */ new Set();
15
18
  for (const logger of config.loggers) {
@@ -18,15 +21,19 @@ function compileScopedConfig(config, allowAsync, createError) {
18
21
  if (configuredCategories.has(key)) throw createError(`Duplicate logger configuration for category: ${key}. Each category can only be configured once.`);
19
22
  configuredCategories.add(key);
20
23
  const sinks = [];
21
- for (const sinkId of logger.sinks ?? []) {
24
+ const sinkIds = logger.sinks ?? [];
25
+ for (const sinkId of sinkIds) {
22
26
  const sink = config.sinks[sinkId];
23
27
  if (!sink) throw createError(`Sink not found: ${sinkId}.`);
28
+ if (typeof sink !== "function") throw createError(`Sink must be a function: ${sinkId}.`);
24
29
  sinks.push(sink);
25
30
  }
26
31
  const filters = [];
27
- for (const filterId of logger.filters ?? []) {
32
+ const filterIds = logger.filters ?? [];
33
+ for (const filterId of filterIds) {
28
34
  const filter = config.filters?.[filterId];
29
35
  if (filter === void 0) throw createError(`Filter not found: ${filterId}.`);
36
+ if (!isFilterLike(filter)) throw createError(`Filter must be a function, log level, or null: ${filterId}.`);
30
37
  filters.push(require_filter.toFilter(filter));
31
38
  }
32
39
  nodes.set(key, {
@@ -41,6 +48,7 @@ function compileScopedConfig(config, allowAsync, createError) {
41
48
  const syncSinks = /* @__PURE__ */ new Set();
42
49
  const asyncSinks = /* @__PURE__ */ new Set();
43
50
  for (const sink of Object.values(config.sinks)) {
51
+ if (!isObjectLike(sink)) continue;
44
52
  if (Symbol.asyncDispose in sink) {
45
53
  if (!allowAsync) throw createError("Async disposables cannot be used with withConfigSync().");
46
54
  asyncSinks.add(sink);
@@ -48,7 +56,7 @@ function compileScopedConfig(config, allowAsync, createError) {
48
56
  if (Symbol.dispose in sink) syncSinks.add(sink);
49
57
  }
50
58
  for (const filter of Object.values(config.filters ?? {})) {
51
- if (filter == null || typeof filter === "string") continue;
59
+ if (!isObjectLike(filter)) continue;
52
60
  if (Symbol.asyncDispose in filter) {
53
61
  if (!allowAsync) throw createError("Async disposables cannot be used with withConfigSync().");
54
62
  asyncFilters.add(filter);
@@ -62,6 +70,7 @@ function compileScopedConfig(config, allowAsync, createError) {
62
70
  return {
63
71
  asyncFilters,
64
72
  asyncSinks,
73
+ dispatchCache: /* @__PURE__ */ new Map(),
65
74
  disposed: false,
66
75
  nodes,
67
76
  parent: void 0,
@@ -98,41 +107,43 @@ function emitWithScopedConfig(scopedConfig, record, bypassSinks, emitToSink) {
98
107
  emitToSink(sink, bypassSinks);
99
108
  }
100
109
  }
101
- async function disposeScopedConfig(scopedConfig) {
110
+ async function disposeScopedConfig(scopedConfig, retainedDisposables) {
111
+ const parentDisposables = getParentScopedDisposables(scopedConfig, retainedDisposables);
102
112
  scopedConfig.disposed = true;
103
113
  const errors = [];
104
114
  try {
105
- disposeSyncDisposables(scopedConfig.syncFilters);
115
+ disposeSyncDisposables(scopedConfig.syncFilters, parentDisposables);
106
116
  } catch (error) {
107
117
  errors.push(error);
108
118
  }
109
119
  try {
110
- await disposeAsyncDisposables(scopedConfig.asyncFilters);
120
+ await disposeAsyncDisposables(scopedConfig.asyncFilters, parentDisposables);
111
121
  } catch (error) {
112
122
  errors.push(error);
113
123
  }
114
124
  try {
115
- disposeSyncDisposables(scopedConfig.syncSinks);
125
+ disposeSyncDisposables(scopedConfig.syncSinks, parentDisposables);
116
126
  } catch (error) {
117
127
  errors.push(error);
118
128
  }
119
129
  try {
120
- await disposeAsyncDisposables(scopedConfig.asyncSinks);
130
+ await disposeAsyncDisposables(scopedConfig.asyncSinks, parentDisposables);
121
131
  } catch (error) {
122
132
  errors.push(error);
123
133
  }
124
134
  throwDisposeErrors(errors);
125
135
  }
126
- function disposeScopedConfigSync(scopedConfig) {
136
+ function disposeScopedConfigSync(scopedConfig, retainedDisposables) {
137
+ const parentDisposables = getParentScopedDisposables(scopedConfig, retainedDisposables);
127
138
  scopedConfig.disposed = true;
128
139
  const errors = [];
129
140
  try {
130
- disposeSyncDisposables(scopedConfig.syncFilters);
141
+ disposeSyncDisposables(scopedConfig.syncFilters, parentDisposables);
131
142
  } catch (error) {
132
143
  errors.push(error);
133
144
  }
134
145
  try {
135
- disposeSyncDisposables(scopedConfig.syncSinks);
146
+ disposeSyncDisposables(scopedConfig.syncSinks, parentDisposables);
136
147
  } catch (error) {
137
148
  errors.push(error);
138
149
  }
@@ -144,11 +155,34 @@ function throwCombinedErrors(primary, secondary) {
144
155
  function isCompiledScopedConfig(value) {
145
156
  return value != null && typeof value === "object" && "nodes" in value;
146
157
  }
158
+ function isObjectLike(value) {
159
+ return value != null && (typeof value === "object" || typeof value === "function");
160
+ }
161
+ function isFilterLike(value) {
162
+ return value == null || typeof value === "function" || typeof value === "string" && require_level.isLogLevel(value);
163
+ }
147
164
  function getActiveScopedConfig(scopedConfig) {
148
165
  let activeConfig = scopedConfig;
149
166
  while (activeConfig?.disposed) activeConfig = activeConfig.parent;
150
167
  return activeConfig;
151
168
  }
169
+ function getParentScopedDisposables(scopedConfig, extraRetained) {
170
+ const disposables = new Set(extraRetained);
171
+ let parent = scopedConfig.parent;
172
+ while (parent != null) {
173
+ const activeParent = getActiveScopedConfig(parent);
174
+ if (activeParent == null) break;
175
+ addScopedDisposables(disposables, activeParent);
176
+ parent = activeParent.parent;
177
+ }
178
+ return disposables;
179
+ }
180
+ function addScopedDisposables(disposables, scopedConfig) {
181
+ for (const disposable of scopedConfig.syncFilters) disposables.add(disposable);
182
+ for (const disposable of scopedConfig.asyncFilters) disposables.add(disposable);
183
+ for (const disposable of scopedConfig.syncSinks) disposables.add(disposable);
184
+ for (const disposable of scopedConfig.asyncSinks) disposables.add(disposable);
185
+ }
152
186
  function normalizeCategory(category) {
153
187
  return typeof category === "string" ? [category] : [...category];
154
188
  }
@@ -156,7 +190,13 @@ function categoryKey(category) {
156
190
  return JSON.stringify(category);
157
191
  }
158
192
  function getScopedSinkDispatchPlan(scopedConfig, category, level) {
159
- return getScopedSinkDispatchPlanForPrefix(scopedConfig, category, category.length, level);
193
+ const cacheKey = `${categoryKey(category)}:${level}`;
194
+ let plan = scopedConfig.dispatchCache.get(cacheKey);
195
+ if (plan == null) {
196
+ plan = getScopedSinkDispatchPlanForPrefix(scopedConfig, category, category.length, level);
197
+ scopedConfig.dispatchCache.set(cacheKey, plan);
198
+ }
199
+ return plan;
160
200
  }
161
201
  function getScopedSinkDispatchPlanForPrefix(scopedConfig, category, length, level) {
162
202
  const prefix = category.slice(0, length);
@@ -191,18 +231,33 @@ function filterScopedRecord(scopedConfig, category, record) {
191
231
  }
192
232
  return true;
193
233
  }
194
- function disposeSyncDisposables(disposables) {
234
+ function disposeSyncDisposables(disposables, retainedDisposables) {
195
235
  const errors = [];
196
- for (const disposable of disposables) try {
197
- disposable[Symbol.dispose]();
198
- } catch (error) {
199
- errors.push(error);
236
+ try {
237
+ for (const disposable of disposables) try {
238
+ if (!retainedDisposables.has(disposable)) disposable[Symbol.dispose]();
239
+ } catch (error) {
240
+ errors.push(error);
241
+ } finally {
242
+ disposables.delete(disposable);
243
+ }
244
+ } finally {
245
+ disposables.clear();
200
246
  }
201
247
  throwDisposeErrors(errors);
202
248
  }
203
- async function disposeAsyncDisposables(disposables) {
204
- const results = await Promise.allSettled(Array.from(disposables, (disposable) => Promise.resolve().then(() => disposable[Symbol.asyncDispose]())));
205
- throwDisposeErrors(results.filter((result) => result.status === "rejected").map((result) => result.reason));
249
+ async function disposeAsyncDisposables(disposables, retainedDisposables) {
250
+ const disposableList = filterRetainedDisposables(disposables, retainedDisposables);
251
+ for (const disposable of disposableList) disposables.delete(disposable);
252
+ try {
253
+ const results = await Promise.allSettled(disposableList.map((disposable) => Promise.resolve().then(() => disposable[Symbol.asyncDispose]())));
254
+ throwDisposeErrors(results.filter((result) => result.status === "rejected").map((result) => result.reason));
255
+ } finally {
256
+ disposables.clear();
257
+ }
258
+ }
259
+ function filterRetainedDisposables(disposables, retainedDisposables) {
260
+ return Array.from(disposables).filter((disposable) => !retainedDisposables.has(disposable));
206
261
  }
207
262
  function throwDisposeErrors(errors) {
208
263
  if (errors.length < 1) return;
@@ -1,5 +1,5 @@
1
1
  import { toFilter } from "./filter.js";
2
- import { compareLogLevel } from "./level.js";
2
+ import { compareLogLevel, isLogLevel } from "./level.js";
3
3
 
4
4
  //#region src/scoped-config.ts
5
5
  const scopedConfigSymbol = Symbol.for("logtape.scopedConfig");
@@ -10,6 +10,9 @@ const defaultScopedLogger = {
10
10
  sinks: []
11
11
  };
12
12
  function compileScopedConfig(config, allowAsync, createError) {
13
+ if (!isObjectLike(config)) throw createError("Configuration must be an object.");
14
+ if (!isObjectLike(config.sinks)) throw createError("Configuration must include a sinks object.");
15
+ if (!Array.isArray(config.loggers)) throw createError("Configuration must include a loggers array.");
13
16
  const nodes = /* @__PURE__ */ new Map();
14
17
  const configuredCategories = /* @__PURE__ */ new Set();
15
18
  for (const logger of config.loggers) {
@@ -18,15 +21,19 @@ function compileScopedConfig(config, allowAsync, createError) {
18
21
  if (configuredCategories.has(key)) throw createError(`Duplicate logger configuration for category: ${key}. Each category can only be configured once.`);
19
22
  configuredCategories.add(key);
20
23
  const sinks = [];
21
- for (const sinkId of logger.sinks ?? []) {
24
+ const sinkIds = logger.sinks ?? [];
25
+ for (const sinkId of sinkIds) {
22
26
  const sink = config.sinks[sinkId];
23
27
  if (!sink) throw createError(`Sink not found: ${sinkId}.`);
28
+ if (typeof sink !== "function") throw createError(`Sink must be a function: ${sinkId}.`);
24
29
  sinks.push(sink);
25
30
  }
26
31
  const filters = [];
27
- for (const filterId of logger.filters ?? []) {
32
+ const filterIds = logger.filters ?? [];
33
+ for (const filterId of filterIds) {
28
34
  const filter = config.filters?.[filterId];
29
35
  if (filter === void 0) throw createError(`Filter not found: ${filterId}.`);
36
+ if (!isFilterLike(filter)) throw createError(`Filter must be a function, log level, or null: ${filterId}.`);
30
37
  filters.push(toFilter(filter));
31
38
  }
32
39
  nodes.set(key, {
@@ -41,6 +48,7 @@ function compileScopedConfig(config, allowAsync, createError) {
41
48
  const syncSinks = /* @__PURE__ */ new Set();
42
49
  const asyncSinks = /* @__PURE__ */ new Set();
43
50
  for (const sink of Object.values(config.sinks)) {
51
+ if (!isObjectLike(sink)) continue;
44
52
  if (Symbol.asyncDispose in sink) {
45
53
  if (!allowAsync) throw createError("Async disposables cannot be used with withConfigSync().");
46
54
  asyncSinks.add(sink);
@@ -48,7 +56,7 @@ function compileScopedConfig(config, allowAsync, createError) {
48
56
  if (Symbol.dispose in sink) syncSinks.add(sink);
49
57
  }
50
58
  for (const filter of Object.values(config.filters ?? {})) {
51
- if (filter == null || typeof filter === "string") continue;
59
+ if (!isObjectLike(filter)) continue;
52
60
  if (Symbol.asyncDispose in filter) {
53
61
  if (!allowAsync) throw createError("Async disposables cannot be used with withConfigSync().");
54
62
  asyncFilters.add(filter);
@@ -62,6 +70,7 @@ function compileScopedConfig(config, allowAsync, createError) {
62
70
  return {
63
71
  asyncFilters,
64
72
  asyncSinks,
73
+ dispatchCache: /* @__PURE__ */ new Map(),
65
74
  disposed: false,
66
75
  nodes,
67
76
  parent: void 0,
@@ -98,41 +107,43 @@ function emitWithScopedConfig(scopedConfig, record, bypassSinks, emitToSink) {
98
107
  emitToSink(sink, bypassSinks);
99
108
  }
100
109
  }
101
- async function disposeScopedConfig(scopedConfig) {
110
+ async function disposeScopedConfig(scopedConfig, retainedDisposables) {
111
+ const parentDisposables = getParentScopedDisposables(scopedConfig, retainedDisposables);
102
112
  scopedConfig.disposed = true;
103
113
  const errors = [];
104
114
  try {
105
- disposeSyncDisposables(scopedConfig.syncFilters);
115
+ disposeSyncDisposables(scopedConfig.syncFilters, parentDisposables);
106
116
  } catch (error) {
107
117
  errors.push(error);
108
118
  }
109
119
  try {
110
- await disposeAsyncDisposables(scopedConfig.asyncFilters);
120
+ await disposeAsyncDisposables(scopedConfig.asyncFilters, parentDisposables);
111
121
  } catch (error) {
112
122
  errors.push(error);
113
123
  }
114
124
  try {
115
- disposeSyncDisposables(scopedConfig.syncSinks);
125
+ disposeSyncDisposables(scopedConfig.syncSinks, parentDisposables);
116
126
  } catch (error) {
117
127
  errors.push(error);
118
128
  }
119
129
  try {
120
- await disposeAsyncDisposables(scopedConfig.asyncSinks);
130
+ await disposeAsyncDisposables(scopedConfig.asyncSinks, parentDisposables);
121
131
  } catch (error) {
122
132
  errors.push(error);
123
133
  }
124
134
  throwDisposeErrors(errors);
125
135
  }
126
- function disposeScopedConfigSync(scopedConfig) {
136
+ function disposeScopedConfigSync(scopedConfig, retainedDisposables) {
137
+ const parentDisposables = getParentScopedDisposables(scopedConfig, retainedDisposables);
127
138
  scopedConfig.disposed = true;
128
139
  const errors = [];
129
140
  try {
130
- disposeSyncDisposables(scopedConfig.syncFilters);
141
+ disposeSyncDisposables(scopedConfig.syncFilters, parentDisposables);
131
142
  } catch (error) {
132
143
  errors.push(error);
133
144
  }
134
145
  try {
135
- disposeSyncDisposables(scopedConfig.syncSinks);
146
+ disposeSyncDisposables(scopedConfig.syncSinks, parentDisposables);
136
147
  } catch (error) {
137
148
  errors.push(error);
138
149
  }
@@ -144,11 +155,34 @@ function throwCombinedErrors(primary, secondary) {
144
155
  function isCompiledScopedConfig(value) {
145
156
  return value != null && typeof value === "object" && "nodes" in value;
146
157
  }
158
+ function isObjectLike(value) {
159
+ return value != null && (typeof value === "object" || typeof value === "function");
160
+ }
161
+ function isFilterLike(value) {
162
+ return value == null || typeof value === "function" || typeof value === "string" && isLogLevel(value);
163
+ }
147
164
  function getActiveScopedConfig(scopedConfig) {
148
165
  let activeConfig = scopedConfig;
149
166
  while (activeConfig?.disposed) activeConfig = activeConfig.parent;
150
167
  return activeConfig;
151
168
  }
169
+ function getParentScopedDisposables(scopedConfig, extraRetained) {
170
+ const disposables = new Set(extraRetained);
171
+ let parent = scopedConfig.parent;
172
+ while (parent != null) {
173
+ const activeParent = getActiveScopedConfig(parent);
174
+ if (activeParent == null) break;
175
+ addScopedDisposables(disposables, activeParent);
176
+ parent = activeParent.parent;
177
+ }
178
+ return disposables;
179
+ }
180
+ function addScopedDisposables(disposables, scopedConfig) {
181
+ for (const disposable of scopedConfig.syncFilters) disposables.add(disposable);
182
+ for (const disposable of scopedConfig.asyncFilters) disposables.add(disposable);
183
+ for (const disposable of scopedConfig.syncSinks) disposables.add(disposable);
184
+ for (const disposable of scopedConfig.asyncSinks) disposables.add(disposable);
185
+ }
152
186
  function normalizeCategory(category) {
153
187
  return typeof category === "string" ? [category] : [...category];
154
188
  }
@@ -156,7 +190,13 @@ function categoryKey(category) {
156
190
  return JSON.stringify(category);
157
191
  }
158
192
  function getScopedSinkDispatchPlan(scopedConfig, category, level) {
159
- return getScopedSinkDispatchPlanForPrefix(scopedConfig, category, category.length, level);
193
+ const cacheKey = `${categoryKey(category)}:${level}`;
194
+ let plan = scopedConfig.dispatchCache.get(cacheKey);
195
+ if (plan == null) {
196
+ plan = getScopedSinkDispatchPlanForPrefix(scopedConfig, category, category.length, level);
197
+ scopedConfig.dispatchCache.set(cacheKey, plan);
198
+ }
199
+ return plan;
160
200
  }
161
201
  function getScopedSinkDispatchPlanForPrefix(scopedConfig, category, length, level) {
162
202
  const prefix = category.slice(0, length);
@@ -191,18 +231,33 @@ function filterScopedRecord(scopedConfig, category, record) {
191
231
  }
192
232
  return true;
193
233
  }
194
- function disposeSyncDisposables(disposables) {
234
+ function disposeSyncDisposables(disposables, retainedDisposables) {
195
235
  const errors = [];
196
- for (const disposable of disposables) try {
197
- disposable[Symbol.dispose]();
198
- } catch (error) {
199
- errors.push(error);
236
+ try {
237
+ for (const disposable of disposables) try {
238
+ if (!retainedDisposables.has(disposable)) disposable[Symbol.dispose]();
239
+ } catch (error) {
240
+ errors.push(error);
241
+ } finally {
242
+ disposables.delete(disposable);
243
+ }
244
+ } finally {
245
+ disposables.clear();
200
246
  }
201
247
  throwDisposeErrors(errors);
202
248
  }
203
- async function disposeAsyncDisposables(disposables) {
204
- const results = await Promise.allSettled(Array.from(disposables, (disposable) => Promise.resolve().then(() => disposable[Symbol.asyncDispose]())));
205
- throwDisposeErrors(results.filter((result) => result.status === "rejected").map((result) => result.reason));
249
+ async function disposeAsyncDisposables(disposables, retainedDisposables) {
250
+ const disposableList = filterRetainedDisposables(disposables, retainedDisposables);
251
+ for (const disposable of disposableList) disposables.delete(disposable);
252
+ try {
253
+ const results = await Promise.allSettled(disposableList.map((disposable) => Promise.resolve().then(() => disposable[Symbol.asyncDispose]())));
254
+ throwDisposeErrors(results.filter((result) => result.status === "rejected").map((result) => result.reason));
255
+ } finally {
256
+ disposables.clear();
257
+ }
258
+ }
259
+ function filterRetainedDisposables(disposables, retainedDisposables) {
260
+ return Array.from(disposables).filter((disposable) => !retainedDisposables.has(disposable));
206
261
  }
207
262
  function throwDisposeErrors(errors) {
208
263
  if (errors.length < 1) return;
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-config.js","names":["scopedConfigSymbol: unique symbol","defaultScopedLogger: CompiledScopedLogger","config: ScopedConfigLike<TSinkId, TFilterId>","allowAsync: boolean","createError: (message: string) => Error","sinks: Sink[]","filters: ((record: LogRecord) => boolean)[]","contextLocalStorage:\n | ContextLocalStorage<Record<string, unknown>>\n | undefined","contextLocalStorage: ContextLocalStorage<Record<string, unknown>>","scopedConfig: CompiledScopedConfig","callback: () => R","category: readonly string[]","level: LogLevel","record: LogRecord","bypassSinks: Set<Sink> | undefined","emitToSink: (sink: Sink, bypassSinks: Set<Sink> | undefined) => void","errors: unknown[]","primary: unknown","secondary: unknown","value: unknown","activeConfig: CompiledScopedConfig | undefined","category: string | readonly string[]","length: number","firstSink: Sink | undefined","sinks: Sink[] | undefined","sink: Sink","disposables: ReadonlySet<Disposable>","disposables: ReadonlySet<AsyncDisposable>","errors: readonly unknown[]","flattened: unknown[]"],"sources":["../src/scoped-config.ts"],"sourcesContent":["import type { ContextLocalStorage } from \"./context.ts\";\nimport { type FilterLike, toFilter } from \"./filter.ts\";\nimport { compareLogLevel, type LogLevel } from \"./level.ts\";\nimport type { LogRecord } from \"./record.ts\";\nimport type { Sink } from \"./sink.ts\";\n\nexport type ScopedConfigSymbol = typeof scopedConfigSymbol;\n\nexport const scopedConfigSymbol: unique symbol = Symbol.for(\n \"logtape.scopedConfig\",\n);\n\nexport interface ScopedConfigLike<\n TSinkId extends string,\n TFilterId extends string,\n> {\n readonly sinks: Record<TSinkId, Sink>;\n readonly filters?: Record<TFilterId, FilterLike>;\n readonly loggers: readonly ScopedLoggerConfigLike<TSinkId, TFilterId>[];\n}\n\nexport interface ScopedLoggerConfigLike<\n TSinkId extends string,\n TFilterId extends string,\n> {\n readonly category: string | readonly string[];\n readonly sinks?: readonly TSinkId[];\n readonly parentSinks?: \"inherit\" | \"override\";\n readonly filters?: readonly TFilterId[];\n readonly lowestLevel?: LogLevel | null;\n}\n\nexport interface CompiledScopedConfig {\n readonly nodes: ReadonlyMap<string, CompiledScopedLogger>;\n parent: CompiledScopedConfig | undefined;\n disposed: boolean;\n readonly syncFilters: ReadonlySet<Disposable>;\n readonly asyncFilters: ReadonlySet<AsyncDisposable>;\n readonly syncSinks: ReadonlySet<Disposable>;\n readonly asyncSinks: ReadonlySet<AsyncDisposable>;\n}\n\ninterface CompiledScopedLogger {\n readonly filters: readonly ((record: LogRecord) => boolean)[];\n readonly lowestLevel: LogLevel | null;\n readonly parentSinks: \"inherit\" | \"override\";\n readonly sinks: readonly Sink[];\n}\n\ntype ScopedSinkDispatchPlan =\n | { readonly kind: \"none\" }\n | { readonly kind: \"one\"; readonly sink: Sink }\n | { readonly kind: \"many\"; readonly sinks: readonly Sink[] };\n\nconst defaultScopedLogger: CompiledScopedLogger = {\n filters: [],\n lowestLevel: \"trace\",\n parentSinks: \"inherit\",\n sinks: [],\n};\n\nexport function compileScopedConfig<\n TSinkId extends string,\n TFilterId extends string,\n>(\n config: ScopedConfigLike<TSinkId, TFilterId>,\n allowAsync: boolean,\n createError: (message: string) => Error,\n): CompiledScopedConfig {\n const nodes = new Map<string, CompiledScopedLogger>();\n const configuredCategories = new Set<string>();\n for (const logger of config.loggers) {\n const category = normalizeCategory(logger.category);\n const key = categoryKey(category);\n if (configuredCategories.has(key)) {\n throw createError(\n `Duplicate logger configuration for category: ${key}. ` +\n \"Each category can only be configured once.\",\n );\n }\n configuredCategories.add(key);\n\n const sinks: Sink[] = [];\n for (const sinkId of logger.sinks ?? []) {\n const sink = config.sinks[sinkId];\n if (!sink) throw createError(`Sink not found: ${sinkId}.`);\n sinks.push(sink);\n }\n\n const filters: ((record: LogRecord) => boolean)[] = [];\n for (const filterId of logger.filters ?? []) {\n const filter = config.filters?.[filterId];\n if (filter === undefined) {\n throw createError(`Filter not found: ${filterId}.`);\n }\n filters.push(toFilter(filter));\n }\n\n nodes.set(key, {\n filters,\n lowestLevel: logger.lowestLevel === undefined\n ? \"trace\"\n : logger.lowestLevel,\n parentSinks: logger.parentSinks ?? \"inherit\",\n sinks,\n });\n }\n\n const syncFilters = new Set<Disposable>();\n const asyncFilters = new Set<AsyncDisposable>();\n const syncSinks = new Set<Disposable>();\n const asyncSinks = new Set<AsyncDisposable>();\n\n for (const sink of Object.values<Sink>(config.sinks)) {\n if (Symbol.asyncDispose in sink) {\n if (!allowAsync) {\n throw createError(\n \"Async disposables cannot be used with withConfigSync().\",\n );\n }\n asyncSinks.add(sink as AsyncDisposable);\n }\n if (Symbol.dispose in sink) syncSinks.add(sink as Disposable);\n }\n\n for (const filter of Object.values<FilterLike>(config.filters ?? {})) {\n if (filter == null || typeof filter === \"string\") continue;\n if (Symbol.asyncDispose in filter) {\n if (!allowAsync) {\n throw createError(\n \"Async disposables cannot be used with withConfigSync().\",\n );\n }\n asyncFilters.add(filter as AsyncDisposable);\n asyncSinks.delete(filter as AsyncDisposable);\n }\n if (Symbol.dispose in filter) {\n syncFilters.add(filter as Disposable);\n syncSinks.delete(filter as Disposable);\n }\n }\n\n return {\n asyncFilters,\n asyncSinks,\n disposed: false,\n nodes,\n parent: undefined,\n syncFilters,\n syncSinks,\n };\n}\n\nexport function getCurrentScopedConfig(\n contextLocalStorage:\n | ContextLocalStorage<Record<string, unknown>>\n | undefined,\n): CompiledScopedConfig | undefined {\n const store = contextLocalStorage?.getStore();\n const scopedConfig = (store as Record<ScopedConfigSymbol, unknown> | null)\n ?.[scopedConfigSymbol];\n return isCompiledScopedConfig(scopedConfig)\n ? getActiveScopedConfig(scopedConfig)\n : undefined;\n}\n\nexport function runWithScopedConfig<R>(\n contextLocalStorage: ContextLocalStorage<Record<string, unknown>>,\n scopedConfig: CompiledScopedConfig,\n callback: () => R,\n): R {\n const parentStore = contextLocalStorage.getStore() ?? {};\n scopedConfig.parent = getCurrentScopedConfig(contextLocalStorage);\n return contextLocalStorage.run({\n ...parentStore,\n [scopedConfigSymbol]: scopedConfig,\n } as Record<string, unknown>, callback);\n}\n\nexport function scopedConfigHasSink(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n level: LogLevel,\n): boolean {\n return getScopedSinkDispatchPlan(scopedConfig, category, level).kind !==\n \"none\";\n}\n\nexport function emitWithScopedConfig(\n scopedConfig: CompiledScopedConfig,\n record: LogRecord,\n bypassSinks: Set<Sink> | undefined,\n emitToSink: (sink: Sink, bypassSinks: Set<Sink> | undefined) => void,\n): void {\n const plan = getScopedSinkDispatchPlan(\n scopedConfig,\n record.category,\n record.level,\n );\n if (plan.kind === \"none\") return;\n if (!filterScopedRecord(scopedConfig, record.category, record)) return;\n if (plan.kind === \"one\") {\n if (!bypassSinks?.has(plan.sink)) emitToSink(plan.sink, bypassSinks);\n return;\n }\n for (const sink of plan.sinks) {\n if (bypassSinks?.has(sink)) continue;\n emitToSink(sink, bypassSinks);\n }\n}\n\nexport async function disposeScopedConfig(\n scopedConfig: CompiledScopedConfig,\n): Promise<void> {\n scopedConfig.disposed = true;\n const errors: unknown[] = [];\n try {\n disposeSyncDisposables(scopedConfig.syncFilters);\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncDisposables(scopedConfig.asyncFilters);\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncDisposables(scopedConfig.syncSinks);\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncDisposables(scopedConfig.asyncSinks);\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\nexport function disposeScopedConfigSync(\n scopedConfig: CompiledScopedConfig,\n): void {\n scopedConfig.disposed = true;\n const errors: unknown[] = [];\n try {\n disposeSyncDisposables(scopedConfig.syncFilters);\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncDisposables(scopedConfig.syncSinks);\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\nexport function throwCombinedErrors(\n primary: unknown,\n secondary: unknown,\n): never {\n throw new AggregateError(\n flattenErrors(primary, secondary),\n \"Multiple errors occurred while running LogTape scoped configuration.\",\n );\n}\n\nfunction isCompiledScopedConfig(value: unknown): value is CompiledScopedConfig {\n return value != null && typeof value === \"object\" && \"nodes\" in value;\n}\n\nfunction getActiveScopedConfig(\n scopedConfig: CompiledScopedConfig,\n): CompiledScopedConfig | undefined {\n let activeConfig: CompiledScopedConfig | undefined = scopedConfig;\n while (activeConfig?.disposed) activeConfig = activeConfig.parent;\n return activeConfig;\n}\n\nfunction normalizeCategory(category: string | readonly string[]): string[] {\n return typeof category === \"string\" ? [category] : [...category];\n}\n\nfunction categoryKey(category: readonly string[]): string {\n return JSON.stringify(category);\n}\n\nfunction getScopedSinkDispatchPlan(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n level: LogLevel,\n): ScopedSinkDispatchPlan {\n return getScopedSinkDispatchPlanForPrefix(\n scopedConfig,\n category,\n category.length,\n level,\n );\n}\n\nfunction getScopedSinkDispatchPlanForPrefix(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n length: number,\n level: LogLevel,\n): ScopedSinkDispatchPlan {\n const prefix = category.slice(0, length);\n const logger = scopedConfig.nodes.get(categoryKey(prefix)) ??\n defaultScopedLogger;\n if (\n logger.lowestLevel === null ||\n compareLogLevel(level, logger.lowestLevel) < 0\n ) {\n return { kind: \"none\" };\n }\n\n const parentPlan = length > 0 && logger.parentSinks === \"inherit\"\n ? getScopedSinkDispatchPlanForPrefix(\n scopedConfig,\n category,\n length - 1,\n level,\n )\n : { kind: \"none\" } as const;\n\n let firstSink: Sink | undefined;\n let sinks: Sink[] | undefined;\n const appendSink = (sink: Sink): void => {\n if (sinks != null) {\n sinks.push(sink);\n } else if (firstSink == null) {\n firstSink = sink;\n } else {\n sinks = [firstSink, sink];\n }\n };\n\n if (parentPlan.kind === \"one\") appendSink(parentPlan.sink);\n else if (parentPlan.kind === \"many\") {\n for (const sink of parentPlan.sinks) appendSink(sink);\n }\n for (const sink of logger.sinks) appendSink(sink);\n\n if (sinks != null) return { kind: \"many\", sinks };\n if (firstSink != null) return { kind: \"one\", sink: firstSink };\n return { kind: \"none\" };\n}\n\nfunction filterScopedRecord(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n record: LogRecord,\n): boolean {\n for (let length = category.length; length >= 0; length--) {\n const logger = scopedConfig.nodes.get(\n categoryKey(category.slice(0, length)),\n );\n if (logger == null || logger.filters.length < 1) continue;\n return logger.filters.every((filter) => filter(record));\n }\n return true;\n}\n\nfunction disposeSyncDisposables(disposables: ReadonlySet<Disposable>): void {\n const errors: unknown[] = [];\n for (const disposable of disposables) {\n try {\n disposable[Symbol.dispose]();\n } catch (error) {\n errors.push(error);\n }\n }\n throwDisposeErrors(errors);\n}\n\nasync function disposeAsyncDisposables(\n disposables: ReadonlySet<AsyncDisposable>,\n): Promise<void> {\n const results = await Promise.allSettled(\n Array.from(\n disposables,\n (disposable) =>\n Promise.resolve().then(() => disposable[Symbol.asyncDispose]()),\n ),\n );\n throwDisposeErrors(\n results\n .filter((result): result is PromiseRejectedResult =>\n result.status === \"rejected\"\n )\n .map((result) => result.reason),\n );\n}\n\nfunction throwDisposeErrors(errors: readonly unknown[]): void {\n if (errors.length < 1) return;\n if (errors.length === 1) throw errors[0];\n throw new AggregateError(\n errors,\n \"Multiple errors occurred while disposing LogTape scoped resources.\",\n );\n}\n\nfunction flattenErrors(...errors: readonly unknown[]): unknown[] {\n const flattened: unknown[] = [];\n for (const error of errors) {\n if (error instanceof AggregateError) flattened.push(...error.errors);\n else flattened.push(error);\n }\n return flattened;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"scoped-config.js","names":["scopedConfigSymbol: unique symbol","defaultScopedLogger: CompiledScopedLogger","config: ScopedConfigLike<TSinkId, TFilterId>","allowAsync: boolean","createError: (message: string) => Error","sinks: Sink[]","sinkIds: readonly TSinkId[]","filters: ((record: LogRecord) => boolean)[]","filterIds: readonly TFilterId[]","contextLocalStorage:\n | ContextLocalStorage<Record<string, unknown>>\n | undefined","contextLocalStorage: ContextLocalStorage<Record<string, unknown>>","scopedConfig: CompiledScopedConfig","callback: () => R","category: readonly string[]","level: LogLevel","record: LogRecord","bypassSinks: Set<Sink> | undefined","emitToSink: (sink: Sink, bypassSinks: Set<Sink> | undefined) => void","retainedDisposables?: ReadonlySet<Disposable | AsyncDisposable>","errors: unknown[]","primary: unknown","secondary: unknown","value: unknown","activeConfig: CompiledScopedConfig | undefined","extraRetained?: ReadonlySet<Disposable | AsyncDisposable>","disposables: Set<Disposable | AsyncDisposable>","category: string | readonly string[]","length: number","firstSink: Sink | undefined","sinks: Sink[] | undefined","sink: Sink","disposables: Set<Disposable>","retainedDisposables: ReadonlySet<Disposable | AsyncDisposable>","disposables: Set<AsyncDisposable>","disposables: ReadonlySet<TDisposable>","errors: readonly unknown[]","flattened: unknown[]"],"sources":["../src/scoped-config.ts"],"sourcesContent":["import type { ContextLocalStorage } from \"./context.ts\";\nimport { type FilterLike, toFilter } from \"./filter.ts\";\nimport { compareLogLevel, isLogLevel, type LogLevel } from \"./level.ts\";\nimport type { LogRecord } from \"./record.ts\";\nimport type { Sink } from \"./sink.ts\";\n\nexport type ScopedConfigSymbol = typeof scopedConfigSymbol;\n\nexport const scopedConfigSymbol: unique symbol = Symbol.for(\n \"logtape.scopedConfig\",\n);\n\nexport interface ScopedConfigLike<\n TSinkId extends string,\n TFilterId extends string,\n> {\n readonly sinks: Record<TSinkId, Sink>;\n readonly filters?: Record<TFilterId, FilterLike>;\n readonly loggers: readonly ScopedLoggerConfigLike<TSinkId, TFilterId>[];\n}\n\nexport interface ScopedLoggerConfigLike<\n TSinkId extends string,\n TFilterId extends string,\n> {\n readonly category: string | readonly string[];\n readonly sinks?: readonly TSinkId[];\n readonly parentSinks?: \"inherit\" | \"override\";\n readonly filters?: readonly TFilterId[];\n readonly lowestLevel?: LogLevel | null;\n}\n\nexport interface CompiledScopedConfig {\n readonly nodes: ReadonlyMap<string, CompiledScopedLogger>;\n readonly dispatchCache: Map<string, ScopedSinkDispatchPlan>;\n parent: CompiledScopedConfig | undefined;\n disposed: boolean;\n readonly syncFilters: Set<Disposable>;\n readonly asyncFilters: Set<AsyncDisposable>;\n readonly syncSinks: Set<Disposable>;\n readonly asyncSinks: Set<AsyncDisposable>;\n}\n\ninterface CompiledScopedLogger {\n readonly filters: readonly ((record: LogRecord) => boolean)[];\n readonly lowestLevel: LogLevel | null;\n readonly parentSinks: \"inherit\" | \"override\";\n readonly sinks: readonly Sink[];\n}\n\ntype ScopedSinkDispatchPlan =\n | { readonly kind: \"none\" }\n | { readonly kind: \"one\"; readonly sink: Sink }\n | { readonly kind: \"many\"; readonly sinks: readonly Sink[] };\n\nconst defaultScopedLogger: CompiledScopedLogger = {\n filters: [],\n lowestLevel: \"trace\",\n parentSinks: \"inherit\",\n sinks: [],\n};\n\nexport function compileScopedConfig<\n TSinkId extends string,\n TFilterId extends string,\n>(\n config: ScopedConfigLike<TSinkId, TFilterId>,\n allowAsync: boolean,\n createError: (message: string) => Error,\n): CompiledScopedConfig {\n if (!isObjectLike(config)) {\n throw createError(\"Configuration must be an object.\");\n }\n if (!isObjectLike(config.sinks)) {\n throw createError(\"Configuration must include a sinks object.\");\n }\n if (!Array.isArray(config.loggers)) {\n throw createError(\"Configuration must include a loggers array.\");\n }\n\n const nodes = new Map<string, CompiledScopedLogger>();\n const configuredCategories = new Set<string>();\n for (const logger of config.loggers) {\n const category = normalizeCategory(logger.category);\n const key = categoryKey(category);\n if (configuredCategories.has(key)) {\n throw createError(\n `Duplicate logger configuration for category: ${key}. ` +\n \"Each category can only be configured once.\",\n );\n }\n configuredCategories.add(key);\n\n const sinks: Sink[] = [];\n const sinkIds: readonly TSinkId[] = logger.sinks ?? [];\n for (const sinkId of sinkIds) {\n const sink = config.sinks[sinkId];\n if (!sink) throw createError(`Sink not found: ${sinkId}.`);\n if (typeof sink !== \"function\") {\n throw createError(`Sink must be a function: ${sinkId}.`);\n }\n sinks.push(sink);\n }\n\n const filters: ((record: LogRecord) => boolean)[] = [];\n const filterIds: readonly TFilterId[] = logger.filters ?? [];\n for (const filterId of filterIds) {\n const filter = config.filters?.[filterId];\n if (filter === undefined) {\n throw createError(`Filter not found: ${filterId}.`);\n }\n if (!isFilterLike(filter)) {\n throw createError(\n `Filter must be a function, log level, or null: ${filterId}.`,\n );\n }\n filters.push(toFilter(filter));\n }\n\n nodes.set(key, {\n filters,\n lowestLevel: logger.lowestLevel === undefined\n ? \"trace\"\n : logger.lowestLevel,\n parentSinks: logger.parentSinks ?? \"inherit\",\n sinks,\n });\n }\n\n const syncFilters = new Set<Disposable>();\n const asyncFilters = new Set<AsyncDisposable>();\n const syncSinks = new Set<Disposable>();\n const asyncSinks = new Set<AsyncDisposable>();\n\n for (const sink of Object.values<Sink>(config.sinks)) {\n if (!isObjectLike(sink)) continue;\n if (Symbol.asyncDispose in sink) {\n if (!allowAsync) {\n throw createError(\n \"Async disposables cannot be used with withConfigSync().\",\n );\n }\n asyncSinks.add(sink as AsyncDisposable);\n }\n if (Symbol.dispose in sink) syncSinks.add(sink as Disposable);\n }\n\n for (const filter of Object.values<FilterLike>(config.filters ?? {})) {\n if (!isObjectLike(filter)) continue;\n if (Symbol.asyncDispose in filter) {\n if (!allowAsync) {\n throw createError(\n \"Async disposables cannot be used with withConfigSync().\",\n );\n }\n asyncFilters.add(filter as AsyncDisposable);\n asyncSinks.delete(filter as AsyncDisposable);\n }\n if (Symbol.dispose in filter) {\n syncFilters.add(filter as Disposable);\n syncSinks.delete(filter as Disposable);\n }\n }\n\n return {\n asyncFilters,\n asyncSinks,\n dispatchCache: new Map(),\n disposed: false,\n nodes,\n parent: undefined,\n syncFilters,\n syncSinks,\n };\n}\n\nexport function getCurrentScopedConfig(\n contextLocalStorage:\n | ContextLocalStorage<Record<string, unknown>>\n | undefined,\n): CompiledScopedConfig | undefined {\n const store = contextLocalStorage?.getStore();\n const scopedConfig = (store as Record<ScopedConfigSymbol, unknown> | null)\n ?.[scopedConfigSymbol];\n return isCompiledScopedConfig(scopedConfig)\n ? getActiveScopedConfig(scopedConfig)\n : undefined;\n}\n\nexport function runWithScopedConfig<R>(\n contextLocalStorage: ContextLocalStorage<Record<string, unknown>>,\n scopedConfig: CompiledScopedConfig,\n callback: () => R,\n): R {\n const parentStore = contextLocalStorage.getStore() ?? {};\n scopedConfig.parent = getCurrentScopedConfig(contextLocalStorage);\n return contextLocalStorage.run({\n ...parentStore,\n [scopedConfigSymbol]: scopedConfig,\n } as Record<string, unknown>, callback);\n}\n\nexport function scopedConfigHasSink(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n level: LogLevel,\n): boolean {\n return getScopedSinkDispatchPlan(scopedConfig, category, level).kind !==\n \"none\";\n}\n\nexport function emitWithScopedConfig(\n scopedConfig: CompiledScopedConfig,\n record: LogRecord,\n bypassSinks: Set<Sink> | undefined,\n emitToSink: (sink: Sink, bypassSinks: Set<Sink> | undefined) => void,\n): void {\n const plan = getScopedSinkDispatchPlan(\n scopedConfig,\n record.category,\n record.level,\n );\n if (plan.kind === \"none\") return;\n if (!filterScopedRecord(scopedConfig, record.category, record)) return;\n if (plan.kind === \"one\") {\n if (!bypassSinks?.has(plan.sink)) emitToSink(plan.sink, bypassSinks);\n return;\n }\n for (const sink of plan.sinks) {\n if (bypassSinks?.has(sink)) continue;\n emitToSink(sink, bypassSinks);\n }\n}\n\nexport async function disposeScopedConfig(\n scopedConfig: CompiledScopedConfig,\n retainedDisposables?: ReadonlySet<Disposable | AsyncDisposable>,\n): Promise<void> {\n const parentDisposables = getParentScopedDisposables(\n scopedConfig,\n retainedDisposables,\n );\n scopedConfig.disposed = true;\n const errors: unknown[] = [];\n try {\n disposeSyncDisposables(scopedConfig.syncFilters, parentDisposables);\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncDisposables(scopedConfig.asyncFilters, parentDisposables);\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncDisposables(scopedConfig.syncSinks, parentDisposables);\n } catch (error) {\n errors.push(error);\n }\n try {\n await disposeAsyncDisposables(scopedConfig.asyncSinks, parentDisposables);\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\nexport function disposeScopedConfigSync(\n scopedConfig: CompiledScopedConfig,\n retainedDisposables?: ReadonlySet<Disposable | AsyncDisposable>,\n): void {\n const parentDisposables = getParentScopedDisposables(\n scopedConfig,\n retainedDisposables,\n );\n scopedConfig.disposed = true;\n const errors: unknown[] = [];\n try {\n disposeSyncDisposables(scopedConfig.syncFilters, parentDisposables);\n } catch (error) {\n errors.push(error);\n }\n try {\n disposeSyncDisposables(scopedConfig.syncSinks, parentDisposables);\n } catch (error) {\n errors.push(error);\n }\n throwDisposeErrors(errors);\n}\n\nexport function throwCombinedErrors(\n primary: unknown,\n secondary: unknown,\n): never {\n throw new AggregateError(\n flattenErrors(primary, secondary),\n \"Multiple errors occurred while running LogTape scoped configuration.\",\n );\n}\n\nfunction isCompiledScopedConfig(value: unknown): value is CompiledScopedConfig {\n return value != null && typeof value === \"object\" && \"nodes\" in value;\n}\n\nfunction isObjectLike(\n value: unknown,\n): value is object | ((...args: never[]) => unknown) {\n return value != null &&\n (typeof value === \"object\" || typeof value === \"function\");\n}\n\nfunction isFilterLike(value: unknown): value is FilterLike {\n return value == null || typeof value === \"function\" ||\n (typeof value === \"string\" && isLogLevel(value));\n}\n\nfunction getActiveScopedConfig(\n scopedConfig: CompiledScopedConfig,\n): CompiledScopedConfig | undefined {\n let activeConfig: CompiledScopedConfig | undefined = scopedConfig;\n while (activeConfig?.disposed) activeConfig = activeConfig.parent;\n return activeConfig;\n}\n\nfunction getParentScopedDisposables(\n scopedConfig: CompiledScopedConfig,\n extraRetained?: ReadonlySet<Disposable | AsyncDisposable>,\n): ReadonlySet<Disposable | AsyncDisposable> {\n const disposables = new Set<Disposable | AsyncDisposable>(extraRetained);\n let parent = scopedConfig.parent;\n while (parent != null) {\n const activeParent = getActiveScopedConfig(parent);\n if (activeParent == null) break;\n addScopedDisposables(disposables, activeParent);\n parent = activeParent.parent;\n }\n return disposables;\n}\n\nfunction addScopedDisposables(\n disposables: Set<Disposable | AsyncDisposable>,\n scopedConfig: CompiledScopedConfig,\n): void {\n for (const disposable of scopedConfig.syncFilters) {\n disposables.add(disposable);\n }\n for (const disposable of scopedConfig.asyncFilters) {\n disposables.add(disposable);\n }\n for (const disposable of scopedConfig.syncSinks) disposables.add(disposable);\n for (const disposable of scopedConfig.asyncSinks) disposables.add(disposable);\n}\n\nfunction normalizeCategory(category: string | readonly string[]): string[] {\n return typeof category === \"string\" ? [category] : [...category];\n}\n\nfunction categoryKey(category: readonly string[]): string {\n return JSON.stringify(category);\n}\n\nfunction getScopedSinkDispatchPlan(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n level: LogLevel,\n): ScopedSinkDispatchPlan {\n const cacheKey = `${categoryKey(category)}:${level}`;\n let plan = scopedConfig.dispatchCache.get(cacheKey);\n if (plan == null) {\n plan = getScopedSinkDispatchPlanForPrefix(\n scopedConfig,\n category,\n category.length,\n level,\n );\n scopedConfig.dispatchCache.set(cacheKey, plan);\n }\n return plan;\n}\n\nfunction getScopedSinkDispatchPlanForPrefix(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n length: number,\n level: LogLevel,\n): ScopedSinkDispatchPlan {\n const prefix = category.slice(0, length);\n const logger = scopedConfig.nodes.get(categoryKey(prefix)) ??\n defaultScopedLogger;\n if (\n logger.lowestLevel === null ||\n compareLogLevel(level, logger.lowestLevel) < 0\n ) {\n return { kind: \"none\" };\n }\n\n const parentPlan = length > 0 && logger.parentSinks === \"inherit\"\n ? getScopedSinkDispatchPlanForPrefix(\n scopedConfig,\n category,\n length - 1,\n level,\n )\n : { kind: \"none\" } as const;\n\n let firstSink: Sink | undefined;\n let sinks: Sink[] | undefined;\n const appendSink = (sink: Sink): void => {\n if (sinks != null) {\n sinks.push(sink);\n } else if (firstSink == null) {\n firstSink = sink;\n } else {\n sinks = [firstSink, sink];\n }\n };\n\n if (parentPlan.kind === \"one\") appendSink(parentPlan.sink);\n else if (parentPlan.kind === \"many\") {\n for (const sink of parentPlan.sinks) appendSink(sink);\n }\n for (const sink of logger.sinks) appendSink(sink);\n\n if (sinks != null) return { kind: \"many\", sinks };\n if (firstSink != null) return { kind: \"one\", sink: firstSink };\n return { kind: \"none\" };\n}\n\nfunction filterScopedRecord(\n scopedConfig: CompiledScopedConfig,\n category: readonly string[],\n record: LogRecord,\n): boolean {\n for (let length = category.length; length >= 0; length--) {\n const logger = scopedConfig.nodes.get(\n categoryKey(category.slice(0, length)),\n );\n if (logger == null || logger.filters.length < 1) continue;\n return logger.filters.every((filter) => filter(record));\n }\n return true;\n}\n\nfunction disposeSyncDisposables(\n disposables: Set<Disposable>,\n retainedDisposables: ReadonlySet<Disposable | AsyncDisposable>,\n): void {\n const errors: unknown[] = [];\n try {\n for (const disposable of disposables) {\n try {\n if (!retainedDisposables.has(disposable)) {\n disposable[Symbol.dispose]();\n }\n } catch (error) {\n errors.push(error);\n } finally {\n disposables.delete(disposable);\n }\n }\n } finally {\n disposables.clear();\n }\n throwDisposeErrors(errors);\n}\n\nasync function disposeAsyncDisposables(\n disposables: Set<AsyncDisposable>,\n retainedDisposables: ReadonlySet<Disposable | AsyncDisposable>,\n): Promise<void> {\n const disposableList = filterRetainedDisposables(\n disposables,\n retainedDisposables,\n );\n for (const disposable of disposableList) disposables.delete(disposable);\n try {\n const results = await Promise.allSettled(\n disposableList.map((disposable) =>\n Promise.resolve().then(() => disposable[Symbol.asyncDispose]())\n ),\n );\n throwDisposeErrors(\n results\n .filter((result): result is PromiseRejectedResult =>\n result.status === \"rejected\"\n )\n .map((result) => result.reason),\n );\n } finally {\n disposables.clear();\n }\n}\n\nfunction filterRetainedDisposables<\n TDisposable extends Disposable | AsyncDisposable,\n>(\n disposables: ReadonlySet<TDisposable>,\n retainedDisposables: ReadonlySet<Disposable | AsyncDisposable>,\n): TDisposable[] {\n return Array.from(\n disposables,\n ).filter((disposable) => !retainedDisposables.has(disposable));\n}\n\nfunction throwDisposeErrors(errors: readonly unknown[]): void {\n if (errors.length < 1) return;\n if (errors.length === 1) throw errors[0];\n throw new AggregateError(\n errors,\n \"Multiple errors occurred while disposing LogTape scoped resources.\",\n );\n}\n\nfunction flattenErrors(...errors: readonly unknown[]): unknown[] {\n const flattened: unknown[] = [];\n for (const error of errors) {\n if (error instanceof AggregateError) flattened.push(...error.errors);\n else flattened.push(error);\n }\n return flattened;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logtape/logtape",
3
- "version": "2.3.0-dev.795+50e33c4d",
3
+ "version": "2.3.0-dev.805+2fc375ff",
4
4
  "description": "Unobtrusive logging library with zero dependencies—library-first design for Deno/Node.js/Bun/browsers/edge",
5
5
  "keywords": [
6
6
  "logging",