ff-serv 0.1.10 → 0.1.11

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 (52) hide show
  1. package/dist/adapter-DLhjFlOu.d.cts +31 -0
  2. package/dist/adapter-DLhjFlOu.d.ts +31 -0
  3. package/dist/cli.js +47 -47
  4. package/dist/exports/cache-bun-redis.cjs +50 -0
  5. package/dist/exports/cache-bun-redis.cjs.map +1 -0
  6. package/dist/exports/cache-bun-redis.d.cts +11 -0
  7. package/dist/exports/cache-bun-redis.d.ts +11 -0
  8. package/dist/exports/cache-bun-redis.js +23 -0
  9. package/dist/exports/cache-bun-redis.js.map +1 -0
  10. package/dist/exports/cache-ioredis.cjs +51 -0
  11. package/dist/exports/cache-ioredis.cjs.map +1 -0
  12. package/dist/exports/cache-ioredis.d.cts +11 -0
  13. package/dist/exports/cache-ioredis.d.ts +11 -0
  14. package/dist/exports/cache-ioredis.js +24 -0
  15. package/dist/exports/cache-ioredis.js.map +1 -0
  16. package/dist/exports/cache.cjs +220 -0
  17. package/dist/exports/cache.cjs.map +1 -0
  18. package/dist/exports/cache.d.cts +30 -0
  19. package/dist/exports/cache.d.ts +30 -0
  20. package/dist/exports/cache.js +199 -0
  21. package/dist/exports/cache.js.map +1 -0
  22. package/dist/exports/orpc.cjs +15 -10
  23. package/dist/exports/orpc.cjs.map +1 -1
  24. package/dist/exports/orpc.js +16 -11
  25. package/dist/exports/orpc.js.map +1 -1
  26. package/dist/index.cjs +15 -10
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +10 -7
  29. package/dist/index.d.ts +10 -7
  30. package/dist/index.js +17 -12
  31. package/dist/index.js.map +1 -1
  32. package/package.json +26 -2
  33. package/src/cache/AGENTS.md +8 -0
  34. package/src/cache/adapter.test.ts +181 -0
  35. package/src/cache/adapter.ts +120 -0
  36. package/src/cache/adapters/bun-redis.ts +29 -0
  37. package/src/cache/adapters/ioredis.ts +30 -0
  38. package/src/cache/cache.test.ts +461 -0
  39. package/src/cache/cache.ts +184 -0
  40. package/src/cli/commands/db/pull.ts +5 -15
  41. package/src/cli/commands/db/shared.ts +7 -7
  42. package/src/cli/config/index.ts +1 -3
  43. package/src/cli/index.ts +1 -1
  44. package/src/cli/utils/database-source.ts +1 -4
  45. package/src/exports/cache-bun-redis.ts +1 -0
  46. package/src/exports/cache-ioredis.ts +1 -0
  47. package/src/exports/cache.ts +6 -0
  48. package/src/exports/orpc.ts +1 -1
  49. package/src/http/fetch-handler.test.ts +1 -1
  50. package/src/index.ts +1 -0
  51. package/src/logger.test.ts +168 -0
  52. package/src/logger.ts +41 -19
