@sleeperhq/mini-core 1.0.3 → 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.
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const readLine = require('readline');
7
+ const fs = require('fs');
8
+
9
+ const isWindows = os.platform() === 'win32';
10
+
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
+ }
66
+
67
+ const spawnProcess = (command, errorMessage) => {
68
+ return new Promise((resolve) => {
69
+ const child = isWindows
70
+ ? spawn('cmd.exe', ['/c', command])
71
+ : spawn(command, { shell: true });
72
+
73
+ child.stdout.on('data', (data) => {
74
+ process.stdout.write(data); // output the data to the console
75
+ });
76
+
77
+ child.stderr.on('data', (data) => {
78
+ process.stderr.write(data); // output the error to the console
79
+ });
80
+
81
+ child.on('close', (code) => {
82
+ if (code !== 0) {
83
+ printError(errorMessage || `command exited with non-zero code: ${code}`);
84
+ process.exit(1);
85
+ }
86
+ resolve();
87
+ });
88
+ });
89
+ };
90
+
91
+ const printError = (error) => {
92
+ console.error("\n\033[91m" + error + "\033[0m");
93
+ };
94
+
95
+ const printInfo = (message) => {
96
+ console.log("\n\033[96m" + message + "\033[0m");
97
+ };
98
+
99
+ const printComplete = (message) => {
100
+ console.log("\033[92m" + message + "\033[0m");
101
+ };
102
+
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
+ });
118
+ };
119
+
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
+ }
137
+
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
+
154
+ // Clean build folders
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");
159
+
160
+ // Build iOS
161
+ printInfo("Building iOS...");
162
+ await spawnProcess(commands.bundleIOS, "webpack-bundle ios command exited with non-zero code");
163
+ printComplete("iOS build complete.");
164
+
165
+ // Build Android
166
+ printInfo("Building Android...");
167
+ await spawnProcess(commands.bundleAndroid, "webpack-bundle android command exited with non-zero code");
168
+ printComplete("Android build complete.");
169
+
170
+ // Create Zip Archive
171
+ printInfo("Creating zip archive...");
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}`);
175
+
176
+ // Clean build folders
177
+ await spawnProcess(commands.cleanBuild, "clean command exited with non-zero code");
178
+
179
+ // Open output folder
180
+ await spawnProcess(commands.openFile);
181
+ process.exit(0);
182
+ };
183
+
184
+ main();
@@ -1,37 +1,37 @@
1
- /// <reference types="react" />
2
- import { GestureResponderEvent } from 'react-native';
3
- export interface ButtonProps {
4
- height?: number;
5
- gradient?: (string | number)[];
6
- start?: {
7
- x: number;
8
- y: number;
9
- };
10
- end?: {
11
- x: number;
12
- y: number;
13
- };
14
- disable?: boolean;
15
- onPress?: (event: GestureResponderEvent) => void;
16
- text?: string;
17
- }
18
- declare const Button: {
19
- (props: ButtonProps): JSX.Element;
20
- defaultProps: {
21
- height: number;
22
- shadowHeight: number;
23
- gradient: string[];
24
- type: string;
25
- size: string;
26
- isForSmallScreen: boolean;
27
- start: {
28
- x: number;
29
- y: number;
30
- };
31
- end: {
32
- x: number;
33
- y: number;
34
- };
35
- };
36
- };
37
- export default Button;
1
+ /// <reference types="react" />
2
+ import { GestureResponderEvent } from 'react-native';
3
+ export interface ButtonProps {
4
+ height?: number;
5
+ gradient?: (string | number)[];
6
+ start?: {
7
+ x: number;
8
+ y: number;
9
+ };
10
+ end?: {
11
+ x: number;
12
+ y: number;
13
+ };
14
+ disable?: boolean;
15
+ onPress?: (event: GestureResponderEvent) => void;
16
+ text?: string;
17
+ }
18
+ declare const Button: {
19
+ (props: ButtonProps): JSX.Element;
20
+ defaultProps: {
21
+ height: number;
22
+ shadowHeight: number;
23
+ gradient: string[];
24
+ type: string;
25
+ size: string;
26
+ isForSmallScreen: boolean;
27
+ start: {
28
+ x: number;
29
+ y: number;
30
+ };
31
+ end: {
32
+ x: number;
33
+ y: number;
34
+ };
35
+ };
36
+ };
37
+ export default Button;
@@ -1,3 +1,4 @@
1
- export * from './types';
2
- declare const _default: any;
3
- export default _default;
1
+ export * from './types';
2
+ export * from './sleeper_message';
3
+ declare const _default: any;
4
+ export default _default;
@@ -1,9 +1,9 @@
1
- import { ColorValue, ViewStyle } from 'react-native';
2
- import SleeperJersey from 'components/ui/jersey';
3
- export interface JerseyProps {
4
- style: ViewStyle;
5
- sport: 'nfl' | 'nba' | 'cbb' | 'cfb' | 'mlb';
6
- number: string;
7
- fill: ColorValue;
8
- }
9
- export default SleeperJersey;
1
+ import { ColorValue, ViewStyle } from 'react-native';
2
+ import SleeperJersey from 'components/ui/jersey';
3
+ export interface JerseyProps {
4
+ style: ViewStyle;
5
+ sport: 'nfl' | 'nba' | 'cbb' | 'cfb' | 'mlb';
6
+ number: string;
7
+ fill: ColorValue;
8
+ }
9
+ export default SleeperJersey;
@@ -1,5 +1,5 @@
1
- import { NavigationType, NavigationTypeId } from './types';
2
- declare class SleeperActions {
3
- navigate: (navType: NavigationType, navTypeId: NavigationTypeId) => void;
4
- }
5
- export default SleeperActions;
1
+ import { NavigationType, NavigationTypeId } from './types';
2
+ declare class SleeperActions {
3
+ navigate: (navType: NavigationType, navTypeId: NavigationTypeId) => void;
4
+ }
5
+ export default SleeperActions;
@@ -1,10 +1,15 @@
1
- import { User, Navigation, League } from './types';
2
- import SleeperActions from './sleeper_actions';
3
- declare class SleeperContext {
4
- user: User;
5
- navigation: Navigation;
6
- league: League;
7
- actions: SleeperActions;
8
- constructor(user: User, navigation: Navigation, league: League);
9
- }
10
- export default SleeperContext;
1
+ import { User, Navigation, League, LeaguesMap, RostersInLeagueMap, UserMap, MatchupsInLeagueMap } from './types';
2
+ import SleeperActions from './sleeper_actions';
3
+ declare class SleeperContext {
4
+ user: User;
5
+ navigation: Navigation;
6
+ league: League;
7
+ leaguesMap: LeaguesMap;
8
+ userLeagueList: string[];
9
+ rostersInLeagueMap: RostersInLeagueMap;
10
+ userMap: UserMap;
11
+ matchupsInLeagueMap: MatchupsInLeagueMap;
12
+ actions: SleeperActions;
13
+ constructor();
14
+ }
15
+ export default SleeperContext;
@@ -0,0 +1,7 @@
1
+ type SocketMessage = {
2
+ _ip?: string;
3
+ _name?: string;
4
+ _webpack?: string;
5
+ _contextGet?: string;
6
+ };
7
+ export default SocketMessage;
@@ -1,13 +1,13 @@
1
- import { PureComponent } from 'react';
2
- import { TextProps } from 'react-native';
3
- declare type Props = {
4
- color?: string;
5
- inheritStyles?: boolean;
6
- screenShrink?: number;
7
- } & TextProps;
8
- export declare type AppTextProps = Props;
9
- export default class AppText extends PureComponent<Props> {
10
- private getStyles;
11
- render(): JSX.Element;
12
- }
13
- export {};
1
+ import { PureComponent } from 'react';
2
+ import { TextProps } from 'react-native';
3
+ type Props = {
4
+ color?: string;
5
+ inheritStyles?: boolean;
6
+ screenShrink?: number;
7
+ } & TextProps;
8
+ export type AppTextProps = Props;
9
+ export default class AppText extends PureComponent<Props> {
10
+ private getStyles;
11
+ render(): JSX.Element;
12
+ }
13
+ export {};
@@ -1,9 +1,20 @@
1
- import { NAVIGATION_ID, NAVIGATION_TYPE } from './redux/native_nav/constants.d';
2
- export declare type NavigationType = typeof NAVIGATION_TYPE[keyof typeof NAVIGATION_TYPE];
3
- export declare type NavigationTypeId = typeof NAVIGATION_ID[keyof typeof NAVIGATION_ID];
4
- export * from './shared/graphql.d';
5
- export declare type Navigation = {
6
- selectedNavType: NavigationType;
7
- selectedNavTypeId: NavigationTypeId;
8
- selectedNavData: {};
9
- };
1
+ import { NAVIGATION_ID, NAVIGATION_TYPE } from './redux/native_nav/constants.d';
2
+ import { League, Roster, User, MatchupLeg } from './shared/graphql.d';
3
+ export type NavigationType = typeof NAVIGATION_TYPE[keyof typeof NAVIGATION_TYPE];
4
+ export type NavigationTypeId = typeof NAVIGATION_ID[keyof typeof NAVIGATION_ID];
5
+ export * from './shared/graphql.d';
6
+ export type Navigation = {
7
+ selectedNavType: NavigationType;
8
+ selectedNavTypeId: NavigationTypeId;
9
+ selectedNavData: {};
10
+ };
11
+ export type LeagueId = string;
12
+ export type RosterId = string;
13
+ export type UserId = string;
14
+ export type MatchupWeek = number;
15
+ export type LeaguesMap = Record<LeagueId, League>;
16
+ export type RostersMap = Record<RosterId, Roster>;
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>;
@@ -1,23 +1,23 @@
1
- export declare const NAVIGATION_TYPE: {
2
- readonly DM: "DM";
3
- readonly INBOX: "INBOX";
4
- readonly MY_FEED: "MY_FEED";
5
- readonly FRIENDS: "FRIENDS";
6
- readonly LEAGUE: "LEAGUE";
7
- readonly ASYNC_SPORT: "ASYNC_SPORT";
8
- readonly CREATE_LEAGUE: "CREATE_LEAGUE";
9
- readonly LEAGUE_SYNC: "LEAGUE_SYNC";
10
- readonly DRAFTBOARDS: "DRAFTBOARDS";
11
- readonly SOLO_OVER_UNDER: "SOLO_OVER_UNDER";
12
- readonly CHANNEL: "CHANNEL";
13
- readonly MANAGE_CHANNELS: "MANAGE_CHANNELS";
14
- };
15
- export declare const NAVIGATION_ID: {
16
- readonly DRAFTBOARDS: 1;
17
- readonly CREATE_DM: 2;
18
- readonly DM_SCREEN: 3;
19
- readonly CREATE_LEAGUE: 4;
20
- readonly MY_FEED: 5;
21
- readonly MENTIONS: 6;
22
- readonly FRIENDS: 7;
23
- };
1
+ export declare const NAVIGATION_TYPE: {
2
+ readonly DM: "DM";
3
+ readonly INBOX: "INBOX";
4
+ readonly MY_FEED: "MY_FEED";
5
+ readonly FRIENDS: "FRIENDS";
6
+ readonly LEAGUE: "LEAGUE";
7
+ readonly ASYNC_SPORT: "ASYNC_SPORT";
8
+ readonly CREATE_LEAGUE: "CREATE_LEAGUE";
9
+ readonly LEAGUE_SYNC: "LEAGUE_SYNC";
10
+ readonly DRAFTBOARDS: "DRAFTBOARDS";
11
+ readonly SOLO_OVER_UNDER: "SOLO_OVER_UNDER";
12
+ readonly CHANNEL: "CHANNEL";
13
+ readonly MANAGE_CHANNELS: "MANAGE_CHANNELS";
14
+ };
15
+ export declare const NAVIGATION_ID: {
16
+ readonly DRAFTBOARDS: 1;
17
+ readonly CREATE_DM: 2;
18
+ readonly DM_SCREEN: 3;
19
+ readonly CREATE_LEAGUE: 4;
20
+ readonly MY_FEED: 5;
21
+ readonly MENTIONS: 6;
22
+ readonly FRIENDS: 7;
23
+ };
@@ -1,98 +1,131 @@
1
- export declare type Maybe<T> = T | null;
2
- export declare type InputMaybe<T> = Maybe<T>;
3
- export declare type Exact<T extends {
4
- [key: string]: unknown;
5
- }> = {
6
- [K in keyof T]: T[K];
7
- };
8
- export declare type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
9
- [SubKey in K]?: Maybe<T[SubKey]>;
10
- };
11
- export declare type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
12
- [SubKey in K]: Maybe<T[SubKey]>;
13
- };
14
- /** All built-in and custom scalars, mapped to their actual values */
15
- export declare type Scalars = {
16
- ID: string;
17
- String: string;
18
- Boolean: boolean;
19
- Int: number;
20
- Float: number;
21
- /** JSON */
22
- Json: {
23
- [key: string]: any;
24
- };
25
- /** Lists */
26
- List: string[];
27
- /** Map / Dictionary */
28
- Map: {
29
- [key: string]: any;
30
- };
31
- MapWithSnowflakeKey: {
32
- [key: string]: any;
33
- };
34
- /** Sets */
35
- Set: string[];
36
- /** Snowflake ID */
37
- Snowflake: string;
38
- SnowflakeList: string[];
39
- SnowflakeSet: string[];
40
- };
41
- export declare type League = {
42
- __typename?: 'League';
43
- avatar?: Maybe<Scalars['String']>;
44
- company_id?: Maybe<Scalars['Snowflake']>;
45
- display_order?: Maybe<Scalars['Int']>;
46
- draft_id?: Maybe<Scalars['Snowflake']>;
47
- group_id?: Maybe<Scalars['Snowflake']>;
48
- last_author_avatar?: Maybe<Scalars['String']>;
49
- last_author_display_name?: Maybe<Scalars['String']>;
50
- last_author_id?: Maybe<Scalars['Snowflake']>;
51
- last_author_is_bot?: Maybe<Scalars['Boolean']>;
52
- last_message_attachment?: Maybe<Scalars['Json']>;
53
- last_message_id?: Maybe<Scalars['Snowflake']>;
54
- last_message_text?: Maybe<Scalars['String']>;
55
- last_message_text_map?: Maybe<Scalars['Json']>;
56
- last_message_time?: Maybe<Scalars['Int']>;
57
- last_pinned_message_id?: Maybe<Scalars['Snowflake']>;
58
- last_read_id?: Maybe<Scalars['Snowflake']>;
59
- last_transaction_id?: Maybe<Scalars['Snowflake']>;
60
- league_id?: Maybe<Scalars['Snowflake']>;
61
- matchup_legs?: Maybe<Scalars['List']>;
62
- metadata?: Maybe<Scalars['Map']>;
63
- name?: Maybe<Scalars['String']>;
64
- previous_league_id?: Maybe<Scalars['Snowflake']>;
65
- roster_positions?: Maybe<Scalars['List']>;
66
- scoring_settings?: Maybe<Scalars['Map']>;
67
- season?: Maybe<Scalars['String']>;
68
- season_type?: Maybe<Scalars['String']>;
69
- settings?: Maybe<Scalars['Map']>;
70
- sport?: Maybe<Scalars['String']>;
71
- status?: Maybe<Scalars['String']>;
72
- total_rosters?: Maybe<Scalars['Int']>;
73
- };
74
- export declare type User = {
75
- __typename?: 'User';
76
- async_bundles?: Maybe<Scalars['List']>;
77
- avatar?: Maybe<Scalars['String']>;
78
- cookies?: Maybe<Scalars['Int']>;
79
- created?: Maybe<Scalars['Int']>;
80
- currencies?: Maybe<Scalars['Map']>;
81
- data_updated?: Maybe<Scalars['Map']>;
82
- deleted?: Maybe<Scalars['Int']>;
83
- display_name?: Maybe<Scalars['String']>;
84
- email?: Maybe<Scalars['String']>;
85
- is_bot?: Maybe<Scalars['Boolean']>;
86
- metadata?: Maybe<Scalars['Json']>;
87
- notifications?: Maybe<Scalars['Map']>;
88
- pending?: Maybe<Scalars['Boolean']>;
89
- phone?: Maybe<Scalars['String']>;
90
- real_name?: Maybe<Scalars['String']>;
91
- solicitable?: Maybe<Scalars['Boolean']>;
92
- summoner_name?: Maybe<Scalars['String']>;
93
- summoner_region?: Maybe<Scalars['String']>;
94
- token?: Maybe<Scalars['String']>;
95
- user_id?: Maybe<Scalars['Snowflake']>;
96
- username?: Maybe<Scalars['String']>;
97
- verification?: Maybe<Scalars['String']>;
98
- };
1
+ export type Maybe<T> = T | null;
2
+ export type InputMaybe<T> = Maybe<T>;
3
+ export type Exact<T extends {
4
+ [key: string]: unknown;
5
+ }> = {
6
+ [K in keyof T]: T[K];
7
+ };
8
+ export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
9
+ [SubKey in K]?: Maybe<T[SubKey]>;
10
+ };
11
+ export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
12
+ [SubKey in K]: Maybe<T[SubKey]>;
13
+ };
14
+ /** All built-in and custom scalars, mapped to their actual values */
15
+ export type Scalars = {
16
+ ID: string;
17
+ String: string;
18
+ Boolean: boolean;
19
+ Int: number;
20
+ Float: number;
21
+ /** JSON */
22
+ Json: {
23
+ [key: string]: any;
24
+ };
25
+ /** Lists */
26
+ List: string[];
27
+ /** Map / Dictionary */
28
+ Map: {
29
+ [key: string]: any;
30
+ };
31
+ MapWithSnowflakeKey: {
32
+ [key: string]: any;
33
+ };
34
+ /** Sets */
35
+ Set: string[];
36
+ /** Snowflake ID */
37
+ Snowflake: string;
38
+ SnowflakeList: string[];
39
+ SnowflakeSet: string[];
40
+ };
41
+ export type League = {
42
+ __typename?: 'League';
43
+ avatar?: Maybe<Scalars['String']>;
44
+ company_id?: Maybe<Scalars['Snowflake']>;
45
+ display_order?: Maybe<Scalars['Int']>;
46
+ draft_id?: Maybe<Scalars['Snowflake']>;
47
+ group_id?: Maybe<Scalars['Snowflake']>;
48
+ last_author_avatar?: Maybe<Scalars['String']>;
49
+ last_author_display_name?: Maybe<Scalars['String']>;
50
+ last_author_id?: Maybe<Scalars['Snowflake']>;
51
+ last_author_is_bot?: Maybe<Scalars['Boolean']>;
52
+ last_message_attachment?: Maybe<Scalars['Json']>;
53
+ last_message_id?: Maybe<Scalars['Snowflake']>;
54
+ last_message_text?: Maybe<Scalars['String']>;
55
+ last_message_text_map?: Maybe<Scalars['Json']>;
56
+ last_message_time?: Maybe<Scalars['Int']>;
57
+ last_pinned_message_id?: Maybe<Scalars['Snowflake']>;
58
+ last_read_id?: Maybe<Scalars['Snowflake']>;
59
+ last_transaction_id?: Maybe<Scalars['Snowflake']>;
60
+ league_id?: Maybe<Scalars['Snowflake']>;
61
+ matchup_legs?: Maybe<Scalars['List']>;
62
+ metadata?: Maybe<Scalars['Map']>;
63
+ name?: Maybe<Scalars['String']>;
64
+ previous_league_id?: Maybe<Scalars['Snowflake']>;
65
+ roster_positions?: Maybe<Scalars['List']>;
66
+ scoring_settings?: Maybe<Scalars['Map']>;
67
+ season?: Maybe<Scalars['String']>;
68
+ season_type?: Maybe<Scalars['String']>;
69
+ settings?: Maybe<Scalars['Map']>;
70
+ sport?: Maybe<Scalars['String']>;
71
+ status?: Maybe<Scalars['String']>;
72
+ total_rosters?: Maybe<Scalars['Int']>;
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
+ };
92
+ export type Roster = {
93
+ __typename?: 'Roster';
94
+ co_owners?: Maybe<Scalars['SnowflakeSet']>;
95
+ keepers?: Maybe<Scalars['Set']>;
96
+ league_id?: Maybe<Scalars['Snowflake']>;
97
+ metadata?: Maybe<Scalars['Map']>;
98
+ owner_id?: Maybe<Scalars['Snowflake']>;
99
+ player_map?: Maybe<Scalars['Map']>;
100
+ players?: Maybe<Scalars['Set']>;
101
+ reserve?: Maybe<Scalars['Set']>;
102
+ roster_id?: Maybe<Scalars['Int']>;
103
+ settings?: Maybe<Scalars['Map']>;
104
+ starters?: Maybe<Scalars['List']>;
105
+ taxi?: Maybe<Scalars['Set']>;
106
+ };
107
+ export type User = {
108
+ __typename?: 'User';
109
+ async_bundles?: Maybe<Scalars['List']>;
110
+ avatar?: Maybe<Scalars['String']>;
111
+ cookies?: Maybe<Scalars['Int']>;
112
+ created?: Maybe<Scalars['Int']>;
113
+ currencies?: Maybe<Scalars['Map']>;
114
+ data_updated?: Maybe<Scalars['Map']>;
115
+ deleted?: Maybe<Scalars['Int']>;
116
+ display_name?: Maybe<Scalars['String']>;
117
+ email?: Maybe<Scalars['String']>;
118
+ is_bot?: Maybe<Scalars['Boolean']>;
119
+ metadata?: Maybe<Scalars['Json']>;
120
+ notifications?: Maybe<Scalars['Map']>;
121
+ pending?: Maybe<Scalars['Boolean']>;
122
+ phone?: Maybe<Scalars['String']>;
123
+ real_name?: Maybe<Scalars['String']>;
124
+ solicitable?: Maybe<Scalars['Boolean']>;
125
+ summoner_name?: Maybe<Scalars['String']>;
126
+ summoner_region?: Maybe<Scalars['String']>;
127
+ token?: Maybe<Scalars['String']>;
128
+ user_id?: Maybe<Scalars['Snowflake']>;
129
+ username?: Maybe<Scalars['String']>;
130
+ verification?: Maybe<Scalars['String']>;
131
+ };
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@sleeperhq/mini-core",
3
- "version": "1.0.3",
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",
7
+ "bin": {
8
+ "build-mini": "./bin/build_mini.js"
9
+ },
7
10
  "scripts": {
8
11
  "test": "jest",
9
12
  "lint": "eslint ."
@@ -30,15 +33,16 @@
30
33
  "access": "public"
31
34
  },
32
35
  "peerDependencies": {
33
- "@callstack/repack": "blitzstudios/repack.git#callstack-repack-v2.5.3-gitpkg",
36
+ "@callstack/repack": "blitzstudios/repack.git#callstack-repack-v2.6.0-gitpkg",
34
37
  "@react-native-community/netinfo": "^9.3.7",
35
38
  "axios": "0.15.3",
36
39
  "lodash": "4.17.21",
37
40
  "react": "17.0.2",
38
41
  "react-native": "0.66.5",
39
42
  "react-native-linear-gradient": "2.5.6",
43
+ "react-native-rename": "^3.2.12",
40
44
  "react-native-svg": "13.7.0",
41
- "react-native-udp": "^4.1.6"
45
+ "react-native-tcp-socket": "^6.0.6"
42
46
  },
43
47
  "devDependencies": {
44
48
  "@babel/core": "^7.12.9",
@@ -1,73 +1,255 @@
1
- import React, {useEffect} from 'react';
1
+ import React, {useEffect, useRef, useState} from 'react';
2
2
  import {Platform} from 'react-native';
3
- import {Config} from '../types';
4
- import {ScriptManager, Federated} from '@callstack/repack/client';
3
+ import {Config, SocketMessage} from '../types';
4
+ import { ScriptLocatorResolver, ScriptManager, Federated } from '@callstack/repack/client';
5
5
  import NetInfo from '@react-native-community/netinfo';
6
- import dgram from 'react-native-udp';
7
- import axios from 'axios';
6
+ import TcpSocket from 'react-native-tcp-socket';
7
+ import { fetchMainVersionMap, getMainUrl } from './url_resolver';
8
8
 
9
9
  let config: Config;
10
+ const RETRY_TIMER = 5000;
10
11
 
11
12
  const DevServer = props => {
12
- const onSocket = msg => {
13
- const json = JSON.parse(msg.toString());
14
- if (json?._connected) {
15
- return;
16
- }
13
+ const connection = useRef<TcpSocket.Socket>();
14
+ const partialMessage = useRef('');
15
+ const _retryTimer = useRef<NodeJS.Timeout>();
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>>();
17
25
 
18
- props.onContextChanged(json);
26
+ const _onConnected = async (value: boolean) => {
27
+ props.onConnected(value);
19
28
  };
20
29
 
21
- const onError = err => {
22
- console.error('[Sleeper] Socket error: ' + err);
30
+ const onSocket = (handler) => msg => {
31
+ let json;
32
+ try {
33
+ json = JSON.parse(msg);
34
+ } catch(e) {
35
+ partialMessage.current += msg;
36
+ }
37
+
38
+ if (partialMessage.current.length > 0) {
39
+ try {
40
+ json = JSON.parse(partialMessage.current);
41
+ partialMessage.current = '';
42
+ } catch (e) {
43
+ return;
44
+ }
45
+ }
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
+
58
+ // We should have a context object now
59
+ const context = new Proxy(json, handler);
60
+ props.onContextChanged(context);
23
61
  };
24
62
 
25
- const bindSocket = async socket => {
63
+ const sendContextRequest = (socket, propertyPath) => {
64
+ const message: SocketMessage = {_contextGet: propertyPath};
65
+ const json = JSON.stringify(message);
66
+ try {
67
+ socket?.write(json + '\n');
68
+ } catch (e) {
69
+ console.log("[Sleeper] Failed to send context request: ", e);
70
+ }
71
+ }
72
+
73
+ const proxyHandler = (socket) => {
74
+ return {
75
+ get: (target, property) => {
76
+ let value = Reflect.get(target, property);
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;
81
+ // Adding proxies to objects
82
+ const handler = proxyHandlerChild(socket, property, isLeaf);
83
+ const proxiedValue = new Proxy(value, handler);
84
+ Reflect.set(target, property, proxiedValue);
85
+ value = proxiedValue;
86
+ }
87
+
88
+ return value;
89
+ }
90
+ };
91
+ }
92
+
93
+ const proxyHandlerChild = (socket, path, isLeaf) => {
94
+ return {
95
+ get: (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);
107
+ }
108
+
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;
123
+ }
124
+ };
125
+ }
126
+
127
+ const startSocket = async () => {
26
128
  const netInfo = await NetInfo.fetch();
27
129
  const netInfoDetails = netInfo?.details;
130
+ const ipAddress = netInfoDetails?.ipAddress;
28
131
 
29
132
  if (!netInfoDetails || !('ipAddress' in netInfoDetails)) {
30
133
  console.error('[Sleeper] Failed to determine local IP address.');
31
- return;
134
+ return stopSocket();
32
135
  }
33
136
 
34
- socket.bind({port: config.localSocketPort, address: netInfoDetails.ipAddress});
35
- };
137
+ connection.current = TcpSocket.createConnection({
138
+ port: config.remoteSocketPort || 9092,
139
+ host: config.remoteIP,
140
+ localAddress: ipAddress,
141
+ reuseAddress: true,
142
+ }, () => {
143
+ // When we establish a connection, send the IP address to the server
144
+ const message: SocketMessage = {
145
+ _ip: ipAddress,
146
+ _name: config.name,
147
+ };
148
+ const json = JSON.stringify(message);
149
+ console.log('[Sleeper] Send IP address: ', ipAddress, config.name);
150
+ try {
151
+ connection.current?.write(json + '\n', "utf8", (error) => {
152
+ if (error) {
153
+ return stopSocket();
154
+ }
155
+ console.log('[Sleeper] Connected to the Sleeper App.');
156
+ _onConnected(true);
157
+ });
158
+ } catch (e) {
159
+ return stopSocket();
160
+ }
161
+ });
162
+
163
+ connection.current.on('data', (data, ...args) => {
164
+ const handler = proxyHandler(connection.current);
165
+ const onSocketHandler = onSocket(handler);
166
+ onSocketHandler(data);
167
+ });
168
+ connection.current.on('error', err => {
169
+ return stopSocket();
170
+ });
171
+ connection.current.on('close', (hadError) => {
172
+ return stopSocket();
173
+ });
174
+ }
36
175
 
37
- const pingServer = socket => {
38
- // Continue to ping the sleeper app server until it responds.
39
- return new Promise(() => {
40
- NetInfo.fetch().then(netInfo => {
41
- const netInfoDetails = netInfo?.details;
42
- if (!netInfoDetails || !('ipAddress' in netInfoDetails)) {
43
- console.error('[Sleeper] Failed to determine local IP address.');
44
- return;
45
- }
176
+ const stopSocket = (retry = true) => {
177
+ _onConnected(false);
46
178
 
47
- const json = JSON.stringify({_ip: netInfoDetails.ipAddress});
179
+ if (connection.current) {
180
+ connection.current.destroy();
181
+ connection.current = undefined;
182
+ }
48
183
 
49
- (async function ping() {
50
- socket.send(json, undefined, undefined, config.remoteSocketPort, config.remoteIP);
51
- setTimeout(ping, 5000);
52
- })();
53
- });
184
+ // Any time the socket is closed, attempt to connect again.
185
+ if (retry) {
186
+ clearTimeout(_retryTimer.current);
187
+ _retryTimer.current = setTimeout(() => {
188
+ console.log('[Sleeper] Unable to connect to sleeper, retrying...');
189
+ startSocket();
190
+ }, RETRY_TIMER);
191
+ }
192
+ }
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
+ })();
54
207
  });
55
208
  };
56
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
+
57
242
  useEffect(() => {
58
243
  if (!config) {
59
244
  console.error('[Sleeper] No config file specified. Please make sure you call DevServer.init() early in the app lifecycle.');
60
245
  return;
61
246
  }
62
247
 
63
- const socket = dgram.createSocket({type: 'udp4'});
64
- bindSocket(socket);
65
- pingServer(socket);
248
+ ScriptManager.shared.addResolver(_resolveRemoteChunk.bind(this));
249
+ startSocket();
66
250
 
67
- socket.on('message', onSocket);
68
- socket.on('error', onError);
69
251
  return () => {
70
- socket.removeAllListeners();
252
+ stopSocket(false);
71
253
  };
72
254
  }, []);
73
255
 
@@ -76,34 +258,6 @@ const DevServer = props => {
76
258
 
77
259
  DevServer.init = (_config: Config) => {
78
260
  config = _config;
79
-
80
- const remoteBundleHost = config.release ? config.remoteIP : 'localhost';
81
- const remoteBundlePort = config.release ? config.remoteBundlePort : 8081;
82
-
83
- ScriptManager.shared.addResolver(async (scriptId, caller) => {
84
- const extension =
85
- scriptId === 'sleeper' ? '.container.bundle' : '.chunk.bundle';
86
- const resolveURL = Federated.createURLResolver({
87
- containers: {
88
- sleeper: `http://${remoteBundleHost}:${remoteBundlePort}/[name]${extension}`,
89
- },
90
- });
91
-
92
- // Try to resolve URL based on scriptId and caller
93
- const url = resolveURL(scriptId, caller);
94
- const query = config.release ? undefined : {platform: Platform.OS};
95
-
96
- const response = await axios
97
- .get(url + '?' + new URLSearchParams(query), {method: 'HEAD'})
98
- .catch(() => ({
99
- status: 404,
100
- }));
101
-
102
- console.log('[Sleeper] load script:', scriptId, caller);
103
- if (response?.status === 200) {
104
- return {url, query};
105
- }
106
- });
107
- };
261
+ }
108
262
 
109
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
+ }
@@ -1,12 +1,11 @@
1
1
  export * from '../../declarations/types/index.d';
2
2
  export { default as Context } from '../../declarations/sleeper_context.d';
3
+ export { default as SocketMessage } from '../../declarations/sleeper_message.d';
3
4
 
4
5
  export type Config = {
5
6
  name: string,
6
7
  displayName: string,
7
8
  remoteIP: string,
8
- localSocketPort: number,
9
- remoteSocketPort: number,
10
- remoteBundlePort: number,
11
- release: boolean,
9
+ remoteSocketPort?: number,
10
+ dev?: boolean,
12
11
  };