embedded-postgres-node 0.0.0-stage → 0.1.0-alpha.2

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -2
  3. package/dist/cjs/binary.d.ts +24 -0
  4. package/dist/cjs/binary.js +168 -0
  5. package/dist/cjs/binary.js.map +1 -0
  6. package/dist/cjs/errors.d.ts +9 -0
  7. package/dist/cjs/errors.js +35 -0
  8. package/dist/cjs/errors.js.map +1 -0
  9. package/dist/cjs/index.d.ts +7 -0
  10. package/dist/cjs/index.js +13 -0
  11. package/dist/cjs/index.js.map +1 -0
  12. package/dist/cjs/lifecycle.d.ts +21 -0
  13. package/dist/cjs/lifecycle.js +209 -0
  14. package/dist/cjs/lifecycle.js.map +1 -0
  15. package/dist/cjs/options.d.ts +46 -0
  16. package/dist/cjs/options.js +79 -0
  17. package/dist/cjs/options.js.map +1 -0
  18. package/dist/cjs/package.json +1 -0
  19. package/dist/cjs/protocol.d.ts +22 -0
  20. package/dist/cjs/protocol.js +70 -0
  21. package/dist/cjs/protocol.js.map +1 -0
  22. package/dist/esm/binary.d.ts +24 -0
  23. package/dist/esm/binary.js +162 -0
  24. package/dist/esm/binary.js.map +1 -0
  25. package/dist/esm/errors.d.ts +9 -0
  26. package/dist/esm/errors.js +29 -0
  27. package/dist/esm/errors.js.map +1 -0
  28. package/dist/esm/index.d.ts +7 -0
  29. package/dist/esm/index.js +4 -0
  30. package/dist/esm/index.js.map +1 -0
  31. package/dist/esm/lifecycle.d.ts +21 -0
  32. package/dist/esm/lifecycle.js +205 -0
  33. package/dist/esm/lifecycle.js.map +1 -0
  34. package/dist/esm/options.d.ts +46 -0
  35. package/dist/esm/options.js +76 -0
  36. package/dist/esm/options.js.map +1 -0
  37. package/dist/esm/protocol.d.ts +22 -0
  38. package/dist/esm/protocol.js +66 -0
  39. package/dist/esm/protocol.js.map +1 -0
  40. package/docs/api.md +73 -0
  41. package/docs/binaries.md +50 -0
  42. package/docs/integration.md +39 -0
  43. package/docs/releasing.md +41 -0
  44. package/docs/validation.md +94 -0
  45. package/examples/jest.config.cjs +1 -0
  46. package/examples/jest.test.cjs +22 -0
  47. package/examples/migrations.mjs +17 -0
  48. package/examples/node-test.mjs +22 -0
  49. package/examples/options.mjs +6 -0
  50. package/examples/scoped.mjs +21 -0
  51. package/examples/vitest.config.mjs +2 -0
  52. package/examples/vitest.test.mjs +21 -0
  53. package/package.json +73 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Fergus Strange
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,114 @@
1
- # Temporary Holding Version
1
+ # embedded-postgres-node
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Real PostgreSQL for Node.js tests, supervised by the [embedded-postgres v2 CLI](https://github.com/fergusstrange/embedded-postgres/releases/tag/v2.0.0-alpha.1). TypeScript types, ESM and CommonJS, dynamic ports, explicit async cleanup, and **zero runtime npm dependencies**. Use your own PostgreSQL driver, test runner and migration framework.
4
+
5
+ **Alpha:** `0.1.0-alpha.2` pins CLI `v2.0.0-alpha.1` with verified SHA-256 checksums for all six platforms. The API may change before a stable release. Prereleases use npm's `next` tag.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm install --save-dev --save-exact embedded-postgres-node@0.1.0-alpha.2
11
+ ```
12
+
13
+ To run the examples from this repository:
14
+
15
+ ```sh
16
+ # In this repository; use Node 22 or 24.
17
+ npm ci
18
+ npm run build
19
+ node --test examples/node-test.mjs
20
+ ```
21
+
22
+ The first startup downloads and verifies the pinned CLI, then acquires PostgreSQL through the CLI's own verified manifest. Later starts reuse verified caches. Consumers do not need Go. PostgreSQL's native runtime-library requirements still apply. No package install script downloads or executes binaries.
23
+
24
+ `startPostgres()` works without CLI configuration. An explicit `cli: { path }` or `EMBEDDED_POSTGRES_CLI` overrides the default release. To test unpublished source changes in another project, run `npm pack` here and install the resulting tarball there.
25
+
26
+ ## A database for a test
27
+
28
+ ```js
29
+ import { withPostgres } from 'embedded-postgres-node';
30
+ import pg from 'pg'; // Install your own driver as a development dependency.
31
+
32
+ await withPostgres({}, async database => {
33
+ const pool = new pg.Pool({ connectionString: database.connectionUrl });
34
+ try {
35
+ await pool.query('CREATE TABLE widgets (id integer PRIMARY KEY)');
36
+ // Run migrations and tests here.
37
+ } finally {
38
+ await pool.end();
39
+ }
40
+ });
41
+ ```
42
+
43
+ `withPostgres` returns your callback's result and awaits cleanup on success or failure. If both fail, it throws an `AggregateError` containing both errors. There are no framework-specific core dependencies or implicit global signal handlers.
44
+
45
+ For suite-level setup, use `startPostgres()` in your runner's setup hook and `await database.stop()` in teardown. Close pools first. Increase the runner's hook timeout to allow initial binary downloads; share a server only when tests isolate their own schemas or data. Each separately started instance gets its own disposable directory and dynamic port.
46
+
47
+ Runnable examples:
48
+
49
+ - [node:test suite](examples/node-test.mjs)
50
+ - [Vitest suite](examples/vitest.test.mjs) and [config](examples/vitest.config.mjs)
51
+ - [Jest suite (CommonJS)](examples/jest.test.cjs) and [config](examples/jest.config.cjs)
52
+ - [Explicit try/finally and scoped helper](examples/scoped.mjs)
53
+ - [User-owned migration and extension setup](examples/migrations.mjs)
54
+
55
+ ## Explicit ownership
56
+
57
+ ```js
58
+ import { startPostgres } from 'embedded-postgres-node';
59
+
60
+ const database = await startPostgres();
61
+ try {
62
+ // database.connectionUrl is a credential. Do not log or snapshot it.
63
+ // database.port is available after an authenticated readiness check.
64
+ } finally {
65
+ await database.stop(); // Idempotent, including concurrent calls.
66
+ }
67
+ ```
68
+
69
+ An optional `signal` controls the entire database lifetime. Cancellation requests cleanup and reports `PostgresError` with `code: 'ABORTED'`. The signal's reason is not copied into diagnostics. `database.closed` always resolves to an exit result, including errors, so a background failure cannot cause an unhandled promise rejection. Observe it for long-lived suites; `stop()` also reports any failure. `[Symbol.asyncDispose]()` delegates to `stop()` for TypeScript `await using` or runtimes supporting that syntax.
70
+
71
+ ## Configuration
72
+
73
+ ```ts
74
+ const database = await startPostgres({
75
+ cli: { cacheDir: '/path/to/shared-cli-cache' }, // Optional; uses the pinned CLI.
76
+ postgresVersion: '18.6.0',
77
+ database: 'application_test',
78
+ port: 0,
79
+ storage: { type: 'disposable', workDir: '/tmp/test-work' },
80
+ cacheDir: '/path/to/shared-postgres-cache',
81
+ parameters: { max_connections: '40' },
82
+ startTimeoutMs: 120_000,
83
+ stopTimeoutMs: 10_000,
84
+ });
85
+ ```
86
+
87
+ Defaults select a dynamic port and a random password. `workDir` is a parent for private disposable children; it is never recursively erased by the wrapper. For persistence, choose `storage: { type: 'persistent', dataDir: '/path/to/data' }` **and explicit `database`, `username`, and `password`**. Data and the adjacent CLI lease file survive shutdown. Reuse the same credentials on restart; deleting persistent data is your responsibility.
88
+
89
+ `binaries` supplies an existing PostgreSQL distribution containing `bin/`. `runAs: { uid, gid }` selects an existing non-root Unix account; the wrapper does not invoke sudo or create users. `cliOptions` provides advanced flags, for example `{ offline: true, 'socket-dir': '/tmp/sockets' }`. The CLI rejects unknown flags. Protocol, timeouts, credentials, configuration files and core options cannot be overridden through this escape hatch. Ambient `EP_*` variables are removed from the child environment; use the typed options and explicit escape hatch. Other environment variables are inherited, with additions through `env`.
90
+
91
+ See the [full API](docs/api.md), [binary acquisition](docs/binaries.md), and [integration and migration guide](docs/integration.md).
92
+
93
+ ## Cleanup and diagnostics
94
+
95
+ The wrapper spawns `run --json --parent-stdin`, keeps stdin open until teardown, parses protocol 1 incrementally, and waits for **both `stopped` and process exit**. Shutdown uses its own deadline, even after cancellation. The default outer shutdown deadline is `stopTimeoutMs + 5 seconds`; an override must exceed `stopTimeoutMs + 2 seconds`. If it expires, the wrapper terminates only its retained CLI child and reports that PostgreSQL cleanup cannot be confirmed. It waits up to two further seconds for process/pipe closure.
96
+
97
+ A Node crash or forced Node termination closes the ownership pipe; the CLI and its independent supervisor perform cleanup. Whole-machine failure, simultaneous termination of the supervisor and PostgreSQL, or an unresponsive supervisor cannot be promised graceful cleanup. See [upstream lifecycle limits](https://github.com/fergusstrange/embedded-postgres/blob/v2.0.0-alpha.1/docs/cli.md). Normal application shutdown should await `stop()`; no import installs process signal handlers. If your application owns signals, install handlers there.
98
+
99
+ Errors expose a stable `code`, a descriptive `message`, and a bounded `stderr` tail. Known passwords and PostgreSQL URLs are redacted; raw protocol, argv, abort reasons and child error causes are not included. Arbitrary custom programs can emit other secrets, so review diagnostics before sharing them. The instance's normal JSON serialization and inspection omit the connection URL; reading the property still returns the real credential.
100
+
101
+ ## Support and development
102
+
103
+ Node 22 and 24 are the supported LTS lines as of 2026-10-05 ([official schedule](https://github.com/nodejs/Release#release-schedule)); Node 20 is EOL, and Node 26 is not yet LTS. CI is configured for both LTS versions on Linux, macOS and Windows, each on x64 and ARM64. Windows ARM64 uses a native CLI and x64 PostgreSQL under emulation. Linux PostgreSQL runtime libraries are still required; Alpine/musl needs a compatible distribution. See [upstream non-root and native requirements](https://github.com/fergusstrange/embedded-postgres/blob/v2.0.0-alpha.1/docs/non-root.md).
104
+
105
+ ```sh
106
+ npm run check # Coverage >=90% and installed-tarball smoke/type checks
107
+ npm run check:release # Fresh verified downloads, real SQL, offline reuse from installed tarball
108
+ npm run test:integration # Requires EMBEDDED_POSTGRES_CLI; fails if missing
109
+ npm run test:examples # Runs all three runners and both hook/scoped examples
110
+ ```
111
+
112
+ Integration tests run SQL and verify port/workspace cleanup after teardown, startup failure, abort and Node parent death, plus persistent restarts. The [validation record](docs/validation.md) includes local and native CI results. [Contributing](CONTRIBUTING.md) explains the pinned upstream development build and CI. [Release preparation](docs/releasing.md) describes the review gate; nothing is published automatically on push.
113
+
114
+ MIT licensed. PostgreSQL distributions retain their own licenses.
@@ -0,0 +1,24 @@
1
+ export type CliPlatform = 'linux-amd64' | 'linux-arm64' | 'darwin-amd64' | 'darwin-arm64' | 'windows-amd64' | 'windows-arm64';
2
+ export interface CliRelease {
3
+ /** Exact published v2 tag. There is deliberately no implicit latest release. */
4
+ version: string;
5
+ /** Trusted SHA-256 pins, copied from reviewed upstream release checksums.txt. */
6
+ checksums: Readonly<Partial<Record<CliPlatform, string>>>;
7
+ /** Directory containing VERSION/ASSET, defaulting to upstream GitHub releases. */
8
+ baseUrl?: string;
9
+ }
10
+ /** Reviewed upstream release; every platform asset was checked against checksums.txt. */
11
+ export declare const DEFAULT_CLI_RELEASE: Readonly<CliRelease>;
12
+ export type CliSource = {
13
+ path: string;
14
+ } | {
15
+ /** Omit to use this package's reviewed DEFAULT_CLI_RELEASE. */
16
+ release?: CliRelease;
17
+ cacheDir?: string;
18
+ offline?: boolean;
19
+ downloadTimeoutMs?: number;
20
+ };
21
+ export declare function cliPlatform(platform?: string, arch?: string): CliPlatform;
22
+ export declare function cliAsset(target: CliPlatform): string;
23
+ /** Explicit source, then EMBEDDED_POSTGRES_CLI, then the package's pinned release. */
24
+ export declare function resolveCli(source?: CliSource, signal?: AbortSignal): Promise<string>;
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_CLI_RELEASE = void 0;
4
+ exports.cliPlatform = cliPlatform;
5
+ exports.cliAsset = cliAsset;
6
+ exports.resolveCli = resolveCli;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const node_fs_1 = require("node:fs");
9
+ const promises_1 = require("node:fs/promises");
10
+ const node_fs_2 = require("node:fs");
11
+ const node_os_1 = require("node:os");
12
+ const node_path_1 = require("node:path");
13
+ const errors_js_1 = require("./errors.js");
14
+ /** Reviewed upstream release; every platform asset was checked against checksums.txt. */
15
+ exports.DEFAULT_CLI_RELEASE = Object.freeze({
16
+ version: 'v2.0.0-alpha.1',
17
+ checksums: Object.freeze({
18
+ 'linux-amd64': '69ef1bd6ca8ec00acf74fe90a5b6204657caa833934aada36591000b90daa7c6',
19
+ 'linux-arm64': '9bb6f2febcf3ce92b678c25aa1e06ae2e0a46a728b6da3ef31709aed2a2493a5',
20
+ 'darwin-amd64': '0f28163e7f346f444d7b3e790205df22e4e92d88acd2de15bd650fa45b114ffb',
21
+ 'darwin-arm64': '97866b33b4a59a5f60a807b55593af162d65f4e4f903696f2c1f638c01de19c0',
22
+ 'windows-amd64': '787e744b5c1c8ae88f77a043d0e3400a220e07e9ce4d03a55b9073a97f4cee6e',
23
+ 'windows-arm64': '07ee6b62c24c0e1a61a3523cf6331560b8a0f64ab780bb5ee6240541a568364d',
24
+ }),
25
+ });
26
+ function cliPlatform(platform = process.platform, arch = process.arch) {
27
+ const os = platform === 'win32' ? 'windows' : platform;
28
+ const cpu = arch === 'x64' ? 'amd64' : arch;
29
+ if (!['linux', 'darwin', 'windows'].includes(os) || !['amd64', 'arm64'].includes(cpu)) {
30
+ throw new errors_js_1.PostgresError('BINARY', 'CLI supports Linux, macOS and Windows on x64 and arm64');
31
+ }
32
+ return `${os}-${cpu}`;
33
+ }
34
+ function cliAsset(target) {
35
+ return `embedded-postgres_${target.replace('-', '_')}${target.startsWith('windows-') ? '.exe' : ''}`;
36
+ }
37
+ async function digest(path) {
38
+ try {
39
+ const hash = (0, node_crypto_1.createHash)('sha256');
40
+ for await (const chunk of (0, node_fs_1.createReadStream)(path))
41
+ hash.update(chunk);
42
+ return hash.digest('hex');
43
+ }
44
+ catch (error) {
45
+ if (error.code === 'ENOENT')
46
+ return undefined;
47
+ throw error;
48
+ }
49
+ }
50
+ const maximumBytes = 128 * 1024 * 1024;
51
+ function validateUrl(url, allowQuery = false) {
52
+ if (url.username || url.password || (!allowQuery && url.search) || url.hash || !(url.protocol === 'https:' || (url.protocol === 'http:' && ['127.0.0.1', '[::1]', 'localhost'].includes(url.hostname)))) {
53
+ throw new errors_js_1.PostgresError('CONFIG', 'CLI downloads require HTTPS (HTTP is allowed only for loopback mirrors), without credentials, query or fragment');
54
+ }
55
+ }
56
+ async function download(url, signal) {
57
+ for (let redirects = 0; redirects <= 5; redirects++) {
58
+ validateUrl(url, true); // GitHub's asset CDN uses signed query parameters.
59
+ const response = await fetch(url, { signal, redirect: 'manual' });
60
+ if (![301, 302, 303, 307, 308].includes(response.status))
61
+ return response;
62
+ await response.body?.cancel();
63
+ const location = response.headers.get('location');
64
+ if (!location)
65
+ throw new errors_js_1.PostgresError('BINARY', 'CLI download redirect has no location');
66
+ url = new URL(location, url);
67
+ }
68
+ throw new errors_js_1.PostgresError('BINARY', 'CLI download has too many redirects');
69
+ }
70
+ /** Explicit source, then EMBEDDED_POSTGRES_CLI, then the package's pinned release. */
71
+ async function resolveCli(source, signal) {
72
+ if (signal?.aborted)
73
+ throw new errors_js_1.PostgresError('ABORTED', 'CLI resolution aborted');
74
+ source ??= process.env.EMBEDDED_POSTGRES_CLI ? { path: process.env.EMBEDDED_POSTGRES_CLI } : {};
75
+ if ('path' in source) {
76
+ if (!source.path || source.path.includes('\0'))
77
+ throw new errors_js_1.PostgresError('CONFIG', 'cli.path must name an executable');
78
+ const path = (0, node_path_1.resolve)(source.path);
79
+ try {
80
+ await (0, promises_1.access)(path, node_fs_2.constants.X_OK);
81
+ }
82
+ catch {
83
+ throw new errors_js_1.PostgresError('BINARY', 'Local CLI is missing or not executable');
84
+ }
85
+ return path;
86
+ }
87
+ const release = source.release ?? exports.DEFAULT_CLI_RELEASE;
88
+ if (!/^v2\.\d+\.\d+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$/.test(release.version))
89
+ throw new errors_js_1.PostgresError('CONFIG', 'CLI release must be an exact v2 tag');
90
+ const target = cliPlatform();
91
+ const expected = release.checksums[target]?.toLowerCase();
92
+ if (!expected || !/^[a-f0-9]{64}$/.test(expected))
93
+ throw new errors_js_1.PostgresError('CONFIG', 'A trusted SHA-256 checksum is required for this CLI platform');
94
+ const timeout = (0, errors_js_1.positiveTimeout)(source.downloadTimeoutMs ?? 120_000, 'downloadTimeoutMs');
95
+ let base;
96
+ try {
97
+ base = new URL(release.baseUrl ?? 'https://github.com/fergusstrange/embedded-postgres/releases/download/');
98
+ }
99
+ catch {
100
+ throw new errors_js_1.PostgresError('CONFIG', 'Invalid CLI release base URL');
101
+ }
102
+ validateUrl(base);
103
+ base.pathname = base.pathname.replace(/\/?$/, '/');
104
+ const asset = cliAsset(target);
105
+ const directory = (0, node_path_1.join)((0, node_path_1.resolve)(source.cacheDir ?? (0, node_path_1.join)((0, node_os_1.homedir)(), '.cache', 'embedded-postgres-node')), release.version, expected);
106
+ const destination = (0, node_path_1.join)(directory, asset);
107
+ let temporary;
108
+ try {
109
+ if (await digest(destination) === expected) {
110
+ await (0, promises_1.chmod)(destination, 0o700);
111
+ return destination;
112
+ }
113
+ if (source.offline)
114
+ throw new errors_js_1.PostgresError('BINARY', 'Verified CLI is unavailable in the offline cache');
115
+ await (0, promises_1.mkdir)(directory, { recursive: true, mode: 0o700 });
116
+ temporary = (0, node_path_1.join)(directory, `.${asset}.${(0, node_crypto_1.randomUUID)()}.tmp`);
117
+ const downloadSignal = AbortSignal.any([AbortSignal.timeout(timeout), ...(signal ? [signal] : [])]);
118
+ const response = await download(new URL(`${release.version}/${asset}`, base), downloadSignal);
119
+ if (!response.ok || !response.body) {
120
+ await response.body?.cancel();
121
+ throw new errors_js_1.PostgresError('BINARY', `CLI download failed (HTTP ${response.status}); verify that the pinned release has been published`);
122
+ }
123
+ if (Number(response.headers.get('content-length')) > maximumBytes) {
124
+ await response.body.cancel();
125
+ throw new errors_js_1.PostgresError('BINARY', 'CLI download exceeds 128 MiB');
126
+ }
127
+ const file = await (0, promises_1.open)(temporary, 'wx', 0o700);
128
+ const hash = (0, node_crypto_1.createHash)('sha256');
129
+ let size = 0;
130
+ try {
131
+ for await (const chunk of response.body) {
132
+ size += chunk.byteLength;
133
+ if (size > maximumBytes)
134
+ throw new errors_js_1.PostgresError('BINARY', 'CLI download exceeds 128 MiB');
135
+ hash.update(chunk);
136
+ await file.writeFile(chunk);
137
+ }
138
+ }
139
+ finally {
140
+ await file.close();
141
+ }
142
+ if (hash.digest('hex') !== expected)
143
+ throw new errors_js_1.PostgresError('BINARY', 'CLI checksum mismatch; downloaded binary was not installed');
144
+ // Atomic replacement; concurrent callers install identical verified bytes.
145
+ try {
146
+ await (0, promises_1.rename)(temporary, destination);
147
+ }
148
+ catch (error) {
149
+ if (await digest(destination) !== expected)
150
+ throw error;
151
+ }
152
+ return destination;
153
+ }
154
+ catch (error) {
155
+ if (signal?.aborted)
156
+ throw new errors_js_1.PostgresError('ABORTED', 'CLI resolution aborted');
157
+ if (error instanceof errors_js_1.PostgresError)
158
+ throw error;
159
+ if (error.name === 'TimeoutError')
160
+ throw new errors_js_1.PostgresError('TIMEOUT', 'CLI download deadline exceeded');
161
+ throw new errors_js_1.PostgresError('BINARY', 'Unable to resolve CLI binary; check network and cache permissions');
162
+ }
163
+ finally {
164
+ if (temporary)
165
+ await (0, promises_1.rm)(temporary, { force: true });
166
+ }
167
+ }
168
+ //# sourceMappingURL=binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary.js","sourceRoot":"","sources":["../../.local/build/src/binary.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from 'node:crypto';\nimport { createReadStream } from 'node:fs';\nimport { access, chmod, mkdir, open, rename, rm } from 'node:fs/promises';\nimport { constants } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { PostgresError, positiveTimeout } from './errors.js';\n\nexport type CliPlatform = 'linux-amd64' | 'linux-arm64' | 'darwin-amd64' | 'darwin-arm64' | 'windows-amd64' | 'windows-arm64';\nexport interface CliRelease {\n /** Exact published v2 tag. There is deliberately no implicit latest release. */\n version: string;\n /** Trusted SHA-256 pins, copied from reviewed upstream release checksums.txt. */\n checksums: Readonly<Partial<Record<CliPlatform, string>>>;\n /** Directory containing VERSION/ASSET, defaulting to upstream GitHub releases. */\n baseUrl?: string;\n}\n\n/** Reviewed upstream release; every platform asset was checked against checksums.txt. */\nexport const DEFAULT_CLI_RELEASE: Readonly<CliRelease> = Object.freeze({\n version: 'v2.0.0-alpha.1',\n checksums: Object.freeze({\n 'linux-amd64': '69ef1bd6ca8ec00acf74fe90a5b6204657caa833934aada36591000b90daa7c6',\n 'linux-arm64': '9bb6f2febcf3ce92b678c25aa1e06ae2e0a46a728b6da3ef31709aed2a2493a5',\n 'darwin-amd64': '0f28163e7f346f444d7b3e790205df22e4e92d88acd2de15bd650fa45b114ffb',\n 'darwin-arm64': '97866b33b4a59a5f60a807b55593af162d65f4e4f903696f2c1f638c01de19c0',\n 'windows-amd64': '787e744b5c1c8ae88f77a043d0e3400a220e07e9ce4d03a55b9073a97f4cee6e',\n 'windows-arm64': '07ee6b62c24c0e1a61a3523cf6331560b8a0f64ab780bb5ee6240541a568364d',\n }),\n});\n\nexport type CliSource = { path: string } | {\n /** Omit to use this package's reviewed DEFAULT_CLI_RELEASE. */\n release?: CliRelease;\n cacheDir?: string;\n offline?: boolean;\n downloadTimeoutMs?: number;\n};\n\nexport function cliPlatform(platform: string = process.platform, arch: string = process.arch): CliPlatform {\n const os = platform === 'win32' ? 'windows' : platform;\n const cpu = arch === 'x64' ? 'amd64' : arch;\n if (!['linux', 'darwin', 'windows'].includes(os) || !['amd64', 'arm64'].includes(cpu)) {\n throw new PostgresError('BINARY', 'CLI supports Linux, macOS and Windows on x64 and arm64');\n }\n return `${os}-${cpu}` as CliPlatform;\n}\n\nexport function cliAsset(target: CliPlatform): string {\n return `embedded-postgres_${target.replace('-', '_')}${target.startsWith('windows-') ? '.exe' : ''}`;\n}\n\nasync function digest(path: string): Promise<string | undefined> {\n try {\n const hash = createHash('sha256');\n for await (const chunk of createReadStream(path)) hash.update(chunk);\n return hash.digest('hex');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nconst maximumBytes = 128 * 1024 * 1024;\nfunction validateUrl(url: URL, allowQuery = false): void {\n if (url.username || url.password || (!allowQuery && url.search) || url.hash || !(url.protocol === 'https:' || (url.protocol === 'http:' && ['127.0.0.1', '[::1]', 'localhost'].includes(url.hostname)))) {\n throw new PostgresError('CONFIG', 'CLI downloads require HTTPS (HTTP is allowed only for loopback mirrors), without credentials, query or fragment');\n }\n}\n\nasync function download(url: URL, signal: AbortSignal): Promise<Response> {\n for (let redirects = 0; redirects <= 5; redirects++) {\n validateUrl(url, true); // GitHub's asset CDN uses signed query parameters.\n const response = await fetch(url, { signal, redirect: 'manual' });\n if (![301, 302, 303, 307, 308].includes(response.status)) return response;\n await response.body?.cancel();\n const location = response.headers.get('location');\n if (!location) throw new PostgresError('BINARY', 'CLI download redirect has no location');\n url = new URL(location, url);\n }\n throw new PostgresError('BINARY', 'CLI download has too many redirects');\n}\n\n/** Explicit source, then EMBEDDED_POSTGRES_CLI, then the package's pinned release. */\nexport async function resolveCli(source?: CliSource, signal?: AbortSignal): Promise<string> {\n if (signal?.aborted) throw new PostgresError('ABORTED', 'CLI resolution aborted');\n source ??= process.env.EMBEDDED_POSTGRES_CLI ? { path: process.env.EMBEDDED_POSTGRES_CLI } : {};\n if ('path' in source) {\n if (!source.path || source.path.includes('\\0')) throw new PostgresError('CONFIG', 'cli.path must name an executable');\n const path = resolve(source.path);\n try { await access(path, constants.X_OK); } catch { throw new PostgresError('BINARY', 'Local CLI is missing or not executable'); }\n return path;\n }\n const release = source.release ?? DEFAULT_CLI_RELEASE;\n if (!/^v2\\.\\d+\\.\\d+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$/.test(release.version)) throw new PostgresError('CONFIG', 'CLI release must be an exact v2 tag');\n const target = cliPlatform();\n const expected = release.checksums[target]?.toLowerCase();\n if (!expected || !/^[a-f0-9]{64}$/.test(expected)) throw new PostgresError('CONFIG', 'A trusted SHA-256 checksum is required for this CLI platform');\n const timeout = positiveTimeout(source.downloadTimeoutMs ?? 120_000, 'downloadTimeoutMs');\n let base: URL;\n try { base = new URL(release.baseUrl ?? 'https://github.com/fergusstrange/embedded-postgres/releases/download/'); } catch { throw new PostgresError('CONFIG', 'Invalid CLI release base URL'); }\n validateUrl(base);\n base.pathname = base.pathname.replace(/\\/?$/, '/');\n const asset = cliAsset(target);\n const directory = join(resolve(source.cacheDir ?? join(homedir(), '.cache', 'embedded-postgres-node')), release.version, expected);\n const destination = join(directory, asset);\n let temporary: string | undefined;\n try {\n if (await digest(destination) === expected) {\n await chmod(destination, 0o700);\n return destination;\n }\n if (source.offline) throw new PostgresError('BINARY', 'Verified CLI is unavailable in the offline cache');\n await mkdir(directory, { recursive: true, mode: 0o700 });\n temporary = join(directory, `.${asset}.${randomUUID()}.tmp`);\n const downloadSignal = AbortSignal.any([AbortSignal.timeout(timeout), ...(signal ? [signal] : [])]);\n const response = await download(new URL(`${release.version}/${asset}`, base), downloadSignal);\n if (!response.ok || !response.body) {\n await response.body?.cancel();\n throw new PostgresError('BINARY', `CLI download failed (HTTP ${response.status}); verify that the pinned release has been published`);\n }\n if (Number(response.headers.get('content-length')) > maximumBytes) {\n await response.body.cancel();\n throw new PostgresError('BINARY', 'CLI download exceeds 128 MiB');\n }\n const file = await open(temporary, 'wx', 0o700);\n const hash = createHash('sha256');\n let size = 0;\n try {\n for await (const chunk of response.body) {\n size += chunk.byteLength;\n if (size > maximumBytes) throw new PostgresError('BINARY', 'CLI download exceeds 128 MiB');\n hash.update(chunk);\n await file.writeFile(chunk);\n }\n } finally { await file.close(); }\n if (hash.digest('hex') !== expected) throw new PostgresError('BINARY', 'CLI checksum mismatch; downloaded binary was not installed');\n // Atomic replacement; concurrent callers install identical verified bytes.\n try { await rename(temporary, destination); } catch (error) {\n if (await digest(destination) !== expected) throw error;\n }\n return destination;\n } catch (error) {\n if (signal?.aborted) throw new PostgresError('ABORTED', 'CLI resolution aborted');\n if (error instanceof PostgresError) throw error;\n if ((error as Error).name === 'TimeoutError') throw new PostgresError('TIMEOUT', 'CLI download deadline exceeded');\n throw new PostgresError('BINARY', 'Unable to resolve CLI binary; check network and cache permissions');\n } finally {\n if (temporary) await rm(temporary, { force: true });\n }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ export type ErrorCode = 'CONFIG' | 'BINARY' | 'SPAWN' | 'PROTOCOL' | 'STARTUP' | 'TIMEOUT' | 'ABORTED' | 'EXIT' | 'SHUTDOWN';
2
+ /** Diagnostics deliberately omit command arguments, raw protocol and error causes. */
3
+ export declare class PostgresError extends Error {
4
+ readonly code: ErrorCode;
5
+ readonly stderr: string;
6
+ constructor(code: ErrorCode, message: string, stderr?: string);
7
+ }
8
+ export declare function redact(text: string, secrets: readonly string[]): string;
9
+ export declare function positiveTimeout(value: number, name: string): number;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresError = void 0;
4
+ exports.redact = redact;
5
+ exports.positiveTimeout = positiveTimeout;
6
+ /** Diagnostics deliberately omit command arguments, raw protocol and error causes. */
7
+ class PostgresError extends Error {
8
+ code;
9
+ stderr;
10
+ constructor(code, message, stderr = '') {
11
+ super(message);
12
+ this.name = 'PostgresError';
13
+ this.code = code;
14
+ this.stderr = stderr;
15
+ }
16
+ }
17
+ exports.PostgresError = PostgresError;
18
+ function redact(text, secrets) {
19
+ // Redact complete URLs, including Unix-socket URLs and query parameters.
20
+ let result = text.replace(/postgres(?:ql)?:\/\/[^\s"'<>]+/gi, '[redacted connection URL]');
21
+ for (const secret of secrets) {
22
+ if (secret) {
23
+ result = result.split(secret).join('[redacted]');
24
+ result = result.split(encodeURIComponent(secret)).join('[redacted]');
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+ function positiveTimeout(value, name) {
30
+ if (!Number.isSafeInteger(value) || value < 1 || value > 2_147_483_647) {
31
+ throw new PostgresError('CONFIG', `${name} must be an integer between 1 and 2147483647 milliseconds`);
32
+ }
33
+ return value;
34
+ }
35
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../.local/build/src/errors.ts"],"names":[],"mappings":";;;;;AAEA,sFAAsF;AACtF,mBAA2B,SAAQ,KAAK;IAC7B,IAAI,CAAY;IAChB,MAAM,CAAS;IACxB,YAAY,IAAe,EAAE,OAAe,EAAE,MAAM,GAAG,EAAE;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;;AAED,gBAAuB,IAAY,EAAE,OAA0B;IAC7D,yEAAyE;IACzE,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,kCAAkC,EAAE,2BAA2B,CAAC,CAAC;IAC3F,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yBAAgC,KAAa,EAAE,IAAY;IACzD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,aAAa,EAAE,CAAC;QACvE,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,2DAA2D,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["export type ErrorCode = 'CONFIG' | 'BINARY' | 'SPAWN' | 'PROTOCOL' | 'STARTUP' | 'TIMEOUT' | 'ABORTED' | 'EXIT' | 'SHUTDOWN';\n\n/** Diagnostics deliberately omit command arguments, raw protocol and error causes. */\nexport class PostgresError extends Error {\n readonly code: ErrorCode;\n readonly stderr: string;\n constructor(code: ErrorCode, message: string, stderr = '') {\n super(message);\n this.name = 'PostgresError';\n this.code = code;\n this.stderr = stderr;\n }\n}\n\nexport function redact(text: string, secrets: readonly string[]): string {\n // Redact complete URLs, including Unix-socket URLs and query parameters.\n let result = text.replace(/postgres(?:ql)?:\\/\\/[^\\s\"'<>]+/gi, '[redacted connection URL]');\n for (const secret of secrets) {\n if (secret) {\n result = result.split(secret).join('[redacted]');\n result = result.split(encodeURIComponent(secret)).join('[redacted]');\n }\n }\n return result;\n}\n\nexport function positiveTimeout(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1 || value > 2_147_483_647) {\n throw new PostgresError('CONFIG', `${name} must be an integer between 1 and 2147483647 milliseconds`);\n }\n return value;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export { startPostgres, withPostgres } from './lifecycle.js';
2
+ export type { PostgresInstance, ExitResult } from './lifecycle.js';
3
+ export type { PostgresOptions } from './options.js';
4
+ export { resolveCli, cliPlatform, DEFAULT_CLI_RELEASE } from './binary.js';
5
+ export type { CliSource, CliRelease, CliPlatform } from './binary.js';
6
+ export { PostgresError } from './errors.js';
7
+ export type { ErrorCode } from './errors.js';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresError = exports.DEFAULT_CLI_RELEASE = exports.cliPlatform = exports.resolveCli = exports.withPostgres = exports.startPostgres = void 0;
4
+ var lifecycle_js_1 = require("./lifecycle.js");
5
+ Object.defineProperty(exports, "startPostgres", { enumerable: true, get: function () { return lifecycle_js_1.startPostgres; } });
6
+ Object.defineProperty(exports, "withPostgres", { enumerable: true, get: function () { return lifecycle_js_1.withPostgres; } });
7
+ var binary_js_1 = require("./binary.js");
8
+ Object.defineProperty(exports, "resolveCli", { enumerable: true, get: function () { return binary_js_1.resolveCli; } });
9
+ Object.defineProperty(exports, "cliPlatform", { enumerable: true, get: function () { return binary_js_1.cliPlatform; } });
10
+ Object.defineProperty(exports, "DEFAULT_CLI_RELEASE", { enumerable: true, get: function () { return binary_js_1.DEFAULT_CLI_RELEASE; } });
11
+ var errors_js_1 = require("./errors.js");
12
+ Object.defineProperty(exports, "PostgresError", { enumerable: true, get: function () { return errors_js_1.PostgresError; } });
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../.local/build/src/index.ts"],"names":[],"mappings":";;;AAAA,+CAA6D;AAApD,6GAAA,aAAa,OAAA;AAAE,4GAAA,YAAY,OAAA;AAGpC,yCAA2E;AAAlE,uGAAA,UAAU,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,gHAAA,mBAAmB,OAAA;AAErD,yCAA4C;AAAnC,0GAAA,aAAa,OAAA","sourcesContent":["export { startPostgres, withPostgres } from './lifecycle.js';\nexport type { PostgresInstance, ExitResult } from './lifecycle.js';\nexport type { PostgresOptions } from './options.js';\nexport { resolveCli, cliPlatform, DEFAULT_CLI_RELEASE } from './binary.js';\nexport type { CliSource, CliRelease, CliPlatform } from './binary.js';\nexport { PostgresError } from './errors.js';\nexport type { ErrorCode } from './errors.js';\n"]}
@@ -0,0 +1,21 @@
1
+ import { PostgresError } from './errors.js';
2
+ import type { PostgresOptions } from './options.js';
3
+ export interface ExitResult {
4
+ readonly exitCode: number | null;
5
+ readonly signal: NodeJS.Signals | null;
6
+ readonly stopped: boolean;
7
+ readonly error?: PostgresError;
8
+ }
9
+ export interface PostgresInstance extends AsyncDisposable {
10
+ /** A credential: do not log or snapshot it. */
11
+ readonly connectionUrl: string;
12
+ readonly port: number;
13
+ /** Resolves on termination; never rejects, so ignoring it cannot cause an unhandled rejection. */
14
+ readonly closed: Promise<ExitResult>;
15
+ /** Closes stdin and waits for stopped and process exit. Repeated calls share one result. */
16
+ stop(): Promise<void>;
17
+ }
18
+ /** Start a database whose lifetime is owned by the caller (or its AbortSignal). */
19
+ export declare function startPostgres(options?: PostgresOptions): Promise<PostgresInstance>;
20
+ /** User-owned migrations/pools belong inside fn; close pools before fn returns. */
21
+ export declare function withPostgres<T>(options: PostgresOptions, fn: (postgres: PostgresInstance) => Promise<T>): Promise<T>;