effect-start 0.17.0 → 0.17.2

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 (148) hide show
  1. package/dist/Commander.d.ts +103 -0
  2. package/dist/Commander.js +333 -0
  3. package/dist/ContentNegotiation.d.ts +13 -0
  4. package/dist/ContentNegotiation.js +364 -0
  5. package/dist/Development.d.ts +34 -0
  6. package/dist/Development.js +52 -0
  7. package/dist/Entity.d.ts +47 -0
  8. package/dist/Entity.js +224 -0
  9. package/dist/FileRouter.d.ts +61 -0
  10. package/dist/FileRouter.js +203 -0
  11. package/dist/FileRouterCodegen.d.ts +19 -0
  12. package/dist/FileRouterCodegen.js +176 -0
  13. package/dist/FileRouterPattern.d.ts +9 -0
  14. package/dist/FileRouterPattern.js +35 -0
  15. package/dist/Http.d.ts +37 -0
  16. package/dist/Http.js +92 -0
  17. package/dist/HttpAppExtra.d.ts +7 -0
  18. package/dist/HttpAppExtra.js +320 -0
  19. package/dist/HttpUtils.d.ts +3 -0
  20. package/dist/HttpUtils.js +11 -0
  21. package/dist/PathPattern.d.ts +134 -0
  22. package/dist/PathPattern.js +415 -0
  23. package/dist/Random.d.ts +5 -0
  24. package/dist/Random.js +49 -0
  25. package/dist/Route.d.ts +98 -0
  26. package/dist/Route.js +81 -0
  27. package/dist/RouteBody.d.ts +53 -0
  28. package/dist/RouteBody.js +67 -0
  29. package/dist/RouteHook.d.ts +12 -0
  30. package/dist/RouteHook.js +45 -0
  31. package/dist/RouteHttp.d.ts +21 -0
  32. package/dist/RouteHttp.js +260 -0
  33. package/dist/RouteHttpTracer.d.ts +10 -0
  34. package/dist/RouteHttpTracer.js +62 -0
  35. package/dist/RouteMount.d.ts +119 -0
  36. package/dist/RouteMount.js +77 -0
  37. package/dist/RouteSchema.d.ts +65 -0
  38. package/dist/RouteSchema.js +155 -0
  39. package/dist/RouteSse.d.ts +21 -0
  40. package/dist/RouteSse.js +85 -0
  41. package/dist/RouteTree.d.ts +56 -0
  42. package/dist/RouteTree.js +91 -0
  43. package/dist/RouteTrie.d.ts +20 -0
  44. package/dist/RouteTrie.js +157 -0
  45. package/dist/RouterPattern.d.ts +118 -0
  46. package/dist/RouterPattern.js +269 -0
  47. package/dist/SchemaExtra.d.ts +7 -0
  48. package/dist/SchemaExtra.js +74 -0
  49. package/dist/Start.d.ts +19 -0
  50. package/dist/Start.js +23 -0
  51. package/dist/StartApp.d.ts +19 -0
  52. package/dist/StartApp.js +21 -0
  53. package/dist/StreamExtra.d.ts +28 -0
  54. package/dist/StreamExtra.js +100 -0
  55. package/dist/TuplePathPattern.d.ts +9 -0
  56. package/dist/TuplePathPattern.js +63 -0
  57. package/dist/Values.d.ts +26 -0
  58. package/dist/Values.js +30 -0
  59. package/dist/bun/BunBundle.d.ts +12 -0
  60. package/dist/bun/BunBundle.js +145 -0
  61. package/dist/bun/BunHttpServer.d.ts +44 -0
  62. package/dist/bun/BunHttpServer.js +187 -0
  63. package/dist/bun/BunHttpServer_web.d.ts +60 -0
  64. package/dist/bun/BunHttpServer_web.js +252 -0
  65. package/dist/bun/BunImportTrackerPlugin.d.ts +13 -0
  66. package/dist/bun/BunImportTrackerPlugin.js +71 -0
  67. package/dist/bun/BunRoute.d.ts +49 -0
  68. package/dist/bun/BunRoute.js +131 -0
  69. package/dist/bun/BunRuntime.d.ts +1 -0
  70. package/dist/bun/BunRuntime.js +26 -0
  71. package/dist/bun/BunVirtualFilesPlugin.d.ts +4 -0
  72. package/dist/bun/BunVirtualFilesPlugin.js +40 -0
  73. package/dist/bun/_BunEnhancedResolve.d.ts +45 -0
  74. package/dist/bun/_BunEnhancedResolve.js +104 -0
  75. package/dist/bun/index.d.ts +4 -0
  76. package/dist/bun/index.js +4 -0
  77. package/dist/bundler/Bundle.d.ts +60 -0
  78. package/dist/bundler/Bundle.js +48 -0
  79. package/dist/bundler/BundleFiles.d.ts +13 -0
  80. package/dist/bundler/BundleFiles.js +94 -0
  81. package/dist/bundler/BundleHttp.d.ts +45 -0
  82. package/dist/bundler/BundleHttp.js +176 -0
  83. package/dist/client/Overlay.d.ts +2 -0
  84. package/dist/client/Overlay.js +32 -0
  85. package/dist/client/ScrollState.d.ts +6 -0
  86. package/dist/client/ScrollState.js +98 -0
  87. package/dist/client/index.d.ts +6 -0
  88. package/dist/client/index.js +81 -0
  89. package/dist/experimental/EncryptedCookies.d.ts +51 -0
  90. package/dist/experimental/EncryptedCookies.js +243 -0
  91. package/dist/experimental/SseHttpResponse.d.ts +7 -0
  92. package/dist/experimental/SseHttpResponse.js +28 -0
  93. package/dist/experimental/index.d.ts +2 -0
  94. package/dist/experimental/index.js +2 -0
  95. package/dist/hyper/Hyper.d.ts +32 -0
  96. package/dist/hyper/Hyper.js +34 -0
  97. package/dist/hyper/HyperHtml.d.ts +23 -0
  98. package/dist/hyper/HyperHtml.js +144 -0
  99. package/dist/hyper/HyperNode.d.ts +14 -0
  100. package/dist/hyper/HyperNode.js +11 -0
  101. package/dist/hyper/HyperRoute.d.ts +8 -0
  102. package/dist/hyper/HyperRoute.js +32 -0
  103. package/dist/hyper/HyperRoute.test.d.ts +1 -0
  104. package/dist/hyper/HyperRoute.test.js +72 -0
  105. package/dist/hyper/index.d.ts +4 -0
  106. package/dist/hyper/index.js +4 -0
  107. package/dist/hyper/jsx-runtime.d.ts +7 -0
  108. package/dist/hyper/jsx-runtime.js +8 -0
  109. package/dist/index.d.ts +6 -0
  110. package/dist/index.js +6 -0
  111. package/dist/inference_check.d.ts +1 -0
  112. package/dist/inference_check.js +15 -0
  113. package/dist/middlewares/BasicAuthMiddleware.d.ts +8 -0
  114. package/dist/middlewares/BasicAuthMiddleware.js +22 -0
  115. package/dist/middlewares/index.d.ts +1 -0
  116. package/dist/middlewares/index.js +1 -0
  117. package/dist/node/FileSystem.d.ts +9 -0
  118. package/dist/node/FileSystem.js +440 -0
  119. package/dist/node/Utils.d.ts +1 -0
  120. package/dist/node/Utils.js +19 -0
  121. package/dist/repro_fail.d.ts +1 -0
  122. package/dist/repro_fail.js +14 -0
  123. package/dist/testing/TestHttpClient.d.ts +13 -0
  124. package/dist/testing/TestHttpClient.js +68 -0
  125. package/dist/testing/TestLogger.d.ts +13 -0
  126. package/dist/testing/TestLogger.js +29 -0
  127. package/dist/testing/index.d.ts +3 -0
  128. package/dist/testing/index.js +3 -0
  129. package/dist/testing/utils.d.ts +9 -0
  130. package/dist/testing/utils.js +39 -0
  131. package/dist/x/cloudflare/CloudflareTunnel.d.ts +13 -0
  132. package/dist/x/cloudflare/CloudflareTunnel.js +43 -0
  133. package/dist/x/cloudflare/index.d.ts +1 -0
  134. package/dist/x/cloudflare/index.js +1 -0
  135. package/dist/x/datastar/Datastar.d.ts +6 -0
  136. package/dist/x/datastar/Datastar.js +46 -0
  137. package/dist/x/datastar/index.d.ts +2 -0
  138. package/dist/x/datastar/index.js +2 -0
  139. package/dist/x/tailwind/TailwindPlugin.d.ts +23 -0
  140. package/dist/x/tailwind/TailwindPlugin.js +219 -0
  141. package/dist/x/tailwind/compile.d.ts +19 -0
  142. package/dist/x/tailwind/compile.js +156 -0
  143. package/dist/x/tailwind/plugin.d.ts +2 -0
  144. package/dist/x/tailwind/plugin.js +15 -0
  145. package/package.json +68 -16
  146. package/src/RouteBody.test.ts +18 -0
  147. package/src/RouteBody.ts +126 -2
  148. package/src/x/tailwind/compile.ts +8 -2
