effect-start 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +3 -3
  2. package/dist/Development.d.ts +8 -3
  3. package/dist/Development.js +14 -7
  4. package/dist/Effectify.d.ts +212 -0
  5. package/dist/Effectify.js +19 -0
  6. package/dist/FilePathPattern.d.ts +29 -0
  7. package/dist/FilePathPattern.js +86 -0
  8. package/dist/FileRouter.d.ts +39 -41
  9. package/dist/FileRouter.js +104 -158
  10. package/dist/FileRouterCodegen.d.ts +7 -8
  11. package/dist/FileRouterCodegen.js +97 -66
  12. package/dist/PlatformError.d.ts +46 -0
  13. package/dist/PlatformError.js +43 -0
  14. package/dist/PlatformRuntime.d.ts +27 -0
  15. package/dist/PlatformRuntime.js +51 -0
  16. package/dist/Route.d.ts +6 -2
  17. package/dist/Route.js +22 -0
  18. package/dist/RouteBody.d.ts +1 -1
  19. package/dist/RouteHttp.d.ts +1 -1
  20. package/dist/RouteHttp.js +12 -19
  21. package/dist/RouteMount.d.ts +2 -1
  22. package/dist/Start.d.ts +33 -6
  23. package/dist/Start.js +31 -13
  24. package/dist/Unique.d.ts +50 -0
  25. package/dist/Unique.js +187 -0
  26. package/dist/bun/BunHttpServer.js +5 -6
  27. package/dist/bun/BunPlatformHttpServer.d.ts +10 -0
  28. package/dist/bun/BunPlatformHttpServer.js +53 -0
  29. package/dist/bun/BunRoute.d.ts +4 -6
  30. package/dist/bun/BunRoute.js +10 -18
  31. package/dist/bun/BunRuntime.d.ts +2 -1
  32. package/dist/bun/BunRuntime.js +10 -5
  33. package/dist/bun/BunServer.d.ts +33 -0
  34. package/dist/bun/BunServer.js +133 -0
  35. package/dist/bun/BunServerRequest.d.ts +60 -0
  36. package/dist/bun/BunServerRequest.js +252 -0
  37. package/dist/bun/index.d.ts +1 -1
  38. package/dist/bun/index.js +1 -1
  39. package/dist/datastar/actions/fetch.d.ts +30 -0
  40. package/dist/datastar/actions/fetch.js +411 -0
  41. package/dist/datastar/actions/peek.d.ts +1 -0
  42. package/dist/datastar/actions/peek.js +14 -0
  43. package/dist/datastar/actions/setAll.d.ts +1 -0
  44. package/dist/datastar/actions/setAll.js +13 -0
  45. package/dist/datastar/actions/toggleAll.d.ts +1 -0
  46. package/dist/datastar/actions/toggleAll.js +13 -0
  47. package/dist/datastar/attributes/attr.d.ts +1 -0
  48. package/dist/datastar/attributes/attr.js +49 -0
  49. package/dist/datastar/attributes/bind.d.ts +1 -0
  50. package/dist/datastar/attributes/bind.js +183 -0
  51. package/dist/datastar/attributes/class.d.ts +1 -0
  52. package/dist/datastar/attributes/class.js +50 -0
  53. package/dist/datastar/attributes/computed.d.ts +1 -0
  54. package/dist/datastar/attributes/computed.js +27 -0
  55. package/dist/datastar/attributes/effect.d.ts +1 -0
  56. package/dist/datastar/attributes/effect.js +10 -0
  57. package/dist/datastar/attributes/indicator.d.ts +1 -0
  58. package/dist/datastar/attributes/indicator.js +32 -0
  59. package/dist/datastar/attributes/init.d.ts +1 -0
  60. package/dist/datastar/attributes/init.js +27 -0
  61. package/dist/datastar/attributes/jsonSignals.d.ts +1 -0
  62. package/dist/datastar/attributes/jsonSignals.js +31 -0
  63. package/dist/datastar/attributes/on.d.ts +1 -0
  64. package/dist/datastar/attributes/on.js +59 -0
  65. package/dist/datastar/attributes/onIntersect.d.ts +1 -0
  66. package/dist/datastar/attributes/onIntersect.js +54 -0
  67. package/dist/datastar/attributes/onInterval.d.ts +1 -0
  68. package/dist/datastar/attributes/onInterval.js +31 -0
  69. package/dist/datastar/attributes/onSignalPatch.d.ts +1 -0
  70. package/dist/datastar/attributes/onSignalPatch.js +44 -0
  71. package/dist/datastar/attributes/ref.d.ts +1 -0
  72. package/dist/datastar/attributes/ref.js +11 -0
  73. package/dist/datastar/attributes/show.d.ts +1 -0
  74. package/dist/datastar/attributes/show.js +32 -0
  75. package/dist/datastar/attributes/signals.d.ts +1 -0
  76. package/dist/datastar/attributes/signals.js +18 -0
  77. package/dist/datastar/attributes/style.d.ts +1 -0
  78. package/dist/datastar/attributes/style.js +56 -0
  79. package/dist/datastar/attributes/text.d.ts +1 -0
  80. package/dist/datastar/attributes/text.js +27 -0
  81. package/dist/datastar/engine.d.ts +156 -0
  82. package/dist/datastar/engine.js +971 -0
  83. package/dist/datastar/index.d.ts +24 -0
  84. package/dist/datastar/index.js +24 -0
  85. package/dist/datastar/load.d.ts +24 -0
  86. package/dist/datastar/load.js +24 -0
  87. package/dist/datastar/utils.d.ts +51 -0
  88. package/dist/datastar/utils.js +205 -0
  89. package/dist/datastar/watchers/patchElements.d.ts +1 -0
  90. package/dist/datastar/watchers/patchElements.js +420 -0
  91. package/dist/datastar/watchers/patchSignals.d.ts +1 -0
  92. package/dist/datastar/watchers/patchSignals.js +15 -0
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.js +1 -0
  95. package/dist/node/Effectify.d.ts +209 -0
  96. package/dist/node/Effectify.js +19 -0
  97. package/dist/node/FileSystem.d.ts +3 -5
  98. package/dist/node/FileSystem.js +42 -62
  99. package/dist/node/NodeFileSystem.d.ts +7 -0
  100. package/dist/node/NodeFileSystem.js +420 -0
  101. package/dist/node/NodeUtils.d.ts +2 -0
  102. package/dist/node/NodeUtils.js +20 -0
  103. package/dist/node/PlatformError.d.ts +46 -0
  104. package/dist/node/PlatformError.js +43 -0
  105. package/dist/testing/TestLogger.js +1 -1
  106. package/dist/x/tailwind/plugin.js +1 -1
  107. package/package.json +18 -7
  108. package/src/Development.ts +36 -40
  109. package/src/Effectify.ts +269 -0
  110. package/src/FilePathPattern.ts +115 -0
  111. package/src/FileRouter.ts +178 -255
  112. package/src/FileRouterCodegen.ts +135 -92
  113. package/src/PlatformError.ts +117 -0
  114. package/src/PlatformRuntime.ts +108 -0
  115. package/src/Route.ts +31 -2
  116. package/src/RouteBody.ts +1 -1
  117. package/src/RouteHttp.ts +15 -29
  118. package/src/RouteMount.ts +1 -1
  119. package/src/Start.ts +61 -27
  120. package/src/Unique.ts +232 -0
  121. package/src/bun/BunPlatformHttpServer.ts +88 -0
  122. package/src/bun/BunRoute.ts +14 -24
  123. package/src/bun/BunRuntime.ts +21 -5
  124. package/src/bun/BunServer.ts +228 -0
  125. package/src/bun/index.ts +1 -1
  126. package/src/datastar/README.md +18 -0
  127. package/src/datastar/actions/fetch.ts +609 -0
  128. package/src/datastar/actions/peek.ts +17 -0
  129. package/src/datastar/actions/setAll.ts +20 -0
  130. package/src/datastar/actions/toggleAll.ts +20 -0
  131. package/src/datastar/attributes/attr.ts +50 -0
  132. package/src/datastar/attributes/bind.ts +220 -0
  133. package/src/datastar/attributes/class.ts +57 -0
  134. package/src/datastar/attributes/computed.ts +33 -0
  135. package/src/datastar/attributes/effect.ts +11 -0
  136. package/src/datastar/attributes/indicator.ts +39 -0
  137. package/src/datastar/attributes/init.ts +35 -0
  138. package/src/datastar/attributes/jsonSignals.ts +38 -0
  139. package/src/datastar/attributes/on.ts +71 -0
  140. package/src/datastar/attributes/onIntersect.ts +65 -0
  141. package/src/datastar/attributes/onInterval.ts +39 -0
  142. package/src/datastar/attributes/onSignalPatch.ts +63 -0
  143. package/src/datastar/attributes/ref.ts +12 -0
  144. package/src/datastar/attributes/show.ts +33 -0
  145. package/src/datastar/attributes/signals.ts +22 -0
  146. package/src/datastar/attributes/style.ts +63 -0
  147. package/src/datastar/attributes/text.ts +30 -0
  148. package/src/datastar/engine.ts +1341 -0
  149. package/src/datastar/index.ts +25 -0
  150. package/src/datastar/utils.ts +286 -0
  151. package/src/datastar/watchers/patchElements.ts +554 -0
  152. package/src/datastar/watchers/patchSignals.ts +15 -0
  153. package/src/index.ts +1 -0
  154. package/src/node/{FileSystem.ts → NodeFileSystem.ts} +59 -97
  155. package/src/node/{Utils.ts → NodeUtils.ts} +2 -0
  156. package/src/testing/TestLogger.ts +1 -1
  157. package/src/x/tailwind/plugin.ts +1 -1
  158. package/dist/Random.d.ts +0 -5
  159. package/dist/Random.js +0 -49
  160. package/src/Commander.test.ts +0 -1639
  161. package/src/ContentNegotiation.test.ts +0 -603
  162. package/src/Development.test.ts +0 -119
  163. package/src/Entity.test.ts +0 -592
  164. package/src/FileRouterCodegen.todo.ts +0 -1133
  165. package/src/FileRouterPattern.test.ts +0 -147
  166. package/src/FileRouterPattern.ts +0 -59
  167. package/src/FileRouter_files.test.ts +0 -64
  168. package/src/FileRouter_path.test.ts +0 -145
  169. package/src/FileRouter_tree.test.ts +0 -132
  170. package/src/Http.test.ts +0 -319
  171. package/src/HttpAppExtra.test.ts +0 -103
  172. package/src/HttpUtils.test.ts +0 -85
  173. package/src/PathPattern.test.ts +0 -648
  174. package/src/Random.ts +0 -59
  175. package/src/RouteBody.test.ts +0 -232
  176. package/src/RouteHook.test.ts +0 -40
  177. package/src/RouteHttp.test.ts +0 -2909
  178. package/src/RouteMount.test.ts +0 -481
  179. package/src/RouteSchema.test.ts +0 -427
  180. package/src/RouteSse.test.ts +0 -249
  181. package/src/RouteTree.test.ts +0 -494
  182. package/src/RouteTrie.test.ts +0 -322
  183. package/src/RouterPattern.test.ts +0 -676
  184. package/src/RouterPattern.ts +0 -416
  185. package/src/StartApp.ts +0 -47
  186. package/src/Values.test.ts +0 -263
  187. package/src/bun/BunBundle.test.ts +0 -268
  188. package/src/bun/BunBundle_imports.test.ts +0 -48
  189. package/src/bun/BunHttpServer.test.ts +0 -251
  190. package/src/bun/BunHttpServer.ts +0 -306
  191. package/src/bun/BunImportTrackerPlugin.test.ts +0 -77
  192. package/src/bun/BunRoute.test.ts +0 -162
  193. package/src/bundler/BundleHttp.test.ts +0 -132
  194. package/src/effect/HttpRouter.test.ts +0 -548
  195. package/src/experimental/EncryptedCookies.test.ts +0 -488
  196. package/src/hyper/HyperHtml.test.ts +0 -209
  197. package/src/hyper/HyperRoute.test.tsx +0 -197
  198. package/src/middlewares/BasicAuthMiddleware.test.ts +0 -84
  199. package/src/testing/TestHttpClient.test.ts +0 -83
  200. package/src/testing/TestLogger.test.ts +0 -51
  201. package/src/x/datastar/Datastar.test.ts +0 -266
  202. package/src/x/tailwind/TailwindPlugin.test.ts +0 -333
  203. /package/src/bun/{BunHttpServer_web.ts → BunServerRequest.ts} +0 -0
