@livestore/utils-dev 0.4.0 → 0.5.0-dev.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/dist/.tsbuildinfo +1 -1
- package/dist/node/DockerCompose/DockerCompose.d.ts +52 -0
- package/dist/node/DockerCompose/DockerCompose.d.ts.map +1 -0
- package/dist/node/DockerCompose/DockerCompose.js +158 -0
- package/dist/node/DockerCompose/DockerCompose.js.map +1 -0
- package/dist/node/DockerCompose/DockerCompose.test.d.ts +2 -0
- package/dist/node/DockerCompose/DockerCompose.test.d.ts.map +1 -0
- package/dist/node/{DockerComposeService/DockerComposeService.test.js → DockerCompose/DockerCompose.test.js} +16 -16
- package/dist/node/DockerCompose/DockerCompose.test.js.map +1 -0
- package/dist/node/DockerCompose/mod.d.ts +2 -0
- package/dist/node/DockerCompose/mod.d.ts.map +1 -0
- package/dist/node/DockerCompose/mod.js +2 -0
- package/dist/node/DockerCompose/mod.js.map +1 -0
- package/dist/node/FileLogger.d.ts +15 -8
- package/dist/node/FileLogger.d.ts.map +1 -1
- package/dist/node/FileLogger.js +63 -79
- package/dist/node/FileLogger.js.map +1 -1
- package/dist/node/cmd-log.d.ts.map +1 -1
- package/dist/node/cmd-log.js +19 -8
- package/dist/node/cmd-log.js.map +1 -1
- package/dist/node/cmd.d.ts +10 -12
- package/dist/node/cmd.d.ts.map +1 -1
- package/dist/node/cmd.js +73 -36
- package/dist/node/cmd.js.map +1 -1
- package/dist/node/cmd.test.js +17 -17
- package/dist/node/cmd.test.js.map +1 -1
- package/dist/node/mod.d.ts +4 -5
- package/dist/node/mod.d.ts.map +1 -1
- package/dist/node/mod.js +58 -55
- package/dist/node/mod.js.map +1 -1
- package/dist/node/workspace.d.ts +2 -2
- package/dist/node/workspace.d.ts.map +1 -1
- package/dist/node/workspace.js +7 -7
- package/dist/node/workspace.js.map +1 -1
- package/dist/node-vitest/Vitest.d.ts +17 -8
- package/dist/node-vitest/Vitest.d.ts.map +1 -1
- package/dist/node-vitest/Vitest.js +5 -5
- package/dist/node-vitest/Vitest.js.map +1 -1
- package/dist/node-vitest/Vitest.test.d.ts +3 -5
- package/dist/node-vitest/Vitest.test.d.ts.map +1 -1
- package/dist/node-vitest/Vitest.test.js +14 -3
- package/dist/node-vitest/Vitest.test.js.map +1 -1
- package/dist/wrangler/WranglerDevServer.d.ts +15 -20
- package/dist/wrangler/WranglerDevServer.d.ts.map +1 -1
- package/dist/wrangler/WranglerDevServer.js +77 -71
- package/dist/wrangler/WranglerDevServer.js.map +1 -1
- package/dist/wrangler/WranglerDevServer.test.js +28 -28
- package/dist/wrangler/WranglerDevServer.test.js.map +1 -1
- package/dist/wrangler/mod.d.ts +1 -1
- package/dist/wrangler/mod.d.ts.map +1 -1
- package/dist/wrangler/mod.js +1 -1
- package/dist/wrangler/mod.js.map +1 -1
- package/package.json +19 -37
- package/src/node/{DockerComposeService/DockerComposeService.test.ts → DockerCompose/DockerCompose.test.ts} +18 -18
- package/src/node/DockerCompose/DockerCompose.ts +328 -0
- package/src/node/DockerCompose/mod.ts +1 -0
- package/src/node/FileLogger.ts +89 -114
- package/src/node/cmd-log.ts +21 -10
- package/src/node/cmd.test.ts +19 -17
- package/src/node/cmd.ts +117 -86
- package/src/node/mod.ts +112 -104
- package/src/node/workspace.ts +9 -10
- package/src/node-vitest/Vitest.test.ts +22 -3
- package/src/node-vitest/Vitest.ts +32 -42
- package/src/wrangler/WranglerDevServer.test.ts +33 -37
- package/src/wrangler/WranglerDevServer.ts +145 -129
- package/src/wrangler/mod.ts +1 -6
- package/dist/node/DockerComposeService/DockerComposeService.d.ts +0 -63
- package/dist/node/DockerComposeService/DockerComposeService.d.ts.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.js +0 -149
- package/dist/node/DockerComposeService/DockerComposeService.js.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.test.d.ts +0 -2
- package/dist/node/DockerComposeService/DockerComposeService.test.d.ts.map +0 -1
- package/dist/node/DockerComposeService/DockerComposeService.test.js.map +0 -1
- package/src/node/DockerComposeService/DockerComposeService.ts +0 -343
- /package/src/node/{DockerComposeService → DockerCompose}/test-fixtures/docker-compose.yml +0 -0
|
@@ -1,44 +1,40 @@
|
|
|
1
1
|
import { expect } from 'vitest'
|
|
2
2
|
|
|
3
3
|
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
4
|
-
import { Effect, FetchHttpClient, Layer } from '@livestore/utils/effect'
|
|
4
|
+
import { Effect, FetchHttpClient, Layer, Result } from '@livestore/utils/effect'
|
|
5
5
|
import { getFreePort, PlatformNode } from '@livestore/utils/node'
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
type StartWranglerDevServerArgs,
|
|
9
|
-
WranglerDevServerError,
|
|
10
|
-
WranglerDevServerService,
|
|
11
|
-
} from './WranglerDevServer.ts'
|
|
7
|
+
import * as WranglerDevServer from './WranglerDevServer.ts'
|
|
12
8
|
|
|
13
9
|
const testTimeout = 60_000
|
|
14
10
|
|
|
15
|
-
const WranglerDevServerTest = (args: Partial<
|
|
16
|
-
|
|
11
|
+
const WranglerDevServerTest = (args: Partial<WranglerDevServer.Options> = {}) =>
|
|
12
|
+
WranglerDevServer.layer({
|
|
17
13
|
cwd: `${import.meta.dirname}/fixtures`,
|
|
18
14
|
...args,
|
|
19
15
|
}).pipe(Layer.provide(FetchHttpClient.layer))
|
|
20
16
|
|
|
21
17
|
Vitest.describe('WranglerDevServer', { timeout: testTimeout }, () => {
|
|
22
18
|
Vitest.describe('Basic Operations', () => {
|
|
23
|
-
const withBasicTest = (args: Partial<
|
|
19
|
+
const withBasicTest = (args: Partial<WranglerDevServer.Options> = {}) =>
|
|
24
20
|
Vitest.makeWithTestCtx({
|
|
25
21
|
timeout: testTimeout,
|
|
26
|
-
makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.
|
|
22
|
+
makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeServices.layer)),
|
|
27
23
|
})
|
|
28
24
|
|
|
29
|
-
Vitest.
|
|
25
|
+
Vitest.live('should start wrangler dev server and return port', (test) =>
|
|
30
26
|
Effect.gen(function* () {
|
|
31
|
-
const server = yield*
|
|
27
|
+
const server = yield* WranglerDevServer.WranglerDevServer
|
|
32
28
|
|
|
33
29
|
expect(server.port).toBeGreaterThan(0)
|
|
34
30
|
expect(server.url).toMatch(/http:\/\/127.0.0.1:\d+/)
|
|
35
31
|
}).pipe(withBasicTest()(test)),
|
|
36
32
|
)
|
|
37
33
|
|
|
38
|
-
Vitest.
|
|
34
|
+
Vitest.live('should use specified port when provided', (test) =>
|
|
39
35
|
Effect.andThen(getFreePort, (port) =>
|
|
40
36
|
Effect.gen(function* () {
|
|
41
|
-
const server = yield*
|
|
37
|
+
const server = yield* WranglerDevServer.WranglerDevServer
|
|
42
38
|
|
|
43
39
|
expect(server.port).toBe(port)
|
|
44
40
|
expect(server.url).toBe(`http://127.0.0.1:${port}`)
|
|
@@ -48,83 +44,83 @@ Vitest.describe('WranglerDevServer', { timeout: testTimeout }, () => {
|
|
|
48
44
|
})
|
|
49
45
|
|
|
50
46
|
Vitest.describe('Error Handling', () => {
|
|
51
|
-
const withErrorTest = (args: Partial<
|
|
47
|
+
const withErrorTest = (args: Partial<WranglerDevServer.Options> = {}) =>
|
|
52
48
|
Vitest.makeWithTestCtx({
|
|
53
49
|
timeout: testTimeout,
|
|
54
|
-
makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.
|
|
50
|
+
makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeServices.layer)),
|
|
55
51
|
})
|
|
56
52
|
|
|
57
|
-
Vitest.
|
|
53
|
+
Vitest.live('should handle missing wrangler.toml but should timeout', (test) =>
|
|
58
54
|
Effect.gen(function* () {
|
|
59
|
-
const error = yield*
|
|
55
|
+
const error = yield* WranglerDevServer.WranglerDevServer.pipe(
|
|
60
56
|
Effect.provide(
|
|
61
57
|
WranglerDevServerTest({
|
|
62
58
|
cwd: '/tmp',
|
|
63
59
|
wranglerConfigPath: '/dev/null',
|
|
64
60
|
readiness: { connectTimeout: '500 millis' },
|
|
65
|
-
}).pipe(Layer.provide(PlatformNode.
|
|
61
|
+
}).pipe(Layer.provide(PlatformNode.NodeServices.layer)),
|
|
66
62
|
),
|
|
67
63
|
Effect.flip,
|
|
68
64
|
)
|
|
69
65
|
|
|
70
|
-
expect(error).toBeInstanceOf(WranglerDevServerError)
|
|
66
|
+
expect(error).toBeInstanceOf(WranglerDevServer.WranglerDevServerError)
|
|
71
67
|
}).pipe(Vitest.withTestCtx(test)),
|
|
72
68
|
)
|
|
73
69
|
|
|
74
|
-
Vitest.
|
|
70
|
+
Vitest.live('should handle invalid working directory', (test) =>
|
|
75
71
|
Effect.gen(function* () {
|
|
76
|
-
const result = yield*
|
|
72
|
+
const result = yield* WranglerDevServer.WranglerDevServer.pipe(
|
|
77
73
|
Effect.provide(
|
|
78
74
|
WranglerDevServerTest({
|
|
79
75
|
cwd: '/completely/nonexistent/directory',
|
|
80
|
-
}).pipe(Layer.provide(PlatformNode.
|
|
76
|
+
}).pipe(Layer.provide(PlatformNode.NodeServices.layer)),
|
|
81
77
|
),
|
|
82
|
-
Effect.
|
|
78
|
+
Effect.result,
|
|
83
79
|
)
|
|
84
80
|
|
|
85
|
-
expect(result._tag).toBe('
|
|
86
|
-
if (result._tag === '
|
|
87
|
-
expect(result.
|
|
81
|
+
expect(result._tag).toBe('Failure')
|
|
82
|
+
if (result._tag === 'Failure') {
|
|
83
|
+
expect(result.failure).toBeInstanceOf(WranglerDevServer.WranglerDevServerError)
|
|
88
84
|
}
|
|
89
85
|
}).pipe(Vitest.withTestCtx(test)),
|
|
90
86
|
)
|
|
91
87
|
|
|
92
|
-
Vitest.
|
|
88
|
+
Vitest.live('should timeout if server fails to start', (test) =>
|
|
93
89
|
Effect.gen(function* () {
|
|
94
90
|
// Create a command that will never output "Ready on"
|
|
95
|
-
const result = yield*
|
|
91
|
+
const result = yield* WranglerDevServer.WranglerDevServer.pipe(
|
|
96
92
|
// Override the timeout for this test to be shorter
|
|
97
93
|
Effect.timeout('5 seconds'),
|
|
98
|
-
Effect.
|
|
94
|
+
Effect.result,
|
|
99
95
|
)
|
|
100
96
|
|
|
101
97
|
// This might succeed or fail depending on actual wrangler behavior
|
|
102
98
|
// The main point is testing timeout functionality
|
|
103
|
-
expect(['
|
|
99
|
+
expect(['Failure', 'Success']).toContain(result._tag)
|
|
104
100
|
}).pipe(withErrorTest()(test)),
|
|
105
101
|
)
|
|
106
102
|
})
|
|
107
103
|
|
|
108
104
|
Vitest.describe('Service Pattern', () => {
|
|
109
|
-
const withServiceTest = (args: Partial<
|
|
105
|
+
const withServiceTest = (args: Partial<WranglerDevServer.Options> = {}) =>
|
|
110
106
|
Vitest.makeWithTestCtx({
|
|
111
107
|
timeout: testTimeout,
|
|
112
|
-
makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.
|
|
108
|
+
makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeServices.layer)),
|
|
113
109
|
})
|
|
114
110
|
|
|
115
|
-
Vitest.
|
|
111
|
+
Vitest.live('should work with service pattern', (test) =>
|
|
116
112
|
Effect.gen(function* () {
|
|
117
|
-
const server = yield*
|
|
113
|
+
const server = yield* WranglerDevServer.WranglerDevServer
|
|
118
114
|
|
|
119
115
|
expect(server.port).toBeGreaterThan(0)
|
|
120
116
|
expect(server.url).toMatch(/http:\/\/127.0.0.1:\d+/)
|
|
121
117
|
}).pipe(withServiceTest()(test)),
|
|
122
118
|
)
|
|
123
119
|
|
|
124
|
-
Vitest.
|
|
120
|
+
Vitest.live('should work with custom port via service', (test) =>
|
|
125
121
|
Effect.andThen(getFreePort, (port) =>
|
|
126
122
|
Effect.gen(function* () {
|
|
127
|
-
const server = yield*
|
|
123
|
+
const server = yield* WranglerDevServer.WranglerDevServer
|
|
128
124
|
|
|
129
125
|
expect(server.port).toBe(port)
|
|
130
126
|
expect(server.url).toBe(`http://127.0.0.1:${port}`)
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import * as path from 'node:path'
|
|
2
2
|
|
|
3
3
|
import * as Toml from '@iarna/toml'
|
|
4
|
+
import * as wrangler from 'wrangler'
|
|
5
|
+
|
|
4
6
|
import { IS_CI } from '@livestore/utils'
|
|
5
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Cause,
|
|
9
|
+
Context,
|
|
10
|
+
Duration,
|
|
11
|
+
Effect,
|
|
12
|
+
FileSystem,
|
|
13
|
+
HttpClient,
|
|
14
|
+
Layer,
|
|
15
|
+
Schedule,
|
|
16
|
+
Schema,
|
|
17
|
+
} from '@livestore/utils/effect'
|
|
6
18
|
import { getFreePort } from '@livestore/utils/node'
|
|
7
|
-
import * as wrangler from 'wrangler'
|
|
8
19
|
|
|
9
20
|
/**
|
|
10
21
|
* Error type for WranglerDevServer operations
|
|
11
22
|
*/
|
|
12
|
-
export class WranglerDevServerError extends Schema.TaggedError<WranglerDevServerError>(
|
|
13
|
-
|
|
23
|
+
export class WranglerDevServerError extends Schema.TaggedError<WranglerDevServerError>(
|
|
24
|
+
'~@livestore/utils-dev/WranglerDevServerError',
|
|
25
|
+
)('WranglerDevServerError', {
|
|
26
|
+
cause: Schema.Defect(),
|
|
14
27
|
message: Schema.String,
|
|
15
|
-
port: Schema.
|
|
28
|
+
port: Schema.Finite,
|
|
16
29
|
}) {}
|
|
17
30
|
|
|
18
31
|
/**
|
|
19
32
|
* WranglerDevServer instance interface
|
|
20
33
|
*/
|
|
21
|
-
export interface
|
|
34
|
+
export interface Service {
|
|
22
35
|
readonly port: number
|
|
23
36
|
readonly url: string
|
|
24
37
|
// readonly processId: number
|
|
@@ -34,14 +47,14 @@ export interface WranglerDevServer {
|
|
|
34
47
|
*/
|
|
35
48
|
export interface WranglerReadinessOptions {
|
|
36
49
|
/** Max time to wait for wrangler to report ready before retrying. */
|
|
37
|
-
startupTimeout?: Duration.
|
|
50
|
+
startupTimeout?: Duration.Input
|
|
38
51
|
/** Max time for the HTTP connectivity check after wrangler reports ready. */
|
|
39
|
-
connectTimeout?: Duration.
|
|
52
|
+
connectTimeout?: Duration.Input
|
|
40
53
|
/** Retry policy for startup attempts (applies when startupTimeout elapses or wrangler throws). */
|
|
41
54
|
retrySchedule?: Schedule.Schedule<unknown>
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
export interface
|
|
57
|
+
export interface Options {
|
|
45
58
|
/** Path to wrangler.toml (defaults to cwd/wrangler.toml). */
|
|
46
59
|
wranglerConfigPath?: string
|
|
47
60
|
/** Working directory wrangler should use. */
|
|
@@ -57,7 +70,7 @@ export interface StartWranglerDevServerArgs {
|
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
/**
|
|
60
|
-
* WranglerDevServer as an Effect.
|
|
73
|
+
* WranglerDevServer as an Effect service.
|
|
61
74
|
*
|
|
62
75
|
* This service provides the WranglerDevServer properties and can be accessed
|
|
63
76
|
* directly to get port and url.
|
|
@@ -65,118 +78,127 @@ export interface StartWranglerDevServerArgs {
|
|
|
65
78
|
* TODO: Allow for config to be passed in via code instead of `wrangler.toml` file
|
|
66
79
|
* (would need to be placed in temporary file as wrangler only accepts files as config)
|
|
67
80
|
*/
|
|
68
|
-
export class
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const showLogs = args.showLogs ?? false
|
|
72
|
-
|
|
73
|
-
// Allocate preferred port (Wrangler may bind a different one if unavailable)
|
|
74
|
-
const preferredPort =
|
|
75
|
-
args.preferredPort ??
|
|
76
|
-
(yield* getFreePort.pipe(
|
|
77
|
-
Effect.mapError(
|
|
78
|
-
(cause) => new WranglerDevServerError({ cause, message: 'Failed to get free port', port: -1 }),
|
|
79
|
-
),
|
|
80
|
-
))
|
|
81
|
-
|
|
82
|
-
yield* Effect.annotateCurrentSpan({ preferredPort })
|
|
83
|
-
|
|
84
|
-
const configPath = path.resolve(args.wranglerConfigPath ?? path.join(args.cwd, 'wrangler.toml'))
|
|
85
|
-
|
|
86
|
-
const fs = yield* FileSystem.FileSystem
|
|
87
|
-
const configContent = yield* fs.readFileString(configPath)
|
|
88
|
-
const parsedConfig = yield* Effect.try(() => Toml.parse(configContent)).pipe(
|
|
89
|
-
Effect.andThen(Schema.decodeUnknown(Schema.Struct({ main: Schema.String }))),
|
|
90
|
-
Effect.mapError(
|
|
91
|
-
(error) => new WranglerDevServerError({ cause: error, message: 'Failed to parse wrangler config', port: -1 }),
|
|
92
|
-
),
|
|
93
|
-
)
|
|
94
|
-
const resolvedMainPath = yield* Effect.try(() => path.resolve(args.cwd, parsedConfig.main))
|
|
95
|
-
|
|
96
|
-
const readiness = args.readiness ?? {}
|
|
97
|
-
const startupTimeout = readiness.startupTimeout ?? Duration.seconds(IS_CI === true ? 30 : 10)
|
|
98
|
-
const devServer = yield* Effect.promise(() =>
|
|
99
|
-
wrangler.unstable_dev(resolvedMainPath, {
|
|
100
|
-
config: configPath,
|
|
101
|
-
port: preferredPort,
|
|
102
|
-
inspectorPort: args.inspectorPort ?? 0,
|
|
103
|
-
persistTo: path.join(args.cwd, '.wrangler/state'),
|
|
104
|
-
logLevel: showLogs === true ? 'debug' : 'none',
|
|
105
|
-
experimental: {
|
|
106
|
-
disableExperimentalWarning: true,
|
|
107
|
-
},
|
|
108
|
-
}),
|
|
109
|
-
).pipe(
|
|
110
|
-
Effect.timeout(startupTimeout),
|
|
111
|
-
Effect.mapError(
|
|
112
|
-
(cause) =>
|
|
113
|
-
new WranglerDevServerError({
|
|
114
|
-
cause,
|
|
115
|
-
message: `Failed to start wrangler dev server within ${Duration.format(startupTimeout)}`,
|
|
116
|
-
port: preferredPort,
|
|
117
|
-
}),
|
|
118
|
-
),
|
|
119
|
-
Effect.tapError((error) =>
|
|
120
|
-
Effect.logError('Wrangler dev server failed to start', {
|
|
121
|
-
message: error.message,
|
|
122
|
-
preferredPort,
|
|
123
|
-
cwd: args.cwd,
|
|
124
|
-
}),
|
|
125
|
-
),
|
|
126
|
-
Effect.retry(readiness.retrySchedule ?? Schedule.recurs(1)),
|
|
127
|
-
)
|
|
81
|
+
export class WranglerDevServer extends Context.Service<WranglerDevServer, Service>()(
|
|
82
|
+
'@livestore/utils-dev/WranglerDevServer',
|
|
83
|
+
) {}
|
|
128
84
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (exit._tag === 'Failure' && Cause.isInterruptedOnly(exit.cause) === false) {
|
|
133
|
-
yield* Effect.logError('Closing wrangler dev server on failure', exit.cause)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
yield* Effect.tryPromise(async () => {
|
|
137
|
-
await devServer.stop()
|
|
138
|
-
// TODO investigate whether we need to wait until exit (see workers-sdk repo/talk to Cloudflare team)
|
|
139
|
-
// await devServer.waitUntilExit()
|
|
140
|
-
})
|
|
141
|
-
},
|
|
142
|
-
Effect.timeout('5 seconds'),
|
|
143
|
-
Effect.orDie,
|
|
144
|
-
Effect.tapCauseLogPretty,
|
|
145
|
-
Effect.withSpan('WranglerDevServerService:stopDevServer'),
|
|
146
|
-
),
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
const actualPort = devServer.port
|
|
150
|
-
const actualHost = devServer.address
|
|
151
|
-
const url = `http://${actualHost}:${actualPort}`
|
|
85
|
+
export const make = (args: Options) =>
|
|
86
|
+
Effect.gen(function* () {
|
|
87
|
+
const showLogs = args.showLogs ?? false
|
|
152
88
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
89
|
+
// Allocate preferred port (Wrangler may bind a different one if unavailable)
|
|
90
|
+
const preferredPort =
|
|
91
|
+
args.preferredPort ??
|
|
92
|
+
(yield* getFreePort.pipe(
|
|
93
|
+
Effect.mapError((cause) => new WranglerDevServerError({ cause, message: 'Failed to get free port', port: -1 })),
|
|
94
|
+
))
|
|
156
95
|
|
|
157
|
-
|
|
96
|
+
yield* Effect.annotateCurrentSpan({ preferredPort })
|
|
158
97
|
|
|
159
|
-
|
|
160
|
-
yield* Effect.logDebug(
|
|
161
|
-
`Wrangler dev server ready and accepting connections on port ${actualPort} (preferred: ${preferredPort})`,
|
|
162
|
-
)
|
|
163
|
-
}
|
|
98
|
+
const configPath = path.resolve(args.wranglerConfigPath ?? path.join(args.cwd, 'wrangler.toml'))
|
|
164
99
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
100
|
+
const fs = yield* FileSystem.FileSystem
|
|
101
|
+
const configContent = yield* fs.readFileString(configPath)
|
|
102
|
+
const parsedConfig = yield* Effect.try({
|
|
103
|
+
try: () => Toml.parse(configContent),
|
|
104
|
+
catch: (cause) => new Cause.UnknownError(cause),
|
|
169
105
|
}).pipe(
|
|
170
|
-
Effect.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
: new WranglerDevServerError({ cause: error, message: 'Failed to start wrangler dev server', port: -1 }),
|
|
106
|
+
Effect.andThen(Schema.decodeUnknownEffect(Schema.Struct({ main: Schema.String }))),
|
|
107
|
+
Effect.mapError(
|
|
108
|
+
(error) => new WranglerDevServerError({ cause: error, message: 'Failed to parse wrangler config', port: -1 }),
|
|
174
109
|
),
|
|
175
|
-
|
|
176
|
-
|
|
110
|
+
)
|
|
111
|
+
const resolvedMainPath = yield* Effect.try({
|
|
112
|
+
try: () => path.resolve(args.cwd, parsedConfig.main),
|
|
113
|
+
catch: (cause) => new Cause.UnknownError(cause),
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const readiness = args.readiness ?? {}
|
|
117
|
+
const startupTimeout = readiness.startupTimeout ?? Duration.seconds(IS_CI === true ? 30 : 10)
|
|
118
|
+
const startupTimeoutDuration = Duration.fromInputUnsafe(startupTimeout)
|
|
119
|
+
const devServer = yield* Effect.promise(() =>
|
|
120
|
+
wrangler.unstable_dev(resolvedMainPath, {
|
|
121
|
+
config: configPath,
|
|
122
|
+
port: preferredPort,
|
|
123
|
+
inspectorPort: args.inspectorPort ?? 0,
|
|
124
|
+
persistTo: path.join(args.cwd, '.wrangler/state'),
|
|
125
|
+
logLevel: showLogs === true ? 'debug' : 'none',
|
|
126
|
+
experimental: {
|
|
127
|
+
disableExperimentalWarning: true,
|
|
128
|
+
},
|
|
177
129
|
}),
|
|
130
|
+
).pipe(
|
|
131
|
+
Effect.timeout(startupTimeout),
|
|
132
|
+
Effect.mapError(
|
|
133
|
+
(cause) =>
|
|
134
|
+
new WranglerDevServerError({
|
|
135
|
+
cause,
|
|
136
|
+
message: `Failed to start wrangler dev server within ${Duration.format(startupTimeoutDuration)}`,
|
|
137
|
+
port: preferredPort,
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
Effect.tapError((error) =>
|
|
141
|
+
Effect.logError('Wrangler dev server failed to start', {
|
|
142
|
+
message: error.message,
|
|
143
|
+
preferredPort,
|
|
144
|
+
cwd: args.cwd,
|
|
145
|
+
}),
|
|
146
|
+
),
|
|
147
|
+
Effect.retry(readiness.retrySchedule ?? Schedule.recurs(1)),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
yield* Effect.addFinalizer(
|
|
151
|
+
Effect.fn(
|
|
152
|
+
function* (exit) {
|
|
153
|
+
if (exit._tag === 'Failure' && Cause.hasInterruptsOnly(exit.cause) === false) {
|
|
154
|
+
yield* Effect.logError('Closing wrangler dev server on failure', exit.cause)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
yield* Effect.tryPromise(async () => {
|
|
158
|
+
await devServer.stop()
|
|
159
|
+
// TODO investigate whether we need to wait until exit (see workers-sdk repo/talk to Cloudflare team)
|
|
160
|
+
// await devServer.waitUntilExit()
|
|
161
|
+
})
|
|
162
|
+
},
|
|
163
|
+
Effect.timeout('5 seconds'),
|
|
164
|
+
Effect.orDie,
|
|
165
|
+
Effect.tapCauseLogPretty,
|
|
166
|
+
Effect.withSpan('WranglerDevServer:stopDevServer'),
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
const actualPort = devServer.port
|
|
171
|
+
const actualHost = devServer.address
|
|
172
|
+
const url = `http://${actualHost}:${actualPort}`
|
|
173
|
+
|
|
174
|
+
// Use longer timeout in CI environments to account for slower HTTP readiness
|
|
175
|
+
const defaultConnectivityTimeout = Duration.seconds(IS_CI === true ? 30 : 5)
|
|
176
|
+
const connectivityTimeout = readiness.connectTimeout ?? defaultConnectivityTimeout
|
|
177
|
+
|
|
178
|
+
yield* verifyHttpConnectivity({ url, showLogs, connectTimeout: connectivityTimeout })
|
|
179
|
+
|
|
180
|
+
if (showLogs === true) {
|
|
181
|
+
yield* Effect.logDebug(
|
|
182
|
+
`Wrangler dev server ready and accepting connections on port ${actualPort} (preferred: ${preferredPort})`,
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return WranglerDevServer.of({
|
|
187
|
+
port: actualPort,
|
|
188
|
+
url,
|
|
189
|
+
})
|
|
190
|
+
}).pipe(
|
|
191
|
+
Effect.mapError((error) =>
|
|
192
|
+
error instanceof WranglerDevServerError
|
|
193
|
+
? error
|
|
194
|
+
: new WranglerDevServerError({ cause: error, message: 'Failed to start wrangler dev server', port: -1 }),
|
|
178
195
|
),
|
|
179
|
-
|
|
196
|
+
Effect.withSpan('WranglerDevServer', {
|
|
197
|
+
attributes: { preferredPort: args.preferredPort ?? 'auto', cwd: args.cwd },
|
|
198
|
+
}),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
export const layer = (options: Options) => Layer.effect(WranglerDevServer, make(options))
|
|
180
202
|
|
|
181
203
|
/**
|
|
182
204
|
* Verifies the server is actually accepting HTTP connections by making a test request
|
|
@@ -188,7 +210,7 @@ const verifyHttpConnectivity = ({
|
|
|
188
210
|
}: {
|
|
189
211
|
url: string
|
|
190
212
|
showLogs: boolean
|
|
191
|
-
connectTimeout: Duration.
|
|
213
|
+
connectTimeout: Duration.Input
|
|
192
214
|
}): Effect.Effect<void, WranglerDevServerError, HttpClient.HttpClient> =>
|
|
193
215
|
Effect.gen(function* () {
|
|
194
216
|
const client = yield* HttpClient.HttpClient
|
|
@@ -197,22 +219,16 @@ const verifyHttpConnectivity = ({
|
|
|
197
219
|
yield* Effect.logDebug(`Verifying HTTP connectivity to ${url}`)
|
|
198
220
|
}
|
|
199
221
|
|
|
200
|
-
// Try to connect with retries using exponential backoff
|
|
201
222
|
yield* client.get(url).pipe(
|
|
202
|
-
Effect.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
cause: error,
|
|
212
|
-
message: `Failed to establish HTTP connection to Wrangler server at ${url} after ${attemptCount} attempts (timeout: ${Duration.toMillis(connectTimeout)}ms)`,
|
|
213
|
-
port: 0,
|
|
214
|
-
}),
|
|
215
|
-
),
|
|
223
|
+
Effect.retry(Schedule.exponential('50 millis', 2).pipe(Schedule.jittered)),
|
|
224
|
+
Effect.timeout(connectTimeout),
|
|
225
|
+
Effect.mapError(
|
|
226
|
+
(error) =>
|
|
227
|
+
new WranglerDevServerError({
|
|
228
|
+
cause: error,
|
|
229
|
+
message: `Failed to establish HTTP connection to Wrangler server at ${url} (timeout: ${Duration.toMillis(Duration.fromInputUnsafe(connectTimeout))}ms)`,
|
|
230
|
+
port: 0,
|
|
231
|
+
}),
|
|
216
232
|
),
|
|
217
233
|
Effect.tap(() => (showLogs === true ? Effect.logDebug(`HTTP connectivity verified for ${url}`) : Effect.void)),
|
|
218
234
|
Effect.asVoid,
|
package/src/wrangler/mod.ts
CHANGED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { type CommandExecutor, Duration, Effect, type PlatformError, Schema, type Scope, Stream } from '@livestore/utils/effect';
|
|
2
|
-
declare const DockerComposeError_base: Schema.TaggedErrorClass<DockerComposeError, "DockerComposeError", {
|
|
3
|
-
readonly _tag: Schema.tag<"DockerComposeError">;
|
|
4
|
-
} & {
|
|
5
|
-
cause: typeof Schema.Defect;
|
|
6
|
-
note: typeof Schema.String;
|
|
7
|
-
}>;
|
|
8
|
-
export declare class DockerComposeError extends DockerComposeError_base {
|
|
9
|
-
}
|
|
10
|
-
export interface DockerComposeArgs {
|
|
11
|
-
readonly cwd: string;
|
|
12
|
-
readonly serviceName?: string;
|
|
13
|
-
/** Unique project name to isolate this compose instance. If not provided, a random one is generated. */
|
|
14
|
-
readonly projectName?: string;
|
|
15
|
-
}
|
|
16
|
-
export interface StartOptions {
|
|
17
|
-
readonly detached?: boolean;
|
|
18
|
-
readonly env?: Record<string, string>;
|
|
19
|
-
readonly healthCheck?: {
|
|
20
|
-
readonly url: string;
|
|
21
|
-
readonly timeout?: Duration.Duration;
|
|
22
|
-
readonly interval?: Duration.Duration;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
export interface LogsOptions {
|
|
26
|
-
readonly follow?: boolean;
|
|
27
|
-
readonly tail?: number;
|
|
28
|
-
readonly since?: string;
|
|
29
|
-
}
|
|
30
|
-
export interface DockerComposeOperations {
|
|
31
|
-
readonly pull: Effect.Effect<void, DockerComposeError | PlatformError.PlatformError>;
|
|
32
|
-
readonly start: (options?: StartOptions) => Effect.Effect<void, DockerComposeError | PlatformError.PlatformError, Scope.Scope>;
|
|
33
|
-
readonly stop: Effect.Effect<void, DockerComposeError | PlatformError.PlatformError>;
|
|
34
|
-
readonly down: (options?: {
|
|
35
|
-
readonly env?: Record<string, string>;
|
|
36
|
-
readonly volumes?: boolean;
|
|
37
|
-
readonly removeOrphans?: boolean;
|
|
38
|
-
}) => Effect.Effect<void, DockerComposeError | PlatformError.PlatformError>;
|
|
39
|
-
readonly logs: (options?: LogsOptions) => Stream.Stream<string, DockerComposeError | PlatformError.PlatformError, Scope.Scope>;
|
|
40
|
-
/** The unique project name used to isolate this compose instance */
|
|
41
|
-
readonly projectName: string;
|
|
42
|
-
}
|
|
43
|
-
declare const DockerComposeService_base: Effect.Service.Class<DockerComposeService, "DockerComposeService", {
|
|
44
|
-
readonly scoped: (args: DockerComposeArgs) => Effect.Effect<{
|
|
45
|
-
pull: Effect.Effect<undefined, PlatformError.PlatformError | DockerComposeError, never>;
|
|
46
|
-
start: (options?: StartOptions | undefined) => Effect.Effect<void, PlatformError.PlatformError | DockerComposeError, Scope.Scope>;
|
|
47
|
-
stop: Effect.Effect<void, PlatformError.PlatformError | DockerComposeError, never>;
|
|
48
|
-
down: (options?: {
|
|
49
|
-
readonly env?: Record<string, string>;
|
|
50
|
-
readonly volumes?: boolean;
|
|
51
|
-
readonly removeOrphans?: boolean;
|
|
52
|
-
} | undefined) => Effect.Effect<void, PlatformError.PlatformError | DockerComposeError, never>;
|
|
53
|
-
logs: (options?: LogsOptions) => Stream.Stream<string, DockerComposeError, never>;
|
|
54
|
-
projectName: string;
|
|
55
|
-
}, never, Scope.Scope | CommandExecutor.CommandExecutor>;
|
|
56
|
-
}>;
|
|
57
|
-
export declare class DockerComposeService extends DockerComposeService_base {
|
|
58
|
-
}
|
|
59
|
-
export declare const startDockerComposeServicesScoped: (args: DockerComposeArgs & {
|
|
60
|
-
healthCheck?: StartOptions["healthCheck"];
|
|
61
|
-
}) => Effect.Effect<void, DockerComposeError | PlatformError.PlatformError, DockerComposeService | CommandExecutor.CommandExecutor | Scope.Scope>;
|
|
62
|
-
export {};
|
|
63
|
-
//# sourceMappingURL=DockerComposeService.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DockerComposeService.d.ts","sourceRoot":"","sources":["../../../src/node/DockerComposeService/DockerComposeService.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,eAAe,EACpB,QAAQ,EACR,MAAM,EAEN,KAAK,aAAa,EAElB,MAAM,EACN,KAAK,KAAK,EACV,MAAM,EACP,MAAM,yBAAyB,CAAA;;;;;;;AAEhC,qBAAa,kBAAmB,SAAQ,uBAGtC;CAAG;AAEL,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,wGAAwG;IACxG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;QACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;QACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAA;KACtC,CAAA;CACF;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC,aAAa,CAAC,CAAA;IACpF,QAAQ,CAAC,KAAK,EAAE,CACd,OAAO,CAAC,EAAE,YAAY,KACnB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC,aAAa,CAAC,CAAA;IACpF,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;QAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;KACjC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC,aAAa,CAAC,CAAA;IAC3E,QAAQ,CAAC,IAAI,EAAE,CACb,OAAO,CAAC,EAAE,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACzF,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;;4BAKgB,iBAAiB;;;;;2BAiLX,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;+BAClB,OAAO;qCACD,OAAO;;yBAvCX,WAAW;;;;AA7IxC,qBAAa,oBAAqB,SAAQ,yBA6NxC;CAAG;AAwCL,eAAO,MAAM,gCAAgC,GAC3C,MAAM,iBAAiB,GAAG;IACxB,WAAW,CAAC,EAAE,YAAY,CAAC,aAAa,CAAC,CAAA;CAC1C,KACA,MAAM,CAAC,MAAM,CACd,IAAI,EACJ,kBAAkB,GAAG,aAAa,CAAC,aAAa,EAChD,oBAAoB,GAAG,eAAe,CAAC,eAAe,GAAG,KAAK,CAAC,KAAK,CAYlE,CAAA"}
|