effect-start 0.25.0 → 0.26.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 (117) hide show
  1. package/package.json +18 -86
  2. package/dist/ChildProcess.js +0 -42
  3. package/dist/Commander.js +0 -410
  4. package/dist/ContentNegotiation.js +0 -465
  5. package/dist/Cookies.js +0 -371
  6. package/dist/Development.js +0 -94
  7. package/dist/Effectify.js +0 -27
  8. package/dist/Entity.js +0 -289
  9. package/dist/Fetch.js +0 -192
  10. package/dist/FilePathPattern.js +0 -97
  11. package/dist/FileRouter.js +0 -204
  12. package/dist/FileRouterCodegen.js +0 -298
  13. package/dist/FileSystem.js +0 -132
  14. package/dist/Http.js +0 -107
  15. package/dist/PathPattern.js +0 -451
  16. package/dist/PlatformError.js +0 -40
  17. package/dist/PlatformRuntime.js +0 -71
  18. package/dist/Route.js +0 -143
  19. package/dist/RouteBody.js +0 -92
  20. package/dist/RouteError.js +0 -76
  21. package/dist/RouteHook.js +0 -64
  22. package/dist/RouteHttp.js +0 -367
  23. package/dist/RouteHttpTracer.js +0 -90
  24. package/dist/RouteMount.js +0 -86
  25. package/dist/RouteSchema.js +0 -271
  26. package/dist/RouteSse.js +0 -94
  27. package/dist/RouteTree.js +0 -119
  28. package/dist/RouteTrie.js +0 -179
  29. package/dist/SchemaExtra.js +0 -99
  30. package/dist/Socket.js +0 -40
  31. package/dist/SqlIntrospect.js +0 -515
  32. package/dist/Start.js +0 -79
  33. package/dist/StartApp.js +0 -3
  34. package/dist/StreamExtra.js +0 -135
  35. package/dist/System.js +0 -38
  36. package/dist/TuplePathPattern.js +0 -74
  37. package/dist/Unique.js +0 -226
  38. package/dist/Values.js +0 -52
  39. package/dist/bun/BunBundle.js +0 -186
  40. package/dist/bun/BunChildProcessSpawner.js +0 -142
  41. package/dist/bun/BunImportTrackerPlugin.js +0 -91
  42. package/dist/bun/BunRoute.js +0 -157
  43. package/dist/bun/BunRuntime.js +0 -41
  44. package/dist/bun/BunServer.js +0 -285
  45. package/dist/bun/BunVirtualFilesPlugin.js +0 -54
  46. package/dist/bun/_BunEnhancedResolve.js +0 -127
  47. package/dist/bun/index.js +0 -5
  48. package/dist/bundler/Bundle.js +0 -92
  49. package/dist/bundler/BundleFiles.js +0 -154
  50. package/dist/bundler/BundleRoute.js +0 -62
  51. package/dist/client/Overlay.js +0 -33
  52. package/dist/client/ScrollState.js +0 -106
  53. package/dist/client/index.js +0 -97
  54. package/dist/console/Console.js +0 -42
  55. package/dist/console/ConsoleErrors.js +0 -211
  56. package/dist/console/ConsoleLogger.js +0 -56
  57. package/dist/console/ConsoleMetrics.js +0 -72
  58. package/dist/console/ConsoleProcess.js +0 -59
  59. package/dist/console/ConsoleStore.js +0 -72
  60. package/dist/console/ConsoleTracer.js +0 -107
  61. package/dist/console/Simulation.js +0 -784
  62. package/dist/console/index.js +0 -3
  63. package/dist/console/routes/tree.js +0 -30
  64. package/dist/datastar/actions/fetch.js +0 -536
  65. package/dist/datastar/actions/peek.js +0 -13
  66. package/dist/datastar/actions/setAll.js +0 -19
  67. package/dist/datastar/actions/toggleAll.js +0 -19
  68. package/dist/datastar/attributes/attr.js +0 -49
  69. package/dist/datastar/attributes/bind.js +0 -194
  70. package/dist/datastar/attributes/class.js +0 -54
  71. package/dist/datastar/attributes/computed.js +0 -25
  72. package/dist/datastar/attributes/effect.js +0 -10
  73. package/dist/datastar/attributes/indicator.js +0 -33
  74. package/dist/datastar/attributes/init.js +0 -27
  75. package/dist/datastar/attributes/jsonSignals.js +0 -33
  76. package/dist/datastar/attributes/on.js +0 -81
  77. package/dist/datastar/attributes/onIntersect.js +0 -53
  78. package/dist/datastar/attributes/onInterval.js +0 -31
  79. package/dist/datastar/attributes/onSignalPatch.js +0 -51
  80. package/dist/datastar/attributes/ref.js +0 -11
  81. package/dist/datastar/attributes/show.js +0 -32
  82. package/dist/datastar/attributes/signals.js +0 -18
  83. package/dist/datastar/attributes/style.js +0 -57
  84. package/dist/datastar/attributes/text.js +0 -29
  85. package/dist/datastar/engine.js +0 -1145
  86. package/dist/datastar/index.js +0 -25
  87. package/dist/datastar/utils.js +0 -250
  88. package/dist/datastar/watchers/patchElements.js +0 -486
  89. package/dist/datastar/watchers/patchSignals.js +0 -14
  90. package/dist/experimental/EncryptedCookies.js +0 -328
  91. package/dist/experimental/index.js +0 -1
  92. package/dist/hyper/Hyper.js +0 -28
  93. package/dist/hyper/HyperHtml.js +0 -165
  94. package/dist/hyper/HyperNode.js +0 -13
  95. package/dist/hyper/HyperRoute.js +0 -45
  96. package/dist/hyper/html.js +0 -30
  97. package/dist/hyper/index.js +0 -5
  98. package/dist/hyper/jsx-runtime.js +0 -14
  99. package/dist/index.js +0 -8
  100. package/dist/node/NodeFileSystem.js +0 -675
  101. package/dist/node/NodeUtils.js +0 -23
  102. package/dist/sql/Sql.js +0 -8
  103. package/dist/sql/bun/index.js +0 -142
  104. package/dist/sql/index.js +0 -1
  105. package/dist/sql/libsql/index.js +0 -156
  106. package/dist/sql/mssql/docker.js +0 -110
  107. package/dist/sql/mssql/index.js +0 -194
  108. package/dist/testing/TestLogger.js +0 -42
  109. package/dist/testing/index.js +0 -2
  110. package/dist/testing/utils.js +0 -61
  111. package/dist/x/cloudflare/CloudflareTunnel.js +0 -63
  112. package/dist/x/cloudflare/index.js +0 -1
  113. package/dist/x/tailscale/TailscaleTunnel.js +0 -94
  114. package/dist/x/tailscale/index.js +0 -1
  115. package/dist/x/tailwind/TailwindPlugin.js +0 -294
  116. package/dist/x/tailwind/compile.js +0 -210
  117. package/dist/x/tailwind/plugin.js +0 -17