@@ -0,0 +1,43 @@
1
+ import * as Data from "effect/Data";
2
+ import * as Predicate from "effect/Predicate";
3
+ import * as Schema from "effect/Schema";
4
+ import { TypeId as TypeId_ } from "@effect/platform/Error";
5
+ export const TypeId = TypeId_;
6
+ export const isPlatformError = (u) => Predicate.hasProperty(u, TypeId);
7
+ export const TypeIdError = (typeId, tag) => {
8
+ class Base extends Data.Error {
9
+ _tag = tag;
10
+ }
11
+ ;
12
+ Base.prototype[typeId] = typeId;
13
+ Base.prototype.name = tag;
14
+ return Base;
15
+ };
16
+ export const Module = Schema.Literal("Clipboard", "Command", "FileSystem", "KeyValueStore", "Path", "Stream", "Terminal");
17
+ export class BadArgument extends Schema.TaggedError("@effect/platform/Error/BadArgument")("BadArgument", {
18
+ module: Module,
19
+ method: Schema.String,
20
+ description: Schema.optional(Schema.String),
21
+ cause: Schema.optional(Schema.Defect),
22
+ }) {
23
+ [TypeId] = TypeId;
24
+ get message() {
25
+ return `${this.module}.${this.method}${this.description ? `: ${this.description}` : ""}`;
26
+ }
27
+ }
28
+ export const SystemErrorReason = Schema.Literal("AlreadyExists", "BadResource", "Busy", "InvalidData", "NotFound", "PermissionDenied", "TimedOut", "UnexpectedEof", "Unknown", "WouldBlock", "WriteZero");
29
+ export class SystemError extends Schema.TaggedError("@effect/platform/Error/SystemError")("SystemError", {
30
+ reason: SystemErrorReason,
31
+ module: Module,
32
+ method: Schema.String,
33
+ description: Schema.optional(Schema.String),
34
+ syscall: Schema.optional(Schema.String),
35
+ pathOrDescriptor: Schema.optional(Schema.Union(Schema.String, Schema.Number)),
36
+ cause: Schema.optional(Schema.Defect),
37
+ }) {
38
+ [TypeId] = TypeId;
39
+ get message() {
40
+ return `${this.reason}: ${this.module}.${this.method}${this.pathOrDescriptor !== undefined ? ` (${this.pathOrDescriptor})` : ""}${this.description ? `: ${this.description}` : ""}`;
41
+ }
42
+ }
43
+ export const PlatformError = Schema.Union(BadArgument, SystemError);
@@ -0,0 +1,27 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Exit from "effect/Exit";
3
+ import type * as Fiber from "effect/Fiber";
4
+ export interface Teardown {
5
+ <E, A>(exit: Exit.Exit<E, A>, onExit: (code: number) => void): void;
6
+ }
7
+ export declare const defaultTeardown: Teardown;
8
+ export interface RunMain {
9
+ (options?: {
10
+ readonly disableErrorReporting?: boolean | undefined;
11
+ readonly disablePrettyLogger?: boolean | undefined;
12
+ readonly teardown?: Teardown | undefined;
13
+ }): <E, A>(effect: Effect.Effect<A, E>) => void;
14
+ <E, A>(effect: Effect.Effect<A, E>, options?: {
15
+ readonly disableErrorReporting?: boolean | undefined;
16
+ readonly disablePrettyLogger?: boolean | undefined;
17
+ readonly teardown?: Teardown | undefined;
18
+ }): void;
19
+ }
20
+ export declare const makeRunMain: (f: <E, A>(options: {
21
+ readonly fiber: Fiber.RuntimeFiber<A, E>;
22
+ readonly teardown: Teardown;
23
+ }) => void) => RunMain;
24
+ /**
25
+ * Are we running within an agent harness, like Claude Code?
26
+ */
27
+ export declare function isAgentHarness(): string | false | undefined;
@@ -0,0 +1,51 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Exit from "effect/Exit";
4
+ import * as FiberRef from "effect/FiberRef";
5
+ import * as FiberRefs from "effect/FiberRefs";
6
+ import * as Function from "effect/Function";
7
+ import * as HashSet from "effect/HashSet";
8
+ import * as Logger from "effect/Logger";
9
+ export const defaultTeardown = (exit, onExit) => {
10
+ onExit(Exit.isFailure(exit) && !Cause.isInterruptedOnly(exit.cause) ? 1 : 0);
11
+ };
12
+ const addPrettyLogger = (refs, fiberId) => {
13
+ const loggers = FiberRefs.getOrDefault(refs, FiberRef.currentLoggers);
14
+ if (!HashSet.has(loggers, Logger.defaultLogger)) {
15
+ return refs;
16
+ }
17
+ return FiberRefs.updateAs(refs, {
18
+ fiberId,
19
+ fiberRef: FiberRef.currentLoggers,
20
+ value: loggers.pipe(HashSet.remove(Logger.defaultLogger), HashSet.add(Logger.prettyLoggerDefault)),
21
+ });
22
+ };
23
+ export const makeRunMain = (f) => Function.dual((args) => Effect.isEffect(args[0]), (effect, options) => {
24
+ const fiber = options?.disableErrorReporting === true
25
+ ? Effect.runFork(effect, {
26
+ updateRefs: options?.disablePrettyLogger === true
27
+ ? undefined
28
+ : addPrettyLogger,
29
+ })
30
+ : Effect.runFork(Effect.tapErrorCause(effect, (cause) => {
31
+ if (Cause.isInterruptedOnly(cause)) {
32
+ return Effect.void;
33
+ }
34
+ return Effect.logError(cause);
35
+ }), {
36
+ updateRefs: options?.disablePrettyLogger === true
37
+ ? undefined
38
+ : addPrettyLogger,
39
+ });
40
+ const teardown = options?.teardown ?? defaultTeardown;
41
+ return f({ fiber, teardown });
42
+ });
43
+ /**
44
+ * Are we running within an agent harness, like Claude Code?
45
+ */
46
+ export function isAgentHarness() {
47
+ return typeof process !== "undefined"
48
+ && !process.stdout.isTTY
49
+ && (process.env.CLAUDECODE
50
+ || process.env.CURSOR_AGENT);
51
+ }
package/dist/Route.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as Context from "effect/Context";
2
- import type * as Effect from "effect/Effect";
2
+ import * as Effect from "effect/Effect";
3
3
  import * as Layer from "effect/Layer";
