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.
- package/dist/Commander.d.ts +103 -0
- package/dist/Commander.js +333 -0
- package/dist/ContentNegotiation.d.ts +13 -0
- package/dist/ContentNegotiation.js +364 -0
- package/dist/Development.d.ts +34 -0
- package/dist/Development.js +52 -0
- package/dist/Entity.d.ts +47 -0
- package/dist/Entity.js +224 -0
- package/dist/FileRouter.d.ts +61 -0
- package/dist/FileRouter.js +203 -0
- package/dist/FileRouterCodegen.d.ts +19 -0
- package/dist/FileRouterCodegen.js +176 -0
- package/dist/FileRouterPattern.d.ts +9 -0
- package/dist/FileRouterPattern.js +35 -0
- package/dist/Http.d.ts +37 -0
- package/dist/Http.js +92 -0
- package/dist/HttpAppExtra.d.ts +7 -0
- package/dist/HttpAppExtra.js +320 -0
- package/dist/HttpUtils.d.ts +3 -0
- package/dist/HttpUtils.js +11 -0
- package/dist/PathPattern.d.ts +134 -0
- package/dist/PathPattern.js +415 -0
- package/dist/Random.d.ts +5 -0
- package/dist/Random.js +49 -0
- package/dist/Route.d.ts +98 -0
- package/dist/Route.js +81 -0
- package/dist/RouteBody.d.ts +53 -0
- package/dist/RouteBody.js +67 -0
- package/dist/RouteHook.d.ts +12 -0
- package/dist/RouteHook.js +45 -0
- package/dist/RouteHttp.d.ts +21 -0
- package/dist/RouteHttp.js +260 -0
- package/dist/RouteHttpTracer.d.ts +10 -0
- package/dist/RouteHttpTracer.js +62 -0
- package/dist/RouteMount.d.ts +119 -0
- package/dist/RouteMount.js +77 -0
- package/dist/RouteSchema.d.ts +65 -0
- package/dist/RouteSchema.js +155 -0
- package/dist/RouteSse.d.ts +21 -0
- package/dist/RouteSse.js +85 -0
- package/dist/RouteTree.d.ts +56 -0
- package/dist/RouteTree.js +91 -0
- package/dist/RouteTrie.d.ts +20 -0
- package/dist/RouteTrie.js +157 -0
- package/dist/RouterPattern.d.ts +118 -0
- package/dist/RouterPattern.js +269 -0
- package/dist/SchemaExtra.d.ts +7 -0
- package/dist/SchemaExtra.js +74 -0
- package/dist/Start.d.ts +19 -0
- package/dist/Start.js +23 -0
- package/dist/StartApp.d.ts +19 -0
- package/dist/StartApp.js +21 -0
- package/dist/StreamExtra.d.ts +28 -0
- package/dist/StreamExtra.js +100 -0
- package/dist/TuplePathPattern.d.ts +9 -0
- package/dist/TuplePathPattern.js +63 -0
- package/dist/Values.d.ts +26 -0
- package/dist/Values.js +30 -0
- package/dist/bun/BunBundle.d.ts +12 -0
- package/dist/bun/BunBundle.js +145 -0
- package/dist/bun/BunHttpServer.d.ts +44 -0
- package/dist/bun/BunHttpServer.js +187 -0
- package/dist/bun/BunHttpServer_web.d.ts +60 -0
- package/dist/bun/BunHttpServer_web.js +252 -0
- package/dist/bun/BunImportTrackerPlugin.d.ts +13 -0
- package/dist/bun/BunImportTrackerPlugin.js +71 -0
- package/dist/bun/BunRoute.d.ts +49 -0
- package/dist/bun/BunRoute.js +131 -0
- package/dist/bun/BunRuntime.d.ts +1 -0
- package/dist/bun/BunRuntime.js +26 -0
- package/dist/bun/BunVirtualFilesPlugin.d.ts +4 -0
- package/dist/bun/BunVirtualFilesPlugin.js +40 -0
- package/dist/bun/_BunEnhancedResolve.d.ts +45 -0
- package/dist/bun/_BunEnhancedResolve.js +104 -0
- package/dist/bun/index.d.ts +4 -0
- package/dist/bun/index.js +4 -0
- package/dist/bundler/Bundle.d.ts +60 -0
- package/dist/bundler/Bundle.js +48 -0
- package/dist/bundler/BundleFiles.d.ts +13 -0
- package/dist/bundler/BundleFiles.js +94 -0
- package/dist/bundler/BundleHttp.d.ts +45 -0
- package/dist/bundler/BundleHttp.js +176 -0
- package/dist/client/Overlay.d.ts +2 -0
- package/dist/client/Overlay.js +32 -0
- package/dist/client/ScrollState.d.ts +6 -0
- package/dist/client/ScrollState.js +98 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +81 -0
- package/dist/experimental/EncryptedCookies.d.ts +51 -0
- package/dist/experimental/EncryptedCookies.js +243 -0
- package/dist/experimental/SseHttpResponse.d.ts +7 -0
- package/dist/experimental/SseHttpResponse.js +28 -0
- package/dist/experimental/index.d.ts +2 -0
- package/dist/experimental/index.js +2 -0
- package/dist/hyper/Hyper.d.ts +32 -0
- package/dist/hyper/Hyper.js +34 -0
- package/dist/hyper/HyperHtml.d.ts +23 -0
- package/dist/hyper/HyperHtml.js +144 -0
- package/dist/hyper/HyperNode.d.ts +14 -0
- package/dist/hyper/HyperNode.js +11 -0
- package/dist/hyper/HyperRoute.d.ts +8 -0
- package/dist/hyper/HyperRoute.js +32 -0
- package/dist/hyper/HyperRoute.test.d.ts +1 -0
- package/dist/hyper/HyperRoute.test.js +72 -0
- package/dist/hyper/index.d.ts +4 -0
- package/dist/hyper/index.js +4 -0
- package/dist/hyper/jsx-runtime.d.ts +7 -0
- package/dist/hyper/jsx-runtime.js +8 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/inference_check.d.ts +1 -0
- package/dist/inference_check.js +15 -0
- package/dist/middlewares/BasicAuthMiddleware.d.ts +8 -0
- package/dist/middlewares/BasicAuthMiddleware.js +22 -0
- package/dist/middlewares/index.d.ts +1 -0
- package/dist/middlewares/index.js +1 -0
- package/dist/node/FileSystem.d.ts +9 -0
- package/dist/node/FileSystem.js +440 -0
- package/dist/node/Utils.d.ts +1 -0
- package/dist/node/Utils.js +19 -0
- package/dist/repro_fail.d.ts +1 -0
- package/dist/repro_fail.js +14 -0
- package/dist/testing/TestHttpClient.d.ts +13 -0
- package/dist/testing/TestHttpClient.js +68 -0
- package/dist/testing/TestLogger.d.ts +13 -0
- package/dist/testing/TestLogger.js +29 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/utils.d.ts +9 -0
- package/dist/testing/utils.js +39 -0
- package/dist/x/cloudflare/CloudflareTunnel.d.ts +13 -0
- package/dist/x/cloudflare/CloudflareTunnel.js +43 -0
- package/dist/x/cloudflare/index.d.ts +1 -0
- package/dist/x/cloudflare/index.js +1 -0
- package/dist/x/datastar/Datastar.d.ts +6 -0
- package/dist/x/datastar/Datastar.js +46 -0
- package/dist/x/datastar/index.d.ts +2 -0
- package/dist/x/datastar/index.js +2 -0
- package/dist/x/tailwind/TailwindPlugin.d.ts +23 -0
- package/dist/x/tailwind/TailwindPlugin.js +219 -0
- package/dist/x/tailwind/compile.d.ts +19 -0
- package/dist/x/tailwind/compile.js +156 -0
- package/dist/x/tailwind/plugin.d.ts +2 -0
- package/dist/x/tailwind/plugin.js +15 -0
- package/package.json +68 -16
- package/src/RouteBody.test.ts +18 -0
- package/src/RouteBody.ts +126 -2
- 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,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
|
+
};
|