effect-start 0.25.0 → 0.27.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 (174) hide show
  1. package/package.json +20 -86
  2. package/src/Entity.ts +6 -6
  3. package/src/FileRouterCodegen.ts +4 -4
  4. package/src/FileSystem.ts +4 -8
  5. package/src/RouteHook.ts +1 -1
  6. package/src/RouteSse.ts +3 -3
  7. package/src/SqlIntrospect.ts +2 -2
  8. package/src/Start.ts +102 -2
  9. package/src/Values.ts +11 -0
  10. package/src/bun/BunRoute.ts +1 -1
  11. package/src/bun/BunRuntime.ts +5 -5
  12. package/src/hyper/HyperHtml.ts +11 -7
  13. package/src/hyper/jsx.d.ts +1 -1
  14. package/src/lint/plugin.js +174 -4
  15. package/src/sql/SqlClient.ts +355 -0
  16. package/src/sql/bun/index.ts +117 -50
  17. package/src/sql/index.ts +1 -1
  18. package/src/sql/libsql/index.ts +91 -77
  19. package/src/sql/libsql/libsql.d.ts +4 -1
  20. package/src/sql/mssql/index.ts +141 -108
  21. package/src/sql/mssql/mssql.d.ts +1 -0
  22. package/src/testing/TestLogger.ts +4 -4
  23. package/src/x/tailwind/compile.ts +6 -14
  24. package/dist/ChildProcess.js +0 -42
  25. package/dist/Commander.js +0 -410
  26. package/dist/ContentNegotiation.js +0 -465
  27. package/dist/Cookies.js +0 -371
  28. package/dist/Development.js +0 -94
  29. package/dist/Effectify.js +0 -27
  30. package/dist/Entity.js +0 -289
  31. package/dist/Fetch.js +0 -192
  32. package/dist/FilePathPattern.js +0 -97
  33. package/dist/FileRouter.js +0 -204
  34. package/dist/FileRouterCodegen.js +0 -298
  35. package/dist/FileSystem.js +0 -132
  36. package/dist/Http.js +0 -107
  37. package/dist/PathPattern.js +0 -451
  38. package/dist/PlatformError.js +0 -40
  39. package/dist/PlatformRuntime.js +0 -71
  40. package/dist/Route.js +0 -143
  41. package/dist/RouteBody.js +0 -92
  42. package/dist/RouteError.js +0 -76
  43. package/dist/RouteHook.js +0 -64
  44. package/dist/RouteHttp.js +0 -367
  45. package/dist/RouteHttpTracer.js +0 -90
  46. package/dist/RouteMount.js +0 -86
  47. package/dist/RouteSchema.js +0 -271
  48. package/dist/RouteSse.js +0 -94
  49. package/dist/RouteTree.js +0 -119
  50. package/dist/RouteTrie.js +0 -179
  51. package/dist/SchemaExtra.js +0 -99
  52. package/dist/Socket.js +0 -40
  53. package/dist/SqlIntrospect.js +0 -515
  54. package/dist/Start.js +0 -79
  55. package/dist/StartApp.js +0 -3
  56. package/dist/StreamExtra.js +0 -135
  57. package/dist/System.js +0 -38
  58. package/dist/TuplePathPattern.js +0 -74
  59. package/dist/Unique.js +0 -226
  60. package/dist/Values.js +0 -52
  61. package/dist/bun/BunBundle.js +0 -186
  62. package/dist/bun/BunChildProcessSpawner.js +0 -142
  63. package/dist/bun/BunImportTrackerPlugin.js +0 -91
  64. package/dist/bun/BunRoute.js +0 -157
  65. package/dist/bun/BunRuntime.js +0 -41
  66. package/dist/bun/BunServer.js +0 -285
  67. package/dist/bun/BunVirtualFilesPlugin.js +0 -54
  68. package/dist/bun/_BunEnhancedResolve.js +0 -127
  69. package/dist/bun/index.js +0 -5
  70. package/dist/bundler/Bundle.js +0 -92
  71. package/dist/bundler/BundleFiles.js +0 -154
  72. package/dist/bundler/BundleRoute.js +0 -62
  73. package/dist/client/Overlay.js +0 -33
  74. package/dist/client/ScrollState.js +0 -106
  75. package/dist/client/index.js +0 -97
  76. package/dist/console/Console.js +0 -42
  77. package/dist/console/ConsoleErrors.js +0 -211
  78. package/dist/console/ConsoleLogger.js +0 -56
  79. package/dist/console/ConsoleMetrics.js +0 -72
  80. package/dist/console/ConsoleProcess.js +0 -59
  81. package/dist/console/ConsoleStore.js +0 -72
  82. package/dist/console/ConsoleTracer.js +0 -107
  83. package/dist/console/Simulation.js +0 -784
  84. package/dist/console/index.js +0 -3
  85. package/dist/console/routes/tree.js +0 -30
  86. package/dist/datastar/actions/fetch.js +0 -536
  87. package/dist/datastar/actions/peek.js +0 -13
  88. package/dist/datastar/actions/setAll.js +0 -19
  89. package/dist/datastar/actions/toggleAll.js +0 -19
  90. package/dist/datastar/attributes/attr.js +0 -49
  91. package/dist/datastar/attributes/bind.js +0 -194
  92. package/dist/datastar/attributes/class.js +0 -54
  93. package/dist/datastar/attributes/computed.js +0 -25
  94. package/dist/datastar/attributes/effect.js +0 -10
  95. package/dist/datastar/attributes/indicator.js +0 -33
  96. package/dist/datastar/attributes/init.js +0 -27
  97. package/dist/datastar/attributes/jsonSignals.js +0 -33
  98. package/dist/datastar/attributes/on.js +0 -81
  99. package/dist/datastar/attributes/onIntersect.js +0 -53
  100. package/dist/datastar/attributes/onInterval.js +0 -31
  101. package/dist/datastar/attributes/onSignalPatch.js +0 -51
  102. package/dist/datastar/attributes/ref.js +0 -11
  103. package/dist/datastar/attributes/show.js +0 -32
  104. package/dist/datastar/attributes/signals.js +0 -18
  105. package/dist/datastar/attributes/style.js +0 -57
  106. package/dist/datastar/attributes/text.js +0 -29
  107. package/dist/datastar/engine.js +0 -1145
  108. package/dist/datastar/index.js +0 -25
  109. package/dist/datastar/utils.js +0 -250
  110. package/dist/datastar/watchers/patchElements.js +0 -486
  111. package/dist/datastar/watchers/patchSignals.js +0 -14
  112. package/dist/experimental/EncryptedCookies.js +0 -328
  113. package/dist/experimental/index.js +0 -1
  114. package/dist/hyper/Hyper.js +0 -28
  115. package/dist/hyper/HyperHtml.js +0 -165
  116. package/dist/hyper/HyperNode.js +0 -13
  117. package/dist/hyper/HyperRoute.js +0 -45
  118. package/dist/hyper/html.js +0 -30
  119. package/dist/hyper/index.js +0 -5
  120. package/dist/hyper/jsx-runtime.js +0 -14
  121. package/dist/index.js +0 -8
  122. package/dist/node/NodeFileSystem.js +0 -675
  123. package/dist/node/NodeUtils.js +0 -23
  124. package/dist/sql/Sql.js +0 -8
  125. package/dist/sql/bun/index.js +0 -142
  126. package/dist/sql/index.js +0 -1
  127. package/dist/sql/libsql/index.js +0 -156
  128. package/dist/sql/mssql/docker.js +0 -110
  129. package/dist/sql/mssql/index.js +0 -194
  130. package/dist/testing/TestLogger.js +0 -42
  131. package/dist/testing/index.js +0 -2
  132. package/dist/testing/utils.js +0 -61
  133. package/dist/x/cloudflare/CloudflareTunnel.js +0 -63
  134. package/dist/x/cloudflare/index.js +0 -1
  135. package/dist/x/tailscale/TailscaleTunnel.js +0 -94
  136. package/dist/x/tailscale/index.js +0 -1
  137. package/dist/x/tailwind/TailwindPlugin.js +0 -294
  138. package/dist/x/tailwind/compile.js +0 -210
  139. package/dist/x/tailwind/plugin.js +0 -17
  140. package/src/console/Console.ts +0 -42
  141. package/src/console/ConsoleErrors.ts +0 -213
  142. package/src/console/ConsoleLogger.ts +0 -56
  143. package/src/console/ConsoleMetrics.ts +0 -72
  144. package/src/console/ConsoleProcess.ts +0 -59
  145. package/src/console/ConsoleStore.ts +0 -187
  146. package/src/console/ConsoleTracer.ts +0 -107
  147. package/src/console/Simulation.ts +0 -814
  148. package/src/console/console.html +0 -340
  149. package/src/console/index.ts +0 -3
  150. package/src/console/routes/errors/route.tsx +0 -97
  151. package/src/console/routes/fiberDetail.tsx +0 -54
  152. package/src/console/routes/fibers/route.tsx +0 -45
  153. package/src/console/routes/git/route.tsx +0 -64
  154. package/src/console/routes/layout.tsx +0 -4
  155. package/src/console/routes/logs/route.tsx +0 -77
  156. package/src/console/routes/metrics/route.tsx +0 -36
  157. package/src/console/routes/route.tsx +0 -8
  158. package/src/console/routes/routes/route.tsx +0 -30
  159. package/src/console/routes/services/route.tsx +0 -21
  160. package/src/console/routes/system/route.tsx +0 -43
  161. package/src/console/routes/traceDetail.tsx +0 -22
  162. package/src/console/routes/traces/route.tsx +0 -81
  163. package/src/console/routes/tree.ts +0 -30
  164. package/src/console/ui/Errors.tsx +0 -76
  165. package/src/console/ui/Fibers.tsx +0 -321
  166. package/src/console/ui/Git.tsx +0 -182
  167. package/src/console/ui/Logs.tsx +0 -46
  168. package/src/console/ui/Metrics.tsx +0 -78
  169. package/src/console/ui/Routes.tsx +0 -125
  170. package/src/console/ui/Services.tsx +0 -273
  171. package/src/console/ui/Shell.tsx +0 -62
  172. package/src/console/ui/System.tsx +0 -131
  173. package/src/console/ui/Traces.tsx +0 -426
  174. package/src/sql/Sql.ts +0 -51
@@ -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
- }