effect-start 0.17.2 → 0.19.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 (80) hide show
  1. package/dist/Development.d.ts +7 -2
  2. package/dist/Development.js +12 -6
  3. package/dist/PlatformRuntime.d.ts +4 -0
  4. package/dist/PlatformRuntime.js +9 -0
  5. package/dist/Route.d.ts +6 -2
  6. package/dist/Route.js +22 -0
  7. package/dist/RouteHttp.d.ts +1 -1
  8. package/dist/RouteHttp.js +12 -19
  9. package/dist/RouteMount.d.ts +2 -1
  10. package/dist/Start.d.ts +1 -5
  11. package/dist/Start.js +1 -8
  12. package/dist/Unique.d.ts +50 -0
  13. package/dist/Unique.js +187 -0
  14. package/dist/bun/BunHttpServer.js +5 -6
  15. package/dist/bun/BunRoute.d.ts +1 -1
  16. package/dist/bun/BunRoute.js +2 -2
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/node/Effectify.d.ts +209 -0
  20. package/dist/node/Effectify.js +19 -0
  21. package/dist/node/FileSystem.d.ts +3 -5
  22. package/dist/node/FileSystem.js +42 -62
  23. package/dist/node/PlatformError.d.ts +46 -0
  24. package/dist/node/PlatformError.js +43 -0
  25. package/dist/testing/TestLogger.js +1 -1
  26. package/package.json +10 -5
  27. package/src/Development.ts +13 -18
  28. package/src/PlatformRuntime.ts +11 -0
  29. package/src/Route.ts +31 -2
  30. package/src/RouteHttp.ts +15 -31
  31. package/src/RouteMount.ts +1 -1
  32. package/src/Start.ts +1 -15
  33. package/src/Unique.ts +232 -0
  34. package/src/bun/BunHttpServer.ts +6 -9
  35. package/src/bun/BunRoute.ts +3 -3
  36. package/src/index.ts +1 -0
  37. package/src/node/Effectify.ts +262 -0
  38. package/src/node/FileSystem.ts +59 -97
  39. package/src/node/PlatformError.ts +102 -0
  40. package/src/testing/TestLogger.ts +1 -1
  41. package/dist/Random.d.ts +0 -5
  42. package/dist/Random.js +0 -49
  43. package/src/Commander.test.ts +0 -1639
  44. package/src/ContentNegotiation.test.ts +0 -603
  45. package/src/Development.test.ts +0 -119
  46. package/src/Entity.test.ts +0 -592
  47. package/src/FileRouterPattern.test.ts +0 -147
  48. package/src/FileRouter_files.test.ts +0 -64
  49. package/src/FileRouter_path.test.ts +0 -145
  50. package/src/FileRouter_tree.test.ts +0 -132
  51. package/src/Http.test.ts +0 -319
  52. package/src/HttpAppExtra.test.ts +0 -103
  53. package/src/HttpUtils.test.ts +0 -85
  54. package/src/PathPattern.test.ts +0 -648
  55. package/src/Random.ts +0 -59
  56. package/src/RouteBody.test.ts +0 -232
  57. package/src/RouteHook.test.ts +0 -40
  58. package/src/RouteHttp.test.ts +0 -2909
  59. package/src/RouteMount.test.ts +0 -481
  60. package/src/RouteSchema.test.ts +0 -427
  61. package/src/RouteSse.test.ts +0 -249
  62. package/src/RouteTree.test.ts +0 -494
  63. package/src/RouteTrie.test.ts +0 -322
  64. package/src/RouterPattern.test.ts +0 -676
  65. package/src/Values.test.ts +0 -263
  66. package/src/bun/BunBundle.test.ts +0 -268
  67. package/src/bun/BunBundle_imports.test.ts +0 -48
  68. package/src/bun/BunHttpServer.test.ts +0 -251
  69. package/src/bun/BunImportTrackerPlugin.test.ts +0 -77
  70. package/src/bun/BunRoute.test.ts +0 -162
  71. package/src/bundler/BundleHttp.test.ts +0 -132
  72. package/src/effect/HttpRouter.test.ts +0 -548
  73. package/src/experimental/EncryptedCookies.test.ts +0 -488
  74. package/src/hyper/HyperHtml.test.ts +0 -209
  75. package/src/hyper/HyperRoute.test.tsx +0 -197
  76. package/src/middlewares/BasicAuthMiddleware.test.ts +0 -84
  77. package/src/testing/TestHttpClient.test.ts +0 -83
  78. package/src/testing/TestLogger.test.ts +0 -51
  79. package/src/x/datastar/Datastar.test.ts +0 -266
  80. package/src/x/tailwind/TailwindPlugin.test.ts +0 -333
