@docker-harpoon/core 0.1.3 → 0.1.5

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 (98) hide show
  1. package/dist/__tests__/bindings.test.js +9 -5
  2. package/dist/__tests__/container.test.js +30 -9
  3. package/dist/__tests__/database.test.js +0 -14
  4. package/dist/__tests__/docker-infra.template.d.ts +2 -0
  5. package/dist/__tests__/docker-infra.template.d.ts.map +1 -0
  6. package/dist/__tests__/docker-infra.template.js +174 -0
  7. package/dist/__tests__/test-setup.d.ts +9 -0
  8. package/dist/__tests__/test-setup.d.ts.map +1 -0
  9. package/dist/__tests__/test-setup.js +27 -0
  10. package/dist/api/index.d.ts +1 -1
  11. package/dist/api/index.d.ts.map +1 -1
  12. package/dist/api/promise.d.ts +13 -3
  13. package/dist/api/promise.d.ts.map +1 -1
  14. package/dist/api/promise.js +33 -18
  15. package/dist/bindings/index.d.ts +2 -2
  16. package/dist/bindings/index.d.ts.map +1 -1
  17. package/dist/bindings/index.js +1 -1
  18. package/dist/bindings/types.d.ts.map +1 -1
  19. package/dist/bindings/types.js +1 -3
  20. package/dist/build-strategies/types.d.ts.map +1 -1
  21. package/dist/config-patchers/index.d.ts.map +1 -1
  22. package/dist/config-patchers/types.d.ts.map +1 -1
  23. package/dist/dockerfile-transformers/core.d.ts.map +1 -1
  24. package/dist/dockerfile-transformers/core.js +2 -5
  25. package/dist/dockerfile-transformers/index.d.ts.map +1 -1
  26. package/dist/dockerfile-transformers/types.d.ts.map +1 -1
  27. package/dist/errors.d.ts +6 -1
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/errors.js +3 -3
  30. package/dist/helpers/database.d.ts.map +1 -1
  31. package/dist/helpers/database.js +1 -3
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/resources/container.d.ts +25 -2
  36. package/dist/resources/container.d.ts.map +1 -1
  37. package/dist/resources/container.js +301 -77
  38. package/dist/resources/image.d.ts.map +1 -1
  39. package/dist/resources/image.js +14 -35
  40. package/dist/resources/index.d.ts +1 -1
  41. package/dist/resources/index.d.ts.map +1 -1
  42. package/dist/resources/network.d.ts.map +1 -1
  43. package/dist/resources/network.js +23 -9
  44. package/dist/resources/schemas.d.ts +178 -18
  45. package/dist/resources/schemas.d.ts.map +1 -1
  46. package/dist/resources/schemas.js +2 -1
  47. package/dist/services/CircuitBreaker.d.ts +83 -0
  48. package/dist/services/CircuitBreaker.d.ts.map +1 -0
  49. package/dist/services/CircuitBreaker.js +164 -0
  50. package/dist/services/ContainerPool.d.ts +82 -0
  51. package/dist/services/ContainerPool.d.ts.map +1 -0
  52. package/dist/services/ContainerPool.js +186 -0
  53. package/dist/services/DockerBatcher.d.ts +74 -0
  54. package/dist/services/DockerBatcher.d.ts.map +1 -0
  55. package/dist/services/DockerBatcher.js +107 -0
  56. package/dist/services/DockerClient.d.ts +125 -0
  57. package/dist/services/DockerClient.d.ts.map +1 -0
  58. package/dist/services/DockerClient.js +220 -0
  59. package/dist/services/DockerErrors.d.ts +145 -0
  60. package/dist/services/DockerErrors.d.ts.map +1 -0
  61. package/dist/services/DockerErrors.js +224 -0
  62. package/dist/services/DockerRateLimiter.d.ts +80 -0
  63. package/dist/services/DockerRateLimiter.d.ts.map +1 -0
  64. package/dist/services/DockerRateLimiter.js +93 -0
  65. package/dist/services/EventBus.d.ts +126 -0
  66. package/dist/services/EventBus.d.ts.map +1 -0
  67. package/dist/services/EventBus.js +111 -0
  68. package/dist/services/Harpoon.d.ts +151 -0
  69. package/dist/services/Harpoon.d.ts.map +1 -0
  70. package/dist/services/Harpoon.js +148 -0
  71. package/dist/services/HarpoonConfig.d.ts +60 -0
  72. package/dist/services/HarpoonConfig.d.ts.map +1 -0
  73. package/dist/services/HarpoonConfig.js +67 -0
  74. package/dist/services/HarpoonLogger.d.ts +36 -0
  75. package/dist/services/HarpoonLogger.d.ts.map +1 -0
  76. package/dist/services/HarpoonLogger.js +94 -0
  77. package/dist/services/ReadinessCoordinator.d.ts +128 -0
  78. package/dist/services/ReadinessCoordinator.d.ts.map +1 -0
  79. package/dist/services/ReadinessCoordinator.js +170 -0
  80. package/dist/services/ResourceTracker.d.ts +74 -0
  81. package/dist/services/ResourceTracker.d.ts.map +1 -0
  82. package/dist/services/ResourceTracker.js +145 -0
  83. package/dist/services/index.d.ts +29 -0
  84. package/dist/services/index.d.ts.map +1 -0
  85. package/dist/services/index.js +47 -0
  86. package/dist/testing/helpers.d.ts +114 -0
  87. package/dist/testing/helpers.d.ts.map +1 -0
  88. package/dist/testing/helpers.js +140 -0
  89. package/dist/testing/index.d.ts +29 -0
  90. package/dist/testing/index.d.ts.map +1 -0
  91. package/dist/testing/index.js +47 -0
  92. package/dist/testing/mocks.d.ts +66 -0
  93. package/dist/testing/mocks.d.ts.map +1 -0
  94. package/dist/testing/mocks.js +224 -0
  95. package/dist/utils/process.d.ts +24 -0
  96. package/dist/utils/process.d.ts.map +1 -0
  97. package/dist/utils/process.js +49 -0
  98. package/package.json +12 -8
