@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/edge/ca.cnf
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[req]
|
|
2
|
+
distinguished_name = req_distinguished_name
|
|
3
|
+
prompt = no
|
|
4
|
+
x509_extensions = v3_ca
|
|
5
|
+
|
|
6
|
+
[req_distinguished_name]
|
|
7
|
+
C = US
|
|
8
|
+
O = essential-apps shopify-test
|
|
9
|
+
CN = essential-apps offline test CA
|
|
10
|
+
|
|
11
|
+
[v3_ca]
|
|
12
|
+
basicConstraints = critical, CA:TRUE
|
|
13
|
+
keyUsage = critical, keyCertSign, cRLSign, digitalSignature
|
|
14
|
+
subjectKeyIdentifier = hash
|
package/src/edge/ca.crt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIDkDCCAnigAwIBAgIUIKGQzgV0DVnoczi/+0ku6XxtXpUwDQYJKoZIhvcNAQEL
|
|
3
|
+
BQAwYDELMAkGA1UEBhMCVVMxJDAiBgNVBAoMG2Vzc2VudGlhbC1hcHBzIHNob3Bp
|
|
4
|
+
ZnktdGVzdDErMCkGA1UEAwwiZXNzZW50aWFsLWFwcHMgb2ZmbGluZSBFMkUgdGVz
|
|
5
|
+
dCBDQTAeFw0yNjA1MTExODIwNTBaFw0zNjA1MDgxODIwNTBaMGAxCzAJBgNVBAYT
|
|
6
|
+
AlVTMSQwIgYDVQQKDBtlc3NlbnRpYWwtYXBwcyBzaG9waWZ5LXRlc3QxKzApBgNV
|
|
7
|
+
BAMMImVzc2VudGlhbC1hcHBzIG9mZmxpbmUgRTJFIHRlc3QgQ0EwggEiMA0GCSqG
|
|
8
|
+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVqEmQ3OULDil0xGp+s5Be9bqo/irOT4/x
|
|
9
|
+
IksWc+ul1oREVJ1F+EXINn2YUZZbrQ8i1tJg3xz/6u2EiGXozTuDsAX4VFa9Ew60
|
|
10
|
+
fvp+MsxKvIDZgQ54xizvlGEbFFIUcTMr0czzyRzhoPsvx0vZxMv+mnlU/nKSuRTv
|
|
11
|
+
0+2rlVi19MjOmlmiF7imaJcZeTkzjAMy5tKgIu1QLk+dTNe7wXdr45gWLK959POy
|
|
12
|
+
JT7juKbpR2kgNhh91yQhvPnUTWibrln2pk/boTBM6s17BX28DwjRJM/UPAT9f+pC
|
|
13
|
+
h3p4Uan9NBHdPaOM9Wvj8cZqXWEz3ErYJmf9xUxYURStakozkFFrAgMBAAGjQjBA
|
|
14
|
+
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBQvul3h
|
|
15
|
+
wRTsFPS1GZbOxAPssSEDfzANBgkqhkiG9w0BAQsFAAOCAQEAMDGe/lf/7x66nchI
|
|
16
|
+
3KQcuX3OQ+hPeODw9F+7zY4KUf4+z1Me89M07kFWVR3cBplGIGUzQm6Sj3BCrShC
|
|
17
|
+
JgskN/jkGT7s5FHssHWRQ5m6uMTbDMctyVZYoW71Ip4pQloyh88fyWRejrJyVRhO
|
|
18
|
+
mAqB7iPEPQz9iSxaAeUJA/RU/zyBukUrP1ppU0qe6SFOTM+mfCMFZrITEJ2pbc9g
|
|
19
|
+
X9SbEk44qgQlOqfrj9FazxJp9Pjj19WLVXsY7JrNglpgzA8jwPWJSIPIU4gF0eTZ
|
|
20
|
+
2B5ORtmHnN2uBnxe4sRf8eOHZy+7SZIR+6y1q/zmwbubVV7rF76ukw/I/kg+gAm+
|
|
21
|
+
eRz9lQ==
|
|
22
|
+
-----END CERTIFICATE-----
|
package/src/edge/ca.key
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
|
2
|
+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCVqEmQ3OULDil0
|
|
3
|
+
xGp+s5Be9bqo/irOT4/xIksWc+ul1oREVJ1F+EXINn2YUZZbrQ8i1tJg3xz/6u2E
|
|
4
|
+
iGXozTuDsAX4VFa9Ew60fvp+MsxKvIDZgQ54xizvlGEbFFIUcTMr0czzyRzhoPsv
|
|
5
|
+
x0vZxMv+mnlU/nKSuRTv0+2rlVi19MjOmlmiF7imaJcZeTkzjAMy5tKgIu1QLk+d
|
|
6
|
+
TNe7wXdr45gWLK959POyJT7juKbpR2kgNhh91yQhvPnUTWibrln2pk/boTBM6s17
|
|
7
|
+
BX28DwjRJM/UPAT9f+pCh3p4Uan9NBHdPaOM9Wvj8cZqXWEz3ErYJmf9xUxYURSt
|
|
8
|
+
akozkFFrAgMBAAECggEAE3H697Wb6mR8AniwQFI4hKjNCL7YSleQI7a0UZsyQn6t
|
|
9
|
+
pOs8RtEherLfKCFsYJu13G2pMX5hQudVgTjvyjXlxmXiq1zaAFXaPUhFEoyrJV16
|
|
10
|
+
KHNaXAWej7/py3OdI/F+wpzPUoW5GPVVGI9+otxX3SGueNqu1whGriVikOl7eKNo
|
|
11
|
+
qUVgD4F+SBWFYjjNzLLnsLrPgP6jukYu77W7mhdu4jaKxJ6OzMgJwVWthjnjCB8W
|
|
12
|
+
5DEFbgUKj/ssTWBKdTQdLAxudtKgoWNERk3uSSSMeqY9cmeqOpARb1n4zLGU6Bfk
|
|
13
|
+
JjVLzEzeJtB/GrsxEby4ZGRgEkhxtA7/mvoEb5ZOAQKBgQDQp8Ng0iY+gKG7ik/g
|
|
14
|
+
RIIUJNGiRNIGN/nuHp5hj04+Ph+zuytHD/eKhSwic5kCu5GQP42AV3Y1RwekKhDn
|
|
15
|
+
DFIpwNtRl5MXkwO3pO/QdlpdZyKftLMAW8qcNSwqqqJieisQPHwd4cgsNfSYCla6
|
|
16
|
+
2ZLoCrowh4ZFThPut1Kuv4j2gQKBgQC3nX0pm9jFeqlpWJSsUxa1hk0j0Ot3kmex
|
|
17
|
+
WLOIb/tC1Gn5EHfvcExNGGL9GEGD2aEc8VYcmRtEw87XBXiT6fQ83zjjwJXCV/Bq
|
|
18
|
+
RGlGhfntUTHTWR+ToGBixfbBTYr+qv7bkI70jf7G9FukkU0Qd/nBDECSF+RGx4Re
|
|
19
|
+
XGtcjWuJ6wKBgQCyKT0rc+UR41W1w7DWZsjHGHUjYC4Q/0TZ7K0B0pJVlUgOeFfI
|
|
20
|
+
srqEPZfkxt20tqHhEFLrbkLR1ReSNhT+o8eYPUNHlOwU6gP3j87xKc2ZCVJIGcvq
|
|
21
|
+
F3aWENTojZBgE76ne23jOgFotp1mIRXTL6o/lcFLZLzienuMjl38NjFlgQKBgQCR
|
|
22
|
+
EnmFmoDW5mdbuIUe8jcLDSV9mt+wBZiv4mlW70MSNknUY1Kfd5aRgycS2UtKJXTK
|
|
23
|
+
LVPgHIgS+LI/6S6vjzVNswB70fmBJ4HoNE0JT2l8O56mYdA1D42X/NlNOTsMo4Xh
|
|
24
|
+
bIHGbzpRb1fI2pSM4n4OLOQHiaDu20yWUWbyJTpGKQKBgEiWUgo8MwDnMl4eRdtI
|
|
25
|
+
q+F3Kkr+O+lZIodHy9jqUT2nINetZXmUTl/NNLDft1qyoZfFFTFgvlIdPJywTz7R
|
|
26
|
+
5e0lapai/q5XGo+oGs19X4lz2S/2zV05HZiZHoGPf2lYv4/YRs6KqouNiPhuQwf/
|
|
27
|
+
91gGPtdtikkIah5l+fOlyXox
|
|
28
|
+
-----END PRIVATE KEY-----
|
package/src/edge/cert.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-signed TLS cert pair used by the edge proxy in offline tests.
|
|
3
|
+
*
|
|
4
|
+
* TWO certs:
|
|
5
|
+
* - **CA cert** (`TEST_CA_CERT_PEM`): the trust anchor. Installed
|
|
6
|
+
* into the container's system CA store + Chrome's NSS DB at
|
|
7
|
+
* boot. Long-lived (10 years), self-signed, CA:TRUE.
|
|
8
|
+
* - **Server cert** (`TEST_SERVER_CERT_PEM` + `TEST_SERVER_KEY_PEM`):
|
|
9
|
+
* what the edge proxy presents to clients. Signed BY the CA
|
|
10
|
+
* above (so Chrome's chain validation finds the trusted root).
|
|
11
|
+
* Has Subject Alternative Names for every Shopify hostname we
|
|
12
|
+
* route: localhost, *.myshopify.com, *.shopify.com, etc.
|
|
13
|
+
*
|
|
14
|
+
* Why two certs instead of one self-signed-with-SANs:
|
|
15
|
+
* Modern Chrome (≥130 or so) rejects single self-signed certs
|
|
16
|
+
* that are also used as their own CA with ERR_CERT_INVALID, even
|
|
17
|
+
* when the cert is trust-anchored in NSS. Proper CA → server
|
|
18
|
+
* hierarchy mirrors how real publicly-trusted certs work (Let's
|
|
19
|
+
* Encrypt root → intermediate → leaf) and passes every modern
|
|
20
|
+
* verifier we've tested.
|
|
21
|
+
*
|
|
22
|
+
* Inlined as TS constants (rather than separate .pem files) so the
|
|
23
|
+
* compiled package ships everything in dist/ — no postbuild copy.
|
|
24
|
+
*
|
|
25
|
+
* This is a TEST-ONLY cert pair. The CA's private key is committed
|
|
26
|
+
* to the repo; anyone who checks out this repo can issue certs
|
|
27
|
+
* signed by it. That's fine in offline test mode where Chromium is
|
|
28
|
+
* launched with the CA explicitly added to its trust store — no
|
|
29
|
+
* production system should ever encounter this CA. If you ever see
|
|
30
|
+
* a real service presenting a cert with issuer
|
|
31
|
+
* "essential-apps offline test CA", that's a serious
|
|
32
|
+
* misconfiguration.
|
|
33
|
+
*
|
|
34
|
+
* To regenerate (e.g. expired, new SAN needed):
|
|
35
|
+
* cd src/edge
|
|
36
|
+
* openssl req -x509 -newkey rsa:2048 -nodes -keyout ca.key -out ca.crt -days 3650 -config ca.cnf
|
|
37
|
+
* openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -config server.cnf
|
|
38
|
+
* openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -extfile server.cnf -extensions v3_server
|
|
39
|
+
* # then paste ca.crt, server.crt, server.key into the constants below
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export const TEST_CA_CERT_PEM = `-----BEGIN CERTIFICATE-----
|
|
43
|
+
MIIDkDCCAnigAwIBAgIUIKGQzgV0DVnoczi/+0ku6XxtXpUwDQYJKoZIhvcNAQEL
|
|
44
|
+
BQAwYDELMAkGA1UEBhMCVVMxJDAiBgNVBAoMG2Vzc2VudGlhbC1hcHBzIHNob3Bp
|
|
45
|
+
ZnktdGVzdDErMCkGA1UEAwwiZXNzZW50aWFsLWFwcHMgb2ZmbGluZSBFMkUgdGVz
|
|
46
|
+
dCBDQTAeFw0yNjA1MTExODIwNTBaFw0zNjA1MDgxODIwNTBaMGAxCzAJBgNVBAYT
|
|
47
|
+
AlVTMSQwIgYDVQQKDBtlc3NlbnRpYWwtYXBwcyBzaG9waWZ5LXRlc3QxKzApBgNV
|
|
48
|
+
BAMMImVzc2VudGlhbC1hcHBzIG9mZmxpbmUgRTJFIHRlc3QgQ0EwggEiMA0GCSqG
|
|
49
|
+
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVqEmQ3OULDil0xGp+s5Be9bqo/irOT4/x
|
|
50
|
+
IksWc+ul1oREVJ1F+EXINn2YUZZbrQ8i1tJg3xz/6u2EiGXozTuDsAX4VFa9Ew60
|
|
51
|
+
fvp+MsxKvIDZgQ54xizvlGEbFFIUcTMr0czzyRzhoPsvx0vZxMv+mnlU/nKSuRTv
|
|
52
|
+
0+2rlVi19MjOmlmiF7imaJcZeTkzjAMy5tKgIu1QLk+dTNe7wXdr45gWLK959POy
|
|
53
|
+
JT7juKbpR2kgNhh91yQhvPnUTWibrln2pk/boTBM6s17BX28DwjRJM/UPAT9f+pC
|
|
54
|
+
h3p4Uan9NBHdPaOM9Wvj8cZqXWEz3ErYJmf9xUxYURStakozkFFrAgMBAAGjQjBA
|
|
55
|
+
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBQvul3h
|
|
56
|
+
wRTsFPS1GZbOxAPssSEDfzANBgkqhkiG9w0BAQsFAAOCAQEAMDGe/lf/7x66nchI
|
|
57
|
+
3KQcuX3OQ+hPeODw9F+7zY4KUf4+z1Me89M07kFWVR3cBplGIGUzQm6Sj3BCrShC
|
|
58
|
+
JgskN/jkGT7s5FHssHWRQ5m6uMTbDMctyVZYoW71Ip4pQloyh88fyWRejrJyVRhO
|
|
59
|
+
mAqB7iPEPQz9iSxaAeUJA/RU/zyBukUrP1ppU0qe6SFOTM+mfCMFZrITEJ2pbc9g
|
|
60
|
+
X9SbEk44qgQlOqfrj9FazxJp9Pjj19WLVXsY7JrNglpgzA8jwPWJSIPIU4gF0eTZ
|
|
61
|
+
2B5ORtmHnN2uBnxe4sRf8eOHZy+7SZIR+6y1q/zmwbubVV7rF76ukw/I/kg+gAm+
|
|
62
|
+
eRz9lQ==
|
|
63
|
+
-----END CERTIFICATE-----`;
|
|
64
|
+
export const TEST_SERVER_CERT_PEM = `-----BEGIN CERTIFICATE-----
|
|
65
|
+
MIIEYjCCA0qgAwIBAgIURPMxG/s+J/RbB610ES+g6+4uz1YwDQYJKoZIhvcNAQEL
|
|
66
|
+
BQAwYDELMAkGA1UEBhMCVVMxJDAiBgNVBAoMG2Vzc2VudGlhbC1hcHBzIHNob3Bp
|
|
67
|
+
ZnktdGVzdDErMCkGA1UEAwwiZXNzZW50aWFsLWFwcHMgb2ZmbGluZSBFMkUgdGVz
|
|
68
|
+
dCBDQTAeFw0yNjA1MTExODIwNTZaFw0zNjA1MDgxODIwNTZaMFUxCzAJBgNVBAYT
|
|
69
|
+
AlVTMSQwIgYDVQQKDBtlc3NlbnRpYWwtYXBwcyBzaG9waWZ5LXRlc3QxIDAeBgNV
|
|
70
|
+
BAMMF3Rlc3Qtc2hvcC5teXNob3BpZnkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
|
71
|
+
AQ8AMIIBCgKCAQEAqKKCIJ+3Z311vVrAzS9O8BFoDuFcS71UhXFOFnGv+32sWkv5
|
|
72
|
+
15FiqsEUA9Y38TWMmATselKFmseqgZo1EgvNe6rFrnjyyJgEeYrLv40bz3JEp/Hs
|
|
73
|
+
GrgID4XaGJFx/tDsMLBtWLJykbkowRcJnQK0NoeYNC9E1JKfmU9WnT0eqOzc9ryn
|
|
74
|
+
iXj9pptDfd4BndYYSW4lvooshdyrVNcLnm67EXdyNukFk37ru9vSu12Oo+QynnZ7
|
|
75
|
+
6Ijghpxjim5HHG3V5VF9fGSTcEKWqgw1l9FRESs6qFtG3GE5CSbg1J15uuUHBCof
|
|
76
|
+
qIxUJwk6foHqD1V+bMUbxZt1Aj05pZ2NJVAjpQIDAQABo4IBHTCCARkwDAYDVR0T
|
|
77
|
+
AQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwgaMG
|
|
78
|
+
A1UdEQSBmzCBmIIJbG9jYWxob3N0gg8qLm15c2hvcGlmeS5jb22CDW15c2hvcGlm
|
|
79
|
+
eS5jb22CDSouc2hvcGlmeS5jb22CC3Nob3BpZnkuY29tghEqLnNob3BpZnlhcHBz
|
|
80
|
+
LmNvbYISKi5zaG9waWZ5Y2xvdWQuY29tghAqLnNob3BpZnljZG4uY29thwR/AAAB
|
|
81
|
+
hxAAAAAAAAAAAAAAAAAAAAABMB0GA1UdDgQWBBTU9DyGKwRHIMACdy1hFqmWj2CW
|
|
82
|
+
6DAfBgNVHSMEGDAWgBQvul3hwRTsFPS1GZbOxAPssSEDfzANBgkqhkiG9w0BAQsF
|
|
83
|
+
AAOCAQEAUJ5MwZ6iRLuOImmWMiiNTvjxYq7e8+/0zYkqXreQpQEah0muzHnoolhp
|
|
84
|
+
A4aolovZGdAJnAQXG38O8mRwIWxtJqcPPXEITz0O2H2Bdli4r+ikcIkUBNlvsuH2
|
|
85
|
+
1QVwDvyWhgp2gW6wyYzQc5GSbZ9wjca0bUZx+CQsuyRMjkerEVMlnTlxyCVIRIuE
|
|
86
|
+
6WxqZeoHiUskowUc7hIYzUIjadOzddFfdePW1nE69Se4aOM4lbvJ5HNwiNim+3nE
|
|
87
|
+
1RTDsQCMcNcrMdY4g30cv9zm0+rjRB+Rmd3K4EBVaMrnbrMq9guXnZsyE8hkbKn3
|
|
88
|
+
VCXk3ZMrF4yyr/5s7FLeAsE8b9PYtw==
|
|
89
|
+
-----END CERTIFICATE-----`;
|
|
90
|
+
export const TEST_SERVER_KEY_PEM = `-----BEGIN PRIVATE KEY-----
|
|
91
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCoooIgn7dnfXW9
|
|
92
|
+
WsDNL07wEWgO4VxLvVSFcU4Wca/7faxaS/nXkWKqwRQD1jfxNYyYBOx6UoWax6qB
|
|
93
|
+
mjUSC817qsWuePLImAR5isu/jRvPckSn8ewauAgPhdoYkXH+0OwwsG1YsnKRuSjB
|
|
94
|
+
FwmdArQ2h5g0L0TUkp+ZT1adPR6o7Nz2vKeJeP2mm0N93gGd1hhJbiW+iiyF3KtU
|
|
95
|
+
1wuebrsRd3I26QWTfuu729K7XY6j5DKednvoiOCGnGOKbkccbdXlUX18ZJNwQpaq
|
|
96
|
+
DDWX0VERKzqoW0bcYTkJJuDUnXm65QcEKh+ojFQnCTp+geoPVX5sxRvFm3UCPTml
|
|
97
|
+
nY0lUCOlAgMBAAECggEAFzC17W+ZZKl7qg0Ta4QgemIiab1zGFVSjMFOqEZ9GXwo
|
|
98
|
+
WgiNtKfhJjNEIdzxN4ISMgunS5ESn3zqxUTkHHW0DdgntD0cwhopr183csGge/Au
|
|
99
|
+
YdwiiHAbZ6sUGYHS5+RqPq3cc7CikcihQqB86XMoPkF6XF7Nu9/oA8jF0/zGPRuQ
|
|
100
|
+
BiR722XwemloFMfNybnhYzLx5vr0EFs5R8UtDCZcRveWAbP5ggbkEfiz73zGA0gr
|
|
101
|
+
VGq0rQaovLntjc8Gtb93Q57pM7NOahgTQy6DXK42Of1gqkWTvFERXHjS5XH9NdzM
|
|
102
|
+
/ahXz6G/8ms2w6Xaj8uhZHxwEW6hF8S1D8q2Llf/AQKBgQDtXY0wFtI4dWE3MhHO
|
|
103
|
+
pQ6v21gMFP7bmAkN3DTbcAJYsDbZiGlzaH+FF+S1Uz8gbwCKlj9FYR4r0oU0ccfF
|
|
104
|
+
h023BQaxQXNCvfYunApxw1XG2NEkXvGWzD9DX/Xhj4z0WWve9Zh0tRVGMEkETjqT
|
|
105
|
+
xzOhGfxl1g2/CixrzTvKpD8+JQKBgQC136TOwQMs3EyRe1YwSWM0wlN7BcPehOPx
|
|
106
|
+
sTQNkyZsxE3u7NkKa6LXK7gdgC+oelWV6+UXbuEs9ZLWT0yh22T5ViO3sLWiEL7P
|
|
107
|
+
yE6y2j8nTucK3RynN8cEFuolBs3V+/B2K/yy3M9y2oahKmyDYnjy7AxI3UFQm9N7
|
|
108
|
+
hxCX4YKXgQKBgQDr2ulPv11jfD78+WN4UcomM21pk/MpgAh/HS/oW4P5XB8kR8eA
|
|
109
|
+
RXVwai13fyBaufFvw5ta9QVlxelWEzjNrYQrN3NO7hn5V4gnCCXYpJ+21fn6idzE
|
|
110
|
+
Wm8CI3fOiTUmFzR4dtDmJojdFV14ScMq0+UZTxjcl7VQ/mrlMykWUd4FgQKBgBfU
|
|
111
|
+
fNimS482MkYhnfJnuzrvd1a4M6jVSrShXkulCzTXJ8r1d5646bY9wTsET7pIhSxG
|
|
112
|
+
o1bFrXVhm+K+szDF+V3+HmH0Imhgv0+kVEN0+y9gVD+FJzr1wPrVMcq2MIQoJaKm
|
|
113
|
+
Ms8QxZGr9lXppBw269gQe6+UZfl04WnfEZqE7sKBAoGAQrP3o4fqVUb200Bn0dO+
|
|
114
|
+
F8fzgHT1lZAoz5NAyo4VOW4KZ6EgDhuxM2kG3WelGhYb0CwYzWS3WzAw/jxwSHMx
|
|
115
|
+
QhJtvjhtZzYM3P4RDHjSCvQm0X8xv6OMMOF3I0RmdZQFHLo7xIAkehPIpbmBaxSf
|
|
116
|
+
2ojRh6FIbwe+526P8Qcpstk=
|
|
117
|
+
-----END PRIVATE KEY-----`;
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge proxy — a real HTTPS reverse-proxy that takes Chromium
|
|
3
|
+
* requests for Shopify-shaped hostnames (test-shop.myshopify.com,
|
|
4
|
+
* admin.shopify.com, cdn.shopify.com) and dispatches them to the
|
|
5
|
+
* right internal mock based on Host + path.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: we abandoned Playwright's `page.route()` for
|
|
8
|
+
* routing because `route.fulfill({status: 302, …})` causes
|
|
9
|
+
* Chromium to follow redirects via its native network stack,
|
|
10
|
+
* bypassing route handlers — and our `test-shop.myshopify.com`
|
|
11
|
+
* domain resolves to real Shopify on the public internet, so any
|
|
12
|
+
* un-intercepted request leaks out of the test harness.
|
|
13
|
+
*
|
|
14
|
+
* The fix: drop down a network layer. With Chromium launched via
|
|
15
|
+
*
|
|
16
|
+
* --host-resolver-rules="MAP test-shop.myshopify.com 127.0.0.1:<edgePort>, …"
|
|
17
|
+
* --ignore-certificate-errors
|
|
18
|
+
*
|
|
19
|
+
* ALL browser traffic to those hostnames (initial requests, redirect
|
|
20
|
+
* follow-ups, sub-resources, XHR, fetch, service workers) lands at
|
|
21
|
+
* this proxy. There is no per-request interception layer to bypass.
|
|
22
|
+
* Redirects are handled by Chromium natively. The browser's URL bar
|
|
23
|
+
* stays correct.
|
|
24
|
+
*
|
|
25
|
+
* The proxy itself uses Node's `http` module (raw — no undici/fetch,
|
|
26
|
+
* which hangs against sibling-process loopback ports), forwards the
|
|
27
|
+
* request to the right internal mock, and streams the response back.
|
|
28
|
+
* 302/307/etc. responses are passed through unchanged; Chromium does
|
|
29
|
+
* the right thing with them.
|
|
30
|
+
*
|
|
31
|
+
* Cert: a static self-signed cert lives in `src/edge/cert.pem` +
|
|
32
|
+
* `src/edge/key.pem`, with SANs covering `*.myshopify.com`,
|
|
33
|
+
* `*.shopify.com`, etc. (See `src/edge/openssl.cnf`.) The cert is
|
|
34
|
+
* trusted because Chromium runs with `--ignore-certificate-errors`.
|
|
35
|
+
*/
|
|
36
|
+
import { createServer as createHttpsServer } from 'node:https';
|
|
37
|
+
import { request as httpRequest, type IncomingHttpHeaders } from 'node:http';
|
|
38
|
+
import type { AddressInfo } from 'node:net';
|
|
39
|
+
import {
|
|
40
|
+
TEST_CA_CERT_PEM,
|
|
41
|
+
TEST_SERVER_CERT_PEM,
|
|
42
|
+
TEST_SERVER_KEY_PEM,
|
|
43
|
+
} from './cert.js';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Internal mock URLs the edge dispatches to. Each is an HTTP origin
|
|
47
|
+
* like `http://127.0.0.1:34567`. The edge speaks HTTPS to the
|
|
48
|
+
* browser, HTTP to the internal mocks (they're loopback-only;
|
|
49
|
+
* TLS between in-process services is pointless ceremony).
|
|
50
|
+
*/
|
|
51
|
+
export interface EdgeBackends {
|
|
52
|
+
/** Mock storefront (Liquid renderer, cart, /cart/add.js, etc.). */
|
|
53
|
+
storefront: string;
|
|
54
|
+
/** Mock Admin GraphQL API. */
|
|
55
|
+
adminApi: string;
|
|
56
|
+
/** Mock Storefront GraphQL API. */
|
|
57
|
+
storefrontApi: string;
|
|
58
|
+
/** Mock admin shell (the admin.shopify.com iframe host). */
|
|
59
|
+
adminShell: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface EdgeOptions {
|
|
63
|
+
/** Backend mock URLs. */
|
|
64
|
+
backends: EdgeBackends;
|
|
65
|
+
/**
|
|
66
|
+
* Hostname of the test shop, e.g. `test-shop.myshopify.com`.
|
|
67
|
+
* Used to recognize storefront requests vs cdn/admin.
|
|
68
|
+
*/
|
|
69
|
+
shopDomain: string;
|
|
70
|
+
/** TCP port to listen on. 0 → OS-assigned. */
|
|
71
|
+
port?: number;
|
|
72
|
+
/** Bind hostname (default `127.0.0.1`). */
|
|
73
|
+
hostname?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface StartedEdge {
|
|
77
|
+
/** The actual port we bound to (resolved when port=0). */
|
|
78
|
+
port: number;
|
|
79
|
+
/** `https://127.0.0.1:<port>` — for diagnostic logging only. */
|
|
80
|
+
baseUrl: string;
|
|
81
|
+
/** Stop the proxy. */
|
|
82
|
+
close: () => Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Resolve an incoming request to (internalBase, overridePath).
|
|
87
|
+
*
|
|
88
|
+
* Host-based first (admin/cdn pick a backend), then path-based on
|
|
89
|
+
* the shop domain (/admin/api/, /api/ go to dedicated mocks).
|
|
90
|
+
*/
|
|
91
|
+
function dispatch(
|
|
92
|
+
hostHeader: string,
|
|
93
|
+
pathWithQuery: string,
|
|
94
|
+
shopDomain: string,
|
|
95
|
+
backends: EdgeBackends,
|
|
96
|
+
): { internalBase: string; overridePath: string } {
|
|
97
|
+
// Strip ":port" — Host may include it; we only care about hostname.
|
|
98
|
+
const host = hostHeader.split(':')[0]?.toLowerCase() ?? '';
|
|
99
|
+
const path = pathWithQuery;
|
|
100
|
+
|
|
101
|
+
// ── admin.shopify.com → admin shell ─────────────────────
|
|
102
|
+
if (host === 'admin.shopify.com') {
|
|
103
|
+
return { internalBase: backends.adminShell, overridePath: path };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ── fonts.shopifycdn.com → storefront (handles its /cdn/fonts route) ─
|
|
107
|
+
// The storefront mock owns the bundled font mirror produced by
|
|
108
|
+
// the `fonts` probe. Routing the real Shopify font-CDN hostname
|
|
109
|
+
// here means `font_face` filter output (which contains the real
|
|
110
|
+
// shopifycdn URL by design) just works in-browser.
|
|
111
|
+
if (host === 'fonts.shopifycdn.com') {
|
|
112
|
+
return { internalBase: backends.storefront, overridePath: `/__shopify-fonts${path}` };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── *.shopifycdn.com → storefront mirror ────────────────────
|
|
116
|
+
// Any other Shopify CDN subdomain not handled above falls back to
|
|
117
|
+
// the generic asset-mirror handler. Bundled by the `mirror` probe.
|
|
118
|
+
if (host.endsWith('.shopifycdn.com')) {
|
|
119
|
+
return {
|
|
120
|
+
internalBase: backends.storefront,
|
|
121
|
+
overridePath: `/__shopify-mirror/${host}${path}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── cdn.shopify.com → split by path ─────────────────────
|
|
126
|
+
if (host === 'cdn.shopify.com') {
|
|
127
|
+
// App Bridge + Polaris CSS etc. live under /shopifycloud
|
|
128
|
+
// and /static, both served by the admin shell mock (which
|
|
129
|
+
// mirrors Shopify's CDN snapshot at packages/mock-admin/
|
|
130
|
+
// cdn-mirror).
|
|
131
|
+
if (path.startsWith('/shopifycloud/')) {
|
|
132
|
+
return { internalBase: backends.adminShell, overridePath: path };
|
|
133
|
+
}
|
|
134
|
+
if (path.startsWith('/static/')) {
|
|
135
|
+
return { internalBase: backends.adminShell, overridePath: path };
|
|
136
|
+
}
|
|
137
|
+
// Extension assets and theme assets live on the storefront
|
|
138
|
+
// mock. cdn.shopify.com/foo/bar/assets/baz.js → /assets/baz.js
|
|
139
|
+
if (path.startsWith('/extensions/')) {
|
|
140
|
+
return { internalBase: backends.storefront, overridePath: path };
|
|
141
|
+
}
|
|
142
|
+
const assetIdx = path.lastIndexOf('/assets/');
|
|
143
|
+
if (assetIdx >= 0) {
|
|
144
|
+
return {
|
|
145
|
+
internalBase: backends.storefront,
|
|
146
|
+
overridePath: path.slice(assetIdx),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
// Fall through: anything else on cdn.shopify.com → storefront.
|
|
150
|
+
return { internalBase: backends.storefront, overridePath: path };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── shop.myshopify.com (or any *.myshopify.com) ──────────
|
|
154
|
+
if (host === shopDomain.toLowerCase() || host.endsWith('.myshopify.com')) {
|
|
155
|
+
if (path.startsWith('/admin/api/')) {
|
|
156
|
+
return { internalBase: backends.adminApi, overridePath: path };
|
|
157
|
+
}
|
|
158
|
+
if (path.startsWith('/api/')) {
|
|
159
|
+
return { internalBase: backends.storefrontApi, overridePath: path };
|
|
160
|
+
}
|
|
161
|
+
// Per-shop CDN proxy: theme assets, shopifycloud scripts,
|
|
162
|
+
// product images, fonts. Real Shopify hosts them all under
|
|
163
|
+
// `<shop>/cdn/*`. The mirror probe captures these into the
|
|
164
|
+
// shared mirror dir; serve from there transparently so any
|
|
165
|
+
// shop hostname resolves to mirrored bytes.
|
|
166
|
+
if (path.startsWith('/cdn/')) {
|
|
167
|
+
return {
|
|
168
|
+
internalBase: backends.storefront,
|
|
169
|
+
overridePath: `/__shopify-mirror/${host}${path}`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return { internalBase: backends.storefront, overridePath: path };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── unknown host: best-effort → storefront ─────────────
|
|
176
|
+
// Chromium with host-resolver-rules wouldn't deliver an
|
|
177
|
+
// unmapped host here, but if someone hits this directly (a
|
|
178
|
+
// test using page.request with a custom host header, etc.)
|
|
179
|
+
// returning storefront 404s with a useful body is friendlier
|
|
180
|
+
// than connection refused.
|
|
181
|
+
return { internalBase: backends.storefront, overridePath: path };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Read a request body into a Buffer. Streaming would be more
|
|
186
|
+
* memory-efficient, but mock-sized POSTs are tiny (cart items,
|
|
187
|
+
* form data) and buffering keeps the proxy logic simple.
|
|
188
|
+
*/
|
|
189
|
+
function readBody(req: NodeJS.ReadableStream): Promise<Buffer> {
|
|
190
|
+
return new Promise((resolveBody, rejectBody) => {
|
|
191
|
+
const chunks: Buffer[] = [];
|
|
192
|
+
req.on('data', (c: Buffer) => chunks.push(c));
|
|
193
|
+
req.on('end', () => resolveBody(Buffer.concat(chunks)));
|
|
194
|
+
req.on('error', rejectBody);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Forward one request to the chosen internal mock and stream the response back. */
|
|
199
|
+
async function forwardOne(
|
|
200
|
+
internalBase: string,
|
|
201
|
+
overridePath: string,
|
|
202
|
+
method: string,
|
|
203
|
+
headers: IncomingHttpHeaders,
|
|
204
|
+
body: Buffer,
|
|
205
|
+
): Promise<{ status: number; headers: IncomingHttpHeaders; body: Buffer }> {
|
|
206
|
+
const target = new URL(internalBase);
|
|
207
|
+
// Drop hop-by-hop headers and the original Host — we set Host
|
|
208
|
+
// to the internal mock's address so Hono's c.req.url is
|
|
209
|
+
// consistent.
|
|
210
|
+
const outHeaders: Record<string, string> = {};
|
|
211
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
212
|
+
if (!v) continue;
|
|
213
|
+
const lower = k.toLowerCase();
|
|
214
|
+
if (lower === 'host' || lower === 'connection' || lower === 'keep-alive' || lower === 'transfer-encoding') continue;
|
|
215
|
+
if (typeof v === 'string') outHeaders[k] = v;
|
|
216
|
+
else if (Array.isArray(v)) outHeaders[k] = v.join(', ');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return new Promise((resolveReq, rejectReq) => {
|
|
220
|
+
const r = httpRequest(
|
|
221
|
+
{
|
|
222
|
+
hostname: target.hostname,
|
|
223
|
+
port: Number(target.port),
|
|
224
|
+
path: overridePath,
|
|
225
|
+
method,
|
|
226
|
+
headers: outHeaders,
|
|
227
|
+
},
|
|
228
|
+
(res) => {
|
|
229
|
+
const chunks: Buffer[] = [];
|
|
230
|
+
res.on('data', (c: Buffer) => chunks.push(c));
|
|
231
|
+
res.on('end', () =>
|
|
232
|
+
resolveReq({
|
|
233
|
+
status: res.statusCode ?? 502,
|
|
234
|
+
headers: res.headers,
|
|
235
|
+
body: Buffer.concat(chunks),
|
|
236
|
+
}),
|
|
237
|
+
);
|
|
238
|
+
res.on('error', rejectReq);
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
r.on('error', rejectReq);
|
|
242
|
+
if (body.length > 0) r.write(body);
|
|
243
|
+
r.end();
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Boot the edge HTTPS proxy. Returns a handle with the bound port
|
|
249
|
+
* and a `close()` to tear it down.
|
|
250
|
+
*/
|
|
251
|
+
export async function startEdgeProxy(opts: EdgeOptions): Promise<StartedEdge> {
|
|
252
|
+
const server = createHttpsServer(
|
|
253
|
+
{
|
|
254
|
+
// Send server cert + CA chain so Chrome's chain validation
|
|
255
|
+
// sees a complete path even without `intermediate_certs`.
|
|
256
|
+
// Some Chrome versions are stricter about chain
|
|
257
|
+
// completeness when the CA is "private" (NSS-trusted only).
|
|
258
|
+
cert: `${TEST_SERVER_CERT_PEM}\n${TEST_CA_CERT_PEM}`,
|
|
259
|
+
key: TEST_SERVER_KEY_PEM,
|
|
260
|
+
},
|
|
261
|
+
async (req, res) => {
|
|
262
|
+
try {
|
|
263
|
+
const hostHeader = req.headers.host ?? '';
|
|
264
|
+
const pathWithQuery = req.url ?? '/';
|
|
265
|
+
const { internalBase, overridePath } = dispatch(
|
|
266
|
+
hostHeader,
|
|
267
|
+
pathWithQuery,
|
|
268
|
+
opts.shopDomain,
|
|
269
|
+
opts.backends,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const body = await readBody(req);
|
|
273
|
+
const proxied = await forwardOne(
|
|
274
|
+
internalBase,
|
|
275
|
+
overridePath,
|
|
276
|
+
req.method ?? 'GET',
|
|
277
|
+
req.headers,
|
|
278
|
+
body,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Strip headers that don't make sense to forward verbatim
|
|
282
|
+
// (Node decompressed the body for us; content-length will
|
|
283
|
+
// be wrong; transfer-encoding shouldn't survive a hop).
|
|
284
|
+
const outHeaders: Record<string, string | string[]> = {};
|
|
285
|
+
for (const [k, v] of Object.entries(proxied.headers)) {
|
|
286
|
+
if (!v) continue;
|
|
287
|
+
const lower = k.toLowerCase();
|
|
288
|
+
if (lower === 'content-encoding' || lower === 'content-length' || lower === 'transfer-encoding') continue;
|
|
289
|
+
if (typeof v === 'string') outHeaders[k] = v;
|
|
290
|
+
else if (Array.isArray(v)) outHeaders[k] = v;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// CORS — the offline stack lives behind ONE edge proxy serving
|
|
294
|
+
// multiple Shopify origins (admin.shopify.com, cdn.shopify.com,
|
|
295
|
+
// *.myshopify.com). Any cross-origin XHR/fetch issued by the
|
|
296
|
+
// embedded app or by storefront JS would otherwise be blocked
|
|
297
|
+
// by Chrome with "TypeError: Failed to fetch" (the symptom in
|
|
298
|
+
// offline-full-stack tests that try to `fetch('https://cdn.
|
|
299
|
+
// shopify.com/...')` from an admin.shopify.com page). Real
|
|
300
|
+
// Shopify sets these on cdn.shopify.com responses too. Allow
|
|
301
|
+
// everything because this is a test-only proxy bound to
|
|
302
|
+
// 127.0.0.1 — no real traffic ever reaches it.
|
|
303
|
+
if (!outHeaders['Access-Control-Allow-Origin']) {
|
|
304
|
+
outHeaders['Access-Control-Allow-Origin'] = '*';
|
|
305
|
+
}
|
|
306
|
+
if (!outHeaders['Access-Control-Allow-Methods']) {
|
|
307
|
+
outHeaders['Access-Control-Allow-Methods'] =
|
|
308
|
+
'GET, POST, PUT, PATCH, DELETE, OPTIONS';
|
|
309
|
+
}
|
|
310
|
+
if (!outHeaders['Access-Control-Allow-Headers']) {
|
|
311
|
+
outHeaders['Access-Control-Allow-Headers'] = '*';
|
|
312
|
+
}
|
|
313
|
+
if (!outHeaders['Access-Control-Expose-Headers']) {
|
|
314
|
+
outHeaders['Access-Control-Expose-Headers'] = '*';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Short-circuit CORS preflights (the upstream mock might not
|
|
318
|
+
// implement OPTIONS at all; serving them here is cheaper anyway).
|
|
319
|
+
if (req.method === 'OPTIONS') {
|
|
320
|
+
res.writeHead(204, outHeaders);
|
|
321
|
+
res.end();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
res.writeHead(proxied.status, outHeaders);
|
|
326
|
+
res.end(proxied.body);
|
|
327
|
+
|
|
328
|
+
if (process.env['TEST_OFFLINE_EDGE_DEBUG'] === 'true') {
|
|
329
|
+
console.log(
|
|
330
|
+
`[edge] ${req.method} https://${hostHeader}${pathWithQuery} → ${internalBase}${overridePath} → ${proxied.status}`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
} catch (err) {
|
|
334
|
+
console.error('[edge] error:', (err as Error).message);
|
|
335
|
+
res.writeHead(502, { 'content-type': 'text/plain' });
|
|
336
|
+
res.end(`edge proxy error: ${(err as Error).message}`);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Wait for listen() to either succeed OR error. Without an error
|
|
341
|
+
// handler, a failed bind (EADDRINUSE, EACCES on <1024 without
|
|
342
|
+
// privileges, no IPv6 stack when hostname='::', etc.) leaves the
|
|
343
|
+
// promise unresolved and the whole offline test run hangs silently.
|
|
344
|
+
const targetHost = opts.hostname ?? '127.0.0.1';
|
|
345
|
+
const targetPort = opts.port ?? 0;
|
|
346
|
+
// Wait for listen() to either succeed OR fail. Without explicit
|
|
347
|
+
// error/timeout handling, a failed bind (EADDRINUSE; EACCES on
|
|
348
|
+
// <1024 ports without privileges; a kernel that hangs on `::`
|
|
349
|
+
// dual-stack bind — the VM's libkrun does this) leaves the
|
|
350
|
+
// promise unresolved and the whole offline run stalls silently.
|
|
351
|
+
// The 10 s timeout surfaces any "listen never called back" case
|
|
352
|
+
// as a real error instead of a wedge.
|
|
353
|
+
await new Promise<void>((resolveListen, rejectListen) => {
|
|
354
|
+
const timer = setTimeout(() => {
|
|
355
|
+
rejectListen(
|
|
356
|
+
new Error(
|
|
357
|
+
`edge proxy listen() did not call back within 10 s on ${targetHost}:${targetPort} ` +
|
|
358
|
+
`(no 'error' event, no 'listening' callback). Kernel issue? ` +
|
|
359
|
+
`Try '0.0.0.0' instead of '::'.`,
|
|
360
|
+
),
|
|
361
|
+
);
|
|
362
|
+
}, 10_000);
|
|
363
|
+
const onError = (err: NodeJS.ErrnoException): void => {
|
|
364
|
+
clearTimeout(timer);
|
|
365
|
+
server.off('error', onError);
|
|
366
|
+
rejectListen(
|
|
367
|
+
new Error(
|
|
368
|
+
`edge proxy bind failed on ${targetHost}:${targetPort}: ` +
|
|
369
|
+
`${err.code ?? '?'} ${err.message}`,
|
|
370
|
+
),
|
|
371
|
+
);
|
|
372
|
+
};
|
|
373
|
+
server.on('error', onError);
|
|
374
|
+
server.listen(targetPort, targetHost, () => {
|
|
375
|
+
clearTimeout(timer);
|
|
376
|
+
server.off('error', onError);
|
|
377
|
+
resolveListen();
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
const addr = server.address() as AddressInfo;
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
port: addr.port,
|
|
384
|
+
baseUrl: `https://${opts.hostname ?? '127.0.0.1'}:${addr.port}`,
|
|
385
|
+
close: () =>
|
|
386
|
+
new Promise<void>((resolveClose, rejectClose) => {
|
|
387
|
+
server.close((err) => (err ? rejectClose(err) : resolveClose()));
|
|
388
|
+
}),
|
|
389
|
+
};
|
|
390
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[req]
|
|
2
|
+
distinguished_name = req_distinguished_name
|
|
3
|
+
req_extensions = v3_server
|
|
4
|
+
prompt = no
|
|
5
|
+
|
|
6
|
+
[req_distinguished_name]
|
|
7
|
+
C = US
|
|
8
|
+
O = essential-apps shopify-test
|
|
9
|
+
CN = test-shop.myshopify.com
|
|
10
|
+
|
|
11
|
+
[v3_server]
|
|
12
|
+
basicConstraints = critical, CA:FALSE
|
|
13
|
+
keyUsage = critical, digitalSignature, keyEncipherment
|
|
14
|
+
extendedKeyUsage = serverAuth
|
|
15
|
+
subjectAltName = @alt_names
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
[alt_names]
|
|
19
|
+
DNS.1 = localhost
|
|
20
|
+
DNS.2 = *.myshopify.com
|
|
21
|
+
DNS.3 = myshopify.com
|
|
22
|
+
DNS.4 = *.shopify.com
|
|
23
|
+
DNS.5 = shopify.com
|
|
24
|
+
DNS.6 = *.shopifyapps.com
|
|
25
|
+
DNS.7 = *.shopifycloud.com
|
|
26
|
+
DNS.8 = *.shopifycdn.com
|
|
27
|
+
IP.1 = 127.0.0.1
|
|
28
|
+
IP.2 = ::1
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIEYjCCA0qgAwIBAgIURPMxG/s+J/RbB610ES+g6+4uz1YwDQYJKoZIhvcNAQEL
|
|
3
|
+
BQAwYDELMAkGA1UEBhMCVVMxJDAiBgNVBAoMG2Vzc2VudGlhbC1hcHBzIHNob3Bp
|
|
4
|
+
ZnktdGVzdDErMCkGA1UEAwwiZXNzZW50aWFsLWFwcHMgb2ZmbGluZSBFMkUgdGVz
|
|
5
|
+
dCBDQTAeFw0yNjA1MTExODIwNTZaFw0zNjA1MDgxODIwNTZaMFUxCzAJBgNVBAYT
|
|
6
|
+
AlVTMSQwIgYDVQQKDBtlc3NlbnRpYWwtYXBwcyBzaG9waWZ5LXRlc3QxIDAeBgNV
|
|
7
|
+
BAMMF3Rlc3Qtc2hvcC5teXNob3BpZnkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
|
8
|
+
AQ8AMIIBCgKCAQEAqKKCIJ+3Z311vVrAzS9O8BFoDuFcS71UhXFOFnGv+32sWkv5
|
|
9
|
+
15FiqsEUA9Y38TWMmATselKFmseqgZo1EgvNe6rFrnjyyJgEeYrLv40bz3JEp/Hs
|
|
10
|
+
GrgID4XaGJFx/tDsMLBtWLJykbkowRcJnQK0NoeYNC9E1JKfmU9WnT0eqOzc9ryn
|
|
11
|
+
iXj9pptDfd4BndYYSW4lvooshdyrVNcLnm67EXdyNukFk37ru9vSu12Oo+QynnZ7
|
|
12
|
+
6Ijghpxjim5HHG3V5VF9fGSTcEKWqgw1l9FRESs6qFtG3GE5CSbg1J15uuUHBCof
|
|
13
|
+
qIxUJwk6foHqD1V+bMUbxZt1Aj05pZ2NJVAjpQIDAQABo4IBHTCCARkwDAYDVR0T
|
|
14
|
+
AQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwgaMG
|
|
15
|
+
A1UdEQSBmzCBmIIJbG9jYWxob3N0gg8qLm15c2hvcGlmeS5jb22CDW15c2hvcGlm
|
|
16
|
+
eS5jb22CDSouc2hvcGlmeS5jb22CC3Nob3BpZnkuY29tghEqLnNob3BpZnlhcHBz
|
|
17
|
+
LmNvbYISKi5zaG9waWZ5Y2xvdWQuY29tghAqLnNob3BpZnljZG4uY29thwR/AAAB
|
|
18
|
+
hxAAAAAAAAAAAAAAAAAAAAABMB0GA1UdDgQWBBTU9DyGKwRHIMACdy1hFqmWj2CW
|
|
19
|
+
6DAfBgNVHSMEGDAWgBQvul3hwRTsFPS1GZbOxAPssSEDfzANBgkqhkiG9w0BAQsF
|
|
20
|
+
AAOCAQEAUJ5MwZ6iRLuOImmWMiiNTvjxYq7e8+/0zYkqXreQpQEah0muzHnoolhp
|
|
21
|
+
A4aolovZGdAJnAQXG38O8mRwIWxtJqcPPXEITz0O2H2Bdli4r+ikcIkUBNlvsuH2
|
|
22
|
+
1QVwDvyWhgp2gW6wyYzQc5GSbZ9wjca0bUZx+CQsuyRMjkerEVMlnTlxyCVIRIuE
|
|
23
|
+
6WxqZeoHiUskowUc7hIYzUIjadOzddFfdePW1nE69Se4aOM4lbvJ5HNwiNim+3nE
|
|
24
|
+
1RTDsQCMcNcrMdY4g30cv9zm0+rjRB+Rmd3K4EBVaMrnbrMq9guXnZsyE8hkbKn3
|
|
25
|
+
VCXk3ZMrF4yyr/5s7FLeAsE8b9PYtw==
|
|
26
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
|
2
|
+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCoooIgn7dnfXW9
|
|
3
|
+
WsDNL07wEWgO4VxLvVSFcU4Wca/7faxaS/nXkWKqwRQD1jfxNYyYBOx6UoWax6qB
|
|
4
|
+
mjUSC817qsWuePLImAR5isu/jRvPckSn8ewauAgPhdoYkXH+0OwwsG1YsnKRuSjB
|
|
5
|
+
FwmdArQ2h5g0L0TUkp+ZT1adPR6o7Nz2vKeJeP2mm0N93gGd1hhJbiW+iiyF3KtU
|
|
6
|
+
1wuebrsRd3I26QWTfuu729K7XY6j5DKednvoiOCGnGOKbkccbdXlUX18ZJNwQpaq
|
|
7
|
+
DDWX0VERKzqoW0bcYTkJJuDUnXm65QcEKh+ojFQnCTp+geoPVX5sxRvFm3UCPTml
|
|
8
|
+
nY0lUCOlAgMBAAECggEAFzC17W+ZZKl7qg0Ta4QgemIiab1zGFVSjMFOqEZ9GXwo
|
|
9
|
+
WgiNtKfhJjNEIdzxN4ISMgunS5ESn3zqxUTkHHW0DdgntD0cwhopr183csGge/Au
|
|
10
|
+
YdwiiHAbZ6sUGYHS5+RqPq3cc7CikcihQqB86XMoPkF6XF7Nu9/oA8jF0/zGPRuQ
|
|
11
|
+
BiR722XwemloFMfNybnhYzLx5vr0EFs5R8UtDCZcRveWAbP5ggbkEfiz73zGA0gr
|
|
12
|
+
VGq0rQaovLntjc8Gtb93Q57pM7NOahgTQy6DXK42Of1gqkWTvFERXHjS5XH9NdzM
|
|
13
|
+
/ahXz6G/8ms2w6Xaj8uhZHxwEW6hF8S1D8q2Llf/AQKBgQDtXY0wFtI4dWE3MhHO
|
|
14
|
+
pQ6v21gMFP7bmAkN3DTbcAJYsDbZiGlzaH+FF+S1Uz8gbwCKlj9FYR4r0oU0ccfF
|
|
15
|
+
h023BQaxQXNCvfYunApxw1XG2NEkXvGWzD9DX/Xhj4z0WWve9Zh0tRVGMEkETjqT
|
|
16
|
+
xzOhGfxl1g2/CixrzTvKpD8+JQKBgQC136TOwQMs3EyRe1YwSWM0wlN7BcPehOPx
|
|
17
|
+
sTQNkyZsxE3u7NkKa6LXK7gdgC+oelWV6+UXbuEs9ZLWT0yh22T5ViO3sLWiEL7P
|
|
18
|
+
yE6y2j8nTucK3RynN8cEFuolBs3V+/B2K/yy3M9y2oahKmyDYnjy7AxI3UFQm9N7
|
|
19
|
+
hxCX4YKXgQKBgQDr2ulPv11jfD78+WN4UcomM21pk/MpgAh/HS/oW4P5XB8kR8eA
|
|
20
|
+
RXVwai13fyBaufFvw5ta9QVlxelWEzjNrYQrN3NO7hn5V4gnCCXYpJ+21fn6idzE
|
|
21
|
+
Wm8CI3fOiTUmFzR4dtDmJojdFV14ScMq0+UZTxjcl7VQ/mrlMykWUd4FgQKBgBfU
|
|
22
|
+
fNimS482MkYhnfJnuzrvd1a4M6jVSrShXkulCzTXJ8r1d5646bY9wTsET7pIhSxG
|
|
23
|
+
o1bFrXVhm+K+szDF+V3+HmH0Imhgv0+kVEN0+y9gVD+FJzr1wPrVMcq2MIQoJaKm
|
|
24
|
+
Ms8QxZGr9lXppBw269gQe6+UZfl04WnfEZqE7sKBAoGAQrP3o4fqVUb200Bn0dO+
|
|
25
|
+
F8fzgHT1lZAoz5NAyo4VOW4KZ6EgDhuxM2kG3WelGhYb0CwYzWS3WzAw/jxwSHMx
|
|
26
|
+
QhJtvjhtZzYM3P4RDHjSCvQm0X8xv6OMMOF3I0RmdZQFHLo7xIAkehPIpbmBaxSf
|
|
27
|
+
2ojRh6FIbwe+526P8Qcpstk=
|
|
28
|
+
-----END PRIVATE KEY-----
|