@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
package/src/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API of @essential-apps/shopify-test-runner.
|
|
3
|
+
*
|
|
4
|
+
* The CLI binaries (shopify-test-run-docker, etc.) are exposed via
|
|
5
|
+
* the `bin` field in package.json — they don't import from this
|
|
6
|
+
* file. This module re-exports a few utilities that consuming apps
|
|
7
|
+
* occasionally use programmatically (port checking, stealth browser
|
|
8
|
+
* launch).
|
|
9
|
+
*
|
|
10
|
+
* Most apps will only import from this package's `/playwright`
|
|
11
|
+
* subpath:
|
|
12
|
+
* import { definePlaywrightConfig } from
|
|
13
|
+
* '@essential-apps/shopify-test-runner/playwright';
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export { isPortFree, findFreePort } from './lib/freePort.js';
|
|
17
|
+
|
|
18
|
+
export { launchStealthBrowser, stealthLaunchArgs } from './lib/stealthLaunch.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Shopify Functions JS-source → deployable .wasm pipeline. Builds
|
|
22
|
+
* without the Shopify CLI; downloads stock Javy + the Shopify-CDN
|
|
23
|
+
* plugin .wasm on first use. See `./lib/functionBuild.ts` for the
|
|
24
|
+
* full rationale.
|
|
25
|
+
*/
|
|
26
|
+
export {
|
|
27
|
+
buildFunctionWasm,
|
|
28
|
+
ensureJavy,
|
|
29
|
+
ensureJavyPlugin,
|
|
30
|
+
parseFunctionToml,
|
|
31
|
+
JAVY_VERSION,
|
|
32
|
+
PLUGIN_URL,
|
|
33
|
+
} from './lib/functionBuild.js';
|
|
34
|
+
export type {
|
|
35
|
+
BuildFunctionWasmOptions,
|
|
36
|
+
BuildFunctionWasmResult,
|
|
37
|
+
FunctionTarget,
|
|
38
|
+
ParsedFunctionToml,
|
|
39
|
+
} from './lib/functionBuild.js';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* App Management API source-zip upload helpers — used together
|
|
43
|
+
* when deploying app versions that contain a function or
|
|
44
|
+
* post-purchase extension (their binary artifacts don't fit
|
|
45
|
+
* inline in the `source` manifest).
|
|
46
|
+
*/
|
|
47
|
+
export {
|
|
48
|
+
requestSourceUploadUrl,
|
|
49
|
+
uploadSourceToSignedUrl,
|
|
50
|
+
} from './lib/sourceZipUpload.js';
|
|
51
|
+
export type {
|
|
52
|
+
RequestSourceUploadUrlOptions,
|
|
53
|
+
UploadSourceOptions,
|
|
54
|
+
} from './lib/sourceZipUpload.js';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Source-bundle (zip) assembly. Produces the artifact uploaded to
|
|
58
|
+
* GCS and referenced via `AppVersionInput.sourceUrl`.
|
|
59
|
+
*/
|
|
60
|
+
export {
|
|
61
|
+
buildSourceBundle,
|
|
62
|
+
readExtensionFilesAsBuffers,
|
|
63
|
+
} from './lib/buildSourceBundle.js';
|
|
64
|
+
export type {
|
|
65
|
+
BuildSourceBundleOptions,
|
|
66
|
+
ManifestModule,
|
|
67
|
+
} from './lib/buildSourceBundle.js';
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assemble the source archive uploaded for `appVersionCreate` via
|
|
3
|
+
* the `sourceUrl` path.
|
|
4
|
+
*
|
|
5
|
+
* What format does Shopify actually accept?
|
|
6
|
+
* -----------------------------------------
|
|
7
|
+
* Empirically (May 2026, App Management /unstable/ endpoint): the
|
|
8
|
+
* server accepts **tar + brotli** (`.tar.br`), AND requires
|
|
9
|
+
* `sourceExtension: BR` when minting the signed-upload URL via
|
|
10
|
+
* `appRequestSourceUploadUrl`. The legacy `sourceExtension: ZIP`
|
|
11
|
+
* branch exists in the schema and returns a working signed URL, but
|
|
12
|
+
* something downstream chokes on zips that contain a per-extension
|
|
13
|
+
* folder layout — the server 500s on any zip whose top level has a
|
|
14
|
+
* `<uid>/...` directory entry. Tar+brotli works.
|
|
15
|
+
*
|
|
16
|
+
* This was reverse-engineered from `Shopify/cli`'s bundle pipeline:
|
|
17
|
+
* packages/app/src/cli/services/bundle.ts
|
|
18
|
+
* packages/cli-kit/src/public/node/archiver.ts
|
|
19
|
+
* The CLI ALWAYS uses tar+brotli; the ZIP code path appears to be
|
|
20
|
+
* unused / vestigial.
|
|
21
|
+
*
|
|
22
|
+
* Layout inside the archive
|
|
23
|
+
* -------------------------
|
|
24
|
+
* manifest.json ← top-level module list
|
|
25
|
+
* <module_uid>/dist/index.wasm ← function WASM, BASE64-
|
|
26
|
+
* ENCODED AS TEXT (not raw
|
|
27
|
+
* binary — see below)
|
|
28
|
+
* <theme_uid>/assets/<file> ← theme files, raw bytes
|
|
29
|
+
* <theme_uid>/blocks/<file>
|
|
30
|
+
* <theme_uid>/locales/<file>
|
|
31
|
+
* <theme_uid>/snippets/<file>
|
|
32
|
+
*
|
|
33
|
+
* The base64-encoding-of-wasm is THE critical detail that gets you
|
|
34
|
+
* past `"Wasm file is not present"`. The Shopify CLI does this in
|
|
35
|
+
* `bundleFunctionExtension(wasmPath, bundlePath)` which calls
|
|
36
|
+
* `readFile(path, {encoding: 'base64'})` and `writeFile(bundlePath, b64)`.
|
|
37
|
+
* The server reads the file at `<uid>/dist/index.wasm` and expects
|
|
38
|
+
* base64 text content, decoding it back to WASM bytes on its side.
|
|
39
|
+
*
|
|
40
|
+
* Manifest format
|
|
41
|
+
* ---------------
|
|
42
|
+
* Mirrors the CLI's `app.manifest()` builder
|
|
43
|
+
* (`packages/app/src/cli/models/app/app.ts`). Each module entry has:
|
|
44
|
+
* {
|
|
45
|
+
* type: <externalIdentifier — e.g. 'function' for plain function>,
|
|
46
|
+
* handle: <toml handle>,
|
|
47
|
+
* uid: <stable uuid>,
|
|
48
|
+
* assets: <uid>, ← same string as uid; folder name in archive
|
|
49
|
+
* target: <contextValue>, ← single-target handle, or '' for multi
|
|
50
|
+
* config: { … per-spec deployConfig output … },
|
|
51
|
+
* }
|
|
52
|
+
*/
|
|
53
|
+
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
54
|
+
import { resolve } from 'node:path';
|
|
55
|
+
import { brotliCompressSync, constants as zlibConst } from 'node:zlib';
|
|
56
|
+
import { pack as tarPack } from 'tar-stream';
|
|
57
|
+
|
|
58
|
+
/** A single entry in the manifest.json's `modules` array. Matches
|
|
59
|
+
* the CLI's `ManifestModule` interface from app.ts. */
|
|
60
|
+
export interface ManifestModule {
|
|
61
|
+
type: string;
|
|
62
|
+
handle: string;
|
|
63
|
+
uid: string;
|
|
64
|
+
/** Folder name inside the archive that holds this module's
|
|
65
|
+
* artifacts. Conventionally equal to `uid`. */
|
|
66
|
+
assets?: string;
|
|
67
|
+
/** Single-target handle (e.g. "cart.lines.discounts.generate.run").
|
|
68
|
+
* Empty string for multi-target functions, omitted for non-targeted
|
|
69
|
+
* modules like app_home / branding / app_access. */
|
|
70
|
+
target?: string;
|
|
71
|
+
config: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface BuildSourceBundleOptions {
|
|
75
|
+
/** The app's top-level display name. Goes into `manifest.name`. */
|
|
76
|
+
name: string;
|
|
77
|
+
/** All modules for this deploy. */
|
|
78
|
+
modules: ManifestModule[];
|
|
79
|
+
/**
|
|
80
|
+
* Files to embed in the archive, keyed by their path-inside-the-
|
|
81
|
+
* archive. Function .wasm files MUST be base64-encoded as text by
|
|
82
|
+
* the caller (we'd do it here, but the caller already has the
|
|
83
|
+
* raw wasm and may or may not have other text/binary content —
|
|
84
|
+
* doing it here would obscure the contract).
|
|
85
|
+
*/
|
|
86
|
+
files: Record<string, Buffer | string>;
|
|
87
|
+
/** Output path. Caller owns the path — typically /tmp/<…>.tar.br. */
|
|
88
|
+
outPath: string;
|
|
89
|
+
/**
|
|
90
|
+
* Brotli quality (0-11). Default 7 matches the CLI's choice. Higher
|
|
91
|
+
* = smaller + slower. Functions + theme bundles fit comfortably in
|
|
92
|
+
* a few hundred KB at quality 7.
|
|
93
|
+
*/
|
|
94
|
+
brotliQuality?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build a tar+brotli source bundle and write it to `outPath`. Returns
|
|
99
|
+
* the path written.
|
|
100
|
+
*
|
|
101
|
+
* Implementation note: we tar in-memory (chunked into Node Buffers)
|
|
102
|
+
* then brotli-compress in one shot. Suitable for the < 10MB bundles
|
|
103
|
+
* we deal with; switch to streaming if that changes.
|
|
104
|
+
*/
|
|
105
|
+
export async function buildSourceBundle(
|
|
106
|
+
opts: BuildSourceBundleOptions,
|
|
107
|
+
): Promise<string> {
|
|
108
|
+
const manifestJson = JSON.stringify(
|
|
109
|
+
{ name: opts.name, handle: '', modules: opts.modules },
|
|
110
|
+
null,
|
|
111
|
+
2,
|
|
112
|
+
);
|
|
113
|
+
const entries: Array<{ name: string; content: Buffer }> = [
|
|
114
|
+
{ name: 'manifest.json', content: Buffer.from(manifestJson, 'utf8') },
|
|
115
|
+
];
|
|
116
|
+
for (const [name, content] of Object.entries(opts.files)) {
|
|
117
|
+
entries.push({
|
|
118
|
+
name,
|
|
119
|
+
content: Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8'),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// tar-stream pack: collect chunks, finalize, then brotli-compress.
|
|
124
|
+
const tarBuf = await new Promise<Buffer>((resolve_, reject) => {
|
|
125
|
+
const pack = tarPack();
|
|
126
|
+
const chunks: Buffer[] = [];
|
|
127
|
+
pack.on('data', (c: Buffer) => chunks.push(c));
|
|
128
|
+
pack.on('end', () => resolve_(Buffer.concat(chunks)));
|
|
129
|
+
pack.on('error', reject);
|
|
130
|
+
(async () => {
|
|
131
|
+
for (const e of entries) {
|
|
132
|
+
await new Promise<void>((res, rej) => {
|
|
133
|
+
pack.entry({ name: e.name }, e.content, (err) => (err ? rej(err) : res()));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
pack.finalize();
|
|
137
|
+
})().catch(reject);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const brBuf = brotliCompressSync(tarBuf, {
|
|
141
|
+
params: {
|
|
142
|
+
[zlibConst.BROTLI_PARAM_QUALITY]: opts.brotliQuality ?? 7,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
writeFileSync(opts.outPath, brBuf);
|
|
146
|
+
return opts.outPath;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Walk an extension directory and return its file contents keyed by
|
|
151
|
+
* the path-inside-the-archive-folder (i.e. NOT prefixed with the
|
|
152
|
+
* uid; the caller prepends that). Returns raw Buffers.
|
|
153
|
+
*
|
|
154
|
+
* Skips dirs/files that aren't shipped (same list as the inline
|
|
155
|
+
* theme bundler in deployAppVersion.ts).
|
|
156
|
+
*/
|
|
157
|
+
export function readExtensionFilesAsBuffers(
|
|
158
|
+
extensionDir: string,
|
|
159
|
+
): Record<string, Buffer> {
|
|
160
|
+
const SKIP_DIRS = new Set([
|
|
161
|
+
'node_modules',
|
|
162
|
+
'.git',
|
|
163
|
+
'src',
|
|
164
|
+
'frontend',
|
|
165
|
+
'generated',
|
|
166
|
+
]);
|
|
167
|
+
const SKIP_FILES = new Set([
|
|
168
|
+
'shopify.extension.toml',
|
|
169
|
+
'package.json',
|
|
170
|
+
'package-lock.json',
|
|
171
|
+
'tsconfig.json',
|
|
172
|
+
'.shopifyignore',
|
|
173
|
+
'.gitignore',
|
|
174
|
+
'README.md',
|
|
175
|
+
]);
|
|
176
|
+
const out: Record<string, Buffer> = {};
|
|
177
|
+
function walk(dir: string, rel: string): void {
|
|
178
|
+
for (const name of readdirSync(dir)) {
|
|
179
|
+
const full = resolve(dir, name);
|
|
180
|
+
const relPath = rel ? `${rel}/${name}` : name;
|
|
181
|
+
const st = statSync(full);
|
|
182
|
+
if (st.isDirectory()) {
|
|
183
|
+
if (SKIP_DIRS.has(name) || name.startsWith('.')) continue;
|
|
184
|
+
walk(full, relPath);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (SKIP_FILES.has(name)) continue;
|
|
188
|
+
if (name.endsWith('.config.ts') || name.endsWith('.config.mts')) continue;
|
|
189
|
+
if (name.endsWith('.config.js') || name.endsWith('.config.cjs')) continue;
|
|
190
|
+
if (name.endsWith('.js.map')) continue; // CLI excludes these
|
|
191
|
+
if (name.endsWith('.metafile.json')) continue; // CLI excludes these
|
|
192
|
+
out[relPath] = readFileSync(full);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
walk(extensionDir, '');
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createServer } from 'node:net';
|
|
2
|
+
|
|
3
|
+
/** Asks the OS for a random free TCP port. */
|
|
4
|
+
export function findFreePort(): Promise<number> {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const srv = createServer();
|
|
7
|
+
srv.unref();
|
|
8
|
+
srv.on('error', reject);
|
|
9
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
10
|
+
const addr = srv.address();
|
|
11
|
+
if (addr && typeof addr === 'object') {
|
|
12
|
+
const port = addr.port;
|
|
13
|
+
srv.close(() => resolve(port));
|
|
14
|
+
} else {
|
|
15
|
+
srv.close();
|
|
16
|
+
reject(new Error('Could not determine free port'));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Returns true if the given port is free to bind. */
|
|
23
|
+
export function isPortFree(port: number): Promise<boolean> {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
const srv = createServer();
|
|
26
|
+
srv.unref();
|
|
27
|
+
srv.once('error', () => resolve(false));
|
|
28
|
+
srv.once('listening', () => {
|
|
29
|
+
srv.close(() => resolve(true));
|
|
30
|
+
});
|
|
31
|
+
srv.listen(port, '127.0.0.1');
|
|
32
|
+
});
|
|
33
|
+
}
|