@@ -6,8 +6,9 @@
6
6
  * - Env var injection into containers
7
7
  * - Binding composition
8
8
  */
9
- import { describe, test, expect, afterEach, setDefaultTimeout } from 'bun:test';
9
+ import { describe, test, expect, afterEach, beforeAll, setDefaultTimeout } from 'bun:test';
10
10
  import { Container, database, createEnvBinding, destroyAll } from '../index';
11
+ import { buildTestImage, TEST_IMAGE } from './test-setup';
11
12
  setDefaultTimeout(30_000);
12
13
  const waitUntil = (fn, timeout = 5000) => {
13
14
  return new Promise((resolve) => {
@@ -20,6 +21,9 @@ const waitUntil = (fn, timeout = 5000) => {
20
21
  });
21
22
  };
22
23
  describe('Bindings', () => {
24
+ beforeAll(async () => {
25
+ await buildTestImage();
26
+ });
23
27
  afterEach(async () => {
24
28
  await destroyAll();
25
29
  });
@@ -40,7 +44,7 @@ describe('Bindings', () => {
40
44
  APP_VERSION: '1.0.0',
41
45
  });
42
46
  const container = await Container('test-binding-env', {
43
- image: 'alpine:latest',
47
+ image: TEST_IMAGE,
44
48
  cmd: ['sleep', '30'],
45
49
  bindings: {
46
50
  config: configBinding,
@@ -65,7 +69,7 @@ describe('Bindings', () => {
65
69
  VAR_D: 'd',
66
70
  });
67
71
  const container = await Container('test-multi-binding', {
68
- image: 'alpine:latest',
72
+ image: TEST_IMAGE,
69
73
  cmd: ['sleep', '30'],
70
74
  bindings: {
71
75
  first: binding1,
@@ -86,7 +90,7 @@ describe('Bindings', () => {
86
90
  MY_VAR: 'from-binding',
87
91
  });
88
92
  const container = await Container('test-env-precedence', {
89
- image: 'alpine:latest',
93
+ image: TEST_IMAGE,
90
94
  cmd: ['sleep', '30'],
91
95
  bindings: {
92
96
  config: binding,
@@ -109,7 +113,7 @@ describe('Bindings', () => {
109
113
  REDIS_URL: db.connectionString,
110
114
  });
111
115
  const container = await Container('test-db-consumer', {
112
- image: 'alpine:latest',
116
+ image: TEST_IMAGE,
113
117
  cmd: ['sleep', '30'],
114
118
  bindings: {
115
119
  db: dbBinding,
@@ -8,16 +8,20 @@
8
8
  * - stats retrieval
9
9
  * - Lifecycle (stop, destroy)
10
10
  */
11
- import { describe, test, expect, afterEach, setDefaultTimeout } from 'bun:test';
11
+ import { describe, test, expect, afterEach, beforeAll, setDefaultTimeout } from 'bun:test';
12
12
  import { Network, Container, destroyAll } from '../index';
13
+ import { buildTestImage, TEST_IMAGE } from './test-setup';
13
14
  setDefaultTimeout(30_000);
14
15
  describe('Container', () => {
16
+ beforeAll(async () => {
17
+ await buildTestImage();
18
+ });
15
19
  afterEach(async () => {
16
20
  await destroyAll();
17
21
  });
18
22
  test('creates a container with basic config', async () => {
19
23
  const container = await Container('test-container-basic', {
20
- image: 'alpine:latest',
24
+ image: TEST_IMAGE,
21
25
  cmd: ['sleep', '30'],
22
26
  });
23
27
  expect(container.id).toBeDefined();
@@ -27,7 +31,7 @@ describe('Container', () => {
27
31
  test('creates a container attached to a network', async () => {
28
32
  const network = await Network('test-container-net');
29
33
  const container = await Container('test-container-networked', {
30
- image: 'alpine:latest',
34
+ image: TEST_IMAGE,
31
35
  cmd: ['sleep', '30'],
32
36
  networks: [network],
33
37
  });
@@ -39,7 +43,7 @@ describe('Container', () => {
39
43
  });
40
44
  test('exec runs commands inside container', async () => {
41
45
  const container = await Container('test-container-exec', {
42
- image: 'alpine:latest',
46
+ image: TEST_IMAGE,
43
47
  cmd: ['sleep', '30'],
44
48
  });
45
49
  await new Promise((r) => setTimeout(r, 500));
@@ -51,7 +55,7 @@ describe('Container', () => {
51
55
  });
52
56
  test('stats returns container resource statistics', async () => {
53
57
  const container = await Container('test-container-stats', {
54
- image: 'alpine:latest',
58
+ image: TEST_IMAGE,
55
59
  cmd: ['sleep', '30'],
56
60
  });
57
61
  await new Promise((r) => setTimeout(r, 500));
@@ -66,7 +70,7 @@ describe('Container', () => {
66
70
  });
67
71
  test('waitForLog detects log patterns', async () => {
68
72
  const container = await Container('test-container-logs', {
69
- image: 'alpine:latest',
73
+ image: TEST_IMAGE,
70
74
  cmd: ['sh', '-c', 'echo "Server started successfully" && sleep 30'],
71
75
  });
72
76
  await container.waitForLog('Server started', 5000);
@@ -75,7 +79,7 @@ describe('Container', () => {
75
79
  });
76
80
  test('stop gracefully shuts down container', async () => {
77
81
  const container = await Container('test-container-stop', {
78
- image: 'alpine:latest',
82
+ image: TEST_IMAGE,
79
83
  cmd: ['sh', '-c', 'trap "exit 0" TERM; while true; do sleep 1; done'],
80
84
  });
81
85
  await new Promise((r) => setTimeout(r, 500));
@@ -87,7 +91,7 @@ describe('Container', () => {
87
91
  });
88
92
  test('supports environment variables', async () => {
89
93
  const container = await Container('test-container-env', {
90
- image: 'alpine:latest',
94
+ image: TEST_IMAGE,
91
95
  cmd: ['sleep', '30'],
92
96
  env: {
93
97
  MY_VAR: 'hello',
@@ -105,11 +109,28 @@ describe('Container', () => {
105
109
  });
106
110
  test('supports port mappings', async () => {
107
111
  const container = await Container('test-container-ports', {
108
- image: 'alpine:latest',
112
+ image: TEST_IMAGE,
109
113
  cmd: ['sleep', '30'],
110
114
  ports: [{ internal: 8080, external: 18080 }],
111
115
  });
112
116
  expect(container.id).toBeDefined();
113
117
  await container.destroy();
114
118
  });
119
+ test('streamLogs terminates without follow option', async () => {
120
+ const container = await Container('test-logs-terminate', {
121
+ image: TEST_IMAGE,
122
+ cmd: ['sh', '-c', 'echo "line1" && echo "line2" && echo "line3" && sleep 30'],
123
+ });
124
+ await container.waitForLog('line3', 5000);
125
+ const lines = [];
126
+ const logStream = await container.streamLogs({ tail: 3 });
127
+ for await (const line of logStream) {
128
+ lines.push(line.message);
129
+ }
130
+ // Loop should exit naturally
131
+ expect(lines).toContain('line1');
132
+ expect(lines).toContain('line2');
133
+ expect(lines).toContain('line3');
134
+ await container.destroy();
135
+ });
115
136
  });
@@ -50,20 +50,6 @@ describe('database', () => {
50
50
  expect(db.connectionString).toBe('redis://:secret@localhost:16380');
51
51
  await db.destroy();
52
52
  });
53
- test('creates a mysql database with connection string', async () => {
54
- const db = await database('test-mysql', {
55
- image: 'mysql:8',
56
- env: {
57
- MYSQL_ROOT_PASSWORD: 'rootpass',
58
- MYSQL_DATABASE: 'mydb',
59
- },
60
- hostPort: 13306,
61
- });
62
- expect(db.port).toBe(3306);
63
- expect(db.hostPort).toBe(13306);
64
- expect(db.connectionString).toBe('mysql://root:rootpass@localhost:13306/mydb');
65
- await db.destroy();
66
- });
67
53
  test('attaches database to network', async () => {
68
54
  const network = await Network('test-db-net');
69
55
  const db = await database('test-db-networked', {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=docker-infra.template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-infra.template.d.ts","sourceRoot":"","sources":["../../src/__tests__/docker-infra.template.ts"],"names":[],"mappings":""}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Docker Infrastructure Test Template
3
+ *
4
+ * Copy and customize this file to test your project's Dockerfile and entrypoint.
5
+ * Replace {{PROJECT_NAME}} and {{PROJECT_IMAGE}} with your values.
6
+ *
7
+ * This template demonstrates Harpoon's complete API for testing Docker infrastructure:
8
+ * - Image building with buildArgs
9
+ * - Container creation with volumes, capabilities, and user overrides
10
+ * - Log waiting and streaming
11
+ * - Command execution inside containers
12
+ * - Network isolation testing
13
+ *
14
+ * @example
15
+ * ```bash
16
+ * # Copy and customize for your project
17
+ * cp docker-infra.template.ts my-project.test.ts
18
+ * # Edit PROJECT config and tests
19
+ * bun test my-project.test.ts
20
+ * ```
21
+ */
22
+ import { describe, test, expect, beforeAll, afterEach, setDefaultTimeout } from 'bun:test';
23
+ import { Image, Container, Network, destroyAll } from '@docker-harpoon/core';
24
+ // ============ PROJECT CONFIGURATION ============
25
+ // Customize these values for your project
26
+ const PROJECT = {
27
+ name: '{{PROJECT_NAME}}',
28
+ image: '{{PROJECT_IMAGE}}:latest',
29
+ context: './',
30
+ dockerfile: 'Dockerfile',
31
+ port: 3000,
32
+ healthPath: '/api/status',
33
+ };
34
+ // Set a generous timeout for Docker operations
35
+ setDefaultTimeout(120_000);
36
+ describe(`${PROJECT.name} Docker Infrastructure`, () => {
37
+ beforeAll(async () => {
38
+ // Build the project image
39
+ await Image(PROJECT.image, {
40
+ context: PROJECT.context,
41
+ dockerfile: PROJECT.dockerfile,
42
+ buildArgs: { SEED_DATABASE: 'true' },
43
+ });
44
+ });
45
+ afterEach(async () => {
46
+ await destroyAll();
47
+ });
48
+ // ============ HEALTH CHECK TEST ============
49
+ test('health check responds 200', async () => {
50
+ const container = await Container('test-health', {
51
+ image: PROJECT.image,
52
+ env: { PORT: String(PROJECT.port) },
53
+ ports: [{ internal: PROJECT.port, external: 13000 }],
54
+ });
55
+ await container.waitForLog(/listening|started|ready/i, 30000);
56
+ const res = await fetch(`http://localhost:13000${PROJECT.healthPath}`);
57
+ expect(res.status).toBe(200);
58
+ });
59
+ // ============ VOLUME MOUNT TEST ============
60
+ test('db snapshot copy-on-boot pattern', async () => {
61
+ const container = await Container('test-snapshot', {
62
+ image: PROJECT.image,
63
+ env: {
64
+ DB_SOURCE: '/data/snapshot/app.db',
65
+ DB_PATH: '/app/data/test.db',
66
+ },
67
+ volumes: [
68
+ { hostPath: '/tmp/test-snapshot.db', containerPath: '/data/snapshot/app.db', readonly: true },
69
+ ],
70
+ });
71
+ await container.waitForLog(/snapshot copied|database ready/i, 30000);
72
+ });
73
+ // ============ FILE SYSTEM TEST ============
74
+ test('file upload directory setup', async () => {
75
+ const container = await Container('test-uploads', {
76
+ image: PROJECT.image,
77
+ env: { FILE_UPLOAD_PATH: '/app/custom-uploads' },
78
+ });
79
+ await container.waitForLog(/upload directory|ensuring/i, 10000);
80
+ const result = await container.exec(['ls', '-la', '/app/custom-uploads']);
81
+ expect(result.exitCode).toBe(0);
82
+ expect(result.stdout).toContain('drwx'); // Directory exists with permissions
83
+ });
84
+ // ============ NETWORK ISOLATION TEST ============
85
+ test('network isolation with iptables', async () => {
86
+ const network = await Network('isolated-net');
87
+ const container = await Container('test-isolation', {
88
+ image: PROJECT.image,
89
+ networks: [network],
90
+ env: { ENABLE_NETWORK_ISOLATION: 'true' },
91
+ capAdd: ['NET_ADMIN'],
92
+ });
93
+ await container.waitForLog(/network isolation configured|iptables/i, 15000);
94
+ });
95
+ // ============ PRIVILEGE DROPPING TEST ============
96
+ test('privilege dropping', async () => {
97
+ const container = await Container('test-privileges', {
98
+ image: PROJECT.image,
99
+ });
100
+ await container.waitForLog(/dropping privileges|started/i, 15000);
101
+ const result = await container.exec(['whoami']);
102
+ expect(result.exitCode).toBe(0);
103
+ expect(result.stdout.trim()).not.toBe('root');
104
+ });
105
+ // ============ ENVIRONMENT VARIABLE TEST ============
106
+ test('environment variable propagation', async () => {
107
+ const container = await Container('test-env', {
108
+ image: PROJECT.image,
109
+ env: {
110
+ PORT: '8080',
111
+ LOG_LEVEL: 'debug',
112
+ CUSTOM_VAR: 'test-value',
113
+ },
114
+ ports: [{ internal: 8080, external: 18080 }],
115
+ });
116
+ await container.waitForLog(/listening|started/i, 30000);
117
+ // Verify env vars are set
118
+ const envResult = await container.exec(['printenv']);
119
+ expect(envResult.stdout).toContain('PORT=8080');
120
+ expect(envResult.stdout).toContain('LOG_LEVEL=debug');
121
+ expect(envResult.stdout).toContain('CUSTOM_VAR=test-value');
122
+ });
123
+ // ============ USER OVERRIDE TEST ============
124
+ test('user override', async () => {
125
+ const container = await Container('test-user', {
126
+ image: PROJECT.image,
127
+ user: '1000:1000',
128
+ });
129
+ await container.waitForLog(/started|ready/i, 15000);
130
+ const result = await container.exec(['id']);
131
+ expect(result.exitCode).toBe(0);
132
+ expect(result.stdout).toContain('uid=1000');
133
+ expect(result.stdout).toContain('gid=1000');
134
+ });
135
+ // ============ CAPABILITIES TEST ============
136
+ test('dropped capabilities', async () => {
137
+ const container = await Container('test-caps', {
138
+ image: PROJECT.image,
139
+ capDrop: ['ALL'],
140
+ capAdd: ['NET_BIND_SERVICE'],
141
+ });
142
+ await container.waitForLog(/started|ready/i, 15000);
143
+ // Container should be running with minimal capabilities
144
+ const result = await container.exec(['cat', '/proc/1/status']);
145
+ expect(result.exitCode).toBe(0);
146
+ // CapEff should show limited capabilities
147
+ expect(result.stdout).toContain('CapEff');
148
+ });
149
+ // ============ GRACEFUL SHUTDOWN TEST ============
150
+ test('graceful shutdown', async () => {
151
+ const container = await Container('test-shutdown', {
152
+ image: PROJECT.image,
153
+ env: { PORT: String(PROJECT.port) },
154
+ });
155
+ await container.waitForLog(/listening|started|ready/i, 30000);
156
+ const metadata = await container.stop('SIGTERM', 10000);
157
+ expect(metadata.graceful).toBe(true);
158
+ expect(metadata.signal).toBe('SIGTERM');
159
+ });
160
+ // ============ RESOURCE STATS TEST ============
161
+ test('container stats', async () => {
162
+ const container = await Container('test-stats', {
163
+ image: PROJECT.image,
164
+ env: { PORT: String(PROJECT.port) },
165
+ });
166
+ await container.waitForLog(/listening|started|ready/i, 30000);
167
+ const stats = await container.stats();
168
+ expect(stats.cpu).toBeDefined();
169
+ expect(stats.cpu.percent).toBeGreaterThanOrEqual(0);
170
+ expect(stats.memory).toBeDefined();
171
+ expect(stats.memory.usage).toBeGreaterThan(0);
172
+ expect(stats.network).toBeDefined();
173
+ });
174
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared test setup for integration tests.
3
+ *
4
+ * Builds a minimal test image that can be used across all tests,
5
+ * ensuring tests are self-contained and don't rely on external images.
6
+ */
7
+ export declare const TEST_IMAGE = "harpoon-test:latest";
8
+ export declare function buildTestImage(): Promise<void>;
9
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/test-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,eAAO,MAAM,UAAU,wBAAwB,CAAC;AAWhD,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAapD"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared test setup for integration tests.
3
+ *
4
+ * Builds a minimal test image that can be used across all tests,
5
+ * ensuring tests are self-contained and don't rely on external images.
6
+ */
7
+ import { Image } from '../index';
8
+ import { join } from 'path';
9
+ export const TEST_IMAGE = 'harpoon-test:latest';
10
+ let imageBuilt = false;
11
+ // Get the source directory path (works in both ESM and CJS)
12
+ const getFixturesDir = () => {
13
+ // Try to resolve from source directory first
14
+ const srcDir = join(process.cwd(), 'src', '__tests__', 'fixtures');
15
+ return srcDir;
16
+ };
17
+ export async function buildTestImage() {
18
+ if (imageBuilt) {
19
+ return;
20
+ }
21
+ const fixturesDir = getFixturesDir();
22
+ await Image(TEST_IMAGE, {
23
+ context: fixturesDir,
24
+ dockerfile: 'Dockerfile',
25
+ });
26
+ imageBuilt = true;
27
+ }
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Re-exports the Promise-based API for cleaner imports.
5
5
  */
6
- export { Network, Container, database, Image, setDocker, resetDocker, destroyAll, type NetworkConfig, type NetworkResource, type ContainerConfig, type ContainerResource, type PortMapping, type ShutdownMetadata, type DatabaseConfig, type DatabaseResource, type ImageConfig, type ImageResource, type ContainerStats, type ExecOptions, type ExecResult, type LogOptions, type LogLine, } from './promise';
6
+ export { Network, Container, database, Image, setDocker, resetDocker, destroyAll, type NetworkConfig, type NetworkResource, type ContainerConfig, type ContainerResource, type PortMapping, type ShutdownMetadata, type DatabaseConfig, type DatabaseResource, type ImageConfig, type ImageResource, type ContainerStats, type ExecOptions, type ExecResult, type LogOptions, type LogLine, type VolumeMapping, } from './promise';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,OAAO,EACP,SAAS,EACT,QAAQ,EACR,KAAK,EAGL,SAAS,EACT,WAAW,EACX,UAAU,EAGV,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGlB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,OAAO,GACb,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,OAAO,EACP,SAAS,EACT,QAAQ,EACR,KAAK,EAGL,SAAS,EACT,WAAW,EACX,UAAU,EAGV,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGlB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC"}
@@ -4,11 +4,11 @@
4
4
  * This module wraps the Effect-based resources with a simple Promise API.
5
5
  * Effect is completely hidden from users - they just use async/await.
6
6
  *
7
- * Inspired by Alchemy.run and Pulumi's IAC patterns.
7
+ * Inspired by Harpoon and Pulumi's IAC patterns.
8
8
  */
9
9
  import Docker from 'dockerode';
10
- import { type ContainerStats, type ExecOptions, type ExecResult, type LogOptions, type LogLine } from '../resources';
11
- export type { ContainerStats, ExecOptions, ExecResult, LogOptions, LogLine, } from '../resources';
10
+ import { type ContainerStats, type ExecOptions, type ExecResult, type LogOptions, type LogLine, type VolumeMapping } from '../resources';
11
+ export type { ContainerStats, ExecOptions, ExecResult, LogOptions, LogLine, VolumeMapping, } from '../resources';
12
12
  import type { Binding } from '../bindings/types';
13
13
  export interface NetworkConfig {
14
14
  /** Network driver (default: 'bridge') */
@@ -41,6 +41,16 @@ export interface ContainerConfig {
41
41
  cmd?: string[];
42
42
  /** Bindings that provide env vars and lifecycle hooks */
43
43
  bindings?: Record<string, Binding>;
44
+ /** Volume mounts (host:container) */
45
+ volumes?: VolumeMapping[];
46
+ /** Capabilities to add (e.g., 'NET_ADMIN', 'SYS_PTRACE') */
47
+ capAdd?: string[];
48
+ /** Capabilities to drop (e.g., 'ALL') */
49
+ capDrop?: string[];
50
+ /** Run in privileged mode */
51
+ privileged?: boolean;
52
+ /** User/group to run as (e.g., '1000:1000') */
53
+ user?: string;
44
54
  }
45
55
  export interface ContainerResource {
46
56
  readonly id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"promise.d.ts","sourceRoot":"","sources":["../../src/api/promise.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"promise.d.ts","sourceRoot":"","sources":["../../src/api/promise.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * This module wraps the Effect-based resources with a simple Promise API.
5
5
  * Effect is completely hidden from users - they just use async/await.
6
6
  *
7
- * Inspired by Alchemy.run and Pulumi's IAC patterns.
7
+ * Inspired by Harpoon and Pulumi's IAC patterns.
8
8
  */
9
9
  import { Effect, Scope, Exit } from 'effect';
10
10
  import { homedir, platform } from 'os';
@@ -23,10 +23,7 @@ let dockerClient = null;
23
23
  * Detect Docker socket path for the current platform.
24
24
  */
25
25
  function detectDockerSocket() {
26
- const candidates = [
27
- '/var/run/docker.sock',
28
- `${homedir()}/.docker/run/docker.sock`,
29
- ];
26
+ const candidates = ['/var/run/docker.sock', `${homedir()}/.docker/run/docker.sock`];
30
27
  for (const path of candidates) {
31
28
  if (existsSync(path)) {
32
29
  return path;
@@ -45,16 +42,29 @@ function getDocker() {
45
42
  }
46
43
  return dockerClient;
47
44
  }
48
- // ============ Docker Auto-Start (Alchemy.run Style) ============
45
+ // ============ Docker Auto-Start (Harpoon Style) ============
49
46
  const DOCKER_STARTUP_TIMEOUT_MS = 60_000;
50
47
  let dockerEnsured = false;
48
+ /**
49
+ * Simple logging helper for promise-based API.
50
+ */
51
+ const harpoonLog = {
52
+ debug: (message) => {
53
+ if (process.env.HARPOON_LOG_LEVEL === 'debug') {
54
+ console.log(`[Harpoon] ${message}`);
55
+ }
56
+ },
57
+ info: (message) => console.log(`[Harpoon] ${message}`),
58
+ warn: (message) => console.warn(`[Harpoon] ${message}`),
59
+ error: (message, err) => console.error(`[Harpoon] ${message}`, err ?? ''),
60
+ };
51
61
  /**
52
62
  * Start Docker Desktop on macOS/Windows if not running.
53
63
  */
54
64
  function startDockerDesktop() {
55
65
  const os = platform();
56
66
  if (os === 'darwin') {
57
- console.log('[Harpoon] Starting Docker Desktop...');
67
+ harpoonLog.info('Starting Docker Desktop...');
58
68
  const child = spawn('open', ['-a', 'Docker', '--background'], {
59
69
  detached: true,
60
70
  stdio: 'ignore',
@@ -62,7 +72,7 @@ function startDockerDesktop() {
62
72
  child.unref();
63
73
  }
64
74
  else if (os === 'win32') {
65
- console.log('[Harpoon] Starting Docker Desktop...');
75
+ harpoonLog.info('Starting Docker Desktop...');
66
76
  const child = spawn('cmd', ['/c', 'start', '', 'Docker Desktop'], {
67
77
  detached: true,
68
78
  stdio: 'ignore',
@@ -82,20 +92,20 @@ async function waitForDockerReady(socketPath, timeoutMs) {
82
92
  if (existsSync(socketPath)) {
83
93
  const docker = new Docker({ socketPath });
84
94
  await docker.ping();
85
- console.log('[Harpoon] Docker is ready');
95
+ harpoonLog.info('Docker is ready');
86
96
  return;
87
97
  }
88
98
  }
89
99
  catch (e) {
90
100
  lastError = e;
91
101
  }
92
- await new Promise(r => setTimeout(r, 500));
102
+ await new Promise((r) => setTimeout(r, 500));
93
103
  }
94
- throw new Error(`[Harpoon] Docker not ready after ${timeoutMs}ms: ${lastError?.message}`);
104
+ throw new Error(`Docker not ready after ${timeoutMs}ms: ${lastError?.message}`);
95
105
  }
96
106
  /**
97
107
  * Ensure Docker is running, auto-starting if needed.
98
- * This is the Alchemy.run-style declarative approach.
108
+ * This is the Harpoon-style declarative approach.
99
109
  */
100
110
  async function ensureDockerRunning() {
101
111
  if (dockerEnsured)
@@ -111,12 +121,12 @@ async function ensureDockerRunning() {
111
121
  }
112
122
  catch {
113
123
  // Socket exists but daemon not responding, wait for it
114
- console.log('[Harpoon] Docker socket exists but daemon not responding, waiting...');
124
+ harpoonLog.debug('Docker socket exists but daemon not responding, waiting...');
115
125
  }
116
126
  }
117
127
  else {
118
128
  // Socket doesn't exist, start Docker
119
- console.log('[Harpoon] Docker not running, attempting auto-start...');
129
+ harpoonLog.info('Docker not running, attempting auto-start...');
120
130
  startDockerDesktop();
121
131
  }
122
132
  // Wait for Docker to be ready
@@ -159,7 +169,7 @@ function registerCleanup() {
159
169
  await resource.cleanup();
160
170
  }
161
171
  catch (e) {
162
- console.error(`[Cleanup] Error cleaning up ${id}:`, e);
172
+ harpoonLog.error(`Error cleaning up ${id}`, e);
163
173
  }
164
174
  }
165
175
  resources.clear();
@@ -229,7 +239,7 @@ export async function Container(name, config) {
229
239
  const docker = await getDockerAsync();
230
240
  const scope = Effect.runSync(Scope.make());
231
241
  // Convert NetworkResource to { name: string }
232
- const networks = config.networks?.map((n) => 'destroy' in n ? { name: n.name } : n);
242
+ const networks = config.networks?.map((n) => ('destroy' in n ? { name: n.name } : n));
233
243
  const effectConfig = {
234
244
  image: config.image,
235
245
  ...(networks !== undefined && { networks }),
@@ -237,6 +247,11 @@ export async function Container(name, config) {
237
247
  ...(config.env !== undefined && { env: config.env }),
238
248
  ...(config.cmd !== undefined && { cmd: config.cmd }),
239
249
  ...(config.bindings !== undefined && { bindings: config.bindings }),
250
+ ...(config.volumes !== undefined && { volumes: config.volumes }),
251
+ ...(config.capAdd !== undefined && { capAdd: config.capAdd }),
252
+ ...(config.capDrop !== undefined && { capDrop: config.capDrop }),
253
+ ...(config.privileged !== undefined && { privileged: config.privileged }),
254
+ ...(config.user !== undefined && { user: config.user }),
240
255
  };
241
256
  const effectResource = await Effect.runPromise(EffectContainer(name, effectConfig, docker).pipe(Scope.extend(scope)));
242
257
  const resourceId = `container:${effectResource.id}`;
@@ -298,7 +313,7 @@ export async function database(name, config) {
298
313
  const docker = await getDockerAsync();
299
314
  const scope = Effect.runSync(Scope.make());
300
315
  // Convert NetworkResource to { name: string }
301
- const networks = config.networks?.map((n) => 'destroy' in n ? { name: n.name } : n);
316
+ const networks = config.networks?.map((n) => ('destroy' in n ? { name: n.name } : n));
302
317
  const effectConfig = {
303
318
  image: config.image,
304
319
  ...(config.port !== undefined && { port: config.port }),
@@ -380,7 +395,7 @@ export async function destroyAll() {
380
395
  await resource.cleanup();
381
396
  }
382
397
  catch (e) {
383
- console.error(`[Cleanup] Error cleaning up ${id}:`, e);
398
+ harpoonLog.error(`Error cleaning up ${id}`, e);
384
399
  }
385
400
  }
386
401
  resources.clear();
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * Exports binding types and utilities for creating resource bindings.
5
5
  */
6
- export type { Binding, BuildBinding, BindingsEnv, FlatBindingsEnv, } from './types';
7
- export { isBuildBinding, mergeBindingsEnv, createEnvBinding, } from './types';
6
+ export type { Binding, BuildBinding, BindingsEnv, FlatBindingsEnv } from './types';
7
+ export { isBuildBinding, mergeBindingsEnv, createEnvBinding } from './types';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bindings/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EACV,OAAO,EACP,YAAY,EACZ,WAAW,EACX,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bindings/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEnF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * Exports binding types and utilities for creating resource bindings.
5
5
  */
6
- export { isBuildBinding, mergeBindingsEnv, createEnvBinding, } from './types';
6
+ export { isBuildBinding, mergeBindingsEnv, createEnvBinding } from './types';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bindings/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bindings/types.ts"],"names":[],"mappings":"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"}