@livestore/utils-dev 0.4.0-dev.7 → 0.4.0-dev.9

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 (68) hide show
  1. package/dist/.tsbuildinfo.json +1 -1
  2. package/dist/node/DockerComposeService/DockerComposeService.d.ts +12 -2
  3. package/dist/node/DockerComposeService/DockerComposeService.d.ts.map +1 -1
  4. package/dist/node/DockerComposeService/DockerComposeService.js +54 -17
  5. package/dist/node/DockerComposeService/DockerComposeService.js.map +1 -1
  6. package/dist/node/mod.d.ts +0 -2
  7. package/dist/node/mod.d.ts.map +1 -1
  8. package/dist/node/mod.js +0 -2
  9. package/dist/node/mod.js.map +1 -1
  10. package/dist/node-vitest/Vitest.d.ts +6 -6
  11. package/dist/node-vitest/Vitest.d.ts.map +1 -1
  12. package/dist/node-vitest/Vitest.js +2 -2
  13. package/dist/node-vitest/Vitest.js.map +1 -1
  14. package/dist/node-vitest/Vitest.test.js +12 -1
  15. package/dist/node-vitest/Vitest.test.js.map +1 -1
  16. package/dist/{node/WranglerDevServer → wrangler}/WranglerDevServer.d.ts +6 -6
  17. package/dist/wrangler/WranglerDevServer.d.ts.map +1 -0
  18. package/dist/wrangler/WranglerDevServer.js +90 -0
  19. package/dist/wrangler/WranglerDevServer.js.map +1 -0
  20. package/dist/wrangler/WranglerDevServer.test.d.ts.map +1 -0
  21. package/dist/wrangler/WranglerDevServer.test.js +77 -0
  22. package/dist/wrangler/WranglerDevServer.test.js.map +1 -0
  23. package/dist/wrangler/fixtures/cf-worker.d.ts.map +1 -0
  24. package/dist/wrangler/fixtures/cf-worker.js.map +1 -0
  25. package/dist/wrangler/mod.d.ts +2 -0
  26. package/dist/wrangler/mod.d.ts.map +1 -0
  27. package/dist/wrangler/mod.js +2 -0
  28. package/dist/wrangler/mod.js.map +1 -0
  29. package/package.json +7 -4
  30. package/src/node/DockerComposeService/DockerComposeService.ts +99 -23
  31. package/src/node/mod.ts +0 -7
  32. package/src/node-vitest/Vitest.test.ts +12 -1
  33. package/src/node-vitest/Vitest.ts +31 -19
  34. package/src/wrangler/WranglerDevServer.test.ts +133 -0
  35. package/src/wrangler/WranglerDevServer.ts +180 -0
  36. package/src/wrangler/mod.ts +6 -0
  37. package/dist/node/WranglerDevServer/WranglerDevServer.d.ts.map +0 -1
  38. package/dist/node/WranglerDevServer/WranglerDevServer.js +0 -122
  39. package/dist/node/WranglerDevServer/WranglerDevServer.js.map +0 -1
  40. package/dist/node/WranglerDevServer/WranglerDevServer.test.d.ts.map +0 -1
  41. package/dist/node/WranglerDevServer/WranglerDevServer.test.js +0 -179
  42. package/dist/node/WranglerDevServer/WranglerDevServer.test.js.map +0 -1
  43. package/dist/node/WranglerDevServer/fixtures/cf-worker.d.ts.map +0 -1
  44. package/dist/node/WranglerDevServer/fixtures/cf-worker.js.map +0 -1
  45. package/dist/node/WranglerDevServer/process-tree-manager.d.ts +0 -55
  46. package/dist/node/WranglerDevServer/process-tree-manager.d.ts.map +0 -1
  47. package/dist/node/WranglerDevServer/process-tree-manager.js +0 -178
  48. package/dist/node/WranglerDevServer/process-tree-manager.js.map +0 -1
  49. package/dist/node/vitest-docker-compose-setup.d.ts +0 -32
  50. package/dist/node/vitest-docker-compose-setup.d.ts.map +0 -1
  51. package/dist/node/vitest-docker-compose-setup.js +0 -131
  52. package/dist/node/vitest-docker-compose-setup.js.map +0 -1
  53. package/dist/node/vitest-wrangler-setup.d.ts +0 -27
  54. package/dist/node/vitest-wrangler-setup.d.ts.map +0 -1
  55. package/dist/node/vitest-wrangler-setup.js +0 -96
  56. package/dist/node/vitest-wrangler-setup.js.map +0 -1
  57. package/dist/node-vitest/polyfill.d.ts +0 -2
  58. package/dist/node-vitest/polyfill.d.ts.map +0 -1
  59. package/dist/node-vitest/polyfill.js +0 -3
  60. package/dist/node-vitest/polyfill.js.map +0 -1
  61. package/src/node/WranglerDevServer/WranglerDevServer.test.ts +0 -266
  62. package/src/node/WranglerDevServer/WranglerDevServer.ts +0 -266
  63. package/src/node/WranglerDevServer/process-tree-manager.ts +0 -263
  64. /package/dist/{node/WranglerDevServer → wrangler}/WranglerDevServer.test.d.ts +0 -0
  65. /package/dist/{node/WranglerDevServer → wrangler}/fixtures/cf-worker.d.ts +0 -0
  66. /package/dist/{node/WranglerDevServer → wrangler}/fixtures/cf-worker.js +0 -0
  67. /package/src/{node/WranglerDevServer → wrangler}/fixtures/cf-worker.ts +0 -0
  68. /package/src/{node/WranglerDevServer → wrangler}/fixtures/wrangler.toml +0 -0