4
4
  import * as Pipeable from "effect/Pipeable";
5
- import type * as Entity from "./Entity.ts";
5
+ import * as Entity from "./Entity.ts";
6
6
  import * as RouteBody from "./RouteBody.ts";
7
7
  import * as RouteTree from "./RouteTree.ts";
8
8
  import * as Values from "./Values.ts";
@@ -91,8 +91,12 @@ export declare const json: RouteBody.BuildReturn<Values.Json, "json">;
91
91
  export declare const bytes: RouteBody.BuildReturn<Uint8Array<ArrayBufferLike>, "bytes">;
92
92
  export { render, } from "./RouteBody.ts";
93
93
  export { sse, } from "./RouteSse.ts";
94
+ export declare function redirect(url: string | URL, status?: 301 | 302 | 303 | 307 | 308): Entity.Entity<"">;
94
95
  declare const Routes_base: Context.TagClass<Routes, "effect-start/Routes", RouteTree.RouteTree<RouteTree.RouteMap>>;
95
96
  export declare class Routes extends Routes_base {
96
97
  }
97
98
  export declare function layer(routes: RouteTree.RouteMap | RouteTree.RouteTree): Layer.Layer<Routes, never, never>;
98
99
  export { make as tree, } from "./RouteTree.ts";
100
+ export declare function lazy<T extends RouteSet.Any>(load: () => Promise<{
101
+ default: T;
102
+ }>): Effect.Effect<T, never, never>;
package/dist/Route.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
2
3
  import * as Layer from "effect/Layer";
