postgres-memory-server 0.2.1 → 0.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgres-memory-server",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "spin up an actual/real postgres server programmatically from within nodejs, for testing or mocking during development",
5
5
  "homepage": "https://github.com/amanthegreatone/postgres-memory-server#readme",
6
6
  "bugs": {
@@ -30,6 +30,7 @@
30
30
  },
31
31
  "files": [
32
32
  "dist",
33
+ "scripts/postinstall.cjs",
33
34
  "README.md",
34
35
  "LICENSE"
35
36
  ],
@@ -46,6 +47,7 @@
46
47
  "build": "tsup",
47
48
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
48
49
  "dev": "tsup --watch",
50
+ "postinstall": "node scripts/postinstall.cjs",
49
51
  "prepack": "npm run build",
50
52
  "test:paradedb": "npm run build && vitest run test/paradedb.test.ts examples/paradedb/hybrid-search.test.ts",
51
53
  "test:postgres": "npm run build && vitest run test/basic.test.ts test/jest.test.ts test/presets.test.ts test/snapshot.test.ts examples/vitest/basic.test.ts",
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable */
3
+ /**
4
+ * Postinstall hotfix for embedded-postgres on Apple Silicon.
5
+ *
6
+ * The @embedded-postgres/darwin-arm64 platform package (as of
7
+ * 18.3.0-beta.16) ships Mach-O universal binaries that contain both
8
+ * x86_64 and arm64 slices. On some Apple Silicon Macs the universal
9
+ * binary fails to start or hangs under load, which manifests as test
10
+ * suites hanging indefinitely during `pg.start()`.
11
+ *
12
+ * This script thins every Mach-O file in the platform package down to
13
+ * the arm64 slice only using `lipo -thin arm64`. It is a no-op on
14
+ * anything that is not darwin-arm64, on files that are already thin,
15
+ * and on systems without `lipo` (so it never breaks an install).
16
+ *
17
+ * Tracking upstream: https://github.com/leinelissen/embedded-postgres
18
+ */
19
+
20
+ "use strict";
21
+
22
+ const fs = require("node:fs");
23
+ const path = require("node:path");
24
+ const { execFileSync, spawnSync } = require("node:child_process");
25
+
26
+ // Bail silently on anything that is not darwin-arm64. The fix only
27
+ // applies to Apple Silicon.
28
+ if (process.platform !== "darwin" || process.arch !== "arm64") {
29
+ process.exit(0);
30
+ }
31
+
32
+ // Users can opt out via env var (useful for debugging or if upstream
33
+ // ever ships fixed binaries and the thinning becomes counterproductive).
34
+ if (process.env.POSTGRES_MEMORY_SERVER_SKIP_THIN === "1") {
35
+ process.exit(0);
36
+ }
37
+
38
+ function log(message) {
39
+ // Keep output quiet unless something is actually happening. npm shows
40
+ // postinstall stdout by default; we don't want to spam.
41
+ if (process.env.POSTGRES_MEMORY_SERVER_THIN_VERBOSE === "1") {
42
+ process.stdout.write(`[postgres-memory-server] ${message}\n`);
43
+ }
44
+ }
45
+
46
+ function lipoAvailable() {
47
+ const result = spawnSync("lipo", ["-info", "/dev/null"], {
48
+ stdio: "ignore",
49
+ });
50
+ return result.error === undefined;
51
+ }
52
+
53
+ function findNativeDir() {
54
+ // Resolve the platform package's main entry and walk up to find the
55
+ // `native/` directory. We avoid requiring the package's package.json
56
+ // directly because the package's `exports` field does not expose it.
57
+ let mainEntry;
58
+ try {
59
+ mainEntry = require.resolve("@embedded-postgres/darwin-arm64");
60
+ } catch {
61
+ return null;
62
+ }
63
+
64
+ let dir = path.dirname(mainEntry);
65
+ while (dir !== path.dirname(dir)) {
66
+ const candidate = path.join(dir, "native");
67
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
68
+ return candidate;
69
+ }
70
+ dir = path.dirname(dir);
71
+ }
72
+ return null;
73
+ }
74
+
75
+ function isMachO(filePath) {
76
+ // Mach-O magic numbers:
77
+ // 0xfeedface / 0xfeedfacf — single-arch 32/64 bit
78
+ // 0xcafebabe / 0xcafebabf — fat (universal) 32/64 bit
79
+ // each has a byte-swapped variant as well.
80
+ try {
81
+ const fd = fs.openSync(filePath, "r");
82
+ const buf = Buffer.alloc(4);
83
+ fs.readSync(fd, buf, 0, 4, 0);
84
+ fs.closeSync(fd);
85
+ const magic = buf.readUInt32BE(0);
86
+ return (
87
+ magic === 0xfeedface ||
88
+ magic === 0xfeedfacf ||
89
+ magic === 0xcefaedfe ||
90
+ magic === 0xcffaedfe ||
91
+ magic === 0xcafebabe ||
92
+ magic === 0xcafebabf ||
93
+ magic === 0xbebafeca ||
94
+ magic === 0xbfbafeca
95
+ );
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ function isFatBinary(filePath) {
102
+ try {
103
+ const fd = fs.openSync(filePath, "r");
104
+ const buf = Buffer.alloc(4);
105
+ fs.readSync(fd, buf, 0, 4, 0);
106
+ fs.closeSync(fd);
107
+ const magic = buf.readUInt32BE(0);
108
+ return (
109
+ magic === 0xcafebabe ||
110
+ magic === 0xcafebabf ||
111
+ magic === 0xbebafeca ||
112
+ magic === 0xbfbafeca
113
+ );
114
+ } catch {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ function hasArm64Slice(filePath) {
120
+ try {
121
+ const out = execFileSync("lipo", ["-archs", filePath], {
122
+ encoding: "utf8",
123
+ });
124
+ return out.split(/\s+/).includes("arm64");
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
129
+
130
+ function thinToArm64(filePath) {
131
+ const tmpPath = `${filePath}.arm64.tmp`;
132
+ try {
133
+ execFileSync("lipo", ["-thin", "arm64", filePath, "-output", tmpPath], {
134
+ stdio: "ignore",
135
+ });
136
+ } catch (err) {
137
+ try {
138
+ fs.unlinkSync(tmpPath);
139
+ } catch {}
140
+ throw err;
141
+ }
142
+
143
+ // Preserve original permissions (particularly the executable bit).
144
+ const origStat = fs.statSync(filePath);
145
+ fs.chmodSync(tmpPath, origStat.mode);
146
+
147
+ fs.renameSync(tmpPath, filePath);
148
+ }
149
+
150
+ function walk(dir, visit) {
151
+ let entries;
152
+ try {
153
+ entries = fs.readdirSync(dir, { withFileTypes: true });
154
+ } catch {
155
+ return;
156
+ }
157
+ for (const entry of entries) {
158
+ const full = path.join(dir, entry.name);
159
+ if (entry.isSymbolicLink()) {
160
+ // Skip symlinks — they'll be visited via their target directory.
161
+ continue;
162
+ }
163
+ if (entry.isDirectory()) {
164
+ walk(full, visit);
165
+ } else if (entry.isFile()) {
166
+ visit(full);
167
+ }
168
+ }
169
+ }
170
+
171
+ function main() {
172
+ if (!lipoAvailable()) {
173
+ log("lipo not found in PATH; skipping universal binary thinning");
174
+ return;
175
+ }
176
+
177
+ const nativeDir = findNativeDir();
178
+ if (!nativeDir) {
179
+ log("@embedded-postgres/darwin-arm64 not installed; nothing to thin");
180
+ return;
181
+ }
182
+
183
+ let scanned = 0;
184
+ let thinned = 0;
185
+ let skipped = 0;
186
+ let failed = 0;
187
+
188
+ walk(nativeDir, (filePath) => {
189
+ scanned += 1;
190
+ if (!isMachO(filePath)) {
191
+ return;
192
+ }
193
+ if (!isFatBinary(filePath)) {
194
+ skipped += 1; // already thin
195
+ return;
196
+ }
197
+ if (!hasArm64Slice(filePath)) {
198
+ // Weird — a universal binary without arm64. Leave it alone.
199
+ skipped += 1;
200
+ return;
201
+ }
202
+ try {
203
+ thinToArm64(filePath);
204
+ thinned += 1;
205
+ } catch (err) {
206
+ failed += 1;
207
+ log(`failed to thin ${filePath}: ${err && err.message}`);
208
+ }
209
+ });
210
+
211
+ if (thinned > 0 || failed > 0) {
212
+ // Only emit a visible line when we actually did work, to keep
213
+ // normal installs quiet.
214
+ process.stdout.write(
215
+ `[postgres-memory-server] thinned ${thinned} universal binar${
216
+ thinned === 1 ? "y" : "ies"
217
+ } to arm64` +
218
+ (failed > 0 ? ` (${failed} failed)` : "") +
219
+ `\n`,
220
+ );
221
+ } else {
222
+ log(
223
+ `nothing to do (${scanned} files scanned, ${skipped} already thin)`,
224
+ );
225
+ }
226
+ }
227
+
228
+ try {
229
+ main();
230
+ } catch (err) {
231
+ // Never break an install. Print a warning and move on.
232
+ process.stderr.write(
233
+ `[postgres-memory-server] postinstall warning: ${
234
+ (err && err.message) || err
235
+ }\n`,
236
+ );
237
+ }