ff-serv 0.1.7 → 0.1.9

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/index.js CHANGED
@@ -2,192 +2,31 @@
2
2
  import { Effect as Effect3 } from "effect";
3
3
 
4
4
  // src/http/fetch-handler.ts
5
- import { Effect as Effect2, FiberSet } from "effect";
5
+ import { Effect as Effect2, FiberSet as FiberSet2 } from "effect";
6
6
  import { nanoid } from "nanoid";
7
7
 
8
8
  // src/logger.ts
9
- import {
10
- Config,
11
- Context,
12
- Effect,
13
- Logger as EffectLogger,
14
- HashMap,
15
- Layer,
16
- LogLevel,
17
- ManagedRuntime,
18
- Option
19
- } from "effect";
20
- import pino from "pino";
21
- var LoggerType;
22
- ((LoggerType2) => {
23
- function fromPino(x) {
24
- return {
25
- info: x.info.bind(x),
26
- error: x.error.bind(x),
27
- warn: x.warn.bind(x),
28
- debug: x.debug.bind(x),
29
- child: (...params) => {
30
- return fromPino(
31
- x.child(...params)
32
- );
33
- },
34
- flush: x.flush.bind(x),
35
- _pino: x
36
- };
37
- }
38
- LoggerType2.fromPino = fromPino;
39
- function create(params) {
40
- const logger = pino(
41
- {
42
- level: "trace",
43
- // Level is managed by Effect, so we set the lowest here
44
- serializers: {
45
- error: pino.stdSerializers.errWithCause,
46
- err: pino.stdSerializers.errWithCause
47
- },
48
- formatters: {
49
- level: (label) => {
50
- return { level: label };
51
- }
52
- }
53
- },
54
- params?.stream
55
- );
56
- return fromPino(logger);
57
- }
58
- LoggerType2.create = create;
59
- function is(x) {
60
- return "_pino" in x;
61
- }
62
- LoggerType2.is = is;
63
- })(LoggerType || (LoggerType = {}));
64
- var createInstance = (params) => Effect.gen(function* () {
65
- const isDev = Option.getOrNull(
66
- yield* Config.boolean("DEV").pipe(Config.option)
67
- );
68
- const logFile = Option.getOrNull(
69
- yield* Config.boolean("LOGGER_LOGFILE").pipe(Config.option)
70
- );
71
- return LoggerType.create({
72
- ...params,
73
- ...isDev && {
74
- stream: logFile ? pino.transport({
75
- target: "pino/file",
76
- options: {
77
- destination: "./logs/server.log",
78
- mkdir: true
79
- }
80
- }) : pino.transport({
81
- target: "pino-pretty",
82
- options: {
83
- ignore: "pid,hostname"
84
- }
85
- })
86
- }
87
- });
88
- });
9
+ import { Effect, FiberSet } from "effect";
89
10
  function extractParams(...[obj, msg]) {
90
11
  if (typeof obj === "string") {
91
12
  return { message: obj };
92
13
  }
93
14
  return { message: msg, attributes: obj };
94
15
  }
95
- function callPino({
96
- annotations,
97
- message
98
- }, call) {
99
- const entries = HashMap.toEntries(annotations);
100
- if (entries.length > 0) {
101
- return call(Object.fromEntries(entries), String(message));
102
- }
103
- return call(String(message));
104
- }
105
- var PinoCtx;
106
- ((PinoCtx2) => {
107
- const tag = "ff-serv/Pino";
108
- function is(obj) {
109
- return typeof obj === "object" && obj != null && "_tag" in obj && obj._tag === tag;
110
- }
111
- PinoCtx2.is = is;
112
- function create(pino2) {
113
- return {
114
- _tag: tag,
115
- pino: pino2,
116
- effectLogger: EffectLogger.make(({ logLevel, ...input }) => {
117
- switch (logLevel) {
118
- case LogLevel.Info:
119
- return callPino(input, pino2.info);
120
- case LogLevel.Debug:
121
- return callPino(input, pino2.debug);
122
- case LogLevel.Warning:
123
- return callPino(input, pino2.warn);
124
- case LogLevel.Error:
125
- case LogLevel.Fatal:
126
- return callPino(input, pino2.error);
127
- default:
128
- return callPino(input, pino2.info);
129
- }
130
- })
131
- };
132
- }
133
- PinoCtx2.create = create;
134
- })(PinoCtx || (PinoCtx = {}));
135
- var Pino = class extends Context.Tag("ff-serv/Pino")() {
136
- };
137
16
  var Logger;
