functype-log 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/ConsoleLogger-6gnkQctP.d.ts +11 -0
  4. package/dist/LogLayerAdapter-CtytCNjQ.d.ts +8 -0
  5. package/dist/LogMiddleware-Bv7EDRjF.d.ts +11 -0
  6. package/dist/Logger-BFum_d5a.d.ts +27 -0
  7. package/dist/LoggerLayer-B-tD-_gO.d.ts +14 -0
  8. package/dist/SilentLogger-BWfCzQqB.d.ts +7 -0
  9. package/dist/TestLogger-4s7rQHxR.d.ts +14 -0
  10. package/dist/adapter/LogLayerAdapter.d.ts +2 -0
  11. package/dist/adapter/LogLayerAdapter.js +2 -0
  12. package/dist/adapter/LogLayerAdapter.js.map +1 -0
  13. package/dist/adapter/index.d.ts +2 -0
  14. package/dist/adapter/index.js +1 -0
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.js +1 -0
  17. package/dist/layers/ConsoleLogger.d.ts +2 -0
  18. package/dist/layers/ConsoleLogger.js +2 -0
  19. package/dist/layers/ConsoleLogger.js.map +1 -0
  20. package/dist/layers/LoggerLayer.d.ts +2 -0
  21. package/dist/layers/LoggerLayer.js +2 -0
  22. package/dist/layers/LoggerLayer.js.map +1 -0
  23. package/dist/layers/SilentLogger.d.ts +2 -0
  24. package/dist/layers/SilentLogger.js +2 -0
  25. package/dist/layers/SilentLogger.js.map +1 -0
  26. package/dist/layers/index.d.ts +4 -0
  27. package/dist/layers/index.js +1 -0
  28. package/dist/logger/Logger.d.ts +2 -0
  29. package/dist/logger/Logger.js +2 -0
  30. package/dist/logger/Logger.js.map +1 -0
  31. package/dist/logger/index.d.ts +2 -0
  32. package/dist/logger/index.js +1 -0
  33. package/dist/middleware/LogMiddleware.d.ts +2 -0
  34. package/dist/middleware/LogMiddleware.js +2 -0
  35. package/dist/middleware/LogMiddleware.js.map +1 -0
  36. package/dist/middleware/index.d.ts +2 -0
  37. package/dist/middleware/index.js +1 -0
  38. package/dist/testing/TestLogger.d.ts +2 -0
  39. package/dist/testing/TestLogger.js +2 -0
  40. package/dist/testing/TestLogger.js.map +1 -0
  41. package/dist/testing/index.d.ts +2 -0
  42. package/dist/testing/index.js +1 -0
  43. package/package.json +85 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Jordan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # functype-log
