@expo/cli 1.0.0-canary-20250404-87e2506 → 1.0.0-canary-20250630-547cd82

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 (182) hide show
  1. package/README.md +33 -24
  2. package/build/bin/cli +1 -1
  3. package/build/metro-require/require.js +6 -4
  4. package/build/src/api/user/actions.js +25 -11
  5. package/build/src/api/user/actions.js.map +1 -1
  6. package/build/src/customize/templates.js +15 -1
  7. package/build/src/customize/templates.js.map +1 -1
  8. package/build/src/export/exportApp.js +26 -14
  9. package/build/src/export/exportApp.js.map +1 -1
  10. package/build/src/export/exportAsync.js +6 -0
  11. package/build/src/export/exportAsync.js.map +1 -1
  12. package/build/src/export/exportHermes.js +2 -2
  13. package/build/src/export/exportHermes.js.map +1 -1
  14. package/build/src/export/persistMetroAssets.js.map +1 -1
  15. package/build/src/install/checkPackages.js +22 -4
  16. package/build/src/install/checkPackages.js.map +1 -1
  17. package/build/src/install/index.js +3 -1
  18. package/build/src/install/index.js.map +1 -1
  19. package/build/src/install/resolveOptions.js +7 -2
  20. package/build/src/install/resolveOptions.js.map +1 -1
  21. package/build/src/install/utils/checkPackagesCompatibility.js +1 -1
  22. package/build/src/install/utils/checkPackagesCompatibility.js.map +1 -1
  23. package/build/src/lint/ESlintPrerequisite.js +32 -48
  24. package/build/src/lint/ESlintPrerequisite.js.map +1 -1
  25. package/build/src/lint/index.js +45 -47
  26. package/build/src/lint/index.js.map +1 -1
  27. package/build/src/lint/lintAsync.js +150 -4
  28. package/build/src/lint/lintAsync.js.map +1 -1
  29. package/build/src/lint/resolveOptions.js +115 -0
  30. package/build/src/lint/resolveOptions.js.map +1 -0
  31. package/build/src/prebuild/renameTemplateAppName.js +4 -1
  32. package/build/src/prebuild/renameTemplateAppName.js.map +1 -1
  33. package/build/src/prebuild/resolveTemplate.js +4 -4
  34. package/build/src/prebuild/resolveTemplate.js.map +1 -1
  35. package/build/src/prebuild/updatePackageJson.js +19 -9
  36. package/build/src/prebuild/updatePackageJson.js.map +1 -1
  37. package/build/src/run/android/resolveOptions.js +13 -1
  38. package/build/src/run/android/resolveOptions.js.map +1 -1
  39. package/build/src/run/android/runAndroidAsync.js +39 -17
  40. package/build/src/run/android/runAndroidAsync.js.map +1 -1
  41. package/build/src/run/ios/XcodeBuild.js +3 -3
  42. package/build/src/run/ios/XcodeBuild.js.map +1 -1
  43. package/build/src/run/ios/options/resolveOptions.js +13 -1
  44. package/build/src/run/ios/options/resolveOptions.js.map +1 -1
  45. package/build/src/run/ios/runIosAsync.js +38 -7
  46. package/build/src/run/ios/runIosAsync.js.map +1 -1
  47. package/build/src/start/doctor/SecurityBinPrerequisite.js +1 -1
  48. package/build/src/start/doctor/SecurityBinPrerequisite.js.map +1 -1
  49. package/build/src/start/doctor/apple/XcodePrerequisite.js +1 -1
  50. package/build/src/start/doctor/apple/XcodePrerequisite.js.map +1 -1
  51. package/build/src/start/doctor/dependencies/ensureDependenciesAsync.js +1 -1
  52. package/build/src/start/doctor/dependencies/ensureDependenciesAsync.js.map +1 -1
  53. package/build/src/start/doctor/dependencies/resolvePackages.js +1 -1
  54. package/build/src/start/doctor/dependencies/resolvePackages.js.map +1 -1
  55. package/build/src/start/doctor/ngrok/ExternalModule.js +1 -1
  56. package/build/src/start/doctor/ngrok/ExternalModule.js.map +1 -1
  57. package/build/src/start/index.js +1 -1
  58. package/build/src/start/index.js.map +1 -1
  59. package/build/src/start/platforms/PlatformManager.js +1 -1
  60. package/build/src/start/platforms/PlatformManager.js.map +1 -1
  61. package/build/src/start/platforms/android/AndroidPlatformManager.js +1 -1
  62. package/build/src/start/platforms/android/AndroidPlatformManager.js.map +1 -1
  63. package/build/src/start/platforms/android/getDevices.js +1 -1
  64. package/build/src/start/platforms/android/getDevices.js.map +1 -1
  65. package/build/src/start/platforms/ios/AppleDeviceManager.js +1 -1
  66. package/build/src/start/platforms/ios/AppleDeviceManager.js.map +1 -1
  67. package/build/src/start/platforms/ios/xcrun.js +1 -1
  68. package/build/src/start/platforms/ios/xcrun.js.map +1 -1
  69. package/build/src/start/project/dotExpo.js +5 -0
  70. package/build/src/start/project/dotExpo.js.map +1 -1
  71. package/build/src/start/resolveOptions.js +3 -0
  72. package/build/src/start/resolveOptions.js.map +1 -1
  73. package/build/src/start/server/metro/MetroBundlerDevServer.js +38 -26
  74. package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
  75. package/build/src/start/server/metro/MetroTerminalReporter.js +29 -2
  76. package/build/src/start/server/metro/MetroTerminalReporter.js.map +1 -1
  77. package/build/src/start/server/metro/createExpoFallbackResolver.js +6 -4
  78. package/build/src/start/server/metro/createExpoFallbackResolver.js.map +1 -1
  79. package/build/src/start/server/metro/createJResolver.js +2 -2
  80. package/build/src/start/server/metro/createJResolver.js.map +1 -1
  81. package/build/src/start/server/metro/createServerComponentsMiddleware.js +16 -5
  82. package/build/src/start/server/metro/createServerComponentsMiddleware.js.map +1 -1
  83. package/build/src/start/server/metro/debugging/createDebugMiddleware.js +19 -19
  84. package/build/src/start/server/metro/debugging/createDebugMiddleware.js.map +1 -1
  85. package/build/src/start/server/metro/externals.js +1 -1
  86. package/build/src/start/server/metro/externals.js.map +1 -1
  87. package/build/src/start/server/metro/instantiateMetro.js +13 -8
  88. package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
  89. package/build/src/start/server/metro/log-box/LogBoxSymbolication.js +21 -2
  90. package/build/src/start/server/metro/log-box/LogBoxSymbolication.js.map +1 -1
  91. package/build/src/start/server/metro/log-box/formatProjectFilePath.js +15 -12
  92. package/build/src/start/server/metro/log-box/formatProjectFilePath.js.map +1 -1
  93. package/build/src/start/server/metro/metroErrorInterface.js +19 -8
  94. package/build/src/start/server/metro/metroErrorInterface.js.map +1 -1
  95. package/build/src/start/server/metro/runServer-fork.js +1 -1
  96. package/build/src/start/server/metro/runServer-fork.js.map +1 -1
  97. package/build/src/start/server/metro/withMetroMultiPlatform.js +8 -2
  98. package/build/src/start/server/metro/withMetroMultiPlatform.js.map +1 -1
  99. package/build/src/start/server/middleware/CorsMiddleware.js +1 -1
  100. package/build/src/start/server/middleware/CorsMiddleware.js.map +1 -1
  101. package/build/src/start/server/middleware/ManifestMiddleware.js +4 -8
  102. package/build/src/start/server/middleware/ManifestMiddleware.js.map +1 -1
  103. package/build/src/start/server/middleware/inspector/JsInspector.js +2 -25
  104. package/build/src/start/server/middleware/inspector/JsInspector.js.map +1 -1
  105. package/build/src/start/server/middleware/metroOptions.js +2 -26
  106. package/build/src/start/server/middleware/metroOptions.js.map +1 -1
  107. package/build/src/utils/build-cache-providers/helpers.js +61 -0
  108. package/build/src/utils/build-cache-providers/helpers.js.map +1 -0
  109. package/build/src/utils/build-cache-providers/index.js +283 -0
  110. package/build/src/utils/build-cache-providers/index.js.map +1 -0
  111. package/build/src/utils/codesigning.js +14 -2
  112. package/build/src/utils/codesigning.js.map +1 -1
  113. package/build/src/utils/exit.js +0 -1
  114. package/build/src/utils/exit.js.map +1 -1
  115. package/build/src/utils/ip.js +7 -104
  116. package/build/src/utils/ip.js.map +1 -1
  117. package/build/src/utils/modifyConfigAsync.js +1 -1
  118. package/build/src/utils/modifyConfigAsync.js.map +1 -1
  119. package/build/src/utils/resolveArgs.js +8 -0
  120. package/build/src/utils/resolveArgs.js.map +1 -1
  121. package/build/src/utils/scheme.js +1 -1
  122. package/build/src/utils/scheme.js.map +1 -1
  123. package/build/src/utils/telemetry/clients/FetchClient.js +2 -2
  124. package/build/src/utils/telemetry/clients/FetchClient.js.map +1 -1
  125. package/build/src/utils/telemetry/utils/context.js +1 -1
  126. package/build/src/utils/tsconfig/evaluateTsConfig.js +7 -2
  127. package/build/src/utils/tsconfig/evaluateTsConfig.js.map +1 -1
  128. package/build/src/utils/variadic.js +63 -6
  129. package/build/src/utils/variadic.js.map +1 -1
  130. package/package.json +20 -20
  131. package/static/canary/react-is/cjs/react-is.development.js +118 -185
  132. package/static/canary/react-is/cjs/react-is.production.js +2 -2
  133. package/static/canary/react-native/Libraries/Renderer/implementations/ReactFabric-dev.js +16582 -26565
  134. package/static/canary/react-native/Libraries/Renderer/implementations/ReactFabric-prod.js +3495 -3357
  135. package/static/canary/react-native/Libraries/Renderer/implementations/ReactFabric-profiling.js +3929 -3801
  136. package/static/canary/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js +16869 -27032
  137. package/static/canary/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js +3535 -3428
  138. package/static/canary/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js +4198 -4095
  139. package/static/canary/scheduler/cjs/scheduler-unstable_mock.development.js +387 -684
  140. package/static/canary/scheduler/cjs/scheduler-unstable_mock.production.js +0 -9
  141. package/static/canary/scheduler/cjs/scheduler-unstable_post_task.development.js +137 -195
  142. package/static/canary/scheduler/cjs/scheduler-unstable_post_task.production.js +0 -5
  143. package/static/canary/scheduler/cjs/scheduler.development.js +339 -600
  144. package/static/canary/scheduler/cjs/scheduler.native.development.js +324 -512
  145. package/static/canary/scheduler/cjs/scheduler.native.production.js +6 -5
  146. package/static/canary/scheduler/cjs/scheduler.production.js +16 -17
  147. package/static/canary-full/react/cjs/react-compiler-runtime.development.js +13 -68
  148. package/static/canary-full/react/cjs/react-jsx-dev-runtime.development.js +317 -1251
  149. package/static/canary-full/react/cjs/react-jsx-dev-runtime.react-server.development.js +353 -1286
  150. package/static/canary-full/react/cjs/react-jsx-runtime.development.js +326 -1279
  151. package/static/canary-full/react/cjs/react-jsx-runtime.react-server.development.js +353 -1286
  152. package/static/canary-full/react/cjs/react.development.js +1204 -2771
  153. package/static/canary-full/react/cjs/react.production.js +25 -20
  154. package/static/canary-full/react/cjs/react.react-server.development.js +783 -2162
  155. package/static/canary-full/react/cjs/react.react-server.production.js +13 -63
  156. package/static/canary-full/react/package.json +1 -1
  157. package/static/canary-full/react-dom/cjs/react-dom-client.development.js +24847 -37099
  158. package/static/canary-full/react-dom/cjs/react-dom-client.production.js +8261 -7475
  159. package/static/canary-full/react-dom/cjs/react-dom-profiling.development.js +25252 -37571
  160. package/static/canary-full/react-dom/cjs/react-dom-profiling.profiling.js +9442 -8662
  161. package/static/canary-full/react-dom/cjs/react-dom-server-legacy.browser.development.js +8944 -11568
  162. package/static/canary-full/react-dom/cjs/react-dom-server-legacy.browser.production.js +1378 -944
  163. package/static/canary-full/react-dom/cjs/react-dom-server-legacy.node.development.js +8944 -11568
  164. package/static/canary-full/react-dom/cjs/react-dom-server-legacy.node.production.js +1386 -954
  165. package/static/canary-full/react-dom/cjs/react-dom-server.browser.development.js +9344 -11600
  166. package/static/canary-full/react-dom/cjs/react-dom-server.browser.production.js +1545 -954
  167. package/static/canary-full/react-dom/cjs/react-dom-server.bun.development.js +8286 -11064
  168. package/static/canary-full/react-dom/cjs/react-dom-server.bun.production.js +1437 -976
  169. package/static/canary-full/react-dom/cjs/react-dom-server.edge.development.js +9356 -11609
  170. package/static/canary-full/react-dom/cjs/react-dom-server.edge.production.js +1542 -970
  171. package/static/canary-full/react-dom/cjs/react-dom-server.node.development.js +9227 -11571
  172. package/static/canary-full/react-dom/cjs/react-dom-server.node.production.js +1787 -1183
  173. package/static/canary-full/react-dom/cjs/react-dom-test-utils.development.js +13 -59
  174. package/static/canary-full/react-dom/cjs/react-dom.development.js +402 -604
  175. package/static/canary-full/react-dom/cjs/react-dom.production.js +4 -3
  176. package/static/canary-full/react-dom/cjs/react-dom.react-server.development.js +322 -382
  177. package/static/canary-full/react-dom/cjs/react-dom.react-server.production.js +6 -7
  178. package/static/canary-full/react-dom/package.json +5 -5
  179. package/static/canary-full/react-dom/static.browser.js +1 -0
  180. package/static/canary-full/react-dom/static.edge.js +1 -0
  181. package/static/canary-full/react-dom/static.node.js +1 -0
  182. package/static/template/eslint.config.js +10 -0
