@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.
Files changed (265) hide show
  1. package/dist/contracts/normalize.d.ts +61 -0
  2. package/dist/contracts/normalize.d.ts.map +1 -0
  3. package/dist/contracts/normalize.js +99 -0
  4. package/dist/contracts/normalize.js.map +1 -0
  5. package/dist/contracts/normalizeHtml.d.ts +37 -0
  6. package/dist/contracts/normalizeHtml.d.ts.map +1 -0
  7. package/dist/contracts/normalizeHtml.js +89 -0
  8. package/dist/contracts/normalizeHtml.js.map +1 -0
  9. package/dist/edge/cert.d.ts +44 -0
  10. package/dist/edge/cert.d.ts.map +1 -0
  11. package/dist/edge/cert.js +117 -0
  12. package/dist/edge/cert.js.map +1 -0
  13. package/dist/edge/edgeProxy.d.ts +43 -0
  14. package/dist/edge/edgeProxy.d.ts.map +1 -0
  15. package/dist/edge/edgeProxy.js +297 -0
  16. package/dist/edge/edgeProxy.js.map +1 -0
  17. package/dist/edge/nodeShim.d.ts +2 -0
  18. package/dist/edge/nodeShim.d.ts.map +1 -0
  19. package/dist/edge/nodeShim.js +217 -0
  20. package/dist/edge/nodeShim.js.map +1 -0
  21. package/dist/index.d.ts +39 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +36 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/lib/buildSourceBundle.d.ts +56 -0
  26. package/dist/lib/buildSourceBundle.d.ts.map +1 -0
  27. package/dist/lib/buildSourceBundle.js +153 -0
  28. package/dist/lib/buildSourceBundle.js.map +1 -0
  29. package/dist/lib/freePort.d.ts +5 -0
  30. package/dist/lib/freePort.d.ts.map +1 -0
  31. package/dist/lib/freePort.js +33 -0
  32. package/dist/lib/freePort.js.map +1 -0
  33. package/dist/lib/functionBuild.d.ts +99 -0
  34. package/dist/lib/functionBuild.d.ts.map +1 -0
  35. package/dist/lib/functionBuild.js +413 -0
  36. package/dist/lib/functionBuild.js.map +1 -0
  37. package/dist/lib/neonWsProxy.d.ts +41 -0
  38. package/dist/lib/neonWsProxy.d.ts.map +1 -0
  39. package/dist/lib/neonWsProxy.js +101 -0
  40. package/dist/lib/neonWsProxy.js.map +1 -0
  41. package/dist/lib/sourceZipUpload.d.ts +45 -0
  42. package/dist/lib/sourceZipUpload.d.ts.map +1 -0
  43. package/dist/lib/sourceZipUpload.js +129 -0
  44. package/dist/lib/sourceZipUpload.js.map +1 -0
  45. package/dist/lib/stealthLaunch.d.ts +35 -0
  46. package/dist/lib/stealthLaunch.d.ts.map +1 -0
  47. package/dist/lib/stealthLaunch.js +46 -0
  48. package/dist/lib/stealthLaunch.js.map +1 -0
  49. package/dist/lib/storeAutomation.d.ts +22 -0
  50. package/dist/lib/storeAutomation.d.ts.map +1 -0
  51. package/dist/lib/storeAutomation.js +85 -0
  52. package/dist/lib/storeAutomation.js.map +1 -0
  53. package/dist/playwright/baseConfig.d.ts +62 -0
  54. package/dist/playwright/baseConfig.d.ts.map +1 -0
  55. package/dist/playwright/baseConfig.js +68 -0
  56. package/dist/playwright/baseConfig.js.map +1 -0
  57. package/dist/playwright/globalSetup.d.ts +2 -0
  58. package/dist/playwright/globalSetup.d.ts.map +1 -0
  59. package/dist/playwright/globalSetup.js +139 -0
  60. package/dist/playwright/globalSetup.js.map +1 -0
  61. package/dist/playwright/index.d.ts +9 -0
  62. package/dist/playwright/index.d.ts.map +1 -0
  63. package/dist/playwright/index.js +9 -0
  64. package/dist/playwright/index.js.map +1 -0
  65. package/dist/probes/fonts.d.ts +4 -0
  66. package/dist/probes/fonts.d.ts.map +1 -0
  67. package/dist/probes/fonts.js +255 -0
  68. package/dist/probes/fonts.js.map +1 -0
  69. package/dist/probes/mirror.d.ts +4 -0
  70. package/dist/probes/mirror.d.ts.map +1 -0
  71. package/dist/probes/mirror.js +260 -0
  72. package/dist/probes/mirror.js.map +1 -0
  73. package/dist/probes/runProbe.d.ts +3 -0
  74. package/dist/probes/runProbe.d.ts.map +1 -0
  75. package/dist/probes/runProbe.js +219 -0
  76. package/dist/probes/runProbe.js.map +1 -0
  77. package/dist/probes/types.d.ts +72 -0
  78. package/dist/probes/types.d.ts.map +1 -0
  79. package/dist/probes/types.js +2 -0
  80. package/dist/probes/types.js.map +1 -0
  81. package/dist/scripts/_probeSourceUrl.d.ts +3 -0
  82. package/dist/scripts/_probeSourceUrl.d.ts.map +1 -0
  83. package/dist/scripts/_probeSourceUrl.js +119 -0
  84. package/dist/scripts/_probeSourceUrl.js.map +1 -0
  85. package/dist/scripts/addStore.d.ts +3 -0
  86. package/dist/scripts/addStore.d.ts.map +1 -0
  87. package/dist/scripts/addStore.js +46 -0
  88. package/dist/scripts/addStore.js.map +1 -0
  89. package/dist/scripts/buildDockerImage.d.ts +3 -0
  90. package/dist/scripts/buildDockerImage.d.ts.map +1 -0
  91. package/dist/scripts/buildDockerImage.js +60 -0
  92. package/dist/scripts/buildDockerImage.js.map +1 -0
  93. package/dist/scripts/captureAuth.d.ts +3 -0
  94. package/dist/scripts/captureAuth.d.ts.map +1 -0
  95. package/dist/scripts/captureAuth.js +124 -0
  96. package/dist/scripts/captureAuth.js.map +1 -0
  97. package/dist/scripts/captureContracts.d.ts +3 -0
  98. package/dist/scripts/captureContracts.d.ts.map +1 -0
  99. package/dist/scripts/captureContracts.js +517 -0
  100. package/dist/scripts/captureContracts.js.map +1 -0
  101. package/dist/scripts/captureRestContracts.d.ts +3 -0
  102. package/dist/scripts/captureRestContracts.d.ts.map +1 -0
  103. package/dist/scripts/captureRestContracts.js +245 -0
  104. package/dist/scripts/captureRestContracts.js.map +1 -0
  105. package/dist/scripts/checkOperationCoverage.d.ts +3 -0
  106. package/dist/scripts/checkOperationCoverage.d.ts.map +1 -0
  107. package/dist/scripts/checkOperationCoverage.js +302 -0
  108. package/dist/scripts/checkOperationCoverage.js.map +1 -0
  109. package/dist/scripts/cleanupStores.d.ts +3 -0
  110. package/dist/scripts/cleanupStores.d.ts.map +1 -0
  111. package/dist/scripts/cleanupStores.js +77 -0
  112. package/dist/scripts/cleanupStores.js.map +1 -0
  113. package/dist/scripts/createStores.d.ts +3 -0
  114. package/dist/scripts/createStores.d.ts.map +1 -0
  115. package/dist/scripts/createStores.js +66 -0
  116. package/dist/scripts/createStores.js.map +1 -0
  117. package/dist/scripts/deployAppVersion.d.ts +3 -0
  118. package/dist/scripts/deployAppVersion.d.ts.map +1 -0
  119. package/dist/scripts/deployAppVersion.js +591 -0
  120. package/dist/scripts/deployAppVersion.js.map +1 -0
  121. package/dist/scripts/devE2eBackend.d.ts +3 -0
  122. package/dist/scripts/devE2eBackend.d.ts.map +1 -0
  123. package/dist/scripts/devE2eBackend.js +117 -0
  124. package/dist/scripts/devE2eBackend.js.map +1 -0
  125. package/dist/scripts/devOnlineBackend.d.ts +3 -0
  126. package/dist/scripts/devOnlineBackend.d.ts.map +1 -0
  127. package/dist/scripts/devOnlineBackend.js +117 -0
  128. package/dist/scripts/devOnlineBackend.js.map +1 -0
  129. package/dist/scripts/installApp.d.ts +3 -0
  130. package/dist/scripts/installApp.d.ts.map +1 -0
  131. package/dist/scripts/installApp.js +163 -0
  132. package/dist/scripts/installApp.js.map +1 -0
  133. package/dist/scripts/listStores.d.ts +3 -0
  134. package/dist/scripts/listStores.d.ts.map +1 -0
  135. package/dist/scripts/listStores.js +18 -0
  136. package/dist/scripts/listStores.js.map +1 -0
  137. package/dist/scripts/runDocker.d.ts +3 -0
  138. package/dist/scripts/runDocker.d.ts.map +1 -0
  139. package/dist/scripts/runDocker.js +88 -0
  140. package/dist/scripts/runDocker.js.map +1 -0
  141. package/dist/scripts/runDockerAuth.d.ts +3 -0
  142. package/dist/scripts/runDockerAuth.d.ts.map +1 -0
  143. package/dist/scripts/runDockerAuth.js +108 -0
  144. package/dist/scripts/runDockerAuth.js.map +1 -0
  145. package/dist/scripts/runDockerOffline.d.ts +3 -0
  146. package/dist/scripts/runDockerOffline.d.ts.map +1 -0
  147. package/dist/scripts/runDockerOffline.js +129 -0
  148. package/dist/scripts/runDockerOffline.js.map +1 -0
  149. package/dist/scripts/runDockerOfflineExplore.d.ts +3 -0
  150. package/dist/scripts/runDockerOfflineExplore.d.ts.map +1 -0
  151. package/dist/scripts/runDockerOfflineExplore.js +116 -0
  152. package/dist/scripts/runDockerOfflineExplore.js.map +1 -0
  153. package/dist/scripts/runIsolatedDockerOffline.d.ts +3 -0
  154. package/dist/scripts/runIsolatedDockerOffline.d.ts.map +1 -0
  155. package/dist/scripts/runIsolatedDockerOffline.js +351 -0
  156. package/dist/scripts/runIsolatedDockerOffline.js.map +1 -0
  157. package/dist/scripts/runOffline.d.ts +3 -0
  158. package/dist/scripts/runOffline.d.ts.map +1 -0
  159. package/dist/scripts/runOffline.js +521 -0
  160. package/dist/scripts/runOffline.js.map +1 -0
  161. package/dist/scripts/runOfflineE2e.d.ts +3 -0
  162. package/dist/scripts/runOfflineE2e.d.ts.map +1 -0
  163. package/dist/scripts/runOfflineE2e.js +408 -0
  164. package/dist/scripts/runOfflineE2e.js.map +1 -0
  165. package/dist/scripts/runOfflineFullTests.d.ts +3 -0
  166. package/dist/scripts/runOfflineFullTests.d.ts.map +1 -0
  167. package/dist/scripts/runOfflineFullTests.js +1456 -0
  168. package/dist/scripts/runOfflineFullTests.js.map +1 -0
  169. package/dist/scripts/runSupermachine.d.ts +3 -0
  170. package/dist/scripts/runSupermachine.d.ts.map +1 -0
  171. package/dist/scripts/runSupermachine.js +474 -0
  172. package/dist/scripts/runSupermachine.js.map +1 -0
  173. package/dist/scripts/runSupermachineAuth.d.ts +3 -0
  174. package/dist/scripts/runSupermachineAuth.d.ts.map +1 -0
  175. package/dist/scripts/runSupermachineAuth.js +454 -0
  176. package/dist/scripts/runSupermachineAuth.js.map +1 -0
  177. package/dist/scripts/runTests.d.ts +3 -0
  178. package/dist/scripts/runTests.d.ts.map +1 -0
  179. package/dist/scripts/runTests.js +278 -0
  180. package/dist/scripts/runTests.js.map +1 -0
  181. package/dist/scripts/runVm.d.ts +3 -0
  182. package/dist/scripts/runVm.d.ts.map +1 -0
  183. package/dist/scripts/runVm.js +524 -0
  184. package/dist/scripts/runVm.js.map +1 -0
  185. package/dist/scripts/runVmAuth.d.ts +3 -0
  186. package/dist/scripts/runVmAuth.d.ts.map +1 -0
  187. package/dist/scripts/runVmAuth.js +475 -0
  188. package/dist/scripts/runVmAuth.js.map +1 -0
  189. package/dist/scripts/runVmScript.d.ts +3 -0
  190. package/dist/scripts/runVmScript.d.ts.map +1 -0
  191. package/dist/scripts/runVmScript.js +242 -0
  192. package/dist/scripts/runVmScript.js.map +1 -0
  193. package/dist/scripts/setupTestDb.d.ts +3 -0
  194. package/dist/scripts/setupTestDb.d.ts.map +1 -0
  195. package/dist/scripts/setupTestDb.js +61 -0
  196. package/dist/scripts/setupTestDb.js.map +1 -0
  197. package/dist/scripts/verifyContracts.d.ts +3 -0
  198. package/dist/scripts/verifyContracts.d.ts.map +1 -0
  199. package/dist/scripts/verifyContracts.js +258 -0
  200. package/dist/scripts/verifyContracts.js.map +1 -0
  201. package/dist/scripts/verifyRestContracts.d.ts +3 -0
  202. package/dist/scripts/verifyRestContracts.d.ts.map +1 -0
  203. package/dist/scripts/verifyRestContracts.js +237 -0
  204. package/dist/scripts/verifyRestContracts.js.map +1 -0
  205. package/dist/vite/offlineConfig.d.ts +34 -0
  206. package/dist/vite/offlineConfig.d.ts.map +1 -0
  207. package/dist/vite/offlineConfig.js +61 -0
  208. package/dist/vite/offlineConfig.js.map +1 -0
  209. package/dist/vite/onlineConfig.d.ts +42 -0
  210. package/dist/vite/onlineConfig.d.ts.map +1 -0
  211. package/dist/vite/onlineConfig.js +56 -0
  212. package/dist/vite/onlineConfig.js.map +1 -0
  213. package/docker/Dockerfile +67 -0
  214. package/docker/Dockerfile.vm +137 -0
  215. package/docker/README.md +50 -0
  216. package/docker/entrypoint.sh +198 -0
  217. package/package.json +85 -0
  218. package/src/contracts/normalize.ts +96 -0
  219. package/src/contracts/normalizeHtml.ts +98 -0
  220. package/src/edge/ca.cnf +14 -0
  221. package/src/edge/ca.crt +22 -0
  222. package/src/edge/ca.key +28 -0
  223. package/src/edge/cert.ts +117 -0
  224. package/src/edge/edgeProxy.ts +390 -0
  225. package/src/edge/server.cnf +28 -0
  226. package/src/edge/server.crt +26 -0
  227. package/src/edge/server.key +28 -0
  228. package/src/index.ts +67 -0
  229. package/src/lib/buildSourceBundle.ts +197 -0
  230. package/src/lib/freePort.ts +33 -0
  231. package/src/lib/functionBuild.ts +490 -0
  232. package/src/lib/neonWsProxy.ts +124 -0
  233. package/src/lib/sourceZipUpload.ts +168 -0
  234. package/src/lib/stealthLaunch.ts +57 -0
  235. package/src/lib/storeAutomation.ts +110 -0
  236. package/src/playwright/baseConfig.ts +120 -0
  237. package/src/playwright/globalSetup.ts +179 -0
  238. package/src/playwright/index.ts +11 -0
  239. package/src/probes/fonts.ts +279 -0
  240. package/src/probes/mirror.ts +283 -0
  241. package/src/probes/runProbe.ts +257 -0
  242. package/src/probes/types.ts +73 -0
  243. package/src/scripts/addStore.ts +59 -0
  244. package/src/scripts/buildDockerImage.ts +66 -0
  245. package/src/scripts/captureAuth.ts +145 -0
  246. package/src/scripts/captureContracts.ts +675 -0
  247. package/src/scripts/captureRestContracts.ts +319 -0
  248. package/src/scripts/checkOperationCoverage.ts +365 -0
  249. package/src/scripts/cleanupStores.ts +91 -0
  250. package/src/scripts/createStores.ts +77 -0
  251. package/src/scripts/deployAppVersion.ts +692 -0
  252. package/src/scripts/devOnlineBackend.ts +141 -0
  253. package/src/scripts/installApp.ts +188 -0
  254. package/src/scripts/listStores.ts +19 -0
  255. package/src/scripts/runDockerAuth.ts +120 -0
  256. package/src/scripts/runOffline.ts +577 -0
  257. package/src/scripts/runOfflineFullTests.ts +1634 -0
  258. package/src/scripts/runTests.ts +306 -0
  259. package/src/scripts/runVm.ts +562 -0
  260. package/src/scripts/runVmAuth.ts +541 -0
  261. package/src/scripts/runVmScript.ts +282 -0
  262. package/src/scripts/setupTestDb.ts +71 -0
  263. package/src/scripts/verifyContracts.ts +310 -0
  264. package/src/scripts/verifyRestContracts.ts +275 -0
  265. 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"}