@@ -1,603 +0,0 @@
1
- import * as Headers from "@effect/platform/Headers"
2
- import * as test from "bun:test"
3
- import * as ContentNegotiation from "./ContentNegotiation.ts"
4
-
5
- test.describe("ContentNegotiation.media", () => {
6
- test.it("returns empty array when no types provided", () => {
7
- const result = ContentNegotiation.media("text/html", [])
8
- test
9
- .expect(result)
10
- .toEqual([])
11
- })
12
-
13
- test.it("returns matching type", () => {
14
- const result = ContentNegotiation.media(
15
- "application/json",
16
- ["text/html", "application/json"],
17
- )
18
- test
19
- .expect(result)
20
- .toEqual(["application/json"])
21
- })
22
-
23
- test.it("returns types sorted by quality", () => {
24
- const result = ContentNegotiation.media(
25
- "text/html;q=0.5, application/json;q=0.9",
26
- ["text/html", "application/json"],
27
- )
28
- test
29
- .expect(result)
30
- .toEqual(["application/json", "text/html"])
31
- })
32
-
33
- test.it("returns empty array when no matching type", () => {
34
- const result = ContentNegotiation.media(
35
- "text/plain",
36
- ["text/html", "application/json"],
37
- )
38
- test
39
- .expect(result)
40
- .toEqual([])
41
- })
42
-
43
- test.it("handles wildcard subtype", () => {
44
- const result = ContentNegotiation.media(
45
- "text/*",
46
- ["application/json", "text/html", "text/plain"],
47
- )
48
- test
49
- .expect(result)
50
- .toEqual(["text/html", "text/plain"])
51
- })
52
-
53
- test.it("prefers exact match over wildcard", () => {
54
- const result = ContentNegotiation.media(
55
- "text/*, text/html",
56
- ["text/plain", "text/html"],
57
- )
58
- test
59
- .expect(result)
60
- .toEqual(["text/html", "text/plain"])
61
- })
62
-
63
- test.it("handles complex accept header", () => {
64
- const result = ContentNegotiation.media(
65
- "text/html, application/*;q=0.2, image/jpeg;q=0.8",
66
- ["image/jpeg", "application/json", "text/html"],
67
- )
68
- test
69
- .expect(result)
70
- .toEqual(["text/html", "image/jpeg", "application/json"])
71
- })
72
-
73
- test.it("returns type as provided (preserves original string)", () => {
74
- const result = ContentNegotiation.media(
75
- "application/json",
76
- ["text/HTML", "Application/JSON"],
77
- )
78
- test
79
- .expect(result)
80
- .toEqual(["Application/JSON"])
81
- })
82
-
83
- test.it("handles */* wildcard", () => {
84
- const result = ContentNegotiation.media(
85
- "*/*",
86
- ["text/html", "application/json"],
87
- )
88
- test
89
- .expect(result)
90
- .toEqual(["text/html", "application/json"])
91
- })
92
-
93
- test.it("returns empty array for invalid accept header", () => {
94
- const result = ContentNegotiation.media(
95
- "invalid",
96
- ["text/html", "application/json"],
97
- )
98
- test
99
- .expect(result)
100
- .toEqual([])
101
- })
102
-
103
- test.it("returns all accepted types when available not provided", () => {
104
- const result = ContentNegotiation.media(
105
- "text/html, application/json;q=0.9, text/plain;q=0.5",
106
- )
107
- test
108
- .expect(result)
109
- .toEqual(["text/html", "application/json", "text/plain"])
110
- })
111
-
112
- test.it("returns empty array for empty accept header", () => {
113
- const result = ContentNegotiation.media("", [
114
- "text/html",
115
- "application/json",
116
- ])
117
- test
118
- .expect(result)
119
- .toEqual([])
120
- })
121
-
122
- test.it("excludes types with q=0", () => {
123
- const result = ContentNegotiation.media(
124
- "text/html, application/json;q=0",
125
- ["text/html", "application/json"],
126
- )
127
- test
128
- .expect(result)
129
- .toEqual(["text/html"])
130
- })
131
-
132
- test.it("matches media type with parameters", () => {
133
- const result = ContentNegotiation.media(
134
- "text/html;level=1",
135
- ["text/html;level=1", "text/html;level=2", "text/html"],
136
- )
137
- test
138
- .expect(result)
139
- .toEqual(["text/html;level=1"])
140
- })
141
-
142
- test.it("prefers more specific wildcard match", () => {
143
- const result = ContentNegotiation.media(
144
- "text/*;q=0.5, */*;q=0.1",
145
- ["text/html", "application/json"],
146
- )
147
- test
148
- .expect(result)
149
- .toEqual(["text/html", "application/json"])
150
- })
151
-
152
- test.describe("wildcard in available types", () => {
153
- test.it("text/* matches text/event-stream", () => {
154
- const result = ContentNegotiation.media(
155
- "text/event-stream",
156
- ["text/*", "application/json"],
157
- )
158
- test
159
- .expect(result)
160
- .toEqual(["text/*"])
161
- })
162
-
163
- test.it("text/* matches text/markdown", () => {
164
- const result = ContentNegotiation.media(
165
- "text/markdown",
166
- ["text/*"],
167
- )
168
- test
169
- .expect(result)
170
- .toEqual(["text/*"])
171
- })
172
-
173
- test.it("text/* matches text/plain", () => {
174
- const result = ContentNegotiation.media(
175
- "text/plain",
176
- ["text/*"],
177
- )
178
- test
179
- .expect(result)
180
- .toEqual(["text/*"])
181
- })
182
-
183
- test.it("text/* does not match application/json", () => {
184
- const result = ContentNegotiation.media(
185
- "application/json",
186
- ["text/*"],
187
- )
188
- test
189
- .expect(result)
190
- .toEqual([])
191
- })
192
-
193
- test.it("prefers exact match over wildcard available type", () => {
194
- const result = ContentNegotiation.media(
195
- "text/html",
196
- ["text/*", "text/html"],
197
- )
198
- test
199
- .expect(result)
200
- .toEqual(["text/html", "text/*"])
201
- })
202
-
203
- test.it("text/* matches multiple text types in Accept", () => {
204
- const result = ContentNegotiation.media(
205
- "text/html, text/plain",
206
- ["text/*"],
207
- )
208
- test
209
- .expect(result)
210
- .toEqual(["text/*"])
211
- })
212
-
213
- test.it("application/* matches application/xml", () => {
214
- const result = ContentNegotiation.media(
215
- "application/xml",
216
- ["application/*", "text/html"],
217
- )
218
- test
219
- .expect(result)
220
- .toEqual(["application/*"])
221
- })
222
-
223
- test.it("*/* in available matches any type", () => {
224
- const result = ContentNegotiation.media(
225
- "image/png",
226
- ["*/*"],
227
- )
228
- test
229
- .expect(result)
230
- .toEqual(["*/*"])
231
- })
232
-
233
- test.it("combines client and server wildcards", () => {
234
- // Client wants text/*, server offers text/*
235
- const result = ContentNegotiation.media(
236
- "text/*",
237
- ["text/*"],
238
- )
239
- test
240
- .expect(result)
241
- .toEqual(["text/*"])
242
- })
243
-
244
- test.it("quality values still apply with wildcard available", () => {
245
- const result = ContentNegotiation.media(
246
- "text/html;q=0.5, text/event-stream;q=0.9",
247
- ["text/*"],
248
- )
249
- test
250
- .expect(result)
251
- .toEqual(["text/*"])
252
- })
253
- })
254
- })
255
-
256
- test.describe("ContentNegotiation.language", () => {
257
- test.it("returns empty array when no languages provided", () => {
258
- const result = ContentNegotiation.language("en", [])
259
- test
260
- .expect(result)
261
- .toEqual([])
262
- })
263
-
264
- test.it("returns matching language", () => {
265
- const result = ContentNegotiation.language("fr", ["en", "fr"])
266
- test
267
- .expect(result)
268
- .toEqual(["fr"])
269
- })
270
-
271
- test.it("returns languages sorted by quality", () => {
272
- const result = ContentNegotiation.language(
273
- "en;q=0.5, fr;q=0.9",
274
- ["en", "fr"],
275
- )
276
- test
277
- .expect(result)
278
- .toEqual(["fr", "en"])
279
- })
280
-
281
- test.it("returns empty array when no matching language", () => {
282
- const result = ContentNegotiation.language("de", ["en", "fr"])
283
- test
284
- .expect(result)
285
- .toEqual([])
286
- })
287
-
288
- test.it("handles language prefix match", () => {
289
- const result = ContentNegotiation.language("en", ["en-US", "en-GB", "fr"])
290
- test
291
- .expect(result)
292
- .toEqual(["en-US", "en-GB"])
293
- })
294
-
295
- test.it("handles language with region", () => {
296
- const result = ContentNegotiation.language("en-US", ["en", "en-US", "fr"])
297
- test
298
- .expect(result)
299
- .toEqual(["en-US"])
300
- })
301
-
302
- test.it("prefers exact match over prefix match", () => {
303
- const result = ContentNegotiation.language(
304
- "en-US, en;q=0.9",
305
- ["en", "en-US"],
306
- )
307
- test
308
- .expect(result)
309
- .toEqual(["en-US", "en"])
310
- })
311
-
312
- test.it("handles complex accept-language header", () => {
313
- const result = ContentNegotiation.language(
314
- "en;q=0.8, es, pt",
315
- ["en", "es", "pt"],
316
- )
317
- test
318
- .expect(result)
319
- .toEqual(["es", "pt", "en"])
320
- })
321
-
322
- test.it("handles * wildcard", () => {
323
- const result = ContentNegotiation.language("*", ["en", "fr"])
324
- test
325
- .expect(result)
326
- .toEqual(["en", "fr"])
327
- })
328
-
329
- test.it("returns all accepted languages when available not provided", () => {
330
- const result = ContentNegotiation.language("en-US, fr;q=0.8, de;q=0.5")
331
- test
332
- .expect(result)
333
- .toEqual(["en-us", "fr", "de"])
334
- })
335
-
336
- test.it("returns empty array for empty accept-language header", () => {
337
- const result = ContentNegotiation.language("", ["en", "fr"])
338
- test
339
- .expect(result)
340
- .toEqual([])
341
- })
342
-
343
- test.it("matches case-insensitively", () => {
344
- const result = ContentNegotiation.language("EN-US", ["en-us", "fr"])
345
- test
346
- .expect(result)
347
- .toEqual(["en-us"])
348
- })
349
- })
350
-
351
- test.describe("ContentNegotiation.encoding", () => {
352
- test.it("returns empty array when no encodings provided", () => {
353
- const result = ContentNegotiation.encoding("gzip", [])
354
- test
355
- .expect(result)
356
- .toEqual([])
357
- })
358
-
359
- test.it("returns matching encoding", () => {
360
- const result = ContentNegotiation.encoding("deflate", ["gzip", "deflate"])
361
- test
362
- .expect(result)
363
- .toEqual(["deflate"])
364
- })
365
-
366
- test.it("returns encodings sorted by quality", () => {
367
- const result = ContentNegotiation.encoding(
368
- "gzip;q=0.5, deflate;q=0.9",
369
- ["gzip", "deflate"],
370
- )
371
- test
372
- .expect(result)
373
- .toEqual(["deflate", "gzip"])
374
- })
375
-
376
- test.it(
377
- "returns empty array when no matching encoding (except identity)",
378
- () => {
379
- const result = ContentNegotiation.encoding("br", ["gzip", "deflate"])
380
- test
381
- .expect(result)
382
- .toEqual([])
383
- },
384
- )
385
-
386
- test.it("handles wildcard", () => {
387
- const result = ContentNegotiation.encoding("*", ["gzip", "deflate"])
388
- test
389
- .expect(result)
390
- .toEqual(["gzip", "deflate"])
391
- })
392
-
393
- test.it("handles identity encoding as implicit fallback", () => {
394
- const result = ContentNegotiation.encoding("br", ["identity", "gzip"])
395
- test
396
- .expect(result)
397
- .toEqual(["identity"])
398
- })
399
-
400
- test.it("handles complex accept-encoding header", () => {
401
- const result = ContentNegotiation.encoding(
402
- "gzip;q=1.0, identity;q=0.5, *;q=0",
403
- ["deflate", "gzip", "identity"],
404
- )
405
- test
406
- .expect(result)
407
- .toEqual(["gzip", "identity"])
408
- })
409
-
410
- test.it("returns all accepted encodings when available not provided", () => {
411
- const result = ContentNegotiation.encoding("gzip, deflate;q=0.8, br;q=0.5")
412
- test
413
- .expect(result)
414
- .toEqual(["gzip", "deflate", "br", "identity"])
415
- })
416
-
417
- test.it("returns empty array for empty accept-encoding header", () => {
418
- const result = ContentNegotiation.encoding("", ["gzip", "deflate"])
419
- test
420
- .expect(result)
421
- .toEqual([])
422
- })
423
-
424
- test.it("excludes identity when identity;q=0", () => {
425
- const result = ContentNegotiation.encoding(
426
- "gzip, identity;q=0",
427
- ["gzip", "identity"],
428
- )
429
- test
430
- .expect(result)
431
- .toEqual(["gzip"])
432
- })
433
-
434
- test.it("excludes unspecified encodings when *;q=0", () => {
435
- const result = ContentNegotiation.encoding(
436
- "gzip, *;q=0",
437
- ["gzip", "deflate", "br"],
438
- )
439
- test
440
- .expect(result)
441
- .toEqual(["gzip"])
442
- })
443
- })
444
-
445
- test.describe("ContentNegotiation.charset", () => {
446
- test.it("returns empty array when no charsets provided", () => {
447
- const result = ContentNegotiation.charset("utf-8", [])
448
- test
449
- .expect(result)
450
- .toEqual([])
451
- })
452
-
453
- test.it("returns matching charset", () => {
454
- const result = ContentNegotiation.charset(
455
- "iso-8859-1",
456
- ["utf-8", "iso-8859-1"],
457
- )
458
- test
459
- .expect(result)
460
- .toEqual(["iso-8859-1"])
461
- })
462
-
463
- test.it("returns charsets sorted by quality", () => {
464
- const result = ContentNegotiation.charset(
465
- "utf-8;q=0.5, iso-8859-1;q=0.9",
466
- ["utf-8", "iso-8859-1"],
467
- )
468
- test
469
- .expect(result)
470
- .toEqual(["iso-8859-1", "utf-8"])
471
- })
472
-
473
- test.it("returns empty array when no matching charset", () => {
474
- const result = ContentNegotiation.charset(
475
- "utf-16",
476
- ["utf-8", "iso-8859-1"],
477
- )
478
- test
479
- .expect(result)
480
- .toEqual([])
481
- })
482
-
483
- test.it("handles wildcard", () => {
484
- const result = ContentNegotiation.charset("*", ["utf-8", "iso-8859-1"])
485
- test
486
- .expect(result)
487
- .toEqual(["utf-8", "iso-8859-1"])
488
- })
489
-
490
- test.it("handles complex accept-charset header", () => {
491
- const result = ContentNegotiation.charset(
492
- "utf-8, iso-8859-1;q=0.8, utf-7;q=0.2",
493
- ["utf-7", "iso-8859-1", "utf-8"],
494
- )
495
- test
496
- .expect(result)
497
- .toEqual(["utf-8", "iso-8859-1", "utf-7"])
498
- })
499
-
500
- test.it("returns all accepted charsets when available not provided", () => {
501
- const result = ContentNegotiation.charset(
502
- "utf-8, iso-8859-1;q=0.8, utf-7;q=0.2",
503
- )
504
- test
505
- .expect(result)
506
- .toEqual(["utf-8", "iso-8859-1", "utf-7"])
507
- })
508
-
509
- test.it("returns empty array for empty accept-charset header", () => {
510
- const result = ContentNegotiation.charset("", ["utf-8", "iso-8859-1"])
511
- test
512
- .expect(result)
513
- .toEqual([])
514
- })
515
-
516
- test.it("matches case-insensitively", () => {
517
- const result = ContentNegotiation.charset("UTF-8", ["utf-8", "iso-8859-1"])
518
- test
519
- .expect(result)
520
- .toEqual(["utf-8"])
521
- })
522
- })
523
-
524
- test.describe("ContentNegotiation.headerMedia", () => {
525
- test.it("parses Accept header from Headers object", () => {
526
- const headers = Headers.fromInput({ accept: "text/html, application/json" })
527
- const result = ContentNegotiation.headerMedia(headers, [
528
- "text/html",
529
- "application/json",
530
- ])
531
- test
532
- .expect(result)
533
- .toEqual(["text/html", "application/json"])
534
- })
535
-
536
- test.it("returns empty array when Accept header is missing", () => {
537
- const headers = Headers.fromInput({})
538
- const result = ContentNegotiation.headerMedia(headers, ["text/html"])
539
- test
540
- .expect(result)
541
- .toEqual([])
542
- })
543
- })
544
-
545
- test.describe("ContentNegotiation.headerLanguage", () => {
546
- test.it("parses Accept-Language header from Headers object", () => {
547
- const headers = Headers.fromInput({ "accept-language": "en, fr;q=0.8" })
548
- const result = ContentNegotiation.headerLanguage(headers, ["en", "fr"])
549
- test
550
- .expect(result)
551
- .toEqual(["en", "fr"])
552
- })
553
-
554
- test.it("returns empty array when Accept-Language header is missing", () => {
555
- const headers = Headers.fromInput({})
556
- const result = ContentNegotiation.headerLanguage(headers, ["en"])
557
- test
558
- .expect(result)
559
- .toEqual([])
560
- })
561
- })
562
-
563
- test.describe("ContentNegotiation.headerEncoding", () => {
564
- test.it("parses Accept-Encoding header from Headers object", () => {
565
- const headers = Headers.fromInput({ "accept-encoding": "gzip, deflate" })
566
- const result = ContentNegotiation.headerEncoding(headers, [
567
- "gzip",
568
- "deflate",
569
- ])
570
- test
571
- .expect(result)
572
- .toEqual(["gzip", "deflate"])
573
- })
574
-
575
- test.it("returns empty array when Accept-Encoding header is missing", () => {
576
- const headers = Headers.fromInput({})
577
- const result = ContentNegotiation.headerEncoding(headers, ["gzip"])
578
- test
579
- .expect(result)
580
- .toEqual([])
581
- })
582
- })
583
-
584
- test.describe("ContentNegotiation.headerCharset", () => {
585
- test.it("parses Accept-Charset header from Headers object", () => {
586
- const headers = Headers.fromInput({ "accept-charset": "utf-8, iso-8859-1" })
587
- const result = ContentNegotiation.headerCharset(headers, [
588
- "utf-8",
589
- "iso-8859-1",
590
- ])
591
- test
592
- .expect(result)
593
- .toEqual(["utf-8", "iso-8859-1"])
594
- })
595
-
596
- test.it("returns empty array when Accept-Charset header is missing", () => {
597
- const headers = Headers.fromInput({})
598
- const result = ContentNegotiation.headerCharset(headers, ["utf-8"])
599
- test
600
- .expect(result)
601
- .toEqual([])
602
- })
603
- })