3
4
  import * as Pipeable from "effect/Pipeable";
4
5
  import * as Predicate from "effect/Predicate";
6
+ import * as Entity from "./Entity.js";
5
7
  import * as RouteBody from "./RouteBody.js";
6
8
  import * as RouteTree from "./RouteTree.js";
7
9
  export const RouteItems = Symbol();
@@ -71,6 +73,14 @@ export const bytes = RouteBody.build({
71
73
  });
72
74
  export { render, } from "./RouteBody.js";
73
75
  export { sse, } from "./RouteSse.js";
76
+ export function redirect(url, status = 302) {
77
+ return Entity.make("", {
78
+ status,
79
+ headers: {
80
+ location: url instanceof URL ? url.href : url,
81
+ },
82
+ });
83
+ }
74
84
  export class Routes extends Context.Tag("effect-start/Routes")() {
75
85
  }
76
86
  export function layer(routes) {
@@ -79,3 +89,15 @@ export function layer(routes) {
79
89
  : RouteTree.make(routes));
80
90
  }
81
91
  export { make as tree, } from "./RouteTree.js";
92
+ export function lazy(load) {
93
+ let cached;
94
+ return Effect.suspend(() => {
95
+ if (cached !== undefined) {
96
+ return Effect.succeed(cached);
97
+ }
98
+ return Effect.promise(load).pipe(Effect.map((mod) => {
99
+ cached = mod.default;
100
+ return cached;
101
+ }));
102
+ });
103
+ }
@@ -8,7 +8,7 @@ export type Format = "text" | "html" | "json" | "bytes" | "*";
8
8
  type UnwrapStream<T> = T extends Stream.Stream<infer V, any, any> ? V : T;
9
9
  type YieldError<T> = T extends Utils.YieldWrap<Effect.Effect<any, infer E, any>> ? E : never;
10
10
  type YieldContext<T> = T extends Utils.YieldWrap<Effect.Effect<any, any, infer R>> ? R : never;