@@ -0,0 +1,199 @@
1
+ // src/cache/adapter.ts
2
+ import { Duration, Effect, Option, Schema } from "effect";
3
+ var CacheAdapter;
4
+ ((CacheAdapter2) => {
5
+ function memory(opts) {
6
+ return {
7
+ get: () => Effect.succeed(Option.none()),
8
+ set: () => Effect.void,
9
+ remove: () => Effect.void,
10
+ removeAll: Effect.void,
11
+ capacity: opts?.capacity
12
+ };
13
+ }
14
+ CacheAdapter2.memory = memory;
15
+ function redis(opts) {
16
+ const encodeKey = (key) => `${opts.keyPrefix}:${JSON.stringify(key)}`;
17
+ const encodeEntry = (entry) => {
18
+ if (opts.schema) {
19
+ return Schema.encode(opts.schema)(entry.value).pipe(
20
+ Effect.map(
21
+ (encoded) => JSON.stringify({ value: encoded, storedAt: entry.storedAt })
22
+ ),
23
+ Effect.orDie
24
+ );
25
+ }
26
+ return Effect.succeed(JSON.stringify(entry));
27
+ };
28
+ const decodeEntry = (raw) => {
29
+ const parsed = JSON.parse(raw);
30
+ if (opts.schema) {
31
+ return Schema.decode(opts.schema)(parsed.value).pipe(
32
+ Effect.map((value) => ({
33
+ value,
34
+ storedAt: parsed.storedAt
35
+ })),
36
+ Effect.orDie
37
+ );
38
+ }
39
+ return Effect.succeed(parsed);
40
+ };
41
+ return {
42
+ get: (key) => Effect.gen(function* () {
43
+ const raw = yield* opts.client.get(encodeKey(key));
44
+ if (Option.isNone(raw)) return Option.none();
45
+ const entry = yield* decodeEntry(raw.value);
46
+ return Option.some(entry);
47
+ }),
48
+ set: (key, entry, ttl) => Effect.gen(function* () {
49
+ const encoded = yield* encodeEntry(entry);
50
+ yield* opts.client.set(
51
+ encodeKey(key),
52
+ encoded,
53
+ Duration.toMillis(ttl)
54
+ );
55
+ }),
56
+ remove: (key) => opts.client.del(encodeKey(key)),
57
+ removeAll: Effect.void
58
+ };
59
+ }
60
+ CacheAdapter2.redis = redis;
61
+ function tiered(l1, l2) {
62
+ return {
63
+ get: (key) => Effect.gen(function* () {
64
+ const fromL1 = yield* l1.get(key);
65
+ if (Option.isSome(fromL1)) return fromL1;
66
+ return yield* l2.get(key);
67
+ }),
68
+ set: (key, entry, ttl) => Effect.all([l1.set(key, entry, ttl), l2.set(key, entry, ttl)], {
69
+ discard: true
70
+ }),
71
+ remove: (key) => Effect.all([l1.remove(key), l2.remove(key)], { discard: true }),
72
+ removeAll: Effect.all([l1.removeAll, l2.removeAll], { discard: true }),
73
+ capacity: l1.capacity
74
+ };
75
+ }
76
+ CacheAdapter2.tiered = tiered;
77
+ })(CacheAdapter || (CacheAdapter = {}));
78
+
79
+ // src/cache/cache.ts
80
+ import {
81
+ Clock,
82
+ Duration as Duration2,
83
+ Effect as Effect2,
84
+ Cache as EffectCache,
85
+ Exit,
86
+ Option as Option2
87
+ } from "effect";
88
+ var Cache;
89
+ ((Cache2) => {
90
+ function entry(value, opts) {
91
+ return { _tag: "CacheEntry", value, ttl: opts.ttl, swr: opts.swr };
92
+ }
93
+ Cache2.entry = entry;
94
+ function make(opts) {
95
+ return Effect2.gen(function* () {
96
+ const adapter = opts.adapter;
97
+ const defaultTtlMs = Duration2.toMillis(Duration2.decode(opts.ttl));
98
+ const defaultSwrMs = opts.swr ? Duration2.toMillis(Duration2.decode(opts.swr)) : 0;
99
+ const capacity = adapter?.capacity ?? Number.MAX_SAFE_INTEGER;
100
+ const refreshingKeys = /* @__PURE__ */ new Set();
101
+ const inner = yield* EffectCache.makeWith({
102
+ capacity,
103
+ lookup: (key) => Effect2.gen(function* () {
104
+ const isRefreshing = refreshingKeys.has(JSON.stringify(key));
105
+ if (adapter && !isRefreshing) {
106
+ const cached = yield* adapter.get(key);
107
+ if (Option2.isSome(cached)) {
108
+ const now = yield* Clock.currentTimeMillis;
109
+ const age = now - cached.value.storedAt;
110
+ const totalWindow = defaultTtlMs + defaultSwrMs;
111
+ if (age < totalWindow) {
112
+ return {
113
+ value: cached.value.value,
114
+ ttlMs: Math.max(0, defaultTtlMs - age),
115
+ swrMs: Math.max(
116
+ 0,
117
+ defaultSwrMs - Math.max(0, age - defaultTtlMs)
118
+ )
119
+ };
120
+ }
121
+ }
122
+ }
123
+ const result = yield* opts.lookup(key);
124
+ const cv = resolveLookupResult(result, defaultTtlMs, defaultSwrMs);
125
+ if (adapter) {
126
+ const now = yield* Clock.currentTimeMillis;
127
+ yield* adapter.set(
128
+ key,
129
+ { value: cv.value, storedAt: now },
130
+ Duration2.millis(cv.ttlMs + cv.swrMs)
131
+ );
132
+ }
133
+ return cv;
134
+ }),
135
+ timeToLive: (exit) => {
136
+ if (Exit.isSuccess(exit)) {
137
+ return Duration2.millis(exit.value.ttlMs + exit.value.swrMs);
138
+ }
139
+ return Duration2.zero;
140
+ }
141
+ });
142
+ const get = (key) => Effect2.gen(function* () {
143
+ const cv = yield* inner.get(key);
144
+ if (cv.swrMs > 0) {
145
+ const stats = yield* inner.entryStats(key);
146
+ if (Option2.isSome(stats)) {
147
+ const now = yield* Clock.currentTimeMillis;
148
+ const age = now - stats.value.loadedMillis;
149
+ if (age > cv.ttlMs) {
150
+ const keyStr = JSON.stringify(key);
151
+ if (!refreshingKeys.has(keyStr)) {
152
+ refreshingKeys.add(keyStr);
153
+ yield* Effect2.forkDaemon(
154
+ inner.refresh(key).pipe(
155
+ Effect2.ensuring(
156
+ Effect2.sync(() => {
157
+ refreshingKeys.delete(keyStr);
158
+ })
159
+ ),
160
+ Effect2.ignore
161
+ )
162
+ );
163
+ }
164
+ }
165
+ }
166
+ }
167
+ return cv.value;
168
+ });
169
+ const invalidate = (key) => Effect2.gen(function* () {
170
+ yield* inner.invalidate(key);
171
+ if (adapter) yield* adapter.remove(key);
172
+ });
173
+ const invalidateAll = Effect2.gen(function* () {
174
+ yield* inner.invalidateAll;
175
+ if (adapter) yield* adapter.removeAll;
176
+ });
177
+ return { get, invalidate, invalidateAll };
178
+ });
179
+ }
180
+ Cache2.make = make;
181
+ })(Cache || (Cache = {}));
182
+ function isCacheEntry(result) {
183
+ return typeof result === "object" && result !== null && "_tag" in result && result._tag === "CacheEntry";
184
+ }
185
+ function resolveLookupResult(result, defaultTtlMs, defaultSwrMs) {
186
+ if (isCacheEntry(result)) {
187
+ return {
188
+ value: result.value,
189
+ ttlMs: Duration2.toMillis(Duration2.decode(result.ttl)),
190
+ swrMs: result.swr ? Duration2.toMillis(Duration2.decode(result.swr)) : 0
191
+ };
192
+ }
193
+ return { value: result, ttlMs: defaultTtlMs, swrMs: defaultSwrMs };
194
+ }
195
+ export {
196
+ Cache,
197
+ CacheAdapter
198
+ };
199
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cache/adapter.ts","../../src/cache/cache.ts"],"sourcesContent":["import { Duration, Effect, Option, Schema } from 'effect';\n\nexport type CacheEntry<Value> = {\n\treadonly value: Value;\n\treadonly storedAt: number;\n};\n\nexport type RedisClient = {\n\treadonly get: (key: string) => Effect.Effect<Option.Option<string>>;\n\treadonly set: (\n\t\tkey: string,\n\t\tvalue: string,\n\t\tttlMs: number,\n\t) => Effect.Effect<void>;\n\treadonly del: (key: string) => Effect.Effect<void>;\n};\n\nexport type CacheAdapter<Key, Value> = {\n\treadonly get: (key: Key) => Effect.Effect<Option.Option<CacheEntry<Value>>>;\n\treadonly set: (\n\t\tkey: Key,\n\t\tentry: CacheEntry<Value>,\n\t\tttl: Duration.Duration,\n\t) => Effect.Effect<void>;\n\treadonly remove: (key: Key) => Effect.Effect<void>;\n\treadonly removeAll: Effect.Effect<void>;\n\treadonly capacity?: number;\n};\n\nexport namespace CacheAdapter {\n\t// No-op adapter — Effect Cache IS the in-memory store. This only carries `capacity`.\n\texport function memory<Key, Value>(opts?: {\n\t\tcapacity?: number;\n\t}): CacheAdapter<Key, Value> {\n\t\treturn {\n\t\t\tget: () => Effect.succeed(Option.none()),\n\t\t\tset: () => Effect.void,\n\t\t\tremove: () => Effect.void,\n\t\t\tremoveAll: Effect.void,\n\t\t\tcapacity: opts?.capacity,\n\t\t};\n\t}\n\n\texport function redis<Key, Value>(opts: {\n\t\tclient: RedisClient;\n\t\tkeyPrefix: string;\n\t\tschema?: Schema.Schema<Value, string>;\n\t}): CacheAdapter<Key, Value> {\n\t\tconst encodeKey = (key: Key) => `${opts.keyPrefix}:${JSON.stringify(key)}`;\n\n\t\tconst encodeEntry = (entry: CacheEntry<Value>): Effect.Effect<string> => {\n\t\t\tif (opts.schema) {\n\t\t\t\treturn Schema.encode(opts.schema)(entry.value).pipe(\n\t\t\t\t\tEffect.map((encoded) =>\n\t\t\t\t\t\tJSON.stringify({ value: encoded, storedAt: entry.storedAt }),\n\t\t\t\t\t),\n\t\t\t\t\tEffect.orDie,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn Effect.succeed(JSON.stringify(entry));\n\t\t};\n\n\t\tconst decodeEntry = (raw: string): Effect.Effect<CacheEntry<Value>> => {\n\t\t\tconst parsed = JSON.parse(raw);\n\t\t\tif (opts.schema) {\n\t\t\t\treturn Schema.decode(opts.schema)(parsed.value).pipe(\n\t\t\t\t\tEffect.map((value) => ({\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tstoredAt: parsed.storedAt as number,\n\t\t\t\t\t})),\n\t\t\t\t\tEffect.orDie,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn Effect.succeed(parsed as CacheEntry<Value>);\n\t\t};\n\n\t\treturn {\n\t\t\tget: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst raw = yield* opts.client.get(encodeKey(key));\n\t\t\t\t\tif (Option.isNone(raw)) return Option.none<CacheEntry<Value>>();\n\t\t\t\t\tconst entry = yield* decodeEntry(raw.value);\n\t\t\t\t\treturn Option.some(entry);\n\t\t\t\t}),\n\t\t\tset: (key, entry, ttl) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst encoded = yield* encodeEntry(entry);\n\t\t\t\t\tyield* opts.client.set(\n\t\t\t\t\t\tencodeKey(key),\n\t\t\t\t\t\tencoded,\n\t\t\t\t\t\tDuration.toMillis(ttl),\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\tremove: (key) => opts.client.del(encodeKey(key)),\n\t\t\tremoveAll: Effect.void,\n\t\t};\n\t}\n\n\texport function tiered<Key, Value>(\n\t\tl1: CacheAdapter<Key, Value>,\n\t\tl2: CacheAdapter<Key, Value>,\n\t): CacheAdapter<Key, Value> {\n\t\treturn {\n\t\t\tget: (key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst fromL1 = yield* l1.get(key);\n\t\t\t\t\tif (Option.isSome(fromL1)) return fromL1;\n\t\t\t\t\treturn yield* l2.get(key);\n\t\t\t\t}),\n\t\t\tset: (key, entry, ttl) =>\n\t\t\t\tEffect.all([l1.set(key, entry, ttl), l2.set(key, entry, ttl)], {\n\t\t\t\t\tdiscard: true,\n\t\t\t\t}),\n\t\t\tremove: (key) =>\n\t\t\t\tEffect.all([l1.remove(key), l2.remove(key)], { discard: true }),\n\t\t\tremoveAll: Effect.all([l1.removeAll, l2.removeAll], { discard: true }),\n\t\t\tcapacity: l1.capacity,\n\t\t};\n\t}\n}\n","import {\n\tClock,\n\tDuration,\n\tEffect,\n\tCache as EffectCache,\n\tExit,\n\tOption,\n} from 'effect';\nimport type { CacheAdapter, CacheEntry } from './adapter.js';\n\n// Bundles value with resolved TTL/SWR so the SWR check at read time uses per-entry durations\ntype CacheValue<Value> = {\n\treadonly value: Value;\n\treadonly ttlMs: number;\n\treadonly swrMs: number;\n};\n\nexport type CacheInstance<Key, Value, Error> = {\n\treadonly get: (key: Key) => Effect.Effect<Value, Error>;\n\treadonly invalidate: (key: Key) => Effect.Effect<void>;\n\treadonly invalidateAll: Effect.Effect<void>;\n};\n\nexport namespace Cache {\n\texport type Entry<Value> = {\n\t\treadonly _tag: 'CacheEntry';\n\t\treadonly value: Value;\n\t\treadonly ttl: Duration.DurationInput;\n\t\treadonly swr?: Duration.DurationInput;\n\t};\n\n\texport function entry<Value>(\n\t\tvalue: Value,\n\t\topts: { ttl: Duration.DurationInput; swr?: Duration.DurationInput },\n\t): Entry<Value> {\n\t\treturn { _tag: 'CacheEntry', value, ttl: opts.ttl, swr: opts.swr };\n\t}\n\n\texport type LookupResult<Value> = Value | Entry<Value>;\n\n\texport function make<Key, Value, Error = never, R = never>(opts: {\n\t\tttl: Duration.DurationInput;\n\t\tswr?: Duration.DurationInput;\n\t\tlookup: (key: Key) => Effect.Effect<LookupResult<Value>, Error, R>;\n\t\tadapter?: CacheAdapter<Key, Value>;\n\t}): Effect.Effect<CacheInstance<Key, Value, Error>, never, R> {\n\t\treturn Effect.gen(function* () {\n\t\t\tconst adapter = opts.adapter;\n\t\t\tconst defaultTtlMs = Duration.toMillis(Duration.decode(opts.ttl));\n\t\t\tconst defaultSwrMs = opts.swr\n\t\t\t\t? Duration.toMillis(Duration.decode(opts.swr))\n\t\t\t\t: 0;\n\t\t\tconst capacity = adapter?.capacity ?? Number.MAX_SAFE_INTEGER;\n\n\t\t\t// Safe without synchronization — no yield points between has() and add() (cooperative scheduling)\n\t\t\tconst refreshingKeys = new Set<string>();\n\n\t\t\t// makeWith uses `timeToLive: (exit) => Duration` — the lookup stores CacheValue\n\t\t\t// so timeToLive can extract the total window (ttl + swr) from the exit result\n\t\t\tconst inner = yield* EffectCache.makeWith({\n\t\t\t\tcapacity,\n\t\t\t\tlookup: (key: Key) =>\n\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\tconst isRefreshing = refreshingKeys.has(JSON.stringify(key));\n\n\t\t\t\t\t\tif (adapter && !isRefreshing) {\n\t\t\t\t\t\t\tconst cached = yield* adapter.get(key);\n\t\t\t\t\t\t\tif (Option.isSome(cached)) {\n\t\t\t\t\t\t\t\tconst now = yield* Clock.currentTimeMillis;\n\t\t\t\t\t\t\t\tconst age = now - cached.value.storedAt;\n\t\t\t\t\t\t\t\tconst totalWindow = defaultTtlMs + defaultSwrMs;\n\t\t\t\t\t\t\t\tif (age < totalWindow) {\n\t\t\t\t\t\t\t\t\t// Adjust remaining TTL/SWR for elapsed age so SWR triggers at correct real-world time\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tvalue: cached.value.value,\n\t\t\t\t\t\t\t\t\t\tttlMs: Math.max(0, defaultTtlMs - age),\n\t\t\t\t\t\t\t\t\t\tswrMs: Math.max(\n\t\t\t\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\t\t\t\tdefaultSwrMs - Math.max(0, age - defaultTtlMs),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t} satisfies CacheValue<Value>;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst result = yield* opts.lookup(key);\n\t\t\t\t\t\tconst cv = resolveLookupResult(result, defaultTtlMs, defaultSwrMs);\n\n\t\t\t\t\t\tif (adapter) {\n\t\t\t\t\t\t\tconst now = yield* Clock.currentTimeMillis;\n\t\t\t\t\t\t\tyield* adapter.set(\n\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t{ value: cv.value, storedAt: now } satisfies CacheEntry<Value>,\n\t\t\t\t\t\t\t\tDuration.millis(cv.ttlMs + cv.swrMs),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn cv;\n\t\t\t\t\t}),\n\t\t\t\ttimeToLive: (exit) => {\n\t\t\t\t\tif (Exit.isSuccess(exit)) {\n\t\t\t\t\t\treturn Duration.millis(exit.value.ttlMs + exit.value.swrMs);\n\t\t\t\t\t}\n\t\t\t\t\treturn Duration.zero;\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst get = (key: Key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst cv = yield* inner.get(key);\n\n\t\t\t\t\tif (cv.swrMs > 0) {\n\t\t\t\t\t\tconst stats = yield* inner.entryStats(key);\n\t\t\t\t\t\tif (Option.isSome(stats)) {\n\t\t\t\t\t\t\tconst now = yield* Clock.currentTimeMillis;\n\t\t\t\t\t\t\tconst age = now - stats.value.loadedMillis;\n\t\t\t\t\t\t\tif (age > cv.ttlMs) {\n\t\t\t\t\t\t\t\tconst keyStr = JSON.stringify(key);\n\t\t\t\t\t\t\t\tif (!refreshingKeys.has(keyStr)) {\n\t\t\t\t\t\t\t\t\trefreshingKeys.add(keyStr);\n\t\t\t\t\t\t\t\t\t// refresh() recomputes without invalidating, so stale value remains available during recomputation\n\t\t\t\t\t\t\t\t\tyield* Effect.forkDaemon(\n\t\t\t\t\t\t\t\t\t\tinner.refresh(key).pipe(\n\t\t\t\t\t\t\t\t\t\t\tEffect.ensuring(\n\t\t\t\t\t\t\t\t\t\t\t\tEffect.sync(() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\trefreshingKeys.delete(keyStr);\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\tEffect.ignore,\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\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\treturn cv.value;\n\t\t\t\t});\n\n\t\t\tconst invalidate = (key: Key) =>\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tyield* inner.invalidate(key);\n\t\t\t\t\tif (adapter) yield* adapter.remove(key);\n\t\t\t\t});\n\n\t\t\tconst invalidateAll = Effect.gen(function* () {\n\t\t\t\tyield* inner.invalidateAll;\n\t\t\t\tif (adapter) yield* adapter.removeAll;\n\t\t\t});\n\n\t\t\treturn { get, invalidate, invalidateAll } satisfies CacheInstance<\n\t\t\t\tKey,\n\t\t\t\tValue,\n\t\t\t\tError\n\t\t\t>;\n\t\t});\n\t}\n}\n\nfunction isCacheEntry<Value>(\n\tresult: Cache.LookupResult<Value>,\n): result is Cache.Entry<Value> {\n\treturn (\n\t\ttypeof result === 'object' &&\n\t\tresult !== null &&\n\t\t'_tag' in result &&\n\t\t(result as Cache.Entry<Value>)._tag === 'CacheEntry'\n\t);\n}\n\nfunction resolveLookupResult<Value>(\n\tresult: Cache.LookupResult<Value>,\n\tdefaultTtlMs: number,\n\tdefaultSwrMs: number,\n): CacheValue<Value> {\n\tif (isCacheEntry(result)) {\n\t\treturn {\n\t\t\tvalue: result.value,\n\t\t\tttlMs: Duration.toMillis(Duration.decode(result.ttl)),\n\t\t\tswrMs: result.swr ? Duration.toMillis(Duration.decode(result.swr)) : 0,\n\t\t};\n\t}\n\treturn { value: result, ttlMs: defaultTtlMs, swrMs: defaultSwrMs };\n}\n"],"mappings":"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","names":["CacheAdapter","Duration","Effect","Option","Cache"]}
@@ -39,19 +39,24 @@ function extractParams(...[obj, msg]) {
39
39
  }
40
40
  return { message: msg, attributes: obj };
41
41
  }