@@ -0,0 +1,77 @@
1
+ import { Effect, FetchHttpClient, Layer } from '@livestore/utils/effect';
2
+ import { getFreePort, PlatformNode } from '@livestore/utils/node';
3
+ import { Vitest } from '@livestore/utils-dev/node-vitest';
4
+ import { expect } from 'vitest';
5
+ import { WranglerDevServerError, WranglerDevServerService, } from "./WranglerDevServer.js";
6
+ const testTimeout = 60_000;
7
+ const WranglerDevServerTest = (args = {}) => WranglerDevServerService.Default({
8
+ cwd: `${import.meta.dirname}/fixtures`,
9
+ ...args,
10
+ }).pipe(Layer.provide(FetchHttpClient.layer));
11
+ Vitest.describe('WranglerDevServer', { timeout: testTimeout }, () => {
12
+ Vitest.describe('Basic Operations', () => {
13
+ const withBasicTest = (args = {}) => Vitest.makeWithTestCtx({
14
+ timeout: testTimeout,
15
+ makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
16
+ });
17
+ Vitest.scopedLive('should start wrangler dev server and return port', (test) => Effect.gen(function* () {
18
+ const server = yield* WranglerDevServerService;
19
+ expect(server.port).toBeGreaterThan(0);
20
+ expect(server.url).toMatch(/http:\/\/127.0.0.1:\d+/);
21
+ }).pipe(withBasicTest()(test)));
22
+ Vitest.scopedLive('should use specified port when provided', (test) => Effect.andThen(getFreePort, (port) => Effect.gen(function* () {
23
+ const server = yield* WranglerDevServerService;
24
+ expect(server.port).toBe(port);
25
+ expect(server.url).toBe(`http://127.0.0.1:${port}`);
26
+ }).pipe(withBasicTest({ preferredPort: port })(test))));
27
+ });
28
+ Vitest.describe('Error Handling', () => {
29
+ const withErrorTest = (args = {}) => Vitest.makeWithTestCtx({
30
+ timeout: testTimeout,
31
+ makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
32
+ });
33
+ Vitest.scopedLive('should handle missing wrangler.toml but should timeout', (test) => Effect.gen(function* () {
34
+ const error = yield* WranglerDevServerService.pipe(Effect.provide(WranglerDevServerTest({
35
+ cwd: '/tmp',
36
+ wranglerConfigPath: '/dev/null',
37
+ connectTimeout: '500 millis',
38
+ }).pipe(Layer.provide(PlatformNode.NodeContext.layer))), Effect.flip);
39
+ expect(error).toBeInstanceOf(WranglerDevServerError);
40
+ }).pipe(Vitest.withTestCtx(test)));
41
+ Vitest.scopedLive('should handle invalid working directory', (test) => Effect.gen(function* () {
42
+ const result = yield* WranglerDevServerService.pipe(Effect.provide(WranglerDevServerTest({
43
+ cwd: '/completely/nonexistent/directory',
44
+ }).pipe(Layer.provide(PlatformNode.NodeContext.layer))), Effect.either);
45
+ expect(result._tag).toBe('Left');
46
+ if (result._tag === 'Left') {
47
+ expect(result.left).toBeInstanceOf(WranglerDevServerError);
48
+ }
49
+ }).pipe(Vitest.withTestCtx(test)));
50
+ Vitest.scopedLive('should timeout if server fails to start', (test) => Effect.gen(function* () {
51
+ // Create a command that will never output "Ready on"
52
+ const result = yield* WranglerDevServerService.pipe(
53
+ // Override the timeout for this test to be shorter
54
+ Effect.timeout('5 seconds'), Effect.either);
55
+ // This might succeed or fail depending on actual wrangler behavior
56
+ // The main point is testing timeout functionality
57
+ expect(['Left', 'Right']).toContain(result._tag);
58
+ }).pipe(withErrorTest()(test)));
59
+ });
60
+ Vitest.describe('Service Pattern', () => {
61
+ const withServiceTest = (args = {}) => Vitest.makeWithTestCtx({
62
+ timeout: testTimeout,
63
+ makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
64
+ });
65
+ Vitest.scopedLive('should work with service pattern', (test) => Effect.gen(function* () {
66
+ const server = yield* WranglerDevServerService;
67
+ expect(server.port).toBeGreaterThan(0);
68
+ expect(server.url).toMatch(/http:\/\/127.0.0.1:\d+/);
69
+ }).pipe(withServiceTest()(test)));
70
+ Vitest.scopedLive('should work with custom port via service', (test) => Effect.andThen(getFreePort, (port) => Effect.gen(function* () {
71
+ const server = yield* WranglerDevServerService;
72
+ expect(server.port).toBe(port);
73
+ expect(server.url).toBe(`http://127.0.0.1:${port}`);
74
+ }).pipe(withServiceTest({ preferredPort: port })(test))));
75
+ });
76
+ });
77
+ //# sourceMappingURL=WranglerDevServer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WranglerDevServer.test.js","sourceRoot":"","sources":["../../src/wrangler/WranglerDevServer.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAEL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,wBAAwB,CAAA;AAE/B,MAAM,WAAW,GAAG,MAAM,CAAA;AAE1B,MAAM,qBAAqB,GAAG,CAAC,OAA4C,EAAE,EAAE,EAAE,CAC/E,wBAAwB,CAAC,OAAO,CAAC;IAC/B,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,WAAW;IACtC,GAAG,IAAI;CACR,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAA;AAE/C,MAAM,CAAC,QAAQ,CAAC,mBAAmB,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE;IAClE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACvC,MAAM,aAAa,GAAG,CAAC,OAA4C,EAAE,EAAE,EAAE,CACvE,MAAM,CAAC,eAAe,CAAC;YACrB,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SACjG,CAAC,CAAA;QAEJ,MAAM,CAAC,UAAU,CAAC,kDAAkD,EAAE,CAAC,IAAI,EAAE,EAAE,CAC7E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAA;YAE9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;YACtC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAA;QACtD,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,CAC/B,CAAA;QAED,MAAM,CAAC,UAAU,CAAC,yCAAyC,EAAE,CAAC,IAAI,EAAE,EAAE,CACpE,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CACnC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAA;YAE9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC9B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAA;QACrD,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CACtD,CACF,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QACrC,MAAM,aAAa,GAAG,CAAC,OAA4C,EAAE,EAAE,EAAE,CACvE,MAAM,CAAC,eAAe,CAAC;YACrB,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SACjG,CAAC,CAAA;QAEJ,MAAM,CAAC,UAAU,CAAC,wDAAwD,EAAE,CAAC,IAAI,EAAE,EAAE,CACnF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAC,IAAI,CAChD,MAAM,CAAC,OAAO,CACZ,qBAAqB,CAAC;gBACpB,GAAG,EAAE,MAAM;gBACX,kBAAkB,EAAE,WAAW;gBAC/B,cAAc,EAAE,YAAY;aAC7B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CACvD,EACD,MAAM,CAAC,IAAI,CACZ,CAAA;YAED,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAA;QACtD,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAClC,CAAA;QAED,MAAM,CAAC,UAAU,CAAC,yCAAyC,EAAE,CAAC,IAAI,EAAE,EAAE,CACpE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAC,IAAI,CACjD,MAAM,CAAC,OAAO,CACZ,qBAAqB,CAAC;gBACpB,GAAG,EAAE,mCAAmC;aACzC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CACvD,EACD,MAAM,CAAC,MAAM,CACd,CAAA;YAED,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAChC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC3B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,sBAAsB,CAAC,CAAA;YAC5D,CAAC;QACH,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAClC,CAAA;QAED,MAAM,CAAC,UAAU,CAAC,yCAAyC,EAAE,CAAC,IAAI,EAAE,EAAE,CACpE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,qDAAqD;YACrD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAC,IAAI;YACjD,mDAAmD;YACnD,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAC3B,MAAM,CAAC,MAAM,CACd,CAAA;YAED,mEAAmE;YACnE,kDAAkD;YAClD,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAClD,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,CAC/B,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QACtC,MAAM,eAAe,GAAG,CAAC,OAA4C,EAAE,EAAE,EAAE,CACzE,MAAM,CAAC,eAAe,CAAC;YACrB,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,GAAG,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;SACjG,CAAC,CAAA;QAEJ,MAAM,CAAC,UAAU,CAAC,kCAAkC,EAAE,CAAC,IAAI,EAAE,EAAE,CAC7D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAA;YAE9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;YACtC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAA;QACtD,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC,CACjC,CAAA;QAED,MAAM,CAAC,UAAU,CAAC,0CAA0C,EAAE,CAAC,IAAI,EAAE,EAAE,CACrE,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CACnC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,wBAAwB,CAAA;YAE9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC9B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAA;QACrD,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CACxD,CACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cf-worker.d.ts","sourceRoot":"","sources":["../../../src/wrangler/fixtures/cf-worker.ts"],"names":[],"mappings":";oBACwB,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;;AADnD,wBAIC;AAED,qBAAa,MAAM;IACX,KAAK,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAGlD"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cf-worker.js","sourceRoot":"","sources":["../../../src/wrangler/fixtures/cf-worker.ts"],"names":[],"mappings":"AAAA,eAAe;IACb,KAAK,CAAC,KAAK,CAAC,QAAiB;QAC3B,OAAO,IAAI,QAAQ,CAAC,6CAA6C,CAAC,CAAA;IACpE,CAAC;CACF,CAAA;AAED,MAAM,OAAO,MAAM;IACjB,KAAK,CAAC,KAAK,CAAC,QAAiB;QAC3B,OAAO,IAAI,QAAQ,CAAC,iCAAiC,CAAC,CAAA;IACxD,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export { type StartWranglerDevServerArgs, type WranglerDevServer, WranglerDevServerError, WranglerDevServerService, } from './WranglerDevServer.ts';
2
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/wrangler/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,EACtB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { WranglerDevServerError, WranglerDevServerService, } from "./WranglerDevServer.js";
2
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../../src/wrangler/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,wBAAwB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livestore/utils-dev",
3
- "version": "0.4.0-dev.7",
3
+ "version": "0.4.0-dev.9",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "./src/node-vitest/global.ts",
@@ -8,18 +8,21 @@
8
8
  ],
