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.
- package/package.json +18 -86
- package/dist/ChildProcess.js +0 -42
- package/dist/Commander.js +0 -410
- package/dist/ContentNegotiation.js +0 -465
- package/dist/Cookies.js +0 -371
- package/dist/Development.js +0 -94
- package/dist/Effectify.js +0 -27
- package/dist/Entity.js +0 -289
- package/dist/Fetch.js +0 -192
- package/dist/FilePathPattern.js +0 -97
- package/dist/FileRouter.js +0 -204
- package/dist/FileRouterCodegen.js +0 -298
- package/dist/FileSystem.js +0 -132
- package/dist/Http.js +0 -107
- package/dist/PathPattern.js +0 -451
- package/dist/PlatformError.js +0 -40
- package/dist/PlatformRuntime.js +0 -71
- package/dist/Route.js +0 -143
- package/dist/RouteBody.js +0 -92
- package/dist/RouteError.js +0 -76
- package/dist/RouteHook.js +0 -64
- package/dist/RouteHttp.js +0 -367
- package/dist/RouteHttpTracer.js +0 -90
- package/dist/RouteMount.js +0 -86
- package/dist/RouteSchema.js +0 -271
- package/dist/RouteSse.js +0 -94
- package/dist/RouteTree.js +0 -119
- package/dist/RouteTrie.js +0 -179
- package/dist/SchemaExtra.js +0 -99
- package/dist/Socket.js +0 -40
- package/dist/SqlIntrospect.js +0 -515
- package/dist/Start.js +0 -79
- package/dist/StartApp.js +0 -3
- package/dist/StreamExtra.js +0 -135
- package/dist/System.js +0 -38
- package/dist/TuplePathPattern.js +0 -74
- package/dist/Unique.js +0 -226
- package/dist/Values.js +0 -52
- package/dist/bun/BunBundle.js +0 -186
- package/dist/bun/BunChildProcessSpawner.js +0 -142
- package/dist/bun/BunImportTrackerPlugin.js +0 -91
- package/dist/bun/BunRoute.js +0 -157
- package/dist/bun/BunRuntime.js +0 -41
- package/dist/bun/BunServer.js +0 -285
- package/dist/bun/BunVirtualFilesPlugin.js +0 -54
- package/dist/bun/_BunEnhancedResolve.js +0 -127
- package/dist/bun/index.js +0 -5
- package/dist/bundler/Bundle.js +0 -92
- package/dist/bundler/BundleFiles.js +0 -154
- package/dist/bundler/BundleRoute.js +0 -62
- package/dist/client/Overlay.js +0 -33
- package/dist/client/ScrollState.js +0 -106
- package/dist/client/index.js +0 -97
- package/dist/console/Console.js +0 -42
- package/dist/console/ConsoleErrors.js +0 -211
- package/dist/console/ConsoleLogger.js +0 -56
- package/dist/console/ConsoleMetrics.js +0 -72
- package/dist/console/ConsoleProcess.js +0 -59
- package/dist/console/ConsoleStore.js +0 -72
- package/dist/console/ConsoleTracer.js +0 -107
- package/dist/console/Simulation.js +0 -784
- package/dist/console/index.js +0 -3
- package/dist/console/routes/tree.js +0 -30
- package/dist/datastar/actions/fetch.js +0 -536
- package/dist/datastar/actions/peek.js +0 -13
- package/dist/datastar/actions/setAll.js +0 -19
- package/dist/datastar/actions/toggleAll.js +0 -19
- package/dist/datastar/attributes/attr.js +0 -49
- package/dist/datastar/attributes/bind.js +0 -194
- package/dist/datastar/attributes/class.js +0 -54
- package/dist/datastar/attributes/computed.js +0 -25
- package/dist/datastar/attributes/effect.js +0 -10
- package/dist/datastar/attributes/indicator.js +0 -33
- package/dist/datastar/attributes/init.js +0 -27
- package/dist/datastar/attributes/jsonSignals.js +0 -33
- package/dist/datastar/attributes/on.js +0 -81
- package/dist/datastar/attributes/onIntersect.js +0 -53
- package/dist/datastar/attributes/onInterval.js +0 -31
- package/dist/datastar/attributes/onSignalPatch.js +0 -51
- package/dist/datastar/attributes/ref.js +0 -11
- package/dist/datastar/attributes/show.js +0 -32
- package/dist/datastar/attributes/signals.js +0 -18
- package/dist/datastar/attributes/style.js +0 -57
- package/dist/datastar/attributes/text.js +0 -29
- package/dist/datastar/engine.js +0 -1145
- package/dist/datastar/index.js +0 -25
- package/dist/datastar/utils.js +0 -250
- package/dist/datastar/watchers/patchElements.js +0 -486
- package/dist/datastar/watchers/patchSignals.js +0 -14
- package/dist/experimental/EncryptedCookies.js +0 -328
- package/dist/experimental/index.js +0 -1
- package/dist/hyper/Hyper.js +0 -28
- package/dist/hyper/HyperHtml.js +0 -165
- package/dist/hyper/HyperNode.js +0 -13
- package/dist/hyper/HyperRoute.js +0 -45
- package/dist/hyper/html.js +0 -30
- package/dist/hyper/index.js +0 -5
- package/dist/hyper/jsx-runtime.js +0 -14
- package/dist/index.js +0 -8
- package/dist/node/NodeFileSystem.js +0 -675
- package/dist/node/NodeUtils.js +0 -23
- package/dist/sql/Sql.js +0 -8
- package/dist/sql/bun/index.js +0 -142
- package/dist/sql/index.js +0 -1
- package/dist/sql/libsql/index.js +0 -156
- package/dist/sql/mssql/docker.js +0 -110
- package/dist/sql/mssql/index.js +0 -194
- package/dist/testing/TestLogger.js +0 -42
- package/dist/testing/index.js +0 -2
- package/dist/testing/utils.js +0 -61
- package/dist/x/cloudflare/CloudflareTunnel.js +0 -63
- package/dist/x/cloudflare/index.js +0 -1
- package/dist/x/tailscale/TailscaleTunnel.js +0 -94
- package/dist/x/tailscale/index.js +0 -1
- package/dist/x/tailwind/TailwindPlugin.js +0 -294
- package/dist/x/tailwind/compile.js +0 -210
- package/dist/x/tailwind/plugin.js +0 -17
package/dist/FileRouter.js
DELETED
|
@@ -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
|
-
}
|
package/dist/FileSystem.js
DELETED
|
@@ -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
|
-
}
|