@@ -0,0 +1,157 @@
1
+ import * as PathPattern from "./PathPattern.js";
2
+ import * as Route from "./Route.js";
3
+ function createNode() {
4
+ return {
5
+ children: {},
6
+ paramChild: null,
7
+ paramName: null,
8
+ requiredWildcardChild: null,
9
+ requiredWildcardName: null,
10
+ optionalWildcardChild: null,
11
+ optionalWildcardName: null,
12
+ routes: [],
13
+ };
14
+ }
15
+ function insertRoute(node, segments, route) {
16
+ if (segments.length === 0) {
17
+ node.routes.push(route);
18
+ return;
19
+ }
20
+ const segment = segments[0];
21
+ const rest = segments.slice(1);
22
+ if (segment.startsWith(":")) {
23
+ const name = segment.slice(1);
24
+ if (name.endsWith("+")) {
25
+ if (!node.requiredWildcardChild) {
26
+ node.requiredWildcardChild = createNode();
27
+ }
28
+ node.requiredWildcardChild.requiredWildcardName = name.slice(0, -1);
29
+ node.requiredWildcardChild.routes.push(route);
30
+ }
31
+ else if (name.endsWith("*")) {
32
+ if (!node.optionalWildcardChild) {
33
+ node.optionalWildcardChild = createNode();
34
+ }
35
+ node.optionalWildcardChild.optionalWildcardName = name.slice(0, -1);
36
+ node.optionalWildcardChild.routes.push(route);
37
+ }
38
+ else if (name.endsWith("?")) {
39
+ if (!node.paramChild) {
40
+ node.paramChild = createNode();
41
+ }
42
+ node.paramChild.paramName = name.slice(0, -1);
43
+ insertRoute(node.paramChild, rest, route);
44
+ insertRoute(node, rest, route);
45
+ }
46
+ else {
47
+ if (!node.paramChild) {
48
+ node.paramChild = createNode();
49
+ }
50
+ node.paramChild.paramName = name;
51
+ insertRoute(node.paramChild, rest, route);
52
+ }
53
+ }
54
+ else {
55
+ if (!node.children[segment]) {
56
+ node.children[segment] = createNode();
57
+ }
58
+ insertRoute(node.children[segment], rest, route);
59
+ }
60
+ }
61
+ function collectRoutes(items, parentPath, parentMethod) {
62
+ const results = [];
63
+ for (const item of items) {
64
+ const desc = Route.descriptor(item);
65
+ const currentPath = typeof desc?.path === "string"
66
+ ? parentPath + desc.path
67
+ : parentPath;
68
+ const currentMethod = desc?.method ?? parentMethod;
69
+ if (Route.isRoute(item)) {
70
+ if (currentPath !== "") {
71
+ results.push({
72
+ route: item,
73
+ method: currentMethod,
74
+ path: currentPath,
75
+ });
76
+ }
77
+ }
78
+ else {
79
+ const nestedItems = Route.items(item);
80
+ results.push(...collectRoutes(nestedItems, currentPath, currentMethod));
81
+ }
82
+ }
83
+ return results;
84
+ }
85
+ export function make(set) {
86
+ const methods = {};
87
+ const collected = collectRoutes(Route.items(set), "", "*");
88
+ for (const { route, method, path } of collected) {
89
+ if (!methods[method]) {
90
+ methods[method] = createNode();
91
+ }
92
+ const result = PathPattern.validate(path);
93
+ if (!result.ok) {
94
+ throw new Error(result.error);
95
+ }
96
+ insertRoute(methods[method], result.segments, route);
97
+ }
98
+ return { methods };
99
+ }
100
+ function lookupNode(node, segments, params) {
101
+ const results = [];
102
+ if (segments.length === 0) {
103
+ for (const route of node.routes) {
104
+ results.push({ route, params });
105
+ }
106
+ if (node.optionalWildcardChild
107
+ && node.optionalWildcardChild.optionalWildcardName) {
108
+ for (const route of node.optionalWildcardChild.routes) {
109
+ results.push({ route, params });
110
+ }
111
+ }
112
+ return results;
113
+ }
114
+ const segment = segments[0];
115
+ const rest = segments.slice(1);
116
+ if (node.children[segment]) {
117
+ results.push(...lookupNode(node.children[segment], rest, params));
118
+ }
119
+ if (node.paramChild && node.paramChild.paramName) {
120
+ const newParams = { ...params, [node.paramChild.paramName]: segment };
121
+ results.push(...lookupNode(node.paramChild, rest, newParams));
122
+ }
123
+ if (node.requiredWildcardChild
124
+ && node.requiredWildcardChild.requiredWildcardName) {
125
+ const wildcardValue = segments.join("/");
126
+ const newParams = {
127
+ ...params,
128
+ [node.requiredWildcardChild.requiredWildcardName]: wildcardValue,
129
+ };
130
+ for (const route of node.requiredWildcardChild.routes) {
131
+ results.push({ route, params: newParams });
132
+ }
133
+ }
134
+ if (node.optionalWildcardChild
135
+ && node.optionalWildcardChild.optionalWildcardName) {
136
+ const wildcardValue = segments.join("/");
137
+ const newParams = {
138
+ ...params,
139
+ [node.optionalWildcardChild.optionalWildcardName]: wildcardValue,
140
+ };
141
+ for (const route of node.optionalWildcardChild.routes) {
142
+ results.push({ route, params: newParams });
143
+ }
144
+ }
145
+ return results;
146
+ }
147
+ export function lookup(trie, method, path) {
148
+ const segments = path.split("/").filter(Boolean);
149
+ const results = [];
150
+ if (trie.methods[method]) {
151
+ results.push(...lookupNode(trie.methods[method], segments, {}));
152
+ }
153
+ if (method !== "*" && trie.methods["*"]) {
154
+ results.push(...lookupNode(trie.methods["*"], segments, {}));
155
+ }
156
+ return results;
157
+ }
@@ -0,0 +1,118 @@
1
+ export type RouterPattern = `/${string}`;
2
+ export type ParamDelimiter = "_" | "-" | "." | "," | ";" | "!" | "@" | "~";
3
+ export type ParamPrefix = `${string}${ParamDelimiter}` | "";
4
+ export type ParamSuffix = `${ParamDelimiter}${string}` | "";
5
+ export type LiteralSegment<Value extends string = string> = {
6
+ _tag: "LiteralSegment";
7
+ value: Value;
8
+ };
9
+ export type ParamSegment<Name extends string = string, Optional extends boolean = boolean, Prefix extends ParamPrefix = "", Suffix extends ParamSuffix = ""> = {
10
+ _tag: "ParamSegment";
11
+ name: Name;
12
+ optional?: Optional;
13
+ prefix?: Prefix;
14
+ suffix?: Suffix;
15
+ };
16
+ export type RestSegment<Name extends string = string, Optional extends boolean = boolean> = {
17
+ _tag: "RestSegment";
18
+ name: Name;
19
+ optional?: Optional;
20
+ };
21
+ export type Segment = LiteralSegment | ParamSegment<string, boolean, ParamPrefix, ParamSuffix> | RestSegment;
22
+ /**
23
+ * Parses a route path string into a tuple of Segment types at compile time.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * type Usage = Segments<"/users/[id]/posts/[...rest]">
28
+ * type Expected = [
29
+ * LiteralSegment<"users">,
30
+ * ParamSegment<"id", false>,
31
+ * LiteralSegment<"posts">,
32
+ * RestSegment<"rest", false>
33
+ * ]
34
+ * ```
35
+ *
36
+ * Supports:
37
+ * - Literals: `users` → `LiteralSegment<"users">`
38
+ * - Params: `[id]` → `ParamSegment<"id", false>`
39
+ * - Params (optional): `[[id]]` → `ParamSegment<"id", true>`
40
+ * - Rest: `[...rest]` → `RestSegment<"rest", false>`
41
+ * - Rest (optional): `[[...rest]]` → `RestSegment<"rest", true>`
42
+ * - {Pre,Suf}fixed params: `prefix_[id]_suffix` → `ParamSegment<"id", false, "prefix_", "_suffix">`
43
+ * - Malformed segments: `pk_[id]foo` → `undefined` (suffix must start with delimiter)
44
+ *
45
+ * @limit Paths with more than 48 segments in TypeScript 5.9.3 will fail with
46
+ * `TS2589: Type instantiation is excessively deep and possibly infinite`.
47
+ */
48
+ export type Segments<Path extends string> = Path extends `/${infer PathRest}` ? Segments<PathRest> : Path extends `${infer Head}/${infer Tail}` ? [ExtractSegment<Head>, ...Segments<Tail>] : Path extends "" ? [] : [ExtractSegment<Path>];
49
+ export declare function parseSegment(segment: string): Segment | null;
50
+ export declare function parse(pattern: string): Segment[];
51
+ export declare function formatSegment(seg: Segment): string;
52
+ export declare function format(segments: Segment[]): `/${string}`;
53
+ /**
54
+ * Converts to colon-style path pattern (used by Hono, Bun, itty-router).
55
+ *
56
+ * - `[param]` → `:param`
57
+ * - `[[param]]` → `:param?`
58
+ * - `[...param]` → `*`
59
+ * - `[[...param]]` → `/`, `/*`
60
+ * - `pk_[id]` → `pk_:id`
61
+ */
62
+ export declare function toColon(path: RouterPattern): string[];
63
+ export declare const toHono: typeof toColon;
64
+ /**
65
+ * Converts to Express path pattern.
66
+ *
67
+ * - `[param]` → `:param`
68
+ * - `[[param]]` → `{/:param}`
69
+ * - `[...param]` → `/*param`
70
+ * - `[[...param]]` → `/`, `/*param`
71
+ * - `pk_[id]` → `pk_:id`
72
+ */
73
+ export declare function toExpress(path: RouterPattern): string[];
74
+ /**
75
+ * Converts to Effect HttpRouter/find-my-way path pattern.
76
+ *
77
+ * - `[param]` → `:param`
78
+ * - `[[param]]` → `:param?` (must be final segment)
79
+ * - `[...param]` → `*`
80
+ * - `[[...param]]` → `/`, `/*`
81
+ * - `pk_[id]` → `pk_:id`
82
+ */
83
+ export declare function toEffect(path: RouterPattern): string[];
84
+ /**
85
+ * Converts to URLPattern path pattern.
86
+ *
87
+ * - `[param]` → `:param`
88
+ * - `[[param]]` → `:param?`
89
+ * - `[...param]` → `:param+`
90
+ * - `[[...param]]` → `:param*`
91
+ * - `pk_[id]` → `pk_:id`
92
+ */
93
+ export declare function toURLPattern(path: RouterPattern): string[];
94
+ /**
95
+ * Converts to Remix path pattern.
96
+ *
97
+ * - `[param]` → `$param`
98
+ * - `[[param]]` → `($param)`
99
+ * - `[...param]` → `$`
100
+ * - `[[...param]]` → `/`, `$`
101
+ * - `pk_[id]` → (not supported, emits `pk_$id`)
102
+ */
103
+ export declare function toRemix(path: RouterPattern): string[];
104
+ /**
105
+ * Converts to Bun.serve path pattern.
106
+ *
107
+ * Since Bun doesn't support optional params (`:param?`), optional segments
108
+ * are expanded into multiple routes recursively.
109
+ *
110
+ * - `[param]` → `:param`
111
+ * - `[[param]]` → `/`, `/:param` (two routes)
112
+ * - `[...param]` → `*`
113
+ * - `[[...param]]` → `/`, `/*` (two routes)
114
+ * - `pk_[id]` → `pk_:id`
115
+ */
116
+ export declare function toBun(path: RouterPattern): string[];
117
+ type ExtractSegment<S extends string> = S extends `[[...${infer Name}]]` ? RestSegment<Name, true> : S extends `[...${infer Name}]` ? RestSegment<Name, false> : S extends `[[${infer Name}]]` ? ParamSegment<Name, true, "", ""> : S extends `${infer Pre extends `${string}${ParamDelimiter}`}[${infer Name}]${infer Suf}` ? Suf extends `${infer Delim extends ParamDelimiter}${infer SufRest}` ? ParamSegment<Name, false, Pre, `${Delim}${SufRest}`> : Suf extends "" ? ParamSegment<Name, false, Pre, ""> : undefined : S extends `[${infer Name}]${infer Suf extends `${ParamDelimiter}${string}`}` ? ParamSegment<Name, false, "", Suf> : S extends `[${infer Name}]` ? ParamSegment<Name, false, "", ""> : LiteralSegment<S>;
118
+ export {};
@@ -0,0 +1,269 @@
1
+ const PARAM_PATTERN = /^(?<prefix>.*[^a-zA-Z0-9])?\[(?<name>[^\]]+)\](?<suffix>[^a-zA-Z0-9].*)?$/;
2
+ export function parseSegment(segment) {
3
+ if (segment.startsWith("[[...")
4
+ && segment.endsWith("]]")) {
5
+ return {
6
+ _tag: "RestSegment",
7
+ name: segment.slice(5, -2),
8
+ optional: true,
9
+ };
10
+ }
11
+ if (segment.startsWith("[...")
12
+ && segment.endsWith("]")) {
13
+ return {
14
+ _tag: "RestSegment",
15
+ name: segment.slice(4, -1),
16
+ };
17
+ }
18
+ if (segment.startsWith("[[")
19
+ && segment.endsWith("]]")) {
20
+ return {
21
+ _tag: "ParamSegment",
22
+ name: segment.slice(2, -2),
23
+ optional: true,
24
+ };
25
+ }
26
+ const match = segment.match(PARAM_PATTERN);
27
+ if (match?.groups) {
28
+ const { prefix, name, suffix } = match.groups;
29
+ return {
30
+ _tag: "ParamSegment",
31
+ name,
32
+ prefix: prefix || undefined,
33
+ suffix: suffix || undefined,
34
+ };
35
+ }
36
+ if (/^[\p{L}\p{N}._~-]+$/u.test(segment)) {
37
+ return { _tag: "LiteralSegment", value: segment };
38
+ }
39
+ return null;
40
+ }
41
+ export function parse(pattern) {
42
+ const segments = pattern.split("/").filter(Boolean).map(parseSegment);
43
+ if (segments.some((seg) => seg === null)) {
44
+ throw new Error(`Invalid path segment in "${pattern}": contains invalid characters or format`);
45
+ }
46
+ return segments;
47
+ }
48
+ export function formatSegment(seg) {
49
+ switch (seg._tag) {
50
+ case "LiteralSegment":
51
+ return seg.value;
52
+ case "ParamSegment": {
53
+ const param = seg.optional ? `[[${seg.name}]]` : `[${seg.name}]`;
54
+ return (seg.prefix ?? "") + param + (seg.suffix ?? "");
55
+ }
56
+ case "RestSegment":
57
+ return seg.optional ? `[[...${seg.name}]]` : `[...${seg.name}]`;
58
+ }
59
+ }
60
+ export function format(segments) {
61
+ const joined = segments.map(formatSegment).join("/");
62
+ return (joined ? `/${joined}` : "/");
63
+ }
64
+ function buildPaths(segments, mapper, restWildcard) {
65
+ const optionalRestIndex = segments.findIndex((s) => s._tag === "RestSegment" && s.optional);
66
+ if (optionalRestIndex !== -1) {
67
+ const before = segments.slice(0, optionalRestIndex);
68
+ const beforeJoined = before.map(mapper).join("/");
69
+ const basePath = beforeJoined ? "/" + beforeJoined : "/";
70
+ const withWildcard = basePath === "/"
71
+ ? restWildcard
72
+ : basePath + restWildcard;
73
+ return [basePath, withWildcard];
74
+ }
75
+ const joined = segments.map(mapper).join("/");
76
+ return [joined ? "/" + joined : "/"];
77
+ }
78
+ function colonParamSegment(segment) {
79
+ switch (segment._tag) {
80
+ case "LiteralSegment":
81
+ return segment.value;
82
+ case "ParamSegment": {
83
+ const param = `:${segment.name}${segment.optional ? "?" : ""}`;
84
+ return (segment.prefix ?? "") + param + (segment.suffix ?? "");
85
+ }
86
+ case "RestSegment":
87
+ return "*";
88
+ }
89
+ }
90
+ /**
91
+ * Converts to colon-style path pattern (used by Hono, Bun, itty-router).
92
+ *
93
+ * - `[param]` → `:param`
94
+ * - `[[param]]` → `:param?`
95
+ * - `[...param]` → `*`
96
+ * - `[[...param]]` → `/`, `/*`
97
+ * - `pk_[id]` → `pk_:id`
98
+ */
99
+ export function toColon(path) {
100
+ return buildPaths(parse(path), colonParamSegment, "/*");
101
+ }
102
+ export const toHono = toColon;
103
+ /**
104
+ * Converts to Express path pattern.
105
+ *
106
+ * - `[param]` → `:param`
107
+ * - `[[param]]` → `{/:param}`
108
+ * - `[...param]` → `/*param`
109
+ * - `[[...param]]` → `/`, `/*param`
110
+ * - `pk_[id]` → `pk_:id`
111
+ */
112
+ export function toExpress(path) {
113
+ const segments = parse(path);
114
+ const optionalRestIndex = segments.findIndex((s) => s._tag === "RestSegment" && s.optional);
115
+ const mapper = (segment) => {
116
+ switch (segment._tag) {
117
+ case "LiteralSegment":
118
+ return segment.value;
119
+ case "ParamSegment": {
120
+ const param = `:${segment.name}`;
121
+ return (segment.prefix ?? "") + param + (segment.suffix ?? "");
122
+ }
123
+ case "RestSegment":
124
+ return `*${segment.name}`;
125
+ }
126
+ };
127
+ if (optionalRestIndex !== -1) {
128
+ const before = segments.slice(0, optionalRestIndex);
129
+ const rest = segments[optionalRestIndex];
130
+ if (rest._tag !== "RestSegment")
131
+ throw new Error("unreachable");
132
+ const restName = rest.name;
133
+ const beforeJoined = before.map(mapper).join("/");
134
+ const basePath = beforeJoined ? "/" + beforeJoined : "/";
135
+ const withWildcard = basePath === "/"
136
+ ? `/*${restName}`
137
+ : basePath + `/*${restName}`;
138
+ return [basePath, withWildcard];
139
+ }
140
+ let result = "";
141
+ for (let i = 0; i < segments.length; i++) {
142
+ const segment = segments[i];
143
+ const isFirst = i === 0;
144
+ switch (segment._tag) {
145
+ case "LiteralSegment":
146
+ result += "/" + segment.value;
147
+ break;
148
+ case "ParamSegment":
149
+ if (segment.optional && !segment.prefix && !segment.suffix) {
150
+ result += isFirst
151
+ ? "/{/:$name}".replace("$name", segment.name)
152
+ : `{/:${segment.name}}`;
153
+ }
154
+ else {
155
+ const param = `:${segment.name}`;
156
+ result += "/"
157
+ + (segment.prefix ?? "")
158
+ + param
159
+ + (segment.suffix ?? "");
160
+ }
161
+ break;
162
+ case "RestSegment":
163
+ result += `/*${segment.name}`;
164
+ break;
165
+ }
166
+ }
167
+ return [result || "/"];
168
+ }
169
+ /**
170
+ * Converts to Effect HttpRouter/find-my-way path pattern.
171
+ *
172
+ * - `[param]` → `:param`
173
+ * - `[[param]]` → `:param?` (must be final segment)
174
+ * - `[...param]` → `*`
175
+ * - `[[...param]]` → `/`, `/*`
176
+ * - `pk_[id]` → `pk_:id`
177
+ */
178
+ export function toEffect(path) {
179
+ return buildPaths(parse(path), colonParamSegment, "/*");
180
+ }
181
+ /**
182
+ * Converts to URLPattern path pattern.
183
+ *
184
+ * - `[param]` → `:param`
185
+ * - `[[param]]` → `:param?`
186
+ * - `[...param]` → `:param+`
187
+ * - `[[...param]]` → `:param*`
188
+ * - `pk_[id]` → `pk_:id`
189
+ */
190
+ export function toURLPattern(path) {
191
+ const segments = parse(path);
192
+ const joined = segments
193
+ .map((segment) => {
194
+ switch (segment._tag) {
195
+ case "LiteralSegment":
196
+ return segment.value;
197
+ case "ParamSegment": {
198
+ const param = `:${segment.name}${segment.optional ? "?" : ""}`;
199
+ return (segment.prefix ?? "") + param + (segment.suffix ?? "");
200
+ }
201
+ case "RestSegment":
202
+ return `:${segment.name}${segment.optional ? "*" : "+"}`;
203
+ }
204
+ })
205
+ .join("/");
206
+ return [joined ? "/" + joined : "/"];
207
+ }
208
+ /**
209
+ * Converts to Remix path pattern.
210
+ *
211
+ * - `[param]` → `$param`
212
+ * - `[[param]]` → `($param)`
213
+ * - `[...param]` → `$`
214
+ * - `[[...param]]` → `/`, `$`
215
+ * - `pk_[id]` → (not supported, emits `pk_$id`)
216
+ */
217
+ export function toRemix(path) {
218
+ const segments = parse(path);
219
+ const optionalRestIndex = segments.findIndex((s) => s._tag === "RestSegment" && s.optional);
220
+ const mapper = (segment) => {
221
+ switch (segment._tag) {
222
+ case "LiteralSegment":
223
+ return segment.value;
224
+ case "ParamSegment": {
225
+ const param = segment.optional
226
+ ? `($${segment.name})`
227
+ : `$${segment.name}`;
228
+ return (segment.prefix ?? "") + param + (segment.suffix ?? "");
229
+ }
230
+ case "RestSegment":
231
+ return "$";
232
+ }
233
+ };
234
+ if (optionalRestIndex !== -1) {
235
+ const before = segments.slice(0, optionalRestIndex);
236
+ const beforeJoined = before.map(mapper).join("/");
237
+ const basePath = beforeJoined ? "/" + beforeJoined : "/";
238
+ const withWildcard = basePath === "/" ? "$" : basePath + "/$";
239
+ return [basePath, withWildcard];
240
+ }
241
+ const joined = segments.map(mapper).join("/");
242
+ return [joined ? "/" + joined : "/"];
243
+ }
244
+ /**
245
+ * Converts to Bun.serve path pattern.
246
+ *
247
+ * Since Bun doesn't support optional params (`:param?`), optional segments
248
+ * are expanded into multiple routes recursively.
249
+ *
250
+ * - `[param]` → `:param`
251
+ * - `[[param]]` → `/`, `/:param` (two routes)
252
+ * - `[...param]` → `*`
253
+ * - `[[...param]]` → `/`, `/*` (two routes)
254
+ * - `pk_[id]` → `pk_:id`
255
+ */
256
+ export function toBun(path) {
257
+ const segments = parse(path);
258
+ const optionalIndex = segments.findIndex((s) => (s._tag === "ParamSegment" || s._tag === "RestSegment") && s.optional);
259
+ if (optionalIndex === -1) {
260
+ return buildPaths(segments, colonParamSegment, "/*");
261
+ }
262
+ const before = segments.slice(0, optionalIndex);
263
+ const optional = { ...segments[optionalIndex], optional: false };
264
+ const after = segments.slice(optionalIndex + 1);
265
+ return [
266
+ ...toBun(format(before)),
267
+ ...toBun(format([...before, optional, ...after])),
268
+ ];
269
+ }
@@ -0,0 +1,7 @@
1
+ import * as Schema from "effect/Schema";
2
+ import * as SchemaAST from "effect/SchemaAST";
3
+ export declare function getBaseSchemaAST(schema: Schema.Schema.Any): SchemaAST.AST;
4
+ export declare function isOptional(schema: Schema.Schema.Any): boolean;
5
+ export declare function schemaEqual(userSchema: Schema.Struct<any> | undefined, expectedSchema: Schema.Struct<any> | null): boolean;
6
+ export declare function getSchemaTypeName(schema: Schema.Schema.Any): string;
7
+ export declare function formatSchemaCode(schema: Schema.Struct<any>): string;
@@ -0,0 +1,74 @@
1
+ import * as SchemaAST from "effect/SchemaAST";
2
+ export function getBaseSchemaAST(schema) {
3
+ let current = schema.ast;
4
+ while (SchemaAST.isRefinement(current) || SchemaAST.isTransformation(current)) {
5
+ current = current.from;
6
+ }
7
+ return current;
8
+ }
9
+ export function isOptional(schema) {
10
+ const ast = schema.ast;
11
+ if (ast._tag === "Union") {
12
+ return ast.types.some((t) => t._tag === "UndefinedKeyword");
13
+ }
14
+ return false;
15
+ }
16
+ export function schemaEqual(userSchema, expectedSchema) {
17
+ if (!userSchema && !expectedSchema) {
18
+ return true;
19
+ }
20
+ if (!userSchema || !expectedSchema) {
21
+ return false;
22
+ }
23
+ const userFields = userSchema.fields;
24
+ const expectedFields = expectedSchema.fields;
25
+ const userKeys = Object.keys(userFields).sort();
26
+ const expectedKeys = Object.keys(expectedFields).sort();
27
+ if (userKeys.length !== expectedKeys.length) {
28
+ return false;
29
+ }
30
+ for (let i = 0; i < userKeys.length; i++) {
31
+ if (userKeys[i] !== expectedKeys[i]) {
32
+ return false;
33
+ }
34
+ }
35
+ for (const key of userKeys) {
36
+ const userFieldSchema = userFields[key];
37
+ const expectedFieldSchema = expectedFields[key];
38
+ const userOptional = isOptional(userFieldSchema);
39
+ const expectedOptional = isOptional(expectedFieldSchema);
40
+ if (userOptional !== expectedOptional) {
41
+ return false;
42
+ }
43
+ const userBaseAST = getBaseSchemaAST(userFieldSchema);
44
+ const expectedBaseAST = getBaseSchemaAST(expectedFieldSchema);
45
+ if (userBaseAST._tag !== expectedBaseAST._tag) {
46
+ return false;
47
+ }
48
+ }
49
+ return true;
50
+ }
51
+ export function getSchemaTypeName(schema) {
52
+ const baseAST = getBaseSchemaAST(schema);
53
+ switch (baseAST._tag) {
54
+ case "StringKeyword":
55
+ return "Schema.String";
56
+ case "NumberKeyword":
57
+ return "Schema.Number";
58
+ case "BooleanKeyword":
59
+ return "Schema.Boolean";
60
+ default:
61
+ return "Schema.String";
62
+ }
63
+ }
64
+ export function formatSchemaCode(schema) {
65
+ const fields = schema.fields;
66
+ const fieldStrings = [];
67
+ for (const [key, fieldSchema] of Object.entries(fields)) {
68
+ const optional = isOptional(fieldSchema);
69
+ const typeName = getSchemaTypeName(fieldSchema);
70
+ const fieldStr = optional ? `${key}?: ${typeName}` : `${key}: ${typeName}`;
71
+ fieldStrings.push(fieldStr);
72
+ }
73
+ return `{ ${fieldStrings.join(", ")} }`;
74
+ }
@@ -0,0 +1,19 @@
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
+ import * as Layer from "effect/Layer";
6
+ import * as BunHttpServer from "./bun/BunHttpServer.ts";
7
+ export declare function layer<Layers extends [
8
+ Layer.Layer<never, any, any>,
9
+ ...Array<Layer.Layer<never, any, any>>
10
+ ]>(...layers: Layers): Layer.Layer<{
11
+ [k in keyof Layers]: Layer.Layer.Success<Layers[k]>;
12
+ }[number], {
13
+ [k in keyof Layers]: Layer.Layer.Error<Layers[k]>;
14
+ }[number], {
15
+ [k in keyof Layers]: Layer.Layer.Context<Layers[k]>;
16
+ }[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>;
19
+ }>): void;
package/dist/Start.js ADDED
@@ -0,0 +1,23 @@
1
+ import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
2
+ import * as HttpRouter from "@effect/platform/HttpRouter";
3
+ import * as HttpServer from "@effect/platform/HttpServer";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Function from "effect/Function";
6
+ import * as Layer from "effect/Layer";
7
+ import * as BunHttpServer from "./bun/BunHttpServer.js";
8
+ import * as BunRuntime from "./bun/BunRuntime.js";
9
+ import * as NodeFileSystem from "./node/FileSystem.js";
10
+ import * as StartApp from "./StartApp.js";
11
+ export function layer(...layers) {
12
+ return Layer.mergeAll(...layers);
13
+ }
14
+ export function serve(load) {
15
+ 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);
23
+ }
@@ -0,0 +1,19 @@
1
+ import * as HttpApp from "@effect/platform/HttpApp";
2
+ import * as Context from "effect/Context";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Layer from "effect/Layer";
5
+ import * as PubSub from "effect/PubSub";
6
+ import * as Ref from "effect/Ref";
7
+ type StartMiddleware = <E, R>(self: HttpApp.Default<E, R>) => HttpApp.Default<never, never>;
8
+ declare const StartApp_base: Context.TagClass<StartApp, "effect-start/StartApp", {
9
+ readonly env: "development" | "production" | string;
10
+ readonly addMiddleware: (middleware: StartMiddleware) => Effect.Effect<void>;
11
+ readonly middleware: Ref.Ref<StartMiddleware>;
12
+ readonly events: PubSub.PubSub<any>;
13
+ }>;
14
+ export declare class StartApp extends StartApp_base {
15
+ }
16
+ export declare function layer(options?: {
17
+ env?: string;
18
+ }): Layer.Layer<StartApp, never, never>;
19
+ export {};