9
9
  "exports": {
10
10
  "./node": "./dist/node/mod.js",
11
- "./node-vitest": "./dist/node-vitest/mod.js"
11
+ "./node-vitest": "./dist/node-vitest/mod.js",
12
+ "./wrangler": "./dist/wrangler/mod.js"
12
13
  },
13
14
  "dependencies": {
14
- "@effect/opentelemetry": "0.56.4",
15
+ "@effect/opentelemetry": "0.57.0",
15
16
  "@effect/vitest": "0.25.1",
17
+ "@iarna/toml": "2.2.5",
16
18
  "@opentelemetry/api": "1.9.0",
17
19
  "@opentelemetry/exporter-metrics-otlp-http": "0.203.0",
18
20
  "@opentelemetry/exporter-trace-otlp-http": "0.203.0",
19
21
  "@opentelemetry/sdk-metrics": "2.0.1",
20
22
  "@opentelemetry/sdk-trace-base": "2.0.1",
21
23
  "@opentelemetry/sdk-trace-node": "2.0.1",
22
- "@livestore/utils": "0.4.0-dev.7"
24
+ "wrangler": "4.38.0",
25
+ "@livestore/utils": "0.4.0-dev.9"
23
26
  },
24
27
  "devDependencies": {},
25
28
  "files": [
@@ -4,6 +4,7 @@ import {
4
4
  type CommandExecutor,
5
5
  Duration,
6
6
  Effect,
7
+ Fiber,
7
8
  type PlatformError,
8
9
  Schedule,
9
10
  Schema,
@@ -13,7 +14,7 @@ import {
13
14
 
14
15
  export class DockerComposeError extends Schema.TaggedError<DockerComposeError>()('DockerComposeError', {
15
16
  cause: Schema.Defect,
16
- message: Schema.String,
17
+ note: Schema.String,
17
18
  }) {}
18
19
 
19
20
  export interface DockerComposeArgs {
@@ -43,6 +44,11 @@ export interface DockerComposeOperations {
43
44
  options?: StartOptions,
44
45
  ) => Effect.Effect<void, DockerComposeError | PlatformError.PlatformError, Scope.Scope>
45
46
  readonly stop: Effect.Effect<void, DockerComposeError | PlatformError.PlatformError>
47
+ readonly down: (options?: {
48
+ readonly env?: Record<string, string>
49
+ readonly volumes?: boolean
50
+ readonly removeOrphans?: boolean
51
+ }) => Effect.Effect<void, DockerComposeError | PlatformError.PlatformError>
46
52
  readonly logs: (
47
53
  options?: LogsOptions,
48
54
  ) => Stream.Stream<string, DockerComposeError | PlatformError.PlatformError, Scope.Scope>
@@ -58,24 +64,59 @@ export class DockerComposeService extends Effect.Service<DockerComposeService>()
58
64
  const pull = Effect.gen(function* () {
59
65
  yield* Effect.log(`Pulling Docker Compose images in ${cwd}`)
60
66
 
61
- yield* Command.make('docker', 'compose', 'pull').pipe(
67
+ // TODO (@IMax153) Refactor the effect command related code below as there is probably a much more elegant way to accomplish what we want here in a more effect idiomatic way.
68
+ const pullCommand = Command.make('docker', 'compose', 'pull').pipe(
62
69
  Command.workingDirectory(cwd),
63
- Command.exitCode,
64
- Effect.flatMap((exitCode: number) =>
65
- exitCode === 0
66
- ? Effect.void
67
- : Effect.fail(
68
- new DockerComposeError({
69
- cause: new Error(`Docker compose pull failed with exit code ${exitCode}`),
70
- message: `Docker compose pull failed with exit code ${exitCode}`,
71
- }),
72
- ),
73
- ),
74
- Effect.provide(commandExecutorContext),
70
+ Command.stdout('pipe'),
71
+ Command.stderr('pipe'),
75
72
  )
76
73
 
74
+ const process = yield* pullCommand.pipe(Command.start, Effect.provide(commandExecutorContext))
75
+
76
+ const stdoutFiber = yield* process.stdout.pipe(
77
+ Stream.decodeText('utf8'),
78
+ Stream.runFold('', (acc, chunk) => acc + chunk),
79
+ Effect.fork,
80
+ )
81
+
82
+ const stderrFiber = yield* process.stderr.pipe(
83
+ Stream.decodeText('utf8'),
84
+ Stream.runFold('', (acc, chunk) => acc + chunk),
85
+ Effect.fork,
86
+ )
87
+
88
+ const exitCode = yield* process.exitCode
89
+ const stdout = yield* Fiber.join(stdoutFiber)
90
+ const stderr = yield* Fiber.join(stderrFiber)
91
+
92
+ const exitCodeNumber = Number(exitCode)
93
+
94
+ if (exitCodeNumber !== 0) {
95
+ const stdoutLog = stdout.length > 0 ? stdout : '<empty stdout>'
96
+ const stderrLog = stderr.length > 0 ? stderr : '<empty stderr>'
97
+ const failureMessage = [
98
+ `Docker compose pull failed with exit code ${exitCodeNumber} in ${cwd}`,
99
+ `stdout:\n${stdoutLog}`,
100
+ `stderr:\n${stderrLog}`,
101
+ ].join('\n')
102
+
103
+ yield* Effect.logError(failureMessage)
104
+
105
+ return yield* new DockerComposeError({
106
+ cause: new Error(`Docker compose pull failed with exit code ${exitCodeNumber}`),
107
+ note: failureMessage,
108
+ })
109
+ }
110
+
77
111
  yield* Effect.log(`Successfully pulled Docker Compose images`)
78
- }).pipe(Effect.withSpan('pullDockerComposeImages'))
112
+ }).pipe(
113
+ Effect.retry({
114
+ schedule: Schedule.exponentialBackoff10Sec,
115
+ while: Schema.is(DockerComposeError),
116
+ }),
117
+ Effect.withSpan('pullDockerComposeImages'),
118
+ Effect.scoped,
119
+ )
79
120
 
80
121
  const start = (options: StartOptions = {}) =>
81
122
  Effect.gen(function* () {
@@ -89,12 +130,14 @@ export class DockerComposeService extends Effect.Service<DockerComposeService>()
89
130
  const command = yield* Command.make(baseArgs[0]!, ...baseArgs.slice(1)).pipe(
90
131
  Command.workingDirectory(cwd),
91
132
  Command.env(options.env ?? {}),
133
+ Command.stderr('inherit'),
134
+ Command.stdout('inherit'),
92
135
  Command.start,
93
136
  Effect.catchAll((cause) =>
94
137
  Effect.fail(
95
138
  new DockerComposeError({
96
139
  cause,
97
- message: `Failed to start Docker Compose services in ${cwd}`,
140
+ note: `Failed to start Docker Compose services in ${cwd}`,
98
141
  }),
99
142
  ),
100
143
  ),
@@ -103,13 +146,13 @@ export class DockerComposeService extends Effect.Service<DockerComposeService>()
103
146
 
104
147
  // Wait for command completion
105
148
  yield* command.exitCode.pipe(
106
- Effect.flatMap((exitCode: number) =>
149
+ Effect.flatMap((exitCode) =>
107
150
  exitCode === 0
108
151
  ? Effect.void
109
152
  : Effect.fail(
110
153
  new DockerComposeError({
111
154
  cause: new Error(`Docker compose exited with code ${exitCode}`),
112
- message: `Docker Compose failed to start with exit code ${exitCode}`,
155
+ note: `Docker Compose failed to start with exit code ${exitCode}. Env: ${JSON.stringify(options.env)}`,
113
156
  }),
114
157
  ),
115
158
  ),
@@ -140,7 +183,7 @@ export class DockerComposeService extends Effect.Service<DockerComposeService>()
140
183
  : Effect.fail(
141
184
  new DockerComposeError({
142
185
  cause: new Error(`Docker compose stop exited with code ${exitCode}`),
143
- message: `Failed to stop Docker Compose services`,
186
+ note: `Failed to stop Docker Compose services`,
144
187
  }),
145
188
  ),
146
189
  ),
@@ -167,7 +210,7 @@ export class DockerComposeService extends Effect.Service<DockerComposeService>()
167
210
  Effect.fail(
168
211
  new DockerComposeError({
169
212
  cause,
170
- message: `Failed to read Docker Compose logs in ${cwd}`,
213
+ note: `Failed to read Docker Compose logs in ${cwd}`,
171
214
  }),
172
215
  ),
173
216
  ),
@@ -181,13 +224,46 @@ export class DockerComposeService extends Effect.Service<DockerComposeService>()
181
224
  (cause) =>
182
225
  new DockerComposeError({
183
226
  cause,
184
- message: `Error reading Docker Compose logs in ${cwd}`,
227
+ note: `Error reading Docker Compose logs in ${cwd}`,
185
228
  }),
186
229
  ),
187
230
  )
188
231
  }).pipe(Stream.unwrapScoped)
189
232
 
190
- return { pull, start, stop, logs }
233
+ const down = (options?: {
234
+ readonly env?: Record<string, string>
235
+ readonly volumes?: boolean
236
+ readonly removeOrphans?: boolean
237
+ }) =>
238
+ Effect.gen(function* () {
239
+ yield* Effect.log(`Tearing down Docker Compose services in ${cwd}`)
240
+
241
+ const baseArgs = ['docker', 'compose', 'down']
242
+ if (options?.volumes) baseArgs.push('-v')
243
+ if (options?.removeOrphans) baseArgs.push('--remove-orphans')
244
+ if (serviceName) baseArgs.push(serviceName)
245
+
246
+ yield* Command.make(baseArgs[0]!, ...baseArgs.slice(1)).pipe(
247
+ Command.workingDirectory(cwd),
248
+ Command.env(options?.env ?? {}),
249
+ Command.exitCode,
250
+ Effect.flatMap((exitCode: number) =>
251
+ exitCode === 0
252
+ ? Effect.void
253
+ : Effect.fail(
254
+ new DockerComposeError({
255
+ cause: new Error(`Docker compose down exited with code ${exitCode}`),
256
+ note: `Failed to tear down Docker Compose services`,
257
+ }),
258
+ ),
259
+ ),
260
+ Effect.provide(commandExecutorContext),
261
+ )
262
+
263
+ yield* Effect.log(`Docker Compose services torn down successfully`)
264
+ }).pipe(Effect.withSpan('downDockerCompose'))
265
+
266
+ return { pull, start, stop, down, logs }
191
267
  }),