@@ -0,0 +1,283 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ resolveBuildCache: function() {
13
+ return resolveBuildCache;
14
+ },
15
+ resolveBuildCacheProvider: function() {
16
+ return resolveBuildCacheProvider;
17
+ },
18
+ resolvePluginFunction: function() {
19
+ return resolvePluginFunction;
20
+ },
21
+ uploadBuildCache: function() {
22
+ return uploadBuildCache;
23
+ }
24
+ });
25
+ function _fs() {
26
+ const data = /*#__PURE__*/ _interop_require_default(require("fs"));
27
+ _fs = function() {
28
+ return data;
29
+ };
30
+ return data;
31
+ }
32
+ function _path() {
33
+ const data = /*#__PURE__*/ _interop_require_default(require("path"));
34
+ _path = function() {
35
+ return data;
36
+ };
37
+ return data;
38
+ }
39
+ function _resolvefrom() {
40
+ const data = /*#__PURE__*/ _interop_require_default(require("resolve-from"));
41
+ _resolvefrom = function() {
42
+ return data;
43
+ };
44
+ return data;
45
+ }
46
+ const _helpers = require("./helpers");
47
+ const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../log"));
48
+ const _ensureDependenciesAsync = require("../../start/doctor/dependencies/ensureDependenciesAsync");
49
+ const _errors = require("../errors");
50
+ function _interop_require_default(obj) {
51
+ return obj && obj.__esModule ? obj : {
52
+ default: obj
53
+ };
54
+ }
55
+ function _getRequireWildcardCache(nodeInterop) {
56
+ if (typeof WeakMap !== "function") return null;
57
+ var cacheBabelInterop = new WeakMap();
58
+ var cacheNodeInterop = new WeakMap();
59
+ return (_getRequireWildcardCache = function(nodeInterop) {
60
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
61
+ })(nodeInterop);
62
+ }
63
+ function _interop_require_wildcard(obj, nodeInterop) {
64
+ if (!nodeInterop && obj && obj.__esModule) {
65
+ return obj;
66
+ }
67
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
68
+ return {
69
+ default: obj
70
+ };
71
+ }
72
+ var cache = _getRequireWildcardCache(nodeInterop);
73
+ if (cache && cache.has(obj)) {
74
+ return cache.get(obj);
75
+ }
76
+ var newObj = {
77
+ __proto__: null
78
+ };
79
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
80
+ for(var key in obj){
81
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
82
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
83
+ if (desc && (desc.get || desc.set)) {
84
+ Object.defineProperty(newObj, key, desc);
85
+ } else {
86
+ newObj[key] = obj[key];
87
+ }
88
+ }
89
+ }
90
+ newObj.default = obj;
91
+ if (cache) {
92
+ cache.set(obj, newObj);
93
+ }
94
+ return newObj;
95
+ }
96
+ const debug = require('debug')('expo:run:build-cache-provider');
97
+ const resolveBuildCacheProvider = async (provider, projectRoot)=>{
98
+ if (!provider) {
99
+ return;
100
+ }
101
+ if (provider === 'eas') {
102
+ try {
103
+ await (0, _ensureDependenciesAsync.ensureDependenciesAsync)(projectRoot, {
104
+ isProjectMutable: true,
105
+ installMessage: 'eas-build-cache-provider package is required to use the EAS build cache.\n',
106
+ warningMessage: 'Unable to to use the EAS remote build cache.',
107
+ requiredPackages: [
108
+ {
109
+ pkg: 'eas-build-cache-provider',
110
+ file: 'eas-build-cache-provider/package.json',
111
+ dev: true
112
+ }
113
+ ]
114
+ });
115
+ // We need to manually load dependencies installed on the fly
116
+ const plugin = await manuallyLoadDependency(projectRoot, 'eas-build-cache-provider');
117
+ return {
118
+ plugin: plugin.default ?? plugin,
119
+ options: {}
120
+ };
121
+ } catch (error) {
122
+ if (error instanceof _errors.CommandError) {
123
+ _log.warn(error.message);
124
+ } else {
125
+ throw error;
126
+ }
127
+ return undefined;
128
+ }
129
+ }
130
+ if (typeof provider === 'object' && typeof provider.plugin === 'string') {
131
+ const plugin = resolvePluginFunction(projectRoot, provider.plugin);
132
+ return {
133
+ plugin,
134
+ options: provider.options
135
+ };
136
+ }
137
+ throw new Error('Invalid build cache provider');
138
+ };
139
+ async function resolveBuildCache({ projectRoot, platform, provider, runOptions }) {
140
+ const fingerprintHash = await calculateFingerprintHashAsync({
141
+ projectRoot,
142
+ platform,
143
+ provider,
144
+ runOptions
145
+ });
146
+ if (!fingerprintHash) {
147
+ return null;
148
+ }
149
+ if ('resolveRemoteBuildCache' in provider.plugin) {
150
+ _log.warn('The resolveRemoteBuildCache function is deprecated. Use resolveBuildCache instead.');
151
+ return await provider.plugin.resolveRemoteBuildCache({
152
+ fingerprintHash,
153
+ platform,
154
+ runOptions,
155
+ projectRoot
156
+ }, provider.options);
157
+ }
158
+ return await provider.plugin.resolveBuildCache({
159
+ fingerprintHash,
160
+ platform,
161
+ runOptions,
162
+ projectRoot
163
+ }, provider.options);
164
+ }
165
+ async function uploadBuildCache({ projectRoot, platform, provider, buildPath, runOptions }) {
166
+ const fingerprintHash = await calculateFingerprintHashAsync({
167
+ projectRoot,
168
+ platform,
169
+ provider,
170
+ runOptions
171
+ });
172
+ if (!fingerprintHash) {
173
+ debug('No fingerprint hash found, skipping upload');
174
+ return;
175
+ }
176
+ if ('uploadRemoteBuildCache' in provider.plugin) {
177
+ _log.warn('The uploadRemoteBuildCache function is deprecated. Use uploadBuildCache instead.');
178
+ await provider.plugin.uploadRemoteBuildCache({
179
+ projectRoot,
180
+ platform,
181
+ fingerprintHash,
182
+ buildPath,
183
+ runOptions
184
+ }, provider.options);
185
+ } else {
186
+ await provider.plugin.uploadBuildCache({
187
+ projectRoot,
188
+ platform,
189
+ fingerprintHash,
190
+ buildPath,
191
+ runOptions
192
+ }, provider.options);
193
+ }
194
+ }
195
+ async function calculateFingerprintHashAsync({ projectRoot, platform, provider, runOptions }) {
196
+ if (provider.plugin.calculateFingerprintHash) {
197
+ return await provider.plugin.calculateFingerprintHash({
198
+ projectRoot,
199
+ platform,
200
+ runOptions
201
+ }, provider.options);
202
+ }
203
+ const Fingerprint = importFingerprintForDev(projectRoot);
204
+ if (!Fingerprint) {
205
+ debug('@expo/fingerprint is not installed in the project, unable to calculate fingerprint');
206
+ return null;
207
+ }
208
+ const fingerprint = await Fingerprint.createFingerprintAsync(projectRoot);
209
+ return fingerprint.hash;
210
+ }
211
+ function importFingerprintForDev(projectRoot) {
212
+ try {
213
+ return require(require.resolve('@expo/fingerprint', {
214
+ paths: [
215
+ projectRoot
216
+ ]
217
+ }));
218
+ } catch (error) {
219
+ if ('code' in error && error.code === 'MODULE_NOT_FOUND') {
220
+ return null;
221
+ }
222
+ throw error;
223
+ }
224
+ }
225
+ /**
226
+ * Resolve the provider plugin from a node module or package.
227
+ * If the module or package does not include a provider plugin, this function throws.
228
+ * The resolution is done in following order:
229
+ * 1. Is the reference a relative file path or an import specifier with file path? e.g. `./file.js`, `pkg/file.js` or `@org/pkg/file.js`?
230
+ * - Resolve the provider plugin as-is
231
+ * 2. Does the module have a valid provider plugin in the `main` field?
232
+ * - Resolve the `main` entry point as provider plugin
233
+ */ function resolvePluginFilePathForModule(projectRoot, pluginReference) {
234
+ if ((0, _helpers.moduleNameIsDirectFileReference)(pluginReference)) {
235
+ // Only resolve `./file.js`, `package/file.js`, `@org/package/file.js`
236
+ const pluginScriptFile = _resolvefrom().default.silent(projectRoot, pluginReference);
237
+ if (pluginScriptFile) {
238
+ return pluginScriptFile;
239
+ }
240
+ } else if ((0, _helpers.moduleNameIsPackageReference)(pluginReference)) {
241
+ // Try to resole the `main` entry as config plugin
242
+ return (0, _resolvefrom().default)(projectRoot, pluginReference);
243
+ }
244
+ throw new Error(`Failed to resolve provider plugin for module "${pluginReference}" relative to "${projectRoot}". Do you have node modules installed?`);
245
+ }
246
+ function resolvePluginFunction(projectRoot, pluginReference) {
247
+ const pluginFile = resolvePluginFilePathForModule(projectRoot, pluginReference);
248
+ try {
249
+ let plugin = require(pluginFile);
250
+ if ((plugin == null ? void 0 : plugin.default) != null) {
251
+ plugin = plugin.default;
252
+ }
253
+ if (typeof plugin !== 'object' || typeof plugin.resolveRemoteBuildCache !== 'function' && typeof plugin.resolveBuildCache !== 'function' || typeof plugin.uploadRemoteBuildCache !== 'function' && typeof plugin.uploadBuildCache !== 'function') {
254
+ throw new Error(`
255
+ The provider plugin "${pluginReference}" must export an object containing
256
+ the resolveBuildCache and uploadBuildCache functions.
257
+ `);
258
+ }
259
+ return plugin;
260
+ } catch (error) {
261
+ if (error instanceof SyntaxError) {
262
+ // Add error linking to the docs of how create a valid provider plugin
263
+ }
264
+ throw error;
265
+ }
266
+ }
267
+ async function manuallyLoadDependency(projectRoot, packageName) {
268
+ const possiblePaths = [
269
+ _path().default.join(projectRoot, 'node_modules'),
270
+ ...require.resolve.paths(packageName) ?? []
271
+ ];
272
+ const nodeModulesFolder = possiblePaths == null ? void 0 : possiblePaths.find((p)=>{
273
+ const packagePath = _path().default.join(p, packageName);
274
+ return _fs().default.existsSync(packagePath);
275
+ });
276
+ if (!nodeModulesFolder) {
277
+ throw new Error(`Package ${packageName} not found in ${possiblePaths}`);
278
+ }
279
+ const { main } = await Promise.resolve(_path().default.join(nodeModulesFolder, packageName, 'package.json')).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
280
+ return Promise.resolve(_path().default.join(nodeModulesFolder, packageName, main)).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
281
+ }
282
+
283
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/utils/build-cache-providers/index.ts"],"sourcesContent":["import { ExpoConfig, BuildCacheProviderPlugin, BuildCacheProvider, RunOptions } from '@expo/config';\nimport fs from 'fs';\nimport path from 'path';\nimport resolveFrom from 'resolve-from';\n\nimport { moduleNameIsDirectFileReference, moduleNameIsPackageReference } from './helpers';\nimport * as Log from '../../log';\nimport { ensureDependenciesAsync } from '../../start/doctor/dependencies/ensureDependenciesAsync';\nimport { CommandError } from '../errors';\n\nconst debug = require('debug')('expo:run:build-cache-provider') as typeof console.log;\n\nexport const resolveBuildCacheProvider = async (\n provider: Required<Required<ExpoConfig>['experiments']>['buildCacheProvider'] | undefined,\n projectRoot: string\n): Promise<BuildCacheProvider | undefined> => {\n if (!provider) {\n return;\n }\n\n if (provider === 'eas') {\n try {\n await ensureDependenciesAsync(projectRoot, {\n isProjectMutable: true,\n installMessage:\n 'eas-build-cache-provider package is required to use the EAS build cache.\\n',\n warningMessage: 'Unable to to use the EAS remote build cache.',\n requiredPackages: [\n {\n pkg: 'eas-build-cache-provider',\n file: 'eas-build-cache-provider/package.json',\n dev: true,\n },\n ],\n });\n\n // We need to manually load dependencies installed on the fly\n const plugin = await manuallyLoadDependency(projectRoot, 'eas-build-cache-provider');\n\n return {\n plugin: plugin.default ?? plugin,\n options: {},\n };\n } catch (error: any) {\n if (error instanceof CommandError) {\n Log.warn(error.message);\n } else {\n throw error;\n }\n return undefined;\n }\n }\n\n if (typeof provider === 'object' && typeof provider.plugin === 'string') {\n const plugin = resolvePluginFunction(projectRoot, provider.plugin);\n\n return { plugin, options: provider.options };\n }\n\n throw new Error('Invalid build cache provider');\n};\n\nexport async function resolveBuildCache({\n projectRoot,\n platform,\n provider,\n runOptions,\n}: {\n projectRoot: string;\n platform: 'android' | 'ios';\n provider: BuildCacheProvider;\n runOptions: RunOptions;\n}): Promise<string | null> {\n const fingerprintHash = await calculateFingerprintHashAsync({\n projectRoot,\n platform,\n provider,\n runOptions,\n });\n if (!fingerprintHash) {\n return null;\n }\n\n if ('resolveRemoteBuildCache' in provider.plugin) {\n Log.warn('The resolveRemoteBuildCache function is deprecated. Use resolveBuildCache instead.');\n return await provider.plugin.resolveRemoteBuildCache(\n { fingerprintHash, platform, runOptions, projectRoot },\n provider.options\n );\n }\n return await provider.plugin.resolveBuildCache(\n { fingerprintHash, platform, runOptions, projectRoot },\n provider.options\n );\n}\n\nexport async function uploadBuildCache({\n projectRoot,\n platform,\n provider,\n buildPath,\n runOptions,\n}: {\n projectRoot: string;\n platform: 'android' | 'ios';\n provider: BuildCacheProvider;\n buildPath: string;\n runOptions: RunOptions;\n}): Promise<void> {\n const fingerprintHash = await calculateFingerprintHashAsync({\n projectRoot,\n platform,\n provider,\n runOptions,\n });\n if (!fingerprintHash) {\n debug('No fingerprint hash found, skipping upload');\n return;\n }\n\n if ('uploadRemoteBuildCache' in provider.plugin) {\n Log.warn('The uploadRemoteBuildCache function is deprecated. Use uploadBuildCache instead.');\n await provider.plugin.uploadRemoteBuildCache(\n {\n projectRoot,\n platform,\n fingerprintHash,\n buildPath,\n runOptions,\n },\n provider.options\n );\n } else {\n await provider.plugin.uploadBuildCache(\n {\n projectRoot,\n platform,\n fingerprintHash,\n buildPath,\n runOptions,\n },\n provider.options\n );\n }\n}\n\nasync function calculateFingerprintHashAsync({\n projectRoot,\n platform,\n provider,\n runOptions,\n}: {\n projectRoot: string;\n platform: 'android' | 'ios';\n provider: BuildCacheProvider;\n runOptions: RunOptions;\n}): Promise<string | null> {\n if (provider.plugin.calculateFingerprintHash) {\n return await provider.plugin.calculateFingerprintHash(\n { projectRoot, platform, runOptions },\n provider.options\n );\n }\n\n const Fingerprint = importFingerprintForDev(projectRoot);\n if (!Fingerprint) {\n debug('@expo/fingerprint is not installed in the project, unable to calculate fingerprint');\n return null;\n }\n const fingerprint = await Fingerprint.createFingerprintAsync(projectRoot);\n\n return fingerprint.hash;\n}\n\nfunction importFingerprintForDev(projectRoot: string): null | typeof import('@expo/fingerprint') {\n try {\n return require(require.resolve('@expo/fingerprint', { paths: [projectRoot] }));\n } catch (error: any) {\n if ('code' in error && error.code === 'MODULE_NOT_FOUND') {\n return null;\n }\n throw error;\n }\n}\n\n/**\n * Resolve the provider plugin from a node module or package.\n * If the module or package does not include a provider plugin, this function throws.\n * The resolution is done in following order:\n * 1. Is the reference a relative file path or an import specifier with file path? e.g. `./file.js`, `pkg/file.js` or `@org/pkg/file.js`?\n * - Resolve the provider plugin as-is\n * 2. Does the module have a valid provider plugin in the `main` field?\n * - Resolve the `main` entry point as provider plugin\n */\nfunction resolvePluginFilePathForModule(projectRoot: string, pluginReference: string) {\n if (moduleNameIsDirectFileReference(pluginReference)) {\n // Only resolve `./file.js`, `package/file.js`, `@org/package/file.js`\n const pluginScriptFile = resolveFrom.silent(projectRoot, pluginReference);\n if (pluginScriptFile) {\n return pluginScriptFile;\n }\n } else if (moduleNameIsPackageReference(pluginReference)) {\n // Try to resole the `main` entry as config plugin\n return resolveFrom(projectRoot, pluginReference);\n }\n\n throw new Error(\n `Failed to resolve provider plugin for module \"${pluginReference}\" relative to \"${projectRoot}\". Do you have node modules installed?`\n );\n}\n\n// Resolve the module function and assert type\nexport function resolvePluginFunction(\n projectRoot: string,\n pluginReference: string\n): BuildCacheProviderPlugin {\n const pluginFile = resolvePluginFilePathForModule(projectRoot, pluginReference);\n\n try {\n let plugin = require(pluginFile);\n if (plugin?.default != null) {\n plugin = plugin.default;\n }\n\n if (\n typeof plugin !== 'object' ||\n (typeof plugin.resolveRemoteBuildCache !== 'function' &&\n typeof plugin.resolveBuildCache !== 'function') ||\n (typeof plugin.uploadRemoteBuildCache !== 'function' &&\n typeof plugin.uploadBuildCache !== 'function')\n ) {\n throw new Error(`\n The provider plugin \"${pluginReference}\" must export an object containing\n the resolveBuildCache and uploadBuildCache functions.\n `);\n }\n return plugin;\n } catch (error) {\n if (error instanceof SyntaxError) {\n // Add error linking to the docs of how create a valid provider plugin\n }\n throw error;\n }\n}\n\nasync function manuallyLoadDependency(projectRoot: string, packageName: string) {\n const possiblePaths = [\n path.join(projectRoot, 'node_modules'),\n ...(require.resolve.paths(packageName) ?? []),\n ];\n const nodeModulesFolder = possiblePaths?.find((p) => {\n const packagePath = path.join(p, packageName);\n return fs.existsSync(packagePath);\n });\n if (!nodeModulesFolder) {\n throw new Error(`Package ${packageName} not found in ${possiblePaths}`);\n }\n\n const { main } = await import(path.join(nodeModulesFolder, packageName, 'package.json'));\n return import(path.join(nodeModulesFolder, packageName, main));\n}\n"],"names":["resolveBuildCache","resolveBuildCacheProvider","resolvePluginFunction","uploadBuildCache","debug","require","provider","projectRoot","ensureDependenciesAsync","isProjectMutable","installMessage","warningMessage","requiredPackages","pkg","file","dev","plugin","manuallyLoadDependency","default","options","error","CommandError","Log","warn","message","undefined","Error","platform","runOptions","fingerprintHash","calculateFingerprintHashAsync","resolveRemoteBuildCache","buildPath","uploadRemoteBuildCache","calculateFingerprintHash","Fingerprint","importFingerprintForDev","fingerprint","createFingerprintAsync","hash","resolve","paths","code","resolvePluginFilePathForModule","pluginReference","moduleNameIsDirectFileReference","pluginScriptFile","resolveFrom","silent","moduleNameIsPackageReference","pluginFile","SyntaxError","packageName","possiblePaths","path","join","nodeModulesFolder","find","p","packagePath","fs","existsSync","main"],"mappings":"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"}
@@ -231,7 +231,16 @@ async function getCodeSigningInfoAsync(exp, expectSignatureHeader, privateKeyPat
231
231
  // (overwriting existing dev cert in case projectId changed or it has expired)
232
232
  if (!_env.env.EXPO_OFFLINE) {
233
233
  try {
234
- return await fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId);
234
+ const newCodeSigningInfo = await fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId);
235
+ if (newCodeSigningInfo) {
236
+ return newCodeSigningInfo;
237
+ // fall back to cached certificate if we couldn't fetch a new one
238
+ } else if (validatedCodeSigningInfo) {
239
+ _log.warn('Could not fetch new Expo development certificate, falling back to cached certificate');
240
+ return validatedCodeSigningInfo;
241
+ } else {
242
+ return null;
243
+ }
235
244
  } catch (e) {
236
245
  if (validatedCodeSigningInfo) {
237
246
  _log.warn('There was an error fetching the Expo development certificate, falling back to cached certificate');
@@ -347,7 +356,10 @@ function actorCanGetProjectDevelopmentCertificate(actor, app) {
347
356
  return owningAccountIsActorPrimaryAccount || userHasPublishPermissionForOwningAccount;
348
357
  }
349
358
  async function fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId) {
350
- const actor = await (0, _actions.ensureLoggedInAsync)();
359
+ const actor = await (0, _actions.tryGetUserAsync)();
360
+ if (!actor) {
361
+ return null;
362
+ }
351
363
  let app;
352
364
  try {
353
365
  app = await _AppQuery.AppQuery.byIdAsync(easProjectId);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/codesigning.ts"],"sourcesContent":["import { GraphQLError } from '@0no-co/graphql.web';\nimport {\n convertCertificatePEMToCertificate,\n convertKeyPairToPEM,\n convertCSRToCSRPEM,\n generateKeyPair,\n generateCSR,\n convertPrivateKeyPEMToPrivateKey,\n validateSelfSignedCertificate,\n signBufferRSASHA256AndVerify,\n} from '@expo/code-signing-certificates';\nimport { ExpoConfig } from '@expo/config';\nimport JsonFile, { JSONObject } from '@expo/json-file';\nimport { CombinedError } from '@urql/core';\nimport { promises as fs } from 'fs';\nimport { pki as PKI } from 'node-forge';\nimport path from 'path';\nimport { Dictionary, parseDictionary } from 'structured-headers';\n\nimport { env } from './env';\nimport { CommandError } from './errors';\nimport { getExpoGoIntermediateCertificateAsync } from '../api/getExpoGoIntermediateCertificate';\nimport { getProjectDevelopmentCertificateAsync } from '../api/getProjectDevelopmentCertificate';\nimport { AppQuery } from '../api/graphql/queries/AppQuery';\nimport { getExpoHomeDirectory } from '../api/user/UserSettings';\nimport { ensureLoggedInAsync } from '../api/user/actions';\nimport { Actor } from '../api/user/user';\nimport { AppByIdQuery, Permission } from '../graphql/generated';\nimport * as Log from '../log';\nimport { learnMore } from '../utils/link';\n\nconst debug = require('debug')('expo:codesigning') as typeof console.log;\n\nexport type CodeSigningInfo = {\n keyId: string;\n privateKey: string;\n certificateForPrivateKey: string;\n /**\n * Chain of certificates to serve in the manifest multipart body \"certificate_chain\" part.\n * The leaf certificate must be the 0th element of the array, followed by any intermediate certificates\n * necessary to evaluate the chain of trust ending in the implicitly trusted root certificate embedded in\n * the client.\n *\n * An empty array indicates that there is no need to serve the certificate chain in the multipart response.\n */\n certificateChainForResponse: string[];\n /**\n * Scope key cached for the project when certificate is development Expo Go code signing.\n * For project-specific code signing (keyId == the project's generated keyId) this is undefined.\n */\n scopeKey: string | null;\n};\n\ntype StoredDevelopmentExpoRootCodeSigningInfo = {\n easProjectId: string | null;\n scopeKey: string | null;\n privateKey: string | null;\n certificateChain: string[] | null;\n};\nconst DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME = 'development-code-signing-settings-2.json';\n\nexport function getDevelopmentCodeSigningDirectory(): string {\n return path.join(getExpoHomeDirectory(), 'codesigning');\n}\n\nfunction getProjectDevelopmentCodeSigningInfoFile<T extends JSONObject>(defaults: T) {\n function getFile(easProjectId: string): JsonFile<T> {\n const filePath = path.join(\n getDevelopmentCodeSigningDirectory(),\n easProjectId,\n DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME\n );\n return new JsonFile<T>(filePath);\n }\n\n async function readAsync(easProjectId: string): Promise<T> {\n let projectSettings;\n try {\n projectSettings = await getFile(easProjectId).readAsync();\n } catch {\n projectSettings = await getFile(easProjectId).writeAsync(defaults, { ensureDir: true });\n }\n // Set defaults for any missing fields\n return { ...defaults, ...projectSettings };\n }\n\n async function setAsync(easProjectId: string, json: Partial<T>): Promise<T> {\n try {\n return await getFile(easProjectId).mergeAsync(json, {\n cantReadFileDefault: defaults,\n });\n } catch {\n return await getFile(easProjectId).writeAsync(\n {\n ...defaults,\n ...json,\n },\n { ensureDir: true }\n );\n }\n }\n\n return {\n getFile,\n readAsync,\n setAsync,\n };\n}\n\nexport const DevelopmentCodeSigningInfoFile =\n getProjectDevelopmentCodeSigningInfoFile<StoredDevelopmentExpoRootCodeSigningInfo>({\n easProjectId: null,\n scopeKey: null,\n privateKey: null,\n certificateChain: null,\n });\n\n/**\n * Get info necessary to generate a response `expo-signature` header given a project and incoming request `expo-expect-signature` header.\n * This only knows how to serve two code signing keyids:\n * - `expo-root` indicates that it should use a development certificate in the `expo-root` chain. See {@link getExpoRootDevelopmentCodeSigningInfoAsync}\n * - <developer's expo-updates keyid> indicates that it should sign with the configured certificate. See {@link getProjectCodeSigningCertificateAsync}\n */\nexport async function getCodeSigningInfoAsync(\n exp: ExpoConfig,\n expectSignatureHeader: string | null,\n privateKeyPath: string | undefined\n): Promise<CodeSigningInfo | null> {\n if (!expectSignatureHeader) {\n return null;\n }\n\n let parsedExpectSignature: Dictionary;\n try {\n parsedExpectSignature = parseDictionary(expectSignatureHeader);\n } catch {\n throw new CommandError('Invalid value for expo-expect-signature header');\n }\n\n const expectedKeyIdOuter = parsedExpectSignature.get('keyid');\n if (!expectedKeyIdOuter) {\n throw new CommandError('keyid not present in expo-expect-signature header');\n }\n\n const expectedKeyId = expectedKeyIdOuter[0];\n if (typeof expectedKeyId !== 'string') {\n throw new CommandError(\n `Invalid value for keyid in expo-expect-signature header: ${expectedKeyId}`\n );\n }\n\n let expectedAlg: string | null = null;\n const expectedAlgOuter = parsedExpectSignature.get('alg');\n if (expectedAlgOuter) {\n const expectedAlgTemp = expectedAlgOuter[0];\n if (typeof expectedAlgTemp !== 'string') {\n throw new CommandError('Invalid value for alg in expo-expect-signature header');\n }\n expectedAlg = expectedAlgTemp;\n }\n\n if (expectedKeyId === 'expo-root') {\n return await getExpoRootDevelopmentCodeSigningInfoAsync(exp);\n } else if (expectedKeyId === 'expo-go') {\n throw new CommandError(\n 'Invalid certificate requested: cannot sign with embedded keyid=expo-go key'\n );\n } else {\n return await getProjectCodeSigningCertificateAsync(\n exp,\n privateKeyPath,\n expectedKeyId,\n expectedAlg\n );\n }\n}\n\n/**\n * Get a development code signing certificate for the expo-root -> expo-go -> (development certificate) certificate chain.\n * This requires the user be logged in and online, otherwise try to use the cached development certificate.\n */\nasync function getExpoRootDevelopmentCodeSigningInfoAsync(\n exp: ExpoConfig\n): Promise<CodeSigningInfo | null> {\n const easProjectId = exp.extra?.eas?.projectId;\n // can't check for scope key validity since scope key is derived on the server from projectId and we may be offline.\n // we rely upon the client certificate check to validate the scope key\n if (!easProjectId) {\n debug(\n `WARN: Expo Application Services (EAS) is not configured for your project. Configuring EAS enables a more secure development experience amongst many other benefits. ${learnMore(\n 'https://docs.expo.dev/eas/'\n )}`\n );\n return null;\n }\n\n const developmentCodeSigningInfoFromFile =\n await DevelopmentCodeSigningInfoFile.readAsync(easProjectId);\n const validatedCodeSigningInfo = validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n developmentCodeSigningInfoFromFile,\n easProjectId\n );\n\n // 1. If online, ensure logged in, generate key pair and CSR, fetch and cache certificate chain for projectId\n // (overwriting existing dev cert in case projectId changed or it has expired)\n if (!env.EXPO_OFFLINE) {\n try {\n return await fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId);\n } catch (e: any) {\n if (validatedCodeSigningInfo) {\n Log.warn(\n 'There was an error fetching the Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n // need to return null here and say a message\n throw e;\n }\n }\n }\n\n // 2. check for cached cert/private key matching projectId and scopeKey of project, if found and valid return private key and cert chain including expo-go cert\n if (validatedCodeSigningInfo) {\n return validatedCodeSigningInfo;\n }\n\n // 3. if offline, return null\n Log.warn('Offline and no cached development certificate found, unable to sign manifest');\n return null;\n}\n\n/**\n * Get the certificate configured for expo-updates for this project.\n */\nasync function getProjectCodeSigningCertificateAsync(\n exp: ExpoConfig,\n privateKeyPath: string | undefined,\n expectedKeyId: string,\n expectedAlg: string | null\n): Promise<CodeSigningInfo | null> {\n const codeSigningCertificatePath = exp.updates?.codeSigningCertificate;\n if (!codeSigningCertificatePath) {\n return null;\n }\n\n if (!privateKeyPath) {\n throw new CommandError(\n 'Must specify --private-key-path argument to sign development manifest for requested code signing key'\n );\n }\n\n const codeSigningMetadata = exp.updates?.codeSigningMetadata;\n if (!codeSigningMetadata) {\n throw new CommandError(\n 'Must specify \"codeSigningMetadata\" under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n const { alg, keyid } = codeSigningMetadata;\n if (!alg || !keyid) {\n throw new CommandError(\n 'Must specify \"keyid\" and \"alg\" in the \"codeSigningMetadata\" field under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n if (expectedKeyId !== keyid) {\n throw new CommandError(`keyid mismatch: client=${expectedKeyId}, project=${keyid}`);\n }\n\n if (expectedAlg && expectedAlg !== alg) {\n throw new CommandError(`\"alg\" field mismatch (client=${expectedAlg}, project=${alg})`);\n }\n\n const { privateKeyPEM, certificatePEM } =\n await getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n });\n\n return {\n keyId: keyid,\n privateKey: privateKeyPEM,\n certificateForPrivateKey: certificatePEM,\n certificateChainForResponse: [],\n scopeKey: null,\n };\n}\n\nasync function readFileWithErrorAsync(path: string, errorMessage: string): Promise<string> {\n try {\n return await fs.readFile(path, 'utf8');\n } catch {\n throw new CommandError(errorMessage);\n }\n}\n\nasync function getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n}: {\n codeSigningCertificatePath: string;\n privateKeyPath: string;\n}): Promise<{ privateKeyPEM: string; certificatePEM: string }> {\n const [codeSigningCertificatePEM, privateKeyPEM] = await Promise.all([\n readFileWithErrorAsync(\n codeSigningCertificatePath,\n `Code signing certificate cannot be read from path: ${codeSigningCertificatePath}`\n ),\n readFileWithErrorAsync(\n privateKeyPath,\n `Code signing private key cannot be read from path: ${privateKeyPath}`\n ),\n ]);\n\n const privateKey = convertPrivateKeyPEMToPrivateKey(privateKeyPEM);\n const certificate = convertCertificatePEMToCertificate(codeSigningCertificatePEM);\n validateSelfSignedCertificate(certificate, {\n publicKey: certificate.publicKey as PKI.rsa.PublicKey,\n privateKey,\n });\n\n return { privateKeyPEM, certificatePEM: codeSigningCertificatePEM };\n}\n\n/**\n * Validate that the cached code signing info is still valid for the current project and\n * that it hasn't expired. If invalid, return null.\n */\nfunction validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n codeSigningInfo: StoredDevelopmentExpoRootCodeSigningInfo,\n easProjectId: string\n): CodeSigningInfo | null {\n if (codeSigningInfo.easProjectId !== easProjectId) {\n return null;\n }\n\n const {\n privateKey: privateKeyPEM,\n certificateChain: certificatePEMs,\n scopeKey,\n } = codeSigningInfo;\n if (!privateKeyPEM || !certificatePEMs) {\n return null;\n }\n\n const certificateChain = certificatePEMs.map((certificatePEM) =>\n convertCertificatePEMToCertificate(certificatePEM)\n );\n\n // TODO(wschurman): maybe move to @expo/code-signing-certificates\n\n // ensure all intermediate certificates are valid\n for (const certificate of certificateChain) {\n const now = new Date();\n if (certificate.validity.notBefore > now || certificate.validity.notAfter < now) {\n return null;\n }\n }\n\n // TODO(wschurman): maybe do more validation, like validation of projectID and scopeKey within eas certificate extension\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: certificatePEMs,\n certificateForPrivateKey: certificatePEMs[0],\n privateKey: privateKeyPEM,\n scopeKey,\n };\n}\n\nfunction actorCanGetProjectDevelopmentCertificate(actor: Actor, app: AppByIdQuery['app']['byId']) {\n const owningAccountId = app.ownerAccount.id;\n\n const owningAccountIsActorPrimaryAccount =\n actor.__typename === 'User' || actor.__typename === 'SSOUser'\n ? actor.primaryAccount.id === owningAccountId\n : false;\n const userHasPublishPermissionForOwningAccount = !!actor.accounts\n .find((account) => account.id === owningAccountId)\n ?.users?.find((userPermission) => userPermission.actor.id === actor.id)\n ?.permissions?.includes(Permission.Publish);\n return owningAccountIsActorPrimaryAccount || userHasPublishPermissionForOwningAccount;\n}\n\nasync function fetchAndCacheNewDevelopmentCodeSigningInfoAsync(\n easProjectId: string\n): Promise<CodeSigningInfo | null> {\n const actor = await ensureLoggedInAsync();\n let app: AppByIdQuery['app']['byId'];\n try {\n app = await AppQuery.byIdAsync(easProjectId);\n } catch (e) {\n if (e instanceof GraphQLError || e instanceof CombinedError) {\n return null;\n }\n throw e;\n }\n if (!actorCanGetProjectDevelopmentCertificate(actor, app)) {\n return null;\n }\n\n const keyPair = generateKeyPair();\n const keyPairPEM = convertKeyPairToPEM(keyPair);\n const csr = generateCSR(keyPair, `Development Certificate for ${easProjectId}`);\n const csrPEM = convertCSRToCSRPEM(csr);\n const [developmentSigningCertificate, expoGoIntermediateCertificate] = await Promise.all([\n getProjectDevelopmentCertificateAsync(easProjectId, csrPEM),\n getExpoGoIntermediateCertificateAsync(easProjectId),\n ]);\n\n await DevelopmentCodeSigningInfoFile.setAsync(easProjectId, {\n easProjectId,\n scopeKey: app.scopeKey,\n privateKey: keyPairPEM.privateKeyPEM,\n certificateChain: [developmentSigningCertificate, expoGoIntermediateCertificate],\n });\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: [developmentSigningCertificate, expoGoIntermediateCertificate],\n certificateForPrivateKey: developmentSigningCertificate,\n privateKey: keyPairPEM.privateKeyPEM,\n scopeKey: app.scopeKey,\n };\n}\n/**\n * Generate the `expo-signature` header for a manifest and code signing info.\n */\nexport function signManifestString(\n stringifiedManifest: string,\n codeSigningInfo: CodeSigningInfo\n): string {\n const privateKey = convertPrivateKeyPEMToPrivateKey(codeSigningInfo.privateKey);\n const certificate = convertCertificatePEMToCertificate(codeSigningInfo.certificateForPrivateKey);\n return signBufferRSASHA256AndVerify(\n privateKey,\n certificate,\n Buffer.from(stringifiedManifest, 'utf8')\n );\n}\n"],"names":["DevelopmentCodeSigningInfoFile","getCodeSigningInfoAsync","getDevelopmentCodeSigningDirectory","signManifestString","debug","require","DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME","path","join","getExpoHomeDirectory","getProjectDevelopmentCodeSigningInfoFile","defaults","getFile","easProjectId","filePath","JsonFile","readAsync","projectSettings","writeAsync","ensureDir","setAsync","json","mergeAsync","cantReadFileDefault","scopeKey","privateKey","certificateChain","exp","expectSignatureHeader","privateKeyPath","parsedExpectSignature","parseDictionary","CommandError","expectedKeyIdOuter","get","expectedKeyId","expectedAlg","expectedAlgOuter","expectedAlgTemp","getExpoRootDevelopmentCodeSigningInfoAsync","getProjectCodeSigningCertificateAsync","extra","eas","projectId","learnMore","developmentCodeSigningInfoFromFile","validatedCodeSigningInfo","validateStoredDevelopmentExpoRootCertificateCodeSigningInfo","env","EXPO_OFFLINE","fetchAndCacheNewDevelopmentCodeSigningInfoAsync","e","Log","warn","codeSigningCertificatePath","updates","codeSigningCertificate","codeSigningMetadata","alg","keyid","privateKeyPEM","certificatePEM","getProjectPrivateKeyAndCertificateFromFilePathsAsync","keyId","certificateForPrivateKey","certificateChainForResponse","readFileWithErrorAsync","errorMessage","fs","readFile","codeSigningCertificatePEM","Promise","all","convertPrivateKeyPEMToPrivateKey","certificate","convertCertificatePEMToCertificate","validateSelfSignedCertificate","publicKey","codeSigningInfo","certificatePEMs","map","now","Date","validity","notBefore","notAfter","actorCanGetProjectDevelopmentCertificate","actor","app","owningAccountId","ownerAccount","id","owningAccountIsActorPrimaryAccount","__typename","primaryAccount","userHasPublishPermissionForOwningAccount","accounts","find","account","users","userPermission","permissions","includes","Permission","Publish","ensureLoggedInAsync","AppQuery","byIdAsync","GraphQLError","CombinedError","keyPair","generateKeyPair","keyPairPEM","convertKeyPairToPEM","csr","generateCSR","csrPEM","convertCSRToCSRPEM","developmentSigningCertificate","expoGoIntermediateCertificate","getProjectDevelopmentCertificateAsync","getExpoGoIntermediateCertificateAsync","stringifiedManifest","signBufferRSASHA256AndVerify","Buffer","from"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/utils/codesigning.ts"],"sourcesContent":["import { GraphQLError } from '@0no-co/graphql.web';\nimport {\n convertCertificatePEMToCertificate,\n convertKeyPairToPEM,\n convertCSRToCSRPEM,\n generateKeyPair,\n generateCSR,\n convertPrivateKeyPEMToPrivateKey,\n validateSelfSignedCertificate,\n signBufferRSASHA256AndVerify,\n} from '@expo/code-signing-certificates';\nimport { ExpoConfig } from '@expo/config';\nimport JsonFile, { JSONObject } from '@expo/json-file';\nimport { CombinedError } from '@urql/core';\nimport { promises as fs } from 'fs';\nimport { pki as PKI } from 'node-forge';\nimport path from 'path';\nimport { Dictionary, parseDictionary } from 'structured-headers';\n\nimport { env } from './env';\nimport { CommandError } from './errors';\nimport { getExpoGoIntermediateCertificateAsync } from '../api/getExpoGoIntermediateCertificate';\nimport { getProjectDevelopmentCertificateAsync } from '../api/getProjectDevelopmentCertificate';\nimport { AppQuery } from '../api/graphql/queries/AppQuery';\nimport { getExpoHomeDirectory } from '../api/user/UserSettings';\nimport { tryGetUserAsync } from '../api/user/actions';\nimport { Actor } from '../api/user/user';\nimport { AppByIdQuery, Permission } from '../graphql/generated';\nimport * as Log from '../log';\nimport { learnMore } from '../utils/link';\n\nconst debug = require('debug')('expo:codesigning') as typeof console.log;\n\nexport type CodeSigningInfo = {\n keyId: string;\n privateKey: string;\n certificateForPrivateKey: string;\n /**\n * Chain of certificates to serve in the manifest multipart body \"certificate_chain\" part.\n * The leaf certificate must be the 0th element of the array, followed by any intermediate certificates\n * necessary to evaluate the chain of trust ending in the implicitly trusted root certificate embedded in\n * the client.\n *\n * An empty array indicates that there is no need to serve the certificate chain in the multipart response.\n */\n certificateChainForResponse: string[];\n /**\n * Scope key cached for the project when certificate is development Expo Go code signing.\n * For project-specific code signing (keyId == the project's generated keyId) this is undefined.\n */\n scopeKey: string | null;\n};\n\ntype StoredDevelopmentExpoRootCodeSigningInfo = {\n easProjectId: string | null;\n scopeKey: string | null;\n privateKey: string | null;\n certificateChain: string[] | null;\n};\nconst DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME = 'development-code-signing-settings-2.json';\n\nexport function getDevelopmentCodeSigningDirectory(): string {\n return path.join(getExpoHomeDirectory(), 'codesigning');\n}\n\nfunction getProjectDevelopmentCodeSigningInfoFile<T extends JSONObject>(defaults: T) {\n function getFile(easProjectId: string): JsonFile<T> {\n const filePath = path.join(\n getDevelopmentCodeSigningDirectory(),\n easProjectId,\n DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME\n );\n return new JsonFile<T>(filePath);\n }\n\n async function readAsync(easProjectId: string): Promise<T> {\n let projectSettings;\n try {\n projectSettings = await getFile(easProjectId).readAsync();\n } catch {\n projectSettings = await getFile(easProjectId).writeAsync(defaults, { ensureDir: true });\n }\n // Set defaults for any missing fields\n return { ...defaults, ...projectSettings };\n }\n\n async function setAsync(easProjectId: string, json: Partial<T>): Promise<T> {\n try {\n return await getFile(easProjectId).mergeAsync(json, {\n cantReadFileDefault: defaults,\n });\n } catch {\n return await getFile(easProjectId).writeAsync(\n {\n ...defaults,\n ...json,\n },\n { ensureDir: true }\n );\n }\n }\n\n return {\n getFile,\n readAsync,\n setAsync,\n };\n}\n\nexport const DevelopmentCodeSigningInfoFile =\n getProjectDevelopmentCodeSigningInfoFile<StoredDevelopmentExpoRootCodeSigningInfo>({\n easProjectId: null,\n scopeKey: null,\n privateKey: null,\n certificateChain: null,\n });\n\n/**\n * Get info necessary to generate a response `expo-signature` header given a project and incoming request `expo-expect-signature` header.\n * This only knows how to serve two code signing keyids:\n * - `expo-root` indicates that it should use a development certificate in the `expo-root` chain. See {@link getExpoRootDevelopmentCodeSigningInfoAsync}\n * - <developer's expo-updates keyid> indicates that it should sign with the configured certificate. See {@link getProjectCodeSigningCertificateAsync}\n */\nexport async function getCodeSigningInfoAsync(\n exp: ExpoConfig,\n expectSignatureHeader: string | null,\n privateKeyPath: string | undefined\n): Promise<CodeSigningInfo | null> {\n if (!expectSignatureHeader) {\n return null;\n }\n\n let parsedExpectSignature: Dictionary;\n try {\n parsedExpectSignature = parseDictionary(expectSignatureHeader);\n } catch {\n throw new CommandError('Invalid value for expo-expect-signature header');\n }\n\n const expectedKeyIdOuter = parsedExpectSignature.get('keyid');\n if (!expectedKeyIdOuter) {\n throw new CommandError('keyid not present in expo-expect-signature header');\n }\n\n const expectedKeyId = expectedKeyIdOuter[0];\n if (typeof expectedKeyId !== 'string') {\n throw new CommandError(\n `Invalid value for keyid in expo-expect-signature header: ${expectedKeyId}`\n );\n }\n\n let expectedAlg: string | null = null;\n const expectedAlgOuter = parsedExpectSignature.get('alg');\n if (expectedAlgOuter) {\n const expectedAlgTemp = expectedAlgOuter[0];\n if (typeof expectedAlgTemp !== 'string') {\n throw new CommandError('Invalid value for alg in expo-expect-signature header');\n }\n expectedAlg = expectedAlgTemp;\n }\n\n if (expectedKeyId === 'expo-root') {\n return await getExpoRootDevelopmentCodeSigningInfoAsync(exp);\n } else if (expectedKeyId === 'expo-go') {\n throw new CommandError(\n 'Invalid certificate requested: cannot sign with embedded keyid=expo-go key'\n );\n } else {\n return await getProjectCodeSigningCertificateAsync(\n exp,\n privateKeyPath,\n expectedKeyId,\n expectedAlg\n );\n }\n}\n\n/**\n * Get a development code signing certificate for the expo-root -> expo-go -> (development certificate) certificate chain.\n * This requires the user be logged in and online, otherwise try to use the cached development certificate.\n */\nasync function getExpoRootDevelopmentCodeSigningInfoAsync(\n exp: ExpoConfig\n): Promise<CodeSigningInfo | null> {\n const easProjectId = exp.extra?.eas?.projectId;\n // can't check for scope key validity since scope key is derived on the server from projectId and we may be offline.\n // we rely upon the client certificate check to validate the scope key\n if (!easProjectId) {\n debug(\n `WARN: Expo Application Services (EAS) is not configured for your project. Configuring EAS enables a more secure development experience amongst many other benefits. ${learnMore(\n 'https://docs.expo.dev/eas/'\n )}`\n );\n return null;\n }\n\n const developmentCodeSigningInfoFromFile =\n await DevelopmentCodeSigningInfoFile.readAsync(easProjectId);\n const validatedCodeSigningInfo = validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n developmentCodeSigningInfoFromFile,\n easProjectId\n );\n\n // 1. If online, ensure logged in, generate key pair and CSR, fetch and cache certificate chain for projectId\n // (overwriting existing dev cert in case projectId changed or it has expired)\n if (!env.EXPO_OFFLINE) {\n try {\n const newCodeSigningInfo =\n await fetchAndCacheNewDevelopmentCodeSigningInfoAsync(easProjectId);\n\n if (newCodeSigningInfo) {\n return newCodeSigningInfo;\n // fall back to cached certificate if we couldn't fetch a new one\n } else if (validatedCodeSigningInfo) {\n Log.warn(\n 'Could not fetch new Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n return null;\n }\n } catch (e: any) {\n if (validatedCodeSigningInfo) {\n Log.warn(\n 'There was an error fetching the Expo development certificate, falling back to cached certificate'\n );\n return validatedCodeSigningInfo;\n } else {\n // need to return null here and say a message\n throw e;\n }\n }\n }\n\n // 2. check for cached cert/private key matching projectId and scopeKey of project, if found and valid return private key and cert chain including expo-go cert\n if (validatedCodeSigningInfo) {\n return validatedCodeSigningInfo;\n }\n\n // 3. if offline, return null\n Log.warn('Offline and no cached development certificate found, unable to sign manifest');\n return null;\n}\n\n/**\n * Get the certificate configured for expo-updates for this project.\n */\nasync function getProjectCodeSigningCertificateAsync(\n exp: ExpoConfig,\n privateKeyPath: string | undefined,\n expectedKeyId: string,\n expectedAlg: string | null\n): Promise<CodeSigningInfo | null> {\n const codeSigningCertificatePath = exp.updates?.codeSigningCertificate;\n if (!codeSigningCertificatePath) {\n return null;\n }\n\n if (!privateKeyPath) {\n throw new CommandError(\n 'Must specify --private-key-path argument to sign development manifest for requested code signing key'\n );\n }\n\n const codeSigningMetadata = exp.updates?.codeSigningMetadata;\n if (!codeSigningMetadata) {\n throw new CommandError(\n 'Must specify \"codeSigningMetadata\" under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n const { alg, keyid } = codeSigningMetadata;\n if (!alg || !keyid) {\n throw new CommandError(\n 'Must specify \"keyid\" and \"alg\" in the \"codeSigningMetadata\" field under the \"updates\" field of your app config file to use EAS code signing'\n );\n }\n\n if (expectedKeyId !== keyid) {\n throw new CommandError(`keyid mismatch: client=${expectedKeyId}, project=${keyid}`);\n }\n\n if (expectedAlg && expectedAlg !== alg) {\n throw new CommandError(`\"alg\" field mismatch (client=${expectedAlg}, project=${alg})`);\n }\n\n const { privateKeyPEM, certificatePEM } =\n await getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n });\n\n return {\n keyId: keyid,\n privateKey: privateKeyPEM,\n certificateForPrivateKey: certificatePEM,\n certificateChainForResponse: [],\n scopeKey: null,\n };\n}\n\nasync function readFileWithErrorAsync(path: string, errorMessage: string): Promise<string> {\n try {\n return await fs.readFile(path, 'utf8');\n } catch {\n throw new CommandError(errorMessage);\n }\n}\n\nasync function getProjectPrivateKeyAndCertificateFromFilePathsAsync({\n codeSigningCertificatePath,\n privateKeyPath,\n}: {\n codeSigningCertificatePath: string;\n privateKeyPath: string;\n}): Promise<{ privateKeyPEM: string; certificatePEM: string }> {\n const [codeSigningCertificatePEM, privateKeyPEM] = await Promise.all([\n readFileWithErrorAsync(\n codeSigningCertificatePath,\n `Code signing certificate cannot be read from path: ${codeSigningCertificatePath}`\n ),\n readFileWithErrorAsync(\n privateKeyPath,\n `Code signing private key cannot be read from path: ${privateKeyPath}`\n ),\n ]);\n\n const privateKey = convertPrivateKeyPEMToPrivateKey(privateKeyPEM);\n const certificate = convertCertificatePEMToCertificate(codeSigningCertificatePEM);\n validateSelfSignedCertificate(certificate, {\n publicKey: certificate.publicKey as PKI.rsa.PublicKey,\n privateKey,\n });\n\n return { privateKeyPEM, certificatePEM: codeSigningCertificatePEM };\n}\n\n/**\n * Validate that the cached code signing info is still valid for the current project and\n * that it hasn't expired. If invalid, return null.\n */\nfunction validateStoredDevelopmentExpoRootCertificateCodeSigningInfo(\n codeSigningInfo: StoredDevelopmentExpoRootCodeSigningInfo,\n easProjectId: string\n): CodeSigningInfo | null {\n if (codeSigningInfo.easProjectId !== easProjectId) {\n return null;\n }\n\n const {\n privateKey: privateKeyPEM,\n certificateChain: certificatePEMs,\n scopeKey,\n } = codeSigningInfo;\n if (!privateKeyPEM || !certificatePEMs) {\n return null;\n }\n\n const certificateChain = certificatePEMs.map((certificatePEM) =>\n convertCertificatePEMToCertificate(certificatePEM)\n );\n\n // TODO(wschurman): maybe move to @expo/code-signing-certificates\n\n // ensure all intermediate certificates are valid\n for (const certificate of certificateChain) {\n const now = new Date();\n if (certificate.validity.notBefore > now || certificate.validity.notAfter < now) {\n return null;\n }\n }\n\n // TODO(wschurman): maybe do more validation, like validation of projectID and scopeKey within eas certificate extension\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: certificatePEMs,\n certificateForPrivateKey: certificatePEMs[0],\n privateKey: privateKeyPEM,\n scopeKey,\n };\n}\n\nfunction actorCanGetProjectDevelopmentCertificate(actor: Actor, app: AppByIdQuery['app']['byId']) {\n const owningAccountId = app.ownerAccount.id;\n\n const owningAccountIsActorPrimaryAccount =\n actor.__typename === 'User' || actor.__typename === 'SSOUser'\n ? actor.primaryAccount.id === owningAccountId\n : false;\n const userHasPublishPermissionForOwningAccount = !!actor.accounts\n .find((account) => account.id === owningAccountId)\n ?.users?.find((userPermission) => userPermission.actor.id === actor.id)\n ?.permissions?.includes(Permission.Publish);\n return owningAccountIsActorPrimaryAccount || userHasPublishPermissionForOwningAccount;\n}\n\nasync function fetchAndCacheNewDevelopmentCodeSigningInfoAsync(\n easProjectId: string\n): Promise<CodeSigningInfo | null> {\n const actor = await tryGetUserAsync();\n\n if (!actor) {\n return null;\n }\n\n let app: AppByIdQuery['app']['byId'];\n try {\n app = await AppQuery.byIdAsync(easProjectId);\n } catch (e) {\n if (e instanceof GraphQLError || e instanceof CombinedError) {\n return null;\n }\n throw e;\n }\n if (!actorCanGetProjectDevelopmentCertificate(actor, app)) {\n return null;\n }\n\n const keyPair = generateKeyPair();\n const keyPairPEM = convertKeyPairToPEM(keyPair);\n const csr = generateCSR(keyPair, `Development Certificate for ${easProjectId}`);\n const csrPEM = convertCSRToCSRPEM(csr);\n const [developmentSigningCertificate, expoGoIntermediateCertificate] = await Promise.all([\n getProjectDevelopmentCertificateAsync(easProjectId, csrPEM),\n getExpoGoIntermediateCertificateAsync(easProjectId),\n ]);\n\n await DevelopmentCodeSigningInfoFile.setAsync(easProjectId, {\n easProjectId,\n scopeKey: app.scopeKey,\n privateKey: keyPairPEM.privateKeyPEM,\n certificateChain: [developmentSigningCertificate, expoGoIntermediateCertificate],\n });\n\n return {\n keyId: 'expo-go',\n certificateChainForResponse: [developmentSigningCertificate, expoGoIntermediateCertificate],\n certificateForPrivateKey: developmentSigningCertificate,\n privateKey: keyPairPEM.privateKeyPEM,\n scopeKey: app.scopeKey,\n };\n}\n/**\n * Generate the `expo-signature` header for a manifest and code signing info.\n */\nexport function signManifestString(\n stringifiedManifest: string,\n codeSigningInfo: CodeSigningInfo\n): string {\n const privateKey = convertPrivateKeyPEMToPrivateKey(codeSigningInfo.privateKey);\n const certificate = convertCertificatePEMToCertificate(codeSigningInfo.certificateForPrivateKey);\n return signBufferRSASHA256AndVerify(\n privateKey,\n certificate,\n Buffer.from(stringifiedManifest, 'utf8')\n );\n}\n"],"names":["DevelopmentCodeSigningInfoFile","getCodeSigningInfoAsync","getDevelopmentCodeSigningDirectory","signManifestString","debug","require","DEVELOPMENT_CODE_SIGNING_SETTINGS_FILE_NAME","path","join","getExpoHomeDirectory","getProjectDevelopmentCodeSigningInfoFile","defaults","getFile","easProjectId","filePath","JsonFile","readAsync","projectSettings","writeAsync","ensureDir","setAsync","json","mergeAsync","cantReadFileDefault","scopeKey","privateKey","certificateChain","exp","expectSignatureHeader","privateKeyPath","parsedExpectSignature","parseDictionary","CommandError","expectedKeyIdOuter","get","expectedKeyId","expectedAlg","expectedAlgOuter","expectedAlgTemp","getExpoRootDevelopmentCodeSigningInfoAsync","getProjectCodeSigningCertificateAsync","extra","eas","projectId","learnMore","developmentCodeSigningInfoFromFile","validatedCodeSigningInfo","validateStoredDevelopmentExpoRootCertificateCodeSigningInfo","env","EXPO_OFFLINE","newCodeSigningInfo","fetchAndCacheNewDevelopmentCodeSigningInfoAsync","Log","warn","e","codeSigningCertificatePath","updates","codeSigningCertificate","codeSigningMetadata","alg","keyid","privateKeyPEM","certificatePEM","getProjectPrivateKeyAndCertificateFromFilePathsAsync","keyId","certificateForPrivateKey","certificateChainForResponse","readFileWithErrorAsync","errorMessage","fs","readFile","codeSigningCertificatePEM","Promise","all","convertPrivateKeyPEMToPrivateKey","certificate","convertCertificatePEMToCertificate","validateSelfSignedCertificate","publicKey","codeSigningInfo","certificatePEMs","map","now","Date","validity","notBefore","notAfter","actorCanGetProjectDevelopmentCertificate","actor","app","owningAccountId","ownerAccount","id","owningAccountIsActorPrimaryAccount","__typename","primaryAccount","userHasPublishPermissionForOwningAccount","accounts","find","account","users","userPermission","permissions","includes","Permission","Publish","tryGetUserAsync","AppQuery","byIdAsync","GraphQLError","CombinedError","keyPair","generateKeyPair","keyPairPEM","convertKeyPairToPEM","csr","generateCSR","csrPEM","convertCSRToCSRPEM","developmentSigningCertificate","expoGoIntermediateCertificate","getProjectDevelopmentCertificateAsync","getExpoGoIntermediateCertificateAsync","stringifiedManifest","signBufferRSASHA256AndVerify","Buffer","from"],"mappings":"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"}
@@ -106,7 +106,6 @@ function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Dat
106
106
  _nodeprocess().default.stdin.isTTY ? 'TTYWrap' : 'PipeWrap'
107
107
  ];
108
108
  // Check active resources, besides the TTYWrap/PipeWrap (process.stdin, process.stdout, process.stderr)
109
- // @ts-expect-error Added in v17.3.0, v16.14.0 but unavailable in v18 typings
110
109
  const activeResources = _nodeprocess().default.getActiveResourcesInfo();
111
110
  // Filter the active resource list by subtracting the expected resources, in undeterministic order
112
111
  const unexpectedActiveResources = activeResources.filter((activeResource)=>{
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/exit.ts"],"sourcesContent":["import { ChildProcess } from 'node:child_process';\nimport process from 'node:process';\n\nimport { guardAsync } from './fn';\nimport { warn } from '../log';\n\nconst debug = require('debug')('expo:utils:exit') as typeof console.log;\n\ntype AsyncExitHook = (signal: NodeJS.Signals) => void | Promise<void>;\n\nconst PRE_EXIT_SIGNALS: NodeJS.Signals[] = ['SIGHUP', 'SIGINT', 'SIGTERM', 'SIGBREAK'];\n\n// We create a queue since Node.js throws an error if we try to append too many listeners:\n// (node:4405) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGINT listeners added to [process]. Use emitter.setMaxListeners() to increase limit\nconst queue: AsyncExitHook[] = [];\n\nlet unsubscribe: (() => void) | null = null;\n\n/** Add functions that run before the process exits. Returns a function for removing the listeners. */\nexport function installExitHooks(asyncExitHook: AsyncExitHook): () => void {\n // We need to instantiate the master listener the first time the queue is used.\n if (!queue.length) {\n // Track the master listener so we can remove it later.\n unsubscribe = attachMasterListener();\n }\n\n queue.push(asyncExitHook);\n\n return () => {\n const index = queue.indexOf(asyncExitHook);\n if (index >= 0) {\n queue.splice(index, 1);\n }\n // Clean up the master listener if we don't need it anymore.\n if (!queue.length) {\n unsubscribe?.();\n }\n };\n}\n\n// Create a function that runs before the process exits and guards against running multiple times.\nfunction createExitHook(signal: NodeJS.Signals) {\n return guardAsync(async () => {\n debug(`pre-exit (signal: ${signal}, queue length: ${queue.length})`);\n\n for (const [index, hookAsync] of Object.entries(queue)) {\n try {\n await hookAsync(signal);\n } catch (error: any) {\n debug(`Error in exit hook: %O (queue: ${index})`, error);\n }\n }\n\n debug(`post-exit (code: ${process.exitCode ?? 0})`);\n\n process.exit();\n });\n}\n\nfunction attachMasterListener() {\n const hooks: [NodeJS.Signals, () => any][] = [];\n for (const signal of PRE_EXIT_SIGNALS) {\n const hook = createExitHook(signal);\n hooks.push([signal, hook]);\n process.on(signal, hook);\n }\n return () => {\n for (const [signal, hook] of hooks) {\n process.removeListener(signal, hook);\n }\n };\n}\n\n/**\n * Monitor if the current process is exiting before the delay is reached.\n * If there are active resources, the process will be forced to exit after the delay is reached.\n *\n * @see https://nodejs.org/docs/latest-v18.x/api/process.html#processgetactiveresourcesinfo\n */\nexport function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Date.now()) {\n // Create a list of the expected active resources before exiting.\n // Note, the order is undeterministic\n const expectedResources = [\n process.stdout.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stderr.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stdin.isTTY ? 'TTYWrap' : 'PipeWrap',\n ];\n // Check active resources, besides the TTYWrap/PipeWrap (process.stdin, process.stdout, process.stderr)\n // @ts-expect-error Added in v17.3.0, v16.14.0 but unavailable in v18 typings\n const activeResources = process.getActiveResourcesInfo() as string[];\n // Filter the active resource list by subtracting the expected resources, in undeterministic order\n const unexpectedActiveResources = activeResources.filter((activeResource) => {\n const index = expectedResources.indexOf(activeResource);\n if (index >= 0) {\n expectedResources.splice(index, 1);\n return false;\n }\n\n return true;\n });\n\n const canExitProcess = !unexpectedActiveResources.length;\n if (canExitProcess) {\n return debug('no active resources detected, process can safely exit');\n } else {\n debug(\n `process is trying to exit, but is stuck on unexpected active resources:`,\n unexpectedActiveResources\n );\n }\n\n // Check if the process needs to be force-closed\n const elapsedTime = Date.now() - startedAtMs;\n if (elapsedTime > waitUntilExitMs) {\n debug('active handles detected past the exit delay, forcefully exiting:', activeResources);\n tryWarnActiveProcesses();\n return process.exit(0);\n }\n\n const timeoutId = setTimeout(() => {\n // Ensure the timeout is cleared before checking the active resources\n clearTimeout(timeoutId);\n // Check if the process can exit\n ensureProcessExitsAfterDelay(waitUntilExitMs, startedAtMs);\n }, 100);\n}\n\n/**\n * Try to warn the user about unexpected active processes running in the background.\n * This uses the internal `process._getActiveHandles` method, within a try-catch block.\n * If active child processes are detected, the commands of these processes are logged.\n *\n * @example ```bash\n * Done writing bundle output\n * Detected 2 processes preventing Expo from exiting, forcefully exiting now.\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * ```\n */\nfunction tryWarnActiveProcesses() {\n let activeProcesses: string[] = [];\n\n try {\n const children: ChildProcess[] = process\n // @ts-expect-error - This is an internal method, not designed to be exposed. It's also our only way to get this info\n ._getActiveHandles()\n .filter((handle: any) => handle instanceof ChildProcess);\n\n if (children.length) {\n activeProcesses = children.map((child) => child.spawnargs.join(' '));\n }\n } catch (error) {\n debug('failed to get active process information:', error);\n }\n\n if (!activeProcesses.length) {\n warn('Something prevented Expo from exiting, forcefully exiting now.');\n } else {\n const singularOrPlural =\n activeProcesses.length === 1 ? '1 process' : `${activeProcesses.length} processes`;\n\n warn(`Detected ${singularOrPlural} preventing Expo from exiting, forcefully exiting now.`);\n warn(' - ' + activeProcesses.join('\\n - '));\n }\n}\n"],"names":["ensureProcessExitsAfterDelay","installExitHooks","debug","require","PRE_EXIT_SIGNALS","queue","unsubscribe","asyncExitHook","length","attachMasterListener","push","index","indexOf","splice","createExitHook","signal","guardAsync","hookAsync","Object","entries","error","process","exitCode","exit","hooks","hook","on","removeListener","waitUntilExitMs","startedAtMs","Date","now","expectedResources","stdout","isTTY","stderr","stdin","activeResources","getActiveResourcesInfo","unexpectedActiveResources","filter","activeResource","canExitProcess","elapsedTime","tryWarnActiveProcesses","timeoutId","setTimeout","clearTimeout","activeProcesses","children","_getActiveHandles","handle","ChildProcess","map","child","spawnargs","join","warn","singularOrPlural"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/utils/exit.ts"],"sourcesContent":["import { ChildProcess } from 'node:child_process';\nimport process from 'node:process';\n\nimport { guardAsync } from './fn';\nimport { warn } from '../log';\n\nconst debug = require('debug')('expo:utils:exit') as typeof console.log;\n\ntype AsyncExitHook = (signal: NodeJS.Signals) => void | Promise<void>;\n\nconst PRE_EXIT_SIGNALS: NodeJS.Signals[] = ['SIGHUP', 'SIGINT', 'SIGTERM', 'SIGBREAK'];\n\n// We create a queue since Node.js throws an error if we try to append too many listeners:\n// (node:4405) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGINT listeners added to [process]. Use emitter.setMaxListeners() to increase limit\nconst queue: AsyncExitHook[] = [];\n\nlet unsubscribe: (() => void) | null = null;\n\n/** Add functions that run before the process exits. Returns a function for removing the listeners. */\nexport function installExitHooks(asyncExitHook: AsyncExitHook): () => void {\n // We need to instantiate the master listener the first time the queue is used.\n if (!queue.length) {\n // Track the master listener so we can remove it later.\n unsubscribe = attachMasterListener();\n }\n\n queue.push(asyncExitHook);\n\n return () => {\n const index = queue.indexOf(asyncExitHook);\n if (index >= 0) {\n queue.splice(index, 1);\n }\n // Clean up the master listener if we don't need it anymore.\n if (!queue.length) {\n unsubscribe?.();\n }\n };\n}\n\n// Create a function that runs before the process exits and guards against running multiple times.\nfunction createExitHook(signal: NodeJS.Signals) {\n return guardAsync(async () => {\n debug(`pre-exit (signal: ${signal}, queue length: ${queue.length})`);\n\n for (const [index, hookAsync] of Object.entries(queue)) {\n try {\n await hookAsync(signal);\n } catch (error: any) {\n debug(`Error in exit hook: %O (queue: ${index})`, error);\n }\n }\n\n debug(`post-exit (code: ${process.exitCode ?? 0})`);\n\n process.exit();\n });\n}\n\nfunction attachMasterListener() {\n const hooks: [NodeJS.Signals, () => any][] = [];\n for (const signal of PRE_EXIT_SIGNALS) {\n const hook = createExitHook(signal);\n hooks.push([signal, hook]);\n process.on(signal, hook);\n }\n return () => {\n for (const [signal, hook] of hooks) {\n process.removeListener(signal, hook);\n }\n };\n}\n\n/**\n * Monitor if the current process is exiting before the delay is reached.\n * If there are active resources, the process will be forced to exit after the delay is reached.\n *\n * @see https://nodejs.org/docs/latest-v18.x/api/process.html#processgetactiveresourcesinfo\n */\nexport function ensureProcessExitsAfterDelay(waitUntilExitMs = 10000, startedAtMs = Date.now()) {\n // Create a list of the expected active resources before exiting.\n // Note, the order is undeterministic\n const expectedResources = [\n process.stdout.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stderr.isTTY ? 'TTYWrap' : 'PipeWrap',\n process.stdin.isTTY ? 'TTYWrap' : 'PipeWrap',\n ];\n // Check active resources, besides the TTYWrap/PipeWrap (process.stdin, process.stdout, process.stderr)\n const activeResources = process.getActiveResourcesInfo() as string[];\n // Filter the active resource list by subtracting the expected resources, in undeterministic order\n const unexpectedActiveResources = activeResources.filter((activeResource) => {\n const index = expectedResources.indexOf(activeResource);\n if (index >= 0) {\n expectedResources.splice(index, 1);\n return false;\n }\n\n return true;\n });\n\n const canExitProcess = !unexpectedActiveResources.length;\n if (canExitProcess) {\n return debug('no active resources detected, process can safely exit');\n } else {\n debug(\n `process is trying to exit, but is stuck on unexpected active resources:`,\n unexpectedActiveResources\n );\n }\n\n // Check if the process needs to be force-closed\n const elapsedTime = Date.now() - startedAtMs;\n if (elapsedTime > waitUntilExitMs) {\n debug('active handles detected past the exit delay, forcefully exiting:', activeResources);\n tryWarnActiveProcesses();\n return process.exit(0);\n }\n\n const timeoutId = setTimeout(() => {\n // Ensure the timeout is cleared before checking the active resources\n clearTimeout(timeoutId);\n // Check if the process can exit\n ensureProcessExitsAfterDelay(waitUntilExitMs, startedAtMs);\n }, 100);\n}\n\n/**\n * Try to warn the user about unexpected active processes running in the background.\n * This uses the internal `process._getActiveHandles` method, within a try-catch block.\n * If active child processes are detected, the commands of these processes are logged.\n *\n * @example ```bash\n * Done writing bundle output\n * Detected 2 processes preventing Expo from exiting, forcefully exiting now.\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * - node /Users/cedric/../node_modules/nativewind/dist/metro/tailwind/v3/child.js\n * ```\n */\nfunction tryWarnActiveProcesses() {\n let activeProcesses: string[] = [];\n\n try {\n const children: ChildProcess[] = process\n // @ts-expect-error - This is an internal method, not designed to be exposed. It's also our only way to get this info\n ._getActiveHandles()\n .filter((handle: any) => handle instanceof ChildProcess);\n\n if (children.length) {\n activeProcesses = children.map((child) => child.spawnargs.join(' '));\n }\n } catch (error) {\n debug('failed to get active process information:', error);\n }\n\n if (!activeProcesses.length) {\n warn('Something prevented Expo from exiting, forcefully exiting now.');\n } else {\n const singularOrPlural =\n activeProcesses.length === 1 ? '1 process' : `${activeProcesses.length} processes`;\n\n warn(`Detected ${singularOrPlural} preventing Expo from exiting, forcefully exiting now.`);\n warn(' - ' + activeProcesses.join('\\n - '));\n }\n}\n"],"names":["ensureProcessExitsAfterDelay","installExitHooks","debug","require","PRE_EXIT_SIGNALS","queue","unsubscribe","asyncExitHook","length","attachMasterListener","push","index","indexOf","splice","createExitHook","signal","guardAsync","hookAsync","Object","entries","error","process","exitCode","exit","hooks","hook","on","removeListener","waitUntilExitMs","startedAtMs","Date","now","expectedResources","stdout","isTTY","stderr","stdin","activeResources","getActiveResourcesInfo","unexpectedActiveResources","filter","activeResource","canExitProcess","elapsedTime","tryWarnActiveProcesses","timeoutId","setTimeout","clearTimeout","activeProcesses","children","_getActiveHandles","handle","ChildProcess","map","child","spawnargs","join","warn","singularOrPlural"],"mappings":"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"}