@sleeperhq/mini-core 1.0.4 → 1.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.
package/bin/build_mini.js CHANGED
@@ -3,24 +3,66 @@
3
3
  const { spawn } = require('child_process');
4
4
  const os = require('os');
5
5
  const path = require('path');
6
+ const readLine = require('readline');
7
+ const fs = require('fs');
6
8
 
7
9
  const isWindows = os.platform() === 'win32';
8
10
 
9
- const outputFile = 'mini-app';
10
- const zipFile = `${outputFile}.zip`;
11
- const assetsDestPath = {
12
- ios: path.join(outputFile, 'ios'),
13
- android: path.join(outputFile, 'android'),
14
- };
15
- const bundleOutputPath = {
16
- ios: path.join(assetsDestPath["ios"], 'index.bundle'),
17
- android: path.join(assetsDestPath["android"], 'index.bundle'),
18
- };
19
- const sourcemapOutputPath = {
20
- ios: path.join(assetsDestPath["ios"], 'index.bundle.map'),
21
- android: path.join(assetsDestPath["android"], 'index.bundle.map'),
22
- };
23
- const reactNativeCliPath = path.join('node_modules', 'react-native', 'cli.js');
11
+ const getCommands = (projectName) => {
12
+ // Pathing
13
+ const distPath = path.join('dist', projectName);
14
+ const zipFilePath = `${distPath}.zip`;
15
+ const assetsDestPath = {
16
+ ios: path.join(distPath, 'ios'),
17
+ android: path.join(distPath, 'android'),
18
+ };
19
+ const bundleOutputPath = {
20
+ ios: path.join(assetsDestPath["ios"], 'index.bundle'),
21
+ android: path.join(assetsDestPath["android"], 'index.bundle'),
22
+ };
23
+ const sourcemapOutputPath = {
24
+ ios: path.join(assetsDestPath["ios"], 'index.bundle.map'),
25
+ android: path.join(assetsDestPath["android"], 'index.bundle.map'),
26
+ };
27
+ const reactNativeCliPath = path.join('node_modules', 'react-native', 'cli.js');
28
+
29
+ // Commands
30
+ const removeDir = (isWindows ? 'rmdir /s /q ' : 'rm -rf ');
31
+ // TODO (Windows): double check this zip command.
32
+ const zip = isWindows ?
33
+ `powershell Compress-Archive -Path "${distPath}" -DestinationPath "${zipFilePath}"` :
34
+ `cd dist && zip -r "${projectName}.zip" * && cd -`;
35
+ const openFile = isWindows ? `start .` : `open .`;
36
+ const cleanIndex = `${removeDir} "${bundleOutputPath["ios"]}" "${bundleOutputPath["android"]}" "${sourcemapOutputPath["ios"]}" "${sourcemapOutputPath["android"]}"`;
37
+ const cleanAll = `${removeDir} dist node_modules`;
38
+ const cleanBuild = `${removeDir} "${distPath}" "${assetsDestPath["ios"]}" "${assetsDestPath["android"]}"`;
39
+ const install = `yarn install && cd ios && pod install && cd -`;
40
+
41
+ const getBundleCommand = (platform) => {
42
+ return `node "${reactNativeCliPath}" webpack-bundle \
43
+ --entry-file index.tsx \
44
+ --platform ${platform} \
45
+ --dev false \
46
+ --reset-cache \
47
+ --bundle-output "${bundleOutputPath[platform]}" \
48
+ --sourcemap-output "${sourcemapOutputPath[platform]}" \
49
+ --minify false \
50
+ --assets-dest "${assetsDestPath[platform]}"`;
51
+ };
52
+
53
+ // Exposed
54
+ return {
55
+ bundleIOS: getBundleCommand('ios'),
56
+ bundleAndroid: getBundleCommand('android'),
57
+ zip,
58
+ openFile,
59
+ cleanIndex,
60
+ cleanAll,
61
+ cleanBuild,
62
+ zipFilePath,
63
+ install,
64
+ }
65
+ }
24
66
 