@@ -1,204 +0,0 @@
1
- import * as FileSystem from "./FileSystem.js"
2
- import * as Data from "effect/Data"
3
- import * as Effect from "effect/Effect"
4
- import * as Either from "effect/Either"
5
- import * as Function from "effect/Function"
6
- import * as Layer from "effect/Layer"
7
- import * as Stream from "effect/Stream"
8
- import * as NPath from "node:path"
9
- import * as NUrl from "node:url"
10
- import * as Development from "./Development.js"
11
- import * as FilePathPattern from "./FilePathPattern.js"
12
- import * as FileRouterCodegen from "./FileRouterCodegen.js"
13
- import * as NodeUtils from "./node/NodeUtils.js"
14
- import * as Route from "./Route.js"
15
- import * as RouteTree from "./RouteTree.js"
16
-
17
- export class FileRouterError extends Data.TaggedError("FileRouterError") {}
18
-
19
- /**
20
- * Routes sorted by depth, with rest parameters at the end.
21
- * - layer.tsx
22
- * - users/route.tsx
23
- * - users/[userId]/route.tsx
24
- * - [[rest]]/route.tsx
25
- */
26
-
27
- const ROUTE_PATH_REGEX = /^\/?(.*\/?)(?:route|layer)\.(jsx?|tsx?)$/
28
-
29
- export function parseRoute(path) {
30
- const segs = FilePathPattern.segments(path)
31
-
32
- const lastSeg = segs.at(-1)
33
- const handleMatch =
34
- lastSeg?._tag === "LiteralSegment" && lastSeg.value.match(/^(route|layer)\.(tsx?|jsx?)$/)
35
- const handle = handleMatch ? (handleMatch[1]) : null
36
-
37
- if (!handle) {
38
- return null
39
- }
40
-
41
- const pathSegments = segs.slice(0, -1)
42
-
43
- const validated = FilePathPattern.validate(FilePathPattern.format(pathSegments))
44
- if (Either.isLeft(validated)) {
45
- return null
46
- }
47
-
48
- const restIndex = pathSegments.findIndex((seg) => seg._tag === "RestSegment")
49
- if (restIndex !== -1 && restIndex !== pathSegments.length - 1) {
50
- return null
51
- }
52
-
53
- const routePathSegments = pathSegments.filter((seg) => seg._tag !== "GroupSegment")
54
- const routePath = FilePathPattern.format(routePathSegments)
55
-
56
- return {
57
- handle,
58
- modulePath: `/${path}`,
59
- routePath,
60
- segments: pathSegments,
61
- }
62
- }
63
-
64
- function importModule(load) {
65
- return Effect.tryPromise({
66
- try: () => load(),
67
- catch: (cause) => new FileRouterError({ reason: "Import", cause }),
68
- })
69
- }
70
-
71
- /**
72
- * Generates a tree file that references all routes.
73
- */
74
- export function layer(
75
- loadOrOptions,
76
- ) {
77
- const options =
78
- typeof loadOrOptions === "function"
79
- ? {
80
- load: loadOrOptions,
81
- path: NPath.join(NodeUtils.getEntrypoint(), "routes"),
82
- }
83
- : loadOrOptions
84
- let treePath = options.path
85
- if (treePath.startsWith("file://")) {
86
- treePath = NUrl.fileURLToPath(treePath)
87
- }
88
- if (NPath.extname(treePath) === "") {
89
- treePath = NPath.join(treePath, "server.gen.ts")
90
- }
91
-
92
- const routesPath = NPath.dirname(treePath)
93
- const treeFilename = NPath.basename(treePath)
94
- const relativeRoutesPath = NPath.relative(process.cwd(), routesPath)
95
-
96
- return Layer.scoped(
97
- Route.Routes,
98
- Effect.gen(function* () {
99
- // Generate routes file before loading
100
- yield* FileRouterCodegen.update(routesPath, treeFilename)
101
-
102
- // Load and build route tree
103
- const m = yield* importModule(options.load)
104
- const routeTree = yield* fromFileRoutes(m.default)
105
-
106
- // Watch for changes (only when Development service is available)
107
- yield* Function.pipe(
108
- Development.stream(),
109
- Stream.filter((e) => e._tag !== "Reload" && e.path.startsWith(relativeRoutesPath)),
110
- Stream.runForEach(() => FileRouterCodegen.update(routesPath, treeFilename)),
111
- Effect.fork,
112
- )
113
-
114
- return routeTree
115
- }),
116
- )
117
- }
118
-
119
- export function fromFileRoutes(fileRoutes) {
120
- return Effect.gen(function* () {
121
- const mounts = {}
122
-
123
- for (const [path, loaders] of Object.entries(fileRoutes)) {
124
- const modules = yield* Effect.forEach(loaders, (loader) => Effect.promise(() => loader()))
125
-
126
- const allRoutes = []
127
-
128
- for (const m of modules) {
129
- if (Route.isRouteSet(m.default)) {
130
- for (const route of m.default) {
131
- ;(allRoutes).push(route)
132
- }
133
- }
134
- }
135
-
136
- mounts[path] = allRoutes
137
- }
138
-
139
- return RouteTree.make(mounts)
140
- })
141
- }
142
-
143
- export function walkRoutesDirectory(
144
- dir,
145
- ) {
146
- return Effect.gen(function* () {
147
- const fs = yield* FileSystem.FileSystem
148
- const files = yield* fs.readDirectory(dir, { recursive: true })
149
-
150
- return yield* getFileRoutes(files)
151
- })
152
- }
153
-
154
- export function getFileRoutes(
155
- paths,
156
- ) {
157
- return Effect.gen(function* () {
158
- const routes = paths
159
- .map((f) => f.match(ROUTE_PATH_REGEX))
160
- .filter(Boolean)
161
- .map((v) => {
162
- const path = v[0]
163
- try {
164
- return parseRoute(path)
165
- } catch {
166
- return null
167
- }
168
- })
169
- .filter((route) => route !== null)
170
- .toSorted((a, b) => {
171
- const aDepth = a.segments.length
172
- const bDepth = b.segments.length
173
- const aHasRest = a.segments.some((seg) => seg._tag === "RestSegment")
174
- const bHasRest = b.segments.some((seg) => seg._tag === "RestSegment")
175
-
176
- return (
177
- // rest is a dominant factor (routes with rest come last)
178
- (+aHasRest - +bHasRest) * 1000 +
179
- // depth is reversed for rest
180
- (aDepth - bDepth) * (1 - 2 * +aHasRest) +
181
- // lexicographic comparison as tiebreaker
182
- a.modulePath.localeCompare(b.modulePath) * 0.001
183
- )
184
- })
185
-
186
- // Detect conflicting routes at the same path
187
- const routesByPath = new Map()
188
- for (const route of routes) {
189
- const existing = routesByPath.get(route.routePath) || []
190
- existing.push(route)
191
- routesByPath.set(route.routePath, existing)
192
- }
193
-
194
- for (const [path, pathRoutes] of routesByPath) {
195
- const routeHandles = pathRoutes.filter((h) => h.handle === "route")
196
-
197
- if (routeHandles.length > 1) {
198
- yield* new FileRouterError({ reason: "Conflict", path })
199
- }
200
- }
201
-
202
- return routes
203
- })
204
- }
@@ -1,298 +0,0 @@
1
- import * as FileSystem from "./FileSystem.js"
2
- import * as Effect from "effect/Effect"
3
- import * as Either from "effect/Either"
4
- import * as Schema from "effect/Schema"
5
- import * as NPath from "node:path"
6
- import * as FilePathPattern from "./FilePathPattern.js"
7
- import * as FileRouter from "./FileRouter.js"
8
- import * as SchemaExtra from "./SchemaExtra.js"
9
-
10
- export function validateRouteModule(module) {
11
- if (typeof module !== "object" || module === null) {
12
- return false
13
- }
14
-
15
- if (!("default" in module)) {
16
- return false
17
- }
18
-
19
- // TODO: verify we're exporting a proper shape
20
- return true
21
- }
22
-
23
- export function generatePathParamsSchema(
24
- segments,
25
- ) {
26
- const fields = {}
27
-
28
- for (const segment of segments) {
29
- if (segment._tag === "ParamSegment") {
30
- fields[segment.name] = Schema.String
31
- } else if (segment._tag === "RestSegment") {
32
- fields[segment.name] = Schema.optional(Schema.String)
33
- }
34
- }
35
-
36
- if (Object.keys(fields).length === 0) {
37
- return null
38
- }
39
-
40
- return Schema.Struct(fields)
41
- }
42
-
43
- /**
44
- * Validates all route modules in the given route handles.
45
- */
46
-
47
- export function validateRouteModules(
48
- path,
49
- routes,
50
- ) {
51
- return Effect.gen(function* () {
52
- const fs = yield* FileSystem.FileSystem
53
- const routeHandles = routes.filter((h) => h.handle === "route")
54
-
55
- for (const handle of routeHandles) {
56
- const routeModulePath = NPath.resolve(path, handle.modulePath)
57
- const expectedSchema = generatePathParamsSchema(handle.segments)
58
-
59
- const fileExists = yield* fs
60
- .exists(routeModulePath)
61
- .pipe(Effect.catchAll(() => Effect.succeed(false)))
62
- if (!fileExists) {
63
- continue
64
- }
65
-
66
- const module = yield* Effect.tryPromise({
67
- try: () => import(routeModulePath),
68
- catch: (cause) =>
69
- new FileRouter.FileRouterError({
70
- reason: "Import",
71
- cause,
72
- path: routeModulePath,
73
- }),
74
- })
75
-
76
- if (!validateRouteModule(module)) {
77
- yield* Effect.logWarning(`Route module ${routeModulePath} should export default Route`)
78
- continue
79
- }
80
-
81
- const routeSet = module.default
82
- // extract user schema
83
- const userSchema = undefined
84
-
85
- if (expectedSchema && userSchema && !SchemaExtra.schemaEqual(userSchema, expectedSchema)) {
86
- const relativeFilePath = NPath.relative(process.cwd(), routeModulePath)
87
- yield* Effect.logError(
88
- `Route '${relativeFilePath}' has incorrect PathParams schema, expected schemaPathParams(${SchemaExtra.formatSchemaCode(
89
- expectedSchema,
90
- )})`,
91
- )
92
- }
93
- }
94
- })
95
- }
96
-
97
- export function generateCode(fileRoutes) {
98
- // Group routes by path to find layers
99
- const routesByPath = new Map()
100
-
101
- for (const fileRoute of fileRoutes) {
102
- const existing = routesByPath.get(fileRoute.routePath) || { layers: [] }
103
- if (fileRoute.handle === "route") {
104
- existing.route = fileRoute
105
- } else if (fileRoute.handle === "layer") {
106
- existing.layers.push(fileRoute)
107
- }
108
- routesByPath.set(fileRoute.routePath, existing)
109
- }
110
-
111
- // Helper to check if layer's path is an ancestor of route's path
112
- const layerMatchesRoute = (layer, route) => {
113
- const layerDir = layer.modulePath.replace(/\/(layer)\.(tsx?|jsx?)$/, "")
114
- if (layerDir === "/") return true
115
- return route.modulePath.startsWith(layerDir + "/")
116
- }
117
-
118
- // Build entries for each route path
119
- const entries = []
120
-
121
- for (const [path, { route }] of routesByPath) {
122
- if (!route) continue
123
-
124
- // Collect all parent layers that match the route's groups
125
- const allLayers = []
126
- let currentPath = path
127
-
128
- while (true) {
129
- const pathData = routesByPath.get(currentPath)
130
- if (pathData?.layers) {
131
- const matchingLayers = pathData.layers.filter((layer) => layerMatchesRoute(layer, route))
132
- allLayers.unshift(...matchingLayers)
133
- }
134
-
135
- if (currentPath === "/") break
136
-
137
- const parentPath = currentPath.substring(0, currentPath.lastIndexOf("/"))
138
- currentPath = parentPath || "/"
139
- }
140
-
141
- // Convert file-style path to colon-style PathPattern
142
- const pathPatternResult = FilePathPattern.toPathPattern(path)
143
- if (Either.isLeft(pathPatternResult)) {
144
- continue
145
- }
146
- const pathPattern = pathPatternResult.right
147
-
148
- // Order: route first, then layers from innermost to outermost
149
- const loaders = [
150
- `() => import(".${route.modulePath}")`,
151
- ...allLayers.reverse().map((layer) => `() => import(".${layer.modulePath}")`),
152
- ]
153
-
154
- entries.push({ path: pathPattern, loaders })
155
- }
156
-
157
- // No routes found - don't create file
158
- if (entries.length === 0) {
159
- return null
160
- }
161
-
162
- const routeEntries = entries
163
- .map(({ path, loaders }) => {
164
- const loadersCode = loaders.join(",\n ")
165
- return ` "${path}": [\n ${loadersCode},\n ]`
166
- })
167
- .join(",\n")
168
-
169
- return `/**
170
- * Auto-generated by effect-start.
171
- */
172
-
173
- export default {
174
- ${routeEntries},
175
- } satisfies import("effect-start/FileRouter").FileRoutes
176
- `
177
- }
178
-
179
- /**
180
- * Updates the tree file only if the generated content differs from the existing file.
181
- * This prevents infinite loops when watching for file changes.
182
- */
183
- export function update(
184
- routesPath,
185
- treePath = "server.gen.ts",
186
- ) {
187
- return Effect.gen(function* () {
188
- treePath = NPath.resolve(routesPath, treePath)
189
-
190
- const fs = yield* FileSystem.FileSystem
191
- const files = yield* fs.readDirectory(routesPath, { recursive: true }).pipe(
192
- Effect.mapError(
193
- (cause) =>
194
- new FileRouter.FileRouterError({
195
- reason: "FileSystem",
196
- cause,
197
- path: routesPath,
198
- }),
199
- ),
200
- )
201
- const fileRoutes = yield* FileRouter.getFileRoutes(files)
202
-
203
- // Validate route modules
204
- yield* validateRouteModules(routesPath, fileRoutes)
205
-
206
- const newCode = generateCode(fileRoutes)
207
-
208
- // Check if file exists (ok to fail - means file doesn't exist)
209
- const existingCode = yield* fs
210
- .readFileString(treePath)
211
- .pipe(Effect.catchAll(() => Effect.succeed(null)))
212
-
213
- // No routes found
214
- if (newCode === null) {
215
- // If gen file exists, write empty export
216
- if (existingCode !== null) {
217
- const emptyCode = "export default {}\n"
218
- if (existingCode !== emptyCode) {
219
- yield* Effect.logDebug(`Clearing file routes tree: ${treePath}`)
220
- yield* fs.writeFileString(treePath, emptyCode).pipe(
221
- Effect.mapError(
222
- (cause) =>
223
- new FileRouter.FileRouterError({
224
- reason: "FileSystem",
225
- cause,
226
- path: treePath,
227
- }),
228
- ),
229
- )
230
- }
231
- }
232
- return
233
- }
234
-
235
- // Write if content differs
236
- if (existingCode !== newCode) {
237
- yield* Effect.logDebug(`Updating file routes tree: ${treePath}`)
238
- yield* fs.writeFileString(treePath, newCode).pipe(
239
- Effect.mapError(
240
- (cause) =>
241
- new FileRouter.FileRouterError({
242
- reason: "FileSystem",
243
- cause,
244
- path: treePath,
245
- }),
246
- ),
247
- )
248
- } else {
249
- yield* Effect.logDebug(`File routes tree unchanged: ${treePath}`)
250
- }
251
- })
252
- }
253
-
254
- export function dump(
255
- routesPath,
256
- treePath = "server.gen.ts",
257
- ) {
258
- return Effect.gen(function* () {
259
- treePath = NPath.resolve(routesPath, treePath)
260
-
261
- const fs = yield* FileSystem.FileSystem
262
- const files = yield* fs.readDirectory(routesPath, { recursive: true }).pipe(
263
- Effect.mapError(
264
- (cause) =>
265
- new FileRouter.FileRouterError({
266
- reason: "FileSystem",
267
- cause,
268
- path: routesPath,
269
- }),
270
- ),
271
- )
272
- const fileRoutes = yield* FileRouter.getFileRoutes(files)
273
-
274
- // Validate route modules
275
- yield* validateRouteModules(routesPath, fileRoutes)
276
-
277
- const code = generateCode(fileRoutes)
278
-
279
- // No routes found - don't create file
280
- if (code === null) {
281
- yield* Effect.logDebug(`No routes found, skipping: ${treePath}`)
282
- return
283
- }
284
-
285
- yield* Effect.logDebug(`Generating file routes tree: ${treePath}`)
286
-
287
- yield* fs.writeFileString(treePath, code).pipe(
288
- Effect.mapError(
289
- (cause) =>
290
- new FileRouter.FileRouterError({
291
- reason: "FileSystem",
292
- cause,
293
- path: treePath,
294
- }),
295
- ),
296
- )
297
- })
298
- }
@@ -1,132 +0,0 @@
1
- /*
2
- * Adapted from @effect/platform
3
- */
4
- import * as Brand from "effect/Brand"
5
- import * as Channel from "effect/Channel"
6
- import * as Chunk from "effect/Chunk"
7
- import * as Context from "effect/Context"
8
- import * as Data from "effect/Data"
9
- import * as Effect from "effect/Effect"
10
- import * as Function from "effect/Function"
11
- import * as Option from "effect/Option"
12
- import * as Sink from "effect/Sink"
13
- import * as Stream from "effect/Stream"
14
- import * as PlatformError from "./PlatformError.js"
15
-
16
- export const FileSystem = Context.GenericTag(
17
- "@effect/platform/FileSystem",
18
- )
19
-
20
- export const Size = (bytes) =>
21
- typeof bytes === "bigint" ? (bytes) : (BigInt(bytes))
22
-
23
- export const FileTypeId = Symbol.for("@effect/platform/FileSystem/File")
24
-
25
- export const FileDescriptor = Brand.nominal()
26
-
27
- export const WatchEventCreate =
28
- Data.tagged("Create")
29
-
30
- export const WatchEventUpdate =
31
- Data.tagged("Update")
32
-
33
- export const WatchEventRemove =
34
- Data.tagged("Remove")
35
-
36
- export class WatchBackend extends Context.Tag("@effect/platform/FileSystem/WatchBackend")() {}
37
-
38
- export const make = (
39
- impl,
40
- ) => {
41
- return FileSystem.of({
42
- ...impl,
43
- exists: (path) =>
44
- Function.pipe(
45
- impl.access(path),
46
- Effect.as(true),
47
- Effect.catchTag("SystemError", (e) =>
48
- e.reason === "NotFound" ? Effect.succeed(false) : Effect.fail(e),
49
- ),
50
- ),
51
- readFileString: (path, encoding) =>
52
- Effect.tryMap(impl.readFile(path), {
53
- try: (_) => new TextDecoder(encoding).decode(_),
54
- catch: (cause) =>
55
- new PlatformError.BadArgument({
56
- module: "FileSystem",
57
- method: "readFileString",
58
- description: "invalid encoding",
59
- cause,
60
- }),
61
- }),
62
- stream: (path, options) =>
63
- Function.pipe(
64
- impl.open(path, { flag: "r" }),
65
- options?.offset
66
- ? Effect.tap((file) => file.seek(options.offset, "start"))
67
- : Function.identity,
68
- Effect.map((file) => fileStream(file, options)),
69
- Stream.unwrapScoped,
70
- ),
71
- sink: (path, options) =>
72
- Function.pipe(
73
- impl.open(path, { flag: "w", ...options }),
74
- Effect.map((file) => Sink.forEach((_) => file.writeAll(_))),
75
- Sink.unwrapScoped,
76
- ),
77
- writeFileString: (path, data, options) =>
78
- Effect.flatMap(
79
- Effect.try({
80
- try: () => new TextEncoder().encode(data),
81
- catch: (cause) =>
82
- new PlatformError.BadArgument({
83
- module: "FileSystem",
84
- method: "writeFileString",
85
- description: "could not encode string",
86
- cause,
87
- }),
88
- }),
89
- (_) => impl.writeFile(path, _, options),
90
- ),
91
- })
92
- }
93
-
94
- const fileStream = (
95
- file,
96
- {
97
- bufferSize = 16,
98
- bytesToRead: bytesToRead_,
99
- chunkSize: chunkSize_ = Size(64 * 1024),
100
- } = {},
101
- ) => {
102
- const bytesToRead = bytesToRead_ !== undefined ? Size(bytesToRead_) : undefined
103
- const chunkSize = Size(chunkSize_)
104
-
105
- function loop(
106
- totalBytesRead,
107
- ) {
108
- if (bytesToRead !== undefined && bytesToRead <= totalBytesRead) {
109
- return Channel.void
110
- }
111
-
112
- const toRead =
113
- bytesToRead !== undefined && bytesToRead - totalBytesRead < chunkSize
114
- ? bytesToRead - totalBytesRead
115
- : chunkSize
116
-
117
- return Channel.flatMap(
118
- file.readAlloc(toRead),
119
- Option.match({
120
- onNone: () => Channel.void,
121
- onSome: (buf) =>
122
- Channel.flatMap(Channel.write(Chunk.of(buf)), () =>
123
- loop(totalBytesRead + BigInt(buf.length)),
124
- ),
125
- }),
126
- )
127
- }
128
-
129
- return Stream.bufferChunks(Stream.fromChannel(loop(BigInt(0))), {
130
- capacity: bufferSize,
131
- })
132
- }