192
268
  }) {}
193
269
 
@@ -219,7 +295,7 @@ const performHealthCheck = ({
219
295
  Effect.fail(
220
296
  new DockerComposeError({
221
297
  cause: new Error('Health check timeout'),
222
- message: `Health check failed for ${url} after ${Duration.toMillis(timeout)}ms`,
298
+ note: `Health check failed for ${url} after ${Duration.toMillis(timeout)}ms`,
223
299
  }),
224
300
  ),
225
301
  ),
package/src/node/mod.ts CHANGED
@@ -24,13 +24,6 @@ export {
24
24
  startDockerComposeServicesScoped,
25
25
  } from './DockerComposeService/DockerComposeService.ts'
26
26
  export * as FileLogger from './FileLogger.ts'
27
- export * from './WranglerDevServer/process-tree-manager.ts'
28
- export {
29
- type StartWranglerDevServerArgs,
30
- type WranglerDevServer,
31
- WranglerDevServerError,
32
- WranglerDevServerService,
33
- } from './WranglerDevServer/WranglerDevServer.ts'
34
27
 
35
28
  export const OtelLiveHttp = ({
36
29
  serviceName,
@@ -96,6 +96,17 @@ Vitest.describe('Vitest.asProp', () => {
96
96
 
97
97
  return
98
98
  }),
99
- { fastCheck: { numRuns: 5, endOnFailure: true }, fails: true },
99
+ {
100
+ fastCheck: {
101
+ numRuns: 5,
102
+ endOnFailure: true,
103
+ // Provide explicit samples so the second run crosses >50. Randomly drawing five
104
+ // integers has ~3% chance to stay ≤ 50, which would break the `fails: true`
105
+ // expectation even though shrinking remains disabled. The examples keep the
106
+ // demo readable while leaving the remaining runs to FastCheck.
107
+ examples: [[5], [66]],
108
+ },
109
+ fails: true,
110
+ },
100
111
  )
101
112
  })
