@expo/cli 55.0.3 → 55.0.5

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 (81) hide show
  1. package/build/bin/cli +1 -1
  2. package/build/src/api/graphql/client.js +133 -68
  3. package/build/src/api/graphql/client.js.map +1 -1
  4. package/build/src/api/graphql/queries/AppQuery.js +21 -25
  5. package/build/src/api/graphql/queries/AppQuery.js.map +1 -1
  6. package/build/src/api/graphql/queries/UserQuery.js +45 -38
  7. package/build/src/api/graphql/queries/UserQuery.js.map +1 -1
  8. package/build/src/api/rest/cache/FileSystemResponseCache.js.map +1 -1
  9. package/build/src/api/rest/cache/ResponseCache.js.map +1 -1
  10. package/build/src/api/rest/cache/wrapFetchWithCache.js +7 -7
  11. package/build/src/api/rest/cache/wrapFetchWithCache.js.map +1 -1
  12. package/build/src/api/rest/client.js +3 -7
  13. package/build/src/api/rest/client.js.map +1 -1
  14. package/build/src/api/rest/wrapFetchWithProgress.js +1 -8
  15. package/build/src/api/rest/wrapFetchWithProgress.js.map +1 -1
  16. package/build/src/api/user/user.js +6 -36
  17. package/build/src/api/user/user.js.map +1 -1
  18. package/build/src/prebuild/renameTemplateAppName.js +2 -6
  19. package/build/src/prebuild/renameTemplateAppName.js.map +1 -1
  20. package/build/src/prebuild/resolveLocalTemplate.js +2 -4
  21. package/build/src/prebuild/resolveLocalTemplate.js.map +1 -1
  22. package/build/src/prebuild/resolveTemplate.js +13 -17
  23. package/build/src/prebuild/resolveTemplate.js.map +1 -1
  24. package/build/src/prebuild/updateFromTemplate.js +4 -6
  25. package/build/src/prebuild/updateFromTemplate.js.map +1 -1
  26. package/build/src/start/doctor/ngrok/ExternalModule.js +2 -8
  27. package/build/src/start/doctor/ngrok/ExternalModule.js.map +1 -1
  28. package/build/src/start/server/metro/DevToolsPluginWebsocketEndpoint.js +1 -9
  29. package/build/src/start/server/metro/DevToolsPluginWebsocketEndpoint.js.map +1 -1
  30. package/build/src/start/server/metro/MetroBundlerDevServer.js +13 -8
  31. package/build/src/start/server/metro/MetroBundlerDevServer.js.map +1 -1
  32. package/build/src/start/server/metro/createServerComponentsMiddleware.js +4 -14
  33. package/build/src/start/server/metro/createServerComponentsMiddleware.js.map +1 -1
  34. package/build/src/start/server/metro/createServerRouteMiddleware.js +17 -0
  35. package/build/src/start/server/metro/createServerRouteMiddleware.js.map +1 -1
  36. package/build/src/start/server/metro/fetchRouterManifest.js +1 -13
  37. package/build/src/start/server/metro/fetchRouterManifest.js.map +1 -1
  38. package/build/src/start/server/metro/instantiateMetro.js +1 -3
  39. package/build/src/start/server/metro/instantiateMetro.js.map +1 -1
  40. package/build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware.js +47 -34
  41. package/build/src/start/server/middleware/ExpoGoManifestHandlerMiddleware.js.map +1 -1
  42. package/build/src/start/server/middleware/ManifestMiddleware.js +27 -4
  43. package/build/src/start/server/middleware/ManifestMiddleware.js.map +1 -1
  44. package/build/src/start/server/type-generation/routes.js +2 -62
  45. package/build/src/start/server/type-generation/routes.js.map +1 -1
  46. package/build/src/utils/build-cache-providers/index.js +1 -1
  47. package/build/src/utils/build-cache-providers/index.js.map +1 -1
  48. package/build/src/utils/codesigning.js +3 -17
  49. package/build/src/utils/codesigning.js.map +1 -1
  50. package/build/src/utils/createFileTransform.js +3 -38
  51. package/build/src/utils/createFileTransform.js.map +1 -1
  52. package/build/src/utils/downloadAppAsync.js +1 -12
  53. package/build/src/utils/downloadAppAsync.js.map +1 -1
  54. package/build/src/utils/fetch.js +23 -4
  55. package/build/src/utils/fetch.js.map +1 -1
  56. package/build/src/utils/freeport.js +21 -5
  57. package/build/src/utils/freeport.js.map +1 -1
  58. package/build/src/utils/getOrPromptApplicationId.js +2 -15
  59. package/build/src/utils/getOrPromptApplicationId.js.map +1 -1
  60. package/build/src/utils/npm.js +60 -65
  61. package/build/src/utils/npm.js.map +1 -1
  62. package/build/src/utils/port.js +4 -4
  63. package/build/src/utils/port.js.map +1 -1
  64. package/build/src/utils/resolveGlobal.js +195 -0
  65. package/build/src/utils/resolveGlobal.js.map +1 -0
  66. package/build/src/utils/tar.js +138 -69
  67. package/build/src/utils/tar.js.map +1 -1
  68. package/build/src/utils/telemetry/clients/FetchClient.js +12 -24
  69. package/build/src/utils/telemetry/clients/FetchClient.js.map +1 -1
  70. package/build/src/utils/telemetry/utils/context.js +1 -1
  71. package/package.json +18 -28
  72. package/build/src/api/graphql/types/App.js +0 -29
  73. package/build/src/api/graphql/types/App.js.map +0 -1
  74. package/build/src/api/rest/wrapFetchWithProxy.js +0 -31
  75. package/build/src/api/rest/wrapFetchWithProxy.js.map +0 -1
  76. package/build/src/graphql/generated.js +0 -1196
  77. package/build/src/graphql/generated.js.map +0 -1
  78. package/build/src/start/server/middleware/createBuiltinAPIRequestHandler.js +0 -85
  79. package/build/src/start/server/middleware/createBuiltinAPIRequestHandler.js.map +0 -1
  80. package/build/src/utils/multipartMixed.js +0 -56
  81. package/build/src/utils/multipartMixed.js.map +0 -1
package/build/bin/cli CHANGED
@@ -123,7 +123,7 @@ const args = (0, _arg().default)({
123
123
  });
124
124
  if (args['--version']) {
125
125
  // Version is added in the build script.
126
- console.log("55.0.3");
126
+ console.log("55.0.5");
127
127
  process.exit(0);
128
128
  }
