@essential-apps/shopify-test-runner 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contracts/normalize.d.ts +61 -0
- package/dist/contracts/normalize.d.ts.map +1 -0
- package/dist/contracts/normalize.js +99 -0
- package/dist/contracts/normalize.js.map +1 -0
- package/dist/contracts/normalizeHtml.d.ts +37 -0
- package/dist/contracts/normalizeHtml.d.ts.map +1 -0
- package/dist/contracts/normalizeHtml.js +89 -0
- package/dist/contracts/normalizeHtml.js.map +1 -0
- package/dist/edge/cert.d.ts +44 -0
- package/dist/edge/cert.d.ts.map +1 -0
- package/dist/edge/cert.js +117 -0
- package/dist/edge/cert.js.map +1 -0
- package/dist/edge/edgeProxy.d.ts +43 -0
- package/dist/edge/edgeProxy.d.ts.map +1 -0
- package/dist/edge/edgeProxy.js +297 -0
- package/dist/edge/edgeProxy.js.map +1 -0
- package/dist/edge/nodeShim.d.ts +2 -0
- package/dist/edge/nodeShim.d.ts.map +1 -0
- package/dist/edge/nodeShim.js +217 -0
- package/dist/edge/nodeShim.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/buildSourceBundle.d.ts +56 -0
- package/dist/lib/buildSourceBundle.d.ts.map +1 -0
- package/dist/lib/buildSourceBundle.js +153 -0
- package/dist/lib/buildSourceBundle.js.map +1 -0
- package/dist/lib/freePort.d.ts +5 -0
- package/dist/lib/freePort.d.ts.map +1 -0
- package/dist/lib/freePort.js +33 -0
- package/dist/lib/freePort.js.map +1 -0
- package/dist/lib/functionBuild.d.ts +99 -0
- package/dist/lib/functionBuild.d.ts.map +1 -0
- package/dist/lib/functionBuild.js +413 -0
- package/dist/lib/functionBuild.js.map +1 -0
- package/dist/lib/neonWsProxy.d.ts +41 -0
- package/dist/lib/neonWsProxy.d.ts.map +1 -0
- package/dist/lib/neonWsProxy.js +101 -0
- package/dist/lib/neonWsProxy.js.map +1 -0
- package/dist/lib/sourceZipUpload.d.ts +45 -0
- package/dist/lib/sourceZipUpload.d.ts.map +1 -0
- package/dist/lib/sourceZipUpload.js +129 -0
- package/dist/lib/sourceZipUpload.js.map +1 -0
- package/dist/lib/stealthLaunch.d.ts +35 -0
- package/dist/lib/stealthLaunch.d.ts.map +1 -0
- package/dist/lib/stealthLaunch.js +46 -0
- package/dist/lib/stealthLaunch.js.map +1 -0
- package/dist/lib/storeAutomation.d.ts +22 -0
- package/dist/lib/storeAutomation.d.ts.map +1 -0
- package/dist/lib/storeAutomation.js +85 -0
- package/dist/lib/storeAutomation.js.map +1 -0
- package/dist/playwright/baseConfig.d.ts +62 -0
- package/dist/playwright/baseConfig.d.ts.map +1 -0
- package/dist/playwright/baseConfig.js +68 -0
- package/dist/playwright/baseConfig.js.map +1 -0
- package/dist/playwright/globalSetup.d.ts +2 -0
- package/dist/playwright/globalSetup.d.ts.map +1 -0
- package/dist/playwright/globalSetup.js +139 -0
- package/dist/playwright/globalSetup.js.map +1 -0
- package/dist/playwright/index.d.ts +9 -0
- package/dist/playwright/index.d.ts.map +1 -0
- package/dist/playwright/index.js +9 -0
- package/dist/playwright/index.js.map +1 -0
- package/dist/probes/fonts.d.ts +4 -0
- package/dist/probes/fonts.d.ts.map +1 -0
- package/dist/probes/fonts.js +255 -0
- package/dist/probes/fonts.js.map +1 -0
- package/dist/probes/mirror.d.ts +4 -0
- package/dist/probes/mirror.d.ts.map +1 -0
- package/dist/probes/mirror.js +260 -0
- package/dist/probes/mirror.js.map +1 -0
- package/dist/probes/runProbe.d.ts +3 -0
- package/dist/probes/runProbe.d.ts.map +1 -0
- package/dist/probes/runProbe.js +219 -0
- package/dist/probes/runProbe.js.map +1 -0
- package/dist/probes/types.d.ts +72 -0
- package/dist/probes/types.d.ts.map +1 -0
- package/dist/probes/types.js +2 -0
- package/dist/probes/types.js.map +1 -0
- package/dist/scripts/_probeSourceUrl.d.ts +3 -0
- package/dist/scripts/_probeSourceUrl.d.ts.map +1 -0
- package/dist/scripts/_probeSourceUrl.js +119 -0
- package/dist/scripts/_probeSourceUrl.js.map +1 -0
- package/dist/scripts/addStore.d.ts +3 -0
- package/dist/scripts/addStore.d.ts.map +1 -0
- package/dist/scripts/addStore.js +46 -0
- package/dist/scripts/addStore.js.map +1 -0
- package/dist/scripts/buildDockerImage.d.ts +3 -0
- package/dist/scripts/buildDockerImage.d.ts.map +1 -0
- package/dist/scripts/buildDockerImage.js +60 -0
- package/dist/scripts/buildDockerImage.js.map +1 -0
- package/dist/scripts/captureAuth.d.ts +3 -0
- package/dist/scripts/captureAuth.d.ts.map +1 -0
- package/dist/scripts/captureAuth.js +124 -0
- package/dist/scripts/captureAuth.js.map +1 -0
- package/dist/scripts/captureContracts.d.ts +3 -0
- package/dist/scripts/captureContracts.d.ts.map +1 -0
- package/dist/scripts/captureContracts.js +517 -0
- package/dist/scripts/captureContracts.js.map +1 -0
- package/dist/scripts/captureRestContracts.d.ts +3 -0
- package/dist/scripts/captureRestContracts.d.ts.map +1 -0
- package/dist/scripts/captureRestContracts.js +245 -0
- package/dist/scripts/captureRestContracts.js.map +1 -0
- package/dist/scripts/checkOperationCoverage.d.ts +3 -0
- package/dist/scripts/checkOperationCoverage.d.ts.map +1 -0
- package/dist/scripts/checkOperationCoverage.js +302 -0
- package/dist/scripts/checkOperationCoverage.js.map +1 -0
- package/dist/scripts/cleanupStores.d.ts +3 -0
- package/dist/scripts/cleanupStores.d.ts.map +1 -0
- package/dist/scripts/cleanupStores.js +77 -0
- package/dist/scripts/cleanupStores.js.map +1 -0
- package/dist/scripts/createStores.d.ts +3 -0
- package/dist/scripts/createStores.d.ts.map +1 -0
- package/dist/scripts/createStores.js +66 -0
- package/dist/scripts/createStores.js.map +1 -0
- package/dist/scripts/deployAppVersion.d.ts +3 -0
- package/dist/scripts/deployAppVersion.d.ts.map +1 -0
- package/dist/scripts/deployAppVersion.js +591 -0
- package/dist/scripts/deployAppVersion.js.map +1 -0
- package/dist/scripts/devE2eBackend.d.ts +3 -0
- package/dist/scripts/devE2eBackend.d.ts.map +1 -0
- package/dist/scripts/devE2eBackend.js +117 -0
- package/dist/scripts/devE2eBackend.js.map +1 -0
- package/dist/scripts/devOnlineBackend.d.ts +3 -0
- package/dist/scripts/devOnlineBackend.d.ts.map +1 -0
- package/dist/scripts/devOnlineBackend.js +117 -0
- package/dist/scripts/devOnlineBackend.js.map +1 -0
- package/dist/scripts/installApp.d.ts +3 -0
- package/dist/scripts/installApp.d.ts.map +1 -0
- package/dist/scripts/installApp.js +163 -0
- package/dist/scripts/installApp.js.map +1 -0
- package/dist/scripts/listStores.d.ts +3 -0
- package/dist/scripts/listStores.d.ts.map +1 -0
- package/dist/scripts/listStores.js +18 -0
- package/dist/scripts/listStores.js.map +1 -0
- package/dist/scripts/runDocker.d.ts +3 -0
- package/dist/scripts/runDocker.d.ts.map +1 -0
- package/dist/scripts/runDocker.js +88 -0
- package/dist/scripts/runDocker.js.map +1 -0
- package/dist/scripts/runDockerAuth.d.ts +3 -0
- package/dist/scripts/runDockerAuth.d.ts.map +1 -0
- package/dist/scripts/runDockerAuth.js +108 -0
- package/dist/scripts/runDockerAuth.js.map +1 -0
- package/dist/scripts/runDockerOffline.d.ts +3 -0
- package/dist/scripts/runDockerOffline.d.ts.map +1 -0
- package/dist/scripts/runDockerOffline.js +129 -0
- package/dist/scripts/runDockerOffline.js.map +1 -0
- package/dist/scripts/runDockerOfflineExplore.d.ts +3 -0
- package/dist/scripts/runDockerOfflineExplore.d.ts.map +1 -0
- package/dist/scripts/runDockerOfflineExplore.js +116 -0
- package/dist/scripts/runDockerOfflineExplore.js.map +1 -0
- package/dist/scripts/runIsolatedDockerOffline.d.ts +3 -0
- package/dist/scripts/runIsolatedDockerOffline.d.ts.map +1 -0
- package/dist/scripts/runIsolatedDockerOffline.js +351 -0
- package/dist/scripts/runIsolatedDockerOffline.js.map +1 -0
- package/dist/scripts/runOffline.d.ts +3 -0
- package/dist/scripts/runOffline.d.ts.map +1 -0
- package/dist/scripts/runOffline.js +521 -0
- package/dist/scripts/runOffline.js.map +1 -0
- package/dist/scripts/runOfflineE2e.d.ts +3 -0
- package/dist/scripts/runOfflineE2e.d.ts.map +1 -0
- package/dist/scripts/runOfflineE2e.js +408 -0
- package/dist/scripts/runOfflineE2e.js.map +1 -0
- package/dist/scripts/runOfflineFullTests.d.ts +3 -0
- package/dist/scripts/runOfflineFullTests.d.ts.map +1 -0
- package/dist/scripts/runOfflineFullTests.js +1456 -0
- package/dist/scripts/runOfflineFullTests.js.map +1 -0
- package/dist/scripts/runSupermachine.d.ts +3 -0
- package/dist/scripts/runSupermachine.d.ts.map +1 -0
- package/dist/scripts/runSupermachine.js +474 -0
- package/dist/scripts/runSupermachine.js.map +1 -0
- package/dist/scripts/runSupermachineAuth.d.ts +3 -0
- package/dist/scripts/runSupermachineAuth.d.ts.map +1 -0
- package/dist/scripts/runSupermachineAuth.js +454 -0
- package/dist/scripts/runSupermachineAuth.js.map +1 -0
- package/dist/scripts/runTests.d.ts +3 -0
- package/dist/scripts/runTests.d.ts.map +1 -0
- package/dist/scripts/runTests.js +278 -0
- package/dist/scripts/runTests.js.map +1 -0
- package/dist/scripts/runVm.d.ts +3 -0
- package/dist/scripts/runVm.d.ts.map +1 -0
- package/dist/scripts/runVm.js +524 -0
- package/dist/scripts/runVm.js.map +1 -0
- package/dist/scripts/runVmAuth.d.ts +3 -0
- package/dist/scripts/runVmAuth.d.ts.map +1 -0
- package/dist/scripts/runVmAuth.js +475 -0
- package/dist/scripts/runVmAuth.js.map +1 -0
- package/dist/scripts/runVmScript.d.ts +3 -0
- package/dist/scripts/runVmScript.d.ts.map +1 -0
- package/dist/scripts/runVmScript.js +242 -0
- package/dist/scripts/runVmScript.js.map +1 -0
- package/dist/scripts/setupTestDb.d.ts +3 -0
- package/dist/scripts/setupTestDb.d.ts.map +1 -0
- package/dist/scripts/setupTestDb.js +61 -0
- package/dist/scripts/setupTestDb.js.map +1 -0
- package/dist/scripts/verifyContracts.d.ts +3 -0
- package/dist/scripts/verifyContracts.d.ts.map +1 -0
- package/dist/scripts/verifyContracts.js +258 -0
- package/dist/scripts/verifyContracts.js.map +1 -0
- package/dist/scripts/verifyRestContracts.d.ts +3 -0
- package/dist/scripts/verifyRestContracts.d.ts.map +1 -0
- package/dist/scripts/verifyRestContracts.js +237 -0
- package/dist/scripts/verifyRestContracts.js.map +1 -0
- package/dist/vite/offlineConfig.d.ts +34 -0
- package/dist/vite/offlineConfig.d.ts.map +1 -0
- package/dist/vite/offlineConfig.js +61 -0
- package/dist/vite/offlineConfig.js.map +1 -0
- package/dist/vite/onlineConfig.d.ts +42 -0
- package/dist/vite/onlineConfig.d.ts.map +1 -0
- package/dist/vite/onlineConfig.js +56 -0
- package/dist/vite/onlineConfig.js.map +1 -0
- package/docker/Dockerfile +67 -0
- package/docker/Dockerfile.vm +137 -0
- package/docker/README.md +50 -0
- package/docker/entrypoint.sh +198 -0
- package/package.json +85 -0
- package/src/contracts/normalize.ts +96 -0
- package/src/contracts/normalizeHtml.ts +98 -0
- package/src/edge/ca.cnf +14 -0
- package/src/edge/ca.crt +22 -0
- package/src/edge/ca.key +28 -0
- package/src/edge/cert.ts +117 -0
- package/src/edge/edgeProxy.ts +390 -0
- package/src/edge/server.cnf +28 -0
- package/src/edge/server.crt +26 -0
- package/src/edge/server.key +28 -0
- package/src/index.ts +67 -0
- package/src/lib/buildSourceBundle.ts +197 -0
- package/src/lib/freePort.ts +33 -0
- package/src/lib/functionBuild.ts +490 -0
- package/src/lib/neonWsProxy.ts +124 -0
- package/src/lib/sourceZipUpload.ts +168 -0
- package/src/lib/stealthLaunch.ts +57 -0
- package/src/lib/storeAutomation.ts +110 -0
- package/src/playwright/baseConfig.ts +120 -0
- package/src/playwright/globalSetup.ts +179 -0
- package/src/playwright/index.ts +11 -0
- package/src/probes/fonts.ts +279 -0
- package/src/probes/mirror.ts +283 -0
- package/src/probes/runProbe.ts +257 -0
- package/src/probes/types.ts +73 -0
- package/src/scripts/addStore.ts +59 -0
- package/src/scripts/buildDockerImage.ts +66 -0
- package/src/scripts/captureAuth.ts +145 -0
- package/src/scripts/captureContracts.ts +675 -0
- package/src/scripts/captureRestContracts.ts +319 -0
- package/src/scripts/checkOperationCoverage.ts +365 -0
- package/src/scripts/cleanupStores.ts +91 -0
- package/src/scripts/createStores.ts +77 -0
- package/src/scripts/deployAppVersion.ts +692 -0
- package/src/scripts/devOnlineBackend.ts +141 -0
- package/src/scripts/installApp.ts +188 -0
- package/src/scripts/listStores.ts +19 -0
- package/src/scripts/runDockerAuth.ts +120 -0
- package/src/scripts/runOffline.ts +577 -0
- package/src/scripts/runOfflineFullTests.ts +1634 -0
- package/src/scripts/runTests.ts +306 -0
- package/src/scripts/runVm.ts +562 -0
- package/src/scripts/runVmAuth.ts +541 -0
- package/src/scripts/runVmScript.ts +282 -0
- package/src/scripts/setupTestDb.ts +71 -0
- package/src/scripts/verifyContracts.ts +310 -0
- package/src/scripts/verifyRestContracts.ts +275 -0
- package/src/vite/onlineConfig.ts +60 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile a JS Shopify Function source tree into a deployable `.wasm`
|
|
3
|
+
* WITHOUT shelling out to the Shopify CLI.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists
|
|
6
|
+
* ---------------
|
|
7
|
+
* `shopify app function build` is the canonical way to produce the
|
|
8
|
+
* `.wasm` artifact that Shopify Functions accepts. Tests in this repo
|
|
9
|
+
* are forbidden from depending on the Shopify CLI (binary mismatch
|
|
10
|
+
* across dev machines, interactive prompts, version drift) — so we
|
|
11
|
+
* replicate the pipeline directly.
|
|
12
|
+
*
|
|
13
|
+
* The pipeline is small and well-isolated. Traced from
|
|
14
|
+
* `Shopify/cli@packages/app/src/cli/services/function/build.ts`:
|
|
15
|
+
*
|
|
16
|
+
* 1. esbuild — bundle the user's JS source into a single ES module
|
|
17
|
+
* whose default export is a thin wrapper around
|
|
18
|
+
* `@shopify/shopify_function/run`. esbuild's Node API, no
|
|
19
|
+
* subprocess.
|
|
20
|
+
*
|
|
21
|
+
* 2. javy build — wraps the bundled JS in a Wizened QuickJS runtime
|
|
22
|
+
* AND links Shopify's host-binding plugin (`-C dynamic -C
|
|
23
|
+
* plugin=shopify_functions_javy_v3.wasm`). Output: a stand-alone
|
|
24
|
+
* .wasm module that the Shopify Functions runtime can invoke
|
|
25
|
+
* directly.
|
|
26
|
+
*
|
|
27
|
+
* No "trampoline" step is required for current
|
|
28
|
+
* `@shopify/shopify_function` runtimes — the plugin v3 + Javy 7 combo
|
|
29
|
+
* already targets the modern host ABI. (Trampoline exists in the CLI
|
|
30
|
+
* but is conditional on legacy `shopify_function_v{1,2}` imports.)
|
|
31
|
+
*
|
|
32
|
+
* Pinned versions
|
|
33
|
+
* ---------------
|
|
34
|
+
* Javy: bytecodealliance/javy v7.0.1 (stock upstream, NOT a Shopify
|
|
35
|
+
* fork — the Shopify-specific glue lives entirely in the
|
|
36
|
+
* plugin .wasm we pass to `-C plugin=`).
|
|
37
|
+
* Plugin: https://cdn.shopify.com/shopifycloud/shopify-functions-javy-plugin/
|
|
38
|
+
* shopify_functions_javy_v3.wasm
|
|
39
|
+
* (version-pinned in URL path; Shopify CDN, effectively
|
|
40
|
+
* immutable — when Shopify ships `_v4` we bump the const.)
|
|
41
|
+
*
|
|
42
|
+
* Binaries are cached at
|
|
43
|
+
* ~/.cache/essential-apps-shopify-test/bin/
|
|
44
|
+
* keyed by version so multiple test workspaces share the same download.
|
|
45
|
+
*/
|
|
46
|
+
import { chmodSync, createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
47
|
+
import { execFile } from 'node:child_process';
|
|
48
|
+
import { homedir } from 'node:os';
|
|
49
|
+
import { dirname, join, resolve } from 'node:path';
|
|
50
|
+
import { pipeline } from 'node:stream/promises';
|
|
51
|
+
import { promisify } from 'node:util';
|
|
52
|
+
import { createGunzip } from 'node:zlib';
|
|
53
|
+
import { Readable } from 'node:stream';
|
|
54
|
+
import * as esbuild from 'esbuild';
|
|
55
|
+
const execFileP = promisify(execFile);
|
|
56
|
+
/**
|
|
57
|
+
* Pinned Javy version. Bumping = updating the URL pattern in
|
|
58
|
+
* `binaryUrlForJavy` AND verifying the `-C` flag interface hasn't
|
|
59
|
+
* changed across the bump (Javy did break flags between v0.x and v3+).
|
|
60
|
+
*/
|
|
61
|
+
export const JAVY_VERSION = 'v7.0.1';
|
|
62
|
+
/**
|
|
63
|
+
* Pinned Shopify Functions Javy plugin wasm. The URL contains the
|
|
64
|
+
* major version (`_v3`) — if Shopify publishes a `_v4`, we explicitly
|
|
65
|
+
* decide whether to upgrade (the plugin's host ABI is what user JS
|
|
66
|
+
* sees via `@shopify/shopify_function`).
|
|
67
|
+
*/
|
|
68
|
+
export const PLUGIN_URL = 'https://cdn.shopify.com/shopifycloud/shopify-functions-javy-plugin/shopify_functions_javy_v3.wasm';
|
|
69
|
+
/** Filename under the cache dir for the plugin (derived from URL). */
|
|
70
|
+
const PLUGIN_FILENAME = 'shopify_functions_javy_v3.wasm';
|
|
71
|
+
/** `world` name we declare in the synthesized WIT — must match the
|
|
72
|
+
* `wit-world` flag passed to `javy build`. The literal string is
|
|
73
|
+
* uninteresting (Javy uses it only to disambiguate worlds in the WIT
|
|
74
|
+
* file); we follow the CLI's value to minimize surprise. */
|
|
75
|
+
const JAVY_WORLD_NAME = 'shopify-function';
|
|
76
|
+
function cacheDir() {
|
|
77
|
+
const dir = join(homedir(), '.cache', 'essential-apps-shopify-test', 'bin');
|
|
78
|
+
mkdirSync(dir, { recursive: true });
|
|
79
|
+
return dir;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Map Node's `process.{platform,arch}` to the asset-name suffix
|
|
83
|
+
* bytecodealliance/javy uses in its GitHub releases. Mismatches here
|
|
84
|
+
* surface as 404s from the GitHub redirect, so it's worth keeping the
|
|
85
|
+
* list explicit rather than guessing.
|
|
86
|
+
*
|
|
87
|
+
* Known asset names at v7.0.1:
|
|
88
|
+
* javy-arm-macos-v7.0.1.gz
|
|
89
|
+
* javy-x86_64-macos-v7.0.1.gz
|
|
90
|
+
* javy-arm-linux-v7.0.1.gz
|
|
91
|
+
* javy-x86_64-linux-v7.0.1.gz
|
|
92
|
+
* javy-x86_64-windows-v7.0.1.gz (no arm-windows)
|
|
93
|
+
*/
|
|
94
|
+
function platformArch() {
|
|
95
|
+
const p = process.platform;
|
|
96
|
+
const a = process.arch;
|
|
97
|
+
const plat = p === 'darwin' ? 'macos' : p === 'linux' ? 'linux' : p === 'win32' ? 'windows' : null;
|
|
98
|
+
const arch = a === 'arm64' ? 'arm' : a === 'x64' ? 'x86_64' : null;
|
|
99
|
+
if (!plat || !arch) {
|
|
100
|
+
throw new Error(`Unsupported platform/arch for Javy: ${p}/${a}`);
|
|
101
|
+
}
|
|
102
|
+
if (plat === 'windows' && arch === 'arm') {
|
|
103
|
+
throw new Error(`Javy ${JAVY_VERSION} has no arm-windows build; install x86_64 Node or run under Rosetta.`);
|
|
104
|
+
}
|
|
105
|
+
return `${arch}-${plat}`;
|
|
106
|
+
}
|
|
107
|
+
function javyUrl(version) {
|
|
108
|
+
return `https://github.com/bytecodealliance/javy/releases/download/${version}/javy-${platformArch()}-${version}.gz`;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Stream a remote .gz binary to a local file, decompressing on the
|
|
112
|
+
* way. Atomic via tmp + rename so concurrent test workers don't see
|
|
113
|
+
* partial files.
|
|
114
|
+
*/
|
|
115
|
+
async function downloadGz(url, dest) {
|
|
116
|
+
const tmp = `${dest}.tmp.${process.pid}`;
|
|
117
|
+
const r = await fetch(url);
|
|
118
|
+
if (!r.ok || !r.body) {
|
|
119
|
+
throw new Error(`Failed to download ${url} — HTTP ${r.status}`);
|
|
120
|
+
}
|
|
121
|
+
// `r.body` is a Web ReadableStream; convert to a Node Readable so
|
|
122
|
+
// we can pipe it through Node's gunzip + file sink.
|
|
123
|
+
const src = Readable.fromWeb(r.body);
|
|
124
|
+
await pipeline(src, createGunzip(), createWriteStream(tmp));
|
|
125
|
+
// chmod before rename so the executable bit is set atomically with
|
|
126
|
+
// the visible filename.
|
|
127
|
+
chmodSync(tmp, 0o755);
|
|
128
|
+
await import('node:fs/promises').then(({ rename }) => rename(tmp, dest));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Download a remote file as-is (no decompression). Used for the plugin
|
|
132
|
+
* wasm which is served uncompressed by Shopify's CDN.
|
|
133
|
+
*/
|
|
134
|
+
async function downloadFile(url, dest) {
|
|
135
|
+
const tmp = `${dest}.tmp.${process.pid}`;
|
|
136
|
+
const r = await fetch(url);
|
|
137
|
+
if (!r.ok || !r.body) {
|
|
138
|
+
throw new Error(`Failed to download ${url} — HTTP ${r.status}`);
|
|
139
|
+
}
|
|
140
|
+
const src = Readable.fromWeb(r.body);
|
|
141
|
+
await pipeline(src, createWriteStream(tmp));
|
|
142
|
+
await import('node:fs/promises').then(({ rename }) => rename(tmp, dest));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Return a path to the Javy binary, downloading it on first use.
|
|
146
|
+
* Idempotent — second call hits the on-disk cache. `JAVY_BIN`
|
|
147
|
+
* environment variable bypasses the download and points at a local
|
|
148
|
+
* build (useful when tracking Javy main).
|
|
149
|
+
*/
|
|
150
|
+
export async function ensureJavy() {
|
|
151
|
+
const override = process.env['JAVY_BIN'];
|
|
152
|
+
if (override) {
|
|
153
|
+
if (!existsSync(override)) {
|
|
154
|
+
throw new Error(`JAVY_BIN points at non-existent file: ${override}`);
|
|
155
|
+
}
|
|
156
|
+
return override;
|
|
157
|
+
}
|
|
158
|
+
const dest = join(cacheDir(), `javy-${JAVY_VERSION}`);
|
|
159
|
+
if (existsSync(dest))
|
|
160
|
+
return dest;
|
|
161
|
+
const url = javyUrl(JAVY_VERSION);
|
|
162
|
+
console.log(` ↓ Downloading Javy ${JAVY_VERSION} from ${url}`);
|
|
163
|
+
await downloadGz(url, dest);
|
|
164
|
+
return dest;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Return a path to the Shopify Functions Javy plugin wasm,
|
|
168
|
+
* downloading it on first use. `JAVY_PLUGIN_WASM` env overrides for
|
|
169
|
+
* local plugin development.
|
|
170
|
+
*/
|
|
171
|
+
export async function ensureJavyPlugin() {
|
|
172
|
+
const override = process.env['JAVY_PLUGIN_WASM'];
|
|
173
|
+
if (override) {
|
|
174
|
+
if (!existsSync(override)) {
|
|
175
|
+
throw new Error(`JAVY_PLUGIN_WASM points at non-existent file: ${override}`);
|
|
176
|
+
}
|
|
177
|
+
return override;
|
|
178
|
+
}
|
|
179
|
+
const dest = join(cacheDir(), PLUGIN_FILENAME);
|
|
180
|
+
if (existsSync(dest))
|
|
181
|
+
return dest;
|
|
182
|
+
console.log(` ↓ Downloading Shopify Functions Javy plugin from ${PLUGIN_URL}`);
|
|
183
|
+
await downloadFile(PLUGIN_URL, dest);
|
|
184
|
+
return dest;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Bespoke parser for the function's `shopify.extension.toml`. The
|
|
188
|
+
* minimal-toml reader in `deployAppVersion.ts` doesn't grok arrays of
|
|
189
|
+
* inline tables (`[[extensions.targeting]]`), so we do a tiny ad-hoc
|
|
190
|
+
* parse here. It's intentionally narrow — only handles the shape
|
|
191
|
+
* `shopify app function init` produces (essentially: a single
|
|
192
|
+
* `[[extensions]]` table containing N `[[extensions.targeting]]`
|
|
193
|
+
* sub-tables and one `[extensions.build]` sub-table).
|
|
194
|
+
*
|
|
195
|
+
* If/when we deploy a function with multiple top-level `[[extensions]]`
|
|
196
|
+
* entries (rare — usually one file per extension dir), this needs
|
|
197
|
+
* generalizing.
|
|
198
|
+
*/
|
|
199
|
+
export function parseFunctionToml(path) {
|
|
200
|
+
const txt = readFileSync(path, 'utf8');
|
|
201
|
+
const lines = txt.split('\n').map((l) => l.replace(/#.*$/, '').replace(/\s+$/, ''));
|
|
202
|
+
// We track the most-recently-opened table header so kv-lines are
|
|
203
|
+
// attached to the right place.
|
|
204
|
+
let section = 'other';
|
|
205
|
+
const targetingBlocks = [];
|
|
206
|
+
let cur = null;
|
|
207
|
+
const ext = {};
|
|
208
|
+
let apiVersion = '';
|
|
209
|
+
const build = {};
|
|
210
|
+
for (const raw of lines) {
|
|
211
|
+
const line = raw.trim();
|
|
212
|
+
if (!line)
|
|
213
|
+
continue;
|
|
214
|
+
if (line === '[[extensions]]') {
|
|
215
|
+
section = 'extensions';
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (line === '[[extensions.targeting]]') {
|
|
219
|
+
section = 'targeting';
|
|
220
|
+
cur = {};
|
|
221
|
+
targetingBlocks.push(cur);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (line === '[extensions.build]') {
|
|
225
|
+
section = 'build';
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (line.startsWith('[')) {
|
|
229
|
+
section = 'other';
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const m = line.match(/^([a-z_][a-z0-9_]*)\s*=\s*"(.*)"$/i);
|
|
233
|
+
if (!m)
|
|
234
|
+
continue;
|
|
235
|
+
const [, k, v] = m;
|
|
236
|
+
if (section === 'targeting' && cur) {
|
|
237
|
+
if (k === 'target')
|
|
238
|
+
cur.target = v;
|
|
239
|
+
else if (k === 'export')
|
|
240
|
+
cur.export = v;
|
|
241
|
+
else if (k === 'input_query')
|
|
242
|
+
cur.inputQueryPath = v;
|
|
243
|
+
}
|
|
244
|
+
else if (section === 'extensions') {
|
|
245
|
+
if (k === 'handle')
|
|
246
|
+
ext.handle = v;
|
|
247
|
+
else if (k === 'uid')
|
|
248
|
+
ext.uid = v;
|
|
249
|
+
else if (k === 'type')
|
|
250
|
+
ext.type = v;
|
|
251
|
+
}
|
|
252
|
+
else if (section === 'build') {
|
|
253
|
+
if (k === 'path')
|
|
254
|
+
build.path = v;
|
|
255
|
+
}
|
|
256
|
+
else if (section === 'other') {
|
|
257
|
+
if (k === 'api_version')
|
|
258
|
+
apiVersion = v;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!ext.handle || !ext.uid || ext.type !== 'function') {
|
|
262
|
+
throw new Error(`Invalid function toml at ${path} — expected [[extensions]] with handle/uid/type=function. Got: ${JSON.stringify(ext)}`);
|
|
263
|
+
}
|
|
264
|
+
const targets = [];
|
|
265
|
+
for (const t of targetingBlocks) {
|
|
266
|
+
if (!t.target || !t.export || !t.inputQueryPath) {
|
|
267
|
+
throw new Error(`Invalid [[extensions.targeting]] in ${path} — needs target/export/input_query. Got: ${JSON.stringify(t)}`);
|
|
268
|
+
}
|
|
269
|
+
targets.push(t);
|
|
270
|
+
}
|
|
271
|
+
if (targets.length === 0) {
|
|
272
|
+
throw new Error(`No [[extensions.targeting]] blocks in ${path} — at least one is required.`);
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
handle: ext.handle,
|
|
276
|
+
uid: ext.uid,
|
|
277
|
+
type: 'function',
|
|
278
|
+
apiVersion: apiVersion || 'unknown',
|
|
279
|
+
targets,
|
|
280
|
+
outputRelativePath: build.path ?? 'dist/function.wasm',
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* The CLI's `ExportJavyBuilder` synthesises an esbuild entry that
|
|
285
|
+
* re-exports each `[[extensions.targeting]].export` as a top-level
|
|
286
|
+
* function, delegating to the user's symbol of the same name. We
|
|
287
|
+
* mirror that exactly — variable names are arbitrary internal
|
|
288
|
+
* identifiers, only the EXPORTED names matter (they're what Javy
|
|
289
|
+
* sees and what Shopify's runtime invokes).
|
|
290
|
+
*
|
|
291
|
+
* Why kebab-case in the export string matches the JS identifier:
|
|
292
|
+
* Shopify's convention uses kebab-case in toml. The matching JS
|
|
293
|
+
* identifier is the camelCase form (per the Shopify CLI's
|
|
294
|
+
* convention). We accept both spellings — fall back to camelCase if
|
|
295
|
+
* the source happens not to define the literal kebab name (which is
|
|
296
|
+
* not a valid JS identifier anyway when used directly).
|
|
297
|
+
*/
|
|
298
|
+
function jsIdent(exportName) {
|
|
299
|
+
// kebab-case -> camelCase (Shopify CLI convention for the user-side
|
|
300
|
+
// entry symbol). `cart-lines-discounts-generate-run` →
|
|
301
|
+
// `cartLinesDiscountsGenerateRun`.
|
|
302
|
+
return exportName.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
303
|
+
}
|
|
304
|
+
function synthesizeEntry(targets) {
|
|
305
|
+
// The exported NAME has to be the camelCase form of the WIT identifier.
|
|
306
|
+
// Javy translates WIT `%kebab-case-name` → JS export `camelCaseName` when
|
|
307
|
+
// resolving the world's exports. If the JS module exports the kebab-case
|
|
308
|
+
// string form, Javy fails with:
|
|
309
|
+
// "JS module does not export camelCaseName"
|
|
310
|
+
// (Confirmed empirically — see commit history.) The user's source code
|
|
311
|
+
// also exports camelCase by convention.
|
|
312
|
+
const importLines = [
|
|
313
|
+
`import __runFunction from "@shopify/shopify_function/run";`,
|
|
314
|
+
];
|
|
315
|
+
const exportLines = [];
|
|
316
|
+
for (let i = 0; i < targets.length; i++) {
|
|
317
|
+
const t = targets[i];
|
|
318
|
+
const ident = jsIdent(t.export);
|
|
319
|
+
importLines.push(`import { ${ident} as __target${i} } from "user-function";`);
|
|
320
|
+
exportLines.push(`export function ${ident}() { return __runFunction(__target${i}); }`);
|
|
321
|
+
}
|
|
322
|
+
return `${importLines.join('\n')}\n${exportLines.join('\n')}\n`;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Synthesize the WIT world file that Javy needs to know which
|
|
326
|
+
* exports to lift from the JS module. The CLI generates this on the
|
|
327
|
+
* fly per build; we follow suit.
|
|
328
|
+
*/
|
|
329
|
+
function synthesizeWit(targets) {
|
|
330
|
+
const exports = targets
|
|
331
|
+
.map((t) => ` export %${t.export}: func();`)
|
|
332
|
+
.join('\n');
|
|
333
|
+
return `package shopify:function;\nworld ${JAVY_WORLD_NAME} {\n${exports}\n}\n`;
|
|
334
|
+
}
|
|
335
|
+
/** Identify the user-source entry path. Function dirs traditionally
|
|
336
|
+
* use `src/index.ts` (TypeScript) or `src/index.js`. We accept both
|
|
337
|
+
* — if neither exists, the error message points at the convention. */
|
|
338
|
+
function findEntrySource(functionDir) {
|
|
339
|
+
for (const candidate of ['src/index.ts', 'src/index.js']) {
|
|
340
|
+
const p = resolve(functionDir, candidate);
|
|
341
|
+
if (existsSync(p))
|
|
342
|
+
return p;
|
|
343
|
+
}
|
|
344
|
+
throw new Error(`No function entry found in ${functionDir}. Expected one of: src/index.ts, src/index.js`);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Build a deployable `.wasm` for a JS Shopify Function.
|
|
348
|
+
*
|
|
349
|
+
* Idempotent: rebuilds on every call (we don't try to be clever about
|
|
350
|
+
* stale-cache detection — esbuild + javy together are ~hundreds of
|
|
351
|
+
* milliseconds, fast enough that incremental caching is unnecessary
|
|
352
|
+
* for our use case).
|
|
353
|
+
*/
|
|
354
|
+
export async function buildFunctionWasm(opts) {
|
|
355
|
+
const { functionDir } = opts;
|
|
356
|
+
const tomlPath = resolve(functionDir, 'shopify.extension.toml');
|
|
357
|
+
if (!existsSync(tomlPath)) {
|
|
358
|
+
throw new Error(`No shopify.extension.toml at ${tomlPath}`);
|
|
359
|
+
}
|
|
360
|
+
const toml = parseFunctionToml(tomlPath);
|
|
361
|
+
console.log(` • function ${toml.handle} (uid=${toml.uid}, ${toml.targets.length} target(s), api ${toml.apiVersion})`);
|
|
362
|
+
const entryPath = opts.entrySource ?? findEntrySource(functionDir);
|
|
363
|
+
const outputWasm = resolve(functionDir, toml.outputRelativePath);
|
|
364
|
+
const distDir = dirname(outputWasm);
|
|
365
|
+
mkdirSync(distDir, { recursive: true });
|
|
366
|
+
// Step 1: bundle JS with esbuild.
|
|
367
|
+
const bundledJs = resolve(distDir, 'function.js');
|
|
368
|
+
const entryContents = synthesizeEntry(toml.targets);
|
|
369
|
+
await esbuild.build({
|
|
370
|
+
stdin: {
|
|
371
|
+
contents: entryContents,
|
|
372
|
+
resolveDir: functionDir,
|
|
373
|
+
loader: 'ts',
|
|
374
|
+
sourcefile: 'shopify-function-entry.ts',
|
|
375
|
+
},
|
|
376
|
+
bundle: true,
|
|
377
|
+
format: 'esm',
|
|
378
|
+
target: 'es2022',
|
|
379
|
+
legalComments: 'none',
|
|
380
|
+
outfile: bundledJs,
|
|
381
|
+
alias: {
|
|
382
|
+
'user-function': entryPath,
|
|
383
|
+
},
|
|
384
|
+
// Node-side bundler. Functions don't have access to Node APIs
|
|
385
|
+
// (the runtime is QuickJS via Javy), so we set platform=neutral
|
|
386
|
+
// and don't try to polyfill anything Node-specific.
|
|
387
|
+
platform: 'neutral',
|
|
388
|
+
// QuickJS exposes a recent-ish JS surface but ECMAScript modules
|
|
389
|
+
// are handled by Javy's runtime — esbuild's `format: esm` output
|
|
390
|
+
// is what Javy expects.
|
|
391
|
+
mainFields: ['module', 'main'],
|
|
392
|
+
conditions: ['shopify_function', 'import', 'default'],
|
|
393
|
+
});
|
|
394
|
+
// Step 2: WIT + javy build.
|
|
395
|
+
const witPath = resolve(distDir, 'javy-world.wit');
|
|
396
|
+
writeFileSync(witPath, synthesizeWit(toml.targets), 'utf8');
|
|
397
|
+
const [javy, plugin] = await Promise.all([ensureJavy(), ensureJavyPlugin()]);
|
|
398
|
+
const javyArgs = [
|
|
399
|
+
'build',
|
|
400
|
+
'-C', 'dynamic',
|
|
401
|
+
'-C', `plugin=${plugin}`,
|
|
402
|
+
'-C', `wit=${witPath}`,
|
|
403
|
+
'-C', `wit-world=${JAVY_WORLD_NAME}`,
|
|
404
|
+
'-o', outputWasm,
|
|
405
|
+
bundledJs,
|
|
406
|
+
];
|
|
407
|
+
// `cwd: functionDir` so Javy resolves any relative paths in its
|
|
408
|
+
// diagnostics (it doesn't, AFAICT, but parity with the CLI is
|
|
409
|
+
// cheap).
|
|
410
|
+
await execFileP(javy, javyArgs, { cwd: functionDir, maxBuffer: 50 * 1024 * 1024 });
|
|
411
|
+
return { wasmPath: outputWasm, parsedToml: toml };
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=functionBuild.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functionBuild.js","sourceRoot":"","sources":["../../src/lib/functionBuild.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface StartNeonWsProxyOptions {
|
|
2
|
+
/** Port to listen on (0 = OS-assigned). */
|
|
3
|
+
port?: number;
|
|
4
|
+
/** Listen host. Default 127.0.0.1. */
|
|
5
|
+
host?: string;
|
|
6
|
+
/** Postgres host to forward to. Default 127.0.0.1. */
|
|
7
|
+
targetHost?: string;
|
|
8
|
+
/** Postgres port to forward to. Default 5432. */
|
|
9
|
+
targetPort?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface StartedNeonWsProxy {
|
|
12
|
+
/** Port the proxy listens on. */
|
|
13
|
+
port: number;
|
|
14
|
+
/** Stop the proxy + close all bridged sockets. */
|
|
15
|
+
close: () => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Minimal WebSocket→TCP proxy implementing the transport the Neon
|
|
19
|
+
* serverless driver (`@neondatabase/serverless`) speaks to its
|
|
20
|
+
* `wsProxy`. It lets a consuming app's UNMODIFIED prisma client (which
|
|
21
|
+
* uses `new Pool()` from @neondatabase/serverless + `@prisma/adapter-neon`)
|
|
22
|
+
* talk to the offline-full suite's plain LOCAL Postgres — no app-source
|
|
23
|
+
* change, which is the whole point of zero-app-touch onboarding.
|
|
24
|
+
*
|
|
25
|
+
* Target resolution: when `wsProxy` is a custom function, the driver
|
|
26
|
+
* connects to `ws://<wsProxy()>` and does NOT append the DB address to
|
|
27
|
+
* the URL (verified empirically — `?address` is empty), so we forward to
|
|
28
|
+
* a CONFIGURED fixed Postgres (`targetHost`/`targetPort`). The Postgres
|
|
29
|
+
* wire-protocol startup packet (forwarded byte-for-byte) carries the
|
|
30
|
+
* user + database from the connection string, so a fixed server target
|
|
31
|
+
* is sufficient. We still honour `?address=` if a driver version sets it.
|
|
32
|
+
*
|
|
33
|
+
* `localhost` is normalised to `127.0.0.1`: some resolvers / Docker map
|
|
34
|
+
* localhost→::1 where Postgres may listen only on IPv4.
|
|
35
|
+
*
|
|
36
|
+
* Faithful passthrough by design: the REAL Neon driver runs unchanged;
|
|
37
|
+
* we only relocate its endpoint to local Postgres, avoiding the
|
|
38
|
+
* adapter-compat risk of swapping in a different pg driver.
|
|
39
|
+
*/
|
|
40
|
+
export declare function startNeonWsProxy(opts?: StartNeonWsProxyOptions): Promise<StartedNeonWsProxy>;
|
|
41
|
+
//# sourceMappingURL=neonWsProxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neonWsProxy.d.ts","sourceRoot":"","sources":["../../src/lib/neonWsProxy.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,uBAAuB;IACtC,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,kBAAkB,CAAC,CA6E7B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import net from 'node:net';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal WebSocket→TCP proxy implementing the transport the Neon
|
|
5
|
+
* serverless driver (`@neondatabase/serverless`) speaks to its
|
|
6
|
+
* `wsProxy`. It lets a consuming app's UNMODIFIED prisma client (which
|
|
7
|
+
* uses `new Pool()` from @neondatabase/serverless + `@prisma/adapter-neon`)
|
|
8
|
+
* talk to the offline-full suite's plain LOCAL Postgres — no app-source
|
|
9
|
+
* change, which is the whole point of zero-app-touch onboarding.
|
|
10
|
+
*
|
|
11
|
+
* Target resolution: when `wsProxy` is a custom function, the driver
|
|
12
|
+
* connects to `ws://<wsProxy()>` and does NOT append the DB address to
|
|
13
|
+
* the URL (verified empirically — `?address` is empty), so we forward to
|
|
14
|
+
* a CONFIGURED fixed Postgres (`targetHost`/`targetPort`). The Postgres
|
|
15
|
+
* wire-protocol startup packet (forwarded byte-for-byte) carries the
|
|
16
|
+
* user + database from the connection string, so a fixed server target
|
|
17
|
+
* is sufficient. We still honour `?address=` if a driver version sets it.
|
|
18
|
+
*
|
|
19
|
+
* `localhost` is normalised to `127.0.0.1`: some resolvers / Docker map
|
|
20
|
+
* localhost→::1 where Postgres may listen only on IPv4.
|
|
21
|
+
*
|
|
22
|
+
* Faithful passthrough by design: the REAL Neon driver runs unchanged;
|
|
23
|
+
* we only relocate its endpoint to local Postgres, avoiding the
|
|
24
|
+
* adapter-compat risk of swapping in a different pg driver.
|
|
25
|
+
*/
|
|
26
|
+
export async function startNeonWsProxy(opts = {}) {
|
|
27
|
+
const host = opts.host ?? '127.0.0.1';
|
|
28
|
+
const norm = (h) => (h === 'localhost' ? '127.0.0.1' : h);
|
|
29
|
+
const defaultTargetHost = norm(opts.targetHost ?? '127.0.0.1');
|
|
30
|
+
const defaultTargetPort = opts.targetPort ?? 5432;
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const wss = new WebSocketServer({ port: opts.port ?? 0, host });
|
|
33
|
+
wss.once('error', reject);
|
|
34
|
+
wss.once('listening', () => {
|
|
35
|
+
wss.off('error', reject);
|
|
36
|
+
const addr = wss.address();
|
|
37
|
+
const port = typeof addr === 'object' && addr ? addr.port : Number(opts.port ?? 0);
|
|
38
|
+
resolve({
|
|
39
|
+
port,
|
|
40
|
+
close: () => new Promise((res) => {
|
|
41
|
+
for (const client of wss.clients) {
|
|
42
|
+
try {
|
|
43
|
+
client.terminate();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
/* ignore */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
wss.close(() => res());
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
wss.on('connection', (ws, req) => {
|
|
54
|
+
let targetHost = defaultTargetHost;
|
|
55
|
+
let targetPort = defaultTargetPort;
|
|
56
|
+
// Honour `?address=host:port` if the driver ever provides it;
|
|
57
|
+
// otherwise fall back to the configured fixed Postgres.
|
|
58
|
+
const address = new URL(req.url ?? '/', 'http://localhost').searchParams.get('address');
|
|
59
|
+
if (address) {
|
|
60
|
+
const i = address.lastIndexOf(':');
|
|
61
|
+
if (i > 0) {
|
|
62
|
+
targetHost = norm(address.slice(0, i));
|
|
63
|
+
targetPort = Number(address.slice(i + 1)) || defaultTargetPort;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// net.connect queues writes until connected, so we can wire
|
|
67
|
+
// `ws → tcp` immediately without racing the connect.
|
|
68
|
+
const tcp = net.connect({ host: targetHost, port: targetPort });
|
|
69
|
+
let closed = false;
|
|
70
|
+
const cleanup = () => {
|
|
71
|
+
if (closed)
|
|
72
|
+
return;
|
|
73
|
+
closed = true;
|
|
74
|
+
try {
|
|
75
|
+
ws.close();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* ignore */
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
tcp.destroy();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* ignore */
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
// `ws` server delivers binary frames as Buffer (binaryType
|
|
88
|
+
// 'nodebuffer'); the Postgres wire protocol is binary.
|
|
89
|
+
ws.on('message', (data) => tcp.write(data));
|
|
90
|
+
tcp.on('data', (data) => {
|
|
91
|
+
if (ws.readyState === ws.OPEN)
|
|
92
|
+
ws.send(data);
|
|
93
|
+
});
|
|
94
|
+
ws.on('close', cleanup);
|
|
95
|
+
ws.on('error', cleanup);
|
|
96
|
+
tcp.on('close', cleanup);
|
|
97
|
+
tcp.on('error', cleanup);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=neonWsProxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neonWsProxy.js","sourceRoot":"","sources":["../../src/lib/neonWsProxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface RequestSourceUploadUrlOptions {
|
|
2
|
+
/** Bearer token from `exchangeAutomationToken`. */
|
|
3
|
+
accessToken: string;
|
|
4
|
+
/** Numeric Partner-org ID. Required — the endpoint won't infer it
|
|
5
|
+
* from the access token, even though it's already scoped to one
|
|
6
|
+
* org. (Discovered empirically: omitting it yields a generic 404.) */
|
|
7
|
+
organizationId: string;
|
|
8
|
+
/**
|
|
9
|
+
* Archive format. Defaults to 'BR' (tar+brotli) which the
|
|
10
|
+
* App Management server fully supports including function
|
|
11
|
+
* extensions. 'ZIP' is accepted for legacy callers but the
|
|
12
|
+
* server's function-deploy path 500s on zip-with-folders.
|
|
13
|
+
*/
|
|
14
|
+
sourceExtension?: 'BR' | 'ZIP';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Mint a fresh signed upload URL. The URL is valid for 3600 seconds;
|
|
18
|
+
* upload + version-create should both happen well within that window.
|
|
19
|
+
*
|
|
20
|
+
* Returns the GCS URL verbatim — you pass the SAME URL to
|
|
21
|
+
* `appVersionCreate` as `sourceUrl`. Shopify maintains the mapping
|
|
22
|
+
* server-side between the signed URL and the uploaded object.
|
|
23
|
+
*/
|
|
24
|
+
export declare function requestSourceUploadUrl(opts: RequestSourceUploadUrlOptions): Promise<string>;
|
|
25
|
+
export interface UploadSourceOptions {
|
|
26
|
+
uploadUrl: string;
|
|
27
|
+
/** Path on disk to the archive file (typically a `.tar.br` from
|
|
28
|
+
* `buildSourceBundle`, or a legacy `.zip`). */
|
|
29
|
+
archivePath: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* PUT an archive file to the signed GCS URL minted by
|
|
33
|
+
* `requestSourceUploadUrl`. The URL embeds its own auth (signed
|
|
34
|
+
* `X-Goog-*` query params), so no bearer is needed on this request.
|
|
35
|
+
*
|
|
36
|
+
* Headers: we send only `Content-Length`. The CLI doesn't set a
|
|
37
|
+
* Content-Type for these uploads (it builds a multipart form to
|
|
38
|
+
* derive headers but sends the raw buffer as body, and GCS ignores
|
|
39
|
+
* the content-type anyway because the signed URL pins everything
|
|
40
|
+
* server-side). We match that for parity.
|
|
41
|
+
*
|
|
42
|
+
* Streams the file so a multi-MB archive doesn't sit in memory.
|
|
43
|
+
*/
|
|
44
|
+
export declare function uploadSourceToSignedUrl(opts: UploadSourceOptions): Promise<void>;
|
|
45
|
+
//# sourceMappingURL=sourceZipUpload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourceZipUpload.d.ts","sourceRoot":"","sources":["../../src/lib/sourceZipUpload.ts"],"names":[],"mappings":"AAwEA,MAAM,WAAW,6BAA6B;IAC5C,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB;;2EAEuE;IACvE,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,6BAA6B,GAClC,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB;oDACgD;IAChD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|