138
17
  ((Logger2) => {
139
- Logger2.layer = (opts) => EffectLogger.replaceEffect(
140
- EffectLogger.defaultLogger,
141
- Effect.gen(function* () {
142
- return (yield* Pino).effectLogger;
143
- })
144
- ).pipe(
145
- Layer.provideMerge(
146
- Layer.effect(
147
- Pino,
148
- Effect.gen(function* () {
149
- return PinoCtx.create(yield* createInstance(opts));
150
- })
151
- )
152
- )
153
- );
154
18
  Logger2.sync = () => Effect.gen(function* () {
155
- const pino2 = yield* Pino;
156
- const runtime = ManagedRuntime.make(
157
- EffectLogger.replace(EffectLogger.defaultLogger, pino2.effectLogger)
158
- );
19
+ const runPromise = yield* FiberSet.makeRuntimePromise();
20
+ const run = (e) => {
21
+ void runPromise(e);
22
+ };
159
23
  return {
160
- info: (...params) => Logger2.info(...params).pipe((e) => runtime.runSync(e)),
161
- debug: (...params) => Logger2.debug(...params).pipe((e) => runtime.runSync(e)),
162
- warn: (...params) => Logger2.warn(...params).pipe((e) => runtime.runSync(e)),
163
- error: (...params) => Logger2.error(...params).pipe((e) => runtime.runSync(e))
24
+ info: (...params) => run(Logger2.info(...params)),
25
+ debug: (...params) => run(Logger2.debug(...params)),
26
+ warn: (...params) => run(Logger2.warn(...params)),
27
+ error: (...params) => run(Logger2.error(...params))
164
28
  };
165
29
  });
166
- Logger2.get = () => Logger2.sync();
167
- Logger2.replace = (logger) => (e) => Effect.gen(function* () {
168
- const oldPinoCtx = yield* Pino;
169
- const pino2 = LoggerType.is(logger) ? logger : LoggerType.fromPino(logger);
170
- const pinoCtx = PinoCtx.create(pino2);
171
- return yield* Effect.provide(
172
- e,
173
- Layer.mergeAll(
174
- EffectLogger.replace(oldPinoCtx.effectLogger, pinoCtx.effectLogger),
175
- Layer.succeed(Pino, pinoCtx)
176
- )
177
- );
178
- });
179
- Logger2.replaceChild = (...params) => (e) => Effect.gen(function* () {
180
- const oldPinoCtx = yield* Pino;
181
- const pino2 = oldPinoCtx.pino.child(...params);
182
- const pinoCtx = PinoCtx.create(pino2);
183
- return yield* Effect.provide(
184
- Effect.provide(
185
- e,
186
- EffectLogger.replace(oldPinoCtx.effectLogger, pinoCtx.effectLogger)
187
- ),
188
- Layer.succeed(Pino, pinoCtx)
189
- );
190
- });
191
30
  Logger2.info = (...params) => Effect.gen(function* () {
192
31
  const { message, attributes } = extractParams(...params);
193
32
  yield* Effect.logInfo(message).pipe(
@@ -212,7 +51,6 @@ var Logger;
212
51
  attributes ? Effect.annotateLogs(attributes) : (e) => e
213
52
  );
214
53
  });
215
- Logger2.flush = (..._params) => Effect.void;
216
54
  })(Logger || (Logger = {}));
217
55
 
218
56
  // src/http/fetch-handler.ts
@@ -223,7 +61,7 @@ var Handler = class {
223
61
  }
224
62
  };