129
129
  if (args['--non-interactive']) {
@@ -9,35 +9,24 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
- GraphqlError: function() {
13
- return _core().CombinedError;
12
+ UnexpectedServerData: function() {
13
+ return _client.UnexpectedServerData;
14
14
  },
15
- graphqlClient: function() {
16
- return graphqlClient;
15
+ UnexpectedServerError: function() {
16
+ return _client.UnexpectedServerError;
17
17
  },
18
- withErrorHandlingAsync: function() {
19
- return withErrorHandlingAsync;
18
+ graphql: function() {
19
+ return graphql;
20
+ },
21
+ query: function() {
22
+ return query;
20
23
  }
21
24
  });
22
- function _core() {
23
- const data = require("@urql/core");
24
- _core = function() {
25
- return data;
26
- };
27
- return data;
28
- }
29
- function _exchangeretry() {
30
- const data = require("@urql/exchange-retry");
31
- _exchangeretry = function() {
32
- return data;
33
- };
34
- return data;
35
- }
36
25
  const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../log"));
37
26
  const _fetch = require("../../utils/fetch");
38
27
  const _endpoint = require("../endpoint");
28
+ const _client = require("../rest/client");
39
29
  const _wrapFetchWithOffline = require("../rest/wrapFetchWithOffline");
40
- const _wrapFetchWithProxy = require("../rest/wrapFetchWithProxy");
41
30
  const _wrapFetchWithUserAgent = require("../rest/wrapFetchWithUserAgent");
42
31
  const _UserSettings = require("../user/UserSettings");
43
32
  function _getRequireWildcardCache(nodeInterop) {
@@ -81,59 +70,135 @@ function _interop_require_wildcard(obj, nodeInterop) {
81
70
  }
82
71
  return newObj;
83
72
  }
84
- const graphqlClient = (0, _core().createClient)({
85
- url: (0, _endpoint.getExpoApiBaseUrl)() + '/graphql',
86
- exchanges: [
87
- _core().cacheExchange,
88
- (0, _exchangeretry().retryExchange)({
89
- maxDelayMs: 4000,
90
- retryIf: (err)=>!!(err && (err.networkError || err.graphQLErrors.some((e)=>{
91
- var _e_extensions;
92
- return e == null ? void 0 : (_e_extensions = e.extensions) == null ? void 0 : _e_extensions.isTransient;
93
- })))
94
- }),
95
- _core().fetchExchange
96
- ],
97
- // @ts-ignore Type 'typeof fetch' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.
98
- fetch: (0, _wrapFetchWithOffline.wrapFetchWithOffline)((0, _wrapFetchWithProxy.wrapFetchWithProxy)((0, _wrapFetchWithUserAgent.wrapFetchWithUserAgent)(_fetch.fetch))),
99
- fetchOptions: ()=>{
73
+ function graphql(query) {
74
+ return query.trim();
75
+ }
76
+ const query = (()=>{
77
+ const url = (0, _endpoint.getExpoApiBaseUrl)() + '/graphql';
78
+ let _fetch1;
79
+ const wrappedFetch = (...args)=>{
80
+ if (!_fetch1) {
81
+ _fetch1 = (0, _wrapFetchWithOffline.wrapFetchWithOffline)((0, _wrapFetchWithUserAgent.wrapFetchWithUserAgent)(_fetch.fetch));
82
+ }
83
+ return _fetch1(...args);
84
+ };
85
+ const randomDelay = (attemptCount)=>new Promise((resolve)=>{
86
+ setTimeout(resolve, Math.min(500 + Math.random() * 1000 * attemptCount, 4000));
87
+ });
88
+ const getFetchHeaders = ()=>{
100
89
  var _getSession;
101
90
  const token = (0, _UserSettings.getAccessToken)();
91
+ const headers = {
92
+ 'content-type': 'application/json',
93
+ accept: 'application/graphql-response+json, application/graphql+json, application/json'
94
+ };
95
+ let sessionSecret;
102
96
  if (token) {
103
- return {
104
- headers: {
105
- authorization: `Bearer ${token}`
106
- }
107
- };
97
+ headers.authorization = `Bearer ${token}`;
98
+ } else if (sessionSecret = (_getSession = (0, _UserSettings.getSession)()) == null ? void 0 : _getSession.sessionSecret) {
99
+ headers['expo-session'] = sessionSecret;
100
+ }
101
+ return headers;
102
+ };
103
+ // NOTE(@kitten): This only sorted keys one level deep since this is sufficient for most cases
104
+ const stringifySorted = (variables)=>JSON.stringify(Object.keys(variables).sort().reduce((acc, key)=>{
105
+ acc[key] = variables[key];
106
+ return acc;
107
+ }, {}));
108
+ let cache = {};
109
+ let cacheKey;
110
+ function resetCache() {
111
+ cache = {};
112
+ }
113
+ return async function query(query, variables, options) {
114
+ let isTransient = false;
115
+ let response;
116
+ let data;
117
+ let error;
118
+ // Pre-instantiate headers and reset the cache if they've changed
119
+ const headers = {
120
+ ...getFetchHeaders(),
121
+ ...options == null ? void 0 : options.headers
122
+ };
123
+ const headersKey = stringifySorted(headers);
124
+ if (!cacheKey || cacheKey !== headersKey) {
125
+ resetCache();
108
126
  }
109
- const sessionSecret = (_getSession = (0, _UserSettings.getSession)()) == null ? void 0 : _getSession.sessionSecret;
110
- if (sessionSecret) {
111
- return {
112
- headers: {
113
- 'expo-session': sessionSecret
127
+ // Retrieve a cached result, if we have any via a `query => variables => Result` cache key
128
+ const variablesKey = stringifySorted(variables);
129
+ const queryCache = cache[query] || (cache[query] = new Map());
130
+ if (queryCache.has(variablesKey)) {
131
+ data = queryCache.get(variablesKey);
132
+ }
133
+ // Retry the query if it fails due to an unknown or transient error
134
+ for(let attemptCount = 0; attemptCount < 3 && !data; attemptCount++){
135
+ // Add a random delay on each subsequent attempt
136
+ if (attemptCount > 0) {
137
+ await randomDelay(attemptCount);
138
+ }
139
+ try {
140
+ response = await wrappedFetch(url, {
141
+ ...options,
142
+ method: 'POST',
143
+ body: JSON.stringify({
144
+ query,
145
+ variables
146
+ }),
147
+ headers
148
+ });
149
+ } catch (networkError) {
150
+ error = networkError || error;
151
+ continue;
152
+ }
153
+ const json = await response.json();
154
+ if (typeof json === 'object' && json) {
155
+ // If we have a transient error, we retry immediately and discard the data
156
+ // Otherwise, we store the first available error and get the data
157
+ if ('errors' in json && Array.isArray(json.errors)) {
158
+ isTransient = json.errors.some((e)=>{
159
+ var _e_extensions;
160
+ return e == null ? void 0 : (_e_extensions = e.extensions) == null ? void 0 : _e_extensions.isTransient;
161
+ });
162
+ if (isTransient) {
163
+ data = undefined;
164
+ continue;
165
+ } else {
166
+ error = json.errors[0] || error;
167
+ }
168
+ }
169
+ try {
170
+ data = (0, _client.getResponseDataOrThrow)(json);
171
+ } catch (dataError) {
172
+ // We only use the data error, if we don't have an error already
173
+ if (!error) {
174
+ error = dataError || error;
175
+ }
176
+ continue;
114
177
  }
115
- };
178
+ }
116
179
  }
117
- return {};
118
- }
119
- });
120
- async function withErrorHandlingAsync(promise) {
121
- const { data, error } = await promise;
122
- if (error) {
123
- if (error.graphQLErrors.some((e)=>{
124
- var _e_extensions;
125
- return e == null ? void 0 : (_e_extensions = e.extensions) == null ? void 0 : _e_extensions.isTransient;
126
- })) {
127
- _log.error(`We've encountered a transient error, please try again shortly.`);
180
+ // Store the data in the cache, and only return a result if we have any values
181
+ if (data) {
182
+ queryCache.set(variablesKey, data);
183
+ const keys = Object.keys(data);
184
+ if (keys.length > 0 && keys.some((key)=>data[key] != null)) {
185
+ return data;
186
+ }
128
187
  }
129
- throw error;
130
- }
131
- // Check for a malformed response. This only checks the root query's existence. It doesn't affect
132
- // returning responses with an empty result set.
133
- if (!data) {
134
- throw new Error('Returned query result data is null!');
135
- }
136
- return data;
137
- }
188
+ // If we have an error, rethrow it wrapped in our custom errors
189
+ if (error) {
190
+ if (isTransient) {
191
+ _log.error(`We've encountered a transient error, please try again shortly.`);
192
+ }
193
+ const wrappedError = new _client.UnexpectedServerError('' + error.message);
194
+ wrappedError.cause = error;
195
+ throw wrappedError;
196
+ } else if (response && !response.ok) {
197
+ throw new _client.UnexpectedServerError(`Unexpected server error: ${response.statusText}`);
198
+ } else {
199
+ throw new _client.UnexpectedServerData('Unexpected server error: No returned query result');
200
+ }
201
+ };
202
+ })();
138
203
 
139
204
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/api/graphql/client.ts"],"sourcesContent":["import {\n cacheExchange,\n Client,\n CombinedError as GraphqlError,\n AnyVariables,\n DocumentInput,\n createClient as createUrqlClient,\n fetchExchange,\n OperationContext,\n OperationResult,\n OperationResultSource,\n} from '@urql/core';\nimport { retryExchange } from '@urql/exchange-retry';\n\nimport * as Log from '../../log';\nimport { fetch } from '../../utils/fetch';\nimport { getExpoApiBaseUrl } from '../endpoint';\nimport { wrapFetchWithOffline } from '../rest/wrapFetchWithOffline';\nimport { wrapFetchWithProxy } from '../rest/wrapFetchWithProxy';\nimport { wrapFetchWithUserAgent } from '../rest/wrapFetchWithUserAgent';\nimport { getAccessToken, getSession } from '../user/UserSettings';\n\ntype AccessTokenHeaders = {\n authorization: string;\n};\n\ntype SessionHeaders = {\n 'expo-session': string;\n};\n\nexport const graphqlClient = createUrqlClient({\n url: getExpoApiBaseUrl() + '/graphql',\n exchanges: [\n cacheExchange,\n retryExchange({\n maxDelayMs: 4000,\n retryIf: (err) =>\n !!(err && (err.networkError || err.graphQLErrors.some((e) => e?.extensions?.isTransient))),\n }),\n fetchExchange,\n ],\n // @ts-ignore Type 'typeof fetch' is not assignable to type '(input: RequestInfo, init?: RequestInit | undefined) => Promise<Response>'.\n fetch: wrapFetchWithOffline(wrapFetchWithProxy(wrapFetchWithUserAgent(fetch))),\n fetchOptions: (): { headers?: AccessTokenHeaders | SessionHeaders } => {\n const token = getAccessToken();\n if (token) {\n return {\n headers: {\n authorization: `Bearer ${token}`,\n },\n };\n }\n const sessionSecret = getSession()?.sessionSecret;\n if (sessionSecret) {\n return {\n headers: {\n 'expo-session': sessionSecret,\n },\n };\n }\n return {};\n },\n}) as StricterClient;\n\n/* Please specify additionalTypenames in your Graphql queries */\nexport interface StricterClient extends Client {\n query<Data = any, Variables extends AnyVariables = AnyVariables>(\n query: DocumentInput<Data, Variables>,\n variables: Variables,\n context: Partial<OperationContext> & { additionalTypenames: string[] }\n ): OperationResultSource<OperationResult<Data, Variables>>;\n}\n\nexport async function withErrorHandlingAsync<T>(promise: Promise<OperationResult<T>>): Promise<T> {\n const { data, error } = await promise;\n\n if (error) {\n if (error.graphQLErrors.some((e) => e?.extensions?.isTransient)) {\n Log.error(`We've encountered a transient error, please try again shortly.`);\n }\n throw error;\n }\n\n // Check for a malformed response. This only checks the root query's existence. It doesn't affect\n // returning responses with an empty result set.\n if (!data) {\n throw new Error('Returned query result data is null!');\n }\n\n return data;\n}\n\nexport { GraphqlError };\n"],"names":["GraphqlError","graphqlClient","withErrorHandlingAsync","createUrqlClient","url","getExpoApiBaseUrl","exchanges","cacheExchange","retryExchange","maxDelayMs","retryIf","err","networkError","graphQLErrors","some","e","extensions","isTransient","fetchExchange","fetch","wrapFetchWithOffline","wrapFetchWithProxy","wrapFetchWithUserAgent","fetchOptions","getSession","token","getAccessToken","headers","authorization","sessionSecret","promise","data","error","Log","Error"],"mappings":";;;;;;;;;;;IA4FSA,YAAY;eAAZA,qBAAY;;IA9DRC,aAAa;eAAbA;;IA2CSC,sBAAsB;eAAtBA;;;;yBA9Df;;;;;;;yBACuB;;;;;;6DAET;uBACC;0BACY;sCACG;oCACF;wCACI;8BACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUpC,MAAMD,gBAAgBE,IAAAA,oBAAgB,EAAC;IAC5CC,KAAKC,IAAAA,2BAAiB,MAAK;IAC3BC,WAAW;QACTC,qBAAa;QACbC,IAAAA,8BAAa,EAAC;YACZC,YAAY;YACZC,SAAS,CAACC,MACR,CAAC,CAAEA,CAAAA,OAAQA,CAAAA,IAAIC,YAAY,IAAID,IAAIE,aAAa,CAACC,IAAI,CAAC,CAACC;wBAAMA;2BAAAA,sBAAAA,gBAAAA,EAAGC,UAAU,qBAAbD,cAAeE,WAAW;kBAAA,CAAC;QAC5F;QACAC,qBAAa;KACd;IACD,wIAAwI;IACxIC,OAAOC,IAAAA,0CAAoB,EAACC,IAAAA,sCAAkB,EAACC,IAAAA,8CAAsB,EAACH,YAAK;IAC3EI,cAAc;YASUC;QARtB,MAAMC,QAAQC,IAAAA,4BAAc;QAC5B,IAAID,OAAO;YACT,OAAO;gBACLE,SAAS;oBACPC,eAAe,CAAC,OAAO,EAAEH,OAAO;gBAClC;YACF;QACF;QACA,MAAMI,iBAAgBL,cAAAA,IAAAA,wBAAU,wBAAVA,YAAcK,aAAa;QACjD,IAAIA,eAAe;YACjB,OAAO;gBACLF,SAAS;oBACP,gBAAgBE;gBAClB;YACF;QACF;QACA,OAAO,CAAC;IACV;AACF;AAWO,eAAe3B,uBAA0B4B,OAAoC;IAClF,MAAM,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAG,MAAMF;IAE9B,IAAIE,OAAO;QACT,IAAIA,MAAMnB,aAAa,CAACC,IAAI,CAAC,CAACC;gBAAMA;mBAAAA,sBAAAA,gBAAAA,EAAGC,UAAU,qBAAbD,cAAeE,WAAW;YAAG;YAC/DgB,KAAID,KAAK,CAAC,CAAC,8DAA8D,CAAC;QAC5E;QACA,MAAMA;IACR;IAEA,iGAAiG;IACjG,gDAAgD;IAChD,IAAI,CAACD,MAAM;QACT,MAAM,IAAIG,MAAM;IAClB;IAEA,OAAOH;AACT"}
1
+ {"version":3,"sources":["../../../../src/api/graphql/client.ts"],"sourcesContent":["import * as Log from '../../log';\nimport { fetch, type Response } from '../../utils/fetch';\nimport { getExpoApiBaseUrl } from '../endpoint';\nimport {\n getResponseDataOrThrow,\n UnexpectedServerData,\n UnexpectedServerError,\n} from '../rest/client';\nimport { FetchLike } from '../rest/client.types';\nimport { wrapFetchWithOffline } from '../rest/wrapFetchWithOffline';\nimport { wrapFetchWithUserAgent } from '../rest/wrapFetchWithUserAgent';\nimport { getAccessToken, getSession } from '../user/UserSettings';\n\ntype JSONObject = Record<string, unknown>;\ntype EmptyVariables = Record<string, never>;\n\nexport type StaticDocumentNode<Result extends JSONObject, Variables extends JSONObject> = string & {\n readonly __graphql: (vars: Variables) => Result;\n};\n\nexport function graphql<Result extends JSONObject, Variables extends JSONObject = EmptyVariables>(\n query: string\n): StaticDocumentNode<Result, Variables> {\n return query.trim() as StaticDocumentNode<Result, Variables>;\n}\n\nexport { UnexpectedServerError, UnexpectedServerData };\n\nexport interface QueryOptions {\n headers?: Record<string, string>;\n}\n\nexport const query = (() => {\n const url = getExpoApiBaseUrl() + '/graphql';\n\n let _fetch: FetchLike | undefined;\n const wrappedFetch: FetchLike = (...args) => {\n if (!_fetch) {\n _fetch = wrapFetchWithOffline(wrapFetchWithUserAgent(fetch));\n }\n return _fetch(...args);\n };\n\n const randomDelay = (attemptCount: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, Math.min(500 + Math.random() * 1000 * attemptCount, 4_000));\n });\n\n const getFetchHeaders = (): Record<string, string> => {\n const token = getAccessToken();\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/graphql-response+json, application/graphql+json, application/json',\n };\n let sessionSecret: string | undefined;\n if (token) {\n headers.authorization = `Bearer ${token}`;\n } else if ((sessionSecret = getSession()?.sessionSecret)) {\n headers['expo-session'] = sessionSecret;\n }\n return headers;\n };\n\n // NOTE(@kitten): This only sorted keys one level deep since this is sufficient for most cases\n const stringifySorted = (variables: JSONObject): string =>\n JSON.stringify(\n Object.keys(variables)\n .sort()\n .reduce((acc, key) => {\n acc[key] = variables[key];\n return acc;\n }, {} as JSONObject)\n );\n\n let cache: Record<string, Map<string, unknown>> = {};\n let cacheKey: string | undefined;\n\n function resetCache() {\n cache = {};\n }\n\n return async function query<Result extends JSONObject, Variables extends JSONObject>(\n query: StaticDocumentNode<Result, Variables>,\n variables: Variables,\n options?: QueryOptions\n ): Promise<Result> {\n let isTransient = false;\n let response: Response | undefined;\n let data: Result | null | undefined;\n let error: unknown;\n\n // Pre-instantiate headers and reset the cache if they've changed\n const headers = { ...getFetchHeaders(), ...options?.headers };\n const headersKey = stringifySorted(headers);\n if (!cacheKey || cacheKey !== headersKey) {\n resetCache();\n }\n\n // Retrieve a cached result, if we have any via a `query => variables => Result` cache key\n const variablesKey = stringifySorted(variables);\n const queryCache = cache[query] || (cache[query] = new Map());\n if (queryCache.has(variablesKey)) {\n data = queryCache.get(variablesKey) as Result;\n }\n\n // Retry the query if it fails due to an unknown or transient error\n for (let attemptCount = 0; attemptCount < 3 && !data; attemptCount++) {\n // Add a random delay on each subsequent attempt\n if (attemptCount > 0) {\n await randomDelay(attemptCount);\n }\n\n try {\n response = await wrappedFetch(url, {\n ...options,\n method: 'POST',\n body: JSON.stringify({ query, variables }),\n headers,\n });\n } catch (networkError) {\n error = networkError || error;\n continue;\n }\n\n const json = await response.json();\n if (typeof json === 'object' && json) {\n // If we have a transient error, we retry immediately and discard the data\n // Otherwise, we store the first available error and get the data\n if ('errors' in json && Array.isArray(json.errors)) {\n isTransient = json.errors.some((e: any) => e?.extensions?.isTransient);\n if (isTransient) {\n data = undefined;\n continue;\n } else {\n error = json.errors[0] || error;\n }\n }\n\n try {\n data = getResponseDataOrThrow<Result | null>(json);\n } catch (dataError) {\n // We only use the data error, if we don't have an error already\n if (!error) {\n error = dataError || error;\n }\n continue;\n }\n }\n }\n\n // Store the data in the cache, and only return a result if we have any values\n if (data) {\n queryCache.set(variablesKey, data);\n const keys = Object.keys(data);\n if (keys.length > 0 && keys.some((key) => data[key as keyof typeof data] != null)) {\n return data;\n }\n }\n\n // If we have an error, rethrow it wrapped in our custom errors\n if (error) {\n if (isTransient) {\n Log.error(`We've encountered a transient error, please try again shortly.`);\n }\n const wrappedError = new UnexpectedServerError('' + (error as any).message);\n wrappedError.cause = error;\n throw wrappedError;\n } else if (response && !response.ok) {\n throw new UnexpectedServerError(`Unexpected server error: ${response.statusText}`);\n } else {\n throw new UnexpectedServerData('Unexpected server error: No returned query result');\n }\n };\n})();\n"],"names":["UnexpectedServerData","UnexpectedServerError","graphql","query","trim","url","getExpoApiBaseUrl","_fetch","wrappedFetch","args","wrapFetchWithOffline","wrapFetchWithUserAgent","fetch","randomDelay","attemptCount","Promise","resolve","setTimeout","Math","min","random","getFetchHeaders","getSession","token","getAccessToken","headers","accept","sessionSecret","authorization","stringifySorted","variables","JSON","stringify","Object","keys","sort","reduce","acc","key","cache","cacheKey","resetCache","options","isTransient","response","data","error","headersKey","variablesKey","queryCache","Map","has","get","method","body","networkError","json","Array","isArray","errors","some","e","extensions","undefined","getResponseDataOrThrow","dataError","set","length","Log","wrappedError","message","cause","ok","statusText"],"mappings":";;;;;;;;;;;IA0BgCA,oBAAoB;eAApBA,4BAAoB;;IAA3CC,qBAAqB;eAArBA,6BAAqB;;IANdC,OAAO;eAAPA;;IAYHC,KAAK;eAALA;;;6DAhCQ;uBACgB;0BACH;wBAK3B;sCAE8B;wCACE;8BACI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASpC,SAASD,QACdC,KAAa;IAEb,OAAOA,MAAMC,IAAI;AACnB;AAQO,MAAMD,QAAQ,AAAC,CAAA;IACpB,MAAME,MAAMC,IAAAA,2BAAiB,MAAK;IAElC,IAAIC;IACJ,MAAMC,eAA0B,CAAC,GAAGC;QAClC,IAAI,CAACF,SAAQ;YACXA,UAASG,IAAAA,0CAAoB,EAACC,IAAAA,8CAAsB,EAACC,YAAK;QAC5D;QACA,OAAOL,WAAUE;IACnB;IAEA,MAAMI,cAAc,CAACC,eACnB,IAAIC,QAAQ,CAACC;YACXC,WAAWD,SAASE,KAAKC,GAAG,CAAC,MAAMD,KAAKE,MAAM,KAAK,OAAON,cAAc;QAC1E;IAEF,MAAMO,kBAAkB;YASMC;QAR5B,MAAMC,QAAQC,IAAAA,4BAAc;QAC5B,MAAMC,UAAkC;YACtC,gBAAgB;YAChBC,QAAQ;QACV;QACA,IAAIC;QACJ,IAAIJ,OAAO;YACTE,QAAQG,aAAa,GAAG,CAAC,OAAO,EAAEL,OAAO;QAC3C,OAAO,IAAKI,iBAAgBL,cAAAA,IAAAA,wBAAU,wBAAVA,YAAcK,aAAa,EAAG;YACxDF,OAAO,CAAC,eAAe,GAAGE;QAC5B;QACA,OAAOF;IACT;IAEA,8FAA8F;IAC9F,MAAMI,kBAAkB,CAACC,YACvBC,KAAKC,SAAS,CACZC,OAAOC,IAAI,CAACJ,WACTK,IAAI,GACJC,MAAM,CAAC,CAACC,KAAKC;YACZD,GAAG,CAACC,IAAI,GAAGR,SAAS,CAACQ,IAAI;YACzB,OAAOD;QACT,GAAG,CAAC;IAGV,IAAIE,QAA8C,CAAC;IACnD,IAAIC;IAEJ,SAASC;QACPF,QAAQ,CAAC;IACX;IAEA,OAAO,eAAepC,MACpBA,KAA4C,EAC5C2B,SAAoB,EACpBY,OAAsB;QAEtB,IAAIC,cAAc;QAClB,IAAIC;QACJ,IAAIC;QACJ,IAAIC;QAEJ,iEAAiE;QACjE,MAAMrB,UAAU;YAAE,GAAGJ,iBAAiB;eAAKqB,2BAAAA,QAASjB,OAAO,AAAnB;QAAoB;QAC5D,MAAMsB,aAAalB,gBAAgBJ;QACnC,IAAI,CAACe,YAAYA,aAAaO,YAAY;YACxCN;QACF;QAEA,0FAA0F;QAC1F,MAAMO,eAAenB,gBAAgBC;QACrC,MAAMmB,aAAaV,KAAK,CAACpC,MAAM,IAAKoC,CAAAA,KAAK,CAACpC,MAAM,GAAG,IAAI+C,KAAI;QAC3D,IAAID,WAAWE,GAAG,CAACH,eAAe;YAChCH,OAAOI,WAAWG,GAAG,CAACJ;QACxB;QAEA,mEAAmE;QACnE,IAAK,IAAIlC,eAAe,GAAGA,eAAe,KAAK,CAAC+B,MAAM/B,eAAgB;YACpE,gDAAgD;YAChD,IAAIA,eAAe,GAAG;gBACpB,MAAMD,YAAYC;YACpB;YAEA,IAAI;gBACF8B,WAAW,MAAMpC,aAAaH,KAAK;oBACjC,GAAGqC,OAAO;oBACVW,QAAQ;oBACRC,MAAMvB,KAAKC,SAAS,CAAC;wBAAE7B;wBAAO2B;oBAAU;oBACxCL;gBACF;YACF,EAAE,OAAO8B,cAAc;gBACrBT,QAAQS,gBAAgBT;gBACxB;YACF;YAEA,MAAMU,OAAO,MAAMZ,SAASY,IAAI;YAChC,IAAI,OAAOA,SAAS,YAAYA,MAAM;gBACpC,0EAA0E;gBAC1E,iEAAiE;gBACjE,IAAI,YAAYA,QAAQC,MAAMC,OAAO,CAACF,KAAKG,MAAM,GAAG;oBAClDhB,cAAca,KAAKG,MAAM,CAACC,IAAI,CAAC,CAACC;4BAAWA;+BAAAA,sBAAAA,gBAAAA,EAAGC,UAAU,qBAAbD,cAAelB,WAAW;;oBACrE,IAAIA,aAAa;wBACfE,OAAOkB;wBACP;oBACF,OAAO;wBACLjB,QAAQU,KAAKG,MAAM,CAAC,EAAE,IAAIb;oBAC5B;gBACF;gBAEA,IAAI;oBACFD,OAAOmB,IAAAA,8BAAsB,EAAgBR;gBAC/C,EAAE,OAAOS,WAAW;oBAClB,gEAAgE;oBAChE,IAAI,CAACnB,OAAO;wBACVA,QAAQmB,aAAanB;oBACvB;oBACA;gBACF;YACF;QACF;QAEA,8EAA8E;QAC9E,IAAID,MAAM;YACRI,WAAWiB,GAAG,CAAClB,cAAcH;YAC7B,MAAMX,OAAOD,OAAOC,IAAI,CAACW;YACzB,IAAIX,KAAKiC,MAAM,GAAG,KAAKjC,KAAK0B,IAAI,CAAC,CAACtB,MAAQO,IAAI,CAACP,IAAyB,IAAI,OAAO;gBACjF,OAAOO;YACT;QACF;QAEA,+DAA+D;QAC/D,IAAIC,OAAO;YACT,IAAIH,aAAa;gBACfyB,KAAItB,KAAK,CAAC,CAAC,8DAA8D,CAAC;YAC5E;YACA,MAAMuB,eAAe,IAAIpE,6BAAqB,CAAC,KAAK,AAAC6C,MAAcwB,OAAO;YAC1ED,aAAaE,KAAK,GAAGzB;YACrB,MAAMuB;QACR,OAAO,IAAIzB,YAAY,CAACA,SAAS4B,EAAE,EAAE;YACnC,MAAM,IAAIvE,6BAAqB,CAAC,CAAC,yBAAyB,EAAE2C,SAAS6B,UAAU,EAAE;QACnF,OAAO;YACL,MAAM,IAAIzE,4BAAoB,CAAC;QACjC;IACF;AACF,CAAA"}
@@ -8,35 +8,31 @@ Object.defineProperty(exports, "AppQuery", {
8
8
  return AppQuery;
9
9
  }
10
10
  });
11
- function _core() {
12
- const data = require("@urql/core");
13
- _core = function() {
14
- return data;
15
- };
16
- return data;
17
- }
18
11
  const _client = require("../client");
19
- const _App = require("../types/App");
12
+ const AppQueryDocument = (0, _client.graphql)(`
13
+ query AppByIdQuery($appId: String!) {
14
+ app {
15
+ byId(appId: $appId) {
16
+ id
17
+ ...AppFragment
18
+ }
19
+ }
20
+ }
21
+
22
+ fragment AppFragment on App {
23
+ id
24
+ scopeKey
25
+ ownerAccount {
26
+ id
27
+ name
28
+ }
29
+ }
30
+ `);
20
31
  const AppQuery = {
21
32
  async byIdAsync (projectId) {
22
- const data = await (0, _client.withErrorHandlingAsync)(_client.graphqlClient.query((0, _core().gql)`
23
- query AppByIdQuery($appId: String!) {
24
- app {
25
- byId(appId: $appId) {
26
- id
27
- ...AppFragment
28
- }
29
- }
30
- }
31
-
32
- ${_App.AppFragmentNode}
33
- `, {
33
+ const data = await (0, _client.query)(AppQueryDocument, {
34
34
  appId: projectId
35
- }, {
36
- additionalTypenames: [
37
- 'App'
38
- ]
39
- }).toPromise());
35
+ });
40
36
  return data.app.byId;
41
37
  }
42
38
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/api/graphql/queries/AppQuery.ts"],"sourcesContent":["import { gql } from '@urql/core';\n\nimport { AppByIdQuery } from '../../../graphql/generated';\nimport { graphqlClient, withErrorHandlingAsync } from '../client';\nimport { AppFragmentNode } from '../types/App';\n\nexport const AppQuery = {\n async byIdAsync(projectId: string): Promise<AppByIdQuery['app']['byId']> {\n const data = await withErrorHandlingAsync(\n graphqlClient\n .query<AppByIdQuery>(\n gql`\n query AppByIdQuery($appId: String!) {\n app {\n byId(appId: $appId) {\n id\n ...AppFragment\n }\n }\n }\n\n ${AppFragmentNode}\n `,\n { appId: projectId },\n {\n additionalTypenames: ['App'],\n }\n )\n .toPromise()\n );\n return data.app.byId;\n },\n};\n"],"names":["AppQuery","byIdAsync","projectId","data","withErrorHandlingAsync","graphqlClient","query","gql","AppFragmentNode","appId","additionalTypenames","toPromise","app","byId"],"mappings":";;;;+BAMaA;;;eAAAA;;;;yBANO;;;;;;wBAGkC;qBACtB;AAEzB,MAAMA,WAAW;IACtB,MAAMC,WAAUC,SAAiB;QAC/B,MAAMC,OAAO,MAAMC,IAAAA,8BAAsB,EACvCC,qBAAa,CACVC,KAAK,CACJC,IAAAA,WAAG,CAAA,CAAC;;;;;;;;;;YAUF,EAAEC,oBAAe,CAAC;UACpB,CAAC,EACD;YAAEC,OAAOP;QAAU,GACnB;YACEQ,qBAAqB;gBAAC;aAAM;QAC9B,GAEDC,SAAS;QAEd,OAAOR,KAAKS,GAAG,CAACC,IAAI;IACtB;AACF"}
1
+ {"version":3,"sources":["../../../../../src/api/graphql/queries/AppQuery.ts"],"sourcesContent":["import { graphql, query } from '../client';\n\nexport type App = {\n id: string;\n scopeKey: string;\n ownerAccount: {\n id: string;\n name: string;\n };\n};\n\ntype AppQueryData = {\n app: {\n byId: App;\n };\n};\n\ntype AppQueryVariables = {\n appId: string;\n};\n\nconst AppQueryDocument = graphql<AppQueryData, AppQueryVariables>(`\n query AppByIdQuery($appId: String!) {\n app {\n byId(appId: $appId) {\n id\n ...AppFragment\n }\n }\n }\n\n fragment AppFragment on App {\n id\n scopeKey\n ownerAccount {\n id\n name\n }\n }\n`);\n\nexport const AppQuery = {\n async byIdAsync(projectId: string): Promise<AppQueryData['app']['byId']> {\n const data = await query(AppQueryDocument, { appId: projectId });\n return data.app.byId;\n },\n};\n"],"names":["AppQuery","AppQueryDocument","graphql","byIdAsync","projectId","data","query","appId","app","byId"],"mappings":";;;;+BAyCaA;;;eAAAA;;;wBAzCkB;AAqB/B,MAAMC,mBAAmBC,IAAAA,eAAO,EAAkC,CAAC;;;;;;;;;;;;;;;;;;AAkBnE,CAAC;AAEM,MAAMF,WAAW;IACtB,MAAMG,WAAUC,SAAiB;QAC/B,MAAMC,OAAO,MAAMC,IAAAA,aAAK,EAACL,kBAAkB;YAAEM,OAAOH;QAAU;QAC9D,OAAOC,KAAKG,GAAG,CAACC,IAAI;IACtB;AACF"}
@@ -8,48 +8,55 @@ Object.defineProperty(exports, "UserQuery", {
8
8
  return UserQuery;
9
9
  }
10
10
  });
11
- function _core() {
12
- const data = require("@urql/core");
13
- _core = function() {
14
- return data;
15
- };
16
- return data;
17
- }
18
11
  const _client = require("../client");
12
+ const CurrentUserDocument = (0, _client.graphql)(`
13
+ query CurrentUser {
14
+ meActor {
15
+ __typename
16
+ id
17
+ ... on UserActor {
18
+ primaryAccount {
19
+ id
20
+ }
21
+ username
22
+ }
23
+ ... on Robot {
24
+ firstName
25
+ }
26
+ accounts {
27
+ id
28
+ users {
29
+ actor {
30
+ __typename
31
+ id
32
+ }
33
+ permissions
34
+ }
35
+ }
36
+ }
37
+ }
38
+ `);
39
+ const UserQueryDocument = (0, _client.graphql)(`
40
+ query UserQuery {
41
+ meUserActor {
42
+ id
43
+ username
44
+ }
45
+ }
46
+ `);
19
47
  const UserQuery = {
20
48
  async currentUserAsync () {
21
- const data = await (0, _client.withErrorHandlingAsync)(_client.graphqlClient.query((0, _core().gql)`
22
- query CurrentUser {
23
- meActor {
24
- __typename
25
- id
26
- ... on UserActor {
27
- primaryAccount {
28
- id
29
- }
30
- username
31
- }
32
- ... on Robot {
33
- firstName
34
- }
35
- accounts {
36
- id
37
- users {
38
- actor {
39
- id
40
- }
41
- permissions
42
- }
43
- }
44
- }
45
- }
46
- `, /* variables */ undefined, {
47
- additionalTypenames: [
48
- 'User',
49
- 'SSOUser'
50
- ]
51
- }).toPromise());
49
+ const data = await (0, _client.query)(CurrentUserDocument, {});
52
50
  return data.meActor;
51
+ },
52
+ async meUserActorAsync (headers) {
53
+ const data = await (0, _client.query)(UserQueryDocument, {}, {
54
+ headers
55
+ });
56
+ return {
57
+ id: data.meUserActor.id,
58
+ username: data.meUserActor.username
59
+ };
53
60
  }
54
61
  };
55
62
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/api/graphql/queries/UserQuery.ts"],"sourcesContent":["import { gql } from '@urql/core';\n\nimport { CurrentUserQuery } from '../../../graphql/generated';\nimport { graphqlClient, withErrorHandlingAsync } from '../client';\n\nexport const UserQuery = {\n async currentUserAsync(): Promise<CurrentUserQuery['meActor']> {\n const data = await withErrorHandlingAsync(\n graphqlClient\n .query<CurrentUserQuery>(\n gql`\n query CurrentUser {\n meActor {\n __typename\n id\n ... on UserActor {\n primaryAccount {\n id\n }\n username\n }\n ... on Robot {\n firstName\n }\n accounts {\n id\n users {\n actor {\n id\n }\n permissions\n }\n }\n }\n }\n `,\n /* variables */ undefined,\n {\n additionalTypenames: ['User', 'SSOUser'],\n }\n )\n .toPromise()\n );\n\n return data.meActor;\n },\n};\n"],"names":["UserQuery","currentUserAsync","data","withErrorHandlingAsync","graphqlClient","query","gql","undefined","additionalTypenames","toPromise","meActor"],"mappings":";;;;+BAKaA;;;eAAAA;;;;yBALO;;;;;;wBAGkC;AAE/C,MAAMA,YAAY;IACvB,MAAMC;QACJ,MAAMC,OAAO,MAAMC,IAAAA,8BAAsB,EACvCC,qBAAa,CACVC,KAAK,CACJC,IAAAA,WAAG,CAAA,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;UAyBJ,CAAC,EACD,aAAa,GAAGC,WAChB;YACEC,qBAAqB;gBAAC;gBAAQ;aAAU;QAC1C,GAEDC,SAAS;QAGd,OAAOP,KAAKQ,OAAO;IACrB;AACF"}
1
+ {"version":3,"sources":["../../../../../src/api/graphql/queries/UserQuery.ts"],"sourcesContent":["import { graphql, query } from '../client';\n\nexport type Permission = 'ADMIN' | 'OWN' | 'PUBLISH' | 'VIEW';\n\ntype CurrentUserDataUser = {\n permissions: Permission[];\n actor: {\n __typename: 'Robot' | 'SSOUser' | 'User';\n id: string;\n };\n};\n\ntype CurrentUserDataAccount = {\n __typename?: 'Account';\n id: string;\n users: CurrentUserDataUser[];\n};\n\nexport type Actor =\n | {\n __typename: 'Robot';\n firstName?: string | null;\n id: string;\n accounts: CurrentUserDataAccount[];\n }\n | {\n __typename: 'SSOUser' | 'User';\n id: string;\n username: string;\n primaryAccount: {\n id: string;\n };\n accounts: CurrentUserDataAccount[];\n };\n\ntype CurrentUserData = {\n meActor: Actor | null;\n};\n\nconst CurrentUserDocument = graphql<CurrentUserData>(`\n query CurrentUser {\n meActor {\n __typename\n id\n ... on UserActor {\n primaryAccount {\n id\n }\n username\n }\n ... on Robot {\n firstName\n }\n accounts {\n id\n users {\n actor {\n __typename\n id\n }\n permissions\n }\n }\n }\n }\n`);\n\ntype UserQueryData = {\n meUserActor: {\n id: string;\n username: string;\n };\n};\n\nconst UserQueryDocument = graphql<UserQueryData>(`\n query UserQuery {\n meUserActor {\n id\n username\n }\n }\n`);\n\nexport const UserQuery = {\n async currentUserAsync() {\n const data = await query(CurrentUserDocument, {});\n return data.meActor;\n },\n async meUserActorAsync(headers: Record<string, string>) {\n const data = await query(UserQueryDocument, {}, { headers });\n return {\n id: data.meUserActor.id,\n username: data.meUserActor.username,\n };\n },\n};\n"],"names":["UserQuery","CurrentUserDocument","graphql","UserQueryDocument","currentUserAsync","data","query","meActor","meUserActorAsync","headers","id","meUserActor","username"],"mappings":";;;;+BAmFaA;;;eAAAA;;;wBAnFkB;AAuC/B,MAAMC,sBAAsBC,IAAAA,eAAO,EAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BtD,CAAC;AASD,MAAMC,oBAAoBD,IAAAA,eAAO,EAAgB,CAAC;;;;;;;AAOlD,CAAC;AAEM,MAAMF,YAAY;IACvB,MAAMI;QACJ,MAAMC,OAAO,MAAMC,IAAAA,aAAK,EAACL,qBAAqB,CAAC;QAC/C,OAAOI,KAAKE,OAAO;IACrB;IACA,MAAMC,kBAAiBC,OAA+B;QACpD,MAAMJ,OAAO,MAAMC,IAAAA,aAAK,EAACH,mBAAmB,CAAC,GAAG;YAAEM;QAAQ;QAC1D,OAAO;YACLC,IAAIL,KAAKM,WAAW,CAACD,EAAE;YACvBE,UAAUP,KAAKM,WAAW,CAACC,QAAQ;QACrC;IACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/api/rest/cache/FileSystemResponseCache.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport stream, { Readable } from 'node:stream';\nimport { ReadableStream } from 'node:stream/web';\n\nimport type { ResponseCache, ResponseCacheEntry } from './ResponseCache';\nimport { fileExistsAsync } from '../../../utils/dir';\n\ntype FileSystemResponseCacheInfo = ResponseCacheEntry['info'] & {\n /** The path to the cached body file */\n bodyPath?: string;\n /** If there is no response body */\n empty?: boolean;\n /** The expiration time, in milliseconds, when the response should be invalidated */\n expiration?: number;\n};\n\nexport class FileSystemResponseCache implements ResponseCache {\n /** The absolute path to the directory used to store responses */\n private cacheDirectory: string;\n /** Optional auto-expiration for all stored responses */\n private timeToLive?: number;\n\n constructor(options: { cacheDirectory: string; ttl?: number }) {\n this.cacheDirectory = options.cacheDirectory;\n this.timeToLive = options.ttl;\n }\n\n private getFilePaths(cacheKey: string) {\n // Create a hash of the cache key to use as filename\n const hash = crypto.createHash('sha256').update(cacheKey).digest('hex');\n return {\n info: path.join(this.cacheDirectory, `${hash}-info.json`),\n body: path.join(this.cacheDirectory, `${hash}-body.bin`),\n };\n }\n\n /** Retrieve the cache response, if any */\n async get(cacheKey: string): Promise<ResponseCacheEntry | undefined> {\n const paths = this.getFilePaths(cacheKey);\n\n if (!(await fileExistsAsync(paths.info))) {\n return undefined;\n }\n\n // Read and parse the info file\n const infoBuffer = await fs.promises.readFile(paths.info);\n\n try {\n const responseInfo: FileSystemResponseCacheInfo = JSON.parse(infoBuffer.toString());\n\n // Check if the response has expired\n if (responseInfo.expiration && responseInfo.expiration < Date.now()) {\n await this.remove(cacheKey);\n return undefined;\n }\n\n // Remove cache-specific data from the response info\n const { empty, expiration, bodyPath, ...cleanInfo } = responseInfo;\n\n // Create response body stream\n let responseBody: ReadableStream;\n if (empty) {\n responseBody = Readable.toWeb(Readable.from(Buffer.alloc(0)));\n } else {\n const bodyBuffer = await fs.promises.readFile(paths.body);\n responseBody = Readable.toWeb(Readable.from(bodyBuffer));\n }\n\n return {\n body: responseBody,\n info: cleanInfo,\n };\n } catch {\n // If file doesn't exist or other errors, return undefined\n return undefined;\n }\n }\n\n /** Store the response for caching */\n async set(\n cacheKey: string,\n response: ResponseCacheEntry\n ): Promise<ResponseCacheEntry | undefined> {\n await fs.promises.mkdir(this.cacheDirectory, { recursive: true });\n const paths = this.getFilePaths(cacheKey);\n\n // Create a copy of the response info, to add cache-specific data\n const responseInfo: FileSystemResponseCacheInfo = { ...response.info };\n\n // Add expiration time if the \"time to live\" is set\n if (typeof this.timeToLive === 'number') {\n responseInfo.expiration = Date.now() + this.timeToLive;\n }\n\n try {\n // Clone the response body stream since we need to read it twice\n const [forSize, forWrite] = response.body.tee();\n\n // Check if the body is empty by reading the first stream\n const reader = forSize.getReader();\n const { value } = await reader.read();\n reader.releaseLock();\n\n if (!value || value.length === 0) {\n responseInfo.empty = true;\n } else {\n // Create write stream and pipe response body to file\n const writeStream = fs.createWriteStream(paths.body);\n const nodeStream = Readable.fromWeb(forWrite);\n nodeStream.pipe(writeStream);\n\n // Wait for the stream to finish\n await stream.promises.finished(writeStream);\n\n responseInfo.bodyPath = paths.body;\n }\n\n // Write info to file\n await fs.promises.writeFile(paths.info, JSON.stringify(responseInfo));\n\n return await this.get(cacheKey);\n } catch (error) {\n // Clean up any partially written files\n await this.remove(cacheKey);\n throw error;\n }\n }\n\n /** Remove the response from caching */\n async remove(cacheKey: string): Promise<void> {\n const paths = this.getFilePaths(cacheKey);\n await removeAllAsync(paths.info, paths.body);\n }\n}\n\nfunction removeAllAsync(...paths: string[]) {\n return Promise.all(\n paths.map((path) => fs.promises.rm(path, { recursive: true, force: true }).catch(() => {}))\n );\n}\n"],"names":["FileSystemResponseCache","constructor","options","cacheDirectory","timeToLive","ttl","getFilePaths","cacheKey","hash","crypto","createHash","update","digest","info","path","join","body","get","paths","fileExistsAsync","undefined","infoBuffer","fs","promises","readFile","responseInfo","JSON","parse","toString","expiration","Date","now","remove","empty","bodyPath","cleanInfo","responseBody","Readable","toWeb","from","Buffer","alloc","bodyBuffer","set","response","mkdir","recursive","forSize","forWrite","tee","reader","getReader","value","read","releaseLock","length","writeStream","createWriteStream","nodeStream","fromWeb","pipe","stream","finished","writeFile","stringify","error","removeAllAsync","Promise","all","map","rm","force","catch"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../src/api/rest/cache/FileSystemResponseCache.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport stream, { Readable } from 'node:stream';\nimport { ReadableStream } from 'node:stream/web';\n\nimport type { ResponseCache, ResponseCacheEntry } from './ResponseCache';\nimport { fileExistsAsync } from '../../../utils/dir';\n\ntype FileSystemResponseCacheInfo = ResponseCacheEntry['info'] & {\n /** The path to the cached body file */\n bodyPath?: string;\n /** If there is no response body */\n empty?: boolean;\n /** The expiration time, in milliseconds, when the response should be invalidated */\n expiration?: number;\n};\n\nexport class FileSystemResponseCache implements ResponseCache {\n /** The absolute path to the directory used to store responses */\n private cacheDirectory: string;\n /** Optional auto-expiration for all stored responses */\n private timeToLive?: number;\n\n constructor(options: { cacheDirectory: string; ttl?: number }) {\n this.cacheDirectory = options.cacheDirectory;\n this.timeToLive = options.ttl;\n }\n\n private getFilePaths(cacheKey: string) {\n // Create a hash of the cache key to use as filename\n const hash = crypto.createHash('sha256').update(cacheKey).digest('hex');\n return {\n info: path.join(this.cacheDirectory, `${hash}-info.json`),\n body: path.join(this.cacheDirectory, `${hash}-body.bin`),\n };\n }\n\n /** Retrieve the cache response, if any */\n async get(cacheKey: string): Promise<ResponseCacheEntry | undefined> {\n const paths = this.getFilePaths(cacheKey);\n\n if (!(await fileExistsAsync(paths.info))) {\n return undefined;\n }\n\n // Read and parse the info file\n const infoBuffer = await fs.promises.readFile(paths.info);\n\n try {\n const responseInfo: FileSystemResponseCacheInfo = JSON.parse(infoBuffer.toString());\n\n // Check if the response has expired\n if (responseInfo.expiration && responseInfo.expiration < Date.now()) {\n await this.remove(cacheKey);\n return undefined;\n }\n\n // Remove cache-specific data from the response info\n const { empty, expiration, bodyPath, ...cleanInfo } = responseInfo;\n\n // Create response body stream\n let responseBody: ReadableStream;\n if (empty) {\n responseBody = Readable.toWeb(Readable.from(Buffer.alloc(0)));\n } else {\n const bodyBuffer = await fs.promises.readFile(paths.body);\n responseBody = Readable.toWeb(Readable.from(bodyBuffer));\n }\n\n return {\n body: responseBody as globalThis.ReadableStream,\n info: cleanInfo,\n };\n } catch {\n // If file doesn't exist or other errors, return undefined\n return undefined;\n }\n }\n\n /** Store the response for caching */\n async set(\n cacheKey: string,\n response: ResponseCacheEntry\n ): Promise<ResponseCacheEntry | undefined> {\n await fs.promises.mkdir(this.cacheDirectory, { recursive: true });\n const paths = this.getFilePaths(cacheKey);\n\n // Create a copy of the response info, to add cache-specific data\n const responseInfo: FileSystemResponseCacheInfo = { ...response.info };\n\n // Add expiration time if the \"time to live\" is set\n if (typeof this.timeToLive === 'number') {\n responseInfo.expiration = Date.now() + this.timeToLive;\n }\n\n try {\n // Clone the response body stream since we need to read it twice\n const [forSize, forWrite] = response.body.tee();\n\n // Check if the body is empty by reading the first stream\n const reader = forSize.getReader();\n const { value } = await reader.read();\n reader.releaseLock();\n\n if (!value || value.length === 0) {\n responseInfo.empty = true;\n } else {\n // Create write stream and pipe response body to file\n const writeStream = fs.createWriteStream(paths.body);\n const nodeStream = Readable.fromWeb(forWrite as ReadableStream);\n nodeStream.pipe(writeStream);\n\n // Wait for the stream to finish\n await stream.promises.finished(writeStream);\n\n responseInfo.bodyPath = paths.body;\n }\n\n // Write info to file\n await fs.promises.writeFile(paths.info, JSON.stringify(responseInfo));\n\n return await this.get(cacheKey);\n } catch (error) {\n // Clean up any partially written files\n await this.remove(cacheKey);\n throw error;\n }\n }\n\n /** Remove the response from caching */\n async remove(cacheKey: string): Promise<void> {\n const paths = this.getFilePaths(cacheKey);\n await removeAllAsync(paths.info, paths.body);\n }\n}\n\nfunction removeAllAsync(...paths: string[]) {\n return Promise.all(\n paths.map((path) => fs.promises.rm(path, { recursive: true, force: true }).catch(() => {}))\n );\n}\n"],"names":["FileSystemResponseCache","constructor","options","cacheDirectory","timeToLive","ttl","getFilePaths","cacheKey","hash","crypto","createHash","update","digest","info","path","join","body","get","paths","fileExistsAsync","undefined","infoBuffer","fs","promises","readFile","responseInfo","JSON","parse","toString","expiration","Date","now","remove","empty","bodyPath","cleanInfo","responseBody","Readable","toWeb","from","Buffer","alloc","bodyBuffer","set","response","mkdir","recursive","forSize","forWrite","tee","reader","getReader","value","read","releaseLock","length","writeStream","createWriteStream","nodeStream","fromWeb","pipe","stream","finished","writeFile","stringify","error","removeAllAsync","Promise","all","map","rm","force","catch"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/api/rest/cache/ResponseCache.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { ReadStream } from 'fs';\nimport type { Response, RequestInfo, RequestInit } from 'undici';\n\nconst GLOBAL_CACHE_VERSION = 4;\n\nexport type ResponseCacheEntry = {\n body: import('stream/web').ReadableStream;\n info: ReturnType<typeof getResponseInfo>;\n};\n\nexport interface ResponseCache {\n /** Load the response info from cache, if any */\n get(cacheKey: string): Promise<ResponseCacheEntry | undefined>;\n /** Store the response info to cache, and return the cached info */\n set(cacheKey: string, response: ResponseCacheEntry): Promise<ResponseCacheEntry | undefined>;\n /** Remove a response entry from the cache */\n remove(cacheKey: string): Promise<void>;\n}\n\nexport function getResponseInfo(response: Response) {\n const headers = Object.fromEntries(response.headers.entries());\n delete headers['set-cookie'];\n return {\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n headers,\n };\n}\n\nexport function getRequestCacheKey(info: RequestInfo, init?: RequestInit) {\n const infoKeyData = getRequestInfoCacheData(info);\n const initKeyData = { body: init?.body ? getRequestBodyCacheData(init.body) : undefined };\n\n return crypto\n .createHash('md5')\n .update(JSON.stringify([infoKeyData, initKeyData, GLOBAL_CACHE_VERSION]))\n .digest('hex');\n}\n\n/** @internal Exposed for testing */\nexport function getRequestInfoCacheData(info: RequestInfo) {\n if (typeof info === 'string') {\n return { url: info };\n }\n\n if (info instanceof URL) {\n return { url: info.toString() };\n }\n\n if (info instanceof Request) {\n return {\n // cache: req.cache,\n credentials: info.credentials.toString(),\n destination: info.destination.toString(),\n headers: Object.fromEntries(info.headers.entries()),\n integrity: info.integrity,\n method: info.method,\n redirect: info.redirect,\n referrer: info.referrer,\n referrerPolicy: info.referrerPolicy,\n url: info.url.toString(),\n // body: // TODO\n };\n }\n\n throw new Error('Unsupported request info type for caching: ' + typeof info);\n}\n\n/** @internal Exposed for testing */\nexport function getRequestBodyCacheData(body: RequestInit['body']) {\n if (!body) {\n return body;\n }\n\n if (typeof body === 'string') {\n return body;\n }\n\n if (body instanceof URLSearchParams) {\n return body.toString();\n }\n\n // Supported for legacy purposes because node-fetch uses fs.readStream\n if (body instanceof ReadStream) {\n return body.path;\n }\n\n if (body.toString && body.toString() === '[object FormData]') {\n return new URLSearchParams(body as any).toString();\n }\n\n if (body instanceof Buffer) {\n return body.toString();\n }\n\n throw new Error(`Unsupported request body type for caching: ${typeof body}`);\n}\n"],"names":["getRequestBodyCacheData","getRequestCacheKey","getRequestInfoCacheData","getResponseInfo","GLOBAL_CACHE_VERSION","response","headers","Object","fromEntries","entries","url","status","statusText","info","init","infoKeyData","initKeyData","body","undefined","crypto","createHash","update","JSON","stringify","digest","URL","toString","Request","credentials","destination","integrity","method","redirect","referrer","referrerPolicy","Error","URLSearchParams","ReadStream","path","Buffer"],"mappings":";;;;;;;;;;;IAuEgBA,uBAAuB;eAAvBA;;IAxCAC,kBAAkB;eAAlBA;;IAWAC,uBAAuB;eAAvBA;;IAtBAC,eAAe;eAAfA;;;;gEApBG;;;;;;;yBACQ;;;;;;;;;;;AAG3B,MAAMC,uBAAuB;AAgBtB,SAASD,gBAAgBE,QAAkB;IAChD,MAAMC,UAAUC,OAAOC,WAAW,CAACH,SAASC,OAAO,CAACG,OAAO;IAC3D,OAAOH,OAAO,CAAC,aAAa;IAC5B,OAAO;QACLI,KAAKL,SAASK,GAAG;QACjBC,QAAQN,SAASM,MAAM;QACvBC,YAAYP,SAASO,UAAU;QAC/BN;IACF;AACF;AAEO,SAASL,mBAAmBY,IAAiB,EAAEC,IAAkB;IACtE,MAAMC,cAAcb,wBAAwBW;IAC5C,MAAMG,cAAc;QAAEC,MAAMH,CAAAA,wBAAAA,KAAMG,IAAI,IAAGjB,wBAAwBc,KAAKG,IAAI,IAAIC;IAAU;IAExF,OAAOC,iBAAM,CACVC,UAAU,CAAC,OACXC,MAAM,CAACC,KAAKC,SAAS,CAAC;QAACR;QAAaC;QAAaZ;KAAqB,GACtEoB,MAAM,CAAC;AACZ;AAGO,SAAStB,wBAAwBW,IAAiB;IACvD,IAAI,OAAOA,SAAS,UAAU;QAC5B,OAAO;YAAEH,KAAKG;QAAK;IACrB;IAEA,IAAIA,gBAAgBY,KAAK;QACvB,OAAO;YAAEf,KAAKG,KAAKa,QAAQ;QAAG;IAChC;IAEA,IAAIb,gBAAgBc,SAAS;QAC3B,OAAO;YACL,oBAAoB;YACpBC,aAAaf,KAAKe,WAAW,CAACF,QAAQ;YACtCG,aAAahB,KAAKgB,WAAW,CAACH,QAAQ;YACtCpB,SAASC,OAAOC,WAAW,CAACK,KAAKP,OAAO,CAACG,OAAO;YAChDqB,WAAWjB,KAAKiB,SAAS;YACzBC,QAAQlB,KAAKkB,MAAM;YACnBC,UAAUnB,KAAKmB,QAAQ;YACvBC,UAAUpB,KAAKoB,QAAQ;YACvBC,gBAAgBrB,KAAKqB,cAAc;YACnCxB,KAAKG,KAAKH,GAAG,CAACgB,QAAQ;QAExB;IACF;IAEA,MAAM,IAAIS,MAAM,gDAAgD,OAAOtB;AACzE;AAGO,SAASb,wBAAwBiB,IAAyB;IAC/D,IAAI,CAACA,MAAM;QACT,OAAOA;IACT;IAEA,IAAI,OAAOA,SAAS,UAAU;QAC5B,OAAOA;IACT;IAEA,IAAIA,gBAAgBmB,iBAAiB;QACnC,OAAOnB,KAAKS,QAAQ;IACtB;IAEA,sEAAsE;IACtE,IAAIT,gBAAgBoB,gBAAU,EAAE;QAC9B,OAAOpB,KAAKqB,IAAI;IAClB;IAEA,IAAIrB,KAAKS,QAAQ,IAAIT,KAAKS,QAAQ,OAAO,qBAAqB;QAC5D,OAAO,IAAIU,gBAAgBnB,MAAaS,QAAQ;IAClD;IAEA,IAAIT,gBAAgBsB,QAAQ;QAC1B,OAAOtB,KAAKS,QAAQ;IACtB;IAEA,MAAM,IAAIS,MAAM,CAAC,2CAA2C,EAAE,OAAOlB,MAAM;AAC7E"}
1
+ {"version":3,"sources":["../../../../../src/api/rest/cache/ResponseCache.ts"],"sourcesContent":["import crypto from 'crypto';\nimport type { Response, RequestInfo, RequestInit } from 'fetch-nodeshim';\nimport { ReadStream } from 'fs';\n\nconst GLOBAL_CACHE_VERSION = 4;\n\nexport type ResponseCacheEntry = {\n body: ReadableStream;\n info: ReturnType<typeof getResponseInfo>;\n};\n\nexport interface ResponseCache {\n /** Load the response info from cache, if any */\n get(cacheKey: string): Promise<ResponseCacheEntry | undefined>;\n /** Store the response info to cache, and return the cached info */\n set(cacheKey: string, response: ResponseCacheEntry): Promise<ResponseCacheEntry | undefined>;\n /** Remove a response entry from the cache */\n remove(cacheKey: string): Promise<void>;\n}\n\nexport function getResponseInfo(response: Response) {\n const headers = Object.fromEntries(response.headers.entries());\n delete headers['set-cookie'];\n return {\n url: response.url,\n status: response.status,\n statusText: response.statusText,\n headers,\n };\n}\n\nexport function getRequestCacheKey(info: RequestInfo, init?: RequestInit) {\n const infoKeyData = getRequestInfoCacheData(info);\n const initKeyData = { body: init?.body ? getRequestBodyCacheData(init.body) : undefined };\n\n return crypto\n .createHash('md5')\n .update(JSON.stringify([infoKeyData, initKeyData, GLOBAL_CACHE_VERSION]))\n .digest('hex');\n}\n\n/** @internal Exposed for testing */\nexport function getRequestInfoCacheData(info: RequestInfo) {\n if (typeof info === 'string') {\n return { url: info };\n }\n\n if (info instanceof URL) {\n return { url: info.toString() };\n }\n\n if (info instanceof Request) {\n return {\n // cache: req.cache,\n credentials: info.credentials.toString(),\n destination: info.destination.toString(),\n headers: Object.fromEntries(info.headers.entries()),\n integrity: info.integrity,\n method: info.method,\n redirect: info.redirect,\n referrer: info.referrer,\n referrerPolicy: info.referrerPolicy,\n url: info.url.toString(),\n // body: // TODO\n };\n }\n\n throw new Error('Unsupported request info type for caching: ' + typeof info);\n}\n\n/** @internal Exposed for testing */\nexport function getRequestBodyCacheData(body: RequestInit['body']) {\n if (!body) {\n return body;\n }\n\n if (typeof body === 'string') {\n return body;\n }\n\n if (body instanceof URLSearchParams) {\n return body.toString();\n }\n\n // Supported for legacy purposes because node-fetch uses fs.readStream\n if (body instanceof ReadStream) {\n return body.path;\n }\n\n if (body.toString && body.toString() === '[object FormData]') {\n return new URLSearchParams(body as any).toString();\n }\n\n if (body instanceof Buffer) {\n return body.toString();\n }\n\n throw new Error(`Unsupported request body type for caching: ${typeof body}`);\n}\n"],"names":["getRequestBodyCacheData","getRequestCacheKey","getRequestInfoCacheData","getResponseInfo","GLOBAL_CACHE_VERSION","response","headers","Object","fromEntries","entries","url","status","statusText","info","init","infoKeyData","initKeyData","body","undefined","crypto","createHash","update","JSON","stringify","digest","URL","toString","Request","credentials","destination","integrity","method","redirect","referrer","referrerPolicy","Error","URLSearchParams","ReadStream","path","Buffer"],"mappings":";;;;;;;;;;;IAuEgBA,uBAAuB;eAAvBA;;IAxCAC,kBAAkB;eAAlBA;;IAWAC,uBAAuB;eAAvBA;;IAtBAC,eAAe;eAAfA;;;;gEApBG;;;;;;;yBAEQ;;;;;;;;;;;AAE3B,MAAMC,uBAAuB;AAgBtB,SAASD,gBAAgBE,QAAkB;IAChD,MAAMC,UAAUC,OAAOC,WAAW,CAACH,SAASC,OAAO,CAACG,OAAO;IAC3D,OAAOH,OAAO,CAAC,aAAa;IAC5B,OAAO;QACLI,KAAKL,SAASK,GAAG;QACjBC,QAAQN,SAASM,MAAM;QACvBC,YAAYP,SAASO,UAAU;QAC/BN;IACF;AACF;AAEO,SAASL,mBAAmBY,IAAiB,EAAEC,IAAkB;IACtE,MAAMC,cAAcb,wBAAwBW;IAC5C,MAAMG,cAAc;QAAEC,MAAMH,CAAAA,wBAAAA,KAAMG,IAAI,IAAGjB,wBAAwBc,KAAKG,IAAI,IAAIC;IAAU;IAExF,OAAOC,iBAAM,CACVC,UAAU,CAAC,OACXC,MAAM,CAACC,KAAKC,SAAS,CAAC;QAACR;QAAaC;QAAaZ;KAAqB,GACtEoB,MAAM,CAAC;AACZ;AAGO,SAAStB,wBAAwBW,IAAiB;IACvD,IAAI,OAAOA,SAAS,UAAU;QAC5B,OAAO;YAAEH,KAAKG;QAAK;IACrB;IAEA,IAAIA,gBAAgBY,KAAK;QACvB,OAAO;YAAEf,KAAKG,KAAKa,QAAQ;QAAG;IAChC;IAEA,IAAIb,gBAAgBc,SAAS;QAC3B,OAAO;YACL,oBAAoB;YACpBC,aAAaf,KAAKe,WAAW,CAACF,QAAQ;YACtCG,aAAahB,KAAKgB,WAAW,CAACH,QAAQ;YACtCpB,SAASC,OAAOC,WAAW,CAACK,KAAKP,OAAO,CAACG,OAAO;YAChDqB,WAAWjB,KAAKiB,SAAS;YACzBC,QAAQlB,KAAKkB,MAAM;YACnBC,UAAUnB,KAAKmB,QAAQ;YACvBC,UAAUpB,KAAKoB,QAAQ;YACvBC,gBAAgBrB,KAAKqB,cAAc;YACnCxB,KAAKG,KAAKH,GAAG,CAACgB,QAAQ;QAExB;IACF;IAEA,MAAM,IAAIS,MAAM,gDAAgD,OAAOtB;AACzE;AAGO,SAASb,wBAAwBiB,IAAyB;IAC/D,IAAI,CAACA,MAAM;QACT,OAAOA;IACT;IAEA,IAAI,OAAOA,SAAS,UAAU;QAC5B,OAAOA;IACT;IAEA,IAAIA,gBAAgBmB,iBAAiB;QACnC,OAAOnB,KAAKS,QAAQ;IACtB;IAEA,sEAAsE;IACtE,IAAIT,gBAAgBoB,gBAAU,EAAE;QAC9B,OAAOpB,KAAKqB,IAAI;IAClB;IAEA,IAAIrB,KAAKS,QAAQ,IAAIT,KAAKS,QAAQ,OAAO,qBAAqB;QAC5D,OAAO,IAAIU,gBAAgBnB,MAAaS,QAAQ;IAClD;IAEA,IAAIT,gBAAgBsB,QAAQ;QAC1B,OAAOtB,KAAKS,QAAQ;IACtB;IAEA,MAAM,IAAIS,MAAM,CAAC,2CAA2C,EAAE,OAAOlB,MAAM;AAC7E"}
@@ -8,28 +8,28 @@ Object.defineProperty(exports, "wrapFetchWithCache", {
8
8
  return wrapFetchWithCache;
9
9
  }
10
10
  });
11
- function _undici() {
12
- const data = require("undici");
13
- _undici = function() {
11
+ function _fetchnodeshim() {
12
+ const data = require("fetch-nodeshim");
13
+ _fetchnodeshim = function() {
14
14
  return data;
15
15
  };
16
16
  return data;
17
17
  }
18
18
  const _ResponseCache = require("./ResponseCache");
19
- const debug = require('debug')('expo:undici-cache');
19
+ const debug = require('debug')('expo:fetch-cache');
20
20
  function wrapFetchWithCache(fetch, cache) {
21
21
  return async function cachedFetch(url, init) {
22
22
  const cacheKey = (0, _ResponseCache.getRequestCacheKey)(url, init);
23
23
  const cachedResponse = await cache.get(cacheKey);
24
24
  if (cachedResponse) {
25
- return new (_undici()).Response(cachedResponse.body, cachedResponse.info);
25
+ return new (_fetchnodeshim()).Response(cachedResponse.body, cachedResponse.info);
26
26
  }
27
27
  await lock(cacheKey);
28
28
  try {
29
29
  // Retry loading from cache, in case it was stored during the lock
30
30
  let cachedResponse = await cache.get(cacheKey);
31
31
  if (cachedResponse) {
32
- return new (_undici()).Response(cachedResponse.body, cachedResponse.info);
32
+ return new (_fetchnodeshim()).Response(cachedResponse.body, cachedResponse.info);
33
33
  }
34
34
  // Execute the fetch request
35
35
  const response = await fetch(url, init);
@@ -48,7 +48,7 @@ function wrapFetchWithCache(fetch, cache) {
48
48
  return response;
49
49
  }
50
50
  // Return the cached response
51
- return new (_undici()).Response(cachedResponse.body, cachedResponse.info);
51
+ return new (_fetchnodeshim()).Response(cachedResponse.body, cachedResponse.info);
52
52
  } finally{
53
53
  unlock(cacheKey);
54
54
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/api/rest/cache/wrapFetchWithCache.ts"],"sourcesContent":["import { Response, type RequestInfo, type RequestInit } from 'undici';\n\nimport { getRequestCacheKey, getResponseInfo, type ResponseCache } from './ResponseCache';\nimport type { FetchLike } from '../client.types';\n\nconst debug = require('debug')('expo:undici-cache');\n\nexport function wrapFetchWithCache(fetch: FetchLike, cache: ResponseCache): FetchLike {\n return async function cachedFetch(url: RequestInfo, init?: RequestInit) {\n const cacheKey = getRequestCacheKey(url, init);\n const cachedResponse = await cache.get(cacheKey);\n if (cachedResponse) {\n return new Response(cachedResponse.body, cachedResponse.info);\n }\n\n await lock(cacheKey);\n\n try {\n // Retry loading from cache, in case it was stored during the lock\n let cachedResponse = await cache.get(cacheKey);\n if (cachedResponse) {\n return new Response(cachedResponse.body, cachedResponse.info);\n }\n\n // Execute the fetch request\n const response = await fetch(url, init);\n if (!response.ok || !response.body) {\n return response;\n }\n\n // Cache the response\n cachedResponse = await cache.set(cacheKey, {\n body: response.body,\n info: getResponseInfo(response),\n });\n\n // Warn through debug logs that caching failed\n if (!cachedResponse) {\n debug(`Failed to cache response for: ${url}`);\n await cache.remove(cacheKey);\n return response;\n }\n\n // Return the cached response\n return new Response(cachedResponse.body, cachedResponse.info);\n } finally {\n unlock(cacheKey);\n }\n };\n}\n\nconst lockPromiseForKey: Record<string, Promise<any>> = {};\nconst unlockFunctionForKey: Record<string, any> = {};\n\nasync function lock(key: string) {\n if (!lockPromiseForKey[key]) {\n lockPromiseForKey[key] = Promise.resolve();\n }\n\n const takeLockPromise = lockPromiseForKey[key];\n lockPromiseForKey[key] = takeLockPromise.then(\n () =>\n new Promise((fulfill) => {\n unlockFunctionForKey[key] = fulfill;\n })\n );\n\n return takeLockPromise;\n}\n\nfunction unlock(key: string) {\n if (unlockFunctionForKey[key]) {\n unlockFunctionForKey[key]();\n delete unlockFunctionForKey[key];\n }\n}\n"],"names":["wrapFetchWithCache","debug","require","fetch","cache","cachedFetch","url","init","cacheKey","getRequestCacheKey","cachedResponse","get","Response","body","info","lock","response","ok","set","getResponseInfo","remove","unlock","lockPromiseForKey","unlockFunctionForKey","key","Promise","resolve","takeLockPromise","then","fulfill"],"mappings":";;;;+BAOgBA;;;eAAAA;;;;yBAP6C;;;;;;+BAEW;AAGxE,MAAMC,QAAQC,QAAQ,SAAS;AAExB,SAASF,mBAAmBG,KAAgB,EAAEC,KAAoB;IACvE,OAAO,eAAeC,YAAYC,GAAgB,EAAEC,IAAkB;QACpE,MAAMC,WAAWC,IAAAA,iCAAkB,EAACH,KAAKC;QACzC,MAAMG,iBAAiB,MAAMN,MAAMO,GAAG,CAACH;QACvC,IAAIE,gBAAgB;YAClB,OAAO,IAAIE,CAAAA,SAAO,UAAC,CAACF,eAAeG,IAAI,EAAEH,eAAeI,IAAI;QAC9D;QAEA,MAAMC,KAAKP;QAEX,IAAI;YACF,kEAAkE;YAClE,IAAIE,iBAAiB,MAAMN,MAAMO,GAAG,CAACH;YACrC,IAAIE,gBAAgB;gBAClB,OAAO,IAAIE,CAAAA,SAAO,UAAC,CAACF,eAAeG,IAAI,EAAEH,eAAeI,IAAI;YAC9D;YAEA,4BAA4B;YAC5B,MAAME,WAAW,MAAMb,MAAMG,KAAKC;YAClC,IAAI,CAACS,SAASC,EAAE,IAAI,CAACD,SAASH,IAAI,EAAE;gBAClC,OAAOG;YACT;YAEA,qBAAqB;YACrBN,iBAAiB,MAAMN,MAAMc,GAAG,CAACV,UAAU;gBACzCK,MAAMG,SAASH,IAAI;gBACnBC,MAAMK,IAAAA,8BAAe,EAACH;YACxB;YAEA,8CAA8C;YAC9C,IAAI,CAACN,gBAAgB;gBACnBT,MAAM,CAAC,8BAA8B,EAAEK,KAAK;gBAC5C,MAAMF,MAAMgB,MAAM,CAACZ;gBACnB,OAAOQ;YACT;YAEA,6BAA6B;YAC7B,OAAO,IAAIJ,CAAAA,SAAO,UAAC,CAACF,eAAeG,IAAI,EAAEH,eAAeI,IAAI;QAC9D,SAAU;YACRO,OAAOb;QACT;IACF;AACF;AAEA,MAAMc,oBAAkD,CAAC;AACzD,MAAMC,uBAA4C,CAAC;AAEnD,eAAeR,KAAKS,GAAW;IAC7B,IAAI,CAACF,iBAAiB,CAACE,IAAI,EAAE;QAC3BF,iBAAiB,CAACE,IAAI,GAAGC,QAAQC,OAAO;IAC1C;IAEA,MAAMC,kBAAkBL,iBAAiB,CAACE,IAAI;IAC9CF,iBAAiB,CAACE,IAAI,GAAGG,gBAAgBC,IAAI,CAC3C,IACE,IAAIH,QAAQ,CAACI;YACXN,oBAAoB,CAACC,IAAI,GAAGK;QAC9B;IAGJ,OAAOF;AACT;AAEA,SAASN,OAAOG,GAAW;IACzB,IAAID,oBAAoB,CAACC,IAAI,EAAE;QAC7BD,oBAAoB,CAACC,IAAI;QACzB,OAAOD,oBAAoB,CAACC,IAAI;IAClC;AACF"}
1
+ {"version":3,"sources":["../../../../../src/api/rest/cache/wrapFetchWithCache.ts"],"sourcesContent":["import { Response, type RequestInfo, type RequestInit } from 'fetch-nodeshim';\n\nimport { getRequestCacheKey, getResponseInfo, type ResponseCache } from './ResponseCache';\nimport type { FetchLike } from '../client.types';\n\nconst debug = require('debug')('expo:fetch-cache');\n\nexport function wrapFetchWithCache(fetch: FetchLike, cache: ResponseCache): FetchLike {\n return async function cachedFetch(url: RequestInfo, init?: RequestInit) {\n const cacheKey = getRequestCacheKey(url, init);\n const cachedResponse = await cache.get(cacheKey);\n if (cachedResponse) {\n return new Response(cachedResponse.body, cachedResponse.info);\n }\n\n await lock(cacheKey);\n\n try {\n // Retry loading from cache, in case it was stored during the lock\n let cachedResponse = await cache.get(cacheKey);\n if (cachedResponse) {\n return new Response(cachedResponse.body, cachedResponse.info);\n }\n\n // Execute the fetch request\n const response = await fetch(url, init);\n if (!response.ok || !response.body) {\n return response;\n }\n\n // Cache the response\n cachedResponse = await cache.set(cacheKey, {\n body: response.body,\n info: getResponseInfo(response),\n });\n\n // Warn through debug logs that caching failed\n if (!cachedResponse) {\n debug(`Failed to cache response for: ${url}`);\n await cache.remove(cacheKey);\n return response;\n }\n\n // Return the cached response\n return new Response(cachedResponse.body, cachedResponse.info);\n } finally {\n unlock(cacheKey);\n }\n };\n}\n\nconst lockPromiseForKey: Record<string, Promise<any>> = {};\nconst unlockFunctionForKey: Record<string, any> = {};\n\nasync function lock(key: string) {\n if (!lockPromiseForKey[key]) {\n lockPromiseForKey[key] = Promise.resolve();\n }\n\n const takeLockPromise = lockPromiseForKey[key];\n lockPromiseForKey[key] = takeLockPromise.then(\n () =>\n new Promise((fulfill) => {\n unlockFunctionForKey[key] = fulfill;\n })\n );\n\n return takeLockPromise;\n}\n\nfunction unlock(key: string) {\n if (unlockFunctionForKey[key]) {\n unlockFunctionForKey[key]();\n delete unlockFunctionForKey[key];\n }\n}\n"],"names":["wrapFetchWithCache","debug","require","fetch","cache","cachedFetch","url","init","cacheKey","getRequestCacheKey","cachedResponse","get","Response","body","info","lock","response","ok","set","getResponseInfo","remove","unlock","lockPromiseForKey","unlockFunctionForKey","key","Promise","resolve","takeLockPromise","then","fulfill"],"mappings":";;;;+BAOgBA;;;eAAAA;;;;yBAP6C;;;;;;+BAEW;AAGxE,MAAMC,QAAQC,QAAQ,SAAS;AAExB,SAASF,mBAAmBG,KAAgB,EAAEC,KAAoB;IACvE,OAAO,eAAeC,YAAYC,GAAgB,EAAEC,IAAkB;QACpE,MAAMC,WAAWC,IAAAA,iCAAkB,EAACH,KAAKC;QACzC,MAAMG,iBAAiB,MAAMN,MAAMO,GAAG,CAACH;QACvC,IAAIE,gBAAgB;YAClB,OAAO,IAAIE,CAAAA,gBAAO,UAAC,CAACF,eAAeG,IAAI,EAAEH,eAAeI,IAAI;QAC9D;QAEA,MAAMC,KAAKP;QAEX,IAAI;YACF,kEAAkE;YAClE,IAAIE,iBAAiB,MAAMN,MAAMO,GAAG,CAACH;YACrC,IAAIE,gBAAgB;gBAClB,OAAO,IAAIE,CAAAA,gBAAO,UAAC,CAACF,eAAeG,IAAI,EAAEH,eAAeI,IAAI;YAC9D;YAEA,4BAA4B;YAC5B,MAAME,WAAW,MAAMb,MAAMG,KAAKC;YAClC,IAAI,CAACS,SAASC,EAAE,IAAI,CAACD,SAASH,IAAI,EAAE;gBAClC,OAAOG;YACT;YAEA,qBAAqB;YACrBN,iBAAiB,MAAMN,MAAMc,GAAG,CAACV,UAAU;gBACzCK,MAAMG,SAASH,IAAI;gBACnBC,MAAMK,IAAAA,8BAAe,EAACH;YACxB;YAEA,8CAA8C;YAC9C,IAAI,CAACN,gBAAgB;gBACnBT,MAAM,CAAC,8BAA8B,EAAEK,KAAK;gBAC5C,MAAMF,MAAMgB,MAAM,CAACZ;gBACnB,OAAOQ;YACT;YAEA,6BAA6B;YAC7B,OAAO,IAAIJ,CAAAA,gBAAO,UAAC,CAACF,eAAeG,IAAI,EAAEH,eAAeI,IAAI;QAC9D,SAAU;YACRO,OAAOb;QACT;IACF;AACF;AAEA,MAAMc,oBAAkD,CAAC;AACzD,MAAMC,uBAA4C,CAAC;AAEnD,eAAeR,KAAKS,GAAW;IAC7B,IAAI,CAACF,iBAAiB,CAACE,IAAI,EAAE;QAC3BF,iBAAiB,CAACE,IAAI,GAAGC,QAAQC,OAAO;IAC1C;IAEA,MAAMC,kBAAkBL,iBAAiB,CAACE,IAAI;IAC9CF,iBAAiB,CAACE,IAAI,GAAGG,gBAAgBC,IAAI,CAC3C,IACE,IAAIH,QAAQ,CAACI;YACXN,oBAAoB,CAACC,IAAI,GAAGK;QAC9B;IAGJ,OAAOF;AACT;AAEA,SAASN,OAAOG,GAAW;IACzB,IAAID,oBAAoB,CAACC,IAAI,EAAE;QAC7BD,oBAAoB,CAACC,IAAI;QACzB,OAAOD,oBAAoB,CAACC,IAAI;IAClC;AACF"}