42
+ function makeSyncLogger(runtime, annotations) {
43
+ const run = (e) => {
44
+ const annotated = Object.keys(annotations).length > 0 ? e.pipe(import_effect.Effect.annotateLogs(annotations)) : e;
45
+ void import_effect.Runtime.runPromise(runtime)(annotated);
46
+ };
47
+ return {
48
+ info: (...params) => run(Logger.info(...params)),
49
+ debug: (...params) => run(Logger.debug(...params)),
50
+ warn: (...params) => run(Logger.warn(...params)),
51
+ error: (...params) => run(Logger.error(...params)),
52
+ child: (childAnnotations) => makeSyncLogger(runtime, { ...annotations, ...childAnnotations })
53
+ };
54
+ }
42
55
  var Logger;
43
56
  ((Logger2) => {
44
- Logger2.sync = () => import_effect.Effect.gen(function* () {
57
+ Logger2.sync = (annotations) => import_effect.Effect.gen(function* () {
45
58
  const runtime = yield* import_effect.Effect.runtime();
46
- const run = (e) => {
47
- void import_effect.Runtime.runPromise(runtime)(e);
48
- };
49
- return {
50
- info: (...params) => run(Logger2.info(...params)),
51
- debug: (...params) => run(Logger2.debug(...params)),
52
- warn: (...params) => run(Logger2.warn(...params)),
53
- error: (...params) => run(Logger2.error(...params))
54
- };
59
+ return makeSyncLogger(runtime, annotations ?? {});
55
60
  });
56
61
  Logger2.info = (...params) => import_effect.Effect.gen(function* () {
57
62
  const { message, attributes } = extractParams(...params);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/orpc.ts","../../src/http/orpc.ts","../../src/http/fetch-handler.ts","../../src/logger.ts"],"sourcesContent":["export { oRPCHandler } from '../http/orpc.js';","import type { Context } from '@orpc/server';\nimport type { FetchHandler } from '@orpc/server/fetch';\nimport type { FriendlyStandardHandleOptions } from '@orpc/server/standard';\nimport { Effect } from 'effect';\nimport { Handler } from './fetch-handler.js';\n\ntype MaybeOptionalOptions<TOptions> =\n\tRecord<never, never> extends TOptions\n\t\t? [options?: TOptions]\n\t\t: [options: TOptions];\n\ntype PossibleOpt<T extends Context, E, R> =\n\t| FriendlyStandardHandleOptions<T>\n\t| Effect.Effect<FriendlyStandardHandleOptions<T>, E, R>;\n\nfunction getValue<T extends Context, E, R>(opt: PossibleOpt<T, E, R>) {\n\treturn Effect.isEffect(opt) ? opt : Effect.succeed(opt);\n}\n\nexport function oRPCHandler<T extends Context, E, R>(\n\thandler: FetchHandler<T>,\n\topt?: PossibleOpt<T, E, R> | ((request: Request) => PossibleOpt<T, E, R>),\n) {\n\treturn new Handler('oRPCHandler', ({ request }) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst _opt = (\n\t\t\t\topt\n\t\t\t\t\t? [yield* getValue(typeof opt === 'function' ? opt(request) : opt)]\n\t\t\t\t\t: []\n\t\t\t) as MaybeOptionalOptions<FriendlyStandardHandleOptions<T>>;\n\t\t\treturn yield* Effect.tryPromise(() => handler.handle(request, ..._opt));\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, Runtime } 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 runtime = yield* Effect.runtime();\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\t// void runPromise(e);\n\t\t\t\tvoid Runtime.runPromise(runtime)(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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGA,IAAAA,iBAAuB;;;ACHvB,IAAAC,iBAAiC;AAEjC,oBAAuB;;;ACFvB,oBAA0C;AAG1C,SAAS,iBAAiB,CAAC,KAAK,GAAG,GAAc;AAChD,MAAI,OAAO,QAAQ,UAAU;AAC5B,WAAO,EAAE,SAAS,IAAI;AAAA,EACvB;AACA,SAAO,EAAE,SAAS,KAAK,YAAY,IAA2B;AAC/D;AAEO,IAAU;AAAA,CAAV,CAAUC,YAAV;AACC,EAAMA,QAAA,OAAO,MACnB,qBAAO,IAAI,aAAa;AACvB,UAAM,UAAU,OAAO,qBAAO,QAAQ;AAEtC,UAAM,MAAM,CAAC,MAAyC;AAGrD,WAAK,sBAAQ,WAAW,OAAO,EAAE,CAAC;AAAA,IACnC;AAEA,WAAO;AAAA,MACN,MAAM,IAAI,WACT,IAAIA,QAAO,KAAK,GAAG,MAAM,CAAC;AAAA,MAC3B,OAAO,IAAI,WACV,IAAIA,QAAO,MAAM,GAAG,MAAM,CAAC;AAAA,MAC5B,MAAM,IAAI,WACT,IAAIA,QAAO,KAAK,GAAG,MAAM,CAAC;AAAA,MAC3B,OAAO,IAAI,WACV,IAAIA,QAAO,MAAM,GAAG,MAAM,CAAC;AAAA,IAC7B;AAAA,EACD,CAAC;AAIK,EAAMA,QAAA,OAAO,IAAI,WACvB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,QAAQ,OAAO,EAAE;AAAA,MAC9B,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,OAAO,IAAI,WACvB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,WAAW,OAAO,EAAE;AAAA,MACjC,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAAA,GAvDc;;;ADSV,IAAM,UAAN,MAAsC;AAAA,EAC5C,YACU,MACA,QAIR;AALQ;AACA;AAAA,EAIP;AACJ;;;ADZA,SAAS,SAAkC,KAA2B;AACrE,SAAO,sBAAO,SAAS,GAAG,IAAI,MAAM,sBAAO,QAAQ,GAAG;AACvD;AAEO,SAAS,YACf,SACA,KACC;AACD,SAAO,IAAI;AAAA,IAAQ;AAAA,IAAe,CAAC,EAAE,QAAQ,MAC5C,sBAAO,IAAI,aAAa;AACvB,YAAM,OACL,MACG,CAAC,OAAO,SAAS,OAAO,QAAQ,aAAa,IAAI,OAAO,IAAI,GAAG,CAAC,IAChE,CAAC;AAEL,aAAO,OAAO,sBAAO,WAAW,MAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,CAAC;AAAA,IACvE,CAAC;AAAA,EACF;AACD;","names":["import_effect","import_effect","Logger"]}
1
+ {"version":3,"sources":["../../src/exports/orpc.ts","../../src/http/orpc.ts","../../src/http/fetch-handler.ts","../../src/logger.ts"],"sourcesContent":["export { oRPCHandler } from '../http/orpc.js';\n","import type { Context } from '@orpc/server';\nimport type { FetchHandler } from '@orpc/server/fetch';\nimport type { FriendlyStandardHandleOptions } from '@orpc/server/standard';\nimport { Effect } from 'effect';\nimport { Handler } from './fetch-handler.js';\n\ntype MaybeOptionalOptions<TOptions> =\n\tRecord<never, never> extends TOptions\n\t\t? [options?: TOptions]\n\t\t: [options: TOptions];\n\ntype PossibleOpt<T extends Context, E, R> =\n\t| FriendlyStandardHandleOptions<T>\n\t| Effect.Effect<FriendlyStandardHandleOptions<T>, E, R>;\n\nfunction getValue<T extends Context, E, R>(opt: PossibleOpt<T, E, R>) {\n\treturn Effect.isEffect(opt) ? opt : Effect.succeed(opt);\n}\n\nexport function oRPCHandler<T extends Context, E, R>(\n\thandler: FetchHandler<T>,\n\topt?: PossibleOpt<T, E, R> | ((request: Request) => PossibleOpt<T, E, R>),\n) {\n\treturn new Handler('oRPCHandler', ({ request }) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst _opt = (\n\t\t\t\topt\n\t\t\t\t\t? [yield* getValue(typeof opt === 'function' ? opt(request) : opt)]\n\t\t\t\t\t: []\n\t\t\t) as MaybeOptionalOptions<FriendlyStandardHandleOptions<T>>;\n\t\t\treturn yield* Effect.tryPromise(() => handler.handle(request, ..._opt));\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, Runtime } 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\t// biome-ignore lint/suspicious/noExplicitAny: log attributes are unstructured\n\treturn { message: msg, attributes: obj as Record<string, any> };\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: log annotations are unstructured\ntype LogAnnotations = Record<string, any>;\n\nexport type SyncLogger = {\n\tinfo: (...params: Parameters<typeof Logger.info>) => void;\n\tdebug: (...params: Parameters<typeof Logger.debug>) => void;\n\twarn: (...params: Parameters<typeof Logger.warn>) => void;\n\terror: (...params: Parameters<typeof Logger.error>) => void;\n\tchild: (annotations: LogAnnotations) => SyncLogger;\n};\n\nfunction makeSyncLogger(\n\truntime: Runtime.Runtime<never>,\n\tannotations: LogAnnotations,\n): SyncLogger {\n\tconst run = (e: Effect.Effect<void, never, never>) => {\n\t\tconst annotated =\n\t\t\tObject.keys(annotations).length > 0\n\t\t\t\t? e.pipe(Effect.annotateLogs(annotations))\n\t\t\t\t: e;\n\t\tvoid Runtime.runPromise(runtime)(annotated);\n\t};\n\n\treturn {\n\t\tinfo: (...params: Parameters<typeof Logger.info>) =>\n\t\t\trun(Logger.info(...params)),\n\t\tdebug: (...params: Parameters<typeof Logger.debug>) =>\n\t\t\trun(Logger.debug(...params)),\n\t\twarn: (...params: Parameters<typeof Logger.warn>) =>\n\t\t\trun(Logger.warn(...params)),\n\t\terror: (...params: Parameters<typeof Logger.error>) =>\n\t\t\trun(Logger.error(...params)),\n\t\tchild: (childAnnotations: LogAnnotations) =>\n\t\t\tmakeSyncLogger(runtime, { ...annotations, ...childAnnotations }),\n\t};\n}\n\nexport namespace Logger {\n\texport const sync = (annotations?: LogAnnotations) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst runtime = yield* Effect.runtime();\n\t\t\treturn makeSyncLogger(runtime, annotations ?? {});\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"],"mappings":"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","names":["import_effect","import_effect","Logger"]}
@@ -2,7 +2,7 @@
2
2
  import { Effect as Effect3 } from "effect";
3
3
 
4
4
  // src/http/fetch-handler.ts
5
- import { Effect as Effect2, FiberSet as FiberSet2 } from "effect";
5
+ import { Effect as Effect2, FiberSet } from "effect";
6
6
  import { nanoid } from "nanoid";
7
7
 
8
8
  // src/logger.ts
@@ -13,19 +13,24 @@ function extractParams(...[obj, msg]) {
13
13
  }
14
14
  return { message: msg, attributes: obj };
15
15
  }
16
+ function makeSyncLogger(runtime, annotations) {
17
+ const run = (e) => {
18
+ const annotated = Object.keys(annotations).length > 0 ? e.pipe(Effect.annotateLogs(annotations)) : e;
19
+ void Runtime.runPromise(runtime)(annotated);
20
+ };
21
+ return {
22
+ info: (...params) => run(Logger.info(...params)),
23
+ debug: (...params) => run(Logger.debug(...params)),
24
+ warn: (...params) => run(Logger.warn(...params)),
25
+ error: (...params) => run(Logger.error(...params)),
26
+ child: (childAnnotations) => makeSyncLogger(runtime, { ...annotations, ...childAnnotations })
27
+ };
28
+ }
16
29
  var Logger;
17
30
  ((Logger2) => {
18
- Logger2.sync = () => Effect.gen(function* () {
31
+ Logger2.sync = (annotations) => Effect.gen(function* () {
19
32
  const runtime = yield* Effect.runtime();
20
- const run = (e) => {
21
- void Runtime.runPromise(runtime)(e);
22
- };
23
- return {
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))
28
- };
33
+ return makeSyncLogger(runtime, annotations ?? {});
29
34
  });
30
35
  Logger2.info = (...params) => Effect.gen(function* () {
31
36
  const { message, attributes } = extractParams(...params);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/http/orpc.ts","../../src/http/fetch-handler.ts","../../src/logger.ts"],"sourcesContent":["import type { Context } from '@orpc/server';\nimport type { FetchHandler } from '@orpc/server/fetch';\nimport type { FriendlyStandardHandleOptions } from '@orpc/server/standard';\nimport { Effect } from 'effect';\nimport { Handler } from './fetch-handler.js';\n\ntype MaybeOptionalOptions<TOptions> =\n\tRecord<never, never> extends TOptions\n\t\t? [options?: TOptions]\n\t\t: [options: TOptions];\n\ntype PossibleOpt<T extends Context, E, R> =\n\t| FriendlyStandardHandleOptions<T>\n\t| Effect.Effect<FriendlyStandardHandleOptions<T>, E, R>;\n\nfunction getValue<T extends Context, E, R>(opt: PossibleOpt<T, E, R>) {\n\treturn Effect.isEffect(opt) ? opt : Effect.succeed(opt);\n}\n\nexport function oRPCHandler<T extends Context, E, R>(\n\thandler: FetchHandler<T>,\n\topt?: PossibleOpt<T, E, R> | ((request: Request) => PossibleOpt<T, E, R>),\n) {\n\treturn new Handler('oRPCHandler', ({ request }) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst _opt = (\n\t\t\t\topt\n\t\t\t\t\t? [yield* getValue(typeof opt === 'function' ? opt(request) : opt)]\n\t\t\t\t\t: []\n\t\t\t) as MaybeOptionalOptions<FriendlyStandardHandleOptions<T>>;\n\t\t\treturn yield* Effect.tryPromise(() => handler.handle(request, ..._opt));\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, Runtime } 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 runtime = yield* Effect.runtime();\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\t// void runPromise(e);\n\t\t\t\tvoid Runtime.runPromise(runtime)(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"],"mappings":";AAGA,SAAS,UAAAA,eAAc;;;ACHvB,SAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAEjC,SAAS,cAAc;;;ACFvB,SAAS,QAAkB,eAAe;AAG1C,SAAS,iBAAiB,CAAC,KAAK,GAAG,GAAc;AAChD,MAAI,OAAO,QAAQ,UAAU;AAC5B,WAAO,EAAE,SAAS,IAAI;AAAA,EACvB;AACA,SAAO,EAAE,SAAS,KAAK,YAAY,IAA2B;AAC/D;AAEO,IAAU;AAAA,CAAV,CAAUC,YAAV;AACC,EAAMA,QAAA,OAAO,MACnB,OAAO,IAAI,aAAa;AACvB,UAAM,UAAU,OAAO,OAAO,QAAQ;AAEtC,UAAM,MAAM,CAAC,MAAyC;AAGrD,WAAK,QAAQ,WAAW,OAAO,EAAE,CAAC;AAAA,IACnC;AAEA,WAAO;AAAA,MACN,MAAM,IAAI,WACT,IAAIA,QAAO,KAAK,GAAG,MAAM,CAAC;AAAA,MAC3B,OAAO,IAAI,WACV,IAAIA,QAAO,MAAM,GAAG,MAAM,CAAC;AAAA,MAC5B,MAAM,IAAI,WACT,IAAIA,QAAO,KAAK,GAAG,MAAM,CAAC;AAAA,MAC3B,OAAO,IAAI,WACV,IAAIA,QAAO,MAAM,GAAG,MAAM,CAAC;AAAA,IAC7B;AAAA,EACD,CAAC;AAIK,EAAMA,QAAA,OAAO,IAAI,WACvB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,QAAQ,OAAO,EAAE;AAAA,MAC9B,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,OAAO,IAAI,WACvB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,WAAW,OAAO,EAAE;AAAA,MACjC,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAAA,GAvDc;;;ADSV,IAAM,UAAN,MAAsC;AAAA,EAC5C,YACU,MACA,QAIR;AALQ;AACA;AAAA,EAIP;AACJ;;;ADZA,SAAS,SAAkC,KAA2B;AACrE,SAAOC,QAAO,SAAS,GAAG,IAAI,MAAMA,QAAO,QAAQ,GAAG;AACvD;AAEO,SAAS,YACf,SACA,KACC;AACD,SAAO,IAAI;AAAA,IAAQ;AAAA,IAAe,CAAC,EAAE,QAAQ,MAC5CA,QAAO,IAAI,aAAa;AACvB,YAAM,OACL,MACG,CAAC,OAAO,SAAS,OAAO,QAAQ,aAAa,IAAI,OAAO,IAAI,GAAG,CAAC,IAChE,CAAC;AAEL,aAAO,OAAOA,QAAO,WAAW,MAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,CAAC;AAAA,IACvE,CAAC;AAAA,EACF;AACD;","names":["Effect","Effect","FiberSet","Logger","Effect"]}
1
+ {"version":3,"sources":["../../src/http/orpc.ts","../../src/http/fetch-handler.ts","../../src/logger.ts"],"sourcesContent":["import type { Context } from '@orpc/server';\nimport type { FetchHandler } from '@orpc/server/fetch';\nimport type { FriendlyStandardHandleOptions } from '@orpc/server/standard';\nimport { Effect } from 'effect';\nimport { Handler } from './fetch-handler.js';\n\ntype MaybeOptionalOptions<TOptions> =\n\tRecord<never, never> extends TOptions\n\t\t? [options?: TOptions]\n\t\t: [options: TOptions];\n\ntype PossibleOpt<T extends Context, E, R> =\n\t| FriendlyStandardHandleOptions<T>\n\t| Effect.Effect<FriendlyStandardHandleOptions<T>, E, R>;\n\nfunction getValue<T extends Context, E, R>(opt: PossibleOpt<T, E, R>) {\n\treturn Effect.isEffect(opt) ? opt : Effect.succeed(opt);\n}\n\nexport function oRPCHandler<T extends Context, E, R>(\n\thandler: FetchHandler<T>,\n\topt?: PossibleOpt<T, E, R> | ((request: Request) => PossibleOpt<T, E, R>),\n) {\n\treturn new Handler('oRPCHandler', ({ request }) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst _opt = (\n\t\t\t\topt\n\t\t\t\t\t? [yield* getValue(typeof opt === 'function' ? opt(request) : opt)]\n\t\t\t\t\t: []\n\t\t\t) as MaybeOptionalOptions<FriendlyStandardHandleOptions<T>>;\n\t\t\treturn yield* Effect.tryPromise(() => handler.handle(request, ..._opt));\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, Runtime } 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\t// biome-ignore lint/suspicious/noExplicitAny: log attributes are unstructured\n\treturn { message: msg, attributes: obj as Record<string, any> };\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: log annotations are unstructured\ntype LogAnnotations = Record<string, any>;\n\nexport type SyncLogger = {\n\tinfo: (...params: Parameters<typeof Logger.info>) => void;\n\tdebug: (...params: Parameters<typeof Logger.debug>) => void;\n\twarn: (...params: Parameters<typeof Logger.warn>) => void;\n\terror: (...params: Parameters<typeof Logger.error>) => void;\n\tchild: (annotations: LogAnnotations) => SyncLogger;\n};\n\nfunction makeSyncLogger(\n\truntime: Runtime.Runtime<never>,\n\tannotations: LogAnnotations,\n): SyncLogger {\n\tconst run = (e: Effect.Effect<void, never, never>) => {\n\t\tconst annotated =\n\t\t\tObject.keys(annotations).length > 0\n\t\t\t\t? e.pipe(Effect.annotateLogs(annotations))\n\t\t\t\t: e;\n\t\tvoid Runtime.runPromise(runtime)(annotated);\n\t};\n\n\treturn {\n\t\tinfo: (...params: Parameters<typeof Logger.info>) =>\n\t\t\trun(Logger.info(...params)),\n\t\tdebug: (...params: Parameters<typeof Logger.debug>) =>\n\t\t\trun(Logger.debug(...params)),\n\t\twarn: (...params: Parameters<typeof Logger.warn>) =>\n\t\t\trun(Logger.warn(...params)),\n\t\terror: (...params: Parameters<typeof Logger.error>) =>\n\t\t\trun(Logger.error(...params)),\n\t\tchild: (childAnnotations: LogAnnotations) =>\n\t\t\tmakeSyncLogger(runtime, { ...annotations, ...childAnnotations }),\n\t};\n}\n\nexport namespace Logger {\n\texport const sync = (annotations?: LogAnnotations) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst runtime = yield* Effect.runtime();\n\t\t\treturn makeSyncLogger(runtime, annotations ?? {});\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"],"mappings":";AAGA,SAAS,UAAAA,eAAc;;;ACHvB,SAAS,UAAAC,SAAQ,gBAAgB;AAEjC,SAAS,cAAc;;;ACFvB,SAAS,QAAQ,eAAe;AAGhC,SAAS,iBAAiB,CAAC,KAAK,GAAG,GAAc;AAChD,MAAI,OAAO,QAAQ,UAAU;AAC5B,WAAO,EAAE,SAAS,IAAI;AAAA,EACvB;AAEA,SAAO,EAAE,SAAS,KAAK,YAAY,IAA2B;AAC/D;AAaA,SAAS,eACR,SACA,aACa;AACb,QAAM,MAAM,CAAC,MAAyC;AACrD,UAAM,YACL,OAAO,KAAK,WAAW,EAAE,SAAS,IAC/B,EAAE,KAAK,OAAO,aAAa,WAAW,CAAC,IACvC;AACJ,SAAK,QAAQ,WAAW,OAAO,EAAE,SAAS;AAAA,EAC3C;AAEA,SAAO;AAAA,IACN,MAAM,IAAI,WACT,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC;AAAA,IAC3B,OAAO,IAAI,WACV,IAAI,OAAO,MAAM,GAAG,MAAM,CAAC;AAAA,IAC5B,MAAM,IAAI,WACT,IAAI,OAAO,KAAK,GAAG,MAAM,CAAC;AAAA,IAC3B,OAAO,IAAI,WACV,IAAI,OAAO,MAAM,GAAG,MAAM,CAAC;AAAA,IAC5B,OAAO,CAAC,qBACP,eAAe,SAAS,EAAE,GAAG,aAAa,GAAG,iBAAiB,CAAC;AAAA,EACjE;AACD;AAEO,IAAU;AAAA,CAAV,CAAUC,YAAV;AACC,EAAMA,QAAA,OAAO,CAAC,gBACpB,OAAO,IAAI,aAAa;AACvB,UAAM,UAAU,OAAO,OAAO,QAAQ;AACtC,WAAO,eAAe,SAAS,eAAe,CAAC,CAAC;AAAA,EACjD,CAAC;AAIK,EAAMA,QAAA,OAAO,IAAI,WACvB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,QAAQ,OAAO,EAAE;AAAA,MAC9B,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,OAAO,IAAI,WACvB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,WAAW,OAAO,EAAE;AAAA,MACjC,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,OAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,OAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,OAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAAA,GAvCc;;;AD7BV,IAAM,UAAN,MAAsC;AAAA,EAC5C,YACU,MACA,QAIR;AALQ;AACA;AAAA,EAIP;AACJ;;;ADZA,SAAS,SAAkC,KAA2B;AACrE,SAAOC,QAAO,SAAS,GAAG,IAAI,MAAMA,QAAO,QAAQ,GAAG;AACvD;AAEO,SAAS,YACf,SACA,KACC;AACD,SAAO,IAAI;AAAA,IAAQ;AAAA,IAAe,CAAC,EAAE,QAAQ,MAC5CA,QAAO,IAAI,aAAa;AACvB,YAAM,OACL,MACG,CAAC,OAAO,SAAS,OAAO,QAAQ,aAAa,IAAI,OAAO,IAAI,GAAG,CAAC,IAChE,CAAC;AAEL,aAAO,OAAOA,QAAO,WAAW,MAAM,QAAQ,OAAO,SAAS,GAAG,IAAI,CAAC;AAAA,IACvE,CAAC;AAAA,EACF;AACD;","names":["Effect","Effect","Logger","Effect"]}
package/dist/index.cjs CHANGED
@@ -52,19 +52,24 @@ function extractParams(...[obj, msg]) {
52
52
  }
53
53
  return { message: msg, attributes: obj };
54
54
  }
55
+ function makeSyncLogger(runtime, annotations) {
56
+ const run = (e) => {
57
+ const annotated = Object.keys(annotations).length > 0 ? e.pipe(import_effect.Effect.annotateLogs(annotations)) : e;
58
+ void import_effect.Runtime.runPromise(runtime)(annotated);
59
+ };
60
+ return {
61
+ info: (...params) => run(Logger.info(...params)),
62
+ debug: (...params) => run(Logger.debug(...params)),
63
+ warn: (...params) => run(Logger.warn(...params)),
64
+ error: (...params) => run(Logger.error(...params)),
65
+ child: (childAnnotations) => makeSyncLogger(runtime, { ...annotations, ...childAnnotations })
66
+ };
67
+ }
55
68
  var Logger;
56
69
  ((Logger2) => {
57
- Logger2.sync = () => import_effect.Effect.gen(function* () {
70
+ Logger2.sync = (annotations) => import_effect.Effect.gen(function* () {
58
71
  const runtime = yield* import_effect.Effect.runtime();
59
- const run = (e) => {
60
- void import_effect.Runtime.runPromise(runtime)(e);
61
- };
62
- return {
63
- info: (...params) => run(Logger2.info(...params)),
64
- debug: (...params) => run(Logger2.debug(...params)),
65
- warn: (...params) => run(Logger2.warn(...params)),
66
- error: (...params) => run(Logger2.error(...params))
67
- };
72
+ return makeSyncLogger(runtime, annotations ?? {});
68
73
  });
69
74
  Logger2.info = (...params) => import_effect.Effect.gen(function* () {
70
75
  const { message, attributes } = extractParams(...params);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/http/basic.ts","../src/http/fetch-handler.ts","../src/logger.ts","../src/port.ts"],"sourcesContent":["export * from './http/index.js';\nexport { Logger } from './logger.js';\nexport { getPort } from './port.js';\n","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, Runtime } 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 runtime = yield* Effect.runtime();\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\t// void runPromise(e);\n\t\t\t\tvoid Runtime.runPromise(runtime)(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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,iBAAuB;;;ACAvB,IAAAC,iBAAiC;AAEjC,oBAAuB;;;ACFvB,oBAA0C;AAG1C,SAAS,iBAAiB,CAAC,KAAK,GAAG,GAAc;AAChD,MAAI,OAAO,QAAQ,UAAU;AAC5B,WAAO,EAAE,SAAS,IAAI;AAAA,EACvB;AACA,SAAO,EAAE,SAAS,KAAK,YAAY,IAA2B;AAC/D;AAEO,IAAU;AAAA,CAAV,CAAUC,YAAV;AACC,EAAMA,QAAA,OAAO,MACnB,qBAAO,IAAI,aAAa;AACvB,UAAM,UAAU,OAAO,qBAAO,QAAQ;AAEtC,UAAM,MAAM,CAAC,MAAyC;AAGrD,WAAK,sBAAQ,WAAW,OAAO,EAAE,CAAC;AAAA,IACnC;AAEA,WAAO;AAAA,MACN,MAAM,IAAI,WACT,IAAIA,QAAO,KAAK,GAAG,MAAM,CAAC;AAAA,MAC3B,OAAO,IAAI,WACV,IAAIA,QAAO,MAAM,GAAG,MAAM,CAAC;AAAA,MAC5B,MAAM,IAAI,WACT,IAAIA,QAAO,KAAK,GAAG,MAAM,CAAC;AAAA,MAC3B,OAAO,IAAI,WACV,IAAIA,QAAO,MAAM,GAAG,MAAM,CAAC;AAAA,IAC7B;AAAA,EACD,CAAC;AAIK,EAAMA,QAAA,OAAO,IAAI,WACvB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,QAAQ,OAAO,EAAE;AAAA,MAC9B,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,OAAO,IAAI,WACvB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,WAAW,OAAO,EAAE;AAAA,MACjC,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAEK,EAAMA,QAAA,QAAQ,IAAI,WACxB,qBAAO,IAAI,aAAa;AACvB,UAAM,EAAE,SAAS,WAAW,IAAI,cAAc,GAAG,MAAM;AACvD,WAAO,qBAAO,SAAS,OAAO,EAAE;AAAA,MAC/B,aAAa,qBAAO,aAAa,UAAU,IAAI,CAAC,MAAM;AAAA,IACvD;AAAA,EACD,CAAC;AAAA,GAvDc;;;ADSV,IAAM,UAAN,MAAsC;AAAA,EAC5C,YACU,MACA,QAIR;AALQ;AACA;AAAA,EAIP;AACJ;AAMO,IAAM,qBAAqB,CAOjC,UACA,SAOA,sBAAO,IAAI,aAAa;AACvB,QAAM,UAAU,OAAO,wBAAS,mBAAsB;AACtD,SAAO,OAAO,YAAqB;AAClC,UAAM,SAAS,IAAI,IAAI,QAAQ,GAAG;AAClC,UAAM,gBAAY,sBAAO,CAAC;AAE1B,UAAM,SAAS,sBAAO,IAAI,aAAa;AACtC,aAAO,OAAO;AAAA,QACb,EAAE,SAAS,EAAE,UAAU,OAAO,SAAS,EAAE;AAAA,QACzC;AAAA,MACD;AAEA,iBAAW,WAAW,UAAU;AAC/B,YAAI,CAAC,QAAS;AAEd,cAAM,SAAS,OAAO,QAAQ,OAAO,EAAE,KAAK,QAAQ,QAAQ,CAAC,EAAE;AAAA,UAC9D,sBAAO;AAAA,YAAQ,CAAC,EAAE,SAAS,SAAS,MACnC,sBAAO,IAAI,aAAa;AACvB,kBAAI,SAAS;AACZ,uBAAO;AAAA,kBACN,SAAS;AAAA,kBACT,UACC,oBAAoB,UACjB,OAAO,sBAAO,WAAW,MAAM,QAAQ,IACvC;AAAA,gBACL;AAAA,cACD;AACA,qBAAO,EAAE,SAAS,OAAO,UAAU,OAAU;AAAA,YAC9C,CAAC;AAAA,UACF;AAAA,UACA,sBAAO;AAAA,YAAc,CAAC,UACrB,sBAAO,IAAI,aAAa;AACvB,qBAAO,OAAO;AAAA,gBACb,EAAE,MAAM;AAAA,gBACR,wCAAwC,QAAQ,IAAI;AAAA,cACrD;AACA,kBAAI,MAAM,QAAS,QAAO,KAAK,QAAQ,EAAE,MAAM,CAAC;AAChD,qBAAO;AAAA,gBACN,SAAS;AAAA,gBACT,UAAU,IAAI,SAAS,yBAAyB;AAAA,kBAC/C,QAAQ;AAAA,gBACT,CAAC;AAAA,cACF;AAAA,YACD,CAAC;AAAA,UACF;AAAA,QACD;AAEA,YAAI,MAAM;AACT,iBAAO,OAAO;AAAA,YACb,EAAE,SAAS,QAAQ,MAAM,SAAS,OAAO;AAAA,YACzC;AAAA,UACD;AAED,YAAI,CAAC,OAAO,QAAS;AACrB,eAAO,OAAO;AAAA,MACf;AAEA,aAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,IACjD,CAAC,EAAE;AAAA,MACF,sBAAO;AAAA,QAAI,CAAC,aACX,SAAS,KACN,OAAO,KAAK,iCAAiC,SAAS,MAAM,EAAE,IAC9D,OAAO,KAAK,iCAAiC,SAAS,MAAM,EAAE;AAAA,MAClE;AAAA,MACA,sBAAO,SAAS,MAAM;AAAA,MACtB,sBAAO,aAAa,EAAE,UAAU,CAAC;AAAA,MACjC,sBAAO;AAAA,IACR;AAEA,WAAO,QAAQ,MAAM;AAAA,EACtB;AACD,CAAC;;;ADpHK,IAAU;AAAA,CAAV,CAAUC,UAAV;AAEC,WAAS,QAAQ,MAAY,KAAU;AAC7C,WAAO,OAAO,SAAS,aAAa,KAAK,GAAG,IAAI,SAAS,IAAI;AAAA,EAC9D;AAFO,EAAAA,MAAS;AAAA,GAFA;AAOV,IAAU;AAAA,CAAV,CAAUC,QAAV;AAUC,WAAS,KAAQ,OAAiB,CAAC,OAAO,GAAU;AAC1D,UAAM,WAAW,GAAG,OAAO;AAC3B,QAAI,CAAC,sBAAO,SAAS,QAAQ,EAAG,QAAO,sBAAO,QAAQ,QAAQ;AAC9D,WAAO;AAAA,EACR;AAJO,EAAAA,IAAS;AAAA,GAVA;AAyBV,SAAS,aAAgB,MAAiB,IAAiB;AACjE,SAAO,IAAI,QAAQ,gBAAgB,CAAC,EAAE,KAAK,QAAQ,MAAM;AACxD,QAAI,CAAC,KAAK,QAAQ,MAAM,GAAG;AAC1B,aAAO,sBAAO,QAAQ,EAAE,SAAS,OAAO,UAAU,OAAU,CAAC;AAE9D,WAAO,sBAAO,IAAI,aAAa;AAC9B,aAAO;AAAA,QACN,SAAS;AAAA,QACT,UAAU,OAAO,GAAG,KAAK,IAAI,OAAO;AAAA,MACrC;AAAA,IACD,CAAC;AAAA,EACF,CAAC;AACF;;;AG/CA,IAAAC,iBAAuB;AACvB,cAAyB;AAElB,IAAM,UAAU,CAAC,YACvB,sBAAO,WAAW,MAAc,gBAAQ,OAAO,CAAC;","names":["import_effect","import_effect","Logger","Path","Fn","import_effect"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/http/basic.ts","../src/http/fetch-handler.ts","../src/logger.ts","../src/port.ts"],"sourcesContent":["export * from './http/index.js';\nexport type { SyncLogger } from './logger.js';\nexport { Logger } from './logger.js';\nexport { getPort } from './port.js';\n","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, Runtime } 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\t// biome-ignore lint/suspicious/noExplicitAny: log attributes are unstructured\n\treturn { message: msg, attributes: obj as Record<string, any> };\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: log annotations are unstructured\ntype LogAnnotations = Record<string, any>;\n\nexport type SyncLogger = {\n\tinfo: (...params: Parameters<typeof Logger.info>) => void;\n\tdebug: (...params: Parameters<typeof Logger.debug>) => void;\n\twarn: (...params: Parameters<typeof Logger.warn>) => void;\n\terror: (...params: Parameters<typeof Logger.error>) => void;\n\tchild: (annotations: LogAnnotations) => SyncLogger;\n};\n\nfunction makeSyncLogger(\n\truntime: Runtime.Runtime<never>,\n\tannotations: LogAnnotations,\n): SyncLogger {\n\tconst run = (e: Effect.Effect<void, never, never>) => {\n\t\tconst annotated =\n\t\t\tObject.keys(annotations).length > 0\n\t\t\t\t? e.pipe(Effect.annotateLogs(annotations))\n\t\t\t\t: e;\n\t\tvoid Runtime.runPromise(runtime)(annotated);\n\t};\n\n\treturn {\n\t\tinfo: (...params: Parameters<typeof Logger.info>) =>\n\t\t\trun(Logger.info(...params)),\n\t\tdebug: (...params: Parameters<typeof Logger.debug>) =>\n\t\t\trun(Logger.debug(...params)),\n\t\twarn: (...params: Parameters<typeof Logger.warn>) =>\n\t\t\trun(Logger.warn(...params)),\n\t\terror: (...params: Parameters<typeof Logger.error>) =>\n\t\t\trun(Logger.error(...params)),\n\t\tchild: (childAnnotations: LogAnnotations) =>\n\t\t\tmakeSyncLogger(runtime, { ...annotations, ...childAnnotations }),\n\t};\n}\n\nexport namespace Logger {\n\texport const sync = (annotations?: LogAnnotations) =>\n\t\tEffect.gen(function* () {\n\t\t\tconst runtime = yield* Effect.runtime();\n\t\t\treturn makeSyncLogger(runtime, annotations ?? {});\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":["import_effect","import_effect","Logger","Path","Fn","import_effect"]}
package/dist/index.d.cts CHANGED
@@ -23,13 +23,16 @@ declare function basicHandler(path: Path.Type, fn: Fn.FnSync): Handler<'basicHan
23
23
  declare function basicHandler<R>(path: Path.Type, fn: Fn.FnEffect<R>): Handler<'basicHandler', R>;
24
24
 
25
25
  type LogParams = [obj: unknown, msg?: string];
26
+ type LogAnnotations = Record<string, any>;
27
+ type SyncLogger = {
28
+ info: (...params: Parameters<typeof Logger.info>) => void;
29
+ debug: (...params: Parameters<typeof Logger.debug>) => void;
30
+ warn: (...params: Parameters<typeof Logger.warn>) => void;
31
+ error: (...params: Parameters<typeof Logger.error>) => void;
32
+ child: (annotations: LogAnnotations) => SyncLogger;
33
+ };
26
34
  declare namespace Logger {
27
- const sync: () => Effect.Effect<{
28
- info: (obj: unknown, msg?: string | undefined) => void;
29
- debug: (obj: unknown, msg?: string | undefined) => void;
30
- warn: (obj: unknown, msg?: string | undefined) => void;
31
- error: (obj: unknown, msg?: string | undefined) => void;
32
- }, never, never>;
35
+ const sync: (annotations?: LogAnnotations) => Effect.Effect<SyncLogger, never, never>;
33
36
  const info: (...params: LogParams) => Effect.Effect<void, never, never>;
34
37
  const debug: (...params: LogParams) => Effect.Effect<void, never, never>;
35
38
  const warn: (...params: LogParams) => Effect.Effect<void, never, never>;
@@ -38,4 +41,4 @@ declare namespace Logger {
38
41
 
39
42
  declare const getPort: (options?: GetPort.Options) => Effect.Effect<number, effect_Cause.UnknownException, never>;
40
43
 
41
- export { Logger, basicHandler, getPort };
44
+ export { Logger, type SyncLogger, basicHandler, getPort };
package/dist/index.d.ts CHANGED
@@ -23,13 +23,16 @@ declare function basicHandler(path: Path.Type, fn: Fn.FnSync): Handler<'basicHan
23
23
  declare function basicHandler<R>(path: Path.Type, fn: Fn.FnEffect<R>): Handler<'basicHandler', R>;
24
24
 
25
25
  type LogParams = [obj: unknown, msg?: string];
26
+ type LogAnnotations = Record<string, any>;
27
+ type SyncLogger = {
28
+ info: (...params: Parameters<typeof Logger.info>) => void;
29
+ debug: (...params: Parameters<typeof Logger.debug>) => void;
30
+ warn: (...params: Parameters<typeof Logger.warn>) => void;
31
+ error: (...params: Parameters<typeof Logger.error>) => void;
32
+ child: (annotations: LogAnnotations) => SyncLogger;
33
+ };
26
34
  declare namespace Logger {
27
- const sync: () => Effect.Effect<{
28
- info: (obj: unknown, msg?: string | undefined) => void;
29
- debug: (obj: unknown, msg?: string | undefined) => void;
30
- warn: (obj: unknown, msg?: string | undefined) => void;
31
- error: (obj: unknown, msg?: string | undefined) => void;
32
- }, never, never>;
35
+ const sync: (annotations?: LogAnnotations) => Effect.Effect<SyncLogger, never, never>;
33
36
  const info: (...params: LogParams) => Effect.Effect<void, never, never>;
34
37
  const debug: (...params: LogParams) => Effect.Effect<void, never, never>;
35
38
  const warn: (...params: LogParams) => Effect.Effect<void, never, never>;
@@ -38,4 +41,4 @@ declare namespace Logger {
38
41
 
39
42
  declare const getPort: (options?: GetPort.Options) => Effect.Effect<number, effect_Cause.UnknownException, never>;
40
43
 
41
- export { Logger, basicHandler, getPort };
44
+ export { Logger, type SyncLogger, basicHandler, getPort };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { Effect as Effect3 } from "effect";
3
3
 
4
4
  // src/http/fetch-handler.ts
5
- import { Effect as Effect2, FiberSet as FiberSet2 } from "effect";
5
+ import { Effect as Effect2, FiberSet } from "effect";
6
6
  import { nanoid } from "nanoid";
7
7
 
8
8
  // src/logger.ts
@@ -13,19 +13,24 @@ function extractParams(...[obj, msg]) {
13
13
  }
14
14
  return { message: msg, attributes: obj };
15
15
  }
16
+ function makeSyncLogger(runtime, annotations) {
17
+ const run = (e) => {
18
+ const annotated = Object.keys(annotations).length > 0 ? e.pipe(Effect.annotateLogs(annotations)) : e;
19
+ void Runtime.runPromise(runtime)(annotated);
20
+ };
21
+ return {
22
+ info: (...params) => run(Logger.info(...params)),
23
+ debug: (...params) => run(Logger.debug(...params)),
24
+ warn: (...params) => run(Logger.warn(...params)),
25
+ error: (...params) => run(Logger.error(...params)),
26
+ child: (childAnnotations) => makeSyncLogger(runtime, { ...annotations, ...childAnnotations })
27
+ };
28
+ }
16
29
  var Logger;
17
30
  ((Logger2) => {
18
- Logger2.sync = () => Effect.gen(function* () {
31
+ Logger2.sync = (annotations) => Effect.gen(function* () {
19
32
  const runtime = yield* Effect.runtime();
20
- const run = (e) => {
21
- void Runtime.runPromise(runtime)(e);
22
- };
23
- return {
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))
28
- };
33
+ return makeSyncLogger(runtime, annotations ?? {});
29
34
  });
30
35
  Logger2.info = (...params) => Effect.gen(function* () {
31
36
  const { message, attributes } = extractParams(...params);
@@ -61,7 +66,7 @@ var Handler = class {
61
66
  }
62
67
  };
63
68
  var createFetchHandler = (handlers, opts) => Effect2.gen(function* () {
64
- const runFork = yield* FiberSet2.makeRuntimePromise();
69
+ const runFork = yield* FiberSet.makeRuntimePromise();
65
70
  return async (request) => {
66
71
  const urlObj = new URL(request.url);
67
72
  const requestId = nanoid(6);