225
63
  var createFetchHandler = (handlers, opts) => Effect2.gen(function* () {
226
- const runFork = yield* FiberSet.makeRuntimePromise();
64
+ const runFork = yield* FiberSet2.makeRuntimePromise();
227
65
  return async (request) => {
228
66
  const urlObj = new URL(request.url);
229
67
  const requestId = nanoid(6);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/http/basic.ts","../src/http/fetch-handler.ts","../src/logger.ts","../src/port.ts"],"sourcesContent":["import { Effect } from 'effect';\nimport { type AnyResponse, Handler } from './fetch-handler.js';\n\nexport namespace Path {\n\texport type Type = `/${string}` | ((url: URL) => boolean);\n\texport function matched(path: Type, url: URL) {\n\t\treturn typeof path === 'function' ? path(url) : path === url.pathname;\n\t}\n}\n\nexport namespace Fn {\n\ttype Input = [request: Request];\n\n\ttype OutputSync = AnyResponse;\n\ttype OutputEffect<R> = Effect.Effect<AnyResponse, unknown, R>;\n\n\texport type FnSync = (...input: Input) => OutputSync;\n\texport type FnEffect<R> = (...input: Input) => OutputEffect<R>;\n\texport type FnAny<R> = (...input: Input) => OutputSync | OutputEffect<R>;\n\n\texport function exec<R>(fn: FnAny<R>, ...[request]: Input) {\n\t\tconst response = fn(request);\n\t\tif (!Effect.isEffect(response)) return Effect.succeed(response);\n\t\treturn response;\n\t}\n}\n\nexport function basicHandler(\n\tpath: Path.Type,\n\tfn: Fn.FnSync,\n): Handler<'basicHandler', never>;\nexport function basicHandler<R>(\n\tpath: Path.Type,\n\tfn: Fn.FnEffect<R>,\n): Handler<'basicHandler', R>;\nexport function basicHandler<R>(path: Path.Type, fn: Fn.FnAny<R>) {\n\treturn new Handler('basicHandler', ({ url, request }) => {\n\t\tif (!Path.matched(path, url))\n\t\t\treturn Effect.succeed({ matched: false, response: undefined });\n\n\t\treturn Effect.gen(function* () {\n\t\t\treturn {\n\t\t\t\tmatched: true,\n\t\t\t\tresponse: yield* Fn.exec(fn, request),\n\t\t\t};\n\t\t});\n\t});\n}\n","import { Effect, FiberSet } from 'effect';\nimport type { Cause } from 'effect/Cause';\nimport { nanoid } from 'nanoid';\nimport { Logger } from '../logger.js';\n\n// #region Handler\n\nexport type AnyResponse = Response | Promise<Response>;\n\nexport type HandlerResult =\n\t| {\n\t\t\tmatched: true;\n\t\t\tresponse: AnyResponse;\n\t }\n\t| {\n\t\t\tmatched: false;\n\t\t\tresponse: undefined;\n\t };\n\nexport class Handler<NAME extends string, R> {\n\tconstructor(\n\t\treadonly _tag: NAME,\n\t\treadonly handle: (opt: {\n\t\t\turl: URL;\n\t\t\trequest: Request;\n\t\t}) => Effect.Effect<HandlerResult, unknown, R>,\n\t) {}\n}\n\n// #endregion\n\ntype ExtractRequirements<T> = T extends Handler<string, infer R> ? R : never;\n\nexport const createFetchHandler = <\n\tconst HANDLERS extends [\n\t\tHandler<string, unknown>,\n\t\t...Array<Handler<string, unknown>>,\n\t],\n\tR = ExtractRequirements<HANDLERS[number]>,\n>(\n\thandlers: HANDLERS,\n\topts?: {\n\t\tdebug?: boolean;\n\t\tonError?: (ctx: {\n\t\t\terror: Cause<unknown>;\n\t\t}) => Effect.Effect<unknown, unknown>;\n\t},\n) =>\n\tEffect.gen(function* () {\n\t\tconst runFork = yield* FiberSet.makeRuntimePromise<R>();\n\t\treturn async (request: Request) => {\n\t\t\tconst urlObj = new URL(request.url);\n\t\t\tconst requestId = nanoid(6);\n\n\t\t\tconst effect = Effect.gen(function* () {\n\t\t\t\tyield* Logger.info(\n\t\t\t\t\t{ request: { pathname: urlObj.pathname } },\n\t\t\t\t\t'Request started',\n\t\t\t\t);\n\n\t\t\t\tfor (const handler of handlers) {\n\t\t\t\t\tif (!handler) continue;\n\n\t\t\t\t\tconst result = yield* handler.handle({ url: urlObj, request }).pipe(\n\t\t\t\t\t\tEffect.flatMap(({ matched, response }) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tif (matched) {\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tmatched: true,\n\t\t\t\t\t\t\t\t\t\tresponse:\n\t\t\t\t\t\t\t\t\t\t\tresponse instanceof Promise\n\t\t\t\t\t\t\t\t\t\t\t\t? yield* Effect.tryPromise(() => response)\n\t\t\t\t\t\t\t\t\t\t\t\t: response,\n\t\t\t\t\t\t\t\t\t} as const;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn { matched: false, response: undefined } as const;\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tEffect.catchAllCause((error) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tyield* Logger.error(\n\t\t\t\t\t\t\t\t\t{ error },\n\t\t\t\t\t\t\t\t\t`Unhandled exception in HTTP handler '${handler._tag}'`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (opts?.onError) yield* opts.onError({ error });\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tmatched: true,\n\t\t\t\t\t\t\t\t\tresponse: new Response('Internal Server Error', {\n\t\t\t\t\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t} as const;\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\tif (opts?.debug)\n\t\t\t\t\t\tyield* Logger.debug(\n\t\t\t\t\t\t\t{ handler: handler._tag, request, result },\n\t\t\t\t\t\t\t'Processed handler',\n\t\t\t\t\t\t);\n\n\t\t\t\t\tif (!result.matched) continue;\n\t\t\t\t\treturn result.response;\n\t\t\t\t}\n\n\t\t\t\treturn new Response('Not Found', { status: 404 });\n\t\t\t}).pipe(\n\t\t\t\tEffect.tap((response) =>\n\t\t\t\t\tresponse.ok\n\t\t\t\t\t\t? Logger.info(`Request completed with status ${response.status}`)\n\t\t\t\t\t\t: Logger.warn(`Request completed with status ${response.status}`),\n\t\t\t\t),\n\t\t\t\tEffect.withSpan('http'),\n\t\t\t\tEffect.annotateLogs({ requestId }),\n\t\t\t\tEffect.scoped,\n\t\t\t) as Effect.Effect<Response, never, R>;\n\n\t\t\treturn runFork(effect);\n\t\t};\n\t});\n","import {\n\tConfig,\n\tContext,\n\tEffect,\n\tLogger as EffectLogger,\n\tHashMap,\n\tLayer,\n\tLogLevel,\n\tManagedRuntime,\n\tOption,\n} from 'effect';\nimport pino from 'pino';\n\n// Pino Instance\n\nnamespace LoggerType {\n\texport function fromPino(x: pino.Logger<never, boolean>) {\n\t\treturn {\n\t\t\tinfo: x.info.bind(x),\n\t\t\terror: x.error.bind(x),\n\t\t\twarn: x.warn.bind(x),\n\t\t\tdebug: x.debug.bind(x),\n\t\t\tchild: (...params: Parameters<typeof x.child>) => {\n\t\t\t\treturn fromPino(\n\t\t\t\t\tx.child(...params) as unknown as pino.Logger<never, boolean>,\n\t\t\t\t);\n\t\t\t},\n\t\t\tflush: x.flush.bind(x),\n\t\t\t_pino: x,\n\t\t};\n\t}\n\n\texport type CreateParams = {\n\t\tstream?: pino.DestinationStream;\n\t};\n\n\texport function create(params?: CreateParams) {\n\t\tconst logger = pino(\n\t\t\t{\n\t\t\t\tlevel: 'trace', // Level is managed by Effect, so we set the lowest here\n\t\t\t\tserializers: {\n\t\t\t\t\terror: pino.stdSerializers.errWithCause,\n\t\t\t\t\terr: pino.stdSerializers.errWithCause,\n\t\t\t\t},\n\t\t\t\tformatters: {\n\t\t\t\t\tlevel: (label) => {\n\t\t\t\t\t\treturn { level: label };\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tparams?.stream,\n\t\t);\n\t\treturn fromPino(logger);\n\t}\n\n\texport type Type = ReturnType<typeof LoggerType.create>;\n\n\texport function is(x: object): x is Type {\n\t\treturn '_pino' in x;\n\t}\n}\ntype LoggerType = LoggerType.Type;\n\n// The Gist\n\nconst createInstance = (params?: LoggerType.CreateParams) =>\n\tEffect.gen(function* () {\n\t\tconst isDev = Option.getOrNull(\n\t\t\tyield* Config.boolean('DEV').pipe(Config.option),\n\t\t);\n\t\tconst logFile = Option.getOrNull(\n\t\t\tyield* Config.boolean('LOGGER_LOGFILE').pipe(Config.option),\n\t\t);\n\n\t\treturn LoggerType.create({\n\t\t\t...params,\n\t\t\t...(isDev && {\n\t\t\t\tstream: logFile\n\t\t\t\t\t? pino.transport({\n\t\t\t\t\t\t\ttarget: 'pino/file',\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tdestination: './logs/server.log',\n\t\t\t\t\t\t\t\tmkdir: true,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t: pino.transport({\n\t\t\t\t\t\t\ttarget: 'pino-pretty',\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tignore: 'pid,hostname',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t}),\n\t\t});\n\t});\n\n// Pino Context and Effect Logger integration\n\ntype LogParams = [obj: unknown, msg?: string];\nfunction extractParams(...[obj, msg]: LogParams) {\n\tif (typeof obj === 'string') {\n\t\treturn { message: obj };\n\t}\n\treturn { message: msg, attributes: obj as Record<string, any> };\n}\n\n/** A simple helper for calling pino log functions */\nfunction callPino(\n\t{\n\t\tannotations,\n\t\tmessage,\n\t}: Pick<EffectLogger.Logger.Options<unknown>, 'annotations' | 'message'>,\n\tcall: (...params: [obj: unknown, msg?: string]) => void,\n) {\n\tconst entries = HashMap.toEntries(annotations);\n\tif (entries.length > 0) {\n\t\treturn call(Object.fromEntries(entries), String(message));\n\t}\n\n\treturn call(String(message));\n}\n\nnamespace PinoCtx {\n\tconst tag = 'ff-serv/Pino';\n\texport type Type = ReturnType<typeof create>;\n\n\texport function is(obj: unknown): obj is Type {\n\t\treturn (\n\t\t\ttypeof obj === 'object' &&\n\t\t\tobj != null &&\n\t\t\t'_tag' in obj &&\n\t\t\tobj._tag === tag\n\t\t);\n\t}\n\n\texport function create(pino: LoggerType) {\n\t\treturn {\n\t\t\t_tag: tag,\n\t\t\tpino,\n\t\t\teffectLogger: EffectLogger.make(({ logLevel, ...input }) => {\n\t\t\t\tswitch (logLevel) {\n\t\t\t\t\tcase LogLevel.Info:\n\t\t\t\t\t\treturn callPino(input, pino.info);\n\t\t\t\t\tcase LogLevel.Debug:\n\t\t\t\t\t\treturn callPino(input, pino.debug);\n\t\t\t\t\tcase LogLevel.Warning:\n\t\t\t\t\t\treturn callPino(input, pino.warn);\n\t\t\t\t\tcase LogLevel.Error:\n\t\t\t\t\tcase LogLevel.Fatal:\n\t\t\t\t\t\treturn callPino(input, pino.error);\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn callPino(input, pino.info);\n\t\t\t\t}\n\t\t\t}),\n\t\t};\n\t}\n}\ntype PinoCtx = PinoCtx.Type;\n\nclass Pino extends Context.Tag('ff-serv/Pino')<Pino, PinoCtx>() {}\n\n// Facade\n\nexport namespace Logger {\n\texport const layer = (opts?: Parameters<typeof createInstance>[0]) =>\n\t\tEffectLogger.replaceEffect(\n\t\t\tEffectLogger.defaultLogger,\n\t\t\tEffect.gen(function* () {\n\t\t\t\treturn (yield* Pino).effectLogger;\n\t\t\t}),\n\t\t).pipe(\n\t\t\tLayer.provideMerge(\n\t\t\t\tLayer.effect(\n\t\t\t\t\tPino,\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\treturn PinoCtx.create(yield* createInstance(opts));\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t),\n\t\t);\n\n\t//\n\n\texport const sync = () =>\n\t\tEffect.gen(function* () {\n\t\t\tconst pino = yield* Pino;\n\t\t\tconst runtime = ManagedRuntime.make(\n\t\t\t\tEffectLogger.replace(EffectLogger.defaultLogger, pino.effectLogger),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tinfo: (...params: Parameters<typeof Logger.info>) =>\n\t\t\t\t\tLogger.info(...params).pipe((e) => runtime.runSync(e)),\n\t\t\t\tdebug: (...params: Parameters<typeof Logger.debug>) =>\n\t\t\t\t\tLogger.debug(...params).pipe((e) => runtime.runSync(e)),\n\t\t\t\twarn: (...params: Parameters<typeof Logger.warn>) =>\n\t\t\t\t\tLogger.warn(...params).pipe((e) => runtime.runSync(e)),\n\t\t\t\terror: (...params: Parameters<typeof Logger.error>) =>\n\t\t\t\t\tLogger.error(...params).pipe((e) => runtime.runSync(e)),\n\t\t\t};\n\t\t});\n\n\t/** @deprecated — will be renamed to `sync` */\n\texport const get = () => Logger.sync();\n\n\texport const replace =\n\t\t(logger: LoggerType | pino.Logger) =>\n\t\t<A, E, R>(e: Effect.Effect<A, E, R>) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst oldPinoCtx = yield* Pino;\n\t\t\t\tconst pino = LoggerType.is(logger)\n\t\t\t\t\t? logger\n\t\t\t\t\t: LoggerType.fromPino(logger);\n\t\t\t\tconst pinoCtx = PinoCtx.create(pino);\n\n\t\t\t\treturn yield* Effect.provide(\n\t\t\t\t\te,\n\t\t\t\t\tLayer.mergeAll(\n\t\t\t\t\t\tEffectLogger.replace(oldPinoCtx.effectLogger, pinoCtx.effectLogger),\n\t\t\t\t\t\tLayer.succeed(Pino, pinoCtx),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t});\n\n\texport const replaceChild =\n\t\t(...params: Parameters<LoggerType['child']>) =>\n\t\t<A, E, R>(e: Effect.Effect<A, E, R>) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst oldPinoCtx = yield* Pino;\n\t\t\t\tconst pino = oldPinoCtx.pino.child(...params);\n\t\t\t\tconst pinoCtx = PinoCtx.create(pino);\n\n\t\t\t\treturn yield* Effect.provide(\n\t\t\t\t\tEffect.provide(\n\t\t\t\t\t\te,\n\t\t\t\t\t\tEffectLogger.replace(oldPinoCtx.effectLogger, pinoCtx.effectLogger),\n\t\t\t\t\t),\n\t\t\t\t\tLayer.succeed(Pino, pinoCtx),\n\t\t\t\t);\n\t\t\t});\n\n\t// --\n\n\texport const info = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logInfo(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\texport const debug = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logDebug(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\texport const warn = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logWarning(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\texport const error = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logError(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\t/** @deprecated */\n\texport const flush = (..._params: Parameters<LoggerType['flush']>) =>\n\t\tEffect.void;\n}\n","import { Effect } from 'effect';\nimport * as GetPort from 'get-port';\n\nexport const getPort = (options?: GetPort.Options) =>\n\tEffect.tryPromise(() => GetPort.default(options));\n"],"mappings":"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","names":["Effect","Effect","LoggerType","PinoCtx","pino","Logger","Effect","Path","Fn","Effect","Effect"]}
1
+ {"version":3,"sources":["../src/http/basic.ts","../src/http/fetch-handler.ts","../src/logger.ts","../src/port.ts"],"sourcesContent":["import { Effect } from 'effect';\nimport { type AnyResponse, Handler } from './fetch-handler.js';\n\nexport namespace Path {\n\texport type Type = `/${string}` | ((url: URL) => boolean);\n\texport function matched(path: Type, url: URL) {\n\t\treturn typeof path === 'function' ? path(url) : path === url.pathname;\n\t}\n}\n\nexport namespace Fn {\n\ttype Input = [request: Request];\n\n\ttype OutputSync = AnyResponse;\n\ttype OutputEffect<R> = Effect.Effect<AnyResponse, unknown, R>;\n\n\texport type FnSync = (...input: Input) => OutputSync;\n\texport type FnEffect<R> = (...input: Input) => OutputEffect<R>;\n\texport type FnAny<R> = (...input: Input) => OutputSync | OutputEffect<R>;\n\n\texport function exec<R>(fn: FnAny<R>, ...[request]: Input) {\n\t\tconst response = fn(request);\n\t\tif (!Effect.isEffect(response)) return Effect.succeed(response);\n\t\treturn response;\n\t}\n}\n\nexport function basicHandler(\n\tpath: Path.Type,\n\tfn: Fn.FnSync,\n): Handler<'basicHandler', never>;\nexport function basicHandler<R>(\n\tpath: Path.Type,\n\tfn: Fn.FnEffect<R>,\n): Handler<'basicHandler', R>;\nexport function basicHandler<R>(path: Path.Type, fn: Fn.FnAny<R>) {\n\treturn new Handler('basicHandler', ({ url, request }) => {\n\t\tif (!Path.matched(path, url))\n\t\t\treturn Effect.succeed({ matched: false, response: undefined });\n\n\t\treturn Effect.gen(function* () {\n\t\t\treturn {\n\t\t\t\tmatched: true,\n\t\t\t\tresponse: yield* Fn.exec(fn, request),\n\t\t\t};\n\t\t});\n\t});\n}\n","import { Effect, FiberSet } from 'effect';\nimport type { Cause } from 'effect/Cause';\nimport { nanoid } from 'nanoid';\nimport { Logger } from '../logger.js';\n\n// #region Handler\n\nexport type AnyResponse = Response | Promise<Response>;\n\nexport type HandlerResult =\n\t| {\n\t\t\tmatched: true;\n\t\t\tresponse: AnyResponse;\n\t }\n\t| {\n\t\t\tmatched: false;\n\t\t\tresponse: undefined;\n\t };\n\nexport class Handler<NAME extends string, R> {\n\tconstructor(\n\t\treadonly _tag: NAME,\n\t\treadonly handle: (opt: {\n\t\t\turl: URL;\n\t\t\trequest: Request;\n\t\t}) => Effect.Effect<HandlerResult, unknown, R>,\n\t) {}\n}\n\n// #endregion\n\ntype ExtractRequirements<T> = T extends Handler<string, infer R> ? R : never;\n\nexport const createFetchHandler = <\n\tconst HANDLERS extends [\n\t\tHandler<string, unknown>,\n\t\t...Array<Handler<string, unknown>>,\n\t],\n\tR = ExtractRequirements<HANDLERS[number]>,\n>(\n\thandlers: HANDLERS,\n\topts?: {\n\t\tdebug?: boolean;\n\t\tonError?: (ctx: {\n\t\t\terror: Cause<unknown>;\n\t\t}) => Effect.Effect<unknown, unknown>;\n\t},\n) =>\n\tEffect.gen(function* () {\n\t\tconst runFork = yield* FiberSet.makeRuntimePromise<R>();\n\t\treturn async (request: Request) => {\n\t\t\tconst urlObj = new URL(request.url);\n\t\t\tconst requestId = nanoid(6);\n\n\t\t\tconst effect = Effect.gen(function* () {\n\t\t\t\tyield* Logger.info(\n\t\t\t\t\t{ request: { pathname: urlObj.pathname } },\n\t\t\t\t\t'Request started',\n\t\t\t\t);\n\n\t\t\t\tfor (const handler of handlers) {\n\t\t\t\t\tif (!handler) continue;\n\n\t\t\t\t\tconst result = yield* handler.handle({ url: urlObj, request }).pipe(\n\t\t\t\t\t\tEffect.flatMap(({ matched, response }) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tif (matched) {\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tmatched: true,\n\t\t\t\t\t\t\t\t\t\tresponse:\n\t\t\t\t\t\t\t\t\t\t\tresponse instanceof Promise\n\t\t\t\t\t\t\t\t\t\t\t\t? yield* Effect.tryPromise(() => response)\n\t\t\t\t\t\t\t\t\t\t\t\t: response,\n\t\t\t\t\t\t\t\t\t} as const;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn { matched: false, response: undefined } as const;\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tEffect.catchAllCause((error) =>\n\t\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\t\tyield* Logger.error(\n\t\t\t\t\t\t\t\t\t{ error },\n\t\t\t\t\t\t\t\t\t`Unhandled exception in HTTP handler '${handler._tag}'`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (opts?.onError) yield* opts.onError({ error });\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tmatched: true,\n\t\t\t\t\t\t\t\t\tresponse: new Response('Internal Server Error', {\n\t\t\t\t\t\t\t\t\t\tstatus: 500,\n\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t} as const;\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\tif (opts?.debug)\n\t\t\t\t\t\tyield* Logger.debug(\n\t\t\t\t\t\t\t{ handler: handler._tag, request, result },\n\t\t\t\t\t\t\t'Processed handler',\n\t\t\t\t\t\t);\n\n\t\t\t\t\tif (!result.matched) continue;\n\t\t\t\t\treturn result.response;\n\t\t\t\t}\n\n\t\t\t\treturn new Response('Not Found', { status: 404 });\n\t\t\t}).pipe(\n\t\t\t\tEffect.tap((response) =>\n\t\t\t\t\tresponse.ok\n\t\t\t\t\t\t? Logger.info(`Request completed with status ${response.status}`)\n\t\t\t\t\t\t: Logger.warn(`Request completed with status ${response.status}`),\n\t\t\t\t),\n\t\t\t\tEffect.withSpan('http'),\n\t\t\t\tEffect.annotateLogs({ requestId }),\n\t\t\t\tEffect.scoped,\n\t\t\t) as Effect.Effect<Response, never, R>;\n\n\t\t\treturn runFork(effect);\n\t\t};\n\t});\n","import { Effect, FiberSet } from 'effect';\n\ntype LogParams = [obj: unknown, msg?: string];\nfunction extractParams(...[obj, msg]: LogParams) {\n\tif (typeof obj === 'string') {\n\t\treturn { message: obj };\n\t}\n\treturn { message: msg, attributes: obj as Record<string, any> };\n}\n\nexport namespace Logger {\n\texport const sync = () =>\n\t\tEffect.gen(function* () {\n\t\t\tconst runPromise = yield* FiberSet.makeRuntimePromise();\n\n\t\t\tconst run = (e: Effect.Effect<void, never, never>) => {\n\t\t\t\t// Intentionally ignoring the await here\n\t\t\t\tvoid runPromise(e);\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tinfo: (...params: Parameters<typeof Logger.info>) =>\n\t\t\t\t\trun(Logger.info(...params)),\n\t\t\t\tdebug: (...params: Parameters<typeof Logger.debug>) =>\n\t\t\t\t\trun(Logger.debug(...params)),\n\t\t\t\twarn: (...params: Parameters<typeof Logger.warn>) =>\n\t\t\t\t\trun(Logger.warn(...params)),\n\t\t\t\terror: (...params: Parameters<typeof Logger.error>) =>\n\t\t\t\t\trun(Logger.error(...params)),\n\t\t\t};\n\t\t});\n\n\t// --\n\n\texport const info = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logInfo(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\texport const debug = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logDebug(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\texport const warn = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logWarning(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n\n\texport const error = (...params: LogParams) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst { message, attributes } = extractParams(...params);\n\t\t\tyield* Effect.logError(message).pipe(\n\t\t\t\tattributes ? Effect.annotateLogs(attributes) : (e) => e,\n\t\t\t);\n\t\t});\n}\n","import { Effect } from 'effect';\nimport * as GetPort from 'get-port';\n\nexport const getPort = (options?: GetPort.Options) =>\n\tEffect.tryPromise(() => GetPort.default(options));\n"],"mappings":"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","names":["Effect","Effect","FiberSet","Logger","Effect","FiberSet","Path","Fn","Effect","Effect"]}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "ff-serv",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "bin": {
6
- "ff-serv": "./dist/cli"
6
+ "ff-serv": "./dist/cli.js"
7
7
  },
8
8
  "exports": {
9
9
  ".": {
@@ -44,7 +44,6 @@
44
44
  },
45
45
  "peerDependencies": {
46
46
  "effect": "^3",
47
- "pino": "^9",
48
47
  "@effect/opentelemetry": "^0.60.0",
49
48
  "get-port": "^7",
50
49
  "@orpc/server": "^1"
package/src/logger.ts CHANGED
@@ -1,99 +1,4 @@
1
- import {
2
- Config,
3
- Context,
4
- Effect,
5
- Logger as EffectLogger,
6
- HashMap,
7
- Layer,
8
- LogLevel,
9
- ManagedRuntime,
10
- Option,
11
- } from 'effect';
12
- import pino from 'pino';
13
-
14
- // Pino Instance
15
-
16
- namespace LoggerType {
17
- export function fromPino(x: pino.Logger<never, boolean>) {
18
- return {
19
- info: x.info.bind(x),
20
- error: x.error.bind(x),
21
- warn: x.warn.bind(x),
22
- debug: x.debug.bind(x),
23
- child: (...params: Parameters<typeof x.child>) => {
24
- return fromPino(
25
- x.child(...params) as unknown as pino.Logger<never, boolean>,
26
- );
27
- },
28
- flush: x.flush.bind(x),
29
- _pino: x,
30
- };
31
- }
32
-
33
- export type CreateParams = {
34
- stream?: pino.DestinationStream;
35
- };
36
-
37
- export function create(params?: CreateParams) {
38
- const logger = pino(
39
- {
40
- level: 'trace', // Level is managed by Effect, so we set the lowest here
41
- serializers: {
42
- error: pino.stdSerializers.errWithCause,
43
- err: pino.stdSerializers.errWithCause,
44
- },
45
- formatters: {
46
- level: (label) => {
47
- return { level: label };
48
- },
49
- },
50
- },
51
- params?.stream,
52
- );
53
- return fromPino(logger);
54
- }
55
-
56
- export type Type = ReturnType<typeof LoggerType.create>;
57
-
58
- export function is(x: object): x is Type {
59
- return '_pino' in x;
60
- }
61
- }
62
- type LoggerType = LoggerType.Type;
63
-
64
- // The Gist
65
-
66
- const createInstance = (params?: LoggerType.CreateParams) =>
67
- Effect.gen(function* () {
68
- const isDev = Option.getOrNull(
69
- yield* Config.boolean('DEV').pipe(Config.option),
70
- );
71
- const logFile = Option.getOrNull(
72
- yield* Config.boolean('LOGGER_LOGFILE').pipe(Config.option),
73
- );
74
-
75
- return LoggerType.create({
76
- ...params,
77
- ...(isDev && {
78
- stream: logFile
79
- ? pino.transport({
80
- target: 'pino/file',
81
- options: {
82
- destination: './logs/server.log',
83
- mkdir: true,
84
- },
85
- })
86
- : pino.transport({
87
- target: 'pino-pretty',
88
- options: {
89
- ignore: 'pid,hostname',
90
- },
91
- }),
92
- }),
93
- });
94
- });
95
-
96
- // Pino Context and Effect Logger integration
1
+ import { Effect, FiberSet } from 'effect';
97
2
 
98
3
  type LogParams = [obj: unknown, msg?: string];
99
4
  function extractParams(...[obj, msg]: LogParams) {
@@ -103,140 +8,28 @@ function extractParams(...[obj, msg]: LogParams) {
103
8
  return { message: msg, attributes: obj as Record<string, any> };
104
9
  }
105
10
 
106
- /** A simple helper for calling pino log functions */
107
- function callPino(
108
- {
109
- annotations,
110
- message,
111
- }: Pick<EffectLogger.Logger.Options<unknown>, 'annotations' | 'message'>,
112
- call: (...params: [obj: unknown, msg?: string]) => void,
113
- ) {
114
- const entries = HashMap.toEntries(annotations);
115
- if (entries.length > 0) {
116
- return call(Object.fromEntries(entries), String(message));
117
- }
118
-
119
- return call(String(message));
120
- }
121
-
122
- namespace PinoCtx {
123
- const tag = 'ff-serv/Pino';
124
- export type Type = ReturnType<typeof create>;
125
-
126
- export function is(obj: unknown): obj is Type {
127
- return (
128
- typeof obj === 'object' &&
129
- obj != null &&
130
- '_tag' in obj &&
131
- obj._tag === tag
132
- );
133
- }
134
-
135
- export function create(pino: LoggerType) {
136
- return {
137
- _tag: tag,
138
- pino,
139
- effectLogger: EffectLogger.make(({ logLevel, ...input }) => {
140
- switch (logLevel) {
141
- case LogLevel.Info:
142
- return callPino(input, pino.info);
143
- case LogLevel.Debug:
144
- return callPino(input, pino.debug);
145
- case LogLevel.Warning:
146
- return callPino(input, pino.warn);
147
- case LogLevel.Error:
148
- case LogLevel.Fatal:
149
- return callPino(input, pino.error);
150
- default:
151
- return callPino(input, pino.info);
152
- }
153
- }),
154
- };
155
- }
156
- }
157
- type PinoCtx = PinoCtx.Type;
158
-
159
- class Pino extends Context.Tag('ff-serv/Pino')<Pino, PinoCtx>() {}
160
-
161
- // Facade
162
-
163
11
  export namespace Logger {
164
- export const layer = (opts?: Parameters<typeof createInstance>[0]) =>
165
- EffectLogger.replaceEffect(
166
- EffectLogger.defaultLogger,
167
- Effect.gen(function* () {
168
- return (yield* Pino).effectLogger;
169
- }),
170
- ).pipe(
171
- Layer.provideMerge(
172
- Layer.effect(
173
- Pino,
174
- Effect.gen(function* () {
175
- return PinoCtx.create(yield* createInstance(opts));
176
- }),
177
- ),
178
- ),
179
- );
180
-
181
- //
182
-
183
12
  export const sync = () =>
184
13
  Effect.gen(function* () {
185
- const pino = yield* Pino;
186
- const runtime = ManagedRuntime.make(
187
- EffectLogger.replace(EffectLogger.defaultLogger, pino.effectLogger),
188
- );
14
+ const runPromise = yield* FiberSet.makeRuntimePromise();
15
+
16
+ const run = (e: Effect.Effect<void, never, never>) => {
17
+ // Intentionally ignoring the await here
18
+ void runPromise(e);
19
+ };
20
+
189
21
  return {
190
22
  info: (...params: Parameters<typeof Logger.info>) =>
191
- Logger.info(...params).pipe((e) => runtime.runSync(e)),
23
+ run(Logger.info(...params)),
192
24
  debug: (...params: Parameters<typeof Logger.debug>) =>
193
- Logger.debug(...params).pipe((e) => runtime.runSync(e)),
25
+ run(Logger.debug(...params)),
194
26
  warn: (...params: Parameters<typeof Logger.warn>) =>
195
- Logger.warn(...params).pipe((e) => runtime.runSync(e)),
27
+ run(Logger.warn(...params)),
196
28
  error: (...params: Parameters<typeof Logger.error>) =>
197
- Logger.error(...params).pipe((e) => runtime.runSync(e)),
29
+ run(Logger.error(...params)),
198
30
  };
199
31
  });
200
32
 
201
- /** @deprecated — will be renamed to `sync` */
202
- export const get = () => Logger.sync();
203
-
204
- export const replace =
205
- (logger: LoggerType | pino.Logger) =>
206
- <A, E, R>(e: Effect.Effect<A, E, R>) =>
207
- Effect.gen(function* () {
208
- const oldPinoCtx = yield* Pino;
209
- const pino = LoggerType.is(logger)
210
- ? logger
211
- : LoggerType.fromPino(logger);
212
- const pinoCtx = PinoCtx.create(pino);
213
-
214
- return yield* Effect.provide(
215
- e,
216
- Layer.mergeAll(
217
- EffectLogger.replace(oldPinoCtx.effectLogger, pinoCtx.effectLogger),
218
- Layer.succeed(Pino, pinoCtx),
219
- ),
220
- );
221
- });
222
-
223
- export const replaceChild =
224
- (...params: Parameters<LoggerType['child']>) =>
225
- <A, E, R>(e: Effect.Effect<A, E, R>) =>
226
- Effect.gen(function* () {
227
- const oldPinoCtx = yield* Pino;
228
- const pino = oldPinoCtx.pino.child(...params);
229
- const pinoCtx = PinoCtx.create(pino);
230
-
231
- return yield* Effect.provide(
232
- Effect.provide(
233
- e,
234
- EffectLogger.replace(oldPinoCtx.effectLogger, pinoCtx.effectLogger),
235
- ),
236
- Layer.succeed(Pino, pinoCtx),
237
- );
238
- });
239
-
240
33
  // --
241
34
 
242
35
  export const info = (...params: LogParams) =>
@@ -270,8 +63,4 @@ export namespace Logger {
270
63
  attributes ? Effect.annotateLogs(attributes) : (e) => e,
271
64
  );
272
65
  });
273
-
274
- /** @deprecated */
275
- export const flush = (..._params: Parameters<LoggerType['flush']>) =>
276
- Effect.void;
277
66
  }