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,440 @@
1
+ /*
2
+ * Adapted from @effect/platform
3
+ */
4
+ import { effectify } from "@effect/platform/Effectify";
5
+ import * as Error from "@effect/platform/Error";
6
+ import { SystemError } from "@effect/platform/Error";
7
+ import * as FileSystem from "@effect/platform/FileSystem";
8
+ import * as Effect from "effect/Effect";
9
+ import { pipe } from "effect/Function";
10
+ import * as Layer from "effect/Layer";
11
+ import * as Option from "effect/Option";
12
+ import * as Stream from "effect/Stream";
13
+ import * as Crypto from "node:crypto";
14
+ import * as NFS from "node:fs";
15
+ import * as NOS from "node:os";
16
+ import * as NPath from "node:path";
17
+ const handleBadArgument = (method) => (cause) => new Error.BadArgument({
18
+ module: "FileSystem",
19
+ method,
20
+ cause,
21
+ });
22
+ // == access
23
+ const access = (() => {
24
+ const nodeAccess = effectify(NFS.access, handleErrnoException("FileSystem", "access"), handleBadArgument("access"));
25
+ return (path, options) => {
26
+ let mode = NFS.constants.F_OK;
27
+ if (options?.readable) {
28
+ mode |= NFS.constants.R_OK;
29
+ }
30
+ if (options?.writable) {
31
+ mode |= NFS.constants.W_OK;
32
+ }
33
+ return nodeAccess(path, mode);
34
+ };
35
+ })();
36
+ // == copy
37
+ const copy = (() => {
38
+ const nodeCp = effectify(NFS.cp, handleErrnoException("FileSystem", "copy"), handleBadArgument("copy"));
39
+ return (fromPath, toPath, options) => nodeCp(fromPath, toPath, {
40
+ force: options?.overwrite ?? false,
41
+ preserveTimestamps: options?.preserveTimestamps ?? false,
42
+ recursive: true,
43
+ });
44
+ })();
45
+ // == copyFile
46
+ const copyFile = (() => {
47
+ const nodeCopyFile = effectify(NFS.copyFile, handleErrnoException("FileSystem", "copyFile"), handleBadArgument("copyFile"));
48
+ return (fromPath, toPath) => nodeCopyFile(fromPath, toPath);
49
+ })();
50
+ // == chmod
51
+ const chmod = (() => {
52
+ const nodeChmod = effectify(NFS.chmod, handleErrnoException("FileSystem", "chmod"), handleBadArgument("chmod"));
53
+ return (path, mode) => nodeChmod(path, mode);
54
+ })();
55
+ // == chown
56
+ const chown = (() => {
57
+ const nodeChown = effectify(NFS.chown, handleErrnoException("FileSystem", "chown"), handleBadArgument("chown"));
58
+ return (path, uid, gid) => nodeChown(path, uid, gid);
59
+ })();
60
+ // == link
61
+ const link = (() => {
62
+ const nodeLink = effectify(NFS.link, handleErrnoException("FileSystem", "link"), handleBadArgument("link"));
63
+ return (existingPath, newPath) => nodeLink(existingPath, newPath);
64
+ })();
65
+ // == makeDirectory
66
+ const makeDirectory = (() => {
67
+ const nodeMkdir = effectify(NFS.mkdir, handleErrnoException("FileSystem", "makeDirectory"), handleBadArgument("makeDirectory"));
68
+ return (path, options) => nodeMkdir(path, {
69
+ recursive: options?.recursive ?? false,
70
+ mode: options?.mode,
71
+ });
72
+ })();
73
+ // == makeTempDirectory
74
+ const makeTempDirectoryFactory = (method) => {
75
+ const nodeMkdtemp = effectify(NFS.mkdtemp, handleErrnoException("FileSystem", method), handleBadArgument(method));
76
+ return (options) => Effect.suspend(() => {
77
+ const prefix = options?.prefix ?? "";
78
+ const directory = typeof options?.directory === "string"
79
+ ? NPath.join(options.directory, ".")
80
+ : NOS.tmpdir();
81
+ return nodeMkdtemp(prefix ? NPath.join(directory, prefix) : directory + "/");
82
+ });
83
+ };
84
+ const makeTempDirectory = makeTempDirectoryFactory("makeTempDirectory");
85
+ // == remove
86
+ const removeFactory = (method) => {
87
+ const nodeRm = effectify(NFS.rm, handleErrnoException("FileSystem", method), handleBadArgument(method));
88
+ return (path, options) => nodeRm(path, {
89
+ recursive: options?.recursive ?? false,
90
+ force: options?.force ?? false,
91
+ });
92
+ };
93
+ const remove = removeFactory("remove");
94
+ // == makeTempDirectoryScoped
95
+ const makeTempDirectoryScoped = (() => {
96
+ const makeDirectory = makeTempDirectoryFactory("makeTempDirectoryScoped");
97
+ const removeDirectory = removeFactory("makeTempDirectoryScoped");
98
+ return (options) => Effect.acquireRelease(makeDirectory(options), (directory) => Effect.orDie(removeDirectory(directory, { recursive: true })));
99
+ })();
100
+ // == open
101
+ const openFactory = (method) => {
102
+ const nodeOpen = effectify(NFS.open, handleErrnoException("FileSystem", method), handleBadArgument(method));
103
+ const nodeClose = effectify(NFS.close, handleErrnoException("FileSystem", method), handleBadArgument(method));
104
+ return (path, options) => pipe(Effect.acquireRelease(nodeOpen(path, options?.flag ?? "r", options?.mode), (fd) => Effect.orDie(nodeClose(fd))), Effect.map((fd) => makeFile(FileSystem.FileDescriptor(fd), options?.flag?.startsWith("a") ?? false)));
105
+ };
106
+ const open = openFactory("open");
107
+ const makeFile = (() => {
108
+ const nodeReadFactory = (method) => effectify(NFS.read, handleErrnoException("FileSystem", method), handleBadArgument(method));
109
+ const nodeRead = nodeReadFactory("read");
110
+ const nodeReadAlloc = nodeReadFactory("readAlloc");
111
+ const nodeStat = effectify(NFS.fstat, handleErrnoException("FileSystem", "stat"), handleBadArgument("stat"));
112
+ const nodeTruncate = effectify(NFS.ftruncate, handleErrnoException("FileSystem", "truncate"), handleBadArgument("truncate"));
113
+ const nodeSync = effectify(NFS.fsync, handleErrnoException("FileSystem", "sync"), handleBadArgument("sync"));
114
+ const nodeWriteFactory = (method) => effectify(NFS.write, handleErrnoException("FileSystem", method), handleBadArgument(method));
115
+ const nodeWrite = nodeWriteFactory("write");
116
+ const nodeWriteAll = nodeWriteFactory("writeAll");
117
+ class FileImpl {
118
+ [FileSystem.FileTypeId];
119
+ fd;
120
+ append;
121
+ semaphore = Effect.unsafeMakeSemaphore(1);
122
+ position = 0n;
123
+ constructor(fd, append) {
124
+ this[FileSystem.FileTypeId] = FileSystem.FileTypeId;
125
+ this.fd = fd;
126
+ this.append = append;
127
+ }
128
+ get stat() {
129
+ return Effect.map(nodeStat(this.fd), makeFileInfo);
130
+ }
131
+ get sync() {
132
+ return nodeSync(this.fd);
133
+ }
134
+ seek(offset, from) {
135
+ const offsetSize = FileSystem.Size(offset);
136
+ return this.semaphore.withPermits(1)(Effect.sync(() => {
137
+ if (from === "start") {
138
+ this.position = offsetSize;
139
+ }
140
+ else if (from === "current") {
141
+ this.position = this.position + offsetSize;
142
+ }
143
+ return this.position;
144
+ }));
145
+ }
146
+ read(buffer) {
147
+ return this.semaphore.withPermits(1)(Effect.map(Effect.suspend(() => nodeRead(this.fd, {
148
+ buffer,
149
+ position: this.position,
150
+ })), (bytesRead) => {
151
+ const sizeRead = FileSystem.Size(bytesRead);
152
+ this.position = this.position + sizeRead;
153
+ return sizeRead;
154
+ }));
155
+ }
156
+ readAlloc(size) {
157
+ const sizeNumber = Number(size);
158
+ return this.semaphore.withPermits(1)(Effect.flatMap(Effect.sync(() => Buffer.allocUnsafeSlow(sizeNumber)), (buffer) => Effect.map(nodeReadAlloc(this.fd, {
159
+ buffer,
160
+ position: this.position,
161
+ }), (bytesRead) => {
162
+ if (bytesRead === 0) {
163
+ return Option.none();
164
+ }
165
+ this.position = this.position + BigInt(bytesRead);
166
+ if (bytesRead === sizeNumber) {
167
+ return Option.some(buffer);
168
+ }
169
+ const dst = Buffer.allocUnsafeSlow(bytesRead);
170
+ buffer.copy(dst, 0, 0, bytesRead);
171
+ return Option.some(dst);
172
+ })));
173
+ }
174
+ truncate(length) {
175
+ return this.semaphore.withPermits(1)(Effect.map(nodeTruncate(this.fd, length ? Number(length) : undefined), () => {
176
+ if (!this.append) {
177
+ const len = BigInt(length ?? 0);
178
+ if (this.position > len) {
179
+ this.position = len;
180
+ }
181
+ }
182
+ }));
183
+ }
184
+ write(buffer) {
185
+ return this.semaphore.withPermits(1)(Effect.map(Effect.suspend(() => nodeWrite(this.fd, buffer, undefined, undefined, this.append ? undefined : Number(this.position))), (bytesWritten) => {
186
+ const sizeWritten = FileSystem.Size(bytesWritten);
187
+ if (!this.append) {
188
+ this.position = this.position + sizeWritten;
189
+ }
190
+ return sizeWritten;
191
+ }));
192
+ }
193
+ writeAllChunk(buffer) {
194
+ return Effect.flatMap(Effect.suspend(() => nodeWriteAll(this.fd, buffer, undefined, undefined, this.append ? undefined : Number(this.position))), (bytesWritten) => {
195
+ if (bytesWritten === 0) {
196
+ return Effect.fail(new Error.SystemError({
197
+ module: "FileSystem",
198
+ method: "writeAll",
199
+ reason: "WriteZero",
200
+ pathOrDescriptor: this.fd,
201
+ description: "write returned 0 bytes written",
202
+ }));
203
+ }
204
+ if (!this.append) {
205
+ this.position = this.position + BigInt(bytesWritten);
206
+ }
207
+ return bytesWritten < buffer.length
208
+ ? this.writeAllChunk(buffer.subarray(bytesWritten))
209
+ : Effect.void;
210
+ });
211
+ }
212
+ writeAll(buffer) {
213
+ return this.semaphore.withPermits(1)(this.writeAllChunk(buffer));
214
+ }
215
+ }
216
+ return (fd, append) => new FileImpl(fd, append);
217
+ })();
218
+ // == makeTempFile
219
+ const makeTempFileFactory = (method) => {
220
+ const makeDirectory = makeTempDirectoryFactory(method);
221
+ const open = openFactory(method);
222
+ const randomHexString = (bytes) => Effect.sync(() => Crypto.randomBytes(bytes).toString("hex"));
223
+ return (options) => pipe(Effect.zip(makeDirectory(options), randomHexString(6)), Effect.map(([directory, random]) => NPath.join(directory, random + (options?.suffix ?? ""))), Effect.tap((path) => Effect.scoped(open(path, { flag: "w+" }))));
224
+ };
225
+ const makeTempFile = makeTempFileFactory("makeTempFile");
226
+ // == makeTempFileScoped
227
+ const makeTempFileScoped = (() => {
228
+ const makeFile = makeTempFileFactory("makeTempFileScoped");
229
+ const removeDirectory = removeFactory("makeTempFileScoped");
230
+ return (options) => Effect.acquireRelease(makeFile(options), (file) => Effect.orDie(removeDirectory(NPath.dirname(file), { recursive: true })));
231
+ })();
232
+ // == readDirectory
233
+ const readDirectory = (path, options) => Effect.tryPromise({
234
+ try: () => NFS.promises.readdir(path, options),
235
+ catch: (err) => handleErrnoException("FileSystem", "readDirectory")(err, [path]),
236
+ });
237
+ // == readFile
238
+ const readFile = (path) => Effect.async((resume, signal) => {
239
+ try {
240
+ NFS.readFile(path, { signal }, (err, data) => {
241
+ if (err) {
242
+ resume(Effect.fail(handleErrnoException("FileSystem", "readFile")(err, [path])));
243
+ }
244
+ else {
245
+ resume(Effect.succeed(data));
246
+ }
247
+ });
248
+ }
249
+ catch (err) {
250
+ resume(Effect.fail(handleBadArgument("readFile")(err)));
251
+ }
252
+ });
253
+ // == readLink
254
+ const readLink = (() => {
255
+ const nodeReadLink = effectify(NFS.readlink, handleErrnoException("FileSystem", "readLink"), handleBadArgument("readLink"));
256
+ return (path) => nodeReadLink(path);
257
+ })();
258
+ // == realPath
259
+ const realPath = (() => {
260
+ const nodeRealPath = effectify(NFS.realpath, handleErrnoException("FileSystem", "realPath"), handleBadArgument("realPath"));
261
+ return (path) => nodeRealPath(path);
262
+ })();
263
+ // == rename
264
+ const rename = (() => {
265
+ const nodeRename = effectify(NFS.rename, handleErrnoException("FileSystem", "rename"), handleBadArgument("rename"));
266
+ return (oldPath, newPath) => nodeRename(oldPath, newPath);
267
+ })();
268
+ // == stat
269
+ const makeFileInfo = (stat) => ({
270
+ type: stat.isFile()
271
+ ? "File"
272
+ : stat.isDirectory()
273
+ ? "Directory"
274
+ : stat.isSymbolicLink()
275
+ ? "SymbolicLink"
276
+ : stat.isBlockDevice()
277
+ ? "BlockDevice"
278
+ : stat.isCharacterDevice()
279
+ ? "CharacterDevice"
280
+ : stat.isFIFO()
281
+ ? "FIFO"
282
+ : stat.isSocket()
283
+ ? "Socket"
284
+ : "Unknown",
285
+ mtime: Option.fromNullable(stat.mtime),
286
+ atime: Option.fromNullable(stat.atime),
287
+ birthtime: Option.fromNullable(stat.birthtime),
288
+ dev: stat.dev,
289
+ rdev: Option.fromNullable(stat.rdev),
290
+ ino: Option.fromNullable(stat.ino),
291
+ mode: stat.mode,
292
+ nlink: Option.fromNullable(stat.nlink),
293
+ uid: Option.fromNullable(stat.uid),
294
+ gid: Option.fromNullable(stat.gid),
295
+ size: FileSystem.Size(stat.size),
296
+ blksize: Option.map(Option.fromNullable(stat.blksize), FileSystem.Size),
297
+ blocks: Option.fromNullable(stat.blocks),
298
+ });
299
+ const stat = (() => {
300
+ const nodeStat = effectify(NFS.stat, handleErrnoException("FileSystem", "stat"), handleBadArgument("stat"));
301
+ return (path) => Effect.map(nodeStat(path), makeFileInfo);
302
+ })();
303
+ // == symlink
304
+ const symlink = (() => {
305
+ const nodeSymlink = effectify(NFS.symlink, handleErrnoException("FileSystem", "symlink"), handleBadArgument("symlink"));
306
+ return (target, path) => nodeSymlink(target, path);
307
+ })();
308
+ // == truncate
309
+ const truncate = (() => {
310
+ const nodeTruncate = effectify(NFS.truncate, handleErrnoException("FileSystem", "truncate"), handleBadArgument("truncate"));
311
+ return (path, length) => nodeTruncate(path, length !== undefined ? Number(length) : undefined);
312
+ })();
313
+ // == utimes
314
+ const utimes = (() => {
315
+ const nodeUtimes = effectify(NFS.utimes, handleErrnoException("FileSystem", "utime"), handleBadArgument("utime"));
316
+ return (path, atime, mtime) => nodeUtimes(path, atime, mtime);
317
+ })();
318
+ // == watch
319
+ const watchNode = (path, options) => Stream.asyncScoped((emit) => Effect.acquireRelease(Effect.sync(() => {
320
+ const watcher = NFS.watch(path, { recursive: options?.recursive }, (event, path) => {
321
+ if (!path)
322
+ return;
323
+ switch (event) {
324
+ case "rename": {
325
+ emit.fromEffect(Effect.matchEffect(stat(path), {
326
+ onSuccess: (_) => Effect.succeed(FileSystem.WatchEventCreate({ path })),
327
+ onFailure: (err) => err._tag === "SystemError" && err.reason === "NotFound"
328
+ ? Effect.succeed(FileSystem.WatchEventRemove({ path }))
329
+ : Effect.fail(err),
330
+ }));
331
+ return;
332
+ }
333
+ case "change": {
334
+ emit.single(FileSystem.WatchEventUpdate({ path }));
335
+ return;
336
+ }
337
+ }
338
+ });
339
+ watcher.on("error", (error) => {
340
+ emit.fail(new Error.SystemError({
341
+ module: "FileSystem",
342
+ reason: "Unknown",
343
+ method: "watch",
344
+ pathOrDescriptor: path,
345
+ cause: error,
346
+ }));
347
+ });
348
+ watcher.on("close", () => {
349
+ emit.end();
350
+ });
351
+ return watcher;
352
+ }), (watcher) => Effect.sync(() => watcher.close())));
353
+ const watch = (backend, path, options) => stat(path).pipe(Effect.map((stat) => backend.pipe(Option.flatMap((_) => _.register(path, stat, options)), Option.getOrElse(() => watchNode(path, options)))), Stream.unwrap);
354
+ // == writeFile
355
+ const writeFile = (path, data, options) => Effect.async((resume, signal) => {
356
+ try {
357
+ NFS.writeFile(path, data, {
358
+ signal,
359
+ flag: options?.flag,
360
+ mode: options?.mode,
361
+ }, (err) => {
362
+ if (err) {
363
+ resume(Effect.fail(handleErrnoException("FileSystem", "writeFile")(err, [path])));
364
+ }
365
+ else {
366
+ resume(Effect.void);
367
+ }
368
+ });
369
+ }
370
+ catch (err) {
371
+ resume(Effect.fail(handleBadArgument("writeFile")(err)));
372
+ }
373
+ });
374
+ const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend), (backend) => FileSystem.make({
375
+ access,
376
+ chmod,
377
+ chown,
378
+ copy,
379
+ copyFile,
380
+ link,
381
+ makeDirectory,
382
+ makeTempDirectory,
383
+ makeTempDirectoryScoped,
384
+ makeTempFile,
385
+ makeTempFileScoped,
386
+ open,
387
+ readDirectory,
388
+ readFile,
389
+ readLink,
390
+ realPath,
391
+ remove,
392
+ rename,
393
+ stat,
394
+ symlink,
395
+ truncate,
396
+ utimes,
397
+ watch(path, options) {
398
+ return watch(backend, path, options);
399
+ },
400
+ writeFile,
401
+ }));
402
+ export const layer = Layer.effect(FileSystem.FileSystem, makeFileSystem);
403
+ export { Error, FileSystem, };
404
+ export function handleErrnoException(module, method) {
405
+ return function (err, [path]) {
406
+ let reason = "Unknown";
407
+ switch (err.code) {
408
+ case "ENOENT":
409
+ reason = "NotFound";
410
+ break;
411
+ case "EACCES":
412
+ reason = "PermissionDenied";
413
+ break;
414
+ case "EEXIST":
415
+ reason = "AlreadyExists";
416
+ break;
417
+ case "EISDIR":
418
+ reason = "BadResource";
419
+ break;
420
+ case "ENOTDIR":
421
+ reason = "BadResource";
422
+ break;
423
+ case "EBUSY":
424
+ reason = "Busy";
425
+ break;
426
+ case "ELOOP":
427
+ reason = "BadResource";
428
+ break;
429
+ }
430
+ return new SystemError({
431
+ reason,
432
+ module,
433
+ method,
434
+ pathOrDescriptor: path,
435
+ syscall: err.syscall,
436
+ description: err.message,
437
+ cause: err,
438
+ });
439
+ };
440
+ }
@@ -0,0 +1 @@
1
+ export declare const findClosestPackageJson: (path: string) => Promise<string | undefined>;
@@ -0,0 +1,19 @@
1
+ import * as NFS from "node:fs/promises";
2
+ import * as NPath from "node:path";
3
+ export const findClosestPackageJson = async (path) => {
4
+ const resolved = NPath.resolve(path);
5
+ const stat = await NFS.stat(resolved).catch(() => undefined);
6
+ let dir = stat?.isDirectory() ? resolved : NPath.dirname(resolved);
7
+ const root = NPath.parse(dir).root;
8
+ while (dir !== root) {
9
+ const candidate = NPath.join(dir, "package.json");
10
+ try {
11
+ await NFS.access(candidate);
12
+ return candidate;
13
+ }
14
+ catch {
15
+ dir = NPath.dirname(dir);
16
+ }
17
+ }
18
+ return undefined;
19
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { Context } from "effect";
2
+ import * as Route from "./RouteBody";
3
+ const ServiceA = Context.GenericTag("ServiceA");
4
+ const ServiceB = Context.GenericTag("ServiceB");
5
+ // This handler requires BOTH ServiceA and ServiceB.
6
+ // In the original type definition, Generator requires all yields to have the same R.
7
+ // ServiceA != ServiceB, so this should fail type checking.
8
+ const handler = Route.handle(function* () {
9
+ yield* ServiceA;
10
+ yield* ServiceB;
11
+ return "ok";
12
+ });
13
+ // To avoid unused variable warning
14
+ console.log(handler);
@@ -0,0 +1,13 @@
1
+ import * as HttpApp from "@effect/platform/HttpApp";
2
+ import * as HttpClient from "@effect/platform/HttpClient";
3
+ import * as HttpClientError from "@effect/platform/HttpClientError";
4
+ import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
5
+ import { RouteNotFound } from "@effect/platform/HttpServerError";
6
+ import * as HttpServerRequest from "@effect/platform/HttpServerRequest";
7
+ import * as Effect from "effect/Effect";
8
+ export type FetchHandler = (req: Request) => Response | Promise<Response>;
9
+ export declare const isFetchHandler: (app: unknown) => app is FetchHandler;
10
+ export declare const make: <E, R>(appOrHandler: HttpApp.Default<E, R> | FetchHandler, opts?: {
11
+ baseUrl?: string | null;
12
+ handleRouteNotFound?: (e: RouteNotFound) => Effect.Effect<HttpClientResponse.HttpClientResponse> | null;
13
+ }) => HttpClient.HttpClient.With<HttpClientError.HttpClientError | E, Exclude<R, HttpServerRequest.HttpServerRequest>>;
@@ -0,0 +1,68 @@
1
+ import * as HttpClient from "@effect/platform/HttpClient";
2
+ import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
3
+ import * as HttpClientResponse from "@effect/platform/HttpClientResponse";
4
+ import { RouteNotFound } from "@effect/platform/HttpServerError";
5
+ import * as HttpServerRequest from "@effect/platform/HttpServerRequest";
6
+ import * as HttpServerResponse from "@effect/platform/HttpServerResponse";
7
+ import * as UrlParams from "@effect/platform/UrlParams";
8
+ import * as Effect from "effect/Effect";
9
+ import * as Either from "effect/Either";
10
+ import * as Function from "effect/Function";
11
+ import * as Stream from "effect/Stream";
12
+ const WebHeaders = globalThis.Headers;
13
+ export const isFetchHandler = (app) => typeof app === "function" && !Effect.isEffect(app);
14
+ const fromFetchHandler = (handler) => Effect.gen(function* () {
15
+ const serverRequest = yield* HttpServerRequest.HttpServerRequest;
16
+ const webRequest = serverRequest.source;
17
+ const response = yield* Effect.promise(async () => handler(webRequest));
18
+ const body = yield* Effect.promise(() => response.arrayBuffer());
19
+ return HttpServerResponse.raw(new Uint8Array(body), {
20
+ status: response.status,
21
+ statusText: response.statusText,
22
+ headers: Object.fromEntries(response.headers.entries()),
23
+ });
24
+ });
25
+ export const make = (appOrHandler, opts) => {
26
+ const httpApp = isFetchHandler(appOrHandler)
27
+ ? fromFetchHandler(appOrHandler)
28
+ : appOrHandler;
29
+ const execute = (request) => {
30
+ const urlResult = UrlParams.makeUrl(request.url, request.urlParams, request.hash);
31
+ if (Either.isLeft(urlResult)) {
32
+ return Effect.die(urlResult.left);
33
+ }
34
+ const url = urlResult.right;
35
+ const controller = new AbortController();
36
+ const signal = controller.signal;
37
+ const send = (body) => {
38
+ const serverRequest = HttpServerRequest.fromWeb(new Request(url.toString(), {
39
+ method: request.method,
40
+ headers: new WebHeaders(request.headers),
41
+ body,
42
+ duplex: request.body._tag === "Stream" ? "half" : undefined,
43
+ signal,
44
+ }));
45
+ return Function.pipe(httpApp, Effect.provideService(HttpServerRequest.HttpServerRequest, serverRequest), Effect.andThen(HttpServerResponse.toWeb), Effect.andThen(res => HttpClientResponse.fromWeb(request, res)), opts?.handleRouteNotFound === null
46
+ ? Function.identity
47
+ : Effect.catchAll((e) => e instanceof RouteNotFound
48
+ ? Effect.succeed(HttpClientResponse.fromWeb(request, new Response("Failed with RouteNotFound", {
49
+ status: 404,
50
+ })))
51
+ : Effect.fail(e)));
52
+ };
53
+ switch (request.body._tag) {
54
+ case "Raw":
55
+ case "Uint8Array":
56
+ return send(request.body.body);
57
+ case "FormData":
58
+ return send(request.body.formData);
59
+ case "Stream":
60
+ return Effect.flatMap(Stream.toReadableStreamEffect(request.body.stream), send);
61
+ }
62
+ return send(undefined);
63
+ };
64
+ const client = HttpClient.makeWith((requestEffect) => Effect.flatMap(requestEffect, execute), (request) => Effect.succeed(request));
65
+ return client.pipe(opts?.baseUrl === null
66
+ ? Function.identity
67
+ : HttpClient.mapRequest(HttpClientRequest.prependUrl(opts?.baseUrl ?? "http://localhost")));
68
+ };
@@ -0,0 +1,13 @@
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Ref from "effect/Ref";
5
+ export type TestLoggerContext = {
6
+ messages: Ref.Ref<Array<string>>;
7
+ };
8
+ declare const TestLogger_base: Context.TagClass<TestLogger, "effect-start/TestLogger", TestLoggerContext>;
9
+ export declare class TestLogger extends TestLogger_base {
10
+ }
11
+ export declare function layer(): Layer.Layer<TestLogger>;
12
+ export declare const messages: Effect.Effect<Array<string>, never, TestLogger>;
13
+ export {};
@@ -0,0 +1,29 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Context from "effect/Context";
3
+ import * as Effect from "effect/Effect";
4
+ import * as FiberRef from "effect/FiberRef";
5
+ import * as HashSet from "effect/HashSet";
6
+ import * as Layer from "effect/Layer";
7
+ import * as Logger from "effect/Logger";
8
+ import * as MutableRef from "effect/MutableRef";
9
+ import * as Ref from "effect/Ref";
10
+ export class TestLogger extends Context.Tag("effect-start/TestLogger")() {
11
+ }
12
+ export function layer() {
13
+ return Layer.effect(TestLogger, Effect.gen(function* () {
14
+ const messages = yield* Ref.make([]);
15
+ const mutableRef = messages.ref;
16
+ const customLogger = Logger.make(({ message, logLevel, cause }) => {
17
+ const causeStr = !Cause.isEmpty(cause)
18
+ ? ` ${Cause.pretty(cause)}`
19
+ : "";
20
+ MutableRef.update(mutableRef, (msgs) => [...msgs, `[${logLevel._tag}] ${String(message)}${causeStr}`]);
21
+ });
22
+ yield* FiberRef.update(FiberRef.currentLoggers, (loggers) => HashSet.add(HashSet.remove(loggers, Logger.defaultLogger), customLogger));
23
+ return { messages };
24
+ }));
25
+ }
26
+ export const messages = Effect.gen(function* () {
27
+ const logger = yield* TestLogger;
28
+ return yield* Ref.get(logger.messages);
29
+ });
@@ -0,0 +1,3 @@
1
+ export * as TestHttpClient from "./TestHttpClient.ts";
2
+ export * as TestLogger from "./TestLogger.ts";
3
+ export * from "./utils.ts";
@@ -0,0 +1,3 @@
1
+ export * as TestHttpClient from "./TestHttpClient.js";
2
+ export * as TestLogger from "./TestLogger.js";
3
+ export * from "./utils.js";
@@ -0,0 +1,9 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Scope from "effect/Scope";
4
+ import type { YieldWrap } from "effect/Utils";
5
+ /**
6
+ * Creates a scoped Effects and runs is asynchronously.
7
+ * Useful for testing.
8
+ */
9
+ export declare const effectFn: <RL>(layer?: Layer.Layer<RL, any>) => <Eff extends YieldWrap<Effect.Effect<any, any, RE>>, AEff, RE extends RL | Scope.Scope>(f: () => Generator<Eff, AEff, never>) => Promise<void>;
@@ -0,0 +1,39 @@
1
+ import * as Array from "effect/Array";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Function from "effect/Function";
4
+ import * as Layer from "effect/Layer";
5
+ import * as Logger from "effect/Logger";
6
+ /**
7
+ * Creates a scoped Effects and runs is asynchronously.
8
+ * Useful for testing.
9
+ */
10
+ export const effectFn = (layer) => (f) => Function.pipe(Effect.gen(f), Effect.scoped, Effect.provide(Logger.pretty), Effect.provide(layer ?? Layer.empty),
11
+ // @ts-expect-error will have to figure out how to clear deps
12
+ Effect.runPromise, v => v.then(() => { }, clearStackTraces));
13
+ /*
14
+ * When effect fails, instead of throwing FiberFailure,
15
+ * throw a plain Error with the strack trace and hides
16
+ * effect internals.
17
+ * Otherwise, at least on Bun, the strack trace is repeated,
18
+ * with some junks in between taking half of the screen.
19
+ *
20
+ * Direct children that starts with a dot are excluded because
21
+ * some tools, like effect-start, use it to generate temporary
22
+ * files that are then loaded into a runtime.
23
+ */
24
+ const clearStackTraces = (err) => {
25
+ const ExternalStackTraceLineRegexp = /\(.*\/node_modules\/[^\.]/;
26
+ const message = err instanceof Error
27
+ ? err.message
28
+ : typeof err === "object" && err !== null && "message" in err
29
+ ? String(err.message)
30
+ : String(err);
31
+ const stack = err instanceof Error
32
+ ? err.stack ?? ""
33
+ : typeof err === "object" && err !== null && "stack" in err
34
+ ? String(err.stack)
35
+ : "";
36
+ const newErr = new Error(message);
37
+ newErr.stack = Function.pipe(stack.split("\n"), Array.takeWhile(s => !ExternalStackTraceLineRegexp.test(s)), Array.join("\n"));
38
+ throw newErr;
39
+ };