25
67
  const spawnProcess = (command, errorMessage) => {
26
68
  return new Promise((resolve) => {
@@ -58,52 +100,85 @@ const printComplete = (message) => {
58
100
  console.log("\033[92m" + message + "\033[0m");
59
101
  };
60
102
 
61
- const getBundleCommand = (platform) => {
62
- return `node ${reactNativeCliPath} webpack-bundle \
63
- --entry-file index.tsx \
64
- --platform ${platform} \
65
- --dev false \
66
- --reset-cache \
67
- --bundle-output ${bundleOutputPath[platform]} \
68
- --sourcemap-output ${sourcemapOutputPath[platform]} \
69
- --minify false \
70
- --assets-dest ${assetsDestPath[platform]}`;
103
+ const getInput = (message, fallback) => {
104
+ const interface = readLine.createInterface({
105
+ input: process.stdin,
106
+ output: process.stdout
107
+ });
108
+
109
+ return new Promise((resolve) => {
110
+ interface.question("\n\033[96m[current: " + fallback + "]\033[0m " + message, (name) => {
111
+ const result = name.trim();
112
+ if (!result) {
113
+ resolve(fallback);
114
+ }
115
+ resolve(name.trim());
116
+ });
117
+ });
71
118
  };
72
119
 
73
- const deleteCommand = (isWindows ? 'rmdir /s /q ' : 'rm -rf ');
74
- const zipCommand = isWindows ?
75
- `powershell Compress-Archive -Path ${outputFile} -DestinationPath ${zipFile}` :
76
- `zip -r ${zipFile} ${outputFile}`;
77
- const openCommand = isWindows ? `start .` : `open .`;
78
- const cleanIndexCommand = `${deleteCommand} ${bundleOutputPath["ios"]} ${bundleOutputPath["android"]} ${sourcemapOutputPath["ios"]} ${sourcemapOutputPath["android"]}`;
79
- const cleanAllCommand = `${deleteCommand} ${outputFile} ${zipFile}`;
80
- const cleanBuildCommand = `${deleteCommand} ${outputFile}`;
120
+ const getProjectName = () => {
121
+ const appJsonPath = 'app.json';
122
+ const appJson = JSON.parse(fs.readFileSync(appJsonPath));
123
+ return appJson.name;
124
+ }
125
+
126
+ const setProjectName = (name) => {
127
+ const podfilePath = path.join('ios', 'Podfile');
128
+ const podfileString = fs.readFileSync(podfilePath).toString();
129
+ const newPodfileString = podfileString.replace(/target '.*'/, `target '${name}'`);
130
+ fs.writeFileSync(podfilePath, newPodfileString);
131
+ }
132
+
133
+ const validateProjectName = (name) => {
134
+ const regex = /^[a-zA-Z0-9_]+$/;
135
+ return regex.test(name);
136
+ }
81
137
 
82
138
  const main = async () => {
139
+ // Enter project name.
140
+ const fallback = getProjectName();
141
+ const projectName = await getInput("Enter project name: ", fallback);
142
+ if (!validateProjectName(projectName)) {
143
+ printError("Invalid project name. Only alphanumeric characters and underscores are allowed.");
144
+ process.exit(1);
145
+ }
146
+
147
+ if (projectName !== fallback) {
148
+ await spawnProcess(`yarn react-native-rename "${projectName}" --skipGitStatusCheck`, "rename command exited with non-zero code");
149
+ setProjectName(projectName);
150
+ }
151
+
152
+ const commands = getCommands(projectName);
153
+
83
154
  // Clean build folders
84
- await spawnProcess(cleanAllCommand, "clean command exited with non-zero code");
155
+ await spawnProcess(commands.cleanAll, "clean command exited with non-zero code");
156
+
157
+ // Run yarn + pod install
158
+ await spawnProcess(commands.install, "clean command exited with non-zero code");
85
159
 
86
160
  // Build iOS
87
161
  printInfo("Building iOS...");
88
- await spawnProcess(getBundleCommand("ios"), "webpack-bundle ios command exited with non-zero code");
162
+ await spawnProcess(commands.bundleIOS, "webpack-bundle ios command exited with non-zero code");
89
163
  printComplete("iOS build complete.");
90
164
 
91
165
  // Build Android
92
166
  printInfo("Building Android...");
93
- await spawnProcess(getBundleCommand("android"), "webpack-bundle android command exited with non-zero code");
167
+ await spawnProcess(commands.bundleAndroid, "webpack-bundle android command exited with non-zero code");
94
168
  printComplete("Android build complete.");
95
169
 
96
170
  // Create Zip Archive
97
171
  printInfo("Creating zip archive...");
98
- await spawnProcess(cleanIndexCommand, "clean command exited with non-zero code");
99
- await spawnProcess(zipCommand, "zip command exited with non-zero code");
100
- printComplete("Zip archive created successfully at mini-app.zip.");
172
+ await spawnProcess(commands.cleanIndex, "clean command exited with non-zero code");
173
+ await spawnProcess(commands.zip, "zip command exited with non-zero code");
174
+ printComplete(`Zip archive created successfully at ${commands.zipFilePath}`);
101
175
 
102
176
  // Clean build folders
103
- await spawnProcess(cleanBuildCommand, "clean command exited with non-zero code");
177
+ await spawnProcess(commands.cleanBuild, "clean command exited with non-zero code");
104
178
 
105
179
  // Open output folder
106
- spawnProcess(openCommand);
180
+ await spawnProcess(commands.openFile);
181
+ process.exit(0);
107
182
  };
108
183
 
109
184
  main();
@@ -1,13 +1,5 @@
1
- import { User, Navigation, League, LeaguesMap, RostersInLeagueMap } from './types';
1
+ import { User, Navigation, League, LeaguesMap, RostersInLeagueMap, UserMap, MatchupsInLeagueMap } from './types';
2
2
  import SleeperActions from './sleeper_actions';
3
- type SleeperContextConfig = {
4
- user: User;
5
- navigation: Navigation;
6
- league: League;
7
- userLeagueList: string[];
8
- leaguesMap: LeaguesMap;
9
- rostersInLeagueMap: RostersInLeagueMap;
10
- };
11
3
  declare class SleeperContext {
12
4
  user: User;
13
5
  navigation: Navigation;
@@ -15,7 +7,9 @@ declare class SleeperContext {
15
7
  leaguesMap: LeaguesMap;
16
8
  userLeagueList: string[];
17
9
  rostersInLeagueMap: RostersInLeagueMap;
10
+ userMap: UserMap;
11
+ matchupsInLeagueMap: MatchupsInLeagueMap;
18
12
  actions: SleeperActions;
19
- constructor(config: SleeperContextConfig);
13
+ constructor();
20
14
  }
21
15
  export default SleeperContext;
@@ -1,5 +1,6 @@
1
1
  type SocketMessage = {
2
2
  _ip?: string;
3
+ _name?: string;
3
4
  _webpack?: string;
4
5
  _contextGet?: string;
5
6
  };
@@ -1,8 +1,8 @@
1
1
  import { NAVIGATION_ID, NAVIGATION_TYPE } from './redux/native_nav/constants.d';
2
- import { League, Roster } from './shared/graphql.d';
3
- export * from './shared/graphql.d';
2
+ import { League, Roster, User, MatchupLeg } from './shared/graphql.d';
4
3
  export type NavigationType = typeof NAVIGATION_TYPE[keyof typeof NAVIGATION_TYPE];
5
4
  export type NavigationTypeId = typeof NAVIGATION_ID[keyof typeof NAVIGATION_ID];
5
+ export * from './shared/graphql.d';
6
6
  export type Navigation = {
7
7
  selectedNavType: NavigationType;
8
8
  selectedNavTypeId: NavigationTypeId;
@@ -10,6 +10,11 @@ export type Navigation = {
10
10
  };
11
11
  export type LeagueId = string;
12
12
  export type RosterId = string;
13
+ export type UserId = string;
14
+ export type MatchupWeek = number;
13
15
  export type LeaguesMap = Record<LeagueId, League>;
14
16
  export type RostersMap = Record<RosterId, Roster>;
15
17
  export type RostersInLeagueMap = Record<LeagueId, RostersMap>;
18
+ export type UserMap = Record<UserId, User>;
19
+ export type MathchupWeekMap = Record<MatchupWeek, MatchupLeg>;
20
+ export type MatchupsInLeagueMap = Record<LeagueId, MathchupWeekMap>;
@@ -71,6 +71,24 @@ export type League = {
71
71
  status?: Maybe<Scalars['String']>;
72
72
  total_rosters?: Maybe<Scalars['Int']>;
73
73
  };
74
+ export type MatchupLeg = {
75
+ __typename?: 'MatchupLeg';
76
+ bans?: Maybe<Scalars['Map']>;
77
+ custom_points?: Maybe<Scalars['Float']>;
78
+ league_id?: Maybe<Scalars['Snowflake']>;
79
+ leg?: Maybe<Scalars['Int']>;
80
+ matchup_id?: Maybe<Scalars['Int']>;
81
+ max_points?: Maybe<Scalars['Float']>;
82
+ picks?: Maybe<Scalars['Map']>;
83
+ player_map?: Maybe<Scalars['Map']>;
84
+ players?: Maybe<Scalars['Set']>;
85
+ points?: Maybe<Scalars['Float']>;
86
+ proj_points?: Maybe<Scalars['Float']>;
87
+ roster_id?: Maybe<Scalars['Int']>;
88
+ round?: Maybe<Scalars['Int']>;
89
+ starters?: Maybe<Scalars['List']>;
90
+ starters_games?: Maybe<Scalars['Map']>;
91
+ };
74
92
  export type Roster = {
75
93
  __typename?: 'Roster';
76
94
  co_owners?: Maybe<Scalars['SnowflakeSet']>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sleeperhq/mini-core",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Core library frameworks for developing Sleeper Mini Apps.",
5
5
  "main": "index.ts",
6
6
  "types": "index.d.ts",
@@ -40,6 +40,7 @@
40
40
  "react": "17.0.2",
41
41
  "react-native": "0.66.5",
42
42
  "react-native-linear-gradient": "2.5.6",
43
+ "react-native-rename": "^3.2.12",
43
44
  "react-native-svg": "13.7.0",
44
45
  "react-native-tcp-socket": "^6.0.6"
45
46
  },
@@ -1,10 +1,10 @@
1
1
  import React, {useEffect, useRef, useState} from 'react';
2
2
  import {Platform} from 'react-native';
3
3
  import {Config, SocketMessage} from '../types';
4
- import {ScriptManager, Federated} from '@callstack/repack/client';
4
+ import { ScriptLocatorResolver, ScriptManager, Federated } from '@callstack/repack/client';
5
5
  import NetInfo from '@react-native-community/netinfo';
6
6
  import TcpSocket from 'react-native-tcp-socket';
7
- import axios from 'axios';
7
+ import { fetchMainVersionMap, getMainUrl } from './url_resolver';
8
8
 
9
9
  let config: Config;
10
10
  const RETRY_TIMER = 5000;
@@ -14,6 +14,15 @@ const DevServer = props => {
14
14
  const partialMessage = useRef('');
15
15
  const _retryTimer = useRef<NodeJS.Timeout>();
16
16
 
17
+ const [data, setData] = useState({
18
+ platform: '',
19
+ binaryVersion: '',
20
+ dist: '',
21
+ isStaging: false,
22
+ });
23
+ const _dataRef = useRef<typeof data>();
24
+ const _versionMap = useRef<Record<string, string>>();
25
+
17
26
  const _onConnected = async (value: boolean) => {
18
27
  props.onConnected(value);
19
28
  };
@@ -21,7 +30,7 @@ const DevServer = props => {
21
30
  const onSocket = (handler) => msg => {
22
31
  let json;
23
32
  try {
24
- json = JSON.parse(msg);
33
+ json = JSON.parse(msg);
25
34
  } catch(e) {
26
35
  partialMessage.current += msg;
27
36
  }
@@ -35,6 +44,17 @@ const DevServer = props => {
35
44
  }
36
45
  }
37
46
 
47
+ // Set connection data
48
+ if (json._platform || json._binaryVersion || json._dist || json._isStaging) {
49
+ console.log("[Sleeper] Received context data:", json._platform, json._binaryVersion, json._dist, json._isStaging);
50
+ setData({
51
+ platform: json._platform,
52
+ binaryVersion: json._binaryVersion,
53
+ dist: json._dist,
54
+ isStaging: json._isStaging,
55
+ });
56
+ }
57
+
38
58
  // We should have a context object now
39
59
  const context = new Proxy(json, handler);
40
60
  props.onContextChanged(context);
@@ -44,7 +64,7 @@ const DevServer = props => {
44
64
  const message: SocketMessage = {_contextGet: propertyPath};
45
65
  const json = JSON.stringify(message);
46
66
  try {
47
- socket?.write(json);
67
+ socket?.write(json + '\n');
48
68
  } catch (e) {
49
69
  console.log("[Sleeper] Failed to send context request: ", e);
50
70
  }
@@ -53,40 +73,53 @@ const DevServer = props => {
53
73
  const proxyHandler = (socket) => {
54
74
  return {
55
75
  get: (target, property) => {
56
- if (property in target && target[property] !== null) {
57
- const value = target[property];
58
-
59
- if (typeof value !== 'object' || value._isProxy) {
60
- return value;
61
- }
76
+ let value = Reflect.get(target, property);
62
77
 
78
+ // Check if we need to add a proxy to this object
79
+ if (!!value && typeof value === 'object' && !value._isProxy && value._isProxyInternal) {
80
+ const isLeaf = !value._continueProxy;
63
81
  // Adding proxies to objects
64
- // Intentionally only going 1 level deep
65
- const handler = proxyHandlerLeaf(socket, property);
66
- target[property] = new Proxy(value, handler);
67
- target[property]._isProxy = true;
68
-
69
- return target[property];
82
+ const handler = proxyHandlerChild(socket, property, isLeaf);
83
+ const proxiedValue = new Proxy(value, handler);
84
+ Reflect.set(target, property, proxiedValue);
85
+ value = proxiedValue;
70
86
  }
71
87
 
72
- // This property is not in the context object yet
73
- sendContextRequest(socket, property);
74
- return null;
88
+ return value;
75
89
  }
76
90
  };
77
91
  }
78
92
 
79
- // This handler is for leaf nodes only
80
- const proxyHandlerLeaf = (socket, path) => {
93
+ const proxyHandlerChild = (socket, path, isLeaf) => {
81
94
  return {
82
95
  get: (target, property) => {
83
- if (target[property] !== undefined) { // Only checking undefined properites for leaves
84
- return target[property];
96
+ // Check if a proxy was already added to this object
97
+ if (property === '_isProxy') {
98
+ return true;
99
+ }
100
+
101
+ const value = Reflect.get(target, property);
102
+ const fullPropertyPath = `${path}.${property}`;
103
+
104
+ // If the value is undefined, we need to request it from the server
105
+ if (value === undefined && isLeaf) {
106
+ sendContextRequest(socket, fullPropertyPath);
85
107
  }
86
108
 
87
- const fullProp = path + '.' + property;
88
- sendContextRequest(socket, fullProp);
89
- return null;
109
+ // Check if we need to add a second layer proxy to this object
110
+ if (!isLeaf && !value?._isProxy) {
111
+ const nextLeaf = true; // Currently we only support 2 layers of proxies
112
+ // Adding proxies to objects
113
+ // These proxies aren't stored in the context object so we will regenerate them every time
114
+ const handler = proxyHandlerChild(socket, fullPropertyPath, nextLeaf);
115
+ if (value === undefined) {
116
+ return new Proxy({}, handler);
117
+ } else {
118
+ return new Proxy(value, handler);
119
+ }
120
+ }
121
+
122
+ return value;
90
123
  }
91
124
  };
92
125
  }
@@ -102,16 +135,20 @@ const DevServer = props => {
102
135
  }
103
136
 
104
137
  connection.current = TcpSocket.createConnection({
105
- port: config.remoteSocketPort,
138
+ port: config.remoteSocketPort || 9092,
106
139
  host: config.remoteIP,
107
140
  localAddress: ipAddress,
108
141
  reuseAddress: true,
109
142
  }, () => {
110
143
  // When we establish a connection, send the IP address to the server
111
- const message: SocketMessage = { _ip: ipAddress };
144
+ const message: SocketMessage = {
145
+ _ip: ipAddress,
146
+ _name: config.name,
147
+ };
112
148
  const json = JSON.stringify(message);
149
+ console.log('[Sleeper] Send IP address: ', ipAddress, config.name);
113
150
  try {
114
- connection.current?.write(json, undefined, (error) => {
151
+ connection.current?.write(json + '\n', "utf8", (error) => {
115
152
  if (error) {
116
153
  return stopSocket();
117
154
  }
@@ -154,12 +191,61 @@ const DevServer = props => {
154
191
  }
155
192
  }
156
193
 
194
+ const _waitForInitialization = () => {
195
+ return new Promise<void>((resolve) => {
196
+ (function checkData() {
197
+ let isInitialized = !!_dataRef.current?.dist;
198
+
199
+ // Non-staging builds also require a version map to be defined.
200
+ if (!_dataRef.current?.isStaging) {
201
+ isInitialized = !!_versionMap.current;
202
+ }
203
+
204
+ if (isInitialized) return resolve();
205
+ setTimeout(checkData, 1000);
206
+ })();
207
+ });
208
+ };
209
+
210
+ const _resolveRemoteChunk: ScriptLocatorResolver = async (scriptId: string, caller: string) => {
211
+ await _waitForInitialization();
212
+
213
+ const bundleName = !caller ? `${scriptId}.container.bundle` : `${scriptId}.chunk.bundle`;
214
+
215
+ // Try to resolve URL based on scriptId and caller
216
+ const url = getMainUrl(scriptId, caller, {
217
+ platform: _dataRef.current?.platform,
218
+ bundleVersion: _versionMap.current?.[bundleName],
219
+ binaryVersion: _dataRef.current?.binaryVersion,
220
+ dist: _dataRef.current?.dist,
221
+ isStaging: _dataRef.current?.isStaging,
222
+ remoteIP: config.remoteIP,
223
+ dev: config.dev,
224
+ });
225
+ const query = config.dev ? {platform: Platform.OS} : undefined;
226
+
227
+ console.log('[Sleeper] load script:', scriptId, caller, url);
228
+ return {url, query};
229
+ }
230
+
231
+ const _fetchVersionMap = async (platform, binaryVersion, dist) => {
232
+ _versionMap.current = await fetchMainVersionMap(platform, binaryVersion, dist);
233
+ }
234
+
235
+ useEffect(() => {
236
+ _dataRef.current = data;
237
+ if (!data.platform || !data.binaryVersion || !data.dist) return;
238
+
239
+ _fetchVersionMap(data.platform, data.binaryVersion, data.dist);
240
+ }, [data.platform, data.binaryVersion, data.dist, data.isStaging]);
241
+
157
242
  useEffect(() => {
158
243
  if (!config) {
159
244
  console.error('[Sleeper] No config file specified. Please make sure you call DevServer.init() early in the app lifecycle.');
160
245
  return;
161
246
  }
162
247
 
248
+ ScriptManager.shared.addResolver(_resolveRemoteChunk.bind(this));
163
249
  startSocket();
164
250
 
165
251
  return () => {
@@ -172,34 +258,6 @@ const DevServer = props => {
172
258
 
173
259
  DevServer.init = (_config: Config) => {
174
260
  config = _config;
175
-
176
- const remoteBundleHost = config.release ? config.remoteIP : 'localhost';
177
- const remoteBundlePort = config.release ? config.remoteBundlePort : 8081;
178
-
179
- ScriptManager.shared.addResolver(async (scriptId, caller) => {
180
- const extension =
181
- scriptId === 'sleeper' ? '.container.bundle' : '.chunk.bundle';
182
- const resolveURL = Federated.createURLResolver({
183
- containers: {
184
- sleeper: `http://${remoteBundleHost}:${remoteBundlePort}/[name]${extension}`,
185
- },
186
- });
187
-
188
- // Try to resolve URL based on scriptId and caller
189
- const url = resolveURL(scriptId, caller);
190
- const query = config.release ? undefined : {platform: Platform.OS};
191
-
192
- const response = await axios
193
- .get(url + '?' + new URLSearchParams(query), {method: 'HEAD'})
194
- .catch(() => ({
195
- status: 404,
196
- }));
197
-
198
- console.log('[Sleeper] load script:', scriptId, caller);
199
- if (response?.status === 200) {
200
- return {url, query};
201
- }
202
- });
203
- };
261
+ }
204
262
 
205
263
  export default DevServer;
@@ -0,0 +1,71 @@
1
+ import axios, { AxiosPromise, AxiosRequestConfig } from 'axios';
2
+ import {
3
+ getWebpackContext,
4
+ Federated,
5
+ } from '@callstack/repack/client';
6
+ import { Platform } from 'react-native';
7
+
8
+ export type MainPlatformData = {
9
+ // Received from socket
10
+ platform?: string,
11
+ bundleVersion?: string,
12
+ binaryVersion?: string,
13
+ dist?: string,
14
+ isStaging?: boolean,
15
+
16
+ // From app.json
17
+ remoteIP: string,
18
+ dev?: boolean,
19
+ }
20
+
21
+ const PROD_CDN = "https://sleepercdn.com/bundles/";
22
+ const TEST_CDN = "https://test.sleepercdn.com/bundles/";
23
+
24
+ // Query a list of urls, and grab the first one that returns a 200 response.
25
+ const _fetchFirstAvailable = (config: AxiosRequestConfig, ...urls: string[]) => {
26
+ return new Promise<any>((resolve) => {
27
+ (async function fetchMap() {
28
+ const promises = urls.map((url) => axios.get(url, config).catch(() => null));
29
+ const responses = await Promise.all<AxiosPromise>(promises);
30
+
31
+ const successfulResponse = responses.find((response) => response?.status === 200);
32
+ if (config?.method === 'HEAD') {
33
+ successfulResponse ? resolve({ url: successfulResponse.config.url }) : setTimeout(fetchMap, 5000);
34
+ } else {
35
+ successfulResponse ? resolve(successfulResponse.data) : setTimeout(fetchMap, 5000);
36
+ }
37
+ })();
38
+ });
39
+ }
40
+
41
+ export const fetchMainVersionMap = (platform: string, binaryVersion: string, dist: string) => {
42
+ const baseUrl = `${PROD_CDN}/version_maps/${platform}/${binaryVersion}/${dist}.json`;
43
+ const codepushUrl = `${PROD_CDN}/version_maps/${platform}/codepush/${dist}.json`;
44
+
45
+ return _fetchFirstAvailable({}, baseUrl, codepushUrl);
46
+ }
47
+
48
+ export const getMainUrl = (scriptId: string, caller: string, config: MainPlatformData) => {
49
+ let sleeper: string;
50
+
51
+ if (config.dev) {
52
+ sleeper = `http://${config.remoteIP}:8081/`;
53
+ } else if (config.isStaging) {
54
+ if (config.dist === '0') {
55
+ sleeper = `${TEST_CDN}/${config.platform}/${config.binaryVersion}/${config.dist}/`;
56
+ } else {
57
+ sleeper = `${TEST_CDN}/${config.platform}/codepush/${config.dist}/`;
58
+ }
59
+ } else {
60
+ sleeper = `${PROD_CDN}/data/${Platform.OS}/${config.bundleVersion}/`;
61
+ }
62
+
63
+ const resolveURL = Federated.createURLResolver({
64
+ containers: {
65
+ sleeper: `${sleeper}[name][ext]`,
66
+ }
67
+ });
68
+ const bundleLocation = resolveURL(scriptId, caller);
69
+
70
+ return bundleLocation;
71
+ }
@@ -6,8 +6,6 @@ export type Config = {
6
6
  name: string,
7
7
  displayName: string,
8
8
  remoteIP: string,
9
- localSocketPort: number,
10
- remoteSocketPort: number,
11
- remoteBundlePort: number,
12
- release: boolean,
9
+ remoteSocketPort?: number,
10
+ dev?: boolean,
13
11
  };