11
- export type GeneratorHandler<B, A, Y> = (context: Values.Simplify<B>, next: (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<UnwrapStream<A>>) => Generator<Y, A | Entity.Entity<A>, unknown>;
11
+ export type GeneratorHandler<B, A, Y> = (context: Values.Simplify<B>, next: (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<UnwrapStream<A>>) => Generator<Y, A | Entity.Entity<A>, never>;
12
12
  export type HandlerInput<B, A, E, R> = A | Entity.Entity<A> | Effect.Effect<A | Entity.Entity<A>, E, R> | ((context: Values.Simplify<B>, next: (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<UnwrapStream<A>>) => Effect.Effect<A | Entity.Entity<A>, E, R> | Generator<Utils.YieldWrap<Effect.Effect<unknown, E, R>>, A | Entity.Entity<A>, unknown>);
13
13
  export declare function handle<B, A, Y extends Utils.YieldWrap<Effect.Effect<any, any, any>>>(handler: GeneratorHandler<B, A, Y>): Route.Route.Handler<B, A, YieldError<Y>, YieldContext<Y>>;
14
14
  export declare function handle<B, A, E, R>(handler: HandlerInput<B, A, E, R>): Route.Route.Handler<B, A, E, R>;
@@ -5,7 +5,6 @@ import * as Route from "./Route.ts";
5
5
  import * as RouteBody from "./RouteBody.ts";
6
6
  import * as RouteMount from "./RouteMount.ts";
7
7
  import * as RouteTree from "./RouteTree.ts";
8
- export { currentSpanNameGenerator, currentTracerDisabledWhen, parentSpanFromHeaders, withSpanNameGenerator, withTracerDisabledWhen, } from "./RouteHttpTracer.ts";
9
8
  type UnboundedRouteWithMethod = Route.Route.With<{
10
9
  method: RouteMount.RouteMount.Method;
11
10
  format?: RouteBody.Format;
@@ -19,3 +18,4 @@ export declare const clientAbortFiberId: FiberId.Runtime;
19
18
  export declare const toWebHandlerRuntime: <R>(runtime: Runtime.Runtime<R>) => (routes: Iterable<UnboundedRouteWithMethod>) => Http.WebHandler;
20
19
  export declare const toWebHandler: (routes: Iterable<UnboundedRouteWithMethod>) => Http.WebHandler;
21
20
  export declare function walkHandles(tree: RouteTree.RouteTree, runtime?: Runtime.Runtime<never>): Generator<[path: string, handler: Http.WebHandler]>;
21
+ export {};
package/dist/RouteHttp.js CHANGED
@@ -12,7 +12,6 @@ import * as Route from "./Route.js";
12
12
  import * as RouteHttpTracer from "./RouteHttpTracer.js";
13
13
  import * as RouteTree from "./RouteTree.js";
14
14
  import * as StreamExtra from "./StreamExtra.js";
15
- export { currentSpanNameGenerator, currentTracerDisabledWhen, parentSpanFromHeaders, withSpanNameGenerator, withTracerDisabledWhen, } from "./RouteHttpTracer.js";
16
15
  // Used to match Accept headers against available route formats.
17
16
  // text/* matches any text type (text/plain, text/event-stream, text/markdown, etc.)
18
17
  const formatToMediaType = {
@@ -46,6 +45,10 @@ const getStatusFromCause = (cause) => {
46
45
  }
47
46
  return 500;
48
47
  };
48
+ const respondError = (options) => new Response(JSON.stringify(options, null, 2), {
49
+ status: options.status,
50
+ headers: { "content-type": "application/json" },
51
+ });
49
52
  function streamResponse(stream, headers, status, runtime) {
50
53
  const encoder = new TextEncoder();
51
54
  const byteStream = stream.pipe(Stream.map((chunk) => typeof chunk === "string"
@@ -116,9 +119,7 @@ export const toWebHandlerRuntime = (runtime) => {
116
119
  const accept = request.headers.get("accept");
117
120
  const methodRoutes = methodGroups[method] ?? [];
118
121
  if (methodRoutes.length === 0 && wildcards.length === 0) {
119
- return Promise.resolve(Response.json({ status: 405, message: "method not allowed" }, {
120
- status: 405,
121
- }));
122
+ return Promise.resolve(respondError({ status: 405, message: "method not allowed" }));
122
123
  }
123
124
  const allRoutes = [...wildcards, ...methodRoutes];
124
125
  const selectedFormat = determineSelectedFormat(accept, allRoutes);
@@ -130,9 +131,7 @@ export const toWebHandlerRuntime = (runtime) => {
130
131
  if (selectedFormat === undefined
131
132
  && hasSpecificFormatRoutes
132
133
  && !hasWildcardFormatRoutes) {
133
- return Promise.resolve(Response.json({ status: 406, message: "not acceptable" }, {
134
- status: 406,
135
- }));
134
+ return Promise.resolve(respondError({ status: 406, message: "not acceptable" }));
136
135
  }
137
136
  const createChain = (initialContext) => {
138
137
  let index = 0;
@@ -178,9 +177,7 @@ export const toWebHandlerRuntime = (runtime) => {
178
177
  ? result
179
178
  : Entity.make(result, { status: 200 });
180
179
  if (entity.status === 404 && entity.body === undefined) {
181
- return Response.json({ status: 406, message: "not acceptable" }, {
182
- status: 406,
183
- });
180
+ return respondError({ status: 406, message: "not acceptable" });
184
181
  }
185
182
  return yield* toResponse(entity, selectedFormat, runtime);
186
183
  });
@@ -217,9 +214,8 @@ export const toWebHandlerRuntime = (runtime) => {
217
214
  const fiber = runFork(effect.pipe(Effect.scoped, Effect.catchAllCause((cause) => Effect.gen(function* () {
218
215
  yield* Effect.logError(cause);
219
216
  const status = getStatusFromCause(cause);
220
- return Response.json({ status, message: Cause.pretty(cause) }, {
221
- status,
222
- });
217
+ const message = Cause.pretty(cause, { renderErrorCause: true });
218
+ return respondError({ status, message });
223
219
  }))));
224
220
  request.signal?.addEventListener("abort", () => {
225
221
  fiber.unsafeInterruptAsFork(clientAbortFiberId);
@@ -229,15 +225,12 @@ export const toWebHandlerRuntime = (runtime) => {
229
225
  resolve(exit.value);
230
226
  }
231
227
  else if (isClientAbort(exit.cause)) {
232
- resolve(Response.json({ status: 499, message: "client closed request" }, {
233
- status: 499,
234
- }));
228
+ resolve(respondError({ status: 499, message: "client closed request" }));
235
229
  }
236
230
  else {
237
231
  const status = getStatusFromCause(exit.cause);
238
- resolve(Response.json({ status, message: Cause.pretty(exit.cause) }, {
239
- status,
240
- }));
232
+ const message = Cause.pretty(exit.cause, { renderErrorCause: true });
233
+ resolve(respondError({ status, message }));
241
234
  }
242
235
  });
243
236
  });
@@ -2,6 +2,7 @@ import * as Types from "effect/Types";
2
2
  import * as Http from "./Http.ts";
3
3
  import * as PathPattern from "./PathPattern.ts";
4
4
  import * as Route from "./Route.ts";
5
+ import * as RouteBody from "./RouteBody.ts";
5
6
  type Module = typeof import("./RouteMount.ts");
6
7
  export type Self = RouteMount.Builder | Module;
7
8
  export declare const use: RouteMount.Describer<"*">;
@@ -16,7 +17,7 @@ export declare const add: RouteMount.Add;
16
17
  export type MountedRoute = Route.Route.Route<{
17
18
  method: RouteMount.Method;
18
19
  path: PathPattern.PathPattern;
19
- format?: string;
20
+ format?: RouteBody.Format;
20
21
  }, {}, any, any, any>;
21
22
  export declare namespace RouteMount {
22
23
  export type Method = "*" | Http.Method;
package/dist/Start.d.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  import * as FileSystem from "@effect/platform/FileSystem";
2
- import * as HttpClient from "@effect/platform/HttpClient";
3
- import * as HttpRouter from "@effect/platform/HttpRouter";
4
- import * as HttpServer from "@effect/platform/HttpServer";
5
2
  import * as Layer from "effect/Layer";
6
- import * as BunHttpServer from "./bun/BunHttpServer.ts";
3
+ import * as BunServer from "./bun/BunServer.ts";
7
4
  export declare function layer<Layers extends [
8
5
  Layer.Layer<never, any, any>,
9
6
  ...Array<Layer.Layer<never, any, any>>
@@ -14,6 +11,36 @@ export declare function layer<Layers extends [
14
11
  }[number], {
15
12
  [k in keyof Layers]: Layer.Layer.Context<Layers[k]>;
16
13
  }[number]>;
17
- export declare function serve<ROut, E>(load: () => Promise<{
18
- default: Layer.Layer<ROut, E, HttpServer.HttpServer | HttpClient.HttpClient | HttpRouter.Default | FileSystem.FileSystem | BunHttpServer.BunHttpServer>;
14
+ /**
15
+ * Bundles layers together, wiring their dependencies automatically.
16
+ *
17
+ * Equivalent to chaining `Layer.provide` calls, but more concise.
18
+ *
19
+ * **Ordering: dependents first, dependencies last.**
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * // UserRepo needs Database, Database needs Logger
24
+ * const AppLayer = Start.pack(
25
+ * UserRepoLive, // needs Database, Logger
26
+ * DatabaseLive, // needs Logger
27
+ * LoggerLive, // no deps
28
+ * )
29
+ * // Result: Layer<UserRepo | Database | Logger, never, never>
30
+ * ```
31
+ *
32
+ * @since 1.0.0
33
+ * @category constructors
34
+ */
35
+ export declare function pack<const Layers extends readonly [Layer.Layer.Any, ...Array<Layer.Layer.Any>]>(...layers: Layers): Layer.Layer<{
36
+ [K in keyof Layers]: Layer.Layer.Success<Layers[K]>;
37
+ }[number], {
38
+ [K in keyof Layers]: Layer.Layer.Error<Layers[K]>;
39
+ }[number], Exclude<{
40
+ [K in keyof Layers]: Layer.Layer.Context<Layers[K]>;
41
+ }[number], {
42
+ [K in keyof Layers]: Layer.Layer.Success<Layers[K]>;
43
+ }[number]>>;
44
+ export declare function serve<ROut, E, RIn extends BunServer.BunServer | FileSystem.FileSystem>(load: () => Promise<{
45
+ default: Layer.Layer<ROut, E, RIn>;
19
46
  }>): void;
package/dist/Start.js CHANGED
@@ -1,23 +1,41 @@
1
- import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
2
- import * as HttpRouter from "@effect/platform/HttpRouter";
3
- import * as HttpServer from "@effect/platform/HttpServer";
1
+ import * as Context from "effect/Context";
4
2
  import * as Effect from "effect/Effect";
5
3
  import * as Function from "effect/Function";
6
4
  import * as Layer from "effect/Layer";
7
- import * as BunHttpServer from "./bun/BunHttpServer.js";
8
5
  import * as BunRuntime from "./bun/BunRuntime.js";
9
- import * as NodeFileSystem from "./node/FileSystem.js";
10
- import * as StartApp from "./StartApp.js";
6
+ import * as BunServer from "./bun/BunServer.js";
7
+ import * as NodeFileSystem from "./node/NodeFileSystem.js";
11
8
  export function layer(...layers) {
12
9
  return Layer.mergeAll(...layers);
13
10
  }
11
+ /**
12
+ * Bundles layers together, wiring their dependencies automatically.
13
+ *
14
+ * Equivalent to chaining `Layer.provide` calls, but more concise.
15
+ *
16
+ * **Ordering: dependents first, dependencies last.**
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * // UserRepo needs Database, Database needs Logger
21
+ * const AppLayer = Start.pack(
22
+ * UserRepoLive, // needs Database, Logger
23
+ * DatabaseLive, // needs Logger
24
+ * LoggerLive, // no deps
25
+ * )
26
+ * // Result: Layer<UserRepo | Database | Logger, never, never>
27
+ * ```
28
+ *
29
+ * @since 1.0.0
30
+ * @category constructors
31
+ */
32
+ export function pack(...layers) {
33
+ const layerArray = layers;
34
+ const result = layerArray.reduce((acc, layer) => Layer.provideMerge(acc, layer), Layer.succeedContext(Context.empty()));
35
+ return result;
36
+ }
14
37
  export function serve(load) {
15
38
  const appLayer = Function.pipe(Effect.tryPromise(load), Effect.map(v => v.default), Effect.orDie, Layer.unwrapEffect);
16
- return Function.pipe(BunHttpServer.layerAuto(), HttpServer.withLogAddress, Layer.provide(appLayer), Layer.provide([
17
- FetchHttpClient.layer,
18
- HttpRouter.Default.Live,
19
- BunHttpServer.layer(),
20
- NodeFileSystem.layer,
21
- StartApp.layer(),
22
- ]), Layer.launch, BunRuntime.runMain);
39
+ const composed = Function.pipe(BunServer.layer(), BunServer.withLogAddress, Layer.provide(appLayer), Layer.provide(NodeFileSystem.layer), Layer.provide(BunServer.layer()));
40
+ return Function.pipe(composed, Layer.launch, BunRuntime.runMain);
23
41
  }
@@ -0,0 +1,50 @@
1
+ export declare const ALPHABET_BASE32_CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2
+ export declare const ALPHABET_BASE32_RFC4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
3
+ export declare const ALPHABET_BASE64_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4
+ export declare const ALPHABET_HEX = "0123456789abcdef";
5
+ /**
6
+ * Generate a random string for ids, session tokens, and API keys.
7
+ * It uses human-friendly crockford base32 encoding (5 bit of entropy per char)
8
+ *
9
+ * Minimal recommended length:
10
+ * - public ids: 16 chars (~80 bits)
11
+ * - API keys: 32 chars (~160 bits)
12
+ * - session tokens: 32-40 chars (~160-200 bits)
13
+ */
14
+ export declare function token(length?: number): string;
15
+ export declare function bytes(length: number): Uint8Array;
16
+ export declare const UUID_NIL = "00000000-0000-0000-0000-000000000000";
17
+ export declare function uuid4(): string;
18
+ export declare function uuid7(time?: number): string;
19
+ /**
20
+ * Decode a 48-bit Unix timestamp (ms) from UUID7 or ULID.
21
+ *
22
+ * @example
23
+ * const bytes = Unique.uuid7Bytes()
24
+ * const timestamp = Unique.toTimestamp(bytes)
25
+ *
26
+ * @example
27
+ * const bytes = Unique.ulidBytes()
28
+ * const timestamp = Unique.toTimestamp(bytes)
29
+ */
30
+ export declare function toTimestamp(bytes: Uint8Array): number;
31
+ export declare function uuid7Bytes(time?: number): Uint8Array;
32
+ /**
33
+ * Convert UUID bytes to canonical (RFC9562) representation.
34
+ *
35
+ * @example
36
+ * Unique.formatUuid(new Uint8Array(16))
37
+ */
38
+ export declare function formatUuid(bytes: Uint8Array): string;
39
+ export declare function ulid(time?: number): string;
40
+ export declare function ulidBytes(time?: number): Uint8Array;
41
+ /**
42
+ * Generate a nanoid-style random string.
43
+ *
44
+ * FUN_FACT: Original nanoid implementation uses base64url alphabet
45
+ * with non-standard custom order where charater form common words found
46
+ * in source code (like use, random, strict) to make gzip/brotli more efficient.
47
+ * It's qt lil opt from the times where web developers
48
+ * were competing to have the smallest possible bundle size.
49
+ */
50
+ export declare function nanoid(size?: number, alphabet?: string): string;
package/dist/Unique.js ADDED
@@ -0,0 +1,187 @@
1
+ export const ALPHABET_BASE32_CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2
+ export const ALPHABET_BASE32_RFC4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
3
+ export const ALPHABET_BASE64_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4
+ export const ALPHABET_HEX = "0123456789abcdef";
5
+ /**
6
+ * Generate a random string for ids, session tokens, and API keys.
7
+ * It uses human-friendly crockford base32 encoding (5 bit of entropy per char)
8
+ *
9
+ * Minimal recommended length:
10
+ * - public ids: 16 chars (~80 bits)
11
+ * - API keys: 32 chars (~160 bits)
12
+ * - session tokens: 32-40 chars (~160-200 bits)
13
+ */
14
+ export function token(length = 32) {
15
+ if (length <= 0)
16
+ return "";
17
+ const buf = new Uint8Array(length);
18
+ crypto.getRandomValues(buf);
19
+ let result = "";
20
+ for (let i = 0; i < buf.length; i++) {
21
+ result += ALPHABET_BASE32_CROCKFORD[buf[i] & 31];
22
+ }
23
+ return result;
24
+ }
25
+ export function bytes(length) {
26
+ const buf = new Uint8Array(length);
27
+ crypto.getRandomValues(buf);
28
+ return buf;
29
+ }
30
+ export const UUID_NIL = "00000000-0000-0000-0000-000000000000";
31
+ export function uuid4() {
32
+ return formatUuid(uuid4bytes());
33
+ }
34
+ export function uuid7(time = Date.now()) {
35
+ return formatUuid(uuid7Bytes(time));
36
+ }
37
+ function uuid4bytes() {
38
+ const buf = bytes(16);
39
+ buf[6] = (buf[6] & 0x0f) | 0x40; // version 4
40
+ buf[8] = (buf[8] & 0x3f) | 0x80; // variant
41
+ return buf;
42
+ }
43
+ /**
44
+ * Decode a 48-bit Unix timestamp (ms) from UUID7 or ULID.
45
+ *
46
+ * @example
47
+ * const bytes = Unique.uuid7Bytes()
48
+ * const timestamp = Unique.toTimestamp(bytes)
49
+ *
50
+ * @example
51
+ * const bytes = Unique.ulidBytes()
52
+ * const timestamp = Unique.toTimestamp(bytes)
53
+ */
54
+ export function toTimestamp(bytes) {
55
+ if (bytes.length < 6)
56
+ return 0;
57
+ return (bytes[0] * 0x10000000000
58
+ + bytes[1] * 0x100000000
59
+ + bytes[2] * 0x1000000
60
+ + bytes[3] * 0x10000
61
+ + bytes[4] * 0x100
62
+ + bytes[5]);
63
+ }
64
+ export function uuid7Bytes(time = Date.now()) {
65
+ const buf = new Uint8Array(16);
66
+ const timestamp = BigInt(toSafeTime(time));
67
+ // 48-bit timestamp (6 bytes)
68
+ buf[0] = Number((timestamp >> 40n) & 0xffn);
69
+ buf[1] = Number((timestamp >> 32n) & 0xffn);
70
+ buf[2] = Number((timestamp >> 24n) & 0xffn);
71
+ buf[3] = Number((timestamp >> 16n) & 0xffn);
72
+ buf[4] = Number((timestamp >> 8n) & 0xffn);
73
+ buf[5] = Number(timestamp & 0xffn);
74
+ // 12-bit random A (1.5 bytes)
75
+ crypto.getRandomValues(buf.subarray(6, 8));
76
+ buf[6] = (buf[6] & 0x0f) | 0x70; // version 7
77
+ // 2-bit variant + 62-bit random B (8 bytes)
78
+ crypto.getRandomValues(buf.subarray(8, 16));
79
+ buf[8] = (buf[8] & 0x3f) | 0x80; // variant
80
+ return buf;
81
+ }
82
+ /**
83
+ * Convert UUID bytes to canonical (RFC9562) representation.
84
+ *
85
+ * @example
86
+ * Unique.formatUuid(new Uint8Array(16))
87
+ */
88
+ export function formatUuid(bytes) {
89
+ if (bytes.length === 0)
90
+ return "";
91
+ let result = "";
92
+ for (let i = 0; i < bytes.length; i++) {
93
+ const byte = bytes[i];
94
+ result += ALPHABET_HEX[(byte >> 4) & 0x0f];
95
+ result += ALPHABET_HEX[byte & 0x0f];
96
+ if (i === 3 || i === 5 || i === 7 || i === 9)
97
+ result += "-";
98
+ }
99
+ return result;
100
+ }
101
+ export function ulid(time = Date.now()) {
102
+ const bytes = ulidBytes(time);
103
+ return formatUlid(bytes);
104
+ }
105
+ export function ulidBytes(time = Date.now()) {
106
+ const buf = new Uint8Array(16);
107
+ const timestamp = BigInt(toSafeTime(time));
108
+ buf[0] = Number((timestamp >> 40n) & 0xffn);
109
+ buf[1] = Number((timestamp >> 32n) & 0xffn);
110
+ buf[2] = Number((timestamp >> 24n) & 0xffn);
111
+ buf[3] = Number((timestamp >> 16n) & 0xffn);
112
+ buf[4] = Number((timestamp >> 8n) & 0xffn);
113
+ buf[5] = Number(timestamp & 0xffn);
114
+ crypto.getRandomValues(buf.subarray(6, 16));
115
+ return buf;
116
+ }
117
+ function formatUlid(bytes) {
118
+ if (bytes.length !== 16)
119
+ return "";
120
+ const timestamp = toTimestamp(bytes);
121
+ const timePart = encodeUlidTime(timestamp);
122
+ const randomPart = toBase32(bytes.subarray(6, 16));
123
+ return `${timePart}${randomPart}`;
124
+ }
125
+ function encodeUlidTime(time) {
126
+ let value = BigInt(time);
127
+ const result = new Array(10);
128
+ for (let i = 9; i >= 0; i--) {
129
+ result[i] = ALPHABET_BASE32_CROCKFORD[Number(value & 31n)];
130
+ value >>= 5n;
131
+ }
132
+ return result.join("");
133
+ }
134
+ function toSafeTime(time) {
135
+ if (!Number.isFinite(time))
136
+ return 0;
137
+ return Math.max(0, Math.trunc(time));
138
+ }
139
+ /**
140
+ * Generate a nanoid-style random string.
141
+ *
142
+ * FUN_FACT: Original nanoid implementation uses base64url alphabet
143
+ * with non-standard custom order where charater form common words found
144
+ * in source code (like use, random, strict) to make gzip/brotli more efficient.
145
+ * It's qt lil opt from the times where web developers
146
+ * were competing to have the smallest possible bundle size.
147
+ */
148
+ export function nanoid(size = 21, alphabet = ALPHABET_BASE64_URL) {
149
+ if (size <= 0 || alphabet.length === 0)
150
+ return "";
151
+ const length = alphabet.length;
152
+ const mask = (2 << Math.floor(Math.log2(length - 1))) - 1;
153
+ const step = Math.ceil((1.6 * mask * size) / length);
154
+ let id = "";
155
+ while (id.length < size) {
156
+ const bytes = new Uint8Array(step);
157
+ crypto.getRandomValues(bytes);
158
+ for (let i = 0; i < step && id.length < size; i++) {
159
+ const index = bytes[i] & mask;
160
+ if (index < length)
161
+ id += alphabet[index];
162
+ }
163
+ }
164
+ return id;
165
+ }
166
+ function toBase32(bytes, alphabet = ALPHABET_BASE32_CROCKFORD) {
167
+ if (bytes.length === 0)
168
+ return "";
169
+ let result = "";
170
+ let buffer = 0;
171
+ let bits = 0;
172
+ for (const byte of bytes) {
173
+ buffer = (buffer << 8) | byte;
174
+ bits += 8;
175
+ while (bits >= 5) {
176
+ bits -= 5;
177
+ const index = (buffer >> bits) & 31;
178
+ result += alphabet[index];
179
+ buffer &= (1 << bits) - 1;
180
+ }
181
+ }
182
+ if (bits > 0) {
183
+ const index = (buffer << (5 - bits)) & 31;
184
+ result += alphabet[index];
185
+ }
186
+ return result;
187
+ }