2
+
3
+ IO-native logging for the [functype](https://github.com/jordanburke/functype) ecosystem. Wraps [LogLayer](https://loglayer.dev/) with functype's `Tag`/`Layer` dependency injection system.
4
+
5
+ Every log method returns `IO<never, never, void>` — logging is lazy, composable, and testable.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add functype-log functype
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { IO } from "functype"
17
+ import { Logger, LoggerLive } from "functype-log"
18
+
19
+ const program = IO.gen(function* () {
20
+ const log = yield* IO.service(Logger)
21
+ yield* log.info("Starting", { version: "1.0.0" })
22
+ const result = yield* doWork()
23
+ yield* log.info("Done", { result })
24
+ return result
25
+ })
26
+
27
+ // Run with console logging
28
+ await program.provideLayer(LoggerLive.console()).runOrThrow()
29
+ ```
30
+
31
+ ## Layers
32
+
33
+ ### Console (dev)
34
+
35
+ ```typescript
36
+ await program.provideLayer(LoggerLive.console()).runOrThrow()
37
+ await program.provideLayer(LoggerLive.console({ prefix: "[APP]" })).runOrThrow()
38
+ ```
39
+
40
+ ### Silent (testing/suppression)
41
+
42
+ ```typescript
43
+ await program.provideLayer(LoggerLive.silent()).runOrThrow()
44
+ ```
45
+
46
+ ### From LogLayer (production)
47
+
48
+ Use any of LogLayer's 40+ transports — pino, winston, datadog, bunyan, OpenTelemetry, etc.
49
+
50
+ ```typescript
51
+ import { LogLayer } from "loglayer"
52
+ import { PinoTransport } from "@loglayer/transport-pino"
53
+ import pino from "pino"
54
+
55
+ const pinoLog = new LogLayer({
56
+ transport: new PinoTransport({ logger: pino() }),
57
+ })
58
+
59
+ await program.provideLayer(LoggerLive.fromLogLayer(pinoLog)).runOrThrow()
60
+ ```
61
+
62
+ ### OpenTelemetry
63
+
64
+ ```typescript
65
+ import { openTelemetryPlugin } from "@loglayer/plugin-opentelemetry"
66
+
67
+ const otelLog = new LogLayer({
68
+ transport: new PinoTransport({ logger: pino() }),
69
+ plugins: [openTelemetryPlugin()],
70
+ })
71
+
72
+ await program.provideLayer(LoggerLive.fromLogLayer(otelLog)).runOrThrow()
73
+ ```
74
+
75
+ ## Logger API
76
+
77
+ ```typescript
78
+ const log = yield * IO.service(Logger)
79
+
80
+ // Log levels
81
+ yield * log.trace("verbose detail")
82
+ yield * log.debug("debug info")
83
+ yield * log.info("informational")
84
+ yield * log.warn("warning")
85
+ yield * log.error("error occurred")
86
+ yield * log.fatal("fatal error")
87
+
88
+ // Structured metadata
89
+ yield * log.info("user action", { userId: "123", action: "login" })
90
+
91
+ // Error context
92
+ yield * log.withError(err).error("operation failed")
93
+
94
+ // Persistent context
95
+ const reqLog = log.withContext({ requestId: "req-1" })
96
+ yield * reqLog.info("first") // includes requestId
97
+ yield * reqLog.info("second") // includes requestId
98
+
99
+ // Child logger
100
+ const child = log.child({ handler: "users" })
101
+ ```
102
+
103
+ ## Middleware
104
+
105
+ ### withLogging
106
+
107
+ Wraps any IO with start/complete logging at debug level:
108
+
109
+ ```typescript
110
+ import { withLogging } from "functype-log"
111
+
112
+ const fetchUsers = Http.get("/api/users", { validate: UserSchema })
113
+ const logged = withLogging("fetchUsers", fetchUsers)
114
+ // Logs: "fetchUsers: starting" then "fetchUsers: completed"
115
+ ```
116
+
117
+ ### tapLog
118
+
119
+ Logs after an effect completes:
120
+
121
+ ```typescript
122
+ import { tapLog } from "functype-log"
123
+
124
+ const result = await tapLog<number[]>(
125
+ "info",
126
+ (arr) => `fetched ${arr.length} items`,
127
+ )(IO.succeed([1, 2, 3]))
128
+ .provideService(Logger, logger)
129
+ .runOrThrow()
130
+ ```
131
+
132
+ ## Testing
133
+
134
+ `createTestLogger` captures log entries in memory for assertions:
135
+
136
+ ```typescript
137
+ import { createTestLogger } from "functype-log"
138
+
139
+ const { logger, entries, hasEntry, clear } = createTestLogger()
140
+
141
+ await myProgram.provideService(Logger, logger).runOrThrow()
142
+
143
+ // Assert on captured entries
144
+ expect(entries().size).toBe(2)
145
+ expect(hasEntry("info", "Starting")).toBe(true)
146
+ expect(hasEntry("info", /fetched \d+ items/)).toBe(true)
147
+
148
+ // Entries are List<LogEntry> with full metadata
149
+ const first = entries().head
150
+ expect(first.level).toBe("info")
151
+ expect(first.metadata).toEqual({ version: "1.0.0" })
152
+ expect(first.timestamp).toBeInstanceOf(Date)
153
+ ```
154
+
155
+ ## Subpath Exports
156
+
157
+ ```typescript
158
+ import { Logger } from "functype-log" // everything
159
+ import { Logger } from "functype-log/logger" // types only
160
+ import { LoggerLive } from "functype-log/layers" // layer constructors
161
+ import { createTestLogger } from "functype-log/testing" // test utilities
162
+ import { withLogging } from "functype-log/middleware" // middleware
163
+ ```
164
+
165
+ ## Requirements
166
+
167
+ - functype >= 0.55.0
168
+ - Node.js >= 18.17.0
169
+
170
+ ## License
171
+
172
+ MIT
@@ -0,0 +1,11 @@
1
+ import { i as Logger, n as LogLevel } from "./Logger-BFum_d5a.js";
2
+
3
+ //#region src/layers/ConsoleLogger.d.ts
4
+ type ConsoleLoggerOptions = {
5
+ readonly level?: LogLevel;
6
+ readonly prefix?: string;
7
+ };
8
+ declare const createConsoleLogger: (options?: ConsoleLoggerOptions) => Logger;
9
+ //#endregion
10
+ export { createConsoleLogger as n, ConsoleLoggerOptions as t };
11
+ //# sourceMappingURL=ConsoleLogger-6gnkQctP.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { i as Logger } from "./Logger-BFum_d5a.js";
2
+ import { ILogLayer } from "loglayer";
3
+
4
+ //#region src/adapter/LogLayerAdapter.d.ts
5
+ declare const logLayerAdapter: (logLayer: ILogLayer, baseError?: Error) => Logger;
6
+ //#endregion
7
+ export { logLayerAdapter as t };
8
+ //# sourceMappingURL=LogLayerAdapter-CtytCNjQ.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { i as Logger, n as LogLevel } from "./Logger-BFum_d5a.js";
2
+ import { IO } from "functype";
3
+
4
+ //#region src/middleware/LogMiddleware.d.ts
5
+ /** Wrap any IO with start/complete/error logging at debug level */
6
+ declare const withLogging: <R, E, A>(name: string, effect: IO<R, E, A>) => IO<R | Logger, E, A>;
7
+ /** Create a tap function that logs at the specified level */
8
+ declare const tapLog: <A>(level: LogLevel, message: string | ((a: A) => string)) => <R, E>(effect: IO<R, E, A>) => IO<R | Logger, E, A>;
9
+ //#endregion
10
+ export { withLogging as n, tapLog as t };
11
+ //# sourceMappingURL=LogMiddleware-Bv7EDRjF.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { IO, Tag } from "functype";
2
+
3
+ //#region src/logger/Logger.d.ts
4
+ type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
5
+ type LogMetadata = Record<string, unknown>;
6
+ type LogEntry = {
7
+ readonly level: LogLevel;
8
+ readonly message: string;
9
+ readonly metadata?: LogMetadata;
10
+ readonly error?: Error;
11
+ readonly timestamp: Date;
12
+ };
13
+ type Logger = {
14
+ readonly trace: (message: string, metadata?: LogMetadata) => IO<never, never, void>;
15
+ readonly debug: (message: string, metadata?: LogMetadata) => IO<never, never, void>;
16
+ readonly info: (message: string, metadata?: LogMetadata) => IO<never, never, void>;
17
+ readonly warn: (message: string, metadata?: LogMetadata) => IO<never, never, void>;
18
+ readonly error: (message: string, metadata?: LogMetadata) => IO<never, never, void>;
19
+ readonly fatal: (message: string, metadata?: LogMetadata) => IO<never, never, void>;
20
+ readonly withError: (error: Error) => Logger;
21
+ readonly withContext: (context: LogMetadata) => Logger;
22
+ readonly child: (context?: LogMetadata) => Logger;
23
+ };
24
+ declare const Logger: Tag<Logger>;
25
+ //#endregion
26
+ export { Logger as i, LogLevel as n, LogMetadata as r, LogEntry as t };
27
+ //# sourceMappingURL=Logger-BFum_d5a.d.ts.map
@@ -0,0 +1,14 @@
1
+ import { i as Logger } from "./Logger-BFum_d5a.js";
2
+ import { t as ConsoleLoggerOptions } from "./ConsoleLogger-6gnkQctP.js";
3
+ import { Layer } from "functype";
4
+ import { ILogLayer } from "loglayer";
5
+
6
+ //#region src/layers/LoggerLayer.d.ts
7
+ declare const LoggerLive: {
8
+ /** From an existing LogLayer instance — escape hatch for power users (pino, winston, datadog, etc.) */fromLogLayer: (logLayer: ILogLayer) => Layer<never, never, Logger>; /** Console logger — zero config, great for dev */
9
+ console: (options?: ConsoleLoggerOptions) => Layer<never, never, Logger>; /** Silent/noop logger — for testing or suppression */
10
+ silent: () => Layer<never, never, Logger>;
11
+ };
12
+ //#endregion
13
+ export { LoggerLive as t };
14
+ //# sourceMappingURL=LoggerLayer-B-tD-_gO.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { i as Logger } from "./Logger-BFum_d5a.js";
2
+
3
+ //#region src/layers/SilentLogger.d.ts
4
+ declare const silentLogger: Logger;
5
+ //#endregion
6
+ export { silentLogger as t };
7
+ //# sourceMappingURL=SilentLogger-BWfCzQqB.d.ts.map
@@ -0,0 +1,14 @@
1
+ import { i as Logger, n as LogLevel, t as LogEntry } from "./Logger-BFum_d5a.js";
2
+ import { List } from "functype";
3
+
4
+ //#region src/testing/TestLogger.d.ts
5
+ type TestLoggerHandle = {
6
+ readonly logger: Logger;
7
+ readonly entries: () => List<LogEntry>;
8
+ readonly clear: () => void;
9
+ readonly hasEntry: (level: LogLevel, messagePattern: string | RegExp) => boolean;
10
+ };
11
+ declare const createTestLogger: () => TestLoggerHandle;
12
+ //#endregion
13
+ export { createTestLogger as n, TestLoggerHandle as t };
14
+ //# sourceMappingURL=TestLogger-4s7rQHxR.d.ts.map
@@ -0,0 +1,2 @@
1
+ import { t as logLayerAdapter } from "../LogLayerAdapter-CtytCNjQ.js";
2
+ export { logLayerAdapter };
@@ -0,0 +1,2 @@
1
+ import{IO as e}from"functype";const t=(t,n,r)=>(i,a)=>e.sync(()=>{let e=a?t.withMetadata(a):t;r&&(e=e.withError(r)),e[n](i)}),n=(e,r)=>({trace:t(e,`trace`,r),debug:t(e,`debug`,r),info:t(e,`info`,r),warn:t(e,`warn`,r),error:t(e,`error`,r),fatal:t(e,`fatal`,r),withError:t=>n(e,t),withContext:t=>n(e.withContext(t),r),child:t=>{let r=e.child();return n(t?r.withContext(t):r)}});export{n as logLayerAdapter};
2
+ //# sourceMappingURL=LogLayerAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogLayerAdapter.js","names":[],"sources":["../../src/adapter/LogLayerAdapter.ts"],"sourcesContent":["import { IO } from \"functype\"\nimport type { ILogLayer } from \"loglayer\"\n\nimport type { Logger, LogLevel, LogMetadata } from \"../logger/Logger\"\n\nconst makeMethod =\n (logLayer: ILogLayer, level: LogLevel, baseError?: Error) =>\n (message: string, metadata?: LogMetadata): IO<never, never, void> =>\n IO.sync(() => {\n let entry = metadata ? logLayer.withMetadata(metadata) : logLayer\n if (baseError) {\n entry = entry.withError(baseError)\n }\n entry[level](message)\n })\n\nexport const logLayerAdapter = (logLayer: ILogLayer, baseError?: Error): Logger => ({\n trace: makeMethod(logLayer, \"trace\", baseError),\n debug: makeMethod(logLayer, \"debug\", baseError),\n info: makeMethod(logLayer, \"info\", baseError),\n warn: makeMethod(logLayer, \"warn\", baseError),\n error: makeMethod(logLayer, \"error\", baseError),\n fatal: makeMethod(logLayer, \"fatal\", baseError),\n withError: (err: Error) => logLayerAdapter(logLayer, err),\n withContext: (ctx: LogMetadata) => logLayerAdapter(logLayer.withContext(ctx), baseError),\n child: (ctx?: LogMetadata) => {\n const childLog = logLayer.child()\n return ctx ? logLayerAdapter(childLog.withContext(ctx)) : logLayerAdapter(childLog)\n },\n})\n"],"mappings":"8BAKA,MAAM,GACH,EAAqB,EAAiB,KACtC,EAAiB,IAChB,EAAG,SAAW,CACZ,IAAI,EAAQ,EAAW,EAAS,aAAa,EAAS,CAAG,EACrD,IACF,EAAQ,EAAM,UAAU,EAAU,EAEpC,EAAM,GAAO,EAAQ,EACrB,CAEO,GAAmB,EAAqB,KAA+B,CAClF,MAAO,EAAW,EAAU,QAAS,EAAU,CAC/C,MAAO,EAAW,EAAU,QAAS,EAAU,CAC/C,KAAM,EAAW,EAAU,OAAQ,EAAU,CAC7C,KAAM,EAAW,EAAU,OAAQ,EAAU,CAC7C,MAAO,EAAW,EAAU,QAAS,EAAU,CAC/C,MAAO,EAAW,EAAU,QAAS,EAAU,CAC/C,UAAY,GAAe,EAAgB,EAAU,EAAI,CACzD,YAAc,GAAqB,EAAgB,EAAS,YAAY,EAAI,CAAE,EAAU,CACxF,MAAQ,GAAsB,CAC5B,IAAM,EAAW,EAAS,OAAO,CACjC,OAAa,EAAN,EAAsB,EAAS,YAAY,EAAI,CAAoB,EAAS,EAEtF"}
@@ -0,0 +1,2 @@
1
+ import { t as logLayerAdapter } from "../LogLayerAdapter-CtytCNjQ.js";
2
+ export { logLayerAdapter };
@@ -0,0 +1 @@
1
+ import{logLayerAdapter as e}from"./LogLayerAdapter.js";export{e as logLayerAdapter};
@@ -0,0 +1,8 @@
1
+ import { i as Logger, n as LogLevel, r as LogMetadata, t as LogEntry } from "./Logger-BFum_d5a.js";
2
+ import { t as logLayerAdapter } from "./LogLayerAdapter-CtytCNjQ.js";
3
+ import { n as createConsoleLogger, t as ConsoleLoggerOptions } from "./ConsoleLogger-6gnkQctP.js";
4
+ import { t as LoggerLive } from "./LoggerLayer-B-tD-_gO.js";
5
+ import { t as silentLogger } from "./SilentLogger-BWfCzQqB.js";
6
+ import { n as createTestLogger, t as TestLoggerHandle } from "./TestLogger-4s7rQHxR.js";
7
+ import { n as withLogging, t as tapLog } from "./LogMiddleware-Bv7EDRjF.js";
8
+ export { type ConsoleLoggerOptions, type LogEntry, type LogLevel, type LogMetadata, Logger, LoggerLive, type TestLoggerHandle, createConsoleLogger, createTestLogger, logLayerAdapter, silentLogger, tapLog, withLogging };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{Logger as e}from"./logger/Logger.js";import"./logger/index.js";import{logLayerAdapter as t}from"./adapter/LogLayerAdapter.js";import{createConsoleLogger as n}from"./layers/ConsoleLogger.js";import{silentLogger as r}from"./layers/SilentLogger.js";import{LoggerLive as i}from"./layers/LoggerLayer.js";import"./layers/index.js";import"./adapter/index.js";import{createTestLogger as a}from"./testing/TestLogger.js";import"./testing/index.js";import{tapLog as o,withLogging as s}from"./middleware/LogMiddleware.js";import"./middleware/index.js";export{e as Logger,i as LoggerLive,n as createConsoleLogger,a as createTestLogger,t as logLayerAdapter,r as silentLogger,o as tapLog,s as withLogging};
@@ -0,0 +1,2 @@
1
+ import { n as createConsoleLogger, t as ConsoleLoggerOptions } from "../ConsoleLogger-6gnkQctP.js";
2
+ export { ConsoleLoggerOptions, createConsoleLogger };
@@ -0,0 +1,2 @@
1
+ import{logLayerAdapter as e}from"../adapter/LogLayerAdapter.js";import{ConsoleTransport as t,LogLayer as n}from"loglayer";const r=r=>e(new n({transport:new t({logger:console}),...r?.prefix?{prefix:r.prefix}:{}}));export{r as createConsoleLogger};
2
+ //# sourceMappingURL=ConsoleLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConsoleLogger.js","names":[],"sources":["../../src/layers/ConsoleLogger.ts"],"sourcesContent":["import { ConsoleTransport, LogLayer } from \"loglayer\"\n\nimport { logLayerAdapter } from \"../adapter/LogLayerAdapter\"\nimport type { Logger, LogLevel } from \"../logger/Logger\"\n\nexport type ConsoleLoggerOptions = {\n readonly level?: LogLevel\n readonly prefix?: string\n}\n\nexport const createConsoleLogger = (options?: ConsoleLoggerOptions): Logger => {\n const logLayer = new LogLayer({\n transport: new ConsoleTransport({ logger: console }),\n ...(options?.prefix ? { prefix: options.prefix } : {}),\n })\n\n return logLayerAdapter(logLayer)\n}\n"],"mappings":"0HAUA,MAAa,EAAuB,GAM3B,EALU,IAAI,EAAS,CAC5B,UAAW,IAAI,EAAiB,CAAE,OAAQ,QAAS,CAAC,CACpD,GAAI,GAAS,OAAS,CAAE,OAAQ,EAAQ,OAAQ,CAAG,EAAE,CACtD,CAAC,CAE8B"}
@@ -0,0 +1,2 @@
1
+ import { t as LoggerLive } from "../LoggerLayer-B-tD-_gO.js";
2
+ export { LoggerLive };
@@ -0,0 +1,2 @@
1
+ import{Logger as e}from"../logger/Logger.js";import{logLayerAdapter as t}from"../adapter/LogLayerAdapter.js";import{createConsoleLogger as n}from"./ConsoleLogger.js";import{silentLogger as r}from"./SilentLogger.js";import{Layer as i}from"functype";const a={fromLogLayer:n=>i.succeed(e,t(n)),console:t=>i.sync(e,()=>n(t)),silent:()=>i.succeed(e,r)};export{a as LoggerLive};
2
+ //# sourceMappingURL=LoggerLayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoggerLayer.js","names":[],"sources":["../../src/layers/LoggerLayer.ts"],"sourcesContent":["import { Layer } from \"functype\"\nimport type { ILogLayer } from \"loglayer\"\n\nimport { logLayerAdapter } from \"../adapter/LogLayerAdapter\"\nimport { Logger } from \"../logger/Logger\"\nimport type { ConsoleLoggerOptions } from \"./ConsoleLogger\"\nimport { createConsoleLogger } from \"./ConsoleLogger\"\nimport { silentLogger } from \"./SilentLogger\"\n\nexport const LoggerLive = {\n /** From an existing LogLayer instance — escape hatch for power users (pino, winston, datadog, etc.) */\n fromLogLayer: (logLayer: ILogLayer): Layer<never, never, Logger> => Layer.succeed(Logger, logLayerAdapter(logLayer)),\n\n /** Console logger — zero config, great for dev */\n console: (options?: ConsoleLoggerOptions): Layer<never, never, Logger> =>\n Layer.sync(Logger, () => createConsoleLogger(options)),\n\n /** Silent/noop logger — for testing or suppression */\n silent: (): Layer<never, never, Logger> => Layer.succeed(Logger, silentLogger),\n}\n"],"mappings":"wPASA,MAAa,EAAa,CAExB,aAAe,GAAqD,EAAM,QAAQ,EAAQ,EAAgB,EAAS,CAAC,CAGpH,QAAU,GACR,EAAM,KAAK,MAAc,EAAoB,EAAQ,CAAC,CAGxD,WAA2C,EAAM,QAAQ,EAAQ,EAAa,CAC/E"}
@@ -0,0 +1,2 @@
1
+ import { t as silentLogger } from "../SilentLogger-BWfCzQqB.js";
2
+ export { silentLogger };
@@ -0,0 +1,2 @@
1
+ import{IO as e}from"functype";const t=(t,n)=>e.succeed(void 0),n={trace:t,debug:t,info:t,warn:t,error:t,fatal:t,withError:()=>n,withContext:()=>n,child:()=>n};export{n as silentLogger};
2
+ //# sourceMappingURL=SilentLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SilentLogger.js","names":[],"sources":["../../src/layers/SilentLogger.ts"],"sourcesContent":["import { IO } from \"functype\"\n\nimport type { Logger, LogMetadata } from \"../logger/Logger\"\n\nconst noop = (_message: string, _metadata?: LogMetadata): IO<never, never, void> => IO.succeed(undefined as void)\n\nexport const silentLogger: Logger = {\n trace: noop,\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n fatal: noop,\n withError: () => silentLogger,\n withContext: () => silentLogger,\n child: () => silentLogger,\n}\n"],"mappings":"8BAIA,MAAM,GAAQ,EAAkB,IAAoD,EAAG,QAAQ,IAAA,GAAkB,CAEpG,EAAuB,CAClC,MAAO,EACP,MAAO,EACP,KAAM,EACN,KAAM,EACN,MAAO,EACP,MAAO,EACP,cAAiB,EACjB,gBAAmB,EACnB,UAAa,EACd"}
@@ -0,0 +1,4 @@
1
+ import { n as createConsoleLogger, t as ConsoleLoggerOptions } from "../ConsoleLogger-6gnkQctP.js";
2
+ import { t as LoggerLive } from "../LoggerLayer-B-tD-_gO.js";
3
+ import { t as silentLogger } from "../SilentLogger-BWfCzQqB.js";
4
+ export { type ConsoleLoggerOptions, LoggerLive, createConsoleLogger, silentLogger };
@@ -0,0 +1 @@
1
+ import{createConsoleLogger as e}from"./ConsoleLogger.js";import{silentLogger as t}from"./SilentLogger.js";import{LoggerLive as n}from"./LoggerLayer.js";export{n as LoggerLive,e as createConsoleLogger,t as silentLogger};
@@ -0,0 +1,2 @@
1
+ import { i as Logger, n as LogLevel, r as LogMetadata, t as LogEntry } from "../Logger-BFum_d5a.js";
2
+ export { LogEntry, LogLevel, LogMetadata, Logger };
@@ -0,0 +1,2 @@
1
+ import{Tag as e}from"functype";const t=e(`Logger`);export{t as Logger};
2
+ //# sourceMappingURL=Logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Logger.js","names":[],"sources":["../../src/logger/Logger.ts"],"sourcesContent":["import type { IO } from \"functype\"\nimport { Tag } from \"functype\"\n\nexport type LogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\"\n\nexport type LogMetadata = Record<string, unknown>\n\nexport type LogEntry = {\n readonly level: LogLevel\n readonly message: string\n readonly metadata?: LogMetadata\n readonly error?: Error\n readonly timestamp: Date\n}\n\nexport type Logger = {\n readonly trace: (message: string, metadata?: LogMetadata) => IO<never, never, void>\n readonly debug: (message: string, metadata?: LogMetadata) => IO<never, never, void>\n readonly info: (message: string, metadata?: LogMetadata) => IO<never, never, void>\n readonly warn: (message: string, metadata?: LogMetadata) => IO<never, never, void>\n readonly error: (message: string, metadata?: LogMetadata) => IO<never, never, void>\n readonly fatal: (message: string, metadata?: LogMetadata) => IO<never, never, void>\n readonly withError: (error: Error) => Logger\n readonly withContext: (context: LogMetadata) => Logger\n readonly child: (context?: LogMetadata) => Logger\n}\n\nexport const Logger = Tag<Logger>(\"Logger\")\n"],"mappings":"+BA2BA,MAAa,EAAS,EAAY,SAAS"}
@@ -0,0 +1,2 @@
1
+ import { i as Logger, n as LogLevel, r as LogMetadata, t as LogEntry } from "../Logger-BFum_d5a.js";
2
+ export { type LogEntry, type LogLevel, type LogMetadata, Logger };
@@ -0,0 +1 @@
1
+ import{Logger as e}from"./Logger.js";export{e as Logger};
@@ -0,0 +1,2 @@
1
+ import { n as withLogging, t as tapLog } from "../LogMiddleware-Bv7EDRjF.js";
2
+ export { tapLog, withLogging };
@@ -0,0 +1,2 @@
1
+ import{Logger as e}from"../logger/Logger.js";import{IO as t}from"functype";const n=(n,r)=>t.gen(function*(){let i=yield*t.service(e);yield*i.debug(`${n}: starting`);let a=yield*r;return yield*i.debug(`${n}: completed`),a}),r=(n,r)=>i=>t.gen(function*(){let a=yield*i,o=yield*t.service(e),s=typeof r==`string`?r:r(a);return yield*o[n](s),a});export{r as tapLog,n as withLogging};
2
+ //# sourceMappingURL=LogMiddleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogMiddleware.js","names":["IOImpl","LoggerTag"],"sources":["../../src/middleware/LogMiddleware.ts"],"sourcesContent":["import type { IO } from \"functype\"\nimport { IO as IOImpl } from \"functype\"\n\nimport type { Logger, LogLevel } from \"../logger/Logger\"\nimport { Logger as LoggerTag } from \"../logger/Logger\"\n\n/** Wrap any IO with start/complete/error logging at debug level */\nexport const withLogging = <R, E, A>(name: string, effect: IO<R, E, A>): IO<R | Logger, E, A> =>\n IOImpl.gen(function* () {\n const log = yield* IOImpl.service(LoggerTag)\n yield* log.debug(`${name}: starting`)\n const result = yield* effect\n yield* log.debug(`${name}: completed`)\n return result\n }) as IO<R | Logger, E, A>\n\n/** Create a tap function that logs at the specified level */\nexport const tapLog =\n <A>(level: LogLevel, message: string | ((a: A) => string)) =>\n <R, E>(effect: IO<R, E, A>): IO<R | Logger, E, A> =>\n IOImpl.gen(function* () {\n const result = yield* effect\n const log = yield* IOImpl.service(LoggerTag)\n const msg = typeof message === \"string\" ? message : message(result)\n yield* log[level](msg)\n return result\n }) as IO<R | Logger, E, A>\n"],"mappings":"2EAOA,MAAa,GAAwB,EAAc,IACjDA,EAAO,IAAI,WAAa,CACtB,IAAM,EAAM,MAAOA,EAAO,QAAQC,EAAU,CAC5C,MAAO,EAAI,MAAM,GAAG,EAAK,YAAY,CACrC,IAAM,EAAS,MAAO,EAEtB,OADA,MAAO,EAAI,MAAM,GAAG,EAAK,aAAa,CAC/B,GACP,CAGS,GACP,EAAiB,IACd,GACLD,EAAO,IAAI,WAAa,CACtB,IAAM,EAAS,MAAO,EAChB,EAAM,MAAOA,EAAO,QAAQC,EAAU,CACtC,EAAM,OAAO,GAAY,SAAW,EAAU,EAAQ,EAAO,CAEnE,OADA,MAAO,EAAI,GAAO,EAAI,CACf,GACP"}
@@ -0,0 +1,2 @@
1
+ import { n as withLogging, t as tapLog } from "../LogMiddleware-Bv7EDRjF.js";
2
+ export { tapLog, withLogging };
@@ -0,0 +1 @@
1
+ import{tapLog as e,withLogging as t}from"./LogMiddleware.js";export{e as tapLog,t as withLogging};
@@ -0,0 +1,2 @@
1
+ import { n as createTestLogger, t as TestLoggerHandle } from "../TestLogger-4s7rQHxR.js";
2
+ export { TestLoggerHandle, createTestLogger };
@@ -0,0 +1,2 @@
1
+ import{IO as e,List as t}from"functype";const n=(t,r,i)=>{let a=n=>(a,o)=>e.sync(()=>{t.push({level:n,message:a,metadata:Object.keys(r).length>0||o?{...r,...o}:void 0,error:i,timestamp:new Date})});return{trace:a(`trace`),debug:a(`debug`),info:a(`info`),warn:a(`warn`),error:a(`error`),fatal:a(`fatal`),withError:e=>n(t,r,e),withContext:e=>n(t,{...r,...e},i),child:e=>n(t,{...r,...e},void 0)}},r=()=>{let e=[];return{logger:n(e,{}),entries:()=>t(e),clear:()=>{e.length=0},hasEntry:(t,n)=>e.some(e=>e.level===t&&(typeof n==`string`?e.message.includes(n):n.test(e.message)))}};export{r as createTestLogger};
2
+ //# sourceMappingURL=TestLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestLogger.js","names":[],"sources":["../../src/testing/TestLogger.ts"],"sourcesContent":["import { IO, List } from \"functype\"\n\nimport type { LogEntry, Logger, LogLevel, LogMetadata } from \"../logger/Logger\"\n\nexport type TestLoggerHandle = {\n readonly logger: Logger\n readonly entries: () => List<LogEntry>\n readonly clear: () => void\n readonly hasEntry: (level: LogLevel, messagePattern: string | RegExp) => boolean\n}\n\nconst createLoggerFromStore = (store: LogEntry[], baseContext: LogMetadata, baseError?: Error): Logger => {\n const makeMethod =\n (level: LogLevel) =>\n (message: string, metadata?: LogMetadata): IO<never, never, void> =>\n IO.sync(() => {\n store.push({\n level,\n message,\n metadata: Object.keys(baseContext).length > 0 || metadata ? { ...baseContext, ...metadata } : undefined,\n error: baseError,\n timestamp: new Date(),\n })\n })\n\n return {\n trace: makeMethod(\"trace\"),\n debug: makeMethod(\"debug\"),\n info: makeMethod(\"info\"),\n warn: makeMethod(\"warn\"),\n error: makeMethod(\"error\"),\n fatal: makeMethod(\"fatal\"),\n withError: (err: Error) => createLoggerFromStore(store, baseContext, err),\n withContext: (ctx: LogMetadata) => createLoggerFromStore(store, { ...baseContext, ...ctx }, baseError),\n child: (ctx?: LogMetadata) => createLoggerFromStore(store, { ...baseContext, ...ctx }, undefined),\n }\n}\n\nexport const createTestLogger = (): TestLoggerHandle => {\n const store: LogEntry[] = []\n\n return {\n logger: createLoggerFromStore(store, {}),\n entries: () => List(store),\n clear: () => {\n store.length = 0\n },\n hasEntry: (level: LogLevel, messagePattern: string | RegExp) =>\n store.some(\n (e) =>\n e.level === level &&\n (typeof messagePattern === \"string\" ? e.message.includes(messagePattern) : messagePattern.test(e.message)),\n ),\n }\n}\n"],"mappings":"wCAWA,MAAM,GAAyB,EAAmB,EAA0B,IAA8B,CACxG,IAAM,EACH,IACA,EAAiB,IAChB,EAAG,SAAW,CACZ,EAAM,KAAK,CACT,QACA,UACA,SAAU,OAAO,KAAK,EAAY,CAAC,OAAS,GAAK,EAAW,CAAE,GAAG,EAAa,GAAG,EAAU,CAAG,IAAA,GAC9F,MAAO,EACP,UAAW,IAAI,KAChB,CAAC,EACF,CAEN,MAAO,CACL,MAAO,EAAW,QAAQ,CAC1B,MAAO,EAAW,QAAQ,CAC1B,KAAM,EAAW,OAAO,CACxB,KAAM,EAAW,OAAO,CACxB,MAAO,EAAW,QAAQ,CAC1B,MAAO,EAAW,QAAQ,CAC1B,UAAY,GAAe,EAAsB,EAAO,EAAa,EAAI,CACzE,YAAc,GAAqB,EAAsB,EAAO,CAAE,GAAG,EAAa,GAAG,EAAK,CAAE,EAAU,CACtG,MAAQ,GAAsB,EAAsB,EAAO,CAAE,GAAG,EAAa,GAAG,EAAK,CAAE,IAAA,GAAU,CAClG,EAGU,MAA2C,CACtD,IAAM,EAAoB,EAAE,CAE5B,MAAO,CACL,OAAQ,EAAsB,EAAO,EAAE,CAAC,CACxC,YAAe,EAAK,EAAM,CAC1B,UAAa,CACX,EAAM,OAAS,GAEjB,UAAW,EAAiB,IAC1B,EAAM,KACH,GACC,EAAE,QAAU,IACX,OAAO,GAAmB,SAAW,EAAE,QAAQ,SAAS,EAAe,CAAG,EAAe,KAAK,EAAE,QAAQ,EAC5G,CACJ"}
@@ -0,0 +1,2 @@
1
+ import { n as createTestLogger, t as TestLoggerHandle } from "../TestLogger-4s7rQHxR.js";
2
+ export { type TestLoggerHandle, createTestLogger };
@@ -0,0 +1 @@
1
+ import{createTestLogger as e}from"./TestLogger.js";export{e as createTestLogger};
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "functype-log",
3
+ "version": "0.1.0",
4
+ "description": "IO-native logging for functype — wraps LogLayer with Tag/Layer DI, structured logging, and test utilities",
5
+ "keywords": [
6
+ "functype",
7
+ "functional",
8
+ "logging",
9
+ "loglayer",
10
+ "io",
11
+ "dependency-injection",
12
+ "typescript"
13
+ ],
14
+ "author": "jordan.burke@gmail.com",
15
+ "license": "MIT",
16
+ "homepage": "https://github.com/jordanburke/functype-log",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/jordanburke/functype-log"
20
+ },
21
+ "peerDependencies": {
22
+ "functype": ">=0.55.0"
23
+ },
24
+ "dependencies": {
25
+ "loglayer": "^9.1.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^24.12.0",
29
+ "functype": "^0.56.0",
30
+ "ts-builds": "^2.6.3",
31
+ "tsdown": "^0.21.7"
32
+ },
33
+ "type": "module",
34
+ "main": "./dist/index.js",
35
+ "module": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js",
41
+ "default": "./dist/index.js"
42
+ },
43
+ "./logger": {
44
+ "types": "./dist/logger/index.d.ts",
45
+ "import": "./dist/logger/index.js",
46
+ "default": "./dist/logger/index.js"
47
+ },
48
+ "./layers": {
49
+ "types": "./dist/layers/index.d.ts",
50
+ "import": "./dist/layers/index.js",
51
+ "default": "./dist/layers/index.js"
52
+ },
53
+ "./testing": {
54
+ "types": "./dist/testing/index.d.ts",
55
+ "import": "./dist/testing/index.js",
56
+ "default": "./dist/testing/index.js"
57
+ },
58
+ "./middleware": {
59
+ "types": "./dist/middleware/index.d.ts",
60
+ "import": "./dist/middleware/index.js",
61
+ "default": "./dist/middleware/index.js"
62
+ }
63
+ },
64
+ "files": [
65
+ "lib",
66
+ "dist"
67
+ ],
68
+ "prettier": "ts-builds/prettier",
69
+ "engines": {
70
+ "node": ">=18.17.0"
71
+ },
72
+ "scripts": {
73
+ "validate": "ts-builds validate",
74
+ "format": "ts-builds format",
75
+ "format:check": "ts-builds format:check",
76
+ "lint": "ts-builds lint",
77
+ "lint:check": "ts-builds lint:check",
78
+ "typecheck": "ts-builds typecheck",
79
+ "test": "ts-builds test",
80
+ "test:watch": "ts-builds test:watch",
81
+ "test:coverage": "ts-builds test:coverage",
82
+ "build": "ts-builds build",
83
+ "dev": "ts-builds dev"
84
+ }
85
+ }