@@ -20,25 +20,29 @@ export * from '@effect/vitest'
20
20
 
21
21
  export const DEBUGGER_ACTIVE = Boolean(process.env.DEBUGGER_ACTIVE ?? inspector.url() !== undefined)
22
22
 
23
- export const makeWithTestCtx: <R1, E1>(
24
- ctxParams: WithTestCtxParams<R1, E1>,
25
- ) => (
26
- testContext: Vitest.TestContext,
27
- ) => <A, E>(
28
- self: Effect.Effect<A, E, Scope.Scope | NoInfer<R1> | OtelTracer.OtelTracer>,
29
- ) => Effect.Effect<A, E1 | Cause.TimeoutException | E, Scope.Scope> =
30
- (ctxParams) => (testContext: Vitest.TestContext) =>
31
- withTestCtx(testContext, ctxParams)
32
-
33
- export type WithTestCtxParams<R1, E1> = {
23
+ export const makeWithTestCtx: <ROut = never, E1 = never, RIn = never>(
24
+ ctxParams: WithTestCtxParams<ROut, E1, RIn>,
25
+ ) => (testContext: Vitest.TestContext) => <A, E, R>(
26
+ self: Effect.Effect<A, E, R>,
27
+ ) => Effect.Effect<
28
+ A,
29
+ E | E1 | Cause.TimeoutException,
30
+ // Exclude dependencies provided by `withTestCtx` from the layer dependencies
31
+ | Exclude<RIn, OtelTracer.OtelTracer | Scope.Scope>
32
+ // Exclude dependencies provided by `withTestCtx` **and** dependencies produced
33
+ // by the layer from the effect dependencies
34
+ | Exclude<R, ROut | OtelTracer.OtelTracer | Scope.Scope>
35
+ > = (ctxParams) => (testContext: Vitest.TestContext) => withTestCtx(testContext, ctxParams)
36
+
37
+ export type WithTestCtxParams<ROut, E1, RIn> = {
34
38
  suffix?: string
35
- makeLayer?: (testContext: Vitest.TestContext) => Layer.Layer<R1, E1, Scope.Scope>
39
+ makeLayer?: (testContext: Vitest.TestContext) => Layer.Layer<ROut, E1, RIn | Scope.Scope>
36
40
  timeout?: Duration.DurationInput
37
41
  forceOtel?: boolean
38
42
  }
39
43
 
40
44
  export const withTestCtx =
41
- <R1 = never, E1 = never>(
45
+ <ROut = never, E1 = never, RIn = never>(
42
46
  testContext: Vitest.TestContext,
43
47
  {
44
48
  suffix,
@@ -47,23 +51,31 @@ export const withTestCtx =
47
51
  forceOtel = false,
48
52
  }: {
49
53
  suffix?: string
50
- makeLayer?: (testContext: Vitest.TestContext) => Layer.Layer<R1, E1, Scope.Scope>
54
+ makeLayer?: (testContext: Vitest.TestContext) => Layer.Layer<ROut, E1, RIn>
51
55
  timeout?: Duration.DurationInput
52
56
  forceOtel?: boolean
53
57
  } = {},
54
58
  ) =>
55
- <A, E>(
56
- self: Effect.Effect<A, E, Scope.Scope | OtelTracer.OtelTracer | R1>,
57
- ): Effect.Effect<A, E | Cause.TimeoutException | E1, Scope.Scope> => {
59
+ <A, E, R>(
60
+ self: Effect.Effect<A, E, R>,
61
+ ): Effect.Effect<
62
+ A,
63
+ E | E1 | Cause.TimeoutException,
64
+ // Exclude dependencies provided internally from the provided layer's dependencies
65
+ | Exclude<RIn, OtelTracer.OtelTracer | Scope.Scope>
66
+ // Exclude dependencies provided internally **and** dependencies produced by the
67
+ // provided layer from the effect dependencies
68
+ | Exclude<R, ROut | OtelTracer.OtelTracer | Scope.Scope>
69
+ > => {
58
70
  const spanName = `${testContext.task.suite?.name}:${testContext.task.name}${suffix ? `:${suffix}` : ''}`
59
- const layer = makeLayer?.(testContext)
71
+ const layer = makeLayer?.(testContext) ?? Layer.empty
60
72
 
61
73
  const otelLayer =
62
74
  DEBUGGER_ACTIVE || forceOtel
63
75
  ? OtelLiveHttp({ rootSpanName: spanName, serviceName: 'vitest-runner', skipLogUrl: false })
64
76
  : OtelLiveDummy
65
77
 
66
- const combinedLayer = (layer ?? Layer.empty).pipe(Layer.provideMerge(otelLayer))
78
+ const combinedLayer = layer.pipe(Layer.provideMerge(otelLayer))
67
79
 
68
80
  return self.pipe(
69
81
  DEBUGGER_ACTIVE
@@ -0,0 +1,133 @@
1
+ import { Effect, FetchHttpClient, Layer } from '@livestore/utils/effect'
2
+ import { getFreePort, PlatformNode } from '@livestore/utils/node'
3
+ import { Vitest } from '@livestore/utils-dev/node-vitest'
4
+ import { expect } from 'vitest'
5
+ import {
6
+ type StartWranglerDevServerArgs,
7
+ WranglerDevServerError,
8
+ WranglerDevServerService,
9
+ } from './WranglerDevServer.ts'
10
+
11
+ const testTimeout = 60_000
12
+
13
+ const WranglerDevServerTest = (args: Partial<StartWranglerDevServerArgs> = {}) =>
14
+ WranglerDevServerService.Default({
15
+ cwd: `${import.meta.dirname}/fixtures`,
16
+ ...args,
17
+ }).pipe(Layer.provide(FetchHttpClient.layer))
18
+
19
+ Vitest.describe('WranglerDevServer', { timeout: testTimeout }, () => {
20
+ Vitest.describe('Basic Operations', () => {
21
+ const withBasicTest = (args: Partial<StartWranglerDevServerArgs> = {}) =>
22
+ Vitest.makeWithTestCtx({
23
+ timeout: testTimeout,
24
+ makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
25
+ })
26
+
27
+ Vitest.scopedLive('should start wrangler dev server and return port', (test) =>
28
+ Effect.gen(function* () {
29
+ const server = yield* WranglerDevServerService
30
+
31
+ expect(server.port).toBeGreaterThan(0)
32
+ expect(server.url).toMatch(/http:\/\/127.0.0.1:\d+/)
33
+ }).pipe(withBasicTest()(test)),
34
+ )
35
+
36
+ Vitest.scopedLive('should use specified port when provided', (test) =>
37
+ Effect.andThen(getFreePort, (port) =>
38
+ Effect.gen(function* () {
39
+ const server = yield* WranglerDevServerService
40
+
41
+ expect(server.port).toBe(port)
42
+ expect(server.url).toBe(`http://127.0.0.1:${port}`)
43
+ }).pipe(withBasicTest({ preferredPort: port })(test)),
44
+ ),
45
+ )
46
+ })
47
+
48
+ Vitest.describe('Error Handling', () => {
49
+ const withErrorTest = (args: Partial<StartWranglerDevServerArgs> = {}) =>
50
+ Vitest.makeWithTestCtx({
51
+ timeout: testTimeout,
52
+ makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
53
+ })
54
+
55
+ Vitest.scopedLive('should handle missing wrangler.toml but should timeout', (test) =>
56
+ Effect.gen(function* () {
57
+ const error = yield* WranglerDevServerService.pipe(
58
+ Effect.provide(
59
+ WranglerDevServerTest({
60
+ cwd: '/tmp',
61
+ wranglerConfigPath: '/dev/null',
62
+ connectTimeout: '500 millis',
63
+ }).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
64
+ ),
65
+ Effect.flip,
66
+ )
67
+
68
+ expect(error).toBeInstanceOf(WranglerDevServerError)
69
+ }).pipe(Vitest.withTestCtx(test)),
70
+ )
71
+
72
+ Vitest.scopedLive('should handle invalid working directory', (test) =>
73
+ Effect.gen(function* () {
74
+ const result = yield* WranglerDevServerService.pipe(
75
+ Effect.provide(
76
+ WranglerDevServerTest({
77
+ cwd: '/completely/nonexistent/directory',
78
+ }).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
79
+ ),
80
+ Effect.either,
81
+ )
82
+
83
+ expect(result._tag).toBe('Left')
84
+ if (result._tag === 'Left') {
85
+ expect(result.left).toBeInstanceOf(WranglerDevServerError)
86
+ }
87
+ }).pipe(Vitest.withTestCtx(test)),
88
+ )
89
+
90
+ Vitest.scopedLive('should timeout if server fails to start', (test) =>
91
+ Effect.gen(function* () {
92
+ // Create a command that will never output "Ready on"
93
+ const result = yield* WranglerDevServerService.pipe(
94
+ // Override the timeout for this test to be shorter
95
+ Effect.timeout('5 seconds'),
96
+ Effect.either,
97
+ )
98
+
99
+ // This might succeed or fail depending on actual wrangler behavior
100
+ // The main point is testing timeout functionality
101
+ expect(['Left', 'Right']).toContain(result._tag)
102
+ }).pipe(withErrorTest()(test)),
103
+ )
104
+ })
105
+
106
+ Vitest.describe('Service Pattern', () => {
107
+ const withServiceTest = (args: Partial<StartWranglerDevServerArgs> = {}) =>
108
+ Vitest.makeWithTestCtx({
109
+ timeout: testTimeout,
110
+ makeLayer: () => WranglerDevServerTest(args).pipe(Layer.provide(PlatformNode.NodeContext.layer)),
111
+ })
112
+
113
+ Vitest.scopedLive('should work with service pattern', (test) =>
114
+ Effect.gen(function* () {
115
+ const server = yield* WranglerDevServerService
116
+
117
+ expect(server.port).toBeGreaterThan(0)
118
+ expect(server.url).toMatch(/http:\/\/127.0.0.1:\d+/)
119
+ }).pipe(withServiceTest()(test)),
120
+ )
121
+
122
+ Vitest.scopedLive('should work with custom port via service', (test) =>
123
+ Effect.andThen(getFreePort, (port) =>
124
+ Effect.gen(function* () {
125
+ const server = yield* WranglerDevServerService
126
+
127
+ expect(server.port).toBe(port)
128
+ expect(server.url).toBe(`http://127.0.0.1:${port}`)
129
+ }).pipe(withServiceTest({ preferredPort: port })(test)),
130
+ ),
131
+ )
132
+ })
133
+ })