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,415 @@
1
+ function isValidSegment(segment) {
2
+ if (segment.startsWith(":")) {
3
+ const rest = segment.slice(1);
4
+ if (rest.endsWith("*") || rest.endsWith("+") || rest.endsWith("?")) {
5
+ const name = rest.slice(0, -1);
6
+ return name !== "" && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name);
7
+ }
8
+ return rest !== "" && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(rest);
9
+ }
10
+ return /^[\p{L}\p{N}._~-]+$/u.test(segment);
11
+ }
12
+ export function validate(path) {
13
+ const segments = path.split("/").filter(Boolean);
14
+ for (const segment of segments) {
15
+ if (!isValidSegment(segment)) {
16
+ return {
17
+ ok: false,
18
+ error: `Invalid segment "${segment}" in "${path}"`,
19
+ };
20
+ }
21
+ }
22
+ return { ok: true, segments };
23
+ }
24
+ export function match(pattern, path) {
25
+ const patternSegments = pattern.split("/").filter(Boolean);
26
+ const pathSegments = path.split("/").filter(Boolean);
27
+ const params = {};
28
+ let patternIndex = 0;
29
+ let pathIndex = 0;
30
+ while (patternIndex < patternSegments.length) {
31
+ const seg = patternSegments[patternIndex];
32
+ if (seg.startsWith(":")) {
33
+ const rest = seg.slice(1);
34
+ if (rest.endsWith("+")) {
35
+ const name = rest.slice(0, -1);
36
+ const remaining = pathSegments.slice(pathIndex);
37
+ if (remaining.length === 0) {
38
+ return null;
39
+ }
40
+ params[name] = remaining.join("/");
41
+ return params;
42
+ }
43
+ if (rest.endsWith("*")) {
44
+ const name = rest.slice(0, -1);
45
+ const remaining = pathSegments.slice(pathIndex);
46
+ if (remaining.length > 0) {
47
+ params[name] = remaining.join("/");
48
+ }
49
+ return params;
50
+ }
51
+ if (rest.endsWith("?")) {
52
+ const name = rest.slice(0, -1);
53
+ if (pathIndex < pathSegments.length) {
54
+ params[name] = pathSegments[pathIndex];
55
+ pathIndex++;
56
+ }
57
+ patternIndex++;
58
+ continue;
59
+ }
60
+ if (pathIndex >= pathSegments.length) {
61
+ return null;
62
+ }
63
+ params[rest] = pathSegments[pathIndex];
64
+ pathIndex++;
65
+ patternIndex++;
66
+ continue;
67
+ }
68
+ if (pathIndex >= pathSegments.length) {
69
+ return null;
70
+ }
71
+ if (seg !== pathSegments[pathIndex]) {
72
+ return null;
73
+ }
74
+ pathIndex++;
75
+ patternIndex++;
76
+ }
77
+ if (pathIndex !== pathSegments.length) {
78
+ return null;
79
+ }
80
+ return params;
81
+ }
82
+ export function toRegex(path) {
83
+ const result = path
84
+ .replace(/\/+(\/|$)/g, "$1")
85
+ .replace(/\./g, "\\.")
86
+ .replace(/(\/?):(\w+)\+/g, "($1(?<$2>*))")
87
+ .replace(/(\/?):(\w+)\*/g, "(?:\\/(?<$2>.*))?")
88
+ .replace(/(\/?):(\w+)/g, "($1(?<$2>[^$1/]+?))")
89
+ .replace(/(\/?)\*/g, "($1.*)?");
90
+ return new RegExp(`^${result}/*$`);
91
+ }
92
+ function getModifier(seg) {
93
+ const last = seg[seg.length - 1];
94
+ if (last === "?" || last === "*" || last === "+")
95
+ return last;
96
+ return "";
97
+ }
98
+ function getParamName(seg) {
99
+ const modifier = getModifier(seg);
100
+ return modifier ? seg.slice(1, -1) : seg.slice(1);
101
+ }
102
+ /**
103
+ * Converts to Express path pattern.
104
+ *
105
+ * @see https://expressjs.com/en/guide/routing.html
106
+ *
107
+ * - `:param` → `:param`
108
+ * - `:param?` → `{/:param}`
109
+ * - `:param+` → `/*param`
110
+ * - `:param*` → `/`, `/*param`
111
+ */
112
+ export function toExpress(path) {
113
+ const segments = path.split("/").filter(Boolean);
114
+ const optionalWildcardIndex = segments.findIndex((s) => s.startsWith(":") && s.endsWith("*"));
115
+ if (optionalWildcardIndex !== -1) {
116
+ const before = segments.slice(0, optionalWildcardIndex);
117
+ const rest = segments[optionalWildcardIndex];
118
+ const name = getParamName(rest);
119
+ const beforeJoined = before
120
+ .map((s) => (s.startsWith(":") ? `:${getParamName(s)}` : s))
121
+ .join("/");
122
+ const basePath = beforeJoined ? "/" + beforeJoined : "/";
123
+ const withWildcard = basePath === "/" ? `/*${name}` : basePath + `/*${name}`;
124
+ return [basePath, withWildcard];
125
+ }
126
+ let result = "";
127
+ for (const seg of segments) {
128
+ if (!seg.startsWith(":")) {
129
+ result += "/" + seg;
130
+ }
131
+ else {
132
+ const name = getParamName(seg);
133
+ const modifier = getModifier(seg);
134
+ switch (modifier) {
135
+ case "":
136
+ result += `/:${name}`;
137
+ break;
138
+ case "?":
139
+ result += `{/:${name}}`;
140
+ break;
141
+ case "+":
142
+ result += `/*${name}`;
143
+ break;
144
+ case "*":
145
+ result += `/*${name}`;
146
+ break;
147
+ }
148
+ }
149
+ }
150
+ return [result || "/"];
151
+ }
152
+ /**
153
+ * Converts to URLPattern path pattern.
154
+ *
155
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
156
+ *
157
+ * - `:param` → `:param`
158
+ * - `:param?` → `:param?`
159
+ * - `:param+` → `:param+`
160
+ * - `:param*` → `:param*`
161
+ */
162
+ export function toURLPattern(path) {
163
+ const segments = path.split("/").filter(Boolean);
164
+ const joined = segments
165
+ .map((seg) => {
166
+ if (!seg.startsWith(":"))
167
+ return seg;
168
+ const name = getParamName(seg);
169
+ const modifier = getModifier(seg);
170
+ return `:${name}${modifier}`;
171
+ })
172
+ .join("/");
173
+ return [joined ? "/" + joined : "/"];
174
+ }
175
+ /**
176
+ * Converts to React Router path pattern.
177
+ *
178
+ * @see https://reactrouter.com/start/framework/routing
179
+ *
180
+ * - `:param` → `:param`
181
+ * - `:param?` → `:param?`
182
+ * - `:param+` → `*` (splat, required)
183
+ * - `:param*` → `/`, `/*` (splat, optional - two routes)
184
+ */
185
+ export function toReactRouter(path) {
186
+ const segments = path.split("/").filter(Boolean);
187
+ const optionalWildcardIndex = segments.findIndex((s) => s.startsWith(":") && s.endsWith("*"));
188
+ if (optionalWildcardIndex !== -1) {
189
+ const before = segments.slice(0, optionalWildcardIndex);
190
+ const beforeJoined = before
191
+ .map((s) => {
192
+ if (!s.startsWith(":"))
193
+ return s;
194
+ const name = getParamName(s);
195
+ const modifier = getModifier(s);
196
+ return modifier === "?" ? `:${name}?` : `:${name}`;
197
+ })
198
+ .join("/");
199
+ const basePath = beforeJoined ? "/" + beforeJoined : "/";
200
+ const withWildcard = basePath === "/" ? "/*" : basePath + "/*";
201
+ return [basePath, withWildcard];
202
+ }
203
+ const joined = segments
204
+ .map((s) => {
205
+ if (!s.startsWith(":"))
206
+ return s;
207
+ const name = getParamName(s);
208
+ const modifier = getModifier(s);
209
+ switch (modifier) {
210
+ case "":
211
+ return `:${name}`;
212
+ case "?":
213
+ return `:${name}?`;
214
+ case "+":
215
+ case "*":
216
+ return "*";
217
+ }
218
+ })
219
+ .join("/");
220
+ return [joined ? "/" + joined : "/"];
221
+ }
222
+ /**
223
+ * Alias for toReactRouter.
224
+ *
225
+ * @see https://reactrouter.com/start/framework/routing
226
+ */
227
+ export const toRemix = toReactRouter;
228
+ /**
229
+ * Converts to Remix file-based route naming convention.
230
+ *
231
+ * Returns a file path segment (without extension) for Remix's
232
+ * flat file routing convention.
233
+ *
234
+ * @see https://remix.run/docs/file-conventions/routes
235
+ *
236
+ * - `:param` → `$param`
237
+ * - `:param?` → `($param)`
238
+ * - `:param+` → `$` (splat)
239
+ * - `:param*` → `($)` (optional splat) - Note: not officially supported
240
+ */
241
+ export function toRemixFile(path) {
242
+ const segments = path.split("/").filter(Boolean);
243
+ const mapped = segments.map((seg) => {
244
+ if (!seg.startsWith(":"))
245
+ return seg;
246
+ const name = getParamName(seg);
247
+ const modifier = getModifier(seg);
248
+ switch (modifier) {
249
+ case "":
250
+ return `$${name}`;
251
+ case "?":
252
+ return `($${name})`;
253
+ case "+":
254
+ return "$";
255
+ case "*":
256
+ return "($)";
257
+ }
258
+ });
259
+ return mapped.join(".");
260
+ }
261
+ /**
262
+ * Converts to TanStack Router path/file pattern.
263
+ *
264
+ * TanStack uses the same `$param` syntax for both route paths and file names.
265
+ * Returns a dot-separated file name (without extension).
266
+ *
267
+ * @see https://tanstack.com/router/v1/docs/framework/react/guide/path-params
268
+ * @see https://tanstack.com/router/v1/docs/framework/react/routing/file-naming-conventions
269
+ *
270
+ * - `:param` → `$param`
271
+ * - `:param?` → `{-$param}` (optional segment)
272
+ * - `:param+` → `$` (splat)
273
+ * - `:param*` → `$` (splat, optional not supported - treated as required)
274
+ */
275
+ export function toTanStack(path) {
276
+ const segments = path.split("/").filter(Boolean);
277
+ const mapped = segments.map((seg) => {
278
+ if (!seg.startsWith(":"))
279
+ return seg;
280
+ const name = getParamName(seg);
281
+ const modifier = getModifier(seg);
282
+ switch (modifier) {
283
+ case "":
284
+ return `$${name}`;
285
+ case "?":
286
+ return `{-$${name}}`;
287
+ case "+":
288
+ return "$";
289
+ case "*":
290
+ return "$";
291
+ }
292
+ });
293
+ return mapped.join(".");
294
+ }
295
+ /**
296
+ * Converts to Hono path pattern.
297
+ *
298
+ * Hono uses unnamed wildcards - they are NOT accessible via c.req.param().
299
+ * Use c.req.path to access the matched path for wildcard routes.
300
+ *
301
+ * @see https://hono.dev/docs/api/routing
302
+ *
303
+ * - `:param` → `:param`
304
+ * - `:param?` → `:param?`
305
+ * - `:param+` → `*` (unnamed, required)
306
+ * - `:param*` → `/`, `/*` (unnamed, optional - two routes)
307
+ */
308
+ export function toHono(path) {
309
+ const segments = path.split("/").filter(Boolean);
310
+ const optionalWildcardIndex = segments.findIndex((s) => s.startsWith(":") && s.endsWith("*"));
311
+ if (optionalWildcardIndex !== -1) {
312
+ const before = segments.slice(0, optionalWildcardIndex);
313
+ const beforeJoined = before
314
+ .map((s) => {
315
+ if (!s.startsWith(":"))
316
+ return s;
317
+ const name = getParamName(s);
318
+ const modifier = getModifier(s);
319
+ return modifier === "?" ? `:${name}?` : `:${name}`;
320
+ })
321
+ .join("/");
322
+ const basePath = beforeJoined ? "/" + beforeJoined : "/";
323
+ const withWildcard = basePath === "/" ? "/*" : basePath + "/*";
324
+ return [basePath, withWildcard];
325
+ }
326
+ const joined = segments
327
+ .map((s) => {
328
+ if (!s.startsWith(":"))
329
+ return s;
330
+ const name = getParamName(s);
331
+ const modifier = getModifier(s);
332
+ switch (modifier) {
333
+ case "":
334
+ return `:${name}`;
335
+ case "?":
336
+ return `:${name}?`;
337
+ case "+":
338
+ case "*":
339
+ return "*";
340
+ }
341
+ })
342
+ .join("/");
343
+ return [joined ? "/" + joined : "/"];
344
+ }
345
+ /**
346
+ * Converts to Effect HttpRouter / find-my-way path pattern.
347
+ *
348
+ * Effect uses colon-style params with unnamed wildcards.
349
+ *
350
+ * @see https://effect.website/docs/platform/http-router
351
+ *
352
+ * - `:param` → `:param`
353
+ * - `:param?` → `:param?`
354
+ * - `:param+` → `*` (unnamed)
355
+ * - `:param*` → `/`, `/*` (two routes)
356
+ */
357
+ export function toEffect(path) {
358
+ return toHono(path);
359
+ }
360
+ /**
361
+ * Converts to Bun.serve path pattern.
362
+ *
363
+ * Since Bun doesn't support optional params (`:param?`), optional segments
364
+ * are expanded into multiple routes.
365
+ *
366
+ * @see https://bun.sh/docs/api/http#routing
367
+ *
368
+ * - `:param` → `:param`
369
+ * - `:param?` → `/`, `/:param` (two routes)
370
+ * - `:param+` → `/*`
371
+ * - `:param*` → `/`, `/*` (two routes)
372
+ */
373
+ export function toBun(path) {
374
+ const segments = path.split("/").filter(Boolean);
375
+ const optionalIndex = segments.findIndex((s) => s.startsWith(":") && (s.endsWith("?") || s.endsWith("*")));
376
+ if (optionalIndex === -1) {
377
+ const joined = segments
378
+ .map((s) => {
379
+ if (!s.startsWith(":"))
380
+ return s;
381
+ const modifier = getModifier(s);
382
+ const name = getParamName(s);
383
+ return modifier === "+" || modifier === "*" ? "*" : `:${name}`;
384
+ })
385
+ .join("/");
386
+ return [joined ? `/${joined}` : "/"];
387
+ }
388
+ const before = segments.slice(0, optionalIndex);
389
+ const optional = segments[optionalIndex];
390
+ const after = segments.slice(optionalIndex + 1);
391
+ const formatSegment = (s) => {
392
+ if (!s.startsWith(":"))
393
+ return s;
394
+ const modifier = getModifier(s);
395
+ const name = getParamName(s);
396
+ switch (modifier) {
397
+ case "":
398
+ case "?":
399
+ return `:${name}`;
400
+ case "+":
401
+ case "*":
402
+ return "*";
403
+ }
404
+ };
405
+ const beforePath = before.map(formatSegment).join("/");
406
+ const basePath = beforePath ? `/${beforePath}` : "/";
407
+ const optionalModifier = getModifier(optional);
408
+ const optionalName = getParamName(optional);
409
+ const requiredOptional = optionalModifier === "*"
410
+ ? `:${optionalName}+`
411
+ : `:${optionalName}`;
412
+ const withOptionalSegments = [...before, requiredOptional, ...after];
413
+ const withOptionalPath = `/${withOptionalSegments.map(formatSegment).join("/")}`;
414
+ return [...toBun(basePath), ...toBun(withOptionalPath)];
415
+ }
@@ -0,0 +1,5 @@
1
+ export declare function uuid(): string;
2
+ export declare function uuidSorted(): string;
3
+ export declare function token(length: number): string;
4
+ export declare function bytes(length: number): Uint8Array;
5
+ export declare function uuid4bytes(): Uint8Array;
package/dist/Random.js ADDED
@@ -0,0 +1,49 @@
1
+ export function uuid() {
2
+ return base36(uuid4bytes());
3
+ }
4
+ export function uuidSorted() {
5
+ return base36(uuid7bytes());
6
+ }
7
+ export function token(length) {
8
+ return base36(bytes(length));
9
+ }
10
+ function base36(bytes) {
11
+ if (bytes.length === 0)
12
+ return "";
13
+ let zeros = 0;
14
+ while (zeros < bytes.length && bytes[zeros] === 0)
15
+ zeros++;
16
+ let n = 0n;
17
+ for (let i = zeros; i < bytes.length; i++)
18
+ n = (n << 8n) + BigInt(bytes[i]);
19
+ return "0".repeat(zeros) + (n === 0n ? "" : n.toString(36));
20
+ }
21
+ export function bytes(length) {
22
+ const buf = new Uint8Array(length);
23
+ crypto.getRandomValues(buf);
24
+ return buf;
25
+ }
26
+ export function uuid4bytes() {
27
+ const buf = bytes(16);
28
+ buf[6] = (buf[6] & 0x0f) | 0x40; // version 4
29
+ buf[8] = (buf[8] & 0x3f) | 0x80; // variant
30
+ return buf;
31
+ }
32
+ function uuid7bytes() {
33
+ const buf = new Uint8Array(16);
34
+ const timestamp = BigInt(Date.now());
35
+ // 48-bit timestamp (6 bytes)
36
+ buf[0] = Number((timestamp >> 40n) & 0xffn);
37
+ buf[1] = Number((timestamp >> 32n) & 0xffn);
38
+ buf[2] = Number((timestamp >> 24n) & 0xffn);
39
+ buf[3] = Number((timestamp >> 16n) & 0xffn);
40
+ buf[4] = Number((timestamp >> 8n) & 0xffn);
41
+ buf[5] = Number(timestamp & 0xffn);
42
+ // 12-bit random A (1.5 bytes)
43
+ crypto.getRandomValues(buf.subarray(6, 8));
44
+ buf[6] = (buf[6] & 0x0f) | 0x70; // version 7
45
+ // 2-bit variant + 62-bit random B (8 bytes)
46
+ crypto.getRandomValues(buf.subarray(8, 16));
47
+ buf[8] = (buf[8] & 0x3f) | 0x80; // variant
48
+ return buf;
49
+ }
@@ -0,0 +1,98 @@
1
+ import * as Context from "effect/Context";
2
+ import type * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Pipeable from "effect/Pipeable";
5
+ import type * as Entity from "./Entity.ts";
6
+ import * as RouteBody from "./RouteBody.ts";
7
+ import * as RouteTree from "./RouteTree.ts";
8
+ import * as Values from "./Values.ts";
9
+ export declare const RouteItems: unique symbol;
10
+ export declare const RouteDescriptor: unique symbol;
11
+ export declare const RouteBindings: unique symbol;
12
+ export declare const TypeId: unique symbol;
13
+ export declare namespace RouteDescriptor {
14
+ type Any = {
15
+ [key: string]: unknown;
16
+ };
17
+ }
18
+ export declare namespace RouteSet {
19
+ type RouteSet<D extends RouteDescriptor.Any = {}, B = {}, M extends Route.Tuple = []> = Data<D, B, M> & {
20
+ [TypeId]: typeof TypeId;
21
+ } & Pipeable.Pipeable & Iterable<M[number]>;
22
+ type Data<D extends RouteDescriptor.Any = {}, B = {}, M extends Route.Tuple = []> = {
23
+ [RouteItems]: M;
24
+ [RouteDescriptor]: D;
25
+ [RouteBindings]: B;
26
+ };
27
+ type Proto = Pipeable.Pipeable & Iterable<Route.Route<any, any, any, any, any>> & {
28
+ [TypeId]: typeof TypeId;
29
+ };
30
+ type Any = RouteSet<{}, {}, Route.Tuple>;
31
+ type Infer<R> = R extends RouteSet<infer D, infer B, infer I> ? RouteSet<D, B, I> : R;
32
+ type Items<T extends Data<any, any, any>> = T extends Data<any, any, infer M> ? M : never;
33
+ type Descriptor<T extends Data<any, any, any>> = T extends Data<infer D, any, any> ? D : never;
34
+ }
35
+ export declare namespace Route {
36
+ export interface Route<D extends RouteDescriptor.Any = {}, B = {}, A = any, E = never, R = never> extends RouteSet.RouteSet<D, {}, [
37
+ Route<D, B, A, E, R>
38
+ ]> {
39
+ readonly handler: Handler<B & D, A, E, R>;
40
+ }
41
+ export type With<D extends RouteDescriptor.Any> = Route<any, any, any, any, any> & {
42
+ [RouteDescriptor]: D;
43
+ };
44
+ export type Tuple<_D extends RouteDescriptor.Any = {}> = [
45
+ ...Route<any, any, any, any, any>[]
46
+ ];
47
+ export type Handler<B, A, E, R> = (context: B, next: (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<A>) => Effect.Effect<Entity.Entity<A>, E, R>;
48
+ /**
49
+ * Extracts only the bindings (B) from routes, excluding descriptors.
50
+ */
51
+ export type Bindings<T extends RouteSet.Any, M extends Tuple = RouteSet.Items<T>> = M extends [
52
+ infer Head,
53
+ ...infer Tail extends Tuple
54
+ ] ? (Head extends Route<any, infer B, any, any, any> ? ShallowMerge<B, Bindings<T, Tail>> : Bindings<T, Tail>) : {};
55
+ /**
56
+ * Extracts the full handler context from a RouteSet.
57
+ * Merges descriptors and bindings from all routes, with later values
58
+ * taking precedence via ShallowMerge to avoid `never` from conflicting
59
+ * literal types (e.g. `{ method: "*" } & { method: "GET" }`).
60
+ */
61
+ export type Context<T extends RouteSet.Any> = Omit<RouteSet.Descriptor<T>, keyof ExtractContext<RouteSet.Items<T>>> & ExtractContext<RouteSet.Items<T>>;
62
+ type ExtractContext<M extends Tuple> = M extends [
63
+ infer Head,
64
+ ...infer Tail extends Tuple
65
+ ] ? (Head extends Route<infer D, infer B, any, any, any> ? ShallowMerge<Omit<D, keyof B> & B, ExtractContext<Tail>> : ExtractContext<Tail>) : {};
66
+ export {};
67
+ }
68
+ export declare function isRouteSet(input: unknown): input is RouteSet.Any;
69
+ export declare function isRoute(input: unknown): input is Route.Route;
70
+ export declare function set<D extends RouteDescriptor.Any = {}, B = {}, I extends Route.Tuple = []>(items?: I, descriptor?: D): RouteSet.RouteSet<D, B, I>;
71
+ export declare function make<D extends RouteDescriptor.Any, B, A, E = never, R = never>(handler: Route.Handler<B & D, A, E, R>, descriptor?: D): Route.Route<D, B, A, E, R>;
72
+ export declare const empty: RouteSet.RouteSet<{}, {}, []>;
73
+ export declare function describe<D extends RouteDescriptor.Any>(descriptor: D): RouteSet.RouteSet<D, {}, never[]>;
74
+ export declare function items<T extends RouteSet.Data<any, any, any>>(self: T): RouteSet.Items<T>;
75
+ export declare function descriptor<T extends RouteSet.Data<any, any, any>>(self: T): T[typeof RouteDescriptor];
76
+ export declare function descriptor<T extends RouteSet.Data<any, any, any>>(self: Iterable<T>): T[typeof RouteDescriptor][];
77
+ export type ExtractBindings<M extends Route.Tuple> = M extends [
78
+ infer Head,
79
+ ...infer Tail extends Route.Tuple
80
+ ] ? (Head extends Route.Route<any, infer B, any, any, any> ? ShallowMerge<B, ExtractBindings<Tail>> : ExtractBindings<Tail>) : {};
81
+ type ShallowMerge<A, B> = Omit<A, keyof B> & {
82
+ [K in keyof B]: K extends keyof A ? A[K] & B[K] : B[K];
83
+ };
84
+ export type ExtractContext<Items extends Route.Tuple, Descriptor extends RouteDescriptor.Any> = ExtractBindings<Items> & Descriptor;
85
+ export * from "./RouteHook.ts";
86
+ export * from "./RouteSchema.ts";
87
+ export { add, del, get, head, options, patch, post, put, use, } from "./RouteMount.ts";
88
+ export declare const text: RouteBody.BuildReturn<string, "text">;
89
+ export declare const html: RouteBody.BuildReturn<string, "html">;
90
+ export declare const json: RouteBody.BuildReturn<Values.Json, "json">;
91
+ export declare const bytes: RouteBody.BuildReturn<Uint8Array<ArrayBufferLike>, "bytes">;
92
+ export { render, } from "./RouteBody.ts";
93
+ export { sse, } from "./RouteSse.ts";
94
+ declare const Routes_base: Context.TagClass<Routes, "effect-start/Routes", RouteTree.RouteTree<RouteTree.RouteMap>>;
95
+ export declare class Routes extends Routes_base {
96
+ }
97
+ export declare function layer(routes: RouteTree.RouteMap | RouteTree.RouteTree): Layer.Layer<Routes, never, never>;
98
+ export { make as tree, } from "./RouteTree.ts";
package/dist/Route.js ADDED
@@ -0,0 +1,81 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Pipeable from "effect/Pipeable";
4
+ import * as Predicate from "effect/Predicate";
5
+ import * as RouteBody from "./RouteBody.js";
6
+ import * as RouteTree from "./RouteTree.js";
7
+ export const RouteItems = Symbol();
8
+ export const RouteDescriptor = Symbol();
9
+ // only for structural type matching
10
+ export const RouteBindings = Symbol();
11
+ export const TypeId = Symbol.for("effect-start/RouteSet");
12
+ const Proto = {
13
+ [TypeId]: TypeId,
14
+ pipe() {
15
+ return Pipeable.pipeArguments(this, arguments);
16
+ },
17
+ *[Symbol.iterator]() {
18
+ yield* items(this);
19
+ },
20
+ };
21
+ export function isRouteSet(input) {
22
+ return Predicate.hasProperty(input, TypeId);
23
+ }
24
+ export function isRoute(input) {
25
+ return isRouteSet(input)
26
+ && Predicate.hasProperty(input, "handler");
27
+ }
28
+ export function set(items = [], descriptor = {}) {
29
+ return Object.assign(Object.create(Proto), {
30
+ [RouteItems]: items,
31
+ [RouteDescriptor]: descriptor,
32
+ });
33
+ }
34
+ export function make(handler, descriptor) {
35
+ const items = [];
36
+ const route = Object.assign(Object.create(Proto), {
37
+ [RouteItems]: items,
38
+ [RouteDescriptor]: descriptor,
39
+ handler,
40
+ });
41
+ items.push(route);
42
+ return route;
43
+ }
44
+ export const empty = set();
45
+ export function describe(descriptor) {
46
+ return set([], descriptor);
47
+ }
48
+ export function items(self) {
49
+ return self[RouteItems];
50
+ }
51
+ export function descriptor(self) {
52
+ if (RouteDescriptor in self) {
53
+ return self[RouteDescriptor];
54
+ }
55
+ return [...self].map((r) => r[RouteDescriptor]);
56
+ }
57
+ export * from "./RouteHook.js";
58
+ export * from "./RouteSchema.js";
59
+ export { add, del, get, head, options, patch, post, put, use, } from "./RouteMount.js";
60
+ export const text = RouteBody.build({
61
+ format: "text",
62
+ });
63
+ export const html = RouteBody.build({
64
+ format: "html",
65
+ });
66
+ export const json = RouteBody.build({
67
+ format: "json",
68
+ });
69
+ export const bytes = RouteBody.build({
70
+ format: "bytes",
71
+ });
72
+ export { render, } from "./RouteBody.js";
73
+ export { sse, } from "./RouteSse.js";
74
+ export class Routes extends Context.Tag("effect-start/Routes")() {
75
+ }
76
+ export function layer(routes) {
77
+ return Layer.sync(Routes, () => RouteTree.isRouteTree(routes)
78
+ ? routes
79
+ : RouteTree.make(routes));
80
+ }
81
+ export { make as tree, } from "./RouteTree.js";